space-data-module-sdk 0.8.14 → 0.8.16
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/bin/space-data-module.js +91 -0
- package/docs/_shell/consumer-assets.html +5 -0
- package/docs/browser-wasmedge-isomorphic.html +261 -0
- package/docs/byo-wasm-quickstart.html +226 -0
- package/docs/byo-wasm-quickstart.md +204 -0
- package/docs/conformance.html +156 -0
- package/docs/conformance.md +92 -0
- package/docs/emception-shared-memory-verdict.md +209 -0
- package/docs/events-abi.html +313 -0
- package/docs/events-abi.md +530 -0
- package/docs/families/analytics.html +178 -0
- package/docs/families/analytics.md +104 -0
- package/docs/families/attitude.html +157 -0
- package/docs/families/attitude.md +36 -0
- package/docs/families/behavior.html +157 -0
- package/docs/families/behavior.md +36 -0
- package/docs/families/breakup.html +157 -0
- package/docs/families/breakup.md +36 -0
- package/docs/families/conjunction.html +186 -0
- package/docs/families/conjunction.md +121 -0
- package/docs/families/data-source.html +318 -0
- package/docs/families/effects.html +157 -0
- package/docs/families/effects.md +36 -0
- package/docs/families/environment.html +170 -0
- package/docs/families/environment.md +73 -0
- package/docs/families/estimation.html +190 -0
- package/docs/families/estimation.md +128 -0
- package/docs/families/gnc.html +157 -0
- package/docs/families/gnc.md +36 -0
- package/docs/families/maneuver.html +209 -0
- package/docs/families/maneuver.md +206 -0
- package/docs/families/obstruction.html +191 -0
- package/docs/families/obstruction.md +96 -0
- package/docs/families/propagator.html +333 -0
- package/docs/families/propulsion.html +157 -0
- package/docs/families/propulsion.md +36 -0
- package/docs/families/reentry.html +157 -0
- package/docs/families/reentry.md +36 -0
- package/docs/families/rf.html +193 -0
- package/docs/families/rf.md +136 -0
- package/docs/families/scheduler.html +157 -0
- package/docs/families/scheduler.md +36 -0
- package/docs/families/sensor.html +157 -0
- package/docs/families/sensor.md +36 -0
- package/docs/families/signature.html +157 -0
- package/docs/families/signature.md +36 -0
- package/docs/flatsql-host-contract.html +176 -0
- package/docs/flatsql-streaming-standard.html +375 -0
- package/docs/gpu-module-abi.html +233 -0
- package/docs/harness-family-matrix.md +135 -0
- package/docs/index.html +216 -85
- package/docs/isomorphic-pthreads.html +228 -0
- package/docs/isomorphic-pthreads.md +84 -0
- package/docs/isomorphic-sdn-runtime-plan.html +274 -0
- package/docs/language-runtime-matrix.html +182 -0
- package/docs/llms.txt +86 -0
- package/docs/module-bundle-runtime-plan.html +146 -0
- package/docs/module-publication-standard.html +503 -0
- package/docs/node-red-default-node-parity.html +201 -0
- package/docs/propagator-abi.md +73 -21
- package/docs/protect-and-sign.html +181 -0
- package/docs/protect-and-sign.md +120 -0
- package/docs/protocol-installation.html +296 -0
- package/docs/publication-submission.html +158 -0
- package/docs/publication-submission.md +78 -0
- package/docs/secrets-capability.html +206 -0
- package/docs/styles.css +860 -167
- package/docs/testing-harness.html +408 -0
- package/docs/tri-runtime-parity-gate.html +182 -0
- package/docs/tri-runtime-parity.html +203 -0
- package/include/orbpro/orbpro_event_runner.h +951 -0
- package/include/orbpro/orbpro_events_abi.h +565 -0
- package/include/orbpro/orbpro_propagator_abi.h +17 -0
- package/package.json +15 -5
- package/schemas/orbpro/Events.fbs +501 -0
- package/schemas/orbpro/Propagator.fbs +72 -9
- package/schemas/orbpro/reference-frame-crosswalk.json +108 -0
- package/schemas/orbpro/reference-frame.lock.json +98 -0
- package/src/browser.js +13 -0
- package/src/conformance/abiDriver.js +225 -0
- package/src/conformance/index.js +151 -0
- package/src/conformance/propagatorSuite.js +526 -0
- package/src/conformance/selfTest.js +275 -0
- package/src/conformance/selfTestCorpus.js +68 -0
- package/src/conformance/twoBodyReference.js +132 -0
- package/src/generated/orbpro/events-abi.js +206 -0
- package/src/generated/orbpro/events-abi.ts +319 -0
- package/src/generated/orbpro/propagator/reference-frame.js +17 -0
- package/src/generated/orbpro/propagator/reference-frame.ts +91 -1
- package/src/generated/orbpro/propagator-abi.js +17 -0
- package/src/generated/orbpro/propagator-abi.ts +17 -0
- package/src/generated/spacedatastandards/plg/CCT.d.ts +94 -0
- package/src/generated/spacedatastandards/plg/CCT.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/CCT.js +208 -0
- package/src/generated/spacedatastandards/plg/CCT.ts +281 -0
- package/src/generated/spacedatastandards/plg/CCTCategory.d.ts +98 -0
- package/src/generated/spacedatastandards/plg/CCTCategory.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/CCTCategory.js +177 -0
- package/src/generated/spacedatastandards/plg/CCTCategory.ts +269 -0
- package/src/generated/spacedatastandards/plg/CCTCategoryRollup.d.ts +54 -0
- package/src/generated/spacedatastandards/plg/CCTCategoryRollup.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/CCTCategoryRollup.js +106 -0
- package/src/generated/spacedatastandards/plg/CCTCategoryRollup.ts +144 -0
- package/src/generated/spacedatastandards/plg/PLG.d.ts +46 -2
- package/src/generated/spacedatastandards/plg/PLG.d.ts.map +1 -1
- package/src/generated/spacedatastandards/plg/PLG.js +78 -5
- package/src/generated/spacedatastandards/plg/PLG.ts +90 -5
- package/src/generated/spacedatastandards/plg/capabilityClass.d.ts +181 -0
- package/src/generated/spacedatastandards/plg/capabilityClass.d.ts.map +1 -0
- package/src/generated/spacedatastandards/plg/capabilityClass.js +183 -0
- package/src/generated/spacedatastandards/plg/capabilityClass.ts +208 -0
- package/src/generated/spacedatastandards/plg/main.d.ts +4 -0
- package/src/generated/spacedatastandards/plg/main.d.ts.map +1 -1
- package/src/generated/spacedatastandards/plg/main.js +4 -0
- package/src/generated/spacedatastandards/plg/main.ts +4 -0
- package/src/generated/spacedatastandards/plg/pluginCategory.d.ts +40 -2
- package/src/generated/spacedatastandards/plg/pluginCategory.d.ts.map +1 -1
- package/src/generated/spacedatastandards/plg/pluginCategory.js +40 -2
- package/src/generated/spacedatastandards/plg/pluginCategory.ts +40 -2
- package/src/host/browserModuleHarness.js +8 -0
- package/src/host/wasiThreadHost.js +189 -14
- package/src/index.d.ts +18 -1
- package/src/testing/isomorphicHarness.js +233 -0
- package/src/transport/index.js +16 -0
- package/src/transport/records.js +1015 -3
|
@@ -39,11 +39,128 @@ const IS_NODE =
|
|
|
39
39
|
!!process.release &&
|
|
40
40
|
process.release.name === "node";
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Browser worker asset resolution (the BUNDLED-CONSUMER anchor).
|
|
44
|
+
//
|
|
45
|
+
// `import.meta.url` is only a correct anchor when this file is served in its
|
|
46
|
+
// package layout (unbundled Node/dev). Any bundler that inlines this source
|
|
47
|
+
// rewrites `import.meta.url` to the BUNDLE's URL, and the sibling
|
|
48
|
+
// `wasiThreadBrowserWorker.mjs` was never emitted next to the bundle -> the
|
|
49
|
+
// Worker 404s. That is a real defect that shipped: five OrbPro/sdn-js bundles
|
|
50
|
+
// re-emitted this literal and the published sandcastle bucket requested
|
|
51
|
+
// `Build/CesiumUnminified/wasiThreadBrowserWorker.mjs`.
|
|
52
|
+
//
|
|
53
|
+
// So the anchor is now an EXPLICIT, documented parameter:
|
|
54
|
+
// - `createWasiThreadSpawn({ browserWorkerUrl })` — the worker file itself, or
|
|
55
|
+
// - `createWasiThreadSpawn({ browserWorkerBaseUrl })` — the DIRECTORY that
|
|
56
|
+
// holds the worker chain, or
|
|
57
|
+
// - `setBrowserWasiThreadWorkerBase(baseUrl)` — a process-wide default a host
|
|
58
|
+
// shim installs once (OrbPro points it at the served
|
|
59
|
+
// `js/vendor/space-data-module-sdk/src/host/`).
|
|
60
|
+
// The default stays today's `import.meta.url` sibling, so unbundled behavior is
|
|
61
|
+
// byte-for-byte unchanged.
|
|
62
|
+
//
|
|
63
|
+
// IMPORTANT: the anchor names a DIRECTORY that must contain the WHOLE chain —
|
|
64
|
+
// `wasiThreadBrowserWorker.mjs` imports `./wasiThreadWorkerRuntime.js`. Staging
|
|
65
|
+
// only the single `.mjs` next to a bundle does NOT work.
|
|
66
|
+
//
|
|
67
|
+
// There is deliberately NO consumer-side `location` sniffing and NO
|
|
68
|
+
// fetch-and-retry probe: resolution must be deterministic (Node never fetches;
|
|
69
|
+
// browser thread count may not become a function of network timing), and an
|
|
70
|
+
// unreachable worker asset fails LOUD (see WasiThreadWorkerUnreachableError).
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
const BROWSER_WORKER_FILENAME = "wasiThreadBrowserWorker.mjs";
|
|
74
|
+
|
|
75
|
+
/** Default anchor: the sibling asset in this file's own package layout. */
|
|
76
|
+
export const DEFAULT_BROWSER_WORKER_URL = new URL(
|
|
77
|
+
`./${BROWSER_WORKER_FILENAME}`,
|
|
44
78
|
import.meta.url,
|
|
45
79
|
);
|
|
46
80
|
|
|
81
|
+
let browserWorkerBaseOverride = null;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Install the process-wide browser worker base URL. Intended for a host shim
|
|
85
|
+
* (e.g. an engine bundle) that knows where its build published the SDK host
|
|
86
|
+
* directory. Pass `null` to restore the packaged default.
|
|
87
|
+
*
|
|
88
|
+
* @param {string|URL|null} baseUrl directory URL containing
|
|
89
|
+
* `wasiThreadBrowserWorker.mjs` AND `wasiThreadWorkerRuntime.js`. A trailing
|
|
90
|
+
* slash is added when missing.
|
|
91
|
+
*/
|
|
92
|
+
export function setBrowserWasiThreadWorkerBase(baseUrl) {
|
|
93
|
+
browserWorkerBaseOverride = baseUrl == null ? null : normalizeBase(baseUrl);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Current process-wide base override, or null when unset. */
|
|
97
|
+
export function getBrowserWasiThreadWorkerBase() {
|
|
98
|
+
return browserWorkerBaseOverride;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeBase(baseUrl) {
|
|
102
|
+
const asString = String(baseUrl);
|
|
103
|
+
return asString.endsWith("/") ? asString : `${asString}/`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the browser worker URL for one host. Precedence (highest first):
|
|
108
|
+
* 1. `browserWorkerUrl` option (the worker file itself)
|
|
109
|
+
* 2. `browserWorkerBaseUrl` option (directory)
|
|
110
|
+
* 3. `setBrowserWasiThreadWorkerBase()` process-wide base
|
|
111
|
+
* 4. packaged `import.meta.url` sibling (default)
|
|
112
|
+
*/
|
|
113
|
+
export function resolveBrowserWorkerUrl({
|
|
114
|
+
browserWorkerUrl,
|
|
115
|
+
browserWorkerBaseUrl,
|
|
116
|
+
} = {}) {
|
|
117
|
+
if (browserWorkerUrl) {
|
|
118
|
+
return String(browserWorkerUrl);
|
|
119
|
+
}
|
|
120
|
+
const base = browserWorkerBaseUrl
|
|
121
|
+
? normalizeBase(browserWorkerBaseUrl)
|
|
122
|
+
: browserWorkerBaseOverride;
|
|
123
|
+
if (base) {
|
|
124
|
+
// Resolve relative bases (e.g. "js/vendor/space-data-module-sdk/src/host/")
|
|
125
|
+
// against the document when one exists; absolute URLs pass through.
|
|
126
|
+
const documentBase =
|
|
127
|
+
typeof globalThis !== "undefined" &&
|
|
128
|
+
globalThis.location &&
|
|
129
|
+
typeof globalThis.location.href === "string"
|
|
130
|
+
? globalThis.location.href
|
|
131
|
+
: undefined;
|
|
132
|
+
return documentBase
|
|
133
|
+
? new URL(`${base}${BROWSER_WORKER_FILENAME}`, documentBase).href
|
|
134
|
+
: `${base}${BROWSER_WORKER_FILENAME}`;
|
|
135
|
+
}
|
|
136
|
+
return String(DEFAULT_BROWSER_WORKER_URL);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Thrown when the pooled browser path was REQUESTED (threads enabled,
|
|
141
|
+
* cross-origin isolated, shared memory) but the worker asset at the resolved
|
|
142
|
+
* anchor could not be loaded at all. This is the fail-loud replacement for the
|
|
143
|
+
* old silent sequential fallback: a 404'd worker is a deployment defect, not a
|
|
144
|
+
* capability negotiation, and it must never degrade quietly to one thread.
|
|
145
|
+
*/
|
|
146
|
+
export class WasiThreadWorkerUnreachableError extends Error {
|
|
147
|
+
constructor(workerUrl, cause) {
|
|
148
|
+
super(
|
|
149
|
+
`[wasi-thread] pooled browser worker asset is unreachable at ${workerUrl}. ` +
|
|
150
|
+
`The anchor must name a directory that serves BOTH ${BROWSER_WORKER_FILENAME} ` +
|
|
151
|
+
`and wasiThreadWorkerRuntime.js. When this host source is bundled, pass ` +
|
|
152
|
+
`browserWorkerBaseUrl / browserWorkerUrl to createWasiThreadSpawn (or call ` +
|
|
153
|
+
`setBrowserWasiThreadWorkerBase) — import.meta.url anchors to the bundle, not ` +
|
|
154
|
+
`to the package layout.`,
|
|
155
|
+
);
|
|
156
|
+
this.name = "WasiThreadWorkerUnreachableError";
|
|
157
|
+
this.workerUrl = String(workerUrl);
|
|
158
|
+
if (cause !== undefined) {
|
|
159
|
+
this.cause = cause;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
47
164
|
/**
|
|
48
165
|
* Does the compiled module require the wasi-threads host (i.e. does it import
|
|
49
166
|
* `wasi.thread-spawn`)? Single-thread artifacts do not, so the host is only
|
|
@@ -68,12 +185,21 @@ export function isWasiThreadsModule(wasmModule) {
|
|
|
68
185
|
const BROWSER_POOL_PROBE_TIMEOUT_MS = 1500;
|
|
69
186
|
|
|
70
187
|
// Arm the browser warm pool: probe every created worker and resolve a single
|
|
71
|
-
// all-or-nothing decision
|
|
72
|
-
// {t:"ready", ok:true}
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
188
|
+
// all-or-nothing decision, as `{ ok, unreachable, error }`.
|
|
189
|
+
// ok:true -> every worker confirmed {t:"ready", ok:true}
|
|
190
|
+
// ok:false -> a worker reported not-ready or missed the
|
|
191
|
+
// probe deadline: a committed, fast SEQUENTIAL
|
|
192
|
+
// fallback (capability negotiation).
|
|
193
|
+
// ok:false, unreachable:true -> a worker raised a worker-level `onerror`
|
|
194
|
+
// without ever speaking the pool protocol,
|
|
195
|
+
// i.e. the worker ASSET did not load (404 /
|
|
196
|
+
// wrong anchor / broken import chain). That is
|
|
197
|
+
// a deployment defect, not a capability, and
|
|
198
|
+
// the caller must fail LOUD.
|
|
199
|
+
// It resolves the instant any worker loses — never a wait for the slowest loser.
|
|
200
|
+
// The per-worker onmessage handler installed here is PERSISTENT: after arming it
|
|
201
|
+
// keeps dispatching {t:"exit"} (idle return) and {t:"error"} (guest fault
|
|
202
|
+
// surfacing) for the life of the pool.
|
|
77
203
|
function armBrowserPool(
|
|
78
204
|
created,
|
|
79
205
|
{ wasmModule, memory, hostcallChannel, timeoutMs, onExit },
|
|
@@ -82,7 +208,8 @@ function armBrowserPool(
|
|
|
82
208
|
let remaining = created.length;
|
|
83
209
|
let settled = false;
|
|
84
210
|
const timers = [];
|
|
85
|
-
const
|
|
211
|
+
const spoke = new WeakSet();
|
|
212
|
+
const finish = (ok, extra = {}) => {
|
|
86
213
|
if (settled) {
|
|
87
214
|
return;
|
|
88
215
|
}
|
|
@@ -90,13 +217,16 @@ function armBrowserPool(
|
|
|
90
217
|
for (const timer of timers) {
|
|
91
218
|
clearTimeout(timer);
|
|
92
219
|
}
|
|
93
|
-
resolve(ok);
|
|
220
|
+
resolve({ ok, unreachable: false, error: null, ...extra });
|
|
94
221
|
};
|
|
95
222
|
for (const worker of created) {
|
|
96
223
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
97
224
|
timers.push(timer);
|
|
98
225
|
worker.onmessage = (event) => {
|
|
99
226
|
const message = event.data || {};
|
|
227
|
+
// Any protocol message proves the worker script LOADED; a later onerror
|
|
228
|
+
// is then a guest fault, not an unreachable asset.
|
|
229
|
+
spoke.add(worker);
|
|
100
230
|
if (message.t === "ready") {
|
|
101
231
|
clearTimeout(timer);
|
|
102
232
|
if (message.ok === true) {
|
|
@@ -127,7 +257,9 @@ function armBrowserPool(
|
|
|
127
257
|
"[wasi-thread] pooled worker error:",
|
|
128
258
|
error?.message ?? error,
|
|
129
259
|
);
|
|
130
|
-
|
|
260
|
+
// A worker that errored without ever answering the pool protocol never
|
|
261
|
+
// ran our script: the asset at the resolved anchor is unreachable.
|
|
262
|
+
finish(false, { unreachable: !spoke.has(worker), error });
|
|
131
263
|
};
|
|
132
264
|
worker.postMessage({
|
|
133
265
|
t: "probe",
|
|
@@ -181,7 +313,20 @@ function detectHardwareConcurrency() {
|
|
|
181
313
|
* the generic module-host ABI.
|
|
182
314
|
* @param {boolean} [options.enableBrowserThreads] explicit successful host
|
|
183
315
|
* capability negotiation for an owning cross-origin-isolated worker harness.
|
|
316
|
+
* @param {string|URL} [options.browserWorkerBaseUrl] BROWSER ANCHOR — the
|
|
317
|
+
* directory URL under which this build serves the SDK host worker chain
|
|
318
|
+
* (`wasiThreadBrowserWorker.mjs` + `wasiThreadWorkerRuntime.js`). REQUIRED
|
|
319
|
+
* whenever this host source is BUNDLED: `import.meta.url` then resolves to the
|
|
320
|
+
* bundle, not to the package layout, and the sibling asset 404s. Defaults to
|
|
321
|
+
* the process-wide `setBrowserWasiThreadWorkerBase()` value, then to the
|
|
322
|
+
* packaged sibling.
|
|
323
|
+
* @param {string|URL} [options.browserWorkerUrl] the worker file itself; wins
|
|
324
|
+
* over `browserWorkerBaseUrl`. Use only when the file is not named
|
|
325
|
+
* `wasiThreadBrowserWorker.mjs` in its served directory.
|
|
184
326
|
* @returns {Promise<{ threadSpawn: Function, activeThreadCount: () => number, spawnCount: () => number, distinctOsThreadCount: () => number, terminateAll: () => Promise<void> }>}
|
|
327
|
+
* @throws {WasiThreadWorkerUnreachableError} in the browser, when the pooled
|
|
328
|
+
* path was requested but the worker asset at the resolved anchor never loaded.
|
|
329
|
+
* Deliberately fatal: a silent drop to one thread is the defect this replaces.
|
|
185
330
|
*/
|
|
186
331
|
export async function createWasiThreadSpawn({
|
|
187
332
|
wasmModule,
|
|
@@ -190,6 +335,8 @@ export async function createWasiThreadSpawn({
|
|
|
190
335
|
hostcallChannel,
|
|
191
336
|
requiresHostcalls = false,
|
|
192
337
|
enableBrowserThreads,
|
|
338
|
+
browserWorkerBaseUrl,
|
|
339
|
+
browserWorkerUrl,
|
|
193
340
|
} = {}) {
|
|
194
341
|
let nextTid = 0;
|
|
195
342
|
let spawnCount = 0;
|
|
@@ -319,17 +466,45 @@ export async function createWasiThreadSpawn({
|
|
|
319
466
|
};
|
|
320
467
|
|
|
321
468
|
if (poolSize > 0) {
|
|
469
|
+
const workerUrl = resolveBrowserWorkerUrl({
|
|
470
|
+
browserWorkerUrl,
|
|
471
|
+
browserWorkerBaseUrl,
|
|
472
|
+
});
|
|
322
473
|
const created = [];
|
|
323
|
-
|
|
324
|
-
|
|
474
|
+
try {
|
|
475
|
+
for (let i = 0; i < poolSize; i += 1) {
|
|
476
|
+
created.push(new Worker(workerUrl, { type: "module" }));
|
|
477
|
+
}
|
|
478
|
+
} catch (error) {
|
|
479
|
+
// Constructing a module Worker throws synchronously for a malformed or
|
|
480
|
+
// cross-origin-forbidden URL. Same class of defect as a 404: fail LOUD.
|
|
481
|
+
for (const worker of created) {
|
|
482
|
+
try {
|
|
483
|
+
worker.terminate();
|
|
484
|
+
} catch {
|
|
485
|
+
// best effort
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
throw new WasiThreadWorkerUnreachableError(workerUrl, error);
|
|
325
489
|
}
|
|
326
|
-
const
|
|
490
|
+
const armResult = await armBrowserPool(created, {
|
|
327
491
|
wasmModule,
|
|
328
492
|
memory,
|
|
329
493
|
hostcallChannel,
|
|
330
494
|
timeoutMs: BROWSER_POOL_PROBE_TIMEOUT_MS,
|
|
331
495
|
onExit: returnWorkerToIdle,
|
|
332
496
|
});
|
|
497
|
+
const armed = armResult.ok;
|
|
498
|
+
if (armResult.unreachable) {
|
|
499
|
+
for (const worker of created) {
|
|
500
|
+
try {
|
|
501
|
+
worker.terminate();
|
|
502
|
+
} catch {
|
|
503
|
+
// best effort
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
throw new WasiThreadWorkerUnreachableError(workerUrl, armResult.error);
|
|
507
|
+
}
|
|
333
508
|
if (armed) {
|
|
334
509
|
poolDisabled = false;
|
|
335
510
|
for (const worker of created) {
|
package/src/index.d.ts
CHANGED
|
@@ -996,13 +996,30 @@ export interface GuestLinkArtifact {
|
|
|
996
996
|
objectBytes: Uint8Array;
|
|
997
997
|
}
|
|
998
998
|
|
|
999
|
+
/**
|
|
1000
|
+
* The three legitimate thread models.
|
|
1001
|
+
*
|
|
1002
|
+
* `"wasi-sequential"` was missing here while `src/compiler/compileModule.js`
|
|
1003
|
+
* has accepted it since the model was introduced, so every TypeScript consumer
|
|
1004
|
+
* that declared the model the propagator and event-locator families call for
|
|
1005
|
+
* failed to typecheck against a compiler that would have accepted it. Adding
|
|
1006
|
+
* it is a declaration repair, not a new capability.
|
|
1007
|
+
*
|
|
1008
|
+
* `"single-thread"` is the LEGACY EMSCRIPTEN profile and routes to `em++`. It
|
|
1009
|
+
* is not a synonym for "this guest does not thread" — a guest that provably
|
|
1010
|
+
* never spawns declares `"wasi-sequential"`, which is built by the sanctioned
|
|
1011
|
+
* clang `wasm32-wasip1-threads` toolchain and requires
|
|
1012
|
+
* `manifest.sequentialJustification`.
|
|
1013
|
+
*/
|
|
999
1014
|
export type ModuleThreadModelName =
|
|
1000
1015
|
| "single-thread"
|
|
1001
|
-
| "emscripten-pthreads"
|
|
1016
|
+
| "emscripten-pthreads"
|
|
1017
|
+
| "wasi-sequential";
|
|
1002
1018
|
|
|
1003
1019
|
export const ModuleThreadModel: {
|
|
1004
1020
|
readonly SINGLE_THREAD: "single-thread";
|
|
1005
1021
|
readonly EMSCRIPTEN_PTHREADS: "emscripten-pthreads";
|
|
1022
|
+
readonly WASI_SEQUENTIAL: "wasi-sequential";
|
|
1006
1023
|
};
|
|
1007
1024
|
|
|
1008
1025
|
export interface ProtectedArtifact {
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The isomorphic module-test harness — ONE dist/isomorphic/module.wasm driven
|
|
3
|
+
* through the browser harness (SAB + Workers) and native WasmEdge with one
|
|
4
|
+
* API, so a module's behavior tests are written once and run per lane.
|
|
5
|
+
*
|
|
6
|
+
* MOVED INTO THE SDK by W1.4 of the official-harness-shapes program (it began
|
|
7
|
+
* life as space-data-network-modules/tests/lib/isomorphicHarness.mjs, which
|
|
8
|
+
* now re-exports this file). Living here makes it part of the SDK's testing
|
|
9
|
+
* contract: module repos stop copying it, and the conformance runner and the
|
|
10
|
+
* module suites drive artifacts through the same loader.
|
|
11
|
+
*
|
|
12
|
+
* Node-only (child_process, fs): import via `space-data-module-sdk/testing/isomorphic`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
|
|
20
|
+
import { createBrowserModuleHarness } from "../host/browserModuleHarness.js";
|
|
21
|
+
import { loadModule } from "../host/isomorphicLoader.js";
|
|
22
|
+
|
|
23
|
+
export const STANDALONE_RUNTIME_KINDS = Object.freeze(["browser", "wasmedge"]);
|
|
24
|
+
|
|
25
|
+
function resolveWasmPath(wasmPath) {
|
|
26
|
+
if (wasmPath instanceof URL) {
|
|
27
|
+
return fileURLToPath(wasmPath);
|
|
28
|
+
}
|
|
29
|
+
return String(wasmPath);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isMissingWasmEdgeError(error) {
|
|
33
|
+
return /spawn wasmedge ENOENT|command not found|Failed to launch/i.test(
|
|
34
|
+
String(error),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isWasmEdgeAvailable() {
|
|
39
|
+
const result = spawnSync("wasmedge", ["--version"], { stdio: "ignore" });
|
|
40
|
+
return result.status === 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function createStandaloneHarness(runtimeKind, wasmPath, options = {}) {
|
|
44
|
+
const resolvedWasmPath = resolveWasmPath(wasmPath);
|
|
45
|
+
if (runtimeKind === "browser") {
|
|
46
|
+
return createBrowserModuleHarness({
|
|
47
|
+
wasmSource: fs.readFileSync(resolvedWasmPath),
|
|
48
|
+
surface: options.surface ?? "command",
|
|
49
|
+
host: options.host,
|
|
50
|
+
hostOptions: options.hostOptions,
|
|
51
|
+
args: options.args,
|
|
52
|
+
env: options.env,
|
|
53
|
+
logOutput: options.logOutput,
|
|
54
|
+
performance: options.performance,
|
|
55
|
+
wasmMemory: options.wasmMemory,
|
|
56
|
+
memory: options.memory,
|
|
57
|
+
sharedMemory: options.sharedMemory,
|
|
58
|
+
allowRawInvoke: options.allowRawInvoke,
|
|
59
|
+
initialMemoryBytes: options.initialMemoryBytes,
|
|
60
|
+
maximumMemoryBytes: options.maximumMemoryBytes,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (runtimeKind === "wasmedge") {
|
|
65
|
+
return loadModule({
|
|
66
|
+
wasmSource: resolvedWasmPath,
|
|
67
|
+
runtimeKind: "wasmedge",
|
|
68
|
+
enableThreads: options.enableThreads ?? false,
|
|
69
|
+
wasmEdgeBinary: options.wasmEdgeBinary,
|
|
70
|
+
wasmEdgeRunnerBinary: options.wasmEdgeRunnerBinary,
|
|
71
|
+
args: options.args,
|
|
72
|
+
env: options.env,
|
|
73
|
+
cwd: options.cwd,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new Error(`Unsupported runtime kind: ${runtimeKind}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function createStandaloneHarnessOrSkip(
|
|
81
|
+
runtimeKind,
|
|
82
|
+
wasmPath,
|
|
83
|
+
t,
|
|
84
|
+
options = {},
|
|
85
|
+
) {
|
|
86
|
+
if (runtimeKind === "wasmedge" && !isWasmEdgeAvailable()) {
|
|
87
|
+
t.skip("Install wasmedge to verify the server-path harness.");
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
return await createStandaloneHarness(runtimeKind, wasmPath, options);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (runtimeKind === "wasmedge" && isMissingWasmEdgeError(error)) {
|
|
95
|
+
t.skip("Install wasmedge to verify the server-path harness.");
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function assertSuccessfulResponse(
|
|
103
|
+
response,
|
|
104
|
+
{ outputPortId = "response" } = {},
|
|
105
|
+
) {
|
|
106
|
+
assert.equal(response.statusCode, 0);
|
|
107
|
+
assert.ok(response.errorCode === "" || response.errorCode === null);
|
|
108
|
+
const frame = response.outputs.find((entry) => entry.portId === outputPortId);
|
|
109
|
+
assert.ok(frame, `missing ${outputPortId} output frame`);
|
|
110
|
+
return frame.payload;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function toPayloadBytes(payload) {
|
|
114
|
+
if (payload instanceof Uint8Array) {
|
|
115
|
+
return payload;
|
|
116
|
+
}
|
|
117
|
+
if (ArrayBuffer.isView(payload)) {
|
|
118
|
+
return new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
119
|
+
}
|
|
120
|
+
if (payload instanceof ArrayBuffer) {
|
|
121
|
+
return new Uint8Array(payload);
|
|
122
|
+
}
|
|
123
|
+
throw new TypeError("Binary module requests require Uint8Array-compatible payload bytes.");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isBrowserDirectHarness(harness) {
|
|
127
|
+
return harness?.runtime?.kind === "browser" && harness?.runtime?.surface === "direct";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function invokeBinaryRequest(
|
|
131
|
+
harness,
|
|
132
|
+
payload,
|
|
133
|
+
{
|
|
134
|
+
methodId = "invoke",
|
|
135
|
+
inputPortId = "request",
|
|
136
|
+
inputTypeRef = null,
|
|
137
|
+
alignment = 8,
|
|
138
|
+
} = {},
|
|
139
|
+
) {
|
|
140
|
+
const payloadBytes = toPayloadBytes(payload);
|
|
141
|
+
|
|
142
|
+
if (!isBrowserDirectHarness(harness)) {
|
|
143
|
+
const response = await harness.invoke({
|
|
144
|
+
methodId,
|
|
145
|
+
inputs: [
|
|
146
|
+
{
|
|
147
|
+
portId: inputPortId,
|
|
148
|
+
typeRef: inputTypeRef,
|
|
149
|
+
payload: payloadBytes,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
return response;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
typeof SharedArrayBuffer !== "function" ||
|
|
158
|
+
!(harness.memory?.buffer instanceof SharedArrayBuffer)
|
|
159
|
+
) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
"Browser direct binary requests require SharedArrayBuffer-backed module memory.",
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
const alloc = harness.instance?.exports?.plugin_alloc;
|
|
165
|
+
const free = harness.instance?.exports?.plugin_free;
|
|
166
|
+
if (typeof alloc !== "function" || typeof free !== "function") {
|
|
167
|
+
throw new Error(
|
|
168
|
+
"Browser direct binary requests require plugin_alloc and plugin_free exports.",
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const payloadSize = payloadBytes.byteLength;
|
|
173
|
+
const payloadPtr = payloadSize > 0 ? alloc(payloadSize) : 0;
|
|
174
|
+
if (payloadSize > 0 && !payloadPtr) {
|
|
175
|
+
throw new Error("plugin_alloc returned null for binary request payload.");
|
|
176
|
+
}
|
|
177
|
+
if (payloadSize > 0 && payloadPtr % alignment !== 0) {
|
|
178
|
+
if (payloadPtr > 0) {
|
|
179
|
+
free(payloadPtr, payloadSize);
|
|
180
|
+
}
|
|
181
|
+
throw new Error(
|
|
182
|
+
`plugin_alloc returned a payload pointer (${payloadPtr}) that is not ${alignment}-byte aligned.`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
if (payloadSize > 0) {
|
|
188
|
+
new Uint8Array(harness.memory.buffer, payloadPtr, payloadSize).set(payloadBytes);
|
|
189
|
+
}
|
|
190
|
+
const response = await harness.invoke({
|
|
191
|
+
methodId,
|
|
192
|
+
externalArena: new Uint8Array(harness.memory.buffer),
|
|
193
|
+
inputs: [
|
|
194
|
+
{
|
|
195
|
+
portId: inputPortId,
|
|
196
|
+
typeRef: inputTypeRef,
|
|
197
|
+
offset: payloadPtr,
|
|
198
|
+
size: payloadSize,
|
|
199
|
+
alignment,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
return response;
|
|
204
|
+
} finally {
|
|
205
|
+
if (payloadSize > 0) {
|
|
206
|
+
free(payloadPtr, payloadSize);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function invokeJsonRequest(
|
|
212
|
+
harness,
|
|
213
|
+
request,
|
|
214
|
+
{
|
|
215
|
+
methodId = "invoke",
|
|
216
|
+
inputPortId = "request",
|
|
217
|
+
outputPortId = "response",
|
|
218
|
+
inputTypeRef = null,
|
|
219
|
+
} = {},
|
|
220
|
+
) {
|
|
221
|
+
const response = await harness.invoke({
|
|
222
|
+
methodId,
|
|
223
|
+
inputs: [
|
|
224
|
+
{
|
|
225
|
+
portId: inputPortId,
|
|
226
|
+
typeRef: inputTypeRef,
|
|
227
|
+
payload: Buffer.from(JSON.stringify(request), "utf8"),
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
const payload = assertSuccessfulResponse(response, { outputPortId });
|
|
232
|
+
return JSON.parse(new TextDecoder().decode(payload));
|
|
233
|
+
}
|
package/src/transport/index.js
CHANGED
|
@@ -13,18 +13,34 @@ export {
|
|
|
13
13
|
|
|
14
14
|
export {
|
|
15
15
|
appendPublicationRecordCollection,
|
|
16
|
+
BUILD_PROFILE_FILE_IDENTIFIER,
|
|
17
|
+
BUILD_PROFILE_SIGNATURE_LENGTH,
|
|
18
|
+
canonicalBuildProfileJson,
|
|
16
19
|
createCidV1Raw,
|
|
17
20
|
createEncryptedEnvelopePayload,
|
|
18
21
|
createPublicationNotice,
|
|
19
22
|
decodeEncRecord,
|
|
20
23
|
decodePnmRecord,
|
|
24
|
+
decodeBuildProfile,
|
|
25
|
+
decodeBuildProfileRecordCollection,
|
|
21
26
|
decodeProtectedBlobBase64,
|
|
22
27
|
decodePublicationRecordCollection,
|
|
28
|
+
DEFAULT_RUNTIME_LOCK_TTL_DAYS,
|
|
29
|
+
encodeBuildProfile,
|
|
30
|
+
encodeBuildProfileRecordCollection,
|
|
23
31
|
encodeEncRecord,
|
|
24
32
|
encodePnmRecord,
|
|
25
33
|
encodePublicationRecordCollection,
|
|
26
34
|
extractPublicationRecordCollection,
|
|
35
|
+
MAX_RUNTIME_LOCK_TTL_DAYS,
|
|
36
|
+
MIN_RUNTIME_LOCK_TTL_DAYS,
|
|
37
|
+
signBuildProfile,
|
|
27
38
|
stripPublicationRecordCollection,
|
|
28
39
|
TRAILER_FOOTER_LENGTH,
|
|
29
40
|
TRAILER_MAGIC_TEXT,
|
|
41
|
+
validateBuildProfile,
|
|
42
|
+
verifyBuildProfile,
|
|
43
|
+
verifyBuildProfileCanonicalJsonSignature,
|
|
44
|
+
verifyBuildProfileFlatbufferSignature,
|
|
45
|
+
zeroBuildProfileSignaturePayloads,
|
|
30
46
|
} from "./records.js";
|