@uptimizr/sdk-core 0.1.0

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 (65) hide show
  1. package/AGENTS.md +70 -0
  2. package/LICENSE +201 -0
  3. package/README.md +109 -0
  4. package/dist/client.d.ts +116 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +451 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/coordinates.d.ts +98 -0
  9. package/dist/coordinates.d.ts.map +1 -0
  10. package/dist/coordinates.js +127 -0
  11. package/dist/coordinates.js.map +1 -0
  12. package/dist/gesture.d.ts +96 -0
  13. package/dist/gesture.d.ts.map +1 -0
  14. package/dist/gesture.js +178 -0
  15. package/dist/gesture.js.map +1 -0
  16. package/dist/idgen.d.ts +8 -0
  17. package/dist/idgen.d.ts.map +1 -0
  18. package/dist/idgen.js +15 -0
  19. package/dist/idgen.js.map +1 -0
  20. package/dist/index.d.ts +24 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +18 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/matrix.d.ts +22 -0
  25. package/dist/matrix.d.ts.map +1 -0
  26. package/dist/matrix.js +60 -0
  27. package/dist/matrix.js.map +1 -0
  28. package/dist/offloadProtocol.d.ts +40 -0
  29. package/dist/offloadProtocol.d.ts.map +1 -0
  30. package/dist/offloadProtocol.js +2 -0
  31. package/dist/offloadProtocol.js.map +1 -0
  32. package/dist/offloadWorker.d.ts +2 -0
  33. package/dist/offloadWorker.d.ts.map +1 -0
  34. package/dist/offloadWorker.js +41 -0
  35. package/dist/offloadWorker.js.map +1 -0
  36. package/dist/processor.d.ts +102 -0
  37. package/dist/processor.d.ts.map +1 -0
  38. package/dist/processor.js +145 -0
  39. package/dist/processor.js.map +1 -0
  40. package/dist/queue.d.ts +22 -0
  41. package/dist/queue.d.ts.map +1 -0
  42. package/dist/queue.js +39 -0
  43. package/dist/queue.js.map +1 -0
  44. package/dist/sampling.d.ts +124 -0
  45. package/dist/sampling.d.ts.map +1 -0
  46. package/dist/sampling.js +36 -0
  47. package/dist/sampling.js.map +1 -0
  48. package/dist/transport.d.ts +10 -0
  49. package/dist/transport.d.ts.map +1 -0
  50. package/dist/transport.js +37 -0
  51. package/dist/transport.js.map +1 -0
  52. package/dist/types.d.ts +186 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +2 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/version.d.ts +3 -0
  57. package/dist/version.d.ts.map +1 -0
  58. package/dist/version.js +3 -0
  59. package/dist/version.js.map +1 -0
  60. package/dist/xrInput.d.ts +54 -0
  61. package/dist/xrInput.d.ts.map +1 -0
  62. package/dist/xrInput.js +20 -0
  63. package/dist/xrInput.js.map +1 -0
  64. package/llms.txt +16 -0
  65. package/package.json +61 -0
@@ -0,0 +1,40 @@
1
+ import type { CollectRequest } from "@uptimizr/schema";
2
+ /**
3
+ * Wire protocol for the opt-in offload worker (ADR 0031).
4
+ *
5
+ * These are **type-only** definitions shared by the main-thread processor
6
+ * ([`processor.ts`](./processor.ts)) and the worker entry
7
+ * ([`offloadWorker.ts`](./offloadWorker.ts)). Keeping the protocol in its own
8
+ * module (with no runtime exports) means the worker bundle pulls in none of the
9
+ * processor's runtime code — it stays tiny.
10
+ *
11
+ * The only thing that crosses the boundary is the plain-data `CollectRequest`
12
+ * DTO: by the time a batch reaches the worker it is an array of Zod-shaped plain
13
+ * objects with no engine/DOM handles, so it travels by structured clone (or, for
14
+ * buffer-backed fields, by transfer).
15
+ */
16
+ /** Sets the collector URL the worker dispatches to. Sent once at construction. */
17
+ export interface WorkerInitMessage {
18
+ type: "init";
19
+ /** Fully-resolved collect endpoint (e.g. `https://host/api/v1/collect`). */
20
+ url: string;
21
+ }
22
+ /** A batch to serialize and dispatch off the main thread. */
23
+ export interface WorkerBatchMessage {
24
+ type: "batch";
25
+ /** Correlates the result back to the awaiting `process()` call. */
26
+ id: number;
27
+ /** The plain-data batch to serialize and send. */
28
+ batch: CollectRequest;
29
+ }
30
+ /** Messages the main thread sends to the worker. */
31
+ export type WorkerInbound = WorkerInitMessage | WorkerBatchMessage;
32
+ /** The delivery result for one batch, reported back to the main thread. */
33
+ export interface WorkerResultMessage {
34
+ type: "result";
35
+ id: number;
36
+ ok: boolean;
37
+ }
38
+ /** Messages the worker sends back to the main thread. */
39
+ export type WorkerOutbound = WorkerResultMessage;
40
+ //# sourceMappingURL=offloadProtocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offloadProtocol.d.ts","sourceRoot":"","sources":["../src/offloadProtocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AAEH,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;CACb;AAED,6DAA6D;AAC7D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,kDAAkD;IAClD,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAEnE,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;CACb;AAED,yDAAyD;AACzD,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=offloadProtocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offloadProtocol.js","sourceRoot":"","sources":["../src/offloadProtocol.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=offloadWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offloadWorker.d.ts","sourceRoot":"","sources":["../src/offloadWorker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ // The build uses the DOM lib (not WebWorker), so narrow `self` structurally.
2
+ const scope = self;
3
+ /** Resolved collector URL, set by the `init` message before any batch arrives. */
4
+ let collectUrl = "";
5
+ scope.addEventListener("message", (event) => {
6
+ const message = event.data;
7
+ if (!message) {
8
+ return;
9
+ }
10
+ if (message.type === "init") {
11
+ collectUrl = message.url;
12
+ return;
13
+ }
14
+ if (message.type === "batch") {
15
+ void dispatch(message.id, message.batch);
16
+ }
17
+ });
18
+ /** Serialize and POST one batch, then report the outcome back to the page. */
19
+ async function dispatch(id, batch) {
20
+ let ok = false;
21
+ try {
22
+ if (collectUrl) {
23
+ const body = JSON.stringify(batch);
24
+ const res = await scope.fetch(collectUrl, {
25
+ method: "POST",
26
+ headers: { "content-type": "application/json" },
27
+ body,
28
+ // `keepalive` has a ~64 KB browser cap; only request it under the limit.
29
+ keepalive: body.length < 64_000,
30
+ });
31
+ ok = res.ok;
32
+ }
33
+ }
34
+ catch {
35
+ ok = false;
36
+ }
37
+ const result = { type: "result", id, ok };
38
+ scope.postMessage(result);
39
+ }
40
+ export {};
41
+ //# sourceMappingURL=offloadWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offloadWorker.js","sourceRoot":"","sources":["../src/offloadWorker.ts"],"names":[],"mappings":"AAuBA,6EAA6E;AAC7E,MAAM,KAAK,GAAG,IAA8B,CAAC;AAE7C,kFAAkF;AAClF,IAAI,UAAU,GAAG,EAAE,CAAC;AAEpB,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAiC,CAAC;IACxD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC;QACzB,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC7B,KAAK,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,KAAK,UAAU,QAAQ,CAAC,EAAU,EAAE,KAAc;IAChD,IAAI,EAAE,GAAG,KAAK,CAAC;IACf,IAAI,CAAC;QACH,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE;gBACxC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI;gBACJ,yEAAyE;gBACzE,SAAS,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM;aAChC,CAAC,CAAC;YACH,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,EAAE,GAAG,KAAK,CAAC;IACb,CAAC;IACD,MAAM,MAAM,GAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAC/D,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,102 @@
1
+ import type { CollectRequest } from "@uptimizr/schema";
2
+ import type { Transport } from "./types.js";
3
+ /**
4
+ * A **Processor** owns the offload-eligible *processing* phase of the SDK
5
+ * (ADR 0031): turning a drained batch into a delivered one — serialization plus
6
+ * network dispatch. It sits between the client's flush loop and the wire.
7
+ *
8
+ * The boundary that makes this work is the plain-data {@link CollectRequest}
9
+ * DTO. By the time a batch reaches a processor it is already an array of
10
+ * Zod-shaped plain objects with no engine or DOM handles, so it can either be
11
+ * serialized in place (main-thread processor) or shipped to a worker
12
+ * (worker processor) without touching the live 3D scene.
13
+ *
14
+ * Two implementations exist:
15
+ * - {@link createMainProcessor} — the default; runs everything on the main
16
+ * thread via the configured {@link Transport}. Behaviour is unchanged from
17
+ * before this seam existed, byte-for-byte.
18
+ * - {@link createWorkerProcessor} — opt-in; moves steady-state serialization +
19
+ * dispatch to a Web Worker, keeping the terminal unload flush on the main
20
+ * thread.
21
+ */
22
+ export interface Processor {
23
+ /**
24
+ * Serialize and deliver a steady-state batch. Resolves `true` on success;
25
+ * `false` re-queues the batch for the next attempt.
26
+ */
27
+ process(batch: CollectRequest): Promise<boolean>;
28
+ /**
29
+ * Deliver the **final** batch on page unload. This MUST run on the main/page
30
+ * thread: `navigator.sendBeacon` is only reliable from the page context during
31
+ * `visibilitychange: hidden` / `pagehide` (ADR 0031 §5, ADR 0006), and a
32
+ * worker may be torn down with the page before an async post completes.
33
+ */
34
+ processUnload(batch: CollectRequest): Promise<boolean>;
35
+ /** Release resources (e.g. terminate the worker). Idempotent. */
36
+ dispose(): void;
37
+ }
38
+ /**
39
+ * The default processor: serialize + dispatch on the main thread via the given
40
+ * transport. This is the no-op-fallback baseline — identical to the behaviour
41
+ * before the seam was introduced. Both the steady-state and unload paths use the
42
+ * same transport (the beacon transport already prefers `sendBeacon`).
43
+ */
44
+ export declare function createMainProcessor(transport: Transport): Processor;
45
+ /**
46
+ * Minimal structural type for a dedicated worker — the subset of the DOM
47
+ * `Worker` interface the processor uses. A real `Worker` satisfies it; tests
48
+ * inject a lightweight stub so the protocol can be exercised without a runtime
49
+ * worker (jsdom/Node have no usable `Worker`).
50
+ */
51
+ export interface WorkerLike {
52
+ postMessage(message: unknown, transfer?: Transferable[]): void;
53
+ addEventListener(type: "message", listener: (event: {
54
+ data: unknown;
55
+ }) => void): void;
56
+ addEventListener(type: "error", listener: (event: unknown) => void): void;
57
+ terminate?(): void;
58
+ }
59
+ /** Constructs the offload worker. Injectable so bundlers/tests can override it. */
60
+ export type WorkerFactory = () => WorkerLike;
61
+ export interface WorkerProcessorOptions {
62
+ /** Collector base URL (the same value as the client `endpoint`). */
63
+ endpoint: string;
64
+ /**
65
+ * Transport used for the **unload** flush, which stays on the main thread
66
+ * (ADR 0031 §5). Normally the client's default beacon transport.
67
+ */
68
+ unloadTransport: Transport;
69
+ /**
70
+ * How to construct the worker. Defaults to a module worker bundled with the
71
+ * SDK via the `new Worker(new URL("./offloadWorker.js", import.meta.url))`
72
+ * pattern recognized by Vite/webpack5/Rollup/esbuild.
73
+ */
74
+ workerFactory?: WorkerFactory;
75
+ }
76
+ /**
77
+ * Collect the transferable buffers carried by a batch so they move to the worker
78
+ * **zero-copy** instead of being structured-cloned (ADR 0031 §6, #98).
79
+ *
80
+ * Today's events are plain JSON objects, so this returns an empty list — the
81
+ * mechanism is in place for the buffer-backed continuous-channel DTOs that the
82
+ * connector-side offload follow-up will introduce. The scan is shallow (one pass
83
+ * over each event's own values) to keep the per-flush cost negligible, and it
84
+ * de-duplicates so a buffer shared across fields is only transferred once
85
+ * (a duplicate in the transfer list would throw).
86
+ *
87
+ * Caveat: transferring a buffer **neuters** it on the main thread. Continuous
88
+ * channels that opt in must own a fresh buffer per batch.
89
+ */
90
+ export declare function collectTransferables(batch: CollectRequest): Transferable[];
91
+ /**
92
+ * Create a worker-backed processor that runs steady-state serialization +
93
+ * dispatch off the main thread (ADR 0031). Returns `null` when a worker cannot
94
+ * be constructed (no `Worker` global, the bundler did not emit the asset, a
95
+ * restrictive CSP, SSR, or tests) so the caller can fall back to the
96
+ * main-thread processor — worker mode is never required for correctness.
97
+ *
98
+ * The unload flush deliberately does **not** use the worker; it goes through
99
+ * {@link WorkerProcessorOptions.unloadTransport} on the main thread.
100
+ */
101
+ export declare function createWorkerProcessor(options: WorkerProcessorOptions): Processor | null;
102
+ //# sourceMappingURL=processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,iEAAiE;IACjE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAQnE;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC/D,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACtF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,SAAS,CAAC,IAAI,IAAI,CAAC;CACpB;AAED,mFAAmF;AACnF,MAAM,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC;AAE7C,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,eAAe,EAAE,SAAS,CAAC;IAC3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AASD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,YAAY,EAAE,CAkB1E;AAWD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,SAAS,GAAG,IAAI,CA8EvF"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The default processor: serialize + dispatch on the main thread via the given
3
+ * transport. This is the no-op-fallback baseline — identical to the behaviour
4
+ * before the seam was introduced. Both the steady-state and unload paths use the
5
+ * same transport (the beacon transport already prefers `sendBeacon`).
6
+ */
7
+ export function createMainProcessor(transport) {
8
+ return {
9
+ process: (batch) => transport.send(batch),
10
+ processUnload: (batch) => transport.send(batch),
11
+ dispose: () => {
12
+ /* nothing to release */
13
+ },
14
+ };
15
+ }
16
+ const COLLECT_PATH = "/api/v1/collect";
17
+ /** Build the collector POST URL the worker dispatches to. */
18
+ function collectUrl(endpoint) {
19
+ return endpoint.replace(/\/$/, "") + COLLECT_PATH;
20
+ }
21
+ /**
22
+ * Collect the transferable buffers carried by a batch so they move to the worker
23
+ * **zero-copy** instead of being structured-cloned (ADR 0031 §6, #98).
24
+ *
25
+ * Today's events are plain JSON objects, so this returns an empty list — the
26
+ * mechanism is in place for the buffer-backed continuous-channel DTOs that the
27
+ * connector-side offload follow-up will introduce. The scan is shallow (one pass
28
+ * over each event's own values) to keep the per-flush cost negligible, and it
29
+ * de-duplicates so a buffer shared across fields is only transferred once
30
+ * (a duplicate in the transfer list would throw).
31
+ *
32
+ * Caveat: transferring a buffer **neuters** it on the main thread. Continuous
33
+ * channels that opt in must own a fresh buffer per batch.
34
+ */
35
+ export function collectTransferables(batch) {
36
+ const seen = new Set();
37
+ const out = [];
38
+ for (const event of batch.events) {
39
+ for (const value of Object.values(event)) {
40
+ let buffer;
41
+ if (value instanceof ArrayBuffer) {
42
+ buffer = value;
43
+ }
44
+ else if (ArrayBuffer.isView(value)) {
45
+ buffer = value.buffer;
46
+ }
47
+ if (buffer && !seen.has(buffer)) {
48
+ seen.add(buffer);
49
+ out.push(buffer);
50
+ }
51
+ }
52
+ }
53
+ return out;
54
+ }
55
+ /** Default factory: a module worker shipped alongside the SDK in `dist/`. */
56
+ function defaultWorkerFactory() {
57
+ // `new URL(..., import.meta.url)` + `new Worker` is the pattern bundlers
58
+ // recognize to emit and resolve a library-owned worker asset.
59
+ return new Worker(new URL("./offloadWorker.js", import.meta.url), {
60
+ type: "module",
61
+ });
62
+ }
63
+ /**
64
+ * Create a worker-backed processor that runs steady-state serialization +
65
+ * dispatch off the main thread (ADR 0031). Returns `null` when a worker cannot
66
+ * be constructed (no `Worker` global, the bundler did not emit the asset, a
67
+ * restrictive CSP, SSR, or tests) so the caller can fall back to the
68
+ * main-thread processor — worker mode is never required for correctness.
69
+ *
70
+ * The unload flush deliberately does **not** use the worker; it goes through
71
+ * {@link WorkerProcessorOptions.unloadTransport} on the main thread.
72
+ */
73
+ export function createWorkerProcessor(options) {
74
+ const factory = options.workerFactory ?? defaultWorkerFactory;
75
+ let worker;
76
+ try {
77
+ worker = factory();
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ let nextId = 1;
83
+ let disposed = false;
84
+ const pending = new Map();
85
+ const settleAll = (ok) => {
86
+ for (const resolve of pending.values()) {
87
+ resolve(ok);
88
+ }
89
+ pending.clear();
90
+ };
91
+ worker.addEventListener("message", (event) => {
92
+ const data = event.data;
93
+ if (!data || data.type !== "result") {
94
+ return;
95
+ }
96
+ const resolve = pending.get(data.id);
97
+ if (resolve) {
98
+ pending.delete(data.id);
99
+ resolve(data.ok);
100
+ }
101
+ });
102
+ // If the worker errors, fail every in-flight send so the batches re-queue and
103
+ // are retried on the main thread — no events are lost.
104
+ worker.addEventListener("error", () => {
105
+ settleAll(false);
106
+ });
107
+ try {
108
+ worker.postMessage({
109
+ type: "init",
110
+ url: collectUrl(options.endpoint),
111
+ });
112
+ }
113
+ catch {
114
+ worker.terminate?.();
115
+ return null;
116
+ }
117
+ return {
118
+ process(batch) {
119
+ if (disposed) {
120
+ return Promise.resolve(false);
121
+ }
122
+ const id = nextId++;
123
+ return new Promise((resolve) => {
124
+ pending.set(id, resolve);
125
+ try {
126
+ worker.postMessage({ type: "batch", id, batch }, collectTransferables(batch));
127
+ }
128
+ catch {
129
+ pending.delete(id);
130
+ resolve(false);
131
+ }
132
+ });
133
+ },
134
+ processUnload: (batch) => options.unloadTransport.send(batch),
135
+ dispose: () => {
136
+ if (disposed) {
137
+ return;
138
+ }
139
+ disposed = true;
140
+ settleAll(false);
141
+ worker.terminate?.();
142
+ },
143
+ };
144
+ }
145
+ //# sourceMappingURL=processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.js","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { AnyEvent } from "@uptimizr/schema";
2
+ /**
3
+ * A bounded in-memory FIFO queue for pending events.
4
+ *
5
+ * When the queue exceeds `maxSize` (e.g. the device is offline and flushes keep
6
+ * failing), the oldest events are dropped to cap memory use. This is a deliberate
7
+ * trade-off: recent events are more valuable than unbounded retention.
8
+ */
9
+ export declare class EventQueue {
10
+ private readonly maxSize;
11
+ private items;
12
+ constructor(maxSize: number);
13
+ /** Number of queued events. */
14
+ get size(): number;
15
+ /** Append an event, dropping the oldest if the cap is exceeded. */
16
+ enqueue(event: AnyEvent): void;
17
+ /** Remove and return all queued events. */
18
+ drain(): AnyEvent[];
19
+ /** Put events back at the front (e.g. after a failed flush). */
20
+ prepend(events: AnyEvent[]): void;
21
+ }
22
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;;;GAMG;AACH,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,KAAK,CAAkB;gBAEF,OAAO,EAAE,MAAM;IAE5C,+BAA+B;IAC/B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,mEAAmE;IACnE,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAO9B,2CAA2C;IAC3C,KAAK,IAAI,QAAQ,EAAE;IAMnB,gEAAgE;IAChE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI;CAMlC"}
package/dist/queue.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A bounded in-memory FIFO queue for pending events.
3
+ *
4
+ * When the queue exceeds `maxSize` (e.g. the device is offline and flushes keep
5
+ * failing), the oldest events are dropped to cap memory use. This is a deliberate
6
+ * trade-off: recent events are more valuable than unbounded retention.
7
+ */
8
+ export class EventQueue {
9
+ maxSize;
10
+ items = [];
11
+ constructor(maxSize) {
12
+ this.maxSize = maxSize;
13
+ }
14
+ /** Number of queued events. */
15
+ get size() {
16
+ return this.items.length;
17
+ }
18
+ /** Append an event, dropping the oldest if the cap is exceeded. */
19
+ enqueue(event) {
20
+ this.items.push(event);
21
+ if (this.items.length > this.maxSize) {
22
+ this.items.splice(0, this.items.length - this.maxSize);
23
+ }
24
+ }
25
+ /** Remove and return all queued events. */
26
+ drain() {
27
+ const drained = this.items;
28
+ this.items = [];
29
+ return drained;
30
+ }
31
+ /** Put events back at the front (e.g. after a failed flush). */
32
+ prepend(events) {
33
+ this.items = [...events, ...this.items];
34
+ if (this.items.length > this.maxSize) {
35
+ this.items.splice(0, this.items.length - this.maxSize);
36
+ }
37
+ }
38
+ }
39
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IAGQ;IAFrB,KAAK,GAAe,EAAE,CAAC;IAE/B,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEhD,+BAA+B;IAC/B,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,mEAAmE;IACnE,OAAO,CAAC,KAAe;QACrB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,KAAK;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,gEAAgE;IAChE,OAAO,CAAC,MAAkB;QACxB,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Capture-fidelity sampling profile (ADR 0012).
3
+ *
4
+ * Fidelity is an explicit, per-channel, developer-owned contract with
5
+ * conservative defaults and no hard cap. The dial governs **continuous** channels
6
+ * only — camera/head pose, pointer move, and (ADR 0011) controller/hand poses.
7
+ * **Discrete** semantic events (`pointer_click`, `pointer_down`/`pointer_up`,
8
+ * `mesh_interaction`, `scene_change`, `session_start`/`session_end`, `custom`)
9
+ * are always captured at 100% and MUST NOT be rate-limited.
10
+ *
11
+ * The profile is a static literal at init today; it is shaped so the same object
12
+ * can later be fetched from the collector per project (remote config) without a
13
+ * redesign.
14
+ */
15
+ /**
16
+ * A per-channel sample rate:
17
+ * - a positive number — target rate in **Hz** (samples/second),
18
+ * - `0` — channel off (do not sample),
19
+ * - `"frame"` — emit every render tick (the 100% / per-frame case).
20
+ */
21
+ export type SampleRate = number | "frame";
22
+ /**
23
+ * Per-channel and per-source sampling rates. Omitted channels fall back to the
24
+ * connector's conservative defaults (≈1 Hz camera, ≈4 Hz pointer, ≈0.5 Hz perf).
25
+ */
26
+ export interface SamplingProfile {
27
+ /** Camera/head pose channel. */
28
+ camera?: SampleRate;
29
+ /** Pointer-move channel. */
30
+ pointerMove?: SampleRate;
31
+ /** Performance (FPS) channel. */
32
+ perf?: SampleRate;
33
+ /**
34
+ * Per-source overrides keyed by input-source id (ADR 0011), e.g.
35
+ * `{ leftController: 30, rightHand: 30, gaze: 0 }`. Forward-looking: connectors
36
+ * apply these to the continuous pose stream of each source they capture.
37
+ */
38
+ perSource?: Record<string, SampleRate>;
39
+ /**
40
+ * Scene-actor (`node_transform`) capture rates, keyed by the developer-declared
41
+ * actor id (ADR 0027 Tier 1). **Default OFF** — only ids listed here are
42
+ * sampled, and each MUST also be declared in the connector's `actors` map; an
43
+ * unknown id is a no-op with a dev-mode warning. There is no "track all nodes"
44
+ * switch (cost + privacy). Example: `{ "npc-guard": 10, elevator: "frame" }`.
45
+ *
46
+ * A value may also be a {@link NodeSamplingConfig} to opt the actor's **subtree**
47
+ * into capture (ADR 0033) — e.g. a whole glTF whose internal parts move
48
+ * independently. The descendant transforms are emitted as `node_transform`
49
+ * samples carrying a `childPath` (engine node path relative to the actor); the
50
+ * walk is anchored, bounded (`maxDepth`/`maxNodes`), and still opt-in.
51
+ */
52
+ nodes?: Record<string, SampleRate | NodeSamplingConfig>;
53
+ /**
54
+ * Skeleton-bone (`node_transform` with `boneId`) capture, keyed by the
55
+ * developer-declared actor id (ADR 0027 **Tier 2** — opt-in, higher cost and
56
+ * privacy). The actor MUST also be declared in `actors` and resolve to a
57
+ * skinned node with a skeleton. Each entry allowlists the bones to capture
58
+ * (`include`) and an optional rate (`hz`); bone transforms are skeleton-local.
59
+ * A humanoid rig is ~50–65 bones, so there is **no whole-skeleton default** —
60
+ * `include: "*"` is a permitted but explicitly expensive opt-in.
61
+ * Example: `{ "npc-guard": { include: ["mixamorig:RightHand"], hz: 30 } }`.
62
+ */
63
+ bones?: Record<string, BoneSamplingConfig>;
64
+ }
65
+ /**
66
+ * Tier-2 bone-capture configuration for one declared actor (ADR 0027 §5). The
67
+ * `include` allowlist is required — either an explicit list of bone names or the
68
+ * explicit `"*"` wildcard for the full rig (documented as expensive). `hz`
69
+ * follows the same {@link SampleRate} vocabulary as every other channel and
70
+ * defaults to the connector's node default when omitted.
71
+ */
72
+ export interface BoneSamplingConfig {
73
+ /** Bone names to capture, or `"*"` for the whole rig (explicit, expensive). */
74
+ include: string[] | "*";
75
+ /** Capture rate for this actor's bones (Hz / `"frame"` / `0`-off). */
76
+ hz?: SampleRate;
77
+ }
78
+ /**
79
+ * Tier-1 **subtree** capture configuration for one declared actor (ADR 0033).
80
+ * Lets a single declared actor stand in for a moving hierarchy (e.g. a glTF whose
81
+ * internal parts animate) without naming every descendant. Each captured
82
+ * descendant is emitted as a `node_transform` carrying a `childPath` (the engine
83
+ * node path relative to the actor). The walk is anchored to the actor, visits
84
+ * transform nodes only (bones go through {@link BoneSamplingConfig}; cameras are
85
+ * refused), and is **bounded** so a deep/wide hierarchy cannot blow up the wire.
86
+ */
87
+ export interface NodeSamplingConfig {
88
+ /** Capture rate for the actor and its captured descendants. */
89
+ hz?: SampleRate;
90
+ /**
91
+ * Descendant node names to also capture, or `"*"` for every descendant under
92
+ * the caps below. Omitted ⇒ root-only (identical to a bare {@link SampleRate}).
93
+ */
94
+ include?: string[] | "*";
95
+ /** Max depth below the actor to descend (root is depth 0). Default 8. */
96
+ maxDepth?: number;
97
+ /** Max number of descendants to capture after deterministic BFS truncation. Default 64. */
98
+ maxNodes?: number;
99
+ /** Descendant node names to skip (and prune their subtree). */
100
+ exclude?: string[];
101
+ }
102
+ /** A channel's resolved cadence: off, every render frame, or a fixed interval. */
103
+ export type ResolvedCadence = {
104
+ readonly mode: "off";
105
+ } | {
106
+ readonly mode: "frame";
107
+ } | {
108
+ readonly mode: "interval";
109
+ readonly ms: number;
110
+ };
111
+ /**
112
+ * Resolve a {@link SampleRate} (or `undefined`) into a concrete cadence.
113
+ *
114
+ * - `undefined` ⇒ the connector default (`defaultMs`), preserving the legacy
115
+ * `sampleCameraMs`/`samplePerfMs`/`pointerMoveThrottleMs` knobs.
116
+ * - `"frame"` ⇒ every render tick.
117
+ * - `0` (or any non-positive number) ⇒ off.
118
+ * - `N` Hz ⇒ an interval of `1000 / N` ms.
119
+ *
120
+ * There is no enforced upper bound in the OSS SDK (ADR 0012 §3); a caller may opt
121
+ * into `"frame"` on every channel.
122
+ */
123
+ export declare function resolveCadence(rate: SampleRate | undefined, defaultMs: number): ResolvedCadence;
124
+ //# sourceMappingURL=sampling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,iCAAiC;IACjC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACvC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,kBAAkB,CAAC,CAAC;IACxD;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACxB,sEAAsE;IACtE,EAAE,CAAC,EAAE,UAAU,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,kFAAkF;AAClF,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAK/F"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Capture-fidelity sampling profile (ADR 0012).
3
+ *
4
+ * Fidelity is an explicit, per-channel, developer-owned contract with
5
+ * conservative defaults and no hard cap. The dial governs **continuous** channels
6
+ * only — camera/head pose, pointer move, and (ADR 0011) controller/hand poses.
7
+ * **Discrete** semantic events (`pointer_click`, `pointer_down`/`pointer_up`,
8
+ * `mesh_interaction`, `scene_change`, `session_start`/`session_end`, `custom`)
9
+ * are always captured at 100% and MUST NOT be rate-limited.
10
+ *
11
+ * The profile is a static literal at init today; it is shaped so the same object
12
+ * can later be fetched from the collector per project (remote config) without a
13
+ * redesign.
14
+ */
15
+ /**
16
+ * Resolve a {@link SampleRate} (or `undefined`) into a concrete cadence.
17
+ *
18
+ * - `undefined` ⇒ the connector default (`defaultMs`), preserving the legacy
19
+ * `sampleCameraMs`/`samplePerfMs`/`pointerMoveThrottleMs` knobs.
20
+ * - `"frame"` ⇒ every render tick.
21
+ * - `0` (or any non-positive number) ⇒ off.
22
+ * - `N` Hz ⇒ an interval of `1000 / N` ms.
23
+ *
24
+ * There is no enforced upper bound in the OSS SDK (ADR 0012 §3); a caller may opt
25
+ * into `"frame"` on every channel.
26
+ */
27
+ export function resolveCadence(rate, defaultMs) {
28
+ if (rate === undefined)
29
+ return { mode: "interval", ms: defaultMs };
30
+ if (rate === "frame")
31
+ return { mode: "frame" };
32
+ if (rate <= 0)
33
+ return { mode: "off" };
34
+ return { mode: "interval", ms: 1000 / rate };
35
+ }
36
+ //# sourceMappingURL=sampling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAmGH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,IAA4B,EAAE,SAAiB;IAC5E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;IACnE,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC/C,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACtC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Transport } from "./types.js";
2
+ /**
3
+ * Default browser transport.
4
+ *
5
+ * Prefers `navigator.sendBeacon` so in-flight batches survive page unload, and
6
+ * falls back to `fetch` with `keepalive` (and finally a plain `fetch`) in
7
+ * environments where beacon is unavailable (including Node for tests).
8
+ */
9
+ export declare function createBeaconTransport(endpoint: string): Transport;
10
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CA+BjE"}