From 31a9744822da936c74b36f7388d67ad32352dc24 Mon Sep 17 00:00:00 2001 From: Devyash Saini Date: Tue, 2 Jun 2026 23:20:15 +0530 Subject: [PATCH 1/6] feat: populate reportedTimestamp with current time, allow user override --- packages/scrawn/src/core/scrawn.ts | 6 ++++-- packages/scrawn/src/core/types/event.ts | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/scrawn/src/core/scrawn.ts b/packages/scrawn/src/core/scrawn.ts index d1ca92c..1f4a775 100644 --- a/packages/scrawn/src/core/scrawn.ts +++ b/packages/scrawn/src/core/scrawn.ts @@ -811,7 +811,8 @@ export class Scrawn< const request = { type: EventType.BASIC_USAGE, userId: payload.userId, - reportedTimestamp: 0, + reportedTimestamp: + payload.reportedTimestamp ?? Math.floor(Date.now() / 1000), eventId, idempotencyKey, basicUsage, @@ -1153,7 +1154,8 @@ export class Scrawn< const request = { type: EventType.AI_TOKEN_USAGE, userId: validated.userId, - reportedTimestamp: 0, + reportedTimestamp: + validated.reportedTimestamp ?? Math.floor(Date.now() / 1000), eventId, idempotencyKey, aiTokenUsage, diff --git a/packages/scrawn/src/core/types/event.ts b/packages/scrawn/src/core/types/event.ts index 64fca62..e9d3042 100644 --- a/packages/scrawn/src/core/types/event.ts +++ b/packages/scrawn/src/core/types/event.ts @@ -98,6 +98,7 @@ export const EventPayloadSchema = z.object({ userId: z.string().min(1, "userId must be a non-empty string"), debit: DebitSchemaNoTokens, metadata: z.record(z.string(), z.unknown()).optional(), + reportedTimestamp: z.number().int().optional(), }); /** @@ -160,6 +161,7 @@ export type EventPayload = { userId: string; debit: Debit; metadata?: Record; + reportedTimestamp?: number; }; /** @@ -348,6 +350,7 @@ export const AITokenUsagePayloadSchema = z.object({ .nonnegative("outputCacheTokens must be non-negative") .optional(), outputCacheDebit: DebitSchemaWithTokens.optional(), + reportedTimestamp: z.number().int().optional(), }); /** @@ -425,4 +428,6 @@ export type AITokenUsagePayload = { outputCacheTokens?: number; /** Debit pricing for cached output tokens. */ outputCacheDebit?: Debit; + /** Unix timestamp (seconds) when this event was created. Auto-set if omitted. */ + reportedTimestamp?: number; }; From a2fdad58b76b27b28624688375f3dc460c348c58 Mon Sep 17 00:00:00 2001 From: Devyash Saini Date: Tue, 2 Jun 2026 23:21:52 +0530 Subject: [PATCH 2/6] chore: release Signed-off-by: Devyash Saini --- .changeset/bold-impalas-wait.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/bold-impalas-wait.md diff --git a/.changeset/bold-impalas-wait.md b/.changeset/bold-impalas-wait.md new file mode 100644 index 0000000..6fd32b0 --- /dev/null +++ b/.changeset/bold-impalas-wait.md @@ -0,0 +1,5 @@ +--- +"@scrawn/core": patch +--- + +feat: reported timestamp From 7b56e457a77fd5dd1f2456de240d438d11a67a9f Mon Sep 17 00:00:00 2001 From: Devyash Saini Date: Tue, 2 Jun 2026 23:25:11 +0530 Subject: [PATCH 3/6] fix: thread reportedTimestamp through consumeEvent and normalizedPayload --- packages/scrawn/src/core/scrawn.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/scrawn/src/core/scrawn.ts b/packages/scrawn/src/core/scrawn.ts index 1f4a775..8ab6733 100644 --- a/packages/scrawn/src/core/scrawn.ts +++ b/packages/scrawn/src/core/scrawn.ts @@ -475,6 +475,7 @@ export class Scrawn< userId: validationResult.data.userId, debit, metadata: validationResult.data.metadata, + reportedTimestamp: validationResult.data.reportedTimestamp, }; const attempt = () => @@ -763,6 +764,7 @@ export class Scrawn< userId: string; debit: NormalizedDebit; metadata?: Record; + reportedTimestamp?: number; }, authMethodName: K, eventType: "RAW" | "MIDDLEWARE_CALL", From 4d3d6b8123c28c9cacc63f8ea1d80916dd5d4d25 Mon Sep 17 00:00:00 2001 From: Devyash Saini Date: Tue, 2 Jun 2026 23:28:38 +0530 Subject: [PATCH 4/6] Revert "chore: release" This reverts commit a2fdad58b76b27b28624688375f3dc460c348c58. --- .changeset/bold-impalas-wait.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/bold-impalas-wait.md diff --git a/.changeset/bold-impalas-wait.md b/.changeset/bold-impalas-wait.md deleted file mode 100644 index 6fd32b0..0000000 --- a/.changeset/bold-impalas-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@scrawn/core": patch ---- - -feat: reported timestamp From cf2647509e700d18bbc4b289578894fa75220524 Mon Sep 17 00:00:00 2001 From: Devyash Saini Date: Tue, 2 Jun 2026 23:34:41 +0530 Subject: [PATCH 5/6] fix: hoist timestamp above retry loop, forward through middleware, add nonnegative guard --- packages/scrawn/src/core/scrawn.ts | 9 +++++++-- packages/scrawn/src/core/types/event.ts | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/scrawn/src/core/scrawn.ts b/packages/scrawn/src/core/scrawn.ts index 8ab6733..12a75e4 100644 --- a/packages/scrawn/src/core/scrawn.ts +++ b/packages/scrawn/src/core/scrawn.ts @@ -637,6 +637,7 @@ export class Scrawn< userId: extractedPayload.userId, debit: extractedPayload.debit, metadata: extractedPayload.metadata, + reportedTimestamp: extractedPayload.reportedTimestamp, }; const validationResult = EventPayloadSchema.safeParse(rawPayload); if (!validationResult.success) { @@ -662,6 +663,7 @@ export class Scrawn< userId: validationResult.data.userId, debit, metadata: validationResult.data.metadata, + reportedTimestamp: validationResult.data.reportedTimestamp, }; this.consumeEvent( @@ -794,6 +796,10 @@ export class Scrawn< // Build debit field — already normalized by caller const debitField = payload.debit; + // Resolve timestamp once — stable across retries + const resolvedTimestamp = + payload.reportedTimestamp ?? Math.floor(Date.now() / 1000); + // Retry loop for retryable failures for (let attempt = 0; ; attempt++) { try { @@ -813,8 +819,7 @@ export class Scrawn< const request = { type: EventType.BASIC_USAGE, userId: payload.userId, - reportedTimestamp: - payload.reportedTimestamp ?? Math.floor(Date.now() / 1000), + reportedTimestamp: resolvedTimestamp, eventId, idempotencyKey, basicUsage, diff --git a/packages/scrawn/src/core/types/event.ts b/packages/scrawn/src/core/types/event.ts index e9d3042..c32fc68 100644 --- a/packages/scrawn/src/core/types/event.ts +++ b/packages/scrawn/src/core/types/event.ts @@ -98,7 +98,7 @@ export const EventPayloadSchema = z.object({ userId: z.string().min(1, "userId must be a non-empty string"), debit: DebitSchemaNoTokens, metadata: z.record(z.string(), z.unknown()).optional(), - reportedTimestamp: z.number().int().optional(), + reportedTimestamp: z.number().int().nonnegative().optional(), }); /** @@ -350,7 +350,7 @@ export const AITokenUsagePayloadSchema = z.object({ .nonnegative("outputCacheTokens must be non-negative") .optional(), outputCacheDebit: DebitSchemaWithTokens.optional(), - reportedTimestamp: z.number().int().optional(), + reportedTimestamp: z.number().int().nonnegative().optional(), }); /** From ac930100105a810acf7d1e0242c782f7fa80101c Mon Sep 17 00:00:00 2001 From: Devyash Saini Date: Tue, 2 Jun 2026 23:37:05 +0530 Subject: [PATCH 6/6] chore: release Signed-off-by: Devyash Saini --- .changeset/common-glasses-sit.md | 5 ----- .changeset/ready-horses-punch.md | 5 ----- .changeset/spotty-plums-enter.md | 5 +++++ 3 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 .changeset/common-glasses-sit.md delete mode 100644 .changeset/ready-horses-punch.md create mode 100644 .changeset/spotty-plums-enter.md diff --git a/.changeset/common-glasses-sit.md b/.changeset/common-glasses-sit.md deleted file mode 100644 index 93e1e76..0000000 --- a/.changeset/common-glasses-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@scrawn/analytics": patch ---- - -fix: allow mode filtering diff --git a/.changeset/ready-horses-punch.md b/.changeset/ready-horses-punch.md deleted file mode 100644 index 7e880cf..0000000 --- a/.changeset/ready-horses-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@scrawn/core": patch ---- - -feat: pass in httpurl and webhook public key to constructor diff --git a/.changeset/spotty-plums-enter.md b/.changeset/spotty-plums-enter.md new file mode 100644 index 0000000..6fd32b0 --- /dev/null +++ b/.changeset/spotty-plums-enter.md @@ -0,0 +1,5 @@ +--- +"@scrawn/core": patch +--- + +feat: reported timestamp