mustardscript 0.1.0

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.
Files changed (99) hide show
  1. package/Cargo.lock +1579 -0
  2. package/Cargo.toml +40 -0
  3. package/LICENSE +201 -0
  4. package/README.md +828 -0
  5. package/SECURITY.md +34 -0
  6. package/crates/mustard/Cargo.toml +31 -0
  7. package/crates/mustard/src/cancellation.rs +28 -0
  8. package/crates/mustard/src/diagnostic.rs +145 -0
  9. package/crates/mustard/src/ir.rs +435 -0
  10. package/crates/mustard/src/lib.rs +21 -0
  11. package/crates/mustard/src/limits.rs +22 -0
  12. package/crates/mustard/src/parser/expressions.rs +723 -0
  13. package/crates/mustard/src/parser/mod.rs +115 -0
  14. package/crates/mustard/src/parser/operators.rs +105 -0
  15. package/crates/mustard/src/parser/patterns.rs +123 -0
  16. package/crates/mustard/src/parser/scope.rs +107 -0
  17. package/crates/mustard/src/parser/statements.rs +298 -0
  18. package/crates/mustard/src/parser/tests/acceptance.rs +339 -0
  19. package/crates/mustard/src/parser/tests/mod.rs +2 -0
  20. package/crates/mustard/src/parser/tests/rejections.rs +107 -0
  21. package/crates/mustard/src/runtime/accounting.rs +613 -0
  22. package/crates/mustard/src/runtime/api.rs +192 -0
  23. package/crates/mustard/src/runtime/async_runtime/mod.rs +5 -0
  24. package/crates/mustard/src/runtime/async_runtime/promises.rs +246 -0
  25. package/crates/mustard/src/runtime/async_runtime/reactions.rs +400 -0
  26. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +224 -0
  27. package/crates/mustard/src/runtime/builtins/arrays.rs +1205 -0
  28. package/crates/mustard/src/runtime/builtins/collections.rs +573 -0
  29. package/crates/mustard/src/runtime/builtins/install.rs +501 -0
  30. package/crates/mustard/src/runtime/builtins/intl.rs +553 -0
  31. package/crates/mustard/src/runtime/builtins/mod.rs +25 -0
  32. package/crates/mustard/src/runtime/builtins/objects.rs +405 -0
  33. package/crates/mustard/src/runtime/builtins/primitives.rs +859 -0
  34. package/crates/mustard/src/runtime/builtins/promises.rs +335 -0
  35. package/crates/mustard/src/runtime/builtins/regexp.rs +356 -0
  36. package/crates/mustard/src/runtime/builtins/strings.rs +803 -0
  37. package/crates/mustard/src/runtime/builtins/support.rs +561 -0
  38. package/crates/mustard/src/runtime/bytecode.rs +123 -0
  39. package/crates/mustard/src/runtime/compiler/assignments.rs +690 -0
  40. package/crates/mustard/src/runtime/compiler/bindings.rs +92 -0
  41. package/crates/mustard/src/runtime/compiler/context.rs +46 -0
  42. package/crates/mustard/src/runtime/compiler/control.rs +342 -0
  43. package/crates/mustard/src/runtime/compiler/expressions.rs +372 -0
  44. package/crates/mustard/src/runtime/compiler/mod.rs +173 -0
  45. package/crates/mustard/src/runtime/compiler/statements.rs +459 -0
  46. package/crates/mustard/src/runtime/conversions/boundary.rs +293 -0
  47. package/crates/mustard/src/runtime/conversions/coercions.rs +217 -0
  48. package/crates/mustard/src/runtime/conversions/errors.rs +118 -0
  49. package/crates/mustard/src/runtime/conversions/mod.rs +14 -0
  50. package/crates/mustard/src/runtime/conversions/operators.rs +334 -0
  51. package/crates/mustard/src/runtime/env.rs +355 -0
  52. package/crates/mustard/src/runtime/exceptions.rs +377 -0
  53. package/crates/mustard/src/runtime/gc.rs +595 -0
  54. package/crates/mustard/src/runtime/mod.rs +318 -0
  55. package/crates/mustard/src/runtime/properties.rs +1762 -0
  56. package/crates/mustard/src/runtime/serialization.rs +127 -0
  57. package/crates/mustard/src/runtime/shared.rs +108 -0
  58. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +93 -0
  59. package/crates/mustard/src/runtime/state.rs +652 -0
  60. package/crates/mustard/src/runtime/tests/async_host.rs +104 -0
  61. package/crates/mustard/src/runtime/tests/collections.rs +50 -0
  62. package/crates/mustard/src/runtime/tests/diagnostics.rs +36 -0
  63. package/crates/mustard/src/runtime/tests/exceptions.rs +122 -0
  64. package/crates/mustard/src/runtime/tests/execution.rs +553 -0
  65. package/crates/mustard/src/runtime/tests/gc.rs +533 -0
  66. package/crates/mustard/src/runtime/tests/mod.rs +56 -0
  67. package/crates/mustard/src/runtime/tests/serialization.rs +170 -0
  68. package/crates/mustard/src/runtime/validation/bytecode.rs +484 -0
  69. package/crates/mustard/src/runtime/validation/mod.rs +14 -0
  70. package/crates/mustard/src/runtime/validation/policy.rs +94 -0
  71. package/crates/mustard/src/runtime/validation/snapshot.rs +406 -0
  72. package/crates/mustard/src/runtime/validation/walk.rs +206 -0
  73. package/crates/mustard/src/runtime/vm.rs +1016 -0
  74. package/crates/mustard/src/span.rs +22 -0
  75. package/crates/mustard/src/structured.rs +107 -0
  76. package/crates/mustard-bridge/Cargo.toml +17 -0
  77. package/crates/mustard-bridge/src/codec.rs +46 -0
  78. package/crates/mustard-bridge/src/dto.rs +99 -0
  79. package/crates/mustard-bridge/src/lib.rs +12 -0
  80. package/crates/mustard-bridge/src/operations.rs +142 -0
  81. package/crates/mustard-node/Cargo.toml +24 -0
  82. package/crates/mustard-node/build.rs +3 -0
  83. package/crates/mustard-node/src/lib.rs +236 -0
  84. package/crates/mustard-sidecar/Cargo.toml +21 -0
  85. package/crates/mustard-sidecar/src/lib.rs +134 -0
  86. package/crates/mustard-sidecar/src/main.rs +36 -0
  87. package/dist/index.js +20 -0
  88. package/dist/install.js +117 -0
  89. package/dist/lib/cancellation.js +124 -0
  90. package/dist/lib/errors.js +46 -0
  91. package/dist/lib/executor.js +555 -0
  92. package/dist/lib/policy.js +292 -0
  93. package/dist/lib/progress.js +356 -0
  94. package/dist/lib/runtime.js +109 -0
  95. package/dist/lib/structured.js +286 -0
  96. package/dist/native-loader.js +227 -0
  97. package/index.d.ts +23 -0
  98. package/mustard.d.ts +220 -0
  99. package/package.json +97 -0
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ const { callNative } = require('./errors.js');
4
+ const {
5
+ getAbortSignal,
6
+ settleCapabilityInvocation,
7
+ throwIfAborted,
8
+ withCancellationSignal,
9
+ } = require('./cancellation.js');
10
+ const {
11
+ createExecutionPolicy,
12
+ encodeSnapshotPolicy,
13
+ snapshotIdentity,
14
+ snapshotToken,
15
+ } = require('./policy.js');
16
+ const {
17
+ encodeResumePayloadCancel,
18
+ encodeResumePayloadError,
19
+ encodeResumePayloadValue,
20
+ encodeStartOptions,
21
+ } = require('./structured.js');
22
+
23
+ function createMustardClass({ native, materializeStep, parseStep }) {
24
+ function compileProgram(code) {
25
+ return callNative(native.compileProgram, code);
26
+ }
27
+
28
+ return class Mustard {
29
+ constructor(code, options = {}) {
30
+ this._program = compileProgram(code);
31
+ this._inputNames = options.inputs ?? [];
32
+ }
33
+
34
+ static validateProgram(code) {
35
+ compileProgram(code);
36
+ }
37
+
38
+ async run(options = {}) {
39
+ const signal = getAbortSignal(options, 'run options');
40
+ throwIfAborted(signal);
41
+ const { hostHandlers, policy, snapshotKey } = createExecutionPolicy(options);
42
+ let step = parseStep(
43
+ withCancellationSignal(
44
+ native,
45
+ native.startProgram,
46
+ [this._program, encodeStartOptions(options.inputs, policy)],
47
+ signal,
48
+ ),
49
+ );
50
+ while (step.type === 'suspended') {
51
+ const capability = hostHandlers[step.capability];
52
+ if (typeof capability !== 'function') {
53
+ throw new Error(`Missing capability: ${step.capability}`);
54
+ }
55
+ const outcome = await settleCapabilityInvocation(capability, step.args, signal);
56
+ const snapshotId = snapshotIdentity(step.snapshot);
57
+ const policyJson = encodeSnapshotPolicy(policy, {
58
+ snapshotId,
59
+ snapshotKey,
60
+ snapshotToken: snapshotToken(step.snapshot, snapshotKey, snapshotId),
61
+ });
62
+ if (outcome.type === 'cancelled') {
63
+ step = parseStep(
64
+ callNative(native.resumeProgram, step.snapshot, encodeResumePayloadCancel(), policyJson),
65
+ );
66
+ continue;
67
+ }
68
+ const payload =
69
+ outcome.type === 'value'
70
+ ? encodeResumePayloadValue(outcome.value)
71
+ : encodeResumePayloadError(outcome.error);
72
+ step = parseStep(
73
+ withCancellationSignal(native, native.resumeProgram, [step.snapshot, payload, policyJson], signal),
74
+ );
75
+ }
76
+ return step.value;
77
+ }
78
+
79
+ start(options = {}) {
80
+ const signal = getAbortSignal(options, 'start options');
81
+ throwIfAborted(signal);
82
+ const { policy, snapshotKey } = createExecutionPolicy(options);
83
+ const step = parseStep(
84
+ withCancellationSignal(
85
+ native,
86
+ native.startProgram,
87
+ [this._program, encodeStartOptions(options.inputs, policy)],
88
+ signal,
89
+ ),
90
+ );
91
+ return materializeStep(step, policy, snapshotKey);
92
+ }
93
+
94
+ dump() {
95
+ return Buffer.from(this._program);
96
+ }
97
+
98
+ static load(buffer) {
99
+ const instance = Object.create(Mustard.prototype);
100
+ instance._program = Buffer.from(buffer);
101
+ instance._inputNames = [];
102
+ return instance;
103
+ }
104
+ };
105
+ }
106
+
107
+ module.exports = {
108
+ createMustardClass,
109
+ };
@@ -0,0 +1,286 @@
1
+ 'use strict';
2
+
3
+ const { types } = require('node:util');
4
+
5
+ const HOST_BOUNDARY_MAX_DEPTH = 128;
6
+ const HOST_BOUNDARY_MAX_ARRAY_LENGTH = 1_000_000;
7
+
8
+ function assertBoundaryDepth(depth, label) {
9
+ if (depth > HOST_BOUNDARY_MAX_DEPTH) {
10
+ throw new TypeError(`${label} nesting limit exceeded`);
11
+ }
12
+ }
13
+
14
+ function assertBoundaryArrayLength(length, label) {
15
+ if (length > HOST_BOUNDARY_MAX_ARRAY_LENGTH) {
16
+ throw new TypeError(
17
+ `${label} arrays longer than ${HOST_BOUNDARY_MAX_ARRAY_LENGTH} elements cannot cross the host boundary`,
18
+ );
19
+ }
20
+ }
21
+
22
+ function encodeNumber(value) {
23
+ if (Number.isNaN(value)) {
24
+ return { Number: 'NaN' };
25
+ }
26
+ if (Object.is(value, -0)) {
27
+ return { Number: 'NegZero' };
28
+ }
29
+ if (value === Infinity) {
30
+ return { Number: 'Infinity' };
31
+ }
32
+ if (value === -Infinity) {
33
+ return { Number: 'NegInfinity' };
34
+ }
35
+ return { Number: { Finite: value } };
36
+ }
37
+
38
+ function isObjectLike(value) {
39
+ return value !== null && (typeof value === 'object' || typeof value === 'function');
40
+ }
41
+
42
+ function assertNotProxy(value) {
43
+ if (isObjectLike(value) && types.isProxy(value)) {
44
+ throw new TypeError('Proxy values cannot cross the host boundary');
45
+ }
46
+ }
47
+
48
+ function isPlainStructuredObject(value) {
49
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
50
+ return false;
51
+ }
52
+ assertNotProxy(value);
53
+ const prototype = Object.getPrototypeOf(value);
54
+ return prototype === Object.prototype || prototype === null;
55
+ }
56
+
57
+ function hasOwnProperty(value, key) {
58
+ return Object.prototype.hasOwnProperty.call(value, key);
59
+ }
60
+
61
+ function defineEnumerableProperty(target, key, value) {
62
+ Object.defineProperty(target, key, {
63
+ value,
64
+ enumerable: true,
65
+ writable: true,
66
+ configurable: true,
67
+ });
68
+ }
69
+
70
+ function isAccessorDescriptor(descriptor) {
71
+ return hasOwnProperty(descriptor, 'get') || hasOwnProperty(descriptor, 'set');
72
+ }
73
+
74
+ function enumerateDataProperties(value) {
75
+ assertNotProxy(value);
76
+ return Object.entries(Object.getOwnPropertyDescriptors(value)).filter(([, descriptor]) => {
77
+ if (!descriptor.enumerable) {
78
+ return false;
79
+ }
80
+ if (isAccessorDescriptor(descriptor)) {
81
+ throw new TypeError('host objects with accessors cannot cross the host boundary');
82
+ }
83
+ return true;
84
+ });
85
+ }
86
+
87
+ function enterStructuredTraversal(value, traversal) {
88
+ if (!isObjectLike(value)) {
89
+ return () => {};
90
+ }
91
+ if (traversal.active.has(value)) {
92
+ throw new TypeError('cyclic values cannot cross the host boundary');
93
+ }
94
+ traversal.active.add(value);
95
+ return () => {
96
+ traversal.active.delete(value);
97
+ };
98
+ }
99
+
100
+ function encodeStructuredArray(value, traversal, depth) {
101
+ assertNotProxy(value);
102
+ assertBoundaryArrayLength(value.length, 'host boundary');
103
+ const leave = enterStructuredTraversal(value, traversal);
104
+ try {
105
+ const entries = new Array(value.length);
106
+ for (let index = 0; index < value.length; index += 1) {
107
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
108
+ if (descriptor === undefined) {
109
+ entries[index] = 'Hole';
110
+ continue;
111
+ }
112
+ if (isAccessorDescriptor(descriptor)) {
113
+ throw new TypeError('host objects with accessors cannot cross the host boundary');
114
+ }
115
+ entries[index] = encodeStructured(descriptor.value, traversal, depth + 1);
116
+ }
117
+ return { Array: entries };
118
+ } finally {
119
+ leave();
120
+ }
121
+ }
122
+
123
+ function encodeStructuredObject(value, traversal, depth) {
124
+ const leave = enterStructuredTraversal(value, traversal);
125
+ try {
126
+ const object = {};
127
+ for (const [key, descriptor] of enumerateDataProperties(value)) {
128
+ defineEnumerableProperty(
129
+ object,
130
+ key,
131
+ encodeStructured(descriptor.value, traversal, depth + 1),
132
+ );
133
+ }
134
+ return { Object: object };
135
+ } finally {
136
+ leave();
137
+ }
138
+ }
139
+
140
+ function encodeStructured(value, traversal = { active: new WeakSet() }, depth = 1) {
141
+ assertBoundaryDepth(depth, 'host boundary');
142
+ if (value === undefined) {
143
+ return 'Undefined';
144
+ }
145
+ if (value === null) {
146
+ return 'Null';
147
+ }
148
+ if (typeof value === 'boolean') {
149
+ return { Bool: value };
150
+ }
151
+ if (typeof value === 'number') {
152
+ return encodeNumber(value);
153
+ }
154
+ if (typeof value === 'string') {
155
+ return { String: value };
156
+ }
157
+ if (Array.isArray(value)) {
158
+ return encodeStructuredArray(value, traversal, depth);
159
+ }
160
+ if (typeof value === 'object') {
161
+ if (!isPlainStructuredObject(value)) {
162
+ throw new TypeError(
163
+ 'Unsupported host value: only plain objects and arrays can cross the host boundary',
164
+ );
165
+ }
166
+ return encodeStructuredObject(value, traversal, depth);
167
+ }
168
+ throw new TypeError('Unsupported host value');
169
+ }
170
+
171
+ function decodeStructured(value, depth = 1) {
172
+ assertBoundaryDepth(depth, 'structured host boundary');
173
+ if (value === 'Undefined') {
174
+ return undefined;
175
+ }
176
+ if (value === 'Null') {
177
+ return null;
178
+ }
179
+ if (value !== null && typeof value === 'object' && hasOwnProperty(value, 'Bool')) {
180
+ return value.Bool;
181
+ }
182
+ if (value !== null && typeof value === 'object' && hasOwnProperty(value, 'String')) {
183
+ return value.String;
184
+ }
185
+ if (value !== null && typeof value === 'object' && hasOwnProperty(value, 'Number')) {
186
+ const encoded = value.Number;
187
+ if (encoded === 'NaN') {
188
+ return NaN;
189
+ }
190
+ if (encoded === 'Infinity') {
191
+ return Infinity;
192
+ }
193
+ if (encoded === 'NegInfinity') {
194
+ return -Infinity;
195
+ }
196
+ if (encoded === 'NegZero') {
197
+ return -0;
198
+ }
199
+ return encoded.Finite;
200
+ }
201
+ if (value !== null && typeof value === 'object' && hasOwnProperty(value, 'Array')) {
202
+ assertBoundaryArrayLength(value.Array.length, 'structured host boundary');
203
+ const array = new Array(value.Array.length);
204
+ value.Array.forEach((entry, index) => {
205
+ if (entry !== 'Hole') {
206
+ array[index] = decodeStructured(entry, depth + 1);
207
+ }
208
+ });
209
+ return array;
210
+ }
211
+ if (value !== null && typeof value === 'object' && hasOwnProperty(value, 'Object')) {
212
+ const object = {};
213
+ for (const [key, entry] of Object.entries(value.Object)) {
214
+ defineEnumerableProperty(object, key, decodeStructured(entry, depth + 1));
215
+ }
216
+ return object;
217
+ }
218
+ throw new TypeError(`Unsupported structured value: ${JSON.stringify(value)}`);
219
+ }
220
+
221
+ function encodeStartOptions(inputs = {}, policy) {
222
+ const encodedInputs = {};
223
+ for (const [key, descriptor] of enumerateDataProperties(inputs)) {
224
+ defineEnumerableProperty(encodedInputs, key, encodeStructured(descriptor.value));
225
+ }
226
+ return JSON.stringify({
227
+ inputs: encodedInputs,
228
+ capabilities: policy.capabilities,
229
+ limits: policy.limits,
230
+ });
231
+ }
232
+
233
+ function encodeResumePayloadValue(value) {
234
+ return JSON.stringify({
235
+ type: 'value',
236
+ value: encodeStructured(value),
237
+ });
238
+ }
239
+
240
+ function readOwnDataProperty(value, key, label) {
241
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
242
+ if (descriptor === undefined) {
243
+ return undefined;
244
+ }
245
+ if (isAccessorDescriptor(descriptor)) {
246
+ throw new TypeError(`${label} cannot use accessor-backed ${key} properties`);
247
+ }
248
+ return descriptor.value;
249
+ }
250
+
251
+ function encodeResumePayloadError(error) {
252
+ const source = error instanceof Error ? error : Object(error);
253
+ assertNotProxy(source);
254
+ const name = readOwnDataProperty(source, 'name', 'host errors');
255
+ const message = readOwnDataProperty(source, 'message', 'host errors');
256
+ const code = readOwnDataProperty(source, 'code', 'host errors');
257
+ const details = readOwnDataProperty(source, 'details', 'host errors');
258
+ return JSON.stringify({
259
+ type: 'error',
260
+ error: {
261
+ name: typeof name === 'string' ? name : 'Error',
262
+ message: typeof message === 'string' ? message : '',
263
+ code: typeof code === 'string' ? code : null,
264
+ details: details === undefined ? null : encodeStructured(details),
265
+ },
266
+ });
267
+ }
268
+
269
+ function encodeResumePayloadCancel() {
270
+ return JSON.stringify({
271
+ type: 'cancelled',
272
+ });
273
+ }
274
+
275
+ module.exports = {
276
+ decodeStructured,
277
+ defineEnumerableProperty,
278
+ encodeResumePayloadCancel,
279
+ encodeResumePayloadError,
280
+ encodeResumePayloadValue,
281
+ encodeStartOptions,
282
+ encodeStructured,
283
+ enumerateDataProperties,
284
+ hasOwnProperty,
285
+ isAccessorDescriptor,
286
+ };
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ function packageRoot(fromDir = __dirname) {
7
+ return path.basename(fromDir) === 'dist' ? path.dirname(fromDir) : fromDir;
8
+ }
9
+
10
+ const PREBUILT_TARGETS = Object.freeze([
11
+ {
12
+ triple: 'x86_64-pc-windows-msvc',
13
+ platform: 'win32',
14
+ arch: 'x64',
15
+ platformArchABI: 'win32-x64-msvc',
16
+ packageName: 'mustardscript-win32-x64-msvc',
17
+ localFile: 'index.win32-x64-msvc.node',
18
+ os: ['win32'],
19
+ cpu: ['x64'],
20
+ },
21
+ {
22
+ triple: 'x86_64-apple-darwin',
23
+ platform: 'darwin',
24
+ arch: 'x64',
25
+ platformArchABI: 'darwin-x64',
26
+ packageName: 'mustardscript-darwin-x64',
27
+ localFile: 'index.darwin-x64.node',
28
+ os: ['darwin'],
29
+ cpu: ['x64'],
30
+ },
31
+ {
32
+ triple: 'aarch64-apple-darwin',
33
+ platform: 'darwin',
34
+ arch: 'arm64',
35
+ platformArchABI: 'darwin-arm64',
36
+ packageName: 'mustardscript-darwin-arm64',
37
+ localFile: 'index.darwin-arm64.node',
38
+ os: ['darwin'],
39
+ cpu: ['arm64'],
40
+ },
41
+ {
42
+ triple: 'x86_64-unknown-linux-gnu',
43
+ platform: 'linux',
44
+ arch: 'x64',
45
+ platformArchABI: 'linux-x64-gnu',
46
+ packageName: 'mustardscript-linux-x64-gnu',
47
+ localFile: 'index.linux-x64-gnu.node',
48
+ os: ['linux'],
49
+ cpu: ['x64'],
50
+ libc: ['glibc'],
51
+ },
52
+ ]);
53
+
54
+ const TARGETS_BY_RUNTIME = new Map(
55
+ PREBUILT_TARGETS.map((target) => [`${target.platform}:${target.arch}`, target]),
56
+ );
57
+
58
+ function isExplicitFilePath(specifier) {
59
+ return (
60
+ path.isAbsolute(specifier) ||
61
+ specifier.startsWith(`.${path.sep}`) ||
62
+ specifier.startsWith(`..${path.sep}`) ||
63
+ specifier.startsWith('./') ||
64
+ specifier.startsWith('../')
65
+ );
66
+ }
67
+
68
+ function resolveNativeAddonPath(candidate, label, cwd = process.cwd()) {
69
+ if (typeof candidate !== 'string' || candidate.trim() === '') {
70
+ throw new Error(`${label} must be a non-empty file path to a native .node addon`);
71
+ }
72
+ if (!isExplicitFilePath(candidate)) {
73
+ throw new Error(
74
+ `${label} must be an explicit absolute or relative file path to a native .node addon`,
75
+ );
76
+ }
77
+ const resolved = path.resolve(cwd, candidate);
78
+ if (path.extname(resolved) !== '.node') {
79
+ throw new Error(`${label} must point to a native .node addon`);
80
+ }
81
+ const stats = fs.statSync(resolved, { throwIfNoEntry: false });
82
+ if (!stats?.isFile()) {
83
+ throw new Error(`${label} does not exist: ${resolved}`);
84
+ }
85
+ return resolved;
86
+ }
87
+
88
+ function getCurrentPrebuiltTarget() {
89
+ return TARGETS_BY_RUNTIME.get(`${process.platform}:${process.arch}`) ?? null;
90
+ }
91
+
92
+ function getLocalBuildOutputFile() {
93
+ return getCurrentPrebuiltTarget()?.localFile ?? 'index.node';
94
+ }
95
+
96
+ function getLocalBinaryFilenames() {
97
+ return [...new Set([getLocalBuildOutputFile(), 'index.node'])];
98
+ }
99
+
100
+ function validatePrebuiltPackageManifest(manifest, target, packageJsonPath) {
101
+ if (manifest?.name !== target.packageName) {
102
+ throw new Error(
103
+ `optional prebuilt package at ${packageJsonPath} does not match ${target.packageName}`,
104
+ );
105
+ }
106
+ if (manifest?.main !== target.localFile) {
107
+ throw new Error(
108
+ `optional prebuilt package ${target.packageName} must expose its native addon as ${target.localFile}`,
109
+ );
110
+ }
111
+ }
112
+
113
+ function resolvePrebuiltPackage(searchRoot = packageRoot()) {
114
+ const target = getCurrentPrebuiltTarget();
115
+ if (!target) {
116
+ return null;
117
+ }
118
+
119
+ let packageJsonPath;
120
+ try {
121
+ packageJsonPath = require.resolve(`${target.packageName}/package.json`, {
122
+ paths: [searchRoot],
123
+ });
124
+ } catch {
125
+ return null;
126
+ }
127
+
128
+ const packageRoot = path.dirname(packageJsonPath);
129
+ const manifest = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
130
+ validatePrebuiltPackageManifest(manifest, target, packageJsonPath);
131
+
132
+ const binaryPath = path.join(packageRoot, target.localFile);
133
+ const stats = fs.statSync(binaryPath, { throwIfNoEntry: false });
134
+ if (!stats?.isFile()) {
135
+ throw new Error(`optional prebuilt package ${target.packageName} is missing ${target.localFile}`);
136
+ }
137
+
138
+ return {
139
+ ...target,
140
+ packageJsonPath,
141
+ packageRoot,
142
+ binaryPath,
143
+ };
144
+ }
145
+
146
+ function localBinaryCandidates(searchRoot = packageRoot()) {
147
+ const roots = [
148
+ searchRoot,
149
+ path.join(searchRoot, 'crates', 'mustard-node'),
150
+ ];
151
+ const candidates = [];
152
+
153
+ for (const root of roots) {
154
+ if (!fs.existsSync(root)) {
155
+ continue;
156
+ }
157
+
158
+ for (const filename of getLocalBinaryFilenames()) {
159
+ const candidate = path.join(root, filename);
160
+ const stats = fs.statSync(candidate, { throwIfNoEntry: false });
161
+ if (stats?.isFile()) {
162
+ candidates.push(candidate);
163
+ }
164
+ }
165
+ }
166
+
167
+ return candidates;
168
+ }
169
+
170
+ function loadNative(options = {}) {
171
+ const env = options.env ?? process.env;
172
+ const searchRoot = options.searchRoot ?? packageRoot();
173
+ const overrideCwd = options.overrideCwd ?? process.cwd();
174
+ const loadErrors = [];
175
+ const overridePath =
176
+ env.MUSTARDSCRIPT_NATIVE_LIBRARY_PATH ??
177
+ env.MUSTARD_NATIVE_LIBRARY_PATH ??
178
+ env.NAPI_RS_NATIVE_LIBRARY_PATH;
179
+ if (overridePath) {
180
+ try {
181
+ return require(resolveNativeAddonPath(overridePath, 'native library override', overrideCwd));
182
+ } catch (error) {
183
+ loadErrors.push(error);
184
+ }
185
+ }
186
+
187
+ for (const candidate of localBinaryCandidates(searchRoot)) {
188
+ try {
189
+ return require(candidate);
190
+ } catch (error) {
191
+ loadErrors.push(error);
192
+ }
193
+ }
194
+
195
+ try {
196
+ const prebuilt = resolvePrebuiltPackage(searchRoot);
197
+ if (prebuilt) {
198
+ try {
199
+ return require(prebuilt.binaryPath);
200
+ } catch (error) {
201
+ loadErrors.push(error);
202
+ }
203
+ }
204
+ } catch (error) {
205
+ loadErrors.push(error);
206
+ }
207
+
208
+ const target = getCurrentPrebuiltTarget();
209
+ const platformHint = target
210
+ ? `${target.platformArchABI} via ${target.packageName}`
211
+ : `${process.platform}-${process.arch}`;
212
+ throw new AggregateError(
213
+ loadErrors,
214
+ `Unable to locate a MustardScript native addon for ${platformHint}. Install a matching optional prebuilt package or allow the source build to run.`,
215
+ );
216
+ }
217
+
218
+ module.exports = {
219
+ PREBUILT_TARGETS,
220
+ getCurrentPrebuiltTarget,
221
+ getLocalBinaryFilenames,
222
+ getLocalBuildOutputFile,
223
+ localBinaryCandidates,
224
+ resolveNativeAddonPath,
225
+ resolvePrebuiltPackage,
226
+ loadNative,
227
+ };
package/index.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+ export declare function cancelCancellationToken(tokenId: string): void
4
+
5
+ export declare function claimProgressSnapshot(snapshotIdentity: string): boolean
6
+
7
+ export declare function compileProgram(source: string): Buffer
8
+
9
+ export declare function createCancellationToken(): string
10
+
11
+ export declare function inspectSnapshot(snapshot: Buffer, policyJson: string): string
12
+
13
+ export declare function isProgressSnapshotUsed(snapshotIdentity: string): boolean
14
+
15
+ export declare function releaseCancellationToken(tokenId: string): void
16
+
17
+ export declare function releaseProgressSnapshot(snapshotIdentity: string): void
18
+
19
+ export declare function resumeProgram(snapshot: Buffer, payloadJson: string, policyJson: string, cancellationTokenId?: string | undefined | null): string
20
+
21
+ export declare function snapshotIdentity(snapshot: Buffer): string
22
+
23
+ export declare function startProgram(program: Buffer, optionsJson: string, cancellationTokenId?: string | undefined | null): string