@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.
- package/AGENTS.md +70 -0
- package/LICENSE +201 -0
- package/README.md +109 -0
- package/dist/client.d.ts +116 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +451 -0
- package/dist/client.js.map +1 -0
- package/dist/coordinates.d.ts +98 -0
- package/dist/coordinates.d.ts.map +1 -0
- package/dist/coordinates.js +127 -0
- package/dist/coordinates.js.map +1 -0
- package/dist/gesture.d.ts +96 -0
- package/dist/gesture.d.ts.map +1 -0
- package/dist/gesture.js +178 -0
- package/dist/gesture.js.map +1 -0
- package/dist/idgen.d.ts +8 -0
- package/dist/idgen.d.ts.map +1 -0
- package/dist/idgen.js +15 -0
- package/dist/idgen.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/matrix.d.ts +22 -0
- package/dist/matrix.d.ts.map +1 -0
- package/dist/matrix.js +60 -0
- package/dist/matrix.js.map +1 -0
- package/dist/offloadProtocol.d.ts +40 -0
- package/dist/offloadProtocol.d.ts.map +1 -0
- package/dist/offloadProtocol.js +2 -0
- package/dist/offloadProtocol.js.map +1 -0
- package/dist/offloadWorker.d.ts +2 -0
- package/dist/offloadWorker.d.ts.map +1 -0
- package/dist/offloadWorker.js +41 -0
- package/dist/offloadWorker.js.map +1 -0
- package/dist/processor.d.ts +102 -0
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +145 -0
- package/dist/processor.js.map +1 -0
- package/dist/queue.d.ts +22 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +39 -0
- package/dist/queue.js.map +1 -0
- package/dist/sampling.d.ts +124 -0
- package/dist/sampling.d.ts.map +1 -0
- package/dist/sampling.js +36 -0
- package/dist/sampling.js.map +1 -0
- package/dist/transport.d.ts +10 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +37 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/xrInput.d.ts +54 -0
- package/dist/xrInput.d.ts.map +1 -0
- package/dist/xrInput.js +20 -0
- package/dist/xrInput.js.map +1 -0
- package/llms.txt +16 -0
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"offloadProtocol.js","sourceRoot":"","sources":["../src/offloadProtocol.ts"],"names":[],"mappings":""}
|
|
@@ -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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"}
|
package/dist/queue.d.ts
ADDED
|
@@ -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"}
|
package/dist/sampling.js
ADDED
|
@@ -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"}
|