measurr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,619 @@
1
+ import { z } from 'zod';
2
+ import type { AnyDataset, Dataset, DimensionValue, MeasureValue } from './dataset.js';
3
+ import { type PeriodInput } from './period.js';
4
+ export declare const MAX_MEASURES = 4;
5
+ export declare const MAX_GROUP_BY = 2;
6
+ export declare const MAX_LIMIT = 100;
7
+ export declare const DEFAULT_LIMIT = 20;
8
+ type EmptinessFilter<Name> = {
9
+ dimension: Name;
10
+ op: 'isEmpty' | 'isNotEmpty';
11
+ };
12
+ type FilterFor<Name extends string, Dim> = Dim extends {
13
+ category: 'closed';
14
+ keyType?: infer K;
15
+ } ? {
16
+ dimension: Name;
17
+ op: 'in' | 'notIn';
18
+ values: readonly K[];
19
+ } | EmptinessFilter<Name> : Dim extends {
20
+ category: 'open';
21
+ } ? {
22
+ dimension: Name;
23
+ op: 'in' | 'notIn';
24
+ values: readonly string[];
25
+ } | EmptinessFilter<Name> : Dim extends {
26
+ category: 'numeric';
27
+ } ? {
28
+ dimension: Name;
29
+ op: 'between';
30
+ from: number;
31
+ to: number;
32
+ } | EmptinessFilter<Name> : {
33
+ dimension: Name;
34
+ op: 'between';
35
+ from: string;
36
+ to: string;
37
+ } | EmptinessFilter<Name>;
38
+ /** Dimensions a query may group by: all but `filterOnly` ones. */
39
+ export type GroupableKey<Dm> = {
40
+ [K in keyof Dm & string]: Dm[K] extends {
41
+ groupable: false;
42
+ } ? never : K;
43
+ }[keyof Dm & string];
44
+ /** Dimensions a query may filter by: all but `groupOnly` ones. */
45
+ export type FilterableKey<Dm> = {
46
+ [K in keyof Dm & string]: Dm[K] extends {
47
+ filterable: false;
48
+ } ? never : K;
49
+ }[keyof Dm & string];
50
+ export type DatasetFilter<Dm> = {
51
+ [Name in FilterableKey<Dm>]: FilterFor<Name, Dm[Name]>;
52
+ }[FilterableKey<Dm>];
53
+ /** The query one dataset accepts. Distributes over a union of datasets. */
54
+ export type DatasetQuery<DS> = DS extends Dataset<infer K, infer M, infer Dm, infer T> ? {
55
+ dataset: K;
56
+ measures: readonly (keyof M & string)[];
57
+ groupBy?: readonly GroupableKey<Dm>[] | undefined;
58
+ filters?: readonly DatasetFilter<Dm>[] | undefined;
59
+ /** Null or omitted: the default period. */
60
+ period?: PeriodInput | null | undefined;
61
+ time?: T | undefined;
62
+ compareToPrevious?: boolean | undefined;
63
+ sort?: {
64
+ by: (keyof M & string) | GroupableKey<Dm>;
65
+ direction?: 'asc' | 'desc' | undefined;
66
+ } | undefined;
67
+ limit?: number | undefined;
68
+ } : never;
69
+ type GroupKeys<Q> = Q extends {
70
+ groupBy: readonly (infer G extends string)[];
71
+ } ? G : never;
72
+ type Simplify<T> = {
73
+ [K in keyof T]: T[K];
74
+ } & {};
75
+ export type ResultRow<M, Dm, MeasureKey extends string, GroupKey extends string> = Simplify<{
76
+ [G in GroupKey & keyof Dm]: DimensionValue<Dm[G]>;
77
+ } & {
78
+ [Key in MeasureKey & keyof M]: MeasureValue<M[Key]>;
79
+ }>;
80
+ export type ResultTotals<M, MeasureKey extends string> = Simplify<{
81
+ [Key in MeasureKey & keyof M]: MeasureValue<M[Key]>;
82
+ }>;
83
+ export type QueryResult<Row, Totals> = {
84
+ period: {
85
+ from: string;
86
+ to: string;
87
+ timezone: string;
88
+ };
89
+ rows: Row[];
90
+ totals: Totals;
91
+ previous?: {
92
+ period: {
93
+ from: string;
94
+ to: string;
95
+ timezone: string;
96
+ };
97
+ rows: Row[];
98
+ totals: Totals;
99
+ };
100
+ notes: string[];
101
+ truncated: boolean;
102
+ };
103
+ /** The typed result of query `Q` against the datasets `DS`. */
104
+ export type ResultOf<DS, Q> = DS extends Dataset<infer K, infer M, infer Dm, string> ? Q extends {
105
+ dataset: K;
106
+ measures: readonly (infer Mk extends string)[];
107
+ } ? QueryResult<ResultRow<M, Dm, Mk, GroupKeys<Q>>, ResultTotals<M, Mk>> : never : never;
108
+ export declare const periodSchema: z.ZodUnion<readonly [z.ZodObject<{
109
+ last: z.ZodObject<{
110
+ days: z.ZodNumber;
111
+ }, z.core.$strip>;
112
+ }, z.core.$strip>, z.ZodObject<{
113
+ from: z.ZodISODate;
114
+ to: z.ZodISODate;
115
+ }, z.core.$strip>, z.ZodObject<{
116
+ preset: z.ZodEnum<{
117
+ lastMonth: "lastMonth";
118
+ lastWeek: "lastWeek";
119
+ thisMonth: "thisMonth";
120
+ thisWeek: "thisWeek";
121
+ thisYear: "thisYear";
122
+ today: "today";
123
+ yesterday: "yesterday";
124
+ }>;
125
+ }, z.core.$strip>]>;
126
+ /** The input schema branch for one dataset. */
127
+ export declare function datasetQuerySchema(dataset: AnyDataset): z.ZodObject<{
128
+ dataset: z.ZodLiteral<string>;
129
+ measures: z.ZodArray<z.ZodEnum<{
130
+ [x: string]: string;
131
+ }>>;
132
+ groupBy?: z.ZodOptional<z.ZodArray<z.ZodEnum<{
133
+ [x: string]: string;
134
+ }>>>;
135
+ filters?: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodDiscriminatedUnion<[z.ZodObject<{
136
+ dimension: z.ZodLiteral<string>;
137
+ op: z.ZodEnum<{
138
+ in: "in";
139
+ notIn: "notIn";
140
+ }>;
141
+ values: z.ZodArray<z.ZodEnum<{
142
+ [x: string]: string;
143
+ }>>;
144
+ }, z.core.$strip>, z.ZodObject<{
145
+ dimension: z.ZodLiteral<string>;
146
+ op: z.ZodEnum<{
147
+ isEmpty: "isEmpty";
148
+ isNotEmpty: "isNotEmpty";
149
+ }>;
150
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
151
+ dimension: z.ZodLiteral<string>;
152
+ op: z.ZodEnum<{
153
+ in: "in";
154
+ notIn: "notIn";
155
+ }>;
156
+ values: z.ZodArray<z.ZodString>;
157
+ }, z.core.$strip>, z.ZodObject<{
158
+ dimension: z.ZodLiteral<string>;
159
+ op: z.ZodEnum<{
160
+ isEmpty: "isEmpty";
161
+ isNotEmpty: "isNotEmpty";
162
+ }>;
163
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
164
+ dimension: z.ZodLiteral<string>;
165
+ op: z.ZodLiteral<"between">;
166
+ from: z.ZodNumber;
167
+ to: z.ZodNumber;
168
+ }, z.core.$strip>, z.ZodObject<{
169
+ dimension: z.ZodLiteral<string>;
170
+ op: z.ZodEnum<{
171
+ isEmpty: "isEmpty";
172
+ isNotEmpty: "isNotEmpty";
173
+ }>;
174
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
175
+ dimension: z.ZodLiteral<string>;
176
+ op: z.ZodLiteral<"between">;
177
+ from: z.ZodISODate;
178
+ to: z.ZodISODate;
179
+ }, z.core.$strip>, z.ZodObject<{
180
+ dimension: z.ZodLiteral<string>;
181
+ op: z.ZodEnum<{
182
+ isEmpty: "isEmpty";
183
+ isNotEmpty: "isNotEmpty";
184
+ }>;
185
+ }, z.core.$strip>], "op">, ...(z.ZodDiscriminatedUnion<[z.ZodObject<{
186
+ dimension: z.ZodLiteral<string>;
187
+ op: z.ZodEnum<{
188
+ in: "in";
189
+ notIn: "notIn";
190
+ }>;
191
+ values: z.ZodArray<z.ZodEnum<{
192
+ [x: string]: string;
193
+ }>>;
194
+ }, z.core.$strip>, z.ZodObject<{
195
+ dimension: z.ZodLiteral<string>;
196
+ op: z.ZodEnum<{
197
+ isEmpty: "isEmpty";
198
+ isNotEmpty: "isNotEmpty";
199
+ }>;
200
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
201
+ dimension: z.ZodLiteral<string>;
202
+ op: z.ZodEnum<{
203
+ in: "in";
204
+ notIn: "notIn";
205
+ }>;
206
+ values: z.ZodArray<z.ZodString>;
207
+ }, z.core.$strip>, z.ZodObject<{
208
+ dimension: z.ZodLiteral<string>;
209
+ op: z.ZodEnum<{
210
+ isEmpty: "isEmpty";
211
+ isNotEmpty: "isNotEmpty";
212
+ }>;
213
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
214
+ dimension: z.ZodLiteral<string>;
215
+ op: z.ZodLiteral<"between">;
216
+ from: z.ZodNumber;
217
+ to: z.ZodNumber;
218
+ }, z.core.$strip>, z.ZodObject<{
219
+ dimension: z.ZodLiteral<string>;
220
+ op: z.ZodEnum<{
221
+ isEmpty: "isEmpty";
222
+ isNotEmpty: "isNotEmpty";
223
+ }>;
224
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
225
+ dimension: z.ZodLiteral<string>;
226
+ op: z.ZodLiteral<"between">;
227
+ from: z.ZodISODate;
228
+ to: z.ZodISODate;
229
+ }, z.core.$strip>, z.ZodObject<{
230
+ dimension: z.ZodLiteral<string>;
231
+ op: z.ZodEnum<{
232
+ isEmpty: "isEmpty";
233
+ isNotEmpty: "isNotEmpty";
234
+ }>;
235
+ }, z.core.$strip>], "op">)[]], "dimension">>>;
236
+ period: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
237
+ last: z.ZodObject<{
238
+ days: z.ZodNumber;
239
+ }, z.core.$strip>;
240
+ }, z.core.$strip>, z.ZodObject<{
241
+ from: z.ZodISODate;
242
+ to: z.ZodISODate;
243
+ }, z.core.$strip>, z.ZodObject<{
244
+ preset: z.ZodEnum<{
245
+ lastMonth: "lastMonth";
246
+ lastWeek: "lastWeek";
247
+ thisMonth: "thisMonth";
248
+ thisWeek: "thisWeek";
249
+ thisYear: "thisYear";
250
+ today: "today";
251
+ yesterday: "yesterday";
252
+ }>;
253
+ }, z.core.$strip>]>>>;
254
+ time?: z.ZodOptional<z.ZodEnum<{
255
+ [x: string]: string;
256
+ }>>;
257
+ compareToPrevious: z.ZodOptional<z.ZodBoolean>;
258
+ sort: z.ZodOptional<z.ZodObject<{
259
+ by: z.ZodEnum<{
260
+ [x: string]: string;
261
+ }>;
262
+ direction: z.ZodOptional<z.ZodEnum<{
263
+ asc: "asc";
264
+ desc: "desc";
265
+ }>>;
266
+ }, z.core.$strip>>;
267
+ limit: z.ZodOptional<z.ZodNumber>;
268
+ }, z.core.$strip>;
269
+ /** A discriminated union on `dataset`, one branch per dataset. */
270
+ export declare function buildQuerySchema(datasets: readonly AnyDataset[]): z.ZodDiscriminatedUnion<[z.ZodObject<{
271
+ dataset: z.ZodLiteral<string>;
272
+ measures: z.ZodArray<z.ZodEnum<{
273
+ [x: string]: string;
274
+ }>>;
275
+ groupBy?: z.ZodOptional<z.ZodArray<z.ZodEnum<{
276
+ [x: string]: string;
277
+ }>>>;
278
+ filters?: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodDiscriminatedUnion<[z.ZodObject<{
279
+ dimension: z.ZodLiteral<string>;
280
+ op: z.ZodEnum<{
281
+ in: "in";
282
+ notIn: "notIn";
283
+ }>;
284
+ values: z.ZodArray<z.ZodEnum<{
285
+ [x: string]: string;
286
+ }>>;
287
+ }, z.core.$strip>, z.ZodObject<{
288
+ dimension: z.ZodLiteral<string>;
289
+ op: z.ZodEnum<{
290
+ isEmpty: "isEmpty";
291
+ isNotEmpty: "isNotEmpty";
292
+ }>;
293
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
294
+ dimension: z.ZodLiteral<string>;
295
+ op: z.ZodEnum<{
296
+ in: "in";
297
+ notIn: "notIn";
298
+ }>;
299
+ values: z.ZodArray<z.ZodString>;
300
+ }, z.core.$strip>, z.ZodObject<{
301
+ dimension: z.ZodLiteral<string>;
302
+ op: z.ZodEnum<{
303
+ isEmpty: "isEmpty";
304
+ isNotEmpty: "isNotEmpty";
305
+ }>;
306
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
307
+ dimension: z.ZodLiteral<string>;
308
+ op: z.ZodLiteral<"between">;
309
+ from: z.ZodNumber;
310
+ to: z.ZodNumber;
311
+ }, z.core.$strip>, z.ZodObject<{
312
+ dimension: z.ZodLiteral<string>;
313
+ op: z.ZodEnum<{
314
+ isEmpty: "isEmpty";
315
+ isNotEmpty: "isNotEmpty";
316
+ }>;
317
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
318
+ dimension: z.ZodLiteral<string>;
319
+ op: z.ZodLiteral<"between">;
320
+ from: z.ZodISODate;
321
+ to: z.ZodISODate;
322
+ }, z.core.$strip>, z.ZodObject<{
323
+ dimension: z.ZodLiteral<string>;
324
+ op: z.ZodEnum<{
325
+ isEmpty: "isEmpty";
326
+ isNotEmpty: "isNotEmpty";
327
+ }>;
328
+ }, z.core.$strip>], "op">, ...(z.ZodDiscriminatedUnion<[z.ZodObject<{
329
+ dimension: z.ZodLiteral<string>;
330
+ op: z.ZodEnum<{
331
+ in: "in";
332
+ notIn: "notIn";
333
+ }>;
334
+ values: z.ZodArray<z.ZodEnum<{
335
+ [x: string]: string;
336
+ }>>;
337
+ }, z.core.$strip>, z.ZodObject<{
338
+ dimension: z.ZodLiteral<string>;
339
+ op: z.ZodEnum<{
340
+ isEmpty: "isEmpty";
341
+ isNotEmpty: "isNotEmpty";
342
+ }>;
343
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
344
+ dimension: z.ZodLiteral<string>;
345
+ op: z.ZodEnum<{
346
+ in: "in";
347
+ notIn: "notIn";
348
+ }>;
349
+ values: z.ZodArray<z.ZodString>;
350
+ }, z.core.$strip>, z.ZodObject<{
351
+ dimension: z.ZodLiteral<string>;
352
+ op: z.ZodEnum<{
353
+ isEmpty: "isEmpty";
354
+ isNotEmpty: "isNotEmpty";
355
+ }>;
356
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
357
+ dimension: z.ZodLiteral<string>;
358
+ op: z.ZodLiteral<"between">;
359
+ from: z.ZodNumber;
360
+ to: z.ZodNumber;
361
+ }, z.core.$strip>, z.ZodObject<{
362
+ dimension: z.ZodLiteral<string>;
363
+ op: z.ZodEnum<{
364
+ isEmpty: "isEmpty";
365
+ isNotEmpty: "isNotEmpty";
366
+ }>;
367
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
368
+ dimension: z.ZodLiteral<string>;
369
+ op: z.ZodLiteral<"between">;
370
+ from: z.ZodISODate;
371
+ to: z.ZodISODate;
372
+ }, z.core.$strip>, z.ZodObject<{
373
+ dimension: z.ZodLiteral<string>;
374
+ op: z.ZodEnum<{
375
+ isEmpty: "isEmpty";
376
+ isNotEmpty: "isNotEmpty";
377
+ }>;
378
+ }, z.core.$strip>], "op">)[]], "dimension">>>;
379
+ period: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
380
+ last: z.ZodObject<{
381
+ days: z.ZodNumber;
382
+ }, z.core.$strip>;
383
+ }, z.core.$strip>, z.ZodObject<{
384
+ from: z.ZodISODate;
385
+ to: z.ZodISODate;
386
+ }, z.core.$strip>, z.ZodObject<{
387
+ preset: z.ZodEnum<{
388
+ lastMonth: "lastMonth";
389
+ lastWeek: "lastWeek";
390
+ thisMonth: "thisMonth";
391
+ thisWeek: "thisWeek";
392
+ thisYear: "thisYear";
393
+ today: "today";
394
+ yesterday: "yesterday";
395
+ }>;
396
+ }, z.core.$strip>]>>>;
397
+ time?: z.ZodOptional<z.ZodEnum<{
398
+ [x: string]: string;
399
+ }>>;
400
+ compareToPrevious: z.ZodOptional<z.ZodBoolean>;
401
+ sort: z.ZodOptional<z.ZodObject<{
402
+ by: z.ZodEnum<{
403
+ [x: string]: string;
404
+ }>;
405
+ direction: z.ZodOptional<z.ZodEnum<{
406
+ asc: "asc";
407
+ desc: "desc";
408
+ }>>;
409
+ }, z.core.$strip>>;
410
+ limit: z.ZodOptional<z.ZodNumber>;
411
+ }, z.core.$strip>, ...z.ZodObject<{
412
+ dataset: z.ZodLiteral<string>;
413
+ measures: z.ZodArray<z.ZodEnum<{
414
+ [x: string]: string;
415
+ }>>;
416
+ groupBy?: z.ZodOptional<z.ZodArray<z.ZodEnum<{
417
+ [x: string]: string;
418
+ }>>>;
419
+ filters?: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodDiscriminatedUnion<[z.ZodObject<{
420
+ dimension: z.ZodLiteral<string>;
421
+ op: z.ZodEnum<{
422
+ in: "in";
423
+ notIn: "notIn";
424
+ }>;
425
+ values: z.ZodArray<z.ZodEnum<{
426
+ [x: string]: string;
427
+ }>>;
428
+ }, z.core.$strip>, z.ZodObject<{
429
+ dimension: z.ZodLiteral<string>;
430
+ op: z.ZodEnum<{
431
+ isEmpty: "isEmpty";
432
+ isNotEmpty: "isNotEmpty";
433
+ }>;
434
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
435
+ dimension: z.ZodLiteral<string>;
436
+ op: z.ZodEnum<{
437
+ in: "in";
438
+ notIn: "notIn";
439
+ }>;
440
+ values: z.ZodArray<z.ZodString>;
441
+ }, z.core.$strip>, z.ZodObject<{
442
+ dimension: z.ZodLiteral<string>;
443
+ op: z.ZodEnum<{
444
+ isEmpty: "isEmpty";
445
+ isNotEmpty: "isNotEmpty";
446
+ }>;
447
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
448
+ dimension: z.ZodLiteral<string>;
449
+ op: z.ZodLiteral<"between">;
450
+ from: z.ZodNumber;
451
+ to: z.ZodNumber;
452
+ }, z.core.$strip>, z.ZodObject<{
453
+ dimension: z.ZodLiteral<string>;
454
+ op: z.ZodEnum<{
455
+ isEmpty: "isEmpty";
456
+ isNotEmpty: "isNotEmpty";
457
+ }>;
458
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
459
+ dimension: z.ZodLiteral<string>;
460
+ op: z.ZodLiteral<"between">;
461
+ from: z.ZodISODate;
462
+ to: z.ZodISODate;
463
+ }, z.core.$strip>, z.ZodObject<{
464
+ dimension: z.ZodLiteral<string>;
465
+ op: z.ZodEnum<{
466
+ isEmpty: "isEmpty";
467
+ isNotEmpty: "isNotEmpty";
468
+ }>;
469
+ }, z.core.$strip>], "op">, ...(z.ZodDiscriminatedUnion<[z.ZodObject<{
470
+ dimension: z.ZodLiteral<string>;
471
+ op: z.ZodEnum<{
472
+ in: "in";
473
+ notIn: "notIn";
474
+ }>;
475
+ values: z.ZodArray<z.ZodEnum<{
476
+ [x: string]: string;
477
+ }>>;
478
+ }, z.core.$strip>, z.ZodObject<{
479
+ dimension: z.ZodLiteral<string>;
480
+ op: z.ZodEnum<{
481
+ isEmpty: "isEmpty";
482
+ isNotEmpty: "isNotEmpty";
483
+ }>;
484
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
485
+ dimension: z.ZodLiteral<string>;
486
+ op: z.ZodEnum<{
487
+ in: "in";
488
+ notIn: "notIn";
489
+ }>;
490
+ values: z.ZodArray<z.ZodString>;
491
+ }, z.core.$strip>, z.ZodObject<{
492
+ dimension: z.ZodLiteral<string>;
493
+ op: z.ZodEnum<{
494
+ isEmpty: "isEmpty";
495
+ isNotEmpty: "isNotEmpty";
496
+ }>;
497
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
498
+ dimension: z.ZodLiteral<string>;
499
+ op: z.ZodLiteral<"between">;
500
+ from: z.ZodNumber;
501
+ to: z.ZodNumber;
502
+ }, z.core.$strip>, z.ZodObject<{
503
+ dimension: z.ZodLiteral<string>;
504
+ op: z.ZodEnum<{
505
+ isEmpty: "isEmpty";
506
+ isNotEmpty: "isNotEmpty";
507
+ }>;
508
+ }, z.core.$strip>], "op"> | z.ZodDiscriminatedUnion<[z.ZodObject<{
509
+ dimension: z.ZodLiteral<string>;
510
+ op: z.ZodLiteral<"between">;
511
+ from: z.ZodISODate;
512
+ to: z.ZodISODate;
513
+ }, z.core.$strip>, z.ZodObject<{
514
+ dimension: z.ZodLiteral<string>;
515
+ op: z.ZodEnum<{
516
+ isEmpty: "isEmpty";
517
+ isNotEmpty: "isNotEmpty";
518
+ }>;
519
+ }, z.core.$strip>], "op">)[]], "dimension">>>;
520
+ period: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodObject<{
521
+ last: z.ZodObject<{
522
+ days: z.ZodNumber;
523
+ }, z.core.$strip>;
524
+ }, z.core.$strip>, z.ZodObject<{
525
+ from: z.ZodISODate;
526
+ to: z.ZodISODate;
527
+ }, z.core.$strip>, z.ZodObject<{
528
+ preset: z.ZodEnum<{
529
+ lastMonth: "lastMonth";
530
+ lastWeek: "lastWeek";
531
+ thisMonth: "thisMonth";
532
+ thisWeek: "thisWeek";
533
+ thisYear: "thisYear";
534
+ today: "today";
535
+ yesterday: "yesterday";
536
+ }>;
537
+ }, z.core.$strip>]>>>;
538
+ time?: z.ZodOptional<z.ZodEnum<{
539
+ [x: string]: string;
540
+ }>>;
541
+ compareToPrevious: z.ZodOptional<z.ZodBoolean>;
542
+ sort: z.ZodOptional<z.ZodObject<{
543
+ by: z.ZodEnum<{
544
+ [x: string]: string;
545
+ }>;
546
+ direction: z.ZodOptional<z.ZodEnum<{
547
+ asc: "asc";
548
+ desc: "desc";
549
+ }>>;
550
+ }, z.core.$strip>>;
551
+ limit: z.ZodOptional<z.ZodNumber>;
552
+ }, z.core.$strip>[]], "dataset">;
553
+ /** The result schema for one dataset. Row fields exist only for grouped dimensions. */
554
+ export declare function datasetResultSchema(dataset: AnyDataset): z.ZodObject<{
555
+ period: z.ZodObject<{
556
+ from: z.ZodString;
557
+ to: z.ZodString;
558
+ timezone: z.ZodString;
559
+ }, z.core.$strip>;
560
+ rows: z.ZodArray<z.ZodObject<{
561
+ [x: string]: z.ZodOptional<z.ZodNullable<z.ZodNumber>> | z.ZodOptional<z.ZodObject<{
562
+ key: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
563
+ label: z.ZodString;
564
+ }, z.core.$strip>>;
565
+ }, z.core.$strip>>;
566
+ totals: z.ZodObject<{
567
+ [x: string]: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
568
+ }, z.core.$strip>;
569
+ previous: z.ZodOptional<z.ZodObject<{
570
+ period: z.ZodObject<{
571
+ from: z.ZodString;
572
+ to: z.ZodString;
573
+ timezone: z.ZodString;
574
+ }, z.core.$strip>;
575
+ rows: z.ZodArray<z.ZodObject<{
576
+ [x: string]: z.ZodOptional<z.ZodNullable<z.ZodNumber>> | z.ZodOptional<z.ZodObject<{
577
+ key: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
578
+ label: z.ZodString;
579
+ }, z.core.$strip>>;
580
+ }, z.core.$strip>>;
581
+ totals: z.ZodObject<{
582
+ [x: string]: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
583
+ }, z.core.$strip>;
584
+ }, z.core.$strip>>;
585
+ notes: z.ZodArray<z.ZodString>;
586
+ truncated: z.ZodBoolean;
587
+ }, z.core.$strip>;
588
+ /**
589
+ * Any dataset's result, without the per-dataset keys (`analytics.resultSchema(key)` has
590
+ * those): rows hold `{ key, label }` per grouped dimension and a number or null per measure.
591
+ * For contracts that must not depend on the dataset list, such as an LLM tool's output.
592
+ */
593
+ export declare const AnalyticsResultSchema: z.ZodObject<{
594
+ period: z.ZodObject<{
595
+ from: z.ZodString;
596
+ to: z.ZodString;
597
+ timezone: z.ZodString;
598
+ }, z.core.$strip>;
599
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
600
+ key: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
601
+ label: z.ZodString;
602
+ }, z.core.$strip>, z.ZodNullable<z.ZodNumber>]>>>;
603
+ totals: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodNumber>>;
604
+ previous: z.ZodOptional<z.ZodObject<{
605
+ period: z.ZodObject<{
606
+ from: z.ZodString;
607
+ to: z.ZodString;
608
+ timezone: z.ZodString;
609
+ }, z.core.$strip>;
610
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
611
+ key: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
612
+ label: z.ZodString;
613
+ }, z.core.$strip>, z.ZodNullable<z.ZodNumber>]>>>;
614
+ totals: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodNumber>>;
615
+ }, z.core.$strip>>;
616
+ notes: z.ZodArray<z.ZodString>;
617
+ truncated: z.ZodBoolean;
618
+ }, z.core.$strip>;
619
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The contract test kit: the same checks for every dataset, run against a seeded database.
3
+ * Framework-free: `checkDatasetContract` returns a report and `assertDatasetContract` throws
4
+ * with every failure listed, so it works from `bun:test`, vitest or a script.
5
+ */
6
+ import { type AnalyticsSource } from './analytics.js';
7
+ import { type AnyDataset } from './dataset.js';
8
+ import type { PeriodInput } from './period.js';
9
+ export type ContractCheck = {
10
+ name: string;
11
+ ok: boolean;
12
+ message: string | undefined;
13
+ };
14
+ export type ContractReport = {
15
+ ok: boolean;
16
+ checks: ContractCheck[];
17
+ };
18
+ export type ContractOptions = {
19
+ dataset: AnyDataset;
20
+ source: AnalyticsSource;
21
+ /** A tenant with seeded rows in the period. */
22
+ tenant: string | number;
23
+ /** A tenant with no rows; defaults to a sentinel that no real tenant uses. */
24
+ emptyTenant?: string | number;
25
+ /** A timezone far from UTC makes bucketing mistakes visible. Defaults to Pacific/Auckland. */
26
+ timezone?: string;
27
+ /** Defaults to the last 365 days. */
28
+ period?: PeriodInput;
29
+ now?: () => Date;
30
+ };
31
+ /**
32
+ * Checks that every measure, grouping and filter compiles and runs, that groups add up to the
33
+ * total, that labels resolve, that another tenant's rows never count, and that day buckets
34
+ * agree with periods in the timezone.
35
+ */
36
+ export declare function checkDatasetContract(options: ContractOptions): Promise<ContractReport>;
37
+ /** Runs `checkDatasetContract` and throws with every failed check listed. */
38
+ export declare function assertDatasetContract(options: ContractOptions): Promise<ContractReport>;