@xo-cash/utils 0.0.2 → 0.0.3-development.15744988955

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,6 +1,7 @@
1
- import { binToHex, hexToBin, sha256, utf8ToBin } from "@bitauth/libauth";
2
- import { BchVmVersions, XOTemplateLockingTypes, XOTemplateNftCapabilities, XOTemplatePrimitiveTypes } from "@xo-cash/types";
1
+ import { bigIntToVmNumber, binToHex, binToUtf8, createCompilerBch, hexToBin, sha256, utf8ToBin, vmNumberToBigInt } from "@bitauth/libauth";
2
+ import { BchVmVersions, XOTemplateBaseTypes, XOTemplateLockingTypes, XOTemplateNftCapabilities, XOTemplatePrimitiveTypes } from "@xo-cash/types";
3
3
  import { z } from "zod";
4
+ import { FungibleTokenAmount, NFTCommitment, PublicKey, Satoshis, SchnorrSignature, TemplateIdentifier, Timestamp, TokenCategory, TransactionHash } from "@xo-cash/primitives";
4
5
 
5
6
  //#region source/extended-json.ts
6
7
  /**
@@ -51,6 +52,24 @@ const extendedJsonReviver = (_propertyKey, value) => {
51
52
  if (uint8arrayPatternMatch) return hexToBin(uint8arrayPatternMatch.groups.hex);
52
53
  return value;
53
54
  };
55
+ /**
56
+ * Serializes an object to a string using the {@link extendedJsonReplacer}.
57
+ *
58
+ * @param object The object to serialize.
59
+ * @returns The string representation of the object in Extended JSON format.
60
+ */
61
+ const toExtendedJson = (object) => {
62
+ return JSON.stringify(object, extendedJsonReplacer);
63
+ };
64
+ /**
65
+ * Deserializes a string to an object using the {@link extendedJsonReviver}.
66
+ *
67
+ * @param serializedObject The string to deserialize.
68
+ * @returns The object reconstructed from the string.
69
+ */
70
+ const fromExtendedJson = (serializedObject) => {
71
+ return JSON.parse(serializedObject, extendedJsonReviver);
72
+ };
54
73
 
55
74
  //#endregion
56
75
  //#region source/script.ts
@@ -63,6 +82,282 @@ const scriptToScriptHash = (script) => {
63
82
  return binToHex(sha256.hash(script).reverse());
64
83
  };
65
84
 
85
+ //#endregion
86
+ //#region source/sse-session/async-push-iterator.ts
87
+ /**
88
+ * An async iterable queue that bridges push-based producers and pull-based consumers.
89
+ *
90
+ * Composes an internal {@link ReadableStream} instead of extending it, so producers
91
+ * call {@link push} while consumers use standard async iteration (`for await...of`).
92
+ *
93
+ * ```ts
94
+ * const messages = new AsyncPushIterator<SSEvent>();
95
+ *
96
+ * // Producer (elsewhere)
97
+ * messages.push(event);
98
+ *
99
+ * // Consumer
100
+ * for await (const event of messages) {
101
+ * handle(event);
102
+ * }
103
+ * ```
104
+ *
105
+ * {@link Symbol.asyncIterator} returns `stream.values({ preventCancel: true })` so
106
+ * breaking out of `for await...of` does not cancel the underlying stream. That
107
+ * matters for long-lived sessions where the producer keeps pushing after a consumer
108
+ * stops reading early (for example, test helpers that only collect a fixed count).
109
+ */
110
+ var AsyncPushIterator = class {
111
+ /** ReadableStream backing the async iterator returned from {@link Symbol.asyncIterator}. */
112
+ #stream;
113
+ /** Controller used to enqueue values and close the stream from {@link push} and {@link close}. */
114
+ #controller;
115
+ /** When true, no more values are accepted and iteration eventually completes. */
116
+ #closed = false;
117
+ constructor() {
118
+ this.#stream = new ReadableStream({ start: (controller) => {
119
+ this.#controller = controller;
120
+ } });
121
+ }
122
+ /**
123
+ * Flag indicating if the iterator is closed.
124
+ */
125
+ get closed() {
126
+ return this.#closed;
127
+ }
128
+ /**
129
+ * Enqueues a value for the consumer.
130
+ *
131
+ * After {@link close}, pushes are silently dropped.
132
+ *
133
+ * @param value - The next value to yield from the iterator.
134
+ */
135
+ push(value) {
136
+ if (this.#closed) return;
137
+ this.#controller?.enqueue(value);
138
+ }
139
+ /**
140
+ * Causes any future interactions with the associated stream to error with {@link error}.
141
+ * Calling this will also clear the pending values immediately, so iterators that were listening will not receive them.
142
+ *
143
+ * @param error - The error to throw from the stream.
144
+ */
145
+ error(error) {
146
+ if (this.#closed) return;
147
+ this.#closed = true;
148
+ this.#controller?.error(error);
149
+ }
150
+ /**
151
+ * Ends the stream.
152
+ *
153
+ * Marks the iterator closed so future {@link push} calls are ignored.
154
+ * Buffered values are still yielded before iteration completes.
155
+ */
156
+ close() {
157
+ this.#closed = true;
158
+ try {
159
+ this.#controller?.close();
160
+ } catch {}
161
+ }
162
+ /**
163
+ * Returns an async iterator over the composed stream.
164
+ *
165
+ * Uses `preventCancel: true` so early `break` from `for await...of` does not
166
+ * close the stream and block later pushes.
167
+ *
168
+ * Because values are discarded after being read, only a single consumer is supported.
169
+ * Additional consumers will receive a stream lock error. Unread values will be preserved until {@link close} is called.
170
+ */
171
+ [Symbol.asyncIterator]() {
172
+ return this.#stream.values({ preventCancel: true });
173
+ }
174
+ };
175
+
176
+ //#endregion
177
+ //#region source/sse-session/constants.ts
178
+ /**
179
+ * Regex that splits decoded SSE text into lines.
180
+ *
181
+ * The SSE wire format is line-oriented (`field: value` per line). Servers may
182
+ * send `\r\n` (HTTP default), `\n` (Unix), or `\r` (legacy Mac). Matching all
183
+ * three keeps parsing correct regardless of platform or server implementation.
184
+ */
185
+ const SSE_LINE_ENDINGS = /\r\n|\r|\n/;
186
+ /**
187
+ * Regex that matches the single optional leading space in an SSE field value.
188
+ *
189
+ * Per the SSE spec, `field: value` may include one space immediately after the
190
+ * colon; that space is not part of the value. Used with `.replace()` to strip
191
+ * it when parsing lines such as `data: hello` → `hello`.
192
+ */
193
+ const SSE_FIELD_VALUE_REGEX = /^ /;
194
+ /**
195
+ * Regex that matches a trailing newline at the end of a string.
196
+ *
197
+ * Multiple `data:` lines in one event are joined with `\n`. When the event is
198
+ * completed, this removes any stray trailing newline so callers receive the
199
+ * payload without an extra line break at the end.
200
+ */
201
+ const SSE_TRAILING_NEWLINE_REGEX = /\n$/;
202
+ /**
203
+ * The newline character used when normalizing SSE text internally.
204
+ *
205
+ * Used to join consecutive `data:` lines into one payload and to reassemble
206
+ * buffered partial lines between streamed chunks before the next parse call.
207
+ */
208
+ const NEW_LINE = "\n";
209
+
210
+ //#endregion
211
+ //#region source/sse-session/sse-event-parser.ts
212
+ /**
213
+ * Incrementally parses Server-Sent Events (SSE) from streamed byte chunks.
214
+ *
215
+ * SSE payloads are line-oriented: each event is a sequence of `field: value`
216
+ * lines terminated by a blank line. This parser accepts arbitrary chunk
217
+ * boundaries from a live HTTP response body and emits only complete events.
218
+ *
219
+ * Typical usage is one parser instance per connection, calling {@link parseEvents}
220
+ * for each chunk received from the stream:
221
+ *
222
+ * ```ts
223
+ * const parser = new SSEEventParser();
224
+ *
225
+ * for await (const chunk of response.body) {
226
+ * for (const event of parser.parseEvents(chunk)) {
227
+ * // handle event.data, event.event, event.id, event.retry
228
+ * }
229
+ * }
230
+ * ```
231
+ *
232
+ * Supported fields follow the SSE spec: `data`, `event`, `id`, and `retry`.
233
+ * Multiple `data:` lines in one event are joined with `\n`. An event is only
234
+ * emitted once a blank line is seen and at least one `data` field was collected.
235
+ */
236
+ var SSEEventParser = class {
237
+ #textDecoder;
238
+ /** Bytes from a partial line or incomplete event, carried over to the next chunk. */
239
+ #messageBuffer = "";
240
+ /**
241
+ * Creates a parser for one SSE stream.
242
+ *
243
+ * Inject custom encoders in tests or when a non-default character encoding
244
+ * is required; production callers can rely on the defaults.
245
+ *
246
+ * @param options - Optional text encoders for decode/encode of stream bytes.
247
+ */
248
+ constructor(options = {}) {
249
+ this.#textDecoder = options.textDecoder ?? new TextDecoder();
250
+ }
251
+ /**
252
+ * Clears any buffered bytes from a partial line or incomplete event.
253
+ *
254
+ * Call when abandoning a transport so the next connection does not prepend
255
+ * stale bytes to incoming chunks.
256
+ */
257
+ reset() {
258
+ this.#messageBuffer = "";
259
+ this.#textDecoder.decode();
260
+ }
261
+ /**
262
+ * Parses all complete SSE events contained in a newly received chunk.
263
+ *
264
+ * The chunk is appended to any bytes buffered from earlier calls. Complete
265
+ * events (blank-line delimited blocks with at least one `data` field) are
266
+ * returned immediately; any trailing partial line or in-progress event stays
267
+ * in the internal buffer until a later chunk completes it.
268
+ *
269
+ * @param chunk - Newly received SSE stream bytes.
270
+ * @returns Zero or more complete parsed SSE events from this chunk.
271
+ */
272
+ parseEvents(chunk) {
273
+ const lines = this.getBufferedLines(chunk);
274
+ const eventLines = lines.slice(0, -1);
275
+ const events = [];
276
+ let event = {};
277
+ let processedLineCount = 0;
278
+ for (const [index, line] of eventLines.entries()) {
279
+ if (line === "") {
280
+ if (event.data !== void 0) {
281
+ events.push(this.completeEvent(event));
282
+ event = {};
283
+ processedLineCount = index + 1;
284
+ }
285
+ continue;
286
+ }
287
+ this.parseLine(line, event);
288
+ }
289
+ this.storeRemainingLines(lines, processedLineCount);
290
+ return events;
291
+ }
292
+ /**
293
+ * Appends a new chunk to the buffered bytes and splits the combined payload
294
+ * into lines.
295
+ *
296
+ * Accepts `\r\n`, `\r`, and `\n` line endings so events parse correctly
297
+ * regardless of server or platform conventions.
298
+ */
299
+ getBufferedLines(chunk) {
300
+ this.#messageBuffer += this.#textDecoder.decode(chunk, { stream: true });
301
+ return this.#messageBuffer.split(SSE_LINE_ENDINGS);
302
+ }
303
+ /**
304
+ * Parses one SSE field line into an in-progress event.
305
+ *
306
+ * Lines without a colon are ignored. A single optional space after the colon
307
+ * is stripped from the field value, per the SSE spec.
308
+ */
309
+ parseLine(line, event) {
310
+ const colonIndex = line.indexOf(":");
311
+ if (colonIndex === -1) return;
312
+ const field = line.slice(0, colonIndex);
313
+ const value = line.slice(colonIndex + 1).replace(SSE_FIELD_VALUE_REGEX, "");
314
+ switch (field) {
315
+ case "data":
316
+ event.data = event.data ? `${event.data}${NEW_LINE}${value}` : value;
317
+ return;
318
+ case "event":
319
+ event.event = value;
320
+ return;
321
+ case "id":
322
+ event.id = value;
323
+ return;
324
+ case "retry":
325
+ this.parseRetry(value, event);
326
+ return;
327
+ }
328
+ }
329
+ /**
330
+ * Applies a numeric `retry:` field to an in-progress event.
331
+ *
332
+ * Non-numeric values are ignored rather than failing the parse.
333
+ */
334
+ parseRetry(value, event) {
335
+ const retry = parseInt(value, 10);
336
+ if (!isNaN(retry)) event.retry = retry;
337
+ }
338
+ /**
339
+ * Constructs a completed SSE event from accumulated fields.
340
+ *
341
+ * Trims a trailing newline from multi-line `data` values so callers receive
342
+ * the payload without an extra line break at the end.
343
+ */
344
+ completeEvent(event) {
345
+ return {
346
+ ...event,
347
+ data: event.data?.replace(SSE_TRAILING_NEWLINE_REGEX, "")
348
+ };
349
+ }
350
+ /**
351
+ * Preserves incomplete trailing lines for the next received chunk.
352
+ *
353
+ * Only lines that were fully processed (through a completed event boundary)
354
+ * are discarded; the remainder is re-encoded into {@link messageBuffer}.
355
+ */
356
+ storeRemainingLines(lines, processedLineCount) {
357
+ this.#messageBuffer = lines.slice(processedLineCount).join(NEW_LINE);
358
+ }
359
+ };
360
+
66
361
  //#endregion
67
362
  //#region source/template/errors.ts
68
363
  /**
@@ -210,8 +505,13 @@ const xoTemplateNftCapabilitySchema = z.enum(XOTemplateNftCapabilities);
210
505
  */
211
506
  const xoTemplateLockingTypeSchema = z.enum(XOTemplateLockingTypes);
212
507
  /**
213
- * Validation schema for a primitive type identifier. Defines the set of primitive types
214
- * that can be declared in an XO template. Used by constants, variables, and data fields.
508
+ * Validation schema for a base type identifier.
509
+ * Accepts values from XOTemplateBaseTypes for the `type` field on constants, variables, and data.
510
+ */
511
+ const xoTemplateBaseTypeSchema = z.enum(XOTemplateBaseTypes);
512
+ /**
513
+ * Validation schema for a primitive type identifier.
514
+ * Accepts values from XOTemplatePrimitiveTypes for the `hint` field on constants, variables, and data.
215
515
  */
216
516
  const xoTemplatePrimitiveTypeSchema = z.enum(XOTemplatePrimitiveTypes);
217
517
  /**
@@ -682,9 +982,9 @@ const xoTemplateTransactionSchema = xoTemplateViewPropertiesSchema.extend({
682
982
  * ```
683
983
  */
684
984
  const xoTemplateConstantSchema = xoTemplateViewPropertiesSchema.extend({
685
- type: xoTemplatePrimitiveTypeSchema.describe("The data type of this constant."),
985
+ type: xoTemplateBaseTypeSchema.describe("The data type of this constant."),
686
986
  value: z.unknown().describe("The value of this constant."),
687
- hint: z.string().optional().describe("An optional hint to help apps and users understand what this constant represents.")
987
+ hint: xoTemplatePrimitiveTypeSchema.optional().describe("An optional hint to help apps and users understand what this constant represents.")
688
988
  }).strict();
689
989
  /**
690
990
  * Validation schema for a data field definition.
@@ -698,9 +998,9 @@ const xoTemplateConstantSchema = xoTemplateViewPropertiesSchema.extend({
698
998
  * ```
699
999
  */
700
1000
  const xoTemplateDataSchema = z.object({
701
- type: xoTemplatePrimitiveTypeSchema.describe("The data type of this data field."),
1001
+ type: xoTemplateBaseTypeSchema.describe("The data type of this data field."),
702
1002
  value: z.unknown().describe("The value for this data field."),
703
- hint: z.string().optional().describe("An optional hint to help apps and users understand this data field.")
1003
+ hint: xoTemplatePrimitiveTypeSchema.optional().describe("An optional hint to help apps and users understand this data field.")
704
1004
  }).strict();
705
1005
  /**
706
1006
  * Validation schema for an import default value intent. Extends the base intent with optional
@@ -729,8 +1029,8 @@ const xoTemplateImportDefaultValueSchema = xoTemplateIntentSchema.extend(xoTempl
729
1029
  * ```
730
1030
  */
731
1031
  const xoTemplateVariableSchema = xoTemplateViewPropertiesSchema.extend({
732
- type: xoTemplatePrimitiveTypeSchema.optional().describe("The data type of this variable."),
733
- hint: z.string().optional().describe("A hint to help users understand what value to provide."),
1032
+ type: xoTemplateBaseTypeSchema.optional().describe("The data type of this variable."),
1033
+ hint: xoTemplatePrimitiveTypeSchema.optional().describe("A hint to help users understand what value to provide."),
734
1034
  importDefaultValue: xoTemplateImportDefaultValueSchema.optional().describe("A neutral intent that the engine uses to populate the default value for this variable.")
735
1035
  }).strict();
736
1036
  /**
@@ -813,5 +1113,427 @@ const parseTemplate = (inputTemplate) => {
813
1113
  };
814
1114
 
815
1115
  //#endregion
816
- export { TemplateInvalidError, TemplateJsonMalformedError, TemplateSerializationFailedError, VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH, VIEW_PROPERTIES_ICON_MAX_LENGTH, VIEW_PROPERTIES_NAME_MAX_LENGTH, bchVmVersionSchema, buildErrorDescription, extendedJsonReplacer, extendedJsonReviver, generateTemplateIdentifier, parseTemplate, satoshisSchema, scriptToScriptHash, serializeTemplate, 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 };
1116
+ //#region source/cash-assembly/errors.ts
1117
+ /**
1118
+ * Error thrown when a required variable is missing.
1119
+ */
1120
+ var CashAssemblyRequiredVariableMissingError = class extends Error {
1121
+ constructor(variableNames) {
1122
+ const defaultMessage = "Missing required variable";
1123
+ if (variableNames !== void 0 && variableNames.length > 0) super(`${defaultMessage}: variableNames [${variableNames.join(", ")}]`);
1124
+ else super(defaultMessage);
1125
+ }
1126
+ };
1127
+ /**
1128
+ * Error thrown when cash assembly compilation fails.
1129
+ */
1130
+ var CashAssemblyCompilationFailedError = class extends Error {
1131
+ constructor(message) {
1132
+ const defaultMessage = "Cash assembly compilation failed";
1133
+ super(message ? `${defaultMessage}: ${message}` : defaultMessage);
1134
+ }
1135
+ };
1136
+ /**
1137
+ * Error thrown when a variable's runtime type does not match the type required for compilation.
1138
+ */
1139
+ var CashAssemblyVariableTypeMismatchError = class extends Error {
1140
+ constructor(variableKey, expectedType, actualType) {
1141
+ super(`Variable type mismatch: variableKey "${variableKey}", expected ${expectedType}, got ${actualType}`);
1142
+ }
1143
+ };
1144
+ /**
1145
+ * Error thrown when a supported primitive hint does not expose the requested method.
1146
+ */
1147
+ var CashAssemblyPrimitiveMethodMissingError = class extends Error {
1148
+ constructor(identifier, methodName, hint) {
1149
+ super(`CashAssembly primitive method does not exist: identifier "${identifier}", methodName "${methodName}", hint "${hint}"`);
1150
+ }
1151
+ };
1152
+ /**
1153
+ * Error thrown when a value cannot be resolved as bytes.
1154
+ */
1155
+ var CashAssemblyUnsupportedValueTypeError = class extends Error {
1156
+ constructor(identifier, returnedType) {
1157
+ super(`CashAssembly value type is unsupported for byte resolution: identifier "${identifier}", returnedType "${returnedType}"`);
1158
+ }
1159
+ };
1160
+ /**
1161
+ * Error thrown when a number cannot be safely encoded as a CashAssembly VM number.
1162
+ */
1163
+ var CashAssemblyNumberNotSafeIntegerError = class extends Error {
1164
+ constructor(identifier, value) {
1165
+ super(`CashAssembly number is not a safe integer: identifier "${identifier}", got ${String(value)}`);
1166
+ }
1167
+ };
1168
+ /**
1169
+ * Error thrown when a supported primitive is selected but its value is missing when provided in the variables map.
1170
+ */
1171
+ var CashAssemblyPrimitiveVariableMissingError = class extends Error {
1172
+ constructor(identifier, variableName) {
1173
+ super(`CashAssembly primitive variable is missing from the variables map: identifier "${identifier}", variableName "${variableName}"`);
1174
+ }
1175
+ };
1176
+ /**
1177
+ * Error thrown when compiled evaluation bytes cannot be decoded as a VM number.
1178
+ */
1179
+ var CashAssemblyVmNumberDecodeError = class extends Error {
1180
+ constructor(reason) {
1181
+ super(`CashAssembly evaluation could not be decoded as a VM number: ${reason}`);
1182
+ }
1183
+ };
1184
+
1185
+ //#endregion
1186
+ //#region source/cash-assembly/defaults.ts
1187
+ /**
1188
+ * Detects whether a string is a pure CashAssembly expression.
1189
+ *
1190
+ * CashAssembly expressions look like `$(<variable>)` or `$(<a> <b>)`. This pattern checks
1191
+ * that the entire string is one such expression and nothing else. It will not match if
1192
+ * there is other text surrounding the expression.
1193
+ *
1194
+ * For example:
1195
+ * `$(<fee>)` matches (a full expression)
1196
+ * `OP_DUP $(<fee>)` does not match (extra text before it)
1197
+ * `$()` does not match (empty expression)
1198
+ */
1199
+ const CASHASSEMBLY_EXPRESSION_PATTERN = /^\$\([^)]+\)$/;
1200
+ /**
1201
+ * Finds all CashAssembly evaluations embedded in a larger string.
1202
+ *
1203
+ * An evaluation looks like `$(...)`, for example `$(<fee>)` or `$(<a> <b>)`. This pattern
1204
+ * locates every occurrence in the input and returns them all (global flag `g`).
1205
+ * Empty evaluations `$()` are intentionally excluded because they reference no variables.
1206
+ *
1207
+ * For example, scanning `"OP_DUP <$(<pubkeyHash>)> OP_HASH160 $(<fee>)"` would return
1208
+ * `['$(<pubkeyHash>)', '$(<fee>)']`.
1209
+ */
1210
+ const CASHASSEMBLY_EVALUATION_PATTERN = /\$\([^)]+\)/g;
1211
+ /**
1212
+ * Extracts variable names from angle-bracket references inside a CashAssembly evaluation.
1213
+ *
1214
+ * Inside an evaluation like `$(<pubkeyHash> <fee>)`, variables are referenced as `<name>`.
1215
+ * This pattern captures the name between the brackets. The global flag `g` allows iterating
1216
+ * over every variable reference in a single evaluation string.
1217
+ *
1218
+ * For example, running this against `$(<pubkeyHash> <fee>)` would return the variable names
1219
+ * `["pubkeyHash", "fee"]`.
1220
+ */
1221
+ const CASHASSEMBLY_VARIABLE_PATTERN = /<([^>]+)>/g;
1222
+ /**
1223
+ * Identifies CashAssembly literal tokens that appear inside angle-bracket push statements.
1224
+ *
1225
+ * Inside an evaluation, not everything between `<` and `>` is a variable name. Literals are
1226
+ * also valid push contents: numeric literals (e.g. `<0>`, `<32>`), hex literals (`<0x02>`),
1227
+ * binary literals (`<0b1010>`), and string literals (`<"minting">`, `<'hello'>`). This pattern
1228
+ * matches any captured token that starts with a digit or a quote character.
1229
+ */
1230
+ const CASHASSEMBLY_LITERAL_TOKEN_PATTERN = /^[0-9"']/;
1231
+ /**
1232
+ * Matches a single dot variable method reference inside an angle-bracket identifier.
1233
+ *
1234
+ * Used to detect primitive method references such as `expiry.toIso8601`.
1235
+ *
1236
+ * For example:
1237
+ * `expiry.toIso8601` matches (base `expiry`, method `toIso8601`)
1238
+ * `requestedSatoshis` does not match (no method)
1239
+ * `key.schnorr_signature.all_outputs` does not match (more than one dot)
1240
+ * `key.public_key` matches the pattern shape but it is only resolved
1241
+ * when `hint` maps to a primitive
1242
+ */
1243
+ const CASHASSEMBLY_VARIABLE_METHOD_REFERENCE_PATTERN = /^([^.]+)\.([^.]+)$/;
1244
+
1245
+ //#endregion
1246
+ //#region source/cash-assembly/bytes.ts
1247
+ /**
1248
+ * Converts a value into bytes representation.
1249
+ *
1250
+ * @param {unknown} value - Value to encode, should be one of: Uint8Array, bigint, boolean, string, or a safe integer number.
1251
+ * @param {string} valueIdentifier - Identifier used in error messages.
1252
+ * @returns {Uint8Array} Bytes representation of the value.
1253
+ * @throws {@link CashAssemblyNumberNotSafeIntegerError} When a number is not a safe integer.
1254
+ * @throws {@link CashAssemblyUnsupportedValueTypeError} When the value type cannot be resolved.
1255
+ */
1256
+ const convertValueToBytes = (value, valueIdentifier) => {
1257
+ if (value instanceof Uint8Array) return value;
1258
+ if (typeof value === "bigint") return bigIntToVmNumber(value);
1259
+ if (typeof value === "boolean") return new Uint8Array(value ? [1] : []);
1260
+ if (typeof value === "string") return utf8ToBin(value);
1261
+ if (typeof value === "number") {
1262
+ if (Number.isSafeInteger(value) === true) return bigIntToVmNumber(BigInt(value));
1263
+ throw new CashAssemblyNumberNotSafeIntegerError(valueIdentifier, value);
1264
+ }
1265
+ throw new CashAssemblyUnsupportedValueTypeError(valueIdentifier, typeof value);
1266
+ };
1267
+
1268
+ //#endregion
1269
+ //#region source/cash-assembly/primitive-evaluations.ts
1270
+ /**
1271
+ * Template hint values mapped to a primitive class for resolving primitive method evaluations.
1272
+ * Keys are values from XOTemplatePrimitiveTypes.
1273
+ */
1274
+ const PRIMITIVE_BY_TEMPLATE_HINT = {
1275
+ [XOTemplatePrimitiveTypes.FUNGIBLE_TOKEN_AMOUNT]: FungibleTokenAmount,
1276
+ [XOTemplatePrimitiveTypes.NFT_COMMITMENT]: NFTCommitment,
1277
+ [XOTemplatePrimitiveTypes.PUBLIC_KEY]: PublicKey,
1278
+ [XOTemplatePrimitiveTypes.SATOSHIS]: Satoshis,
1279
+ [XOTemplatePrimitiveTypes.SCHNORR_SIGNATURE]: SchnorrSignature,
1280
+ [XOTemplatePrimitiveTypes.TEMPLATE_IDENTIFIER]: TemplateIdentifier,
1281
+ [XOTemplatePrimitiveTypes.TIMESTAMP]: Timestamp,
1282
+ [XOTemplatePrimitiveTypes.TOKEN_CATEGORY]: TokenCategory,
1283
+ [XOTemplatePrimitiveTypes.TRANSACTION_HASH]: TransactionHash
1284
+ };
1285
+ /**
1286
+ * Returns true when `hint` maps to a supported primitive class in `PRIMITIVE_BY_TEMPLATE_HINT`.
1287
+ *
1288
+ * @param {XOTemplatePrimitiveType | undefined} hint - Template variable hint.
1289
+ * @returns {boolean} True when the hint selects a supported primitive class.
1290
+ */
1291
+ const isSupportedPrimitiveHint = (hint) => {
1292
+ return hint !== void 0 && Object.hasOwn(PRIMITIVE_BY_TEMPLATE_HINT, hint) === true;
1293
+ };
1294
+ /**
1295
+ * Returns true when `methodName` is an own function on the primitive class for `hint`.
1296
+ *
1297
+ * @param {SupportedPrimitiveHint} hint - Supported template hint.
1298
+ * @param {string} methodName - Method name from the evaluation text, for example `toSatoshis`.
1299
+ * @returns {boolean} True when that class exposes the named method.
1300
+ */
1301
+ const canResolvePrimitiveMethod = (hint, methodName) => {
1302
+ const PrimitiveClass = PRIMITIVE_BY_TEMPLATE_HINT[hint];
1303
+ if (Object.hasOwn(PrimitiveClass.prototype, methodName) === false) return false;
1304
+ return typeof Reflect.get(PrimitiveClass.prototype, methodName) === "function";
1305
+ };
1306
+ /**
1307
+ * Constructs a primitive from a raw value and calls one instance method on it.
1308
+ *
1309
+ * Call only after `canResolvePrimitiveMethod` is true for the same hint and method.
1310
+ *
1311
+ * @param {CallPrimitiveMethodParameters} parameters - Identifier, method, value, and supported hint.
1312
+ * @returns {unknown} Method return value, later encoded as CashAssembly push bytes.
1313
+ * @throws {@link CashAssemblyPrimitiveMethodMissingError} When the prototype member is not a function.
1314
+ * @throws When the primitive constructor rejects the raw value (validation errors from `@xo-cash/primitives`).
1315
+ */
1316
+ const callPrimitiveMethod = (parameters) => {
1317
+ const { identifier, methodName, value, hint } = parameters;
1318
+ const PrimitiveClass = PRIMITIVE_BY_TEMPLATE_HINT[hint];
1319
+ const primitiveInstance = new PrimitiveClass(value);
1320
+ const primitiveMethod = Reflect.get(PrimitiveClass.prototype, methodName);
1321
+ if (typeof primitiveMethod !== "function") throw new CashAssemblyPrimitiveMethodMissingError(identifier, methodName, hint);
1322
+ return primitiveMethod.call(primitiveInstance);
1323
+ };
1324
+ /**
1325
+ * Resolves supported `base.method` identifiers to CashAssembly variable bytes.
1326
+ *
1327
+ * Each single dot identifier whose `hint` maps to a supported primitive is resolved and stored under
1328
+ * the full identifier (`base.method`). Unsupported or multi dot identifiers are left for CashAssembly.
1329
+ *
1330
+ * When `templateVariables` is omitted, returns an empty map.
1331
+ *
1332
+ * @param {ResolvePrimitiveMethodBytesParameters} parameters - Identifiers, values, and optional template metadata.
1333
+ * @returns {Record<string, Uint8Array>} Resolved method identifiers mapped to bytes for CashAssembly pushes.
1334
+ * @throws {@link CashAssemblyPrimitiveMethodMissingError} When the hint is a supported primitive but the method is missing.
1335
+ * @throws {@link CashAssemblyPrimitiveVariableMissingError} When the runtime value is missing from the variables map.
1336
+ * @throws {@link CashAssemblyUnsupportedValueTypeError} When the method return type cannot be embedded.
1337
+ * @throws {@link CashAssemblyNumberNotSafeIntegerError} When a method return value is a number that is not a safe integer.
1338
+ */
1339
+ const resolvePrimitiveMethodBytes = (parameters) => {
1340
+ const { identifiers, templateVariables, variables } = parameters;
1341
+ if (templateVariables === void 0) return {};
1342
+ const resolvedBytes = {};
1343
+ for (const identifier of identifiers) {
1344
+ if (Object.hasOwn(resolvedBytes, identifier) === true) continue;
1345
+ const methodReferenceMatch = identifier.match(CASHASSEMBLY_VARIABLE_METHOD_REFERENCE_PATTERN);
1346
+ if (methodReferenceMatch === null) continue;
1347
+ const [, baseName, methodName] = methodReferenceMatch;
1348
+ if (Object.hasOwn(templateVariables, baseName) === false) continue;
1349
+ const hint = templateVariables[baseName].hint;
1350
+ if (isSupportedPrimitiveHint(hint) === false) continue;
1351
+ if (canResolvePrimitiveMethod(hint, methodName) === false) throw new CashAssemblyPrimitiveMethodMissingError(identifier, methodName, hint);
1352
+ if (Object.hasOwn(variables, baseName) === false) throw new CashAssemblyPrimitiveVariableMissingError(identifier, baseName);
1353
+ resolvedBytes[identifier] = convertValueToBytes(callPrimitiveMethod({
1354
+ identifier,
1355
+ methodName,
1356
+ value: variables[baseName],
1357
+ hint
1358
+ }), identifier);
1359
+ }
1360
+ return resolvedBytes;
1361
+ };
1362
+
1363
+ //#endregion
1364
+ //#region source/cash-assembly/evaluations.ts
1365
+ /**
1366
+ * Checks if the expression is a CashAssembly expression.
1367
+ *
1368
+ * @param {unknown} expression - The expression to check.
1369
+ * @returns {boolean} True if the expression is a CashAssembly expression, false otherwise.
1370
+ */
1371
+ const isCashAssemblyExpression = (expression) => {
1372
+ return typeof expression === "string" && CASHASSEMBLY_EXPRESSION_PATTERN.test(expression);
1373
+ };
1374
+ /**
1375
+ * Extracts all CashAssembly evaluations (i.e., substrings like $(...)) from the input text.
1376
+ *
1377
+ * @param {string} text - The input string to scan for CashAssembly evaluations.
1378
+ * @returns {string[]} An array of evaluation strings found in the input.
1379
+ *
1380
+ * @example
1381
+ * extractCashAssemblyEvaluations("OP_DUP <$(<foo>)> OP_HASH160 $(<bar>)");
1382
+ * // returns ['$(<foo>)', '$(<bar>)']
1383
+ */
1384
+ const extractCashAssemblyEvaluations = (text) => {
1385
+ return text.match(CASHASSEMBLY_EVALUATION_PATTERN) ?? [];
1386
+ };
1387
+ /**
1388
+ * Returns the segment of `identifier` before the first `.`.
1389
+ *
1390
+ * When there is no `.`, returns `identifier` unchanged.
1391
+ * Multi segment identifiers such as `foo.bar.baz` resolve to `foo`.
1392
+ *
1393
+ * @param {string} identifier - Identifier that may contain a dot.
1394
+ * @returns {string} The base name before the first `.`.
1395
+ */
1396
+ const resolveIdentifierBaseName = (identifier) => {
1397
+ const firstDotIndex = identifier.indexOf(".");
1398
+ if (firstDotIndex === -1) return identifier;
1399
+ return identifier.slice(0, firstDotIndex);
1400
+ };
1401
+ /**
1402
+ * Extracts unique variable identifiers enclosed in angle brackets from each evaluation string.
1403
+ *
1404
+ * CashAssembly literal tokens such as hex bytes, numbers, and quoted strings are excluded via
1405
+ * {@link CASHASSEMBLY_LITERAL_TOKEN_PATTERN}. For example, `<0x02>` and `<"minting">` are not returned.
1406
+ *
1407
+ * @param {string[]} evaluations - An array of evaluation strings from which to extract variable names.
1408
+ * @returns {string[]} An array of variable names.
1409
+ */
1410
+ const extractVariablesFromEvaluations = (evaluations) => {
1411
+ const uniqueVariables = /* @__PURE__ */ new Set();
1412
+ for (const evaluation of evaluations) for (const [, extractedIdentifier] of evaluation.matchAll(CASHASSEMBLY_VARIABLE_PATTERN)) {
1413
+ if (CASHASSEMBLY_LITERAL_TOKEN_PATTERN.test(extractedIdentifier)) continue;
1414
+ uniqueVariables.add(extractedIdentifier);
1415
+ }
1416
+ return [...uniqueVariables];
1417
+ };
1418
+ /**
1419
+ * Decodes compiled CashAssembly evaluation bytes into a string representation.
1420
+ *
1421
+ * 'evaluationDecodeMode' determines how the evaluation bytes are interpreted and presented.
1422
+ * Use `bigint` for numeric values like satoshis, `utf8` for text labels, `hex` for binary data
1423
+ * such as hashes, and `boolean` to represent boolean values.
1424
+ *
1425
+ * @param {Uint8Array} compiledResult - The compiled evaluation bytecode.
1426
+ * @param {CompiledCashAssemblyDecodeMode} [evaluationDecodeMode='utf8'] - The decode mode used to convert
1427
+ * bytes to text.
1428
+ * @returns {string} The decoded value as a string suitable for inline replacement.
1429
+ * @throws {@link CashAssemblyVmNumberDecodeError} When `evaluationDecodeMode` is `bigint` and the bytes are not a VM number.
1430
+ */
1431
+ const decodeCompiledCashAssemblyEvaluation = (compiledResult, evaluationDecodeMode = "utf8") => {
1432
+ if (evaluationDecodeMode === "uint8array") return String(compiledResult);
1433
+ if (evaluationDecodeMode === "boolean") return compiledResult.length === 0 ? "false" : "true";
1434
+ if (evaluationDecodeMode === "hex") return binToHex(compiledResult);
1435
+ if (evaluationDecodeMode === "bigint") {
1436
+ const vmNumberResult = vmNumberToBigInt(compiledResult);
1437
+ if (typeof vmNumberResult === "bigint") return vmNumberResult.toString();
1438
+ throw new CashAssemblyVmNumberDecodeError(vmNumberResult);
1439
+ }
1440
+ return binToUtf8(compiledResult);
1441
+ };
1442
+ /**
1443
+ * Generates bytecode for a specific CashAssembly evaluation using given variable values and a prepared compiler.
1444
+ *
1445
+ * @param {CompilerBch} compiler - The libauth compiler from {@link compileCashAssemblyEvaluations}.
1446
+ * @param {string} evaluation - The specific evaluation string to compile.
1447
+ * @param {Record<string, Uint8Array>} variables - A record mapping variable names to their values.
1448
+ * @returns {Uint8Array} The compiled bytecode.
1449
+ * @throws {@link CashAssemblyRequiredVariableMissingError} If a required variable is not present.
1450
+ * @throws {@link CashAssemblyVariableTypeMismatchError} If a variable value is not a Uint8Array.
1451
+ * @throws {@link CashAssemblyCompilationFailedError} If libauth compilation fails.
1452
+ */
1453
+ const generateCashAssemblyBytecode = (compiler, evaluation, variables) => {
1454
+ const variableNames = extractVariablesFromEvaluations([evaluation]);
1455
+ const missingVariables = variableNames.filter((name) => !Object.hasOwn(variables, name));
1456
+ if (missingVariables.length > 0) throw new CashAssemblyRequiredVariableMissingError(missingVariables);
1457
+ const bytecode = {};
1458
+ for (const variableName of variableNames) {
1459
+ const value = variables[variableName];
1460
+ if (!(value instanceof Uint8Array)) throw new CashAssemblyVariableTypeMismatchError(variableName, "Uint8Array", typeof value);
1461
+ bytecode[variableName] = value;
1462
+ }
1463
+ const compiledBytecode = compiler.generateBytecode({
1464
+ data: { bytecode },
1465
+ scriptId: evaluation
1466
+ });
1467
+ if (!compiledBytecode.success) {
1468
+ let compilationFailureMessage = "unknown compilation failure";
1469
+ if ("errors" in compiledBytecode && compiledBytecode.errors.length > 0) compilationFailureMessage = compiledBytecode.errors.map((compilationError) => compilationError.error).join("; ");
1470
+ throw new CashAssemblyCompilationFailedError(compilationFailureMessage);
1471
+ }
1472
+ return compiledBytecode.bytecode;
1473
+ };
1474
+ /**
1475
+ * Prepares a compiler for the provided CashAssembly evaluations, setting required variables as 'WalletData'.
1476
+ *
1477
+ * @param {string[]} evaluations - Array of evaluation strings (e.g., ['$(<var1>)', '$(<var2> <var3>)']).
1478
+ * @returns {CompilerBch} A Libauth compiler instance for use with these evaluations.
1479
+ */
1480
+ const compileCashAssemblyEvaluations = (evaluations) => {
1481
+ const scripts = {};
1482
+ for (const evaluation of evaluations) scripts[evaluation] = evaluation;
1483
+ const variableNames = extractVariablesFromEvaluations(evaluations);
1484
+ const variables = {};
1485
+ for (const variableName of variableNames) variables[resolveIdentifierBaseName(variableName)] = { type: "WalletData" };
1486
+ return createCompilerBch({
1487
+ scripts,
1488
+ variables
1489
+ });
1490
+ };
1491
+ /**
1492
+ * Compiles all CashAssembly evaluations in a text string and replaces each evaluation
1493
+ * with a decoded string representation.
1494
+ *
1495
+ * @param {CompileCashAssemblyStringParameters} parameters - Parameters for compiling the CashAssembly string.
1496
+ * @param {string} parameters.cashAssemblyText - The string with CashAssembly evaluations.
1497
+ * @param {Record<string, XOInvitationVariableValue | Uint8Array>} parameters.variables - Object mapping
1498
+ * variable names to values for compilation.
1499
+ * @param {CompiledCashAssemblyDecodeMode} [parameters.evaluationDecodeMode='utf8'] - The decode mode used
1500
+ * after each evaluation is compiled. See {@link decodeCompiledCashAssemblyEvaluation}.
1501
+ * @param {XOTemplate['variables']} [parameters.templateVariables] - Optional template variable definitions
1502
+ * used to resolve supported `<name.method>` pushes via each variable's `hint`.
1503
+ * @returns {string} Compiled text with all evaluations replaced by decoded string values.
1504
+ * @throws {@link CashAssemblyRequiredVariableMissingError} When a required variable is not present in the variables map.
1505
+ * @throws {@link CashAssemblyPrimitiveMethodMissingError} When a supported primitive hint has an unknown method.
1506
+ * @throws {@link CashAssemblyPrimitiveVariableMissingError} When a supported primitive method is missing its runtime value.
1507
+ * @throws {@link CashAssemblyUnsupportedValueTypeError} When a primitive method return type cannot be embedded as bytes.
1508
+ * @throws {@link CashAssemblyNumberNotSafeIntegerError} When a number variable is not a safe integer.
1509
+ * @throws {@link CashAssemblyVmNumberDecodeError} When `evaluationDecodeMode` is `bigint` and an evaluation is not a VM number.
1510
+ */
1511
+ const compileCashAssemblyString = (parameters) => {
1512
+ const { cashAssemblyText, variables, evaluationDecodeMode = "utf8", templateVariables } = parameters;
1513
+ return cashAssemblyText.replace(CASHASSEMBLY_EVALUATION_PATTERN, (evaluation) => {
1514
+ const variableNames = extractVariablesFromEvaluations([evaluation]);
1515
+ const primitiveMethodBytes = resolvePrimitiveMethodBytes({
1516
+ identifiers: variableNames,
1517
+ templateVariables,
1518
+ variables
1519
+ });
1520
+ const missingVariables = variableNames.filter((variableName) => {
1521
+ if (Object.hasOwn(primitiveMethodBytes, variableName) === true) return false;
1522
+ return Object.hasOwn(variables, variableName) === false;
1523
+ });
1524
+ if (missingVariables.length > 0) throw new CashAssemblyRequiredVariableMissingError(missingVariables);
1525
+ const variableBytes = {};
1526
+ for (const variableName of variableNames) {
1527
+ if (Object.hasOwn(primitiveMethodBytes, variableName) === true) {
1528
+ variableBytes[variableName] = primitiveMethodBytes[variableName];
1529
+ continue;
1530
+ }
1531
+ variableBytes[variableName] = convertValueToBytes(variables[variableName], variableName);
1532
+ }
1533
+ return decodeCompiledCashAssemblyEvaluation(generateCashAssemblyBytecode(compileCashAssemblyEvaluations([evaluation]), evaluation, variableBytes), evaluationDecodeMode);
1534
+ });
1535
+ };
1536
+
1537
+ //#endregion
1538
+ export { AsyncPushIterator, CASHASSEMBLY_EVALUATION_PATTERN, CASHASSEMBLY_EXPRESSION_PATTERN, CASHASSEMBLY_LITERAL_TOKEN_PATTERN, CASHASSEMBLY_VARIABLE_METHOD_REFERENCE_PATTERN, CASHASSEMBLY_VARIABLE_PATTERN, CashAssemblyCompilationFailedError, CashAssemblyNumberNotSafeIntegerError, CashAssemblyPrimitiveMethodMissingError, CashAssemblyPrimitiveVariableMissingError, CashAssemblyRequiredVariableMissingError, CashAssemblyUnsupportedValueTypeError, CashAssemblyVariableTypeMismatchError, CashAssemblyVmNumberDecodeError, SSEEventParser, TemplateInvalidError, TemplateJsonMalformedError, TemplateSerializationFailedError, VIEW_PROPERTIES_DESCRIPTION_MAX_LENGTH, VIEW_PROPERTIES_ICON_MAX_LENGTH, VIEW_PROPERTIES_NAME_MAX_LENGTH, bchVmVersionSchema, buildErrorDescription, compileCashAssemblyEvaluations, compileCashAssemblyString, convertValueToBytes, decodeCompiledCashAssemblyEvaluation, extendedJsonReplacer, extendedJsonReviver, extractCashAssemblyEvaluations, extractVariablesFromEvaluations, fromExtendedJson, generateCashAssemblyBytecode, generateTemplateIdentifier, isCashAssemblyExpression, parseTemplate, resolvePrimitiveMethodBytes, satoshisSchema, scriptToScriptHash, serializeTemplate, toExtendedJson, uint8ArraySchema, xoTemplateActionIntentSchema, xoTemplateActionRequirementsSchema, xoTemplateActionRoleRequirementsSchema, xoTemplateActionRoleSchema, xoTemplateActionSchema, xoTemplateAssetAmountsSchema, xoTemplateBaseTypeSchema, 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 };
817
1539
  //# sourceMappingURL=index.mjs.map