@beesolve/analytics-processor 0.2.6 → 0.3.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/dist/index.d.ts CHANGED
@@ -39,81 +39,6 @@ declare class AppliedMarkers {
39
39
  }) => TransactPutItem;
40
40
  }
41
41
  import { AthenaClient } from "@aws-sdk/client-athena";
42
- import { DynamoDBDocumentClient as DynamoDBDocumentClient2, TransactWriteCommandInput as TransactWriteCommandInput2 } from "@aws-sdk/lib-dynamodb";
43
- import * as v2 from "valibot";
44
- type TransactWriteItem2 = NonNullable<TransactWriteCommandInput2["TransactItems"]>[number];
45
- type UpdateMembers = NonNullable<TransactWriteItem2["Update"]>;
46
- interface TransactUpdateItem {
47
- readonly Update: UpdateMembers;
48
- }
49
- interface TotalsInput {
50
- readonly timeKey: string;
51
- readonly hits: number;
52
- readonly success: number;
53
- readonly errors: number;
54
- readonly errors4xx: number;
55
- readonly errors5xx: number;
56
- readonly bytesSent: number;
57
- readonly bytesReceived: number;
58
- readonly p50TimeTaken?: number;
59
- readonly p95TimeTaken?: number;
60
- readonly cacheHitRatio?: number;
61
- readonly uniqueIpCount?: number;
62
- }
63
- declare const totalsSchema: v2.ObjectSchema<{
64
- readonly pk: v2.StringSchema<undefined>;
65
- readonly sk: v2.StringSchema<undefined>;
66
- readonly grain: v2.PicklistSchema<readonly ["hour", "day", "month"], undefined>;
67
- readonly timeKey: v2.StringSchema<undefined>;
68
- readonly hits: v2.NumberSchema<undefined>;
69
- readonly success: v2.NumberSchema<undefined>;
70
- readonly errors: v2.NumberSchema<undefined>;
71
- readonly errors4xx: v2.NumberSchema<undefined>;
72
- readonly errors5xx: v2.NumberSchema<undefined>;
73
- readonly bytesSent: v2.NumberSchema<undefined>;
74
- readonly bytesReceived: v2.NumberSchema<undefined>;
75
- readonly p50TimeTaken: v2.OptionalSchema<v2.NumberSchema<undefined>, undefined>;
76
- readonly p95TimeTaken: v2.OptionalSchema<v2.NumberSchema<undefined>, undefined>;
77
- readonly cacheHitRatio: v2.OptionalSchema<v2.NumberSchema<undefined>, undefined>;
78
- readonly uniqueIpCount: v2.OptionalSchema<v2.NumberSchema<undefined>, undefined>;
79
- readonly ttl: v2.OptionalSchema<v2.NumberSchema<undefined>, undefined>;
80
- }, undefined>;
81
- type TotalsItem = v2.InferOutput<typeof totalsSchema>;
82
- declare class Totals {
83
- private readonly props;
84
- constructor(props: {
85
- readonly dynamo: Pick<DynamoDBDocumentClient2, "send">;
86
- readonly tableName: string;
87
- readonly reverseIndexName?: string;
88
- });
89
- readonly addHourly: (props: {
90
- readonly item: TotalsInput;
91
- readonly ttl: number;
92
- }) => Promise<void>;
93
- readonly addAggregate: (props: {
94
- readonly grain: "day" | "month";
95
- readonly item: TotalsInput;
96
- }) => Promise<void>;
97
- readonly buildHourlyUpdate: (props: {
98
- readonly item: TotalsInput;
99
- readonly ttl: number;
100
- }) => TransactUpdateItem;
101
- readonly buildAggregateUpdate: (props: {
102
- readonly grain: "day" | "month";
103
- readonly item: TotalsInput;
104
- }) => TransactUpdateItem;
105
- readonly queryWindow: (props: {
106
- readonly grain: Grain;
107
- readonly startKey: string;
108
- readonly endKey: string;
109
- }) => Promise<Array<TotalsItem>>;
110
- readonly getOne: (props: {
111
- readonly grain: Grain;
112
- readonly timeKey: string;
113
- }) => Promise<TotalsItem | undefined>;
114
- private readonly buildUpdateMembers;
115
- private readonly parseOne;
116
- }
117
42
  interface BreakdownRow {
118
43
  readonly value: string;
119
44
  readonly hits: number;
@@ -128,7 +53,7 @@ interface SummedTotals {
128
53
  readonly bytesSent: number;
129
54
  readonly bytesReceived: number;
130
55
  }
131
- declare function sumTotals(items: Array<TotalsInput>): SummedTotals;
56
+ declare function sumTotals(items: Array<SummedTotals>): SummedTotals;
132
57
  declare function sumBreakdown(items: Array<BreakdownRow>): Array<BreakdownRow>;
133
58
  declare function topNWithOther({ items, n, otherValue }: {
134
59
  items: Array<BreakdownRow>;
@@ -227,34 +152,34 @@ declare function runHourlyAthenaQuery({ athena, database, table, workgroup, outp
227
152
  hourKey: string;
228
153
  topPaths: number;
229
154
  }): Promise<HourlyAthenaResult>;
230
- import { DynamoDBDocumentClient as DynamoDBDocumentClient3, TransactWriteCommandInput as TransactWriteCommandInput3 } from "@aws-sdk/lib-dynamodb";
231
- import * as v3 from "valibot";
232
- type TransactWriteItem3 = NonNullable<TransactWriteCommandInput3["TransactItems"]>[number];
233
- type UpdateMembers2 = NonNullable<TransactWriteItem3["Update"]>;
234
- interface TransactUpdateItem2 {
235
- readonly Update: UpdateMembers2;
155
+ import { DynamoDBDocumentClient as DynamoDBDocumentClient2, TransactWriteCommandInput as TransactWriteCommandInput2 } from "@aws-sdk/lib-dynamodb";
156
+ import * as v2 from "valibot";
157
+ type TransactWriteItem2 = NonNullable<TransactWriteCommandInput2["TransactItems"]>[number];
158
+ type UpdateMembers = NonNullable<TransactWriteItem2["Update"]>;
159
+ interface TransactUpdateItem {
160
+ readonly Update: UpdateMembers;
236
161
  }
237
162
  interface BreakdownInput {
238
163
  readonly value: string;
239
164
  readonly hits: number;
240
165
  readonly bytes: number;
241
166
  }
242
- declare const breakdownSchema: v3.ObjectSchema<{
243
- readonly pk: v3.StringSchema<undefined>;
244
- readonly sk: v3.StringSchema<undefined>;
245
- readonly dimension: v3.PicklistSchema<readonly ["status", "country", "device", "path"], undefined>;
246
- readonly grain: v3.PicklistSchema<readonly ["hour", "day", "month"], undefined>;
247
- readonly periodKey: v3.StringSchema<undefined>;
248
- readonly value: v3.StringSchema<undefined>;
249
- readonly hits: v3.NumberSchema<undefined>;
250
- readonly bytes: v3.NumberSchema<undefined>;
251
- readonly ttl: v3.OptionalSchema<v3.NumberSchema<undefined>, undefined>;
167
+ declare const breakdownSchema: v2.ObjectSchema<{
168
+ readonly pk: v2.StringSchema<undefined>;
169
+ readonly sk: v2.StringSchema<undefined>;
170
+ readonly dimension: v2.PicklistSchema<readonly ["status", "country", "device", "path"], undefined>;
171
+ readonly grain: v2.PicklistSchema<readonly ["hour", "day", "month"], undefined>;
172
+ readonly periodKey: v2.StringSchema<undefined>;
173
+ readonly value: v2.StringSchema<undefined>;
174
+ readonly hits: v2.NumberSchema<undefined>;
175
+ readonly bytes: v2.NumberSchema<undefined>;
176
+ readonly ttl: v2.OptionalSchema<v2.NumberSchema<undefined>, undefined>;
252
177
  }, undefined>;
253
- type BreakdownItem = v3.InferOutput<typeof breakdownSchema>;
178
+ type BreakdownItem = v2.InferOutput<typeof breakdownSchema>;
254
179
  declare class Breakdowns {
255
180
  private readonly props;
256
181
  constructor(props: {
257
- readonly dynamo: Pick<DynamoDBDocumentClient3, "send">;
182
+ readonly dynamo: Pick<DynamoDBDocumentClient2, "send">;
258
183
  readonly tableName: string;
259
184
  readonly reverseIndexName?: string;
260
185
  });
@@ -271,7 +196,7 @@ declare class Breakdowns {
271
196
  readonly periodKey: string;
272
197
  readonly items: Array<BreakdownInput>;
273
198
  readonly ttl?: number;
274
- }) => Array<TransactUpdateItem2>;
199
+ }) => Array<TransactUpdateItem>;
275
200
  readonly getForPeriod: (props: {
276
201
  readonly dimension: Dimension;
277
202
  readonly grain: Grain;
@@ -280,17 +205,119 @@ declare class Breakdowns {
280
205
  private readonly buildUpdateMembers;
281
206
  private readonly parseOne;
282
207
  }
208
+ import { DynamoDBDocumentClient as DynamoDBDocumentClient3 } from "@aws-sdk/lib-dynamodb";
209
+ import * as v3 from "valibot";
210
+ declare const configSchema: v3.ObjectSchema<{
211
+ readonly pk: v3.LiteralSchema<"system#config", undefined>;
212
+ readonly sk: v3.LiteralSchema<"analytics", undefined>;
213
+ readonly startKey: v3.StringSchema<undefined>;
214
+ readonly startedAt: v3.StringSchema<undefined>;
215
+ }, undefined>;
216
+ type AnalyticsConfig = v3.InferOutput<typeof configSchema>;
217
+ declare class Config {
218
+ private readonly props;
219
+ constructor(props: {
220
+ readonly dynamo: Pick<DynamoDBDocumentClient3, "send">;
221
+ readonly tableName: string;
222
+ });
223
+ readonly get: () => Promise<AnalyticsConfig | null>;
224
+ /**
225
+ * Records the earliest ingested hour the first time any data lands, so the UI
226
+ * can clamp navigation to [startKey, now]. Idempotent: once `startKey` exists
227
+ * it is never moved forward, and a conditional guard lets it move earlier only
228
+ * when a backfill ingests an hour older than the stored boundary.
229
+ */
230
+ readonly ensureStart: (props: {
231
+ readonly hourKey: string;
232
+ }) => Promise<void>;
233
+ }
283
234
  type DeviceBucket = "mobile" | "desktop" | "bot" | "other";
284
235
  declare function classifyDevice(userAgent: string): DeviceBucket;
285
236
  import { AthenaClient as AthenaClient2 } from "@aws-sdk/client-athena";
286
- import { DynamoDBDocumentClient as DynamoDBDocumentClient4, TransactWriteCommandInput as TransactWriteCommandInput4 } from "@aws-sdk/lib-dynamodb";
237
+ import { DynamoDBDocumentClient as DynamoDBDocumentClient5, TransactWriteCommandInput as TransactWriteCommandInput4 } from "@aws-sdk/lib-dynamodb";
287
238
  import { IpInfoCache } from "@beesolve/ip-info";
239
+ import { DynamoDBDocumentClient as DynamoDBDocumentClient4, TransactWriteCommandInput as TransactWriteCommandInput3 } from "@aws-sdk/lib-dynamodb";
240
+ import * as v4 from "valibot";
241
+ type TransactWriteItem3 = NonNullable<TransactWriteCommandInput3["TransactItems"]>[number];
242
+ type UpdateMembers2 = NonNullable<TransactWriteItem3["Update"]>;
243
+ interface TransactUpdateItem2 {
244
+ readonly Update: UpdateMembers2;
245
+ }
246
+ interface TotalsInput {
247
+ readonly timeKey: string;
248
+ readonly hits: number;
249
+ readonly success: number;
250
+ readonly errors: number;
251
+ readonly errors4xx: number;
252
+ readonly errors5xx: number;
253
+ readonly bytesSent: number;
254
+ readonly bytesReceived: number;
255
+ readonly p50TimeTaken?: number;
256
+ readonly p95TimeTaken?: number;
257
+ readonly cacheHitRatio?: number;
258
+ readonly uniqueIpCount?: number;
259
+ }
260
+ declare const totalsSchema: v4.ObjectSchema<{
261
+ readonly pk: v4.StringSchema<undefined>;
262
+ readonly sk: v4.StringSchema<undefined>;
263
+ readonly grain: v4.PicklistSchema<readonly ["hour", "day", "month"], undefined>;
264
+ readonly timeKey: v4.StringSchema<undefined>;
265
+ readonly hits: v4.NumberSchema<undefined>;
266
+ readonly success: v4.NumberSchema<undefined>;
267
+ readonly errors: v4.NumberSchema<undefined>;
268
+ readonly errors4xx: v4.NumberSchema<undefined>;
269
+ readonly errors5xx: v4.NumberSchema<undefined>;
270
+ readonly bytesSent: v4.NumberSchema<undefined>;
271
+ readonly bytesReceived: v4.NumberSchema<undefined>;
272
+ readonly p50TimeTaken: v4.OptionalSchema<v4.NumberSchema<undefined>, undefined>;
273
+ readonly p95TimeTaken: v4.OptionalSchema<v4.NumberSchema<undefined>, undefined>;
274
+ readonly cacheHitRatio: v4.OptionalSchema<v4.NumberSchema<undefined>, undefined>;
275
+ readonly uniqueIpCount: v4.OptionalSchema<v4.NumberSchema<undefined>, undefined>;
276
+ readonly ttl: v4.OptionalSchema<v4.NumberSchema<undefined>, undefined>;
277
+ }, undefined>;
278
+ type TotalsItem = v4.InferOutput<typeof totalsSchema>;
279
+ declare class Totals {
280
+ private readonly props;
281
+ constructor(props: {
282
+ readonly dynamo: Pick<DynamoDBDocumentClient4, "send">;
283
+ readonly tableName: string;
284
+ readonly reverseIndexName?: string;
285
+ });
286
+ readonly addHourly: (props: {
287
+ readonly item: TotalsInput;
288
+ readonly ttl: number;
289
+ }) => Promise<void>;
290
+ readonly addAggregate: (props: {
291
+ readonly grain: "day" | "month";
292
+ readonly item: TotalsInput;
293
+ }) => Promise<void>;
294
+ readonly buildHourlyUpdate: (props: {
295
+ readonly item: TotalsInput;
296
+ readonly ttl: number;
297
+ }) => TransactUpdateItem2;
298
+ readonly buildAggregateUpdate: (props: {
299
+ readonly grain: "day" | "month";
300
+ readonly item: TotalsInput;
301
+ }) => TransactUpdateItem2;
302
+ readonly queryWindow: (props: {
303
+ readonly grain: Grain;
304
+ readonly startKey: string;
305
+ readonly endKey: string;
306
+ }) => Promise<Array<TotalsItem>>;
307
+ readonly getOne: (props: {
308
+ readonly grain: Grain;
309
+ readonly timeKey: string;
310
+ }) => Promise<TotalsItem | undefined>;
311
+ private readonly buildUpdateMembers;
312
+ private readonly parseOne;
313
+ }
288
314
  type TransactItem = NonNullable<TransactWriteCommandInput4["TransactItems"]>[number];
289
315
  interface ProcessHourModels {
290
316
  readonly totals: Pick<Totals, "buildHourlyUpdate">;
291
317
  readonly breakdowns: Pick<Breakdowns, "buildUpdates">;
292
318
  readonly markers: Pick<AppliedMarkers, "isApplied" | "buildMarkerPut">;
293
319
  readonly ipCache: Pick<IpInfoCache, "enrichMany">;
320
+ readonly config?: Pick<Config, "ensureStart">;
294
321
  }
295
322
  interface ProcessHourAthena {
296
323
  readonly client: Pick<AthenaClient2, "send">;
@@ -313,17 +340,17 @@ declare function processHour({ hourKey, source, models, athena, options, dynamo
313
340
  models: ProcessHourModels;
314
341
  athena: ProcessHourAthena;
315
342
  options: ProcessHourOptions;
316
- dynamo: Pick<DynamoDBDocumentClient4, "send">;
343
+ dynamo: Pick<DynamoDBDocumentClient5, "send">;
317
344
  }): Promise<ProcessHourResult>;
318
345
  declare function applyDailyRollup({ dayKey, models, dynamo }: {
319
346
  dayKey: string;
320
347
  models: RollupModels;
321
- dynamo: Pick<DynamoDBDocumentClient4, "send">;
348
+ dynamo: Pick<DynamoDBDocumentClient5, "send">;
322
349
  }): Promise<ProcessHourResult>;
323
350
  declare function applyMonthlyRollup({ monthKey, models, dynamo }: {
324
351
  monthKey: string;
325
352
  models: RollupModels;
326
- dynamo: Pick<DynamoDBDocumentClient4, "send">;
353
+ dynamo: Pick<DynamoDBDocumentClient5, "send">;
327
354
  }): Promise<ProcessHourResult>;
328
355
  declare function chunkTransactItems({ leading, rest, limit }: {
329
356
  leading: Array<TransactItem>;
@@ -335,10 +362,48 @@ interface RollupModels {
335
362
  readonly breakdowns: Pick<Breakdowns, "buildUpdates" | "getForPeriod">;
336
363
  readonly markers: Pick<AppliedMarkers, "isApplied" | "buildMarkerPut">;
337
364
  }
365
+ type View = Grain | "year";
366
+ interface ResolveModels {
367
+ readonly totals: Pick<Totals, "queryWindow" | "getOne">;
368
+ readonly breakdowns: Pick<Breakdowns, "getForPeriod">;
369
+ readonly markers: Pick<AppliedMarkers, "isApplied">;
370
+ }
371
+ /**
372
+ * Resolves one bucket's totals, computing an in-progress period on the fly from
373
+ * the grain below it. A past period whose rollup marker is applied is read
374
+ * directly from the stored aggregate ("sealed"); the current period, and any
375
+ * past period still missing its marker, is summed from its children — the
376
+ * current month folds down through the current day to the hours ingested so far.
377
+ *
378
+ * Each level reads its children with a single ranged query (never a get per
379
+ * child): a day sums its hours via one hourly `queryWindow`; a month sums its
380
+ * days, and the one in-progress day among them is resolved from hours. `hour`
381
+ * is terminal.
382
+ */
383
+ declare function resolveTotalsLive({ models, grain, periodKey, now }: {
384
+ readonly models: ResolveModels;
385
+ readonly grain: Grain;
386
+ readonly periodKey: string;
387
+ readonly now: Date;
388
+ }): Promise<SummedTotals>;
389
+ declare function resolveBreakdownLive({ models, dimension, grain, periodKey, now }: {
390
+ readonly models: ResolveModels;
391
+ readonly dimension: Dimension;
392
+ readonly grain: Grain;
393
+ readonly periodKey: string;
394
+ readonly now: Date;
395
+ }): Promise<Array<BreakdownRow>>;
338
396
  declare function yearKey(date: Date): string;
339
397
  declare function monthKey2(date: Date): string;
340
398
  declare function dayKey2(date: Date): string;
341
399
  declare function hourKey2(date: Date): string;
400
+ /**
401
+ * Parses a period key (any grain) back to the UTC Date at the start of that
402
+ * bucket. `YYYY` → Jan 1 00:00, `YYYY-MM` → 1st 00:00, `YYYY-MM-DD` → 00:00,
403
+ * `YYYY-MM-DD-HH` → that hour. Throws on a malformed key.
404
+ */
405
+ declare function parsePeriodKey(periodKey: string): Date;
406
+ declare function grainForPeriodKey(periodKey: string): Grain | "year";
342
407
  declare function maxWindowFor(grain: Grain): number;
343
408
  declare function shiftAnchor({ grain, anchor, size, direction }: {
344
409
  grain: Grain;
@@ -358,4 +423,4 @@ declare function hoursBetween({ from, to }: {
358
423
  from: Date;
359
424
  to: Date;
360
425
  }): Array<string>;
361
- export { AppliedMarker, AppliedMarkers, AppliedSource, AthenaColumnMap, BreakdownInput, BreakdownItem, BreakdownRow, Breakdowns, DeviceBucket, Dimension, Grain, HourlyAthenaResult, HourlyAthenaTotals, ProcessHourAthena, ProcessHourModels, ProcessHourOptions, ProcessHourResult, SummedTotals, Totals, TotalsInput, TotalsItem, TransactItem, appliedMarkerSchema, appliedSources, applyDailyRollup, applyMonthlyRollup, breakdownSchema, buildHourlyQuery, chunkTransactItems, classifyDevice, columnMaps, dayKey2 as dayKey, dimensions, grains, hourKey2 as hourKey, hourKeyParts, hoursBetween, maxWindowFor, monthKey2 as monthKey, processHour, requiredRecordFields, runHourlyAthenaQuery, shiftAnchor, sumBreakdown, sumTotals, topNWithOther, totalsSchema, windowKeys, yearKey };
426
+ export { AnalyticsConfig, AppliedMarker, AppliedMarkers, AppliedSource, AthenaColumnMap, BreakdownInput, BreakdownItem, BreakdownRow, Breakdowns, Config, DeviceBucket, Dimension, Grain, HourlyAthenaResult, HourlyAthenaTotals, ProcessHourAthena, ProcessHourModels, ProcessHourOptions, ProcessHourResult, ResolveModels, SummedTotals, Totals, TotalsInput, TotalsItem, TransactItem, View, appliedMarkerSchema, appliedSources, applyDailyRollup, applyMonthlyRollup, breakdownSchema, buildHourlyQuery, chunkTransactItems, classifyDevice, columnMaps, configSchema, dayKey2 as dayKey, dimensions, grainForPeriodKey, grains, hourKey2 as hourKey, hourKeyParts, hoursBetween, maxWindowFor, monthKey2 as monthKey, parsePeriodKey, processHour, requiredRecordFields, resolveBreakdownLive, resolveTotalsLive, runHourlyAthenaQuery, shiftAnchor, sumBreakdown, sumTotals, topNWithOther, totalsSchema, windowKeys, yearKey };