better-ship 0.13.0 → 0.15.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 (103) hide show
  1. package/dist/{application-DnlgWcOZ.js → application-C4JOf71r.js} +22 -3
  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/durable-object-client.d.ts +18 -0
  14. package/dist/cloudflare/durable-object-client.d.ts.map +1 -0
  15. package/dist/cloudflare/durable-object-client.js +46 -0
  16. package/dist/cloudflare/durable-object-client.js.map +1 -0
  17. package/dist/cloudflare/durable-object-errors.d.ts +40 -0
  18. package/dist/cloudflare/durable-object-errors.d.ts.map +1 -0
  19. package/dist/cloudflare/durable-object-errors.js +48 -0
  20. package/dist/cloudflare/durable-object-errors.js.map +1 -0
  21. package/dist/cloudflare/queue.d.ts +2 -2
  22. package/dist/cloudflare/queue.js +2 -2
  23. package/dist/cloudflare/scheduled.d.ts +1 -1
  24. package/dist/cloudflare/scheduled.js +1 -1
  25. package/dist/cloudflare.d.ts +3 -3
  26. package/dist/cloudflare.js +3 -3
  27. package/dist/{core-BiqdOMwU.js → core-hdaNiCOU.js} +21 -2
  28. package/dist/core-hdaNiCOU.js.map +1 -0
  29. package/dist/core.d.ts +2 -2
  30. package/dist/core.js +2 -2
  31. package/dist/http.d.ts +2 -2
  32. package/dist/http.js +2 -2
  33. package/dist/{index-DG4jkwad.d.ts → index-B45Dr5gs.d.ts} +58 -5
  34. package/dist/index-B45Dr5gs.d.ts.map +1 -0
  35. package/dist/{index-B5O6mNjZ.d.ts → index-G1QG0P5g.d.ts} +34 -2
  36. package/dist/index-G1QG0P5g.d.ts.map +1 -0
  37. package/dist/{outbox-repository-options-BiilQf4_.d.ts → outbox-repository-options-CwTTuy7B.d.ts} +3 -3
  38. package/dist/{outbox-repository-options-BiilQf4_.d.ts.map → outbox-repository-options-CwTTuy7B.d.ts.map} +1 -1
  39. package/dist/{outbox-repository-options-BI75j7b7.js → outbox-repository-options-DOTpJH-e.js} +2 -2
  40. package/dist/{outbox-repository-options-BI75j7b7.js.map → outbox-repository-options-DOTpJH-e.js.map} +1 -1
  41. package/dist/postgres.d.ts +3 -3
  42. package/dist/postgres.js +3 -3
  43. package/dist/posthog/client.d.ts +22 -0
  44. package/dist/posthog/client.d.ts.map +1 -0
  45. package/dist/posthog/client.js +34 -0
  46. package/dist/posthog/client.js.map +1 -0
  47. package/dist/posthog/metrics.d.ts +18 -0
  48. package/dist/posthog/metrics.d.ts.map +1 -0
  49. package/dist/posthog/metrics.js +30 -0
  50. package/dist/posthog/metrics.js.map +1 -0
  51. package/dist/posthog/proxy.d.ts +17 -0
  52. package/dist/posthog/proxy.d.ts.map +1 -0
  53. package/dist/posthog/proxy.js +34 -0
  54. package/dist/posthog/proxy.js.map +1 -0
  55. package/dist/posthog/server.d.ts +37 -0
  56. package/dist/posthog/server.d.ts.map +1 -0
  57. package/dist/posthog/server.js +81 -0
  58. package/dist/posthog/server.js.map +1 -0
  59. package/dist/resend.d.ts +3 -3
  60. package/dist/resend.js +3 -3
  61. package/dist/resend.js.map +1 -1
  62. package/dist/sentry/client.d.ts +22 -0
  63. package/dist/sentry/client.d.ts.map +1 -0
  64. package/dist/sentry/client.js +48 -0
  65. package/dist/sentry/client.js.map +1 -0
  66. package/dist/sentry/server.d.ts +24 -0
  67. package/dist/sentry/server.d.ts.map +1 -0
  68. package/dist/sentry/server.js +48 -0
  69. package/dist/sentry/server.js.map +1 -0
  70. package/dist/sentry/vite.d.ts +11 -0
  71. package/dist/sentry/vite.d.ts.map +1 -0
  72. package/dist/sentry/vite.js +17 -0
  73. package/dist/sentry/vite.js.map +1 -0
  74. package/dist/tanstack.d.ts +1 -1
  75. package/dist/tanstack.js +2 -2
  76. package/dist/testing/outbox-repository.d.ts +2 -2
  77. package/dist/{webhook-verification-CyBoERFz.d.ts → webhook-verification-D7DIhH6C.d.ts} +2 -2
  78. package/dist/{webhook-verification-CyBoERFz.d.ts.map → webhook-verification-D7DIhH6C.d.ts.map} +1 -1
  79. package/dist/{webhook-verification-CeXUJXVP.js → webhook-verification-trEgyz7I.js} +2 -2
  80. package/dist/{webhook-verification-CeXUJXVP.js.map → webhook-verification-trEgyz7I.js.map} +1 -1
  81. package/package.json +11 -2
  82. package/src/application/analytics-service.ts +21 -0
  83. package/src/application/email-service.ts +2 -2
  84. package/src/application/index.ts +13 -0
  85. package/src/application/metrics-service.ts +28 -0
  86. package/src/application/observability-service.ts +16 -0
  87. package/src/core/analytics-event.ts +37 -0
  88. package/src/core/index.ts +9 -0
  89. package/src/core/missing-configuration.error.ts +12 -0
  90. package/src/infrastructure/cloudflare/durable-object-client.ts +70 -0
  91. package/src/infrastructure/cloudflare/durable-object-errors.ts +70 -0
  92. package/src/infrastructure/posthog/client.ts +41 -0
  93. package/src/infrastructure/posthog/metrics.ts +30 -0
  94. package/src/infrastructure/posthog/proxy.ts +47 -0
  95. package/src/infrastructure/posthog/server.ts +107 -0
  96. package/src/infrastructure/resend/resend-email-service.ts +4 -1
  97. package/src/infrastructure/sentry/client.ts +55 -0
  98. package/src/infrastructure/sentry/server.ts +51 -0
  99. package/src/infrastructure/sentry/vite.ts +17 -0
  100. package/dist/application-DnlgWcOZ.js.map +0 -1
  101. package/dist/core-BiqdOMwU.js.map +0 -1
  102. package/dist/index-B5O6mNjZ.d.ts.map +0 -1
  103. package/dist/index-DG4jkwad.d.ts.map +0 -1
@@ -0,0 +1,30 @@
1
+ //#region src/infrastructure/posthog/metrics.ts
2
+ /** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */
3
+ var PostHogMetricsService = class {
4
+ client;
5
+ waitUntil;
6
+ /** Use the analytics client's metrics; the runtime owns each flush promise. */
7
+ constructor(client, waitUntil) {
8
+ this.client = client;
9
+ this.waitUntil = waitUntil;
10
+ }
11
+ /** Record a total and schedule its flush. */
12
+ count(name, value = 1, options) {
13
+ this.client.metrics.count(name, value, options);
14
+ this.waitUntil(this.client.metrics.flush());
15
+ }
16
+ /** Record a current value and schedule its flush. */
17
+ gauge(name, value, options) {
18
+ this.client.metrics.gauge(name, value, options);
19
+ this.waitUntil(this.client.metrics.flush());
20
+ }
21
+ /** Record an observation and schedule its flush. */
22
+ histogram(name, value, options) {
23
+ this.client.metrics.histogram(name, value, options);
24
+ this.waitUntil(this.client.metrics.flush());
25
+ }
26
+ };
27
+
28
+ //#endregion
29
+ export { PostHogMetricsService };
30
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","names":[],"sources":["../../src/infrastructure/posthog/metrics.ts"],"sourcesContent":["import type { PostHog } from 'posthog-node'\n\nimport type { MetricOptions, MetricsService } from '@/application'\n\n/** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */\nexport class PostHogMetricsService implements MetricsService {\n /** Use the analytics client's metrics; the runtime owns each flush promise. */\n constructor(\n private readonly client: Pick<PostHog, 'metrics'>,\n private readonly waitUntil: (promise: Promise<unknown>) => void,\n ) {}\n\n /** Record a total and schedule its flush. */\n count(name: string, value = 1, options?: MetricOptions): void {\n this.client.metrics.count(name, value, options)\n this.waitUntil(this.client.metrics.flush())\n }\n\n /** Record a current value and schedule its flush. */\n gauge(name: string, value: number, options?: MetricOptions): void {\n this.client.metrics.gauge(name, value, options)\n this.waitUntil(this.client.metrics.flush())\n }\n\n /** Record an observation and schedule its flush. */\n histogram(name: string, value: number, options?: MetricOptions): void {\n this.client.metrics.histogram(name, value, options)\n this.waitUntil(this.client.metrics.flush())\n }\n}\n"],"mappings":";;AAKA,IAAa,wBAAb,MAA6D;CAGxC;CACA;;CAFnB,YACE,AAAiB,QACjB,AAAiB,WACjB;EAFiB;EACA;CAChB;;CAGH,MAAM,MAAc,QAAQ,GAAG,SAA+B;EAC5D,KAAK,OAAO,QAAQ,MAAM,MAAM,OAAO,OAAO;EAC9C,KAAK,UAAU,KAAK,OAAO,QAAQ,MAAM,CAAC;CAC5C;;CAGA,MAAM,MAAc,OAAe,SAA+B;EAChE,KAAK,OAAO,QAAQ,MAAM,MAAM,OAAO,OAAO;EAC9C,KAAK,UAAU,KAAK,OAAO,QAAQ,MAAM,CAAC;CAC5C;;CAGA,UAAU,MAAc,OAAe,SAA+B;EACpE,KAAK,OAAO,QAAQ,UAAU,MAAM,OAAO,OAAO;EAClD,KAAK,UAAU,KAAK,OAAO,QAAQ,MAAM,CAAC;CAC5C;AACF"}
@@ -0,0 +1,17 @@
1
+ //#region src/infrastructure/posthog/proxy.d.ts
2
+ /** Runtime resources and deployment hosts for Typist's same-origin proxy. */
3
+ export interface PostHogProxyOptions {
4
+ readonly cache: Pick<Cache, 'match' | 'put'>;
5
+ readonly waitUntil: (promise: Promise<void>) => void;
6
+ readonly basePath?: string;
7
+ readonly ingestHost?: string;
8
+ readonly assetHost?: string;
9
+ readonly fetch?: typeof globalThis.fetch;
10
+ }
11
+ /**
12
+ * Mount at /relay/$ with GET, POST, and OPTIONS handlers. Supply Cloudflare's asset cache and waitUntil.
13
+ * The application must use the same base path in the browser's api_host option.
14
+ */
15
+ export declare function proxyPostHogRequest(request: Request, options: PostHogProxyOptions): Promise<Response>;
16
+ //#endregion
17
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","names":[],"sources":["../../src/infrastructure/posthog/proxy.ts"],"mappings":";;iBACiB;WACN,OAAO,KAAK;WACZ,YAAY,SAAS;WACrB;WACA;WACA;WACA,eAAe,WAAW;;;;;;wBAOf,oBACpB,SAAS,SACT,SAAS,sBACR,QAAQ"}
@@ -0,0 +1,34 @@
1
+ //#region src/infrastructure/posthog/proxy.ts
2
+ /**
3
+ * Mount at /relay/$ with GET, POST, and OPTIONS handlers. Supply Cloudflare's asset cache and waitUntil.
4
+ * The application must use the same base path in the browser's api_host option.
5
+ */
6
+ async function proxyPostHogRequest(request, options) {
7
+ const url = new URL(request.url);
8
+ const basePath = options.basePath ?? "/relay";
9
+ if (!url.pathname.startsWith(`${basePath}/`)) return new Response(null, { status: 404 });
10
+ const pathname = url.pathname.slice(basePath.length);
11
+ const pathWithSearch = pathname + url.search;
12
+ const send = options.fetch ?? globalThis.fetch;
13
+ if (pathname.startsWith("/static/") || pathname.startsWith("/array/")) {
14
+ const cached = await options.cache.match(request);
15
+ if (cached) return cached;
16
+ const response = await send(`https://${options.assetHost ?? "us-assets.i.posthog.com"}${pathWithSearch}`);
17
+ options.waitUntil(options.cache.put(request, response.clone()));
18
+ return response;
19
+ }
20
+ const headers = new Headers(request.headers);
21
+ headers.delete("cookie");
22
+ headers.delete("authorization");
23
+ headers.set("X-Forwarded-For", request.headers.get("CF-Connecting-IP") ?? "");
24
+ return await send(`https://${options.ingestHost ?? "us.i.posthog.com"}${pathWithSearch}`, {
25
+ method: request.method,
26
+ headers,
27
+ body: request.method !== "GET" && request.method !== "HEAD" ? await request.arrayBuffer() : null,
28
+ redirect: request.redirect
29
+ });
30
+ }
31
+
32
+ //#endregion
33
+ export { proxyPostHogRequest };
34
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","names":[],"sources":["../../src/infrastructure/posthog/proxy.ts"],"sourcesContent":["/** Runtime resources and deployment hosts for Typist's same-origin proxy. */\nexport interface PostHogProxyOptions {\n readonly cache: Pick<Cache, 'match' | 'put'>\n readonly waitUntil: (promise: Promise<void>) => void\n readonly basePath?: string\n readonly ingestHost?: string\n readonly assetHost?: string\n readonly fetch?: typeof globalThis.fetch\n}\n\n/**\n * Mount at /relay/$ with GET, POST, and OPTIONS handlers. Supply Cloudflare's asset cache and waitUntil.\n * The application must use the same base path in the browser's api_host option.\n */\nexport async function proxyPostHogRequest(\n request: Request,\n options: PostHogProxyOptions,\n): Promise<Response> {\n const url = new URL(request.url)\n const basePath = options.basePath ?? '/relay'\n if (!url.pathname.startsWith(`${basePath}/`)) return new Response(null, { status: 404 })\n const pathname = url.pathname.slice(basePath.length)\n const pathWithSearch = pathname + url.search\n const send = options.fetch ?? globalThis.fetch\n\n if (pathname.startsWith('/static/') || pathname.startsWith('/array/')) {\n const cached = await options.cache.match(request)\n if (cached) return cached\n const response = await send(\n `https://${options.assetHost ?? 'us-assets.i.posthog.com'}${pathWithSearch}`,\n )\n options.waitUntil(options.cache.put(request, response.clone()))\n return response\n }\n\n const headers = new Headers(request.headers)\n headers.delete('cookie')\n headers.delete('authorization')\n headers.set('X-Forwarded-For', request.headers.get('CF-Connecting-IP') ?? '')\n return await send(`https://${options.ingestHost ?? 'us.i.posthog.com'}${pathWithSearch}`, {\n method: request.method,\n headers,\n body:\n request.method !== 'GET' && request.method !== 'HEAD' ? await request.arrayBuffer() : null,\n redirect: request.redirect,\n })\n}\n"],"mappings":";;;;;AAcA,eAAsB,oBACpB,SACA,SACmB;CACnB,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;CAC/B,MAAM,WAAW,QAAQ,YAAY;CACrC,IAAI,CAAC,IAAI,SAAS,WAAW,GAAG,SAAS,EAAE,GAAG,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;CACvF,MAAM,WAAW,IAAI,SAAS,MAAM,SAAS,MAAM;CACnD,MAAM,iBAAiB,WAAW,IAAI;CACtC,MAAM,OAAO,QAAQ,SAAS,WAAW;CAEzC,IAAI,SAAS,WAAW,UAAU,KAAK,SAAS,WAAW,SAAS,GAAG;EACrE,MAAM,SAAS,MAAM,QAAQ,MAAM,MAAM,OAAO;EAChD,IAAI,QAAQ,OAAO;EACnB,MAAM,WAAW,MAAM,KACrB,WAAW,QAAQ,aAAa,4BAA4B,gBAC9D;EACA,QAAQ,UAAU,QAAQ,MAAM,IAAI,SAAS,SAAS,MAAM,CAAC,CAAC;EAC9D,OAAO;CACT;CAEA,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;CAC3C,QAAQ,OAAO,QAAQ;CACvB,QAAQ,OAAO,eAAe;CAC9B,QAAQ,IAAI,mBAAmB,QAAQ,QAAQ,IAAI,kBAAkB,KAAK,EAAE;CAC5E,OAAO,MAAM,KAAK,WAAW,QAAQ,cAAc,qBAAqB,kBAAkB;EACxF,QAAQ,QAAQ;EAChB;EACA,MACE,QAAQ,WAAW,SAAS,QAAQ,WAAW,SAAS,MAAM,QAAQ,YAAY,IAAI;EACxF,UAAU,QAAQ;CACpB,CAAC;AACH"}
@@ -0,0 +1,37 @@
1
+ import { Jt as AnalyticsProperties, Kt as AnalyticsEventIdentity, qt as AnalyticsEventInput } from "../index-G1QG0P5g.js";
2
+ import { X as AnalyticsService } from "../index-B45Dr5gs.js";
3
+ import { PostHogMetricsService } from "./metrics.js";
4
+ import { PostHog, PostHogOptions } from "posthog-node";
5
+ //#region src/infrastructure/posthog/server.d.ts
6
+ /** Deployment values and SDK options; the runtime owns delivery promises. */
7
+ export interface PostHogAnalyticsConfig {
8
+ readonly apiKey: string;
9
+ readonly environment: string;
10
+ readonly source: string;
11
+ readonly waitUntil: (promise: Promise<unknown>) => void;
12
+ readonly options?: Omit<PostHogOptions, 'waitUntil' | 'flushAt' | 'flushInterval' | 'metrics' | 'disabled'> & {
13
+ readonly metrics?: Omit<NonNullable<PostHogOptions['metrics']>, 'serviceName' | 'environment'>;
14
+ };
15
+ }
16
+ /** Worker analytics ported from Typist; disabled applications select Porte's NULL_ANALYTICS instead. */
17
+ export declare class PostHogAnalyticsService implements AnalyticsService {
18
+ /** Raw SDK access for integrations such as PostHog AI tracing. */
19
+ readonly posthog: PostHog;
20
+ private readonly waitUntil;
21
+ /** Metrics share this service's SDK client and delivery lifetime. */
22
+ readonly metrics: PostHogMetricsService;
23
+ private constructor();
24
+ /** Construct once per Worker invocation; a blank key throws MissingConfigurationError. */
25
+ static create(config: PostHogAnalyticsConfig): PostHogAnalyticsService;
26
+ /** Schedule person properties; the SDK's waitUntil option protects queued delivery. */
27
+ identify(distinctId: string, properties?: AnalyticsProperties): void;
28
+ /** Schedule an irreversible person merge; only alias IDs the application proves belong to one person. */
29
+ alias(distinctId: string, previousDistinctId: string): void;
30
+ /** Schedule an event; omit identity for SDK-generated UUID and capture time. */
31
+ track(event: AnalyticsEventInput, distinctId: string, identity?: AnalyticsEventIdentity): void;
32
+ /** Schedule an event without creating a person profile for an attempt or resource ID. */
33
+ trackAnonymous(event: AnalyticsEventInput, distinctId: string, identity?: AnalyticsEventIdentity): void;
34
+ private capture;
35
+ }
36
+ //#endregion
37
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/infrastructure/posthog/server.ts"],"mappings":";;;;;;iBAaiB;WACN;WACA;WACA;WACA,YAAY,SAAS;WACrB,UAAU,KACjB;aAGS,UAAU,KAAK,YAAY;;;;qBAK3B,mCAAmC;;WAMnC,SAAS;mBACD;;WALV,SAAS;UAEX;;SASA,OAAO,QAAQ,yBAAyB;;EAqB/C,SAAS,oBAAoB,aAAa;;EAO1C,MAAM,oBAAoB;;EAK1B,MAAM,OAAO,qBAAqB,oBAAoB,WAAW;;EAKjE,eACE,OAAO,qBACP,oBACA,WAAW;UAKL"}
@@ -0,0 +1,81 @@
1
+ import { et as MissingConfigurationError } from "../core-hdaNiCOU.js";
2
+ import { PostHogMetricsService } from "./metrics.js";
3
+ import { PostHog } from "posthog-node";
4
+
5
+ //#region src/infrastructure/posthog/server.ts
6
+ /** Worker analytics ported from Typist; disabled applications select Porte's NULL_ANALYTICS instead. */
7
+ var PostHogAnalyticsService = class PostHogAnalyticsService {
8
+ posthog;
9
+ waitUntil;
10
+ /** Metrics share this service's SDK client and delivery lifetime. */
11
+ metrics;
12
+ constructor(posthog, waitUntil) {
13
+ this.posthog = posthog;
14
+ this.waitUntil = waitUntil;
15
+ this.metrics = new PostHogMetricsService(posthog, waitUntil);
16
+ }
17
+ /** Construct once per Worker invocation; a blank key throws MissingConfigurationError. */
18
+ static create(config) {
19
+ const apiKey = config.apiKey.trim();
20
+ if (apiKey.length === 0) throw new MissingConfigurationError("POSTHOG_API_KEY");
21
+ const client = new PostHog(apiKey, {
22
+ host: "https://us.i.posthog.com",
23
+ ...config.options,
24
+ flushAt: 1,
25
+ flushInterval: 0,
26
+ waitUntil: config.waitUntil,
27
+ metrics: {
28
+ ...config.options?.metrics,
29
+ environment: config.environment,
30
+ serviceName: config.source
31
+ }
32
+ });
33
+ client.register({
34
+ environment: config.environment,
35
+ source: config.source
36
+ });
37
+ return new PostHogAnalyticsService(client, config.waitUntil);
38
+ }
39
+ /** Schedule person properties; the SDK's waitUntil option protects queued delivery. */
40
+ identify(distinctId, properties) {
41
+ const payload = { distinctId };
42
+ if (properties !== void 0) payload.properties = properties;
43
+ this.posthog.identify(payload);
44
+ }
45
+ /** Schedule an irreversible person merge; only alias IDs the application proves belong to one person. */
46
+ alias(distinctId, previousDistinctId) {
47
+ this.waitUntil(this.posthog.aliasImmediate({
48
+ distinctId,
49
+ alias: previousDistinctId
50
+ }));
51
+ }
52
+ /** Schedule an event; omit identity for SDK-generated UUID and capture time. */
53
+ track(event, distinctId, identity) {
54
+ this.capture(event, distinctId, "person", identity);
55
+ }
56
+ /** Schedule an event without creating a person profile for an attempt or resource ID. */
57
+ trackAnonymous(event, distinctId, identity) {
58
+ this.capture(event, distinctId, "anonymous", identity);
59
+ }
60
+ capture(event, distinctId, profile, identity) {
61
+ const properties = { ...event.properties };
62
+ const payload = {
63
+ distinctId,
64
+ event: event.name,
65
+ properties
66
+ };
67
+ if (profile === "anonymous") payload.properties = {
68
+ ...properties,
69
+ $process_person_profile: false
70
+ };
71
+ if (identity !== void 0) {
72
+ payload.uuid = identity.uuid;
73
+ payload.timestamp = identity.timestamp;
74
+ }
75
+ this.waitUntil(this.posthog.captureImmediate(payload));
76
+ }
77
+ };
78
+
79
+ //#endregion
80
+ export { PostHogAnalyticsService };
81
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../src/infrastructure/posthog/server.ts"],"sourcesContent":["import { PostHog, type PostHogOptions } from 'posthog-node'\n\nimport type { AnalyticsService } from '@/application'\nimport {\n MissingConfigurationError,\n type AnalyticsEventIdentity,\n type AnalyticsEventInput,\n type AnalyticsProperties,\n} from '@/core'\n\nimport { PostHogMetricsService } from './metrics.ts'\n\n/** Deployment values and SDK options; the runtime owns delivery promises. */\nexport interface PostHogAnalyticsConfig {\n readonly apiKey: string\n readonly environment: string\n readonly source: string\n readonly waitUntil: (promise: Promise<unknown>) => void\n readonly options?: Omit<\n PostHogOptions,\n 'waitUntil' | 'flushAt' | 'flushInterval' | 'metrics' | 'disabled'\n > & {\n readonly metrics?: Omit<NonNullable<PostHogOptions['metrics']>, 'serviceName' | 'environment'>\n }\n}\n\n/** Worker analytics ported from Typist; disabled applications select Porte's NULL_ANALYTICS instead. */\nexport class PostHogAnalyticsService implements AnalyticsService {\n /** Metrics share this service's SDK client and delivery lifetime. */\n readonly metrics: PostHogMetricsService\n\n private constructor(\n /** Raw SDK access for integrations such as PostHog AI tracing. */\n readonly posthog: PostHog,\n private readonly waitUntil: (promise: Promise<unknown>) => void,\n ) {\n this.metrics = new PostHogMetricsService(posthog, waitUntil)\n }\n\n /** Construct once per Worker invocation; a blank key throws MissingConfigurationError. */\n static create(config: PostHogAnalyticsConfig): PostHogAnalyticsService {\n const apiKey = config.apiKey.trim()\n if (apiKey.length === 0) throw new MissingConfigurationError('POSTHOG_API_KEY')\n const client = new PostHog(apiKey, {\n host: 'https://us.i.posthog.com',\n ...config.options,\n flushAt: 1,\n flushInterval: 0,\n waitUntil: config.waitUntil,\n metrics: {\n ...config.options?.metrics,\n environment: config.environment,\n serviceName: config.source,\n },\n })\n // The SDK applies in-memory super properties synchronously before this promise resolves.\n void client.register({ environment: config.environment, source: config.source })\n return new PostHogAnalyticsService(client, config.waitUntil)\n }\n\n /** Schedule person properties; the SDK's waitUntil option protects queued delivery. */\n identify(distinctId: string, properties?: AnalyticsProperties): void {\n const payload: Parameters<PostHog['identify']>[0] = { distinctId }\n if (properties !== undefined) payload.properties = properties\n this.posthog.identify(payload)\n }\n\n /** Schedule an irreversible person merge; only alias IDs the application proves belong to one person. */\n alias(distinctId: string, previousDistinctId: string): void {\n this.waitUntil(this.posthog.aliasImmediate({ distinctId, alias: previousDistinctId }))\n }\n\n /** Schedule an event; omit identity for SDK-generated UUID and capture time. */\n track(event: AnalyticsEventInput, distinctId: string, identity?: AnalyticsEventIdentity): void {\n this.capture(event, distinctId, 'person', identity)\n }\n\n /** Schedule an event without creating a person profile for an attempt or resource ID. */\n trackAnonymous(\n event: AnalyticsEventInput,\n distinctId: string,\n identity?: AnalyticsEventIdentity,\n ): void {\n this.capture(event, distinctId, 'anonymous', identity)\n }\n\n private capture(\n event: AnalyticsEventInput,\n distinctId: string,\n profile: 'person' | 'anonymous',\n identity?: AnalyticsEventIdentity,\n ): void {\n const properties = { ...event.properties }\n const payload: Parameters<PostHog['captureImmediate']>[0] = {\n distinctId,\n event: event.name,\n properties,\n }\n if (profile === 'anonymous')\n payload.properties = { ...properties, $process_person_profile: false }\n if (identity !== undefined) {\n payload.uuid = identity.uuid\n payload.timestamp = identity.timestamp\n }\n this.waitUntil(this.posthog.captureImmediate(payload))\n }\n}\n"],"mappings":";;;;;;AA2BA,IAAa,0BAAb,MAAa,wBAAoD;CAMpD;CACQ;;CALnB,AAAS;CAET,AAAQ,YAEN,AAAS,SACT,AAAiB,WACjB;EAFS;EACQ;EAEjB,KAAK,UAAU,IAAI,sBAAsB,SAAS,SAAS;CAC7D;;CAGA,OAAO,OAAO,QAAyD;EACrE,MAAM,SAAS,OAAO,OAAO,KAAK;EAClC,IAAI,OAAO,WAAW,GAAG,MAAM,IAAI,0BAA0B,iBAAiB;EAC9E,MAAM,SAAS,IAAI,QAAQ,QAAQ;GACjC,MAAM;GACN,GAAG,OAAO;GACV,SAAS;GACT,eAAe;GACf,WAAW,OAAO;GAClB,SAAS;IACP,GAAG,OAAO,SAAS;IACnB,aAAa,OAAO;IACpB,aAAa,OAAO;GACtB;EACF,CAAC;EAED,AAAK,OAAO,SAAS;GAAE,aAAa,OAAO;GAAa,QAAQ,OAAO;EAAO,CAAC;EAC/E,OAAO,IAAI,wBAAwB,QAAQ,OAAO,SAAS;CAC7D;;CAGA,SAAS,YAAoB,YAAwC;EACnE,MAAM,UAA8C,EAAE,WAAW;EACjE,IAAI,eAAe,QAAW,QAAQ,aAAa;EACnD,KAAK,QAAQ,SAAS,OAAO;CAC/B;;CAGA,MAAM,YAAoB,oBAAkC;EAC1D,KAAK,UAAU,KAAK,QAAQ,eAAe;GAAE;GAAY,OAAO;EAAmB,CAAC,CAAC;CACvF;;CAGA,MAAM,OAA4B,YAAoB,UAAyC;EAC7F,KAAK,QAAQ,OAAO,YAAY,UAAU,QAAQ;CACpD;;CAGA,eACE,OACA,YACA,UACM;EACN,KAAK,QAAQ,OAAO,YAAY,aAAa,QAAQ;CACvD;CAEA,AAAQ,QACN,OACA,YACA,SACA,UACM;EACN,MAAM,aAAa,EAAE,GAAG,MAAM,WAAW;EACzC,MAAM,UAAsD;GAC1D;GACA,OAAO,MAAM;GACb;EACF;EACA,IAAI,YAAY,aACd,QAAQ,aAAa;GAAE,GAAG;GAAY,yBAAyB;EAAM;EACvE,IAAI,aAAa,QAAW;GAC1B,QAAQ,OAAO,SAAS;GACxB,QAAQ,YAAY,SAAS;EAC/B;EACA,KAAK,UAAU,KAAK,QAAQ,iBAAiB,OAAO,CAAC;CACvD;AACF"}
package/dist/resend.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Mt as BounceType } from "./index-B5O6mNjZ.js";
2
- import { B as OutboundEmail, I as EmailAccepted, R as IEmailService } from "./index-DG4jkwad.js";
3
- import { r as WebhookVerifier, t as WebhookVerification } from "./webhook-verification-CyBoERFz.js";
1
+ import { Mt as BounceType } from "./index-G1QG0P5g.js";
2
+ import { B as OutboundEmail, I as EmailAccepted, R as IEmailService } from "./index-B45Dr5gs.js";
3
+ import { r as WebhookVerifier, t as WebhookVerification } from "./webhook-verification-D7DIhH6C.js";
4
4
  import { Resend } from "resend";
5
5
  //#region src/infrastructure/resend/resend-email-service.d.ts
6
6
  /** The SDK operations required by the adapter; a test factory can return a plain object. */
package/dist/resend.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Q as EmailAddressSchema, Y as EmailServiceError, q as parseBounceType } from "./core-BiqdOMwU.js";
2
- import { t as WebhookVerificationError } from "./webhook-verification-CeXUJXVP.js";
1
+ import { Q as EmailAddressSchema, Y as EmailServiceError, q as parseBounceType } from "./core-hdaNiCOU.js";
2
+ import { t as WebhookVerificationError } from "./webhook-verification-trEgyz7I.js";
3
3
  import { z } from "zod";
4
4
  import { Buffer } from "node:buffer";
5
5
 
@@ -90,7 +90,7 @@ var ResendEmailService = class ResendEmailService {
90
90
  value
91
91
  }));
92
92
  if (email.attachments !== void 0) payload.attachments = email.attachments.map(toAttachment);
93
- const result = await client.emails.send(payload, { idempotencyKey: email.idempotencyKey });
93
+ const result = await client.emails.send(payload, email.idempotencyKey === void 0 ? void 0 : { idempotencyKey: email.idempotencyKey });
94
94
  if (result.error !== null) throw new EmailServiceError({
95
95
  reason: reasonFromResend(result.error),
96
96
  provider: "resend",
@@ -1 +1 @@
1
- {"version":3,"file":"resend.js","names":[],"sources":["../src/infrastructure/resend/resend-email-service.ts","../src/infrastructure/resend/email-event.ts","../src/infrastructure/resend/resend-webhook.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\n\nimport type { Attachment, CreateEmailOptions, ErrorResponse, Resend } from 'resend'\n\nimport type { EmailAccepted, EmailAttachment, IEmailService, OutboundEmail } from '@/application'\nimport { EmailServiceError, type EmailSendFailureReason } from '@/core'\n\n// SDK additions must receive an explicit classification before the package builds.\nconst SDK_REASONS = {\n invalid_idempotency_key: 'invalid_request',\n validation_error: 'invalid_request',\n missing_api_key: 'configuration',\n restricted_api_key: 'configuration',\n invalid_api_key: 'configuration',\n not_found: 'invalid_request',\n method_not_allowed: 'invalid_request',\n invalid_idempotent_request: 'invalid_request',\n concurrent_idempotent_requests: 'idempotency_conflict',\n invalid_attachment: 'invalid_request',\n invalid_from_address: 'invalid_request',\n invalid_access: 'configuration',\n invalid_parameter: 'invalid_request',\n invalid_region: 'invalid_request',\n missing_required_field: 'invalid_request',\n monthly_quota_exceeded: 'quota_exceeded',\n daily_quota_exceeded: 'quota_exceeded',\n rate_limit_exceeded: 'rate_limited',\n security_error: 'configuration',\n application_error: 'provider_error',\n internal_server_error: 'provider_error',\n} satisfies Record<ErrorResponse['name'], EmailSendFailureReason>\n\n// Resend documents these codes before its SDK includes them in ErrorResponse.\nconst DOCUMENTED_REASONS = new Map<string, EmailSendFailureReason>([\n ['email_above_quota', 'quota_exceeded'],\n ['invalid_permission', 'configuration'],\n ['suspended_api_key', 'configuration'],\n ['resource_locked', 'idempotency_conflict'],\n ['service_unavailable', 'provider_error'],\n ['missing_required_parameter', 'invalid_request'],\n])\n\nfunction reasonFromResend(error: ErrorResponse): EmailSendFailureReason {\n if (Object.hasOwn(SDK_REASONS, error.name)) return SDK_REASONS[error.name]\n const reason = DOCUMENTED_REASONS.get(error.name)\n if (reason !== undefined) return reason\n if (error.statusCode === 429) return 'rate_limited'\n if (error.statusCode !== null && error.statusCode >= 400 && error.statusCode < 500)\n return 'invalid_request'\n return 'provider_error'\n}\n\nfunction retryAfterSeconds(value: string | undefined): number | null {\n if (value === undefined || !/^\\d+$/.test(value)) return null\n const seconds = Number(value)\n return Number.isSafeInteger(seconds) ? seconds : null\n}\n\nfunction toAttachment(attachment: EmailAttachment): Attachment {\n const result: Attachment = {\n filename: attachment.filename,\n content: ArrayBuffer.isView(attachment.content)\n ? Buffer.from(attachment.content)\n : attachment.content,\n }\n if (attachment.contentType !== undefined) result.contentType = attachment.contentType\n return result\n}\n\n/** The SDK operations required by the adapter; a test factory can return a plain object. */\nexport type ResendEmailClient = { readonly emails: Pick<Resend['emails'], 'send'> }\n\n/**\n * Send without package retries or logs. Ported from Suparelay's resend-email-service.ts.\n * The Resend SDK itself logs API errors outside production.\n */\nexport class ResendEmailService implements IEmailService {\n /** Load the SDK on the first send. Constructing the service does not load it. */\n static create(apiKey: string, fromAddress: string): ResendEmailService {\n return new ResendEmailService(\n () => import('resend').then(({ Resend }) => new Resend(apiKey)),\n fromAddress,\n )\n }\n\n private client: Promise<ResendEmailClient> | undefined\n\n /** Supply an async factory. The service calls it once, on the first send, and keeps its promise. */\n constructor(\n private readonly createClient: () => Promise<ResendEmailClient>,\n private readonly fromAddress: string,\n ) {}\n\n /** Resolve after acceptance. Provider failures throw EmailServiceError; SDK defects retain their identity. */\n async sendEmail(email: OutboundEmail): Promise<EmailAccepted> {\n if (this.client === undefined) {\n // Cache synchronous factory throws as rejections too, so later sends cannot initialize again.\n this.client = Promise.resolve().then(this.createClient)\n }\n const client = await this.client\n const payload: CreateEmailOptions = {\n from: email.from ?? this.fromAddress,\n to: [email.to].flat(),\n subject: email.subject,\n html: email.html,\n }\n if (email.text !== undefined) payload.text = email.text\n if (email.replyTo !== undefined) payload.replyTo = email.replyTo\n if (email.tags !== undefined)\n payload.tags = Object.entries(email.tags).map(([name, value]) => ({ name, value }))\n if (email.attachments !== undefined) payload.attachments = email.attachments.map(toAttachment)\n const result = await client.emails.send(payload, { idempotencyKey: email.idempotencyKey })\n if (result.error !== null) {\n throw new EmailServiceError({\n reason: reasonFromResend(result.error),\n provider: 'resend',\n providerCode: result.error.name,\n statusCode: result.error.statusCode,\n retryAfter: retryAfterSeconds(result.headers?.['retry-after']),\n cause: result.error,\n })\n }\n return { providerId: result.data.id }\n }\n}\n","import type { WebhookEventPayload } from 'resend'\nimport { z } from 'zod'\n\nimport { EmailAddressSchema, parseBounceType, type BounceType } from '@/core'\n\nconst SIMPLE_EMAIL_TYPES = [\n 'email.sent',\n 'email.scheduled',\n 'email.delivered',\n 'email.delivery_delayed',\n 'email.complained',\n 'email.opened',\n 'email.received',\n 'email.suppressed',\n] as const\n\n/** Resend email event names supported by this module. */\nexport type EmailEventType =\n | (typeof SIMPLE_EMAIL_TYPES)[number]\n | 'email.bounced'\n | 'email.failed'\n | 'email.clicked'\n\nconst EMAIL_TYPES = {\n 'email.sent': true,\n 'email.scheduled': true,\n 'email.delivered': true,\n 'email.delivery_delayed': true,\n 'email.complained': true,\n 'email.opened': true,\n 'email.received': true,\n 'email.suppressed': true,\n 'email.bounced': true,\n 'email.failed': true,\n 'email.clicked': true,\n} satisfies Record<Extract<WebhookEventPayload['type'], `email.${string}`>, true>\n\nconst BaseDataSchema = z.object({\n email_id: z.string().min(1),\n to: z.array(EmailAddressSchema).min(1),\n subject: z.string(),\n tags: z.record(z.string(), z.string()).default({}),\n})\n\nconst EmailPayloadSchema = z.discriminatedUnion('type', [\n z.object({ type: z.enum(SIMPLE_EMAIL_TYPES), data: BaseDataSchema }),\n z.object({\n type: z.literal('email.bounced'),\n data: BaseDataSchema.extend({\n bounce: z.object({\n type: z.string().optional(),\n subType: z.string().nullable().optional(),\n message: z.string(),\n }),\n }),\n }),\n z.object({\n type: z.literal('email.failed'),\n data: BaseDataSchema.extend({ failed: z.object({ reason: z.string() }) }),\n }),\n z.object({\n type: z.literal('email.clicked'),\n data: BaseDataSchema.extend({ click: z.object({ link: z.string() }) }),\n }),\n])\n\ntype EmailEventBase = {\n readonly id: string\n readonly emailId: string\n readonly recipients: readonly string[]\n readonly subject: string\n readonly tags: Readonly<Record<string, string>>\n readonly occurredAt: string\n}\n\n/** A verified event. `id` is the Svix identity; use `resend:event:${id}` as the command id. */\nexport type EmailEvent =\n | (EmailEventBase & {\n readonly type: 'email.bounced'\n readonly bounce: {\n readonly type: BounceType\n readonly subType: string | null\n readonly message: string\n }\n })\n | (EmailEventBase & { readonly type: 'email.failed'; readonly reason: string })\n | (EmailEventBase & { readonly type: 'email.clicked'; readonly link: string })\n | (EmailEventBase & { readonly type: (typeof SIMPLE_EMAIL_TYPES)[number] })\n\nconst EventHeaderSchema = z.object({\n type: z.string(),\n created_at: z.iso.datetime({ offset: true }),\n})\n\n/** Validate signed provider data before projecting it into the public event contract. */\nexport function parseEmailEvent(\n payload: WebhookEventPayload,\n id: string,\n):\n | { readonly outcome: 'event'; readonly event: EmailEvent }\n | { readonly outcome: 'ignored'; readonly type: string }\n | { readonly outcome: 'invalid' } {\n const header = EventHeaderSchema.safeParse(payload)\n if (!header.success) return { outcome: 'invalid' }\n if (!Object.hasOwn(EMAIL_TYPES, header.data.type))\n return { outcome: 'ignored', type: header.data.type }\n const parsed = EmailPayloadSchema.safeParse(payload)\n if (!parsed.success) return { outcome: 'invalid' }\n const event = parsed.data\n const base: EmailEventBase = {\n id,\n emailId: event.data.email_id,\n recipients: event.data.to,\n subject: event.data.subject,\n tags: event.data.tags,\n occurredAt: header.data.created_at,\n }\n switch (event.type) {\n case 'email.bounced':\n return {\n outcome: 'event',\n event: {\n ...base,\n type: event.type,\n bounce: {\n type: parseBounceType(event.data.bounce.type),\n subType: event.data.bounce.subType ?? null,\n message: event.data.bounce.message,\n },\n },\n }\n case 'email.failed':\n return {\n outcome: 'event',\n event: { ...base, type: event.type, reason: event.data.failed.reason },\n }\n case 'email.clicked':\n return { outcome: 'event', event: { ...base, type: event.type, link: event.data.click.link } }\n default:\n return { outcome: 'event', event: { ...base, type: event.type } }\n }\n}\n","import type { Resend } from 'resend'\nimport { z } from 'zod'\n\nimport {\n WebhookVerificationError,\n type WebhookVerification,\n type WebhookVerifier,\n} from '../http/webhook-verification.ts'\nimport { parseEmailEvent, type EmailEvent } from './email-event.ts'\n\n// Separate standardwebhooks versions have different prototypes; its error name remains the same.\nconst SignatureErrorSchema = z\n .instanceof(Error)\n .refine((error) => error.name === 'WebhookVerificationError')\n\n/** The SDK's local signature verifier. It makes no API request. */\nexport type ResendWebhookClient = Pick<Resend['webhooks'], 'verify'>\n\n/** Verify Svix signatures and map Resend email events. Ported from Typist's Resend route. */\nexport class ResendWebhook implements WebhookVerifier<EmailEvent> {\n /** Load the SDK on the first verification. Only the webhook secret is required. */\n static create(webhookSecret: string): ResendWebhook {\n return new ResendWebhook(webhookSecret, async () => {\n const { Resend } = await import('resend')\n // The SDK requires a nonempty key even though its local verifier never uses that key.\n return new Resend('re_webhook_verification_only').webhooks\n })\n }\n\n private client: Promise<ResendWebhookClient> | undefined\n\n /** Inject the local SDK verifier when testing. */\n constructor(\n private readonly webhookSecret: string,\n private readonly verifier: () => Promise<ResendWebhookClient>,\n ) {}\n\n /** Verify the raw body and Svix headers. Invalid requests return rejected; configuration and SDK defects propagate. */\n async verify(body: string, headers: Headers): Promise<WebhookVerification<EmailEvent>> {\n const id = headers.get('svix-id')\n const timestamp = headers.get('svix-timestamp')\n const signature = headers.get('svix-signature')\n if (!id || !timestamp || !signature)\n return { outcome: 'rejected', error: new WebhookVerificationError('missing_headers') }\n const client = await (this.client ??= this.verifier())\n let payload: ReturnType<ResendWebhookClient['verify']>\n try {\n payload = client.verify({\n payload: body,\n headers: { id, timestamp, signature },\n webhookSecret: this.webhookSecret,\n })\n } catch (cause) {\n if (cause instanceof SyntaxError)\n return {\n outcome: 'rejected',\n error: new WebhookVerificationError('invalid_payload', cause),\n }\n if (!SignatureErrorSchema.safeParse(cause).success) throw cause\n return {\n outcome: 'rejected',\n error: new WebhookVerificationError('invalid_signature', cause),\n }\n }\n const result = parseEmailEvent(payload, id)\n if (result.outcome === 'invalid')\n return { outcome: 'rejected', error: new WebhookVerificationError('invalid_payload') }\n return result\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resend.js","names":[],"sources":["../src/infrastructure/resend/resend-email-service.ts","../src/infrastructure/resend/email-event.ts","../src/infrastructure/resend/resend-webhook.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\n\nimport type { Attachment, CreateEmailOptions, ErrorResponse, Resend } from 'resend'\n\nimport type { EmailAccepted, EmailAttachment, IEmailService, OutboundEmail } from '@/application'\nimport { EmailServiceError, type EmailSendFailureReason } from '@/core'\n\n// SDK additions must receive an explicit classification before the package builds.\nconst SDK_REASONS = {\n invalid_idempotency_key: 'invalid_request',\n validation_error: 'invalid_request',\n missing_api_key: 'configuration',\n restricted_api_key: 'configuration',\n invalid_api_key: 'configuration',\n not_found: 'invalid_request',\n method_not_allowed: 'invalid_request',\n invalid_idempotent_request: 'invalid_request',\n concurrent_idempotent_requests: 'idempotency_conflict',\n invalid_attachment: 'invalid_request',\n invalid_from_address: 'invalid_request',\n invalid_access: 'configuration',\n invalid_parameter: 'invalid_request',\n invalid_region: 'invalid_request',\n missing_required_field: 'invalid_request',\n monthly_quota_exceeded: 'quota_exceeded',\n daily_quota_exceeded: 'quota_exceeded',\n rate_limit_exceeded: 'rate_limited',\n security_error: 'configuration',\n application_error: 'provider_error',\n internal_server_error: 'provider_error',\n} satisfies Record<ErrorResponse['name'], EmailSendFailureReason>\n\n// Resend documents these codes before its SDK includes them in ErrorResponse.\nconst DOCUMENTED_REASONS = new Map<string, EmailSendFailureReason>([\n ['email_above_quota', 'quota_exceeded'],\n ['invalid_permission', 'configuration'],\n ['suspended_api_key', 'configuration'],\n ['resource_locked', 'idempotency_conflict'],\n ['service_unavailable', 'provider_error'],\n ['missing_required_parameter', 'invalid_request'],\n])\n\nfunction reasonFromResend(error: ErrorResponse): EmailSendFailureReason {\n if (Object.hasOwn(SDK_REASONS, error.name)) return SDK_REASONS[error.name]\n const reason = DOCUMENTED_REASONS.get(error.name)\n if (reason !== undefined) return reason\n if (error.statusCode === 429) return 'rate_limited'\n if (error.statusCode !== null && error.statusCode >= 400 && error.statusCode < 500)\n return 'invalid_request'\n return 'provider_error'\n}\n\nfunction retryAfterSeconds(value: string | undefined): number | null {\n if (value === undefined || !/^\\d+$/.test(value)) return null\n const seconds = Number(value)\n return Number.isSafeInteger(seconds) ? seconds : null\n}\n\nfunction toAttachment(attachment: EmailAttachment): Attachment {\n const result: Attachment = {\n filename: attachment.filename,\n content: ArrayBuffer.isView(attachment.content)\n ? Buffer.from(attachment.content)\n : attachment.content,\n }\n if (attachment.contentType !== undefined) result.contentType = attachment.contentType\n return result\n}\n\n/** The SDK operations required by the adapter; a test factory can return a plain object. */\nexport type ResendEmailClient = { readonly emails: Pick<Resend['emails'], 'send'> }\n\n/**\n * Send without package retries or logs. Ported from Suparelay's resend-email-service.ts.\n * The Resend SDK itself logs API errors outside production.\n */\nexport class ResendEmailService implements IEmailService {\n /** Load the SDK on the first send. Constructing the service does not load it. */\n static create(apiKey: string, fromAddress: string): ResendEmailService {\n return new ResendEmailService(\n () => import('resend').then(({ Resend }) => new Resend(apiKey)),\n fromAddress,\n )\n }\n\n private client: Promise<ResendEmailClient> | undefined\n\n /** Supply an async factory. The service calls it once, on the first send, and keeps its promise. */\n constructor(\n private readonly createClient: () => Promise<ResendEmailClient>,\n private readonly fromAddress: string,\n ) {}\n\n /** Resolve after acceptance. Provider failures throw EmailServiceError; SDK defects retain their identity. */\n async sendEmail(email: OutboundEmail): Promise<EmailAccepted> {\n if (this.client === undefined) {\n // Cache synchronous factory throws as rejections too, so later sends cannot initialize again.\n this.client = Promise.resolve().then(this.createClient)\n }\n const client = await this.client\n const payload: CreateEmailOptions = {\n from: email.from ?? this.fromAddress,\n to: [email.to].flat(),\n subject: email.subject,\n html: email.html,\n }\n if (email.text !== undefined) payload.text = email.text\n if (email.replyTo !== undefined) payload.replyTo = email.replyTo\n if (email.tags !== undefined)\n payload.tags = Object.entries(email.tags).map(([name, value]) => ({ name, value }))\n if (email.attachments !== undefined) payload.attachments = email.attachments.map(toAttachment)\n const result = await client.emails.send(\n payload,\n email.idempotencyKey === undefined ? undefined : { idempotencyKey: email.idempotencyKey },\n )\n if (result.error !== null) {\n throw new EmailServiceError({\n reason: reasonFromResend(result.error),\n provider: 'resend',\n providerCode: result.error.name,\n statusCode: result.error.statusCode,\n retryAfter: retryAfterSeconds(result.headers?.['retry-after']),\n cause: result.error,\n })\n }\n return { providerId: result.data.id }\n }\n}\n","import type { WebhookEventPayload } from 'resend'\nimport { z } from 'zod'\n\nimport { EmailAddressSchema, parseBounceType, type BounceType } from '@/core'\n\nconst SIMPLE_EMAIL_TYPES = [\n 'email.sent',\n 'email.scheduled',\n 'email.delivered',\n 'email.delivery_delayed',\n 'email.complained',\n 'email.opened',\n 'email.received',\n 'email.suppressed',\n] as const\n\n/** Resend email event names supported by this module. */\nexport type EmailEventType =\n | (typeof SIMPLE_EMAIL_TYPES)[number]\n | 'email.bounced'\n | 'email.failed'\n | 'email.clicked'\n\nconst EMAIL_TYPES = {\n 'email.sent': true,\n 'email.scheduled': true,\n 'email.delivered': true,\n 'email.delivery_delayed': true,\n 'email.complained': true,\n 'email.opened': true,\n 'email.received': true,\n 'email.suppressed': true,\n 'email.bounced': true,\n 'email.failed': true,\n 'email.clicked': true,\n} satisfies Record<Extract<WebhookEventPayload['type'], `email.${string}`>, true>\n\nconst BaseDataSchema = z.object({\n email_id: z.string().min(1),\n to: z.array(EmailAddressSchema).min(1),\n subject: z.string(),\n tags: z.record(z.string(), z.string()).default({}),\n})\n\nconst EmailPayloadSchema = z.discriminatedUnion('type', [\n z.object({ type: z.enum(SIMPLE_EMAIL_TYPES), data: BaseDataSchema }),\n z.object({\n type: z.literal('email.bounced'),\n data: BaseDataSchema.extend({\n bounce: z.object({\n type: z.string().optional(),\n subType: z.string().nullable().optional(),\n message: z.string(),\n }),\n }),\n }),\n z.object({\n type: z.literal('email.failed'),\n data: BaseDataSchema.extend({ failed: z.object({ reason: z.string() }) }),\n }),\n z.object({\n type: z.literal('email.clicked'),\n data: BaseDataSchema.extend({ click: z.object({ link: z.string() }) }),\n }),\n])\n\ntype EmailEventBase = {\n readonly id: string\n readonly emailId: string\n readonly recipients: readonly string[]\n readonly subject: string\n readonly tags: Readonly<Record<string, string>>\n readonly occurredAt: string\n}\n\n/** A verified event. `id` is the Svix identity; use `resend:event:${id}` as the command id. */\nexport type EmailEvent =\n | (EmailEventBase & {\n readonly type: 'email.bounced'\n readonly bounce: {\n readonly type: BounceType\n readonly subType: string | null\n readonly message: string\n }\n })\n | (EmailEventBase & { readonly type: 'email.failed'; readonly reason: string })\n | (EmailEventBase & { readonly type: 'email.clicked'; readonly link: string })\n | (EmailEventBase & { readonly type: (typeof SIMPLE_EMAIL_TYPES)[number] })\n\nconst EventHeaderSchema = z.object({\n type: z.string(),\n created_at: z.iso.datetime({ offset: true }),\n})\n\n/** Validate signed provider data before projecting it into the public event contract. */\nexport function parseEmailEvent(\n payload: WebhookEventPayload,\n id: string,\n):\n | { readonly outcome: 'event'; readonly event: EmailEvent }\n | { readonly outcome: 'ignored'; readonly type: string }\n | { readonly outcome: 'invalid' } {\n const header = EventHeaderSchema.safeParse(payload)\n if (!header.success) return { outcome: 'invalid' }\n if (!Object.hasOwn(EMAIL_TYPES, header.data.type))\n return { outcome: 'ignored', type: header.data.type }\n const parsed = EmailPayloadSchema.safeParse(payload)\n if (!parsed.success) return { outcome: 'invalid' }\n const event = parsed.data\n const base: EmailEventBase = {\n id,\n emailId: event.data.email_id,\n recipients: event.data.to,\n subject: event.data.subject,\n tags: event.data.tags,\n occurredAt: header.data.created_at,\n }\n switch (event.type) {\n case 'email.bounced':\n return {\n outcome: 'event',\n event: {\n ...base,\n type: event.type,\n bounce: {\n type: parseBounceType(event.data.bounce.type),\n subType: event.data.bounce.subType ?? null,\n message: event.data.bounce.message,\n },\n },\n }\n case 'email.failed':\n return {\n outcome: 'event',\n event: { ...base, type: event.type, reason: event.data.failed.reason },\n }\n case 'email.clicked':\n return { outcome: 'event', event: { ...base, type: event.type, link: event.data.click.link } }\n default:\n return { outcome: 'event', event: { ...base, type: event.type } }\n }\n}\n","import type { Resend } from 'resend'\nimport { z } from 'zod'\n\nimport {\n WebhookVerificationError,\n type WebhookVerification,\n type WebhookVerifier,\n} from '../http/webhook-verification.ts'\nimport { parseEmailEvent, type EmailEvent } from './email-event.ts'\n\n// Separate standardwebhooks versions have different prototypes; its error name remains the same.\nconst SignatureErrorSchema = z\n .instanceof(Error)\n .refine((error) => error.name === 'WebhookVerificationError')\n\n/** The SDK's local signature verifier. It makes no API request. */\nexport type ResendWebhookClient = Pick<Resend['webhooks'], 'verify'>\n\n/** Verify Svix signatures and map Resend email events. Ported from Typist's Resend route. */\nexport class ResendWebhook implements WebhookVerifier<EmailEvent> {\n /** Load the SDK on the first verification. Only the webhook secret is required. */\n static create(webhookSecret: string): ResendWebhook {\n return new ResendWebhook(webhookSecret, async () => {\n const { Resend } = await import('resend')\n // The SDK requires a nonempty key even though its local verifier never uses that key.\n return new Resend('re_webhook_verification_only').webhooks\n })\n }\n\n private client: Promise<ResendWebhookClient> | undefined\n\n /** Inject the local SDK verifier when testing. */\n constructor(\n private readonly webhookSecret: string,\n private readonly verifier: () => Promise<ResendWebhookClient>,\n ) {}\n\n /** Verify the raw body and Svix headers. Invalid requests return rejected; configuration and SDK defects propagate. */\n async verify(body: string, headers: Headers): Promise<WebhookVerification<EmailEvent>> {\n const id = headers.get('svix-id')\n const timestamp = headers.get('svix-timestamp')\n const signature = headers.get('svix-signature')\n if (!id || !timestamp || !signature)\n return { outcome: 'rejected', error: new WebhookVerificationError('missing_headers') }\n const client = await (this.client ??= this.verifier())\n let payload: ReturnType<ResendWebhookClient['verify']>\n try {\n payload = client.verify({\n payload: body,\n headers: { id, timestamp, signature },\n webhookSecret: this.webhookSecret,\n })\n } catch (cause) {\n if (cause instanceof SyntaxError)\n return {\n outcome: 'rejected',\n error: new WebhookVerificationError('invalid_payload', cause),\n }\n if (!SignatureErrorSchema.safeParse(cause).success) throw cause\n return {\n outcome: 'rejected',\n error: new WebhookVerificationError('invalid_signature', cause),\n }\n }\n const result = parseEmailEvent(payload, id)\n if (result.outcome === 'invalid')\n return { outcome: 'rejected', error: new WebhookVerificationError('invalid_payload') }\n return result\n }\n}\n"],"mappings":"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"}
@@ -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-B45Dr5gs.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 { st as ErrorTable } from "./index-B5O6mNjZ.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 { $ as AppError, L as ValidationError, U as AuthenticationError, V as StaleSessionError, a as createLogger } from "./core-BiqdOMwU.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");
@@ -1,5 +1,5 @@
1
- import { G as DomainEvent } from "../index-B5O6mNjZ.js";
2
- import { A as IOutboxRepository } from "../index-DG4jkwad.js";
1
+ import { G as DomainEvent } from "../index-G1QG0P5g.js";
2
+ import { A as IOutboxRepository } from "../index-B45Dr5gs.js";
3
3
  //#region src/infrastructure/testing/outbox-repository-contract.d.ts
4
4
  /** Stored fields observed by the adapter contract, independent of a database library. */
5
5
  export type OutboxContractRow = {
@@ -1,4 +1,4 @@
1
- import { Wt as AppError } from "./index-B5O6mNjZ.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-CyBoERFz.d.ts.map
29
+ //# sourceMappingURL=webhook-verification-D7DIhH6C.d.ts.map