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.
Files changed (125) hide show
  1. package/bin/space-data-module.js +91 -0
  2. package/docs/_shell/consumer-assets.html +5 -0
  3. package/docs/browser-wasmedge-isomorphic.html +261 -0
  4. package/docs/byo-wasm-quickstart.html +226 -0
  5. package/docs/byo-wasm-quickstart.md +204 -0
  6. package/docs/conformance.html +156 -0
  7. package/docs/conformance.md +92 -0
  8. package/docs/emception-shared-memory-verdict.md +209 -0
  9. package/docs/events-abi.html +313 -0
  10. package/docs/events-abi.md +530 -0
  11. package/docs/families/analytics.html +178 -0
  12. package/docs/families/analytics.md +104 -0
  13. package/docs/families/attitude.html +157 -0
  14. package/docs/families/attitude.md +36 -0
  15. package/docs/families/behavior.html +157 -0
  16. package/docs/families/behavior.md +36 -0
  17. package/docs/families/breakup.html +157 -0
  18. package/docs/families/breakup.md +36 -0
  19. package/docs/families/conjunction.html +186 -0
  20. package/docs/families/conjunction.md +121 -0
  21. package/docs/families/data-source.html +318 -0
  22. package/docs/families/effects.html +157 -0
  23. package/docs/families/effects.md +36 -0
  24. package/docs/families/environment.html +170 -0
  25. package/docs/families/environment.md +73 -0
  26. package/docs/families/estimation.html +190 -0
  27. package/docs/families/estimation.md +128 -0
  28. package/docs/families/gnc.html +157 -0
  29. package/docs/families/gnc.md +36 -0
  30. package/docs/families/maneuver.html +209 -0
  31. package/docs/families/maneuver.md +206 -0
  32. package/docs/families/obstruction.html +191 -0
  33. package/docs/families/obstruction.md +96 -0
  34. package/docs/families/propagator.html +333 -0
  35. package/docs/families/propulsion.html +157 -0
  36. package/docs/families/propulsion.md +36 -0
  37. package/docs/families/reentry.html +157 -0
  38. package/docs/families/reentry.md +36 -0
  39. package/docs/families/rf.html +193 -0
  40. package/docs/families/rf.md +136 -0
  41. package/docs/families/scheduler.html +157 -0
  42. package/docs/families/scheduler.md +36 -0
  43. package/docs/families/sensor.html +157 -0
  44. package/docs/families/sensor.md +36 -0
  45. package/docs/families/signature.html +157 -0
  46. package/docs/families/signature.md +36 -0
  47. package/docs/flatsql-host-contract.html +176 -0
  48. package/docs/flatsql-streaming-standard.html +375 -0
  49. package/docs/gpu-module-abi.html +233 -0
  50. package/docs/harness-family-matrix.md +135 -0
  51. package/docs/index.html +216 -85
  52. package/docs/isomorphic-pthreads.html +228 -0
  53. package/docs/isomorphic-pthreads.md +84 -0
  54. package/docs/isomorphic-sdn-runtime-plan.html +274 -0
  55. package/docs/language-runtime-matrix.html +182 -0
  56. package/docs/llms.txt +86 -0
  57. package/docs/module-bundle-runtime-plan.html +146 -0
  58. package/docs/module-publication-standard.html +503 -0
  59. package/docs/node-red-default-node-parity.html +201 -0
  60. package/docs/propagator-abi.md +73 -21
  61. package/docs/protect-and-sign.html +181 -0
  62. package/docs/protect-and-sign.md +120 -0
  63. package/docs/protocol-installation.html +296 -0
  64. package/docs/publication-submission.html +158 -0
  65. package/docs/publication-submission.md +78 -0
  66. package/docs/secrets-capability.html +206 -0
  67. package/docs/styles.css +860 -167
  68. package/docs/testing-harness.html +408 -0
  69. package/docs/tri-runtime-parity-gate.html +182 -0
  70. package/docs/tri-runtime-parity.html +203 -0
  71. package/include/orbpro/orbpro_event_runner.h +951 -0
  72. package/include/orbpro/orbpro_events_abi.h +565 -0
  73. package/include/orbpro/orbpro_propagator_abi.h +17 -0
  74. package/package.json +15 -5
  75. package/schemas/orbpro/Events.fbs +501 -0
  76. package/schemas/orbpro/Propagator.fbs +72 -9
  77. package/schemas/orbpro/reference-frame-crosswalk.json +108 -0
  78. package/schemas/orbpro/reference-frame.lock.json +98 -0
  79. package/src/browser.js +13 -0
  80. package/src/conformance/abiDriver.js +225 -0
  81. package/src/conformance/index.js +151 -0
  82. package/src/conformance/propagatorSuite.js +526 -0
  83. package/src/conformance/selfTest.js +275 -0
  84. package/src/conformance/selfTestCorpus.js +68 -0
  85. package/src/conformance/twoBodyReference.js +132 -0
  86. package/src/generated/orbpro/events-abi.js +206 -0
  87. package/src/generated/orbpro/events-abi.ts +319 -0
  88. package/src/generated/orbpro/propagator/reference-frame.js +17 -0
  89. package/src/generated/orbpro/propagator/reference-frame.ts +91 -1
  90. package/src/generated/orbpro/propagator-abi.js +17 -0
  91. package/src/generated/orbpro/propagator-abi.ts +17 -0
  92. package/src/generated/spacedatastandards/plg/CCT.d.ts +94 -0
  93. package/src/generated/spacedatastandards/plg/CCT.d.ts.map +1 -0
  94. package/src/generated/spacedatastandards/plg/CCT.js +208 -0
  95. package/src/generated/spacedatastandards/plg/CCT.ts +281 -0
  96. package/src/generated/spacedatastandards/plg/CCTCategory.d.ts +98 -0
  97. package/src/generated/spacedatastandards/plg/CCTCategory.d.ts.map +1 -0
  98. package/src/generated/spacedatastandards/plg/CCTCategory.js +177 -0
  99. package/src/generated/spacedatastandards/plg/CCTCategory.ts +269 -0
  100. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.d.ts +54 -0
  101. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.d.ts.map +1 -0
  102. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.js +106 -0
  103. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.ts +144 -0
  104. package/src/generated/spacedatastandards/plg/PLG.d.ts +46 -2
  105. package/src/generated/spacedatastandards/plg/PLG.d.ts.map +1 -1
  106. package/src/generated/spacedatastandards/plg/PLG.js +78 -5
  107. package/src/generated/spacedatastandards/plg/PLG.ts +90 -5
  108. package/src/generated/spacedatastandards/plg/capabilityClass.d.ts +181 -0
  109. package/src/generated/spacedatastandards/plg/capabilityClass.d.ts.map +1 -0
  110. package/src/generated/spacedatastandards/plg/capabilityClass.js +183 -0
  111. package/src/generated/spacedatastandards/plg/capabilityClass.ts +208 -0
  112. package/src/generated/spacedatastandards/plg/main.d.ts +4 -0
  113. package/src/generated/spacedatastandards/plg/main.d.ts.map +1 -1
  114. package/src/generated/spacedatastandards/plg/main.js +4 -0
  115. package/src/generated/spacedatastandards/plg/main.ts +4 -0
  116. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts +40 -2
  117. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts.map +1 -1
  118. package/src/generated/spacedatastandards/plg/pluginCategory.js +40 -2
  119. package/src/generated/spacedatastandards/plg/pluginCategory.ts +40 -2
  120. package/src/host/browserModuleHarness.js +8 -0
  121. package/src/host/wasiThreadHost.js +189 -14
  122. package/src/index.d.ts +18 -1
  123. package/src/testing/isomorphicHarness.js +233 -0
  124. package/src/transport/index.js +16 -0
  125. package/src/transport/records.js +1015 -3
@@ -0,0 +1,108 @@
1
+ {
2
+ "note": "Every ReferenceFrame-shaped vocabulary that crosses a seam with the orbpro.propagator ABI enum, and how each of its members maps ONTO that enum BY NAMED TOKEN. Numeric identity is never asserted across repositories: ECI==0, TEME==0 and FIXED==0 all collide, and ECEF is 1 in orbpro.plugins but 3 in the ABI. scripts/check-reference-frame-uniqueness.mjs holds this file and schemas/orbpro/Propagator.fbs to each other, and re-reads any listed source that is present on disk.",
3
+ "authority": "schemas/orbpro/Propagator.fbs :: enum ReferenceFrame",
4
+ "vocabularies": [
5
+ {
6
+ "id": "orbpro.plugins",
7
+ "description": "PropagatorState.fbs / \"PRST\" \u2014 the wire the compiled sgp4, hpop, conjunction-assessment and sensor-shader modules already emit. Its values are FROZEN by artifacts in the field; collapsing it into the ABI enum is a wire break, tracked as sdk-reference-frame-enum-unification.",
8
+ "repo": "space-data-network-modules",
9
+ "source": "propagator/sgp4/schemas/PropagatorState.fbs",
10
+ "members": {
11
+ "ECI": "ICRF",
12
+ "ECEF": "ECEF",
13
+ "TEME": "TEME",
14
+ "ICRF": "ICRF"
15
+ }
16
+ },
17
+ {
18
+ "id": "orbpro.engine",
19
+ "description": "The OrbPro engine's ReferenceFrame.js. NINE members, not the two this SDK's comment block claimed until 2026-08-30. FIXED/INERTIAL are INVERTED against the ABI's 0/1 \u2014 a live semantic difference, not a numbering accident \u2014 and reconciling that is sdk-reference-frame-enum-unification's job, not this crosswalk's. The four local orbital triads collapse onto OBJECT_REFERENCED with an axes spec.",
20
+ "repo": "OrbPro",
21
+ "source": "packages/engine/Source/Core/ReferenceFrame.js",
22
+ "members": {
23
+ "FIXED": "ECEF",
24
+ "INERTIAL": "ICRF",
25
+ "TEME": "TEME",
26
+ "VVLH": "OBJECT_REFERENCED",
27
+ "ENU": "TOPOCENTRIC",
28
+ "NED": "TOPOCENTRIC",
29
+ "NEU": "TOPOCENTRIC",
30
+ "RIC": "OBJECT_REFERENCED",
31
+ "LVLH": "OBJECT_REFERENCED"
32
+ }
33
+ },
34
+ {
35
+ "id": "orbpro.conjunction",
36
+ "description": "ConjunctionCommon.fbs, carried by the conjunction module.",
37
+ "repo": "space-data-network-modules",
38
+ "source": "analysis/conjunction-assessment/schemas/ConjunctionCommon.fbs",
39
+ "members": {
40
+ "UNKNOWN": null,
41
+ "ECI": "ICRF",
42
+ "ECEF": "ECEF",
43
+ "TEME": "TEME",
44
+ "ICRF": "ICRF"
45
+ }
46
+ },
47
+ {
48
+ "id": "modules.foundation.frames",
49
+ "description": "foundation/frames AxisType, mirroring SDS rfmAxisType. It is an ORIENTATION RULE roster and carries no origin, so it is not a ReferenceFrame; the mapping below is the axis half only, and a consumer still needs the RFMOrigin to have a coordinate system.",
50
+ "repo": "space-data-network-modules",
51
+ "source": "foundation/frames/src/axis_engine.hpp",
52
+ "members": {
53
+ "UNSPECIFIED": null,
54
+ "MJ2000EQ": "J2000",
55
+ "MJ2000EC": "MJ2000EC",
56
+ "ICRF": "ICRF",
57
+ "TEME": "TEME",
58
+ "MOD": "MOD",
59
+ "TOD": "TOD",
60
+ "MOE": "MOE",
61
+ "TOE": "TOE",
62
+ "BODY_FIXED": "BODY_FIXED",
63
+ "BODY_INERTIAL": "BODY_INERTIAL",
64
+ "OBJECT_REFERENCED": "OBJECT_REFERENCED",
65
+ "LOCAL_ALIGNED_CONSTRAINED": "LOCAL_ALIGNED_CONSTRAINED",
66
+ "EQUATOR": "EQUATOR",
67
+ "GSE": "GSE",
68
+ "GSM": "GSM",
69
+ "TOPOCENTRIC": "TOPOCENTRIC",
70
+ "BODY_SPIN_SUN": "BODY_SPIN_SUN",
71
+ "SPICE_DEFINED": "SPICE_DEFINED",
72
+ "MOD_FK5": "MOD_FK5",
73
+ "TOD_FK5": "TOD_FK5",
74
+ "ITRF": "ECEF"
75
+ }
76
+ },
77
+ {
78
+ "id": "modules.hpop.coords",
79
+ "description": "The HPOP propagator's internal chain vocabulary.",
80
+ "repo": "space-data-network-modules",
81
+ "source": "propagator/hpop/lib/coords_types.h",
82
+ "members": {
83
+ "TEME": "TEME",
84
+ "GCRF": "ICRF",
85
+ "ITRF": "ECEF",
86
+ "J2000": "J2000",
87
+ "ECEF": "ECEF",
88
+ "TOD": "TOD",
89
+ "MOD": "MOD"
90
+ }
91
+ }
92
+ ],
93
+ "rtnConvention": {
94
+ "abiMember": "OBJECT_REFERENCED",
95
+ "note": "The three live RTN triads collapse to ONE ABI member plus an axes spec. RIC, RSW and RTN name the SAME triad (x radial, z orbit normal, y completing); LVLH and VVLH are relabellings of it produced by naming a different primary. None of them is a separate ReferenceFrame, and adding a second RTN-named ABI member is a defect this check refuses.",
96
+ "aliases": [
97
+ "RIC",
98
+ "RSW",
99
+ "RTN",
100
+ "LVLH",
101
+ "VVLH",
102
+ "QSW",
103
+ "NTW",
104
+ "TNW",
105
+ "UVW"
106
+ ]
107
+ }
108
+ }
@@ -0,0 +1,98 @@
1
+ {
2
+ "note": "The committed numbering of the ONE ReferenceFrame enum that crosses the ABI seam. scripts/check-reference-frame-uniqueness.mjs holds schemas/orbpro/Propagator.fbs to this file: values 0-5 are FROZEN and no member may ever be renamed, renumbered or removed. Appending a member means appending a row here in the same commit.",
3
+ "frozenThrough": 5,
4
+ "members": [
5
+ {
6
+ "name": "TEME",
7
+ "value": 0
8
+ },
9
+ {
10
+ "name": "J2000",
11
+ "value": 1
12
+ },
13
+ {
14
+ "name": "ICRF",
15
+ "value": 2
16
+ },
17
+ {
18
+ "name": "ECEF",
19
+ "value": 3
20
+ },
21
+ {
22
+ "name": "MCI",
23
+ "value": 4
24
+ },
25
+ {
26
+ "name": "MCMF",
27
+ "value": 5
28
+ },
29
+ {
30
+ "name": "MJ2000EC",
31
+ "value": 6
32
+ },
33
+ {
34
+ "name": "MOD",
35
+ "value": 7
36
+ },
37
+ {
38
+ "name": "TOD",
39
+ "value": 8
40
+ },
41
+ {
42
+ "name": "MOE",
43
+ "value": 9
44
+ },
45
+ {
46
+ "name": "TOE",
47
+ "value": 10
48
+ },
49
+ {
50
+ "name": "BODY_FIXED",
51
+ "value": 11
52
+ },
53
+ {
54
+ "name": "BODY_INERTIAL",
55
+ "value": 12
56
+ },
57
+ {
58
+ "name": "OBJECT_REFERENCED",
59
+ "value": 13
60
+ },
61
+ {
62
+ "name": "LOCAL_ALIGNED_CONSTRAINED",
63
+ "value": 14
64
+ },
65
+ {
66
+ "name": "EQUATOR",
67
+ "value": 15
68
+ },
69
+ {
70
+ "name": "GSE",
71
+ "value": 16
72
+ },
73
+ {
74
+ "name": "GSM",
75
+ "value": 17
76
+ },
77
+ {
78
+ "name": "TOPOCENTRIC",
79
+ "value": 18
80
+ },
81
+ {
82
+ "name": "BODY_SPIN_SUN",
83
+ "value": 19
84
+ },
85
+ {
86
+ "name": "SPICE_DEFINED",
87
+ "value": 20
88
+ },
89
+ {
90
+ "name": "MOD_FK5",
91
+ "value": 21
92
+ },
93
+ {
94
+ "name": "TOD_FK5",
95
+ "value": 22
96
+ }
97
+ ]
98
+ }
package/src/browser.js CHANGED
@@ -29,6 +29,19 @@ export {
29
29
  zeroWasmBytes,
30
30
  isSharedArrayBufferLike,
31
31
  } from "./host/browserModuleHarness.js";
32
+ // The wasi-threads BROWSER WORKER ANCHOR. A bundling consumer MUST tell the SDK
33
+ // where its build serves the host worker chain from — `import.meta.url` inside a
34
+ // bundle points at the bundle, not at the package layout, and the worker 404s
35
+ // (fail-loud: WasiThreadWorkerUnreachableError). Either call
36
+ // `setBrowserWasiThreadWorkerBase(...)` once in the host shim, or pass
37
+ // `wasiThreadWorkerBaseUrl` to `createBrowserModuleHarness`.
38
+ export {
39
+ setBrowserWasiThreadWorkerBase,
40
+ getBrowserWasiThreadWorkerBase,
41
+ resolveBrowserWorkerUrl,
42
+ DEFAULT_BROWSER_WORKER_URL,
43
+ WasiThreadWorkerUnreachableError,
44
+ } from "./host/wasiThreadHost.js";
32
45
  // The runtime-target gate is reachable so a consumer can catch the refusal by
33
46
  // CLASS (`error instanceof RuntimeTargetError`) rather than by matching a
34
47
  // message string, and can ask the same question the loaders ask before it
@@ -0,0 +1,225 @@
1
+ /**
2
+ * The propagator-family conformance driver: load a WASM artifact and drive the
3
+ * official OrbPro propagator ABI (docs/propagator-abi.md) from JavaScript.
4
+ *
5
+ * Note what this file does NOT contain: a single hard-coded byte offset. Every
6
+ * read goes through ORBPRO_STATE_VECTOR / ORBPRO_OMM_RECORD, the bindings
7
+ * GENERATED from the same IDL the C header comes from
8
+ * (schemas/orbpro/Propagator.fbs -> scripts/generate-propagator-abi.mjs).
9
+ * The JS reader and the C writer cannot disagree, because neither of them
10
+ * wrote the layout down.
11
+ *
12
+ * This is the generalization of the reference module's test harness
13
+ * (space-data-network-modules propagator/keplerian-reference/tests/harness.mjs,
14
+ * the W1.2 exemplar) into the SDK, so `space-data-module conformance` can
15
+ * target ANY artifact claiming the propagator family — W1.4 of
16
+ * graph/tasks/official-harness-shapes-program.md.
17
+ */
18
+
19
+ import fs from "node:fs/promises";
20
+
21
+ import {
22
+ ORBPRO_OMM_RECORD,
23
+ ORBPRO_STATE_VECTOR,
24
+ ReferenceFrame,
25
+ StateFlags,
26
+ } from "../generated/orbpro/propagator-abi.js";
27
+
28
+ export { ORBPRO_OMM_RECORD, ORBPRO_STATE_VECTOR, ReferenceFrame, StateFlags };
29
+
30
+ /**
31
+ * Documented error codes — the ABI "Error codes" table in
32
+ * docs/propagator-abi.md. Every failure returns its OWN code; a propagator
33
+ * that answers -1 for everything is unconformable because the host cannot
34
+ * place the failure on the degradation ladder.
35
+ */
36
+ export const ErrorCode = Object.freeze({
37
+ OK: 0,
38
+ NOT_INITIALIZED: -1,
39
+ BAD_ENTITY_INDEX: -2,
40
+ NULL_OUTPUT: -3,
41
+ BAD_INPUT: -4,
42
+ NOT_CONVERGED: -5,
43
+ UNPHYSICAL: -6,
44
+ });
45
+
46
+ /**
47
+ * The export set the propagator harness requires. Structural absence of any
48
+ * of these is a Tier 0 conformance failure with the missing name in the
49
+ * report — never "not found".
50
+ */
51
+ export const REQUIRED_ABI_EXPORTS = Object.freeze([
52
+ "memory",
53
+ "plugin_alloc",
54
+ "plugin_free",
55
+ "plugin_init_omm",
56
+ "plugin_ingest_omm_one",
57
+ "plugin_propagate",
58
+ "plugin_propagate_batch",
59
+ "plugin_entity_count",
60
+ "plugin_destroy",
61
+ ]);
62
+
63
+ /**
64
+ * Instantiate an artifact under WASI preview1 and wrap it in the driver
65
+ * interface the conformance suite consumes.
66
+ *
67
+ * Deliberately NOT calling wasi.start(): SDK-built artifacts declare the
68
+ * `command` invoke surface, so `_start` is the stdin-driven invoke loop and
69
+ * blocks forever when driven from a test. The propagator ABI is a set of
70
+ * directly-callable exports — which is exactly how the engine calls it — so
71
+ * conformance calls them directly. The command surface is exercised by the
72
+ * parity gate, which stays the Tier 0 cross-runtime authority.
73
+ */
74
+ export async function loadPropagatorArtifact(artifactPath) {
75
+ const { WASI } = await import("node:wasi");
76
+ const bytes = await fs.readFile(artifactPath);
77
+ const wasi = new WASI({ version: "preview1", args: ["module"], env: {} });
78
+ const module = await WebAssembly.compile(bytes);
79
+ const instance = await WebAssembly.instantiate(module, wasi.getImportObject());
80
+ return new PropagatorAbiDriver(instance);
81
+ }
82
+
83
+ export class PropagatorAbiDriver {
84
+ constructor(instance) {
85
+ this.instance = instance;
86
+ this.exports = instance.exports;
87
+ }
88
+
89
+ /** Export names, for the Tier 0 structural check. */
90
+ exportNames() {
91
+ return Object.keys(this.exports);
92
+ }
93
+
94
+ get memory() {
95
+ return this.exports.memory;
96
+ }
97
+
98
+ /** Linear-memory size, the leak test's only honest metric. */
99
+ memoryBytes() {
100
+ return this.exports.memory.buffer.byteLength;
101
+ }
102
+
103
+ alloc(byteLength) {
104
+ const pointer = this.exports.plugin_alloc(byteLength);
105
+ if (pointer === 0) throw new Error(`plugin_alloc(${byteLength}) returned 0`);
106
+ return pointer;
107
+ }
108
+
109
+ free(pointer) {
110
+ this.exports.plugin_free(pointer);
111
+ }
112
+
113
+ /** Pack element sets into the ABI's OrbProOMMRecord layout. */
114
+ packOmmRecords(records) {
115
+ const { size, offsets } = ORBPRO_OMM_RECORD;
116
+ const buffer = new ArrayBuffer(size * records.length);
117
+ const view = new DataView(buffer);
118
+ records.forEach((record, index) => {
119
+ const base = index * size;
120
+ view.setFloat64(base + offsets.epoch_jd, record.epochJd, true);
121
+ view.setFloat64(base + offsets.mean_motion, record.meanMotionRevPerDay, true);
122
+ view.setFloat64(base + offsets.eccentricity, record.eccentricity, true);
123
+ view.setFloat64(base + offsets.inclination, record.inclinationDeg, true);
124
+ view.setFloat64(base + offsets.ra_of_asc_node, record.raOfAscNodeDeg, true);
125
+ view.setFloat64(base + offsets.arg_of_pericenter, record.argOfPericenterDeg, true);
126
+ view.setFloat64(base + offsets.mean_anomaly, record.meanAnomalyDeg, true);
127
+ view.setFloat64(base + offsets.bstar, record.bstar ?? 0, true);
128
+ view.setFloat64(base + offsets.mean_motion_dot, record.meanMotionDot ?? 0, true);
129
+ view.setFloat64(base + offsets.mean_motion_ddot, record.meanMotionDdot ?? 0, true);
130
+ view.setUint32(base + offsets.norad_cat_id, record.noradCatId ?? 0, true);
131
+ });
132
+ return new Uint8Array(buffer);
133
+ }
134
+
135
+ initFromOmm(records) {
136
+ const packed = this.packOmmRecords(records);
137
+ const pointer = this.alloc(packed.byteLength);
138
+ new Uint8Array(this.memory.buffer).set(packed, pointer);
139
+ try {
140
+ return this.exports.plugin_init_omm(pointer, records.length);
141
+ } finally {
142
+ this.free(pointer);
143
+ }
144
+ }
145
+
146
+ ingestOne(record) {
147
+ const packed = this.packOmmRecords([record]);
148
+ const pointer = this.alloc(packed.byteLength);
149
+ new Uint8Array(this.memory.buffer).set(packed, pointer);
150
+ try {
151
+ return this.exports.plugin_ingest_omm_one(pointer);
152
+ } finally {
153
+ this.free(pointer);
154
+ }
155
+ }
156
+
157
+ /** Returns {status, state, bytes} — bytes for the byte-identity checks. */
158
+ propagate(julianDate, entityIndex) {
159
+ const pointer = this.alloc(ORBPRO_STATE_VECTOR.size);
160
+ try {
161
+ const status = this.exports.plugin_propagate(julianDate, entityIndex, pointer);
162
+ const bytes = new Uint8Array(
163
+ this.memory.buffer.slice(pointer, pointer + ORBPRO_STATE_VECTOR.size),
164
+ );
165
+ return { status, state: decodeStateVector(bytes), bytes };
166
+ } finally {
167
+ this.free(pointer);
168
+ }
169
+ }
170
+
171
+ propagateBatch(julianDate, count) {
172
+ const pointer = this.alloc(ORBPRO_STATE_VECTOR.size * count);
173
+ try {
174
+ const status = this.exports.plugin_propagate_batch(julianDate, pointer, count);
175
+ const states = [];
176
+ for (let index = 0; index < count; index += 1) {
177
+ const start = pointer + index * ORBPRO_STATE_VECTOR.size;
178
+ states.push(
179
+ decodeStateVector(
180
+ new Uint8Array(this.memory.buffer.slice(start, start + ORBPRO_STATE_VECTOR.size)),
181
+ ),
182
+ );
183
+ }
184
+ return { status, states };
185
+ } finally {
186
+ this.free(pointer);
187
+ }
188
+ }
189
+
190
+ entityCount() {
191
+ return this.exports.plugin_entity_count();
192
+ }
193
+
194
+ destroy() {
195
+ this.exports.plugin_destroy();
196
+ }
197
+ }
198
+
199
+ /** Decode a state vector using the GENERATED offsets. */
200
+ export function decodeStateVector(bytes) {
201
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
202
+ const { offsets } = ORBPRO_STATE_VECTOR;
203
+ return {
204
+ epoch: view.getFloat64(offsets.epoch, true),
205
+ position: [
206
+ view.getFloat64(offsets.position + 0, true),
207
+ view.getFloat64(offsets.position + 8, true),
208
+ view.getFloat64(offsets.position + 16, true),
209
+ ],
210
+ velocity: [
211
+ view.getFloat64(offsets.velocity + 0, true),
212
+ view.getFloat64(offsets.velocity + 8, true),
213
+ view.getFloat64(offsets.velocity + 16, true),
214
+ ],
215
+ referenceFrame: view.getUint8(offsets.reference_frame),
216
+ // The three IDL-reserved bytes. Read them explicitly: they are part of the
217
+ // contract, and a writer that skips them leaves the previous call behind.
218
+ reserved: [
219
+ view.getUint8(offsets.reference_frame + 1),
220
+ view.getUint8(offsets.reference_frame + 2),
221
+ view.getUint8(offsets.reference_frame + 3),
222
+ ],
223
+ flags: view.getUint32(offsets.flags, true),
224
+ };
225
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * `space-data-module conformance <family>` — the official-harness conformance
3
+ * runner (finding graph/findings/official-harness-shapes.md §5; W1.4 of the
4
+ * harness program).
5
+ *
6
+ * WASM artifacts ONLY (owner ruling 2026-08-10: "No JS propagator!!!! WASM
7
+ * ONLY") — the runner instantiates a compiled module and drives the family's
8
+ * ABI; there is no path that certifies a JS object, because JS registries are
9
+ * internal engine plumbing, never a public contract.
10
+ *
11
+ * Verdict vocabulary matches the gauntlet's: PASS / PASS-WITH-GAPS / FAIL.
12
+ * A gap is a check that could not be adjudicated HERE (no corpus, or a lane
13
+ * another command owns) — named, never silent.
14
+ */
15
+
16
+ import crypto from "node:crypto";
17
+ import fs from "node:fs/promises";
18
+ import path from "node:path";
19
+
20
+ import { loadPropagatorArtifact } from "./abiDriver.js";
21
+ import { computeVerdict, runPropagatorSuite } from "./propagatorSuite.js";
22
+
23
+ export { ErrorCode, REQUIRED_ABI_EXPORTS } from "./abiDriver.js";
24
+ export {
25
+ computeVerdict,
26
+ runPropagatorSuite,
27
+ DEFAULT_LEAK_OPTIONS,
28
+ } from "./propagatorSuite.js";
29
+ export { runPropagatorSelfTest, formatSelfTestReport } from "./selfTest.js";
30
+
31
+ /**
32
+ * Families with a conformance kit. The vocabulary is the SDS pluginCategory
33
+ * projection (W0.3): unknown families are refused BY NAME with the known set,
34
+ * never coerced — the ANALYSIS fallback was the namespace corruption the
35
+ * finding killed.
36
+ */
37
+ export const CONFORMANCE_FAMILIES = Object.freeze(["propagator"]);
38
+
39
+ export class UnknownConformanceFamilyError extends Error {
40
+ constructor(family) {
41
+ super(
42
+ `no conformance kit for family "${family}" — kits exist for: ` +
43
+ `${CONFORMANCE_FAMILIES.join(", ")}. A family with no kit can never be CORE ` +
44
+ "(finding §5); maneuver is Wave 2 (EXPERIMENTAL), OD is deferred by ruling.",
45
+ );
46
+ this.name = "UnknownConformanceFamilyError";
47
+ this.family = family;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Locate a module's own corpus: <package-root>/vectors/vectors.json, walking
53
+ * up from the artifact (dist/isomorphic/module.wasm -> package root).
54
+ */
55
+ export async function resolveCorpusPath(artifactPath) {
56
+ let dir = path.dirname(path.resolve(artifactPath));
57
+ for (let depth = 0; depth < 5; depth += 1) {
58
+ const candidate = path.join(dir, "vectors", "vectors.json");
59
+ try {
60
+ await fs.access(candidate);
61
+ return candidate;
62
+ } catch {
63
+ // keep walking
64
+ }
65
+ const parent = path.dirname(dir);
66
+ if (parent === dir) break;
67
+ dir = parent;
68
+ }
69
+ return null;
70
+ }
71
+
72
+ export async function loadCorpus(vectorsPath) {
73
+ const raw = await fs.readFile(vectorsPath, "utf8");
74
+ const corpus = JSON.parse(raw);
75
+ if (!Array.isArray(corpus.cases)) {
76
+ throw new Error(`${vectorsPath} has no cases[] — not a conformance corpus`);
77
+ }
78
+ return corpus;
79
+ }
80
+
81
+ /**
82
+ * Run conformance for one family against one WASM artifact.
83
+ *
84
+ * @param {object} options
85
+ * @param {string} options.family e.g. "propagator"
86
+ * @param {string} options.artifactPath dist/isomorphic/module.wasm
87
+ * @param {string} [options.vectorsPath] corpus override; default = the
88
+ * module's own vectors/vectors.json, found by walking up from the artifact
89
+ * @param {object} [options.leak] {warmupCycles, measureCycles, entities}
90
+ */
91
+ export async function runConformance(options) {
92
+ const family = String(options.family ?? "").trim().toLowerCase();
93
+ if (!CONFORMANCE_FAMILIES.includes(family)) {
94
+ throw new UnknownConformanceFamilyError(options.family);
95
+ }
96
+ const artifactPath = path.resolve(options.artifactPath);
97
+ const artifactBytes = await fs.readFile(artifactPath);
98
+ const artifactSha256 = crypto
99
+ .createHash("sha256")
100
+ .update(artifactBytes)
101
+ .digest("hex");
102
+
103
+ let vectorsPath = options.vectorsPath
104
+ ? path.resolve(options.vectorsPath)
105
+ : await resolveCorpusPath(artifactPath);
106
+ let corpus = null;
107
+ if (vectorsPath) {
108
+ corpus = await loadCorpus(vectorsPath);
109
+ }
110
+
111
+ const checks = await runPropagatorSuite(
112
+ () => loadPropagatorArtifact(artifactPath),
113
+ { corpus, leak: options.leak },
114
+ );
115
+
116
+ return {
117
+ family,
118
+ artifact: { path: artifactPath, sha256: artifactSha256 },
119
+ corpus: vectorsPath
120
+ ? {
121
+ path: vectorsPath,
122
+ schemaVersion: corpus.schemaVersion ?? null,
123
+ cases: corpus.cases.length,
124
+ model: corpus.conformance?.model ?? null,
125
+ }
126
+ : null,
127
+ checks,
128
+ verdict: computeVerdict(checks),
129
+ };
130
+ }
131
+
132
+ export function formatConformanceReport(report) {
133
+ const lines = [];
134
+ lines.push(`conformance ${report.family} — ${report.verdict}`);
135
+ lines.push(` artifact ${report.artifact.path}`);
136
+ lines.push(` sha256 ${report.artifact.sha256}`);
137
+ if (report.corpus) {
138
+ lines.push(
139
+ ` corpus ${report.corpus.path} (${report.corpus.cases} cases` +
140
+ `${report.corpus.model ? `, model: ${report.corpus.model}` : ""})`,
141
+ );
142
+ } else {
143
+ lines.push(" corpus none supplied");
144
+ }
145
+ for (const check of report.checks) {
146
+ const marker =
147
+ check.status === "pass" ? "PASS" : check.status === "gap" ? "GAP " : "FAIL";
148
+ lines.push(` [${marker}] ${check.id} — ${check.detail}`);
149
+ }
150
+ return lines.join("\n");
151
+ }