@mongrov/analytics 0.6.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 +125 -23
- 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/metric_metadata.d.ts +55 -0
- package/dist/core/metric_metadata.d.ts.map +1 -1
- package/dist/core/metric_metadata.js +35 -7
- package/dist/core/metric_metadata.js.map +1 -1
- package/dist/core/migrations.d.ts.map +1 -1
- package/dist/core/migrations.js +64 -2
- 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 +49 -22
- package/dist/core/retention.js.map +1 -1
- package/dist/core/schemas.d.ts +73 -2
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/schemas.js +154 -3
- package/dist/core/schemas.js.map +1 -1
- package/dist/core/table_metadata.d.ts.map +1 -1
- package/dist/core/table_metadata.js +3 -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 +75 -4
- 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 +108 -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 +43 -7
- 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 +3 -1
- 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/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 +66 -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
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod contract for the firmware export payload (principle 60 — firmware
|
|
3
|
+
* fixture governance).
|
|
4
|
+
*
|
|
5
|
+
* Models `FirmwareExport` from `./types.ts` EXACTLY, with `.strict()` on
|
|
6
|
+
* every object so unknown keys fail parsing. This is the CI gate: when the
|
|
7
|
+
* firmware team lands a new versioned fixture, any key the mapper doesn't
|
|
8
|
+
* know about surfaces as a test failure — a PR-blocking event that forces a
|
|
9
|
+
* deliberate schema + mapper update instead of a silent drop.
|
|
10
|
+
*
|
|
11
|
+
* `FIRMWARE_EXPORT_KEY_PATHS` is the same contract expressed as a key-path
|
|
12
|
+
* set (`heartrate`, `heartrate[].timestamp`, ...) for the fixture key-diff
|
|
13
|
+
* test in `__tests__/schema.test.ts`. Keep both in lockstep with
|
|
14
|
+
* `FirmwareExport` — the key-diff test asserts the fixture corpus exercises
|
|
15
|
+
* exactly this set.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
export declare const firmwareHRRowSchema: z.ZodObject<{
|
|
19
|
+
timestamp: z.ZodString;
|
|
20
|
+
singleHR: z.ZodNumber;
|
|
21
|
+
}, "strict", z.ZodTypeAny, {
|
|
22
|
+
timestamp: string;
|
|
23
|
+
singleHR: number;
|
|
24
|
+
}, {
|
|
25
|
+
timestamp: string;
|
|
26
|
+
singleHR: number;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const firmwareHRVRowSchema: z.ZodObject<{
|
|
29
|
+
timestamp: z.ZodString;
|
|
30
|
+
hrv: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
stress: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
heartRate: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
systolicBP: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
diastolicBP: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
vascularAging: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
}, "strict", z.ZodTypeAny, {
|
|
37
|
+
timestamp: string;
|
|
38
|
+
hrv?: number | undefined;
|
|
39
|
+
stress?: number | undefined;
|
|
40
|
+
heartRate?: number | undefined;
|
|
41
|
+
systolicBP?: number | undefined;
|
|
42
|
+
diastolicBP?: number | undefined;
|
|
43
|
+
vascularAging?: number | undefined;
|
|
44
|
+
}, {
|
|
45
|
+
timestamp: string;
|
|
46
|
+
hrv?: number | undefined;
|
|
47
|
+
stress?: number | undefined;
|
|
48
|
+
heartRate?: number | undefined;
|
|
49
|
+
systolicBP?: number | undefined;
|
|
50
|
+
diastolicBP?: number | undefined;
|
|
51
|
+
vascularAging?: number | undefined;
|
|
52
|
+
}>;
|
|
53
|
+
export declare const firmwareSpO2RowSchema: z.ZodObject<{
|
|
54
|
+
timestamp: z.ZodString;
|
|
55
|
+
automaticSpo2Data: z.ZodNumber;
|
|
56
|
+
}, "strict", z.ZodTypeAny, {
|
|
57
|
+
timestamp: string;
|
|
58
|
+
automaticSpo2Data: number;
|
|
59
|
+
}, {
|
|
60
|
+
timestamp: string;
|
|
61
|
+
automaticSpo2Data: number;
|
|
62
|
+
}>;
|
|
63
|
+
export declare const firmwareTempRowSchema: z.ZodObject<{
|
|
64
|
+
timestamp: z.ZodString;
|
|
65
|
+
temperature: z.ZodNumber;
|
|
66
|
+
}, "strict", z.ZodTypeAny, {
|
|
67
|
+
temperature: number;
|
|
68
|
+
timestamp: string;
|
|
69
|
+
}, {
|
|
70
|
+
temperature: number;
|
|
71
|
+
timestamp: string;
|
|
72
|
+
}>;
|
|
73
|
+
export declare const firmwareActivityRowSchema: z.ZodObject<{
|
|
74
|
+
timestamp: z.ZodString;
|
|
75
|
+
step: z.ZodNumber;
|
|
76
|
+
calories: z.ZodNumber;
|
|
77
|
+
distance: z.ZodNumber;
|
|
78
|
+
arraySteps: z.ZodArray<z.ZodNumber, "many">;
|
|
79
|
+
}, "strict", z.ZodTypeAny, {
|
|
80
|
+
calories: number;
|
|
81
|
+
timestamp: string;
|
|
82
|
+
step: number;
|
|
83
|
+
distance: number;
|
|
84
|
+
arraySteps: number[];
|
|
85
|
+
}, {
|
|
86
|
+
calories: number;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
step: number;
|
|
89
|
+
distance: number;
|
|
90
|
+
arraySteps: number[];
|
|
91
|
+
}>;
|
|
92
|
+
export declare const firmwareSleepRowSchema: z.ZodObject<{
|
|
93
|
+
start: z.ZodString;
|
|
94
|
+
end: z.ZodString;
|
|
95
|
+
block_type: z.ZodString;
|
|
96
|
+
confidence: z.ZodNumber;
|
|
97
|
+
timestamp: z.ZodString;
|
|
98
|
+
}, "strict", z.ZodTypeAny, {
|
|
99
|
+
start: string;
|
|
100
|
+
timestamp: string;
|
|
101
|
+
end: string;
|
|
102
|
+
block_type: string;
|
|
103
|
+
confidence: number;
|
|
104
|
+
}, {
|
|
105
|
+
start: string;
|
|
106
|
+
timestamp: string;
|
|
107
|
+
end: string;
|
|
108
|
+
block_type: string;
|
|
109
|
+
confidence: number;
|
|
110
|
+
}>;
|
|
111
|
+
export declare const firmwareBatteryRowSchema: z.ZodObject<{
|
|
112
|
+
timestamp: z.ZodString;
|
|
113
|
+
battery: z.ZodNumber;
|
|
114
|
+
}, "strict", z.ZodTypeAny, {
|
|
115
|
+
timestamp: string;
|
|
116
|
+
battery: number;
|
|
117
|
+
}, {
|
|
118
|
+
timestamp: string;
|
|
119
|
+
battery: number;
|
|
120
|
+
}>;
|
|
121
|
+
export declare const firmwareMonitoringWindowSchema: z.ZodObject<{
|
|
122
|
+
metric: z.ZodString;
|
|
123
|
+
interval_minutes: z.ZodNumber;
|
|
124
|
+
start_hour: z.ZodNumber;
|
|
125
|
+
end_hour: z.ZodNumber;
|
|
126
|
+
}, "strict", z.ZodTypeAny, {
|
|
127
|
+
metric: string;
|
|
128
|
+
interval_minutes: number;
|
|
129
|
+
start_hour: number;
|
|
130
|
+
end_hour: number;
|
|
131
|
+
}, {
|
|
132
|
+
metric: string;
|
|
133
|
+
interval_minutes: number;
|
|
134
|
+
start_hour: number;
|
|
135
|
+
end_hour: number;
|
|
136
|
+
}>;
|
|
137
|
+
export declare const firmwareRingConfigSchema: z.ZodObject<{
|
|
138
|
+
automaticMonitoringData: z.ZodArray<z.ZodObject<{
|
|
139
|
+
metric: z.ZodString;
|
|
140
|
+
interval_minutes: z.ZodNumber;
|
|
141
|
+
start_hour: z.ZodNumber;
|
|
142
|
+
end_hour: z.ZodNumber;
|
|
143
|
+
}, "strict", z.ZodTypeAny, {
|
|
144
|
+
metric: string;
|
|
145
|
+
interval_minutes: number;
|
|
146
|
+
start_hour: number;
|
|
147
|
+
end_hour: number;
|
|
148
|
+
}, {
|
|
149
|
+
metric: string;
|
|
150
|
+
interval_minutes: number;
|
|
151
|
+
start_hour: number;
|
|
152
|
+
end_hour: number;
|
|
153
|
+
}>, "many">;
|
|
154
|
+
}, "strict", z.ZodTypeAny, {
|
|
155
|
+
automaticMonitoringData: {
|
|
156
|
+
metric: string;
|
|
157
|
+
interval_minutes: number;
|
|
158
|
+
start_hour: number;
|
|
159
|
+
end_hour: number;
|
|
160
|
+
}[];
|
|
161
|
+
}, {
|
|
162
|
+
automaticMonitoringData: {
|
|
163
|
+
metric: string;
|
|
164
|
+
interval_minutes: number;
|
|
165
|
+
start_hour: number;
|
|
166
|
+
end_hour: number;
|
|
167
|
+
}[];
|
|
168
|
+
}>;
|
|
169
|
+
export declare const firmwareExportSchema: z.ZodObject<{
|
|
170
|
+
$comment: z.ZodOptional<z.ZodString>;
|
|
171
|
+
heartrate: z.ZodArray<z.ZodObject<{
|
|
172
|
+
timestamp: z.ZodString;
|
|
173
|
+
singleHR: z.ZodNumber;
|
|
174
|
+
}, "strict", z.ZodTypeAny, {
|
|
175
|
+
timestamp: string;
|
|
176
|
+
singleHR: number;
|
|
177
|
+
}, {
|
|
178
|
+
timestamp: string;
|
|
179
|
+
singleHR: number;
|
|
180
|
+
}>, "many">;
|
|
181
|
+
hrv_table: z.ZodArray<z.ZodObject<{
|
|
182
|
+
timestamp: z.ZodString;
|
|
183
|
+
hrv: z.ZodOptional<z.ZodNumber>;
|
|
184
|
+
stress: z.ZodOptional<z.ZodNumber>;
|
|
185
|
+
heartRate: z.ZodOptional<z.ZodNumber>;
|
|
186
|
+
systolicBP: z.ZodOptional<z.ZodNumber>;
|
|
187
|
+
diastolicBP: z.ZodOptional<z.ZodNumber>;
|
|
188
|
+
vascularAging: z.ZodOptional<z.ZodNumber>;
|
|
189
|
+
}, "strict", z.ZodTypeAny, {
|
|
190
|
+
timestamp: string;
|
|
191
|
+
hrv?: number | undefined;
|
|
192
|
+
stress?: number | undefined;
|
|
193
|
+
heartRate?: number | undefined;
|
|
194
|
+
systolicBP?: number | undefined;
|
|
195
|
+
diastolicBP?: number | undefined;
|
|
196
|
+
vascularAging?: number | undefined;
|
|
197
|
+
}, {
|
|
198
|
+
timestamp: string;
|
|
199
|
+
hrv?: number | undefined;
|
|
200
|
+
stress?: number | undefined;
|
|
201
|
+
heartRate?: number | undefined;
|
|
202
|
+
systolicBP?: number | undefined;
|
|
203
|
+
diastolicBP?: number | undefined;
|
|
204
|
+
vascularAging?: number | undefined;
|
|
205
|
+
}>, "many">;
|
|
206
|
+
spo2: z.ZodArray<z.ZodObject<{
|
|
207
|
+
timestamp: z.ZodString;
|
|
208
|
+
automaticSpo2Data: z.ZodNumber;
|
|
209
|
+
}, "strict", z.ZodTypeAny, {
|
|
210
|
+
timestamp: string;
|
|
211
|
+
automaticSpo2Data: number;
|
|
212
|
+
}, {
|
|
213
|
+
timestamp: string;
|
|
214
|
+
automaticSpo2Data: number;
|
|
215
|
+
}>, "many">;
|
|
216
|
+
temperature_table: z.ZodArray<z.ZodObject<{
|
|
217
|
+
timestamp: z.ZodString;
|
|
218
|
+
temperature: z.ZodNumber;
|
|
219
|
+
}, "strict", z.ZodTypeAny, {
|
|
220
|
+
temperature: number;
|
|
221
|
+
timestamp: string;
|
|
222
|
+
}, {
|
|
223
|
+
temperature: number;
|
|
224
|
+
timestamp: string;
|
|
225
|
+
}>, "many">;
|
|
226
|
+
activitydetails: z.ZodArray<z.ZodObject<{
|
|
227
|
+
timestamp: z.ZodString;
|
|
228
|
+
step: z.ZodNumber;
|
|
229
|
+
calories: z.ZodNumber;
|
|
230
|
+
distance: z.ZodNumber;
|
|
231
|
+
arraySteps: z.ZodArray<z.ZodNumber, "many">;
|
|
232
|
+
}, "strict", z.ZodTypeAny, {
|
|
233
|
+
calories: number;
|
|
234
|
+
timestamp: string;
|
|
235
|
+
step: number;
|
|
236
|
+
distance: number;
|
|
237
|
+
arraySteps: number[];
|
|
238
|
+
}, {
|
|
239
|
+
calories: number;
|
|
240
|
+
timestamp: string;
|
|
241
|
+
step: number;
|
|
242
|
+
distance: number;
|
|
243
|
+
arraySteps: number[];
|
|
244
|
+
}>, "many">;
|
|
245
|
+
sleep_processed: z.ZodArray<z.ZodObject<{
|
|
246
|
+
start: z.ZodString;
|
|
247
|
+
end: z.ZodString;
|
|
248
|
+
block_type: z.ZodString;
|
|
249
|
+
confidence: z.ZodNumber;
|
|
250
|
+
timestamp: z.ZodString;
|
|
251
|
+
}, "strict", z.ZodTypeAny, {
|
|
252
|
+
start: string;
|
|
253
|
+
timestamp: string;
|
|
254
|
+
end: string;
|
|
255
|
+
block_type: string;
|
|
256
|
+
confidence: number;
|
|
257
|
+
}, {
|
|
258
|
+
start: string;
|
|
259
|
+
timestamp: string;
|
|
260
|
+
end: string;
|
|
261
|
+
block_type: string;
|
|
262
|
+
confidence: number;
|
|
263
|
+
}>, "many">;
|
|
264
|
+
battery_table: z.ZodArray<z.ZodObject<{
|
|
265
|
+
timestamp: z.ZodString;
|
|
266
|
+
battery: z.ZodNumber;
|
|
267
|
+
}, "strict", z.ZodTypeAny, {
|
|
268
|
+
timestamp: string;
|
|
269
|
+
battery: number;
|
|
270
|
+
}, {
|
|
271
|
+
timestamp: string;
|
|
272
|
+
battery: number;
|
|
273
|
+
}>, "many">;
|
|
274
|
+
ring: z.ZodObject<{
|
|
275
|
+
automaticMonitoringData: z.ZodArray<z.ZodObject<{
|
|
276
|
+
metric: z.ZodString;
|
|
277
|
+
interval_minutes: z.ZodNumber;
|
|
278
|
+
start_hour: z.ZodNumber;
|
|
279
|
+
end_hour: z.ZodNumber;
|
|
280
|
+
}, "strict", z.ZodTypeAny, {
|
|
281
|
+
metric: string;
|
|
282
|
+
interval_minutes: number;
|
|
283
|
+
start_hour: number;
|
|
284
|
+
end_hour: number;
|
|
285
|
+
}, {
|
|
286
|
+
metric: string;
|
|
287
|
+
interval_minutes: number;
|
|
288
|
+
start_hour: number;
|
|
289
|
+
end_hour: number;
|
|
290
|
+
}>, "many">;
|
|
291
|
+
}, "strict", z.ZodTypeAny, {
|
|
292
|
+
automaticMonitoringData: {
|
|
293
|
+
metric: string;
|
|
294
|
+
interval_minutes: number;
|
|
295
|
+
start_hour: number;
|
|
296
|
+
end_hour: number;
|
|
297
|
+
}[];
|
|
298
|
+
}, {
|
|
299
|
+
automaticMonitoringData: {
|
|
300
|
+
metric: string;
|
|
301
|
+
interval_minutes: number;
|
|
302
|
+
start_hour: number;
|
|
303
|
+
end_hour: number;
|
|
304
|
+
}[];
|
|
305
|
+
}>;
|
|
306
|
+
}, "strict", z.ZodTypeAny, {
|
|
307
|
+
spo2: {
|
|
308
|
+
timestamp: string;
|
|
309
|
+
automaticSpo2Data: number;
|
|
310
|
+
}[];
|
|
311
|
+
heartrate: {
|
|
312
|
+
timestamp: string;
|
|
313
|
+
singleHR: number;
|
|
314
|
+
}[];
|
|
315
|
+
hrv_table: {
|
|
316
|
+
timestamp: string;
|
|
317
|
+
hrv?: number | undefined;
|
|
318
|
+
stress?: number | undefined;
|
|
319
|
+
heartRate?: number | undefined;
|
|
320
|
+
systolicBP?: number | undefined;
|
|
321
|
+
diastolicBP?: number | undefined;
|
|
322
|
+
vascularAging?: number | undefined;
|
|
323
|
+
}[];
|
|
324
|
+
temperature_table: {
|
|
325
|
+
temperature: number;
|
|
326
|
+
timestamp: string;
|
|
327
|
+
}[];
|
|
328
|
+
activitydetails: {
|
|
329
|
+
calories: number;
|
|
330
|
+
timestamp: string;
|
|
331
|
+
step: number;
|
|
332
|
+
distance: number;
|
|
333
|
+
arraySteps: number[];
|
|
334
|
+
}[];
|
|
335
|
+
sleep_processed: {
|
|
336
|
+
start: string;
|
|
337
|
+
timestamp: string;
|
|
338
|
+
end: string;
|
|
339
|
+
block_type: string;
|
|
340
|
+
confidence: number;
|
|
341
|
+
}[];
|
|
342
|
+
battery_table: {
|
|
343
|
+
timestamp: string;
|
|
344
|
+
battery: number;
|
|
345
|
+
}[];
|
|
346
|
+
ring: {
|
|
347
|
+
automaticMonitoringData: {
|
|
348
|
+
metric: string;
|
|
349
|
+
interval_minutes: number;
|
|
350
|
+
start_hour: number;
|
|
351
|
+
end_hour: number;
|
|
352
|
+
}[];
|
|
353
|
+
};
|
|
354
|
+
$comment?: string | undefined;
|
|
355
|
+
}, {
|
|
356
|
+
spo2: {
|
|
357
|
+
timestamp: string;
|
|
358
|
+
automaticSpo2Data: number;
|
|
359
|
+
}[];
|
|
360
|
+
heartrate: {
|
|
361
|
+
timestamp: string;
|
|
362
|
+
singleHR: number;
|
|
363
|
+
}[];
|
|
364
|
+
hrv_table: {
|
|
365
|
+
timestamp: string;
|
|
366
|
+
hrv?: number | undefined;
|
|
367
|
+
stress?: number | undefined;
|
|
368
|
+
heartRate?: number | undefined;
|
|
369
|
+
systolicBP?: number | undefined;
|
|
370
|
+
diastolicBP?: number | undefined;
|
|
371
|
+
vascularAging?: number | undefined;
|
|
372
|
+
}[];
|
|
373
|
+
temperature_table: {
|
|
374
|
+
temperature: number;
|
|
375
|
+
timestamp: string;
|
|
376
|
+
}[];
|
|
377
|
+
activitydetails: {
|
|
378
|
+
calories: number;
|
|
379
|
+
timestamp: string;
|
|
380
|
+
step: number;
|
|
381
|
+
distance: number;
|
|
382
|
+
arraySteps: number[];
|
|
383
|
+
}[];
|
|
384
|
+
sleep_processed: {
|
|
385
|
+
start: string;
|
|
386
|
+
timestamp: string;
|
|
387
|
+
end: string;
|
|
388
|
+
block_type: string;
|
|
389
|
+
confidence: number;
|
|
390
|
+
}[];
|
|
391
|
+
battery_table: {
|
|
392
|
+
timestamp: string;
|
|
393
|
+
battery: number;
|
|
394
|
+
}[];
|
|
395
|
+
ring: {
|
|
396
|
+
automaticMonitoringData: {
|
|
397
|
+
metric: string;
|
|
398
|
+
interval_minutes: number;
|
|
399
|
+
start_hour: number;
|
|
400
|
+
end_hour: number;
|
|
401
|
+
}[];
|
|
402
|
+
};
|
|
403
|
+
$comment?: string | undefined;
|
|
404
|
+
}>;
|
|
405
|
+
export type FirmwareExportParsed = z.infer<typeof firmwareExportSchema>;
|
|
406
|
+
/**
|
|
407
|
+
* Every key-path the schema knows about. Array elements are flattened as
|
|
408
|
+
* `section[].key`. Used by the fixture key-diff test (principle 60).
|
|
409
|
+
*/
|
|
410
|
+
export declare const FIRMWARE_EXPORT_KEY_PATHS: ReadonlySet<string>;
|
|
411
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/sync/mapper/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AASvB,eAAO,MAAM,mBAAmB;;;;;;;;;EAGrB,CAAA;AAEX,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;EAQtB,CAAA;AAEX,eAAO,MAAM,qBAAqB;;;;;;;;;EAGvB,CAAA;AAEX,eAAO,MAAM,qBAAqB;;;;;;;;;EAGvB,CAAA;AAEX,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;EAM3B,CAAA;AAEX,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;EAMxB,CAAA;AAEX,eAAO,MAAM,wBAAwB;;;;;;;;;EAG1B,CAAA;AAEX,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;EAKhC,CAAA;AAEX,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE1B,CAAA;AAEX,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtB,CAAA;AAEX,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AASvE;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAwCxD,CAAA"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod contract for the firmware export payload (principle 60 — firmware
|
|
3
|
+
* fixture governance).
|
|
4
|
+
*
|
|
5
|
+
* Models `FirmwareExport` from `./types.ts` EXACTLY, with `.strict()` on
|
|
6
|
+
* every object so unknown keys fail parsing. This is the CI gate: when the
|
|
7
|
+
* firmware team lands a new versioned fixture, any key the mapper doesn't
|
|
8
|
+
* know about surfaces as a test failure — a PR-blocking event that forces a
|
|
9
|
+
* deliberate schema + mapper update instead of a silent drop.
|
|
10
|
+
*
|
|
11
|
+
* `FIRMWARE_EXPORT_KEY_PATHS` is the same contract expressed as a key-path
|
|
12
|
+
* set (`heartrate`, `heartrate[].timestamp`, ...) for the fixture key-diff
|
|
13
|
+
* test in `__tests__/schema.test.ts`. Keep both in lockstep with
|
|
14
|
+
* `FirmwareExport` — the key-diff test asserts the fixture corpus exercises
|
|
15
|
+
* exactly this set.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
/** Firmware timestamp: `"YYYY.MM.DD HH:MM:SS"` UTC wall-clock (see time.ts). */
|
|
19
|
+
const firmwareTimestamp = z.string().regex(/^\d{4}\.\d{1,2}\.\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}$/, 'expected firmware "YYYY.MM.DD HH:MM:SS" timestamp');
|
|
20
|
+
export const firmwareHRRowSchema = z.object({
|
|
21
|
+
timestamp: firmwareTimestamp,
|
|
22
|
+
singleHR: z.number(),
|
|
23
|
+
}).strict();
|
|
24
|
+
export const firmwareHRVRowSchema = z.object({
|
|
25
|
+
timestamp: firmwareTimestamp,
|
|
26
|
+
hrv: z.number().optional(),
|
|
27
|
+
stress: z.number().optional(),
|
|
28
|
+
heartRate: z.number().optional(),
|
|
29
|
+
systolicBP: z.number().optional(),
|
|
30
|
+
diastolicBP: z.number().optional(),
|
|
31
|
+
vascularAging: z.number().optional(),
|
|
32
|
+
}).strict();
|
|
33
|
+
export const firmwareSpO2RowSchema = z.object({
|
|
34
|
+
timestamp: firmwareTimestamp,
|
|
35
|
+
automaticSpo2Data: z.number(),
|
|
36
|
+
}).strict();
|
|
37
|
+
export const firmwareTempRowSchema = z.object({
|
|
38
|
+
timestamp: firmwareTimestamp,
|
|
39
|
+
temperature: z.number(),
|
|
40
|
+
}).strict();
|
|
41
|
+
export const firmwareActivityRowSchema = z.object({
|
|
42
|
+
timestamp: firmwareTimestamp,
|
|
43
|
+
step: z.number(),
|
|
44
|
+
calories: z.number(),
|
|
45
|
+
distance: z.number(),
|
|
46
|
+
arraySteps: z.array(z.number()).length(10),
|
|
47
|
+
}).strict();
|
|
48
|
+
export const firmwareSleepRowSchema = z.object({
|
|
49
|
+
start: firmwareTimestamp,
|
|
50
|
+
end: firmwareTimestamp,
|
|
51
|
+
block_type: z.string(),
|
|
52
|
+
confidence: z.number(),
|
|
53
|
+
timestamp: firmwareTimestamp,
|
|
54
|
+
}).strict();
|
|
55
|
+
export const firmwareBatteryRowSchema = z.object({
|
|
56
|
+
timestamp: firmwareTimestamp,
|
|
57
|
+
battery: z.number(),
|
|
58
|
+
}).strict();
|
|
59
|
+
export const firmwareMonitoringWindowSchema = z.object({
|
|
60
|
+
metric: z.string(),
|
|
61
|
+
interval_minutes: z.number(),
|
|
62
|
+
start_hour: z.number(),
|
|
63
|
+
end_hour: z.number(),
|
|
64
|
+
}).strict();
|
|
65
|
+
export const firmwareRingConfigSchema = z.object({
|
|
66
|
+
automaticMonitoringData: z.array(firmwareMonitoringWindowSchema),
|
|
67
|
+
}).strict();
|
|
68
|
+
export const firmwareExportSchema = z.object({
|
|
69
|
+
// Fixture files carry a human-readable `$comment`; real device exports
|
|
70
|
+
// may omit it. Not part of `FirmwareExport` — stripped before mapping.
|
|
71
|
+
$comment: z.string().optional(),
|
|
72
|
+
heartrate: z.array(firmwareHRRowSchema),
|
|
73
|
+
hrv_table: z.array(firmwareHRVRowSchema),
|
|
74
|
+
spo2: z.array(firmwareSpO2RowSchema),
|
|
75
|
+
temperature_table: z.array(firmwareTempRowSchema),
|
|
76
|
+
activitydetails: z.array(firmwareActivityRowSchema),
|
|
77
|
+
sleep_processed: z.array(firmwareSleepRowSchema),
|
|
78
|
+
battery_table: z.array(firmwareBatteryRowSchema),
|
|
79
|
+
ring: firmwareRingConfigSchema,
|
|
80
|
+
}).strict();
|
|
81
|
+
const _assertParsedMatchesType = true;
|
|
82
|
+
void _assertParsedMatchesType;
|
|
83
|
+
/**
|
|
84
|
+
* Every key-path the schema knows about. Array elements are flattened as
|
|
85
|
+
* `section[].key`. Used by the fixture key-diff test (principle 60).
|
|
86
|
+
*/
|
|
87
|
+
export const FIRMWARE_EXPORT_KEY_PATHS = new Set([
|
|
88
|
+
'$comment',
|
|
89
|
+
'heartrate',
|
|
90
|
+
'heartrate[].timestamp',
|
|
91
|
+
'heartrate[].singleHR',
|
|
92
|
+
'hrv_table',
|
|
93
|
+
'hrv_table[].timestamp',
|
|
94
|
+
'hrv_table[].hrv',
|
|
95
|
+
'hrv_table[].stress',
|
|
96
|
+
'hrv_table[].heartRate',
|
|
97
|
+
'hrv_table[].systolicBP',
|
|
98
|
+
'hrv_table[].diastolicBP',
|
|
99
|
+
'hrv_table[].vascularAging',
|
|
100
|
+
'spo2',
|
|
101
|
+
'spo2[].timestamp',
|
|
102
|
+
'spo2[].automaticSpo2Data',
|
|
103
|
+
'temperature_table',
|
|
104
|
+
'temperature_table[].timestamp',
|
|
105
|
+
'temperature_table[].temperature',
|
|
106
|
+
'activitydetails',
|
|
107
|
+
'activitydetails[].timestamp',
|
|
108
|
+
'activitydetails[].step',
|
|
109
|
+
'activitydetails[].calories',
|
|
110
|
+
'activitydetails[].distance',
|
|
111
|
+
'activitydetails[].arraySteps',
|
|
112
|
+
'sleep_processed',
|
|
113
|
+
'sleep_processed[].start',
|
|
114
|
+
'sleep_processed[].end',
|
|
115
|
+
'sleep_processed[].block_type',
|
|
116
|
+
'sleep_processed[].confidence',
|
|
117
|
+
'sleep_processed[].timestamp',
|
|
118
|
+
'battery_table',
|
|
119
|
+
'battery_table[].timestamp',
|
|
120
|
+
'battery_table[].battery',
|
|
121
|
+
'ring',
|
|
122
|
+
'ring.automaticMonitoringData',
|
|
123
|
+
'ring.automaticMonitoringData[].metric',
|
|
124
|
+
'ring.automaticMonitoringData[].interval_minutes',
|
|
125
|
+
'ring.automaticMonitoringData[].start_hour',
|
|
126
|
+
'ring.automaticMonitoringData[].end_hour',
|
|
127
|
+
]);
|
|
128
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/sync/mapper/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,31 +2,76 @@
|
|
|
2
2
|
* Sleep reconstruction mapper (T-06).
|
|
3
3
|
*
|
|
4
4
|
* Firmware `sleep_processed` emits per-block rows tagged with a `start`
|
|
5
|
-
* (session start), `end
|
|
6
|
-
* `confidence` (0..1), and per-block `timestamp` (block instant).
|
|
7
|
-
* blocks share the same `start` when they belong to the same
|
|
5
|
+
* (session start), `end` (session end), `block_type` (primary / light / deep /
|
|
6
|
+
* rem / awake), `confidence` (0..1), and per-block `timestamp` (block instant).
|
|
7
|
+
* Multiple blocks share the same `start` when they belong to the same session.
|
|
8
8
|
*
|
|
9
9
|
* Reconstruction rules (spec.md §Firmware mapper + tasks.md T-06):
|
|
10
10
|
* - Group rows by `start`.
|
|
11
11
|
* - A session is included if AT LEAST ONE block in the group has
|
|
12
12
|
* `block_type === 'primary'` AND `confidence >= 0.7`. This filters
|
|
13
13
|
* day-time naps and low-confidence noise.
|
|
14
|
-
* - Session boundaries
|
|
15
|
-
* `
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* - `
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* -
|
|
22
|
-
*
|
|
23
|
-
*
|
|
14
|
+
* - Session boundaries come from the firmware's own envelope fields:
|
|
15
|
+
* `ts_start = parse(start)`, `ts_end = parse(end)`. `total_minutes` is
|
|
16
|
+
* their difference. Deriving the envelope from block instants would
|
|
17
|
+
* under-report any session whose first/last minutes went unclassified.
|
|
18
|
+
* - `session_id` = `nanoid(24) + '_' + fnv1a32hex(device_id | user_id |
|
|
19
|
+
* ts_session_start)` (principle 25, locked) — collision-safe even with
|
|
20
|
+
* corrupted timestamps.
|
|
21
|
+
* - `night_of` = 6pm-6pm rule applied to `ts_start` (see `time.ts`).
|
|
22
|
+
* - Stages: one row per classified block, carrying `session_id` and the
|
|
23
|
+
* DDL's integer `stage` code. `primary` is the session envelope marker,
|
|
24
|
+
* not a stage, so it produces no `sleep_stage` row — the DDL's stage
|
|
25
|
+
* enum (1=awake, 2=light, 3=deep, 5=rem) has no code for it. Unknown
|
|
26
|
+
* block types are likewise skipped; nothing is lost, because...
|
|
27
|
+
* - Raw: every input row is preserved in `sleep_raw` regardless of the
|
|
28
|
+
* primary/confidence filter, flattened onto the DDL's columns, so
|
|
29
|
+
* downstream reprocessing can revisit sessions with new heuristics.
|
|
30
|
+
*
|
|
31
|
+
* Principle 20 boundary: firmware `block_type` strings are translated to our
|
|
32
|
+
* integer codes here and never reach the warehouse.
|
|
24
33
|
*/
|
|
25
34
|
import type { FirmwareSleepRow, MapperContext, SleepRawRow, SleepSessionRow, SleepStageRow } from './types';
|
|
35
|
+
/**
|
|
36
|
+
* Block width in minutes when the firmware revision omits `unit_length`.
|
|
37
|
+
* Firmware doc §Sleep processing: blocks are 1-min wide by convention.
|
|
38
|
+
*/
|
|
39
|
+
export declare const DEFAULT_BLOCK_MINUTES = 1;
|
|
40
|
+
/**
|
|
41
|
+
* Firmware `block_type` → `sleep_stage.stage` code (spec §Table schema:
|
|
42
|
+
* `stage SMALLINT NOT NULL -- 1=awake, 2=light, 3=deep, 5=rem`).
|
|
43
|
+
*
|
|
44
|
+
* `primary` is deliberately absent: it marks the session envelope, not a
|
|
45
|
+
* sleep stage. The enum is intentionally sparse (no 4) to match the DDL
|
|
46
|
+
* comment verbatim.
|
|
47
|
+
*/
|
|
48
|
+
export declare const SLEEP_STAGE_CODES: Readonly<Record<string, number>>;
|
|
49
|
+
/** Reverse lookup for the stage-minute accumulator + diagnostics. */
|
|
50
|
+
export declare const SLEEP_STAGE_NAMES: Readonly<Record<number, string>>;
|
|
26
51
|
export interface ReconstructSleepResult {
|
|
27
52
|
sleep_session: SleepSessionRow[];
|
|
28
53
|
sleep_stage: SleepStageRow[];
|
|
29
54
|
sleep_raw: SleepRawRow[];
|
|
30
55
|
}
|
|
31
56
|
export declare function reconstructSleepSessions(rows: readonly FirmwareSleepRow[], ctx: MapperContext): ReconstructSleepResult;
|
|
57
|
+
/**
|
|
58
|
+
* Session id components (principle 25, locked):
|
|
59
|
+
* `nanoid(24) + '_' + fnv1a32hex(device_id | user_id | ts_session_start)`
|
|
60
|
+
*
|
|
61
|
+
* The random prefix makes ids collision-safe even when the firmware emits
|
|
62
|
+
* corrupted/duplicated timestamps; the hash suffix is deterministic over the
|
|
63
|
+
* identifying tuple so ids remain diagnosable (same device+user+start ⇒
|
|
64
|
+
* same suffix). The random source is injectable via `ctx.idGenerator` to
|
|
65
|
+
* keep the mapper testable — the suffix is always deterministic.
|
|
66
|
+
*/
|
|
67
|
+
export declare function sessionIdComponents(ctx: MapperContext, tsSessionStart: Date): {
|
|
68
|
+
random: string;
|
|
69
|
+
hash: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* FNV-1a 32-bit, hex-encoded (8 lowercase hex chars). Implemented inline —
|
|
73
|
+
* a dependency for 10 lines of arithmetic is not worth the supply-chain
|
|
74
|
+
* surface, and the mapper only needs a stable non-cryptographic digest.
|
|
75
|
+
*/
|
|
76
|
+
export declare function fnv1a32hex(input: string): string;
|
|
32
77
|
//# sourceMappingURL=sleep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../../src/sync/mapper/sleep.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../../src/sync/mapper/sleep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,eAAe,EACf,aAAa,EACd,MAAM,SAAS,CAAA;AAMhB;;;GAGG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK7D,CAAA;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK7D,CAAA;AAEF,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,eAAe,EAAE,CAAA;IAChC,WAAW,EAAE,aAAa,EAAE,CAAA;IAC5B,SAAS,EAAE,WAAW,EAAE,CAAA;CACzB;AAoBD,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,GAAG,EAAE,aAAa,GACjB,sBAAsB,CAgGxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,cAAc,EAAE,IAAI,GACnB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMlC;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOhD"}
|