@mongrov/analytics 0.5.0 → 0.7.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/README.md +23 -10
- package/UPGRADING.md +370 -0
- package/dist/core/device-events.d.ts +14 -0
- package/dist/core/device-events.d.ts.map +1 -0
- package/dist/core/device-events.js +13 -0
- package/dist/core/device-events.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 +21 -4
- package/dist/core/extensions.d.ts.map +1 -1
- package/dist/core/extensions.js +21 -4
- package/dist/core/extensions.js.map +1 -1
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +124 -18
- package/dist/core/factory.js.map +1 -1
- package/dist/core/hooks.js +1 -1
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/index.d.ts +11 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/insight.d.ts +44 -0
- package/dist/core/insight.d.ts.map +1 -0
- package/dist/core/insight.js +62 -0
- package/dist/core/insight.js.map +1 -0
- package/dist/core/machine.d.ts.map +1 -1
- package/dist/core/machine.js +9 -1
- package/dist/core/machine.js.map +1 -1
- package/dist/core/metric_metadata.d.ts +57 -2
- package/dist/core/metric_metadata.d.ts.map +1 -1
- package/dist/core/metric_metadata.js +36 -8
- package/dist/core/metric_metadata.js.map +1 -1
- package/dist/core/migrations.d.ts +6 -0
- package/dist/core/migrations.d.ts.map +1 -1
- package/dist/core/migrations.js +92 -4
- package/dist/core/migrations.js.map +1 -1
- package/dist/core/retention.d.ts +44 -13
- package/dist/core/retention.d.ts.map +1 -1
- package/dist/core/retention.js +50 -22
- package/dist/core/retention.js.map +1 -1
- package/dist/core/schemas.d.ts +74 -3
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/schemas.js +168 -5
- package/dist/core/schemas.js.map +1 -1
- package/dist/core/table_metadata.d.ts.map +1 -1
- package/dist/core/table_metadata.js +4 -0
- package/dist/core/table_metadata.js.map +1 -1
- package/dist/core/types.d.ts +45 -10
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/warehouse.d.ts +28 -0
- package/dist/core/warehouse.d.ts.map +1 -1
- package/dist/core/warehouse.js +44 -0
- package/dist/core/warehouse.js.map +1 -1
- package/dist/core/watermark-cache.d.ts +75 -0
- package/dist/core/watermark-cache.d.ts.map +1 -0
- package/dist/core/watermark-cache.js +0 -0
- package/dist/core/watermark-cache.js.map +1 -0
- package/dist/rules/__fakes__/fakeEngine.d.ts +2 -0
- package/dist/rules/__fakes__/fakeEngine.d.ts.map +1 -1
- package/dist/rules/__fakes__/fakeEngine.js +4 -0
- package/dist/rules/__fakes__/fakeEngine.js.map +1 -1
- package/dist/rules/baseline-reader.d.ts +55 -0
- package/dist/rules/baseline-reader.d.ts.map +1 -0
- package/dist/rules/baseline-reader.js +111 -0
- package/dist/rules/baseline-reader.js.map +1 -0
- package/dist/rules/compiler.d.ts +36 -1
- package/dist/rules/compiler.d.ts.map +1 -1
- package/dist/rules/compiler.js +185 -39
- package/dist/rules/compiler.js.map +1 -1
- package/dist/rules/defaults/luminx.d.ts.map +1 -1
- package/dist/rules/defaults/luminx.js +8 -0
- package/dist/rules/defaults/luminx.js.map +1 -1
- package/dist/rules/defaults/viva.d.ts.map +1 -1
- package/dist/rules/defaults/viva.js +7 -0
- package/dist/rules/defaults/viva.js.map +1 -1
- package/dist/rules/defaults/yogaring.d.ts.map +1 -1
- package/dist/rules/defaults/yogaring.js +7 -0
- package/dist/rules/defaults/yogaring.js.map +1 -1
- package/dist/rules/defaults/ziva.d.ts.map +1 -1
- package/dist/rules/defaults/ziva.js +100 -24
- package/dist/rules/defaults/ziva.js.map +1 -1
- package/dist/rules/evaluator.d.ts +14 -3
- package/dist/rules/evaluator.d.ts.map +1 -1
- package/dist/rules/evaluator.js +157 -5
- package/dist/rules/evaluator.js.map +1 -1
- package/dist/rules/factory.d.ts.map +1 -1
- package/dist/rules/factory.js +5 -1
- package/dist/rules/factory.js.map +1 -1
- package/dist/rules/index.d.ts +2 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +2 -1
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/schema.d.ts +78 -7
- package/dist/rules/schema.d.ts.map +1 -1
- package/dist/rules/schema.js +44 -0
- package/dist/rules/schema.js.map +1 -1
- package/dist/rules/types.d.ts +16 -1
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/validator.d.ts.map +1 -1
- package/dist/rules/validator.js +56 -0
- package/dist/rules/validator.js.map +1 -1
- package/dist/sync/baseline-compute.d.ts +63 -0
- package/dist/sync/baseline-compute.d.ts.map +1 -0
- package/dist/sync/baseline-compute.js +211 -0
- package/dist/sync/baseline-compute.js.map +1 -0
- package/dist/sync/events.d.ts +19 -5
- package/dist/sync/events.d.ts.map +1 -1
- package/dist/sync/events.js +17 -0
- package/dist/sync/events.js.map +1 -1
- package/dist/sync/factory.d.ts +25 -4
- package/dist/sync/factory.d.ts.map +1 -1
- package/dist/sync/factory.js +109 -15
- package/dist/sync/factory.js.map +1 -1
- package/dist/sync/fetcher.d.ts +15 -2
- package/dist/sync/fetcher.d.ts.map +1 -1
- package/dist/sync/fetcher.js +59 -11
- package/dist/sync/fetcher.js.map +1 -1
- package/dist/sync/flusher.d.ts +52 -2
- package/dist/sync/flusher.d.ts.map +1 -1
- package/dist/sync/flusher.js +78 -9
- package/dist/sync/flusher.js.map +1 -1
- package/dist/sync/index.d.ts +4 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mapper/activity.d.ts.map +1 -1
- package/dist/sync/mapper/activity.js +2 -1
- package/dist/sync/mapper/activity.js.map +1 -1
- package/dist/sync/mapper/battery.d.ts +14 -7
- package/dist/sync/mapper/battery.d.ts.map +1 -1
- package/dist/sync/mapper/battery.js +13 -7
- package/dist/sync/mapper/battery.js.map +1 -1
- package/dist/sync/mapper/firmware.d.ts.map +1 -1
- package/dist/sync/mapper/firmware.js +4 -1
- package/dist/sync/mapper/firmware.js.map +1 -1
- package/dist/sync/mapper/ring-config.js +1 -1
- package/dist/sync/mapper/ring-config.js.map +1 -1
- package/dist/sync/mapper/schema.d.ts +411 -0
- package/dist/sync/mapper/schema.d.ts.map +1 -0
- package/dist/sync/mapper/schema.js +128 -0
- package/dist/sync/mapper/schema.js.map +1 -0
- package/dist/sync/mapper/sleep.d.ts +58 -13
- package/dist/sync/mapper/sleep.d.ts.map +1 -1
- package/dist/sync/mapper/sleep.js +141 -44
- package/dist/sync/mapper/sleep.js.map +1 -1
- package/dist/sync/mapper/types.d.ts +75 -10
- package/dist/sync/mapper/types.d.ts.map +1 -1
- package/dist/sync/scheduler.d.ts +7 -0
- package/dist/sync/scheduler.d.ts.map +1 -1
- package/dist/sync/scheduler.js +14 -0
- package/dist/sync/scheduler.js.map +1 -1
- package/dist/tools/__fakes__/engine.d.ts +2 -0
- package/dist/tools/__fakes__/engine.d.ts.map +1 -1
- package/dist/tools/__fakes__/engine.js +10 -0
- package/dist/tools/__fakes__/engine.js.map +1 -1
- package/dist/tools/authorize.d.ts +19 -12
- package/dist/tools/authorize.d.ts.map +1 -1
- package/dist/tools/authorize.js +31 -40
- package/dist/tools/authorize.js.map +1 -1
- package/dist/tools/factory.d.ts +2 -0
- package/dist/tools/factory.d.ts.map +1 -1
- package/dist/tools/factory.js +12 -1
- package/dist/tools/factory.js.map +1 -1
- package/dist/tools/formatters/copy-guidelines.d.ts +57 -0
- package/dist/tools/formatters/copy-guidelines.d.ts.map +1 -0
- package/dist/tools/formatters/copy-guidelines.js +110 -0
- package/dist/tools/formatters/copy-guidelines.js.map +1 -0
- package/dist/tools/formatters/index.d.ts +14 -0
- package/dist/tools/formatters/index.d.ts.map +1 -0
- package/dist/tools/formatters/index.js +14 -0
- package/dist/tools/formatters/index.js.map +1 -0
- package/dist/tools/formatters/spo2.d.ts +21 -0
- package/dist/tools/formatters/spo2.d.ts.map +1 -0
- package/dist/tools/formatters/spo2.js +79 -0
- package/dist/tools/formatters/spo2.js.map +1 -0
- package/dist/tools/{formatters.d.ts → formatters/text.d.ts} +1 -1
- package/dist/tools/formatters/text.d.ts.map +1 -0
- package/dist/tools/{formatters.js → formatters/text.js} +1 -1
- package/dist/tools/formatters/text.js.map +1 -0
- package/dist/tools/impls/activity.d.ts +2 -2
- package/dist/tools/impls/activity.js +7 -3
- package/dist/tools/impls/activity.js.map +1 -1
- package/dist/tools/impls/anomaly.d.ts +2 -2
- package/dist/tools/impls/anomaly.js +8 -4
- package/dist/tools/impls/anomaly.js.map +1 -1
- package/dist/tools/impls/compare.d.ts +2 -2
- package/dist/tools/impls/compare.js +8 -4
- package/dist/tools/impls/compare.js.map +1 -1
- package/dist/tools/impls/hrv.d.ts +2 -2
- package/dist/tools/impls/hrv.js +6 -2
- package/dist/tools/impls/hrv.js.map +1 -1
- package/dist/tools/impls/insights.d.ts +4 -4
- package/dist/tools/impls/insights.d.ts.map +1 -1
- package/dist/tools/impls/insights.js +10 -3
- package/dist/tools/impls/insights.js.map +1 -1
- package/dist/tools/impls/sleep.d.ts +2 -2
- package/dist/tools/impls/sleep.js +6 -2
- package/dist/tools/impls/sleep.js.map +1 -1
- package/dist/tools/impls/spo2.d.ts +52 -0
- package/dist/tools/impls/spo2.d.ts.map +1 -0
- package/dist/tools/impls/spo2.js +65 -0
- package/dist/tools/impls/spo2.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp/guard.d.ts +28 -11
- package/dist/tools/mcp/guard.d.ts.map +1 -1
- package/dist/tools/mcp/guard.js +45 -15
- package/dist/tools/mcp/guard.js.map +1 -1
- package/dist/tools/mcp/index.d.ts +1 -1
- package/dist/tools/mcp/index.d.ts.map +1 -1
- package/dist/tools/mcp/index.js +1 -1
- package/dist/tools/mcp/index.js.map +1 -1
- package/dist/tools/mcp/server.d.ts.map +1 -1
- package/dist/tools/mcp/server.js +4 -0
- package/dist/tools/mcp/server.js.map +1 -1
- package/dist/tools/mcp/transports/http.d.ts.map +1 -1
- package/dist/tools/mcp/transports/http.js +4 -0
- package/dist/tools/mcp/transports/http.js.map +1 -1
- package/package.json +8 -5
- package/dist/tools/formatters.d.ts.map +0 -1
- package/dist/tools/formatters.js.map +0 -1
|
@@ -9,13 +9,17 @@
|
|
|
9
9
|
* every active brand-matching rule, fanned out over family members.
|
|
10
10
|
*
|
|
11
11
|
* Both funnel through `evaluateRule(rule, ctx)` which is the throttle-gated
|
|
12
|
-
* single-user check. A returned `RuleViolation` is
|
|
13
|
-
* the shared emitter and
|
|
12
|
+
* single-user check. A returned `RuleViolation` is (1) persisted to the
|
|
13
|
+
* `insight` table, (2) emitted synchronously via the shared emitter, and
|
|
14
|
+
* (3) echoed onto the app event bus (`threshold:violation` +
|
|
15
|
+
* `insight:insert`) when one is configured — principle 35 fire-and-forget:
|
|
16
|
+
* a failed insight INSERT is logged, never thrown into the eval loop, and
|
|
17
|
+
* the private `'violation'` emission still happens.
|
|
14
18
|
*
|
|
15
19
|
* `analytics.execute` throws are caught + logged; a failing rule never
|
|
16
20
|
* blocks the rest of the pass.
|
|
17
21
|
*/
|
|
18
|
-
import type { AnalyticsEngine } from '../core/types';
|
|
22
|
+
import type { AnalyticsEngine, EventBus, KVStore } from '../core/types';
|
|
19
23
|
import type { CompilerCache } from './compiler-cache';
|
|
20
24
|
import type { createEmitter } from './emitter';
|
|
21
25
|
import type { RulesRegistry } from './registry';
|
|
@@ -23,6 +27,11 @@ import type { createThrottleStore } from './throttle';
|
|
|
23
27
|
import type { Clock, RuleViolation, RulesLogger, FlushSummary } from './types';
|
|
24
28
|
export interface EvaluatorConfig {
|
|
25
29
|
registry: RulesRegistry;
|
|
30
|
+
/**
|
|
31
|
+
* KVStore for `target.type: 'user_setting'` threshold resolution
|
|
32
|
+
* (Sprint 5 T-23). Keys are `analytics:{userId}:{rule.target.key}`.
|
|
33
|
+
*/
|
|
34
|
+
storage?: KVStore;
|
|
26
35
|
cache: CompilerCache;
|
|
27
36
|
throttle: ReturnType<typeof createThrottleStore>;
|
|
28
37
|
emitter: ReturnType<typeof createEmitter>;
|
|
@@ -34,6 +43,8 @@ export interface EvaluatorConfig {
|
|
|
34
43
|
familyId: string;
|
|
35
44
|
}) => Promise<string[]>;
|
|
36
45
|
clock: Clock;
|
|
46
|
+
/** Optional app bus for `threshold:violation` / `insight:insert`. */
|
|
47
|
+
eventBus?: EventBus;
|
|
37
48
|
logger?: RulesLogger;
|
|
38
49
|
}
|
|
39
50
|
export interface Evaluator {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/rules/evaluator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/rules/evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,KAAK,EACV,KAAK,EACL,aAAa,EACb,WAAW,EACX,YAAY,EACb,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,aAAa,CAAA;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;IAChD,OAAO,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;IACzC,SAAS,EAAE,eAAe,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,qBAAqB,EAAE,CAAC,GAAG,EAAE;QAC3B,KAAK,EAAE,MAAM,CAAA;QACb,QAAQ,EAAE,MAAM,CAAA;KACjB,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,qEAAqE;IACrE,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAkBD,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IAC9D,iBAAiB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;CAC9C;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAkSlE"}
|
package/dist/rules/evaluator.js
CHANGED
|
@@ -9,16 +9,121 @@
|
|
|
9
9
|
* every active brand-matching rule, fanned out over family members.
|
|
10
10
|
*
|
|
11
11
|
* Both funnel through `evaluateRule(rule, ctx)` which is the throttle-gated
|
|
12
|
-
* single-user check. A returned `RuleViolation` is
|
|
13
|
-
* the shared emitter and
|
|
12
|
+
* single-user check. A returned `RuleViolation` is (1) persisted to the
|
|
13
|
+
* `insight` table, (2) emitted synchronously via the shared emitter, and
|
|
14
|
+
* (3) echoed onto the app event bus (`threshold:violation` +
|
|
15
|
+
* `insight:insert`) when one is configured — principle 35 fire-and-forget:
|
|
16
|
+
* a failed insight INSERT is logged, never thrown into the eval loop, and
|
|
17
|
+
* the private `'violation'` emission still happens.
|
|
14
18
|
*
|
|
15
19
|
* `analytics.execute` throws are caught + logged; a failing rule never
|
|
16
20
|
* blocks the rest of the pass.
|
|
17
21
|
*/
|
|
22
|
+
import { nanoid } from 'nanoid';
|
|
18
23
|
import { METRIC_METADATA } from '../core/metric_metadata';
|
|
19
24
|
import { AnalyticsError } from '../core/errors';
|
|
25
|
+
import { USER_SETTING_PARAM } from './compiler';
|
|
26
|
+
/**
|
|
27
|
+
* Rule severity → insight severity. The rule schema keeps the historical
|
|
28
|
+
* `'critical'` tier (TOML catalogs ship it); the `insight` table + registry
|
|
29
|
+
* contract use `'urgent'` (spec §Table schema).
|
|
30
|
+
*/
|
|
31
|
+
const INSIGHT_SEVERITY = {
|
|
32
|
+
info: 'info',
|
|
33
|
+
warn: 'warn',
|
|
34
|
+
critical: 'urgent',
|
|
35
|
+
};
|
|
20
36
|
export function createEvaluator(config) {
|
|
21
|
-
const { registry, cache, throttle, emitter, analytics, brand, familyId, familyMembersProvider, clock, logger, } = config;
|
|
37
|
+
const { registry, cache, throttle, emitter, analytics, brand, familyId, familyMembersProvider, clock, eventBus, logger, storage, } = config;
|
|
38
|
+
/**
|
|
39
|
+
* Per-eval-batch cache of resolved user settings. A batch may evaluate
|
|
40
|
+
* the same (user, key) across several rules; re-reading MMKV each time
|
|
41
|
+
* would be wasteful, and — worse — could resolve two different
|
|
42
|
+
* thresholds within one pass if the user saved mid-batch.
|
|
43
|
+
*/
|
|
44
|
+
let settingCache = new Map();
|
|
45
|
+
/**
|
|
46
|
+
* T-23 — resolve a `user_setting` threshold at eval time.
|
|
47
|
+
*
|
|
48
|
+
* Falls back to the rule's `defaultValue` when the user has never set
|
|
49
|
+
* one, or when the stored value is unusable. Never throws: a broken
|
|
50
|
+
* setting must not silence the alert the user explicitly asked for — the
|
|
51
|
+
* shipped default is the safer answer.
|
|
52
|
+
*/
|
|
53
|
+
async function resolveUserSetting(userId, key, defaultValue, ruleId) {
|
|
54
|
+
const cacheKey = `${userId} ${key}`;
|
|
55
|
+
const cached = settingCache.get(cacheKey);
|
|
56
|
+
if (cached !== undefined)
|
|
57
|
+
return cached;
|
|
58
|
+
let value = defaultValue;
|
|
59
|
+
let source = 'default';
|
|
60
|
+
if (storage) {
|
|
61
|
+
try {
|
|
62
|
+
const stored = await storage.get(`analytics:${userId}:${key}`);
|
|
63
|
+
const parsed = typeof stored === 'string' ? Number(stored) : stored;
|
|
64
|
+
if (typeof parsed === 'number' && Number.isFinite(parsed)) {
|
|
65
|
+
value = parsed;
|
|
66
|
+
source = 'kvstore';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
logger?.warn('rules.evaluator: user_setting read failed, using default', {
|
|
71
|
+
ruleId,
|
|
72
|
+
userId,
|
|
73
|
+
key,
|
|
74
|
+
err: err.message,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (source === 'default') {
|
|
79
|
+
logger?.debug('rules.evaluator: user_setting fallback to defaultValue', {
|
|
80
|
+
ruleId, userId, key, defaultValue,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
settingCache.set(cacheKey, value);
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Persist a violation as an `insight` row (spec parent §23 / principle
|
|
88
|
+
* 35). Returns the new insight_id, or null when the INSERT failed —
|
|
89
|
+
* failure is logged and swallowed so evaluation always continues.
|
|
90
|
+
* Unqualified `insight` resolves to the local catalog (insights are
|
|
91
|
+
* local-first; the pusher syncs the table to R2).
|
|
92
|
+
*/
|
|
93
|
+
async function persistInsight(rule, violation) {
|
|
94
|
+
const insightId = nanoid(24);
|
|
95
|
+
try {
|
|
96
|
+
await analytics.execute(`INSERT INTO insight (insight_id, ts, brand, family_id, user_id, rule_id, `
|
|
97
|
+
+ `metric, kind, severity, title, body, evidence, acknowledged_at, dismissed_at) `
|
|
98
|
+
+ `VALUES ($insightId, CAST($ts AS TIMESTAMP), $brand, $familyId, $userId, $ruleId, `
|
|
99
|
+
+ `$metric, 'threshold', $severity, $title, $body, $evidence, NULL, NULL)`, {
|
|
100
|
+
insightId,
|
|
101
|
+
ts: violation.observedAt.toISOString(),
|
|
102
|
+
brand: violation.brand,
|
|
103
|
+
familyId: violation.familyId,
|
|
104
|
+
userId: violation.userId,
|
|
105
|
+
ruleId: rule.id,
|
|
106
|
+
metric: rule.metric,
|
|
107
|
+
severity: INSIGHT_SEVERITY[rule.severity],
|
|
108
|
+
title: rule.name,
|
|
109
|
+
body: rule.description ?? null,
|
|
110
|
+
evidence: JSON.stringify({
|
|
111
|
+
...violation.evidence,
|
|
112
|
+
observedValue: violation.observedValue,
|
|
113
|
+
thresholdValue: violation.thresholdValue,
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
return insightId;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
logger?.error('rules.evaluator: insight insert failed', {
|
|
120
|
+
ruleId: rule.id,
|
|
121
|
+
userId: violation.userId,
|
|
122
|
+
err: err.message,
|
|
123
|
+
});
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
22
127
|
async function evaluateRule(rule, ctx) {
|
|
23
128
|
const throttled = await throttle.isThrottled(rule.id, ctx.userId, rule.throttle);
|
|
24
129
|
if (throttled) {
|
|
@@ -29,14 +134,21 @@ export function createEvaluator(config) {
|
|
|
29
134
|
return null;
|
|
30
135
|
}
|
|
31
136
|
let rows;
|
|
137
|
+
// Hoisted so the violation's evidence can report which threshold
|
|
138
|
+
// actually applied.
|
|
139
|
+
let compiled;
|
|
140
|
+
let params = {};
|
|
32
141
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
142
|
+
compiled = cache.getCompiled(rule, registry.rev);
|
|
143
|
+
params = {
|
|
35
144
|
userId: ctx.userId,
|
|
36
145
|
brand,
|
|
37
146
|
familyId: ctx.familyId,
|
|
38
147
|
...compiled.params,
|
|
39
148
|
};
|
|
149
|
+
if (compiled.userSettingKey !== undefined) {
|
|
150
|
+
params[USER_SETTING_PARAM] = await resolveUserSetting(ctx.userId, compiled.userSettingKey, compiled.userSettingDefault ?? 0, rule.id);
|
|
151
|
+
}
|
|
40
152
|
rows = await analytics.execute(compiled.sql, params);
|
|
41
153
|
}
|
|
42
154
|
catch (err) {
|
|
@@ -66,11 +178,49 @@ export function createEvaluator(config) {
|
|
|
66
178
|
window: rule.window,
|
|
67
179
|
aggregation: rule.aggregation,
|
|
68
180
|
compare: rule.compare,
|
|
181
|
+
context: rule.context,
|
|
182
|
+
consecutive: rule.consecutive,
|
|
69
183
|
target: rule.target,
|
|
184
|
+
// T-23: which threshold actually applied, and where it came from.
|
|
185
|
+
// Without this a support ticket about "the alert fired at the
|
|
186
|
+
// wrong number" is unanswerable.
|
|
187
|
+
settingsUsed: compiled?.userSettingKey === undefined
|
|
188
|
+
? undefined
|
|
189
|
+
: { key: compiled.userSettingKey, value: params[USER_SETTING_PARAM] },
|
|
70
190
|
},
|
|
71
191
|
};
|
|
72
192
|
await throttle.recordFire(rule.id, ctx.userId);
|
|
193
|
+
const insightId = await persistInsight(rule, violation);
|
|
73
194
|
emitter.emit('violation', violation);
|
|
195
|
+
if (eventBus) {
|
|
196
|
+
// Bus handlers are app code — a throwing subscriber must not take
|
|
197
|
+
// down the eval loop (fire-and-forget, principle 35).
|
|
198
|
+
try {
|
|
199
|
+
eventBus.emit('threshold:violation', {
|
|
200
|
+
ruleId: rule.id,
|
|
201
|
+
userId: ctx.userId,
|
|
202
|
+
metric: rule.metric,
|
|
203
|
+
severity: INSIGHT_SEVERITY[rule.severity],
|
|
204
|
+
insightId,
|
|
205
|
+
observedValue: violation.observedValue,
|
|
206
|
+
thresholdValue: violation.thresholdValue,
|
|
207
|
+
});
|
|
208
|
+
// `insight:insert` means "a row landed" — skip it when the INSERT failed.
|
|
209
|
+
if (insightId) {
|
|
210
|
+
eventBus.emit('insight:insert', {
|
|
211
|
+
insightId,
|
|
212
|
+
userId: ctx.userId,
|
|
213
|
+
metric: rule.metric,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
logger?.error('rules.evaluator: event bus emit threw', {
|
|
219
|
+
ruleId: rule.id,
|
|
220
|
+
err: err.message,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
74
224
|
return violation;
|
|
75
225
|
}
|
|
76
226
|
function rulesForTables(rules, affectedTables) {
|
|
@@ -82,6 +232,7 @@ export function createEvaluator(config) {
|
|
|
82
232
|
}
|
|
83
233
|
return {
|
|
84
234
|
async evaluateOnBatch(batch) {
|
|
235
|
+
settingCache = new Map();
|
|
85
236
|
const active = registry.getByBrand(brand);
|
|
86
237
|
const relevant = rulesForTables(active, batch.affectedTables);
|
|
87
238
|
if (relevant.length === 0 || batch.affectedUserIds.length === 0) {
|
|
@@ -98,6 +249,7 @@ export function createEvaluator(config) {
|
|
|
98
249
|
return violations;
|
|
99
250
|
},
|
|
100
251
|
async evaluateScheduled() {
|
|
252
|
+
settingCache = new Map();
|
|
101
253
|
if (analytics.state !== 'attached') {
|
|
102
254
|
throw new AnalyticsError('not_attached', `evaluateScheduled requires an attached analytics engine (state=${analytics.state})`);
|
|
103
255
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../src/rules/evaluator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../src/rules/evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/rules/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAE7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/rules/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAE7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CA2ExE"}
|
package/dist/rules/factory.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createEvaluator } from './evaluator';
|
|
|
14
14
|
import { createRulesRegistry } from './registry';
|
|
15
15
|
import { createThrottleStore } from './throttle';
|
|
16
16
|
export function createRulesEngine(config) {
|
|
17
|
-
const { analytics, storage, familyMembersProvider, brand, familyId, logger, clock = () => new Date(), } = config;
|
|
17
|
+
const { analytics, storage, familyMembersProvider, brand, familyId, eventBus, logger, clock = () => new Date(), } = config;
|
|
18
18
|
const emitter = createEmitter({ logger });
|
|
19
19
|
const throttle = createThrottleStore({ storage, clock, logger });
|
|
20
20
|
const cache = createCompilerCache();
|
|
@@ -33,7 +33,11 @@ export function createRulesEngine(config) {
|
|
|
33
33
|
familyId,
|
|
34
34
|
familyMembersProvider,
|
|
35
35
|
clock,
|
|
36
|
+
eventBus,
|
|
36
37
|
logger,
|
|
38
|
+
// Sprint 5 T-23 — same KVStore the throttle uses; user_setting
|
|
39
|
+
// thresholds live under `analytics:{userId}:{key}`.
|
|
40
|
+
storage,
|
|
37
41
|
});
|
|
38
42
|
let closed = false;
|
|
39
43
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/rules/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAGhD,MAAM,UAAU,iBAAiB,CAAC,MAAyB;IACzD,MAAM,EACJ,SAAS,EACT,OAAO,EACP,qBAAqB,EACrB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAK,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,GACzB,GAAG,MAAM,CAAA;IAEV,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;IACzC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;IAChE,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAA;IACnC,MAAM,QAAQ,GAAG,mBAAmB,CAAC;QACnC,OAAO;QACP,MAAM;QACN,YAAY,EAAE,KAAK;KACpB,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,eAAe,CAAC;QAChC,QAAQ;QACR,KAAK;QACL,QAAQ;QACR,OAAO;QACP,SAAS;QACT,KAAK;QACL,QAAQ;QACR,qBAAqB;QACrB,KAAK;QACL,MAAM;
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/rules/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/index.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export { RuleSchema, RuleValidationError, TargetSchema, ThrottleSchema, AGGREGAT
|
|
|
19
19
|
export type { Aggregation, Compare, Rule, Severity, Target, Throttle, Window, } from './schema';
|
|
20
20
|
export type { Clock, CompiledRule, FlushSummary, RuleSeverity, RuleViolation, RulesEngine, RulesEngineConfig, RulesLogger, Unsubscribe, } from './types';
|
|
21
21
|
export { allowedWindowsFor, validateRule } from './validator';
|
|
22
|
-
export {
|
|
22
|
+
export { createBaselineReader, type Baseline, type BaselineReader, type BaselineReaderConfig, } from './baseline-reader';
|
|
23
|
+
export { USER_SETTING_PARAM, emitContextJoin, viewFor, compileRule, sanitizeIdent } from './compiler';
|
|
23
24
|
export { createCompilerCache, type CompilerCache } from './compiler-cache';
|
|
24
25
|
export { createRulesRegistry, type CreateRegistryConfig, type RulesRegistry, } from './registry';
|
|
25
26
|
export { createThrottleStore, type CreateThrottleConfig, type ThrottleStore, } from './throttle';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,OAAO,GACR,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,WAAW,EACX,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,MAAM,GACP,MAAM,UAAU,CAAA;AAEjB,YAAY,EACV,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,WAAW,GACZ,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG7D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,OAAO,GACR,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,WAAW,EACX,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,MAAM,GACP,MAAM,UAAU,CAAA;AAEjB,YAAY,EACV,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,WAAW,GACZ,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG7D,OAAO,EACL,oBAAoB,EACpB,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACrG,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAI1E,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,YAAY,GAClB,MAAM,WAAW,CAAA;AAGlB,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAG7C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA;AAGtC,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,yBAAyB,CAAA"}
|
package/dist/rules/index.js
CHANGED
|
@@ -20,7 +20,8 @@ export { RuleSchema, RuleValidationError, TargetSchema, ThrottleSchema, AGGREGAT
|
|
|
20
20
|
// R2 — validator
|
|
21
21
|
export { allowedWindowsFor, validateRule } from './validator';
|
|
22
22
|
// R3 — compiler + cache
|
|
23
|
-
export {
|
|
23
|
+
export { createBaselineReader, } from './baseline-reader';
|
|
24
|
+
export { USER_SETTING_PARAM, emitContextJoin, viewFor, compileRule, sanitizeIdent } from './compiler';
|
|
24
25
|
export { createCompilerCache } from './compiler-cache';
|
|
25
26
|
// R4-R6 — registry / throttle / emitter (exposed for advanced composition
|
|
26
27
|
// or test doubles; typical apps go through `createRulesEngine`).
|
package/dist/rules/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,sBAAsB;AACtB,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,OAAO,GACR,MAAM,UAAU,CAAA;AAuBjB,iBAAiB;AACjB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE7D,wBAAwB;AACxB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,sBAAsB;AACtB,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,OAAO,GACR,MAAM,UAAU,CAAA;AAuBjB,iBAAiB;AACjB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE7D,wBAAwB;AACxB,OAAO,EACL,oBAAoB,GAIrB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACrG,OAAO,EAAE,mBAAmB,EAAsB,MAAM,kBAAkB,CAAA;AAE1E,0EAA0E;AAC1E,iEAAiE;AACjE,OAAO,EACL,mBAAmB,GAGpB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,mBAAmB,GAGpB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,aAAa,GAGd,MAAM,WAAW,CAAA;AAElB,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAE7C,sBAAsB;AACtB,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA;AAEtC,cAAc;AACd,OAAO,EACL,iBAAiB,GAGlB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,eAAe,GAEhB,MAAM,yBAAyB,CAAA"}
|
package/dist/rules/schema.d.ts
CHANGED
|
@@ -19,6 +19,19 @@ export declare const COMPARES: readonly ["less_than", "greater_than", "equals",
|
|
|
19
19
|
export type Compare = (typeof COMPARES)[number];
|
|
20
20
|
export declare const SEVERITIES: readonly ["info", "warn", "critical"];
|
|
21
21
|
export type Severity = (typeof SEVERITIES)[number];
|
|
22
|
+
/**
|
|
23
|
+
* Physiological context the rule restricts its samples to (Sprint 5 §4).
|
|
24
|
+
*
|
|
25
|
+
* - `any` — every sample in the window
|
|
26
|
+
* - `asleep` — samples falling inside a `v_sleep_session` interval
|
|
27
|
+
* - `resting` — samples in a minute with zero steps
|
|
28
|
+
*
|
|
29
|
+
* Context is a JOIN, not a post-filter: "SpO₂ during sleep" means the
|
|
30
|
+
* aggregate is computed over sleep samples only, not computed over
|
|
31
|
+
* everything and then labelled.
|
|
32
|
+
*/
|
|
33
|
+
export declare const CONTEXTS: readonly ["any", "asleep", "resting"];
|
|
34
|
+
export type RuleContext = (typeof CONTEXTS)[number];
|
|
22
35
|
export declare const TargetSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
23
36
|
type: z.ZodLiteral<"absolute">;
|
|
24
37
|
value: z.ZodNumber;
|
|
@@ -64,6 +77,23 @@ export declare const TargetSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<
|
|
|
64
77
|
type: "range";
|
|
65
78
|
min: number;
|
|
66
79
|
max: number;
|
|
80
|
+
}>, z.ZodObject<{
|
|
81
|
+
type: z.ZodLiteral<"user_setting">;
|
|
82
|
+
/** KVStore key suffix, e.g. `user:spo2SafeLevel`. See the namespace registry. */
|
|
83
|
+
key: z.ZodString;
|
|
84
|
+
/** Used when the user has never set the value. */
|
|
85
|
+
defaultValue: z.ZodNumber;
|
|
86
|
+
compare: z.ZodDefault<z.ZodEnum<["as_configured"]>>;
|
|
87
|
+
}, "strip", z.ZodTypeAny, {
|
|
88
|
+
type: "user_setting";
|
|
89
|
+
key: string;
|
|
90
|
+
defaultValue: number;
|
|
91
|
+
compare: "as_configured";
|
|
92
|
+
}, {
|
|
93
|
+
type: "user_setting";
|
|
94
|
+
key: string;
|
|
95
|
+
defaultValue: number;
|
|
96
|
+
compare?: "as_configured" | undefined;
|
|
67
97
|
}>]>;
|
|
68
98
|
export type Target = z.infer<typeof TargetSchema>;
|
|
69
99
|
export declare const ThrottleSchema: z.ZodDefault<z.ZodObject<{
|
|
@@ -82,10 +112,20 @@ export declare const RuleSchema: z.ZodObject<{
|
|
|
82
112
|
brand: z.ZodOptional<z.ZodString>;
|
|
83
113
|
name: z.ZodString;
|
|
84
114
|
description: z.ZodOptional<z.ZodString>;
|
|
85
|
-
metric: z.ZodEnum<["spo2" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score"
|
|
115
|
+
metric: z.ZodEnum<["spo2" | "device_battery" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score", ...("spo2" | "device_battery" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score")[]]>;
|
|
86
116
|
window: z.ZodEnum<["1h", "6h", "24h", "3d", "7d", "30d"]>;
|
|
87
117
|
aggregation: z.ZodDefault<z.ZodEnum<["avg", "min", "max", "sum", "last", "count"]>>;
|
|
88
118
|
compare: z.ZodEnum<["less_than", "greater_than", "equals", "not_equals", "between"]>;
|
|
119
|
+
/** Restrict samples to a physiological context. Default `any`. */
|
|
120
|
+
context: z.ZodDefault<z.ZodEnum<["any", "asleep", "resting"]>>;
|
|
121
|
+
/**
|
|
122
|
+
* Require `n` ADJACENT breaching samples before the rule fires.
|
|
123
|
+
*
|
|
124
|
+
* Omitted or `1` means "any breach in the window", which the aggregate
|
|
125
|
+
* path already expresses (e.g. `aggregation: 'min'` + `less_than`).
|
|
126
|
+
* `>= 2` switches the compiler to run-length detection.
|
|
127
|
+
*/
|
|
128
|
+
consecutive: z.ZodOptional<z.ZodNumber>;
|
|
89
129
|
target: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
90
130
|
type: z.ZodLiteral<"absolute">;
|
|
91
131
|
value: z.ZodNumber;
|
|
@@ -131,6 +171,23 @@ export declare const RuleSchema: z.ZodObject<{
|
|
|
131
171
|
type: "range";
|
|
132
172
|
min: number;
|
|
133
173
|
max: number;
|
|
174
|
+
}>, z.ZodObject<{
|
|
175
|
+
type: z.ZodLiteral<"user_setting">;
|
|
176
|
+
/** KVStore key suffix, e.g. `user:spo2SafeLevel`. See the namespace registry. */
|
|
177
|
+
key: z.ZodString;
|
|
178
|
+
/** Used when the user has never set the value. */
|
|
179
|
+
defaultValue: z.ZodNumber;
|
|
180
|
+
compare: z.ZodDefault<z.ZodEnum<["as_configured"]>>;
|
|
181
|
+
}, "strip", z.ZodTypeAny, {
|
|
182
|
+
type: "user_setting";
|
|
183
|
+
key: string;
|
|
184
|
+
defaultValue: number;
|
|
185
|
+
compare: "as_configured";
|
|
186
|
+
}, {
|
|
187
|
+
type: "user_setting";
|
|
188
|
+
key: string;
|
|
189
|
+
defaultValue: number;
|
|
190
|
+
compare?: "as_configured" | undefined;
|
|
134
191
|
}>]>;
|
|
135
192
|
severity: z.ZodEnum<["info", "warn", "critical"]>;
|
|
136
193
|
throttle: z.ZodDefault<z.ZodObject<{
|
|
@@ -167,12 +224,18 @@ export declare const RuleSchema: z.ZodObject<{
|
|
|
167
224
|
type: "range";
|
|
168
225
|
min: number;
|
|
169
226
|
max: number;
|
|
227
|
+
} | {
|
|
228
|
+
type: "user_setting";
|
|
229
|
+
key: string;
|
|
230
|
+
defaultValue: number;
|
|
231
|
+
compare: "as_configured";
|
|
170
232
|
};
|
|
233
|
+
context: "any" | "asleep" | "resting";
|
|
171
234
|
name: string;
|
|
172
|
-
metric: "spo2" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score"
|
|
173
|
-
window: "1h" | "6h" | "24h" | "3d" | "7d" | "30d";
|
|
174
|
-
aggregation: "avg" | "min" | "max" | "sum" | "last" | "count";
|
|
235
|
+
metric: "spo2" | "device_battery" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score";
|
|
175
236
|
compare: "less_than" | "greater_than" | "equals" | "not_equals" | "between";
|
|
237
|
+
window: "1h" | "6h" | "24h" | "3d" | "7d" | "30d";
|
|
238
|
+
aggregation: "avg" | "sum" | "min" | "max" | "last" | "count";
|
|
176
239
|
severity: "info" | "warn" | "critical";
|
|
177
240
|
throttle: {
|
|
178
241
|
minGapMinutes: number;
|
|
@@ -180,6 +243,7 @@ export declare const RuleSchema: z.ZodObject<{
|
|
|
180
243
|
};
|
|
181
244
|
description?: string | undefined;
|
|
182
245
|
brand?: string | undefined;
|
|
246
|
+
consecutive?: number | undefined;
|
|
183
247
|
rawSql?: string | undefined;
|
|
184
248
|
rawSqlParams?: string[] | undefined;
|
|
185
249
|
exposureOverride?: boolean | undefined;
|
|
@@ -200,15 +264,22 @@ export declare const RuleSchema: z.ZodObject<{
|
|
|
200
264
|
type: "range";
|
|
201
265
|
min: number;
|
|
202
266
|
max: number;
|
|
267
|
+
} | {
|
|
268
|
+
type: "user_setting";
|
|
269
|
+
key: string;
|
|
270
|
+
defaultValue: number;
|
|
271
|
+
compare?: "as_configured" | undefined;
|
|
203
272
|
};
|
|
204
273
|
name: string;
|
|
205
|
-
metric: "spo2" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score"
|
|
206
|
-
window: "1h" | "6h" | "24h" | "3d" | "7d" | "30d";
|
|
274
|
+
metric: "spo2" | "device_battery" | "hrv_ms" | "stress" | "systolic_bp" | "diastolic_bp" | "vascular_aging" | "hr_bpm" | "temp_c" | "activity_steps" | "calories" | "distance_km" | "sleep_total_minutes" | "sleep_score";
|
|
207
275
|
compare: "less_than" | "greater_than" | "equals" | "not_equals" | "between";
|
|
276
|
+
window: "1h" | "6h" | "24h" | "3d" | "7d" | "30d";
|
|
208
277
|
severity: "info" | "warn" | "critical";
|
|
209
278
|
description?: string | undefined;
|
|
279
|
+
context?: "any" | "asleep" | "resting" | undefined;
|
|
210
280
|
brand?: string | undefined;
|
|
211
|
-
aggregation?: "avg" | "
|
|
281
|
+
aggregation?: "avg" | "sum" | "min" | "max" | "last" | "count" | undefined;
|
|
282
|
+
consecutive?: number | undefined;
|
|
212
283
|
throttle?: {
|
|
213
284
|
minGapMinutes?: number | undefined;
|
|
214
285
|
maxPerDay?: number | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/rules/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,+DAA+D;AAC/D,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAID,eAAO,MAAM,OAAO,iDAAkD,CAAA;AACtE,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7C,eAAO,MAAM,YAAY,wDAAyD,CAAA;AAClF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvD,eAAO,MAAM,QAAQ,2EAMX,CAAA;AACV,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/C,eAAO,MAAM,UAAU,uCAAwC,CAAA;AAC/D,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/rules/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,+DAA+D;AAC/D,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAID,eAAO,MAAM,OAAO,iDAAkD,CAAA;AACtE,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7C,eAAO,MAAM,YAAY,wDAAyD,CAAA;AAClF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvD,eAAO,MAAM,QAAQ,2EAMX,CAAA;AACV,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/C,eAAO,MAAM,UAAU,uCAAwC,CAAA;AAC/D,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAElD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,uCAAwC,CAAA;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AA+CnD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAPvB,iFAAiF;;IAEjF,kDAAkD;;;;;;;;;;;;;IAWlD,CAAA;AACF,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAEjD,eAAO,MAAM,cAAc;;;;;;;;;GAKb,CAAA;AAEd,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAErD,eAAO,MAAM,UAAU;;;;;;;;;IASrB,kEAAkE;;IAElE;;;;;;OAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA1CH,iFAAiF;;QAEjF,kDAAkD;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+ClD;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEH,CAAA;AAEF,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAA"}
|
package/dist/rules/schema.js
CHANGED
|
@@ -26,6 +26,18 @@ export const COMPARES = [
|
|
|
26
26
|
'between',
|
|
27
27
|
];
|
|
28
28
|
export const SEVERITIES = ['info', 'warn', 'critical'];
|
|
29
|
+
/**
|
|
30
|
+
* Physiological context the rule restricts its samples to (Sprint 5 §4).
|
|
31
|
+
*
|
|
32
|
+
* - `any` — every sample in the window
|
|
33
|
+
* - `asleep` — samples falling inside a `v_sleep_session` interval
|
|
34
|
+
* - `resting` — samples in a minute with zero steps
|
|
35
|
+
*
|
|
36
|
+
* Context is a JOIN, not a post-filter: "SpO₂ during sleep" means the
|
|
37
|
+
* aggregate is computed over sleep samples only, not computed over
|
|
38
|
+
* everything and then labelled.
|
|
39
|
+
*/
|
|
40
|
+
export const CONTEXTS = ['any', 'asleep', 'resting'];
|
|
29
41
|
const TargetAbsolute = z.object({
|
|
30
42
|
type: z.literal('absolute'),
|
|
31
43
|
value: z.number(),
|
|
@@ -45,11 +57,33 @@ const TargetRange = z.object({
|
|
|
45
57
|
min: z.number(),
|
|
46
58
|
max: z.number(),
|
|
47
59
|
});
|
|
60
|
+
/**
|
|
61
|
+
* v0.2.0 (Sprint 5 §4, Ziva #1) — threshold read from KVStore at eval time
|
|
62
|
+
* rather than baked into the rule.
|
|
63
|
+
*
|
|
64
|
+
* This is what makes the ⚙ sheet's promise real: the user drags a safe
|
|
65
|
+
* level, the mutation writes `user:spo2SafeLevel`, and the SAME shipped
|
|
66
|
+
* rule re-evaluates against the new number on the next batch. No recompile,
|
|
67
|
+
* no per-user rule rows.
|
|
68
|
+
*
|
|
69
|
+
* `compare` is a single-member enum rather than omitted: the comparison is
|
|
70
|
+
* fixed by the rule's own `compare` field, and a second knob here would let
|
|
71
|
+
* an author write a rule whose two comparison directions disagree.
|
|
72
|
+
*/
|
|
73
|
+
const TargetUserSetting = z.object({
|
|
74
|
+
type: z.literal('user_setting'),
|
|
75
|
+
/** KVStore key suffix, e.g. `user:spo2SafeLevel`. See the namespace registry. */
|
|
76
|
+
key: z.string().min(1),
|
|
77
|
+
/** Used when the user has never set the value. */
|
|
78
|
+
defaultValue: z.number(),
|
|
79
|
+
compare: z.enum(['as_configured']).default('as_configured'),
|
|
80
|
+
});
|
|
48
81
|
export const TargetSchema = z.discriminatedUnion('type', [
|
|
49
82
|
TargetAbsolute,
|
|
50
83
|
TargetBaselinePercent,
|
|
51
84
|
TargetBaselineStddev,
|
|
52
85
|
TargetRange,
|
|
86
|
+
TargetUserSetting,
|
|
53
87
|
]);
|
|
54
88
|
export const ThrottleSchema = z
|
|
55
89
|
.object({
|
|
@@ -66,6 +100,16 @@ export const RuleSchema = z.object({
|
|
|
66
100
|
window: z.enum(WINDOWS),
|
|
67
101
|
aggregation: z.enum(AGGREGATIONS).default('avg'),
|
|
68
102
|
compare: z.enum(COMPARES),
|
|
103
|
+
/** Restrict samples to a physiological context. Default `any`. */
|
|
104
|
+
context: z.enum(CONTEXTS).default('any'),
|
|
105
|
+
/**
|
|
106
|
+
* Require `n` ADJACENT breaching samples before the rule fires.
|
|
107
|
+
*
|
|
108
|
+
* Omitted or `1` means "any breach in the window", which the aggregate
|
|
109
|
+
* path already expresses (e.g. `aggregation: 'min'` + `less_than`).
|
|
110
|
+
* `>= 2` switches the compiler to run-length detection.
|
|
111
|
+
*/
|
|
112
|
+
consecutive: z.number().int().min(1).optional(),
|
|
69
113
|
target: TargetSchema,
|
|
70
114
|
severity: z.enum(SEVERITIES),
|
|
71
115
|
throttle: ThrottleSchema,
|
package/dist/rules/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/rules/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAiB,MAAM,yBAAyB,CAAA;AAE5E,+DAA+D;AAC/D,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AAED,MAAM,gBAAgB,GAAG,mBAAmB,EAA+B,CAAA;AAE3E,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAU,CAAA;AAGtE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAU,CAAA;AAGlF,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,WAAW;IACX,cAAc;IACd,QAAQ;IACR,YAAY;IACZ,SAAS;CACD,CAAA;AAGV,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAU,CAAA;AAG/D,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAA;AAEF,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC;IACnC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAA;AAEF,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC;IAClC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAA;AAEF,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;CAChB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IACvD,cAAc;IACd,qBAAqB;IACrB,oBAAoB;IACpB,WAAW;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/rules/schema.ts"],"names":[],"mappings":"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"}
|