better-ship 0.36.0 → 0.37.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 (36) hide show
  1. package/dist/application.d.ts +2 -2
  2. package/dist/application.js +2 -1
  3. package/dist/browser-user.d.ts +14 -0
  4. package/dist/browser-user.d.ts.map +1 -0
  5. package/dist/browser-user.js +25 -0
  6. package/dist/browser-user.js.map +1 -0
  7. package/dist/cloudflare/queue.d.ts +1 -1
  8. package/dist/cloudflare.d.ts +2 -2
  9. package/dist/{index-CbvNd665.d.ts → index-BosM_da2.d.ts} +8 -2
  10. package/dist/index-BosM_da2.d.ts.map +1 -0
  11. package/dist/{metrics-DXhrc6ii.d.ts → metrics-B89jvpZQ.d.ts} +2 -2
  12. package/dist/{metrics-DXhrc6ii.d.ts.map → metrics-B89jvpZQ.d.ts.map} +1 -1
  13. package/dist/{outbox-repository-options-gk3JOjLB.d.ts → outbox-repository-options-A2fA1UDX.d.ts} +2 -2
  14. package/dist/{outbox-repository-options-gk3JOjLB.d.ts.map → outbox-repository-options-A2fA1UDX.d.ts.map} +1 -1
  15. package/dist/postgres.d.ts +2 -2
  16. package/dist/posthog/metrics.d.ts +1 -1
  17. package/dist/posthog/server.d.ts +2 -2
  18. package/dist/request-user-B4rh1c2-.js +19 -0
  19. package/dist/request-user-B4rh1c2-.js.map +1 -0
  20. package/dist/resend.d.ts +1 -1
  21. package/dist/sentry/server.d.ts +5 -2
  22. package/dist/sentry/server.d.ts.map +1 -1
  23. package/dist/sentry/server.js +9 -1
  24. package/dist/sentry/server.js.map +1 -1
  25. package/dist/tanstack.d.ts +1 -1
  26. package/dist/tanstack.d.ts.map +1 -1
  27. package/dist/tanstack.js +3 -1
  28. package/dist/tanstack.js.map +1 -1
  29. package/dist/testing/outbox-repository.d.ts +1 -1
  30. package/package.json +2 -1
  31. package/src/application/index.ts +1 -0
  32. package/src/application/request-user.ts +18 -0
  33. package/src/infrastructure/observability/browser-user.ts +24 -0
  34. package/src/infrastructure/sentry/server.ts +11 -1
  35. package/src/infrastructure/tanstack/auth.middleware.ts +3 -1
  36. package/dist/index-CbvNd665.d.ts.map +0 -1
@@ -1,3 +1,3 @@
1
1
  import { G as DomainMessage, K as DomainQuery, U as DomainCommand, W as DomainEvent, q as MessageId } from "./index-DUFS0SJ_.js";
2
- import { $ as AnalyticsService, A as IOutboxRepository, B as OutboundEmail, C as UnitOfWorkDeps, D as OutboxRelay, E as IOutboxRelay, F as IEventCollector, G as ObservabilityScope, H as ReceivedContent, I as EmailAccepted, J as MetricAttributeValue, K as ObservabilityService, L as EmailAttachment, M as OutboxClaim, N as OutboxMessage, O as OutboxRelayOptions, P as IMessageStore, Q as NULL_METRICS_SERVICE, R as IEmailService, S as UnitOfWork, T as IOutboxPublisher, U as ReceivedEmail, V as ReceivedAttachment, W as RecordingEmailService, X as MetricOptions, Y as MetricAttributes, Z as MetricsService, _ as RecordEmailEventCommand, a as OutboxRetentionResult, b as recordEmailEvent, c as CommandRegistry, d as MessageRegistry, et as NULL_ANALYTICS, f as MessageResult, g as IEmailDeliverabilityStore, h as EmailBounceRecord, i as OutboxRetentionOptions, j as IOutboxRetentionRepository, k as DeadLetterReason, l as EventRegistry, m as RegistryMessage, n as MessageAlreadyProcessedError, nt as EventHandler, o as IMessageBus, p as QueryRegistry, q as ObservabilityUser, r as OutboxRetention, rt as QueryHandler, s as MessageBus, t as UnitOfWorkAlreadyActiveError, tt as CommandHandler, u as Handlers, v as RecordEmailEventDeps, w as ITransaction, x as IUnitOfWork, y as RecordEmailEventRepos, z as MimeType } from "./index-CbvNd665.js";
3
- export { type AnalyticsService, type CommandHandler, type CommandRegistry, type DeadLetterReason, type DomainCommand, type DomainEvent, type DomainMessage, type DomainQuery, type EmailAccepted, type EmailAttachment, type EmailBounceRecord, type EventHandler, type EventRegistry, type Handlers, type IEmailDeliverabilityStore, type IEmailService, type IEventCollector, type IMessageBus, type IMessageStore, type IOutboxPublisher, type IOutboxRelay, type IOutboxRepository, type IOutboxRetentionRepository, type ITransaction, type IUnitOfWork, MessageAlreadyProcessedError, MessageBus, type MessageId, type MessageRegistry, type MessageResult, type MetricAttributeValue, type MetricAttributes, type MetricOptions, type MetricsService, type MimeType, NULL_ANALYTICS, NULL_METRICS_SERVICE, type ObservabilityScope, type ObservabilityService, type ObservabilityUser, type OutboundEmail, type OutboxClaim, type OutboxMessage, OutboxRelay, type OutboxRelayOptions, OutboxRetention, type OutboxRetentionOptions, type OutboxRetentionResult, type QueryHandler, type QueryRegistry, type ReceivedAttachment, type ReceivedContent, type ReceivedEmail, type RecordEmailEventCommand, type RecordEmailEventDeps, type RecordEmailEventRepos, RecordingEmailService, type RegistryMessage, UnitOfWork, UnitOfWorkAlreadyActiveError, type UnitOfWorkDeps, recordEmailEvent };
2
+ import { $ as MetricsService, A as IOutboxRepository, B as OutboundEmail, C as UnitOfWorkDeps, D as OutboxRelay, E as IOutboxRelay, F as IEventCollector, G as applyObservabilityUser, H as ReceivedContent, I as EmailAccepted, J as ObservabilityService, K as setObservabilityUserHook, L as EmailAttachment, M as OutboxClaim, N as OutboxMessage, O as OutboxRelayOptions, P as IMessageStore, Q as MetricOptions, R as IEmailService, S as UnitOfWork, T as IOutboxPublisher, U as ReceivedEmail, V as ReceivedAttachment, W as RecordingEmailService, X as MetricAttributeValue, Y as ObservabilityUser, Z as MetricAttributes, _ as RecordEmailEventCommand, a as OutboxRetentionResult, at as QueryHandler, b as recordEmailEvent, c as CommandRegistry, d as MessageRegistry, et as NULL_METRICS_SERVICE, f as MessageResult, g as IEmailDeliverabilityStore, h as EmailBounceRecord, i as OutboxRetentionOptions, it as EventHandler, j as IOutboxRetentionRepository, k as DeadLetterReason, l as EventRegistry, m as RegistryMessage, n as MessageAlreadyProcessedError, nt as NULL_ANALYTICS, o as IMessageBus, p as QueryRegistry, q as ObservabilityScope, r as OutboxRetention, rt as CommandHandler, s as MessageBus, t as UnitOfWorkAlreadyActiveError, tt as AnalyticsService, u as Handlers, v as RecordEmailEventDeps, w as ITransaction, x as IUnitOfWork, y as RecordEmailEventRepos, z as MimeType } from "./index-BosM_da2.js";
3
+ export { type AnalyticsService, type CommandHandler, type CommandRegistry, type DeadLetterReason, type DomainCommand, type DomainEvent, type DomainMessage, type DomainQuery, type EmailAccepted, type EmailAttachment, type EmailBounceRecord, type EventHandler, type EventRegistry, type Handlers, type IEmailDeliverabilityStore, type IEmailService, type IEventCollector, type IMessageBus, type IMessageStore, type IOutboxPublisher, type IOutboxRelay, type IOutboxRepository, type IOutboxRetentionRepository, type ITransaction, type IUnitOfWork, MessageAlreadyProcessedError, MessageBus, type MessageId, type MessageRegistry, type MessageResult, type MetricAttributeValue, type MetricAttributes, type MetricOptions, type MetricsService, type MimeType, NULL_ANALYTICS, NULL_METRICS_SERVICE, type ObservabilityScope, type ObservabilityService, type ObservabilityUser, type OutboundEmail, type OutboxClaim, type OutboxMessage, OutboxRelay, type OutboxRelayOptions, OutboxRetention, type OutboxRetentionOptions, type OutboxRetentionResult, type QueryHandler, type QueryRegistry, type ReceivedAttachment, type ReceivedContent, type ReceivedEmail, type RecordEmailEventCommand, type RecordEmailEventDeps, type RecordEmailEventRepos, RecordingEmailService, type RegistryMessage, UnitOfWork, UnitOfWorkAlreadyActiveError, type UnitOfWorkDeps, applyObservabilityUser, recordEmailEvent, setObservabilityUserHook };
@@ -1,3 +1,4 @@
1
1
  import { a as MessageBus, c as RecordingEmailService, i as OutboxRelay, l as NULL_METRICS_SERVICE, n as UnitOfWork, o as MessageAlreadyProcessedError, r as OutboxRetention, s as recordEmailEvent, t as UnitOfWorkAlreadyActiveError, u as NULL_ANALYTICS } from "./application-B5hnNUdI.js";
2
+ import { n as setObservabilityUserHook, t as applyObservabilityUser } from "./request-user-B4rh1c2-.js";
2
3
 
3
- export { MessageAlreadyProcessedError, MessageBus, NULL_ANALYTICS, NULL_METRICS_SERVICE, OutboxRelay, OutboxRetention, RecordingEmailService, UnitOfWork, UnitOfWorkAlreadyActiveError, recordEmailEvent };
4
+ export { MessageAlreadyProcessedError, MessageBus, NULL_ANALYTICS, NULL_METRICS_SERVICE, OutboxRelay, OutboxRetention, RecordingEmailService, UnitOfWork, UnitOfWorkAlreadyActiveError, applyObservabilityUser, recordEmailEvent, setObservabilityUserHook };
@@ -0,0 +1,14 @@
1
+ //#region src/infrastructure/observability/browser-user.d.ts
2
+ /**
3
+ * Identify the signed-in user in PostHog and Sentry.
4
+ * Sends the id only. A client that is not initialized does nothing.
5
+ * Call it when the session user id is present. A loading session does not call it.
6
+ */
7
+ export declare function identifyObservabilityUser(userId: string): void;
8
+ /**
9
+ * Clear the Sentry user and reset PostHog on sign-out.
10
+ * A disabled client does nothing. Call this once from the application's sign-out path.
11
+ */
12
+ export declare function clearObservabilityUser(): void;
13
+ //#endregion
14
+ //# sourceMappingURL=browser-user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-user.d.ts","names":[],"sources":["../src/infrastructure/observability/browser-user.ts"],"mappings":";;;;;;wBAQgB,0BAA0B;;;;;wBAW1B"}
@@ -0,0 +1,25 @@
1
+ import * as Sentry from "@sentry/tanstackstart-react";
2
+ import { posthog as posthog$1 } from "posthog-js";
3
+
4
+ //#region src/infrastructure/observability/browser-user.ts
5
+ /**
6
+ * Identify the signed-in user in PostHog and Sentry.
7
+ * Sends the id only. A client that is not initialized does nothing.
8
+ * Call it when the session user id is present. A loading session does not call it.
9
+ */
10
+ function identifyObservabilityUser(userId) {
11
+ if (posthog$1.__loaded) posthog$1.identify(userId);
12
+ if (Sentry.isEnabled()) Sentry.setUser({ id: userId });
13
+ }
14
+ /**
15
+ * Clear the Sentry user and reset PostHog on sign-out.
16
+ * A disabled client does nothing. Call this once from the application's sign-out path.
17
+ */
18
+ function clearObservabilityUser() {
19
+ if (posthog$1.__loaded) posthog$1.reset();
20
+ if (Sentry.isEnabled()) Sentry.setUser(null);
21
+ }
22
+
23
+ //#endregion
24
+ export { clearObservabilityUser, identifyObservabilityUser };
25
+ //# sourceMappingURL=browser-user.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-user.js","names":["posthog"],"sources":["../src/infrastructure/observability/browser-user.ts"],"sourcesContent":["import * as Sentry from '@sentry/tanstackstart-react'\nimport { posthog } from 'posthog-js'\n\n/**\n * Identify the signed-in user in PostHog and Sentry.\n * Sends the id only. A client that is not initialized does nothing.\n * Call it when the session user id is present. A loading session does not call it.\n */\nexport function identifyObservabilityUser(userId: string): void {\n // identify() warns when the SDK is not loaded. A disabled client must stay silent.\n // oxlint-disable-next-line no-underscore-dangle -- PostHog's public loaded flag. There is no other initialized check.\n if (posthog.__loaded) posthog.identify(userId)\n if (Sentry.isEnabled()) Sentry.setUser({ id: userId })\n}\n\n/**\n * Clear the Sentry user and reset PostHog on sign-out.\n * A disabled client does nothing. Call this once from the application's sign-out path.\n */\nexport function clearObservabilityUser(): void {\n // oxlint-disable-next-line no-underscore-dangle -- Same loaded flag as identifyObservabilityUser.\n if (posthog.__loaded) posthog.reset()\n if (Sentry.isEnabled()) Sentry.setUser(null)\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,0BAA0B,QAAsB;CAG9D,IAAIA,UAAQ,UAAU,UAAQ,SAAS,MAAM;CAC7C,IAAI,OAAO,UAAU,GAAG,OAAO,QAAQ,EAAE,IAAI,OAAO,CAAC;AACvD;;;;;AAMA,SAAgB,yBAA+B;CAE7C,IAAIA,UAAQ,UAAU,UAAQ,MAAM;CACpC,IAAI,OAAO,UAAU,GAAG,OAAO,QAAQ,IAAI;AAC7C"}
@@ -1,5 +1,5 @@
1
1
  import { U as DomainCommand, W as DomainEvent, Xt as AppError, Zt as FailureClassification } from "../index-DUFS0SJ_.js";
2
- import { D as OutboxRelay, E as IOutboxRelay } from "../index-CbvNd665.js";
2
+ import { D as OutboxRelay, E as IOutboxRelay } from "../index-BosM_da2.js";
3
3
  import { z } from "zod";
4
4
  //#region src/infrastructure/cloudflare/queue/queue-message.d.ts
5
5
  /** A message a queue carries: a command from outside the request, or an event from the outbox. */
@@ -1,6 +1,6 @@
1
1
  import { W as DomainEvent, et as D1ConstraintKind, nt as DatabaseFailure, q as MessageId, tt as DatabaseError } from "./index-DUFS0SJ_.js";
2
- import { A as IOutboxRepository, M as OutboxClaim, N as OutboxMessage, P as IMessageStore, k as DeadLetterReason, w as ITransaction } from "./index-CbvNd665.js";
3
- import { n as eventCollector, r as DrizzleSchema, t as OutboxRepositoryOptions } from "./outbox-repository-options-gk3JOjLB.js";
2
+ import { A as IOutboxRepository, M as OutboxClaim, N as OutboxMessage, P as IMessageStore, k as DeadLetterReason, w as ITransaction } from "./index-BosM_da2.js";
3
+ import { n as eventCollector, r as DrizzleSchema, t as OutboxRepositoryOptions } from "./outbox-repository-options-A2fA1UDX.js";
4
4
  import { BatchItem } from "drizzle-orm/batch";
5
5
  import { AnyD1Database, DrizzleD1Database } from "drizzle-orm/d1";
6
6
  //#region src/infrastructure/cloudflare/d1-database-types.d.ts
@@ -66,6 +66,12 @@ interface ObservabilityService extends ObservabilityScope {
66
66
  withIsolationScope<T>(callback: (scope: ObservabilityScope) => T | Promise<T>): Promise<T>;
67
67
  }
68
68
  //#endregion
69
+ //#region src/application/request-user.d.ts
70
+ /** Set or clear the process-wide request user hook. */
71
+ declare function setObservabilityUserHook(hook: ((user: ObservabilityUser) => void) | null): void;
72
+ /** Attribute the current execution to this user. No hook means the call does nothing. */
73
+ declare function applyObservabilityUser(user: ObservabilityUser): void;
74
+ //#endregion
69
75
  //#region src/application/email-service.d.ts
70
76
  /** A media type such as `application/pdf`. */
71
77
  type MimeType = `${string}/${string}`;
@@ -409,5 +415,5 @@ declare class UnitOfWorkAlreadyActiveError extends AppError {
409
415
  constructor();
410
416
  }
411
417
  //#endregion
412
- export { AnalyticsService as $, IOutboxRepository as A, OutboundEmail as B, UnitOfWorkDeps as C, OutboxRelay as D, IOutboxRelay as E, IEventCollector as F, ObservabilityScope as G, ReceivedContent as H, EmailAccepted as I, MetricAttributeValue as J, ObservabilityService as K, EmailAttachment as L, OutboxClaim as M, OutboxMessage as N, OutboxRelayOptions as O, IMessageStore as P, NULL_METRICS_SERVICE as Q, IEmailService as R, UnitOfWork as S, IOutboxPublisher as T, ReceivedEmail as U, ReceivedAttachment as V, RecordingEmailService as W, MetricOptions as X, MetricAttributes as Y, MetricsService as Z, RecordEmailEventCommand as _, OutboxRetentionResult as a, recordEmailEvent as b, CommandRegistry as c, MessageRegistry as d, NULL_ANALYTICS as et, MessageResult as f, IEmailDeliverabilityStore as g, EmailBounceRecord as h, OutboxRetentionOptions as i, IOutboxRetentionRepository as j, DeadLetterReason as k, EventRegistry as l, RegistryMessage as m, MessageAlreadyProcessedError as n, EventHandler as nt, IMessageBus as o, QueryRegistry as p, ObservabilityUser as q, OutboxRetention as r, QueryHandler as rt, MessageBus as s, UnitOfWorkAlreadyActiveError as t, CommandHandler as tt, Handlers as u, RecordEmailEventDeps as v, ITransaction as w, IUnitOfWork as x, RecordEmailEventRepos as y, MimeType as z };
413
- //# sourceMappingURL=index-CbvNd665.d.ts.map
418
+ export { MetricsService as $, IOutboxRepository as A, OutboundEmail as B, UnitOfWorkDeps as C, OutboxRelay as D, IOutboxRelay as E, IEventCollector as F, applyObservabilityUser as G, ReceivedContent as H, EmailAccepted as I, ObservabilityService as J, setObservabilityUserHook as K, EmailAttachment as L, OutboxClaim as M, OutboxMessage as N, OutboxRelayOptions as O, IMessageStore as P, MetricOptions as Q, IEmailService as R, UnitOfWork as S, IOutboxPublisher as T, ReceivedEmail as U, ReceivedAttachment as V, RecordingEmailService as W, MetricAttributeValue as X, ObservabilityUser as Y, MetricAttributes as Z, RecordEmailEventCommand as _, OutboxRetentionResult as a, QueryHandler as at, recordEmailEvent as b, CommandRegistry as c, MessageRegistry as d, NULL_METRICS_SERVICE as et, MessageResult as f, IEmailDeliverabilityStore as g, EmailBounceRecord as h, OutboxRetentionOptions as i, EventHandler as it, IOutboxRetentionRepository as j, DeadLetterReason as k, EventRegistry as l, RegistryMessage as m, MessageAlreadyProcessedError as n, NULL_ANALYTICS as nt, IMessageBus as o, QueryRegistry as p, ObservabilityScope as q, OutboxRetention as r, CommandHandler as rt, MessageBus as s, UnitOfWorkAlreadyActiveError as t, AnalyticsService as tt, Handlers as u, RecordEmailEventDeps as v, ITransaction as w, IUnitOfWork as x, RecordEmailEventRepos as y, MimeType as z };
419
+ //# sourceMappingURL=index-BosM_da2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-BosM_da2.d.ts","names":[],"sources":["../src/application/handlers.ts","../src/application/analytics-service.ts","../src/application/metrics-service.ts","../src/application/observability-service.ts","../src/application/request-user.ts","../src/application/email-service.ts","../src/application/event-collector.ts","../src/application/message-store.ts","../src/application/outbox-repository.ts","../src/application/outbox-relay.ts","../src/application/transaction.ts","../src/application/unit-of-work.ts","../src/application/record-email-event.ts","../src/application/registry.ts","../src/application/message-bus.ts","../src/application/outbox-retention.ts","../src/application/message-already-processed.error.ts","../src/application/unit-of-work.errors.ts"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { X as MetricOptions, Z as MetricsService } from "./index-CbvNd665.js";
1
+ import { $ as MetricsService, Q as MetricOptions } from "./index-BosM_da2.js";
2
2
  import { PostHog } from "posthog-node";
3
3
  //#region src/infrastructure/posthog/metrics.d.ts
4
4
  /** PostHog Metrics with Worker lifetime management, ported from Typist's metrics-service.ts. */
@@ -16,4 +16,4 @@ declare class PostHogMetricsService implements MetricsService {
16
16
  }
17
17
  //#endregion
18
18
  export { PostHogMetricsService as t };
19
- //# sourceMappingURL=metrics-DXhrc6ii.d.ts.map
19
+ //# sourceMappingURL=metrics-B89jvpZQ.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metrics-DXhrc6ii.d.ts","names":[],"sources":["../src/infrastructure/posthog/metrics.ts"],"mappings":";;;;cAKa,iCAAiC;mBAGzB;mBACA;;EAFnB,YACmB,QAAQ,KAAK,qBACb,YAAY,SAAS;;EAIxC,MAAM,cAAc,gBAAW,UAAU;;EAMzC,MAAM,cAAc,eAAe,UAAU;;EAM7C,UAAU,cAAc,eAAe,UAAU"}
1
+ {"version":3,"file":"metrics-B89jvpZQ.d.ts","names":[],"sources":["../src/infrastructure/posthog/metrics.ts"],"mappings":";;;;cAKa,iCAAiC;mBAGzB;mBACA;;EAFnB,YACmB,QAAQ,KAAK,qBACb,YAAY,SAAS;;EAIxC,MAAM,cAAc,gBAAW,UAAU;;EAMzC,MAAM,cAAc,eAAe,UAAU;;EAM7C,UAAU,cAAc,eAAe,UAAU"}
@@ -1,5 +1,5 @@
1
1
  import "./index-DUFS0SJ_.js";
2
- import { F as IEventCollector } from "./index-CbvNd665.js";
2
+ import { F as IEventCollector } from "./index-BosM_da2.js";
3
3
  import { BatchItem } from "drizzle-orm/batch";
4
4
  //#region src/infrastructure/drizzle-schema.d.ts
5
5
  /**
@@ -23,4 +23,4 @@ declare const OUTBOX_REPOSITORY_DEFAULTS: {
23
23
  type OutboxRepositoryOptions = Readonly<Partial<typeof OUTBOX_REPOSITORY_DEFAULTS>>;
24
24
  //#endregion
25
25
  export { eventCollector as n, DrizzleSchema as r, OutboxRepositoryOptions as t };
26
- //# sourceMappingURL=outbox-repository-options-gk3JOjLB.d.ts.map
26
+ //# sourceMappingURL=outbox-repository-options-A2fA1UDX.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"outbox-repository-options-gk3JOjLB.d.ts","names":[],"sources":["../src/infrastructure/drizzle-schema.ts","../src/infrastructure/unit-of-work-context.ts","../src/infrastructure/outbox-repository-options.ts"],"mappings":";;;;;;;;KAKY,gBAAgB;;;;cCuBf,gBAAgB;;;;cC3BhB;EACX;EACA;EACA;;;KAIU,0BAA0B,SAAS,eAAe"}
1
+ {"version":3,"file":"outbox-repository-options-A2fA1UDX.d.ts","names":[],"sources":["../src/infrastructure/drizzle-schema.ts","../src/infrastructure/unit-of-work-context.ts","../src/infrastructure/outbox-repository-options.ts"],"mappings":";;;;;;;;KAKY,gBAAgB;;;;cCuBf,gBAAgB;;;;cC3BhB;EACX;EACA;EACA;;;KAIU,0BAA0B,SAAS,eAAe"}
@@ -1,6 +1,6 @@
1
1
  import { W as DomainEvent, Xt as AppError, nt as DatabaseFailure, q as MessageId, tt as DatabaseError } from "./index-DUFS0SJ_.js";
2
- import { A as IOutboxRepository, M as OutboxClaim, N as OutboxMessage, P as IMessageStore, k as DeadLetterReason, w as ITransaction } from "./index-CbvNd665.js";
3
- import { n as eventCollector, r as DrizzleSchema, t as OutboxRepositoryOptions } from "./outbox-repository-options-gk3JOjLB.js";
2
+ import { A as IOutboxRepository, M as OutboxClaim, N as OutboxMessage, P as IMessageStore, k as DeadLetterReason, w as ITransaction } from "./index-BosM_da2.js";
3
+ import { n as eventCollector, r as DrizzleSchema, t as OutboxRepositoryOptions } from "./outbox-repository-options-A2fA1UDX.js";
4
4
  import { ExtractTablesWithRelations } from "drizzle-orm";
5
5
  import { NodePgDatabase, NodePgTransaction } from "drizzle-orm/node-postgres";
6
6
  import { Pool } from "pg";
@@ -1,2 +1,2 @@
1
- import { t as PostHogMetricsService } from "../metrics-DXhrc6ii.js";
1
+ import { t as PostHogMetricsService } from "../metrics-B89jvpZQ.js";
2
2
  export { PostHogMetricsService };
@@ -1,6 +1,6 @@
1
1
  import { Gt as AnalyticsEventIdentity, Kt as AnalyticsEventInput, qt as AnalyticsProperties } from "../index-DUFS0SJ_.js";
2
- import { $ as AnalyticsService } from "../index-CbvNd665.js";
3
- import { t as PostHogMetricsService } from "../metrics-DXhrc6ii.js";
2
+ import { tt as AnalyticsService } from "../index-BosM_da2.js";
3
+ import { t as PostHogMetricsService } from "../metrics-B89jvpZQ.js";
4
4
  import { PostHog, PostHogOptions } from "posthog-node";
5
5
  //#region src/infrastructure/posthog/server.d.ts
6
6
  /** Deployment values and SDK options; the runtime owns delivery promises. */
@@ -0,0 +1,19 @@
1
+ //#region src/application/request-user.ts
2
+ /**
3
+ * Process-wide request user hook.
4
+ * The Sentry server entry sets it once. Auth middleware calls it after a session resolves.
5
+ * The hook does not store a user id. The adapter writes the id on the current isolation scope.
6
+ */
7
+ let observabilityUserHook = null;
8
+ /** Set or clear the process-wide request user hook. */
9
+ function setObservabilityUserHook(hook) {
10
+ observabilityUserHook = hook;
11
+ }
12
+ /** Attribute the current execution to this user. No hook means the call does nothing. */
13
+ function applyObservabilityUser(user) {
14
+ observabilityUserHook?.(user);
15
+ }
16
+
17
+ //#endregion
18
+ export { setObservabilityUserHook as n, applyObservabilityUser as t };
19
+ //# sourceMappingURL=request-user-B4rh1c2-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-user-B4rh1c2-.js","names":[],"sources":["../src/application/request-user.ts"],"sourcesContent":["import type { ObservabilityUser } from './observability-service.ts'\n\n/**\n * Process-wide request user hook.\n * The Sentry server entry sets it once. Auth middleware calls it after a session resolves.\n * The hook does not store a user id. The adapter writes the id on the current isolation scope.\n */\nlet observabilityUserHook: ((user: ObservabilityUser) => void) | null = null\n\n/** Set or clear the process-wide request user hook. */\nexport function setObservabilityUserHook(hook: ((user: ObservabilityUser) => void) | null): void {\n observabilityUserHook = hook\n}\n\n/** Attribute the current execution to this user. No hook means the call does nothing. */\nexport function applyObservabilityUser(user: ObservabilityUser): void {\n observabilityUserHook?.(user)\n}\n"],"mappings":";;;;;;AAOA,IAAI,wBAAoE;;AAGxE,SAAgB,yBAAyB,MAAwD;CAC/F,wBAAwB;AAC1B;;AAGA,SAAgB,uBAAuB,MAA+B;CACpE,wBAAwB,IAAI;AAC9B"}
package/dist/resend.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jt as BounceType } from "./index-DUFS0SJ_.js";
2
- import { B as OutboundEmail, H as ReceivedContent, I as EmailAccepted, R as IEmailService, U as ReceivedEmail } from "./index-CbvNd665.js";
2
+ import { B as OutboundEmail, H as ReceivedContent, I as EmailAccepted, R as IEmailService, U as ReceivedEmail } from "./index-BosM_da2.js";
3
3
  import { n as WebhookVerifier } from "./webhook-verification-D1c7BY4q.js";
4
4
  import "resend";
5
5
  //#region src/infrastructure/resend/resend-email-service.d.ts
@@ -1,5 +1,5 @@
1
1
  import { u as LoggerErrorHook } from "../index-DUFS0SJ_.js";
2
- import { G as ObservabilityScope, K as ObservabilityService, q as ObservabilityUser } from "../index-CbvNd665.js";
2
+ import { J as ObservabilityService, Y as ObservabilityUser, q as ObservabilityScope } from "../index-BosM_da2.js";
3
3
  import * as Sentry from "@sentry/cloudflare";
4
4
  //#region src/infrastructure/sentry/server.d.ts
5
5
  /** Application-owned deployment settings with the full Cloudflare SDK option contract. */
@@ -8,7 +8,10 @@ export type SentryServerOptions = Sentry.CloudflareOptions & {
8
8
  readonly environment: string;
9
9
  readonly enabled: boolean;
10
10
  };
11
- /** Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults. */
11
+ /**
12
+ * Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults.
13
+ * Registers the request user hook so auth middleware can set `{ id }` on the current isolation scope.
14
+ */
12
15
  export declare function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions;
13
16
  /** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */
14
17
  export declare function createSentryLoggerErrorHook(): LoggerErrorHook;
@@ -1 +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;;wBAK1D,+BAA+B;;qBAWlC,sCAAsC;;EAEjD,QAAQ,MAAM;;EAKR,mBAAmB,GAAG,WAAW,OAAO,uBAAuB,IAAI,QAAQ,KAAK,QAAQ"}
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/infrastructure/sentry/server.ts"],"mappings":";;;;;YAQY,sBAAsB,OAAO;WAC9B;WACA;WACA;;;;;;wBAOK,oBAAoB,SAAS,sBAAsB,OAAO;;wBAU1D,+BAA+B;;qBAWlC,sCAAsC;;EAEjD,QAAQ,MAAM;;EAKR,mBAAmB,GAAG,WAAW,OAAO,uBAAuB,IAAI,QAAQ,KAAK,QAAQ"}
@@ -1,8 +1,16 @@
1
+ import { n as setObservabilityUserHook } from "../request-user-B4rh1c2-.js";
1
2
  import * as Sentry from "@sentry/cloudflare";
2
3
 
3
4
  //#region src/infrastructure/sentry/server.ts
4
- /** Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults. */
5
+ /**
6
+ * Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults.
7
+ * Registers the request user hook so auth middleware can set `{ id }` on the current isolation scope.
8
+ */
5
9
  function createSentryOptions(options) {
10
+ setObservabilityUserHook((user) => {
11
+ if (!Sentry.isEnabled()) return;
12
+ Sentry.setUser({ id: user.id });
13
+ });
6
14
  return options;
7
15
  }
8
16
  /** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */
@@ -1 +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/** Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults. */\nexport function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions {\n return options\n}\n\n/** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */\nexport function createSentryLoggerErrorHook(): LoggerErrorHook {\n return ({ error, userId, attributes }) => {\n if (userId !== undefined) {\n Sentry.captureException(error, { extra: attributes, user: { id: userId } })\n return\n }\n Sentry.captureException(error, { extra: attributes })\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({ id: user.id })\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((scope) =>\n callback({ setUser: (user) => scope.setUser({ id: user.id }) }),\n )\n }\n}\n\nexport { Sentry }\n"],"mappings":";;;;AAaA,SAAgB,oBAAoB,SAAwD;CAC1F,OAAO;AACT;;AAGA,SAAgB,8BAA+C;CAC7D,QAAQ,EAAE,OAAO,QAAQ,iBAAiB;EACxC,IAAI,WAAW,QAAW;GACxB,OAAO,iBAAiB,OAAO;IAAE,OAAO;IAAY,MAAM,EAAE,IAAI,OAAO;GAAE,CAAC;GAC1E;EACF;EACA,OAAO,iBAAiB,OAAO,EAAE,OAAO,WAAW,CAAC;CACtD;AACF;;AAGA,IAAa,6BAAb,MAAwE;;CAEtE,QAAQ,MAA+B;EACrC,OAAO,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC;CAChC;;CAGA,MAAM,mBAAsB,UAAqE;EAC/F,OAAO,MAAM,OAAO,oBAAoB,UACtC,SAAS,EAAE,UAAU,SAAS,MAAM,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC,CAChE;CACF;AACF"}
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\nimport { setObservabilityUserHook } from '../../application/request-user.ts'\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/**\n * Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults.\n * Registers the request user hook so auth middleware can set `{ id }` on the current isolation scope.\n */\nexport function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions {\n // Auth middleware cannot import this SDK. The hook writes `{ id }` only when Sentry is enabled.\n setObservabilityUserHook((user) => {\n if (!Sentry.isEnabled()) return\n Sentry.setUser({ id: user.id })\n })\n return options\n}\n\n/** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */\nexport function createSentryLoggerErrorHook(): LoggerErrorHook {\n return ({ error, userId, attributes }) => {\n if (userId !== undefined) {\n Sentry.captureException(error, { extra: attributes, user: { id: userId } })\n return\n }\n Sentry.captureException(error, { extra: attributes })\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({ id: user.id })\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((scope) =>\n callback({ setUser: (user) => scope.setUser({ id: user.id }) }),\n )\n }\n}\n\nexport { Sentry }\n"],"mappings":";;;;;;;;AAkBA,SAAgB,oBAAoB,SAAwD;CAE1F,0BAA0B,SAAS;EACjC,IAAI,CAAC,OAAO,UAAU,GAAG;EACzB,OAAO,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC;CAChC,CAAC;CACD,OAAO;AACT;;AAGA,SAAgB,8BAA+C;CAC7D,QAAQ,EAAE,OAAO,QAAQ,iBAAiB;EACxC,IAAI,WAAW,QAAW;GACxB,OAAO,iBAAiB,OAAO;IAAE,OAAO;IAAY,MAAM,EAAE,IAAI,OAAO;GAAE,CAAC;GAC1E;EACF;EACA,OAAO,iBAAiB,OAAO,EAAE,OAAO,WAAW,CAAC;CACtD;AACF;;AAGA,IAAa,6BAAb,MAAwE;;CAEtE,QAAQ,MAA+B;EACrC,OAAO,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC;CAChC;;CAGA,MAAM,mBAAsB,UAAqE;EAC/F,OAAO,MAAM,OAAO,oBAAoB,UACtC,SAAS,EAAE,UAAU,SAAS,MAAM,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC,CAChE;CACF;AACF"}
@@ -55,7 +55,7 @@ type AuthMiddlewareOptions<Session extends AuthSession> = {
55
55
  /**
56
56
  * Preserve Porte's function and request middleware contracts with application-owned services.
57
57
  * Optional lookup failures log once and continue as guests. Required lookup failures propagate.
58
- * `onAuthenticated` runs after a session resolves and before the rest of the chain.
58
+ * A resolved session applies `{ id }` on the observability hook, then runs `onAuthenticated`, before the handler.
59
59
  */
60
60
  export declare function createAuthMiddleware<Session extends AuthSession>(options: AuthMiddlewareOptions<Session>): {
61
61
  extractAuth: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, AuthContext<Session>, undefined, undefined, undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack.d.ts","names":[],"sources":["../src/infrastructure/tanstack/request-context.ts","../src/infrastructure/tanstack/auth.middleware.ts","../src/infrastructure/tanstack/error.middleware.ts","../src/infrastructure/tanstack/function-tracing.middleware.ts"],"mappings":";;;;;;;;;;KAQY,iBAAiB;EAAmB;IAAU,sBAAsB;;IAC5E;;;;KCCQ;WACD;aAAiB;;WACjB;aAAoB;;;;KAInB,oBAAoB,gBAAgB;WACrC,MAAM;WACN,SAAS;;;KAIR,YAAY,gBAAgB,eACpC,oBAAoB;WAET;WACA;;;KAIH,qBAAqB,gBAAgB;WACtC,MAAM;WACN,SAAS;WACT,gBAAgB;;;KAIf,sBAAsB,gBAAgB;WACvC,UAAU,SAAS;aACjB;eACE,aAAa;QAAW,SAAS;YAAc,QAAQ;;;;WAI3D,mBAAmB,SAAS,qBAAqB;;;;;;;wBAc5C,qBAAqB,gBAAgB,aACnD,SAAS,sBAAsB;;;;;;;;KC/CrB;WACD,UAAU;aAAmB,aAAa;;;;KAIzC,uBAAuB;WACxB,QAAQ,WAAW;;WAEnB;;WAEA,UAAU,SAAS,mBAAmB;;;;;;wBAOjC,sBAAsB,SAAS,SAAS,uBAAuB;;;;;;;;;;;;;;;;;;;;;wBCnB/D,gCAAgC,aAAa,SAAS,mBAAmB,0CAAO"}
1
+ {"version":3,"file":"tanstack.d.ts","names":[],"sources":["../src/infrastructure/tanstack/request-context.ts","../src/infrastructure/tanstack/auth.middleware.ts","../src/infrastructure/tanstack/error.middleware.ts","../src/infrastructure/tanstack/function-tracing.middleware.ts"],"mappings":";;;;;;;;;;KAQY,iBAAiB;EAAmB;IAAU,sBAAsB;;IAC5E;;;;KCEQ;WACD;aAAiB;;WACjB;aAAoB;;;;KAInB,oBAAoB,gBAAgB;WACrC,MAAM;WACN,SAAS;;;KAIR,YAAY,gBAAgB,eACpC,oBAAoB;WAET;WACA;;;KAIH,qBAAqB,gBAAgB;WACtC,MAAM;WACN,SAAS;WACT,gBAAgB;;;KAIf,sBAAsB,gBAAgB;WACvC,UAAU,SAAS;aACjB;eACE,aAAa;QAAW,SAAS;YAAc,QAAQ;;;;WAI3D,mBAAmB,SAAS,qBAAqB;;;;;;;wBAc5C,qBAAqB,gBAAgB,aACnD,SAAS,sBAAsB;;;;;;;;KChDrB;WACD,UAAU;aAAmB,aAAa;;;;KAIzC,uBAAuB;WACxB,QAAQ,WAAW;;WAEnB;;WAEA,UAAU,SAAS,mBAAmB;;;;;;wBAOjC,sBAAsB,SAAS,SAAS,uBAAuB;;;;;;;;;;;;;;;;;;;;;wBCnB/D,gCAAgC,aAAa,SAAS,mBAAmB,0CAAO"}
package/dist/tanstack.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { H as StaleSessionError, R as ValidationError, W as AuthenticationError, nt as AppError, r as createLogger } from "./core-BiN6S_cm.js";
2
+ import { t as applyObservabilityUser } from "./request-user-B4rh1c2-.js";
2
3
  import { n as withSpan } from "./tracing-C_7B_odV.js";
3
4
  import { z } from "zod";
4
5
  import { getReasonPhrase } from "http-status-codes";
@@ -15,10 +16,11 @@ const requestHeaders = async () => {
15
16
  /**
16
17
  * Preserve Porte's function and request middleware contracts with application-owned services.
17
18
  * Optional lookup failures log once and continue as guests. Required lookup failures propagate.
18
- * `onAuthenticated` runs after a session resolves and before the rest of the chain.
19
+ * A resolved session applies `{ id }` on the observability hook, then runs `onAuthenticated`, before the handler.
19
20
  */
20
21
  function createAuthMiddleware(options) {
21
22
  function notifyAuthenticated(requestContext, resolved) {
23
+ applyObservabilityUser({ id: resolved.user.id });
22
24
  options.onAuthenticated?.({
23
25
  user: resolved.user,
24
26
  session: resolved.session,
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack.js","names":["logger"],"sources":["../src/infrastructure/tanstack/auth.middleware.ts","../src/infrastructure/tanstack/resolve-failure.ts","../src/infrastructure/tanstack/http-error-response.ts","../src/infrastructure/tanstack/session-cookies.ts","../src/infrastructure/tanstack/to-error-payload.ts","../src/infrastructure/tanstack/error.middleware.ts","../src/infrastructure/tanstack/function-tracing.middleware.ts"],"sourcesContent":["import { createMiddleware } from '@tanstack/react-start'\nimport { getSessionCookie } from 'better-auth/cookies'\n\nimport { AuthenticationError, StaleSessionError, createLogger } from '@/core'\n\nimport type { RequestContext } from './request-context.ts'\n\nconst logger = createLogger('auth-middleware')\n\n/** The fields middleware reads; generic callers retain every additional session and user field. */\nexport type AuthSession = {\n readonly user: { readonly id: string }\n readonly session: { readonly id: string }\n}\n\n/** Authenticated context retains the application's complete user and session types. */\nexport type RequiredAuthContext<Session extends AuthSession> = {\n readonly user: Session['user']\n readonly session: Session['session']\n}\n\n/** Optional authentication supplies both fields together or neither field. */\nexport type AuthContext<Session extends AuthSession> =\n | RequiredAuthContext<Session>\n | {\n readonly user: undefined\n readonly session: undefined\n }\n\n/** A resolved session and the request context that produced it. */\nexport type AuthenticatedContext<Session extends AuthSession> = {\n readonly user: Session['user']\n readonly session: Session['session']\n readonly requestContext: RequestContext\n}\n\n/** Select the current request's Better Auth instance from TanStack's registered context. */\nexport type AuthMiddlewareOptions<Session extends AuthSession> = {\n readonly getAuth: (context: RequestContext) => {\n readonly api: {\n readonly getSession: (options: { headers: Headers }) => Promise<Session | null>\n }\n }\n /** Runs after a session resolves and before the handler. A guest or a failed lookup does not call it. */\n readonly onAuthenticated?: (context: AuthenticatedContext<Session>) => void\n}\n\n// The server entry loads react-dom/server; the browser evaluates this module, so it loads lazily inside `.server()`.\nconst requestHeaders = async () => {\n const { getRequestHeaders } = await import('@tanstack/react-start/server')\n return getRequestHeaders()\n}\n\n/**\n * Preserve Porte's function and request middleware contracts with application-owned services.\n * Optional lookup failures log once and continue as guests. Required lookup failures propagate.\n * `onAuthenticated` runs after a session resolves and before the rest of the chain.\n */\nexport function createAuthMiddleware<Session extends AuthSession>(\n options: AuthMiddlewareOptions<Session>,\n) {\n function notifyAuthenticated(requestContext: RequestContext, resolved: Session): void {\n options.onAuthenticated?.({\n user: resolved.user,\n session: resolved.session,\n requestContext,\n })\n }\n const extractAuth = createMiddleware({ type: 'function' }).server<AuthContext<Session>>(\n async ({ next, context }) => {\n let resolved: Session | null\n try {\n const headers = await requestHeaders()\n resolved = await options.getAuth(context).api.getSession({ headers })\n } catch (error) {\n logger.error('get_session_failed', { error })\n resolved = null\n }\n if (resolved === null)\n return next<AuthContext<Session>>({ context: { user: undefined, session: undefined } })\n notifyAuthenticated(context, resolved)\n return next<AuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n },\n )\n\n async function resolveRequiredSession(\n context: RequestContext,\n headers: Headers,\n ): Promise<Session> {\n const resolved = await options.getAuth(context).api.getSession({ headers })\n if (resolved === null) {\n if (getSessionCookie(headers)) throw new StaleSessionError()\n throw new AuthenticationError()\n }\n return resolved\n }\n\n const requireAuth = createMiddleware({ type: 'function' }).server(async ({ next, context }) => {\n const resolved = await resolveRequiredSession(context, await requestHeaders())\n notifyAuthenticated(context, resolved)\n return next<RequiredAuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n })\n const requireAuthRequest = createMiddleware({ type: 'request' }).server(\n async ({ request, next, context }) => {\n const resolved = await resolveRequiredSession(context, request.headers)\n notifyAuthenticated(context, resolved)\n return next<RequiredAuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n },\n )\n return { extractAuth, requireAuth, requireAuthRequest }\n}\n","import { z } from 'zod'\n\nimport {\n AppError,\n ValidationError,\n createLogger,\n type ErrorTable,\n type ResolvedError,\n} from '@/core'\nimport type { ErrorPayloadOf } from '@/core'\n\nconst logger = createLogger('error-boundary')\n\n// TanStack serializes a validator's issues into `Error.message` as JSON.\nconst SerializedIssuesSchema = z\n .array(z.object({ message: z.string(), path: z.array(z.union([z.string(), z.number()])) }))\n .min(1)\n\n// A live ZodError keeps `.issues`. TanStack leaves only JSON in `Error.message`.\nfunction reconstructValidationError(cause: unknown): ValidationError | null {\n if (cause instanceof z.ZodError) return ValidationError.fromZod(cause)\n if (!(cause instanceof Error)) return null\n let message: unknown\n try {\n message = JSON.parse(cause.message)\n } catch {\n return null\n }\n const parsed = SerializedIssuesSchema.safeParse(message)\n return parsed.success ? ValidationError.fromIssues(parsed.data) : null\n}\n\n/**\n * Resolve one failure through the table or fall back to the generic replacement, and log it once.\n * A thrown ZodError, or issues serialized into an error message, becomes a ValidationError first.\n * Both transport converters call this, so one rule decides the log level: mapped is a warning, unmapped is an error with the cause.\n */\nexport function resolveFailure<Entries>(\n table: ErrorTable<Entries>,\n cause: unknown,\n): ResolvedError<ErrorPayloadOf<ErrorTable<Entries>>> {\n const failure = reconstructValidationError(cause) ?? cause\n const resolved = table.resolve(failure)\n if (resolved !== null) {\n logger.warn('handled_error', {\n tag: resolved.payload._tag,\n status: resolved.status,\n })\n return resolved\n }\n const classification = failure instanceof AppError ? failure.classification : 'unknown'\n logger.error('unexpected_error', { error: failure, classification })\n return table.fallback(failure)\n}\n","import { getReasonPhrase } from 'http-status-codes'\nimport { z } from 'zod'\n\nimport type { ErrorTable, ProblemDetails } from '@/core'\n\nimport { resolveFailure } from './resolve-failure.ts'\n\n// A known delay becomes the header; a null delay sends no header.\nconst RetryAfterSchema = z.object({ retryAfter: z.number() })\n\n/**\n * Build the API route converter for one error table.\n * The body is RFC 9457 problem details; public fields become extension members.\n */\nexport function createHttpErrorResponseConverter<Entries>(table: ErrorTable<Entries>) {\n return function toHttpErrorResponse(cause: unknown): Response {\n const { status, payload } = resolveFailure(table, cause)\n const { _tag, message, ...fields } = payload\n const body: ProblemDetails = {\n type: 'about:blank',\n title: getReasonPhrase(status),\n detail: message,\n ...fields,\n }\n const headers = new Headers({ 'Content-Type': 'application/problem+json' })\n const retry = RetryAfterSchema.safeParse(payload)\n if (retry.success) headers.set('Retry-After', String(retry.data.retryAfter))\n return Response.json(body, { status, headers })\n }\n}\n","import type { deleteCookie } from '@tanstack/react-start/server'\nimport type { BetterAuthCookies } from 'better-auth'\nimport { parseCookies } from 'better-auth/cookies'\n\ntype AuthCookie = BetterAuthCookies['sessionToken']\ntype DeleteCookieOptions = NonNullable<Parameters<typeof deleteCookie>[1]>\n\n/**\n * Delete the session, session cache, account cache, and remember-me cookies of one instance.\n * Chunks present on the request go too. Names and attributes come from the instance, so custom prefixes and domain cookies clear.\n */\nexport async function clearStaleSessionCookies(cookies: BetterAuthCookies): Promise<void> {\n // The server entry loads react-dom/server; the browser evaluates this module, so it loads lazily here.\n const server = await import('@tanstack/react-start/server')\n const present = [...parseCookies(server.getRequestHeaders().get('cookie') ?? '').keys()]\n const definitions = [\n cookies.sessionToken,\n cookies.sessionData,\n cookies.accountData,\n cookies.dontRememberToken,\n ]\n for (const cookie of definitions) {\n // Better Auth names a chunk `${name}.${index}`, so a missed chunk would resurrect the value.\n const chunks = present.filter((name) => name.startsWith(`${cookie.name}.`))\n for (const name of new Set([cookie.name, ...chunks]))\n server.deleteCookie(name, deletionOf(cookie.attributes))\n }\n}\n\nfunction deletionOf(attributes: AuthCookie['attributes']): DeleteCookieOptions {\n // Better Auth sets `/` and the secure flag at runtime; the types leave both optional.\n const options: DeleteCookieOptions = {\n path: attributes.path ?? '/',\n secure: attributes.secure ?? false,\n }\n if (attributes.domain !== undefined) options.domain = attributes.domain\n return options\n}\n","import type { ErrorPayloadOf, ErrorTable } from '@/core'\n\nimport { resolveFailure } from './resolve-failure.ts'\n\n/**\n * Build the server-function converter for one error table.\n * The result is a plain object, because only own data survives serialization.\n */\nexport function createErrorPayloadConverter<Entries>(table: ErrorTable<Entries>) {\n return function toErrorPayload(cause: unknown): ErrorPayloadOf<ErrorTable<Entries>> {\n return resolveFailure(table, cause).payload\n }\n}\n","import { isNotFound, isRedirect, redirect } from '@tanstack/react-router'\nimport { createMiddleware } from '@tanstack/react-start'\nimport type { BetterAuthCookies } from 'better-auth'\n\nimport { StaleSessionError, type ErrorTable } from '@/core'\n\nimport { createHttpErrorResponseConverter } from './http-error-response.ts'\nimport type { RequestContext } from './request-context.ts'\nimport { clearStaleSessionCookies } from './session-cookies.ts'\nimport { createErrorPayloadConverter } from './to-error-payload.ts'\n\n/** The part of a Better Auth instance the boundary reads: its computed cookie definitions. */\nexport type AuthCookieSource = {\n readonly $context: Promise<{ readonly authCookies: BetterAuthCookies }>\n}\n\n/** The application's error table, its stale-session redirect, and its Better Auth instance. */\nexport type ErrorMiddlewareOptions<Entries> = {\n readonly errors: ErrorTable<Entries>\n /** The path a server function redirects to for a stale session, with any search string. */\n readonly signIn: string\n /** Select the request's Better Auth instance; its cookie definitions say what to clear. */\n readonly getAuth: (context: RequestContext) => AuthCookieSource\n}\n\n/**\n * Build both transport boundaries from one error table.\n * Register `functionErrorMiddleware` globally. Put `routeErrorMiddleware` first in an API route's `server.middleware`.\n */\nexport function createErrorMiddleware<Entries>(options: ErrorMiddlewareOptions<Entries>) {\n const toErrorPayload = createErrorPayloadConverter(options.errors)\n const toHttpErrorResponse = createHttpErrorResponseConverter(options.errors)\n\n async function clearSession(context: RequestContext): Promise<void> {\n const { authCookies } = await options.getAuth(context).$context\n await clearStaleSessionCookies(authCookies)\n }\n\n async function handleFunctionError(cause: unknown, context: RequestContext): Promise<never> {\n // oxlint-disable-next-line typescript/only-throw-error -- TanStack uses throwable control-flow objects.\n if (isNotFound(cause) || isRedirect(cause)) throw cause\n if (cause instanceof StaleSessionError) {\n await clearSession(context)\n // oxlint-disable-next-line typescript/only-throw-error -- Router redirects by throwing this.\n throw redirect({ href: options.signIn })\n }\n // oxlint-disable-next-line typescript/only-throw-error -- The client rejects with this and reads its tag.\n throw toErrorPayload(cause)\n }\n\n // A route answers 401 instead of a redirect because fetch would follow it into the HTML shell.\n async function handleRouteError(cause: unknown, context: RequestContext): Promise<Response> {\n // oxlint-disable-next-line typescript/only-throw-error -- TanStack uses throwable control-flow objects.\n if (isNotFound(cause) || isRedirect(cause)) throw cause\n if (cause instanceof StaleSessionError) await clearSession(context)\n return toHttpErrorResponse(cause)\n }\n\n const functionErrorMiddleware = createMiddleware({ type: 'function' }).server(\n async ({ next, context }) => {\n try {\n return await next()\n } catch (cause) {\n return handleFunctionError(cause, context)\n }\n },\n )\n\n const routeErrorMiddleware = createMiddleware({ type: 'request' }).server(\n async ({ next, context }) => {\n try {\n return await next()\n } catch (cause) {\n return handleRouteError(cause, context)\n }\n },\n )\n\n return { functionErrorMiddleware, routeErrorMiddleware, toErrorPayload, toHttpErrorResponse }\n}\n","import { createMiddleware } from '@tanstack/react-start'\n\nimport { withSpan } from '../cloudflare/tracing.ts'\nimport type { RequestContext } from './request-context.ts'\n\n/**\n * Run one server function call in a child span named for that function.\n * Ported from Suparelay `tracing.middleware.ts`. `getTracing` runs on each request.\n * A handler rejection is not a span exception, and this middleware does not log.\n */\nexport function createFunctionTracingMiddleware(getTracing: (context: RequestContext) => Tracing) {\n return createMiddleware({ type: 'function' }).server(({ context, serverFnMeta, next }) =>\n withSpan(\n getTracing(context),\n `server_fn ${serverFnMeta.name}`,\n {\n 'code.function.name': serverFnMeta.name,\n 'code.file.path': serverFnMeta.filename,\n },\n () => next(),\n ),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tanstack.js","names":["logger"],"sources":["../src/infrastructure/tanstack/auth.middleware.ts","../src/infrastructure/tanstack/resolve-failure.ts","../src/infrastructure/tanstack/http-error-response.ts","../src/infrastructure/tanstack/session-cookies.ts","../src/infrastructure/tanstack/to-error-payload.ts","../src/infrastructure/tanstack/error.middleware.ts","../src/infrastructure/tanstack/function-tracing.middleware.ts"],"sourcesContent":["import { createMiddleware } from '@tanstack/react-start'\nimport { getSessionCookie } from 'better-auth/cookies'\n\nimport { AuthenticationError, StaleSessionError, createLogger } from '@/core'\n\nimport { applyObservabilityUser } from '../../application/request-user.ts'\nimport type { RequestContext } from './request-context.ts'\n\nconst logger = createLogger('auth-middleware')\n\n/** The fields middleware reads; generic callers retain every additional session and user field. */\nexport type AuthSession = {\n readonly user: { readonly id: string }\n readonly session: { readonly id: string }\n}\n\n/** Authenticated context retains the application's complete user and session types. */\nexport type RequiredAuthContext<Session extends AuthSession> = {\n readonly user: Session['user']\n readonly session: Session['session']\n}\n\n/** Optional authentication supplies both fields together or neither field. */\nexport type AuthContext<Session extends AuthSession> =\n | RequiredAuthContext<Session>\n | {\n readonly user: undefined\n readonly session: undefined\n }\n\n/** A resolved session and the request context that produced it. */\nexport type AuthenticatedContext<Session extends AuthSession> = {\n readonly user: Session['user']\n readonly session: Session['session']\n readonly requestContext: RequestContext\n}\n\n/** Select the current request's Better Auth instance from TanStack's registered context. */\nexport type AuthMiddlewareOptions<Session extends AuthSession> = {\n readonly getAuth: (context: RequestContext) => {\n readonly api: {\n readonly getSession: (options: { headers: Headers }) => Promise<Session | null>\n }\n }\n /** Runs after a session resolves and before the handler. A guest or a failed lookup does not call it. */\n readonly onAuthenticated?: (context: AuthenticatedContext<Session>) => void\n}\n\n// The server entry loads react-dom/server; the browser evaluates this module, so it loads lazily inside `.server()`.\nconst requestHeaders = async () => {\n const { getRequestHeaders } = await import('@tanstack/react-start/server')\n return getRequestHeaders()\n}\n\n/**\n * Preserve Porte's function and request middleware contracts with application-owned services.\n * Optional lookup failures log once and continue as guests. Required lookup failures propagate.\n * A resolved session applies `{ id }` on the observability hook, then runs `onAuthenticated`, before the handler.\n */\nexport function createAuthMiddleware<Session extends AuthSession>(\n options: AuthMiddlewareOptions<Session>,\n) {\n function notifyAuthenticated(requestContext: RequestContext, resolved: Session): void {\n applyObservabilityUser({ id: resolved.user.id })\n options.onAuthenticated?.({\n user: resolved.user,\n session: resolved.session,\n requestContext,\n })\n }\n const extractAuth = createMiddleware({ type: 'function' }).server<AuthContext<Session>>(\n async ({ next, context }) => {\n let resolved: Session | null\n try {\n const headers = await requestHeaders()\n resolved = await options.getAuth(context).api.getSession({ headers })\n } catch (error) {\n logger.error('get_session_failed', { error })\n resolved = null\n }\n if (resolved === null)\n return next<AuthContext<Session>>({ context: { user: undefined, session: undefined } })\n notifyAuthenticated(context, resolved)\n return next<AuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n },\n )\n\n async function resolveRequiredSession(\n context: RequestContext,\n headers: Headers,\n ): Promise<Session> {\n const resolved = await options.getAuth(context).api.getSession({ headers })\n if (resolved === null) {\n if (getSessionCookie(headers)) throw new StaleSessionError()\n throw new AuthenticationError()\n }\n return resolved\n }\n\n const requireAuth = createMiddleware({ type: 'function' }).server(async ({ next, context }) => {\n const resolved = await resolveRequiredSession(context, await requestHeaders())\n notifyAuthenticated(context, resolved)\n return next<RequiredAuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n })\n const requireAuthRequest = createMiddleware({ type: 'request' }).server(\n async ({ request, next, context }) => {\n const resolved = await resolveRequiredSession(context, request.headers)\n notifyAuthenticated(context, resolved)\n return next<RequiredAuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n },\n )\n return { extractAuth, requireAuth, requireAuthRequest }\n}\n","import { z } from 'zod'\n\nimport {\n AppError,\n ValidationError,\n createLogger,\n type ErrorTable,\n type ResolvedError,\n} from '@/core'\nimport type { ErrorPayloadOf } from '@/core'\n\nconst logger = createLogger('error-boundary')\n\n// TanStack serializes a validator's issues into `Error.message` as JSON.\nconst SerializedIssuesSchema = z\n .array(z.object({ message: z.string(), path: z.array(z.union([z.string(), z.number()])) }))\n .min(1)\n\n// A live ZodError keeps `.issues`. TanStack leaves only JSON in `Error.message`.\nfunction reconstructValidationError(cause: unknown): ValidationError | null {\n if (cause instanceof z.ZodError) return ValidationError.fromZod(cause)\n if (!(cause instanceof Error)) return null\n let message: unknown\n try {\n message = JSON.parse(cause.message)\n } catch {\n return null\n }\n const parsed = SerializedIssuesSchema.safeParse(message)\n return parsed.success ? ValidationError.fromIssues(parsed.data) : null\n}\n\n/**\n * Resolve one failure through the table or fall back to the generic replacement, and log it once.\n * A thrown ZodError, or issues serialized into an error message, becomes a ValidationError first.\n * Both transport converters call this, so one rule decides the log level: mapped is a warning, unmapped is an error with the cause.\n */\nexport function resolveFailure<Entries>(\n table: ErrorTable<Entries>,\n cause: unknown,\n): ResolvedError<ErrorPayloadOf<ErrorTable<Entries>>> {\n const failure = reconstructValidationError(cause) ?? cause\n const resolved = table.resolve(failure)\n if (resolved !== null) {\n logger.warn('handled_error', {\n tag: resolved.payload._tag,\n status: resolved.status,\n })\n return resolved\n }\n const classification = failure instanceof AppError ? failure.classification : 'unknown'\n logger.error('unexpected_error', { error: failure, classification })\n return table.fallback(failure)\n}\n","import { getReasonPhrase } from 'http-status-codes'\nimport { z } from 'zod'\n\nimport type { ErrorTable, ProblemDetails } from '@/core'\n\nimport { resolveFailure } from './resolve-failure.ts'\n\n// A known delay becomes the header; a null delay sends no header.\nconst RetryAfterSchema = z.object({ retryAfter: z.number() })\n\n/**\n * Build the API route converter for one error table.\n * The body is RFC 9457 problem details; public fields become extension members.\n */\nexport function createHttpErrorResponseConverter<Entries>(table: ErrorTable<Entries>) {\n return function toHttpErrorResponse(cause: unknown): Response {\n const { status, payload } = resolveFailure(table, cause)\n const { _tag, message, ...fields } = payload\n const body: ProblemDetails = {\n type: 'about:blank',\n title: getReasonPhrase(status),\n detail: message,\n ...fields,\n }\n const headers = new Headers({ 'Content-Type': 'application/problem+json' })\n const retry = RetryAfterSchema.safeParse(payload)\n if (retry.success) headers.set('Retry-After', String(retry.data.retryAfter))\n return Response.json(body, { status, headers })\n }\n}\n","import type { deleteCookie } from '@tanstack/react-start/server'\nimport type { BetterAuthCookies } from 'better-auth'\nimport { parseCookies } from 'better-auth/cookies'\n\ntype AuthCookie = BetterAuthCookies['sessionToken']\ntype DeleteCookieOptions = NonNullable<Parameters<typeof deleteCookie>[1]>\n\n/**\n * Delete the session, session cache, account cache, and remember-me cookies of one instance.\n * Chunks present on the request go too. Names and attributes come from the instance, so custom prefixes and domain cookies clear.\n */\nexport async function clearStaleSessionCookies(cookies: BetterAuthCookies): Promise<void> {\n // The server entry loads react-dom/server; the browser evaluates this module, so it loads lazily here.\n const server = await import('@tanstack/react-start/server')\n const present = [...parseCookies(server.getRequestHeaders().get('cookie') ?? '').keys()]\n const definitions = [\n cookies.sessionToken,\n cookies.sessionData,\n cookies.accountData,\n cookies.dontRememberToken,\n ]\n for (const cookie of definitions) {\n // Better Auth names a chunk `${name}.${index}`, so a missed chunk would resurrect the value.\n const chunks = present.filter((name) => name.startsWith(`${cookie.name}.`))\n for (const name of new Set([cookie.name, ...chunks]))\n server.deleteCookie(name, deletionOf(cookie.attributes))\n }\n}\n\nfunction deletionOf(attributes: AuthCookie['attributes']): DeleteCookieOptions {\n // Better Auth sets `/` and the secure flag at runtime; the types leave both optional.\n const options: DeleteCookieOptions = {\n path: attributes.path ?? '/',\n secure: attributes.secure ?? false,\n }\n if (attributes.domain !== undefined) options.domain = attributes.domain\n return options\n}\n","import type { ErrorPayloadOf, ErrorTable } from '@/core'\n\nimport { resolveFailure } from './resolve-failure.ts'\n\n/**\n * Build the server-function converter for one error table.\n * The result is a plain object, because only own data survives serialization.\n */\nexport function createErrorPayloadConverter<Entries>(table: ErrorTable<Entries>) {\n return function toErrorPayload(cause: unknown): ErrorPayloadOf<ErrorTable<Entries>> {\n return resolveFailure(table, cause).payload\n }\n}\n","import { isNotFound, isRedirect, redirect } from '@tanstack/react-router'\nimport { createMiddleware } from '@tanstack/react-start'\nimport type { BetterAuthCookies } from 'better-auth'\n\nimport { StaleSessionError, type ErrorTable } from '@/core'\n\nimport { createHttpErrorResponseConverter } from './http-error-response.ts'\nimport type { RequestContext } from './request-context.ts'\nimport { clearStaleSessionCookies } from './session-cookies.ts'\nimport { createErrorPayloadConverter } from './to-error-payload.ts'\n\n/** The part of a Better Auth instance the boundary reads: its computed cookie definitions. */\nexport type AuthCookieSource = {\n readonly $context: Promise<{ readonly authCookies: BetterAuthCookies }>\n}\n\n/** The application's error table, its stale-session redirect, and its Better Auth instance. */\nexport type ErrorMiddlewareOptions<Entries> = {\n readonly errors: ErrorTable<Entries>\n /** The path a server function redirects to for a stale session, with any search string. */\n readonly signIn: string\n /** Select the request's Better Auth instance; its cookie definitions say what to clear. */\n readonly getAuth: (context: RequestContext) => AuthCookieSource\n}\n\n/**\n * Build both transport boundaries from one error table.\n * Register `functionErrorMiddleware` globally. Put `routeErrorMiddleware` first in an API route's `server.middleware`.\n */\nexport function createErrorMiddleware<Entries>(options: ErrorMiddlewareOptions<Entries>) {\n const toErrorPayload = createErrorPayloadConverter(options.errors)\n const toHttpErrorResponse = createHttpErrorResponseConverter(options.errors)\n\n async function clearSession(context: RequestContext): Promise<void> {\n const { authCookies } = await options.getAuth(context).$context\n await clearStaleSessionCookies(authCookies)\n }\n\n async function handleFunctionError(cause: unknown, context: RequestContext): Promise<never> {\n // oxlint-disable-next-line typescript/only-throw-error -- TanStack uses throwable control-flow objects.\n if (isNotFound(cause) || isRedirect(cause)) throw cause\n if (cause instanceof StaleSessionError) {\n await clearSession(context)\n // oxlint-disable-next-line typescript/only-throw-error -- Router redirects by throwing this.\n throw redirect({ href: options.signIn })\n }\n // oxlint-disable-next-line typescript/only-throw-error -- The client rejects with this and reads its tag.\n throw toErrorPayload(cause)\n }\n\n // A route answers 401 instead of a redirect because fetch would follow it into the HTML shell.\n async function handleRouteError(cause: unknown, context: RequestContext): Promise<Response> {\n // oxlint-disable-next-line typescript/only-throw-error -- TanStack uses throwable control-flow objects.\n if (isNotFound(cause) || isRedirect(cause)) throw cause\n if (cause instanceof StaleSessionError) await clearSession(context)\n return toHttpErrorResponse(cause)\n }\n\n const functionErrorMiddleware = createMiddleware({ type: 'function' }).server(\n async ({ next, context }) => {\n try {\n return await next()\n } catch (cause) {\n return handleFunctionError(cause, context)\n }\n },\n )\n\n const routeErrorMiddleware = createMiddleware({ type: 'request' }).server(\n async ({ next, context }) => {\n try {\n return await next()\n } catch (cause) {\n return handleRouteError(cause, context)\n }\n },\n )\n\n return { functionErrorMiddleware, routeErrorMiddleware, toErrorPayload, toHttpErrorResponse }\n}\n","import { createMiddleware } from '@tanstack/react-start'\n\nimport { withSpan } from '../cloudflare/tracing.ts'\nimport type { RequestContext } from './request-context.ts'\n\n/**\n * Run one server function call in a child span named for that function.\n * Ported from Suparelay `tracing.middleware.ts`. `getTracing` runs on each request.\n * A handler rejection is not a span exception, and this middleware does not log.\n */\nexport function createFunctionTracingMiddleware(getTracing: (context: RequestContext) => Tracing) {\n return createMiddleware({ type: 'function' }).server(({ context, serverFnMeta, next }) =>\n withSpan(\n getTracing(context),\n `server_fn ${serverFnMeta.name}`,\n {\n 'code.function.name': serverFnMeta.name,\n 'code.file.path': serverFnMeta.filename,\n },\n () => next(),\n ),\n )\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { W as DomainEvent } from "../index-DUFS0SJ_.js";
2
- import { A as IOutboxRepository } from "../index-CbvNd665.js";
2
+ import { A as IOutboxRepository } from "../index-BosM_da2.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 = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://www.schemastore.org/package.json",
3
3
  "name": "better-ship",
4
- "version": "0.36.0",
4
+ "version": "0.37.0",
5
5
  "description": "Foundation for applications on Cloudflare Workers: logger, errors, message bus, infrastructure adapters, and UI primitives",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -31,6 +31,7 @@
31
31
  "./browser-support/oxlint": "./dist/browser-support/oxlint.js",
32
32
  "./browser-support/polyfills": "./dist/browser-support/polyfills.js",
33
33
  "./browser-support/update-screen": "./dist/browser-support/update-screen.js",
34
+ "./browser-user": "./dist/browser-user.js",
34
35
  "./cloudflare": "./dist/cloudflare.js",
35
36
  "./cloudflare/auth-secondary-storage": "./dist/cloudflare/auth-secondary-storage.js",
36
37
  "./cloudflare/durable-object-client": "./dist/cloudflare/durable-object-client.js",
@@ -12,6 +12,7 @@ export type {
12
12
  ObservabilityScope,
13
13
  ObservabilityUser,
14
14
  } from './observability-service.ts'
15
+ export { applyObservabilityUser, setObservabilityUserHook } from './request-user.ts'
15
16
  export {
16
17
  RecordingEmailService,
17
18
  type IEmailService,
@@ -0,0 +1,18 @@
1
+ import type { ObservabilityUser } from './observability-service.ts'
2
+
3
+ /**
4
+ * Process-wide request user hook.
5
+ * The Sentry server entry sets it once. Auth middleware calls it after a session resolves.
6
+ * The hook does not store a user id. The adapter writes the id on the current isolation scope.
7
+ */
8
+ let observabilityUserHook: ((user: ObservabilityUser) => void) | null = null
9
+
10
+ /** Set or clear the process-wide request user hook. */
11
+ export function setObservabilityUserHook(hook: ((user: ObservabilityUser) => void) | null): void {
12
+ observabilityUserHook = hook
13
+ }
14
+
15
+ /** Attribute the current execution to this user. No hook means the call does nothing. */
16
+ export function applyObservabilityUser(user: ObservabilityUser): void {
17
+ observabilityUserHook?.(user)
18
+ }
@@ -0,0 +1,24 @@
1
+ import * as Sentry from '@sentry/tanstackstart-react'
2
+ import { posthog } from 'posthog-js'
3
+
4
+ /**
5
+ * Identify the signed-in user in PostHog and Sentry.
6
+ * Sends the id only. A client that is not initialized does nothing.
7
+ * Call it when the session user id is present. A loading session does not call it.
8
+ */
9
+ export function identifyObservabilityUser(userId: string): void {
10
+ // identify() warns when the SDK is not loaded. A disabled client must stay silent.
11
+ // oxlint-disable-next-line no-underscore-dangle -- PostHog's public loaded flag. There is no other initialized check.
12
+ if (posthog.__loaded) posthog.identify(userId)
13
+ if (Sentry.isEnabled()) Sentry.setUser({ id: userId })
14
+ }
15
+
16
+ /**
17
+ * Clear the Sentry user and reset PostHog on sign-out.
18
+ * A disabled client does nothing. Call this once from the application's sign-out path.
19
+ */
20
+ export function clearObservabilityUser(): void {
21
+ // oxlint-disable-next-line no-underscore-dangle -- Same loaded flag as identifyObservabilityUser.
22
+ if (posthog.__loaded) posthog.reset()
23
+ if (Sentry.isEnabled()) Sentry.setUser(null)
24
+ }
@@ -3,6 +3,8 @@ import * as Sentry from '@sentry/cloudflare'
3
3
  import type { ObservabilityScope, ObservabilityService, ObservabilityUser } from '@/application'
4
4
  import type { LoggerErrorHook } from '@/core'
5
5
 
6
+ import { setObservabilityUserHook } from '../../application/request-user.ts'
7
+
6
8
  /** Application-owned deployment settings with the full Cloudflare SDK option contract. */
7
9
  export type SentryServerOptions = Sentry.CloudflareOptions & {
8
10
  readonly dsn: string
@@ -10,8 +12,16 @@ export type SentryServerOptions = Sentry.CloudflareOptions & {
10
12
  readonly enabled: boolean
11
13
  }
12
14
 
13
- /** Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults. */
15
+ /**
16
+ * Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults.
17
+ * Registers the request user hook so auth middleware can set `{ id }` on the current isolation scope.
18
+ */
14
19
  export function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions {
20
+ // Auth middleware cannot import this SDK. The hook writes `{ id }` only when Sentry is enabled.
21
+ setObservabilityUserHook((user) => {
22
+ if (!Sentry.isEnabled()) return
23
+ Sentry.setUser({ id: user.id })
24
+ })
15
25
  return options
16
26
  }
17
27
 
@@ -3,6 +3,7 @@ import { getSessionCookie } from 'better-auth/cookies'
3
3
 
4
4
  import { AuthenticationError, StaleSessionError, createLogger } from '@/core'
5
5
 
6
+ import { applyObservabilityUser } from '../../application/request-user.ts'
6
7
  import type { RequestContext } from './request-context.ts'
7
8
 
8
9
  const logger = createLogger('auth-middleware')
@@ -54,12 +55,13 @@ const requestHeaders = async () => {
54
55
  /**
55
56
  * Preserve Porte's function and request middleware contracts with application-owned services.
56
57
  * Optional lookup failures log once and continue as guests. Required lookup failures propagate.
57
- * `onAuthenticated` runs after a session resolves and before the rest of the chain.
58
+ * A resolved session applies `{ id }` on the observability hook, then runs `onAuthenticated`, before the handler.
58
59
  */
59
60
  export function createAuthMiddleware<Session extends AuthSession>(
60
61
  options: AuthMiddlewareOptions<Session>,
61
62
  ) {
62
63
  function notifyAuthenticated(requestContext: RequestContext, resolved: Session): void {
64
+ applyObservabilityUser({ id: resolved.user.id })
63
65
  options.onAuthenticated?.({
64
66
  user: resolved.user,
65
67
  session: resolved.session,
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-CbvNd665.d.ts","names":[],"sources":["../src/application/handlers.ts","../src/application/analytics-service.ts","../src/application/metrics-service.ts","../src/application/observability-service.ts","../src/application/email-service.ts","../src/application/event-collector.ts","../src/application/message-store.ts","../src/application/outbox-repository.ts","../src/application/outbox-relay.ts","../src/application/transaction.ts","../src/application/unit-of-work.ts","../src/application/record-email-event.ts","../src/application/registry.ts","../src/application/message-bus.ts","../src/application/outbox-retention.ts","../src/application/message-already-processed.error.ts","../src/application/unit-of-work.errors.ts"],"mappings":"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"}