@mongrov/analytics 0.1.0-alpha.7 → 0.1.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/dist/rules/__fakes__/fakeClock.d.ts +7 -0
  3. package/dist/rules/__fakes__/fakeClock.d.ts.map +1 -0
  4. package/dist/rules/__fakes__/fakeClock.js +13 -0
  5. package/dist/rules/__fakes__/fakeClock.js.map +1 -0
  6. package/dist/rules/__fakes__/fakeEngine.d.ts +12 -0
  7. package/dist/rules/__fakes__/fakeEngine.d.ts.map +1 -0
  8. package/dist/rules/__fakes__/fakeEngine.js +33 -0
  9. package/dist/rules/__fakes__/fakeEngine.js.map +1 -0
  10. package/dist/rules/__fakes__/fakeStorage.d.ts +7 -0
  11. package/dist/rules/__fakes__/fakeStorage.d.ts.map +1 -0
  12. package/dist/rules/__fakes__/fakeStorage.js +22 -0
  13. package/dist/rules/__fakes__/fakeStorage.js.map +1 -0
  14. package/dist/rules/compiler-cache.d.ts +16 -0
  15. package/dist/rules/compiler-cache.d.ts.map +1 -0
  16. package/dist/rules/compiler-cache.js +29 -0
  17. package/dist/rules/compiler-cache.js.map +1 -0
  18. package/dist/rules/compiler.d.ts +27 -0
  19. package/dist/rules/compiler.d.ts.map +1 -0
  20. package/dist/rules/compiler.js +210 -0
  21. package/dist/rules/compiler.js.map +1 -0
  22. package/dist/rules/defaults/index.d.ts +13 -0
  23. package/dist/rules/defaults/index.d.ts.map +1 -0
  24. package/dist/rules/defaults/index.js +13 -0
  25. package/dist/rules/defaults/index.js.map +1 -0
  26. package/dist/rules/defaults/loader.d.ts +18 -0
  27. package/dist/rules/defaults/loader.d.ts.map +1 -0
  28. package/dist/rules/defaults/loader.js +37 -0
  29. package/dist/rules/defaults/loader.js.map +1 -0
  30. package/dist/rules/defaults/luminx.d.ts +6 -0
  31. package/dist/rules/defaults/luminx.d.ts.map +1 -0
  32. package/dist/rules/defaults/luminx.js +64 -0
  33. package/dist/rules/defaults/luminx.js.map +1 -0
  34. package/dist/rules/defaults/viva.d.ts +6 -0
  35. package/dist/rules/defaults/viva.d.ts.map +1 -0
  36. package/dist/rules/defaults/viva.js +45 -0
  37. package/dist/rules/defaults/viva.js.map +1 -0
  38. package/dist/rules/defaults/yogaring.d.ts +6 -0
  39. package/dist/rules/defaults/yogaring.d.ts.map +1 -0
  40. package/dist/rules/defaults/yogaring.js +45 -0
  41. package/dist/rules/defaults/yogaring.js.map +1 -0
  42. package/dist/rules/defaults/ziva.d.ts +10 -0
  43. package/dist/rules/defaults/ziva.d.ts.map +1 -0
  44. package/dist/rules/defaults/ziva.js +87 -0
  45. package/dist/rules/defaults/ziva.js.map +1 -0
  46. package/dist/rules/emitter.d.ts +20 -0
  47. package/dist/rules/emitter.d.ts.map +1 -0
  48. package/dist/rules/emitter.js +32 -0
  49. package/dist/rules/emitter.js.map +1 -0
  50. package/dist/rules/evaluator.d.ts +44 -0
  51. package/dist/rules/evaluator.d.ts.map +1 -0
  52. package/dist/rules/evaluator.js +127 -0
  53. package/dist/rules/evaluator.js.map +1 -0
  54. package/dist/rules/factory.d.ts +13 -0
  55. package/dist/rules/factory.d.ts.map +1 -0
  56. package/dist/rules/factory.js +56 -0
  57. package/dist/rules/factory.js.map +1 -0
  58. package/dist/rules/hooks/useRuleRegistry.d.ts +16 -0
  59. package/dist/rules/hooks/useRuleRegistry.d.ts.map +1 -0
  60. package/dist/rules/hooks/useRuleRegistry.js +26 -0
  61. package/dist/rules/hooks/useRuleRegistry.js.map +1 -0
  62. package/dist/rules/hooks/useRuleViolations.d.ts +21 -0
  63. package/dist/rules/hooks/useRuleViolations.d.ts.map +1 -0
  64. package/dist/rules/hooks/useRuleViolations.js +32 -0
  65. package/dist/rules/hooks/useRuleViolations.js.map +1 -0
  66. package/dist/rules/index.d.ts +30 -1
  67. package/dist/rules/index.d.ts.map +1 -1
  68. package/dist/rules/index.js +36 -1
  69. package/dist/rules/index.js.map +1 -1
  70. package/dist/rules/registry.d.ts +32 -0
  71. package/dist/rules/registry.d.ts.map +1 -0
  72. package/dist/rules/registry.js +98 -0
  73. package/dist/rules/registry.js.map +1 -0
  74. package/dist/rules/schema.d.ts +221 -0
  75. package/dist/rules/schema.d.ts.map +1 -0
  76. package/dist/rules/schema.js +80 -0
  77. package/dist/rules/schema.js.map +1 -0
  78. package/dist/rules/throttle.d.ts +27 -0
  79. package/dist/rules/throttle.d.ts.map +1 -0
  80. package/dist/rules/throttle.js +76 -0
  81. package/dist/rules/throttle.js.map +1 -0
  82. package/dist/rules/types.d.ts +88 -0
  83. package/dist/rules/types.d.ts.map +1 -0
  84. package/dist/rules/types.js +9 -0
  85. package/dist/rules/types.js.map +1 -0
  86. package/dist/rules/validator.d.ts +27 -0
  87. package/dist/rules/validator.d.ts.map +1 -0
  88. package/dist/rules/validator.js +97 -0
  89. package/dist/rules/validator.js.map +1 -0
  90. package/dist/sync/factory.d.ts +8 -0
  91. package/dist/sync/factory.d.ts.map +1 -1
  92. package/dist/sync/factory.js +31 -1
  93. package/dist/sync/factory.js.map +1 -1
  94. package/dist/sync/flusher.d.ts +6 -0
  95. package/dist/sync/flusher.d.ts.map +1 -1
  96. package/dist/sync/flusher.js +13 -1
  97. package/dist/sync/flusher.js.map +1 -1
  98. package/package.json +17 -16
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mongrov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ import type { Clock } from '../types';
2
+ /** Pinnable clock for throttle tests. Advance via `set(date)`. */
3
+ export declare function createFakeClock(initial: Date | string): Clock & {
4
+ set(t: Date | string): void;
5
+ advance(ms: number): void;
6
+ };
7
+ //# sourceMappingURL=fakeClock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakeClock.d.ts","sourceRoot":"","sources":["../../../src/rules/__fakes__/fakeClock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAErC,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG;IAC/D,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAaA"}
@@ -0,0 +1,13 @@
1
+ /** Pinnable clock for throttle tests. Advance via `set(date)`. */
2
+ export function createFakeClock(initial) {
3
+ let now = new Date(initial);
4
+ const clock = (() => new Date(now));
5
+ clock.set = (t) => {
6
+ now = new Date(t);
7
+ };
8
+ clock.advance = (ms) => {
9
+ now = new Date(now.getTime() + ms);
10
+ };
11
+ return clock;
12
+ }
13
+ //# sourceMappingURL=fakeClock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakeClock.js","sourceRoot":"","sources":["../../../src/rules/__fakes__/fakeClock.ts"],"names":[],"mappings":"AAEA,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAAC,OAAsB;IAIpD,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3B,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAGjC,CAAA;IACD,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,EAAE;QAChB,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAA;IACnB,CAAC,CAAA;IACD,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,EAAE,EAAE;QACrB,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAA;IACpC,CAAC,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { AnalyticsEngine } from '../../core/types';
2
+ /** Minimal AnalyticsEngine stub for evaluator tests. */
3
+ export interface FakeEngine extends AnalyticsEngine {
4
+ __calls: {
5
+ sql: string;
6
+ params: Record<string, unknown>;
7
+ }[];
8
+ __setResult(rows: unknown[]): void;
9
+ __setError(err: Error | null): void;
10
+ }
11
+ export declare function createFakeEngine(): FakeEngine;
12
+ //# sourceMappingURL=fakeEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakeEngine.d.ts","sourceRoot":"","sources":["../../../src/rules/__fakes__/fakeEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEvD,wDAAwD;AACxD,MAAM,WAAW,UAAW,SAAQ,eAAe;IACjD,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,CAAA;IAC3D,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAClC,UAAU,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAAA;CACpC;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CA+B7C"}
@@ -0,0 +1,33 @@
1
+ export function createFakeEngine() {
2
+ let queued = [];
3
+ let queuedErr = null;
4
+ const calls = [];
5
+ const engine = {
6
+ async attach() { },
7
+ async detach() { },
8
+ async execute(sql, params) {
9
+ calls.push({ sql, params: params ?? {} });
10
+ if (queuedErr)
11
+ throw queuedErr;
12
+ return queued;
13
+ },
14
+ stream() {
15
+ throw new Error('stream not implemented in fake');
16
+ },
17
+ createAppender() {
18
+ throw new Error('createAppender not implemented in fake');
19
+ },
20
+ state: 'attached',
21
+ lastError: null,
22
+ catalog: 'fake',
23
+ subscribe: () => () => { },
24
+ async setRetention() { },
25
+ async getLastAttach() { return null; },
26
+ async close() { },
27
+ __calls: calls,
28
+ __setResult(rows) { queued = rows; queuedErr = null; },
29
+ __setError(err) { queuedErr = err; },
30
+ };
31
+ return engine;
32
+ }
33
+ //# sourceMappingURL=fakeEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakeEngine.js","sourceRoot":"","sources":["../../../src/rules/__fakes__/fakeEngine.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,gBAAgB;IAC9B,IAAI,MAAM,GAAc,EAAE,CAAA;IAC1B,IAAI,SAAS,GAAiB,IAAI,CAAA;IAClC,MAAM,KAAK,GAAuD,EAAE,CAAA;IAEpE,MAAM,MAAM,GAAe;QACzB,KAAK,CAAC,MAAM,KAAI,CAAC;QACjB,KAAK,CAAC,MAAM,KAAI,CAAC;QACjB,KAAK,CAAC,OAAO,CAAI,GAAW,EAAE,MAAgC;YAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC,CAAA;YACzC,IAAI,SAAS;gBAAE,MAAM,SAAS,CAAA;YAC9B,OAAO,MAAa,CAAA;QACtB,CAAC;QACD,MAAM;YACJ,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACnD,CAAC;QACD,cAAc;YACZ,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAC3D,CAAC;QACD,KAAK,EAAE,UAAU;QACjB,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;QACzB,KAAK,CAAC,YAAY,KAAI,CAAC;QACvB,KAAK,CAAC,aAAa,KAAK,OAAO,IAAI,CAAA,CAAC,CAAC;QACrC,KAAK,CAAC,KAAK,KAAI,CAAC;QAChB,OAAO,EAAE,KAAK;QACd,WAAW,CAAC,IAAI,IAAI,MAAM,GAAG,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAA,CAAC,CAAC;QACrD,UAAU,CAAC,GAAG,IAAI,SAAS,GAAG,GAAG,CAAA,CAAC,CAAC;KACpC,CAAA;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { KVStore } from '../../core/types';
2
+ /** In-memory KVStore for rules tests. */
3
+ export declare function createFakeStorage(): KVStore & {
4
+ __dump: () => Record<string, unknown>;
5
+ __clear: () => void;
6
+ };
7
+ //# sourceMappingURL=fakeStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakeStorage.d.ts","sourceRoot":"","sources":["../../../src/rules/__fakes__/fakeStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE/C,yCAAyC;AACzC,wBAAgB,iBAAiB,IAAI,OAAO,GAAG;IAC7C,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAmBA"}
@@ -0,0 +1,22 @@
1
+ /** In-memory KVStore for rules tests. */
2
+ export function createFakeStorage() {
3
+ const store = new Map();
4
+ return {
5
+ async get(key) {
6
+ return store.get(key);
7
+ },
8
+ async set(key, value) {
9
+ store.set(key, value);
10
+ },
11
+ async delete(key) {
12
+ store.delete(key);
13
+ },
14
+ __dump() {
15
+ return Object.fromEntries(store.entries());
16
+ },
17
+ __clear() {
18
+ store.clear();
19
+ },
20
+ };
21
+ }
22
+ //# sourceMappingURL=fakeStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakeStorage.js","sourceRoot":"","sources":["../../../src/rules/__fakes__/fakeStorage.ts"],"names":[],"mappings":"AAEA,yCAAyC;AACzC,MAAM,UAAU,iBAAiB;IAI/B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmB,CAAA;IACxC,OAAO;QACL,KAAK,CAAC,GAAG,CAAI,GAAW;YACtB,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAkB,CAAA;QACxC,CAAC;QACD,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,KAAQ;YAChC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACvB,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAW;YACtB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACnB,CAAC;QACD,MAAM;YACJ,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAC5C,CAAC;QACD,OAAO;YACL,KAAK,CAAC,KAAK,EAAE,CAAA;QACf,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Memoization layer around `compileRule`.
3
+ *
4
+ * Keyed by `ruleId + registry revision`. Registry bumps `rev` on every
5
+ * register/enable/disable, so a rule mutation transparently invalidates
6
+ * its cached compilation on the next `getCompiled(rule, rev)` call.
7
+ */
8
+ import type { Rule } from './schema';
9
+ import type { CompiledRule } from './types';
10
+ export interface CompilerCache {
11
+ getCompiled(rule: Rule, rev: number): CompiledRule;
12
+ invalidate(ruleId: string): void;
13
+ size(): number;
14
+ }
15
+ export declare function createCompilerCache(): CompilerCache;
16
+ //# sourceMappingURL=compiler-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler-cache.d.ts","sourceRoot":"","sources":["../../src/rules/compiler-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;IAClD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,IAAI,IAAI,MAAM,CAAA;CACf;AAED,wBAAgB,mBAAmB,IAAI,aAAa,CAoBnD"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Memoization layer around `compileRule`.
3
+ *
4
+ * Keyed by `ruleId + registry revision`. Registry bumps `rev` on every
5
+ * register/enable/disable, so a rule mutation transparently invalidates
6
+ * its cached compilation on the next `getCompiled(rule, rev)` call.
7
+ */
8
+ import { compileRule } from './compiler';
9
+ export function createCompilerCache() {
10
+ const entries = new Map();
11
+ return {
12
+ getCompiled(rule, rev) {
13
+ const cached = entries.get(rule.id);
14
+ if (cached && cached.rev === rev) {
15
+ return cached.compiled;
16
+ }
17
+ const compiled = compileRule(rule);
18
+ entries.set(rule.id, { rev, compiled });
19
+ return compiled;
20
+ },
21
+ invalidate(ruleId) {
22
+ entries.delete(ruleId);
23
+ },
24
+ size() {
25
+ return entries.size;
26
+ },
27
+ };
28
+ }
29
+ //# sourceMappingURL=compiler-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler-cache.js","sourceRoot":"","sources":["../../src/rules/compiler-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAUxC,MAAM,UAAU,mBAAmB;IACjC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmD,CAAA;IAE1E,OAAO;QACL,WAAW,CAAC,IAAI,EAAE,GAAG;YACnB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACnC,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBACjC,OAAO,MAAM,CAAC,QAAQ,CAAA;YACxB,CAAC;YACD,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;YAClC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAA;YACvC,OAAO,QAAQ,CAAA;QACjB,CAAC;QACD,UAAU,CAAC,MAAM;YACf,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QACD,IAAI;YACF,OAAO,OAAO,CAAC,IAAI,CAAA;QACrB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SQL compiler — rule → parameterized DuckDB query.
3
+ *
4
+ * Design principles:
5
+ * - `$userId`, `$brand`, `$familyId` are always bound via
6
+ * `analytics.execute(sql, params)`. Never string-concatenated.
7
+ * - Static rule params (thresholds, window edges) are also bound as
8
+ * `$<name>`. Never concatenated.
9
+ * - Table + column identifiers are looked up in `METRIC_METADATA`,
10
+ * sanitized with `[^A-Za-z0-9_]/g → _`, then inlined as literals.
11
+ * DuckDB has no parameter binding for identifiers.
12
+ * - `rawSql` bypasses codegen; the compiler only allow-lists
13
+ * placeholders declared in `rule.rawSqlParams` (plus the three
14
+ * runtime-bound ones).
15
+ *
16
+ * The result rowshape is uniform: every SELECT projects
17
+ * observed_value NUMERIC, threshold_value NUMERIC
18
+ * so the evaluator can build `RuleViolation` without dispatching per
19
+ * target type.
20
+ */
21
+ import type { CompiledRule } from './types';
22
+ import { type Rule } from './schema';
23
+ /** Sanitize an identifier for direct interpolation. */
24
+ export declare function sanitizeIdent(raw: string): string;
25
+ /** Compile a validated rule to a `CompiledRule`. */
26
+ export declare function compileRule(rule: Rule): CompiledRule;
27
+ //# sourceMappingURL=compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/rules/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAGL,KAAK,IAAI,EAIV,MAAM,UAAU,CAAA;AAEjB,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AA4DD,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,CA0CpD"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * SQL compiler — rule → parameterized DuckDB query.
3
+ *
4
+ * Design principles:
5
+ * - `$userId`, `$brand`, `$familyId` are always bound via
6
+ * `analytics.execute(sql, params)`. Never string-concatenated.
7
+ * - Static rule params (thresholds, window edges) are also bound as
8
+ * `$<name>`. Never concatenated.
9
+ * - Table + column identifiers are looked up in `METRIC_METADATA`,
10
+ * sanitized with `[^A-Za-z0-9_]/g → _`, then inlined as literals.
11
+ * DuckDB has no parameter binding for identifiers.
12
+ * - `rawSql` bypasses codegen; the compiler only allow-lists
13
+ * placeholders declared in `rule.rawSqlParams` (plus the three
14
+ * runtime-bound ones).
15
+ *
16
+ * The result rowshape is uniform: every SELECT projects
17
+ * observed_value NUMERIC, threshold_value NUMERIC
18
+ * so the evaluator can build `RuleViolation` without dispatching per
19
+ * target type.
20
+ */
21
+ import { METRIC_METADATA } from '../core/metric_metadata';
22
+ import { RuleValidationError, } from './schema';
23
+ /** Sanitize an identifier for direct interpolation. */
24
+ export function sanitizeIdent(raw) {
25
+ return raw.replace(/[^A-Za-z0-9_]/g, '_');
26
+ }
27
+ /** Timestamp column per table — `sleep_session` uses `ts_start`. */
28
+ function tsColumn(table) {
29
+ return table === 'sleep_session' ? 'ts_start' : 'ts';
30
+ }
31
+ /**
32
+ * DuckDB duration literal for `NOW() - INTERVAL '<window>'`. Encodes
33
+ * hours or days explicitly rather than relying on TZ-aware helpers.
34
+ */
35
+ function windowInterval(window) {
36
+ switch (window) {
37
+ case '1h': return `INTERVAL '1 hour'`;
38
+ case '6h': return `INTERVAL '6 hours'`;
39
+ case '24h': return `INTERVAL '24 hours'`;
40
+ case '3d': return `INTERVAL '3 days'`;
41
+ case '7d': return `INTERVAL '7 days'`;
42
+ case '30d': return `INTERVAL '30 days'`;
43
+ }
44
+ }
45
+ /** SQL fragment mapping an aggregation to its call over `<column>`. */
46
+ function aggExpr(agg, column, ts) {
47
+ switch (agg) {
48
+ case 'avg': return `AVG(${column})`;
49
+ case 'min': return `MIN(${column})`;
50
+ case 'max': return `MAX(${column})`;
51
+ case 'sum': return `SUM(${column})`;
52
+ case 'count': return `COUNT(${column})`;
53
+ case 'last': return `arg_max(${column}, ${ts})`;
54
+ }
55
+ }
56
+ /** `HAVING`-style clause for a compare operator. */
57
+ function compareClause(compare, lhs, rhs) {
58
+ switch (compare) {
59
+ case 'less_than': return `${lhs} < ${rhs}`;
60
+ case 'greater_than': return `${lhs} > ${rhs}`;
61
+ case 'equals': return `${lhs} = ${rhs}`;
62
+ case 'not_equals': return `${lhs} <> ${rhs}`;
63
+ case 'between': throw new Error('between handled per-target');
64
+ }
65
+ }
66
+ const ALLOWED_RAW_PLACEHOLDERS = new Set(['userId', 'brand', 'familyId']);
67
+ /** Extract every `$name` placeholder from a SQL string. */
68
+ function placeholdersOf(sql) {
69
+ const out = [];
70
+ const re = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
71
+ let match;
72
+ match = re.exec(sql);
73
+ while (match !== null) {
74
+ out.push(match[1]);
75
+ match = re.exec(sql);
76
+ }
77
+ return out;
78
+ }
79
+ /** Compile a validated rule to a `CompiledRule`. */
80
+ export function compileRule(rule) {
81
+ const meta = METRIC_METADATA[rule.metric];
82
+ const rawTable = meta.table;
83
+ const rawColumn = meta.column;
84
+ const table = sanitizeIdent(rawTable);
85
+ const column = sanitizeIdent(rawColumn);
86
+ if (rule.rawSql) {
87
+ return compileRawSql(rule, rule.rawSql);
88
+ }
89
+ const ts = tsColumn(rawTable);
90
+ const interval = windowInterval(rule.window);
91
+ const filter = `WHERE user_id = $userId AND brand = $brand AND family_id = $familyId `
92
+ + `AND ${ts} > NOW() - ${interval}`;
93
+ const agg = aggExpr(rule.aggregation, column, ts);
94
+ const params = {};
95
+ const description = `${rule.metric} ${rule.aggregation} over ${rule.window} ${rule.compare} ${describeTarget(rule.target)}`;
96
+ const { sql, params: extraParams } = buildForTarget({
97
+ table,
98
+ ts,
99
+ interval,
100
+ column,
101
+ agg,
102
+ filter,
103
+ target: rule.target,
104
+ compare: rule.compare,
105
+ });
106
+ Object.assign(params, extraParams);
107
+ return {
108
+ ruleId: rule.id,
109
+ metric: rule.metric,
110
+ sql,
111
+ params,
112
+ description,
113
+ };
114
+ }
115
+ function describeTarget(target) {
116
+ switch (target.type) {
117
+ case 'absolute': return `${target.value}`;
118
+ case 'baseline_percent': return `${target.percent}% of ${target.windowDays}d baseline`;
119
+ case 'baseline_stddev': return `${target.stddevs} stddev over ${target.windowDays}d baseline`;
120
+ case 'range': return `[${target.min}, ${target.max}]`;
121
+ }
122
+ }
123
+ function buildForTarget(args) {
124
+ const { table, ts, column, agg, filter, target, compare } = args;
125
+ if (target.type === 'absolute') {
126
+ const params = { threshold_absolute: target.value };
127
+ const sql = `SELECT ${agg} AS observed_value, $threshold_absolute AS threshold_value
128
+ FROM ${table}
129
+ ${filter}
130
+ HAVING ${compareClause(compare, 'observed_value', '$threshold_absolute')};`;
131
+ return { sql, params };
132
+ }
133
+ if (target.type === 'range') {
134
+ const params = { range_min: target.min, range_max: target.max };
135
+ // For range, `between` violates on values OUTSIDE the range; other
136
+ // compares treat range as a threshold-with-tolerance and use the min
137
+ // bound as threshold_value for reporting.
138
+ const outside = compare === 'between'
139
+ ? `NOT (observed_value BETWEEN $range_min AND $range_max)`
140
+ : compareClause(compare, 'observed_value', '$range_min');
141
+ const sql = `SELECT ${agg} AS observed_value, $range_min AS threshold_value
142
+ FROM ${table}
143
+ ${filter}
144
+ HAVING ${outside};`;
145
+ return { sql, params };
146
+ }
147
+ // Baselines use a CTE for the user's historical mean over windowDays.
148
+ // DuckDB can't parametrize inside an INTERVAL literal, but it can
149
+ // multiply a static unit interval by a bound integer.
150
+ const baselineDaysBind = `(INTERVAL 1 DAY) * $baselineDays`;
151
+ if (target.type === 'baseline_percent') {
152
+ const params = {
153
+ baselineDays: target.windowDays,
154
+ pct: target.percent,
155
+ };
156
+ const sql = `WITH baseline AS (
157
+ SELECT AVG(${column}) AS mean
158
+ FROM ${table}
159
+ WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
160
+ AND ${ts} > NOW() - ${baselineDaysBind}
161
+ )
162
+ SELECT ${agg} AS observed_value,
163
+ (SELECT mean FROM baseline) * ($pct / 100.0) AS threshold_value
164
+ FROM ${table}
165
+ ${filter}
166
+ HAVING ${compareClause(compare, 'observed_value', 'threshold_value')};`;
167
+ return { sql, params };
168
+ }
169
+ // baseline_stddev
170
+ const params = {
171
+ baselineDays: target.windowDays,
172
+ stddevs: target.stddevs,
173
+ };
174
+ const sql = `WITH baseline AS (
175
+ SELECT AVG(${column}) AS mean, stddev_pop(${column}) AS sd
176
+ FROM ${table}
177
+ WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
178
+ AND ${ts} > NOW() - ${baselineDaysBind}
179
+ )
180
+ SELECT ${agg} AS observed_value,
181
+ (SELECT mean + $stddevs * sd FROM baseline) AS threshold_value
182
+ FROM ${table}
183
+ ${filter}
184
+ HAVING ${compareClause(compare, 'observed_value', 'threshold_value')};`;
185
+ return { sql, params };
186
+ }
187
+ /**
188
+ * Pass-through path with allow-list check. Placeholders must be one of
189
+ * {userId, brand, familyId} or declared in `rule.rawSqlParams`.
190
+ */
191
+ function compileRawSql(rule, rawSql) {
192
+ const placeholders = placeholdersOf(rawSql);
193
+ const allowed = new Set(ALLOWED_RAW_PLACEHOLDERS);
194
+ for (const p of rule.rawSqlParams ?? []) {
195
+ allowed.add(p);
196
+ }
197
+ for (const p of placeholders) {
198
+ if (!allowed.has(p)) {
199
+ throw new RuleValidationError(`Rule ${rule.id}: rawSql placeholder $${p} not declared in rawSqlParams.`);
200
+ }
201
+ }
202
+ return {
203
+ ruleId: rule.id,
204
+ metric: rule.metric,
205
+ sql: rawSql,
206
+ params: {},
207
+ description: `rawSql:${rule.metric}`,
208
+ };
209
+ }
210
+ //# sourceMappingURL=compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../src/rules/compiler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Brand default rule catalogs.
3
+ *
4
+ * Each brand exports a fully-validated `Rule[]` parsed from its inlined
5
+ * TOML source at package-load time; a malformed default therefore fails
6
+ * fast rather than at first evaluate.
7
+ */
8
+ export { zivaDefaults } from './ziva';
9
+ export { luminxDefaults } from './luminx';
10
+ export { vivaDefaults } from './viva';
11
+ export { yogaringDefaults } from './yogaring';
12
+ export { parseCatalog, type ParseCatalogOptions } from './loader';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/defaults/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Brand default rule catalogs.
3
+ *
4
+ * Each brand exports a fully-validated `Rule[]` parsed from its inlined
5
+ * TOML source at package-load time; a malformed default therefore fails
6
+ * fast rather than at first evaluate.
7
+ */
8
+ export { zivaDefaults } from './ziva';
9
+ export { luminxDefaults } from './luminx';
10
+ export { vivaDefaults } from './viva';
11
+ export { yogaringDefaults } from './yogaring';
12
+ export { parseCatalog } from './loader';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/defaults/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EAAE,YAAY,EAA4B,MAAM,UAAU,CAAA"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * TOML catalog parser for brand default rules.
3
+ *
4
+ * A catalog is a TOML document with one or more `[[rule]]` array-of-tables
5
+ * entries; each entry is passed through `RuleSchema.safeParse` so bad
6
+ * catalogs fail at package load time with a precise message.
7
+ *
8
+ * The parser only wraps `@iarna/toml` — a TOML syntax error is rethrown as
9
+ * a `RuleValidationError` with a catalog-aware prefix so consumers can
10
+ * locate the offending brand file.
11
+ */
12
+ import { type Rule } from '../schema';
13
+ export interface ParseCatalogOptions {
14
+ /** Human-readable catalog name for error messages (e.g. `'ziva'`). */
15
+ name?: string;
16
+ }
17
+ export declare function parseCatalog(toml: string, options?: ParseCatalogOptions): Rule[];
18
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/rules/defaults/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAmC,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAMtE,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,IAAI,EAAE,CA8BR"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * TOML catalog parser for brand default rules.
3
+ *
4
+ * A catalog is a TOML document with one or more `[[rule]]` array-of-tables
5
+ * entries; each entry is passed through `RuleSchema.safeParse` so bad
6
+ * catalogs fail at package load time with a precise message.
7
+ *
8
+ * The parser only wraps `@iarna/toml` — a TOML syntax error is rethrown as
9
+ * a `RuleValidationError` with a catalog-aware prefix so consumers can
10
+ * locate the offending brand file.
11
+ */
12
+ import TOML from '@iarna/toml';
13
+ import { RuleSchema, RuleValidationError } from '../schema';
14
+ export function parseCatalog(toml, options) {
15
+ const label = options?.name ?? 'catalog';
16
+ let doc;
17
+ try {
18
+ doc = TOML.parse(toml);
19
+ }
20
+ catch (err) {
21
+ throw new RuleValidationError(`${label}: TOML parse error — ${err.message}`);
22
+ }
23
+ const raw = doc.rule;
24
+ if (!Array.isArray(raw)) {
25
+ throw new RuleValidationError(`${label}: missing [[rule]] entries`);
26
+ }
27
+ const rules = [];
28
+ for (let i = 0; i < raw.length; i++) {
29
+ const parsed = RuleSchema.safeParse(raw[i]);
30
+ if (!parsed.success) {
31
+ throw new RuleValidationError(`${label}: rule ${i} invalid — ${parsed.error.message}`);
32
+ }
33
+ rules.push(parsed.data);
34
+ }
35
+ return rules;
36
+ }
37
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/rules/defaults/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,IAAI,MAAM,aAAa,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAa,MAAM,WAAW,CAAA;AAWtE,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,OAA6B;IAE7B,MAAM,KAAK,GAAG,OAAO,EAAE,IAAI,IAAI,SAAS,CAAA;IACxC,IAAI,GAAe,CAAA;IACnB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAA;IACtC,CAAC;IACD,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,IAAI,mBAAmB,CAC3B,GAAG,KAAK,wBAAyB,GAAa,CAAC,OAAO,EAAE,CACzD,CAAA;IACH,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAA;IACpB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,mBAAmB,CAC3B,GAAG,KAAK,4BAA4B,CACrC,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAW,EAAE,CAAA;IACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,mBAAmB,CAC3B,GAAG,KAAK,UAAU,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CACxD,CAAA;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * LuminX brand default rules. Sidecar: `luminx.toml`.
3
+ */
4
+ import type { Rule } from '../schema';
5
+ export declare const luminxDefaults: Rule[];
6
+ //# sourceMappingURL=luminx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"luminx.d.ts","sourceRoot":"","sources":["../../../src/rules/defaults/luminx.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AA6DrC,eAAO,MAAM,cAAc,EAAE,IAAI,EAA2C,CAAA"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * LuminX brand default rules. Sidecar: `luminx.toml`.
3
+ */
4
+ import { parseCatalog } from './loader';
5
+ const TOML = `
6
+ [[rule]]
7
+ id = "luminx.battery-low"
8
+ brand = "luminx"
9
+ name = "LuminX battery low"
10
+ description = "Device battery below 15% in the past hour."
11
+ metric = "device_battery"
12
+ window = "1h"
13
+ aggregation = "last"
14
+ compare = "less_than"
15
+ severity = "warn"
16
+
17
+ [rule.target]
18
+ type = "absolute"
19
+ value = 15
20
+
21
+ [rule.throttle]
22
+ minGapMinutes = 240
23
+ maxPerDay = 3
24
+
25
+ [[rule]]
26
+ id = "luminx.battery-critical"
27
+ brand = "luminx"
28
+ name = "LuminX battery critical"
29
+ description = "Device battery below 5% — connectivity likely to drop."
30
+ metric = "device_battery"
31
+ window = "1h"
32
+ aggregation = "last"
33
+ compare = "less_than"
34
+ severity = "critical"
35
+
36
+ [rule.target]
37
+ type = "absolute"
38
+ value = 5
39
+
40
+ [rule.throttle]
41
+ minGapMinutes = 60
42
+ maxPerDay = 4
43
+
44
+ [[rule]]
45
+ id = "luminx.disconnect-24h"
46
+ brand = "luminx"
47
+ name = "LuminX disconnected"
48
+ description = "No device battery events observed in the past 24h."
49
+ metric = "device_battery"
50
+ window = "24h"
51
+ aggregation = "count"
52
+ compare = "equals"
53
+ severity = "warn"
54
+
55
+ [rule.target]
56
+ type = "absolute"
57
+ value = 0
58
+
59
+ [rule.throttle]
60
+ minGapMinutes = 720
61
+ maxPerDay = 1
62
+ `;
63
+ export const luminxDefaults = parseCatalog(TOML, { name: 'luminx' });
64
+ //# sourceMappingURL=luminx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"luminx.js","sourceRoot":"","sources":["../../../src/rules/defaults/luminx.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAGvC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDZ,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAW,YAAY,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Viva brand default rules. Sidecar: `viva.toml`.
3
+ */
4
+ import type { Rule } from '../schema';
5
+ export declare const vivaDefaults: Rule[];
6
+ //# sourceMappingURL=viva.d.ts.map