From cf28aa58cf36c8f8cc8fd682454b4f9b9531beff Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:19:23 -0500 Subject: [PATCH] Init @signalapp/types package Co-authored-by: Jamie <113370520+jamiebuilds-signal@users.noreply.github.com> --- .oxlint/rules/enforceFileSuffix.mjs | 2 + .oxlintrc.json | 5 +- ACKNOWLEDGMENTS.md | 664 ++++++++++++++++++ package.json | 1 + package.schema.json | 2 +- packages/lame/package.json | 1 + packages/mute-state-change/package.json | 1 + packages/types/.gitignore | 1 + packages/types/LICENSE | 661 +++++++++++++++++ packages/types/README.md | 253 +++++++ packages/types/package.json | 51 ++ packages/types/src/Result.std.ts | 51 ++ packages/types/src/_utils/address.std.ts | 43 ++ packages/types/src/_utils/bytes.std.ts | 49 ++ packages/types/src/_utils/datetime.std.ts | 30 + packages/types/src/_utils/numbers.std.ts | 24 + packages/types/src/_utils/pni.std.ts | 13 + .../types/src/datetime/DurationDays.std.ts | 56 ++ packages/types/src/datetime/DurationMs.std.ts | 81 +++ .../types/src/datetime/DurationSecs.std.ts | 76 ++ .../src/datetime/PlainTimeHourMin.std.ts | 51 ++ .../types/src/datetime/TimestampMs.std.ts | 55 ++ .../types/src/datetime/TimestampSecs.std.ts | 49 ++ packages/types/src/encodings/Base64.std.ts | 51 ++ packages/types/src/encodings/Base64Url.std.ts | 51 ++ packages/types/src/encodings/Bytes.std.ts | 35 + packages/types/src/encodings/Hex.std.ts | 48 ++ packages/types/src/encodings/Json.std.ts | 150 ++++ packages/types/src/encodings/Utf8.std.ts | 20 + packages/types/src/formats/HexColor.std.ts | 28 + packages/types/src/formats/HttpUrl.std.ts | 28 + packages/types/src/formats/LanguageTag.std.ts | 37 + packages/types/src/formats/MimeType.std.ts | 28 + packages/types/src/formats/Semver.std.ts | 33 + packages/types/src/formats/Uuid.std.ts | 43 ++ packages/types/src/index.std.ts | 65 ++ packages/types/src/numbers/BigInt128.std.ts | 38 + packages/types/src/numbers/BigInt64.std.ts | 38 + packages/types/src/numbers/BigUint128.std.ts | 38 + packages/types/src/numbers/BigUint64.std.ts | 38 + packages/types/src/numbers/Float16.std.ts | 32 + packages/types/src/numbers/Float32.std.ts | 32 + packages/types/src/numbers/Float64.std.ts | 28 + packages/types/src/numbers/Int16.std.ts | 39 + packages/types/src/numbers/Int32.std.ts | 39 + packages/types/src/numbers/Int8.std.ts | 39 + packages/types/src/numbers/Uint16.std.ts | 39 + packages/types/src/numbers/Uint32.std.ts | 39 + packages/types/src/numbers/Uint8.std.ts | 39 + packages/types/src/service/Aci.std.ts | 53 ++ packages/types/src/service/Address.std.ts | 67 ++ packages/types/src/service/AddressInfo.std.ts | 49 ++ packages/types/src/service/DeviceId.std.ts | 34 + .../types/src/service/DistributionId.std.ts | 53 ++ .../src/service/DistributionListId.std.ts | 53 ++ packages/types/src/service/E164.std.ts | 47 ++ packages/types/src/service/GroupId.std.ts | 52 ++ packages/types/src/service/Pni.std.ts | 64 ++ .../src/service/ReceivedTimestampMs.std.ts | 44 ++ .../types/src/service/SentTimestampMs.std.ts | 44 ++ .../src/service/ServerTimestampMs.std.ts | 44 ++ packages/types/src/service/ServiceId.std.ts | 46 ++ .../types/src/service/StorageItemKey.std.ts | 47 ++ .../src/service/StorageManifestVersion.std.ts | 37 + packages/types/src/service/UntaggedPni.std.ts | 60 ++ packages/types/src/units/UnitBytes.std.ts | 51 ++ packages/types/src/units/UnitKibibytes.std.ts | 46 ++ packages/types/src/units/UnitKilobytes.std.ts | 46 ++ packages/types/tsconfig.json | 51 ++ packages/types/tsdown.config.mjs | 17 + packages/windows-ucv/package.json | 2 +- pnpm-lock.yaml | 620 +++++++++++++++- .../pinned-messages/PinMessageDialog.dom.tsx | 14 +- ts/jobs/conversationJobQueue.preload.ts | 20 +- ts/messageModifiers/PinnedMessages.preload.ts | 11 +- ts/services/backups/import.preload.ts | 7 +- .../pinnedMessagesCleanupService.preload.ts | 5 +- ts/services/retryPlaceholders.std.ts | 5 +- ts/sql/Interface.std.ts | 11 +- ts/state/ducks/conversations.preload.ts | 11 +- .../SignalProtocolStore_test.preload.ts | 35 +- .../services/retryPlaceholders_test.std.ts | 41 +- .../sql/server/pinnedMessages_test.node.ts | 7 +- ts/textsecure/MessageReceiver.preload.ts | 24 +- ts/textsecure/Types.d.ts | 16 +- ts/textsecure/messageReceiverEvents.std.ts | 24 +- ts/textsecure/processDataMessage.preload.ts | 11 +- ts/types/PinnedMessage.std.ts | 15 +- ts/util/getPinMessageTarget.preload.ts | 13 +- ts/util/pinnedMessages.std.ts | 14 +- 90 files changed, 5100 insertions(+), 126 deletions(-) create mode 100644 packages/types/.gitignore create mode 100644 packages/types/LICENSE create mode 100644 packages/types/README.md create mode 100644 packages/types/package.json create mode 100644 packages/types/src/Result.std.ts create mode 100644 packages/types/src/_utils/address.std.ts create mode 100644 packages/types/src/_utils/bytes.std.ts create mode 100644 packages/types/src/_utils/datetime.std.ts create mode 100644 packages/types/src/_utils/numbers.std.ts create mode 100644 packages/types/src/_utils/pni.std.ts create mode 100644 packages/types/src/datetime/DurationDays.std.ts create mode 100644 packages/types/src/datetime/DurationMs.std.ts create mode 100644 packages/types/src/datetime/DurationSecs.std.ts create mode 100644 packages/types/src/datetime/PlainTimeHourMin.std.ts create mode 100644 packages/types/src/datetime/TimestampMs.std.ts create mode 100644 packages/types/src/datetime/TimestampSecs.std.ts create mode 100644 packages/types/src/encodings/Base64.std.ts create mode 100644 packages/types/src/encodings/Base64Url.std.ts create mode 100644 packages/types/src/encodings/Bytes.std.ts create mode 100644 packages/types/src/encodings/Hex.std.ts create mode 100644 packages/types/src/encodings/Json.std.ts create mode 100644 packages/types/src/encodings/Utf8.std.ts create mode 100644 packages/types/src/formats/HexColor.std.ts create mode 100644 packages/types/src/formats/HttpUrl.std.ts create mode 100644 packages/types/src/formats/LanguageTag.std.ts create mode 100644 packages/types/src/formats/MimeType.std.ts create mode 100644 packages/types/src/formats/Semver.std.ts create mode 100644 packages/types/src/formats/Uuid.std.ts create mode 100644 packages/types/src/index.std.ts create mode 100644 packages/types/src/numbers/BigInt128.std.ts create mode 100644 packages/types/src/numbers/BigInt64.std.ts create mode 100644 packages/types/src/numbers/BigUint128.std.ts create mode 100644 packages/types/src/numbers/BigUint64.std.ts create mode 100644 packages/types/src/numbers/Float16.std.ts create mode 100644 packages/types/src/numbers/Float32.std.ts create mode 100644 packages/types/src/numbers/Float64.std.ts create mode 100644 packages/types/src/numbers/Int16.std.ts create mode 100644 packages/types/src/numbers/Int32.std.ts create mode 100644 packages/types/src/numbers/Int8.std.ts create mode 100644 packages/types/src/numbers/Uint16.std.ts create mode 100644 packages/types/src/numbers/Uint32.std.ts create mode 100644 packages/types/src/numbers/Uint8.std.ts create mode 100644 packages/types/src/service/Aci.std.ts create mode 100644 packages/types/src/service/Address.std.ts create mode 100644 packages/types/src/service/AddressInfo.std.ts create mode 100644 packages/types/src/service/DeviceId.std.ts create mode 100644 packages/types/src/service/DistributionId.std.ts create mode 100644 packages/types/src/service/DistributionListId.std.ts create mode 100644 packages/types/src/service/E164.std.ts create mode 100644 packages/types/src/service/GroupId.std.ts create mode 100644 packages/types/src/service/Pni.std.ts create mode 100644 packages/types/src/service/ReceivedTimestampMs.std.ts create mode 100644 packages/types/src/service/SentTimestampMs.std.ts create mode 100644 packages/types/src/service/ServerTimestampMs.std.ts create mode 100644 packages/types/src/service/ServiceId.std.ts create mode 100644 packages/types/src/service/StorageItemKey.std.ts create mode 100644 packages/types/src/service/StorageManifestVersion.std.ts create mode 100644 packages/types/src/service/UntaggedPni.std.ts create mode 100644 packages/types/src/units/UnitBytes.std.ts create mode 100644 packages/types/src/units/UnitKibibytes.std.ts create mode 100644 packages/types/src/units/UnitKilobytes.std.ts create mode 100644 packages/types/tsconfig.json create mode 100644 packages/types/tsdown.config.mjs diff --git a/.oxlint/rules/enforceFileSuffix.mjs b/.oxlint/rules/enforceFileSuffix.mjs index 413bb128ec..f926cc05c7 100644 --- a/.oxlint/rules/enforceFileSuffix.mjs +++ b/.oxlint/rules/enforceFileSuffix.mjs @@ -145,6 +145,7 @@ const NODE_PACKAGES = new Set([ 'svgo', 'synckit', 'tailwindcss', + 'tsdown', 'tsx', 'typescript', 'wait-on', @@ -206,6 +207,7 @@ const STD_PACKAGES = new Set([ '@indutny/sneequals', '@internationalized/date', '@react-types/shared', + '@signalapp/types', '@signalapp/minimask', '@signalapp/parchment-cjs', '@signalapp/quill-cjs', diff --git a/.oxlintrc.json b/.oxlintrc.json index a9bb37d2d2..e387fa8ee7 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1763,7 +1763,10 @@ ".storybook/**", "codemods/**", "danger/**", - "packages/**", + "packages/types/tsdown.config.mjs", + "packages/lame/**", + "packages/mute-state-change/**", + "packages/windows-ucv/**", "scripts/**", "test/test.mjs", ".pnpmfile.mjs", diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index 7c5c3bf607..4d8e5a14e5 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -3368,6 +3368,670 @@ Signal Desktop makes use of the following open source projects. For more information on this, and how to apply and follow the GNU AGPL, see . +## @signalapp/types + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for + software and other kinds of works, specifically designed to ensure + cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed + to take away your freedom to share and change the works. By contrast, + our General Public Licenses are intended to guarantee your freedom to + share and change all versions of a program--to make sure it remains free + software for all its users. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + them if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights + with two steps: (1) assert copyright on the software, and (2) offer + you this License which gives you legal permission to copy, distribute + and/or modify the software. + + A secondary benefit of defending all users' freedom is that + improvements made in alternate versions of the program, if they + receive widespread use, become available for other developers to + incorporate. Many developers of free software are heartened and + encouraged by the resulting cooperation. However, in the case of + software used on network servers, this result may fail to come about. + The GNU General Public License permits making a modified version and + letting the public access it on a server without ever releasing its + source code to the public. + + The GNU Affero General Public License is designed specifically to + ensure that, in such cases, the modified source code becomes available + to the community. It requires the operator of a network server to + provide the source code of the modified version running there to the + users of that server. Therefore, public use of a modified version, on + a publicly accessible server, gives the public access to the source + code of the modified version. + + An older license, called the Affero General Public License and + published by Affero, was designed to accomplish similar goals. This is + a different license, not a version of the Affero GPL, but Affero has + released a new version of the Affero GPL which permits relicensing under + this license. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of + works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work + in a fashion requiring copyright permission, other than the making of an + exact copy. The resulting work is called a "modified version" of the + earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based + on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on a + computer or modifying a private copy. Propagation includes copying, + distribution (with or without modification), making available to the + public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user through + a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the + work under this License, and how to view a copy of this License. If + the interface presents a list of user commands or options, such as a + menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an official + standard defined by a recognized standards body, or, in the case of + interfaces specified for a particular programming language, one that + is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other + than the work as a whole, that (a) is included in the normal form of + packaging a Major Component, but which is not part of that Major + Component, and (b) serves only to enable use of the work with that + Major Component, or to implement a Standard Interface for which an + implementation is available to the public in source code form. A + "Major Component", in this context, means a major essential component + (kernel, window system, and so on) of the specific operating system + (if any) on which the executable work runs, or a compiler used to + produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts to + control those activities. However, it does not include the work's + System Libraries, or general-purpose tools or generally available free + programs which are used unmodified in performing those activities but + which are not part of the work. For example, Corresponding Source + includes interface definition files associated with source files for + the work, and the source code for shared libraries and dynamically + linked subprograms that the work is specifically designed to require, + such as by intimate data communication or control flow between those + subprograms and other parts of the work. + + The Corresponding Source need not include anything that users + can regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running a + covered work is covered by this License only if the output, given its + content, constitutes a covered work. This License acknowledges your + rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise remains + in force. You may convey covered works to others for the sole purpose + of having them make modifications exclusively for you, or provide you + with facilities for running those works, provided that you comply with + the terms of this License in conveying all material for which you do + not control copyright. Those thus making or running the covered works + for you must do so exclusively on your behalf, under your direction + and control, on terms that prohibit them from making any copies of + your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section 10 + makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under article + 11 of the WIPO copyright treaty adopted on 20 December 1996, or + similar laws prohibiting or restricting circumvention of such + measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such circumvention + is effected by exercising rights under this License with respect to + the covered work, and you disclaim any intention to limit operation or + modification of the work as a means of enforcing, against the work's + users, your or third parties' legal rights to forbid circumvention of + technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the code; + keep intact all notices of the absence of any warranty; and give all + recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered work, + and which are not combined with it such as to form a larger program, + in or on a volume of a storage or distribution medium, is called an + "aggregate" if the compilation and its resulting copyright are not + used to limit the access or legal rights of the compilation's users + beyond what the individual works permit. Inclusion of a covered work + in an aggregate does not cause this License to apply to the other + parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this License, + in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded + from the Corresponding Source as a System Library, need not be + included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any + tangible personal property which is normally used for personal, family, + or household purposes, or (2) anything designed or sold for incorporation + into a dwelling. In determining whether a product is a consumer product, + doubtful cases shall be resolved in favor of coverage. For a particular + product received by a particular user, "normally used" refers to a + typical or common use of that class of product, regardless of the status + of the particular user or of the way in which the particular user + actually uses, or expects or is expected to use, the product. A product + is a consumer product regardless of whether the product has substantial + commercial, industrial or non-consumer uses, unless such uses represent + the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to install + and execute modified versions of a covered work in that User Product from + a modified version of its Corresponding Source. The information must + suffice to ensure that the continued functioning of the modified object + code is in no case prevented or interfered with solely because + modification has been made. + + If you convey an object code work under this section in, or with, or + specifically for use in, a User Product, and the conveying occurs as + part of a transaction in which the right of possession and use of the + User Product is transferred to the recipient in perpetuity or for a + fixed term (regardless of how the transaction is characterized), the + Corresponding Source conveyed under this section must be accompanied + by the Installation Information. But this requirement does not apply + if neither you nor any third party retains the ability to install + modified object code on the User Product (for example, the work has + been installed in ROM). + + The requirement to provide Installation Information does not include a + requirement to continue to provide support service, warranty, or updates + for a work that has been modified or installed by the recipient, or for + the User Product in which it has been modified or installed. Access to a + network may be denied when the modification itself materially and + adversely affects the operation of the network or violates the rules and + protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, + in accord with this section must be in a format that is publicly + documented (and with an implementation available to the public in + source code form), and must require no special password or key for + unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this + License by making exceptions from one or more of its conditions. + Additional permissions that are applicable to the entire Program shall + be treated as though they were included in this License, to the extent + that they are valid under applicable law. If additional permissions + apply only to part of the Program, that part may be used separately + under those permissions, but the entire Program remains governed by + this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part of + it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you + add to a covered work, you may (if authorized by the copyright holders of + that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as you + received it, or any part of it, contains a notice stating that it is + governed by this License along with a term that is a further + restriction, you may remove that term. If a license document contains + a further restriction but permits relicensing or conveying under this + License, you may add to a covered work material governed by the terms + of that license document, provided that the further restriction does + not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the + form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights under + this License (including any patent licenses granted under the third + paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the copyright + holder fails to notify you of the violation by some reasonable means + prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from that + copyright holder, and you cure the violation prior to 30 days after + your receipt of the notice. + + Termination of your rights under this section does not terminate the + licenses of parties who have received copies or rights from you under + this License. If your rights have been terminated and not permanently + reinstated, you do not qualify to receive new licenses for the same + material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer transmission + to receive a copy likewise does not require acceptance. However, + nothing other than this License grants you permission to propagate or + modify any covered work. These actions infringe copyright if you do + not accept this License. Therefore, by modifying or propagating a + covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not responsible + for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a covered + work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or could + give under the previous paragraph, plus a right to possession of the + Corresponding Source of the work from the predecessor in interest, if + the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you may + not impose a license fee, royalty, or other charge for exercise of + rights granted under this License, and you may not initiate litigation + (including a cross-claim or counterclaim in a lawsuit) alleging that + any patent claim is infringed by making, using, selling, offering for + sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. The + work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, permitted + by this License, of making, using, or selling its contributor version, + but do not include claims that would be infringed only as a + consequence of further modification of the contributor version. For + purposes of this definition, "control" includes the right to grant + patent sublicenses in a manner consistent with the requirements of + this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free + patent license under the contributor's essential patent claims, to + make, use, sell, offer for sale, import and otherwise run, modify and + propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express + agreement or commitment, however denominated, not to enforce a patent + (such as an express permission to practice a patent or covenant not to + sue for patent infringement). To "grant" such a patent license to a + party means to make such an agreement or commitment not to enforce a + patent against the party. + + If you convey a covered work, knowingly relying on a patent license, + and the Corresponding Source of the work is not available for anyone + to copy, free of charge and under the terms of this License, through a + publicly available network server or other readily accessible means, + then you must either (1) cause the Corresponding Source to be so + available, or (2) arrange to deprive yourself of the benefit of the + patent license for this particular work, or (3) arrange, in a manner + consistent with the requirements of this License, to extend the patent + license to downstream recipients. "Knowingly relying" means you have + actual knowledge that, but for the patent license, your conveying the + covered work in a country, or your recipient's use of the covered work + in a country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, modify + or convey a specific copy of the covered work, then the patent license + you grant is automatically extended to all recipients of the covered + work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that are + specifically granted under this License. You may not convey a covered + work if you are a party to an arrangement with a third party that is + in the business of distributing software, under which you make payment + to the third party based on the extent of your activity of conveying + the work, and under which the third party grants, to any of the + parties who would receive the covered work from you, a discriminatory + patent license (a) in connection with copies of the covered work + conveyed by you (or copies made from those copies), or (b) primarily + for and in connection with specific products or compilations that + contain the covered work, unless you entered into that arrangement, + or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot convey a + covered work so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you may + not convey it at all. For example, if you agree to terms that obligate you + to collect a royalty for further conveying from those to whom you convey + the Program, the only way you could satisfy both those terms and this + License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the + Program, your modified version must prominently offer all users + interacting with it remotely through a computer network (if your version + supports such interaction) an opportunity to receive the Corresponding + Source of your version by providing access to the Corresponding Source + from a network server at no charge, through some standard or customary + means of facilitating copying of software. This Corresponding Source + shall include the Corresponding Source for any work covered by version 3 + of the GNU General Public License that is incorporated pursuant to the + following paragraph. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU General Public License into a single + combined work, and to convey the resulting work. The terms of this + License will continue to apply to the part which is the covered work, + but the work with which it is combined will remain governed by version + 3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of + the GNU Affero General Public License from time to time. Such new versions + will be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU Affero General + Public License "or any later version" applies to it, you have the + option of following the terms and conditions either of that numbered + version or of any later version published by the Free Software + Foundation. If the Program does not specify a version number of the + GNU Affero General Public License, you may choose any version ever published + by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU Affero General Public License can be used, that proxy's + public statement of acceptance of a version permanently authorizes you + to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely approximates + an absolute waiver of all civil liability in connection with the + Program, unless a warranty or assumption of liability accompanies a + copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + state the exclusion of warranty; and each file should have at least + the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer + network, you should also make sure that it provides a way for users to + get its source. For example, if your program is a web application, its + interface could display a "Source" link that leads users to an archive + of the code. There are many ways you could offer source, and different + solutions will be better for different programs; see section 13 for the + specific requirements. + + You should also get your employer (if you work as a programmer) or school, + if any, to sign a "copyright disclaimer" for the program, if necessary. + For more information on this, and how to apply and follow the GNU AGPL, see + . + ## @signalapp/windows-ucv GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/package.json b/package.json index 61fed4539d..5a9fa4f88a 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "dependencies": { "@indutny/mac-screen-share": "1.2.5", "@indutny/simple-windows-notifications": "2.0.21", + "@signalapp/types": "workspace:*", "@signalapp/libsignal-client": "0.96.3", "@signalapp/mute-state-change": "workspace:*", "@signalapp/ringrtc": "2.69.4", diff --git a/package.schema.json b/package.schema.json index 4761d2ebfa..03d2430804 100644 --- a/package.schema.json +++ b/package.schema.json @@ -7,7 +7,7 @@ "type": "object", "properties": { "build": { - "$ref": "./node_modules/app-builder-lib/scheme.json" + "$ref": "https://raw.githubusercontent.com/electron-userland/electron-builder/refs/heads/master/packages/app-builder-lib/scheme.json" } } } diff --git a/packages/lame/package.json b/packages/lame/package.json index 6a2f5ef495..f0337e31df 100644 --- a/packages/lame/package.json +++ b/packages/lame/package.json @@ -1,4 +1,5 @@ { + "private": true, "name": "@signalapp/lame", "description": "LAME MP3 Encoder bindings for Node.js", "version": "1.0.0", diff --git a/packages/mute-state-change/package.json b/packages/mute-state-change/package.json index ec45211cc1..f0c6510698 100644 --- a/packages/mute-state-change/package.json +++ b/packages/mute-state-change/package.json @@ -1,4 +1,5 @@ { + "private": true, "name": "@signalapp/mute-state-change", "description": "ScreenCaptureKit bindings for Node.js", "version": "1.0.0", diff --git a/packages/types/.gitignore b/packages/types/.gitignore new file mode 100644 index 0000000000..1521c8b765 --- /dev/null +++ b/packages/types/.gitignore @@ -0,0 +1 @@ +dist diff --git a/packages/types/LICENSE b/packages/types/LICENSE new file mode 100644 index 0000000000..be3f7b28e5 --- /dev/null +++ b/packages/types/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/packages/types/README.md b/packages/types/README.md new file mode 100644 index 0000000000..a12cb5c682 --- /dev/null +++ b/packages/types/README.md @@ -0,0 +1,253 @@ + + + +# @signalapp/types + +This package provides shared opaque and utility types and related helper +functions for use across different Signal libraries and desktop apps. + +```ts +import { Aci, SentTimestampMs, DurationSecs } from '@signalapp/types'; + +export type PinnedMessage = Readonly<{ + targetSentTimestamp: SentTimestampMs; + targetAuthorAci: Aci; + pinDuration: DurationSecs; +}>; +``` + +## API + +```ts +import type { Tagged } from 'type-fest'; + +// Result +type Result = Result.Ok | Result.Err; +type Result.Ok = Readonly<{ ok: true; value: T; error?: never; }>; +type Result.Err = Readonly<{ ok: false; value?: never; error: E; }>; + +// Numbers +type Uint8 = Tagged; +type Uint16 = Tagged; +type Uint32 = Tagged; +type BigUint64 = Tagged; +type BigUint128 = Tagged; +type Int8 = Tagged; +type Int16 = Tagged; +type Int32 = Tagged; +type BigInt64 = Tagged; +type BigInt128 = Tagged; +type Float16 = Tagged; +type Float32 = Tagged; +type Float64 = Tagged; + +// Units +type UnitBytes = Tagged;; +type UnitKilobytes = Tagged; +type UnitKibibytes = Tagged; + +// Datetime +type TimestampMs = Tagged; +type TimestampSecs = Tagged; +type DurationMs = Tagged; +type DurationSecs = Tagged; +type DurationDays = Tagged; +type PlainTimeHourMin = Tagged; + +// Encodings +type Bytes = Uint8Array; +type Bytes.Of = Tagged; +type Base64 = Tagged; +type Base64.Of = Tagged; +type Base64Url = Tagged; +type Base64Url.Of = Tagged; +type Hex = Tagged; +type Hex.Of = Tagged; +type Json = Tagged; +type Json.Of = Tagged; +type JsonPrimitive = null | boolean | number | string; +type JsonArray = Array | ReadonlyArray; +type JsonObject = { [Key in string]: JsonValue }; +type JsonValue = JsonPrimitive | JsonArray | JsonObject; +type Utf8.Of = Tagged; + +// Formats +type HexColor = Tagged<`#${string}`, 'HexColor'>; +type HttpUrl = Tagged; +type LanguageTag = Tagged; +type MimeType = Tagged<`${string}/${string}`, 'MimeType'>; +type Semver = Tagged; +type Uuid = Tagged; + +// Service +type E164 = E164.Decoded; +type E164.Opaque = Tagged<`+${string}`, 'E164'>; +type E164.Encoded = Utf8.Of; +type E164.Decoded = Bytes.Of; + +type Aci = Aci.Decoded; +type Aci.Opaque = Tagged; +type Aci.Decoded = Utf8.Of; +type Aci.Encoded = Bytes.Of; + +type Pni = Utf8.Of; +type Pni.Opaque = Tagged<`PNI:${Uuid}`, 'Pni'>; +type Pni.Decoded = Utf8.Of; +type Pni.Encoded = Bytes.Of; + +type UntaggedPni = UntaggedPni.Decoded; +type UntaggedPni.Opaque = Tagged; +type UntaggedPni.Decoded = Utf8.Of; +type UntaggedPni.Encoded = Bytes.Of; + +type ServiceId = ServiceId.Decoded; +type ServiceId.Decoded = Aci | Pni; +type ServiceId.Encoded = Bytes.Of; + +type DeviceId = Tagged; + +type Address = Address.Decoded; +type Address.Opaque = Tagged<`${ServiceId}.${DeviceId}`, 'Address'>; +type Address.Decoded = Utf8.Of; +type Address.Encoded = Bytes.Of; + +type AddressInfo = Tagged; +type AddressInfo.Params = Readonly<{ serviceId: ServiceId; deviceId: DeviceId; }>; + +type DistributionId = Utf8.Of; +type DistributionId.Opaque = Tagged; + +type DistributionId = DistributionId.Decoded; +type DistributionId.Opaque = Tagged; +type DistributionId.Decoded = Utf8.Of; +type DistributionId.Encoded = Bytes.Of; + +type GroupId = GroupId.Decoded; +type GroupId.Opaque = Tagged; +type GroupId.Decoded = Base64.Of; +type GroupId.Encoded = Bytes.Of; + +type DistributionListId = DistributionListId.Decoded; +type DistributionListId.Opaque = Tagged; +type DistributionListId.Decoded = Utf8.Of; +type DistributionListId.Encoded = Bytes.Of; + +type SentTimestampMs = Tagged; +type ServerTimestampMs = Tagged; +type ReceivedTimestampMs = Tagged; + +type StorageManifestVersion = Tagged; + +type StorageItemKey = StorageItemKey.Decoded; +type StorageItemKey.Opaque = Tagged; +type StorageItemKey.Decoded = Base64.Of; +type StorageItemKey.Encoded = Bytes.Of; +``` + +### Schemas + +Almost all of these types provide Zod schemas: + +```ts +import { Aci, SentTimestampMs, DurationSecs } from '@signalapp/types'; + +export const PinnedMessageSchema = z.object({ + targetSentTimestamp: SentTimestampMs.Schema, + targetAuthorAci: Aci.Schema, + pinDuration: DurationSecs.Schema, +}); +``` + +### Helpers + +Types have helpers for casting loosely typed data to strict types: + +> Note: You should prefer to use the most specific `from*` method that you can, +> it will generally have less runtime cost checking the type is valid. + +```ts +Aci.isValid(input: string): input is Aci; // boolean +Aci.fromUuid(input: Uuid): Aci; // throws if invalid +Aci.fromString(input: string): Aci; // throws if invalid +``` + +### Encodings + +Encodings have special `.Of` types to represent internal opaque +types: + +```ts +// Maybe this just represents some random number generation that we should not be aware of +export type MyOpaqueType = Tagged; + +export function decode(input: Bytes.Of): Base64.Of { + return Base64.fromBytes(input); +} + +export function encode(input: Bytes.Of): Base64.Of { + return Base64.toBytes(input); +} +``` + +Many of the "service" types use these `.Of` types to wrap their own +internal `Opaque` type with expected encodings along with `encode` and `decode` +helpers: + +```ts +export type StorageItemKey = StorageItemKey.Decoded; + +export namespace StorageItemKey { + type Opaque = Tagged; + + export type Decoded = Base64.Of; + export type Encoded = Bytes.Of; + + export function decode(input: Encoded): StorageItemKey { + return Base64.fromBytes(input); + } + + export function encode(input: StorageItemKey): Encoded { + return Base64.toBytes(input); + } +} +``` + +## Future Ideas + +- General: + - [ ] `NonEmpty` + - [ ] `NonZero` +- Units: + - [ ] `Dimension` + - [ ] `Width` + - [ ] `Height` +- Formats: + - [ ] `BlurHash` + - [ ] `RegionId` + - [ ] `CurrencyCode` + - [ ] `Email` +- File System: + - [ ] `FileName` + - [ ] `FilePath` + - [ ] `FileExtension` +- Net: + - [ ] `BasicAuth` + - [ ] `IpV4` + - [ ] `IpV6` + - [ ] `IpAddr` + - [ ] `Domain/Hostname` + - [ ] `Protocol` + - [ ] `Port` +- Service: + - [ ] `RegistrationId` + - [ ] `ServerGuid` + - [ ] `ProfileKey` + - [ ] `ProfileKeyVersion + - [ ] `Username` + - [ ] `ReportingToken` + - [ ] `GroupVersion` + - [ ] `MasterKey` + - [ ] `PublicParams` + - [ ] `SecretParams` + - [ ] `GroupInviteLinkPassword` + - [ ] `UserText` / `TrustedText` diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000000..6d035f5e7c --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,51 @@ +{ + "name": "@signalapp/types", + "version": "0.0.0", + "license": "AGPL-3.0-only", + "author": { + "name": "Signal Messenger, LLC", + "email": "support@signal.org" + }, + "type": "module", + "sideEffects": false, + "source": "src/index.std.ts", + "main": "dist/index.std.cjs", + "module": "dist/index.std.mjs", + "types": "dist/index.std.d.mts", + "files": [ + "dist" + ], + "exports": { + ".": { + "import": { + "types": "./dist/index.std.d.mts", + "default": "./dist/index.std.mjs" + }, + "require": { + "types": "./dist/index.std.d.cts", + "default": "./dist/index.std.cjs" + } + } + }, + "engines": { + "node": ">=24.17.0" + }, + "scripts": { + "check": "tsgo --noEmit", + "build": "tsdown", + "prepare": "pnpm run build" + }, + "dependencies": { + "buffer": "6.0.3", + "semver": "7.7.4", + "type-fest": "5.5.0", + "uuid": "13.0.0", + "zod": "4.3.6" + }, + "devDependencies": { + "@arethetypeswrong/core": "0.18.4", + "@typescript/native-preview": "7.0.0-dev.20260515.1", + "publint": "0.3.21", + "tsdown": "0.22.3" + } +} diff --git a/packages/types/src/Result.std.ts b/packages/types/src/Result.std.ts new file mode 100644 index 0000000000..c9d08f7af2 --- /dev/null +++ b/packages/types/src/Result.std.ts @@ -0,0 +1,51 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * A discriminated union representing either success ({@link Result.Ok}) or failure ({@link Result.Err}). + * @public + */ +export type Result = Result.Ok | Result.Err; + +export namespace Result { + /** + * Successful result containing a value. + * @public + */ + export type Ok = Readonly<{ + ok: true; + value: T; + error?: never; + }>; + + /** + * Failed result containing an error. + * @public + */ + export type Err = Readonly<{ + ok: false; + value?: never; + error: E; + }>; + + /** @public */ + export function ok(value: T): Ok { + return { ok: true, value }; + } + + /** @public */ + export function err(error: E): Err { + return { ok: false, error }; + } + + /** @public */ + export async function fromPromise( + promise: Promise + ): Promise> { + try { + return ok(await promise); + } catch (error) { + return err(error); + } + } +} diff --git a/packages/types/src/_utils/address.std.ts b/packages/types/src/_utils/address.std.ts new file mode 100644 index 0000000000..0972a44206 --- /dev/null +++ b/packages/types/src/_utils/address.std.ts @@ -0,0 +1,43 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import { Result } from '../Result.std.ts'; +import { ServiceId } from '../service/ServiceId.std.ts'; +import { safeParseNumber } from './numbers.std.ts'; +import { Uint32 } from '../numbers/Uint32.std.ts'; +import { DeviceId } from '../service/DeviceId.std.ts'; +import type { AddressInfo } from '../service/AddressInfo.std.ts'; + +export function parseAddress(input: string): Result { + const parts = input.split('.'); + if (parts.length !== 2) { + return Result.err('must have format "."'); + } + + const [serviceId, deviceIdStr] = parts; + if (serviceId == null || serviceId.length === 0) { + return Result.err('missing service id'); + } + + if (!ServiceId.isValid(serviceId)) { + return Result.err('invalid service id'); + } + + if (deviceIdStr == null || deviceIdStr.length === 0) { + return Result.err('missing device id'); + } + + const deviceId = safeParseNumber(deviceIdStr); + if (deviceId == null) { + return Result.err('device id is not a number'); + } + + if (!Uint32.isValid(deviceId)) { + return Result.err('device id is not a valid uint32'); + } + + if (!DeviceId.isValid(deviceId)) { + return Result.err('invalid device id'); + } + + return Result.ok({ serviceId, deviceId }); +} diff --git a/packages/types/src/_utils/bytes.std.ts b/packages/types/src/_utils/bytes.std.ts new file mode 100644 index 0000000000..bbe9ba17dd --- /dev/null +++ b/packages/types/src/_utils/bytes.std.ts @@ -0,0 +1,49 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// oxlint-disable-next-line unicorn/prefer-node-protocol +import { Buffer } from 'buffer'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * TODO: Once upgrading to Node 25+ we can replace Buffer with Uint8Array methods. + */ + +/** Future: `new TextDecoder().decode(input)` */ +export function TextDecoderDecode(input: Bytes): string { + return Buffer.from(input).toString('utf8'); +} + +/** Future: `new TextEncoder().encode(input)` */ +export function TextEncoderEncode(input: string): Bytes { + return Buffer.from(input, 'utf8'); +} + +/** Future: `input.toBase64()` */ +export function Uint8ArrayToBase64(input: Bytes): string { + return Buffer.from(input).toString('base64'); +} + +/** Future: `Uint8Array.fromBase64(input)` */ +export function Uint8ArrayFromBase64(input: string): Bytes { + return Buffer.from(input, 'base64'); +} + +/** Future: `input.toBase64({ alphabet: 'base64url', omitPadding: true })` */ +export function Uint8ArrayToBase64Url(input: Bytes): string { + return Buffer.from(input).toString('base64url'); +} + +/** Future: `Uint8Array.fromBase64(input, { alphabet: 'base64url' })` */ +export function Uint8ArrayFromBase64Url(input: string): Bytes { + return Buffer.from(input, 'base64url'); +} + +/** Future: `input.toHex()` */ +export function Uint8ArrayToHex(input: Bytes): string { + return Buffer.from(input).toString('hex'); +} + +/** Future: `Uint8Array.fromHex(input)` */ +export function Uint8ArrayFromHex(input: string): Bytes { + return Buffer.from(input, 'hex'); +} diff --git a/packages/types/src/_utils/datetime.std.ts b/packages/types/src/_utils/datetime.std.ts new file mode 100644 index 0000000000..6c0537043c --- /dev/null +++ b/packages/types/src/_utils/datetime.std.ts @@ -0,0 +1,30 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +export const MIN_SAFE_DATE = -8_640_000_000_000_000; +export const MAX_SAFE_DATE = 8_640_000_000_000_000; + +const MS_PER_SEC = 1000; +const MS_PER_MIN: number = 60 * MS_PER_SEC; +const MS_PER_HOUR: number = 60 * MS_PER_MIN; +const MS_PER_DAY: number = 24 * MS_PER_HOUR; + +const SECS_PER_MIN = 60; +const SECS_PER_HOUR: number = 60 * SECS_PER_MIN; +const SECS_PER_DAY: number = 24 * SECS_PER_HOUR; + +export const msToSecsInt = (ms: number): number => Math.trunc(ms / MS_PER_SEC); +export const msToDaysInt = (ms: number): number => Math.trunc(ms / MS_PER_DAY); + +export const secsToMs = (seconds: number): number => seconds * MS_PER_SEC; +export const secsToDays = (secs: number): number => + Math.trunc(secs / SECS_PER_DAY); + +export const minsToMs = (minutes: number): number => minutes * MS_PER_MIN; +export const minsToSecs = (mins: number): number => mins * SECS_PER_MIN; + +export const hoursToMs = (hours: number): number => hours * MS_PER_HOUR; +export const hoursToSecs = (hours: number): number => hours * SECS_PER_HOUR; + +export const daysToMs = (days: number): number => days * MS_PER_DAY; +export const daysToSecs = (days: number): number => days * SECS_PER_DAY; diff --git a/packages/types/src/_utils/numbers.std.ts b/packages/types/src/_utils/numbers.std.ts new file mode 100644 index 0000000000..746be966a1 --- /dev/null +++ b/packages/types/src/_utils/numbers.std.ts @@ -0,0 +1,24 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER); + +export function bigIntToNumber(input: bigint): number { + if (input > MAX_SAFE_INTEGER_BIGINT) { + throw new TypeError('must be <= MAX_SAFE_INTEGER'); + } + + return Number(input); +} + +export function safeParseNumber(input: string): number | null { + const trimmed = input.trim(); + if (trimmed === '') { + return null; + } + const parsed = Number(input); + if (!Number.isFinite(parsed)) { + return null; + } + return parsed; +} diff --git a/packages/types/src/_utils/pni.std.ts b/packages/types/src/_utils/pni.std.ts new file mode 100644 index 0000000000..c9c96f6e94 --- /dev/null +++ b/packages/types/src/_utils/pni.std.ts @@ -0,0 +1,13 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +export const PNI_PREFIX = 'PNI:'; + +const PNI_PREFIX_REGEX = /^PNI:/; + +export function stripPniPrefix(input: string): string { + if (!input.startsWith(PNI_PREFIX)) { + throw new TypeError('missing pni prefix'); + } + return input.replace(PNI_PREFIX_REGEX, ''); +} diff --git a/packages/types/src/datetime/DurationDays.std.ts b/packages/types/src/datetime/DurationDays.std.ts new file mode 100644 index 0000000000..2c2fd8b68c --- /dev/null +++ b/packages/types/src/datetime/DurationDays.std.ts @@ -0,0 +1,56 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uint32 } from '../numbers/Uint32.std.ts'; +import type { DurationMs } from './DurationMs.std.ts'; +import type { DurationSecs } from './DurationSecs.std.ts'; +import { msToDaysInt, secsToDays } from '../_utils/datetime.std.ts'; + +/** + * Duration in days (bounded to `Uint32` range). + * @public + */ +export type DurationDays = Tagged; + +export namespace DurationDays { + /** @public */ + export const DAY = 1 as DurationDays; + /** @public */ + export const SEVEN_DAYS = (DAY * 7) as DurationDays; // "1 week" + /** @public */ + export const TWENTY_EIGHT_DAYS = (DAY * 28) as DurationDays; // "4 weeks" + /** @public */ + export const THIRTY_DAYS = (DAY * 30) as DurationDays; // "1 month" + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uint32.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is DurationDays { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUint32(input: Uint32): DurationDays { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): DurationDays { + return fromUint32(Uint32.fromNumber(input)); + } + + /** @public */ + export function fromDurationMs(input: DurationMs): DurationDays { + return fromNumber(msToDaysInt(input)); + } + + /** @public */ + export function fromDurationSecs(input: DurationSecs): DurationDays { + return fromNumber(secsToDays(input)); + } +} diff --git a/packages/types/src/datetime/DurationMs.std.ts b/packages/types/src/datetime/DurationMs.std.ts new file mode 100644 index 0000000000..2582c93ca3 --- /dev/null +++ b/packages/types/src/datetime/DurationMs.std.ts @@ -0,0 +1,81 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uint32 } from '../numbers/Uint32.std.ts'; +import type { DurationSecs } from './DurationSecs.std.ts'; +import type { DurationDays } from './DurationDays.std.ts'; +import { + daysToMs, + hoursToMs, + minsToMs, + secsToMs, +} from '../_utils/datetime.std.ts'; + +/** + * Duration in milliseconds (bounded to `Uint32` range). + * @public + */ +export type DurationMs = Tagged; + +export namespace DurationMs { + /** @public */ + export const SECOND = 1000 as DurationMs; + /** @public */ + export const MINUTE = (1000 * 60) as DurationMs; + /** @public */ + export const HOUR = (MINUTE * 60) as DurationMs; + /** @public */ + export const DAY = (HOUR * 24) as DurationMs; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uint32.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is DurationMs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUint32(input: Uint32): DurationMs { + return Schema.parse(input); + } + + /** @public */ + export function fromMilliseconds(input: number): DurationMs { + return fromUint32(Uint32.fromNumber(input)); + } + + /** @public */ + export function fromSeconds(input: number): DurationMs { + return fromMilliseconds(secsToMs(input)); + } + + /** @public */ + export function fromMinutes(input: number): DurationMs { + return fromMilliseconds(minsToMs(input)); + } + + /** @public */ + export function fromHours(input: number): DurationMs { + return fromMilliseconds(hoursToMs(input)); + } + + /** @public */ + export function fromDays(input: number): DurationMs { + return fromMilliseconds(daysToMs(input)); + } + + /** @public */ + export function fromDurationSecs(input: DurationSecs): DurationMs { + return fromMilliseconds(secsToMs(input)); + } + + /** @public */ + export function fromDurationDays(input: DurationDays): DurationMs { + return fromMilliseconds(daysToMs(input)); + } +} diff --git a/packages/types/src/datetime/DurationSecs.std.ts b/packages/types/src/datetime/DurationSecs.std.ts new file mode 100644 index 0000000000..fd1556b3ee --- /dev/null +++ b/packages/types/src/datetime/DurationSecs.std.ts @@ -0,0 +1,76 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uint32 } from '../numbers/Uint32.std.ts'; +import type { DurationMs } from './DurationMs.std.ts'; +import type { DurationDays } from './DurationDays.std.ts'; +import { + daysToSecs, + hoursToSecs, + minsToSecs, + msToSecsInt, +} from '../_utils/datetime.std.ts'; + +/** + * Duration in seconds (bounded to `Uint32` range). + * @public + */ +export type DurationSecs = Tagged; + +export namespace DurationSecs { + /** @public */ + export const SECOND = 1 as DurationSecs; + /** @public */ + export const MINUTE = (SECOND * 60) as DurationSecs; + /** @public */ + export const HOUR = (MINUTE * 60) as DurationSecs; + /** @public */ + export const DAY = (HOUR * 24) as DurationSecs; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uint32.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is DurationSecs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUint32(input: Uint32): DurationSecs { + return Schema.parse(input); + } + + /** @public */ + export function fromSeconds(input: number): DurationSecs { + return fromUint32(Uint32.fromNumber(input)); + } + + /** @public */ + export function fromMinutes(input: number): DurationSecs { + return fromSeconds(minsToSecs(input)); + } + + /** @public */ + export function fromHours(input: number): DurationSecs { + return fromSeconds(hoursToSecs(input)); + } + + /** @public */ + export function fromDays(input: number): DurationSecs { + return fromSeconds(daysToSecs(input)); + } + + /** @public */ + export function fromDurationMs(input: DurationMs): DurationSecs { + return fromSeconds(msToSecsInt(input)); + } + + /** @public */ + export function fromDurationDays(input: DurationDays): DurationSecs { + return fromSeconds(daysToSecs(input)); + } +} diff --git a/packages/types/src/datetime/PlainTimeHourMin.std.ts b/packages/types/src/datetime/PlainTimeHourMin.std.ts new file mode 100644 index 0000000000..92544dbf1d --- /dev/null +++ b/packages/types/src/datetime/PlainTimeHourMin.std.ts @@ -0,0 +1,51 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uint32 } from '../numbers/Uint32.std.ts'; + +const MIN = 0; +const MAX = 2359; +const MINS_MAX = 59; + +function getMins(input: number): number { + return input % 100; +} + +function isPlainTimeHourMin(input: number): input is PlainTimeHourMin { + return ( + Number.isInteger(input) && + input >= MIN && + input <= MAX && + getMins(input) <= MINS_MAX + ); +} + +/** + * 24-hour clock time as a 4-digit integer (0000–2359). + * @public + */ +export type PlainTimeHourMin = Tagged; + +export namespace PlainTimeHourMin { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uint32.Schema, + z.custom(isPlainTimeHourMin) + ); + + /** @public */ + export function isValid(input: number): input is PlainTimeHourMin { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUint32(input: Uint32): PlainTimeHourMin { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): PlainTimeHourMin { + return fromUint32(Uint32.fromNumber(input)); + } +} diff --git a/packages/types/src/datetime/TimestampMs.std.ts b/packages/types/src/datetime/TimestampMs.std.ts new file mode 100644 index 0000000000..88aa505781 --- /dev/null +++ b/packages/types/src/datetime/TimestampMs.std.ts @@ -0,0 +1,55 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { + MAX_SAFE_DATE, + MIN_SAFE_DATE, + secsToMs, +} from '../_utils/datetime.std.ts'; +import type { TimestampSecs } from './TimestampSecs.std.ts'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Unix timestamp in milliseconds. + * @public + */ +export type TimestampMs = Tagged; + +export namespace TimestampMs { + /** @public */ + export const MIN = MIN_SAFE_DATE as TimestampMs; + /** @public */ + export const MAX = MAX_SAFE_DATE as TimestampMs; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is TimestampMs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): TimestampMs { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): TimestampMs { + return fromNumber(bigIntToNumber(input)); + } + + /** @public */ + export function fromTimestampSecs(input: TimestampSecs): TimestampMs { + return fromNumber(secsToMs(input)); + } + + /** @public */ + export function now(): TimestampMs { + return fromNumber(Date.now()); + } +} diff --git a/packages/types/src/datetime/TimestampSecs.std.ts b/packages/types/src/datetime/TimestampSecs.std.ts new file mode 100644 index 0000000000..689edcaad3 --- /dev/null +++ b/packages/types/src/datetime/TimestampSecs.std.ts @@ -0,0 +1,49 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { + MAX_SAFE_DATE, + MIN_SAFE_DATE, + msToSecsInt, +} from '../_utils/datetime.std.ts'; +import type { TimestampMs } from './TimestampMs.std.ts'; + +/** + * Unix timestamp in seconds. + * @public + */ +export type TimestampSecs = Tagged; + +export namespace TimestampSecs { + /** @public */ + export const MIN = msToSecsInt(MIN_SAFE_DATE) as TimestampSecs; + /** @public */ + export const MAX = msToSecsInt(MAX_SAFE_DATE) as TimestampSecs; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is TimestampSecs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): TimestampSecs { + return Schema.parse(input); + } + + /** @public */ + export function fromTimestampMs(input: TimestampMs): TimestampSecs { + return fromNumber(msToSecsInt(input)); + } + + /** @public */ + export function now(): TimestampSecs { + return fromNumber(msToSecsInt(Date.now())); + } +} diff --git a/packages/types/src/encodings/Base64.std.ts b/packages/types/src/encodings/Base64.std.ts new file mode 100644 index 0000000000..fc1e26aaec --- /dev/null +++ b/packages/types/src/encodings/Base64.std.ts @@ -0,0 +1,51 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import type { Bytes } from './Bytes.std.ts'; +import { + Uint8ArrayFromBase64, + Uint8ArrayToBase64, +} from '../_utils/bytes.std.ts'; + +/** + * Base64-encoded string. + * @public + */ +export type Base64 = Tagged; + +export namespace Base64 { + /** @public */ + export type Of = Tagged; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.base64()), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is Base64 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Base64 { + return Schema.parse(input); + } + + /** @public */ + export function fromBytes(input: Bytes.Of): Of; + export function fromBytes(input: Bytes): Base64; + export function fromBytes(input: Bytes): Base64 { + return Uint8ArrayToBase64(input) as Base64; + } + + /** @public */ + export function toBytes(input: Of): Bytes.Of; + export function toBytes(input: Base64): Bytes; + export function toBytes(input: Base64): Bytes { + return Uint8ArrayFromBase64(input); + } +} diff --git a/packages/types/src/encodings/Base64Url.std.ts b/packages/types/src/encodings/Base64Url.std.ts new file mode 100644 index 0000000000..2b03ec5267 --- /dev/null +++ b/packages/types/src/encodings/Base64Url.std.ts @@ -0,0 +1,51 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import type { Bytes } from './Bytes.std.ts'; +import { + Uint8ArrayFromBase64Url, + Uint8ArrayToBase64Url, +} from '../_utils/bytes.std.ts'; + +/** + * Base64URL-encoded string (URL-safe variant using `-` and `_` instead of `+` and `/`). + * @public + */ +export type Base64Url = Tagged; + +export namespace Base64Url { + /** @public */ + export type Of = Tagged; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.base64url()), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is Base64Url { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Base64Url { + return Schema.parse(input); + } + + /** @public */ + export function fromBytes(input: Bytes.Of): Of; + export function fromBytes(input: Bytes): Base64Url; + export function fromBytes(input: Bytes): Base64Url { + return Uint8ArrayToBase64Url(input) as Base64Url; + } + + /** @public */ + export function toBytes(input: Of): Bytes.Of; + export function toBytes(input: Base64Url): Bytes; + export function toBytes(input: Base64Url): Bytes { + return Uint8ArrayFromBase64Url(input); + } +} diff --git a/packages/types/src/encodings/Bytes.std.ts b/packages/types/src/encodings/Bytes.std.ts new file mode 100644 index 0000000000..fe3ebb7c2a --- /dev/null +++ b/packages/types/src/encodings/Bytes.std.ts @@ -0,0 +1,35 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +type MaybeSharedBytes = Uint8Array; + +/** + * A `Uint8Array` backed by a standard (non-shared) `ArrayBuffer`. + * @public + */ +export type Bytes = Uint8Array; + +export namespace Bytes { + /** @public */ + export type Of = Tagged; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.instanceof(Uint8Array), + z.custom(input => { + return input.buffer instanceof ArrayBuffer; + }) + ); + + /** @public */ + export function isValid(input: MaybeSharedBytes): input is Bytes { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromMaybeSharedBytes(input: MaybeSharedBytes): Bytes { + return Schema.parse(input); + } +} diff --git a/packages/types/src/encodings/Hex.std.ts b/packages/types/src/encodings/Hex.std.ts new file mode 100644 index 0000000000..1007cd3cf5 --- /dev/null +++ b/packages/types/src/encodings/Hex.std.ts @@ -0,0 +1,48 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import type { Bytes } from './Bytes.std.ts'; +import { Uint8ArrayFromHex, Uint8ArrayToHex } from '../_utils/bytes.std.ts'; + +/** + * Hexadecimal-encoded string. + * @public + */ +export type Hex = Tagged; + +export namespace Hex { + /** @public */ + export type Of = Tagged; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.hex()), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is Hex { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Hex { + return Schema.parse(input); + } + + /** @public */ + export function fromBytes(input: Bytes.Of): Of; + export function fromBytes(input: Bytes): Hex; + export function fromBytes(input: Bytes): Hex { + return Uint8ArrayToHex(input) as Hex; + } + + /** @public */ + export function toBytes(input: Of): Bytes.Of; + export function toBytes(input: Hex): Bytes; + export function toBytes(input: Hex): Bytes { + return Uint8ArrayFromHex(input); + } +} diff --git a/packages/types/src/encodings/Json.std.ts b/packages/types/src/encodings/Json.std.ts new file mode 100644 index 0000000000..e514fab6fa --- /dev/null +++ b/packages/types/src/encodings/Json.std.ts @@ -0,0 +1,150 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * A string containing valid JSON + * @public + */ +export type Json = Tagged; + +/** + * A JSON primitive value: `null`, `boolean`, `number`, or `string`. + * @public + */ +export type JsonPrimitive = null | boolean | number | string; + +/** + * A JSON array. + * @public + */ +export type JsonArray = Array | ReadonlyArray; + +/** + * A JSON object with string keys. + * @public + */ +export type JsonObject = { [Key in string]: JsonValue }; + +/** + * Any valid JSON value. + * @public + */ +export type JsonValue = JsonPrimitive | JsonArray | JsonObject; + +export namespace Json { + /** @public */ + export type Of = Tagged; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check( + z.superRefine((input, ctx) => { + try { + JSON.parse(input); + } catch (error) { + ctx.issues.push({ + code: 'custom', + message: error instanceof Error ? error.message : 'invalid json', + input, + }); + } + }) + ), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is Json { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Json { + return Schema.parse(input); + } + + /** @public */ + export function stringify(input: T): Of; + export function stringify(input: JsonValue): Json; + export function stringify(input: JsonValue): Json { + return JSON.stringify(input) as Json; + } + + /** @public */ + export function parse(input: Of): T; + export function parse(input: Json): JsonValue; + export function parse(input: Json): JsonValue { + return JSON.parse(input); + } +} + +export namespace JsonPrimitive { + /** @public */ + export const Schema: z.ZodMiniType = z.union([ + z.null(), + z.boolean(), + z.number(), + z.string(), + ]); + + /** @public */ + export function isValid(input: unknown): input is JsonPrimitive { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUnknown(input: unknown): JsonPrimitive { + return Schema.parse(input); + } +} + +export namespace JsonArray { + /** @public */ + export const Schema: z.ZodMiniType = z.array(z.json()); + + /** @public */ + export function isValid(input: unknown): input is JsonArray { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUnknown(input: unknown): JsonArray { + return Schema.parse(input); + } +} + +export namespace JsonObject { + /** @public */ + export const Schema: z.ZodMiniType = z.record( + z.string(), + z.json() + ); + + /** @public */ + export function isValid(input: unknown): input is JsonObject { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUnknown(input: unknown): JsonObject { + return Schema.parse(input); + } +} + +export namespace JsonValue { + /** @public */ + export const Schema: z.ZodMiniType = z.json(); + + /** @public */ + export function isValid(input: unknown): input is JsonValue { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUnknown(input: unknown): JsonValue { + return Schema.parse(input); + } +} diff --git a/packages/types/src/encodings/Utf8.std.ts b/packages/types/src/encodings/Utf8.std.ts new file mode 100644 index 0000000000..177c948093 --- /dev/null +++ b/packages/types/src/encodings/Utf8.std.ts @@ -0,0 +1,20 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import type { Bytes } from './Bytes.std.ts'; +import { TextDecoderDecode, TextEncoderEncode } from '../_utils/bytes.std.ts'; + +export namespace Utf8 { + /** @public */ + export type Of = Tagged; + + /** @public */ + export function fromBytes(input: Bytes.Of): Of { + return TextDecoderDecode(input) as Of; + } + + /** @public */ + export function toBytes(input: Of): Bytes.Of { + return TextEncoderEncode(input) as Bytes.Of; + } +} diff --git a/packages/types/src/formats/HexColor.std.ts b/packages/types/src/formats/HexColor.std.ts new file mode 100644 index 0000000000..1252f96dc1 --- /dev/null +++ b/packages/types/src/formats/HexColor.std.ts @@ -0,0 +1,28 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * CSS hex color string starting with `#` (e.g., `"#FFFFFF"`). + * @public + */ +export type HexColor = Tagged<`#${string}`, 'HexColor'>; + +export namespace HexColor { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.startsWith('#')), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is HexColor { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): HexColor { + return Schema.parse(input); + } +} diff --git a/packages/types/src/formats/HttpUrl.std.ts b/packages/types/src/formats/HttpUrl.std.ts new file mode 100644 index 0000000000..8c845b14b2 --- /dev/null +++ b/packages/types/src/formats/HttpUrl.std.ts @@ -0,0 +1,28 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * HTTP or HTTPS URL. + * @public + */ +export type HttpUrl = Tagged; + +export namespace HttpUrl { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.httpUrl()), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is HttpUrl { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): HttpUrl { + return Schema.parse(input); + } +} diff --git a/packages/types/src/formats/LanguageTag.std.ts b/packages/types/src/formats/LanguageTag.std.ts new file mode 100644 index 0000000000..b4a0bdafef --- /dev/null +++ b/packages/types/src/formats/LanguageTag.std.ts @@ -0,0 +1,37 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +function isLanguageTag(input: string): input is LanguageTag { + try { + new Intl.Locale(input); + return true; + } catch { + return false; + } +} + +/** + * BCP 47 language tag (Ex: `"en-US"`). + * @public + */ +export type LanguageTag = Tagged; + +export namespace LanguageTag { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string(), + z.custom(isLanguageTag, 'must be valid language tag') + ); + + /** @public */ + export function isValid(input: string): input is LanguageTag { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): LanguageTag { + return Schema.parse(input); + } +} diff --git a/packages/types/src/formats/MimeType.std.ts b/packages/types/src/formats/MimeType.std.ts new file mode 100644 index 0000000000..c71a6f8cd0 --- /dev/null +++ b/packages/types/src/formats/MimeType.std.ts @@ -0,0 +1,28 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * MIME type string (Ex: `"application/json"`). + * @public + */ +export type MimeType = Tagged<`${string}/${string}`, 'MimeType'>; + +export namespace MimeType { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.includes('/')), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is MimeType { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): MimeType { + return Schema.parse(input); + } +} diff --git a/packages/types/src/formats/Semver.std.ts b/packages/types/src/formats/Semver.std.ts new file mode 100644 index 0000000000..e1ce487b1f --- /dev/null +++ b/packages/types/src/formats/Semver.std.ts @@ -0,0 +1,33 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import semverValid from 'semver/functions/valid'; + +function isSemver(input: string): input is Semver { + return semverValid(input) != null; +} + +/** + * Semantic version string (Ex: `"1.2.3"`). + * @public + */ +export type Semver = Tagged; + +export namespace Semver { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string(), + z.custom(isSemver) + ); + + /** @public */ + export function isValid(input: string): input is Semver { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Semver { + return Schema.parse(input); + } +} diff --git a/packages/types/src/formats/Uuid.std.ts b/packages/types/src/formats/Uuid.std.ts new file mode 100644 index 0000000000..bb4491a2f2 --- /dev/null +++ b/packages/types/src/formats/Uuid.std.ts @@ -0,0 +1,43 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import * as uuid from 'uuid'; + +function isUuid(input: string): input is Uuid { + return uuid.validate(input); +} + +/** + * UUID string (Ex: `"00000000-0000-4000-0000-000000000000"`) + * @public + */ +export type Uuid = Tagged; + +export namespace Uuid { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string(), + z.custom(isUuid) + ); + + /** @public */ + export function isValid(input: string): input is Uuid { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Uuid { + return Schema.parse(input); + } + + /** @public */ + export function createV4(): Uuid { + return uuid.v4() as Uuid; + } + + /** @public */ + export function createV7(): Uuid { + return uuid.v7() as Uuid; + } +} diff --git a/packages/types/src/index.std.ts b/packages/types/src/index.std.ts new file mode 100644 index 0000000000..fe25213d00 --- /dev/null +++ b/packages/types/src/index.std.ts @@ -0,0 +1,65 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +export { Result } from './Result.std.ts'; + +export { Uint8 } from './numbers/Uint8.std.ts'; +export { Uint16 } from './numbers/Uint16.std.ts'; +export { Uint32 } from './numbers/Uint32.std.ts'; +export { BigUint64 } from './numbers/BigUint64.std.ts'; +export { BigUint128 } from './numbers/BigUint128.std.ts'; +export { Int8 } from './numbers/Int8.std.ts'; +export { Int16 } from './numbers/Int16.std.ts'; +export { Int32 } from './numbers/Int32.std.ts'; +export { BigInt64 } from './numbers/BigInt64.std.ts'; +export { BigInt128 } from './numbers/BigInt128.std.ts'; +export { Float16 } from './numbers/Float16.std.ts'; +export { Float32 } from './numbers/Float32.std.ts'; +export { Float64 } from './numbers/Float64.std.ts'; + +export { UnitBytes } from './units/UnitBytes.std.ts'; +export { UnitKilobytes } from './units/UnitKilobytes.std.ts'; +export { UnitKibibytes } from './units/UnitKibibytes.std.ts'; + +export { TimestampMs } from './datetime/TimestampMs.std.ts'; +export { TimestampSecs } from './datetime/TimestampSecs.std.ts'; +export { DurationMs } from './datetime/DurationMs.std.ts'; +export { DurationSecs } from './datetime/DurationSecs.std.ts'; +export { DurationDays } from './datetime/DurationDays.std.ts'; +export { PlainTimeHourMin } from './datetime/PlainTimeHourMin.std.ts'; + +export { Bytes } from './encodings/Bytes.std.ts'; +export { Base64 } from './encodings/Base64.std.ts'; +export { Base64Url } from './encodings/Base64Url.std.ts'; +export { Hex } from './encodings/Hex.std.ts'; +export { + Json, + JsonPrimitive, + JsonArray, + JsonObject, + JsonValue, +} from './encodings/Json.std.ts'; + +export { HexColor } from './formats/HexColor.std.ts'; +export { HttpUrl } from './formats/HttpUrl.std.ts'; +export { LanguageTag } from './formats/LanguageTag.std.ts'; +export { MimeType } from './formats/MimeType.std.ts'; +export { Semver } from './formats/Semver.std.ts'; +export { Uuid } from './formats/Uuid.std.ts'; + +export { E164 } from './service/E164.std.ts'; +export { Aci } from './service/Aci.std.ts'; +export { Pni } from './service/Pni.std.ts'; +export { UntaggedPni } from './service/UntaggedPni.std.ts'; +export { ServiceId } from './service/ServiceId.std.ts'; +export { DeviceId } from './service/DeviceId.std.ts'; +export { Address } from './service/Address.std.ts'; +export { AddressInfo } from './service/AddressInfo.std.ts'; +export { DistributionId } from './service/DistributionId.std.ts'; +export { GroupId } from './service/GroupId.std.ts'; +export { DistributionListId } from './service/DistributionListId.std.ts'; +export { SentTimestampMs } from './service/SentTimestampMs.std.ts'; +export { ServerTimestampMs } from './service/ServerTimestampMs.std.ts'; +export { ReceivedTimestampMs } from './service/ReceivedTimestampMs.std.ts'; +export { StorageManifestVersion } from './service/StorageManifestVersion.std.ts'; +export { StorageItemKey } from './service/StorageItemKey.std.ts'; diff --git a/packages/types/src/numbers/BigInt128.std.ts b/packages/types/src/numbers/BigInt128.std.ts new file mode 100644 index 0000000000..8527b82291 --- /dev/null +++ b/packages/types/src/numbers/BigInt128.std.ts @@ -0,0 +1,38 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * Signed 128-bit integer + * @public + */ +export type BigInt128 = Tagged; + +export namespace BigInt128 { + /** @public */ + export const MIN = -(2n ** 127n) as BigInt128; + /** @public */ + export const MAX = (2n ** 127n - 1n) as BigInt128; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.bigint().check(z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: bigint): input is BigInt128 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBigInt(input: bigint): BigInt128 { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): BigInt128 { + return fromBigInt(BigInt(input)); + } +} diff --git a/packages/types/src/numbers/BigInt64.std.ts b/packages/types/src/numbers/BigInt64.std.ts new file mode 100644 index 0000000000..14e8406bf3 --- /dev/null +++ b/packages/types/src/numbers/BigInt64.std.ts @@ -0,0 +1,38 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * Signed 64-bit integer + * @public + */ +export type BigInt64 = Tagged; + +export namespace BigInt64 { + /** @public */ + export const MIN = -(2n ** 63n) as BigInt64; + /** @public */ + export const MAX = (2n ** 63n - 1n) as BigInt64; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.bigint().check(z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: bigint): input is BigInt64 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBigInt(input: bigint): BigInt64 { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): BigInt64 { + return fromBigInt(BigInt(input)); + } +} diff --git a/packages/types/src/numbers/BigUint128.std.ts b/packages/types/src/numbers/BigUint128.std.ts new file mode 100644 index 0000000000..98aab6c9b7 --- /dev/null +++ b/packages/types/src/numbers/BigUint128.std.ts @@ -0,0 +1,38 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * Unsigned 128-bit integer + * @public + */ +export type BigUint128 = Tagged; + +export namespace BigUint128 { + /** @public */ + export const MIN = 0n as BigUint128; + /** @public */ + export const MAX = (2n ** 128n - 1n) as BigUint128; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.bigint().check(z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: bigint): input is BigUint128 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBigInt(input: bigint): BigUint128 { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): BigUint128 { + return fromBigInt(BigInt(input)); + } +} diff --git a/packages/types/src/numbers/BigUint64.std.ts b/packages/types/src/numbers/BigUint64.std.ts new file mode 100644 index 0000000000..ba96d5b66b --- /dev/null +++ b/packages/types/src/numbers/BigUint64.std.ts @@ -0,0 +1,38 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * Unsigned 64-bit integer + * @public + */ +export type BigUint64 = Tagged; + +export namespace BigUint64 { + /** @public */ + export const MIN = 0n as BigUint64; + /** @public */ + export const MAX = (2n ** 64n - 1n) as BigUint64; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.bigint().check(z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: bigint): input is BigUint64 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBigInt(input: bigint): BigUint64 { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): BigUint64 { + return fromBigInt(BigInt(input)); + } +} diff --git a/packages/types/src/numbers/Float16.std.ts b/packages/types/src/numbers/Float16.std.ts new file mode 100644 index 0000000000..6a63392438 --- /dev/null +++ b/packages/types/src/numbers/Float16.std.ts @@ -0,0 +1,32 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * 16-bit IEEE 754 floating-point number. + * @public + */ +export type Float16 = Tagged; + +export namespace Float16 { + function isFloat16(input: number): boolean { + return Math.f16round(input) === input; + } + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number(), + z.custom(input => isFloat16(input), 'must be valid float16') + ); + + /** @public */ + export function isValid(input: number): input is Float16 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Float16 { + return Schema.parse(input); + } +} diff --git a/packages/types/src/numbers/Float32.std.ts b/packages/types/src/numbers/Float32.std.ts new file mode 100644 index 0000000000..fc13a4e388 --- /dev/null +++ b/packages/types/src/numbers/Float32.std.ts @@ -0,0 +1,32 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * 32-bit IEEE 754 floating-point number. + * @public + */ +export type Float32 = Tagged; + +export namespace Float32 { + function isFloat32(input: number): boolean { + return Math.fround(input) === input; + } + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number(), + z.custom(input => isFloat32(input), 'must be valid float32') + ); + + /** @public */ + export function isValid(input: number): input is Float32 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Float32 { + return Schema.parse(input); + } +} diff --git a/packages/types/src/numbers/Float64.std.ts b/packages/types/src/numbers/Float64.std.ts new file mode 100644 index 0000000000..fb871e906e --- /dev/null +++ b/packages/types/src/numbers/Float64.std.ts @@ -0,0 +1,28 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; + +/** + * 64-bit IEEE 754 floating-point number (standard JavaScript `number`). + * @public + */ +export type Float64 = Tagged; + +export namespace Float64 { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number(), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Float64 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Float64 { + return Schema.parse(input); + } +} diff --git a/packages/types/src/numbers/Int16.std.ts b/packages/types/src/numbers/Int16.std.ts new file mode 100644 index 0000000000..13b584b2b6 --- /dev/null +++ b/packages/types/src/numbers/Int16.std.ts @@ -0,0 +1,39 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Signed 16-bit integer + * @public + */ +export type Int16 = Tagged; + +export namespace Int16 { + /** @public */ + export const MIN = -(2 ** 15) as Int16; + /** @public */ + export const MAX = (2 ** 15 - 1) as Int16; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Int16 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Int16 { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): Int16 { + return fromNumber(bigIntToNumber(input)); + } +} diff --git a/packages/types/src/numbers/Int32.std.ts b/packages/types/src/numbers/Int32.std.ts new file mode 100644 index 0000000000..2c55f13634 --- /dev/null +++ b/packages/types/src/numbers/Int32.std.ts @@ -0,0 +1,39 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Signed 32-bit integer + * @public + */ +export type Int32 = Tagged; + +export namespace Int32 { + /** @public */ + export const MIN = -(2 ** 31) as Int32; + /** @public */ + export const MAX = (2 ** 31 - 1) as Int32; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Int32 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Int32 { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): Int32 { + return fromNumber(bigIntToNumber(input)); + } +} diff --git a/packages/types/src/numbers/Int8.std.ts b/packages/types/src/numbers/Int8.std.ts new file mode 100644 index 0000000000..9c505f73ba --- /dev/null +++ b/packages/types/src/numbers/Int8.std.ts @@ -0,0 +1,39 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Signed 8-bit integer + * @public + */ +export type Int8 = Tagged; + +export namespace Int8 { + /** @public */ + export const MIN = -(2 ** 7) as Int8; + /** @public */ + export const MAX = (2 ** 7 - 1) as Int8; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Int8 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Int8 { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): Int8 { + return fromNumber(bigIntToNumber(input)); + } +} diff --git a/packages/types/src/numbers/Uint16.std.ts b/packages/types/src/numbers/Uint16.std.ts new file mode 100644 index 0000000000..ca2491b095 --- /dev/null +++ b/packages/types/src/numbers/Uint16.std.ts @@ -0,0 +1,39 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Unsigned 16-bit integer + * @public + */ +export type Uint16 = Tagged; + +export namespace Uint16 { + /** @public */ + export const MIN = 0 as Uint16; + /** @public */ + export const MAX = (2 ** 16 - 1) as Uint16; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Uint16 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Uint16 { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): Uint16 { + return fromNumber(bigIntToNumber(input)); + } +} diff --git a/packages/types/src/numbers/Uint32.std.ts b/packages/types/src/numbers/Uint32.std.ts new file mode 100644 index 0000000000..58d9da0404 --- /dev/null +++ b/packages/types/src/numbers/Uint32.std.ts @@ -0,0 +1,39 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Unsigned 32-bit integer + * @public + */ +export type Uint32 = Tagged; + +export namespace Uint32 { + /** @public */ + export const MIN = 0 as Uint32; + /** @public */ + export const MAX = (2 ** 32 - 1) as Uint32; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Uint32 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Uint32 { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): Uint32 { + return fromNumber(bigIntToNumber(input)); + } +} diff --git a/packages/types/src/numbers/Uint8.std.ts b/packages/types/src/numbers/Uint8.std.ts new file mode 100644 index 0000000000..d90a812eb9 --- /dev/null +++ b/packages/types/src/numbers/Uint8.std.ts @@ -0,0 +1,39 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { bigIntToNumber } from '../_utils/numbers.std.ts'; + +/** + * Unsigned 8-bit integer + * @public + */ +export type Uint8 = Tagged; + +export namespace Uint8 { + /** @public */ + export const MIN = 0 as Uint8; + /** @public */ + export const MAX = (2 ** 8 - 1) as Uint8; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.number().check(z.int(), z.minimum(MIN), z.maximum(MAX)), + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is Uint8 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromNumber(input: number): Uint8 { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): Uint8 { + return fromNumber(bigIntToNumber(input)); + } +} diff --git a/packages/types/src/service/Aci.std.ts b/packages/types/src/service/Aci.std.ts new file mode 100644 index 0000000000..f098b42c8d --- /dev/null +++ b/packages/types/src/service/Aci.std.ts @@ -0,0 +1,53 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uuid } from '../formats/Uuid.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * Account Identifier (ACI) — uniquely identifies a Signal account. + * @public + */ +export type Aci = Aci.Decoded; + +export namespace Aci { + type Opaque = Tagged; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uuid.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: Uuid): input is Aci { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUuid(input: Uuid): Aci { + return Schema.parse(input); + } + + /** @public */ + export function fromString(input: string): Aci { + return fromUuid(Uuid.fromString(input)); + } + + /** @public */ + export function decode(input: Encoded): Aci { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: Aci): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/Address.std.ts b/packages/types/src/service/Address.std.ts new file mode 100644 index 0000000000..2da35859a2 --- /dev/null +++ b/packages/types/src/service/Address.std.ts @@ -0,0 +1,67 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import type { ServiceId } from './ServiceId.std.ts'; +import type { DeviceId } from './DeviceId.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; +import { parseAddress } from '../_utils/address.std.ts'; +import type { AddressInfo } from './AddressInfo.std.ts'; + +/** + * Address serialized as `"${serviceId}.${deviceId}"`. + * @public + */ +export type Address = Address.Decoded; + +export namespace Address { + type Opaque = Tagged<`${ServiceId}.${DeviceId}`, 'Address'>; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check( + z.superRefine((input, ctx) => { + const result = parseAddress(input); + if (!result.ok) { + ctx.issues.push({ + code: 'custom', + message: result.error, + input, + }); + } + }) + ), + z.custom
() + ); + + /** @public */ + export function isValid(input: string): input is Address { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Address { + return Schema.parse(input); + } + + /** @public */ + export function fromAddressInfo(input: AddressInfo): Address { + return `${input.serviceId}.${input.deviceId}` as Address; + } + + /** @public */ + export function decode(input: Encoded): Address { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: Address): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/AddressInfo.std.ts b/packages/types/src/service/AddressInfo.std.ts new file mode 100644 index 0000000000..27d3e101f0 --- /dev/null +++ b/packages/types/src/service/AddressInfo.std.ts @@ -0,0 +1,49 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { parseAddress } from '../_utils/address.std.ts'; +import { ServiceId } from './ServiceId.std.ts'; +import { DeviceId } from './DeviceId.std.ts'; +import type { Address } from './Address.std.ts'; + +/** + * A service address identifying a specific device by service ID and device ID. + * @public + */ +export type AddressInfo = Tagged; + +export namespace AddressInfo { + /** @public */ + export type Params = Readonly<{ + serviceId: ServiceId; + deviceId: DeviceId; + }>; + + const Schema: z.ZodMiniType = z.pipe( + z.strictObject({ + serviceId: ServiceId.Schema as z.ZodMiniType, + deviceId: DeviceId.Schema as z.ZodMiniType, + }), + z.custom() + ); + + /** @public */ + export function isValid(input: Params): input is AddressInfo { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromParams(input: Params): AddressInfo { + return Schema.parse(input); + } + + /** @public */ + export function fromAddress(input: Address): AddressInfo { + const result = parseAddress(input); + if (!result.ok) { + throw new TypeError(result.error); + } + return fromParams(result.value); + } +} diff --git a/packages/types/src/service/DeviceId.std.ts b/packages/types/src/service/DeviceId.std.ts new file mode 100644 index 0000000000..b52e1c30da --- /dev/null +++ b/packages/types/src/service/DeviceId.std.ts @@ -0,0 +1,34 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uint32 } from '../numbers/Uint32.std.ts'; + +/** + * Identifies a specific registered device within an account. + * @public + */ +export type DeviceId = Tagged; + +export namespace DeviceId { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uint32.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is DeviceId { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUint32(input: Uint32): DeviceId { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): DeviceId { + return fromUint32(Uint32.fromNumber(input)); + } +} diff --git a/packages/types/src/service/DistributionId.std.ts b/packages/types/src/service/DistributionId.std.ts new file mode 100644 index 0000000000..6f4e2751f7 --- /dev/null +++ b/packages/types/src/service/DistributionId.std.ts @@ -0,0 +1,53 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uuid } from '../formats/Uuid.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; + +/** + * Sender Key Distribution identifier. + * @public + */ +export type DistributionId = DistributionId.Decoded; + +export namespace DistributionId { + type Opaque = Tagged; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uuid.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is DistributionId { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUuid(input: Uuid): DistributionId { + return Schema.parse(input); + } + + /** @public */ + export function fromString(input: string): DistributionId { + return fromUuid(Uuid.fromString(input)); + } + + /** @public */ + export function decode(input: Encoded): DistributionId { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: DistributionId): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/DistributionListId.std.ts b/packages/types/src/service/DistributionListId.std.ts new file mode 100644 index 0000000000..8aa3740dda --- /dev/null +++ b/packages/types/src/service/DistributionListId.std.ts @@ -0,0 +1,53 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uuid } from '../formats/Uuid.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * Story distribution list identifier. + * @public + */ +export type DistributionListId = DistributionListId.Decoded; + +export namespace DistributionListId { + type Opaque = Tagged; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uuid.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is DistributionListId { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUuid(input: Uuid): DistributionListId { + return Schema.parse(input); + } + + /** @public */ + export function fromString(input: string): DistributionListId { + return fromUuid(Uuid.fromString(input)); + } + + /** @public */ + export function decode(input: Encoded): DistributionListId { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: DistributionListId): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/E164.std.ts b/packages/types/src/service/E164.std.ts new file mode 100644 index 0000000000..f606dbe419 --- /dev/null +++ b/packages/types/src/service/E164.std.ts @@ -0,0 +1,47 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Utf8 } from '../encodings/Utf8.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * International phone number in E.164 format (e.g., `"+12125550123"`). + * @public + */ +export type E164 = E164.Decoded; + +export namespace E164 { + type Opaque = Tagged<`+${string}`, 'E164'>; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string().check(z.e164()), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is E164 { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): E164 { + return Schema.parse(input); + } + + /** @public */ + export function decode(input: Encoded): E164 { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: E164): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/GroupId.std.ts b/packages/types/src/service/GroupId.std.ts new file mode 100644 index 0000000000..c2c1fe6030 --- /dev/null +++ b/packages/types/src/service/GroupId.std.ts @@ -0,0 +1,52 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Base64 } from '../encodings/Base64.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * Group identifier (Base64-encoded). + * @public + */ +export type GroupId = GroupId.Decoded; + +export namespace GroupId { + type Opaque = Tagged; + + /** @public */ + export type Decoded = Base64.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Base64.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is GroupId { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBase64(input: Base64): GroupId { + return Schema.parse(input); + } + + /** @public */ + export function fromString(input: string): GroupId { + return fromBase64(Base64.fromString(input)); + } + + /** @public */ + export function decode(input: Encoded): GroupId { + return Base64.fromBytes(input); + } + + /** @public */ + export function encode(input: GroupId): Encoded { + return Base64.toBytes(input); + } +} diff --git a/packages/types/src/service/Pni.std.ts b/packages/types/src/service/Pni.std.ts new file mode 100644 index 0000000000..dc53d74484 --- /dev/null +++ b/packages/types/src/service/Pni.std.ts @@ -0,0 +1,64 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uuid } from '../formats/Uuid.std.ts'; +import { PNI_PREFIX, stripPniPrefix } from '../_utils/pni.std.ts'; +import type { UntaggedPni } from './UntaggedPni.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +function isPni(input: string): input is Pni { + return input.startsWith(PNI_PREFIX) && Uuid.isValid(stripPniPrefix(input)); +} + +/** + * Phone Number Identity (PNI), formatted as `PNI:${uuid}`. + * @public + */ +export type Pni = Pni.Decoded; + +export namespace Pni { + type Opaque = Tagged<`PNI:${Uuid}`, 'Pni'>; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.string(), + z.custom(input => isPni(input)) + ); + + /** @public */ + export function isValid(input: string): input is Pni { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): Pni { + return Schema.parse(input); + } + + /** @public */ + export function fromUuid(input: Uuid): Pni { + return fromString(`${PNI_PREFIX}${input}`); + } + + /** @public */ + export function fromUntaggedPni(input: UntaggedPni): Pni { + return fromUuid(input); + } + + /** @public */ + export function decode(input: Encoded): Pni { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: Pni): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/ReceivedTimestampMs.std.ts b/packages/types/src/service/ReceivedTimestampMs.std.ts new file mode 100644 index 0000000000..608c8e1c00 --- /dev/null +++ b/packages/types/src/service/ReceivedTimestampMs.std.ts @@ -0,0 +1,44 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { TimestampMs } from '../datetime/TimestampMs.std.ts'; + +/** + * When a message was received on this device. + * @public + */ +export type ReceivedTimestampMs = Tagged; + +export namespace ReceivedTimestampMs { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + TimestampMs.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is ReceivedTimestampMs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromTimestampMs(input: TimestampMs): ReceivedTimestampMs { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): ReceivedTimestampMs { + return fromTimestampMs(TimestampMs.fromNumber(input)); + } + + /** @public */ + export function fromBigInt(input: bigint): ReceivedTimestampMs { + return fromTimestampMs(TimestampMs.fromBigInt(input)); + } + + /** @public */ + export function now(): ReceivedTimestampMs { + return fromTimestampMs(TimestampMs.now()); + } +} diff --git a/packages/types/src/service/SentTimestampMs.std.ts b/packages/types/src/service/SentTimestampMs.std.ts new file mode 100644 index 0000000000..9c3bc48495 --- /dev/null +++ b/packages/types/src/service/SentTimestampMs.std.ts @@ -0,0 +1,44 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { TimestampMs } from '../datetime/TimestampMs.std.ts'; + +/** + * When a message was sent by the sender. + * @public + */ +export type SentTimestampMs = Tagged; + +export namespace SentTimestampMs { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + TimestampMs.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is SentTimestampMs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromTimestampMs(input: TimestampMs): SentTimestampMs { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): SentTimestampMs { + return fromTimestampMs(TimestampMs.fromNumber(input)); + } + + /** @public */ + export function fromBigInt(input: bigint): SentTimestampMs { + return fromTimestampMs(TimestampMs.fromBigInt(input)); + } + + /** @public */ + export function now(): SentTimestampMs { + return fromTimestampMs(TimestampMs.now()); + } +} diff --git a/packages/types/src/service/ServerTimestampMs.std.ts b/packages/types/src/service/ServerTimestampMs.std.ts new file mode 100644 index 0000000000..b047472ec1 --- /dev/null +++ b/packages/types/src/service/ServerTimestampMs.std.ts @@ -0,0 +1,44 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { TimestampMs } from '../datetime/TimestampMs.std.ts'; + +/** + * Timestamp assigned by the Signal server when a message is processed. + * @public + */ +export type ServerTimestampMs = Tagged; + +export namespace ServerTimestampMs { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + TimestampMs.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is ServerTimestampMs { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromTimestampMs(input: TimestampMs): ServerTimestampMs { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): ServerTimestampMs { + return fromTimestampMs(TimestampMs.fromNumber(input)); + } + + /** @public */ + export function fromBigInt(input: bigint): ServerTimestampMs { + return fromTimestampMs(TimestampMs.fromBigInt(input)); + } + + /** @public */ + export function now(): ServerTimestampMs { + return fromTimestampMs(TimestampMs.now()); + } +} diff --git a/packages/types/src/service/ServiceId.std.ts b/packages/types/src/service/ServiceId.std.ts new file mode 100644 index 0000000000..86e1518405 --- /dev/null +++ b/packages/types/src/service/ServiceId.std.ts @@ -0,0 +1,46 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import * as z from 'zod/mini'; +import { Aci } from './Aci.std.ts'; +import { Pni } from './Pni.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; + +/** + * A service identifier — either an {@link Aci} or {@link Pni}. + * @public + */ +export type ServiceId = ServiceId.Decoded; + +export namespace ServiceId { + /** @public */ + export type Decoded = Aci | Pni; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + z.union([Aci.Schema, Pni.Schema]), + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is ServiceId { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromString(input: string): ServiceId { + return Schema.parse(input); + } + + /** @public */ + export function decode(input: Encoded): ServiceId { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: ServiceId): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/service/StorageItemKey.std.ts b/packages/types/src/service/StorageItemKey.std.ts new file mode 100644 index 0000000000..c571f274b1 --- /dev/null +++ b/packages/types/src/service/StorageItemKey.std.ts @@ -0,0 +1,47 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Base64 } from '../encodings/Base64.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * Key identifying an item in Signal's encrypted storage service. + * @public + */ +export type StorageItemKey = StorageItemKey.Decoded; + +export namespace StorageItemKey { + type Opaque = Tagged; + + /** @public */ + export type Decoded = Base64.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Base64.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is StorageItemKey { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBase64(input: Base64): StorageItemKey { + return Schema.parse(input); + } + + /** @public */ + export function decode(input: Encoded): StorageItemKey { + return Base64.fromBytes(input); + } + + /** @public */ + export function encode(input: StorageItemKey): Encoded { + return Base64.toBytes(input); + } +} diff --git a/packages/types/src/service/StorageManifestVersion.std.ts b/packages/types/src/service/StorageManifestVersion.std.ts new file mode 100644 index 0000000000..8c551d6a56 --- /dev/null +++ b/packages/types/src/service/StorageManifestVersion.std.ts @@ -0,0 +1,37 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { BigUint64 } from '../numbers/BigUint64.std.ts'; + +/** + * Version number of a Signal storage manifest. + * @public + */ +export type StorageManifestVersion = Tagged< + BigUint64, + 'StorageManifestVersion' +>; + +export namespace StorageManifestVersion { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + BigUint64.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: bigint): input is StorageManifestVersion { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromBigUint64(input: BigUint64): StorageManifestVersion { + return Schema.parse(input); + } + + /** @public */ + export function fromBigInt(input: bigint): StorageManifestVersion { + return fromBigUint64(BigUint64.fromBigInt(input)); + } +} diff --git a/packages/types/src/service/UntaggedPni.std.ts b/packages/types/src/service/UntaggedPni.std.ts new file mode 100644 index 0000000000..aecd77f63a --- /dev/null +++ b/packages/types/src/service/UntaggedPni.std.ts @@ -0,0 +1,60 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Uuid } from '../formats/Uuid.std.ts'; +import type { Pni } from './Pni.std.ts'; +import { stripPniPrefix } from '../_utils/pni.std.ts'; +import { Utf8 } from '../encodings/Utf8.std.ts'; +import type { Bytes } from '../encodings/Bytes.std.ts'; + +/** + * PNI UUID without the `PNI:` prefix. Use {@link Pni} when the prefix is required. + * @public + */ +export type UntaggedPni = UntaggedPni.Decoded; + +export namespace UntaggedPni { + type Opaque = Tagged; + + /** @public */ + export type Decoded = Utf8.Of; + /** @public */ + export type Encoded = Bytes.Of; + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Uuid.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: string): input is UntaggedPni { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromUuid(input: Uuid): UntaggedPni { + return Schema.parse(input); + } + + /** @public */ + export function fromString(input: string): UntaggedPni { + return fromUuid(Uuid.fromString(input)); + } + + /** @public */ + export function fromPni(input: Pni): UntaggedPni { + return fromString(stripPniPrefix(input)); + } + + /** @public */ + export function decode(input: Encoded): UntaggedPni { + return Utf8.fromBytes(input); + } + + /** @public */ + export function encode(input: UntaggedPni): Encoded { + return Utf8.toBytes(input); + } +} diff --git a/packages/types/src/units/UnitBytes.std.ts b/packages/types/src/units/UnitBytes.std.ts new file mode 100644 index 0000000000..9783517af4 --- /dev/null +++ b/packages/types/src/units/UnitBytes.std.ts @@ -0,0 +1,51 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Float64 } from '../numbers/Float64.std.ts'; +import type { UnitKibibytes } from './UnitKibibytes.std.ts'; +import type { UnitKilobytes } from './UnitKilobytes.std.ts'; + +/** + * File or data size measured in bytes. + * @public + */ +export type UnitBytes = Tagged; + +export namespace UnitBytes { + /** @public */ + export const KILOBYTE = 1000 as UnitBytes; // 10^3 + /** @public */ + export const KIBIBYTE = 1024 as UnitBytes; // 2^10 + + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Float64.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is UnitBytes { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromFloat64(input: Float64): UnitBytes { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): UnitBytes { + return fromFloat64(Float64.fromNumber(input)); + } + + /** @public */ + export function fromUnitKibibytes(input: UnitKibibytes): UnitBytes { + return fromNumber(input * KIBIBYTE); + } + + /** @public */ + export function fromUnitKilobytes(input: UnitKilobytes): UnitBytes { + return fromNumber(input * KILOBYTE); + } +} diff --git a/packages/types/src/units/UnitKibibytes.std.ts b/packages/types/src/units/UnitKibibytes.std.ts new file mode 100644 index 0000000000..c968b9a07c --- /dev/null +++ b/packages/types/src/units/UnitKibibytes.std.ts @@ -0,0 +1,46 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Float64 } from '../numbers/Float64.std.ts'; +import { UnitBytes } from './UnitBytes.std.ts'; +import type { UnitKilobytes } from './UnitKilobytes.std.ts'; + +/** + * File or data size measured in kibibytes (1 KiB = 1,024 bytes). + * @public + */ +export type UnitKibibytes = Tagged; + +export namespace UnitKibibytes { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Float64.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is UnitKibibytes { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromFloat64(input: Float64): UnitKibibytes { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): UnitKibibytes { + return fromFloat64(Float64.fromNumber(input)); + } + + /** @public */ + export function fromUnitBytes(input: UnitBytes): UnitKibibytes { + return fromNumber(input / UnitBytes.KIBIBYTE); + } + + /** @public */ + export function fromUnitKilobytes(input: UnitKilobytes): UnitKibibytes { + return fromUnitBytes(UnitBytes.fromUnitKilobytes(input)); + } +} diff --git a/packages/types/src/units/UnitKilobytes.std.ts b/packages/types/src/units/UnitKilobytes.std.ts new file mode 100644 index 0000000000..137a446e4c --- /dev/null +++ b/packages/types/src/units/UnitKilobytes.std.ts @@ -0,0 +1,46 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +import type { Tagged } from 'type-fest'; +import * as z from 'zod/mini'; +import { Float64 } from '../numbers/Float64.std.ts'; +import { UnitBytes } from './UnitBytes.std.ts'; +import type { UnitKibibytes } from './UnitKibibytes.std.ts'; + +/** + * File or data size measured in kilobytes (1 KB = 1,000 bytes). + * @public + */ +export type UnitKilobytes = Tagged; + +export namespace UnitKilobytes { + /** @public */ + export const Schema: z.ZodMiniType = z.pipe( + Float64.Schema, + z.custom() + ); + + /** @public */ + export function isValid(input: number): input is UnitKilobytes { + return Schema.safeParse(input).success; + } + + /** @public */ + export function fromFloat64(input: Float64): UnitKilobytes { + return Schema.parse(input); + } + + /** @public */ + export function fromNumber(input: number): UnitKilobytes { + return fromFloat64(Float64.fromNumber(input)); + } + + /** @public */ + export function fromUnitBytes(input: UnitBytes): UnitKilobytes { + return fromNumber(input / UnitBytes.KILOBYTE); + } + + /** @public */ + export function fromUnitKibibytes(input: UnitKibibytes): UnitKilobytes { + return fromUnitBytes(UnitBytes.fromUnitKibibytes(input)); + } +} diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 0000000000..caa2ac3089 --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,51 @@ +{ + "include": ["./src"], + "compilerOptions": { + "target": "ES2025", + "lib": ["ESNext"], + + "module": "esnext", + "rootDir": "./src", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "resolvePackageJsonExports": true, + "resolvePackageJsonImports": true, + "noUncheckedSideEffectImports": true, + "resolveJsonModule": true, + + "declaration": true, + "declarationMap": true, + "inlineSourceMap": true, + "noEmit": false, + "outDir": "./dist", + + "isolatedModules": true, + "verbatimModuleSyntax": true, + "isolatedDeclarations": true, + "forceConsistentCasingInFileNames": true, + "erasableSyntaxOnly": false, + + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "strictBuiltinIteratorReturn": true, + "noImplicitThis": true, + "useUnknownInCatchVariables": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "exactOptionalPropertyTypes": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "skipLibCheck": false + } +} diff --git a/packages/types/tsdown.config.mjs b/packages/types/tsdown.config.mjs new file mode 100644 index 0000000000..ab1f080da2 --- /dev/null +++ b/packages/types/tsdown.config.mjs @@ -0,0 +1,17 @@ +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: 'src/index.std.ts', + outDir: 'dist', + dts: true, + sourcemap: true, + format: { + esm: {}, + cjs: {}, + }, + publint: true, + attw: true, +}); diff --git a/packages/windows-ucv/package.json b/packages/windows-ucv/package.json index 04d798569a..752f8f66ae 100644 --- a/packages/windows-ucv/package.json +++ b/packages/windows-ucv/package.json @@ -1,7 +1,7 @@ { + "private": true, "name": "@signalapp/windows-ucv", "version": "1.0.1", - "private": true, "description": "Windows User Consent Verification", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b8b293dad..65c2fcebb0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,6 +55,9 @@ importers: '@signalapp/sqlcipher': specifier: 3.3.9 version: 3.3.9 + '@signalapp/types': + specifier: workspace:* + version: link:packages/types '@signalapp/windows-ucv': specifier: workspace:* version: link:packages/windows-ucv @@ -466,7 +469,7 @@ importers: version: 2.1.0 knip: specifier: 6.14.0 - version: 6.14.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + version: 6.14.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) linkify-it: specifier: 5.0.1 version: 5.0.1 @@ -712,6 +715,37 @@ importers: specifier: 3.6.2 version: 3.6.2 + packages/types: + dependencies: + buffer: + specifier: 6.0.3 + version: 6.0.3 + semver: + specifier: 7.7.4 + version: 7.7.4 + type-fest: + specifier: 5.5.0 + version: 5.5.0 + uuid: + specifier: 13.0.0 + version: 13.0.0 + zod: + specifier: 4.3.6 + version: 4.3.6(patch_hash=ab3dbef13a3a4a50b2a2fcab2bdabd3ef94a4b1607e046308492e88bdd9d5553) + devDependencies: + '@arethetypeswrong/core': + specifier: 0.18.4 + version: 0.18.4 + '@typescript/native-preview': + specifier: 7.0.0-dev.20260515.1 + version: 7.0.0-dev.20260515.1 + publint: + specifier: 0.3.21 + version: 0.3.21 + tsdown: + specifier: 0.22.3 + version: 0.22.3(@arethetypeswrong/core@0.18.4)(@typescript/native-preview@7.0.0-dev.20260515.1)(oxc-resolver@11.19.1)(publint@0.3.21)(tsx@4.21.0)(typescript@6.0.3) + packages/windows-ucv: dependencies: bindings: @@ -849,6 +883,13 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, tarball: https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz} engines: {node: '>=10'} + '@andrewbranch/untar.js@1.0.3': + resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==, tarball: https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz} + + '@arethetypeswrong/core@0.18.4': + resolution: {integrity: sha512-M5F0ePyN6h2Z6XxRiyIPqjGbltotXLjR0CKA0uKspsDu0QmgTNYvRb4RSQPMUs2ZXZHCCYpbaZbFbYOXLxCjUA==, tarball: https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.18.4.tgz} + engines: {node: '>=20'} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, tarball: https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz} engines: {node: '>=6.9.0'} @@ -865,6 +906,10 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==, tarball: https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==, tarball: https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==, tarball: https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz} engines: {node: '>=6.9.0'} @@ -924,10 +969,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, tarball: https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==, tarball: https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, tarball: https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.2': + resolution: {integrity: sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==, tarball: https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, tarball: https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz} engines: {node: '>=6.9.0'} @@ -941,6 +994,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.0': + resolution: {integrity: sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==, tarball: https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz} peerDependencies: @@ -1102,9 +1160,16 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==, tarball: https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz} engines: {node: '>=6.9.0'} + '@babel/types@8.0.0': + resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==, tarball: https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, tarball: https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz} + '@braidai/lang@1.1.2': + resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==, tarball: https://registry.npmjs.org/@braidai/lang/-/lang-1.1.2.tgz} + '@cacheable/memory@2.0.8': resolution: {integrity: sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==, tarball: https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.8.tgz} @@ -1229,12 +1294,21 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz} + '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz} + '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==, tarball: https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz} @@ -1898,6 +1972,9 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==, tarball: https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz} + '@loaderkit/resolve@1.0.6': + resolution: {integrity: sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==, tarball: https://registry.npmjs.org/@loaderkit/resolve/-/resolve-1.0.6.tgz} + '@malept/cross-spawn-promise@2.0.0': resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==, tarball: https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz} engines: {node: '>= 12.13.0'} @@ -1968,8 +2045,8 @@ packages: resolution: {integrity: sha512-IJDok7OsGQ+Wd9QOWaOnFszw1mxNtJuynD+tBAhWMV6QJr7fWZx83eYS9xSl+3XR2tUa9oiHc44APcHBgdErmA==, tarball: https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.61.tgz} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz} + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -2171,6 +2248,9 @@ packages: '@oxc-project/types@0.133.0': resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==, tarball: https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz} + '@oxc-project/types@0.138.0': + resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==, tarball: https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.19.1.tgz} cpu: [arm] @@ -2660,6 +2740,13 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==, tarball: https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz} + '@publint/pack@0.1.5': + resolution: {integrity: sha512-edgyN2pP07uXiP4tJs0s8KVmU8M8i60YPbbI0/WDeok1mIJHRXz+CgD8I0nelwDkoCh3EWL/G5kGfbuHjsdbvw==, tarball: https://registry.npmjs.org/@publint/pack/-/pack-0.1.5.tgz} + engines: {node: '>=18'} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==, tarball: https://registry.npmjs.org/@quansync/fs/-/fs-1.0.0.tgz} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==, tarball: https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz} @@ -3994,6 +4081,12 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.1.4': + resolution: {integrity: sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.0.1': resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4006,6 +4099,12 @@ packages: cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.1.4': + resolution: {integrity: sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.0.1': resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4018,6 +4117,12 @@ packages: cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.1.4': + resolution: {integrity: sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.0.1': resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4030,6 +4135,12 @@ packages: cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.1.4': + resolution: {integrity: sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.0.1': resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4042,6 +4153,12 @@ packages: cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + resolution: {integrity: sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.0.1': resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4056,6 +4173,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.1.4': + resolution: {integrity: sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.0.1': resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4070,6 +4194,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.1.4': + resolution: {integrity: sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.0.1': resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4084,6 +4215,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.1.4': + resolution: {integrity: sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.0.1': resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4098,6 +4236,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.1.4': + resolution: {integrity: sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.0.1': resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4112,6 +4257,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.1.4': + resolution: {integrity: sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.0.1': resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4126,6 +4278,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.1.4': + resolution: {integrity: sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.0.1': resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4138,6 +4297,12 @@ packages: cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.1.4': + resolution: {integrity: sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.0.1': resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==, tarball: https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4148,6 +4313,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.1.4': + resolution: {integrity: sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==, tarball: https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + '@rolldown/binding-win32-arm64-msvc@1.0.1': resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4160,6 +4330,12 @@ packages: cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.1.4': + resolution: {integrity: sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.0.1': resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -4172,6 +4348,12 @@ packages: cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.1.4': + resolution: {integrity: sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==, tarball: https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz} @@ -4670,8 +4852,8 @@ packages: resolution: {integrity: sha512-2crDHBKX0hKZfQXgTF5QIQSccg5fzn8L+oxpVRWAuBbeAdAkBIm4zVXV/LfyuQ1FO5zXW7iV/BI9zNWG5merHQ==, tarball: https://registry.npmjs.org/@tus/utils/-/utils-0.5.1.tgz} engines: {node: '>=16'} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, tarball: https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==, tarball: https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, tarball: https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz} @@ -4781,6 +4963,9 @@ packages: '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==, tarball: https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==, tarball: https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, tarball: https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz} @@ -5260,6 +5445,10 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, tarball: https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz} engines: {node: '>=12'} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==, tarball: https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz} + engines: {node: '>=14'} + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, tarball: https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz} @@ -5347,6 +5536,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, tarball: https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz} engines: {node: '>=12'} + ast-kit@3.0.0: + resolution: {integrity: sha512-8OG92q3R35qjC/4i6BLBMg8IB+fClWu/1PEwg2Z9Rn+BuNaiEgJzpzn+pxWOdHJWDCAwu2JP0wCDTozAM4QirQ==, tarball: https://registry.npmjs.org/ast-kit/-/ast-kit-3.0.0.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, tarball: https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz} @@ -5485,6 +5678,9 @@ packages: bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==, tarball: https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz} + birpc@4.0.0: + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==, tarball: https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==, tarball: https://registry.npmjs.org/bl/-/bl-4.1.0.tgz} @@ -5574,6 +5770,10 @@ packages: resolution: {integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==, tarball: https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz} engines: {node: '>= 0.8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==, tarball: https://registry.npmjs.org/cac/-/cac-7.0.0.tgz} + engines: {node: '>=20.19.0'} + cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==, tarball: https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz} engines: {node: '>=10.6.0'} @@ -6176,6 +6376,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, tarball: https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz} engines: {node: '>= 0.4'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==, tarball: https://registry.npmjs.org/defu/-/defu-6.1.7.tgz} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, tarball: https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz} engines: {node: '>=0.4.0'} @@ -6324,6 +6527,15 @@ packages: resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==, tarball: https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz} engines: {node: '>=12'} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==, tarball: https://registry.npmjs.org/dts-resolver/-/dts-resolver-3.0.0.tgz} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, tarball: https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz} engines: {node: '>= 0.4'} @@ -6401,6 +6613,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, tarball: https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==, tarball: https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz} + engines: {node: '>=14'} + encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==, tarball: https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz} @@ -6825,6 +7041,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==, tarball: https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz} engines: {node: ^12.20 || >= 14.13} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==, tarball: https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==, tarball: https://registry.npmjs.org/figures/-/figures-6.1.0.tgz} engines: {node: '>=18'} @@ -7094,6 +7313,10 @@ packages: get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==, tarball: https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz} + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==, tarball: https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.5.tgz} + engines: {node: '>=20.20.0'} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, tarball: https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz} engines: {node: '>= 6'} @@ -7249,6 +7472,9 @@ packages: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==, tarball: https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz} engines: {node: '>=0.10.0'} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==, tarball: https://registry.npmjs.org/hookable/-/hookable-6.1.1.tgz} + hookified@1.15.1: resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==, tarball: https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz} @@ -7399,6 +7625,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, tarball: https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz} + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==, tarball: https://registry.npmjs.org/import-without-cache/-/import-without-cache-0.4.0.tgz} + engines: {node: ^22.18.0 || >=24.0.0} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, tarball: https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz} engines: {node: '>=0.8.19'} @@ -8678,6 +8908,10 @@ packages: objectorarray@1.0.5: resolution: {integrity: sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==, tarball: https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.5.tgz} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==, tarball: https://registry.npmjs.org/obug/-/obug-2.1.3.tgz} + engines: {node: '>=12.20.0'} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==, tarball: https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz} engines: {node: '>=14.0.0'} @@ -8823,6 +9057,9 @@ packages: resolution: {integrity: sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==, tarball: https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz} engines: {node: '>=4'} + package-manager-detector@1.7.0: + resolution: {integrity: sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==, tarball: https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz} + param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==, tarball: https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz} @@ -8900,6 +9137,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, tarball: https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, tarball: https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz} + pathval@2.0.1: resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==, tarball: https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz} engines: {node: '>= 14.16'} @@ -9209,6 +9449,11 @@ packages: pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==, tarball: https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz} + publint@0.3.21: + resolution: {integrity: sha512-OqejcnMV6E9zel2oCrUOJEiiFkGiAAni0A6ibfQNh1k9Gu5z4F+Yso8lllam7AzmV6Do0vp7u3UpZNRBwuXaHQ==, tarball: https://registry.npmjs.org/publint/-/publint-0.3.21.tgz} + engines: {node: '>=18'} + hasBin: true + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==, tarball: https://registry.npmjs.org/pump/-/pump-3.0.4.tgz} @@ -9236,6 +9481,9 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==, tarball: https://registry.npmjs.org/qs/-/qs-6.14.0.tgz} engines: {node: '>=0.6'} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==, tarball: https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, tarball: https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz} @@ -9646,6 +9894,25 @@ packages: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==, tarball: https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz} engines: {node: '>=8.0'} + rolldown-plugin-dts@0.26.0: + resolution: {integrity: sha512-e+kEPtUiDES0htk5iqkSeF4EzAV7R+vugGB44iPDuw1Kw9E+WyL1VG7PaV0IIjGHLiacztMBcMTyrr8ON9CT1Q==, tarball: https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.26.0.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20260325.1' + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + rolldown@1.0.1: resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==, tarball: https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz} engines: {node: ^20.19.0 || >=22.12.0} @@ -9656,6 +9923,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.1.4: + resolution: {integrity: sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==, tarball: https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, tarball: https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz} @@ -9666,6 +9938,10 @@ packages: rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==, tarball: https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz} + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==, tarball: https://registry.npmjs.org/sade/-/sade-1.8.1.tgz} + engines: {node: '>=6'} + safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==, tarball: https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz} engines: {node: '>=0.4'} @@ -9763,6 +10039,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, tarball: https://registry.npmjs.org/semver/-/semver-7.8.5.tgz} + engines: {node: '>=10'} + hasBin: true + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==, tarball: https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz} engines: {node: '>=10'} @@ -10256,6 +10537,10 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==, tarball: https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==, tarball: https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz} + engines: {node: '>=18'} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, tarball: https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz} engines: {node: '>=12.0.0'} @@ -10338,6 +10623,40 @@ packages: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==, tarball: https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz} engines: {node: '>=6'} + tsdown@0.22.3: + resolution: {integrity: sha512-louqbfA8Qf//B9jTTL0FPtXTNpjCWv1VPkbcmQMph2pTpzs+LnB1tbe4tDDRVpo2BjF5SgUXaTZe45SxB8pWHg==, tarball: https://registry.npmjs.org/tsdown/-/tsdown-0.22.3.tgz} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.3 + '@tsdown/exe': 0.22.3 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==, tarball: https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz} @@ -10417,6 +10736,11 @@ packages: typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==, tarball: https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz} + typescript@5.6.1-rc: + resolution: {integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==, tarball: https://registry.npmjs.org/typescript/-/typescript-5.6.1-rc.tgz} + engines: {node: '>=14.17'} + hasBin: true + typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==, tarball: https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz} engines: {node: '>=14.17'} @@ -10437,6 +10761,9 @@ packages: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==, tarball: https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz} engines: {node: '>=0.10.0'} + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==, tarball: https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.5.0.tgz} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, tarball: https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz} @@ -10594,6 +10921,10 @@ packages: typescript: optional: true + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==, tarball: https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + verror@1.10.1: resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==, tarball: https://registry.npmjs.org/verror/-/verror-1.10.1.tgz} engines: {node: '>=0.6.0'} @@ -10933,6 +11264,19 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@andrewbranch/untar.js@1.0.3': {} + + '@arethetypeswrong/core@0.18.4': + dependencies: + '@andrewbranch/untar.js': 1.0.3 + '@loaderkit/resolve': 1.0.6 + cjs-module-lexer: 1.4.3 + fflate: 0.8.3 + lru-cache: 11.2.7 + semver: 7.7.4 + typescript: 5.6.1-rc + validate-npm-package-name: 5.0.1 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -10969,6 +11313,15 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.0': + dependencies: + '@babel/parser': 8.0.0 + '@babel/types': 8.0.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.29.0 @@ -11054,8 +11407,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@8.0.0': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@8.0.2': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.29.2': @@ -11067,6 +11424,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@8.0.0': + dependencies: + '@babel/types': 8.0.0 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -11260,8 +11621,15 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@8.0.0': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + '@bcoe/v8-coverage@0.2.3': {} + '@braidai/lang@1.1.2': {} + '@cacheable/memory@2.0.8': dependencies: '@cacheable/utils': 2.4.0 @@ -11456,16 +11824,32 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@epic-web/invariant@1.0.0': {} '@esbuild/aix-ppc64@0.25.12': @@ -12125,6 +12509,10 @@ snapshots: '@keyv/serialize@1.1.1': {} + '@loaderkit/resolve@1.0.6': + dependencies: + '@braidai/lang': 1.1.2 + '@malept/cross-spawn-promise@2.0.0': dependencies: cross-spawn: 7.0.6 @@ -12177,11 +12565,18 @@ snapshots: '@napi-rs/canvas-linux-x64-musl': 0.1.61 '@napi-rs/canvas-win32-x64-msvc': 0.1.61 - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 optional: true '@nodelib/fs.scandir@2.1.5': @@ -12311,7 +12706,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@oxc-parser/binding-win32-arm64-msvc@0.130.0': @@ -12327,6 +12722,8 @@ snapshots: '@oxc-project/types@0.133.0': {} + '@oxc-project/types@0.138.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -12375,9 +12772,9 @@ snapshots: '@oxc-resolver/binding-openharmony-arm64@11.19.1': optional: true - '@oxc-resolver/binding-wasm32-wasi@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@oxc-resolver/binding-wasm32-wasi@11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -12444,7 +12841,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@oxc-transform/binding-win32-arm64-msvc@0.131.0': @@ -12600,6 +12997,14 @@ snapshots: '@popperjs/core@2.11.8': {} + '@publint/pack@0.1.5': + dependencies: + tinyexec: 1.2.4 + + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -14451,84 +14856,127 @@ snapshots: '@rolldown/binding-android-arm64@1.0.3': optional: true + '@rolldown/binding-android-arm64@1.1.4': + optional: true + '@rolldown/binding-darwin-arm64@1.0.1': optional: true '@rolldown/binding-darwin-arm64@1.0.3': optional: true + '@rolldown/binding-darwin-arm64@1.1.4': + optional: true + '@rolldown/binding-darwin-x64@1.0.1': optional: true '@rolldown/binding-darwin-x64@1.0.3': optional: true + '@rolldown/binding-darwin-x64@1.1.4': + optional: true + '@rolldown/binding-freebsd-x64@1.0.1': optional: true '@rolldown/binding-freebsd-x64@1.0.3': optional: true + '@rolldown/binding-freebsd-x64@1.1.4': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.0.1': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.0.1': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true + '@rolldown/binding-linux-arm64-gnu@1.1.4': + optional: true + '@rolldown/binding-linux-arm64-musl@1.0.1': optional: true '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true + '@rolldown/binding-linux-arm64-musl@1.1.4': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.0.1': optional: true '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.1.4': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.0.1': optional: true '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true + '@rolldown/binding-linux-s390x-gnu@1.1.4': + optional: true + '@rolldown/binding-linux-x64-gnu@1.0.1': optional: true '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true + '@rolldown/binding-linux-x64-gnu@1.1.4': + optional: true + '@rolldown/binding-linux-x64-musl@1.0.1': optional: true '@rolldown/binding-linux-x64-musl@1.0.3': optional: true + '@rolldown/binding-linux-x64-musl@1.1.4': + optional: true + '@rolldown/binding-openharmony-arm64@1.0.1': optional: true '@rolldown/binding-openharmony-arm64@1.0.3': optional: true + '@rolldown/binding-openharmony-arm64@1.1.4': + optional: true + '@rolldown/binding-wasm32-wasi@1.0.1': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@rolldown/binding-wasm32-wasi@1.0.3': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.4': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.1': @@ -14537,12 +14985,18 @@ snapshots: '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true + '@rolldown/binding-win32-arm64-msvc@1.1.4': + optional: true + '@rolldown/binding-win32-x64-msvc@1.0.1': optional: true '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true + '@rolldown/binding-win32-x64-msvc@1.1.4': + optional: true + '@rolldown/pluginutils@1.0.1': {} '@rtsao/scc@1.1.0': {} @@ -15163,7 +15617,7 @@ snapshots: '@tus/utils@0.5.1': {} - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true @@ -15290,6 +15744,8 @@ snapshots: '@types/js-yaml@4.0.9': {} + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/json-to-ast@2.1.4': {} @@ -15817,6 +16273,8 @@ snapshots: ansi-styles@6.2.3: {} + ansis@4.3.1: {} + any-promise@1.3.0: {} anymatch@3.1.3: @@ -15961,6 +16419,12 @@ snapshots: assertion-error@2.0.1: {} + ast-kit@3.0.0: + dependencies: + '@babel/parser': 8.0.0 + estree-walker: 3.0.3 + pathe: 2.0.3 + ast-types-flow@0.0.8: {} ast-types@0.16.1: @@ -16126,6 +16590,8 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 + birpc@4.0.0: {} + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -16241,6 +16707,8 @@ snapshots: bytes@3.1.0: {} + cac@7.0.0: {} + cacheable-lookup@5.0.4: {} cacheable-lookup@7.0.0: {} @@ -16840,6 +17308,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.7: {} + delayed-stream@1.0.0: {} denque@2.1.0: @@ -16999,6 +17469,10 @@ snapshots: dotenv@16.4.7: {} + dts-resolver@3.0.0(oxc-resolver@11.19.1): + optionalDependencies: + oxc-resolver: 11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -17118,6 +17592,8 @@ snapshots: emoji-regex@9.2.2: {} + empathic@2.0.1: {} + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 @@ -17782,6 +18258,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fflate@0.8.3: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -18057,6 +18535,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -18271,6 +18753,8 @@ snapshots: dependencies: parse-passwd: 1.0.0 + hookable@6.1.1: {} + hookified@1.15.1: {} hookified@2.1.0: {} @@ -18443,6 +18927,8 @@ snapshots: import-meta-resolve@4.2.0: {} + import-without-cache@0.4.0: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -19297,7 +19783,7 @@ snapshots: kleur@3.0.3: {} - knip@6.14.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + knip@6.14.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): dependencies: fdir: 6.5.0(picomatch@4.0.4) formatly: 0.3.0 @@ -19305,7 +19791,7 @@ snapshots: jiti: 2.7.0 minimist: 1.2.8 oxc-parser: 0.130.0 - oxc-resolver: 11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + oxc-resolver: 11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) picomatch: 4.0.4 smol-toml: 1.6.1 strip-json-comments: 5.0.3 @@ -19906,6 +20392,8 @@ snapshots: objectorarray@1.0.5: {} + obug@2.1.3: {} + on-exit-leak-free@2.1.2: {} once@1.4.0: @@ -19986,7 +20474,7 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.130.0 '@oxc-parser/binding-win32-x64-msvc': 0.130.0 - oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + oxc-resolver@11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.19.1 '@oxc-resolver/binding-android-arm64': 11.19.1 @@ -20004,7 +20492,7 @@ snapshots: '@oxc-resolver/binding-linux-x64-gnu': 11.19.1 '@oxc-resolver/binding-linux-x64-musl': 11.19.1 '@oxc-resolver/binding-openharmony-arm64': 11.19.1 - '@oxc-resolver/binding-wasm32-wasi': 11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@oxc-resolver/binding-wasm32-wasi': 11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@oxc-resolver/binding-win32-arm64-msvc': 11.19.1 '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 @@ -20136,6 +20624,8 @@ snapshots: registry-url: 3.1.0 semver: 5.7.2 + package-manager-detector@1.7.0: {} + param-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -20196,6 +20686,8 @@ snapshots: path-type@4.0.0: {} + pathe@2.0.3: {} + pathval@2.0.1: {} pe-library@0.4.1: {} @@ -20424,6 +20916,13 @@ snapshots: pseudomap@1.0.2: {} + publint@0.3.21: + dependencies: + '@publint/pack': 0.1.5 + package-manager-detector: 1.7.0 + picocolors: 1.1.1 + sade: 1.8.1 + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -20451,6 +20950,8 @@ snapshots: dependencies: side-channel: 1.1.0 + quansync@1.0.0: {} + queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -21042,6 +21543,23 @@ snapshots: sprintf-js: 1.1.3 optional: true + rolldown-plugin-dts@0.26.0(@typescript/native-preview@7.0.0-dev.20260515.1)(oxc-resolver@11.19.1)(rolldown@1.1.4)(typescript@6.0.3): + dependencies: + '@babel/generator': 8.0.0 + '@babel/helper-validator-identifier': 8.0.2 + '@babel/parser': 8.0.0 + ast-kit: 3.0.0 + birpc: 4.0.0 + dts-resolver: 3.0.0(oxc-resolver@11.19.1) + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.3 + rolldown: 1.1.4 + optionalDependencies: + '@typescript/native-preview': 7.0.0-dev.20260515.1 + typescript: 6.0.3 + transitivePeerDependencies: + - oxc-resolver + rolldown@1.0.1: dependencies: '@oxc-project/types': 0.130.0 @@ -21084,6 +21602,27 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.3 '@rolldown/binding-win32-x64-msvc': 1.0.3 + rolldown@1.1.4: + dependencies: + '@oxc-project/types': 0.138.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.4 + '@rolldown/binding-darwin-arm64': 1.1.4 + '@rolldown/binding-darwin-x64': 1.1.4 + '@rolldown/binding-freebsd-x64': 1.1.4 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.4 + '@rolldown/binding-linux-arm64-gnu': 1.1.4 + '@rolldown/binding-linux-arm64-musl': 1.1.4 + '@rolldown/binding-linux-ppc64-gnu': 1.1.4 + '@rolldown/binding-linux-s390x-gnu': 1.1.4 + '@rolldown/binding-linux-x64-gnu': 1.1.4 + '@rolldown/binding-linux-x64-musl': 1.1.4 + '@rolldown/binding-openharmony-arm64': 1.1.4 + '@rolldown/binding-wasm32-wasi': 1.1.4 + '@rolldown/binding-win32-arm64-msvc': 1.1.4 + '@rolldown/binding-win32-x64-msvc': 1.1.4 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -21096,6 +21635,10 @@ snapshots: dependencies: tslib: 2.8.1 + sade@1.8.1: + dependencies: + mri: 1.2.0 + safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -21182,6 +21725,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.5: {} + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 @@ -21755,6 +22300,8 @@ snapshots: tiny-invariant@1.3.3: {} + tinyexec@1.2.4: {} + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -21840,6 +22387,34 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tsdown@0.22.3(@arethetypeswrong/core@0.18.4)(@typescript/native-preview@7.0.0-dev.20260515.1)(oxc-resolver@11.19.1)(publint@0.3.21)(tsx@4.21.0)(typescript@6.0.3): + dependencies: + ansis: 4.3.1 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.3 + picomatch: 4.0.4 + rolldown: 1.1.4 + rolldown-plugin-dts: 0.26.0(@typescript/native-preview@7.0.0-dev.20260515.1)(oxc-resolver@11.19.1)(rolldown@1.1.4)(typescript@6.0.3) + semver: 7.8.5 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + optionalDependencies: + '@arethetypeswrong/core': 0.18.4 + publint: 0.3.21 + tsx: 4.21.0(patch_hash=251027c4cc25615da4cf5b54757ba1af5ecae4256a23772bcc2c1284f6aa1251) + typescript: 6.0.3 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - vue-tsc + tslib@1.14.1: {} tslib@2.8.1: {} @@ -21920,6 +22495,8 @@ snapshots: dependencies: is-typedarray: 1.0.0 + typescript@5.6.1-rc: {} + typescript@6.0.3: {} uc.micro@2.1.0: {} @@ -21935,6 +22512,11 @@ snapshots: unc-path-regex@0.1.2: {} + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + undici-types@6.21.0: {} undici-types@7.16.0: {} @@ -22074,6 +22656,8 @@ snapshots: optionalDependencies: typescript: 6.0.3 + validate-npm-package-name@5.0.1: {} + verror@1.10.1: dependencies: assert-plus: 1.0.0 diff --git a/ts/components/conversation/pinned-messages/PinMessageDialog.dom.tsx b/ts/components/conversation/pinned-messages/PinMessageDialog.dom.tsx index 90df5a7e1d..b09b64533f 100644 --- a/ts/components/conversation/pinned-messages/PinMessageDialog.dom.tsx +++ b/ts/components/conversation/pinned-messages/PinMessageDialog.dom.tsx @@ -5,10 +5,10 @@ import { memo, useCallback, useMemo, useState } from 'react'; import { AxoDialog } from '../../../axo/AxoDialog.dom.tsx'; import type { LocalizerType } from '../../../types/I18N.std.ts'; import { AxoRadioGroup } from '../../../axo/AxoRadioGroup.dom.tsx'; -import { DurationInSeconds } from '../../../util/durations/duration-in-seconds.std.ts'; import { strictAssert } from '../../../util/assert.std.ts'; import { AxoAlertDialog } from '../../../axo/AxoAlertDialog.dom.tsx'; import { isInternalFeaturesEnabled } from '../../../util/isInternalFeaturesEnabled.dom.ts'; +import { DurationSecs } from '@signalapp/types'; enum DurationOption { TIME_24_HOURS = 'TIME_24_HOURS', @@ -18,12 +18,12 @@ enum DurationOption { DEBUG_10_SECONDS = 'DEBUG_10_SECONDS', } -const DURATION_OPTIONS: Record = { - [DurationOption.TIME_24_HOURS]: DurationInSeconds.fromHours(24), - [DurationOption.TIME_7_DAYS]: DurationInSeconds.fromDays(7), - [DurationOption.TIME_30_DAYS]: DurationInSeconds.fromDays(30), +const DURATION_OPTIONS: Record = { + [DurationOption.TIME_24_HOURS]: DurationSecs.fromHours(24), + [DurationOption.TIME_7_DAYS]: DurationSecs.fromDays(7), + [DurationOption.TIME_30_DAYS]: DurationSecs.fromDays(30), [DurationOption.FOREVER]: null, - [DurationOption.DEBUG_10_SECONDS]: DurationInSeconds.fromSeconds(10), + [DurationOption.DEBUG_10_SECONDS]: DurationSecs.fromSeconds(10), }; enum Step { @@ -48,7 +48,7 @@ export type PinMessageDialogProps = Readonly<{ onSeenPinMessageDisappearingMessagesWarning: () => void; onPinnedMessageAdd: ( messageId: string, - duration: DurationInSeconds | null + duration: DurationSecs | null ) => void; }>; diff --git a/ts/jobs/conversationJobQueue.preload.ts b/ts/jobs/conversationJobQueue.preload.ts index ffe1c974a5..ec0b0d4632 100644 --- a/ts/jobs/conversationJobQueue.preload.ts +++ b/ts/jobs/conversationJobQueue.preload.ts @@ -3,6 +3,12 @@ import { z } from 'zod'; import type PQueue from 'p-queue'; +import { + DurationSecs, + ReceivedTimestampMs, + SentTimestampMs, + TimestampMs, +} from '@signalapp/types'; import { createLogger } from '../logging/log.std.ts'; import * as durations from '../util/durations/index.std.ts'; @@ -207,9 +213,9 @@ const pinMessageJobDataSchema = z.object({ conversationId: z.string(), targetMessageId: z.string(), targetAuthorAci: aciSchema, - targetSentTimestamp: z.number(), - pinDurationSeconds: z.number().nullable(), - pinnedAt: z.number(), + targetSentTimestamp: SentTimestampMs.Schema, + pinDurationSeconds: z.nullable(DurationSecs.Schema), + pinnedAt: TimestampMs.Schema, }); export type PinMessageJobData = z.infer; @@ -239,10 +245,10 @@ const resendRequestJobDataSchema = z.object({ groupId: z.string().optional(), plaintext: z.string(), receivedAtCounter: z.number(), - receivedAtDate: z.number(), + receivedAtDate: ReceivedTimestampMs.Schema, senderAci: aciSchema, senderDevice: z.number(), - timestamp: z.number(), + timestamp: SentTimestampMs.Schema, }); export type ResendRequestJobData = z.infer; @@ -290,8 +296,8 @@ const unpinMessageJobDataSchema = z.object({ conversationId: z.string(), targetMessageId: z.string(), targetAuthorAci: aciSchema, - targetSentTimestamp: z.number(), - unpinnedAt: z.number(), + targetSentTimestamp: SentTimestampMs.Schema, + unpinnedAt: TimestampMs.Schema, isSyncOnly: z.boolean(), }); export type UnpinMessageJobData = z.infer; diff --git a/ts/messageModifiers/PinnedMessages.preload.ts b/ts/messageModifiers/PinnedMessages.preload.ts index f7c7e35bbe..492186c443 100644 --- a/ts/messageModifiers/PinnedMessages.preload.ts +++ b/ts/messageModifiers/PinnedMessages.preload.ts @@ -17,6 +17,11 @@ import { drop } from '../util/drop.std.ts'; import type { AppendPinnedMessageResult } from '../sql/server/pinnedMessages.std.ts'; import * as Errors from '../types/errors.std.ts'; import { isGiftBadge } from '../state/selectors/message.preload.ts'; +import type { + DurationSecs, + ReceivedTimestampMs, + SentTimestampMs, +} from '@signalapp/types'; const { AccessRequired } = Proto.AccessControl; const { Role } = Proto.Member; @@ -26,10 +31,10 @@ const parentLog = createLogger('PinnedMessages'); export type PinnedMessageAddProps = Readonly<{ targetSentTimestamp: number; targetAuthorAci: AciString; - pinDuration: DurationInSeconds | null; + pinDuration: DurationSecs | null; pinnedByAci: AciString; - sentAtTimestamp: number; - receivedAtTimestamp: number; + sentAtTimestamp: SentTimestampMs; + receivedAtTimestamp: ReceivedTimestampMs; expireTimer: DurationInSeconds | null; expirationStartTimestamp: number | null; }>; diff --git a/ts/services/backups/import.preload.ts b/ts/services/backups/import.preload.ts index 375371b6de..fc7cee0c67 100644 --- a/ts/services/backups/import.preload.ts +++ b/ts/services/backups/import.preload.ts @@ -169,6 +169,7 @@ import type { ThemeType } from '../../util/preload.preload.ts'; import { toNumber } from '../../util/toNumber.std.ts'; import { isKnownProtoEnumMember } from '../../util/isKnownProtoEnumMember.std.ts'; import { Emoji } from '../../axo/emoji.std.ts'; +import { TimestampMs } from '@signalapp/types'; const { isNumber } = lodash; @@ -1856,11 +1857,11 @@ export class BackupImportStream extends Writable { strictAssert(pinnedAtTimestamp, 'Missing PinDetails.pinnedAtTimestamp'); strictAssert(pinExpiry, 'Missing PinDetails.pinExpiry'); - const pinnedAt = toNumber(pinnedAtTimestamp); + const pinnedAt = TimestampMs.fromBigInt(pinnedAtTimestamp); - let expiresAt: number | null; + let expiresAt: TimestampMs | null; if (pinExpiry.pinExpiresAtTimestamp != null) { - expiresAt = toNumber(pinExpiry.pinExpiresAtTimestamp); + expiresAt = TimestampMs.fromBigInt(pinExpiry.pinExpiresAtTimestamp); } else { strictAssert( pinExpiry.pinNeverExpires, diff --git a/ts/services/expiring/pinnedMessagesCleanupService.preload.ts b/ts/services/expiring/pinnedMessagesCleanupService.preload.ts index 2051fb733b..cb056b182e 100644 --- a/ts/services/expiring/pinnedMessagesCleanupService.preload.ts +++ b/ts/services/expiring/pinnedMessagesCleanupService.preload.ts @@ -10,6 +10,7 @@ import { } from '../../jobs/conversationJobQueue.preload.ts'; import { getPinnedMessageTarget } from '../../util/getPinMessageTarget.preload.ts'; import { drop } from '../../util/drop.std.ts'; +import { TimestampMs } from '@signalapp/types'; export const pinnedMessagesCleanupService = createExpiringEntityCleanupService({ logPrefix: 'PinnedMessages', @@ -31,7 +32,7 @@ export const pinnedMessagesCleanupService = createExpiringEntityCleanupService({ cleanupExpiredEntities: async () => { const deletedPinnedMessages = await DataWriter.deleteAllExpiredPinnedMessagesBefore(Date.now()); - const unpinnedAt = Date.now(); + const unpinnedAt = TimestampMs.now(); const deletedPinnedMessagesIds = []; const changedConversationIds = new Set(); @@ -53,7 +54,7 @@ export const pinnedMessagesCleanupService = createExpiringEntityCleanupService({ }, }); -async function sendUnpinSync(targetMessageId: string, unpinnedAt: number) { +async function sendUnpinSync(targetMessageId: string, unpinnedAt: TimestampMs) { const target = await getPinnedMessageTarget(targetMessageId); if (target == null) { return; diff --git a/ts/services/retryPlaceholders.std.ts b/ts/services/retryPlaceholders.std.ts index 2c8999c47d..09f5c2c475 100644 --- a/ts/services/retryPlaceholders.std.ts +++ b/ts/services/retryPlaceholders.std.ts @@ -8,6 +8,7 @@ import { aciSchema } from '../types/ServiceId.std.ts'; import { safeParseStrict } from '../util/schemas.std.ts'; import { HOUR } from '../util/durations/index.std.ts'; import type { StorageInterface } from '../types/Storage.d.ts'; +import { ReceivedTimestampMs, SentTimestampMs } from '@signalapp/types'; const { groupBy } = lodash; @@ -16,8 +17,8 @@ const log = createLogger('retryPlaceholders'); const retryItemSchema = z .object({ conversationId: z.string(), - sentAt: z.number(), - receivedAt: z.number(), + sentAt: SentTimestampMs.Schema, + receivedAt: ReceivedTimestampMs.Schema, receivedAtCounter: z.number(), senderAci: aciSchema, wasOpened: z.boolean().optional(), diff --git a/ts/sql/Interface.std.ts b/ts/sql/Interface.std.ts index 4cb8d208d8..67256e0346 100644 --- a/ts/sql/Interface.std.ts +++ b/ts/sql/Interface.std.ts @@ -80,6 +80,11 @@ import type { import { sqlFragment, sqlId, sqlJoin } from './util.std.ts'; import type { MIMEType } from '../types/MIME.std.ts'; import type { Emoji } from '../axo/emoji.std.ts'; +import type { + ReceivedTimestampMs, + SentTimestampMs, + ServerTimestampMs, +} from '@signalapp/types'; export type ReadableDB = Database & { __readable_db: never }; export type WritableDB = ReadableDB & { __writable_db: never }; @@ -444,12 +449,12 @@ export type StickerPackRefType = Readonly<{ export type UnprocessedType = { id: string; - timestamp: number; + timestamp: SentTimestampMs; /* * A client generated date used for removing old envelopes from the table * on startup. */ - receivedAtDate: number; + receivedAtDate: ReceivedTimestampMs; receivedAtCounter: number; attempts: number; type: number; @@ -463,7 +468,7 @@ export type UnprocessedType = { destinationServiceId: ServiceIdString; updatedPni: PniString | undefined; serverGuid: string; - serverTimestamp: number; + serverTimestamp: ServerTimestampMs; urgent: boolean; story: boolean; reportingToken: Uint8Array | undefined; diff --git a/ts/state/ducks/conversations.preload.ts b/ts/state/ducks/conversations.preload.ts index 0357a166d7..331bba8cd2 100644 --- a/ts/state/ducks/conversations.preload.ts +++ b/ts/state/ducks/conversations.preload.ts @@ -261,6 +261,11 @@ import { } from '../../util/Conversation.preload.ts'; import type { Emoji } from '../../axo/emoji.std.ts'; import { isSignalConversation } from '../../util/isSignalConversation.dom.ts'; +import { + type DurationSecs, + SentTimestampMs, + TimestampMs, +} from '@signalapp/types'; const { chunk, difference, fromPairs, omit, orderBy, pick, values, without } = lodash; @@ -5183,7 +5188,7 @@ function onPinnedMessagesChanged( function onPinnedMessageAdd( targetMessageId: string, - pinDurationSeconds: DurationInSeconds | null + pinDurationSeconds: DurationSecs | null ): StateThunk { return async dispatch => { const target = await getPinnedMessageTarget(targetMessageId); @@ -5196,7 +5201,7 @@ function onPinnedMessageAdd( ); strictAssert(targetConversation != null, 'Missing target conversation'); - const pinnedAt = Date.now(); + const pinnedAt = SentTimestampMs.now(); await conversationJobQueue.add({ type: conversationQueueJobEnum.enum.PinMessage, @@ -5242,7 +5247,7 @@ function onPinnedMessageRemove(targetMessageId: string): StateThunk { await conversationJobQueue.add({ type: conversationQueueJobEnum.enum.UnpinMessage, ...target, - unpinnedAt: Date.now(), + unpinnedAt: TimestampMs.now(), isSyncOnly: false, }); await DataWriter.deletePinnedMessageByMessageId(targetMessageId); diff --git a/ts/test-electron/SignalProtocolStore_test.preload.ts b/ts/test-electron/SignalProtocolStore_test.preload.ts index d2e69766ae..c41ed57469 100644 --- a/ts/test-electron/SignalProtocolStore_test.preload.ts +++ b/ts/test-electron/SignalProtocolStore_test.preload.ts @@ -33,12 +33,21 @@ import { } from '../SignalProtocolStore.preload.ts'; import { Address } from '../types/Address.std.ts'; import { QualifiedAddress } from '../types/QualifiedAddress.std.ts'; -import type { IdentityKeyType, KeyPairType } from '../textsecure/Types.d.ts'; +import type { + IdentityKeyType, + KeyPairType, + UnprocessedType, +} from '../textsecure/Types.d.ts'; import { itemStorage } from '../textsecure/Storage.preload.ts'; import { generateAci, generatePni, } from '../test-helpers/serviceIdUtils.std.ts'; +import { + ReceivedTimestampMs, + SentTimestampMs, + ServerTimestampMs, +} from '@signalapp/types'; const { clone } = lodash; @@ -54,7 +63,7 @@ describe('SignalProtocolStore', () => { const NOW = Date.now(); - const unprocessedDefaults = { + const unprocessedDefaults: Omit = { type: 1, messageAgeSec: 1, source: undefined, @@ -68,12 +77,12 @@ describe('SignalProtocolStore', () => { urgent: false, receivedAtCounter: 0, serverGuid: generateUuid(), - serverTimestamp: 1, + serverTimestamp: ServerTimestampMs.fromNumber(1), attempts: 0, isEncrypted: true, content: Buffer.from('content'), - timestamp: NOW, + timestamp: SentTimestampMs.fromNumber(NOW), }; function getSessionRecord(isOpen?: boolean): SessionRecord { @@ -1302,7 +1311,7 @@ describe('SignalProtocolStore', () => { id: '2-two', content: Buffer.from('second'), - receivedAtDate: Date.now() + 2, + receivedAtDate: ReceivedTimestampMs.fromNumber(Date.now() + 2), }, { zone } ); @@ -1360,7 +1369,7 @@ describe('SignalProtocolStore', () => { id: '2-two', content: Buffer.from('second'), - receivedAtDate: 2, + receivedAtDate: ReceivedTimestampMs.fromNumber(2), }, { zone } ); @@ -1500,7 +1509,9 @@ describe('SignalProtocolStore', () => { content: Buffer.from('old envelope'), receivedAtCounter: -1, - receivedAtDate: NOW - 2 * durations.MONTH, + receivedAtDate: ReceivedTimestampMs.fromNumber( + NOW - 2 * durations.MONTH + ), }), store.addUnprocessed({ ...unprocessedDefaults, @@ -1508,7 +1519,7 @@ describe('SignalProtocolStore', () => { content: Buffer.from('second'), receivedAtCounter: 1, - receivedAtDate: NOW + 2, + receivedAtDate: ReceivedTimestampMs.fromNumber(NOW + 2), }), store.addUnprocessed({ ...unprocessedDefaults, @@ -1516,7 +1527,7 @@ describe('SignalProtocolStore', () => { content: Buffer.from('third'), receivedAtCounter: 2, - receivedAtDate: NOW + 3, + receivedAtDate: ReceivedTimestampMs.fromNumber(NOW + 3), }), store.addUnprocessed({ ...unprocessedDefaults, @@ -1524,7 +1535,7 @@ describe('SignalProtocolStore', () => { content: Buffer.from('first'), receivedAtCounter: 0, - receivedAtDate: NOW + 1, + receivedAtDate: ReceivedTimestampMs.fromNumber(NOW + 1), }), ]); @@ -1547,7 +1558,7 @@ describe('SignalProtocolStore', () => { id, - receivedAtDate: NOW + 1, + receivedAtDate: ReceivedTimestampMs.fromNumber(NOW + 1), }); await store.removeUnprocessed(id); @@ -1564,7 +1575,7 @@ describe('SignalProtocolStore', () => { id: '1-one', attempts: 10, - receivedAtDate: NOW + 1, + receivedAtDate: ReceivedTimestampMs.fromNumber(NOW + 1), }); const items = await store.getUnprocessedByIdsAndIncrementAttempts( diff --git a/ts/test-node/services/retryPlaceholders_test.std.ts b/ts/test-node/services/retryPlaceholders_test.std.ts index 7866d53b54..8096dbbd72 100644 --- a/ts/test-node/services/retryPlaceholders_test.std.ts +++ b/ts/test-node/services/retryPlaceholders_test.std.ts @@ -11,6 +11,7 @@ import { STORAGE_KEY, } from '../../services/retryPlaceholders.std.ts'; import { generateAci } from '../../test-helpers/serviceIdUtils.std.ts'; +import { ReceivedTimestampMs, SentTimestampMs } from '@signalapp/types'; describe('RetryPlaceholders', () => { const NOW = 1_000_000; @@ -40,8 +41,8 @@ describe('RetryPlaceholders', () => { function getDefaultItem(): RetryItemType { return { conversationId: 'conversation-id', - sentAt: NOW - 10, - receivedAt: NOW - 5, + sentAt: SentTimestampMs.fromNumber(NOW - 10), + receivedAt: ReceivedTimestampMs.fromNumber(NOW - 5), receivedAtCounter: 4, senderAci: generateAci(), }; @@ -113,11 +114,11 @@ describe('RetryPlaceholders', () => { it('returns soonest expiration given a list, and after add', async () => { const older = { ...getDefaultItem(), - receivedAt: NOW, + receivedAt: ReceivedTimestampMs.fromNumber(NOW), }; const newer = { ...getDefaultItem(), - receivedAt: NOW + 10, + receivedAt: ReceivedTimestampMs.fromNumber(NOW + 10), }; const items: Array = [older, newer]; await storage.put(STORAGE_KEY, items); @@ -129,7 +130,7 @@ describe('RetryPlaceholders', () => { const oldest = { ...getDefaultItem(), - receivedAt: NOW - 5, + receivedAt: ReceivedTimestampMs.fromNumber(NOW - 5), }; await placeholders.add(oldest); @@ -142,11 +143,11 @@ describe('RetryPlaceholders', () => { it('does nothing if no item expired', async () => { const older = { ...getDefaultItem(), - receivedAt: NOW + 10, + receivedAt: ReceivedTimestampMs.fromNumber(NOW + 10), }; const newer = { ...getDefaultItem(), - receivedAt: NOW + 15, + receivedAt: ReceivedTimestampMs.fromNumber(NOW + 15), }; const items: Array = [older, newer]; await storage.put(STORAGE_KEY, items); @@ -160,11 +161,11 @@ describe('RetryPlaceholders', () => { it('removes just one if expired', async () => { const older = { ...getDefaultItem(), - receivedAt: getDeltaIntoPast() - 1000, + receivedAt: ReceivedTimestampMs.fromNumber(getDeltaIntoPast() - 1000), }; const newer = { ...getDefaultItem(), - receivedAt: NOW + 15, + receivedAt: ReceivedTimestampMs.fromNumber(NOW + 15), }; const items: Array = [older, newer]; await storage.put(STORAGE_KEY, items); @@ -179,11 +180,11 @@ describe('RetryPlaceholders', () => { it('removes all if expired', async () => { const older = { ...getDefaultItem(), - receivedAt: getDeltaIntoPast() - 1000, + receivedAt: ReceivedTimestampMs.fromNumber(getDeltaIntoPast() - 1000), }; const newer = { ...getDefaultItem(), - receivedAt: getDeltaIntoPast() - 900, + receivedAt: ReceivedTimestampMs.fromNumber(getDeltaIntoPast() - 900), }; const items: Array = [older, newer]; await storage.put(STORAGE_KEY, items); @@ -225,17 +226,17 @@ describe('RetryPlaceholders', () => { const convo1a = { ...getDefaultItem(), conversationId: 'conversation-id-1', - receivedAt: NOW - 5, + receivedAt: ReceivedTimestampMs.fromNumber(NOW - 5), }; const convo1b = { ...getDefaultItem(), conversationId: 'conversation-id-1', - receivedAt: NOW - 4, + receivedAt: ReceivedTimestampMs.fromNumber(NOW - 4), }; const convo2a = { ...getDefaultItem(), conversationId: 'conversation-id-2', - receivedAt: NOW + 15, + receivedAt: ReceivedTimestampMs.fromNumber(NOW + 15), }; const items: Array = [convo1a, convo1b, convo2a]; await storage.put(STORAGE_KEY, items); @@ -265,7 +266,7 @@ describe('RetryPlaceholders', () => { const convo2b = { ...getDefaultItem(), conversationId: 'conversation-id-2', - receivedAt: NOW + 16, + receivedAt: ReceivedTimestampMs.fromNumber(NOW + 16), }; await placeholders.add(convo2b); @@ -300,17 +301,17 @@ describe('RetryPlaceholders', () => { describe('#findByMessageAndRemove', () => { it('does nothing if no item matching message found', async () => { - const sentAt = NOW - 20; + const sentAt = SentTimestampMs.fromNumber(NOW - 20); const older = { ...getDefaultItem(), conversationId: 'conversation-id-1', - sentAt: NOW - 10, + sentAt: SentTimestampMs.fromNumber(NOW - 10), }; const newer = { ...getDefaultItem(), conversationId: 'conversation-id-1', - sentAt: NOW - 11, + sentAt: SentTimestampMs.fromNumber(NOW - 11), }; const items: Array = [older, newer]; await storage.put(STORAGE_KEY, items); @@ -324,12 +325,12 @@ describe('RetryPlaceholders', () => { assert.strictEqual(2, placeholders.getCount()); }); it('removes the item matching message', async () => { - const sentAt = NOW - 20; + const sentAt = SentTimestampMs.fromNumber(NOW - 20); const older = { ...getDefaultItem(), conversationId: 'conversation-id-1', - sentAt: NOW - 10, + sentAt: SentTimestampMs.fromNumber(NOW - 10), }; const newer = { ...getDefaultItem(), diff --git a/ts/test-node/sql/server/pinnedMessages_test.node.ts b/ts/test-node/sql/server/pinnedMessages_test.node.ts index d92bc661e4..e89edb55fa 100644 --- a/ts/test-node/sql/server/pinnedMessages_test.node.ts +++ b/ts/test-node/sql/server/pinnedMessages_test.node.ts @@ -15,6 +15,7 @@ import type { PinnedMessage, PinnedMessageParams, } from '../../../types/PinnedMessage.std.ts'; +import { TimestampMs } from '@signalapp/types'; function setupData(db: WritableDB) { insertData(db, 'conversations', [{ id: 'c1' }, { id: 'c2' }]); @@ -39,8 +40,8 @@ function getParams( return { messageId, conversationId, - pinnedAt, - expiresAt, + pinnedAt: TimestampMs.fromNumber(pinnedAt), + expiresAt: expiresAt != null ? TimestampMs.fromNumber(expiresAt) : null, }; } @@ -131,7 +132,7 @@ describe('sql/server/pinnedMessages', () => { const pin1 = getParams('c1', 'c1-m1', 1); const pin2 = getParams('c1', 'c1-m2', 2); const pin3 = getParams('c1', 'c1-m3', 3); - const updated = { ...pin3, pinnedAt: 4 }; + const updated = { ...pin3, pinnedAt: TimestampMs.fromNumber(4) }; const row1 = expectInserted(appendPinnedMessage(db, 3, pin1)); const row2 = expectInserted(appendPinnedMessage(db, 3, pin2)); diff --git a/ts/textsecure/MessageReceiver.preload.ts b/ts/textsecure/MessageReceiver.preload.ts index e23f15f568..58681755be 100644 --- a/ts/textsecure/MessageReceiver.preload.ts +++ b/ts/textsecure/MessageReceiver.preload.ts @@ -175,6 +175,12 @@ import { } from '../types/MessageRequestResponseEvent.std.ts'; import { toNumber } from '../util/toNumber.std.ts'; +import { + TimestampMs, + ReceivedTimestampMs, + SentTimestampMs, + ServerTimestampMs, +} from '@signalapp/types'; const { isBoolean, isNumber, isString, noop } = lodash; @@ -404,7 +410,9 @@ export default class MessageReceiver const plaintext = request.body; const decoded = Proto.Envelope.decode(plaintext); - const serverTimestamp = toNumber(decoded.serverTimestamp) ?? 0; + const serverTimestamp = ServerTimestampMs.fromBigInt( + decoded.serverTimestamp ?? 0n + ); const ourAci = this.#storage.user.getCheckedAci(); @@ -415,7 +423,7 @@ export default class MessageReceiver // from logs id: getGuid().replace(/-/g, '.'), receivedAtCounter: incrementMessageCounter(), - receivedAtDate: Date.now(), + receivedAtDate: ReceivedTimestampMs.now(), // Calculate the message age (time on server). messageAgeSec: this.#calculateMessageAge( request.timestamp, @@ -444,7 +452,7 @@ export default class MessageReceiver decoded.updatedPni, 'MessageReceiver.handleRequest.updatedPni' ), - timestamp: toNumber(decoded.clientTimestamp) ?? 0, + timestamp: SentTimestampMs.fromBigInt(decoded.clientTimestamp ?? 0n), content: content ?? new Uint8Array(0), serverGuid: (Bytes.isNotEmpty(decoded.serverGuidBinary) @@ -2111,7 +2119,7 @@ export default class MessageReceiver envelopeId: envelope.id, destinationE164: destinationE164 ?? '', destinationServiceId, - timestamp: toNumber(timestamp), + timestamp: SentTimestampMs.fromBigInt(timestamp), serverTimestamp: envelope.serverTimestamp, device: envelope.sourceDevice, unidentifiedStatus, @@ -2119,7 +2127,9 @@ export default class MessageReceiver isRecipientUpdate, receivedAtCounter: envelope.receivedAtCounter, receivedAtDate: envelope.receivedAtDate, - expirationStartTimestamp: toNumber(expirationStartTimestamp) ?? 0, + expirationStartTimestamp: TimestampMs.fromBigInt( + expirationStartTimestamp ?? 0n + ), }, this.#removeFromCache.bind(this, envelope) ); @@ -3201,7 +3211,9 @@ export default class MessageReceiver isRecipientUpdate, receivedAtCounter: envelope.receivedAtCounter, receivedAtDate: envelope.receivedAtDate, - expirationStartTimestamp: toNumber(expirationStartTimestamp) ?? 0, + expirationStartTimestamp: TimestampMs.fromBigInt( + expirationStartTimestamp ?? 0n + ), }, this.#removeFromCache.bind(this, envelope) ); diff --git a/ts/textsecure/Types.d.ts b/ts/textsecure/Types.d.ts index 151b038626..25038dfbe4 100644 --- a/ts/textsecure/Types.d.ts +++ b/ts/textsecure/Types.d.ts @@ -18,6 +18,12 @@ import type { AnyPaymentEvent } from '../types/Payment.std.ts'; import type { RawBodyRange } from '../types/BodyRange.std.ts'; import type { StoryMessageRecipientsType } from '../types/Stories.std.ts'; import type { Emoji } from '../axo/emoji.std.ts'; +import type { + DurationSecs, + ReceivedTimestampMs, + SentTimestampMs, + ServerTimestampMs, +} from '@signalapp/types'; export type { IdentityKeyType, @@ -86,7 +92,7 @@ export type SessionResetsType = Record; export type ProcessedEnvelope = Readonly<{ id: string; receivedAtCounter: number; - receivedAtDate: number; + receivedAtDate: ReceivedTimestampMs; messageAgeSec: number; // Mostly from Proto.Envelope except for null/undefined @@ -96,10 +102,10 @@ export type ProcessedEnvelope = Readonly<{ sourceDevice: number | undefined; destinationServiceId: ServiceIdString; updatedPni: PniString | undefined; - timestamp: number; + timestamp: SentTimestampMs; content: Uint8Array; serverGuid: string; - serverTimestamp: number; + serverTimestamp: ServerTimestampMs; groupId: string | undefined; urgent: boolean; story: boolean; @@ -189,8 +195,8 @@ export type ProcessedReaction = { export type ProcessedPinMessage = Readonly<{ targetAuthorAci: AciString; - targetSentTimestamp: number; - pinDuration: DurationInSeconds | null; + targetSentTimestamp: SentTimestampMs; + pinDuration: DurationSecs | null; }>; export type ProcessedPollCreate = { diff --git a/ts/textsecure/messageReceiverEvents.std.ts b/ts/textsecure/messageReceiverEvents.std.ts index 3db025a52a..67f5357547 100644 --- a/ts/textsecure/messageReceiverEvents.std.ts +++ b/ts/textsecure/messageReceiverEvents.std.ts @@ -24,6 +24,12 @@ import type { } from '../types/CallDisposition.std.ts'; import type { CallLinkUpdateSyncType } from '../types/CallLink.std.ts'; import { isAciString } from '../util/isAciString.std.ts'; +import type { + ReceivedTimestampMs, + SentTimestampMs, + ServerTimestampMs, + TimestampMs, +} from '@signalapp/types'; export class EmptyEvent extends Event { constructor() { @@ -173,10 +179,10 @@ export type DecryptionErrorEventData = Readonly<{ contentHint: number | undefined; groupId: string | undefined; receivedAtCounter: number; - receivedAtDate: number; + receivedAtDate: ReceivedTimestampMs; senderDevice: number; senderAci: AciString; - timestamp: number; + timestamp: SentTimestampMs; }>; export class DecryptionErrorEvent extends ConfirmableEvent { @@ -227,15 +233,15 @@ export type SentEventData = Readonly<{ envelopeId: string; destinationE164?: string; destinationServiceId?: ServiceIdString; - timestamp: number; - serverTimestamp: number; + timestamp: SentTimestampMs; + serverTimestamp: ServerTimestampMs; device: number | undefined; unidentifiedStatus: ProcessedSent['unidentifiedStatus']; message: ProcessedDataMessage; isRecipientUpdate: boolean; receivedAtCounter: number; - receivedAtDate: number; - expirationStartTimestamp?: number; + receivedAtDate: ReceivedTimestampMs; + expirationStartTimestamp?: TimestampMs; storyDistributionListId?: StoryDistributionIdString; }>; @@ -276,13 +282,13 @@ export type MessageEventData = Readonly<{ sourceAci: AciString; sourceDevice?: number; destinationServiceId: ServiceIdString; - timestamp: number; + timestamp: SentTimestampMs; serverGuid: string; - serverTimestamp: number; + serverTimestamp: ServerTimestampMs; unidentifiedDeliveryReceived: boolean; message: ProcessedDataMessage; receivedAtCounter: number; - receivedAtDate: number; + receivedAtDate: ReceivedTimestampMs; }>; export class MessageEvent extends ConfirmableEvent { diff --git a/ts/textsecure/processDataMessage.preload.ts b/ts/textsecure/processDataMessage.preload.ts index b3da2f1377..a0eb532213 100644 --- a/ts/textsecure/processDataMessage.preload.ts +++ b/ts/textsecure/processDataMessage.preload.ts @@ -57,6 +57,7 @@ import { createLogger } from '../logging/log.std.ts'; import { toNumber } from '../util/toNumber.std.ts'; import { Emoji } from '../axo/emoji.std.ts'; +import { DurationSecs, SentTimestampMs } from '@signalapp/types'; const { isNumber } = lodash; @@ -359,13 +360,15 @@ function processPinMessage( return undefined; } - const targetSentTimestamp = toNumber(pinMessage.targetSentTimestamp); - strictAssert(targetSentTimestamp, 'Missing targetSentTimestamp'); + strictAssert(pinMessage.targetSentTimestamp, 'Missing targetSentTimestamp'); + const targetSentTimestamp = SentTimestampMs.fromBigInt( + pinMessage.targetSentTimestamp + ); const targetAuthorAci = fromAciUuidBytes(pinMessage.targetAuthorAciBinary); strictAssert(targetAuthorAci, 'Missing targetAuthorAciBinary'); - let pinDuration: DurationInSeconds | null; + let pinDuration: DurationSecs | null; if (pinMessage.pinDuration?.pinDurationForever) { pinDuration = null; } else { @@ -373,7 +376,7 @@ function processPinMessage( pinMessage.pinDuration?.pinDurationSeconds, 'Missing pinDurationSeconds' ); - pinDuration = DurationInSeconds.fromSeconds( + pinDuration = DurationSecs.fromSeconds( pinMessage.pinDuration.pinDurationSeconds ); } diff --git a/ts/types/PinnedMessage.std.ts b/ts/types/PinnedMessage.std.ts index d6766e6d36..bbff528cba 100644 --- a/ts/types/PinnedMessage.std.ts +++ b/ts/types/PinnedMessage.std.ts @@ -1,6 +1,11 @@ // Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import type { + DurationSecs, + SentTimestampMs, + TimestampMs, +} from '@signalapp/types'; import type { AciString } from '@signalapp/mock-server/src/types.js'; import type { ReadonlyMessageAttributesType } from '../model-types.d.ts'; @@ -10,8 +15,8 @@ export type PinnedMessage = Readonly<{ id: PinnedMessageId; conversationId: string; messageId: string; - pinnedAt: number; - expiresAt: number | null; + pinnedAt: TimestampMs; + expiresAt: TimestampMs | null; }>; export type PinnedMessageParams = Omit; @@ -23,11 +28,11 @@ export type PinnedMessagePreloadData = Readonly<{ export type SendPinMessageType = Readonly<{ targetAuthorAci: AciString; - targetSentTimestamp: number; - pinDurationSeconds: number | null; + targetSentTimestamp: SentTimestampMs; + pinDurationSeconds: DurationSecs | null; }>; export type SendUnpinMessageType = Readonly<{ targetAuthorAci: AciString; - targetSentTimestamp: number; + targetSentTimestamp: SentTimestampMs; }>; diff --git a/ts/util/getPinMessageTarget.preload.ts b/ts/util/getPinMessageTarget.preload.ts index be6f698d91..46d1819a16 100644 --- a/ts/util/getPinMessageTarget.preload.ts +++ b/ts/util/getPinMessageTarget.preload.ts @@ -1,6 +1,7 @@ // Copyright 2026 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import { SentTimestampMs } from '@signalapp/types'; import { createLogger } from '../logging/log.std.ts'; import { isIncoming } from '../messages/helpers.std.ts'; import type { ReadonlyMessageAttributesType } from '../model-types.d.ts'; @@ -17,7 +18,7 @@ export type PinnedMessageTarget = Readonly<{ conversationId: string; targetMessageId: string; targetAuthorAci: AciString; - targetSentTimestamp: number; + targetSentTimestamp: SentTimestampMs; }>; function getMessageAuthorAci( @@ -44,9 +45,11 @@ export async function getPinnedMessageTarget( conversationId: message.conversationId, targetMessageId: message.id, targetAuthorAci: getMessageAuthorAci(message), - targetSentTimestamp: getMessageSentTimestamp(message, { - includeEdits: true, - log, - }), + targetSentTimestamp: SentTimestampMs.fromNumber( + getMessageSentTimestamp(message, { + includeEdits: true, + log, + }) + ), }; } diff --git a/ts/util/pinnedMessages.std.ts b/ts/util/pinnedMessages.std.ts index 3390f7e004..4f2d236aab 100644 --- a/ts/util/pinnedMessages.std.ts +++ b/ts/util/pinnedMessages.std.ts @@ -1,15 +1,17 @@ // Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { DurationInSeconds } from './durations/duration-in-seconds.std.ts'; +import { DurationMs, type DurationSecs, TimestampMs } from '@signalapp/types'; export function getPinnedMessageExpiresAt( - receivedAtTimestamp: number, - pinDuration: DurationInSeconds | null -): number | null { + pinnedAt: TimestampMs, + pinDuration: DurationSecs | null +): TimestampMs | null { if (pinDuration == null) { return null; } - const pinDurationMs = DurationInSeconds.toMillis(pinDuration); - return receivedAtTimestamp + pinDurationMs; + + const pinDurationMs = DurationMs.fromDurationSecs(pinDuration); + + return TimestampMs.fromNumber(pinnedAt + pinDurationMs); }