@xo-cash/utils 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,26 @@
1
1
  import { IdentifierResolutionType, OpcodesBchSpec, bigIntToVmNumber, binToHex, binToUtf8, createCompilerBch, describeExpectedInput, generateBytecodeMap, hexToBin, parseScript, sha256, utf8ToBin, vmNumberToBigInt } from "@bitauth/libauth";
2
- import { BchVmVersions, XOTemplateLockingTypes, XOTemplateNftCapabilities, XOTemplatePrimitiveTypes } from "@xo-cash/types";
3
2
  import { z } from "zod";
3
+ import { BchVmVersions, XOTemplateLockingTypes, XOTemplateNftCapabilities, XOTemplatePrimitiveTypes } from "@xo-cash/types";
4
4
  import { FungibleTokenAmount, NFTCommitment, PublicKey, Satoshis, SchnorrSignature, TemplateIdentifier, Timestamp, TokenCategory, TransactionHash } from "@xo-cash/primitives";
5
5
 
6
6
  //#region source/errors.ts
7
7
  /**
8
+ * Formats the Zod validation failures into a single string with one line each: "- <field>: <message>" and top level failures
9
+ * with no field path show as "(root)" for better readability.
10
+ *
11
+ * @param issues The Zod validation failures to format.
12
+ * @returns A human readable error string for better debugging.
13
+ */
14
+ const buildErrorDescription = (issues) => {
15
+ const lines = [];
16
+ for (const issue of issues) {
17
+ const issuePath = issue.path.length > 0 ? issue.path.join(".") : "(root)";
18
+ const issueMessage = issue.message.startsWith("Invalid input: ") ? issue.message.slice(15) : issue.message;
19
+ lines.push(`- ${issuePath}: ${issueMessage}`);
20
+ }
21
+ return `\n${lines.join("\n")}`;
22
+ };
23
+ /**
8
24
  * Error thrown when a waitFor timeout is reached
9
25
  */
10
26
  var WaitForTimeoutError = class extends Error {
@@ -34,8 +50,8 @@ var EventEmitter = class {
34
50
  * @returns An off callback that can be called to stop listening for events.
35
51
  */
36
52
  on(type, listener, debounceMilliseconds = 0) {
37
- const { cancel, listener: cancellableListener } = this.cancellable(listener);
38
- const wrappedListener = debounceMilliseconds > 0 ? this.debounce(cancellableListener, debounceMilliseconds) : cancellableListener;
53
+ const { cancel, listener: cancellableListener } = this.#cancellable(listener);
54
+ const wrappedListener = debounceMilliseconds > 0 ? this.#debounce(cancellableListener, debounceMilliseconds) : cancellableListener;
39
55
  if (!this.#listeners.has(type)) this.#listeners.set(type, /* @__PURE__ */ new Set());
40
56
  const listenerEntry = {
41
57
  listener,
@@ -57,8 +73,8 @@ var EventEmitter = class {
57
73
  this.off(type, listener);
58
74
  listener(detail);
59
75
  };
60
- const { cancel, listener: cancellableListener } = this.cancellable(wrappedListener);
61
- const debouncedListener = debounceMilliseconds > 0 ? this.debounce(cancellableListener, debounceMilliseconds) : cancellableListener;
76
+ const { cancel, listener: cancellableListener } = this.#cancellable(wrappedListener);
77
+ const debouncedListener = debounceMilliseconds > 0 ? this.#debounce(cancellableListener, debounceMilliseconds) : cancellableListener;
62
78
  if (!this.#listeners.has(type)) this.#listeners.set(type, /* @__PURE__ */ new Set());
63
79
  const listenerEntry = {
64
80
  listener,
@@ -156,7 +172,7 @@ var EventEmitter = class {
156
172
  * @param wait - The wait time in milliseconds.
157
173
  * @returns The debounced function.
158
174
  */
159
- debounce(func, wait) {
175
+ #debounce(func, wait) {
160
176
  let timeout;
161
177
  return (detail) => {
162
178
  if (timeout !== void 0) clearTimeout(timeout);
@@ -170,7 +186,7 @@ var EventEmitter = class {
170
186
  * @param func - The function to make cancelable.
171
187
  * @returns The cancellable function with a cancel method.
172
188
  */
173
- cancellable(func) {
189
+ #cancellable(func) {
174
190
  let cancelled = false;
175
191
  return {
176
192
  cancel: () => cancelled = true,
@@ -617,876 +633,389 @@ const fromExtendedJson = (serializedObject) => {
617
633
  };
618
634
 
619
635
  //#endregion
620
- //#region source/script.ts
636
+ //#region source/invitation/errors.ts
621
637
  /**
622
- * Converts a script to a scriptHash.
623
- * @param {Uint8Array} script - The script to convert.
624
- * @returns {string} The scriptHash as a reversed hex string.
638
+ * Thrown when a value does not satisfy the XOInvitation schema.
625
639
  */
626
- const scriptToScriptHash = (script) => {
627
- return binToHex(sha256.hash(script).reverse());
640
+ var InvitationInvalidError = class extends Error {
641
+ constructor(details) {
642
+ const defaultMessage = `Invitation invalid, expected a value matching the XOInvitation type, but got: ${details}`;
643
+ super(defaultMessage);
644
+ this.name = "InvitationInvalidError";
645
+ }
628
646
  };
629
-
630
- //#endregion
631
- //#region source/sse-session/async-push-iterator.ts
632
647
  /**
633
- * An async iterable queue that bridges push-based producers and pull-based consumers.
634
- *
635
- * Composes an internal {@link ReadableStream} instead of extending it, so producers
636
- * call {@link push} while consumers use standard async iteration (`for await...of`).
637
- *
638
- * ```ts
639
- * const messages = new AsyncPushIterator<SSEvent>();
640
- *
641
- * // Producer (elsewhere)
642
- * messages.push(event);
643
- *
644
- * // Consumer
645
- * for await (const event of messages) {
646
- * handle(event);
647
- * }
648
- * ```
649
- *
650
- * {@link Symbol.asyncIterator} returns `stream.values({ preventCancel: true })` so
651
- * breaking out of `for await...of` does not cancel the underlying stream. That
652
- * matters for long-lived sessions where the producer keeps pushing after a consumer
653
- * stops reading early (for example, test helpers that only collect a fixed count).
648
+ * Thrown when invitation share JSON cannot be parsed.
654
649
  */
655
- var AsyncPushIterator = class {
656
- /** ReadableStream backing the async iterator returned from {@link Symbol.asyncIterator}. */
657
- #stream;
658
- /** Controller used to enqueue values and close the stream from {@link push} and {@link close}. */
659
- #controller;
660
- /** When true, no more values are accepted and iteration eventually completes. */
661
- #closed = false;
662
- constructor() {
663
- this.#stream = new ReadableStream({ start: (controller) => {
664
- this.#controller = controller;
665
- } });
666
- }
667
- /**
668
- * Flag indicating if the iterator is closed.
669
- */
670
- get closed() {
671
- return this.#closed;
672
- }
673
- /**
674
- * Enqueues a value for the consumer.
675
- *
676
- * After {@link close}, pushes are silently dropped.
677
- *
678
- * @param value - The next value to yield from the iterator.
679
- */
680
- push(value) {
681
- if (this.#closed) return;
682
- this.#controller?.enqueue(value);
683
- }
684
- /**
685
- * Causes any future interactions with the associated stream to error with {@link error}.
686
- * Calling this will also clear the pending values immediately, so iterators that were listening will not receive them.
687
- *
688
- * @param error - The error to throw from the stream.
689
- */
690
- error(error) {
691
- if (this.#closed) return;
692
- this.#closed = true;
693
- this.#controller?.error(error);
694
- }
650
+ var InvitationJsonMalformedError = class extends Error {
695
651
  /**
696
- * Ends the stream.
697
- *
698
- * Marks the iterator closed so future {@link push} calls are ignored.
699
- * Buffered values are still yielded before iteration completes.
652
+ * Parser failure forwarded from JSON parsing.
700
653
  */
701
- close() {
702
- this.#closed = true;
703
- try {
704
- this.#controller?.close();
705
- } catch {}
654
+ reason;
655
+ constructor(reason) {
656
+ const defaultMessage = `Invitation JSON malformed: expected a valid JSON string, but got: ${reason}`;
657
+ super(defaultMessage);
658
+ this.name = "InvitationJsonMalformedError";
659
+ this.reason = reason;
706
660
  }
661
+ };
662
+ /**
663
+ * Thrown when invitation serialization fails.
664
+ */
665
+ var InvitationSerializationFailedError = class extends Error {
707
666
  /**
708
- * Returns an async iterator over the composed stream.
709
- *
710
- * Uses `preventCancel: true` so early `break` from `for await...of` does not
711
- * close the stream and block later pushes.
712
- *
713
- * Because values are discarded after being read, only a single consumer is supported.
714
- * Additional consumers will receive a stream lock error. Unread values will be preserved until {@link close} is called.
667
+ * Serializer failure forwarded from the serialization path.
715
668
  */
716
- [Symbol.asyncIterator]() {
717
- return this.#stream.values({ preventCancel: true });
669
+ reason;
670
+ constructor(reason) {
671
+ const defaultMessage = `Invitation serialization failed: ${reason}`;
672
+ super(defaultMessage);
673
+ this.name = "InvitationSerializationFailedError";
674
+ this.reason = reason;
718
675
  }
719
676
  };
720
677
 
721
678
  //#endregion
722
- //#region source/sse-session/constants.ts
679
+ //#region source/template/schemas.ts
723
680
  /**
724
- * Regex that splits decoded SSE text into lines.
681
+ * Validation schema for a BCH VM version identifier. Defines the set of known BCH VM versions
682
+ * that XO templates declare support for.
725
683
  *
726
- * The SSE wire format is line-oriented (`field: value` per line). Servers may
727
- * send `\r\n` (HTTP default), `\n` (Unix), or `\r` (legacy Mac). Matching all
728
- * three keeps parsing correct regardless of platform or server implementation.
684
+ * Uses `BchVmVersions` from `@xo-cash/types` so template validation stays aligned with the
685
+ * `BchVmVersion` type.
686
+ *
687
+ * ```
688
+ * {
689
+ * "supported": [ "BCH_2025_05" ] ← each value
690
+ * }
691
+ * ```
729
692
  */
730
- const SSE_LINE_ENDINGS = /\r\n|\r|\n/;
693
+ const bchVmVersionSchema = z.enum(BchVmVersions);
731
694
  /**
732
- * Regex that matches the single optional leading space in an SSE field value.
695
+ * Validation schema for the capability of a non-fungible token. Defines the three capability
696
+ * types supported on BCH: minting tokens can create new NFTs, mutable tokens can update their
697
+ * commitment, and none tokens cannot be changed after creation.
733
698
  *
734
- * Per the SSE spec, `field: value` may include one space immediately after the
735
- * colon; that space is not part of the value. Used with `.replace()` to strip
736
- * it when parsing lines such as `data: hello` → `hello`.
699
+ * ```
700
+ * {
701
+ * "inputs|outputs": {
702
+ * "[id]": {
703
+ * "token": {
704
+ * "nft": {
705
+ * "capability": "minting" ← this schema
706
+ * }
707
+ * }
708
+ * }
709
+ * }
710
+ * }
711
+ * ```
737
712
  */
738
- const SSE_FIELD_VALUE_REGEX = /^ /;
713
+ const xoTemplateNftCapabilitySchema = z.enum(XOTemplateNftCapabilities);
739
714
  /**
740
- * Regex that matches a trailing newline at the end of a string.
715
+ * Validation schema for a BCH locking script type. Defines the standard locking script types
716
+ * supported on BCH.
741
717
  *
742
- * Multiple `data:` lines in one event are joined with `\n`. When the event is
743
- * completed, this removes any stray trailing newline so callers receive the
744
- * payload without an extra line break at the end.
718
+ * ```
719
+ * {
720
+ * "lockingScripts": {
721
+ * "[id]": {
722
+ * "lockingType": "p2pkh" ← this schema
723
+ * }
724
+ * }
725
+ * }
726
+ * ```
745
727
  */
746
- const SSE_TRAILING_NEWLINE_REGEX = /\n$/;
728
+ const xoTemplateLockingTypeSchema = z.enum(XOTemplateLockingTypes);
747
729
  /**
748
- * The newline character used when normalizing SSE text internally.
749
- *
750
- * Used to join consecutive `data:` lines into one payload and to reassemble
751
- * buffered partial lines between streamed chunks before the next parse call.
730
+ * Validation schema for a primitive type identifier.
731
+ * Accepts values from XOTemplatePrimitiveTypes for the `hint` field on constants, variables, and data.
752
732
  */
753
- const NEW_LINE = "\n";
733
+ const xoTemplatePrimitiveTypeSchema = z.enum(XOTemplatePrimitiveTypes);
754
734
  /**
755
- * Default exponential backoff options for the SSE session.
735
+ * Validation schema for byte array fields i.e. Uint8Array instance.
756
736
  */
757
- const SSE_SESSION_EXPONENTIAL_BACKOFF_DEFAULTS = {
758
- baseDelay: 250,
759
- maxDelay: 1e4,
760
- maxAttempts: 0,
761
- growthRate: 1.3,
762
- jitter: .3
763
- };
737
+ const uint8ArraySchema = z.instanceof(Uint8Array).describe("A sequence of unsigned 8-bit integers expressed as a Uint8Array.");
764
738
  /**
765
- * Default attempt reconnect flag for the SSE session.
766
- * @default true
739
+ * Validation schema for the Satoshis type i.e. bigint.
767
740
  */
768
- const SSE_SESSION_ATTEMPT_RECONNECT_DEFAULT = true;
741
+ const satoshisSchema = z.bigint().describe("A satoshi amount expressed as a bigint.");
742
+ /** Maximum character length for name fields on view properties. */
743
+ const VIEW_PROPERTIES_NAME_MAX_LENGTH = 1e3;
744
+ /** Maximum character length for description fields on view properties. */
745
+ const VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH = 5e3;
746
+ /** Maximum character length for icon fields on view properties. */
747
+ const VIEW_PROPERTIES_ICON_MAX_LENGTH = 1e3;
769
748
  /**
770
- * Default persistent flag for the SSE session.
771
- * @default false
749
+ * Validation schema for view properties shared across many template elements i.e. name, description, icon.
750
+ * Extended by most other schemas in this file.
772
751
  */
773
- const SSE_SESSION_PERSISTENT_DEFAULT = false;
774
-
775
- //#endregion
776
- //#region source/sse-session/sse-event-parser.ts
752
+ const xoTemplateViewPropertiesSchema = z.object({
753
+ name: z.string().max(VIEW_PROPERTIES_NAME_MAX_LENGTH).describe("A short human-readable label for this element."),
754
+ description: z.string().max(VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH).describe("A human-readable explanation of what this element does and when it is relevant."),
755
+ icon: z.string().max(VIEW_PROPERTIES_ICON_MAX_LENGTH).optional().describe("An optional icon identifier or URL for this element.")
756
+ }).strict();
777
757
  /**
778
- * Incrementally parses Server-Sent Events (SSE) from streamed byte chunks.
779
- *
780
- * SSE payloads are line-oriented: each event is a sequence of `field: value`
781
- * lines terminated by a blank line. This parser accepts arbitrary chunk
782
- * boundaries from a live HTTP response body and emits only complete events.
758
+ * Validation schema for the base intent structure. Describes the common data parameters shared
759
+ * by all intent types regardless of what they target.
783
760
  *
784
- * Typical usage is one parser instance per connection, calling {@link parseEvents}
785
- * for each chunk received from the stream:
761
+ * An optional templateIdentifier allows the intent to reference a target defined in a different
762
+ * template, enabling cross-template interaction.
786
763
  *
787
- * ```ts
788
- * const parser = new SSEEventParser();
764
+ * Extended by: xoTemplateActionIntentSchema, xoTemplateOutputIntentSchema,
765
+ * xoTemplateLockingScriptIntentSchema.
766
+ */
767
+ const xoTemplateIntentSchema = z.object({
768
+ templateIdentifier: z.string().optional().describe("Optional identifier for the template used in this intent. If not provided, uses the current template."),
769
+ role: z.string().optional().describe("Optional identifier for the role used in this intent."),
770
+ generate: z.array(z.string()).optional().describe("Identifiers for items to generate when this intent is resolved, e.g. keys or secrets."),
771
+ variables: z.array(z.record(z.string(), z.unknown())).optional().describe("Variable values to apply when this intent is resolved."),
772
+ constants: z.array(z.record(z.string(), z.unknown())).optional().describe("Constant values to apply when this intent is resolved."),
773
+ secrets: z.array(z.record(z.string(), z.unknown())).optional().describe("Secret values to apply when this intent is resolved.")
774
+ }).strict();
775
+ /**
776
+ * Validation schema for an action intent. Extends the base intent structure with an action
777
+ * identifier. Used in locking script action lists and in the template's start array.
789
778
  *
790
- * for await (const chunk of response.body) {
791
- * for (const event of parser.parseEvents(chunk)) {
792
- * // handle event.data, event.event, event.id, event.retry
793
- * }
794
- * }
795
779
  * ```
796
- *
797
- * Supported fields follow the SSE spec: `data`, `event`, `id`, and `retry`.
798
- * Multiple `data:` lines in one event are joined with `\n`. An event is only
799
- * emitted once a blank line is seen and at least one `data` field was collected.
800
- */
801
- var SSEEventParser = class {
802
- #textDecoder;
803
- /** Bytes from a partial line or incomplete event, carried over to the next chunk. */
804
- #messageBuffer = "";
805
- /**
806
- * Creates a parser for one SSE stream.
807
- *
808
- * Inject custom encoders in tests or when a non-default character encoding
809
- * is required; production callers can rely on the defaults.
810
- *
811
- * @param options - Optional text encoders for decode/encode of stream bytes.
812
- */
813
- constructor(options = {}) {
814
- this.#textDecoder = options.textDecoder ?? new TextDecoder();
815
- }
816
- /**
817
- * Clears any buffered bytes from a partial line or incomplete event.
818
- *
819
- * Call when abandoning a transport so the next connection does not prepend
820
- * stale bytes to incoming chunks.
821
- */
822
- reset() {
823
- this.#messageBuffer = "";
824
- this.#textDecoder.decode();
825
- }
826
- /**
827
- * Parses all complete SSE events contained in a newly received chunk.
828
- *
829
- * The chunk is appended to any bytes buffered from earlier calls. Complete
830
- * events (blank-line delimited blocks with at least one `data` field) are
831
- * returned immediately; any trailing partial line or in-progress event stays
832
- * in the internal buffer until a later chunk completes it.
833
- *
834
- * @param chunk - Newly received SSE stream bytes.
835
- * @returns Zero or more complete parsed SSE events from this chunk.
836
- */
837
- parseEvents(chunk) {
838
- const lines = this.getBufferedLines(chunk);
839
- const eventLines = lines.slice(0, -1);
840
- const events = [];
841
- let event = {};
842
- let processedLineCount = 0;
843
- for (const [index, line] of eventLines.entries()) {
844
- if (line === "") {
845
- if (event.data !== void 0) {
846
- events.push(this.completeEvent(event));
847
- event = {};
848
- processedLineCount = index + 1;
849
- }
850
- continue;
851
- }
852
- this.parseLine(line, event);
853
- }
854
- this.storeRemainingLines(lines, processedLineCount);
855
- return events;
856
- }
857
- /**
858
- * Appends a new chunk to the buffered bytes and splits the combined payload
859
- * into lines.
860
- *
861
- * Accepts `\r\n`, `\r`, and `\n` line endings so events parse correctly
862
- * regardless of server or platform conventions.
863
- */
864
- getBufferedLines(chunk) {
865
- this.#messageBuffer += this.#textDecoder.decode(chunk, { stream: true });
866
- return this.#messageBuffer.split(SSE_LINE_ENDINGS);
867
- }
868
- /**
869
- * Parses one SSE field line into an in-progress event.
870
- *
871
- * Lines without a colon are ignored. A single optional space after the colon
872
- * is stripped from the field value, per the SSE spec.
873
- */
874
- parseLine(line, event) {
875
- const colonIndex = line.indexOf(":");
876
- if (colonIndex === -1) return;
877
- const field = line.slice(0, colonIndex);
878
- const value = line.slice(colonIndex + 1).replace(SSE_FIELD_VALUE_REGEX, "");
879
- switch (field) {
880
- case "data":
881
- event.data = event.data ? `${event.data}${NEW_LINE}${value}` : value;
882
- return;
883
- case "event":
884
- event.event = value;
885
- return;
886
- case "id":
887
- event.id = value;
888
- return;
889
- case "retry":
890
- this.parseRetry(value, event);
891
- return;
892
- }
893
- }
894
- /**
895
- * Applies a numeric `retry:` field to an in-progress event.
896
- *
897
- * Non-numeric values are ignored rather than failing the parse.
898
- */
899
- parseRetry(value, event) {
900
- const retry = parseInt(value, 10);
901
- if (!isNaN(retry)) event.retry = retry;
902
- }
903
- /**
904
- * Constructs a completed SSE event from accumulated fields.
905
- *
906
- * Trims a trailing newline from multi-line `data` values so callers receive
907
- * the payload without an extra line break at the end.
908
- */
909
- completeEvent(event) {
910
- return {
911
- ...event,
912
- data: event.data?.replace(SSE_TRAILING_NEWLINE_REGEX, "")
913
- };
914
- }
915
- /**
916
- * Preserves incomplete trailing lines for the next received chunk.
917
- *
918
- * Only lines that were fully processed (through a completed event boundary)
919
- * are discarded; the remainder is re-encoded into {@link messageBuffer}.
920
- */
921
- storeRemainingLines(lines, processedLineCount) {
922
- this.#messageBuffer = lines.slice(processedLineCount).join(NEW_LINE);
923
- }
924
- };
925
-
926
- //#endregion
927
- //#region source/sse-session/errors.ts
928
- /**
929
- * Error thrown when a response body is null
780
+ * {
781
+ * "start": [
782
+ * { "action": "..." } ← this schema
783
+ * ],
784
+ * "lockingScripts": {
785
+ * "[id]": {
786
+ * "actions": [
787
+ * { "action": "..." } ← this schema
788
+ * ],
789
+ * "roles": {
790
+ * "[roleId]": {
791
+ * "actions": [
792
+ * { "action": "..." } ← this schema
793
+ * ]
794
+ * }
795
+ * }
796
+ * }
797
+ * }
798
+ * }
799
+ * ```
930
800
  */
931
- var ResponseBodyNullError = class extends Error {
932
- constructor() {
933
- super("Response body is null");
934
- this.name = "ResponseBodyNullError";
935
- }
936
- };
801
+ const xoTemplateActionIntentSchema = xoTemplateIntentSchema.extend({ action: z.string().describe("The identifier for the intended action.") }).strict();
937
802
  /**
938
- * Error thrown when an HTTP error occurs
803
+ * Validation schema for an output intent. Extends the base intent structure with an output
804
+ * identifier. Used in the template's defaults block.
805
+ *
806
+ * ```
807
+ * {
808
+ * "defaults": {
809
+ * "change": { "output": "..." } ← this schema
810
+ * }
811
+ * }
812
+ * ```
939
813
  */
940
- var HTTPError = class extends Error {
941
- constructor(status, message) {
942
- super(`HTTP error! Status: ${status} - ${message}`);
943
- this.name = "HTTPError";
944
- }
945
- };
814
+ const xoTemplateOutputIntentSchema = xoTemplateIntentSchema.extend({ output: z.string().describe("The identifier for the intended output.") }).strict();
946
815
  /**
947
- * Error thrown when a plugin's unsubscribe function is not a function
816
+ * Validation schema for a locking script intent. Extends the base intent structure with
817
+ * a locking script identifier.
818
+ *
819
+ * @todo The location of this schema in the template JSON is not yet determined.
948
820
  */
949
- var SSESessionUnsubscribePluginNotAFunctionError = class extends Error {
950
- constructor(pluginName) {
951
- super(`${pluginName}'s unsubscribe function is not a function`);
952
- this.name = "SSESessionUnsubscribePluginNotAFunctionError";
953
- }
954
- };
955
-
956
- //#endregion
957
- //#region source/sse-session/sse-session.ts
821
+ const xoTemplateLockingScriptIntentSchema = xoTemplateIntentSchema.extend({ lockingScript: z.string().describe("The identifier for the intended locking script.") }).strict();
958
822
  /**
959
- * A fetch-based Server-Sent Events (SSE) client with reconnect and optional
960
- * browser tab visibility handling.
961
- *
962
- * Each session maintains one HTTP streaming connection at a time. Incoming
963
- * bytes are parsed into {@link SSEvent} objects and delivered through two
964
- * surfaces:
965
- *
966
- * - **Events** — `"connected"`, `"message"`, `"disconnected"`, `"error"`,
967
- * and `"closed"` on the session itself (extends {@link EventEmitter}).
968
- * - **Messages** — {@link messages}, an async iterable for `for await...of`
969
- * consumers.
970
- *
971
- * Typical usage:
972
- *
973
- * ```ts
974
- * const session = await SSESession.create("/events");
823
+ * Validation schema for the slot count configuration on a role requirement. Declares how many
824
+ * participants of a given role are needed. min sets the lower bound and max sets the upper bound.
825
+ * When max is absent, there is no upper limit.
975
826
  *
976
- * session.on("message", (event) => console.log(event.data));
827
+ * ```
828
+ * {
829
+ * "actions": {
830
+ * "[id]": {
831
+ * "requirements": {
832
+ * "participants": [
833
+ * { "slots": { "min": 1, "max": 1 } } ← this schema
834
+ * ]
835
+ * }
836
+ * }
837
+ * }
838
+ * }
839
+ * ```
840
+ */
841
+ const xoTemplateRoleSlotsRequirementsSchema = z.object({
842
+ min: z.number().describe("Minimum number of participants required for this role."),
843
+ max: z.number().optional().describe("Maximum number of participants allowed for this role. Undefined means unlimited.")
844
+ }).strict();
845
+ /**
846
+ * Validation schema for the capability requirements declared on a role within an action.
847
+ * Describes what data, secrets, or state the role is responsible for providing when participating in an action.
977
848
  *
978
- * for await (const event of session.messages) {
979
- * handle(event);
849
+ * ```
850
+ * {
851
+ * "actions": {
852
+ * "[id]": {
853
+ * "roles": {
854
+ * "[roleId]": {
855
+ * "requirements": { "variables": [], "secrets": [] } ← this schema
856
+ * }
857
+ * }
858
+ * }
859
+ * }
980
860
  * }
981
861
  * ```
862
+ */
863
+ const xoTemplateActionRoleRequirementsSchema = z.object({
864
+ variables: z.array(z.string()).optional().describe("List of variable identifiers required for this role."),
865
+ secrets: z.array(z.string()).optional().describe("List of secret identifiers required for this role.")
866
+ }).strict();
867
+ /**
868
+ * Validation schema for a role-specific definition within an action.
869
+ * All view properties are optional.
982
870
  *
983
- * ## Lifecycle
871
+ * ```
872
+ * {
873
+ * "actions": {
874
+ * "[id]": {
875
+ * "roles": {
876
+ * "[roleId]": { } ← this schema
877
+ * }
878
+ * }
879
+ * }
880
+ * }
881
+ * ```
882
+ */
883
+ const xoTemplateActionRoleSchema = xoTemplateViewPropertiesSchema.partial().extend({
884
+ generate: z.array(z.string()).optional().describe("Identifiers for data items that should be generated for this role when participating in the action."),
885
+ requirements: xoTemplateActionRoleRequirementsSchema.optional().describe("The requirements for this role within this action.")
886
+ }).strict();
887
+ /**
888
+ * Validation schema for a role participation requirement in an action's requirements block.
984
889
  *
985
- * - {@link connect} opens (or reopens) the transport. It resolves once the
986
- * HTTP stream is established; reading continues in the background.
987
- * - {@link disconnect} stops the in-flight fetch without ending the session.
988
- * Used internally for tab visibility. The {@link messages} iterator stays
989
- * open so an existing consumer resumes when the tab becomes visible again.
990
- * - {@link close} aborts the transport, closes {@link messages}, and emits
991
- * `"closed"`.
890
+ * ```
891
+ * {
892
+ * "actions": {
893
+ * "[id]": {
894
+ * "requirements": {
895
+ * "participants": [
896
+ * { "role": "...", "slots": { } } ← this schema
897
+ * ]
898
+ * }
899
+ * }
900
+ * }
901
+ * }
902
+ * ```
903
+ */
904
+ const xoTemplateRoleSlotSchema = z.object({
905
+ role: z.string().describe("The role identifier that this requirement applies to."),
906
+ slots: xoTemplateRoleSlotsRequirementsSchema.describe("Slot configuration specifying how many participants of this role are required.")
907
+ }).strict();
908
+ /**
909
+ * Validation schema for the requirements of an action.
992
910
  *
993
- * Automatic reconnect is controlled by {@link SSESessionOptions.persistent}
994
- * (server closed the stream) and
995
- * {@link SSESessionOptions.attemptReconnect} (transport error).
911
+ * ```
912
+ * {
913
+ * "actions": {
914
+ * "[id]": {
915
+ * "requirements": { "variables": [], "participants": [], "secrets": [] } ← this schema
916
+ * }
917
+ * }
918
+ * }
919
+ * ```
920
+ */
921
+ const xoTemplateActionRequirementsSchema = z.object({
922
+ variables: z.array(z.string()).optional().describe("List of variable identifiers required for this action."),
923
+ participants: z.array(xoTemplateRoleSlotSchema).optional().describe("The participants required for this action."),
924
+ secrets: z.array(z.string()).optional().describe("The secrets required for this action.")
925
+ }).strict();
926
+ /**
927
+ * Validation schema for an action definition.
996
928
  *
997
- * ## Connection supersession
929
+ * ```
930
+ * {
931
+ * "actions": {
932
+ * "[id]": { } ← this schema
933
+ * }
934
+ * }
935
+ * ```
936
+ */
937
+ const xoTemplateActionSchema = xoTemplateViewPropertiesSchema.extend({
938
+ roles: z.record(z.string(), xoTemplateActionRoleSchema).optional().describe("Specific context for each role participating in this action."),
939
+ requirements: xoTemplateActionRequirementsSchema.optional().describe("The requirements for this action."),
940
+ conditions: z.array(z.string()).optional().describe("Conditions that must be met for this action to be available."),
941
+ transaction: z.string().optional().describe("The identifier of the transaction this action produces, referencing an entry in the template's transactions."),
942
+ data: z.string().optional().describe("The identifier of the data field this action produces, referencing an entry in the template's data.")
943
+ }).strict();
944
+ /**
945
+ * Validation schema for the non-fungible token configuration within a token field.
998
946
  *
999
- * Each {@link connect} will create a new controller if one does not exist. Otherwise,
1000
- * it will return immediately.
947
+ * ```
948
+ * {
949
+ * "inputs|outputs": {
950
+ * "[id]": {
951
+ * "token": {
952
+ * "nft": { } ← this schema
953
+ * }
954
+ * }
955
+ * }
956
+ * }
957
+ * ```
1001
958
  */
1002
- var SSESession = class SSESession extends EventEmitter {
1003
- /**
1004
- * Creates a session and waits until the first connection is established.
1005
- *
1006
- * @param url - The SSE endpoint URL.
1007
- * @param options - Configuration merged with instance defaults.
1008
- * @returns A connected session.
1009
- * @throws When the initial connection cannot be established.
1010
- */
1011
- static async create(url, options = {}) {
1012
- const session = new SSESession(url, options);
1013
- await session.connect();
1014
- return session;
1015
- }
1016
- /**
1017
- * Enables SSE resume semantics by sending `Last-Event-ID` on reconnect.
1018
- *
1019
- * Listens for incoming `"message"` events and remembers the most recent
1020
- * {@link SSEvent.id}. On every subsequent connect or reconnect, the session's
1021
- * {@link onRequest} hook is wrapped so that header is attached when an id is
1022
- * known, allowing the server to replay only events the client has not yet
1023
- * received.
1024
- *
1025
- * The existing {@link onRequest} callback is preserved and runs after the
1026
- * header is applied, so auth or other header mutations continue to work.
1027
- *
1028
- * Since this relies on listening to the `"message"` event, this should ideally
1029
- * be called before "{@link connect} is called so no messages (and no ids) are missed.
1030
- *
1031
- * ```ts
1032
- * const session = new SSESession(url);
1033
- * SSESession.addLastEventIdReconnect(session);
1034
- * await session.connect();
1035
- * // Reconnects send Last-Event-ID once an event with an id is received.
1036
- * ```
1037
- *
1038
- * @param session - The session to instrument.
1039
- * @returns The same session, for chaining.
1040
- */
1041
- static addLastEventIdReconnect(session) {
1042
- if (session.plugins.has(SSESession.addLastEventIdReconnect)) {
1043
- const removeListener = session.plugins.get(SSESession.addLastEventIdReconnect);
1044
- if (typeof removeListener !== "function") throw new SSESessionUnsubscribePluginNotAFunctionError("addLastEventIdReconnect");
1045
- return {
1046
- session,
1047
- removeListener
1048
- };
1049
- }
1050
- let lastEventId;
1051
- const messageListener = (event) => {
1052
- lastEventId = event.id ?? lastEventId;
1053
- };
1054
- session.on("message", messageListener);
1055
- const originalOnRequest = session.options.onRequest;
1056
- let enabled = true;
1057
- session.options.onRequest = async (request) => {
1058
- if (lastEventId && enabled) request.headers = {
1059
- ...request.headers,
1060
- "Last-Event-ID": lastEventId
1061
- };
1062
- return originalOnRequest(request);
1063
- };
1064
- const removeListener = () => {
1065
- enabled = false;
1066
- session.off("message", messageListener);
1067
- if (session.plugins.get(SSESession.addLastEventIdReconnect) === removeListener) session.plugins.delete(SSESession.addLastEventIdReconnect);
1068
- };
1069
- session.plugins.set(SSESession.addLastEventIdReconnect, removeListener);
1070
- return {
1071
- session,
1072
- removeListener
1073
- };
1074
- }
1075
- /**
1076
- * Pauses and resumes a session based on browser tab visibility.
1077
- *
1078
- * Uses the Page Visibility API (`document.visibilitychange`):
1079
- *
1080
- * - **hidden** — {@link disconnect} stops the active fetch. {@link messages}
1081
- * stays open; `"disconnected"` fires but `"closed"` does not.
1082
- * - **visible** — {@link connect} re-establishes the stream if needed.
1083
- *
1084
- * This controller will not re-connect if the session was disconnected by something else.
1085
- *
1086
- * No-op in non-browser environments where `document` is undefined.
1087
- *
1088
- * @param session - The session to manage.
1089
- */
1090
- static addBrowserVisibilityHandler(session) {
1091
- if (typeof document === "undefined") return { session };
1092
- if (session.plugins.has(SSESession.addBrowserVisibilityHandler)) {
1093
- const removeListener = session.plugins.get(SSESession.addBrowserVisibilityHandler);
1094
- if (typeof removeListener !== "function") throw new SSESessionUnsubscribePluginNotAFunctionError("addBrowserVisibilityHandler");
1095
- return {
1096
- session,
1097
- removeListener
1098
- };
1099
- }
1100
- const reconnectOnVisible = async () => {
1101
- if (document.visibilityState === "visible") {
1102
- document.removeEventListener("visibilitychange", reconnectOnVisible);
1103
- await session.connect();
1104
- }
1105
- };
1106
- const disableOnExternalDisconnect = ({ source }) => {
1107
- if (source !== SSESession.addBrowserVisibilityHandler) document.removeEventListener("visibilitychange", reconnectOnVisible);
1108
- };
1109
- const handleVisibilityChange = async () => {
1110
- if (!session.active) return;
1111
- if (document.visibilityState === "hidden") {
1112
- document.addEventListener("visibilitychange", reconnectOnVisible);
1113
- session.disconnect({ source: SSESession.addBrowserVisibilityHandler });
1114
- }
1115
- };
1116
- document.addEventListener("visibilitychange", handleVisibilityChange);
1117
- session.on("beforeDisconnect", disableOnExternalDisconnect);
1118
- const removeListener = () => {
1119
- document.removeEventListener("visibilitychange", handleVisibilityChange);
1120
- document.removeEventListener("visibilitychange", reconnectOnVisible);
1121
- session.off("beforeDisconnect", disableOnExternalDisconnect);
1122
- if (session.plugins.get(SSESession.addBrowserVisibilityHandler) === removeListener) session.plugins.delete(SSESession.addBrowserVisibilityHandler);
1123
- };
1124
- session.plugins.set(SSESession.addBrowserVisibilityHandler, removeListener);
1125
- return {
1126
- session,
1127
- removeListener
1128
- };
1129
- }
1130
- /** SSE endpoint URL for this session. */
1131
- #url;
1132
- /**
1133
- * Per-instance configuration.
1134
- *
1135
- * Defaults live on the instance field (not a shared static) so each session
1136
- * gets its own {@link SSEEventParser} and {@link ExponentialBackoff}.
1137
- */
1138
- options = {
1139
- fetch: (...args) => fetch(...args),
1140
- method: "GET",
1141
- headers: {
1142
- Accept: "text/event-stream",
1143
- "Cache-Control": "no-cache"
1144
- },
1145
- body: new FormData(),
1146
- onRequest: (request) => Promise.resolve(request),
1147
- retry: new ExponentialBackoff({ ...SSE_SESSION_EXPONENTIAL_BACKOFF_DEFAULTS }),
1148
- attemptReconnect: SSE_SESSION_ATTEMPT_RECONNECT_DEFAULT,
1149
- persistent: SSE_SESSION_PERSISTENT_DEFAULT,
1150
- eventParser: new SSEEventParser()
1151
- };
1152
- /**
1153
- * Registered plugins for the session.
1154
- * This can be used to prevent duplicate plugins from being added to the session.
1155
- * The value for a plugin is arbitrary, for example a function that removes the plugin.
1156
- */
1157
- plugins = /* @__PURE__ */ new Map();
1158
- /** AbortController for the currently active fetch, if any. */
1159
- #connectionController = null;
1160
- /** The server's requested retry interval in milliseconds (per SSE spec) if any */
1161
- #retryInterval = null;
1162
- /**
1163
- * Asynchronous stream of parsed SSE events for the active connection.
1164
- *
1165
- * Stays open across {@link disconnect} and automatic reconnects so an existing
1166
- * `for await` consumer keeps receiving events after visibility resumes.
1167
- *
1168
- * Closes when:
1169
- * - the server ends the stream and {@link SSESessionOptions.persistent}
1170
- * is false,
1171
- * - {@link close} is called, or
1172
- * - a transport error occurs with
1173
- * {@link SSESessionOptions.attemptReconnect} disabled.
1174
- *
1175
- * A later {@link connect} replaces this with a new iterator when the
1176
- * previous one was closed. Consumers should read from `session.messages`
1177
- * rather than caching a reference across terminal disconnects.
1178
- */
1179
- messages = new AsyncPushIterator();
1180
- constructor(url, options = {}) {
1181
- super();
1182
- const { onConnected, onDisconnected, onError, ...restOptions } = options;
1183
- this.#url = url;
1184
- this.options = {
1185
- ...this.options,
1186
- ...restOptions,
1187
- headers: {
1188
- ...this.options.headers,
1189
- ...options.headers
1190
- }
1191
- };
1192
- if (onConnected) this.on("connected", onConnected);
1193
- if (onDisconnected) this.on("disconnected", onDisconnected);
1194
- if (onError) this.on("error", onError);
1195
- }
1196
- /**
1197
- * Returns true if the session is active by checking if the controller is not null.
1198
- */
1199
- get active() {
1200
- return this.#connectionController !== null;
1201
- }
1202
- /**
1203
- * Connects or reconnects to the SSE endpoint.
1204
- *
1205
- * Resolves once the HTTP stream is established and `"connected"` has been
1206
- * emitted. Body reading continues asynchronously in the background via the
1207
- * internal `#readStream` method.
1208
- *
1209
- * @throws When the fetch retry policy exhausts attempts or the connection
1210
- * is superseded before the reader is handed off (in the latter case the
1211
- * promise resolves without throwing).
1212
- */
1213
- async connect() {
1214
- if (this.#connectionController) return;
1215
- this.#resetEventParser();
1216
- this.#ensureMessageStreamOpen();
1217
- const connectionController = new AbortController();
1218
- this.#connectionController = connectionController;
1219
- const { method, headers, body } = this.options;
1220
- const fetchOptions = {
1221
- method,
1222
- headers: headers || {},
1223
- signal: connectionController.signal,
1224
- cache: "no-store"
1225
- };
1226
- if (method === "POST") fetchOptions.body = body || null;
1227
- let reader;
1228
- try {
1229
- reader = await this.options.retry.run(() => this.#createReader(fetchOptions), { signal: connectionController.signal });
1230
- } catch (error) {
1231
- if (this.#connectionController !== connectionController) return;
1232
- this.#connectionController = null;
1233
- const normalizedError = normalizeError(error);
1234
- this.emit("disconnected", {
1235
- reason: "error",
1236
- error: normalizedError
1237
- });
1238
- this.emit("error", normalizedError);
1239
- this.#closeMessageStream();
1240
- throw error;
1241
- }
1242
- if (this.#connectionController !== connectionController) {
1243
- await reader.cancel();
1244
- return;
1245
- }
1246
- this.emit("connected", void 0);
1247
- this.#readStream(reader, connectionController).catch((error) => {
1248
- this.emit("error", error);
1249
- });
1250
- }
1251
- /**
1252
- * Disconnects only the currently active transport.
1253
- *
1254
- * `beforeDisconnect` is emitted on every invocation, even when no
1255
- * transport is currently active. This allows observers to react to
1256
- * an explicit disconnect operation.
1257
- *
1258
- * @param context - Context describing the operation initiator.
1259
- * @emits `beforeDisconnect` with the supplied operation context.
1260
- * @emits `disconnected` with reason `"disconnect"` when an active
1261
- * transport is actually disconnected.
1262
- */
1263
- disconnect(context = {}) {
1264
- this.emit("beforeDisconnect", context);
1265
- if (this.#connectionController) {
1266
- const connectionController = this.#connectionController;
1267
- this.#connectionController = null;
1268
- connectionController.abort();
1269
- this.emit("disconnected", {
1270
- reason: "disconnect",
1271
- source: context.source
1272
- });
1273
- }
1274
- this.#resetEventParser();
1275
- }
1276
- /**
1277
- * Terminates the session and disables attached visibility handling until
1278
- * the same instance is manually {@link connect connected} again.
1279
- *
1280
- * Closes {@link messages} and emits `"closed"`.
1281
- */
1282
- close() {
1283
- this.disconnect();
1284
- this.#closeMessageStream();
1285
- this.emit("closed", void 0);
1286
- }
1287
- /**
1288
- * Performs the HTTP request and returns a reader for the response body.
1289
- *
1290
- * {@link SSESessionOptions.onRequest} may mutate headers (for example auth
1291
- * tokens or `Last-Event-ID`) before the fetch runs.
1292
- */
1293
- async #createReader(fetchOptions) {
1294
- const requestOptions = await this.options.onRequest(fetchOptions);
1295
- const response = await this.options.fetch(this.#url, requestOptions);
1296
- if (!response.ok) {
1297
- const responseCode = response.status;
1298
- const error = new HTTPError(responseCode, await response.text());
1299
- this.emit("error", error);
1300
- throw error;
1301
- }
1302
- if (!response.body) {
1303
- const error = new ResponseBodyNullError();
1304
- this.emit("error", error);
1305
- throw error;
1306
- }
1307
- return response.body.getReader();
1308
- }
1309
- /**
1310
- * Reads bytes from an established stream until it ends, errors, or is
1311
- * superseded by a newer connection.
1312
- */
1313
- async #readStream(reader, connectionController) {
1314
- try {
1315
- while (this.#connectionController === connectionController) {
1316
- const { done, value } = await reader.read();
1317
- if (this.#connectionController !== connectionController) return;
1318
- if (done) {
1319
- this.#connectionController = null;
1320
- this.emit("disconnected", { reason: "remote" });
1321
- if (this.options.persistent) await this.connect();
1322
- else this.#closeMessageStream();
1323
- return;
1324
- }
1325
- if (!value) continue;
1326
- for (const event of this.options.eventParser.parseEvents(value)) {
1327
- if (event.retry) this.#retryInterval = event.retry;
1328
- this.emit("message", event);
1329
- this.messages.push(event);
1330
- }
1331
- }
1332
- } catch (error) {
1333
- if (connectionController !== this.#connectionController) return;
1334
- this.#connectionController = null;
1335
- const normalizedError = normalizeError(error);
1336
- this.emit("disconnected", {
1337
- reason: "error",
1338
- error: normalizedError
1339
- });
1340
- if (connectionController.signal.aborted) return;
1341
- this.emit("error", normalizedError);
1342
- if (this.options.attemptReconnect) {
1343
- if (this.#retryInterval) await new Promise((resolve) => setTimeout(resolve, this.#retryInterval));
1344
- await this.connect();
1345
- } else this.#closeMessageStream();
1346
- }
1347
- }
1348
- /** Clears partial SSE frames left over from an abandoned transport. */
1349
- #resetEventParser() {
1350
- this.options.eventParser.reset();
1351
- }
1352
- /**
1353
- * Creates a new {@link messages} iterator when the previous one was closed
1354
- * by a terminal disconnect or server stream end.
1355
- */
1356
- #ensureMessageStreamOpen() {
1357
- if (!this.messages.closed) return;
1358
- this.messages = new AsyncPushIterator();
1359
- }
1360
- /** Ends the message iteration loop for the current connection span. */
1361
- #closeMessageStream() {
1362
- if (this.messages.closed) return;
1363
- this.messages.close();
1364
- }
1365
- };
1366
-
1367
- //#endregion
1368
- //#region source/template/errors.ts
959
+ const xoTemplateNonFungibleTokenDetailsSchema = z.object({
960
+ capability: z.union([xoTemplateNftCapabilitySchema, z.string()]).optional().describe("The capability of the NFT. May be a known capability value or a CashASM expression resolving to a capability."),
961
+ commitment: z.string().optional().describe("The commitment data for the NFT, as a string or CashASM expression resolving to a commitment.")
962
+ }).strict();
1369
963
  /**
1370
- * Formats the Zod validation failures into a single string with one line each: "- <field>: <message>" and top level failures
1371
- * with no field path show as "(root)" for better readability.
964
+ * Validation schema for the token configuration on inputs and outputs.
1372
965
  *
1373
- * @param issues The Zod validation failures to format.
1374
- * @returns A human readable error string for better debugging.
966
+ * ```
967
+ * {
968
+ * "inputs|outputs": {
969
+ * "[id]": {
970
+ * "token": { } ← this schema
971
+ * }
972
+ * }
973
+ * }
974
+ * ```
1375
975
  */
1376
- const buildErrorDescription = (issues) => {
1377
- const lines = [];
1378
- for (const issue of issues) {
1379
- const issuePath = issue.path.length > 0 ? issue.path.join(".") : "(root)";
1380
- const issueMessage = issue.message.startsWith("Invalid input: ") ? issue.message.slice(15) : issue.message;
1381
- lines.push(`- ${issuePath}: ${issueMessage}`);
1382
- }
1383
- return `\n${lines.join("\n")}`;
1384
- };
976
+ const xoTemplateTokenSchema = z.object({
977
+ category: z.string().optional().describe("The category of the token, as a string or CashASM expression resolving to a category."),
978
+ amount: z.union([
979
+ z.bigint(),
980
+ z.string(),
981
+ z.null()
982
+ ]).optional().describe("The amount of fungible tokens as a bigint, a CashASM expression resolving to a bigint, or null indicating no FT is present."),
983
+ nft: xoTemplateNonFungibleTokenDetailsSchema.nullable().optional().describe("Non-fungible token configuration. Null indicates no NFT is present.")
984
+ }).strict();
1385
985
  /**
1386
- * Thrown when the provided template does not satisfy the XOTemplate schema.
986
+ * Validation schema for the asset amounts configuration. Used by omitChangeAmounts on inputs
987
+ * and by balance on locking scripts, outputs, and their roles.
1387
988
  */
1388
- var TemplateInvalidError = class extends Error {
1389
- constructor(details) {
1390
- const message = `Template invalid: ${details}`;
1391
- super(message);
1392
- this.name = "TemplateInvalidError";
1393
- }
1394
- };
989
+ const xoTemplateAssetAmountsSchema = z.object({
990
+ satoshis: z.union([
991
+ satoshisSchema,
992
+ z.string(),
993
+ z.literal(true)
994
+ ]).optional().describe("The satoshi amount. Accepts a bigint for a specific value, a CashASM expression, or true for the entire amount."),
995
+ fungibleTokens: z.union([
996
+ z.bigint(),
997
+ z.string(),
998
+ z.literal(true)
999
+ ]).optional().describe("The fungible token amount. Accepts a bigint for a specific value, a CashASM expression, or true for the entire amount."),
1000
+ nonfungibleTokens: z.union([
1001
+ z.literal(0),
1002
+ z.literal(1),
1003
+ z.string(),
1004
+ z.literal(true)
1005
+ ]).optional().describe("Whether an NFT is present: 0 for absent, 1 for present, a CashASM expression evaluating to 0 or 1, or true to estimate the NFT as part of the balance if present, or absent from it if not.")
1006
+ }).strict();
1395
1007
  /**
1396
- * Thrown when a string passed to `deserializeTemplate` cannot be parsed as JSON.
1397
- */
1398
- var TemplateJsonMalformedError = class extends Error {
1399
- constructor(reason) {
1400
- super(`Template JSON malformed, expected a valid JSON string: ${reason}`);
1401
- this.name = "TemplateJsonMalformedError";
1402
- }
1403
- };
1404
- /**
1405
- * Thrown when `serializeTemplate` fails to produce a JSON string from the template.
1406
- */
1407
- var TemplateSerializationFailedError = class extends Error {
1408
- constructor(reason) {
1409
- super(`Template serialization failed: ${reason}`);
1410
- this.name = "TemplateSerializationFailedError";
1411
- }
1412
- };
1413
-
1414
- //#endregion
1415
- //#region source/template/serialization.ts
1416
- /**
1417
- * Serializes an XOTemplate to a JSON string. Encodes `bigint` and `Uint8Array` fields in
1418
- * Extended JSON format so they can be reconstructed by `deserializeTemplate`.
1419
- *
1420
- * @param template The template to serialize.
1421
- * @returns A JSON string representation of the template.
1422
- * @throws {TemplateSerializationFailedError} If the template cannot be serialized to JSON.
1423
- */
1424
- const serializeTemplate = (template) => {
1425
- try {
1426
- return JSON.stringify(template, extendedJsonReplacer);
1427
- } catch (serializationError) {
1428
- throw new TemplateSerializationFailedError(serializationError instanceof Error ? serializationError.message : "unknown error while serializing template");
1429
- }
1430
- };
1431
- /**
1432
- * Deserializes a JSON string back into an XOTemplate object. Restores `bigint` and
1433
- * `Uint8Array` fields encoded in Extended JSON format by `serializeTemplate`.
1434
- *
1435
- * @param serializedTemplate - A JSON string of an XOTemplate object.
1436
- * @returns The reconstructed XOTemplate object.
1437
- * @throws {TemplateJsonMalformedError} If the serialized template is not valid JSON.
1438
- */
1439
- const deserializeTemplate = (serializedTemplate) => {
1440
- try {
1441
- return JSON.parse(serializedTemplate, extendedJsonReviver);
1442
- } catch (parsingError) {
1443
- throw new TemplateJsonMalformedError(parsingError instanceof Error ? parsingError.message : "unknown error while deserializing template");
1444
- }
1445
- };
1446
-
1447
- //#endregion
1448
- //#region source/template/identifier.ts
1449
- /**
1450
- * Generates a deterministic template identifier by hashing the template.
1451
- *
1452
- * Note: This expects a template that has been validated by `parseTemplate`.
1453
- *
1454
- * @param template - The template to generate an identifier for.
1455
- * @returns The sha256 hex identifier for the template.
1456
- */
1457
- const generateTemplateIdentifier = (template) => {
1458
- const serializedTemplate = serializeTemplate(template);
1459
- return binToHex(sha256.hash(utf8ToBin(serializedTemplate)));
1460
- };
1461
-
1462
- //#endregion
1463
- //#region source/template/schemas.ts
1464
- /**
1465
- * Validation schema for a BCH VM version identifier. Defines the set of known BCH VM versions
1466
- * that XO templates declare support for.
1467
- *
1468
- * Uses `BchVmVersions` from `@xo-cash/types` so template validation stays aligned with the
1469
- * `BchVmVersion` type.
1470
- *
1471
- * ```
1472
- * {
1473
- * "supported": [ "BCH_2025_05" ] ← each value
1474
- * }
1475
- * ```
1476
- */
1477
- const bchVmVersionSchema = z.enum(BchVmVersions);
1478
- /**
1479
- * Validation schema for the capability of a non-fungible token. Defines the three capability
1480
- * types supported on BCH: minting tokens can create new NFTs, mutable tokens can update their
1481
- * commitment, and none tokens cannot be changed after creation.
1008
+ * Validation schema for the state configuration shared by a locking script and its individual roles.
1009
+ * Declares which variables and secrets are tracked in the on-chain state for a given participant.
1482
1010
  *
1483
1011
  * ```
1484
1012
  * {
1485
- * "inputs|outputs": {
1013
+ * "lockingScripts": {
1486
1014
  * "[id]": {
1487
- * "token": {
1488
- * "nft": {
1489
- * "capability": "minting" ← this schema
1015
+ * "state": { "variables": [], "secrets": [] } ← this schema
1016
+ * "roles": {
1017
+ * "[roleId]": {
1018
+ * "state": { "variables": [], "secrets": [] } ← this schema
1490
1019
  * }
1491
1020
  * }
1492
1021
  * }
@@ -1494,609 +1023,1356 @@ const bchVmVersionSchema = z.enum(BchVmVersions);
1494
1023
  * }
1495
1024
  * ```
1496
1025
  */
1497
- const xoTemplateNftCapabilitySchema = z.enum(XOTemplateNftCapabilities);
1026
+ const xoTemplateStateSchema = z.object({
1027
+ variables: z.array(z.string()).optional().describe("List of variable identifiers to track in state."),
1028
+ secrets: z.array(z.string()).optional().describe("List of secret identifiers to track in state.")
1029
+ }).strict();
1498
1030
  /**
1499
- * Validation schema for a BCH locking script type. Defines the standard locking script types
1500
- * supported on BCH.
1031
+ * Validation schema for a role definition for a locking script.
1501
1032
  *
1502
1033
  * ```
1503
1034
  * {
1504
1035
  * "lockingScripts": {
1505
1036
  * "[id]": {
1506
- * "lockingType": "p2pkh" ← this schema
1037
+ * "roles": {
1038
+ * "[roleId]": { } ← this schema
1039
+ * }
1507
1040
  * }
1508
1041
  * }
1509
1042
  * }
1510
1043
  * ```
1511
1044
  */
1512
- const xoTemplateLockingTypeSchema = z.enum(XOTemplateLockingTypes);
1513
- /**
1514
- * Validation schema for a primitive type identifier.
1515
- * Accepts values from XOTemplatePrimitiveTypes for the `hint` field on constants, variables, and data.
1516
- */
1517
- const xoTemplatePrimitiveTypeSchema = z.enum(XOTemplatePrimitiveTypes);
1518
- /**
1519
- * Validation schema for byte array fields i.e. Uint8Array instance.
1520
- */
1521
- const uint8ArraySchema = z.instanceof(Uint8Array).describe("A sequence of unsigned 8-bit integers expressed as a Uint8Array.");
1522
- /**
1523
- * Validation schema for the Satoshis type i.e. bigint.
1524
- */
1525
- const satoshisSchema = z.bigint().describe("A satoshi amount expressed as a bigint.");
1526
- /** Maximum character length for name fields on view properties. */
1527
- const VIEW_PROPERTIES_NAME_MAX_LENGTH = 1e3;
1528
- /** Maximum character length for description fields on view properties. */
1529
- const VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH = 5e3;
1530
- /** Maximum character length for icon fields on view properties. */
1531
- const VIEW_PROPERTIES_ICON_MAX_LENGTH = 1e3;
1532
- /**
1533
- * Validation schema for view properties shared across many template elements i.e. name, description, icon.
1534
- * Extended by most other schemas in this file.
1535
- */
1536
- const xoTemplateViewPropertiesSchema = z.object({
1537
- name: z.string().max(VIEW_PROPERTIES_NAME_MAX_LENGTH).describe("A short human-readable label for this element."),
1538
- description: z.string().max(VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH).describe("A human-readable explanation of what this element does and when it is relevant."),
1539
- icon: z.string().max(VIEW_PROPERTIES_ICON_MAX_LENGTH).optional().describe("An optional icon identifier or URL for this element.")
1540
- }).strict();
1541
- /**
1542
- * Validation schema for the base intent structure. Describes the common data parameters shared
1543
- * by all intent types regardless of what they target.
1544
- *
1545
- * An optional templateIdentifier allows the intent to reference a target defined in a different
1546
- * template, enabling cross-template interaction.
1547
- *
1548
- * Extended by: xoTemplateActionIntentSchema, xoTemplateOutputIntentSchema,
1549
- * xoTemplateLockingScriptIntentSchema.
1550
- */
1551
- const xoTemplateIntentSchema = z.object({
1552
- templateIdentifier: z.string().optional().describe("Optional identifier for the template used in this intent. If not provided, uses the current template."),
1553
- role: z.string().optional().describe("Optional identifier for the role used in this intent."),
1554
- generate: z.array(z.string()).optional().describe("Identifiers for items to generate when this intent is resolved, e.g. keys or secrets."),
1555
- variables: z.array(z.record(z.string(), z.unknown())).optional().describe("Variable values to apply when this intent is resolved."),
1556
- constants: z.array(z.record(z.string(), z.unknown())).optional().describe("Constant values to apply when this intent is resolved."),
1557
- secrets: z.array(z.record(z.string(), z.unknown())).optional().describe("Secret values to apply when this intent is resolved.")
1045
+ const xoTemplateLockingScriptRoleSchema = xoTemplateViewPropertiesSchema.partial().extend({
1046
+ state: xoTemplateStateSchema.optional().describe("List of items to track as state for this role."),
1047
+ actions: z.array(xoTemplateActionIntentSchema).optional().describe("List of action references available to this role."),
1048
+ balance: xoTemplateAssetAmountsSchema.partial().optional().describe("Estimated ownership in the optional set of asset amounts specified."),
1049
+ selectable: z.boolean().optional().describe("Whether outputs locked to this script should be available for coin selection."),
1050
+ relevant: z.union([z.boolean(), z.string()]).optional().describe("Whether this output or locking script should be tracked by the engine, When omitted, the engine treats this field as true.Accepts true, false, or a CashASM expression that evaluates to a boolean relevance value."),
1051
+ privacy: z.union([z.number(), z.string()]).optional().describe("Privacy level for outputs locked to this script. A numeric level or a CashASM expression that evaluates to a privacy level.")
1558
1052
  }).strict();
1559
1053
  /**
1560
- * Validation schema for an action intent. Extends the base intent structure with an action
1561
- * identifier. Used in locking script action lists and in the template's start array.
1054
+ * Validation schema for a locking script definition.
1562
1055
  *
1563
1056
  * ```
1564
1057
  * {
1565
- * "start": [
1566
- * { "action": "..." } ← this schema
1567
- * ],
1568
1058
  * "lockingScripts": {
1569
- * "[id]": {
1570
- * "actions": [
1571
- * { "action": "..." } ← this schema
1572
- * ],
1573
- * "roles": {
1574
- * "[roleId]": {
1575
- * "actions": [
1576
- * { "action": "..." } ← this schema
1577
- * ]
1578
- * }
1579
- * }
1580
- * }
1059
+ * "[id]": { } ← this schema
1581
1060
  * }
1582
1061
  * }
1583
1062
  * ```
1584
1063
  */
1585
- const xoTemplateActionIntentSchema = xoTemplateIntentSchema.extend({ action: z.string().describe("The identifier for the intended action.") }).strict();
1064
+ const xoTemplateLockingScriptSchema = xoTemplateViewPropertiesSchema.extend({
1065
+ lockingType: xoTemplateLockingTypeSchema.optional().describe("The type of locking mechanism. Defaults to p2s if not specified."),
1066
+ lockingBytecode: z.string().describe("The locking script bytecode."),
1067
+ unlockingBytecode: z.string().optional().describe("Optional default unlocking bytecode when used in automatic coin selection."),
1068
+ actions: z.array(xoTemplateActionIntentSchema).optional().describe("The actions available for this locking script."),
1069
+ state: xoTemplateStateSchema.optional().describe("List of items to track as state for all participants."),
1070
+ balance: xoTemplateAssetAmountsSchema.partial().optional().describe("Estimated ownership in the optional set of asset amounts specified."),
1071
+ selectable: z.boolean().optional().describe("Whether outputs locked to this script should be available for coin selection."),
1072
+ privacy: z.union([z.number(), z.string()]).optional().describe("Privacy level for outputs locked to this script. A numeric level or a CashASM expression that evaluates to a privacy level."),
1073
+ roles: z.record(z.string(), xoTemplateLockingScriptRoleSchema).optional().describe("Specific context for each role participating in this locking script.")
1074
+ }).strict();
1586
1075
  /**
1587
- * Validation schema for an output intent. Extends the base intent structure with an output
1588
- * identifier. Used in the template's defaults block.
1076
+ * Validation schema for an input definition in the template. Extends view properties with optional
1077
+ * satoshi value, token configuration, and other transaction level fields.
1589
1078
  *
1590
1079
  * ```
1591
1080
  * {
1592
- * "defaults": {
1593
- * "change": { "output": "..." } ← this schema
1081
+ * "inputs": {
1082
+ * "[id]": { } ← this schema
1594
1083
  * }
1595
1084
  * }
1596
1085
  * ```
1597
1086
  */
1598
- const xoTemplateOutputIntentSchema = xoTemplateIntentSchema.extend({ output: z.string().describe("The identifier for the intended output.") }).strict();
1599
- /**
1600
- * Validation schema for a locking script intent. Extends the base intent structure with
1601
- * a locking script identifier.
1602
- *
1603
- * @todo The location of this schema in the template JSON is not yet determined.
1604
- */
1605
- const xoTemplateLockingScriptIntentSchema = xoTemplateIntentSchema.extend({ lockingScript: z.string().describe("The identifier for the intended locking script.") }).strict();
1087
+ const xoTemplateInputSchema = xoTemplateViewPropertiesSchema.extend({
1088
+ valueSatoshis: z.union([satoshisSchema, z.string()]).optional().describe("The amount of satoshis for this input as a bigint or a CashASM expression resolving to the amount."),
1089
+ token: xoTemplateTokenSchema.nullable().optional().describe("Token configuration for this input."),
1090
+ sequenceNumber: z.union([z.number(), z.string()]).optional().describe("The sequence number of this input as a specific number or a CashASM expression."),
1091
+ unlockingScript: z.string().optional().describe("Identifier of the unlocking script to use for the UTXO provided for this input."),
1092
+ omitChangeAmounts: xoTemplateAssetAmountsSchema.optional().describe("Amount of change that should be omitted from the automatic change handling. WARNING: Setting this can result in loss of funds!")
1093
+ }).strict();
1606
1094
  /**
1607
- * Validation schema for the slot count configuration on a role requirement. Declares how many
1608
- * participants of a given role are needed. min sets the lower bound and max sets the upper bound.
1609
- * When max is absent, there is no upper limit.
1095
+ * Validation schema for an output definition. Extends the locking script schema so that
1096
+ * every output inherits the same locking script fields and adds output-specific fields.
1610
1097
  *
1611
1098
  * ```
1612
1099
  * {
1613
- * "actions": {
1614
- * "[id]": {
1615
- * "requirements": {
1616
- * "participants": [
1617
- * { "slots": { "min": 1, "max": 1 } } ← this schema
1618
- * ]
1619
- * }
1620
- * }
1100
+ * "outputs": {
1101
+ * "[id]": { } ← this schema
1621
1102
  * }
1622
1103
  * }
1623
1104
  * ```
1624
1105
  */
1625
- const xoTemplateRoleSlotsRequirementsSchema = z.object({
1626
- min: z.number().describe("Minimum number of participants required for this role."),
1627
- max: z.number().optional().describe("Maximum number of participants allowed for this role. Undefined means unlimited.")
1106
+ const xoTemplateOutputSchema = xoTemplateLockingScriptSchema.omit({
1107
+ lockingType: true,
1108
+ lockingBytecode: true,
1109
+ unlockingBytecode: true
1110
+ }).extend({
1111
+ lockingScript: z.string().describe("Identifier of the locking script to use for this output."),
1112
+ valueSatoshis: z.union([satoshisSchema, z.string()]).optional().describe("The amount of satoshis for this output as a bigint or a CashASM expression resolving to the amount."),
1113
+ token: xoTemplateTokenSchema.nullable().optional().describe("Token configuration for this output.")
1628
1114
  }).strict();
1629
1115
  /**
1630
- * Validation schema for the capability requirements declared on a role within an action.
1631
- * Describes what data, secrets, or state the role is responsible for providing when participating in an action.
1116
+ * Validation schema for a transaction input reference for a transaction definition.
1632
1117
  *
1633
1118
  * ```
1634
1119
  * {
1635
- * "actions": {
1120
+ * "transactions": {
1636
1121
  * "[id]": {
1637
- * "roles": {
1638
- * "[roleId]": {
1639
- * "requirements": { "variables": [], "secrets": [] } ← this schema
1640
- * }
1641
- * }
1122
+ * "inputs": [
1123
+ * { "input": "..." } ← this schema
1124
+ * ]
1642
1125
  * }
1643
1126
  * }
1644
1127
  * }
1645
1128
  * ```
1646
1129
  */
1647
- const xoTemplateActionRoleRequirementsSchema = z.object({
1648
- variables: z.array(z.string()).optional().describe("List of variable identifiers required for this role."),
1649
- secrets: z.array(z.string()).optional().describe("List of secret identifiers required for this role.")
1130
+ const xoTemplateTransactionInputSchema = z.object({
1131
+ input: z.string().describe("The input definition identifier."),
1132
+ inputIndex: z.number().optional().describe("Optional index of this input in the transaction.")
1650
1133
  }).strict();
1651
1134
  /**
1652
- * Validation schema for a role-specific definition within an action.
1653
- * All view properties are optional.
1135
+ * Validation schema for a transaction output reference for a transaction definition.
1654
1136
  *
1655
1137
  * ```
1656
1138
  * {
1657
- * "actions": {
1139
+ * "transactions": {
1658
1140
  * "[id]": {
1659
- * "roles": {
1660
- * "[roleId]": { } ← this schema
1661
- * }
1141
+ * "outputs": [
1142
+ * { "output": "..." } ← this schema
1143
+ * ]
1662
1144
  * }
1663
1145
  * }
1664
1146
  * }
1665
1147
  * ```
1666
1148
  */
1667
- const xoTemplateActionRoleSchema = xoTemplateViewPropertiesSchema.partial().extend({
1668
- generate: z.array(z.string()).optional().describe("Identifiers for data items that should be generated for this role when participating in the action."),
1669
- requirements: xoTemplateActionRoleRequirementsSchema.optional().describe("The requirements for this role within this action.")
1149
+ const xoTemplateTransactionOutputSchema = z.object({
1150
+ output: z.string().describe("The output definition identifier."),
1151
+ outputIndex: z.number().optional().describe("Optional index of this output in the transaction.")
1670
1152
  }).strict();
1671
1153
  /**
1672
- * Validation schema for a role participation requirement in an action's requirements block.
1154
+ * Validation schema for role-specific data for a transaction definition.
1673
1155
  *
1674
1156
  * ```
1675
1157
  * {
1676
- * "actions": {
1158
+ * "transactions": {
1677
1159
  * "[id]": {
1678
- * "requirements": {
1679
- * "participants": [
1680
- * { "role": "...", "slots": { } } ← this schema
1681
- * ]
1160
+ * "roles": {
1161
+ * "[roleId]": { } ← this schema
1682
1162
  * }
1683
1163
  * }
1684
1164
  * }
1685
1165
  * }
1686
1166
  * ```
1687
1167
  */
1688
- const xoTemplateRoleSlotSchema = z.object({
1689
- role: z.string().describe("The role identifier that this requirement applies to."),
1690
- slots: xoTemplateRoleSlotsRequirementsSchema.describe("Slot configuration specifying how many participants of this role are required.")
1168
+ const xoTemplateTransactionRoleDataSchema = xoTemplateViewPropertiesSchema.partial().extend({
1169
+ inputs: z.array(xoTemplateTransactionInputSchema).optional().describe("The inputs required for this role."),
1170
+ outputs: z.array(xoTemplateTransactionOutputSchema).optional().describe("The outputs required for this role.")
1691
1171
  }).strict();
1692
1172
  /**
1693
- * Validation schema for the requirements of an action.
1173
+ * Validation schema for a transaction template definition.
1694
1174
  *
1695
1175
  * ```
1696
1176
  * {
1697
- * "actions": {
1698
- * "[id]": {
1699
- * "requirements": { "variables": [], "participants": [], "secrets": [] } ← this schema
1700
- * }
1177
+ * "transactions": {
1178
+ * "[id]": { } ← this schema
1701
1179
  * }
1702
1180
  * }
1703
1181
  * ```
1704
1182
  */
1705
- const xoTemplateActionRequirementsSchema = z.object({
1706
- variables: z.array(z.string()).optional().describe("List of variable identifiers required for this action."),
1707
- participants: z.array(xoTemplateRoleSlotSchema).optional().describe("The participants required for this action."),
1708
- secrets: z.array(z.string()).optional().describe("The secrets required for this action.")
1183
+ const xoTemplateTransactionSchema = xoTemplateViewPropertiesSchema.extend({
1184
+ version: z.number().optional().describe("The version of the transaction."),
1185
+ locktime: z.union([z.number(), z.string()]).optional().describe("The locktime for this transaction as a specific number or a CashASM expression."),
1186
+ inputs: z.array(xoTemplateTransactionInputSchema).describe("The inputs for this transaction."),
1187
+ outputs: z.array(xoTemplateTransactionOutputSchema).describe("The outputs for this transaction."),
1188
+ roles: z.record(z.string(), xoTemplateTransactionRoleDataSchema).optional().describe("Specific context for each role participating in this transaction."),
1189
+ composable: z.boolean().optional().describe("Whether this transaction can be composed with other transactions.")
1709
1190
  }).strict();
1710
1191
  /**
1711
- * Validation schema for an action definition.
1192
+ * Validation schema for a constant value definition.
1712
1193
  *
1713
1194
  * ```
1714
1195
  * {
1715
- * "actions": {
1196
+ * "constants": {
1716
1197
  * "[id]": { } ← this schema
1717
1198
  * }
1718
1199
  * }
1719
1200
  * ```
1720
1201
  */
1721
- const xoTemplateActionSchema = xoTemplateViewPropertiesSchema.extend({
1722
- roles: z.record(z.string(), xoTemplateActionRoleSchema).optional().describe("Specific context for each role participating in this action."),
1723
- requirements: xoTemplateActionRequirementsSchema.optional().describe("The requirements for this action."),
1724
- conditions: z.array(z.string()).optional().describe("Conditions that must be met for this action to be available."),
1725
- transaction: z.string().optional().describe("The identifier of the transaction this action produces, referencing an entry in the template's transactions."),
1726
- data: z.string().optional().describe("The identifier of the data field this action produces, referencing an entry in the template's data.")
1202
+ const xoTemplateConstantSchema = xoTemplateViewPropertiesSchema.extend({
1203
+ type: xoTemplatePrimitiveTypeSchema.describe("The data type of this constant."),
1204
+ value: z.unknown().describe("The value of this constant."),
1205
+ hint: z.unknown().optional().describe("An optional hint to help apps and users understand what this constant represents.")
1727
1206
  }).strict();
1728
1207
  /**
1729
- * Validation schema for the non-fungible token configuration within a token field.
1208
+ * Validation schema for a data field definition.
1730
1209
  *
1731
1210
  * ```
1732
1211
  * {
1733
- * "inputs|outputs": {
1734
- * "[id]": {
1735
- * "token": {
1736
- * "nft": { } ← this schema
1737
- * }
1738
- * }
1212
+ * "data": {
1213
+ * "[id]": { } ← this schema
1739
1214
  * }
1740
1215
  * }
1741
1216
  * ```
1742
1217
  */
1743
- const xoTemplateNonFungibleTokenDetailsSchema = z.object({
1744
- capability: z.union([xoTemplateNftCapabilitySchema, z.string()]).optional().describe("The capability of the NFT. May be a known capability value or a CashASM expression resolving to a capability."),
1745
- commitment: z.string().optional().describe("The commitment data for the NFT, as a string or CashASM expression resolving to a commitment.")
1218
+ const xoTemplateDataSchema = z.object({
1219
+ type: xoTemplatePrimitiveTypeSchema.describe("The data type of this data field."),
1220
+ value: z.unknown().describe("The value for this data field."),
1221
+ hint: z.unknown().optional().describe("An optional hint to help apps and users understand this data field.")
1746
1222
  }).strict();
1747
1223
  /**
1748
- * Validation schema for the token configuration on inputs and outputs.
1224
+ * Validation schema for an import default value intent. Extends the base intent with optional
1225
+ * view properties that the engine evaluates at runtime to produce human-readable output.
1749
1226
  *
1750
1227
  * ```
1751
1228
  * {
1752
- * "inputs|outputs": {
1229
+ * "variables": {
1753
1230
  * "[id]": {
1754
- * "token": { } ← this schema
1231
+ * "importDefaultValue": { } ← this schema
1755
1232
  * }
1756
1233
  * }
1757
1234
  * }
1758
1235
  * ```
1759
1236
  */
1760
- const xoTemplateTokenSchema = z.object({
1761
- category: z.string().optional().describe("The category of the token, as a string or CashASM expression resolving to a category."),
1762
- amount: z.union([
1763
- z.bigint(),
1764
- z.string(),
1765
- z.null()
1766
- ]).optional().describe("The amount of fungible tokens as a bigint, a CashASM expression resolving to a bigint, or null indicating no FT is present."),
1767
- nft: xoTemplateNonFungibleTokenDetailsSchema.nullable().optional().describe("Non-fungible token configuration. Null indicates no NFT is present.")
1768
- }).strict();
1769
- /**
1770
- * Validation schema for the asset amounts configuration. Used by omitChangeAmounts on inputs
1771
- * and by balance on locking scripts, outputs, and their roles.
1772
- */
1773
- const xoTemplateAssetAmountsSchema = z.object({
1774
- satoshis: z.union([
1775
- satoshisSchema,
1776
- z.string(),
1777
- z.literal(true)
1778
- ]).optional().describe("The satoshi amount. Accepts a bigint for a specific value, a CashASM expression, or true for the entire amount."),
1779
- fungibleTokens: z.union([
1780
- z.bigint(),
1781
- z.string(),
1782
- z.literal(true)
1783
- ]).optional().describe("The fungible token amount. Accepts a bigint for a specific value, a CashASM expression, or true for the entire amount."),
1784
- nonfungibleTokens: z.union([
1785
- z.literal(0),
1786
- z.literal(1),
1787
- z.string(),
1788
- z.literal(true)
1789
- ]).optional().describe("Whether an NFT is present: 0 for absent, 1 for present, a CashASM expression evaluating to 0 or 1, or true to estimate the NFT as part of the balance if present, or absent from it if not.")
1790
- }).strict();
1237
+ const xoTemplateImportDefaultValueSchema = xoTemplateIntentSchema.extend(xoTemplateViewPropertiesSchema.partial().shape).strict();
1791
1238
  /**
1792
- * Validation schema for the state configuration shared by a locking script and its individual roles.
1793
- * Declares which variables and secrets are tracked in the on-chain state for a given participant.
1239
+ * Validation schema for a variable definition.
1794
1240
  *
1795
1241
  * ```
1796
1242
  * {
1797
- * "lockingScripts": {
1798
- * "[id]": {
1799
- * "state": { "variables": [], "secrets": [] } ← this schema
1800
- * "roles": {
1801
- * "[roleId]": {
1802
- * "state": { "variables": [], "secrets": [] } ← this schema
1803
- * }
1804
- * }
1805
- * }
1243
+ * "variables": {
1244
+ * "[id]": { } ← this schema
1806
1245
  * }
1807
1246
  * }
1808
1247
  * ```
1809
1248
  */
1810
- const xoTemplateStateSchema = z.object({
1811
- variables: z.array(z.string()).optional().describe("List of variable identifiers to track in state."),
1812
- secrets: z.array(z.string()).optional().describe("List of secret identifiers to track in state.")
1249
+ const xoTemplateVariableSchema = xoTemplateViewPropertiesSchema.extend({
1250
+ type: xoTemplatePrimitiveTypeSchema.optional().describe("The data type of this variable."),
1251
+ hint: z.unknown().optional().describe("A hint to help users understand what value to provide."),
1252
+ importDefaultValue: xoTemplateImportDefaultValueSchema.optional().describe("A neutral intent that the engine uses to populate the default value for this variable.")
1813
1253
  }).strict();
1814
1254
  /**
1815
- * Validation schema for a role definition for a locking script.
1255
+ * Validation schema for a resource reference attached to a template element. Extends view
1256
+ * properties with a URL pointing to external documentation or tooling.
1816
1257
  *
1817
1258
  * ```
1818
1259
  * {
1819
- * "lockingScripts": {
1820
- * "[id]": {
1821
- * "roles": {
1822
- * "[roleId]": { } ← this schema
1823
- * }
1824
- * }
1825
- * }
1260
+ * "resources": [
1261
+ * { "name": "...", "description": "...", "url": "..." } ← this schema
1262
+ * ]
1826
1263
  * }
1827
1264
  * ```
1828
1265
  */
1829
- const xoTemplateLockingScriptRoleSchema = xoTemplateViewPropertiesSchema.partial().extend({
1830
- state: xoTemplateStateSchema.optional().describe("List of items to track as state for this role."),
1831
- actions: z.array(xoTemplateActionIntentSchema).optional().describe("List of action references available to this role."),
1832
- balance: xoTemplateAssetAmountsSchema.partial().optional().describe("Estimated ownership in the optional set of asset amounts specified."),
1833
- selectable: z.boolean().optional().describe("Whether outputs locked to this script should be available for coin selection."),
1834
- relevant: z.union([z.boolean(), z.string()]).optional().describe("Whether this output or locking script should be tracked by the engine, When omitted, the engine treats this field as true.Accepts true, false, or a CashASM expression that evaluates to a boolean relevance value."),
1835
- privacy: z.union([z.number(), z.string()]).optional().describe("Privacy level for outputs locked to this script. A numeric level or a CashASM expression that evaluates to a privacy level.")
1836
- }).strict();
1266
+ const xoTemplateResourceSchema = xoTemplateViewPropertiesSchema.extend({ url: z.string().describe("The URL for this resource.") }).strict();
1837
1267
  /**
1838
- * Validation schema for a locking script definition.
1268
+ * Validation schema for an icon reference.
1839
1269
  *
1840
1270
  * ```
1841
1271
  * {
1842
- * "lockingScripts": {
1843
- * "[id]": { } ← this schema
1844
- * }
1272
+ * "icons": [
1273
+ * { "name": "...", "hash": "..." } ← this schema
1274
+ * ]
1845
1275
  * }
1846
1276
  * ```
1847
1277
  */
1848
- const xoTemplateLockingScriptSchema = xoTemplateViewPropertiesSchema.extend({
1849
- lockingType: xoTemplateLockingTypeSchema.optional().describe("The type of locking mechanism. Defaults to p2s if not specified."),
1850
- lockingBytecode: z.string().describe("The locking script bytecode."),
1851
- unlockingBytecode: z.string().optional().describe("Optional default unlocking bytecode when used in automatic coin selection."),
1852
- actions: z.array(xoTemplateActionIntentSchema).optional().describe("The actions available for this locking script."),
1853
- state: xoTemplateStateSchema.optional().describe("List of items to track as state for all participants."),
1854
- balance: xoTemplateAssetAmountsSchema.partial().optional().describe("Estimated ownership in the optional set of asset amounts specified."),
1855
- selectable: z.boolean().optional().describe("Whether outputs locked to this script should be available for coin selection."),
1856
- privacy: z.union([z.number(), z.string()]).optional().describe("Privacy level for outputs locked to this script. A numeric level or a CashASM expression that evaluates to a privacy level."),
1857
- roles: z.record(z.string(), xoTemplateLockingScriptRoleSchema).optional().describe("Specific context for each role participating in this locking script.")
1858
- }).strict();
1278
+ const xoTemplateIconSchema = xoTemplateViewPropertiesSchema.pick({ name: true }).extend({ hash: z.string().describe("The identifier of the icon.") }).strict();
1859
1279
  /**
1860
- * Validation schema for an input definition in the template. Extends view properties with optional
1861
- * satoshi value, token configuration, and other transaction level fields.
1280
+ * Validation schema for the defaults block of a template.
1862
1281
  *
1863
1282
  * ```
1864
1283
  * {
1865
- * "inputs": {
1866
- * "[id]": { } ← this schema
1867
- * }
1284
+ * "defaults": { } ← this schema
1868
1285
  * }
1869
1286
  * ```
1870
1287
  */
1871
- const xoTemplateInputSchema = xoTemplateViewPropertiesSchema.extend({
1872
- valueSatoshis: z.union([satoshisSchema, z.string()]).optional().describe("The amount of satoshis for this input as a bigint or a CashASM expression resolving to the amount."),
1873
- token: xoTemplateTokenSchema.nullable().optional().describe("Token configuration for this input."),
1874
- sequenceNumber: z.union([z.number(), z.string()]).optional().describe("The sequence number of this input as a specific number or a CashASM expression."),
1875
- unlockingScript: z.string().optional().describe("Identifier of the unlocking script to use for the UTXO provided for this input."),
1876
- omitChangeAmounts: xoTemplateAssetAmountsSchema.optional().describe("Amount of change that should be omitted from the automatic change handling. WARNING: Setting this can result in loss of funds!")
1288
+ const xoTemplateDefaultsSchema = z.object({
1289
+ change: xoTemplateOutputIntentSchema.optional().describe("Instructions for how to construct automated change output."),
1290
+ address: xoTemplateLockingScriptIntentSchema.optional().describe("Configure the default intent structure for creating receiving addresses / locking scripts."),
1291
+ receive: xoTemplateOutputIntentSchema.optional().describe("Configure the default intent structure for creating receive outputs.")
1877
1292
  }).strict();
1878
1293
  /**
1879
- * Validation schema for an output definition. Extends the locking script schema so that
1880
- * every output inherits the same locking script fields and adds output-specific fields.
1881
- *
1882
- * ```
1883
- * {
1884
- * "outputs": {
1885
- * "[id]": { } ← this schema
1886
- * }
1887
- * }
1888
- * ```
1294
+ * Validation schema for the full XOTemplate type.
1889
1295
  */
1890
- const xoTemplateOutputSchema = xoTemplateLockingScriptSchema.omit({
1891
- lockingType: true,
1892
- lockingBytecode: true,
1893
- unlockingBytecode: true
1894
- }).extend({
1895
- lockingScript: z.string().describe("Identifier of the locking script to use for this output."),
1896
- valueSatoshis: z.union([satoshisSchema, z.string()]).optional().describe("The amount of satoshis for this output as a bigint or a CashASM expression resolving to the amount."),
1897
- token: xoTemplateTokenSchema.nullable().optional().describe("Token configuration for this output.")
1296
+ const xoTemplateSchema = xoTemplateViewPropertiesSchema.extend({
1297
+ $schema: z.string().describe("The URI that identifies the JSON Schema used by this template. This enables documentation, autocompletion, and validation in JSON documents."),
1298
+ version: z.string().optional().describe("A string identifying the version of this template."),
1299
+ supported: z.array(bchVmVersionSchema).min(1).describe("The BCH VM versions that this template supports. At least one version is required."),
1300
+ defaults: xoTemplateDefaultsSchema.optional().describe("Optional default settings used in this template."),
1301
+ roles: z.record(z.string(), xoTemplateViewPropertiesSchema).describe("The roles defined in this template."),
1302
+ start: z.array(xoTemplateActionIntentSchema).describe("A list of entry points defining which actions are available at the start."),
1303
+ actions: z.record(z.string(), xoTemplateActionSchema).describe("The actions defined in this template."),
1304
+ data: z.record(z.string(), xoTemplateDataSchema).optional().describe("The data fields defined in this template."),
1305
+ transactions: z.record(z.string(), xoTemplateTransactionSchema).optional().describe("The transaction templates defined in this template."),
1306
+ inputs: z.record(z.string(), xoTemplateInputSchema).describe("The inputs defined in this template."),
1307
+ outputs: z.record(z.string(), xoTemplateOutputSchema).describe("The outputs defined in this template."),
1308
+ lockingScripts: z.record(z.string(), xoTemplateLockingScriptSchema).describe("The locking script templates defined in this template."),
1309
+ scripts: z.record(z.string(), z.string()).describe("Scripts used in this template. Keys are script identifiers, values are bytecode or CashASM expressions."),
1310
+ constants: z.record(z.string(), xoTemplateConstantSchema).optional().describe("The constants defined in this template."),
1311
+ variables: z.record(z.string(), xoTemplateVariableSchema).optional().describe("The variables that must be provided for use in the template's scripts."),
1312
+ resources: z.array(xoTemplateResourceSchema).optional().describe("Resource references providing external documentation or tooling links."),
1313
+ icons: z.array(xoTemplateIconSchema).optional().describe("The icons available for use throughout the template."),
1314
+ scenarios: z.unknown().optional().describe("The scenarios defined in this template.")
1898
1315
  }).strict();
1316
+
1317
+ //#endregion
1318
+ //#region source/invitation/schemas.ts
1899
1319
  /**
1900
- * Validation schema for a transaction input reference for a transaction definition.
1320
+ * Validation schema for a reference to an input or output in an earlier commit that a later item extends.
1901
1321
  *
1902
1322
  * ```
1903
1323
  * {
1904
- * "transactions": {
1905
- * "[id]": {
1906
- * "inputs": [
1907
- * { "input": "..." } ← this schema
1908
- * ]
1324
+ * "inputs|outputs": [
1325
+ * {
1326
+ * "mergesWith": { "commitIdentifier": "...", "itemIndex": 0 } ← this schema
1909
1327
  * }
1910
- * }
1328
+ * ]
1911
1329
  * }
1912
1330
  * ```
1913
1331
  */
1914
- const xoTemplateTransactionInputSchema = z.object({
1915
- input: z.string().describe("The input definition identifier."),
1916
- inputIndex: z.number().optional().describe("Optional index of this input in the transaction.")
1332
+ const xoInvitationMergesWithReferenceSchema = z.object({
1333
+ commitIdentifier: z.string().describe("The commit that holds the item being extended."),
1334
+ itemIndex: z.number().int().nonnegative().describe("The position of the extended item inside that commit.")
1917
1335
  }).strict();
1918
1336
  /**
1919
- * Validation schema for a transaction output reference for a transaction definition.
1920
- *
1921
- * ```
1922
- * {
1923
- * "transactions": {
1924
- * "[id]": {
1925
- * "outputs": [
1926
- * { "output": "..." } ← this schema
1927
- * ]
1928
- * }
1929
- * }
1930
- * }
1931
- * ```
1337
+ * Validation schema for the template action a commit is resolved against when it differs from the invitation root.
1932
1338
  */
1933
- const xoTemplateTransactionOutputSchema = z.object({
1934
- output: z.string().describe("The output definition identifier."),
1935
- outputIndex: z.number().optional().describe("Optional index of this output in the transaction.")
1339
+ const xoInvitationActionReferenceSchema = z.object({
1340
+ templateIdentifier: z.string().describe("The template that defines the action."),
1341
+ actionIdentifier: z.string().describe("The action inside that template.")
1936
1342
  }).strict();
1937
1343
  /**
1938
- * Validation schema for role-specific data for a transaction definition.
1939
- *
1940
- * ```
1941
- * {
1942
- * "transactions": {
1943
- * "[id]": {
1944
- * "roles": {
1945
- * "[roleId]": { } ← this schema
1946
- * }
1947
- * }
1948
- * }
1949
- * }
1950
- * ```
1344
+ * Validation schema for a variable value. Matches every value type an invitation variable can hold.
1951
1345
  */
1952
- const xoTemplateTransactionRoleDataSchema = xoTemplateViewPropertiesSchema.partial().extend({
1953
- inputs: z.array(xoTemplateTransactionInputSchema).optional().describe("The inputs required for this role."),
1954
- outputs: z.array(xoTemplateTransactionOutputSchema).optional().describe("The outputs required for this role.")
1346
+ const xoInvitationVariableValueSchema = z.union([
1347
+ z.bigint(),
1348
+ z.boolean(),
1349
+ z.number(),
1350
+ z.string()
1351
+ ]).describe("A variable value expressed as a bigint, boolean, number, or string.");
1352
+ /**
1353
+ * Validation schema for a variable supplied by a commit.
1354
+ */
1355
+ const xoInvitationVariableSchema = z.object({
1356
+ variableIdentifier: z.string().describe("The template variable this value is for."),
1357
+ value: xoInvitationVariableValueSchema,
1358
+ roleIdentifier: z.string().exactOptional().describe("The role this variable belongs to."),
1359
+ mergesWith: xoInvitationMergesWithReferenceSchema.exactOptional()
1955
1360
  }).strict();
1956
1361
  /**
1957
- * Validation schema for a transaction template definition.
1362
+ * Validation schema for a cashtoken.
1363
+ */
1364
+ const outputTokenSchema = z.object({
1365
+ category: uint8ArraySchema.describe("The token category."),
1366
+ amount: z.bigint().describe("The fungible token amount."),
1367
+ nft: z.object({
1368
+ capability: xoTemplateNftCapabilitySchema,
1369
+ commitment: uint8ArraySchema.describe("The NFT commitment.")
1370
+ }).strict().exactOptional()
1371
+ }).strict();
1372
+ /**
1373
+ * Validation schema for a cashtoken supplied by a commit output. Every field is optional because a later commit may
1374
+ * supply the fields an earlier commit left unset.
1375
+ */
1376
+ const xoInvitationTokenSchema = z.object({
1377
+ amount: z.bigint().exactOptional().describe("The fungible token amount."),
1378
+ category: uint8ArraySchema.exactOptional().describe("The token category."),
1379
+ nft: z.object({
1380
+ capability: xoTemplateNftCapabilitySchema.exactOptional(),
1381
+ commitment: uint8ArraySchema.exactOptional().describe("The NFT commitment.")
1382
+ }).strict().exactOptional()
1383
+ }).strict();
1384
+ /**
1385
+ * Validation schema for a value one participant resolved from its own template key, such as a public key or a signature.
1386
+ */
1387
+ const xoInvitationResolutionSchema = z.object({
1388
+ resolutionIdentifier: z.string().describe("The key operation this value resolves, such as someKey.public_key."),
1389
+ value: uint8ArraySchema.describe("The resolved bytes."),
1390
+ roleIdentifier: z.string().exactOptional().describe("The role whose key this value was resolved from.")
1391
+ }).strict();
1392
+ /**
1393
+ * Validation schema for a transaction input supplied by a commit.
1394
+ */
1395
+ const xoInvitationInputSchema = z.object({
1396
+ outpointIndex: z.number().int().nonnegative().exactOptional().describe("The index of the spent output in its transaction."),
1397
+ outpointTransactionHash: uint8ArraySchema.exactOptional().describe("The hash of the transaction that holds the spent output."),
1398
+ sequenceNumber: z.number().int().nonnegative().exactOptional().describe("The sequence number of this input."),
1399
+ unlockingBytecode: uint8ArraySchema.exactOptional().describe("The script that unlocks the spent output."),
1400
+ inputIdentifier: z.string().exactOptional().describe("The template input this input fills."),
1401
+ transactionIndex: z.number().int().nonnegative().exactOptional().describe("The position this input must take in the final transaction."),
1402
+ roleIdentifier: z.string().exactOptional().describe("The role this input belongs to."),
1403
+ resolutions: z.array(xoInvitationResolutionSchema).exactOptional(),
1404
+ mergesWith: xoInvitationMergesWithReferenceSchema.exactOptional(),
1405
+ valueSatoshis: satoshisSchema.exactOptional(),
1406
+ token: outputTokenSchema.exactOptional()
1407
+ }).strict();
1408
+ /**
1409
+ * Validation schema for a transaction output supplied by a commit.
1410
+ */
1411
+ const xoInvitationOutputSchema = z.object({
1412
+ lockingBytecode: uint8ArraySchema.exactOptional().describe("The locking bytecode of this output."),
1413
+ valueSatoshis: satoshisSchema.exactOptional(),
1414
+ token: xoInvitationTokenSchema.exactOptional(),
1415
+ outputIdentifier: z.string().exactOptional().describe("The template output this output fills."),
1416
+ transactionIndex: z.number().int().nonnegative().exactOptional().describe("The position this output must take in the final transaction."),
1417
+ secretIdentifier: z.string().exactOptional().describe("The secret used to derive this output."),
1418
+ roleIdentifier: z.string().exactOptional().describe("The role this output belongs to."),
1419
+ mergesWith: xoInvitationMergesWithReferenceSchema.exactOptional()
1420
+ }).strict();
1421
+ /**
1422
+ * Validation schema for the data a commit supplies.
1423
+ */
1424
+ const xoInvitationCommitDataSchema = z.object({
1425
+ transactionVersion: z.number().int().nonnegative().exactOptional().describe("The transaction version."),
1426
+ transactionLocktime: z.number().int().nonnegative().exactOptional().describe("The transaction locktime."),
1427
+ variables: z.array(xoInvitationVariableSchema).exactOptional(),
1428
+ inputs: z.array(xoInvitationInputSchema).exactOptional(),
1429
+ outputs: z.array(xoInvitationOutputSchema).exactOptional(),
1430
+ resolutions: z.array(xoInvitationResolutionSchema).exactOptional(),
1431
+ roleIdentifier: z.string().exactOptional().describe("The role the committing entity takes in the action with this commit.")
1432
+ }).strict();
1433
+ /**
1434
+ * Validation schema for one signed commit in an invitation.
1435
+ */
1436
+ const xoInvitationCommitSchema = z.object({
1437
+ commitIdentifier: z.string().describe("The identifier of this commit."),
1438
+ previousCommitIdentifier: z.string().optional().prefault(void 0).describe("The commit this one follows. Undefined on the first commit."),
1439
+ entityIdentifier: z.string().describe("The entity that made this commit."),
1440
+ createdAtTimestamp: z.number().int().nonnegative().describe("When this commit was created, as a Unix timestamp in milliseconds."),
1441
+ actionReference: xoInvitationActionReferenceSchema.exactOptional(),
1442
+ data: xoInvitationCommitDataSchema,
1443
+ signature: z.string().describe("The signature over this commit."),
1444
+ expiresAtTimestamp: z.number().int().nonnegative().describe("When this commit expires, as a Unix timestamp in milliseconds."),
1445
+ memo: z.string().exactOptional().describe("A free text note attached to this commit.")
1446
+ }).strict();
1447
+ /**
1448
+ * Validation schema for a transaction result that has been created but not broadcast.
1449
+ */
1450
+ const xoInvitationTransactionCreatedResultSchema = z.object({
1451
+ transactionHash: z.string().describe("The hash of the created transaction."),
1452
+ createdAtTimestamp: z.number().int().nonnegative().describe("When the transaction was created, as a Unix timestamp in milliseconds."),
1453
+ createdByEntityIdentifier: z.string().describe("The entity that created the transaction."),
1454
+ createdBySignature: z.string().describe("The signature over the created result.")
1455
+ }).strict();
1456
+ /**
1457
+ * Validation schema for a transaction result that has been created and broadcast.
1458
+ */
1459
+ const xoInvitationTransactionBroadcastedResultSchema = xoInvitationTransactionCreatedResultSchema.extend({
1460
+ broadcastedAtTimestamp: z.number().int().nonnegative().describe("When the transaction was broadcasted, as a Unix timestamp in milliseconds."),
1461
+ broadcastedByEntityIdentifier: z.string().describe("The entity that broadcasted the transaction."),
1462
+ broadcastedBySignature: z.string().describe("The signature over the broadcasted result.")
1463
+ }).strict();
1464
+ /**
1465
+ * Validation schema for a data result created on an invitation.
1466
+ */
1467
+ const xoInvitationCreatedResultSchema = z.object({
1468
+ value: xoInvitationVariableValueSchema,
1469
+ createdAtTimestamp: z.number().int().nonnegative().describe("When the value was created, as a Unix timestamp in milliseconds."),
1470
+ createdByEntityIdentifier: z.string().describe("The entity that created the value."),
1471
+ createdBySignature: z.string().describe("The signature over the created value.")
1472
+ }).strict();
1473
+ /**
1474
+ * Validation schema for the results created on an invitation.
1475
+ */
1476
+ const xoInvitationResultSchema = z.object({
1477
+ transactions: z.record(z.string(), z.union([xoInvitationTransactionBroadcastedResultSchema, xoInvitationTransactionCreatedResultSchema])).exactOptional(),
1478
+ data: z.record(z.string(), xoInvitationCreatedResultSchema).exactOptional()
1479
+ }).strict();
1480
+ /**
1481
+ * Validation schema for an invitation.
1482
+ */
1483
+ const xoInvitationSchema = z.object({
1484
+ invitationIdentifier: z.string().describe("The identifier of this invitation."),
1485
+ createdAtTimestamp: z.number().int().nonnegative().describe("When this invitation was created, as a Unix timestamp in milliseconds."),
1486
+ templateIdentifier: z.string().describe("The template this invitation uses."),
1487
+ actionIdentifier: z.string().describe("The template action this invitation performs."),
1488
+ commits: z.array(xoInvitationCommitSchema),
1489
+ result: xoInvitationResultSchema.exactOptional()
1490
+ }).strict();
1491
+
1492
+ //#endregion
1493
+ //#region source/invitation/parser.ts
1494
+ /**
1495
+ * Validates an untyped value against the XOInvitation schema and returns it as a typed invitation.
1958
1496
  *
1959
- * ```
1960
- * {
1961
- * "transactions": {
1962
- * "[id]": { } ← this schema
1963
- * }
1497
+ * The schema parser returns a rebuilt copy whose keys follow the schema order. Commit signatures cover the JSON text of
1498
+ * each commit's data, and that text depends on key order, so a rebuilt copy no longer matches its signatures. Only the
1499
+ * value that was received keeps the order it was signed with.
1500
+ *
1501
+ * The received value itself is returned, not a copy, so the key order its commit signatures were made over is kept.
1502
+ *
1503
+ * @param {unknown} invitationValue - Value to validate, such as the output of Extended JSON parsing.
1504
+ * @returns {XOInvitation} - The validated invitation, as the same object that was passed in.
1505
+ * @throws {@link InvitationInvalidError} - When any field is missing, has the wrong type, or is not part of the invitation type.
1506
+ */
1507
+ const parseInvitation = (invitationValue) => {
1508
+ const parseResult = xoInvitationSchema.safeParse(invitationValue);
1509
+ if (parseResult.success === false) throw new InvitationInvalidError(buildErrorDescription(parseResult.error.issues));
1510
+ return invitationValue;
1511
+ };
1512
+
1513
+ //#endregion
1514
+ //#region source/invitation/serialization.ts
1515
+ /**
1516
+ * Serializes an invitation for sharing. Encodes bigint and Uint8Array fields using Extended JSON.
1517
+ *
1518
+ * @param invitation Shared invitation to export.
1519
+ * @returns JSON string that {@link deserializeInvitation} can restore.
1520
+ * @throws {InvitationSerializationFailedError} When JSON serialization fails.
1521
+ */
1522
+ const serializeInvitation = (invitation) => {
1523
+ try {
1524
+ return toExtendedJson(invitation);
1525
+ } catch (serializationError) {
1526
+ throw new InvitationSerializationFailedError(serializationError instanceof Error ? serializationError.message : "unknown error while serializing invitation");
1527
+ }
1528
+ };
1529
+ /**
1530
+ * Deserializes a shared invitation JSON string into an invitation.
1531
+ *
1532
+ * Every field is checked against its declared type, so the returned invitation can be trusted to match
1533
+ * {@link XOInvitation}.
1534
+ *
1535
+ * @param serializedInvitation JSON produced by {@link serializeInvitation}.
1536
+ * @returns Parsed invitation whose fields match their declared types.
1537
+ * @throws {InvitationJsonMalformedError} When the string is not valid JSON.
1538
+ * @throws {InvitationInvalidError} When the parsed value does not match the XOInvitation type.
1539
+ */
1540
+ const deserializeInvitation = (serializedInvitation) => {
1541
+ let parsedValue;
1542
+ try {
1543
+ parsedValue = fromExtendedJson(serializedInvitation);
1544
+ } catch (parsingError) {
1545
+ throw new InvitationJsonMalformedError(parsingError instanceof Error ? parsingError.message : "unknown error while deserializing invitation");
1546
+ }
1547
+ return parseInvitation(parsedValue);
1548
+ };
1549
+
1550
+ //#endregion
1551
+ //#region source/script.ts
1552
+ /**
1553
+ * Converts a script to a scriptHash.
1554
+ * @param {Uint8Array} script - The script to convert.
1555
+ * @returns {string} The scriptHash as a reversed hex string.
1556
+ */
1557
+ const scriptToScriptHash = (script) => {
1558
+ return binToHex(sha256.hash(script).reverse());
1559
+ };
1560
+
1561
+ //#endregion
1562
+ //#region source/sse-session/async-push-iterator.ts
1563
+ /**
1564
+ * An async iterable queue that bridges push-based producers and pull-based consumers.
1565
+ *
1566
+ * Composes an internal {@link ReadableStream} instead of extending it, so producers
1567
+ * call {@link push} while consumers use standard async iteration (`for await...of`).
1568
+ *
1569
+ * ```ts
1570
+ * const messages = new AsyncPushIterator<SSEvent>();
1571
+ *
1572
+ * // Producer (elsewhere)
1573
+ * messages.push(event);
1574
+ *
1575
+ * // Consumer
1576
+ * for await (const event of messages) {
1577
+ * handle(event);
1964
1578
  * }
1965
1579
  * ```
1580
+ *
1581
+ * {@link Symbol.asyncIterator} returns `stream.values({ preventCancel: true })` so
1582
+ * breaking out of `for await...of` does not cancel the underlying stream. That
1583
+ * matters for long-lived sessions where the producer keeps pushing after a consumer
1584
+ * stops reading early (for example, test helpers that only collect a fixed count).
1966
1585
  */
1967
- const xoTemplateTransactionSchema = xoTemplateViewPropertiesSchema.extend({
1968
- version: z.number().optional().describe("The version of the transaction."),
1969
- locktime: z.union([z.number(), z.string()]).optional().describe("The locktime for this transaction as a specific number or a CashASM expression."),
1970
- inputs: z.array(xoTemplateTransactionInputSchema).describe("The inputs for this transaction."),
1971
- outputs: z.array(xoTemplateTransactionOutputSchema).describe("The outputs for this transaction."),
1972
- roles: z.record(z.string(), xoTemplateTransactionRoleDataSchema).optional().describe("Specific context for each role participating in this transaction."),
1973
- composable: z.boolean().optional().describe("Whether this transaction can be composed with other transactions.")
1974
- }).strict();
1586
+ var AsyncPushIterator = class {
1587
+ /** ReadableStream backing the async iterator returned from {@link Symbol.asyncIterator}. */
1588
+ #stream;
1589
+ /** Controller used to enqueue values and close the stream from {@link push} and {@link close}. */
1590
+ #controller;
1591
+ /** When true, no more values are accepted and iteration eventually completes. */
1592
+ #closed = false;
1593
+ constructor() {
1594
+ this.#stream = new ReadableStream({ start: (controller) => {
1595
+ this.#controller = controller;
1596
+ } });
1597
+ }
1598
+ /**
1599
+ * Flag indicating if the iterator is closed.
1600
+ */
1601
+ get closed() {
1602
+ return this.#closed;
1603
+ }
1604
+ /**
1605
+ * Enqueues a value for the consumer.
1606
+ *
1607
+ * After {@link close}, pushes are silently dropped.
1608
+ *
1609
+ * @param value - The next value to yield from the iterator.
1610
+ */
1611
+ push(value) {
1612
+ if (this.#closed) return;
1613
+ this.#controller?.enqueue(value);
1614
+ }
1615
+ /**
1616
+ * Causes any future interactions with the associated stream to error with {@link error}.
1617
+ * Calling this will also clear the pending values immediately, so iterators that were listening will not receive them.
1618
+ *
1619
+ * @param error - The error to throw from the stream.
1620
+ */
1621
+ error(error) {
1622
+ if (this.#closed) return;
1623
+ this.#closed = true;
1624
+ this.#controller?.error(error);
1625
+ }
1626
+ /**
1627
+ * Ends the stream.
1628
+ *
1629
+ * Marks the iterator closed so future {@link push} calls are ignored.
1630
+ * Buffered values are still yielded before iteration completes.
1631
+ */
1632
+ close() {
1633
+ this.#closed = true;
1634
+ try {
1635
+ this.#controller?.close();
1636
+ } catch {}
1637
+ }
1638
+ /**
1639
+ * Returns an async iterator over the composed stream.
1640
+ *
1641
+ * Uses `preventCancel: true` so early `break` from `for await...of` does not
1642
+ * close the stream and block later pushes.
1643
+ *
1644
+ * Because values are discarded after being read, only a single consumer is supported.
1645
+ * Additional consumers will receive a stream lock error. Unread values will be preserved until {@link close} is called.
1646
+ */
1647
+ [Symbol.asyncIterator]() {
1648
+ return this.#stream.values({ preventCancel: true });
1649
+ }
1650
+ };
1651
+
1652
+ //#endregion
1653
+ //#region source/sse-session/constants.ts
1654
+ /**
1655
+ * Regex that splits decoded SSE text into lines.
1656
+ *
1657
+ * The SSE wire format is line-oriented (`field: value` per line). Servers may
1658
+ * send `\r\n` (HTTP default), `\n` (Unix), or `\r` (legacy Mac). Matching all
1659
+ * three keeps parsing correct regardless of platform or server implementation.
1660
+ */
1661
+ const SSE_LINE_ENDINGS = /\r\n|\r|\n/;
1662
+ /**
1663
+ * Regex that matches the single optional leading space in an SSE field value.
1664
+ *
1665
+ * Per the SSE spec, `field: value` may include one space immediately after the
1666
+ * colon; that space is not part of the value. Used with `.replace()` to strip
1667
+ * it when parsing lines such as `data: hello` → `hello`.
1668
+ */
1669
+ const SSE_FIELD_VALUE_REGEX = /^ /;
1670
+ /**
1671
+ * Regex that matches a trailing newline at the end of a string.
1672
+ *
1673
+ * Multiple `data:` lines in one event are joined with `\n`. When the event is
1674
+ * completed, this removes any stray trailing newline so callers receive the
1675
+ * payload without an extra line break at the end.
1676
+ */
1677
+ const SSE_TRAILING_NEWLINE_REGEX = /\n$/;
1678
+ /**
1679
+ * The newline character used when normalizing SSE text internally.
1680
+ *
1681
+ * Used to join consecutive `data:` lines into one payload and to reassemble
1682
+ * buffered partial lines between streamed chunks before the next parse call.
1683
+ */
1684
+ const NEW_LINE = "\n";
1685
+ /**
1686
+ * Default exponential backoff options for the SSE session.
1687
+ */
1688
+ const SSE_SESSION_EXPONENTIAL_BACKOFF_DEFAULTS = {
1689
+ baseDelay: 250,
1690
+ maxDelay: 1e4,
1691
+ maxAttempts: 0,
1692
+ growthRate: 1.3,
1693
+ jitter: .3
1694
+ };
1695
+ /**
1696
+ * Default attempt reconnect flag for the SSE session.
1697
+ * @default true
1698
+ */
1699
+ const SSE_SESSION_ATTEMPT_RECONNECT_DEFAULT = true;
1700
+ /**
1701
+ * Default persistent flag for the SSE session.
1702
+ * @default false
1703
+ */
1704
+ const SSE_SESSION_PERSISTENT_DEFAULT = false;
1705
+
1706
+ //#endregion
1707
+ //#region source/sse-session/sse-event-parser.ts
1708
+ /**
1709
+ * Incrementally parses Server-Sent Events (SSE) from streamed byte chunks.
1710
+ *
1711
+ * SSE payloads are line-oriented: each event is a sequence of `field: value`
1712
+ * lines terminated by a blank line. This parser accepts arbitrary chunk
1713
+ * boundaries from a live HTTP response body and emits only complete events.
1714
+ *
1715
+ * Typical usage is one parser instance per connection, calling {@link parseEvents}
1716
+ * for each chunk received from the stream:
1717
+ *
1718
+ * ```ts
1719
+ * const parser = new SSEEventParser();
1720
+ *
1721
+ * for await (const chunk of response.body) {
1722
+ * for (const event of parser.parseEvents(chunk)) {
1723
+ * // handle event.data, event.event, event.id, event.retry
1724
+ * }
1725
+ * }
1726
+ * ```
1727
+ *
1728
+ * Supported fields follow the SSE spec: `data`, `event`, `id`, and `retry`.
1729
+ * Multiple `data:` lines in one event are joined with `\n`. An event is only
1730
+ * emitted once a blank line is seen and at least one `data` field was collected.
1731
+ */
1732
+ var SSEEventParser = class {
1733
+ #textDecoder;
1734
+ /** Bytes from a partial line or incomplete event, carried over to the next chunk. */
1735
+ #messageBuffer = "";
1736
+ /**
1737
+ * Creates a parser for one SSE stream.
1738
+ *
1739
+ * Inject custom encoders in tests or when a non-default character encoding
1740
+ * is required; production callers can rely on the defaults.
1741
+ *
1742
+ * @param options - Optional text encoders for decode/encode of stream bytes.
1743
+ */
1744
+ constructor(options = {}) {
1745
+ this.#textDecoder = options.textDecoder ?? new TextDecoder();
1746
+ }
1747
+ /**
1748
+ * Clears any buffered bytes from a partial line or incomplete event.
1749
+ *
1750
+ * Call when abandoning a transport so the next connection does not prepend
1751
+ * stale bytes to incoming chunks.
1752
+ */
1753
+ reset() {
1754
+ this.#messageBuffer = "";
1755
+ this.#textDecoder.decode();
1756
+ }
1757
+ /**
1758
+ * Parses all complete SSE events contained in a newly received chunk.
1759
+ *
1760
+ * The chunk is appended to any bytes buffered from earlier calls. Complete
1761
+ * events (blank-line delimited blocks with at least one `data` field) are
1762
+ * returned immediately; any trailing partial line or in-progress event stays
1763
+ * in the internal buffer until a later chunk completes it.
1764
+ *
1765
+ * @param chunk - Newly received SSE stream bytes.
1766
+ * @returns Zero or more complete parsed SSE events from this chunk.
1767
+ */
1768
+ parseEvents(chunk) {
1769
+ const lines = this.#getBufferedLines(chunk);
1770
+ const eventLines = lines.slice(0, -1);
1771
+ const events = [];
1772
+ let event = {};
1773
+ let processedLineCount = 0;
1774
+ for (const [index, line] of eventLines.entries()) {
1775
+ if (line === "") {
1776
+ if (event.data !== void 0) {
1777
+ events.push(this.#completeEvent(event));
1778
+ event = {};
1779
+ processedLineCount = index + 1;
1780
+ }
1781
+ continue;
1782
+ }
1783
+ this.#parseLine(line, event);
1784
+ }
1785
+ this.#storeRemainingLines(lines, processedLineCount);
1786
+ return events;
1787
+ }
1788
+ /**
1789
+ * Appends a new chunk to the buffered bytes and splits the combined payload
1790
+ * into lines.
1791
+ *
1792
+ * Accepts `\r\n`, `\r`, and `\n` line endings so events parse correctly
1793
+ * regardless of server or platform conventions.
1794
+ */
1795
+ #getBufferedLines(chunk) {
1796
+ this.#messageBuffer += this.#textDecoder.decode(chunk, { stream: true });
1797
+ return this.#messageBuffer.split(SSE_LINE_ENDINGS);
1798
+ }
1799
+ /**
1800
+ * Parses one SSE field line into an in-progress event.
1801
+ *
1802
+ * Lines without a colon are ignored. A single optional space after the colon
1803
+ * is stripped from the field value, per the SSE spec.
1804
+ */
1805
+ #parseLine(line, event) {
1806
+ const colonIndex = line.indexOf(":");
1807
+ if (colonIndex === -1) return;
1808
+ const field = line.slice(0, colonIndex);
1809
+ const value = line.slice(colonIndex + 1).replace(SSE_FIELD_VALUE_REGEX, "");
1810
+ switch (field) {
1811
+ case "data":
1812
+ event.data = event.data ? `${event.data}${NEW_LINE}${value}` : value;
1813
+ return;
1814
+ case "event":
1815
+ event.event = value;
1816
+ return;
1817
+ case "id":
1818
+ event.id = value;
1819
+ return;
1820
+ case "retry":
1821
+ this.#parseRetry(value, event);
1822
+ return;
1823
+ }
1824
+ }
1825
+ /**
1826
+ * Applies a numeric `retry:` field to an in-progress event.
1827
+ *
1828
+ * Non-numeric values are ignored rather than failing the parse.
1829
+ */
1830
+ #parseRetry(value, event) {
1831
+ const retry = parseInt(value, 10);
1832
+ if (!isNaN(retry)) event.retry = retry;
1833
+ }
1834
+ /**
1835
+ * Constructs a completed SSE event from accumulated fields.
1836
+ *
1837
+ * Trims a trailing newline from multi-line `data` values so callers receive
1838
+ * the payload without an extra line break at the end.
1839
+ */
1840
+ #completeEvent(event) {
1841
+ return {
1842
+ ...event,
1843
+ data: event.data?.replace(SSE_TRAILING_NEWLINE_REGEX, "")
1844
+ };
1845
+ }
1846
+ /**
1847
+ * Preserves incomplete trailing lines for the next received chunk.
1848
+ *
1849
+ * Only lines that were fully processed (through a completed event boundary)
1850
+ * are discarded; the remainder is re-encoded into {@link messageBuffer}.
1851
+ */
1852
+ #storeRemainingLines(lines, processedLineCount) {
1853
+ this.#messageBuffer = lines.slice(processedLineCount).join(NEW_LINE);
1854
+ }
1855
+ };
1856
+
1857
+ //#endregion
1858
+ //#region source/sse-session/errors.ts
1859
+ /**
1860
+ * Error thrown when a response body is null
1861
+ */
1862
+ var ResponseBodyNullError = class extends Error {
1863
+ constructor() {
1864
+ super("Response body is null");
1865
+ this.name = "ResponseBodyNullError";
1866
+ }
1867
+ };
1868
+ /**
1869
+ * Error thrown when an HTTP error occurs
1870
+ */
1871
+ var HTTPError = class extends Error {
1872
+ constructor(status, message) {
1873
+ super(`HTTP error! Status: ${status} - ${message}`);
1874
+ this.name = "HTTPError";
1875
+ }
1876
+ };
1877
+ /**
1878
+ * Error thrown when a plugin's unsubscribe function is not a function
1879
+ */
1880
+ var SSESessionUnsubscribePluginNotAFunctionError = class extends Error {
1881
+ constructor(pluginName) {
1882
+ super(`${pluginName}'s unsubscribe function is not a function`);
1883
+ this.name = "SSESessionUnsubscribePluginNotAFunctionError";
1884
+ }
1885
+ };
1886
+
1887
+ //#endregion
1888
+ //#region source/sse-session/sse-session.ts
1889
+ /**
1890
+ * A fetch-based Server-Sent Events (SSE) client with reconnect and optional
1891
+ * browser tab visibility handling.
1892
+ *
1893
+ * Each session maintains one HTTP streaming connection at a time. Incoming
1894
+ * bytes are parsed into {@link SSEvent} objects and delivered through two
1895
+ * surfaces:
1896
+ *
1897
+ * - **Events** — `"connected"`, `"message"`, `"disconnected"`, `"error"`,
1898
+ * and `"closed"` on the session itself (extends {@link EventEmitter}).
1899
+ * - **Messages** — {@link messages}, an async iterable for `for await...of`
1900
+ * consumers.
1901
+ *
1902
+ * Typical usage:
1903
+ *
1904
+ * ```ts
1905
+ * const session = await SSESession.create("/events");
1906
+ *
1907
+ * session.on("message", (event) => console.log(event.data));
1908
+ *
1909
+ * for await (const event of session.messages) {
1910
+ * handle(event);
1911
+ * }
1912
+ * ```
1913
+ *
1914
+ * ## Lifecycle
1915
+ *
1916
+ * - {@link connect} opens (or reopens) the transport. It resolves once the
1917
+ * HTTP stream is established; reading continues in the background.
1918
+ * - {@link disconnect} stops the in-flight fetch without ending the session.
1919
+ * Used internally for tab visibility. The {@link messages} iterator stays
1920
+ * open so an existing consumer resumes when the tab becomes visible again.
1921
+ * - {@link close} aborts the transport, closes {@link messages}, and emits
1922
+ * `"closed"`.
1923
+ *
1924
+ * Automatic reconnect is controlled by {@link SSESessionOptions.persistent}
1925
+ * (server closed the stream) and
1926
+ * {@link SSESessionOptions.attemptReconnect} (transport error).
1927
+ *
1928
+ * ## Connection supersession
1929
+ *
1930
+ * Each {@link connect} will create a new controller if one does not exist. Otherwise,
1931
+ * it will return immediately.
1932
+ */
1933
+ var SSESession = class SSESession extends EventEmitter {
1934
+ /**
1935
+ * Creates a session and waits until the first connection is established.
1936
+ *
1937
+ * @param url - The SSE endpoint URL.
1938
+ * @param options - Configuration merged with instance defaults.
1939
+ * @returns A connected session.
1940
+ * @throws When the initial connection cannot be established.
1941
+ */
1942
+ static async create(url, options = {}) {
1943
+ const session = new SSESession(url, options);
1944
+ await session.connect();
1945
+ return session;
1946
+ }
1947
+ /**
1948
+ * Enables SSE resume semantics by sending `Last-Event-ID` on reconnect.
1949
+ *
1950
+ * Listens for incoming `"message"` events and remembers the most recent
1951
+ * {@link SSEvent.id}. On every subsequent connect or reconnect, the session's
1952
+ * {@link onRequest} hook is wrapped so that header is attached when an id is
1953
+ * known, allowing the server to replay only events the client has not yet
1954
+ * received.
1955
+ *
1956
+ * The existing {@link onRequest} callback is preserved and runs after the
1957
+ * header is applied, so auth or other header mutations continue to work.
1958
+ *
1959
+ * Since this relies on listening to the `"message"` event, this should ideally
1960
+ * be called before "{@link connect} is called so no messages (and no ids) are missed.
1961
+ *
1962
+ * ```ts
1963
+ * const session = new SSESession(url);
1964
+ * SSESession.addLastEventIdReconnect(session);
1965
+ * await session.connect();
1966
+ * // Reconnects send Last-Event-ID once an event with an id is received.
1967
+ * ```
1968
+ *
1969
+ * @param session - The session to instrument.
1970
+ * @returns The same session, for chaining.
1971
+ */
1972
+ static addLastEventIdReconnect(session) {
1973
+ if (session.plugins.has(SSESession.addLastEventIdReconnect)) {
1974
+ const removeListener = session.plugins.get(SSESession.addLastEventIdReconnect);
1975
+ if (typeof removeListener !== "function") throw new SSESessionUnsubscribePluginNotAFunctionError("addLastEventIdReconnect");
1976
+ return {
1977
+ session,
1978
+ removeListener
1979
+ };
1980
+ }
1981
+ let lastEventId;
1982
+ const messageListener = (event) => {
1983
+ lastEventId = event.id ?? lastEventId;
1984
+ };
1985
+ session.on("message", messageListener);
1986
+ const originalOnRequest = session.options.onRequest;
1987
+ let enabled = true;
1988
+ session.options.onRequest = async (request) => {
1989
+ if (lastEventId && enabled) request.headers = {
1990
+ ...request.headers,
1991
+ "Last-Event-ID": lastEventId
1992
+ };
1993
+ return originalOnRequest(request);
1994
+ };
1995
+ const removeListener = () => {
1996
+ enabled = false;
1997
+ session.off("message", messageListener);
1998
+ if (session.plugins.get(SSESession.addLastEventIdReconnect) === removeListener) session.plugins.delete(SSESession.addLastEventIdReconnect);
1999
+ };
2000
+ session.plugins.set(SSESession.addLastEventIdReconnect, removeListener);
2001
+ return {
2002
+ session,
2003
+ removeListener
2004
+ };
2005
+ }
2006
+ /**
2007
+ * Pauses and resumes a session based on browser tab visibility.
2008
+ *
2009
+ * Uses the Page Visibility API (`document.visibilitychange`):
2010
+ *
2011
+ * - **hidden** — {@link disconnect} stops the active fetch. {@link messages}
2012
+ * stays open; `"disconnected"` fires but `"closed"` does not.
2013
+ * - **visible** — {@link connect} re-establishes the stream if needed.
2014
+ *
2015
+ * This controller will not re-connect if the session was disconnected by something else.
2016
+ *
2017
+ * No-op in non-browser environments where `document` is undefined.
2018
+ *
2019
+ * @param session - The session to manage.
2020
+ */
2021
+ static addBrowserVisibilityHandler(session) {
2022
+ if (typeof document === "undefined") return { session };
2023
+ if (session.plugins.has(SSESession.addBrowserVisibilityHandler)) {
2024
+ const removeListener = session.plugins.get(SSESession.addBrowserVisibilityHandler);
2025
+ if (typeof removeListener !== "function") throw new SSESessionUnsubscribePluginNotAFunctionError("addBrowserVisibilityHandler");
2026
+ return {
2027
+ session,
2028
+ removeListener
2029
+ };
2030
+ }
2031
+ const reconnectOnVisible = async () => {
2032
+ if (document.visibilityState === "visible") {
2033
+ document.removeEventListener("visibilitychange", reconnectOnVisible);
2034
+ await session.connect();
2035
+ }
2036
+ };
2037
+ const disableOnExternalDisconnect = ({ source }) => {
2038
+ if (source !== SSESession.addBrowserVisibilityHandler) document.removeEventListener("visibilitychange", reconnectOnVisible);
2039
+ };
2040
+ const handleVisibilityChange = async () => {
2041
+ if (!session.active) return;
2042
+ if (document.visibilityState === "hidden") {
2043
+ document.addEventListener("visibilitychange", reconnectOnVisible);
2044
+ session.disconnect({ source: SSESession.addBrowserVisibilityHandler });
2045
+ }
2046
+ };
2047
+ document.addEventListener("visibilitychange", handleVisibilityChange);
2048
+ session.on("beforeDisconnect", disableOnExternalDisconnect);
2049
+ const removeListener = () => {
2050
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
2051
+ document.removeEventListener("visibilitychange", reconnectOnVisible);
2052
+ session.off("beforeDisconnect", disableOnExternalDisconnect);
2053
+ if (session.plugins.get(SSESession.addBrowserVisibilityHandler) === removeListener) session.plugins.delete(SSESession.addBrowserVisibilityHandler);
2054
+ };
2055
+ session.plugins.set(SSESession.addBrowserVisibilityHandler, removeListener);
2056
+ return {
2057
+ session,
2058
+ removeListener
2059
+ };
2060
+ }
2061
+ /** SSE endpoint URL for this session. */
2062
+ #url;
2063
+ /**
2064
+ * Per-instance configuration.
2065
+ *
2066
+ * Defaults live on the instance field (not a shared static) so each session
2067
+ * gets its own {@link SSEEventParser} and {@link ExponentialBackoff}.
2068
+ */
2069
+ options = {
2070
+ fetch: (...args) => fetch(...args),
2071
+ method: "GET",
2072
+ headers: {
2073
+ Accept: "text/event-stream",
2074
+ "Cache-Control": "no-cache"
2075
+ },
2076
+ body: new FormData(),
2077
+ onRequest: (request) => Promise.resolve(request),
2078
+ retry: new ExponentialBackoff({ ...SSE_SESSION_EXPONENTIAL_BACKOFF_DEFAULTS }),
2079
+ attemptReconnect: SSE_SESSION_ATTEMPT_RECONNECT_DEFAULT,
2080
+ persistent: SSE_SESSION_PERSISTENT_DEFAULT,
2081
+ eventParser: new SSEEventParser()
2082
+ };
2083
+ /**
2084
+ * Registered plugins for the session.
2085
+ * This can be used to prevent duplicate plugins from being added to the session.
2086
+ * The value for a plugin is arbitrary, for example a function that removes the plugin.
2087
+ */
2088
+ plugins = /* @__PURE__ */ new Map();
2089
+ /** AbortController for the currently active fetch, if any. */
2090
+ #connectionController = null;
2091
+ /** The server's requested retry interval in milliseconds (per SSE spec) if any */
2092
+ #retryInterval = null;
2093
+ /**
2094
+ * Asynchronous stream of parsed SSE events for the active connection.
2095
+ *
2096
+ * Stays open across {@link disconnect} and automatic reconnects so an existing
2097
+ * `for await` consumer keeps receiving events after visibility resumes.
2098
+ *
2099
+ * Closes when:
2100
+ * - the server ends the stream and {@link SSESessionOptions.persistent}
2101
+ * is false,
2102
+ * - {@link close} is called, or
2103
+ * - a transport error occurs with
2104
+ * {@link SSESessionOptions.attemptReconnect} disabled.
2105
+ *
2106
+ * A later {@link connect} replaces this with a new iterator when the
2107
+ * previous one was closed. Consumers should read from `session.messages`
2108
+ * rather than caching a reference across terminal disconnects.
2109
+ */
2110
+ messages = new AsyncPushIterator();
2111
+ constructor(url, options = {}) {
2112
+ super();
2113
+ const { onConnected, onDisconnected, onError, ...restOptions } = options;
2114
+ this.#url = url;
2115
+ this.options = {
2116
+ ...this.options,
2117
+ ...restOptions,
2118
+ headers: {
2119
+ ...this.options.headers,
2120
+ ...options.headers
2121
+ }
2122
+ };
2123
+ if (onConnected) this.on("connected", onConnected);
2124
+ if (onDisconnected) this.on("disconnected", onDisconnected);
2125
+ if (onError) this.on("error", onError);
2126
+ }
2127
+ /**
2128
+ * Returns true if the session is active by checking if the controller is not null.
2129
+ */
2130
+ get active() {
2131
+ return this.#connectionController !== null;
2132
+ }
2133
+ /**
2134
+ * Connects or reconnects to the SSE endpoint.
2135
+ *
2136
+ * Resolves once the HTTP stream is established and `"connected"` has been
2137
+ * emitted. Body reading continues asynchronously in the background via the
2138
+ * internal `#readStream` method.
2139
+ *
2140
+ * @throws When the fetch retry policy exhausts attempts or the connection
2141
+ * is superseded before the reader is handed off (in the latter case the
2142
+ * promise resolves without throwing).
2143
+ */
2144
+ async connect() {
2145
+ if (this.#connectionController) return;
2146
+ this.#resetEventParser();
2147
+ this.#ensureMessageStreamOpen();
2148
+ const connectionController = new AbortController();
2149
+ this.#connectionController = connectionController;
2150
+ const { method, headers, body } = this.options;
2151
+ const fetchOptions = {
2152
+ method,
2153
+ headers: headers || {},
2154
+ signal: connectionController.signal,
2155
+ cache: "no-store"
2156
+ };
2157
+ if (method === "POST") fetchOptions.body = body || null;
2158
+ let reader;
2159
+ try {
2160
+ reader = await this.options.retry.run(() => this.#createReader(fetchOptions), { signal: connectionController.signal });
2161
+ } catch (error) {
2162
+ if (this.#connectionController !== connectionController) return;
2163
+ this.#connectionController = null;
2164
+ const normalizedError = normalizeError(error);
2165
+ this.emit("disconnected", {
2166
+ reason: "error",
2167
+ error: normalizedError
2168
+ });
2169
+ this.emit("error", normalizedError);
2170
+ this.#closeMessageStream();
2171
+ throw error;
2172
+ }
2173
+ if (this.#connectionController !== connectionController) {
2174
+ await reader.cancel();
2175
+ return;
2176
+ }
2177
+ this.emit("connected", void 0);
2178
+ this.#readStream(reader, connectionController).catch((error) => {
2179
+ this.emit("error", error);
2180
+ });
2181
+ }
2182
+ /**
2183
+ * Disconnects only the currently active transport.
2184
+ *
2185
+ * `beforeDisconnect` is emitted on every invocation, even when no
2186
+ * transport is currently active. This allows observers to react to
2187
+ * an explicit disconnect operation.
2188
+ *
2189
+ * @param context - Context describing the operation initiator.
2190
+ * @emits `beforeDisconnect` with the supplied operation context.
2191
+ * @emits `disconnected` with reason `"disconnect"` when an active
2192
+ * transport is actually disconnected.
2193
+ */
2194
+ disconnect(context = {}) {
2195
+ this.emit("beforeDisconnect", context);
2196
+ if (this.#connectionController) {
2197
+ const connectionController = this.#connectionController;
2198
+ this.#connectionController = null;
2199
+ connectionController.abort();
2200
+ this.emit("disconnected", {
2201
+ reason: "disconnect",
2202
+ source: context.source
2203
+ });
2204
+ }
2205
+ this.#resetEventParser();
2206
+ }
2207
+ /**
2208
+ * Terminates the session and disables attached visibility handling until
2209
+ * the same instance is manually {@link connect connected} again.
2210
+ *
2211
+ * Closes {@link messages} and emits `"closed"`.
2212
+ */
2213
+ close() {
2214
+ this.disconnect();
2215
+ this.#closeMessageStream();
2216
+ this.emit("closed", void 0);
2217
+ }
2218
+ /**
2219
+ * Performs the HTTP request and returns a reader for the response body.
2220
+ *
2221
+ * {@link SSESessionOptions.onRequest} may mutate headers (for example auth
2222
+ * tokens or `Last-Event-ID`) before the fetch runs.
2223
+ */
2224
+ async #createReader(fetchOptions) {
2225
+ const requestOptions = await this.options.onRequest(fetchOptions);
2226
+ const response = await this.options.fetch(this.#url, requestOptions);
2227
+ if (!response.ok) {
2228
+ const responseCode = response.status;
2229
+ const error = new HTTPError(responseCode, await response.text());
2230
+ this.emit("error", error);
2231
+ throw error;
2232
+ }
2233
+ if (!response.body) {
2234
+ const error = new ResponseBodyNullError();
2235
+ this.emit("error", error);
2236
+ throw error;
2237
+ }
2238
+ return response.body.getReader();
2239
+ }
2240
+ /**
2241
+ * Reads bytes from an established stream until it ends, errors, or is
2242
+ * superseded by a newer connection.
2243
+ */
2244
+ async #readStream(reader, connectionController) {
2245
+ try {
2246
+ while (this.#connectionController === connectionController) {
2247
+ const { done, value } = await reader.read();
2248
+ if (this.#connectionController !== connectionController) return;
2249
+ if (done) {
2250
+ this.#connectionController = null;
2251
+ this.emit("disconnected", { reason: "remote" });
2252
+ if (this.options.persistent) await this.connect();
2253
+ else this.#closeMessageStream();
2254
+ return;
2255
+ }
2256
+ if (!value) continue;
2257
+ for (const event of this.options.eventParser.parseEvents(value)) {
2258
+ if (event.retry) this.#retryInterval = event.retry;
2259
+ this.emit("message", event);
2260
+ this.messages.push(event);
2261
+ }
2262
+ }
2263
+ } catch (error) {
2264
+ if (connectionController !== this.#connectionController) return;
2265
+ this.#connectionController = null;
2266
+ const normalizedError = normalizeError(error);
2267
+ this.emit("disconnected", {
2268
+ reason: "error",
2269
+ error: normalizedError
2270
+ });
2271
+ if (connectionController.signal.aborted) return;
2272
+ this.emit("error", normalizedError);
2273
+ if (this.options.attemptReconnect) {
2274
+ if (this.#retryInterval) await new Promise((resolve) => setTimeout(resolve, this.#retryInterval));
2275
+ await this.connect();
2276
+ } else this.#closeMessageStream();
2277
+ }
2278
+ }
2279
+ /** Clears partial SSE frames left over from an abandoned transport. */
2280
+ #resetEventParser() {
2281
+ this.options.eventParser.reset();
2282
+ }
2283
+ /**
2284
+ * Creates a new {@link messages} iterator when the previous one was closed
2285
+ * by a terminal disconnect or server stream end.
2286
+ */
2287
+ #ensureMessageStreamOpen() {
2288
+ if (!this.messages.closed) return;
2289
+ this.messages = new AsyncPushIterator();
2290
+ }
2291
+ /** Ends the message iteration loop for the current connection span. */
2292
+ #closeMessageStream() {
2293
+ if (this.messages.closed) return;
2294
+ this.messages.close();
2295
+ }
2296
+ };
2297
+
2298
+ //#endregion
2299
+ //#region source/template/errors.ts
1975
2300
  /**
1976
- * Validation schema for a constant value definition.
1977
- *
1978
- * ```
1979
- * {
1980
- * "constants": {
1981
- * "[id]": { } ← this schema
1982
- * }
1983
- * }
1984
- * ```
2301
+ * Thrown when the provided template does not satisfy the XOTemplate schema.
1985
2302
  */
1986
- const xoTemplateConstantSchema = xoTemplateViewPropertiesSchema.extend({
1987
- type: xoTemplatePrimitiveTypeSchema.describe("The data type of this constant."),
1988
- value: z.unknown().describe("The value of this constant."),
1989
- hint: z.unknown().optional().describe("An optional hint to help apps and users understand what this constant represents.")
1990
- }).strict();
2303
+ var TemplateInvalidError = class extends Error {
2304
+ constructor(details) {
2305
+ const message = `Template invalid: ${details}`;
2306
+ super(message);
2307
+ this.name = "TemplateInvalidError";
2308
+ }
2309
+ };
1991
2310
  /**
1992
- * Validation schema for a data field definition.
1993
- *
1994
- * ```
1995
- * {
1996
- * "data": {
1997
- * "[id]": { } ← this schema
1998
- * }
1999
- * }
2000
- * ```
2311
+ * Thrown when a string passed to `deserializeTemplate` cannot be parsed as JSON.
2001
2312
  */
2002
- const xoTemplateDataSchema = z.object({
2003
- type: xoTemplatePrimitiveTypeSchema.describe("The data type of this data field."),
2004
- value: z.unknown().describe("The value for this data field."),
2005
- hint: z.unknown().optional().describe("An optional hint to help apps and users understand this data field.")
2006
- }).strict();
2313
+ var TemplateJsonMalformedError = class extends Error {
2314
+ constructor(reason) {
2315
+ super(`Template JSON malformed, expected a valid JSON string: ${reason}`);
2316
+ this.name = "TemplateJsonMalformedError";
2317
+ }
2318
+ };
2007
2319
  /**
2008
- * Validation schema for an import default value intent. Extends the base intent with optional
2009
- * view properties that the engine evaluates at runtime to produce human-readable output.
2010
- *
2011
- * ```
2012
- * {
2013
- * "variables": {
2014
- * "[id]": {
2015
- * "importDefaultValue": { } ← this schema
2016
- * }
2017
- * }
2018
- * }
2019
- * ```
2320
+ * Thrown when `serializeTemplate` fails to produce a JSON string from the template.
2020
2321
  */
2021
- const xoTemplateImportDefaultValueSchema = xoTemplateIntentSchema.extend(xoTemplateViewPropertiesSchema.partial().shape).strict();
2322
+ var TemplateSerializationFailedError = class extends Error {
2323
+ constructor(reason) {
2324
+ super(`Template serialization failed: ${reason}`);
2325
+ this.name = "TemplateSerializationFailedError";
2326
+ }
2327
+ };
2328
+
2329
+ //#endregion
2330
+ //#region source/template/serialization.ts
2022
2331
  /**
2023
- * Validation schema for a variable definition.
2332
+ * Serializes an XOTemplate to a JSON string. Encodes `bigint` and `Uint8Array` fields in
2333
+ * Extended JSON format so they can be reconstructed by `deserializeTemplate`.
2024
2334
  *
2025
- * ```
2026
- * {
2027
- * "variables": {
2028
- * "[id]": { } ← this schema
2029
- * }
2030
- * }
2031
- * ```
2335
+ * @param template The template to serialize.
2336
+ * @returns A JSON string representation of the template.
2337
+ * @throws {TemplateSerializationFailedError} If the template cannot be serialized to JSON.
2032
2338
  */
2033
- const xoTemplateVariableSchema = xoTemplateViewPropertiesSchema.extend({
2034
- type: xoTemplatePrimitiveTypeSchema.optional().describe("The data type of this variable."),
2035
- hint: z.unknown().optional().describe("A hint to help users understand what value to provide."),
2036
- importDefaultValue: xoTemplateImportDefaultValueSchema.optional().describe("A neutral intent that the engine uses to populate the default value for this variable.")
2037
- }).strict();
2339
+ const serializeTemplate = (template) => {
2340
+ try {
2341
+ return JSON.stringify(template, extendedJsonReplacer);
2342
+ } catch (serializationError) {
2343
+ throw new TemplateSerializationFailedError(serializationError instanceof Error ? serializationError.message : "unknown error while serializing template");
2344
+ }
2345
+ };
2038
2346
  /**
2039
- * Validation schema for a resource reference attached to a template element. Extends view
2040
- * properties with a URL pointing to external documentation or tooling.
2347
+ * Deserializes a JSON string back into an XOTemplate object. Restores `bigint` and
2348
+ * `Uint8Array` fields encoded in Extended JSON format by `serializeTemplate`.
2041
2349
  *
2042
- * ```
2043
- * {
2044
- * "resources": [
2045
- * { "name": "...", "description": "...", "url": "..." } ← this schema
2046
- * ]
2047
- * }
2048
- * ```
2350
+ * @param serializedTemplate - A JSON string of an XOTemplate object.
2351
+ * @returns The reconstructed XOTemplate object.
2352
+ * @throws {TemplateJsonMalformedError} If the serialized template is not valid JSON.
2049
2353
  */
2050
- const xoTemplateResourceSchema = xoTemplateViewPropertiesSchema.extend({ url: z.string().describe("The URL for this resource.") }).strict();
2354
+ const deserializeTemplate = (serializedTemplate) => {
2355
+ try {
2356
+ return JSON.parse(serializedTemplate, extendedJsonReviver);
2357
+ } catch (parsingError) {
2358
+ throw new TemplateJsonMalformedError(parsingError instanceof Error ? parsingError.message : "unknown error while deserializing template");
2359
+ }
2360
+ };
2361
+
2362
+ //#endregion
2363
+ //#region source/template/identifier.ts
2051
2364
  /**
2052
- * Validation schema for an icon reference.
2365
+ * Generates a deterministic template identifier by hashing the template.
2053
2366
  *
2054
- * ```
2055
- * {
2056
- * "icons": [
2057
- * { "name": "...", "hash": "..." } ← this schema
2058
- * ]
2059
- * }
2060
- * ```
2061
- */
2062
- const xoTemplateIconSchema = xoTemplateViewPropertiesSchema.pick({ name: true }).extend({ hash: z.string().describe("The identifier of the icon.") }).strict();
2063
- /**
2064
- * Validation schema for the defaults block of a template.
2367
+ * Note: This expects a template that has been validated by `parseTemplate`.
2065
2368
  *
2066
- * ```
2067
- * {
2068
- * "defaults": { } ← this schema
2069
- * }
2070
- * ```
2071
- */
2072
- const xoTemplateDefaultsSchema = z.object({
2073
- change: xoTemplateOutputIntentSchema.optional().describe("Instructions for how to construct automated change output."),
2074
- address: xoTemplateLockingScriptIntentSchema.optional().describe("Configure the default intent structure for creating receiving addresses / locking scripts."),
2075
- receive: xoTemplateOutputIntentSchema.optional().describe("Configure the default intent structure for creating receive outputs.")
2076
- }).strict();
2077
- /**
2078
- * Validation schema for the full XOTemplate type.
2369
+ * @param template - The template to generate an identifier for.
2370
+ * @returns The sha256 hex identifier for the template.
2079
2371
  */
2080
- const xoTemplateSchema = xoTemplateViewPropertiesSchema.extend({
2081
- $schema: z.string().describe("The URI that identifies the JSON Schema used by this template. This enables documentation, autocompletion, and validation in JSON documents."),
2082
- version: z.string().optional().describe("A string identifying the version of this template."),
2083
- supported: z.array(bchVmVersionSchema).min(1).describe("The BCH VM versions that this template supports. At least one version is required."),
2084
- defaults: xoTemplateDefaultsSchema.optional().describe("Optional default settings used in this template."),
2085
- roles: z.record(z.string(), xoTemplateViewPropertiesSchema).describe("The roles defined in this template."),
2086
- start: z.array(xoTemplateActionIntentSchema).describe("A list of entry points defining which actions are available at the start."),
2087
- actions: z.record(z.string(), xoTemplateActionSchema).describe("The actions defined in this template."),
2088
- data: z.record(z.string(), xoTemplateDataSchema).optional().describe("The data fields defined in this template."),
2089
- transactions: z.record(z.string(), xoTemplateTransactionSchema).optional().describe("The transaction templates defined in this template."),
2090
- inputs: z.record(z.string(), xoTemplateInputSchema).describe("The inputs defined in this template."),
2091
- outputs: z.record(z.string(), xoTemplateOutputSchema).describe("The outputs defined in this template."),
2092
- lockingScripts: z.record(z.string(), xoTemplateLockingScriptSchema).describe("The locking script templates defined in this template."),
2093
- scripts: z.record(z.string(), z.string()).describe("Scripts used in this template. Keys are script identifiers, values are bytecode or CashASM expressions."),
2094
- constants: z.record(z.string(), xoTemplateConstantSchema).optional().describe("The constants defined in this template."),
2095
- variables: z.record(z.string(), xoTemplateVariableSchema).optional().describe("The variables that must be provided for use in the template's scripts."),
2096
- resources: z.array(xoTemplateResourceSchema).optional().describe("Resource references providing external documentation or tooling links."),
2097
- icons: z.array(xoTemplateIconSchema).optional().describe("The icons available for use throughout the template."),
2098
- scenarios: z.unknown().optional().describe("The scenarios defined in this template.")
2099
- }).strict();
2372
+ const generateTemplateIdentifier = (template) => {
2373
+ const serializedTemplate = serializeTemplate(template);
2374
+ return binToHex(sha256.hash(utf8ToBin(serializedTemplate)));
2375
+ };
2100
2376
 
2101
2377
  //#endregion
2102
2378
  //#region source/template/parser.ts
@@ -2384,7 +2660,7 @@ const collectFromParsedScript = (parameters) => {
2384
2660
  * first seen.
2385
2661
  * A visited id is skipped on later encounters. Opcodes and literals are ignored. Unrelated scripts
2386
2662
  * in the map are not visited. An Identifier inside a nested Push is collected from that inner Push,
2387
- * for example `<$(<ownerKey.public_key> OP_HASH160)>` collects `ownerKey.public_key`.
2663
+ * for example `<$(<someKey.public_key> OP_HASH160)>` collects `someKey.public_key`.
2388
2664
  *
2389
2665
  * @param {string} evaluation - CashAssembly evaluation or script id text to scan.
2390
2666
  * @param {CashAssemblyTemplateScripts} [templateScripts] - Optional template scripts map used to visit nested script ids.
@@ -3009,5 +3285,5 @@ const compileCashAssemblyString = (parameters) => {
3009
3285
  };
3010
3286
 
3011
3287
  //#endregion
3012
- export { AsyncPushIterator, CashAssemblyBlockCommentUnclosedError, CashAssemblyCompilationFailedError, CashAssemblyEvaluationUnclosedError, CashAssemblyIdentifierCollisionError, CashAssemblyNumberNotSafeIntegerError, CashAssemblyPrimitiveMethodMissingError, CashAssemblyPrimitiveVariableMissingError, CashAssemblyQuotedLiteralUnclosedError, CashAssemblyRequiredVariableMissingError, CashAssemblyUnsupportedValueTypeError, CashAssemblyVariableTypeMismatchError, CashAssemblyVmNumberDecodeError, EventEmitter, ExponentialBackoff, ExponentialBackoffExternallyAbortable, ExponentialBackoffMaxRetriesHitError, ExponentialBackoffNonIntegerError, ExponentialBackoffNumberNotFiniteError, ExponentialBackoffNumberOutOfBoundsError, ExponentialBackoffNumberTooSmallError, ExponentialBackoffStoppedRetriesError, ExternallyAbortedExponentialBackoffExternalSignalAbortedError, ExternallyAbortedExponentialBackoffInternalSignalAbortedError, SSEEventParser, SSESession, TemplateInvalidError, TemplateJsonMalformedError, TemplateSerializationFailedError, VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH, VIEW_PROPERTIES_ICON_MAX_LENGTH, VIEW_PROPERTIES_NAME_MAX_LENGTH, WaitForTimeoutError, bchVmVersionSchema, buildErrorDescription, collectVariablesUsingParseScript, compileCashAssemblyString, deepFreeze, extendedJsonReplacer, extendedJsonReviver, extractCashAssemblyEvaluations, fromExtendedJson, generateTemplateIdentifier, isCashAssemblyExpression, isWithinBounds, normalizeError, parseTemplate, satoshisSchema, scriptToScriptHash, serializeTemplate, toExtendedJson, uint8ArraySchema, xoTemplateActionIntentSchema, xoTemplateActionRequirementsSchema, xoTemplateActionRoleRequirementsSchema, xoTemplateActionRoleSchema, xoTemplateActionSchema, xoTemplateAssetAmountsSchema, xoTemplateConstantSchema, xoTemplateDataSchema, xoTemplateDefaultsSchema, xoTemplateIconSchema, xoTemplateImportDefaultValueSchema, xoTemplateInputSchema, xoTemplateIntentSchema, xoTemplateLockingScriptIntentSchema, xoTemplateLockingScriptRoleSchema, xoTemplateLockingScriptSchema, xoTemplateLockingTypeSchema, xoTemplateNftCapabilitySchema, xoTemplateNonFungibleTokenDetailsSchema, xoTemplateOutputIntentSchema, xoTemplateOutputSchema, xoTemplatePrimitiveTypeSchema, xoTemplateResourceSchema, xoTemplateRoleSlotSchema, xoTemplateRoleSlotsRequirementsSchema, xoTemplateSchema, xoTemplateStateSchema, xoTemplateTokenSchema, xoTemplateTransactionInputSchema, xoTemplateTransactionOutputSchema, xoTemplateTransactionRoleDataSchema, xoTemplateTransactionSchema, xoTemplateVariableSchema, xoTemplateViewPropertiesSchema };
3288
+ export { AsyncPushIterator, CashAssemblyBlockCommentUnclosedError, CashAssemblyCompilationFailedError, CashAssemblyEvaluationUnclosedError, CashAssemblyIdentifierCollisionError, CashAssemblyNumberNotSafeIntegerError, CashAssemblyPrimitiveMethodMissingError, CashAssemblyPrimitiveVariableMissingError, CashAssemblyQuotedLiteralUnclosedError, CashAssemblyRequiredVariableMissingError, CashAssemblyUnsupportedValueTypeError, CashAssemblyVariableTypeMismatchError, CashAssemblyVmNumberDecodeError, EventEmitter, ExponentialBackoff, ExponentialBackoffExternallyAbortable, ExponentialBackoffMaxRetriesHitError, ExponentialBackoffNonIntegerError, ExponentialBackoffNumberNotFiniteError, ExponentialBackoffNumberOutOfBoundsError, ExponentialBackoffNumberTooSmallError, ExponentialBackoffStoppedRetriesError, ExternallyAbortedExponentialBackoffExternalSignalAbortedError, ExternallyAbortedExponentialBackoffInternalSignalAbortedError, InvitationInvalidError, InvitationJsonMalformedError, InvitationSerializationFailedError, SSEEventParser, SSESession, TemplateInvalidError, TemplateJsonMalformedError, TemplateSerializationFailedError, VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH, VIEW_PROPERTIES_ICON_MAX_LENGTH, VIEW_PROPERTIES_NAME_MAX_LENGTH, WaitForTimeoutError, bchVmVersionSchema, buildErrorDescription, collectVariablesUsingParseScript, compileCashAssemblyString, deepFreeze, deserializeInvitation, extendedJsonReplacer, extendedJsonReviver, extractCashAssemblyEvaluations, fromExtendedJson, generateTemplateIdentifier, isCashAssemblyExpression, isWithinBounds, normalizeError, outputTokenSchema, parseInvitation, parseTemplate, satoshisSchema, scriptToScriptHash, serializeInvitation, serializeTemplate, toExtendedJson, uint8ArraySchema, xoInvitationActionReferenceSchema, xoInvitationCommitDataSchema, xoInvitationCommitSchema, xoInvitationCreatedResultSchema, xoInvitationInputSchema, xoInvitationMergesWithReferenceSchema, xoInvitationOutputSchema, xoInvitationResolutionSchema, xoInvitationResultSchema, xoInvitationSchema, xoInvitationTokenSchema, xoInvitationTransactionBroadcastedResultSchema, xoInvitationTransactionCreatedResultSchema, xoInvitationVariableSchema, xoInvitationVariableValueSchema, xoTemplateActionIntentSchema, xoTemplateActionRequirementsSchema, xoTemplateActionRoleRequirementsSchema, xoTemplateActionRoleSchema, xoTemplateActionSchema, xoTemplateAssetAmountsSchema, xoTemplateConstantSchema, xoTemplateDataSchema, xoTemplateDefaultsSchema, xoTemplateIconSchema, xoTemplateImportDefaultValueSchema, xoTemplateInputSchema, xoTemplateIntentSchema, xoTemplateLockingScriptIntentSchema, xoTemplateLockingScriptRoleSchema, xoTemplateLockingScriptSchema, xoTemplateLockingTypeSchema, xoTemplateNftCapabilitySchema, xoTemplateNonFungibleTokenDetailsSchema, xoTemplateOutputIntentSchema, xoTemplateOutputSchema, xoTemplatePrimitiveTypeSchema, xoTemplateResourceSchema, xoTemplateRoleSlotSchema, xoTemplateRoleSlotsRequirementsSchema, xoTemplateSchema, xoTemplateStateSchema, xoTemplateTokenSchema, xoTemplateTransactionInputSchema, xoTemplateTransactionOutputSchema, xoTemplateTransactionRoleDataSchema, xoTemplateTransactionSchema, xoTemplateVariableSchema, xoTemplateViewPropertiesSchema };
3013
3289
  //# sourceMappingURL=index.mjs.map