@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.d.mts +583 -19
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +733 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -10
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
|
|
214
|
-
*
|
|
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:
|
|
985
|
+
type: xoTemplateBaseTypeSchema.describe("The data type of this constant."),
|
|
686
986
|
value: z.unknown().describe("The value of this constant."),
|
|
687
|
-
hint:
|
|
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:
|
|
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:
|
|
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:
|
|
733
|
-
hint:
|
|
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
|
-
|
|
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
|