@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.
Files changed (221) hide show
  1. package/README.md +145 -84
  2. package/backend-contract/dist/.tsbuildinfo +1 -1
  3. package/backend-contract/dist/domains/cells-windows.d.ts +94 -0
  4. package/backend-contract/dist/domains/cells-windows.js +10 -0
  5. package/backend-contract/dist/domains/coords-vectors.d.ts +53 -3
  6. package/backend-contract/dist/domains/dsk.d.ts +49 -0
  7. package/backend-contract/dist/domains/dsk.js +2 -0
  8. package/backend-contract/dist/domains/ek.d.ts +186 -0
  9. package/backend-contract/dist/domains/ek.js +8 -0
  10. package/backend-contract/dist/domains/ephemeris.d.ts +141 -3
  11. package/backend-contract/dist/domains/error.d.ts +42 -0
  12. package/backend-contract/dist/domains/error.js +33 -0
  13. package/backend-contract/dist/domains/file-io.d.ts +114 -0
  14. package/backend-contract/dist/domains/file-io.js +8 -0
  15. package/backend-contract/dist/domains/frames.d.ts +44 -4
  16. package/backend-contract/dist/domains/geometry-gf.d.ts +44 -0
  17. package/backend-contract/dist/domains/geometry-gf.js +14 -0
  18. package/backend-contract/dist/domains/geometry.d.ts +21 -1
  19. package/backend-contract/dist/domains/ids-names-normalize.d.ts +3 -0
  20. package/backend-contract/dist/domains/ids-names-normalize.js +74 -0
  21. package/backend-contract/dist/domains/ids-names.d.ts +37 -0
  22. package/backend-contract/dist/domains/kernel-pool.d.ts +134 -0
  23. package/backend-contract/dist/domains/kernel-pool.js +2 -0
  24. package/backend-contract/dist/domains/kernels-utils.d.ts +44 -0
  25. package/backend-contract/dist/domains/kernels-utils.js +265 -0
  26. package/backend-contract/dist/domains/kernels.d.ts +39 -3
  27. package/backend-contract/dist/domains/time.d.ts +102 -0
  28. package/backend-contract/dist/index.d.ts +34 -15
  29. package/backend-contract/dist/index.js +15 -1
  30. package/backend-contract/dist/shared/errors.d.ts +6 -0
  31. package/backend-contract/dist/shared/errors.js +8 -0
  32. package/backend-contract/dist/shared/mat3.d.ts +52 -0
  33. package/backend-contract/dist/shared/mat3.js +150 -0
  34. package/backend-contract/dist/shared/mat6.d.ts +34 -0
  35. package/backend-contract/dist/shared/mat6.js +116 -0
  36. package/backend-contract/dist/shared/spice-handles.d.ts +20 -0
  37. package/backend-contract/dist/shared/spice-handles.js +82 -0
  38. package/backend-contract/dist/shared/spice-int.d.ts +32 -0
  39. package/backend-contract/dist/shared/spice-int.js +41 -0
  40. package/backend-contract/dist/shared/types.d.ts +136 -5
  41. package/backend-contract/dist/shared/types.js +1 -1
  42. package/backend-contract/dist/shared/vec.d.ts +54 -0
  43. package/backend-contract/dist/shared/vec.js +162 -0
  44. package/backend-fake/dist/.tsbuildinfo +1 -1
  45. package/backend-fake/dist/index.d.ts +21 -1
  46. package/backend-fake/dist/index.js +1112 -33
  47. package/backend-node/dist/.tsbuildinfo +1 -1
  48. package/backend-node/dist/codec/arrays.d.ts +9 -0
  49. package/backend-node/dist/codec/arrays.js +36 -0
  50. package/backend-node/dist/codec/errors.d.ts +6 -6
  51. package/backend-node/dist/codec/errors.js +6 -6
  52. package/backend-node/dist/domains/cells-windows.d.ts +5 -0
  53. package/backend-node/dist/domains/cells-windows.js +112 -0
  54. package/backend-node/dist/domains/coords-vectors.d.ts +1 -0
  55. package/backend-node/dist/domains/coords-vectors.js +66 -0
  56. package/backend-node/dist/domains/dsk.d.ts +6 -0
  57. package/backend-node/dist/domains/dsk.js +108 -0
  58. package/backend-node/dist/domains/ek.d.ts +10 -0
  59. package/backend-node/dist/domains/ek.js +100 -0
  60. package/backend-node/dist/domains/ephemeris.d.ts +5 -1
  61. package/backend-node/dist/domains/ephemeris.js +150 -1
  62. package/backend-node/dist/domains/error.d.ts +5 -0
  63. package/backend-node/dist/domains/error.js +34 -0
  64. package/backend-node/dist/domains/file-io.d.ts +7 -0
  65. package/backend-node/dist/domains/file-io.js +105 -0
  66. package/backend-node/dist/domains/frames.d.ts +1 -0
  67. package/backend-node/dist/domains/frames.js +58 -6
  68. package/backend-node/dist/domains/geometry-gf.d.ts +5 -0
  69. package/backend-node/dist/domains/geometry-gf.js +74 -0
  70. package/backend-node/dist/domains/geometry.d.ts +1 -0
  71. package/backend-node/dist/domains/geometry.js +62 -0
  72. package/backend-node/dist/domains/ids-names.d.ts +2 -1
  73. package/backend-node/dist/domains/ids-names.js +30 -0
  74. package/backend-node/dist/domains/kernel-pool.d.ts +5 -0
  75. package/backend-node/dist/domains/kernel-pool.js +74 -0
  76. package/backend-node/dist/domains/kernels.d.ts +1 -0
  77. package/backend-node/dist/domains/kernels.js +100 -13
  78. package/backend-node/dist/domains/time.d.ts +1 -0
  79. package/backend-node/dist/domains/time.js +75 -1
  80. package/backend-node/dist/index.d.ts +5 -1
  81. package/backend-node/dist/index.js +62 -1
  82. package/backend-node/dist/lowlevel/binding.d.ts +3 -0
  83. package/backend-node/dist/lowlevel/binding.js +115 -0
  84. package/backend-node/dist/runtime/addon.d.ts +273 -2
  85. package/backend-node/dist/runtime/addon.js +3 -0
  86. package/backend-node/dist/runtime/kernel-staging.d.ts +17 -0
  87. package/backend-node/dist/runtime/kernel-staging.js +80 -7
  88. package/backend-node/dist/runtime/spice-handles.d.ts +3 -0
  89. package/backend-node/dist/runtime/spice-handles.js +2 -0
  90. package/backend-node/dist/runtime/virtual-output-staging.d.ts +16 -0
  91. package/backend-node/dist/runtime/virtual-output-staging.js +148 -0
  92. package/backend-wasm/dist/.tsbuildinfo +1 -1
  93. package/backend-wasm/dist/codec/alloc.d.ts +19 -0
  94. package/backend-wasm/dist/codec/alloc.js +64 -0
  95. package/backend-wasm/dist/codec/calls.d.ts +2 -0
  96. package/backend-wasm/dist/codec/calls.js +13 -24
  97. package/backend-wasm/dist/codec/errors.d.ts +6 -0
  98. package/backend-wasm/dist/codec/errors.js +34 -2
  99. package/backend-wasm/dist/codec/found.d.ts +2 -0
  100. package/backend-wasm/dist/codec/found.js +20 -43
  101. package/backend-wasm/dist/codec/strings.d.ts +31 -1
  102. package/backend-wasm/dist/codec/strings.js +93 -6
  103. package/backend-wasm/dist/domains/cells-windows.d.ts +9 -0
  104. package/backend-wasm/dist/domains/cells-windows.js +392 -0
  105. package/backend-wasm/dist/domains/coords-vectors.d.ts +1 -0
  106. package/backend-wasm/dist/domains/coords-vectors.js +377 -184
  107. package/backend-wasm/dist/domains/dsk.d.ts +6 -0
  108. package/backend-wasm/dist/domains/dsk.js +179 -0
  109. package/backend-wasm/dist/domains/ek.d.ts +6 -0
  110. package/backend-wasm/dist/domains/ek.js +543 -0
  111. package/backend-wasm/dist/domains/ephemeris.d.ts +4 -1
  112. package/backend-wasm/dist/domains/ephemeris.js +405 -46
  113. package/backend-wasm/dist/domains/error.d.ts +5 -0
  114. package/backend-wasm/dist/domains/error.js +109 -0
  115. package/backend-wasm/dist/domains/file-io.d.ts +7 -0
  116. package/backend-wasm/dist/domains/file-io.js +462 -0
  117. package/backend-wasm/dist/domains/frames.d.ts +1 -0
  118. package/backend-wasm/dist/domains/frames.js +139 -6
  119. package/backend-wasm/dist/domains/geometry-gf.d.ts +5 -0
  120. package/backend-wasm/dist/domains/geometry-gf.js +178 -0
  121. package/backend-wasm/dist/domains/geometry.d.ts +1 -0
  122. package/backend-wasm/dist/domains/geometry.js +210 -0
  123. package/backend-wasm/dist/domains/ids-names.d.ts +2 -1
  124. package/backend-wasm/dist/domains/ids-names.js +89 -0
  125. package/backend-wasm/dist/domains/kernel-pool.d.ts +5 -0
  126. package/backend-wasm/dist/domains/kernel-pool.js +357 -0
  127. package/backend-wasm/dist/domains/kernels.d.ts +1 -0
  128. package/backend-wasm/dist/domains/kernels.js +108 -4
  129. package/backend-wasm/dist/domains/time.d.ts +2 -0
  130. package/backend-wasm/dist/domains/time.js +235 -133
  131. package/backend-wasm/dist/index.d.ts +4 -2
  132. package/backend-wasm/dist/lowlevel/exports.d.ts +215 -1
  133. package/backend-wasm/dist/lowlevel/exports.js +217 -38
  134. package/backend-wasm/dist/runtime/create-backend-options.d.ts +21 -0
  135. package/backend-wasm/dist/runtime/create-backend.node.d.ts +11 -2
  136. package/backend-wasm/dist/runtime/create-backend.node.js +283 -14
  137. package/backend-wasm/dist/runtime/create-backend.web.d.ts +5 -2
  138. package/backend-wasm/dist/runtime/create-backend.web.js +40 -6
  139. package/backend-wasm/dist/runtime/fs.d.ts +6 -0
  140. package/backend-wasm/dist/runtime/fs.js +29 -3
  141. package/backend-wasm/dist/runtime/spice-handles.d.ts +3 -0
  142. package/backend-wasm/dist/runtime/spice-handles.js +2 -0
  143. package/backend-wasm/dist/runtime/virtual-outputs.d.ts +16 -0
  144. package/backend-wasm/dist/runtime/virtual-outputs.js +35 -0
  145. package/backend-wasm/dist/tspice_backend_wasm.node.js +3 -3
  146. package/backend-wasm/dist/tspice_backend_wasm.wasm +0 -0
  147. package/backend-wasm/dist/tspice_backend_wasm.web.js +1 -1
  148. package/core/dist/.tsbuildinfo +1 -1
  149. package/core/dist/index.d.ts +21 -0
  150. package/core/dist/index.js +57 -0
  151. package/dist/.tsbuildinfo +1 -1
  152. package/dist/backend.d.ts +15 -6
  153. package/dist/backend.js +3 -6
  154. package/dist/clients/createSpiceAsyncFromTransport.d.ts +5 -0
  155. package/dist/clients/createSpiceAsyncFromTransport.js +90 -0
  156. package/dist/clients/createSpiceSyncFromTransport.d.ts +5 -0
  157. package/dist/clients/createSpiceSyncFromTransport.js +88 -0
  158. package/dist/clients/spiceClients.d.ts +59 -0
  159. package/dist/clients/spiceClients.js +292 -0
  160. package/dist/errors.d.ts +4 -0
  161. package/dist/errors.js +4 -0
  162. package/dist/index.d.ts +12 -7
  163. package/dist/index.js +5 -2
  164. package/dist/kernels/defaultKernelPathFromUrl.d.ts +8 -0
  165. package/dist/kernels/defaultKernelPathFromUrl.js +32 -0
  166. package/dist/kernels/kernelPack.d.ts +88 -0
  167. package/dist/kernels/kernelPack.js +122 -0
  168. package/dist/kernels/kernels.d.ts +98 -0
  169. package/dist/kernels/kernels.js +217 -0
  170. package/dist/kernels/naifKernelId.d.ts +2 -0
  171. package/dist/kernels/naifKernelId.js +2 -0
  172. package/dist/kit/index.d.ts +4 -0
  173. package/dist/kit/index.js +3 -0
  174. package/dist/kit/math/mat3.d.ts +31 -0
  175. package/dist/kit/math/mat3.js +82 -0
  176. package/dist/kit/spice/create-kit.d.ts +12 -0
  177. package/dist/kit/spice/create-kit.js +23 -0
  178. package/dist/kit/spice/frames.d.ts +8 -0
  179. package/dist/kit/spice/frames.js +16 -0
  180. package/dist/kit/spice/kernels.d.ts +14 -0
  181. package/dist/kit/spice/kernels.js +39 -0
  182. package/dist/kit/spice/state.d.ts +7 -0
  183. package/dist/kit/spice/state.js +36 -0
  184. package/dist/kit/spice/time.d.ts +9 -0
  185. package/dist/kit/spice/time.js +31 -0
  186. package/dist/kit/types/spice-types.d.ts +51 -0
  187. package/dist/spice.d.ts +10 -1
  188. package/dist/spice.js +84 -72
  189. package/dist/transport/caching/policy.d.ts +16 -0
  190. package/dist/transport/caching/policy.js +77 -0
  191. package/dist/transport/caching/withCaching.d.ts +125 -0
  192. package/dist/transport/caching/withCaching.js +335 -0
  193. package/dist/transport/caching/withCachingSync.d.ts +24 -0
  194. package/dist/transport/caching/withCachingSync.js +161 -0
  195. package/dist/transport/rpc/protocol.d.ts +35 -0
  196. package/dist/transport/rpc/protocol.js +56 -0
  197. package/dist/transport/rpc/taskScheduling.d.ts +20 -0
  198. package/dist/transport/rpc/taskScheduling.js +98 -0
  199. package/dist/transport/rpc/valueCodec.d.ts +5 -0
  200. package/dist/transport/rpc/valueCodec.js +106 -0
  201. package/dist/transport/types.d.ts +7 -0
  202. package/dist/transport/types.js +2 -0
  203. package/dist/types.d.ts +8 -17
  204. package/dist/types.js +2 -1
  205. package/dist/worker/browser/createSpiceWorker.d.ts +22 -0
  206. package/dist/worker/browser/createSpiceWorker.js +41 -0
  207. package/dist/worker/browser/createSpiceWorkerClient.d.ts +40 -0
  208. package/dist/worker/browser/createSpiceWorkerClient.js +99 -0
  209. package/dist/worker/browser/spiceWorkerEntry.d.ts +2 -0
  210. package/dist/worker/browser/spiceWorkerEntry.js +129 -0
  211. package/dist/worker/browser/spiceWorkerInlineSource.d.ts +2 -0
  212. package/dist/worker/browser/spiceWorkerInlineSource.js +4 -0
  213. package/dist/worker/index.d.ts +10 -0
  214. package/dist/worker/index.js +7 -0
  215. package/dist/worker/transport/createWorkerTransport.d.ts +69 -0
  216. package/dist/worker/transport/createWorkerTransport.js +398 -0
  217. package/dist/worker/transport/exposeTransportToWorker.d.ts +51 -0
  218. package/dist/worker/transport/exposeTransportToWorker.js +196 -0
  219. package/package.json +4 -4
  220. package/dist/spice-types.d.ts +0 -36
  221. /package/dist/{spice-types.js → kit/types/spice-types.js} +0 -0
@@ -0,0 +1,335 @@
1
+ import { createCachePolicy } from "./policy.js";
2
+ export const MAX_KEY_SCAN = 10_000;
3
+ export const MAX_KEY_LENGTH = 8192;
4
+ export const MAX_KEY_STRING_LENGTH = 2048;
5
+ const cachingTransportBrand = new WeakSet();
6
+ const defaultOnWarning = (message) => {
7
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
8
+ console.warn(message);
9
+ }
10
+ };
11
+ /**
12
+ * Type guard for narrowing a transport returned by `withCaching()`.
13
+ */
14
+ export function isCachingTransport(t) {
15
+ if (typeof t !== "object" || t === null)
16
+ return false;
17
+ if (!cachingTransportBrand.has(t))
18
+ return false;
19
+ const v = t;
20
+ return (typeof v.request === "function" &&
21
+ typeof v.clear === "function" &&
22
+ typeof v.dispose === "function");
23
+ }
24
+ function tryUnrefTimer(timer) {
25
+ let unref;
26
+ try {
27
+ unref = timer.unref;
28
+ }
29
+ catch {
30
+ return;
31
+ }
32
+ if (typeof unref !== "function")
33
+ return;
34
+ try {
35
+ unref.call(timer);
36
+ }
37
+ catch {
38
+ // Ignore. Some runtimes / shims may throw for `unref` even when present.
39
+ }
40
+ }
41
+ function isPlainObject(x) {
42
+ if (x === null || typeof x !== "object")
43
+ return false;
44
+ let proto;
45
+ try {
46
+ proto = Object.getPrototypeOf(x);
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ return proto === Object.prototype || proto === null;
52
+ }
53
+ function containsBinaryLikeData(value, seen, budget) {
54
+ if (value == null)
55
+ return false;
56
+ const g = globalThis;
57
+ // Node: Buffer
58
+ if (g.Buffer?.isBuffer?.(value))
59
+ return true;
60
+ // ArrayBuffer / TypedArrays / DataView
61
+ if (typeof ArrayBuffer !== "undefined") {
62
+ if (value instanceof ArrayBuffer)
63
+ return true;
64
+ if (ArrayBuffer.isView(value))
65
+ return true;
66
+ }
67
+ // SharedArrayBuffer (when available)
68
+ if (g.SharedArrayBuffer && value instanceof g.SharedArrayBuffer)
69
+ return true;
70
+ // Blob / File (when available)
71
+ if (typeof Blob !== "undefined" && value instanceof Blob)
72
+ return true;
73
+ if (typeof File !== "undefined" && value instanceof File)
74
+ return true;
75
+ if (typeof value === "string") {
76
+ // Treat very large strings like binary payloads: they produce huge keys and
77
+ // are often effectively opaque.
78
+ return value.length > MAX_KEY_STRING_LENGTH;
79
+ }
80
+ const t = typeof value;
81
+ if (t === "function")
82
+ return true;
83
+ if (t !== "object")
84
+ return false;
85
+ const obj = value;
86
+ if (seen.has(obj))
87
+ return false;
88
+ seen.add(obj);
89
+ // Only traverse JSON-like containers: arrays and plain objects with enumerable
90
+ // data properties. Everything else fails closed (disable caching).
91
+ if (Array.isArray(value)) {
92
+ let len;
93
+ try {
94
+ len = value.length;
95
+ }
96
+ catch {
97
+ return true;
98
+ }
99
+ // Scan budget is a *total* budget across args; treat large arrays as
100
+ // non-cacheable (fail closed) rather than doing excessive descriptor work.
101
+ if (len > budget.remaining)
102
+ return true;
103
+ for (let i = 0; i < len; i++) {
104
+ if (budget.remaining-- <= 0)
105
+ return true;
106
+ // Detect holes (sparse arrays). JSON.stringify normalizes these to `null`,
107
+ // which can lead to key collisions. Fail closed.
108
+ if (!Object.prototype.hasOwnProperty.call(value, i))
109
+ return true;
110
+ let desc;
111
+ try {
112
+ desc = Object.getOwnPropertyDescriptor(value, i);
113
+ }
114
+ catch {
115
+ return true;
116
+ }
117
+ // Should be impossible after hasOwnProperty, but fail closed just in case
118
+ // (e.g. Proxies).
119
+ if (!desc)
120
+ return true;
121
+ if (desc.get || desc.set)
122
+ return true;
123
+ if (containsBinaryLikeData(desc.value, seen, budget))
124
+ return true;
125
+ }
126
+ return false;
127
+ }
128
+ if (!isPlainObject(value))
129
+ return true;
130
+ // Avoid Object.getOwnPropertyDescriptors(value) (bulk descriptor allocation).
131
+ // Scan incrementally and bail early if we hit the global scan budget.
132
+ for (const key of Object.keys(value)) {
133
+ if (budget.remaining-- <= 0)
134
+ return true;
135
+ let desc;
136
+ try {
137
+ desc = Object.getOwnPropertyDescriptor(value, key);
138
+ }
139
+ catch {
140
+ return true;
141
+ }
142
+ if (!desc)
143
+ return true;
144
+ // `for...in` only enumerates enumerable properties, but keep this as a
145
+ // defensive guardrail.
146
+ if (!desc.enumerable)
147
+ continue;
148
+ if (desc.get || desc.set)
149
+ return true;
150
+ if (containsBinaryLikeData(desc.value, seen, budget))
151
+ return true;
152
+ }
153
+ return false;
154
+ }
155
+ /**
156
+ * Default cache key function used by `withCaching()`.
157
+ *
158
+ * Note: for plain objects, cache key stability (and hit rate) depends on object
159
+ * key insertion order, since this uses `JSON.stringify` and does not sort
160
+ * keys.
161
+ */
162
+ export function defaultSpiceCacheKey(op, args) {
163
+ try {
164
+ // Normalize certain ops for better cache hit rates.
165
+ //
166
+ // - `kit.getState({ target, observer, ... })` accepts `string | number` body
167
+ // refs, but downstream SPICE expects strings. Normalize to strings for
168
+ // cache keys so `1` and "1" don't diverge.
169
+ const normalizedArgs = op === "kit.getState" &&
170
+ args.length === 1 &&
171
+ isPlainObject(args[0]) &&
172
+ (typeof args[0].target === "string" ||
173
+ typeof args[0].target === "number") &&
174
+ (typeof args[0].observer === "string" ||
175
+ typeof args[0].observer === "number")
176
+ ? [
177
+ {
178
+ ...args[0],
179
+ target: String(args[0].target),
180
+ observer: String(args[0].observer),
181
+ },
182
+ ]
183
+ : args;
184
+ const seen = new WeakSet();
185
+ const budget = { remaining: MAX_KEY_SCAN };
186
+ for (const arg of normalizedArgs) {
187
+ if (containsBinaryLikeData(arg, seen, budget))
188
+ return null;
189
+ }
190
+ const key = JSON.stringify([op, normalizedArgs]);
191
+ if (key.length > MAX_KEY_LENGTH)
192
+ return null;
193
+ return key;
194
+ }
195
+ catch {
196
+ // Safer failure mode: if we can't build a stable key, don't cache.
197
+ return null;
198
+ }
199
+ }
200
+ /**
201
+ * Add a cache layer to a transport.
202
+ *
203
+ * Note: cached values are returned by reference. If a caller mutates the
204
+ * returned object/array, subsequent cache hits will observe that mutation.
205
+ * Treat results as immutable (or clone them yourself) when caching is enabled.
206
+ */
207
+ export function withCaching(base, opts) {
208
+ const rawMaxEntries = opts?.maxEntries;
209
+ const maxEntries = rawMaxEntries ?? 1000;
210
+ const maxEntriesLimit = maxEntries === Infinity
211
+ ? undefined
212
+ : Number.isFinite(maxEntries)
213
+ ? maxEntries
214
+ : 0;
215
+ const rawTtlMs = opts?.ttlMs;
216
+ const ttlMs = rawTtlMs == null ? undefined : rawTtlMs;
217
+ const cachingEnabled = (ttlMs === undefined || ttlMs > 0) &&
218
+ (maxEntriesLimit === undefined || maxEntriesLimit > 0);
219
+ // True no-op mode: preserve the input object identity and avoid allocating
220
+ // any wrapper state when caching is disabled.
221
+ if (!cachingEnabled)
222
+ return base;
223
+ // Only pick defaults after the no-op early return so disabled caching pays
224
+ // ~0 overhead.
225
+ const now = opts?.now ?? Date.now;
226
+ const onWarning = opts?.onWarning ?? defaultOnWarning;
227
+ const keyFn = opts?.key ?? defaultSpiceCacheKey;
228
+ const policyByOp = opts?.policy;
229
+ const allowUnsafePolicyOverrides = opts?.allowUnsafePolicyOverrides === true;
230
+ const allowBroadNoStorePrefixes = opts?.allowBroadNoStorePrefixes === true;
231
+ const getPolicy = createCachePolicy({
232
+ wrapperName: "withCaching()",
233
+ onWarning,
234
+ policyByOp,
235
+ noStorePrefixes: opts?.noStorePrefixes,
236
+ allowBroadNoStorePrefixes,
237
+ allowUnsafePolicyOverrides,
238
+ });
239
+ const cache = new Map();
240
+ let sweepTimer;
241
+ const isExpired = (entry, now) => entry.expiresAt !== undefined && entry.expiresAt <= now;
242
+ const cleanupExpired = (now) => {
243
+ // TTL eviction is optional. When ttlMs is undefined, the cache is “forever”
244
+ // (still LRU-bounded by maxEntries).
245
+ if (ttlMs === undefined || ttlMs <= 0)
246
+ return;
247
+ for (const [k, entry] of cache) {
248
+ if (isExpired(entry, now))
249
+ cache.delete(k);
250
+ }
251
+ };
252
+ if (cachingEnabled && ttlMs !== undefined && ttlMs > 0) {
253
+ const sweepIntervalMs = opts?.sweepIntervalMs;
254
+ if (sweepIntervalMs !== undefined && sweepIntervalMs > 0) {
255
+ sweepTimer = setInterval(() => cleanupExpired(now()), sweepIntervalMs);
256
+ // In Node, interval timers keep the event loop alive by default. `unref()`
257
+ // prevents this from pinning test runners / CLIs. Browsers return a
258
+ // numeric id (no-op).
259
+ tryUnrefTimer(sweepTimer);
260
+ }
261
+ }
262
+ const clear = () => {
263
+ cache.clear();
264
+ };
265
+ const dispose = () => {
266
+ if (sweepTimer !== undefined) {
267
+ clearInterval(sweepTimer);
268
+ sweepTimer = undefined;
269
+ }
270
+ cache.clear();
271
+ };
272
+ const touch = (k, entry) => {
273
+ cache.delete(k);
274
+ cache.set(k, entry);
275
+ };
276
+ const enforceMaxEntries = () => {
277
+ if (maxEntriesLimit === undefined)
278
+ return;
279
+ while (cache.size > maxEntriesLimit) {
280
+ const oldestKey = cache.keys().next().value;
281
+ if (oldestKey === undefined)
282
+ return;
283
+ cache.delete(oldestKey);
284
+ }
285
+ };
286
+ const request = (op, args) => {
287
+ // Per-method cache policy. When bypassing, skip *all* cache work (no key
288
+ // computation, no reads/writes, no TTL sweeps).
289
+ if (getPolicy(op) === "no-store")
290
+ return base.request(op, args);
291
+ const k = keyFn(op, args);
292
+ if (k == null)
293
+ return base.request(op, args);
294
+ const nowMs = now();
295
+ cleanupExpired(nowMs);
296
+ const existing = cache.get(k);
297
+ if (existing && !isExpired(existing, nowMs)) {
298
+ // LRU touch on access. TTL remains absolute (does not refresh on access).
299
+ touch(k, existing);
300
+ return existing.promise;
301
+ }
302
+ if (existing)
303
+ cache.delete(k);
304
+ // Cache the in-flight promise to dedupe concurrent callers.
305
+ let promise;
306
+ promise = base.request(op, args).then((value) => {
307
+ if (ttlMs !== undefined && ttlMs > 0) {
308
+ const current = cache.get(k);
309
+ if (current?.promise === promise) {
310
+ current.expiresAt = now() + ttlMs;
311
+ }
312
+ }
313
+ return value;
314
+ }, (err) => {
315
+ // Never cache rejections.
316
+ const current = cache.get(k);
317
+ if (current?.promise === promise)
318
+ cache.delete(k);
319
+ throw err;
320
+ });
321
+ cache.set(k, { promise });
322
+ enforceMaxEntries();
323
+ return promise;
324
+ };
325
+ const transport = { request, clear, dispose };
326
+ // Preserve any extra properties on the base transport (e.g. testing hooks)
327
+ // while still allowing us to override the core methods.
328
+ //
329
+ // This also keeps `instanceof`/prototype checks (if any) behaving as users
330
+ // might expect.
331
+ const transportWithProto = Object.assign(Object.create(base), transport);
332
+ cachingTransportBrand.add(transportWithProto);
333
+ return transportWithProto;
334
+ }
335
+ //# sourceMappingURL=withCaching.js.map
@@ -0,0 +1,24 @@
1
+ import type { SpiceTransportSync } from "../types.js";
2
+ import { type WithCachingOptions } from "./withCaching.js";
3
+ export type CachingTransportSync = SpiceTransportSync & {
4
+ /**
5
+ * Clear all cached entries.
6
+ *
7
+ * Notes:
8
+ * - This does not cancel in-flight requests; it just drops references so
9
+ * results won't be reused.
10
+ * - This does not stop the optional TTL sweep timer. Use `dispose()` to stop
11
+ * timers and clear cache.
12
+ */
13
+ clear(): void;
14
+ /** Stop any sweep timers and clear all cached entries. */
15
+ dispose(): void;
16
+ };
17
+ export type WithCachingSyncResult = SpiceTransportSync | CachingTransportSync;
18
+ /**
19
+ * Type guard for narrowing a transport returned by `withCachingSync()`.
20
+ */
21
+ export declare function isCachingTransportSync(t: unknown): t is CachingTransportSync;
22
+ /** Wrap a sync transport with an in-memory response cache (LRU-ish + optional TTL). */
23
+ export declare function withCachingSync(base: SpiceTransportSync, opts?: WithCachingOptions): WithCachingSyncResult;
24
+ //# sourceMappingURL=withCachingSync.d.ts.map
@@ -0,0 +1,161 @@
1
+ import { createCachePolicy } from "./policy.js";
2
+ import { defaultSpiceCacheKey, } from "./withCaching.js";
3
+ const cachingTransportBrand = new WeakSet();
4
+ const defaultOnWarning = (message) => {
5
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
6
+ console.warn(message);
7
+ }
8
+ };
9
+ /**
10
+ * Type guard for narrowing a transport returned by `withCachingSync()`.
11
+ */
12
+ export function isCachingTransportSync(t) {
13
+ if (typeof t !== "object" || t === null)
14
+ return false;
15
+ if (!cachingTransportBrand.has(t))
16
+ return false;
17
+ const v = t;
18
+ return (typeof v.request === "function" &&
19
+ typeof v.clear === "function" &&
20
+ typeof v.dispose === "function");
21
+ }
22
+ function tryUnrefTimer(timer) {
23
+ let unref;
24
+ try {
25
+ unref = timer.unref;
26
+ }
27
+ catch {
28
+ return;
29
+ }
30
+ if (typeof unref !== "function")
31
+ return;
32
+ try {
33
+ unref.call(timer);
34
+ }
35
+ catch {
36
+ // Ignore. Some runtimes / shims may throw for `unref` even when present.
37
+ }
38
+ }
39
+ /** Wrap a sync transport with an in-memory response cache (LRU-ish + optional TTL). */
40
+ export function withCachingSync(base, opts) {
41
+ // Note: cached values are returned by reference. If a caller mutates the
42
+ // returned object/array, subsequent cache hits will observe that mutation.
43
+ // Treat results as immutable (or clone them yourself) when caching is enabled.
44
+ const rawMaxEntries = opts?.maxEntries;
45
+ const maxEntries = rawMaxEntries ?? 1000;
46
+ const maxEntriesLimit = maxEntries === Infinity
47
+ ? undefined
48
+ : Number.isFinite(maxEntries)
49
+ ? maxEntries
50
+ : 0;
51
+ const rawTtlMs = opts?.ttlMs;
52
+ const ttlMs = rawTtlMs == null ? undefined : rawTtlMs;
53
+ const cachingEnabled = (ttlMs === undefined || ttlMs > 0) &&
54
+ (maxEntriesLimit === undefined || maxEntriesLimit > 0);
55
+ // True no-op mode: preserve the input object identity and avoid allocating
56
+ // any wrapper state when caching is disabled.
57
+ if (!cachingEnabled)
58
+ return base;
59
+ // Only pick defaults after the no-op early return so disabled caching pays
60
+ // ~0 overhead.
61
+ const now = opts?.now ?? Date.now;
62
+ const onWarning = opts?.onWarning ?? defaultOnWarning;
63
+ const keyFn = opts?.key ?? defaultSpiceCacheKey;
64
+ const policyByOp = opts?.policy;
65
+ const allowUnsafePolicyOverrides = opts?.allowUnsafePolicyOverrides === true;
66
+ const allowBroadNoStorePrefixes = opts?.allowBroadNoStorePrefixes === true;
67
+ const getPolicy = createCachePolicy({
68
+ wrapperName: "withCachingSync()",
69
+ onWarning,
70
+ policyByOp,
71
+ noStorePrefixes: opts?.noStorePrefixes,
72
+ allowBroadNoStorePrefixes,
73
+ allowUnsafePolicyOverrides,
74
+ });
75
+ const cache = new Map();
76
+ let sweepTimer;
77
+ const isExpired = (entry, now) => entry.expiresAt !== undefined && entry.expiresAt <= now;
78
+ const cleanupExpired = (now) => {
79
+ // TTL eviction is optional. When ttlMs is undefined, the cache is “forever”
80
+ // (still LRU-bounded by maxEntries).
81
+ if (ttlMs === undefined || ttlMs <= 0)
82
+ return;
83
+ for (const [k, entry] of cache) {
84
+ if (isExpired(entry, now))
85
+ cache.delete(k);
86
+ }
87
+ };
88
+ if (cachingEnabled && ttlMs !== undefined && ttlMs > 0) {
89
+ const sweepIntervalMs = opts?.sweepIntervalMs;
90
+ if (sweepIntervalMs !== undefined && sweepIntervalMs > 0) {
91
+ sweepTimer = setInterval(() => cleanupExpired(now()), sweepIntervalMs);
92
+ // In Node, interval timers keep the event loop alive by default. `unref()`
93
+ // prevents this from pinning test runners / CLIs. Browsers return a
94
+ // numeric id (no-op).
95
+ tryUnrefTimer(sweepTimer);
96
+ }
97
+ }
98
+ const clear = () => {
99
+ cache.clear();
100
+ };
101
+ const dispose = () => {
102
+ if (sweepTimer !== undefined) {
103
+ clearInterval(sweepTimer);
104
+ sweepTimer = undefined;
105
+ }
106
+ cache.clear();
107
+ };
108
+ const touch = (k, entry) => {
109
+ cache.delete(k);
110
+ cache.set(k, entry);
111
+ };
112
+ const enforceMaxEntries = () => {
113
+ if (maxEntriesLimit === undefined)
114
+ return;
115
+ while (cache.size > maxEntriesLimit) {
116
+ const oldestKey = cache.keys().next().value;
117
+ if (oldestKey === undefined)
118
+ return;
119
+ cache.delete(oldestKey);
120
+ }
121
+ };
122
+ const request = (op, args) => {
123
+ // Per-method cache policy. When bypassing, skip *all* cache work (no key
124
+ // computation, no reads/writes, no TTL sweeps).
125
+ if (getPolicy(op) === "no-store")
126
+ return base.request(op, args);
127
+ const k = keyFn(op, args);
128
+ if (k == null)
129
+ return base.request(op, args);
130
+ const nowMs = now();
131
+ cleanupExpired(nowMs);
132
+ const existing = cache.get(k);
133
+ if (existing && !isExpired(existing, nowMs)) {
134
+ // LRU touch on access. TTL remains absolute (does not refresh on access).
135
+ touch(k, existing);
136
+ return existing.value;
137
+ }
138
+ if (existing)
139
+ cache.delete(k);
140
+ // Only cache successful responses.
141
+ let value;
142
+ try {
143
+ value = base.request(op, args);
144
+ }
145
+ catch (err) {
146
+ cache.delete(k);
147
+ throw err;
148
+ }
149
+ const entry = { value };
150
+ if (ttlMs !== undefined && ttlMs > 0) {
151
+ entry.expiresAt = now() + ttlMs;
152
+ }
153
+ cache.set(k, entry);
154
+ enforceMaxEntries();
155
+ return value;
156
+ };
157
+ const transport = { request, clear, dispose };
158
+ cachingTransportBrand.add(transport);
159
+ return transport;
160
+ }
161
+ //# sourceMappingURL=withCachingSync.js.map
@@ -0,0 +1,35 @@
1
+ export declare const tspiceRpcRequestType: "tspice:request";
2
+ export declare const tspiceRpcResponseType: "tspice:response";
3
+ export declare const tspiceRpcDisposeType: "tspice:dispose";
4
+ export type RpcRequest = {
5
+ type: typeof tspiceRpcRequestType;
6
+ id: number;
7
+ op: string;
8
+ args: unknown[];
9
+ };
10
+ export type RpcDispose = {
11
+ type: typeof tspiceRpcDisposeType;
12
+ };
13
+ export type SerializedError = {
14
+ message: string;
15
+ name?: string;
16
+ stack?: string;
17
+ };
18
+ export type RpcResponse = {
19
+ type: typeof tspiceRpcResponseType;
20
+ id: number;
21
+ ok: true;
22
+ value: unknown;
23
+ } | {
24
+ type: typeof tspiceRpcResponseType;
25
+ id: number;
26
+ ok: false;
27
+ error: SerializedError;
28
+ };
29
+ export type RpcMessageFromMain = RpcRequest | RpcDispose;
30
+ export type RpcMessageFromWorker = RpcResponse;
31
+ /** Serialize an unknown error into a structured, transferable shape for RPC. */
32
+ export declare function serializeError(err: unknown): SerializedError;
33
+ /** Deserialize an RPC-transferred error shape back into an {@link Error}. */
34
+ export declare function deserializeError(err: unknown): Error;
35
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1,56 @@
1
+ export const tspiceRpcRequestType = "tspice:request";
2
+ export const tspiceRpcResponseType = "tspice:response";
3
+ export const tspiceRpcDisposeType = "tspice:dispose";
4
+ /** Serialize an unknown error into a structured, transferable shape for RPC. */
5
+ export function serializeError(err) {
6
+ const defaultMessage = "Worker request failed";
7
+ const readStringProp = (obj, key) => {
8
+ if (!obj || typeof obj !== "object")
9
+ return undefined;
10
+ const v = obj[key];
11
+ return typeof v === "string" ? v : undefined;
12
+ };
13
+ if (err instanceof Error) {
14
+ const out = { message: err.message || defaultMessage };
15
+ if (typeof err.name === "string" && err.name)
16
+ out.name = err.name;
17
+ if (typeof err.stack === "string")
18
+ out.stack = err.stack;
19
+ return out;
20
+ }
21
+ if (typeof err === "string") {
22
+ return { message: err };
23
+ }
24
+ if (err && typeof err === "object") {
25
+ const message = readStringProp(err, "message") ?? defaultMessage;
26
+ const name = readStringProp(err, "name");
27
+ const stack = readStringProp(err, "stack");
28
+ const out = { message };
29
+ if (typeof name === "string" && name)
30
+ out.name = name;
31
+ if (typeof stack === "string")
32
+ out.stack = stack;
33
+ return out;
34
+ }
35
+ // Only stringify primitives (avoid calling an arbitrary `.toString()` on objects).
36
+ if (typeof err === "number" ||
37
+ typeof err === "boolean" ||
38
+ typeof err === "bigint") {
39
+ return { message: String(err) };
40
+ }
41
+ return { message: defaultMessage };
42
+ }
43
+ /** Deserialize an RPC-transferred error shape back into an {@link Error}. */
44
+ export function deserializeError(err) {
45
+ if (err && typeof err === "object") {
46
+ const e = err;
47
+ const out = new Error(typeof e.message === "string" ? e.message : "Worker request failed");
48
+ if (typeof e.name === "string")
49
+ out.name = e.name;
50
+ if (typeof e.stack === "string")
51
+ out.stack = e.stack;
52
+ return out;
53
+ }
54
+ return new Error(typeof err === "string" ? err : "Worker request failed");
55
+ }
56
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1,20 @@
1
+ export type QueueMacrotaskOptions = {
2
+ /**
3
+ * When no macrotask scheduler exists (no `MessageChannel` and no `setTimeout`),
4
+ * there is no way to create a real task boundary.
5
+ *
6
+ * - `true` (default): invoke `fn()` synchronously as a best-effort fallback.
7
+ * - `false`: do not call `fn()` and return `false`.
8
+ */
9
+ allowSyncFallback?: boolean;
10
+ };
11
+ /**
12
+ * Probe whether a true macrotask scheduler is available in the current runtime
13
+ * (without actually scheduling a task).
14
+ */
15
+ export declare function canQueueMacrotask(): boolean;
16
+ /** Queue `fn` on the next macrotask (MessageChannel preferred; setTimeout fallback). */
17
+ export declare function queueMacrotask(fn: () => void, opts?: QueueMacrotaskOptions): boolean;
18
+ /** Return a promise that resolves on the next macrotask boundary (rejects if unavailable). */
19
+ export declare function nextMacrotask(): Promise<void>;
20
+ //# sourceMappingURL=taskScheduling.d.ts.map