space-data-module-sdk 0.8.12 → 0.8.14

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.
@@ -77,12 +77,64 @@ export const BrowserIncompatibleCapabilityIds = Object.freeze([
77
77
  "process_exec",
78
78
  "wallet_sign",
79
79
  "ipfs",
80
+ ]);
81
+
82
+ /**
83
+ * ENGINE-HOSTED capabilities: the three that reach into a live 3D engine —
84
+ * its scene graph, its entity collection, its render loop.
85
+ *
86
+ * These used to sit in `BrowserIncompatibleCapabilityIds`, which was exactly
87
+ * backwards and produced a compliance paradox: they are also in
88
+ * `RecommendedCapabilityIds`, so a module was simultaneously told to declare
89
+ * them and refused for declaring them alongside the only runtime where they
90
+ * can possibly work. The flow compiler compounded it by unconditionally
91
+ * subtracting `browser` from any composed flow whose capability union touched
92
+ * one — meaning no flow that needs the engine could ever target the runtime
93
+ * the engine lives in.
94
+ *
95
+ * The truth, established by sweep:
96
+ * - The ONE implementation of `scene_access` in the stack is OrbPro's
97
+ * `ProviderAccessPort` + the SDK's `providerAccessEngineAdapter`, both of
98
+ * which require a live Cesium/OrbPro `Scene`. That is a BROWSER object.
99
+ * - `NodeHostSupportedCapabilities` contains none of the three, and
100
+ * `assertCapability()` fails them closed with `host-capability-unsupported`.
101
+ * - `entity_access` and `render_hooks` have no serving implementation on any
102
+ * runtime yet — only manifest-vocabulary codec entries. They are declared
103
+ * engine-hosted here because that is where they can ever be served, not
104
+ * because they are wired today.
105
+ *
106
+ * So they are wasmedge-incompatible, not browser-incompatible. (The `wasi`
107
+ * portability baseline already refuses them via
108
+ * `StandaloneWasiCapabilityIds` — nothing outside that five-member subset is
109
+ * admitted to a standalone WASI artifact.)
110
+ *
111
+ * Ruling: graph/findings/official-harness-shapes.md §8.4
112
+ * Task: graph/tasks/harness-w0-immediate-fixes.md (W0.4)
113
+ */
114
+ export const EngineHostedCapabilityIds = Object.freeze([
80
115
  "scene_access",
81
116
  "entity_access",
82
117
  "render_hooks",
83
118
  ]);
84
119
 
85
- /** Which capabilities each runtime leg cannot serve. */
120
+ /**
121
+ * Capabilities the headless WasmEdge leg cannot serve. There is no scene, no
122
+ * entity collection and no render loop in a headless server runtime, and no
123
+ * adapter can invent one.
124
+ */
125
+ export const WasmEdgeIncompatibleCapabilityIds = Object.freeze([
126
+ ...EngineHostedCapabilityIds,
127
+ ]);
128
+
129
+ /**
130
+ * Which capabilities each runtime leg cannot serve.
131
+ *
132
+ * Consumed by `runtimeTargetGate.js` (load-time admission), the flow compiler's
133
+ * `collectFlowRuntimeTargets` (derived runtimeTargets), and
134
+ * `pluginCompliance.js` (publish-time declaration policy) — all three read
135
+ * THIS table, so a leg's policy is stated once.
136
+ */
86
137
  export const LegIncompatibleCapabilityIds = Object.freeze({
87
138
  browser: BrowserIncompatibleCapabilityIds,
139
+ wasmedge: WasmEdgeIncompatibleCapabilityIds,
88
140
  });
@@ -12,8 +12,11 @@ import {
12
12
  } from "./pluginCompliance.js";
13
13
  import {
14
14
  BrowserIncompatibleCapabilityIds,
15
+ EngineHostedCapabilityIds,
16
+ LegIncompatibleCapabilityIds,
15
17
  RecommendedCapabilityIds,
16
18
  StandaloneWasiCapabilityIds,
19
+ WasmEdgeIncompatibleCapabilityIds,
17
20
  } from "../capabilities.js";
18
21
  import { validateManifestAgainstStandardsCatalog } from "../standards/index.js";
19
22
 
@@ -50,6 +53,9 @@ export async function validateArtifactWithStandards(options = {}) {
50
53
 
51
54
  export {
52
55
  BrowserIncompatibleCapabilityIds,
56
+ EngineHostedCapabilityIds,
57
+ LegIncompatibleCapabilityIds,
58
+ WasmEdgeIncompatibleCapabilityIds,
53
59
  findManifestFiles,
54
60
  getWasmExportNames,
55
61
  getWasmExportNamesFromFile,
@@ -13,7 +13,7 @@ import {
13
13
  RuntimeTarget,
14
14
  } from "../runtime/constants.js";
15
15
  import {
16
- BrowserIncompatibleCapabilityIds,
16
+ LegIncompatibleCapabilityIds,
17
17
  RecommendedCapabilityIds,
18
18
  StandaloneWasiCapabilityIds,
19
19
  } from "../capabilities.js";
@@ -26,6 +26,10 @@ import {
26
26
  isPayloadSchemaHashValid,
27
27
  payloadSchemaIdentitiesEqual,
28
28
  } from "../manifest/typeRefs.js";
29
+ import {
30
+ PluginFamilyNames,
31
+ pluginFamilyByName,
32
+ } from "../manifest/normalize.js";
29
33
  import { isLegacyWildcardPort } from "./legacyWildcardPorts.js";
30
34
  import { SDS_MANIFEST_SECTION_NAME } from "../bundle/constants.js";
31
35
  import {
@@ -50,7 +54,18 @@ const ExternalInterfaceDirectionSet = new Set(
50
54
  const ExternalInterfaceKindSet = new Set(Object.values(ExternalInterfaceKind));
51
55
  const ProtocolRoleSet = new Set(Object.values(ProtocolRole));
52
56
  const ProtocolTransportKindSet = new Set(Object.values(ProtocolTransportKind));
53
- const BrowserIncompatibleCapabilitySet = new Set(BrowserIncompatibleCapabilityIds);
57
+ // One entry per runtime leg that has a declaration policy, built from the
58
+ // single table in capabilities.js. This used to be a browser-only constant,
59
+ // which is why an engine-hosted capability could only ever be described as
60
+ // "not available in the browser" — the exact inverse of the truth.
61
+ const LegIncompatibleCapabilitySets = Object.freeze(
62
+ Object.fromEntries(
63
+ Object.entries(LegIncompatibleCapabilityIds).map(([leg, ids]) => [
64
+ leg,
65
+ new Set(ids),
66
+ ]),
67
+ ),
68
+ );
54
69
  const StandaloneWasiProtocolTransportKindSet = new Set([
55
70
  ProtocolTransportKind.WASI_PIPE,
56
71
  ]);
@@ -126,6 +141,45 @@ function validateStringField(issues, value, location, label) {
126
141
  return true;
127
142
  }
128
143
 
144
+ /**
145
+ * `pluginFamily` is an ENUM, not a free string.
146
+ *
147
+ * This used to be `validateStringField`, so any spelling passed compliance and
148
+ * `normalizePluginFamily` then silently coerced it to ANALYSIS — the two
149
+ * halves of the same defect. Compliance now enforces the same vocabulary the
150
+ * normalizer refuses on, so a bad family is caught at check time with the
151
+ * valid set named, instead of at load time as a mislabelled module.
152
+ *
153
+ * Ruling: graph/findings/official-harness-shapes.md §4.7 / §8.3
154
+ */
155
+ function validatePluginFamilyField(issues, value, location) {
156
+ if (!isNonEmptyString(value)) {
157
+ pushIssue(
158
+ issues,
159
+ "error",
160
+ "missing-string",
161
+ "pluginFamily must be a non-empty string.",
162
+ location,
163
+ );
164
+ return false;
165
+ }
166
+ const normalized = value.trim().toLowerCase();
167
+ if (pluginFamilyByName[normalized] === undefined) {
168
+ pushIssue(
169
+ issues,
170
+ "error",
171
+ "unknown-plugin-family",
172
+ `pluginFamily "${value}" is not a member of the plugin family vocabulary. ` +
173
+ `Valid families: ${PluginFamilyNames.join(", ")}. ` +
174
+ `A new family is appended to schemas/PluginManifest.fbs (a projection ` +
175
+ `of the authoritative SDS pluginCategory), never invented in a manifest.`,
176
+ location,
177
+ );
178
+ return false;
179
+ }
180
+ return true;
181
+ }
182
+
129
183
  function validateCapabilityEntry(capability, issues, location) {
130
184
  if (isNonEmptyString(capability)) {
131
185
  return capability;
@@ -1412,21 +1466,27 @@ function validateRuntimeTargets(runtimeTargets, declaredCapabilities, issues, so
1412
1466
  );
1413
1467
  }
1414
1468
  }
1415
- if (
1416
- seenTargets.has(RuntimeTarget.BROWSER) &&
1417
- Array.isArray(declaredCapabilities)
1418
- ) {
1419
- for (const capability of declaredCapabilities) {
1420
- if (!BrowserIncompatibleCapabilitySet.has(capability)) {
1469
+ if (Array.isArray(declaredCapabilities)) {
1470
+ // Check EVERY declared leg that has a policy, not just the browser. An
1471
+ // engine-hosted capability (scene_access/entity_access/render_hooks) is
1472
+ // browser-ONLY, so the conflict it can raise is against `wasmedge`.
1473
+ for (const target of seenTargets) {
1474
+ const incompatible = LegIncompatibleCapabilitySets[target];
1475
+ if (!incompatible) {
1421
1476
  continue;
1422
1477
  }
1423
- pushIssue(
1424
- issues,
1425
- "error",
1426
- "capability-runtime-conflict",
1427
- `Capability "${capability}" is not available in the canonical browser runtime target.`,
1428
- `${sourceName}.capabilities`,
1429
- );
1478
+ for (const capability of declaredCapabilities) {
1479
+ if (!incompatible.has(capability)) {
1480
+ continue;
1481
+ }
1482
+ pushIssue(
1483
+ issues,
1484
+ "error",
1485
+ "capability-runtime-conflict",
1486
+ `Capability "${capability}" is not available in the canonical ${target} runtime target.`,
1487
+ `${sourceName}.capabilities`,
1488
+ );
1489
+ }
1430
1490
  }
1431
1491
  }
1432
1492
  }
@@ -1734,7 +1794,7 @@ export function validatePluginManifest(manifest, options = {}) {
1734
1794
  validateStringField(issues, manifest.pluginId, `${sourceName}.pluginId`, "pluginId");
1735
1795
  validateStringField(issues, manifest.name, `${sourceName}.name`, "name");
1736
1796
  validateStringField(issues, manifest.version, `${sourceName}.version`, "version");
1737
- validateStringField(issues, manifest.pluginFamily, `${sourceName}.pluginFamily`, "pluginFamily");
1797
+ validatePluginFamilyField(issues, manifest.pluginFamily, `${sourceName}.pluginFamily`);
1738
1798
 
1739
1799
  const rawDeclaredCapabilities = manifest.capabilities;
1740
1800
  let declaredCapabilities = null;
@@ -56,7 +56,7 @@ import {
56
56
  } from "../compiler/pthreadArtifactGuard.js";
57
57
  import { resolveWasiThreadsToolchain } from "../compiler/wasiThreadsToolchain.js";
58
58
  import {
59
- BrowserIncompatibleCapabilityIds,
59
+ LegIncompatibleCapabilityIds,
60
60
  validateArtifactWithStandards,
61
61
  validatePluginManifest,
62
62
  } from "../compliance/index.js";
@@ -904,9 +904,19 @@ function collectComponentDependencies({ nodePluginIds, dependencies, issues, cap
904
904
  return [...components.values()].sort((a, b) => a.pluginId.localeCompare(b.pluginId));
905
905
  }
906
906
 
907
- const BROWSER_RUNTIME_TARGET = "browser";
908
- const BrowserIncompatibleCapabilitySet = new Set(
909
- BrowserIncompatibleCapabilityIds,
907
+ // Per-leg incompatibility, read from the ONE table in capabilities.js rather
908
+ // than a browser-only constant. An engine-hosted capability
909
+ // (scene_access/entity_access/render_hooks) is browser-ONLY, so the leg it
910
+ // narrows away is `wasmedge`; the old browser-only form could only ever
911
+ // subtract `browser`, which is how a flow that needs the engine ended up
912
+ // unable to target the runtime the engine lives in.
913
+ const LegIncompatibleCapabilitySets = Object.freeze(
914
+ Object.fromEntries(
915
+ Object.entries(LegIncompatibleCapabilityIds).map(([leg, ids]) => [
916
+ leg,
917
+ new Set(ids),
918
+ ]),
919
+ ),
910
920
  );
911
921
 
912
922
  function declaredRuntimeTargets(manifest) {
@@ -938,9 +948,11 @@ function declaredRuntimeTargets(manifest) {
938
948
  * exactly that capability against a browser target. Deriving only from the
939
949
  * declarations left the commonest manifest shape re-creating the original
940
950
  * defect — the compiler stamping a target its own compliance pass then
941
- * rejects. So `browser` is also dropped whenever the flow's capability union
942
- * meets `BrowserIncompatibleCapabilityIds`, and the diagnostic names the
943
- * capability that cost it.
951
+ * rejects. So a leg is also dropped whenever the flow's capability union meets
952
+ * that leg's entry in `LegIncompatibleCapabilityIds`, and the diagnostic names
953
+ * the capability that cost it. That table is per-leg, not browser-only: the
954
+ * engine-hosted capabilities (scene_access/entity_access/render_hooks) narrow
955
+ * away `wasmedge`, in the other direction.
944
956
  *
945
957
  * This is the difference between a truthful manifest and a wish. The hardcoded
946
958
  * ["browser","wasmedge"] made every composition claim the browser, which made
@@ -1006,16 +1018,20 @@ function collectFlowRuntimeTargets({
1006
1018
  );
1007
1019
  }
1008
1020
 
1009
- const browserIncompatible = [...(capabilities ?? [])]
1010
- .filter((capability) => BrowserIncompatibleCapabilitySet.has(capability))
1011
- .sort();
1012
- if (browserIncompatible.length > 0) {
1021
+ const unionCapabilities = [...(capabilities ?? [])];
1022
+ for (const leg of COMPOSED_FLOW_RUNTIME_TARGET_UNIVERSE) {
1023
+ const incompatible = LegIncompatibleCapabilitySets[leg];
1024
+ if (!incompatible) continue;
1025
+ const offending = unionCapabilities
1026
+ .filter((capability) => incompatible.has(capability))
1027
+ .sort();
1028
+ if (offending.length === 0) continue;
1013
1029
  narrow(
1014
- (target) => target !== BROWSER_RUNTIME_TARGET,
1030
+ (target) => target !== leg,
1015
1031
  () =>
1016
- `the flow's capability union contains ${browserIncompatible
1032
+ `the flow's capability union contains ${offending
1017
1033
  .map((capability) => `"${capability}"`)
1018
- .join(", ")}, which the canonical browser runtime target cannot serve`,
1034
+ .join(", ")}, which the canonical ${leg} runtime target cannot serve`,
1019
1035
  );
1020
1036
  }
1021
1037
 
@@ -12,6 +12,14 @@ export declare enum PluginFamily {
12
12
  SDF = 7,
13
13
  INFRASTRUCTURE = 8,
14
14
  FLOW = 9,
15
- BRIDGE = 10
15
+ BRIDGE = 10,
16
+ MANEUVER = 11,
17
+ ORBIT_DETERMINATION = 12,
18
+ FOUNDATION = 13,
19
+ PARSER = 14,
20
+ VALIDATOR = 15,
21
+ EXPORTER = 16,
22
+ PUBLISHER = 17,
23
+ BASILISK = 18
16
24
  }
17
25
  //# sourceMappingURL=plugin-family.d.ts.map
@@ -16,5 +16,13 @@ export var PluginFamily;
16
16
  PluginFamily[PluginFamily["INFRASTRUCTURE"] = 8] = "INFRASTRUCTURE";
17
17
  PluginFamily[PluginFamily["FLOW"] = 9] = "FLOW";
18
18
  PluginFamily[PluginFamily["BRIDGE"] = 10] = "BRIDGE";
19
+ PluginFamily[PluginFamily["MANEUVER"] = 11] = "MANEUVER";
20
+ PluginFamily[PluginFamily["ORBIT_DETERMINATION"] = 12] = "ORBIT_DETERMINATION";
21
+ PluginFamily[PluginFamily["FOUNDATION"] = 13] = "FOUNDATION";
22
+ PluginFamily[PluginFamily["PARSER"] = 14] = "PARSER";
23
+ PluginFamily[PluginFamily["VALIDATOR"] = 15] = "VALIDATOR";
24
+ PluginFamily[PluginFamily["EXPORTER"] = 16] = "EXPORTER";
25
+ PluginFamily[PluginFamily["PUBLISHER"] = 17] = "PUBLISHER";
26
+ PluginFamily[PluginFamily["BASILISK"] = 18] = "BASILISK";
19
27
  })(PluginFamily || (PluginFamily = {}));
20
28
  //# sourceMappingURL=plugin-family.js.map
@@ -17,4 +17,12 @@ export enum PluginFamily {
17
17
  INFRASTRUCTURE = 8,
18
18
  FLOW = 9,
19
19
  BRIDGE = 10,
20
+ MANEUVER = 11,
21
+ ORBIT_DETERMINATION = 12,
22
+ FOUNDATION = 13,
23
+ PARSER = 14,
24
+ VALIDATOR = 15,
25
+ EXPORTER = 16,
26
+ PUBLISHER = 17,
27
+ BASILISK = 18,
20
28
  }
@@ -0,0 +1,118 @@
1
+ // ===========================================================================
2
+ // GENERATED FILE — DO NOT EDIT.
3
+ //
4
+ // Source of truth : schemas/orbpro/Propagator.fbs
5
+ // Generator : scripts/generate-propagator-abi.mjs
6
+ // Drift gate : scripts/check-propagator-abi.mjs (runs in `npm test`)
7
+ // Contract : docs/propagator-abi.md
8
+ //
9
+ // These constants exist so that no JavaScript consumer ever hard-codes a byte
10
+ // offset again. Read a state vector with ORBPRO_STATE_VECTOR.offsets.position,
11
+ // never with the literal 8.
12
+ // ===========================================================================
13
+
14
+ export const ReferenceFrame = Object.freeze({
15
+ TEME: 0,
16
+ J2000: 1,
17
+ ICRF: 2,
18
+ ECEF: 3,
19
+ MCI: 4,
20
+ MCMF: 5,
21
+ });
22
+
23
+ export const StateFlags = Object.freeze({
24
+ NONE: 0,
25
+ VALID: 1,
26
+ IN_ECLIPSE: 2,
27
+ DECAYED: 4,
28
+ MANEUVERING: 8,
29
+ EXTRAPOLATED: 16,
30
+ HAS_COVARIANCE: 32,
31
+ });
32
+
33
+ export const ORBPRO_STATE_VECTOR = Object.freeze({
34
+ name: "StateVector",
35
+ cName: "OrbProStateVector",
36
+ size: 64,
37
+ alignment: 8,
38
+ offsets: Object.freeze({
39
+ epoch: 0,
40
+ position: 8,
41
+ velocity: 32,
42
+ reference_frame: 56,
43
+ flags: 60,
44
+ }),
45
+ fields: Object.freeze({
46
+ epoch: Object.freeze({ offset: 0, size: 8, length: 1, view: "Float64" }),
47
+ position: Object.freeze({ offset: 8, size: 24, length: 3, view: "Float64" }),
48
+ velocity: Object.freeze({ offset: 32, size: 24, length: 3, view: "Float64" }),
49
+ reference_frame: Object.freeze({ offset: 56, size: 1, length: 1, view: "Uint8" }),
50
+ flags: Object.freeze({ offset: 60, size: 4, length: 1, view: "Uint32" }),
51
+ }),
52
+ });
53
+
54
+ export const ORBPRO_ORBITAL_ELEMENTS = Object.freeze({
55
+ name: "OrbitalElements",
56
+ cName: "OrbProOrbitalElements",
57
+ size: 64,
58
+ alignment: 8,
59
+ offsets: Object.freeze({
60
+ semi_major_axis: 0,
61
+ eccentricity: 8,
62
+ inclination: 16,
63
+ raan: 24,
64
+ arg_periapsis: 32,
65
+ true_anomaly: 40,
66
+ epoch: 48,
67
+ reserved: 56,
68
+ }),
69
+ fields: Object.freeze({
70
+ semi_major_axis: Object.freeze({ offset: 0, size: 8, length: 1, view: "Float64" }),
71
+ eccentricity: Object.freeze({ offset: 8, size: 8, length: 1, view: "Float64" }),
72
+ inclination: Object.freeze({ offset: 16, size: 8, length: 1, view: "Float64" }),
73
+ raan: Object.freeze({ offset: 24, size: 8, length: 1, view: "Float64" }),
74
+ arg_periapsis: Object.freeze({ offset: 32, size: 8, length: 1, view: "Float64" }),
75
+ true_anomaly: Object.freeze({ offset: 40, size: 8, length: 1, view: "Float64" }),
76
+ epoch: Object.freeze({ offset: 48, size: 8, length: 1, view: "Float64" }),
77
+ reserved: Object.freeze({ offset: 56, size: 8, length: 1, view: "Float64" }),
78
+ }),
79
+ });
80
+
81
+ export const ORBPRO_OMM_RECORD = Object.freeze({
82
+ name: "OMMRecord",
83
+ cName: "OrbProOMMRecord",
84
+ size: 88,
85
+ alignment: 8,
86
+ offsets: Object.freeze({
87
+ epoch_jd: 0,
88
+ mean_motion: 8,
89
+ eccentricity: 16,
90
+ inclination: 24,
91
+ ra_of_asc_node: 32,
92
+ arg_of_pericenter: 40,
93
+ mean_anomaly: 48,
94
+ bstar: 56,
95
+ mean_motion_dot: 64,
96
+ mean_motion_ddot: 72,
97
+ norad_cat_id: 80,
98
+ }),
99
+ fields: Object.freeze({
100
+ epoch_jd: Object.freeze({ offset: 0, size: 8, length: 1, view: "Float64" }),
101
+ mean_motion: Object.freeze({ offset: 8, size: 8, length: 1, view: "Float64" }),
102
+ eccentricity: Object.freeze({ offset: 16, size: 8, length: 1, view: "Float64" }),
103
+ inclination: Object.freeze({ offset: 24, size: 8, length: 1, view: "Float64" }),
104
+ ra_of_asc_node: Object.freeze({ offset: 32, size: 8, length: 1, view: "Float64" }),
105
+ arg_of_pericenter: Object.freeze({ offset: 40, size: 8, length: 1, view: "Float64" }),
106
+ mean_anomaly: Object.freeze({ offset: 48, size: 8, length: 1, view: "Float64" }),
107
+ bstar: Object.freeze({ offset: 56, size: 8, length: 1, view: "Float64" }),
108
+ mean_motion_dot: Object.freeze({ offset: 64, size: 8, length: 1, view: "Float64" }),
109
+ mean_motion_ddot: Object.freeze({ offset: 72, size: 8, length: 1, view: "Float64" }),
110
+ norad_cat_id: Object.freeze({ offset: 80, size: 4, length: 1, view: "Uint32" }),
111
+ }),
112
+ });
113
+
114
+ export const ORBPRO_PROPAGATOR_ABI = Object.freeze({
115
+ StateVector: ORBPRO_STATE_VECTOR,
116
+ OrbitalElements: ORBPRO_ORBITAL_ELEMENTS,
117
+ OMMRecord: ORBPRO_OMM_RECORD,
118
+ });
@@ -0,0 +1,199 @@
1
+ // ===========================================================================
2
+ // GENERATED FILE — DO NOT EDIT.
3
+ //
4
+ // Source of truth : schemas/orbpro/Propagator.fbs
5
+ // Generator : scripts/generate-propagator-abi.mjs
6
+ // Drift gate : scripts/check-propagator-abi.mjs (runs in `npm test`)
7
+ // Contract : docs/propagator-abi.md
8
+ //
9
+ // These constants exist so that no JavaScript consumer ever hard-codes a byte
10
+ // offset again. Read a state vector with ORBPRO_STATE_VECTOR.offsets.position,
11
+ // never with the literal 8.
12
+ // ===========================================================================
13
+
14
+
15
+ export enum ReferenceFrame {
16
+ TEME = 0,
17
+ J2000 = 1,
18
+ ICRF = 2,
19
+ ECEF = 3,
20
+ MCI = 4,
21
+ MCMF = 5,
22
+ }
23
+
24
+ /**
25
+ * StateVector.flags is a BITFIELD carrying any OR-combination of these, so it
26
+ * is declared `uint` on the struct rather than typed to this enum. The C
27
+ * enumerators are generated from here anyway — they are the contract.
28
+ */
29
+ export enum StateFlags {
30
+ NONE = 0,
31
+ VALID = 1,
32
+ IN_ECLIPSE = 2,
33
+ DECAYED = 4,
34
+ MANEUVERING = 8,
35
+ EXTRAPOLATED = 16,
36
+ HAS_COVARIANCE = 32,
37
+ }
38
+
39
+ /** One field's placement inside an ABI struct. */
40
+ export interface AbiField {
41
+ readonly offset: number;
42
+ readonly size: number;
43
+ /** Element count for array fields, 1 for scalars. */
44
+ readonly length: number;
45
+ /** DataView accessor suffix, e.g. "Float64" for getFloat64. */
46
+ readonly view: string;
47
+ }
48
+
49
+ /** One ABI struct's byte layout. */
50
+ export interface AbiStruct {
51
+ readonly name: string;
52
+ readonly cName: string;
53
+ readonly size: number;
54
+ readonly alignment: number;
55
+ readonly offsets: Readonly<Record<string, number>>;
56
+ readonly fields: Readonly<Record<string, AbiField>>;
57
+ }
58
+
59
+ /**
60
+ * Orbital state vector — 64 bytes, 8-byte aligned. Mirrors
61
+ * `OrbProStateVector` in orbpro-integration/sdk/include/orbpro_propagator.h
62
+ * byte for byte:
63
+ *
64
+ * 0 8 epoch (Julian date, float64)
65
+ * 8 24 position (METERS)
66
+ * 32 24 velocity (METERS/SECOND)
67
+ * 56 1 reference_frame (ubyte)
68
+ * 57 3 padding, MUST be zero
69
+ * 60 4 flags (uint32)
70
+ *
71
+ * NORMATIVE UNITS: position METERS, velocity METERS/SECOND. There is no km
72
+ * variant and no host-side conversion — the engine hands these straight to
73
+ * Cesium Cartesian3, whose unit is metres, and both shipped propagators emit
74
+ * meters. The C header used to declare `reference_frame` as a uint32 at
75
+ * offset 56, wire-identical to this ubyte+padding only by little-endian
76
+ * accident; it now declares ubyte + 3 reserved so the two agree by
77
+ * construction.
78
+ *
79
+ * Ruling: graph/findings/official-harness-shapes.md §4.1 / §4.2
80
+ */
81
+ export const ORBPRO_STATE_VECTOR: AbiStruct = {
82
+ name: "StateVector",
83
+ cName: "OrbProStateVector",
84
+ size: 64,
85
+ alignment: 8,
86
+ offsets: {
87
+ epoch: 0,
88
+ position: 8,
89
+ velocity: 32,
90
+ reference_frame: 56,
91
+ flags: 60,
92
+ },
93
+ fields: {
94
+ epoch: { offset: 0, size: 8, length: 1, view: "Float64" },
95
+ position: { offset: 8, size: 24, length: 3, view: "Float64" },
96
+ velocity: { offset: 32, size: 24, length: 3, view: "Float64" },
97
+ reference_frame: { offset: 56, size: 1, length: 1, view: "Uint8" },
98
+ flags: { offset: 60, size: 4, length: 1, view: "Uint32" },
99
+ },
100
+ } as const;
101
+
102
+ /**
103
+ * Keplerian orbital elements — the OPTIONAL initialization input accepted by
104
+ * `plugin_init_elements`. 64 bytes, 8-byte aligned.
105
+ *
106
+ * UNITS NOTE: `semi_major_axis` is KILOMETRES. That is deliberate and it is
107
+ * NOT an inconsistency with StateVector's metres: this is an INPUT element
108
+ * set, not an output state vector, and the two are different structs on
109
+ * different sides of the call. Do not "unify" them — see the normative units
110
+ * block in the generated C header.
111
+ */
112
+ export const ORBPRO_ORBITAL_ELEMENTS: AbiStruct = {
113
+ name: "OrbitalElements",
114
+ cName: "OrbProOrbitalElements",
115
+ size: 64,
116
+ alignment: 8,
117
+ offsets: {
118
+ semi_major_axis: 0,
119
+ eccentricity: 8,
120
+ inclination: 16,
121
+ raan: 24,
122
+ arg_periapsis: 32,
123
+ true_anomaly: 40,
124
+ epoch: 48,
125
+ reserved: 56,
126
+ },
127
+ fields: {
128
+ semi_major_axis: { offset: 0, size: 8, length: 1, view: "Float64" },
129
+ eccentricity: { offset: 8, size: 8, length: 1, view: "Float64" },
130
+ inclination: { offset: 16, size: 8, length: 1, view: "Float64" },
131
+ raan: { offset: 24, size: 8, length: 1, view: "Float64" },
132
+ arg_periapsis: { offset: 32, size: 8, length: 1, view: "Float64" },
133
+ true_anomaly: { offset: 40, size: 8, length: 1, view: "Float64" },
134
+ epoch: { offset: 48, size: 8, length: 1, view: "Float64" },
135
+ reserved: { offset: 56, size: 8, length: 1, view: "Float64" },
136
+ },
137
+ } as const;
138
+
139
+ /**
140
+ * Binary OMM record — the mean-element ingest struct. 88 bytes, 8-byte
141
+ * aligned.
142
+ *
143
+ * !! THIS STRUCT IS ALSO AN ON-DISK FORMAT !!
144
+ * -----------------------------------------------------------------------
145
+ * It crosses the ABI (`plugin_init_omm`, `plugin_entity_add_omm`) AND is
146
+ * persisted verbatim as a SQLite BLOB by the first-party SGP4 module
147
+ * (`sgp4_plugin.cpp`, `sqlite3_bind_blob(..., &omm, sizeof(OrbProOMMRecord), ...)`).
148
+ * Until W1.1 it carried NO size or offset lock anywhere in the stack, so the
149
+ * layout that every stored blob depends on was held only by the field order
150
+ * of one hand-written C struct in one module.
151
+ *
152
+ * The layout declared here is that layout, EXACTLY as it has been written to
153
+ * disk — the four trailing padding bytes at offset 84 included. This is a
154
+ * description of the wire as it already exists, not a redesign of it; the
155
+ * generated locks now pin it. Migrating the format is explicitly out of
156
+ * scope and would invalidate every stored blob.
157
+ *
158
+ * UNITS: angles in DEGREES, mean motion in REV/DAY, bstar in 1/earth-radii.
159
+ * These are the SDS $OMM units, carried through unconverted.
160
+ */
161
+ export const ORBPRO_OMM_RECORD: AbiStruct = {
162
+ name: "OMMRecord",
163
+ cName: "OrbProOMMRecord",
164
+ size: 88,
165
+ alignment: 8,
166
+ offsets: {
167
+ epoch_jd: 0,
168
+ mean_motion: 8,
169
+ eccentricity: 16,
170
+ inclination: 24,
171
+ ra_of_asc_node: 32,
172
+ arg_of_pericenter: 40,
173
+ mean_anomaly: 48,
174
+ bstar: 56,
175
+ mean_motion_dot: 64,
176
+ mean_motion_ddot: 72,
177
+ norad_cat_id: 80,
178
+ },
179
+ fields: {
180
+ epoch_jd: { offset: 0, size: 8, length: 1, view: "Float64" },
181
+ mean_motion: { offset: 8, size: 8, length: 1, view: "Float64" },
182
+ eccentricity: { offset: 16, size: 8, length: 1, view: "Float64" },
183
+ inclination: { offset: 24, size: 8, length: 1, view: "Float64" },
184
+ ra_of_asc_node: { offset: 32, size: 8, length: 1, view: "Float64" },
185
+ arg_of_pericenter: { offset: 40, size: 8, length: 1, view: "Float64" },
186
+ mean_anomaly: { offset: 48, size: 8, length: 1, view: "Float64" },
187
+ bstar: { offset: 56, size: 8, length: 1, view: "Float64" },
188
+ mean_motion_dot: { offset: 64, size: 8, length: 1, view: "Float64" },
189
+ mean_motion_ddot: { offset: 72, size: 8, length: 1, view: "Float64" },
190
+ norad_cat_id: { offset: 80, size: 4, length: 1, view: "Uint32" },
191
+ },
192
+ } as const;
193
+
194
+ /** Every ABI struct, keyed by its IDL name. */
195
+ export const ORBPRO_PROPAGATOR_ABI: Readonly<Record<string, AbiStruct>> = {
196
+ StateVector: ORBPRO_STATE_VECTOR,
197
+ OrbitalElements: ORBPRO_ORBITAL_ELEMENTS,
198
+ OMMRecord: ORBPRO_OMM_RECORD,
199
+ } as const;