@rybosome/tspice 0.0.3 → 0.0.8
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/README.md +145 -84
- package/backend-contract/dist/.tsbuildinfo +1 -1
- package/backend-contract/dist/domains/cells-windows.d.ts +94 -0
- package/backend-contract/dist/domains/cells-windows.js +10 -0
- package/backend-contract/dist/domains/coords-vectors.d.ts +53 -3
- package/backend-contract/dist/domains/dsk.d.ts +49 -0
- package/backend-contract/dist/domains/dsk.js +2 -0
- package/backend-contract/dist/domains/ek.d.ts +186 -0
- package/backend-contract/dist/domains/ek.js +8 -0
- package/backend-contract/dist/domains/ephemeris.d.ts +141 -3
- package/backend-contract/dist/domains/error.d.ts +42 -0
- package/backend-contract/dist/domains/error.js +33 -0
- package/backend-contract/dist/domains/file-io.d.ts +114 -0
- package/backend-contract/dist/domains/file-io.js +8 -0
- package/backend-contract/dist/domains/frames.d.ts +44 -4
- package/backend-contract/dist/domains/geometry-gf.d.ts +44 -0
- package/backend-contract/dist/domains/geometry-gf.js +14 -0
- package/backend-contract/dist/domains/geometry.d.ts +21 -1
- package/backend-contract/dist/domains/ids-names-normalize.d.ts +3 -0
- package/backend-contract/dist/domains/ids-names-normalize.js +74 -0
- package/backend-contract/dist/domains/ids-names.d.ts +37 -0
- package/backend-contract/dist/domains/kernel-pool.d.ts +134 -0
- package/backend-contract/dist/domains/kernel-pool.js +2 -0
- package/backend-contract/dist/domains/kernels-utils.d.ts +44 -0
- package/backend-contract/dist/domains/kernels-utils.js +265 -0
- package/backend-contract/dist/domains/kernels.d.ts +39 -3
- package/backend-contract/dist/domains/time.d.ts +102 -0
- package/backend-contract/dist/index.d.ts +34 -15
- package/backend-contract/dist/index.js +15 -1
- package/backend-contract/dist/shared/errors.d.ts +6 -0
- package/backend-contract/dist/shared/errors.js +8 -0
- package/backend-contract/dist/shared/mat3.d.ts +52 -0
- package/backend-contract/dist/shared/mat3.js +150 -0
- package/backend-contract/dist/shared/mat6.d.ts +34 -0
- package/backend-contract/dist/shared/mat6.js +116 -0
- package/backend-contract/dist/shared/spice-handles.d.ts +20 -0
- package/backend-contract/dist/shared/spice-handles.js +82 -0
- package/backend-contract/dist/shared/spice-int.d.ts +32 -0
- package/backend-contract/dist/shared/spice-int.js +41 -0
- package/backend-contract/dist/shared/types.d.ts +136 -5
- package/backend-contract/dist/shared/types.js +1 -1
- package/backend-contract/dist/shared/vec.d.ts +54 -0
- package/backend-contract/dist/shared/vec.js +162 -0
- package/backend-fake/dist/.tsbuildinfo +1 -1
- package/backend-fake/dist/index.d.ts +21 -1
- package/backend-fake/dist/index.js +1112 -33
- package/backend-node/dist/.tsbuildinfo +1 -1
- package/backend-node/dist/codec/arrays.d.ts +9 -0
- package/backend-node/dist/codec/arrays.js +36 -0
- package/backend-node/dist/codec/errors.d.ts +6 -6
- package/backend-node/dist/codec/errors.js +6 -6
- package/backend-node/dist/domains/cells-windows.d.ts +5 -0
- package/backend-node/dist/domains/cells-windows.js +112 -0
- package/backend-node/dist/domains/coords-vectors.d.ts +1 -0
- package/backend-node/dist/domains/coords-vectors.js +66 -0
- package/backend-node/dist/domains/dsk.d.ts +6 -0
- package/backend-node/dist/domains/dsk.js +108 -0
- package/backend-node/dist/domains/ek.d.ts +10 -0
- package/backend-node/dist/domains/ek.js +100 -0
- package/backend-node/dist/domains/ephemeris.d.ts +5 -1
- package/backend-node/dist/domains/ephemeris.js +150 -1
- package/backend-node/dist/domains/error.d.ts +5 -0
- package/backend-node/dist/domains/error.js +34 -0
- package/backend-node/dist/domains/file-io.d.ts +7 -0
- package/backend-node/dist/domains/file-io.js +105 -0
- package/backend-node/dist/domains/frames.d.ts +1 -0
- package/backend-node/dist/domains/frames.js +58 -6
- package/backend-node/dist/domains/geometry-gf.d.ts +5 -0
- package/backend-node/dist/domains/geometry-gf.js +74 -0
- package/backend-node/dist/domains/geometry.d.ts +1 -0
- package/backend-node/dist/domains/geometry.js +62 -0
- package/backend-node/dist/domains/ids-names.d.ts +2 -1
- package/backend-node/dist/domains/ids-names.js +30 -0
- package/backend-node/dist/domains/kernel-pool.d.ts +5 -0
- package/backend-node/dist/domains/kernel-pool.js +74 -0
- package/backend-node/dist/domains/kernels.d.ts +1 -0
- package/backend-node/dist/domains/kernels.js +100 -13
- package/backend-node/dist/domains/time.d.ts +1 -0
- package/backend-node/dist/domains/time.js +75 -1
- package/backend-node/dist/index.d.ts +5 -1
- package/backend-node/dist/index.js +62 -1
- package/backend-node/dist/lowlevel/binding.d.ts +3 -0
- package/backend-node/dist/lowlevel/binding.js +115 -0
- package/backend-node/dist/runtime/addon.d.ts +273 -2
- package/backend-node/dist/runtime/addon.js +3 -0
- package/backend-node/dist/runtime/kernel-staging.d.ts +17 -0
- package/backend-node/dist/runtime/kernel-staging.js +80 -7
- package/backend-node/dist/runtime/spice-handles.d.ts +3 -0
- package/backend-node/dist/runtime/spice-handles.js +2 -0
- package/backend-node/dist/runtime/virtual-output-staging.d.ts +16 -0
- package/backend-node/dist/runtime/virtual-output-staging.js +148 -0
- package/backend-wasm/dist/.tsbuildinfo +1 -1
- package/backend-wasm/dist/codec/alloc.d.ts +19 -0
- package/backend-wasm/dist/codec/alloc.js +64 -0
- package/backend-wasm/dist/codec/calls.d.ts +2 -0
- package/backend-wasm/dist/codec/calls.js +13 -24
- package/backend-wasm/dist/codec/errors.d.ts +6 -0
- package/backend-wasm/dist/codec/errors.js +34 -2
- package/backend-wasm/dist/codec/found.d.ts +2 -0
- package/backend-wasm/dist/codec/found.js +20 -43
- package/backend-wasm/dist/codec/strings.d.ts +31 -1
- package/backend-wasm/dist/codec/strings.js +93 -6
- package/backend-wasm/dist/domains/cells-windows.d.ts +9 -0
- package/backend-wasm/dist/domains/cells-windows.js +392 -0
- package/backend-wasm/dist/domains/coords-vectors.d.ts +1 -0
- package/backend-wasm/dist/domains/coords-vectors.js +377 -184
- package/backend-wasm/dist/domains/dsk.d.ts +6 -0
- package/backend-wasm/dist/domains/dsk.js +179 -0
- package/backend-wasm/dist/domains/ek.d.ts +6 -0
- package/backend-wasm/dist/domains/ek.js +543 -0
- package/backend-wasm/dist/domains/ephemeris.d.ts +4 -1
- package/backend-wasm/dist/domains/ephemeris.js +405 -46
- package/backend-wasm/dist/domains/error.d.ts +5 -0
- package/backend-wasm/dist/domains/error.js +109 -0
- package/backend-wasm/dist/domains/file-io.d.ts +7 -0
- package/backend-wasm/dist/domains/file-io.js +462 -0
- package/backend-wasm/dist/domains/frames.d.ts +1 -0
- package/backend-wasm/dist/domains/frames.js +139 -6
- package/backend-wasm/dist/domains/geometry-gf.d.ts +5 -0
- package/backend-wasm/dist/domains/geometry-gf.js +178 -0
- package/backend-wasm/dist/domains/geometry.d.ts +1 -0
- package/backend-wasm/dist/domains/geometry.js +210 -0
- package/backend-wasm/dist/domains/ids-names.d.ts +2 -1
- package/backend-wasm/dist/domains/ids-names.js +89 -0
- package/backend-wasm/dist/domains/kernel-pool.d.ts +5 -0
- package/backend-wasm/dist/domains/kernel-pool.js +357 -0
- package/backend-wasm/dist/domains/kernels.d.ts +1 -0
- package/backend-wasm/dist/domains/kernels.js +108 -4
- package/backend-wasm/dist/domains/time.d.ts +2 -0
- package/backend-wasm/dist/domains/time.js +235 -133
- package/backend-wasm/dist/index.d.ts +4 -2
- package/backend-wasm/dist/lowlevel/exports.d.ts +215 -1
- package/backend-wasm/dist/lowlevel/exports.js +217 -38
- package/backend-wasm/dist/runtime/create-backend-options.d.ts +21 -0
- package/backend-wasm/dist/runtime/create-backend.node.d.ts +11 -2
- package/backend-wasm/dist/runtime/create-backend.node.js +283 -14
- package/backend-wasm/dist/runtime/create-backend.web.d.ts +5 -2
- package/backend-wasm/dist/runtime/create-backend.web.js +40 -6
- package/backend-wasm/dist/runtime/fs.d.ts +6 -0
- package/backend-wasm/dist/runtime/fs.js +29 -3
- package/backend-wasm/dist/runtime/spice-handles.d.ts +3 -0
- package/backend-wasm/dist/runtime/spice-handles.js +2 -0
- package/backend-wasm/dist/runtime/virtual-outputs.d.ts +16 -0
- package/backend-wasm/dist/runtime/virtual-outputs.js +35 -0
- package/backend-wasm/dist/tspice_backend_wasm.node.js +3 -3
- package/backend-wasm/dist/tspice_backend_wasm.wasm +0 -0
- package/backend-wasm/dist/tspice_backend_wasm.web.js +1 -1
- package/core/dist/.tsbuildinfo +1 -1
- package/core/dist/index.d.ts +21 -0
- package/core/dist/index.js +57 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/backend.d.ts +15 -6
- package/dist/backend.js +3 -6
- package/dist/clients/createSpiceAsyncFromTransport.d.ts +5 -0
- package/dist/clients/createSpiceAsyncFromTransport.js +90 -0
- package/dist/clients/createSpiceSyncFromTransport.d.ts +5 -0
- package/dist/clients/createSpiceSyncFromTransport.js +88 -0
- package/dist/clients/spiceClients.d.ts +59 -0
- package/dist/clients/spiceClients.js +292 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +4 -0
- package/dist/index.d.ts +12 -7
- package/dist/index.js +5 -2
- package/dist/kernels/defaultKernelPathFromUrl.d.ts +8 -0
- package/dist/kernels/defaultKernelPathFromUrl.js +32 -0
- package/dist/kernels/kernelPack.d.ts +88 -0
- package/dist/kernels/kernelPack.js +122 -0
- package/dist/kernels/kernels.d.ts +98 -0
- package/dist/kernels/kernels.js +217 -0
- package/dist/kernels/naifKernelId.d.ts +2 -0
- package/dist/kernels/naifKernelId.js +2 -0
- package/dist/kit/index.d.ts +4 -0
- package/dist/kit/index.js +3 -0
- package/dist/kit/math/mat3.d.ts +31 -0
- package/dist/kit/math/mat3.js +82 -0
- package/dist/kit/spice/create-kit.d.ts +12 -0
- package/dist/kit/spice/create-kit.js +23 -0
- package/dist/kit/spice/frames.d.ts +8 -0
- package/dist/kit/spice/frames.js +16 -0
- package/dist/kit/spice/kernels.d.ts +14 -0
- package/dist/kit/spice/kernels.js +39 -0
- package/dist/kit/spice/state.d.ts +7 -0
- package/dist/kit/spice/state.js +36 -0
- package/dist/kit/spice/time.d.ts +9 -0
- package/dist/kit/spice/time.js +31 -0
- package/dist/kit/types/spice-types.d.ts +51 -0
- package/dist/spice.d.ts +10 -1
- package/dist/spice.js +84 -72
- package/dist/transport/caching/policy.d.ts +16 -0
- package/dist/transport/caching/policy.js +77 -0
- package/dist/transport/caching/withCaching.d.ts +125 -0
- package/dist/transport/caching/withCaching.js +335 -0
- package/dist/transport/caching/withCachingSync.d.ts +24 -0
- package/dist/transport/caching/withCachingSync.js +161 -0
- package/dist/transport/rpc/protocol.d.ts +35 -0
- package/dist/transport/rpc/protocol.js +56 -0
- package/dist/transport/rpc/taskScheduling.d.ts +20 -0
- package/dist/transport/rpc/taskScheduling.js +98 -0
- package/dist/transport/rpc/valueCodec.d.ts +5 -0
- package/dist/transport/rpc/valueCodec.js +106 -0
- package/dist/transport/types.d.ts +7 -0
- package/dist/transport/types.js +2 -0
- package/dist/types.d.ts +8 -17
- package/dist/types.js +2 -1
- package/dist/worker/browser/createSpiceWorker.d.ts +22 -0
- package/dist/worker/browser/createSpiceWorker.js +41 -0
- package/dist/worker/browser/createSpiceWorkerClient.d.ts +40 -0
- package/dist/worker/browser/createSpiceWorkerClient.js +99 -0
- package/dist/worker/browser/spiceWorkerEntry.d.ts +2 -0
- package/dist/worker/browser/spiceWorkerEntry.js +129 -0
- package/dist/worker/browser/spiceWorkerInlineSource.d.ts +2 -0
- package/dist/worker/browser/spiceWorkerInlineSource.js +4 -0
- package/dist/worker/index.d.ts +10 -0
- package/dist/worker/index.js +7 -0
- package/dist/worker/transport/createWorkerTransport.d.ts +69 -0
- package/dist/worker/transport/createWorkerTransport.js +398 -0
- package/dist/worker/transport/exposeTransportToWorker.d.ts +51 -0
- package/dist/worker/transport/exposeTransportToWorker.js +196 -0
- package/package.json +4 -4
- package/dist/spice-types.d.ts +0 -36
- /package/dist/{spice-types.js → kit/types/spice-types.js} +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Probe whether a true macrotask scheduler is available in the current runtime
|
|
3
|
+
* (without actually scheduling a task).
|
|
4
|
+
*/
|
|
5
|
+
export function canQueueMacrotask() {
|
|
6
|
+
// Prefer MessageChannel when available.
|
|
7
|
+
//
|
|
8
|
+
// Important: if MessageChannel exists but is broken (e.g. constructible but
|
|
9
|
+
// `postMessage` throws), we fall through to `setTimeout` rather than failing
|
|
10
|
+
// closed. This matches `queueMacrotask()` behavior.
|
|
11
|
+
if (typeof MessageChannel !== "undefined") {
|
|
12
|
+
try {
|
|
13
|
+
const { port1, port2 } = new MessageChannel();
|
|
14
|
+
try {
|
|
15
|
+
// Probe that `postMessage` works (some polyfills allow construction but
|
|
16
|
+
// throw on `postMessage`, which would otherwise deadlock queueing).
|
|
17
|
+
port2.postMessage(undefined);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
// Always close ports so they don't keep the event loop alive in Node.
|
|
22
|
+
// Be defensive: some polyfills/test environments may not implement `close()`.
|
|
23
|
+
try {
|
|
24
|
+
port1.close?.();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// ignore
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
port2.close?.();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Ignore and fall back to setTimeout.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return typeof setTimeout === "function";
|
|
42
|
+
}
|
|
43
|
+
/** Queue `fn` on the next macrotask (MessageChannel preferred; setTimeout fallback). */
|
|
44
|
+
export function queueMacrotask(fn, opts) {
|
|
45
|
+
const allowSyncFallback = opts?.allowSyncFallback ?? true;
|
|
46
|
+
// Prefer MessageChannel when available. This schedules a real task boundary
|
|
47
|
+
// without relying on timers, which makes it friendlier to fake-timer test
|
|
48
|
+
// environments.
|
|
49
|
+
try {
|
|
50
|
+
if (typeof MessageChannel !== "undefined") {
|
|
51
|
+
const { port1, port2 } = new MessageChannel();
|
|
52
|
+
const port1WithOnMessage = port1;
|
|
53
|
+
port1WithOnMessage.onmessage = () => {
|
|
54
|
+
port1WithOnMessage.onmessage = null;
|
|
55
|
+
// Close ports so this doesn't keep the event loop alive in Node.
|
|
56
|
+
// Be defensive: some polyfills/test environments may not implement
|
|
57
|
+
// `close()`.
|
|
58
|
+
try {
|
|
59
|
+
port1.close?.();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ignore
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
port2.close?.();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// ignore
|
|
69
|
+
}
|
|
70
|
+
fn();
|
|
71
|
+
};
|
|
72
|
+
port2.postMessage(undefined);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Ignore and fall back to setTimeout.
|
|
78
|
+
}
|
|
79
|
+
// Next preference: a real macrotask via setTimeout.
|
|
80
|
+
if (typeof setTimeout === "function") {
|
|
81
|
+
setTimeout(fn, 0);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// No macrotask scheduler available in this runtime.
|
|
85
|
+
if (allowSyncFallback)
|
|
86
|
+
fn();
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
/** Return a promise that resolves on the next macrotask boundary (rejects if unavailable). */
|
|
90
|
+
export function nextMacrotask() {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const ok = queueMacrotask(resolve, { allowSyncFallback: false });
|
|
93
|
+
if (!ok) {
|
|
94
|
+
reject(new Error("nextMacrotask(): no macrotask scheduler available (MessageChannel/setTimeout missing)"));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=taskScheduling.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Encode an arbitrary value into a structured-clone-safe shape. */
|
|
2
|
+
export declare function encodeRpcValue(value: unknown): unknown;
|
|
3
|
+
/** Decode a value that was encoded by {@link encodeRpcValue}. */
|
|
4
|
+
export declare function decodeRpcValue(value: unknown): unknown;
|
|
5
|
+
//# sourceMappingURL=valueCodec.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Mat3 } from "../../kit/math/mat3.js";
|
|
2
|
+
/**
|
|
3
|
+
* Values sent across the worker boundary must be structured-clone-safe.
|
|
4
|
+
*
|
|
5
|
+
* This codec provides a minimal, extensible tagged encoding for non-plain
|
|
6
|
+
* objects (e.g. `Mat3`) used by the tspice API.
|
|
7
|
+
*/
|
|
8
|
+
const tspiceRpcTagKey = "__tspiceRpcTag";
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return value !== null && typeof value === "object";
|
|
11
|
+
}
|
|
12
|
+
function isTaggedRecord(value) {
|
|
13
|
+
return isRecord(value) && tspiceRpcTagKey in value;
|
|
14
|
+
}
|
|
15
|
+
function isFiniteNumber(value) {
|
|
16
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
17
|
+
}
|
|
18
|
+
function isMat3RowMajorData(value) {
|
|
19
|
+
return (Array.isArray(value) && value.length === 9 && value.every(isFiniteNumber));
|
|
20
|
+
}
|
|
21
|
+
/** Encode an arbitrary value into a structured-clone-safe shape. */
|
|
22
|
+
export function encodeRpcValue(value) {
|
|
23
|
+
if (value instanceof Mat3) {
|
|
24
|
+
const data = Array.from(value.rowMajor);
|
|
25
|
+
return {
|
|
26
|
+
[tspiceRpcTagKey]: "Mat3",
|
|
27
|
+
layout: "rowMajor",
|
|
28
|
+
data,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
return value.map(encodeRpcValue);
|
|
33
|
+
}
|
|
34
|
+
// Fast-path: ArrayBuffer + views (TypedArrays/DataView) are structured-clone-
|
|
35
|
+
// safe and should pass through unchanged. This is important for transferring
|
|
36
|
+
// kernel bytes (e.g. Uint8Array) into a worker.
|
|
37
|
+
if (value instanceof ArrayBuffer)
|
|
38
|
+
return value;
|
|
39
|
+
if (ArrayBuffer.isView(value))
|
|
40
|
+
return value;
|
|
41
|
+
// SharedArrayBuffer exists in some environments (Node, some browsers) but is
|
|
42
|
+
// gated by cross-origin isolation in browsers. Allow it when present.
|
|
43
|
+
if (typeof SharedArrayBuffer !== "undefined" &&
|
|
44
|
+
value instanceof SharedArrayBuffer) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
if (isRecord(value)) {
|
|
48
|
+
// Allow byte buffers/typed arrays to cross the worker boundary.
|
|
49
|
+
// These are structured-clone-safe and commonly used for kernel outputs.
|
|
50
|
+
if (value instanceof ArrayBuffer)
|
|
51
|
+
return value;
|
|
52
|
+
if (ArrayBuffer.isView(value))
|
|
53
|
+
return value;
|
|
54
|
+
if (typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
// Preserve Date/Map/Set/etc as-is? No: those are not guaranteed to be
|
|
58
|
+
// structured-clone-safe across all targets. For now, we only support plain
|
|
59
|
+
// object literals.
|
|
60
|
+
const proto = Object.getPrototypeOf(value);
|
|
61
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
62
|
+
const ctorName = value.constructor?.name ??
|
|
63
|
+
proto.constructor?.name;
|
|
64
|
+
throw new Error("encodeRpcValue(): unsupported non-plain object for worker RPC " +
|
|
65
|
+
`(constructor=${String(ctorName)}). ` +
|
|
66
|
+
"Pass plain objects/arrays/primitives or extend the codec.");
|
|
67
|
+
}
|
|
68
|
+
const out = {};
|
|
69
|
+
for (const [k, v] of Object.entries(value)) {
|
|
70
|
+
out[k] = encodeRpcValue(v);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
/** Decode a value that was encoded by {@link encodeRpcValue}. */
|
|
77
|
+
export function decodeRpcValue(value) {
|
|
78
|
+
if (isTaggedRecord(value)) {
|
|
79
|
+
const tag = value[tspiceRpcTagKey];
|
|
80
|
+
if (tag === "Mat3") {
|
|
81
|
+
const layout = value.layout;
|
|
82
|
+
const data = value.data;
|
|
83
|
+
if (layout === "rowMajor" && isMat3RowMajorData(data)) {
|
|
84
|
+
// Mat3.fromRowMajor expects a branded Mat3RowMajor type. We validated
|
|
85
|
+
// the runtime shape here, so the cast is safe.
|
|
86
|
+
return Mat3.fromRowMajor(data);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
|
+
return value.map(decodeRpcValue);
|
|
92
|
+
}
|
|
93
|
+
if (isRecord(value)) {
|
|
94
|
+
// Preserve non-plain objects (e.g. TypedArrays) as-is.
|
|
95
|
+
const proto = Object.getPrototypeOf(value);
|
|
96
|
+
if (proto !== Object.prototype && proto !== null)
|
|
97
|
+
return value;
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [k, v] of Object.entries(value)) {
|
|
100
|
+
out[k] = decodeRpcValue(v);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=valueCodec.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -9,25 +9,16 @@ export type Vec6 = readonly [
|
|
|
9
9
|
number,
|
|
10
10
|
number
|
|
11
11
|
];
|
|
12
|
-
/** 3x3 matrix, row-major. */
|
|
13
|
-
export type Mat3 = readonly [
|
|
14
|
-
number,
|
|
15
|
-
number,
|
|
16
|
-
number,
|
|
17
|
-
number,
|
|
18
|
-
number,
|
|
19
|
-
number,
|
|
20
|
-
number,
|
|
21
|
-
number,
|
|
22
|
-
number
|
|
23
|
-
];
|
|
24
12
|
/** SPICE frame name (e.g. "J2000", "IAU_EARTH"). */
|
|
25
13
|
export type FrameName = string;
|
|
14
|
+
/** Canonical inertial reference frame. */
|
|
15
|
+
export declare const J2000: "J2000";
|
|
26
16
|
/** Aberration correction string accepted by `spkezr`. */
|
|
27
17
|
export type AberrationCorrection = "NONE" | "LT" | "LT+S" | "CN" | "CN+S" | "XLT" | "XLT+S" | "XCN" | "XCN+S";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
/** Seconds past J2000 (ET). */
|
|
19
|
+
export type SpiceTime = number;
|
|
20
|
+
/** Body identifier accepted by kit APIs (name or NAIF id). */
|
|
21
|
+
export type BodyRef = string | number;
|
|
31
22
|
export type StateVector = {
|
|
32
23
|
/** Seconds past J2000 (ET). */
|
|
33
24
|
et: SpiceTime;
|
|
@@ -45,8 +36,8 @@ export type StateVector = {
|
|
|
45
36
|
lightTime: number;
|
|
46
37
|
};
|
|
47
38
|
export type GetStateArgs = {
|
|
48
|
-
target:
|
|
49
|
-
observer:
|
|
39
|
+
target: BodyRef;
|
|
40
|
+
observer: BodyRef;
|
|
50
41
|
at: SpiceTime;
|
|
51
42
|
frame?: FrameName;
|
|
52
43
|
aberration?: AberrationCorrection;
|
package/dist/types.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { WorkerLike } from "../transport/createWorkerTransport.js";
|
|
2
|
+
export type CreateSpiceWorkerOptions = {
|
|
3
|
+
/** Override the worker entrypoint (advanced). */
|
|
4
|
+
url?: string | URL;
|
|
5
|
+
/**
|
|
6
|
+
* Override the WASM binary URL used by the default inline worker.
|
|
7
|
+
*
|
|
8
|
+
* When omitted, `createSpiceWorker()` will resolve the WASM binary URL
|
|
9
|
+
* relative to the published package layout.
|
|
10
|
+
*/
|
|
11
|
+
wasmUrl?: string | URL;
|
|
12
|
+
/**
|
|
13
|
+
* Options passed through to the `Worker` constructor.
|
|
14
|
+
*
|
|
15
|
+
* Typed loosely on purpose so this package doesn't require `lib.dom` types
|
|
16
|
+
* (and so it can be consumed in non-DOM TS configs).
|
|
17
|
+
*/
|
|
18
|
+
workerOptions?: Record<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
/** Create a Web Worker instance for the tspice worker entrypoint (inline by default). */
|
|
21
|
+
export declare function createSpiceWorker(opts?: CreateSpiceWorkerOptions): WorkerLike;
|
|
22
|
+
//# sourceMappingURL=createSpiceWorker.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SPICE_WORKER_INLINE_SOURCE } from "./spiceWorkerInlineSource.js";
|
|
2
|
+
/** Create a Web Worker instance for the tspice worker entrypoint (inline by default). */
|
|
3
|
+
export function createSpiceWorker(opts = {}) {
|
|
4
|
+
const WorkerCtor = globalThis.Worker;
|
|
5
|
+
if (typeof WorkerCtor !== "function") {
|
|
6
|
+
throw new Error("createSpiceWorker() requires Web Worker support in the current runtime");
|
|
7
|
+
}
|
|
8
|
+
// Default to module workers since this package is ESM and relies on
|
|
9
|
+
// ESM-only dependencies.
|
|
10
|
+
const workerOptions = {
|
|
11
|
+
type: "module",
|
|
12
|
+
...(opts.workerOptions ?? {}),
|
|
13
|
+
};
|
|
14
|
+
if (opts.url != null) {
|
|
15
|
+
return new WorkerCtor(opts.url, workerOptions);
|
|
16
|
+
}
|
|
17
|
+
// Inline (blob) worker by default.
|
|
18
|
+
//
|
|
19
|
+
// This avoids requiring consumers to separately bundle/host a worker JS asset
|
|
20
|
+
// URL. It also means the worker entry cannot use `import.meta.url` to locate
|
|
21
|
+
// assets, since the entrypoint URL will be `blob:`.
|
|
22
|
+
if (workerOptions.type !== "module") {
|
|
23
|
+
throw new Error('createSpiceWorker() inline worker requires a module worker (workerOptions.type="module")');
|
|
24
|
+
}
|
|
25
|
+
const wasmUrl = opts.wasmUrl?.toString() ??
|
|
26
|
+
// Published package layout (and what `build:dist-publish` produces):
|
|
27
|
+
// dist/worker/browser/createSpiceWorker.js
|
|
28
|
+
// backend-wasm/dist/tspice_backend_wasm.wasm
|
|
29
|
+
new URL("../../../backend-wasm/dist/tspice_backend_wasm.wasm", import.meta.url).href;
|
|
30
|
+
const workerSource = `globalThis.__TSPICE_WORKER_CONFIG__ = ${JSON.stringify({ wasmUrl })};\n` +
|
|
31
|
+
SPICE_WORKER_INLINE_SOURCE;
|
|
32
|
+
const blob = new Blob([workerSource], { type: "text/javascript" });
|
|
33
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
34
|
+
try {
|
|
35
|
+
return new WorkerCtor(blobUrl, workerOptions);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
URL.revokeObjectURL(blobUrl);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=createSpiceWorker.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SpiceAsync } from "../../kit/types/spice-types.js";
|
|
2
|
+
import type { SpiceTransport } from "../../transport/types.js";
|
|
3
|
+
import { type WorkerLike, type WorkerTransport } from "../transport/createWorkerTransport.js";
|
|
4
|
+
export type SpiceWorkerClient<TTransport extends SpiceTransport = WorkerTransport> = {
|
|
5
|
+
worker: WorkerLike;
|
|
6
|
+
/** The underlying request/response RPC transport (always a WorkerTransport). */
|
|
7
|
+
baseTransport: WorkerTransport;
|
|
8
|
+
/** The transport after applying `wrapTransport` (e.g. `withCaching`). */
|
|
9
|
+
transport: TTransport;
|
|
10
|
+
/** A `SpiceAsync` client backed by `transport`. */
|
|
11
|
+
spice: SpiceAsync;
|
|
12
|
+
/** Dispose wrapper transport (if any) and the worker transport. */
|
|
13
|
+
dispose: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Async dispose variant that awaits wrapper transport cleanup (if any) before
|
|
16
|
+
* disposing the underlying worker transport.
|
|
17
|
+
*/
|
|
18
|
+
disposeAsync: () => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
export type CreateSpiceWorkerClientOptions<TTransport extends SpiceTransport = WorkerTransport> = {
|
|
21
|
+
/**
|
|
22
|
+
* Pass an existing Worker or a factory to create one.
|
|
23
|
+
*
|
|
24
|
+
* Defaults to `() => createSpiceWorker()`.
|
|
25
|
+
*/
|
|
26
|
+
worker?: WorkerLike | (() => WorkerLike);
|
|
27
|
+
/** Default request timeout forwarded to `createWorkerTransport`. */
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
/** Forwarded to `createWorkerTransport`. Defaults to `true` when `worker` is a factory. */
|
|
30
|
+
terminateOnDispose?: boolean;
|
|
31
|
+
/** Forwarded to `createWorkerTransport`. Defaults to `terminateOnDispose`. */
|
|
32
|
+
signalDispose?: boolean;
|
|
33
|
+
/** Optional transport wrapper (e.g. `withCaching`). */
|
|
34
|
+
wrapTransport?: (t: WorkerTransport) => TTransport;
|
|
35
|
+
/** Called if `disposeAsync()` rejects when invoked via fire-and-forget `dispose()`. */
|
|
36
|
+
onDisposeError?: (err: unknown) => void;
|
|
37
|
+
};
|
|
38
|
+
/** Create a {@link SpiceWorkerClient} that communicates with a tspice Web Worker via RPC. */
|
|
39
|
+
export declare function createSpiceWorkerClient<TTransport extends SpiceTransport = WorkerTransport>(opts?: CreateSpiceWorkerClientOptions<TTransport>): SpiceWorkerClient<TTransport>;
|
|
40
|
+
//# sourceMappingURL=createSpiceWorkerClient.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createSpiceAsyncFromTransport } from "../../clients/createSpiceAsyncFromTransport.js";
|
|
2
|
+
import { createWorkerTransport, } from "../transport/createWorkerTransport.js";
|
|
3
|
+
import { createSpiceWorker } from "./createSpiceWorker.js";
|
|
4
|
+
function getDisposeFn(value) {
|
|
5
|
+
if (value === null)
|
|
6
|
+
return undefined;
|
|
7
|
+
const t = typeof value;
|
|
8
|
+
if (t !== "object" && t !== "function")
|
|
9
|
+
return undefined;
|
|
10
|
+
const dispose = value.dispose;
|
|
11
|
+
return typeof dispose === "function" ? dispose : undefined;
|
|
12
|
+
}
|
|
13
|
+
function isPromiseLike(value) {
|
|
14
|
+
if (value === null)
|
|
15
|
+
return false;
|
|
16
|
+
const t = typeof value;
|
|
17
|
+
if (t !== "object" && t !== "function")
|
|
18
|
+
return false;
|
|
19
|
+
return typeof value.then === "function";
|
|
20
|
+
}
|
|
21
|
+
/** Create a {@link SpiceWorkerClient} that communicates with a tspice Web Worker via RPC. */
|
|
22
|
+
export function createSpiceWorkerClient(opts) {
|
|
23
|
+
const workerInput = opts?.worker ?? (() => createSpiceWorker());
|
|
24
|
+
const worker = typeof workerInput === "function" ? workerInput() : workerInput;
|
|
25
|
+
const terminateOnDispose = opts?.terminateOnDispose ??
|
|
26
|
+
(typeof workerInput === "function" ? true : false);
|
|
27
|
+
const baseTransport = createWorkerTransport({
|
|
28
|
+
worker,
|
|
29
|
+
...(opts?.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
30
|
+
terminateOnDispose,
|
|
31
|
+
...(opts?.signalDispose === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { signalDispose: opts.signalDispose }),
|
|
34
|
+
});
|
|
35
|
+
const transport = opts?.wrapTransport
|
|
36
|
+
? opts.wrapTransport(baseTransport)
|
|
37
|
+
: baseTransport;
|
|
38
|
+
const spice = createSpiceAsyncFromTransport(transport);
|
|
39
|
+
let disposePromise;
|
|
40
|
+
const disposeAsync = () => {
|
|
41
|
+
if (disposePromise)
|
|
42
|
+
return disposePromise;
|
|
43
|
+
disposePromise = (async () => {
|
|
44
|
+
try {
|
|
45
|
+
// If a wrapper transport supports cleanup, run it before disposing the
|
|
46
|
+
// underlying worker transport.
|
|
47
|
+
if (transport !== baseTransport) {
|
|
48
|
+
const wrapperDispose = getDisposeFn(transport);
|
|
49
|
+
if (wrapperDispose) {
|
|
50
|
+
const result = wrapperDispose.call(transport);
|
|
51
|
+
if (isPromiseLike(result))
|
|
52
|
+
await result;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
baseTransport.dispose();
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
return disposePromise;
|
|
61
|
+
};
|
|
62
|
+
const dispose = () => {
|
|
63
|
+
// Fire-and-forget. Ensure we don't surface unhandled rejections if wrapper
|
|
64
|
+
// cleanup fails.
|
|
65
|
+
void disposeAsync().catch((err) => {
|
|
66
|
+
try {
|
|
67
|
+
opts?.onDisposeError?.(err);
|
|
68
|
+
}
|
|
69
|
+
catch (callbackErr) {
|
|
70
|
+
if (typeof console !== "undefined" &&
|
|
71
|
+
typeof console.error === "function") {
|
|
72
|
+
// Log both: the original disposal failure and the error thrown by the
|
|
73
|
+
// error callback.
|
|
74
|
+
try {
|
|
75
|
+
console.error("createSpiceWorkerClient.dispose(): disposeAsync() failed", err);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// ignore
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
console.error("createSpiceWorkerClient.dispose(): onDisposeError threw", callbackErr);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ignore
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
worker,
|
|
92
|
+
baseTransport,
|
|
93
|
+
transport,
|
|
94
|
+
spice,
|
|
95
|
+
dispose,
|
|
96
|
+
disposeAsync,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=createSpiceWorkerClient.js.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createSpiceAsync } from "../../spice.js";
|
|
2
|
+
import { exposeTransportToWorker } from "../transport/exposeTransportToWorker.js";
|
|
3
|
+
const getWorkerConfig = () => {
|
|
4
|
+
const cfg = globalThis
|
|
5
|
+
.__TSPICE_WORKER_CONFIG__;
|
|
6
|
+
if (!cfg || typeof cfg !== "object")
|
|
7
|
+
return undefined;
|
|
8
|
+
return cfg;
|
|
9
|
+
};
|
|
10
|
+
const blockedStringKeys = new Set([
|
|
11
|
+
// Promise / thenable
|
|
12
|
+
"then",
|
|
13
|
+
// Prototype / constructor escapes
|
|
14
|
+
"__proto__",
|
|
15
|
+
"prototype",
|
|
16
|
+
"constructor",
|
|
17
|
+
// Common stringification / inspection hooks
|
|
18
|
+
"toJSON",
|
|
19
|
+
"inspect",
|
|
20
|
+
// Object.prototype keys (avoid accidental RPC calls during introspection)
|
|
21
|
+
"toString",
|
|
22
|
+
"valueOf",
|
|
23
|
+
"toLocaleString",
|
|
24
|
+
"hasOwnProperty",
|
|
25
|
+
"isPrototypeOf",
|
|
26
|
+
"propertyIsEnumerable",
|
|
27
|
+
"__defineGetter__",
|
|
28
|
+
"__defineSetter__",
|
|
29
|
+
"__lookupGetter__",
|
|
30
|
+
"__lookupSetter__",
|
|
31
|
+
]);
|
|
32
|
+
const isSafeRpcKey = (key) => /^[A-Za-z_$][\w$]*$/.test(key);
|
|
33
|
+
const allowedKitMethodList = [
|
|
34
|
+
"loadKernel",
|
|
35
|
+
"unloadKernel",
|
|
36
|
+
"kclear",
|
|
37
|
+
"toolkitVersion",
|
|
38
|
+
"utcToEt",
|
|
39
|
+
"etToUtc",
|
|
40
|
+
"frameTransform",
|
|
41
|
+
"getState",
|
|
42
|
+
];
|
|
43
|
+
const defaultAllowlist = {
|
|
44
|
+
// NOTE: `kit` is deliberately allowlisted because it's a small, curated API.
|
|
45
|
+
// `raw` is intentionally not allowlisted here (see module comment below).
|
|
46
|
+
kit: new Set(allowedKitMethodList),
|
|
47
|
+
};
|
|
48
|
+
function createSpiceTransportFromSpiceAsync(spice, opts) {
|
|
49
|
+
const allowlist = opts?.allowlist ?? defaultAllowlist;
|
|
50
|
+
return {
|
|
51
|
+
request: async (op, args) => {
|
|
52
|
+
const dot = op.indexOf(".");
|
|
53
|
+
if (dot <= 0 || dot === op.length - 1) {
|
|
54
|
+
throw new Error(`Invalid op: ${op}`);
|
|
55
|
+
}
|
|
56
|
+
const namespace = op.slice(0, dot);
|
|
57
|
+
const method = op.slice(dot + 1);
|
|
58
|
+
if (namespace !== "raw" && namespace !== "kit") {
|
|
59
|
+
throw new Error(`Unknown namespace: ${namespace}`);
|
|
60
|
+
}
|
|
61
|
+
if (!isSafeRpcKey(method) || blockedStringKeys.has(method)) {
|
|
62
|
+
throw new Error(`Invalid method name: ${method}`);
|
|
63
|
+
}
|
|
64
|
+
const ns = namespace;
|
|
65
|
+
const nsAllowlist = allowlist[ns];
|
|
66
|
+
if (nsAllowlist && !nsAllowlist.has(method)) {
|
|
67
|
+
throw new Error(`Disallowed op: ${op}`);
|
|
68
|
+
}
|
|
69
|
+
const target = spice[ns];
|
|
70
|
+
const fn = target[method];
|
|
71
|
+
if (typeof fn !== "function") {
|
|
72
|
+
throw new Error(`Unknown op: ${op}`);
|
|
73
|
+
}
|
|
74
|
+
// `spice.raw` and `spice.kit` are proxies that return bound/wrapped
|
|
75
|
+
// functions, but use Reflect.apply to be defensive about `this`.
|
|
76
|
+
return await Reflect.apply(fn, target, args);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// NOTE: This file is meant to be loaded as a Web Worker module.
|
|
81
|
+
// It intentionally has no exports and runs as a side-effect.
|
|
82
|
+
//
|
|
83
|
+
// Security/design note:
|
|
84
|
+
// - This worker entry is intended for internal workspace use.
|
|
85
|
+
// - By default, it exposes:
|
|
86
|
+
// - `kit.*` as a small, curated allowlist (see `allowedKitMethodList`), and
|
|
87
|
+
// - `raw.*` without an allowlist (subject to the blocked key checks above).
|
|
88
|
+
// - If you need a tighter RPC capability set (especially for `raw.*`), create
|
|
89
|
+
// a custom worker entry and provide an explicit allowlist.
|
|
90
|
+
//
|
|
91
|
+
// IMPORTANT: `exposeTransportToWorker()` must run synchronously so the worker's
|
|
92
|
+
// `message` handler is installed immediately. Otherwise, early RPC messages from
|
|
93
|
+
// the main thread can be dropped while the WASM backend is still initializing.
|
|
94
|
+
let spicePromise;
|
|
95
|
+
let transportPromise;
|
|
96
|
+
const getSpicePromise = () => {
|
|
97
|
+
if (spicePromise)
|
|
98
|
+
return spicePromise;
|
|
99
|
+
const config = getWorkerConfig();
|
|
100
|
+
const wasmUrl = config?.wasmUrl;
|
|
101
|
+
spicePromise = createSpiceAsync({
|
|
102
|
+
backend: "wasm",
|
|
103
|
+
...(wasmUrl === undefined ? {} : { wasmUrl }),
|
|
104
|
+
});
|
|
105
|
+
return spicePromise;
|
|
106
|
+
};
|
|
107
|
+
const getTransportPromise = () => (transportPromise ??= getSpicePromise().then((spice) => createSpiceTransportFromSpiceAsync(spice)));
|
|
108
|
+
exposeTransportToWorker({
|
|
109
|
+
transport: {
|
|
110
|
+
request: async (op, args) => (await getTransportPromise()).request(op, args),
|
|
111
|
+
},
|
|
112
|
+
onDispose: async () => {
|
|
113
|
+
// Best-effort cleanup. Worker termination also releases resources, but this
|
|
114
|
+
// helps callers who keep the worker alive.
|
|
115
|
+
if (!spicePromise) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const spice = await getSpicePromise();
|
|
120
|
+
await spice.raw.kclear();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// ignore
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// Kick off init after the `message` handler has been installed.
|
|
128
|
+
void getTransportPromise();
|
|
129
|
+
//# sourceMappingURL=spiceWorkerEntry.js.map
|