@uptimizr/collector-server 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/LICENSE +201 -0
- package/README.md +169 -0
- package/dist/app.d.ts +22 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +96 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +148 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +59 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/csp.d.ts +18 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +91 -0
- package/dist/csp.js.map +1 -0
- package/dist/duckdbStore.d.ts +14 -0
- package/dist/duckdbStore.d.ts.map +1 -0
- package/dist/duckdbStore.js +69 -0
- package/dist/duckdbStore.js.map +1 -0
- package/dist/enrich.d.ts +8 -0
- package/dist/enrich.d.ts.map +1 -0
- package/dist/enrich.js +9 -0
- package/dist/enrich.js.map +1 -0
- package/dist/liveBus.d.ts +86 -0
- package/dist/liveBus.d.ts.map +1 -0
- package/dist/liveBus.js +195 -0
- package/dist/liveBus.js.map +1 -0
- package/dist/liveToken.d.ts +11 -0
- package/dist/liveToken.d.ts.map +1 -0
- package/dist/liveToken.js +44 -0
- package/dist/liveToken.js.map +1 -0
- package/dist/memoryStore.d.ts +22 -0
- package/dist/memoryStore.d.ts.map +1 -0
- package/dist/memoryStore.js +207 -0
- package/dist/memoryStore.js.map +1 -0
- package/dist/routes/collect.d.ts +25 -0
- package/dist/routes/collect.d.ts.map +1 -0
- package/dist/routes/collect.js +55 -0
- package/dist/routes/collect.js.map +1 -0
- package/dist/routes/live.d.ts +18 -0
- package/dist/routes/live.d.ts.map +1 -0
- package/dist/routes/live.js +179 -0
- package/dist/routes/live.js.map +1 -0
- package/dist/routes/query.d.ts +14 -0
- package/dist/routes/query.d.ts.map +1 -0
- package/dist/routes/query.js +610 -0
- package/dist/routes/query.js.map +1 -0
- package/dist/serve.d.ts +18 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +48 -0
- package/dist/serve.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +12 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +263 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +2 -0
- package/dist/store.js.map +1 -0
- package/dist/visitor.d.ts +12 -0
- package/dist/visitor.d.ts.map +1 -0
- package/dist/visitor.js +18 -0
- package/dist/visitor.js.map +1 -0
- package/package.json +64 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collector server configuration, read from the environment (see `.env.example`).
|
|
3
|
+
* Fails fast when a required secret is missing.
|
|
4
|
+
*/
|
|
5
|
+
export interface CollectorConfig {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
/** Allowed CORS origins. Empty disables cross-origin browser access. */
|
|
9
|
+
corsOrigins: string[];
|
|
10
|
+
/** Secret seed for the daily-rotating cookieless visitor hash. */
|
|
11
|
+
visitorHashSecret: string;
|
|
12
|
+
/** Opt-in raw per-session retention; gates the replay/timeline endpoint (ADR 0003). */
|
|
13
|
+
enableRawSessionRetention: boolean;
|
|
14
|
+
/** Liveness window in ms for live presence/follow (ADR 0032 §1). */
|
|
15
|
+
liveWindowMs: number;
|
|
16
|
+
/** Secret for signing short-lived live SSE tokens (ADR 0032 §7). */
|
|
17
|
+
liveTokenSecret: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether a dedicated `LIVE_TOKEN_SECRET` was supplied. When false the live
|
|
20
|
+
* token secret falls back to {@link visitorHashSecret}; the server warns at
|
|
21
|
+
* startup so production deployments give the two secrets independent values.
|
|
22
|
+
*/
|
|
23
|
+
liveTokenSecretIsDedicated: boolean;
|
|
24
|
+
/** Live SSE token lifetime in ms (ADR 0032 §7). */
|
|
25
|
+
liveTokenTtlMs: number;
|
|
26
|
+
/** Max concurrent live SSE connections per collector (ADR 0032 §6). */
|
|
27
|
+
liveMaxConnections: number;
|
|
28
|
+
/** Interval between pushed presence snapshots / SSE heartbeats, in ms. */
|
|
29
|
+
livePresenceIntervalMs: number;
|
|
30
|
+
/** Max requests per window per client for rate limiting. */
|
|
31
|
+
rateLimitMax: number;
|
|
32
|
+
/** Rate-limit window in ms. */
|
|
33
|
+
rateLimitWindowMs: number;
|
|
34
|
+
/** Stricter, dedicated per-client request budget for the public ingest route. */
|
|
35
|
+
ingestRateLimitMax: number;
|
|
36
|
+
/** Ingest rate-limit window in ms. */
|
|
37
|
+
ingestRateLimitWindowMs: number;
|
|
38
|
+
/**
|
|
39
|
+
* Trust `X-Forwarded-*` headers from a reverse proxy / load balancer. Required
|
|
40
|
+
* when the collector runs behind TLS termination so the per-visitor hash and
|
|
41
|
+
* the rate-limit bucket key on the real client IP rather than the proxy's.
|
|
42
|
+
* `false` (default) trusts only the direct socket peer. Accepts `true`/`false`,
|
|
43
|
+
* a hop count, or an IP/subnet/comma-list passed through to Fastify.
|
|
44
|
+
*/
|
|
45
|
+
trustProxy: boolean | number | string;
|
|
46
|
+
/** Max accepted request body size in bytes (defends against oversized payloads). */
|
|
47
|
+
bodyLimit: number;
|
|
48
|
+
/** Content-Security-Policy for the bundled dashboard: `strict` (default) or `off`. */
|
|
49
|
+
cspMode: "strict" | "off";
|
|
50
|
+
/**
|
|
51
|
+
* Absolute path to a pre-built static dashboard (`out/`) to serve as an
|
|
52
|
+
* all-in-one bundle. Unset (the default) keeps the collector headless.
|
|
53
|
+
*/
|
|
54
|
+
dashboardDir?: string;
|
|
55
|
+
}
|
|
56
|
+
type Env = Record<string, string | undefined>;
|
|
57
|
+
export declare function loadConfig(env?: Env): CollectorConfig;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kEAAkE;IAClE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uFAAuF;IACvF,yBAAyB,EAAE,OAAO,CAAC;IACnC,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,0BAA0B,EAAE,OAAO,CAAC;IACpC,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,OAAO,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAsB9C,wBAAgB,UAAU,CAAC,GAAG,GAAE,GAAiB,GAAG,eAAe,CAgClE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
function bool(value) {
|
|
3
|
+
return value === "1" || value?.toLowerCase() === "true";
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parse `COLLECTOR_TRUST_PROXY` into a value Fastify's `trustProxy` accepts:
|
|
7
|
+
* a boolean, a hop count, or an IP/subnet/comma-separated list passed through
|
|
8
|
+
* verbatim. Empty/unset means do not trust proxy headers.
|
|
9
|
+
*/
|
|
10
|
+
function parseTrustProxy(value) {
|
|
11
|
+
if (value == null || value.trim() === "")
|
|
12
|
+
return false;
|
|
13
|
+
const trimmed = value.trim();
|
|
14
|
+
const lower = trimmed.toLowerCase();
|
|
15
|
+
if (lower === "true")
|
|
16
|
+
return true;
|
|
17
|
+
if (lower === "false")
|
|
18
|
+
return false;
|
|
19
|
+
const n = Number(trimmed);
|
|
20
|
+
if (Number.isInteger(n) && String(n) === trimmed)
|
|
21
|
+
return n;
|
|
22
|
+
return trimmed;
|
|
23
|
+
}
|
|
24
|
+
export function loadConfig(env = process.env) {
|
|
25
|
+
const visitorHashSecret = env.VISITOR_HASH_SECRET;
|
|
26
|
+
if (!visitorHashSecret) {
|
|
27
|
+
throw new Error("VISITOR_HASH_SECRET is required but was not set");
|
|
28
|
+
}
|
|
29
|
+
const liveTokenSecretIsDedicated = Boolean(env.LIVE_TOKEN_SECRET);
|
|
30
|
+
return {
|
|
31
|
+
host: env.COLLECTOR_HOST ?? "0.0.0.0",
|
|
32
|
+
port: Number(env.COLLECTOR_PORT ?? 4318),
|
|
33
|
+
corsOrigins: (env.COLLECTOR_CORS_ORIGINS ?? "")
|
|
34
|
+
.split(",")
|
|
35
|
+
.map((s) => s.trim())
|
|
36
|
+
.filter(Boolean),
|
|
37
|
+
visitorHashSecret,
|
|
38
|
+
enableRawSessionRetention: bool(env.ENABLE_RAW_SESSION_RETENTION),
|
|
39
|
+
liveWindowMs: Number(env.LIVE_WINDOW_MS ?? 30_000),
|
|
40
|
+
liveTokenSecret: env.LIVE_TOKEN_SECRET ?? visitorHashSecret,
|
|
41
|
+
liveTokenSecretIsDedicated,
|
|
42
|
+
liveTokenTtlMs: Number(env.LIVE_TOKEN_TTL_MS ?? 900_000),
|
|
43
|
+
liveMaxConnections: Number(env.LIVE_MAX_CONNECTIONS ?? 200),
|
|
44
|
+
livePresenceIntervalMs: Number(env.LIVE_PRESENCE_INTERVAL_MS ?? 2_000),
|
|
45
|
+
rateLimitMax: Number(env.COLLECTOR_RATE_LIMIT_MAX ?? 600),
|
|
46
|
+
rateLimitWindowMs: Number(env.COLLECTOR_RATE_LIMIT_WINDOW_MS ?? 60_000),
|
|
47
|
+
ingestRateLimitMax: Number(env.COLLECTOR_INGEST_RATE_LIMIT_MAX ?? 300),
|
|
48
|
+
ingestRateLimitWindowMs: Number(env.COLLECTOR_INGEST_RATE_LIMIT_WINDOW_MS ?? 60_000),
|
|
49
|
+
trustProxy: parseTrustProxy(env.COLLECTOR_TRUST_PROXY),
|
|
50
|
+
bodyLimit: Number(env.COLLECTOR_BODY_LIMIT ?? 1_048_576),
|
|
51
|
+
cspMode: env.COLLECTOR_CSP === "off" ? "off" : "strict",
|
|
52
|
+
dashboardDir: env.COLLECTOR_DASHBOARD_DIR ? resolve(env.COLLECTOR_DASHBOARD_DIR) : undefined,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/csp.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect the `'sha256-...'` sources for every inline script in the exported
|
|
3
|
+
* dashboard's HTML files. Reads `*.html` at the export root (the SPA entry plus
|
|
4
|
+
* Next's `404.html` / `_not-found.html`), so any page the host might serve is
|
|
5
|
+
* covered. Best-effort: unreadable files are skipped.
|
|
6
|
+
*/
|
|
7
|
+
export declare function dashboardScriptHashes(dashboardDir: string): string[];
|
|
8
|
+
/**
|
|
9
|
+
* Build the helmet `contentSecurityPolicy` directives for the bundled dashboard.
|
|
10
|
+
* `connectOrigins` (the configured CORS origins) are added to `connect-src` so a
|
|
11
|
+
* dashboard served from a different origin than the collector can still reach the
|
|
12
|
+
* query + live-SSE API.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildDashboardCsp(dashboardDir: string, connectOrigins: readonly string[]): {
|
|
15
|
+
useDefaults: false;
|
|
16
|
+
directives: Record<string, string[]>;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=csp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.d.ts","sourceRoot":"","sources":["../src/csp.ts"],"names":[],"mappings":"AAsCA;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBpE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,SAAS,MAAM,EAAE,GAChC;IAAE,WAAW,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CAAE,CAqB9D"}
|
package/dist/csp.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Content-Security-Policy for the bundled static dashboard.
|
|
6
|
+
*
|
|
7
|
+
* A Next.js static export (`output: "export"`) ships a handful of **inline**
|
|
8
|
+
* bootstrap `<script>` tags. A strict CSP normally allows inline scripts with a
|
|
9
|
+
* per-response *nonce*, but a static export has no server to mint one — the
|
|
10
|
+
* files are byte-for-byte identical on every request. The serving layer (here,
|
|
11
|
+
* or the standalone dashboard server) must therefore allow those exact scripts
|
|
12
|
+
* by their **SHA-256 hash** instead. We enumerate every inline script across the
|
|
13
|
+
* exported HTML at startup and pin its hash, so `script-src` stays free of the
|
|
14
|
+
* blanket `'unsafe-inline'` escape hatch while the app still boots.
|
|
15
|
+
*
|
|
16
|
+
* Everything else is locked down: no plugins (`object-src 'none'`), no framing
|
|
17
|
+
* (`frame-ancestors 'none'`), self-only base URI, and same-origin defaults.
|
|
18
|
+
*/
|
|
19
|
+
/** Extract the text of every inline (`src`-less) `<script>` in an HTML document. */
|
|
20
|
+
function inlineScripts(html) {
|
|
21
|
+
const out = [];
|
|
22
|
+
const re = /<script(?![^>]*\bsrc=)[^>]*>([\s\S]*?)<\/script>/gi;
|
|
23
|
+
let match;
|
|
24
|
+
while ((match = re.exec(html)) !== null) {
|
|
25
|
+
const body = match[1] ?? "";
|
|
26
|
+
if (body.length > 0)
|
|
27
|
+
out.push(body);
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
/** CSP `'sha256-...'` source token for an inline script body. */
|
|
32
|
+
function sha256Source(scriptBody) {
|
|
33
|
+
const digest = createHash("sha256").update(scriptBody, "utf8").digest("base64");
|
|
34
|
+
return `'sha256-${digest}'`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Collect the `'sha256-...'` sources for every inline script in the exported
|
|
38
|
+
* dashboard's HTML files. Reads `*.html` at the export root (the SPA entry plus
|
|
39
|
+
* Next's `404.html` / `_not-found.html`), so any page the host might serve is
|
|
40
|
+
* covered. Best-effort: unreadable files are skipped.
|
|
41
|
+
*/
|
|
42
|
+
export function dashboardScriptHashes(dashboardDir) {
|
|
43
|
+
const hashes = new Set();
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = readdirSync(dashboardDir).filter((f) => f.endsWith(".html"));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
for (const file of entries) {
|
|
52
|
+
try {
|
|
53
|
+
const html = readFileSync(join(dashboardDir, file), "utf8");
|
|
54
|
+
for (const body of inlineScripts(html))
|
|
55
|
+
hashes.add(sha256Source(body));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Skip files we cannot read; the remaining hashes still apply.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return [...hashes];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build the helmet `contentSecurityPolicy` directives for the bundled dashboard.
|
|
65
|
+
* `connectOrigins` (the configured CORS origins) are added to `connect-src` so a
|
|
66
|
+
* dashboard served from a different origin than the collector can still reach the
|
|
67
|
+
* query + live-SSE API.
|
|
68
|
+
*/
|
|
69
|
+
export function buildDashboardCsp(dashboardDir, connectOrigins) {
|
|
70
|
+
const scriptSrc = ["'self'", ...dashboardScriptHashes(dashboardDir)];
|
|
71
|
+
const connectSrc = ["'self'", ...connectOrigins];
|
|
72
|
+
return {
|
|
73
|
+
useDefaults: false,
|
|
74
|
+
directives: {
|
|
75
|
+
defaultSrc: ["'self'"],
|
|
76
|
+
baseUri: ["'self'"],
|
|
77
|
+
objectSrc: ["'none'"],
|
|
78
|
+
frameAncestors: ["'none'"],
|
|
79
|
+
formAction: ["'self'"],
|
|
80
|
+
scriptSrc,
|
|
81
|
+
// Static export + Babylon panels apply styles the framework injects inline;
|
|
82
|
+
// inline styles are far lower-risk than scripts, so allow them here.
|
|
83
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
84
|
+
imgSrc: ["'self'", "data:", "blob:"],
|
|
85
|
+
fontSrc: ["'self'", "data:"],
|
|
86
|
+
workerSrc: ["'self'", "blob:"],
|
|
87
|
+
connectSrc,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=csp.js.map
|
package/dist/csp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csp.js","sourceRoot":"","sources":["../src/csp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;;;;;;GAcG;AAEH,oFAAoF;AACpF,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,oDAAoD,CAAC;IAChE,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iEAAiE;AACjE,SAAS,YAAY,CAAC,UAAkB;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChF,OAAO,WAAW,MAAM,GAAG,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,YAAoB;IACxD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAC5D,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;QACjE,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,YAAoB,EACpB,cAAiC;IAEjC,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,GAAG,cAAc,CAAC,CAAC;IACjD,OAAO;QACL,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE;YACV,UAAU,EAAE,CAAC,QAAQ,CAAC;YACtB,OAAO,EAAE,CAAC,QAAQ,CAAC;YACnB,SAAS,EAAE,CAAC,QAAQ,CAAC;YACrB,cAAc,EAAE,CAAC,QAAQ,CAAC;YAC1B,UAAU,EAAE,CAAC,QAAQ,CAAC;YACtB,SAAS;YACT,4EAA4E;YAC5E,qEAAqE;YACrE,QAAQ,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC;YACvC,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC;YACpC,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;YAC5B,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;YAC9B,UAAU;SACX;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CollectorStore } from "./store.js";
|
|
2
|
+
/**
|
|
3
|
+
* OSS default store (ADR 0020): a single persisted DuckDB file holding both
|
|
4
|
+
* events and metadata, with the **full** analytics surface — no stubbed
|
|
5
|
+
* aggregates. In-process and zero-service, so a self-hosted collector needs no
|
|
6
|
+
* external database. Every aggregation is the dialect-agnostic builder rendered
|
|
7
|
+
* with {@link duckdbDialect}; metadata (projects, API keys, scene registry) is
|
|
8
|
+
* re-homed into the same file.
|
|
9
|
+
*
|
|
10
|
+
* The schema is migrated on creation (migrations are idempotent), so the store
|
|
11
|
+
* is usable out of the box. Pass `":memory:"` for an ephemeral store (tests).
|
|
12
|
+
*/
|
|
13
|
+
export declare function createDuckdbStore(path?: string): Promise<CollectorStore>;
|
|
14
|
+
//# sourceMappingURL=duckdbStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duckdbStore.d.ts","sourceRoot":"","sources":["../src/duckdbStore.ts"],"names":[],"mappings":"AA0FA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA0H9E"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { buildCameraDirectionHeatmap, buildCameraDistance, buildCameraPositionHeatmap, buildClickGazeRay, buildDeadClicks, buildRageClicks, buildHoverDwell, buildCompileStalls, buildResourceSummary, buildCapabilityChanges, buildCameraGestures, buildDistinctScenes, buildEventTypeCounts, buildFlowHeatmap, buildListSessions, buildNavigationStats, buildXrRotationRate, buildXrSourceUsage, buildXrAbandonment, buildInteractionsBySource, buildMeshDwell, buildPerfSummary, buildPerfDistribution, buildFpsHistogram, buildFrameTimePercentiles, buildJankRate, buildPerfByDevice, buildPerfByScene, buildResourcePercentiles, buildStabilityCounts, buildPointerHeatmap, buildSceneCoverage, buildSessionTrajectory, buildTimeseries, buildTopMeshes, buildWorldHeatmap, buildGazeHeatmap, createDuckdbClient, duckdbDialect, duckdbGetSceneRepresentation, duckdbGetSessionEvents, duckdbGetSessionMeta, duckdbGetProject, duckdbInsertEvents, duckdbListSceneRepresentations, duckdbResolveApiKey, duckdbStreamSessionEvents, duckdbUpsertSceneProxy, migrateDuckdb, readDbSettings, runDuckdbQuery, } from "@uptimizr/db";
|
|
2
|
+
/**
|
|
3
|
+
* OSS default store (ADR 0020): a single persisted DuckDB file holding both
|
|
4
|
+
* events and metadata, with the **full** analytics surface — no stubbed
|
|
5
|
+
* aggregates. In-process and zero-service, so a self-hosted collector needs no
|
|
6
|
+
* external database. Every aggregation is the dialect-agnostic builder rendered
|
|
7
|
+
* with {@link duckdbDialect}; metadata (projects, API keys, scene registry) is
|
|
8
|
+
* re-homed into the same file.
|
|
9
|
+
*
|
|
10
|
+
* The schema is migrated on creation (migrations are idempotent), so the store
|
|
11
|
+
* is usable out of the box. Pass `":memory:"` for an ephemeral store (tests).
|
|
12
|
+
*/
|
|
13
|
+
export async function createDuckdbStore(path) {
|
|
14
|
+
const filePath = path ?? readDbSettings().duckdb.path;
|
|
15
|
+
const db = await createDuckdbClient(filePath);
|
|
16
|
+
await migrateDuckdb(db);
|
|
17
|
+
return {
|
|
18
|
+
resolveApiKey: (key) => duckdbResolveApiKey(db, key),
|
|
19
|
+
projectExists: async (projectId) => (await duckdbGetProject(db, projectId)) !== null,
|
|
20
|
+
insertEvents: (events) => duckdbInsertEvents(db, [...events]),
|
|
21
|
+
listSessions: (projectId, opts = {}) => runDuckdbQuery(db, buildListSessions(projectId, opts, duckdbDialect)),
|
|
22
|
+
pointerHeatmap: (projectId, opts = {}) => runDuckdbQuery(db, buildPointerHeatmap(projectId, opts, duckdbDialect)),
|
|
23
|
+
worldHeatmap: (projectId, opts = {}) => runDuckdbQuery(db, buildWorldHeatmap(projectId, opts, duckdbDialect)),
|
|
24
|
+
gazeHeatmap: (projectId, opts = {}) => runDuckdbQuery(db, buildGazeHeatmap(projectId, opts, duckdbDialect)),
|
|
25
|
+
cameraHeatmap: (projectId, opts = {}) => runDuckdbQuery(db, buildCameraDirectionHeatmap(projectId, opts, duckdbDialect)),
|
|
26
|
+
cameraPositionHeatmap: (projectId, opts = {}) => runDuckdbQuery(db, buildCameraPositionHeatmap(projectId, opts, duckdbDialect)),
|
|
27
|
+
sessionTrajectory: (projectId, sessionId, opts = {}) => runDuckdbQuery(db, buildSessionTrajectory(projectId, { ...opts, session: sessionId }, duckdbDialect)),
|
|
28
|
+
clickGazeRays: (projectId, opts = {}) => runDuckdbQuery(db, buildClickGazeRay(projectId, opts, duckdbDialect)),
|
|
29
|
+
flowHeatmap: (projectId, opts = {}) => runDuckdbQuery(db, buildFlowHeatmap(projectId, opts, duckdbDialect)),
|
|
30
|
+
topMeshes: (projectId, opts = {}) => runDuckdbQuery(db, buildTopMeshes(projectId, opts, duckdbDialect)),
|
|
31
|
+
meshDwell: (projectId, opts = {}) => runDuckdbQuery(db, buildMeshDwell(projectId, opts, duckdbDialect)),
|
|
32
|
+
deadClicks: (projectId, opts = {}) => runDuckdbQuery(db, buildDeadClicks(projectId, opts, duckdbDialect)),
|
|
33
|
+
rageClicks: (projectId, opts = {}) => runDuckdbQuery(db, buildRageClicks(projectId, opts, duckdbDialect)),
|
|
34
|
+
hoverDwell: (projectId, opts = {}) => runDuckdbQuery(db, buildHoverDwell(projectId, opts, duckdbDialect)),
|
|
35
|
+
compileStalls: (projectId, opts = {}) => runDuckdbQuery(db, buildCompileStalls(projectId, opts, duckdbDialect)),
|
|
36
|
+
resourceSummary: (projectId, opts = {}) => runDuckdbQuery(db, buildResourceSummary(projectId, opts, duckdbDialect)),
|
|
37
|
+
capabilityChanges: (projectId, opts = {}) => runDuckdbQuery(db, buildCapabilityChanges(projectId, opts, duckdbDialect)),
|
|
38
|
+
cameraGestures: (projectId, opts = {}) => runDuckdbQuery(db, buildCameraGestures(projectId, opts, duckdbDialect)),
|
|
39
|
+
perfSummary: (projectId, opts = {}) => runDuckdbQuery(db, buildPerfSummary(projectId, opts, duckdbDialect)),
|
|
40
|
+
perfDistribution: (projectId, opts = {}) => runDuckdbQuery(db, buildPerfDistribution(projectId, opts, duckdbDialect)),
|
|
41
|
+
fpsHistogram: (projectId, opts = {}) => runDuckdbQuery(db, buildFpsHistogram(projectId, opts, duckdbDialect)),
|
|
42
|
+
frameTimePercentiles: (projectId, opts = {}) => runDuckdbQuery(db, buildFrameTimePercentiles(projectId, opts, duckdbDialect)),
|
|
43
|
+
jankRate: (projectId, opts = {}) => runDuckdbQuery(db, buildJankRate(projectId, opts, duckdbDialect)),
|
|
44
|
+
perfByDevice: (projectId, opts = {}) => runDuckdbQuery(db, buildPerfByDevice(projectId, opts, duckdbDialect)),
|
|
45
|
+
perfByScene: (projectId, opts = {}) => runDuckdbQuery(db, buildPerfByScene(projectId, opts, duckdbDialect)),
|
|
46
|
+
resourcePercentiles: (projectId, opts = {}) => runDuckdbQuery(db, buildResourcePercentiles(projectId, opts, duckdbDialect)),
|
|
47
|
+
stabilityCounts: (projectId, opts = {}) => runDuckdbQuery(db, buildStabilityCounts(projectId, opts, duckdbDialect)),
|
|
48
|
+
sceneCoverage: (projectId, opts = {}) => runDuckdbQuery(db, buildSceneCoverage(projectId, opts, duckdbDialect)),
|
|
49
|
+
cameraDistance: (projectId, opts = {}) => runDuckdbQuery(db, buildCameraDistance(projectId, opts, duckdbDialect)),
|
|
50
|
+
navigationStats: (projectId, opts = {}) => runDuckdbQuery(db, buildNavigationStats(projectId, opts, duckdbDialect)),
|
|
51
|
+
xrRotationRate: (projectId, opts = {}) => runDuckdbQuery(db, buildXrRotationRate(projectId, opts, duckdbDialect)),
|
|
52
|
+
xrSourceUsage: (projectId, opts = {}) => runDuckdbQuery(db, buildXrSourceUsage(projectId, opts, duckdbDialect)),
|
|
53
|
+
xrAbandonment: (projectId, opts = {}) => runDuckdbQuery(db, buildXrAbandonment(projectId, opts, duckdbDialect)),
|
|
54
|
+
interactionsBySource: (projectId, opts = {}) => runDuckdbQuery(db, buildInteractionsBySource(projectId, opts, duckdbDialect)),
|
|
55
|
+
scenes: (projectId, opts = {}) => runDuckdbQuery(db, buildDistinctScenes(projectId, opts, duckdbDialect)),
|
|
56
|
+
timeseries: (projectId, opts = {}) => runDuckdbQuery(db, buildTimeseries(projectId, opts, duckdbDialect)),
|
|
57
|
+
eventTypeCounts: (projectId, opts = {}) => runDuckdbQuery(db, buildEventTypeCounts(projectId, opts, duckdbDialect)),
|
|
58
|
+
getSessionEvents: (projectId, sessionId) => duckdbGetSessionEvents(db, projectId, sessionId),
|
|
59
|
+
streamSessionEvents: (projectId, sessionId) => duckdbStreamSessionEvents(db, projectId, sessionId),
|
|
60
|
+
getSessionMeta: (projectId, sessionId) => duckdbGetSessionMeta(db, projectId, sessionId),
|
|
61
|
+
putSceneProxy: (projectId, proxy, label) => duckdbUpsertSceneProxy(db, projectId, proxy, label),
|
|
62
|
+
getSceneRepresentation: (projectId, sceneId) => duckdbGetSceneRepresentation(db, projectId, sceneId),
|
|
63
|
+
listSceneRepresentations: (projectId) => duckdbListSceneRepresentations(db, projectId),
|
|
64
|
+
async close() {
|
|
65
|
+
await db.close();
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=duckdbStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duckdbStore.js","sourceRoot":"","sources":["../src/duckdbStore.ts"],"names":[],"mappings":"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"}
|
package/dist/enrich.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AnyEvent } from "@uptimizr/schema";
|
|
2
|
+
/**
|
|
3
|
+
* Server-authoritative enrichment: stamp every event with the derived cookieless
|
|
4
|
+
* `visitorId`, overriding anything the client may have sent. Order is preserved
|
|
5
|
+
* so the stored stream stays replay-complete.
|
|
6
|
+
*/
|
|
7
|
+
export declare function enrichEvents(events: readonly AnyEvent[], visitorId: string): AnyEvent[];
|
|
8
|
+
//# sourceMappingURL=enrich.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich.d.ts","sourceRoot":"","sources":["../src/enrich.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,EAAE,CAEvF"}
|
package/dist/enrich.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-authoritative enrichment: stamp every event with the derived cookieless
|
|
3
|
+
* `visitorId`, overriding anything the client may have sent. Order is preserved
|
|
4
|
+
* so the stored stream stays replay-complete.
|
|
5
|
+
*/
|
|
6
|
+
export function enrichEvents(events, visitorId) {
|
|
7
|
+
return events.map((event) => ({ ...event, visitorId }));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=enrich.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich.js","sourceRoot":"","sources":["../src/enrich.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAA2B,EAAE,SAAiB;IACzE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { AnyEvent } from "@uptimizr/schema";
|
|
2
|
+
/**
|
|
3
|
+
* In-process live event bus + presence tracker (ADR 0032 §1, §2, §3a, §6).
|
|
4
|
+
*
|
|
5
|
+
* The collector is a single process (DuckDB single-writer, ADR 0020), so every
|
|
6
|
+
* ingested event passes through one place. The ingest route publishes each
|
|
7
|
+
* enriched, validated event here; live consumers (SSE endpoints, ADR §3) read
|
|
8
|
+
* from it. This is the OSS fan-out source — a multi-instance scale tier
|
|
9
|
+
* supplies its own shared-bus implementation behind the same interface
|
|
10
|
+
* (ADR 0004/0020) without touching `oss/**`.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here is persisted: presence is a rolling in-memory view and the
|
|
13
|
+
* per-session backfill ring is bounded. Privacy: the roster exposed to clients
|
|
14
|
+
* is non-identifying (no geo/UA/visitorId — ADR §3a); `visitorId` is used only
|
|
15
|
+
* internally to count distinct active visitors.
|
|
16
|
+
*/
|
|
17
|
+
/** Coarse, non-identifying recency bucket for a live session (ADR §3a). */
|
|
18
|
+
export type ActivityLevel = "active" | "recent" | "idle";
|
|
19
|
+
/** A single non-identifying roster entry surfaced to clients (ADR §3a). */
|
|
20
|
+
export interface PresenceRosterItem {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
sceneId: string;
|
|
23
|
+
/** Server receive time of the session's first seen event (epoch ms). */
|
|
24
|
+
startedAt: number;
|
|
25
|
+
/** Server receive time of the session's most recent event (epoch ms). */
|
|
26
|
+
lastSeen: number;
|
|
27
|
+
/** Coarse recency bucket derived from `lastSeen`. */
|
|
28
|
+
activity: ActivityLevel;
|
|
29
|
+
}
|
|
30
|
+
/** Aggregate live snapshot for a project (ADR §3). */
|
|
31
|
+
export interface PresenceSnapshot {
|
|
32
|
+
/** Distinct live sessions within the window. */
|
|
33
|
+
activeSessions: number;
|
|
34
|
+
/** Distinct live visitors within the window. */
|
|
35
|
+
activeVisitors: number;
|
|
36
|
+
/** Non-identifying roster, most-recently-active first. */
|
|
37
|
+
sessions: PresenceRosterItem[];
|
|
38
|
+
}
|
|
39
|
+
/** Options for a live subscription (used by the SSE endpoints — ADR §3). */
|
|
40
|
+
export interface LiveSubscribeOptions {
|
|
41
|
+
projectId: string;
|
|
42
|
+
/** Restrict to one session (live-follow tail); omit for the project firehose. */
|
|
43
|
+
sessionId?: string;
|
|
44
|
+
/** Optional event-type allow-list. */
|
|
45
|
+
types?: ReadonlySet<string>;
|
|
46
|
+
/** Per-subscriber bounded queue size; overrides the bus default. */
|
|
47
|
+
queueLimit?: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A bounded, drop-oldest async stream of events (ADR §6). A slow consumer loses
|
|
51
|
+
* its oldest buffered events (counted in {@link dropped}); it never back-pressures
|
|
52
|
+
* ingest or grows memory without bound.
|
|
53
|
+
*/
|
|
54
|
+
export interface LiveSubscriber extends AsyncIterable<AnyEvent> {
|
|
55
|
+
/** Close the subscription and release it from the bus. Idempotent. */
|
|
56
|
+
close(): void;
|
|
57
|
+
/** Count of events dropped due to a full queue. */
|
|
58
|
+
readonly dropped: number;
|
|
59
|
+
}
|
|
60
|
+
export interface LiveBus {
|
|
61
|
+
/** Publish enriched, validated events (called from the ingest route). */
|
|
62
|
+
publish(events: readonly AnyEvent[]): void;
|
|
63
|
+
/** Current aggregate + roster snapshot for a project (ADR §3). */
|
|
64
|
+
presence(projectId: string): PresenceSnapshot;
|
|
65
|
+
/** Recent buffered events for one session, oldest first (connect-time backfill). */
|
|
66
|
+
recentForSession(projectId: string, sessionId: string): AnyEvent[];
|
|
67
|
+
/** Open a bounded live subscription (ADR §6). */
|
|
68
|
+
subscribe(options: LiveSubscribeOptions): LiveSubscriber;
|
|
69
|
+
/** Number of open subscriptions (for connection caps — ADR §6). */
|
|
70
|
+
readonly subscriberCount: number;
|
|
71
|
+
/** Release timers/subscribers. */
|
|
72
|
+
stop(): void;
|
|
73
|
+
}
|
|
74
|
+
export interface CreateLiveBusOptions {
|
|
75
|
+
/** Liveness window in ms (ADR §1, default 30_000). Must be ≥ SDK flush cadence. */
|
|
76
|
+
windowMs?: number;
|
|
77
|
+
/** Per-session backfill ring size (ADR §3, default 200). */
|
|
78
|
+
backfillRingSize?: number;
|
|
79
|
+
/** Default per-subscriber queue size (ADR §6, default 1_000). */
|
|
80
|
+
subscriberQueueLimit?: number;
|
|
81
|
+
/** Clock injection for tests; defaults to `Date.now`. */
|
|
82
|
+
now?: () => number;
|
|
83
|
+
}
|
|
84
|
+
/** Create the default in-process live bus (ADR 0032 §2). */
|
|
85
|
+
export declare function createLiveBus(options?: CreateLiveBusOptions): LiveBus;
|
|
86
|
+
//# sourceMappingURL=liveBus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveBus.d.ts","sourceRoot":"","sources":["../src/liveBus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGjD;;;;;;;;;;;;;;GAcG;AAEH,2EAA2E;AAC3E,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEzD,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,cAAc,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,cAAc,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa,CAAC,QAAQ,CAAC;IAC7D,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;IACd,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,OAAO;IACtB,yEAAyE;IACzE,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC9C,oFAAoF;IACpF,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;IACnE,iDAAiD;IACjD,SAAS,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAAC;IACzD,mEAAmE;IACnE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,kCAAkC;IAClC,IAAI,IAAI,IAAI,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAsED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAsIzE"}
|