@mongrov/analytics 0.1.0-alpha.0 → 0.1.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +456 -0
- package/dist/core/context.d.ts +24 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +30 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/engine.d.ts +56 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +119 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/extensions.d.ts +33 -0
- package/dist/core/extensions.d.ts.map +1 -0
- package/dist/core/extensions.js +58 -0
- package/dist/core/extensions.js.map +1 -0
- package/dist/core/factory.d.ts +22 -5
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +231 -6
- package/dist/core/factory.js.map +1 -1
- package/dist/core/hooks.d.ts +32 -13
- package/dist/core/hooks.d.ts.map +1 -1
- package/dist/core/hooks.js +137 -15
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/logger.d.ts +25 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +33 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/machine.d.ts +174 -0
- package/dist/core/machine.d.ts.map +1 -0
- package/dist/core/machine.js +228 -0
- package/dist/core/machine.js.map +1 -0
- package/dist/core/metric_metadata.d.ts +128 -0
- package/dist/core/metric_metadata.d.ts.map +1 -0
- package/dist/core/metric_metadata.js +47 -0
- package/dist/core/metric_metadata.js.map +1 -0
- package/dist/core/migrations.d.ts +52 -0
- package/dist/core/migrations.d.ts.map +1 -0
- package/dist/core/migrations.js +66 -0
- package/dist/core/migrations.js.map +1 -0
- package/dist/core/persistence.d.ts +50 -0
- package/dist/core/persistence.d.ts.map +1 -0
- package/dist/core/persistence.js +80 -0
- package/dist/core/persistence.js.map +1 -0
- package/dist/core/retention.d.ts +90 -0
- package/dist/core/retention.d.ts.map +1 -0
- package/dist/core/retention.js +119 -0
- package/dist/core/retention.js.map +1 -0
- package/dist/core/schemas.d.ts +35 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +209 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/warehouse.d.ts +58 -0
- package/dist/core/warehouse.d.ts.map +1 -0
- package/dist/core/warehouse.js +116 -0
- package/dist/core/warehouse.js.map +1 -0
- package/dist/sync/buffer.d.ts +51 -0
- package/dist/sync/buffer.d.ts.map +1 -0
- package/dist/sync/buffer.js +188 -0
- package/dist/sync/buffer.js.map +1 -0
- package/dist/sync/context.d.ts +17 -0
- package/dist/sync/context.d.ts.map +1 -0
- package/dist/sync/context.js +23 -0
- package/dist/sync/context.js.map +1 -0
- package/dist/sync/errors.d.ts +19 -0
- package/dist/sync/errors.d.ts.map +1 -0
- package/dist/sync/errors.js +21 -0
- package/dist/sync/errors.js.map +1 -0
- package/dist/sync/events.d.ts +49 -0
- package/dist/sync/events.d.ts.map +1 -0
- package/dist/sync/events.js +46 -0
- package/dist/sync/events.js.map +1 -0
- package/dist/sync/factory.d.ts +57 -0
- package/dist/sync/factory.d.ts.map +1 -0
- package/dist/sync/factory.js +249 -0
- package/dist/sync/factory.js.map +1 -0
- package/dist/sync/fetcher.d.ts +74 -0
- package/dist/sync/fetcher.d.ts.map +1 -0
- package/dist/sync/fetcher.js +166 -0
- package/dist/sync/fetcher.js.map +1 -0
- package/dist/sync/flusher.d.ts +93 -0
- package/dist/sync/flusher.d.ts.map +1 -0
- package/dist/sync/flusher.js +234 -0
- package/dist/sync/flusher.js.map +1 -0
- package/dist/sync/hooks.d.ts +17 -0
- package/dist/sync/hooks.d.ts.map +1 -0
- package/dist/sync/hooks.js +48 -0
- package/dist/sync/hooks.js.map +1 -0
- package/dist/sync/index.d.ts +32 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +36 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/manager.d.ts +48 -0
- package/dist/sync/manager.d.ts.map +1 -0
- package/dist/sync/manager.js +9 -0
- package/dist/sync/manager.js.map +1 -0
- package/dist/sync/mapper/activity.d.ts +31 -0
- package/dist/sync/mapper/activity.d.ts.map +1 -0
- package/dist/sync/mapper/activity.js +56 -0
- package/dist/sync/mapper/activity.js.map +1 -0
- package/dist/sync/mapper/battery.d.ts +12 -0
- package/dist/sync/mapper/battery.d.ts.map +1 -0
- package/dist/sync/mapper/battery.js +22 -0
- package/dist/sync/mapper/battery.js.map +1 -0
- package/dist/sync/mapper/firmware.d.ts +28 -0
- package/dist/sync/mapper/firmware.d.ts.map +1 -0
- package/dist/sync/mapper/firmware.js +43 -0
- package/dist/sync/mapper/firmware.js.map +1 -0
- package/dist/sync/mapper/heart-rate.d.ts +10 -0
- package/dist/sync/mapper/heart-rate.d.ts.map +1 -0
- package/dist/sync/mapper/heart-rate.js +19 -0
- package/dist/sync/mapper/heart-rate.js.map +1 -0
- package/dist/sync/mapper/hrv.d.ts +22 -0
- package/dist/sync/mapper/hrv.d.ts.map +1 -0
- package/dist/sync/mapper/hrv.js +56 -0
- package/dist/sync/mapper/hrv.js.map +1 -0
- package/dist/sync/mapper/ring-config.d.ts +33 -0
- package/dist/sync/mapper/ring-config.d.ts.map +1 -0
- package/dist/sync/mapper/ring-config.js +48 -0
- package/dist/sync/mapper/ring-config.js.map +1 -0
- package/dist/sync/mapper/sleep.d.ts +32 -0
- package/dist/sync/mapper/sleep.d.ts.map +1 -0
- package/dist/sync/mapper/sleep.js +103 -0
- package/dist/sync/mapper/sleep.js.map +1 -0
- package/dist/sync/mapper/spo2.d.ts +9 -0
- package/dist/sync/mapper/spo2.d.ts.map +1 -0
- package/dist/sync/mapper/spo2.js +18 -0
- package/dist/sync/mapper/spo2.js.map +1 -0
- package/dist/sync/mapper/temperature.d.ts +10 -0
- package/dist/sync/mapper/temperature.d.ts.map +1 -0
- package/dist/sync/mapper/temperature.js +19 -0
- package/dist/sync/mapper/temperature.js.map +1 -0
- package/dist/sync/mapper/time.d.ts +49 -0
- package/dist/sync/mapper/time.d.ts.map +1 -0
- package/dist/sync/mapper/time.js +107 -0
- package/dist/sync/mapper/time.js.map +1 -0
- package/dist/sync/mapper/types.d.ts +152 -0
- package/dist/sync/mapper/types.d.ts.map +1 -0
- package/dist/sync/mapper/types.js +15 -0
- package/dist/sync/mapper/types.js.map +1 -0
- package/dist/sync/overflow.d.ts +30 -0
- package/dist/sync/overflow.d.ts.map +1 -0
- package/dist/sync/overflow.js +114 -0
- package/dist/sync/overflow.js.map +1 -0
- package/dist/sync/pusher.d.ts +59 -0
- package/dist/sync/pusher.d.ts.map +1 -0
- package/dist/sync/pusher.js +115 -0
- package/dist/sync/pusher.js.map +1 -0
- package/dist/sync/scheduler.d.ts +84 -0
- package/dist/sync/scheduler.d.ts.map +1 -0
- package/dist/sync/scheduler.js +132 -0
- package/dist/sync/scheduler.js.map +1 -0
- package/dist/sync/triggers.d.ts +64 -0
- package/dist/sync/triggers.d.ts.map +1 -0
- package/dist/sync/triggers.js +113 -0
- package/dist/sync/triggers.js.map +1 -0
- package/dist/sync/types.d.ts +50 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +8 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/sync/watermark.d.ts +32 -0
- package/dist/sync/watermark.d.ts.map +1 -0
- package/dist/sync/watermark.js +61 -0
- package/dist/sync/watermark.js.map +1 -0
- package/package.json +27 -16
- package/LICENSE +0 -21
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around a native DuckDB instance.
|
|
3
|
+
*
|
|
4
|
+
* Owns lifecycle (`open`/`close`), maps native errors into the analytics
|
|
5
|
+
* error taxonomy, and normalises `execute`/`stream`/`createAppender` so the
|
|
6
|
+
* rest of the package doesn't touch `react-native-duckdb` directly.
|
|
7
|
+
*
|
|
8
|
+
* We depend on `react-native-duckdb` only structurally (via the
|
|
9
|
+
* `DuckDBInstance` interface below) so the package builds and tests without
|
|
10
|
+
* the native module installed. The public factory (T-10) supplies a real
|
|
11
|
+
* instance via a `DuckDBFactory`; test suites supply fakes.
|
|
12
|
+
*/
|
|
13
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
14
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
15
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
16
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
17
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
18
|
+
};
|
|
19
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
20
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
21
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
22
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
23
|
+
};
|
|
24
|
+
var _HybridDuckDB_instances, _HybridDuckDB_factory, _HybridDuckDB_instance, _HybridDuckDB_closed, _HybridDuckDB_ready;
|
|
25
|
+
import { AnalyticsError } from './errors';
|
|
26
|
+
/**
|
|
27
|
+
* Owns the DuckDB connection lifecycle.
|
|
28
|
+
*
|
|
29
|
+
* - `open()` is idempotent: repeated calls after a successful open are no-ops.
|
|
30
|
+
* - `close()` is idempotent and terminal — subsequent `execute`/`stream`/
|
|
31
|
+
* `createAppender` throw `not_ready`.
|
|
32
|
+
* - `execute()` returns a full row set; `stream()` yields pages of rows
|
|
33
|
+
* (recommended page size `HybridDuckDB.PAGE_SIZE`, applied by the native
|
|
34
|
+
* driver; the wrapper just forwards).
|
|
35
|
+
* - Any native throw is wrapped in `AnalyticsError`.
|
|
36
|
+
*/
|
|
37
|
+
export class HybridDuckDB {
|
|
38
|
+
constructor(factory) {
|
|
39
|
+
_HybridDuckDB_instances.add(this);
|
|
40
|
+
_HybridDuckDB_factory.set(this, void 0);
|
|
41
|
+
_HybridDuckDB_instance.set(this, void 0);
|
|
42
|
+
_HybridDuckDB_closed.set(this, false);
|
|
43
|
+
__classPrivateFieldSet(this, _HybridDuckDB_factory, factory, "f");
|
|
44
|
+
}
|
|
45
|
+
/** Whether `open()` has completed successfully and `close()` has not been called. */
|
|
46
|
+
get isOpen() {
|
|
47
|
+
return __classPrivateFieldGet(this, _HybridDuckDB_instance, "f") !== undefined && !__classPrivateFieldGet(this, _HybridDuckDB_closed, "f");
|
|
48
|
+
}
|
|
49
|
+
async open() {
|
|
50
|
+
if (__classPrivateFieldGet(this, _HybridDuckDB_closed, "f")) {
|
|
51
|
+
throw new AnalyticsError('not_ready', 'HybridDuckDB has been closed');
|
|
52
|
+
}
|
|
53
|
+
if (__classPrivateFieldGet(this, _HybridDuckDB_instance, "f")) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
__classPrivateFieldSet(this, _HybridDuckDB_instance, await __classPrivateFieldGet(this, _HybridDuckDB_factory, "f").call(this), "f");
|
|
58
|
+
}
|
|
59
|
+
catch (cause) {
|
|
60
|
+
throw new AnalyticsError('engine_open_failed', 'Failed to open DuckDB instance', cause);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async execute(sql, params) {
|
|
64
|
+
const inst = __classPrivateFieldGet(this, _HybridDuckDB_instances, "m", _HybridDuckDB_ready).call(this);
|
|
65
|
+
try {
|
|
66
|
+
const rows = await inst.execute(sql, params);
|
|
67
|
+
return rows;
|
|
68
|
+
}
|
|
69
|
+
catch (cause) {
|
|
70
|
+
throw new AnalyticsError('query_failed', `execute failed: ${previewSql(sql)}`, cause);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async *stream(sql, params) {
|
|
74
|
+
const inst = __classPrivateFieldGet(this, _HybridDuckDB_instances, "m", _HybridDuckDB_ready).call(this);
|
|
75
|
+
try {
|
|
76
|
+
for await (const page of inst.stream(sql, params)) {
|
|
77
|
+
yield page;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (cause) {
|
|
81
|
+
throw new AnalyticsError('query_failed', `stream failed: ${previewSql(sql)}`, cause);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
createAppender(table) {
|
|
85
|
+
const inst = __classPrivateFieldGet(this, _HybridDuckDB_instances, "m", _HybridDuckDB_ready).call(this);
|
|
86
|
+
try {
|
|
87
|
+
return inst.createAppender(table);
|
|
88
|
+
}
|
|
89
|
+
catch (cause) {
|
|
90
|
+
throw new AnalyticsError('query_failed', `createAppender failed for table ${table}`, cause);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async close() {
|
|
94
|
+
if (__classPrivateFieldGet(this, _HybridDuckDB_closed, "f")) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
__classPrivateFieldSet(this, _HybridDuckDB_closed, true, "f");
|
|
98
|
+
const inst = __classPrivateFieldGet(this, _HybridDuckDB_instance, "f");
|
|
99
|
+
__classPrivateFieldSet(this, _HybridDuckDB_instance, undefined, "f");
|
|
100
|
+
if (inst) {
|
|
101
|
+
await inst.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
_HybridDuckDB_factory = new WeakMap(), _HybridDuckDB_instance = new WeakMap(), _HybridDuckDB_closed = new WeakMap(), _HybridDuckDB_instances = new WeakSet(), _HybridDuckDB_ready = function _HybridDuckDB_ready() {
|
|
106
|
+
if (__classPrivateFieldGet(this, _HybridDuckDB_closed, "f")) {
|
|
107
|
+
throw new AnalyticsError('not_ready', 'HybridDuckDB is closed');
|
|
108
|
+
}
|
|
109
|
+
if (!__classPrivateFieldGet(this, _HybridDuckDB_instance, "f")) {
|
|
110
|
+
throw new AnalyticsError('not_ready', 'HybridDuckDB.open() has not been called');
|
|
111
|
+
}
|
|
112
|
+
return __classPrivateFieldGet(this, _HybridDuckDB_instance, "f");
|
|
113
|
+
};
|
|
114
|
+
/** Recommended page size for streamed queries (spec §Engine). */
|
|
115
|
+
HybridDuckDB.PAGE_SIZE = 500;
|
|
116
|
+
function previewSql(sql) {
|
|
117
|
+
return sql.length > 120 ? `${sql.slice(0, 120)}…` : sql;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ships the class and code union so stubs can throw typed errors from
|
|
6
6
|
* day one; downstream phases add more codes without breaking imports.
|
|
7
7
|
*/
|
|
8
|
-
export type AnalyticsErrorCode = 'engine_open_failed' | 'extension_load_failed' | 'attach_failed' | 'token_vendor_failed' | 'migration_failed' | 'query_failed' | 'not_attached' | 'not_ready' | 'not_implemented';
|
|
8
|
+
export type AnalyticsErrorCode = 'engine_open_failed' | 'extension_load_failed' | 'attach_failed' | 'detach_failed' | 'token_vendor_failed' | 'migration_failed' | 'retention_failed' | 'query_failed' | 'not_attached' | 'not_ready' | 'not_implemented';
|
|
9
9
|
export declare class AnalyticsError extends Error {
|
|
10
10
|
readonly code: AnalyticsErrorCode;
|
|
11
11
|
readonly cause?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,uBAAuB,GACvB,eAAe,GACf,qBAAqB,GACrB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,WAAW,GACX,iBAAiB,CAAA;AAErB,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,kBAAkB,CAAA;IACxC,SAAgB,KAAK,CAAC,EAAE,OAAO,CAAA;gBAEnB,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAMvE;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,cAAc;gBACzC,MAAM,EAAE,MAAM;CAI3B"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,uBAAuB,GACvB,eAAe,GACf,eAAe,GACf,qBAAqB,GACrB,kBAAkB,GAClB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,WAAW,GACX,iBAAiB,CAAA;AAErB,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,kBAAkB,CAAA;IACxC,SAAgB,KAAK,CAAC,EAAE,OAAO,CAAA;gBAEnB,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAMvE;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,cAAc;gBACzC,MAAM,EAAE,MAAM;CAI3B"}
|
package/dist/core/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAIvC,YAAY,IAAwB,EAAE,OAAe,EAAE,KAAe;QACpE,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,cAAc;IACrD,YAAY,MAAc;QACxB,KAAK,CAAC,iBAAiB,EAAE,GAAG,MAAM,yDAAyD,CAAC,CAAA;QAC5F,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB extension bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* Analytics needs `httpfs` + `iceberg` + `parquet` loaded before any
|
|
5
|
+
* `ATTACH 'iceberg://…'` can succeed. This module is the single place we
|
|
6
|
+
* issue `INSTALL/LOAD`, so failures map cleanly to
|
|
7
|
+
* `AnalyticsError('extension_load_failed', <name>)`.
|
|
8
|
+
*
|
|
9
|
+
* Bootstrapping is idempotent — a `#booted: Set<string>` guards repeated
|
|
10
|
+
* calls, and DuckDB itself no-ops re-loads, so calling twice is safe.
|
|
11
|
+
*/
|
|
12
|
+
import type { HybridDuckDB } from './engine';
|
|
13
|
+
/** Extensions required for R2 Iceberg attach, in load order. */
|
|
14
|
+
export declare const REQUIRED_EXTENSIONS: readonly ["httpfs", "iceberg", "parquet"];
|
|
15
|
+
export type RequiredExtension = (typeof REQUIRED_EXTENSIONS)[number];
|
|
16
|
+
/**
|
|
17
|
+
* Issue `INSTALL <ext>; LOAD <ext>;` for each required extension in order.
|
|
18
|
+
*
|
|
19
|
+
* Idempotent per-engine: extensions already loaded on the same
|
|
20
|
+
* `HybridDuckDB` instance are skipped.
|
|
21
|
+
*
|
|
22
|
+
* @throws `AnalyticsError('extension_load_failed', <ext>)` if any extension
|
|
23
|
+
* fails; the extension name appears in the message + cause is preserved.
|
|
24
|
+
*/
|
|
25
|
+
export declare function bootstrapExtensions(db: HybridDuckDB): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Test-only: introspect which extensions have been booted for a given engine.
|
|
28
|
+
*
|
|
29
|
+
* Exposed for coverage of the idempotency path in
|
|
30
|
+
* `__tests__/extensions.test.ts`; not re-exported from `core/index.ts`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getBootedExtensions(db: HybridDuckDB): ReadonlySet<string>;
|
|
33
|
+
//# sourceMappingURL=extensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../src/core/extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,2CAA4C,CAAA;AAE5E,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAQpE;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBzE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,CAEzE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB extension bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* Analytics needs `httpfs` + `iceberg` + `parquet` loaded before any
|
|
5
|
+
* `ATTACH 'iceberg://…'` can succeed. This module is the single place we
|
|
6
|
+
* issue `INSTALL/LOAD`, so failures map cleanly to
|
|
7
|
+
* `AnalyticsError('extension_load_failed', <name>)`.
|
|
8
|
+
*
|
|
9
|
+
* Bootstrapping is idempotent — a `#booted: Set<string>` guards repeated
|
|
10
|
+
* calls, and DuckDB itself no-ops re-loads, so calling twice is safe.
|
|
11
|
+
*/
|
|
12
|
+
import { AnalyticsError } from './errors';
|
|
13
|
+
/** Extensions required for R2 Iceberg attach, in load order. */
|
|
14
|
+
export const REQUIRED_EXTENSIONS = ['httpfs', 'iceberg', 'parquet'];
|
|
15
|
+
/**
|
|
16
|
+
* Tracks which extensions this process has already booted. Keyed by
|
|
17
|
+
* `HybridDuckDB` instance to keep isolation across engines in tests.
|
|
18
|
+
*/
|
|
19
|
+
const bootedByDb = new WeakMap();
|
|
20
|
+
/**
|
|
21
|
+
* Issue `INSTALL <ext>; LOAD <ext>;` for each required extension in order.
|
|
22
|
+
*
|
|
23
|
+
* Idempotent per-engine: extensions already loaded on the same
|
|
24
|
+
* `HybridDuckDB` instance are skipped.
|
|
25
|
+
*
|
|
26
|
+
* @throws `AnalyticsError('extension_load_failed', <ext>)` if any extension
|
|
27
|
+
* fails; the extension name appears in the message + cause is preserved.
|
|
28
|
+
*/
|
|
29
|
+
export async function bootstrapExtensions(db) {
|
|
30
|
+
let booted = bootedByDb.get(db);
|
|
31
|
+
if (!booted) {
|
|
32
|
+
booted = new Set();
|
|
33
|
+
bootedByDb.set(db, booted);
|
|
34
|
+
}
|
|
35
|
+
for (const ext of REQUIRED_EXTENSIONS) {
|
|
36
|
+
if (booted.has(ext)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await db.execute(`INSTALL ${ext};`);
|
|
41
|
+
await db.execute(`LOAD ${ext};`);
|
|
42
|
+
}
|
|
43
|
+
catch (cause) {
|
|
44
|
+
throw new AnalyticsError('extension_load_failed', `Failed to load DuckDB extension: ${ext}`, cause);
|
|
45
|
+
}
|
|
46
|
+
booted.add(ext);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Test-only: introspect which extensions have been booted for a given engine.
|
|
51
|
+
*
|
|
52
|
+
* Exposed for coverage of the idempotency path in
|
|
53
|
+
* `__tests__/extensions.test.ts`; not re-exported from `core/index.ts`.
|
|
54
|
+
*/
|
|
55
|
+
export function getBootedExtensions(db) {
|
|
56
|
+
return bootedByDb.get(db) ?? new Set();
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/core/extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAGzC,gEAAgE;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAU,CAAA;AAI5E;;;GAGG;AACH,MAAM,UAAU,GAAG,IAAI,OAAO,EAA6B,CAAA;AAE3D;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EAAgB;IACxD,IAAI,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,GAAG,EAAE,CAAA;QAClB,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,SAAQ;QACV,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC,CAAA;YACnC,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAA;QAClC,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,IAAI,cAAc,CACtB,uBAAuB,EACvB,oCAAoC,GAAG,EAAE,EACzC,KAAK,CACN,CAAA;QACH,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAgB;IAClD,OAAO,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,EAAE,CAAA;AACxC,CAAC"}
|
package/dist/core/factory.d.ts
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
import type { AnalyticsConfig, AnalyticsEngine } from './types';
|
|
2
1
|
/**
|
|
3
|
-
* Wires config
|
|
2
|
+
* Wires config + engine + machine into an `AnalyticsEngine`.
|
|
3
|
+
*
|
|
4
|
+
* Public entry: `createAnalytics(config)` — used by apps. Uses the injected
|
|
5
|
+
* duckdb factory from `config.duckdb?.factory` (see below) or a lazy
|
|
6
|
+
* `react-native-duckdb` resolver.
|
|
4
7
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
8
|
+
* The 2nd (optional) `internal` argument is an escape hatch for tests +
|
|
9
|
+
* embed contexts that need to substitute a `DuckDBFactory` without shipping
|
|
10
|
+
* `react-native-duckdb`. Not part of the spec surface; consumers should
|
|
11
|
+
* stick to the 1-arg form.
|
|
12
|
+
*/
|
|
13
|
+
import type { DuckDBFactory } from './engine';
|
|
14
|
+
import type { AnalyticsConfig, AnalyticsEngine } from './types';
|
|
15
|
+
/**
|
|
16
|
+
* Internal-only extra input for `createAnalytics`. Not exported from the
|
|
17
|
+
* package barrel; ships in the `factory.ts` module surface so the test suite
|
|
18
|
+
* and embedders can substitute a `DuckDBFactory` without shipping
|
|
19
|
+
* `react-native-duckdb`.
|
|
7
20
|
*/
|
|
8
|
-
export
|
|
21
|
+
export interface CreateAnalyticsInternal {
|
|
22
|
+
/** Substitute the DuckDB factory (fakes in tests; native adapter in app). */
|
|
23
|
+
duckdbFactory?: DuckDBFactory;
|
|
24
|
+
}
|
|
25
|
+
export declare function createAnalytics(config: AnalyticsConfig, internal?: CreateAnalyticsInternal): AnalyticsEngine;
|
|
9
26
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/core/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/core/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,UAAU,CAAA;AAe7D,OAAO,KAAK,EAEV,eAAe,EACf,eAAe,EAIhB,MAAM,SAAS,CAAA;AA6BhB;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,aAAa,CAAA;CAC9B;AAoCD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,GAAE,uBAA4B,GACrC,eAAe,CA2LjB"}
|
package/dist/core/factory.js
CHANGED
|
@@ -1,11 +1,236 @@
|
|
|
1
|
-
import { NotImplementedError } from './errors';
|
|
2
1
|
/**
|
|
3
|
-
* Wires config
|
|
2
|
+
* Wires config + engine + machine into an `AnalyticsEngine`.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Public entry: `createAnalytics(config)` — used by apps. Uses the injected
|
|
5
|
+
* duckdb factory from `config.duckdb?.factory` (see below) or a lazy
|
|
6
|
+
* `react-native-duckdb` resolver.
|
|
7
|
+
*
|
|
8
|
+
* The 2nd (optional) `internal` argument is an escape hatch for tests +
|
|
9
|
+
* embed contexts that need to substitute a `DuckDBFactory` without shipping
|
|
10
|
+
* `react-native-duckdb`. Not part of the spec surface; consumers should
|
|
11
|
+
* stick to the 1-arg form.
|
|
12
|
+
*/
|
|
13
|
+
import { createActor } from 'xstate';
|
|
14
|
+
import { AnalyticsError, NotImplementedError } from './errors';
|
|
15
|
+
import { HybridDuckDB } from './engine';
|
|
16
|
+
import { bootstrapExtensions } from './extensions';
|
|
17
|
+
import { resolveLogger } from './logger';
|
|
18
|
+
import { analyticsMachine } from './machine';
|
|
19
|
+
import { ensureMigrations } from './migrations';
|
|
20
|
+
import { clearLastAttach, loadLastAttach, loadRetentionOverride, saveLastAttach, saveRetentionOverride, } from './persistence';
|
|
21
|
+
import { resolveEffectiveRetention, runRetentionSweep } from './retention';
|
|
22
|
+
import { attachWarehouse, detachWarehouse, warehouseSecretName, } from './warehouse';
|
|
23
|
+
// -------------------- default DuckDB factory --------------------
|
|
24
|
+
/**
|
|
25
|
+
* Lazy resolver for `react-native-duckdb`. Only fires on `open()` — importing
|
|
26
|
+
* `createAnalytics` on a Node/CI host without the native module must not
|
|
27
|
+
* throw. Failure is deferred to the machine's `openEngine` actor, which
|
|
28
|
+
* surfaces as `AnalyticsError('engine_open_failed')` per spec.
|
|
29
|
+
*/
|
|
30
|
+
function defaultDuckdbFactory() {
|
|
31
|
+
return async () => {
|
|
32
|
+
throw new NotImplementedError('defaultDuckdbFactory — supply internal.duckdbFactory or wait for react-native-duckdb adapter');
|
|
33
|
+
// T-10 acceptance: apps must supply a factory via the internal arg while
|
|
34
|
+
// the react-native-duckdb adapter lands (tracked separately). Returning
|
|
35
|
+
// an unreachable satisfies the `DuckDBInstance` return contract.
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// -------------------- state promise helper --------------------
|
|
40
|
+
/**
|
|
41
|
+
* Resolve when the actor next enters one of `targets` (excluding its
|
|
42
|
+
* *current* state at the time of subscription).
|
|
7
43
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
44
|
+
function waitForNextState(actor, targets) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
let seenFirst = false;
|
|
47
|
+
const targetSet = new Set(targets);
|
|
48
|
+
const sub = actor.subscribe((snapshot) => {
|
|
49
|
+
if (!seenFirst) {
|
|
50
|
+
seenFirst = true;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const value = typeof snapshot.value === 'string' ? snapshot.value : '';
|
|
54
|
+
if (targetSet.has(value)) {
|
|
55
|
+
sub.unsubscribe();
|
|
56
|
+
resolve(value);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function currentState(actor) {
|
|
62
|
+
const raw = actor.getSnapshot().value;
|
|
63
|
+
return typeof raw === 'string' ? raw : 'idle';
|
|
64
|
+
}
|
|
65
|
+
// -------------------- factory --------------------
|
|
66
|
+
export function createAnalytics(config, internal = {}) {
|
|
67
|
+
const log = resolveLogger(config.logger);
|
|
68
|
+
const duckdbFactory = internal.duckdbFactory ?? defaultDuckdbFactory();
|
|
69
|
+
const db = new HybridDuckDB(duckdbFactory);
|
|
70
|
+
const machineActors = {
|
|
71
|
+
async openEngine() {
|
|
72
|
+
await db.open();
|
|
73
|
+
await bootstrapExtensions(db);
|
|
74
|
+
},
|
|
75
|
+
async attachEngine({ ctx }) {
|
|
76
|
+
const attach = await attachWarehouse(db, ctx, {
|
|
77
|
+
warehouseUriBuilder: config.warehouseUriBuilder,
|
|
78
|
+
tokenVendor: config.tokenVendor,
|
|
79
|
+
familyMembersProvider: config.familyMembersProvider,
|
|
80
|
+
catalogEndpoint: config.catalogEndpoint,
|
|
81
|
+
});
|
|
82
|
+
await ensureMigrations(db, config.storage, { brand: ctx.brand, tenantId: ctx.tenantId }, attach.warehouseSecret);
|
|
83
|
+
return {
|
|
84
|
+
warehouseSecret: attach.warehouseSecret,
|
|
85
|
+
tokenExpiresAt: attach.tokenExpiresAt.getTime(),
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
async detachEngine({ ctx }) {
|
|
89
|
+
await detachWarehouse(db, ctx.tenantId);
|
|
90
|
+
},
|
|
91
|
+
async refreshToken({ ctx }) {
|
|
92
|
+
const token = await config.tokenVendor.fetch({
|
|
93
|
+
brand: ctx.brand,
|
|
94
|
+
tenantScope: ctx.tenantScope,
|
|
95
|
+
tenantId: ctx.tenantId,
|
|
96
|
+
});
|
|
97
|
+
const secretName = warehouseSecretName(ctx.tenantId);
|
|
98
|
+
try {
|
|
99
|
+
await db.execute(`CREATE OR REPLACE SECRET ${secretName} (TYPE ICEBERG, TOKEN $token, ENDPOINT $endpoint);`, { token: token.token, endpoint: config.catalogEndpoint });
|
|
100
|
+
}
|
|
101
|
+
catch (cause) {
|
|
102
|
+
throw new AnalyticsError('token_vendor_failed', `refreshToken: CREATE OR REPLACE SECRET failed for ${secretName}`, cause);
|
|
103
|
+
}
|
|
104
|
+
return { tokenExpiresAt: token.expiresAt.getTime() };
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const actor = createActor(analyticsMachine, { input: { deps: machineActors } });
|
|
108
|
+
const publicSubscribers = new Set();
|
|
109
|
+
let lastLoggedState;
|
|
110
|
+
const internalSub = actor.subscribe(() => {
|
|
111
|
+
const s = currentState(actor);
|
|
112
|
+
if (s !== lastLoggedState) {
|
|
113
|
+
lastLoggedState = s;
|
|
114
|
+
log.debug('analytics.state', { value: s });
|
|
115
|
+
}
|
|
116
|
+
for (const fn of publicSubscribers)
|
|
117
|
+
fn(s);
|
|
118
|
+
});
|
|
119
|
+
actor.start();
|
|
120
|
+
actor.send({ type: 'OPEN' });
|
|
121
|
+
/**
|
|
122
|
+
* Ctx of the last successful attach; cleared on detach. Used by
|
|
123
|
+
* `setRetention` (needs `brand/tenantId/userId` for the override key) and
|
|
124
|
+
* by the attach-time persistence + sweep hook.
|
|
125
|
+
*/
|
|
126
|
+
let currentCtx;
|
|
127
|
+
/**
|
|
128
|
+
* Load user override (if any), resolve effective retention against the
|
|
129
|
+
* brand default from `AnalyticsConfig.retention`, run one sweep. Errors
|
|
130
|
+
* are re-thrown; callers may choose to swallow (attach-time sweep does).
|
|
131
|
+
*/
|
|
132
|
+
async function sweep(ctx, catalog) {
|
|
133
|
+
const brandCfg = config.retention[ctx.brand];
|
|
134
|
+
const brandDefault = brandCfg?.days ?? 0;
|
|
135
|
+
const userOverride = (await loadRetentionOverride(config.storage, ctx)) ?? undefined;
|
|
136
|
+
const effectiveDays = resolveEffectiveRetention({ brandDefault, userOverride });
|
|
137
|
+
if (effectiveDays <= 0)
|
|
138
|
+
return;
|
|
139
|
+
await runRetentionSweep(db, catalog, { effectiveDays });
|
|
140
|
+
}
|
|
141
|
+
const engine = {
|
|
142
|
+
async attach(ctx) {
|
|
143
|
+
const pending = waitForNextState(actor, ['attached', 'error']);
|
|
144
|
+
actor.send({ type: 'ATTACH', ctx });
|
|
145
|
+
const final = await pending;
|
|
146
|
+
if (final === 'error') {
|
|
147
|
+
throw actor.getSnapshot().context.lastError ?? new AnalyticsError('attach_failed', 'attach failed');
|
|
148
|
+
}
|
|
149
|
+
currentCtx = ctx;
|
|
150
|
+
log.info('analytics.attached', {
|
|
151
|
+
brand: ctx.brand,
|
|
152
|
+
tenantId: ctx.tenantId,
|
|
153
|
+
tenantScope: ctx.tenantScope,
|
|
154
|
+
});
|
|
155
|
+
// Persistence + retention sweep are best-effort so a KV / DELETE
|
|
156
|
+
// failure never rejects a successful attach.
|
|
157
|
+
try {
|
|
158
|
+
await saveLastAttach(config.storage, ctx);
|
|
159
|
+
}
|
|
160
|
+
catch (cause) {
|
|
161
|
+
log.warn('analytics.persist.last_attach_failed', { cause });
|
|
162
|
+
}
|
|
163
|
+
const catalog = actor.getSnapshot().context.warehouseSecret;
|
|
164
|
+
if (catalog) {
|
|
165
|
+
try {
|
|
166
|
+
await sweep(ctx, catalog);
|
|
167
|
+
}
|
|
168
|
+
catch (cause) {
|
|
169
|
+
log.warn('analytics.retention.sweep_failed', { cause });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
async detach() {
|
|
174
|
+
const ctxAtDetach = currentCtx;
|
|
175
|
+
const pending = waitForNextState(actor, ['ready', 'error']);
|
|
176
|
+
actor.send({ type: 'DETACH' });
|
|
177
|
+
const final = await pending;
|
|
178
|
+
if (final === 'error') {
|
|
179
|
+
throw actor.getSnapshot().context.lastError ?? new AnalyticsError('detach_failed', 'detach failed');
|
|
180
|
+
}
|
|
181
|
+
currentCtx = undefined;
|
|
182
|
+
if (ctxAtDetach) {
|
|
183
|
+
log.info('analytics.detached', {
|
|
184
|
+
brand: ctxAtDetach.brand,
|
|
185
|
+
tenantId: ctxAtDetach.tenantId,
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
await clearLastAttach(config.storage, ctxAtDetach.brand);
|
|
189
|
+
}
|
|
190
|
+
catch (cause) {
|
|
191
|
+
log.warn('analytics.persist.clear_last_attach_failed', { cause });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
execute: (sql, params) => db.execute(sql, params),
|
|
196
|
+
stream: (sql, params) => db.stream(sql, params),
|
|
197
|
+
createAppender: (table) => db.createAppender(table),
|
|
198
|
+
get state() {
|
|
199
|
+
return currentState(actor);
|
|
200
|
+
},
|
|
201
|
+
get lastError() {
|
|
202
|
+
return actor.getSnapshot().context.lastError ?? null;
|
|
203
|
+
},
|
|
204
|
+
get catalog() {
|
|
205
|
+
return actor.getSnapshot().context.warehouseSecret;
|
|
206
|
+
},
|
|
207
|
+
subscribe(listener) {
|
|
208
|
+
publicSubscribers.add(listener);
|
|
209
|
+
return () => publicSubscribers.delete(listener);
|
|
210
|
+
},
|
|
211
|
+
async setRetention(days) {
|
|
212
|
+
if (!currentCtx) {
|
|
213
|
+
throw new AnalyticsError('not_attached', 'setRetention requires an attached engine');
|
|
214
|
+
}
|
|
215
|
+
const catalog = actor.getSnapshot().context.warehouseSecret;
|
|
216
|
+
if (!catalog) {
|
|
217
|
+
throw new AnalyticsError('not_attached', 'setRetention: no warehouse catalog');
|
|
218
|
+
}
|
|
219
|
+
await saveRetentionOverride(config.storage, currentCtx, days);
|
|
220
|
+
await sweep(currentCtx, catalog);
|
|
221
|
+
},
|
|
222
|
+
async getLastAttach(brand) {
|
|
223
|
+
return loadLastAttach(config.storage, brand);
|
|
224
|
+
},
|
|
225
|
+
async close() {
|
|
226
|
+
log.info('analytics.closed');
|
|
227
|
+
actor.send({ type: 'CLOSE' });
|
|
228
|
+
internalSub.unsubscribe();
|
|
229
|
+
publicSubscribers.clear();
|
|
230
|
+
await db.close();
|
|
231
|
+
actor.stop();
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
return engine;
|
|
10
235
|
}
|
|
11
236
|
//# sourceMappingURL=factory.js.map
|
package/dist/core/factory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/core/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/core/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1,27 +1,46 @@
|
|
|
1
|
-
import type { AnalyticsState, Insight } from './types';
|
|
2
1
|
/**
|
|
3
|
-
* React hooks over the analytics engine.
|
|
4
|
-
* `useSyncExternalStore` on `engine.subscribe()` lands in Phase 5 (T-11/12/13).
|
|
2
|
+
* React hooks over the analytics engine.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* All three hooks resolve the engine via `useAnalyticsEngine()` (context) and
|
|
5
|
+
* subscribe to state transitions with `useSyncExternalStore`. Query hooks
|
|
6
|
+
* (`useTimeseries`, `useInsight`) block until the engine is attached; a
|
|
7
|
+
* detach + re-attach (family switch) refires them via the state subscription.
|
|
10
8
|
*/
|
|
11
|
-
|
|
9
|
+
import type { AnalyticsState, Insight } from './types';
|
|
10
|
+
export interface UseAnalyticsResult {
|
|
12
11
|
state: AnalyticsState;
|
|
13
12
|
isReady: boolean;
|
|
14
13
|
isAttached: boolean;
|
|
15
14
|
error: Error | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Subscribe to the engine's state machine. Re-renders on every transition.
|
|
18
|
+
* `isReady` is true from `ready` onward (engine opened); `isAttached` is true
|
|
19
|
+
* only in the `attached` state.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useAnalytics(): UseAnalyticsResult;
|
|
22
|
+
export interface UseTimeseriesResult<T> {
|
|
18
23
|
data: T[] | undefined;
|
|
19
24
|
loading: boolean;
|
|
20
25
|
error: Error | null;
|
|
21
26
|
refetch: () => Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs `sql` (with `params`) against the engine whenever `key`, `sql`,
|
|
30
|
+
* `params`, or the engine's attached state changes. `key === undefined`
|
|
31
|
+
* disables the query (data stays `undefined`).
|
|
32
|
+
*
|
|
33
|
+
* `refetch()` re-runs the current query; stale results from prior effects are
|
|
34
|
+
* discarded via a per-effect abort flag so out-of-order responses never win.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useTimeseries<T = unknown>(key: string | undefined, sql: string, params?: Record<string, unknown>): UseTimeseriesResult<T>;
|
|
37
|
+
export interface UseInsightResult {
|
|
24
38
|
insight: Insight | undefined;
|
|
25
39
|
loading: boolean;
|
|
26
|
-
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reads a single insight row by id. Returns `undefined` if the engine is not
|
|
43
|
+
* attached or the row does not exist.
|
|
44
|
+
*/
|
|
45
|
+
export declare function useInsight(id: string): UseInsightResult;
|
|
27
46
|
//# sourceMappingURL=hooks.d.ts.map
|
package/dist/core/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/core/hooks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/core/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAmB,cAAc,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAIvE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,CAAA;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,kBAAkB,CASjD;AAID,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,CAAC,EAAE,GAAG,SAAS,CAAA;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AA8BD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EACvC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,mBAAmB,CAAC,CAAC,CAAC,CA8ExB;AAID,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,CAgBvD"}
|