better-ship 0.12.0 → 0.14.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.
Files changed (116) hide show
  1. package/dist/application-C4JOf71r.js +291 -0
  2. package/dist/application-C4JOf71r.js.map +1 -0
  3. package/dist/application.d.ts +3 -3
  4. package/dist/application.js +2 -2
  5. package/dist/better-auth/apple-client-secret.d.ts +1 -1
  6. package/dist/better-auth/apple-client-secret.js +1 -1
  7. package/dist/better-auth/auth-client.js +1 -1
  8. package/dist/better-auth/rate-limit.d.ts +1 -1
  9. package/dist/better-auth/rate-limit.js +1 -1
  10. package/dist/better-auth.js +1 -1
  11. package/dist/cloudflare/auth-secondary-storage.d.ts +1 -1
  12. package/dist/cloudflare/auth-secondary-storage.js +1 -1
  13. package/dist/cloudflare/queue.d.ts +8 -3
  14. package/dist/cloudflare/queue.d.ts.map +1 -1
  15. package/dist/cloudflare/queue.js +26 -16
  16. package/dist/cloudflare/queue.js.map +1 -1
  17. package/dist/cloudflare/scheduled.d.ts +1 -1
  18. package/dist/cloudflare/scheduled.js +1 -1
  19. package/dist/cloudflare.d.ts +30 -4
  20. package/dist/cloudflare.d.ts.map +1 -1
  21. package/dist/cloudflare.js +98 -5
  22. package/dist/cloudflare.js.map +1 -1
  23. package/dist/{core-DG_cC8YT.js → core-hdaNiCOU.js} +22 -2
  24. package/dist/core-hdaNiCOU.js.map +1 -0
  25. package/dist/core.d.ts +2 -2
  26. package/dist/core.js +2 -2
  27. package/dist/http.d.ts +2 -2
  28. package/dist/http.js +2 -2
  29. package/dist/{index-DwWxIdQP.d.ts → index-C28HgVfY.d.ts} +145 -3
  30. package/dist/index-C28HgVfY.d.ts.map +1 -0
  31. package/dist/{index-Bl0K1RQW.d.ts → index-G1QG0P5g.d.ts} +36 -2
  32. package/dist/index-G1QG0P5g.d.ts.map +1 -0
  33. package/dist/outbox-repository-options-BZjfDqGf.d.ts +26 -0
  34. package/dist/outbox-repository-options-BZjfDqGf.d.ts.map +1 -0
  35. package/dist/{unit-of-work-context-BPE1In2f.js → outbox-repository-options-DOTpJH-e.js} +12 -3
  36. package/dist/outbox-repository-options-DOTpJH-e.js.map +1 -0
  37. package/dist/postgres.d.ts +30 -4
  38. package/dist/postgres.d.ts.map +1 -1
  39. package/dist/postgres.js +92 -5
  40. package/dist/postgres.js.map +1 -1
  41. package/dist/posthog/client.d.ts +22 -0
  42. package/dist/posthog/client.d.ts.map +1 -0
  43. package/dist/posthog/client.js +34 -0
  44. package/dist/posthog/client.js.map +1 -0
  45. package/dist/posthog/metrics.d.ts +18 -0
  46. package/dist/posthog/metrics.d.ts.map +1 -0
  47. package/dist/posthog/metrics.js +30 -0
  48. package/dist/posthog/metrics.js.map +1 -0
  49. package/dist/posthog/proxy.d.ts +17 -0
  50. package/dist/posthog/proxy.d.ts.map +1 -0
  51. package/dist/posthog/proxy.js +34 -0
  52. package/dist/posthog/proxy.js.map +1 -0
  53. package/dist/posthog/server.d.ts +37 -0
  54. package/dist/posthog/server.d.ts.map +1 -0
  55. package/dist/posthog/server.js +81 -0
  56. package/dist/posthog/server.js.map +1 -0
  57. package/dist/resend.d.ts +3 -3
  58. package/dist/resend.js +2 -2
  59. package/dist/sentry/client.d.ts +22 -0
  60. package/dist/sentry/client.d.ts.map +1 -0
  61. package/dist/sentry/client.js +48 -0
  62. package/dist/sentry/client.js.map +1 -0
  63. package/dist/sentry/server.d.ts +24 -0
  64. package/dist/sentry/server.d.ts.map +1 -0
  65. package/dist/sentry/server.js +48 -0
  66. package/dist/sentry/server.js.map +1 -0
  67. package/dist/sentry/vite.d.ts +11 -0
  68. package/dist/sentry/vite.d.ts.map +1 -0
  69. package/dist/sentry/vite.js +17 -0
  70. package/dist/sentry/vite.js.map +1 -0
  71. package/dist/tanstack.d.ts +1 -1
  72. package/dist/tanstack.js +2 -2
  73. package/dist/testing/outbox-repository.d.ts +44 -0
  74. package/dist/testing/outbox-repository.d.ts.map +1 -0
  75. package/dist/testing/outbox-repository.js +206 -0
  76. package/dist/testing/outbox-repository.js.map +1 -0
  77. package/dist/{webhook-verification-sZtwTI5n.d.ts → webhook-verification-D7DIhH6C.d.ts} +2 -2
  78. package/dist/{webhook-verification-sZtwTI5n.d.ts.map → webhook-verification-D7DIhH6C.d.ts.map} +1 -1
  79. package/dist/{webhook-verification-BQ9qZnVK.js → webhook-verification-trEgyz7I.js} +2 -2
  80. package/dist/{webhook-verification-BQ9qZnVK.js.map → webhook-verification-trEgyz7I.js.map} +1 -1
  81. package/package.json +42 -1
  82. package/src/application/analytics-service.ts +21 -0
  83. package/src/application/index.ts +31 -1
  84. package/src/application/metrics-service.ts +28 -0
  85. package/src/application/observability-service.ts +16 -0
  86. package/src/application/outbox-relay.ts +117 -0
  87. package/src/application/outbox-repository.ts +46 -0
  88. package/src/application/outbox-retention.ts +61 -0
  89. package/src/core/analytics-event.ts +37 -0
  90. package/src/core/backoff.ts +6 -1
  91. package/src/core/index.ts +10 -1
  92. package/src/core/missing-configuration.error.ts +12 -0
  93. package/src/infrastructure/cloudflare/d1-outbox-repository.ts +224 -0
  94. package/src/infrastructure/cloudflare/index.ts +2 -0
  95. package/src/infrastructure/cloudflare/queue/index.ts +1 -0
  96. package/src/infrastructure/cloudflare/queue/queue-client.ts +6 -2
  97. package/src/infrastructure/cloudflare/queue/wait-until-wake.ts +19 -0
  98. package/src/infrastructure/outbox-repository-options.ts +9 -0
  99. package/src/infrastructure/postgres/index.ts +2 -0
  100. package/src/infrastructure/postgres/postgres-outbox-repository.ts +216 -0
  101. package/src/infrastructure/posthog/client.ts +41 -0
  102. package/src/infrastructure/posthog/metrics.ts +30 -0
  103. package/src/infrastructure/posthog/proxy.ts +47 -0
  104. package/src/infrastructure/posthog/server.ts +107 -0
  105. package/src/infrastructure/sentry/client.ts +55 -0
  106. package/src/infrastructure/sentry/server.ts +51 -0
  107. package/src/infrastructure/sentry/vite.ts +17 -0
  108. package/src/infrastructure/testing/outbox-repository-contract.ts +228 -0
  109. package/dist/application-CIxo_BpK.js +0 -160
  110. package/dist/application-CIxo_BpK.js.map +0 -1
  111. package/dist/core-DG_cC8YT.js.map +0 -1
  112. package/dist/index-Bl0K1RQW.d.ts.map +0 -1
  113. package/dist/index-DwWxIdQP.d.ts.map +0 -1
  114. package/dist/unit-of-work-context-BPE1In2f.js.map +0 -1
  115. package/dist/unit-of-work-context-D5ytJIa-.d.ts +0 -16
  116. package/dist/unit-of-work-context-D5ytJIa-.d.ts.map +0 -1
@@ -0,0 +1,22 @@
1
+ import { f as LoggerErrorHook } from "../index-G1QG0P5g.js";
2
+ import * as Sentry from "@sentry/tanstackstart-react";
3
+ //#region src/infrastructure/sentry/client.d.ts
4
+ /** Application settings use Sentry's names; enablement and environment belong to the application. */
5
+ export type SentryClientOptions = Sentry.BrowserOptions & {
6
+ readonly dsn: string;
7
+ readonly environment: string;
8
+ readonly enabled: boolean;
9
+ };
10
+ /** Privacy defaults from Porte and browser noise filtering from Typist; explicit SDK options take precedence. */
11
+ export declare function createSentryClientOptions(options: SentryClientOptions): Sentry.BrowserOptions;
12
+ /** Forward logged browser errors once, excluding TanStack navigation control flow. */
13
+ export declare function createSentryLoggerErrorHook(): LoggerErrorHook;
14
+ /** Initialize once in the browser entrypoint before hydration; server imports have no side effects. */
15
+ export declare function initializeSentry(options: SentryClientOptions): ReturnType<typeof Sentry.init>;
16
+ /** Attach tracing once after creating the browser router, as Porte does in getRouter. */
17
+ export declare function instrumentSentryRouter(router: Parameters<typeof Sentry.tanstackRouterBrowserTracingIntegration>[0]): void;
18
+ /** The SDK error boundary; the application supplies its fallback component. */
19
+ export declare const ErrorBoundary: typeof Sentry.ErrorBoundary;
20
+ //#endregion
21
+ export { Sentry };
22
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","names":[],"sources":["../../src/infrastructure/sentry/client.ts"],"mappings":";;;;YAMY,sBAAsB,OAAO;WAC9B;WACA;WACA;;;wBAIK,0BAA0B,SAAS,sBAAsB,OAAO;;wBAehE,+BAA+B;;wBAS/B,iBAAiB,SAAS,sBAAsB,kBAAkB,OAAO;;wBAOzE,uBACd,QAAQ,kBAAkB,OAAO;;qBAOtB,sBAAsB,OAAO"}
@@ -0,0 +1,48 @@
1
+ import { s as setLoggerErrorHook } from "../core-hdaNiCOU.js";
2
+ import * as Sentry from "@sentry/tanstackstart-react";
3
+ import { isNotFound, isRedirect } from "@tanstack/react-router";
4
+
5
+ //#region src/infrastructure/sentry/client.ts
6
+ /** Privacy defaults from Porte and browser noise filtering from Typist; explicit SDK options take precedence. */
7
+ function createSentryClientOptions(options) {
8
+ return {
9
+ enableLogs: true,
10
+ ignoreErrors: [/Failed to execute '(insertBefore|removeChild)' on 'Node'/],
11
+ ...options,
12
+ dataCollection: {
13
+ userInfo: false,
14
+ httpBodies: [],
15
+ ...options.dataCollection,
16
+ genAI: {
17
+ inputs: false,
18
+ outputs: false,
19
+ ...options.dataCollection?.genAI
20
+ }
21
+ }
22
+ };
23
+ }
24
+ /** Forward logged browser errors once, excluding TanStack navigation control flow. */
25
+ function createSentryLoggerErrorHook() {
26
+ return ({ error, context }) => {
27
+ if (isNotFound(error) || isRedirect(error)) return;
28
+ const captured = error instanceof Error ? error : /* @__PURE__ */ new Error("Logger received a non-Error value");
29
+ Sentry.captureException(captured, { extra: context });
30
+ };
31
+ }
32
+ /** Initialize once in the browser entrypoint before hydration; server imports have no side effects. */
33
+ function initializeSentry(options) {
34
+ const client = Sentry.init(createSentryClientOptions(options));
35
+ setLoggerErrorHook(createSentryLoggerErrorHook());
36
+ return client;
37
+ }
38
+ /** Attach tracing once after creating the browser router, as Porte does in getRouter. */
39
+ function instrumentSentryRouter(router) {
40
+ if (router.isServer) return;
41
+ Sentry.addIntegration(Sentry.tanstackRouterBrowserTracingIntegration(router));
42
+ }
43
+ /** The SDK error boundary; the application supplies its fallback component. */
44
+ const ErrorBoundary = Sentry.ErrorBoundary;
45
+
46
+ //#endregion
47
+ export { ErrorBoundary, Sentry, createSentryClientOptions, createSentryLoggerErrorHook, initializeSentry, instrumentSentryRouter };
48
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../src/infrastructure/sentry/client.ts"],"sourcesContent":["import * as Sentry from '@sentry/tanstackstart-react'\nimport { isNotFound, isRedirect } from '@tanstack/react-router'\n\nimport { setLoggerErrorHook, type LoggerErrorHook } from '@/core'\n\n/** Application settings use Sentry's names; enablement and environment belong to the application. */\nexport type SentryClientOptions = Sentry.BrowserOptions & {\n readonly dsn: string\n readonly environment: string\n readonly enabled: boolean\n}\n\n/** Privacy defaults from Porte and browser noise filtering from Typist; explicit SDK options take precedence. */\nexport function createSentryClientOptions(options: SentryClientOptions): Sentry.BrowserOptions {\n return {\n enableLogs: true,\n ignoreErrors: [/Failed to execute '(insertBefore|removeChild)' on 'Node'/],\n ...options,\n dataCollection: {\n userInfo: false,\n httpBodies: [],\n ...options.dataCollection,\n genAI: { inputs: false, outputs: false, ...options.dataCollection?.genAI },\n },\n }\n}\n\n/** Forward logged browser errors once, excluding TanStack navigation control flow. */\nexport function createSentryLoggerErrorHook(): LoggerErrorHook {\n return ({ error, context }) => {\n if (isNotFound(error) || isRedirect(error)) return\n const captured = error instanceof Error ? error : new Error('Logger received a non-Error value')\n Sentry.captureException(captured, { extra: context })\n }\n}\n\n/** Initialize once in the browser entrypoint before hydration; server imports have no side effects. */\nexport function initializeSentry(options: SentryClientOptions): ReturnType<typeof Sentry.init> {\n const client = Sentry.init(createSentryClientOptions(options))\n setLoggerErrorHook(createSentryLoggerErrorHook())\n return client\n}\n\n/** Attach tracing once after creating the browser router, as Porte does in getRouter. */\nexport function instrumentSentryRouter(\n router: Parameters<typeof Sentry.tanstackRouterBrowserTracingIntegration>[0],\n): void {\n if (router.isServer) return\n Sentry.addIntegration(Sentry.tanstackRouterBrowserTracingIntegration(router))\n}\n\n/** The SDK error boundary; the application supplies its fallback component. */\nexport const ErrorBoundary: typeof Sentry.ErrorBoundary = Sentry.ErrorBoundary\n\nexport { Sentry }\n"],"mappings":";;;;;;AAaA,SAAgB,0BAA0B,SAAqD;CAC7F,OAAO;EACL,YAAY;EACZ,cAAc,CAAC,0DAA0D;EACzE,GAAG;EACH,gBAAgB;GACd,UAAU;GACV,YAAY,CAAC;GACb,GAAG,QAAQ;GACX,OAAO;IAAE,QAAQ;IAAO,SAAS;IAAO,GAAG,QAAQ,gBAAgB;GAAM;EAC3E;CACF;AACF;;AAGA,SAAgB,8BAA+C;CAC7D,QAAQ,EAAE,OAAO,cAAc;EAC7B,IAAI,WAAW,KAAK,KAAK,WAAW,KAAK,GAAG;EAC5C,MAAM,WAAW,iBAAiB,QAAQ,wBAAQ,IAAI,MAAM,mCAAmC;EAC/F,OAAO,iBAAiB,UAAU,EAAE,OAAO,QAAQ,CAAC;CACtD;AACF;;AAGA,SAAgB,iBAAiB,SAA8D;CAC7F,MAAM,SAAS,OAAO,KAAK,0BAA0B,OAAO,CAAC;CAC7D,mBAAmB,4BAA4B,CAAC;CAChD,OAAO;AACT;;AAGA,SAAgB,uBACd,QACM;CACN,IAAI,OAAO,UAAU;CACrB,OAAO,eAAe,OAAO,wCAAwC,MAAM,CAAC;AAC9E;;AAGA,MAAa,gBAA6C,OAAO"}
@@ -0,0 +1,24 @@
1
+ import { f as LoggerErrorHook } from "../index-G1QG0P5g.js";
2
+ import { H as ObservabilityScope, U as ObservabilityService, W as ObservabilityUser } from "../index-C28HgVfY.js";
3
+ import * as Sentry from "@sentry/cloudflare";
4
+ //#region src/infrastructure/sentry/server.d.ts
5
+ /** Application-owned deployment settings with the full Cloudflare SDK option contract. */
6
+ export type SentryServerOptions = Sentry.CloudflareOptions & {
7
+ readonly dsn: string;
8
+ readonly environment: string;
9
+ readonly enabled: boolean;
10
+ };
11
+ /** Options for Workers, Durable Objects, and Workflows, ported from Typist and Porte. */
12
+ export declare function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions;
13
+ /** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */
14
+ export declare function createSentryLoggerErrorHook(): LoggerErrorHook;
15
+ /** Identity and scope adapter from Typist and Porte; call inside Sentry's instrumented runtime. */
16
+ export declare class SentryObservabilityService implements ObservabilityService {
17
+ /** Attribute subsequent reports in the current isolation scope. */
18
+ setUser(user: ObservabilityUser): void;
19
+ /** Await work in a separate scope for each queue message or concurrent operation. */
20
+ withIsolationScope<T>(callback: (scope: ObservabilityScope) => T | Promise<T>): Promise<T>;
21
+ }
22
+ //#endregion
23
+ export { Sentry };
24
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/infrastructure/sentry/server.ts"],"mappings":";;;;;YAMY,sBAAsB,OAAO;WAC9B;WACA;WACA;;;wBAIK,oBAAoB,SAAS,sBAAsB,OAAO;;wBAc1D,+BAA+B;;qBAWlC,sCAAsC;;EAEjD,QAAQ,MAAM;;EAKR,mBAAmB,GAAG,WAAW,OAAO,uBAAuB,IAAI,QAAQ,KAAK,QAAQ"}
@@ -0,0 +1,48 @@
1
+ import * as Sentry from "@sentry/cloudflare";
2
+
3
+ //#region src/infrastructure/sentry/server.ts
4
+ /** Options for Workers, Durable Objects, and Workflows, ported from Typist and Porte. */
5
+ function createSentryOptions(options) {
6
+ return {
7
+ enableLogs: true,
8
+ ...options,
9
+ dataCollection: {
10
+ userInfo: false,
11
+ httpBodies: [],
12
+ ...options.dataCollection,
13
+ genAI: {
14
+ inputs: false,
15
+ outputs: false,
16
+ ...options.dataCollection?.genAI
17
+ }
18
+ }
19
+ };
20
+ }
21
+ /** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */
22
+ function createSentryLoggerErrorHook() {
23
+ return ({ error, distinctId, context }) => {
24
+ if (distinctId !== void 0) {
25
+ Sentry.captureException(error, {
26
+ extra: context,
27
+ user: { id: distinctId }
28
+ });
29
+ return;
30
+ }
31
+ Sentry.captureException(error, { extra: context });
32
+ };
33
+ }
34
+ /** Identity and scope adapter from Typist and Porte; call inside Sentry's instrumented runtime. */
35
+ var SentryObservabilityService = class {
36
+ /** Attribute subsequent reports in the current isolation scope. */
37
+ setUser(user) {
38
+ Sentry.setUser(user);
39
+ }
40
+ /** Await work in a separate scope for each queue message or concurrent operation. */
41
+ async withIsolationScope(callback) {
42
+ return await Sentry.withIsolationScope(callback);
43
+ }
44
+ };
45
+
46
+ //#endregion
47
+ export { Sentry, SentryObservabilityService, createSentryLoggerErrorHook, createSentryOptions };
48
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../src/infrastructure/sentry/server.ts"],"sourcesContent":["import * as Sentry from '@sentry/cloudflare'\n\nimport type { ObservabilityScope, ObservabilityService, ObservabilityUser } from '@/application'\nimport type { LoggerErrorHook } from '@/core'\n\n/** Application-owned deployment settings with the full Cloudflare SDK option contract. */\nexport type SentryServerOptions = Sentry.CloudflareOptions & {\n readonly dsn: string\n readonly environment: string\n readonly enabled: boolean\n}\n\n/** Options for Workers, Durable Objects, and Workflows, ported from Typist and Porte. */\nexport function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions {\n return {\n enableLogs: true,\n ...options,\n dataCollection: {\n userInfo: false,\n httpBodies: [],\n ...options.dataCollection,\n genAI: { inputs: false, outputs: false, ...options.dataCollection?.genAI },\n },\n }\n}\n\n/** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */\nexport function createSentryLoggerErrorHook(): LoggerErrorHook {\n return ({ error, distinctId, context }) => {\n if (distinctId !== undefined) {\n Sentry.captureException(error, { extra: context, user: { id: distinctId } })\n return\n }\n Sentry.captureException(error, { extra: context })\n }\n}\n\n/** Identity and scope adapter from Typist and Porte; call inside Sentry's instrumented runtime. */\nexport class SentryObservabilityService implements ObservabilityService {\n /** Attribute subsequent reports in the current isolation scope. */\n setUser(user: ObservabilityUser): void {\n Sentry.setUser(user)\n }\n\n /** Await work in a separate scope for each queue message or concurrent operation. */\n async withIsolationScope<T>(callback: (scope: ObservabilityScope) => T | Promise<T>): Promise<T> {\n return await Sentry.withIsolationScope(callback)\n }\n}\n\nexport { Sentry }\n"],"mappings":";;;;AAaA,SAAgB,oBAAoB,SAAwD;CAC1F,OAAO;EACL,YAAY;EACZ,GAAG;EACH,gBAAgB;GACd,UAAU;GACV,YAAY,CAAC;GACb,GAAG,QAAQ;GACX,OAAO;IAAE,QAAQ;IAAO,SAAS;IAAO,GAAG,QAAQ,gBAAgB;GAAM;EAC3E;CACF;AACF;;AAGA,SAAgB,8BAA+C;CAC7D,QAAQ,EAAE,OAAO,YAAY,cAAc;EACzC,IAAI,eAAe,QAAW;GAC5B,OAAO,iBAAiB,OAAO;IAAE,OAAO;IAAS,MAAM,EAAE,IAAI,WAAW;GAAE,CAAC;GAC3E;EACF;EACA,OAAO,iBAAiB,OAAO,EAAE,OAAO,QAAQ,CAAC;CACnD;AACF;;AAGA,IAAa,6BAAb,MAAwE;;CAEtE,QAAQ,MAA+B;EACrC,OAAO,QAAQ,IAAI;CACrB;;CAGA,MAAM,mBAAsB,UAAqE;EAC/F,OAAO,MAAM,OAAO,mBAAmB,QAAQ;CACjD;AACF"}
@@ -0,0 +1,11 @@
1
+ import { SentryTanstackStartOptions, sentryTanstackStart as sentryTanstackStart$1 } from "@sentry/tanstackstart-react/vite";
2
+ //#region src/infrastructure/sentry/vite.d.ts
3
+ /** Sentry's Vite options; the SDK creates the tunnel route and sets the browser tunnel URL. */
4
+ export type SentryViteOptions = SentryTanstackStartOptions;
5
+ /**
6
+ * Add after tanstackStart() in Vite. The SDK enables a same-origin tunnel by default here.
7
+ * Set tunnelRoute.allowedDsns when the browser and Worker use different Sentry projects.
8
+ */
9
+ export declare function sentryTanstackStart(options?: SentryViteOptions): ReturnType<typeof sentryTanstackStart$1>;
10
+ //#endregion
11
+ //# sourceMappingURL=vite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.d.ts","names":[],"sources":["../../src/infrastructure/sentry/vite.ts"],"mappings":";;;YAMY,oBAAoB;;;;;wBAMhB,oBACd,UAAS,oBACR,kBAAkB"}
@@ -0,0 +1,17 @@
1
+ import { sentryTanstackStart as sentryTanstackStart$1 } from "@sentry/tanstackstart-react/vite";
2
+
3
+ //#region src/infrastructure/sentry/vite.ts
4
+ /**
5
+ * Add after tanstackStart() in Vite. The SDK enables a same-origin tunnel by default here.
6
+ * Set tunnelRoute.allowedDsns when the browser and Worker use different Sentry projects.
7
+ */
8
+ function sentryTanstackStart(options = {}) {
9
+ return sentryTanstackStart$1({
10
+ ...options,
11
+ tunnelRoute: options.tunnelRoute ?? true
12
+ });
13
+ }
14
+
15
+ //#endregion
16
+ export { sentryTanstackStart };
17
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","names":["sentryPlugin"],"sources":["../../src/infrastructure/sentry/vite.ts"],"sourcesContent":["import {\n sentryTanstackStart as sentryPlugin,\n type SentryTanstackStartOptions,\n} from '@sentry/tanstackstart-react/vite'\n\n/** Sentry's Vite options; the SDK creates the tunnel route and sets the browser tunnel URL. */\nexport type SentryViteOptions = SentryTanstackStartOptions\n\n/**\n * Add after tanstackStart() in Vite. The SDK enables a same-origin tunnel by default here.\n * Set tunnelRoute.allowedDsns when the browser and Worker use different Sentry projects.\n */\nexport function sentryTanstackStart(\n options: SentryViteOptions = {},\n): ReturnType<typeof sentryPlugin> {\n return sentryPlugin({ ...options, tunnelRoute: options.tunnelRoute ?? true })\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,oBACd,UAA6B,CAAC,GACG;CACjC,OAAOA,sBAAa;EAAE,GAAG;EAAS,aAAa,QAAQ,eAAe;CAAK,CAAC;AAC9E"}
@@ -1,4 +1,4 @@
1
- import { ot as ErrorTable } from "./index-Bl0K1RQW.js";
1
+ import { st as ErrorTable } from "./index-G1QG0P5g.js";
2
2
  import { Register } from "@tanstack/react-start";
3
3
  import { BetterAuthCookies } from "better-auth";
4
4
  //#region src/infrastructure/tanstack/request-context.d.ts
package/dist/tanstack.js CHANGED
@@ -1,9 +1,9 @@
1
- import { B as StaleSessionError, H as AuthenticationError, I as ValidationError, Q as AppError, a as createLogger } from "./core-DG_cC8YT.js";
1
+ import { L as ValidationError, U as AuthenticationError, V as StaleSessionError, a as createLogger, tt as AppError } from "./core-hdaNiCOU.js";
2
2
  import { z } from "zod";
3
3
  import { getReasonPhrase } from "http-status-codes";
4
+ import { isNotFound, isRedirect, redirect } from "@tanstack/react-router";
4
5
  import { createMiddleware } from "@tanstack/react-start";
5
6
  import { getSessionCookie, parseCookies } from "better-auth/cookies";
6
- import { isNotFound, isRedirect, redirect } from "@tanstack/react-router";
7
7
 
8
8
  //#region src/infrastructure/tanstack/auth.middleware.ts
9
9
  const logger$1 = createLogger("auth-middleware");
@@ -0,0 +1,44 @@
1
+ import { G as DomainEvent } from "../index-G1QG0P5g.js";
2
+ import { A as IOutboxRepository } from "../index-C28HgVfY.js";
3
+ //#region src/infrastructure/testing/outbox-repository-contract.d.ts
4
+ /** Stored fields observed by the adapter contract, independent of a database library. */
5
+ export type OutboxContractRow = {
6
+ readonly id: string;
7
+ readonly payload: DomainEvent;
8
+ readonly createdAt: Date;
9
+ readonly availableAt: Date;
10
+ readonly attempts: number;
11
+ readonly claimToken: string | null;
12
+ readonly claimExpiresAt: Date | null;
13
+ readonly publishedAt: Date | null;
14
+ readonly failedAt: Date | null;
15
+ readonly lastError: string | null;
16
+ };
17
+ /** Fixture input for one stored row; omitted fields use fresh, due defaults. */
18
+ export type OutboxContractSeed = {
19
+ readonly id: string;
20
+ } & Partial<Omit<OutboxContractRow, 'id' | 'payload'>>;
21
+ /**
22
+ * An isolated database fixture. Configure a 60-second lease, 1-second base delay, and 60-second cap.
23
+ * The clock starts at a whole second. Each test receives fresh tables and an independent clock.
24
+ */
25
+ export interface OutboxRepositoryContractFixture {
26
+ readonly repository: IOutboxRepository;
27
+ now(this: void): Date;
28
+ advance(ms: number): void;
29
+ seed(rows: ReadonlyArray<OutboxContractSeed>): Promise<void>;
30
+ /** Return rows ordered by id. */
31
+ rows(): Promise<ReadonlyArray<OutboxContractRow>>;
32
+ seedReceipts(rows: ReadonlyArray<{
33
+ readonly messageId: string;
34
+ readonly processedAt: Date;
35
+ }>): Promise<void>;
36
+ receipts(): Promise<ReadonlyArray<{
37
+ readonly messageId: string;
38
+ readonly processedAt: Date;
39
+ }>>;
40
+ }
41
+ /** Register the shared repository contract with an isolated fixture per test; run tests serially if they share a database. */
42
+ export declare function outboxRepositoryContract(test: (name: string, run: () => Promise<void>) => void, create: () => Promise<OutboxRepositoryContractFixture>): void;
43
+ //#endregion
44
+ //# sourceMappingURL=outbox-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-repository.d.ts","names":[],"sources":["../../src/infrastructure/testing/outbox-repository-contract.ts"],"mappings":";;;;YAMY;WACD;WACA,SAAS;WACT,WAAW;WACX,aAAa;WACb;WACA;WACA,gBAAgB;WAChB,aAAa;WACb,UAAU;WACV;;;YAIC;WAAgC;IAAe,QACzD,KAAK;;;;;iBAOU;WACN,YAAY;EACrB,IAAI,aAAa;EACjB,QAAQ;EACR,KAAK,MAAM,cAAc,sBAAsB;;EAE/C,QAAQ,QAAQ,cAAc;EAC9B,aACE,MAAM;aAAyB;aAA4B,aAAa;OACvE;EACH,YAAY,QAAQ;aAAyB;aAA4B,aAAa;;;;wBAIxE,yBACd,OAAO,cAAc,WAAW,wBAChC,cAAc,QAAQ"}
@@ -0,0 +1,206 @@
1
+ import { deepStrictEqual, equal, notEqual, ok } from "node:assert/strict";
2
+
3
+ //#region src/infrastructure/testing/outbox-repository-contract.ts
4
+ /** Register the shared repository contract with an isolated fixture per test; run tests serially if they share a database. */
5
+ function outboxRepositoryContract(test, create) {
6
+ test("claims due rows with one token and incremented attempts", async () => {
7
+ const f = await create();
8
+ await f.seed([{ id: "a" }, {
9
+ id: "b",
10
+ attempts: 3
11
+ }]);
12
+ const claim = await f.repository.claimPending(50);
13
+ deepStrictEqual(claim.messages.map((row) => row.attempts), [1, 4]);
14
+ ok(claim.token.length > 0);
15
+ deepStrictEqual((await f.rows()).map((row) => row.claimToken), [claim.token, claim.token]);
16
+ });
17
+ test("skips terminal, future, and leased rows", async () => {
18
+ const f = await create();
19
+ const future = new Date(f.now().getTime() + 6e4);
20
+ await f.seed([
21
+ {
22
+ id: "published",
23
+ publishedAt: f.now()
24
+ },
25
+ {
26
+ id: "failed",
27
+ failedAt: f.now()
28
+ },
29
+ {
30
+ id: "future",
31
+ availableAt: future
32
+ },
33
+ {
34
+ id: "leased",
35
+ claimToken: "old",
36
+ claimExpiresAt: future
37
+ },
38
+ { id: "due" }
39
+ ]);
40
+ const claim = await f.repository.claimPending(50);
41
+ deepStrictEqual(claim.messages.map((row) => row.payload.id), ["due"]);
42
+ });
43
+ test("limits a claim and takes the oldest due row first", async () => {
44
+ const f = await create();
45
+ await f.seed([{ id: "new" }, {
46
+ id: "old",
47
+ availableAt: /* @__PURE__ */ new Date(f.now().getTime() - 1e3)
48
+ }]);
49
+ const claim = await f.repository.claimPending(1);
50
+ deepStrictEqual(claim.messages.map((row) => row.payload.id), ["old"]);
51
+ });
52
+ test("concurrent claims never share a row", async () => {
53
+ const f = await create();
54
+ await f.seed([{ id: "a" }, { id: "b" }]);
55
+ const ids = (await Promise.all([f.repository.claimPending(1), f.repository.claimPending(1)])).flatMap((claim) => claim.messages.map((row) => row.payload.id));
56
+ equal(ids.length, 2);
57
+ equal(new Set(ids).size, 2);
58
+ });
59
+ test("reclaims an expired lease and increments attempts again", async () => {
60
+ const f = await create();
61
+ await f.seed([{ id: "a" }]);
62
+ const first = await f.repository.claimPending(1);
63
+ f.advance(6e4);
64
+ const second = await f.repository.claimPending(1);
65
+ notEqual(first.token, second.token);
66
+ deepStrictEqual(second.messages.map((row) => row.attempts), [2]);
67
+ });
68
+ for (const operation of [
69
+ "publish",
70
+ "release",
71
+ "fail"
72
+ ]) test(`a stale claim cannot ${operation} a row`, async () => {
73
+ const f = await create();
74
+ await f.seed([{ id: "a" }]);
75
+ const old = await f.repository.claimPending(1);
76
+ f.advance(6e4);
77
+ await f.repository.claimPending(1);
78
+ const before = await f.rows();
79
+ if (operation === "publish") await f.repository.markPublished(old, old.messages);
80
+ if (operation === "release") await f.repository.releaseClaim(old, old.messages);
81
+ if (operation === "fail") await f.repository.deadLetter(old, old.messages, "terminal", "denied");
82
+ deepStrictEqual(await f.rows(), before);
83
+ });
84
+ test("publishes only the supplied subset and clears its lease", async () => {
85
+ const f = await create();
86
+ await f.seed([{ id: "a" }, { id: "b" }]);
87
+ const claim = await f.repository.claimPending(50);
88
+ await f.repository.markPublished(claim, claim.messages.slice(0, 1));
89
+ deepStrictEqual((await f.rows()).map((row) => ({
90
+ publishedAt: row.publishedAt,
91
+ token: row.claimToken
92
+ })), [{
93
+ publishedAt: f.now(),
94
+ token: null
95
+ }, {
96
+ publishedAt: null,
97
+ token: claim.token
98
+ }]);
99
+ });
100
+ test("releases only the supplied subset with per-row capped backoff", async () => {
101
+ const f = await create();
102
+ await f.seed([
103
+ { id: "a" },
104
+ {
105
+ id: "b",
106
+ attempts: 2
107
+ },
108
+ {
109
+ id: "c",
110
+ attempts: 8
111
+ },
112
+ { id: "d" }
113
+ ]);
114
+ const claim = await f.repository.claimPending(50);
115
+ await f.repository.releaseClaim(claim, claim.messages.slice(0, 3));
116
+ deepStrictEqual((await f.rows()).map((row) => [row.availableAt.getTime() - f.now().getTime(), row.claimToken]), [
117
+ [1e3, null],
118
+ [4e3, null],
119
+ [6e4, null],
120
+ [0, claim.token]
121
+ ]);
122
+ });
123
+ test("dead-letters only the supplied subset and keeps its payload", async () => {
124
+ const f = await create();
125
+ await f.seed([{ id: "a" }, { id: "b" }]);
126
+ const claim = await f.repository.claimPending(50);
127
+ await f.repository.deadLetter(claim, claim.messages.slice(0, 1), "oversized", "too large");
128
+ deepStrictEqual((await f.rows()).map((row) => [
129
+ row.payload.id,
130
+ row.lastError,
131
+ row.claimToken
132
+ ]), [[
133
+ "a",
134
+ "oversized: too large",
135
+ null
136
+ ], [
137
+ "b",
138
+ null,
139
+ claim.token
140
+ ]]);
141
+ });
142
+ for (const operation of [
143
+ "publish",
144
+ "release",
145
+ "fail"
146
+ ]) test(`settles a 120-row claim through ${operation}`, async () => {
147
+ const f = await create();
148
+ await f.seed(Array.from({ length: 120 }, (_, i) => ({ id: String(i) })));
149
+ const claim = await f.repository.claimPending(120);
150
+ equal(claim.messages.length, 120);
151
+ if (operation === "publish") await f.repository.markPublished(claim, claim.messages);
152
+ if (operation === "release") await f.repository.releaseClaim(claim, claim.messages);
153
+ if (operation === "fail") await f.repository.deadLetter(claim, claim.messages, "terminal", "denied");
154
+ ok((await f.rows()).every((row) => row.claimToken === null));
155
+ });
156
+ for (const category of ["published", "failed"]) test(`deletes only old ${category} events within the batch limit`, async () => {
157
+ const f = await create();
158
+ const old = /* @__PURE__ */ new Date(f.now().getTime() - 1e3);
159
+ const dates = category === "published" ? { publishedAt: old } : { failedAt: old };
160
+ const atCutoff = category === "published" ? { publishedAt: f.now() } : { failedAt: f.now() };
161
+ await f.seed([
162
+ {
163
+ id: "old-a",
164
+ ...dates
165
+ },
166
+ {
167
+ id: "old-b",
168
+ ...dates
169
+ },
170
+ {
171
+ id: "cutoff",
172
+ ...atCutoff
173
+ },
174
+ { id: "pending" }
175
+ ]);
176
+ const remove = category === "published" ? f.repository.deletePublishedBefore.bind(f.repository) : f.repository.deleteFailedBefore.bind(f.repository);
177
+ equal(await remove(f.now(), 1), 1);
178
+ equal(await remove(f.now(), 50), 1);
179
+ deepStrictEqual((await f.rows()).map((row) => row.id), ["cutoff", "pending"]);
180
+ });
181
+ test("deletes only old receipts within the batch limit", async () => {
182
+ const f = await create();
183
+ const old = /* @__PURE__ */ new Date(f.now().getTime() - 1e3);
184
+ await f.seedReceipts([
185
+ {
186
+ messageId: "old-a",
187
+ processedAt: old
188
+ },
189
+ {
190
+ messageId: "old-b",
191
+ processedAt: old
192
+ },
193
+ {
194
+ messageId: "cutoff",
195
+ processedAt: f.now()
196
+ }
197
+ ]);
198
+ equal(await f.repository.deleteReceiptsBefore(f.now(), 1), 1);
199
+ equal(await f.repository.deleteReceiptsBefore(f.now(), 50), 1);
200
+ deepStrictEqual((await f.receipts()).map((row) => row.messageId), ["cutoff"]);
201
+ });
202
+ }
203
+
204
+ //#endregion
205
+ export { outboxRepositoryContract };
206
+ //# sourceMappingURL=outbox-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-repository.js","names":[],"sources":["../../src/infrastructure/testing/outbox-repository-contract.ts"],"sourcesContent":["import { deepStrictEqual, equal, notEqual, ok } from 'node:assert/strict'\n\nimport type { IOutboxRepository } from '@/application'\nimport type { DomainEvent } from '@/core'\n\n/** Stored fields observed by the adapter contract, independent of a database library. */\nexport type OutboxContractRow = {\n readonly id: string\n readonly payload: DomainEvent\n readonly createdAt: Date\n readonly availableAt: Date\n readonly attempts: number\n readonly claimToken: string | null\n readonly claimExpiresAt: Date | null\n readonly publishedAt: Date | null\n readonly failedAt: Date | null\n readonly lastError: string | null\n}\n\n/** Fixture input for one stored row; omitted fields use fresh, due defaults. */\nexport type OutboxContractSeed = { readonly id: string } & Partial<\n Omit<OutboxContractRow, 'id' | 'payload'>\n>\n\n/**\n * An isolated database fixture. Configure a 60-second lease, 1-second base delay, and 60-second cap.\n * The clock starts at a whole second. Each test receives fresh tables and an independent clock.\n */\nexport interface OutboxRepositoryContractFixture {\n readonly repository: IOutboxRepository\n now(this: void): Date\n advance(ms: number): void\n seed(rows: ReadonlyArray<OutboxContractSeed>): Promise<void>\n /** Return rows ordered by id. */\n rows(): Promise<ReadonlyArray<OutboxContractRow>>\n seedReceipts(\n rows: ReadonlyArray<{ readonly messageId: string; readonly processedAt: Date }>,\n ): Promise<void>\n receipts(): Promise<ReadonlyArray<{ readonly messageId: string; readonly processedAt: Date }>>\n}\n\n/** Register the shared repository contract with an isolated fixture per test; run tests serially if they share a database. */\nexport function outboxRepositoryContract(\n test: (name: string, run: () => Promise<void>) => void,\n create: () => Promise<OutboxRepositoryContractFixture>,\n): void {\n test('claims due rows with one token and incremented attempts', async () => {\n const f = await create()\n await f.seed([{ id: 'a' }, { id: 'b', attempts: 3 }])\n const claim = await f.repository.claimPending(50)\n deepStrictEqual(\n claim.messages.map((row) => row.attempts),\n [1, 4],\n )\n ok(claim.token.length > 0)\n deepStrictEqual(\n (await f.rows()).map((row) => row.claimToken),\n [claim.token, claim.token],\n )\n })\n\n test('skips terminal, future, and leased rows', async () => {\n const f = await create()\n const future = new Date(f.now().getTime() + 60_000)\n await f.seed([\n { id: 'published', publishedAt: f.now() },\n { id: 'failed', failedAt: f.now() },\n { id: 'future', availableAt: future },\n { id: 'leased', claimToken: 'old', claimExpiresAt: future },\n { id: 'due' },\n ])\n const claim = await f.repository.claimPending(50)\n deepStrictEqual(\n claim.messages.map((row) => row.payload.id),\n ['due'],\n )\n })\n\n test('limits a claim and takes the oldest due row first', async () => {\n const f = await create()\n await f.seed([{ id: 'new' }, { id: 'old', availableAt: new Date(f.now().getTime() - 1_000) }])\n const claim = await f.repository.claimPending(1)\n deepStrictEqual(\n claim.messages.map((row) => row.payload.id),\n ['old'],\n )\n })\n\n test('concurrent claims never share a row', async () => {\n const f = await create()\n await f.seed([{ id: 'a' }, { id: 'b' }])\n const claims = await Promise.all([f.repository.claimPending(1), f.repository.claimPending(1)])\n const ids = claims.flatMap((claim) => claim.messages.map((row) => row.payload.id))\n equal(ids.length, 2)\n equal(new Set(ids).size, 2)\n })\n\n test('reclaims an expired lease and increments attempts again', async () => {\n const f = await create()\n await f.seed([{ id: 'a' }])\n const first = await f.repository.claimPending(1)\n f.advance(60_000)\n const second = await f.repository.claimPending(1)\n notEqual(first.token, second.token)\n deepStrictEqual(\n second.messages.map((row) => row.attempts),\n [2],\n )\n })\n\n for (const operation of ['publish', 'release', 'fail']) {\n test(`a stale claim cannot ${operation} a row`, async () => {\n const f = await create()\n await f.seed([{ id: 'a' }])\n const old = await f.repository.claimPending(1)\n f.advance(60_000)\n await f.repository.claimPending(1)\n const before = await f.rows()\n if (operation === 'publish') await f.repository.markPublished(old, old.messages)\n if (operation === 'release') await f.repository.releaseClaim(old, old.messages)\n if (operation === 'fail')\n await f.repository.deadLetter(old, old.messages, 'terminal', 'denied')\n deepStrictEqual(await f.rows(), before)\n })\n }\n\n test('publishes only the supplied subset and clears its lease', async () => {\n const f = await create()\n await f.seed([{ id: 'a' }, { id: 'b' }])\n const claim = await f.repository.claimPending(50)\n await f.repository.markPublished(claim, claim.messages.slice(0, 1))\n deepStrictEqual(\n (await f.rows()).map((row) => ({ publishedAt: row.publishedAt, token: row.claimToken })),\n [\n { publishedAt: f.now(), token: null },\n { publishedAt: null, token: claim.token },\n ],\n )\n })\n\n test('releases only the supplied subset with per-row capped backoff', async () => {\n const f = await create()\n await f.seed([{ id: 'a' }, { id: 'b', attempts: 2 }, { id: 'c', attempts: 8 }, { id: 'd' }])\n const claim = await f.repository.claimPending(50)\n await f.repository.releaseClaim(claim, claim.messages.slice(0, 3))\n deepStrictEqual(\n (await f.rows()).map((row) => [\n row.availableAt.getTime() - f.now().getTime(),\n row.claimToken,\n ]),\n [\n [1000, null],\n [4000, null],\n [60000, null],\n [0, claim.token],\n ],\n )\n })\n\n test('dead-letters only the supplied subset and keeps its payload', async () => {\n const f = await create()\n await f.seed([{ id: 'a' }, { id: 'b' }])\n const claim = await f.repository.claimPending(50)\n await f.repository.deadLetter(claim, claim.messages.slice(0, 1), 'oversized', 'too large')\n deepStrictEqual(\n (await f.rows()).map((row) => [row.payload.id, row.lastError, row.claimToken]),\n [\n ['a', 'oversized: too large', null],\n ['b', null, claim.token],\n ],\n )\n })\n\n for (const operation of ['publish', 'release', 'fail']) {\n test(`settles a 120-row claim through ${operation}`, async () => {\n const f = await create()\n await f.seed(Array.from({ length: 120 }, (_, i) => ({ id: String(i) })))\n const claim = await f.repository.claimPending(120)\n equal(claim.messages.length, 120)\n if (operation === 'publish') await f.repository.markPublished(claim, claim.messages)\n if (operation === 'release') await f.repository.releaseClaim(claim, claim.messages)\n if (operation === 'fail')\n await f.repository.deadLetter(claim, claim.messages, 'terminal', 'denied')\n ok((await f.rows()).every((row) => row.claimToken === null))\n })\n }\n\n for (const category of ['published', 'failed']) {\n test(`deletes only old ${category} events within the batch limit`, async () => {\n const f = await create()\n const old = new Date(f.now().getTime() - 1000)\n const dates = category === 'published' ? { publishedAt: old } : { failedAt: old }\n const atCutoff = category === 'published' ? { publishedAt: f.now() } : { failedAt: f.now() }\n await f.seed([\n { id: 'old-a', ...dates },\n { id: 'old-b', ...dates },\n { id: 'cutoff', ...atCutoff },\n { id: 'pending' },\n ])\n const remove =\n category === 'published'\n ? f.repository.deletePublishedBefore.bind(f.repository)\n : f.repository.deleteFailedBefore.bind(f.repository)\n equal(await remove(f.now(), 1), 1)\n equal(await remove(f.now(), 50), 1)\n deepStrictEqual(\n (await f.rows()).map((row) => row.id),\n ['cutoff', 'pending'],\n )\n })\n }\n\n test('deletes only old receipts within the batch limit', async () => {\n const f = await create()\n const old = new Date(f.now().getTime() - 1000)\n await f.seedReceipts([\n { messageId: 'old-a', processedAt: old },\n { messageId: 'old-b', processedAt: old },\n { messageId: 'cutoff', processedAt: f.now() },\n ])\n equal(await f.repository.deleteReceiptsBefore(f.now(), 1), 1)\n equal(await f.repository.deleteReceiptsBefore(f.now(), 50), 1)\n deepStrictEqual(\n (await f.receipts()).map((row) => row.messageId),\n ['cutoff'],\n )\n })\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Ut as AppError } from "./index-Bl0K1RQW.js";
1
+ import { Zt as AppError } from "./index-G1QG0P5g.js";
2
2
  //#region src/infrastructure/http/webhook-verification.d.ts
3
3
  /** A webhook cannot be trusted or parsed. The HTTP handler logs the rejection once. */
4
4
  declare class WebhookVerificationError extends AppError {
@@ -26,4 +26,4 @@ interface WebhookVerifier<Event> {
26
26
  }
27
27
  //#endregion
28
28
  export { WebhookVerificationError as n, WebhookVerifier as r, WebhookVerification as t };
29
- //# sourceMappingURL=webhook-verification-sZtwTI5n.d.ts.map
29
+ //# sourceMappingURL=webhook-verification-D7DIhH6C.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-verification-sZtwTI5n.d.ts","names":[],"sources":["../src/infrastructure/http/webhook-verification.ts"],"mappings":";;;cAGa,iCAAiC;WAMjC;WALF;WACA;;EAGT,YACW,qEACT;;;KAOQ,oBAAoB;WACjB;WAA2B,OAAO;;WAClC;WAA6B;;WAC7B;WAA8B,OAAO;;;UAGnC,gBAAgB;;EAE/B,OAAO,cAAc,SAAS,UAAU,QAAQ,oBAAoB"}
1
+ {"version":3,"file":"webhook-verification-D7DIhH6C.d.ts","names":[],"sources":["../src/infrastructure/http/webhook-verification.ts"],"mappings":";;;cAGa,iCAAiC;WAMjC;WALF;WACA;;EAGT,YACW,qEACT;;;KAOQ,oBAAoB;WACjB;WAA2B,OAAO;;WAClC;WAA6B;;WAC7B;WAA8B,OAAO;;;UAGnC,gBAAgB;;EAE/B,OAAO,cAAc,SAAS,UAAU,QAAQ,oBAAoB"}
@@ -1,4 +1,4 @@
1
- import { Q as AppError } from "./core-DG_cC8YT.js";
1
+ import { tt as AppError } from "./core-hdaNiCOU.js";
2
2
 
3
3
  //#region src/infrastructure/http/webhook-verification.ts
4
4
  /** A webhook cannot be trusted or parsed. The HTTP handler logs the rejection once. */
@@ -15,4 +15,4 @@ var WebhookVerificationError = class extends AppError {
15
15
 
16
16
  //#endregion
17
17
  export { WebhookVerificationError as t };
18
- //# sourceMappingURL=webhook-verification-BQ9qZnVK.js.map
18
+ //# sourceMappingURL=webhook-verification-trEgyz7I.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-verification-BQ9qZnVK.js","names":[],"sources":["../src/infrastructure/http/webhook-verification.ts"],"sourcesContent":["import { AppError } from '@/core'\n\n/** A webhook cannot be trusted or parsed. The HTTP handler logs the rejection once. */\nexport class WebhookVerificationError extends AppError {\n readonly _tag = 'WebhookVerificationError'\n readonly classification = 'terminal'\n\n /** Keep secrets and payload details out of the public message. */\n constructor(\n readonly reason: 'missing_headers' | 'invalid_signature' | 'invalid_payload',\n cause?: unknown,\n ) {\n super(`Webhook rejected: ${reason}`, { cause })\n }\n}\n\n/** A verified event, an ignored event, or an invalid request. */\nexport type WebhookVerification<Event> =\n | { readonly outcome: 'event'; readonly event: Event }\n | { readonly outcome: 'ignored'; readonly type: string }\n | { readonly outcome: 'rejected'; readonly error: WebhookVerificationError }\n\n/** The provider owns signature verification, timestamp tolerance, and payload parsing. */\nexport interface WebhookVerifier<Event> {\n /** Verify the exact body before parsing it. Return invalid requests; throw operational failures. */\n verify(body: string, headers: Headers): Promise<WebhookVerification<Event>>\n}\n"],"mappings":";;;;AAGA,IAAa,2BAAb,cAA8C,SAAS;CAM1C;CALX,AAAS,OAAO;CAChB,AAAS,iBAAiB;;CAG1B,YACE,AAAS,QACT,OACA;EACA,MAAM,qBAAqB,UAAU,EAAE,MAAM,CAAC;EAHrC;CAIX;AACF"}
1
+ {"version":3,"file":"webhook-verification-trEgyz7I.js","names":[],"sources":["../src/infrastructure/http/webhook-verification.ts"],"sourcesContent":["import { AppError } from '@/core'\n\n/** A webhook cannot be trusted or parsed. The HTTP handler logs the rejection once. */\nexport class WebhookVerificationError extends AppError {\n readonly _tag = 'WebhookVerificationError'\n readonly classification = 'terminal'\n\n /** Keep secrets and payload details out of the public message. */\n constructor(\n readonly reason: 'missing_headers' | 'invalid_signature' | 'invalid_payload',\n cause?: unknown,\n ) {\n super(`Webhook rejected: ${reason}`, { cause })\n }\n}\n\n/** A verified event, an ignored event, or an invalid request. */\nexport type WebhookVerification<Event> =\n | { readonly outcome: 'event'; readonly event: Event }\n | { readonly outcome: 'ignored'; readonly type: string }\n | { readonly outcome: 'rejected'; readonly error: WebhookVerificationError }\n\n/** The provider owns signature verification, timestamp tolerance, and payload parsing. */\nexport interface WebhookVerifier<Event> {\n /** Verify the exact body before parsing it. Return invalid requests; throw operational failures. */\n verify(body: string, headers: Headers): Promise<WebhookVerification<Event>>\n}\n"],"mappings":";;;;AAGA,IAAa,2BAAb,cAA8C,SAAS;CAM1C;CALX,AAAS,OAAO;CAChB,AAAS,iBAAiB;;CAG1B,YACE,AAAS,QACT,OACA;EACA,MAAM,qBAAqB,UAAU,EAAE,MAAM,CAAC;EAHrC;CAIX;AACF"}