better-ship 0.4.0 → 0.5.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 (81) hide show
  1. package/dist/{application-BpSwCKJ5.js → application-DEerezCl.js} +2 -2
  2. package/dist/{application-BpSwCKJ5.js.map → application-DEerezCl.js.map} +1 -1
  3. package/dist/application.d.ts +1 -1
  4. package/dist/application.js +1 -1
  5. package/dist/better-auth/apple-client-secret.d.ts +21 -0
  6. package/dist/better-auth/apple-client-secret.d.ts.map +1 -0
  7. package/dist/better-auth/apple-client-secret.js +44 -0
  8. package/dist/better-auth/apple-client-secret.js.map +1 -0
  9. package/dist/better-auth/auth-client.d.ts +16 -0
  10. package/dist/better-auth/auth-client.d.ts.map +1 -0
  11. package/dist/better-auth/auth-client.js +24 -0
  12. package/dist/better-auth/auth-client.js.map +1 -0
  13. package/dist/better-auth/rate-limit.d.ts +34 -0
  14. package/dist/better-auth/rate-limit.d.ts.map +1 -0
  15. package/dist/better-auth/rate-limit.js +59 -0
  16. package/dist/better-auth/rate-limit.js.map +1 -0
  17. package/dist/better-auth/secondary-storage.d.ts +11 -0
  18. package/dist/better-auth/secondary-storage.d.ts.map +1 -0
  19. package/dist/better-auth/secondary-storage.js +18 -0
  20. package/dist/better-auth/secondary-storage.js.map +1 -0
  21. package/dist/better-auth/stripe.d.ts +9 -0
  22. package/dist/better-auth/stripe.d.ts.map +1 -0
  23. package/dist/better-auth/stripe.js +17 -0
  24. package/dist/better-auth/stripe.js.map +1 -0
  25. package/dist/better-auth.d.ts +100 -0
  26. package/dist/better-auth.d.ts.map +1 -0
  27. package/dist/better-auth.js +112 -0
  28. package/dist/better-auth.js.map +1 -0
  29. package/dist/cloudflare/auth-secondary-storage.d.ts +36 -0
  30. package/dist/cloudflare/auth-secondary-storage.d.ts.map +1 -0
  31. package/dist/cloudflare/auth-secondary-storage.js +98 -0
  32. package/dist/cloudflare/auth-secondary-storage.js.map +1 -0
  33. package/dist/cloudflare.d.ts +3 -3
  34. package/dist/cloudflare.js +3 -3
  35. package/dist/core-CnwWj4MY.js +620 -0
  36. package/dist/core-CnwWj4MY.js.map +1 -0
  37. package/dist/core.d.ts +2 -2
  38. package/dist/core.js +2 -2
  39. package/dist/index-B9Y1hVh6.d.ts +460 -0
  40. package/dist/index-B9Y1hVh6.d.ts.map +1 -0
  41. package/dist/{index-CkaFqbbV.d.ts → index-Bdyh3_xC.d.ts} +2 -2
  42. package/dist/{index-CkaFqbbV.d.ts.map → index-Bdyh3_xC.d.ts.map} +1 -1
  43. package/dist/postgres.d.ts +3 -3
  44. package/dist/postgres.js +3 -3
  45. package/dist/tanstack.d.ts +81 -1
  46. package/dist/tanstack.d.ts.map +1 -0
  47. package/dist/tanstack.js +214 -0
  48. package/dist/tanstack.js.map +1 -0
  49. package/dist/{unit-of-work-context-CILhzKWb.js → unit-of-work-context-C7vz5OYS.js} +2 -2
  50. package/dist/{unit-of-work-context-CILhzKWb.js.map → unit-of-work-context-C7vz5OYS.js.map} +1 -1
  51. package/dist/{unit-of-work-context-DOkIB7HK.d.ts → unit-of-work-context-ml9L9cyY.d.ts} +2 -2
  52. package/dist/{unit-of-work-context-DOkIB7HK.d.ts.map → unit-of-work-context-ml9L9cyY.d.ts.map} +1 -1
  53. package/package.json +57 -12
  54. package/src/core/authentication.error.ts +11 -0
  55. package/src/core/error-table.ts +188 -0
  56. package/src/core/index.ts +31 -0
  57. package/src/core/internal.errors.ts +21 -0
  58. package/src/core/logger.ts +40 -19
  59. package/src/core/not-authorized.error.ts +11 -0
  60. package/src/core/problem-details.ts +57 -0
  61. package/src/core/rate-limit.error.ts +12 -0
  62. package/src/core/request.errors.ts +86 -0
  63. package/src/core/stale-session.error.ts +11 -0
  64. package/src/infrastructure/better-auth/apple-client-secret.ts +54 -0
  65. package/src/infrastructure/better-auth/auth-client.ts +26 -0
  66. package/src/infrastructure/better-auth/options.ts +232 -0
  67. package/src/infrastructure/better-auth/rate-limit.ts +69 -0
  68. package/src/infrastructure/better-auth/secondary-storage.ts +21 -0
  69. package/src/infrastructure/better-auth/stripe.ts +11 -0
  70. package/src/infrastructure/cloudflare/auth-secondary-storage.ts +113 -0
  71. package/src/infrastructure/tanstack/auth.middleware.ts +89 -0
  72. package/src/infrastructure/tanstack/error.middleware.ts +81 -0
  73. package/src/infrastructure/tanstack/http-error-response.ts +30 -0
  74. package/src/infrastructure/tanstack/index.ts +12 -0
  75. package/src/infrastructure/tanstack/resolve-failure.ts +24 -0
  76. package/src/infrastructure/tanstack/session-cookies.ts +36 -0
  77. package/src/infrastructure/tanstack/to-error-payload.ts +32 -0
  78. package/dist/core-D3G-2gIx.js +0 -298
  79. package/dist/core-D3G-2gIx.js.map +0 -1
  80. package/dist/index-CT1yZd4D.d.ts +0 -212
  81. package/dist/index-CT1yZd4D.d.ts.map +0 -1
@@ -1,4 +1,4 @@
1
- import { a as createLogger, f as unreachable, m as AppError, p as DatabaseError } from "./core-D3G-2gIx.js";
1
+ import { A as AppError, a as createLogger, f as unreachable, p as DatabaseError } from "./core-CnwWj4MY.js";
2
2
  import { panic } from "better-result";
3
3
 
4
4
  //#region src/application/message-already-processed.error.ts
@@ -128,4 +128,4 @@ var UnitOfWorkAlreadyActiveError = class extends AppError {
128
128
 
129
129
  //#endregion
130
130
  export { MessageAlreadyProcessedError as i, UnitOfWork as n, MessageBus as r, UnitOfWorkAlreadyActiveError as t };
131
- //# sourceMappingURL=application-BpSwCKJ5.js.map
131
+ //# sourceMappingURL=application-DEerezCl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"application-BpSwCKJ5.js","names":[],"sources":["../src/application/message-already-processed.error.ts","../src/application/message-bus.ts","../src/application/unit-of-work.ts","../src/application/unit-of-work.errors.ts"],"sourcesContent":["import { AppError, DatabaseError } from '@/core'\n\nimport type { MessageId } from './messages.ts'\n\n/** The message's effects already committed. A caller can skip duplicate delivery without repeating those effects. */\nexport class MessageAlreadyProcessedError extends AppError {\n readonly _tag = 'MessageAlreadyProcessedError'\n readonly classification = 'terminal'\n\n constructor(\n readonly messageId: MessageId,\n options?: { readonly cause: unknown },\n ) {\n super(`message ${messageId} already processed`, options)\n }\n\n /** Recognize only the processed-message primary key; preserve other failures unchanged at the call site. */\n static from(cause: unknown, messageId: MessageId): MessageAlreadyProcessedError | undefined {\n if (cause instanceof MessageAlreadyProcessedError) return cause\n if (!(cause instanceof DatabaseError)) return undefined\n const details = cause.details\n const duplicate =\n details.database === 'd1'\n ? details.kind === 'unique' &&\n details.table === 'processed_messages' &&\n details.column === 'message_id'\n : details.code === '23505' &&\n details.table_name === 'processed_messages' &&\n details.constraint_name === 'processed_messages_pkey'\n if (!duplicate) return undefined\n return new MessageAlreadyProcessedError(messageId, { cause })\n }\n}\n","import { panic } from 'better-result'\n\nimport { createLogger, unreachable } from '@/core'\n\nimport { MessageAlreadyProcessedError } from './message-already-processed.error.ts'\nimport type { Command, Event, Message, Query } from './messages.ts'\nimport type { Handlers, MessageResult, RegistryMessage } from './registry.ts'\n\nconst logger = createLogger('message-bus')\n\n/** The port an entrypoint dispatches through. `AppDeps` names this as `IMessageBus<typeof registry>`. */\nexport interface IMessageBus<TRegistry extends Handlers<never>> {\n handle<TMessage extends Message & RegistryMessage<TRegistry>>(\n message: TMessage,\n ): Promise<MessageResult<TMessage, TRegistry>>\n}\n\n/**\n * Routes one message to its slot. The bus opens no transaction and keeps no outbox:\n * a handler that writes opens the unit of work itself.\n */\nexport class MessageBus<Deps, TRegistry extends Handlers<Deps>> implements IMessageBus<TRegistry> {\n constructor(\n private readonly deps: Deps,\n private readonly registry: TRegistry,\n ) {}\n\n /** Resolves to the handler's own value for a command or query, and to void for an event. */\n async handle<TMessage extends Message & RegistryMessage<TRegistry>>(\n message: TMessage,\n ): Promise<MessageResult<TMessage, TRegistry>> {\n // SAFETY: the slot for this name is what `MessageResult` names, and dispatch returns its value.\n return (await this.dispatch(message)) as MessageResult<TMessage, TRegistry>\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-returns -- one table holds every slot's value; `handle` names it.\n private dispatch(message: Message): Promise<unknown> {\n if (message.type === 'command') return this.handleCommand(message)\n if (message.type === 'query') return this.handleQuery(message)\n if (message.type === 'event') return this.handleEvent(message)\n // A message parsed at a boundary cannot reach here; one that does must not resolve silently.\n return unreachable(message)\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-returns -- see dispatch.\n private handleCommand(command: Command): Promise<unknown> {\n // `satisfies` proved the registry complete, so a missing slot is a broken registry object.\n const handler = this.registry.commands[command.name]\n if (handler === undefined) return panic(`No handler for command ${command.name}`)\n logger.debug('handling_command', { name: command.name, id: command.id })\n return handler(command, this.deps)\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-returns -- see dispatch.\n private handleQuery(query: Query): Promise<unknown> {\n const handler = this.registry.queries[query.name]\n if (handler === undefined) return panic(`No handler for query ${query.name}`)\n return handler(query, this.deps)\n }\n\n /**\n * Every subscriber runs to completion. One failure is rethrown as is. Several are thrown as\n * one `AggregateError`. A `MessageAlreadyProcessedError` means that subscriber already committed on\n * an earlier delivery, so it counts as delivered.\n */\n private async handleEvent(event: Event): Promise<void> {\n const subscriptions = this.registry.events[event.name] ?? []\n const outcomes = await Promise.allSettled(\n subscriptions.map((subscription) => subscription(event, this.deps)),\n )\n const failures: unknown[] = []\n for (const [subscriber, outcome] of outcomes.entries()) {\n if (outcome.status === 'fulfilled') continue\n if (outcome.reason instanceof MessageAlreadyProcessedError) {\n // The bus swallows this error, so this line is the only record that the skip happened.\n logger.info('duplicate_subscriber_ignored', { event: event.name, id: event.id, subscriber })\n continue\n }\n failures.push(outcome.reason)\n }\n if (failures.length === 1) throw failures[0]\n if (failures.length > 1) {\n throw new AggregateError(failures, `${event.name}: ${failures.length} subscribers failed`)\n }\n }\n}\n","import type { IEventCollector } from './event-collector.ts'\nimport { MessageAlreadyProcessedError } from './message-already-processed.error.ts'\nimport type { IMessageStore } from './message-store.ts'\nimport type { MessageId } from './messages.ts'\nimport type { IOutboxRelay } from './outbox-relay.ts'\nimport type { ITransaction } from './transaction.ts'\n\n/**\n * Commits the processed-message record, repository writes, and emitted events together before resolving.\n * An existing record rejects with `MessageAlreadyProcessedError`; external effects must run through the outbox after commit.\n * Nested calls reject with `UnitOfWorkAlreadyActiveError`; independent concurrent calls are allowed.\n */\nexport interface IUnitOfWork<Repos> {\n run<T>(messageId: MessageId, work: (repos: Repos) => Promise<T>): Promise<T>\n}\n\nexport type UnitOfWorkDeps<Tx, Repos> = {\n readonly transaction: ITransaction<Tx>\n /** The processed-messages and outbox repository for this transaction. */\n readonly messageStore: (tx: Tx) => IMessageStore\n /** Repositories participate in the current transaction. */\n readonly createRepositories: (tx: Tx) => Repos\n readonly eventCollector: IEventCollector\n readonly relay: IOutboxRelay\n}\n\n/** The unit of work: the boundary, the repositories, the events. Every database call is a port. */\nexport class UnitOfWork<Tx, Repos> implements IUnitOfWork<Repos> {\n constructor(private readonly deps: UnitOfWorkDeps<Tx, Repos>) {}\n\n async run<T>(messageId: MessageId, work: (repos: Repos) => Promise<T>): Promise<T> {\n const { transaction, messageStore, createRepositories, eventCollector, relay } = this.deps\n let written = 0\n\n let value: T\n try {\n value = await transaction.transaction(async (tx) => {\n const store = messageStore(tx)\n await store.claim(messageId)\n const repos = createRepositories(tx)\n const result = await work(repos)\n const pending = eventCollector.collect()\n await store.persistEvents(pending)\n written = pending.length\n return result\n })\n } catch (error) {\n throw MessageAlreadyProcessedError.from(error, messageId) ?? error\n }\n\n if (written > 0) relay.wake()\n return value\n }\n}\n","import { AppError } from '@/core'\n\n/** A boundary was opened inside another. One message, one atomic scope. */\nexport class UnitOfWorkAlreadyActiveError extends AppError {\n readonly _tag = 'UnitOfWorkAlreadyActiveError'\n readonly classification = 'terminal'\n\n constructor() {\n super('a unit of work is already active')\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"application-DEerezCl.js","names":[],"sources":["../src/application/message-already-processed.error.ts","../src/application/message-bus.ts","../src/application/unit-of-work.ts","../src/application/unit-of-work.errors.ts"],"sourcesContent":["import { AppError, DatabaseError } from '@/core'\n\nimport type { MessageId } from './messages.ts'\n\n/** The message's effects already committed. A caller can skip duplicate delivery without repeating those effects. */\nexport class MessageAlreadyProcessedError extends AppError {\n readonly _tag = 'MessageAlreadyProcessedError'\n readonly classification = 'terminal'\n\n constructor(\n readonly messageId: MessageId,\n options?: { readonly cause: unknown },\n ) {\n super(`message ${messageId} already processed`, options)\n }\n\n /** Recognize only the processed-message primary key; preserve other failures unchanged at the call site. */\n static from(cause: unknown, messageId: MessageId): MessageAlreadyProcessedError | undefined {\n if (cause instanceof MessageAlreadyProcessedError) return cause\n if (!(cause instanceof DatabaseError)) return undefined\n const details = cause.details\n const duplicate =\n details.database === 'd1'\n ? details.kind === 'unique' &&\n details.table === 'processed_messages' &&\n details.column === 'message_id'\n : details.code === '23505' &&\n details.table_name === 'processed_messages' &&\n details.constraint_name === 'processed_messages_pkey'\n if (!duplicate) return undefined\n return new MessageAlreadyProcessedError(messageId, { cause })\n }\n}\n","import { panic } from 'better-result'\n\nimport { createLogger, unreachable } from '@/core'\n\nimport { MessageAlreadyProcessedError } from './message-already-processed.error.ts'\nimport type { Command, Event, Message, Query } from './messages.ts'\nimport type { Handlers, MessageResult, RegistryMessage } from './registry.ts'\n\nconst logger = createLogger('message-bus')\n\n/** The port an entrypoint dispatches through. `AppDeps` names this as `IMessageBus<typeof registry>`. */\nexport interface IMessageBus<TRegistry extends Handlers<never>> {\n handle<TMessage extends Message & RegistryMessage<TRegistry>>(\n message: TMessage,\n ): Promise<MessageResult<TMessage, TRegistry>>\n}\n\n/**\n * Routes one message to its slot. The bus opens no transaction and keeps no outbox:\n * a handler that writes opens the unit of work itself.\n */\nexport class MessageBus<Deps, TRegistry extends Handlers<Deps>> implements IMessageBus<TRegistry> {\n constructor(\n private readonly deps: Deps,\n private readonly registry: TRegistry,\n ) {}\n\n /** Resolves to the handler's own value for a command or query, and to void for an event. */\n async handle<TMessage extends Message & RegistryMessage<TRegistry>>(\n message: TMessage,\n ): Promise<MessageResult<TMessage, TRegistry>> {\n // SAFETY: the slot for this name is what `MessageResult` names, and dispatch returns its value.\n return (await this.dispatch(message)) as MessageResult<TMessage, TRegistry>\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-returns -- one table holds every slot's value; `handle` names it.\n private dispatch(message: Message): Promise<unknown> {\n if (message.type === 'command') return this.handleCommand(message)\n if (message.type === 'query') return this.handleQuery(message)\n if (message.type === 'event') return this.handleEvent(message)\n // A message parsed at a boundary cannot reach here; one that does must not resolve silently.\n return unreachable(message)\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-returns -- see dispatch.\n private handleCommand(command: Command): Promise<unknown> {\n // `satisfies` proved the registry complete, so a missing slot is a broken registry object.\n const handler = this.registry.commands[command.name]\n if (handler === undefined) return panic(`No handler for command ${command.name}`)\n logger.debug('handling_command', { name: command.name, id: command.id })\n return handler(command, this.deps)\n }\n\n // oxlint-disable-next-line anti-slop/no-unknown-returns -- see dispatch.\n private handleQuery(query: Query): Promise<unknown> {\n const handler = this.registry.queries[query.name]\n if (handler === undefined) return panic(`No handler for query ${query.name}`)\n return handler(query, this.deps)\n }\n\n /**\n * Every subscriber runs to completion. One failure is rethrown as is. Several are thrown as\n * one `AggregateError`. A `MessageAlreadyProcessedError` means that subscriber already committed on\n * an earlier delivery, so it counts as delivered.\n */\n private async handleEvent(event: Event): Promise<void> {\n const subscriptions = this.registry.events[event.name] ?? []\n const outcomes = await Promise.allSettled(\n subscriptions.map((subscription) => subscription(event, this.deps)),\n )\n const failures: unknown[] = []\n for (const [subscriber, outcome] of outcomes.entries()) {\n if (outcome.status === 'fulfilled') continue\n if (outcome.reason instanceof MessageAlreadyProcessedError) {\n // The bus swallows this error, so this line is the only record that the skip happened.\n logger.info('duplicate_subscriber_ignored', { event: event.name, id: event.id, subscriber })\n continue\n }\n failures.push(outcome.reason)\n }\n if (failures.length === 1) throw failures[0]\n if (failures.length > 1) {\n throw new AggregateError(failures, `${event.name}: ${failures.length} subscribers failed`)\n }\n }\n}\n","import type { IEventCollector } from './event-collector.ts'\nimport { MessageAlreadyProcessedError } from './message-already-processed.error.ts'\nimport type { IMessageStore } from './message-store.ts'\nimport type { MessageId } from './messages.ts'\nimport type { IOutboxRelay } from './outbox-relay.ts'\nimport type { ITransaction } from './transaction.ts'\n\n/**\n * Commits the processed-message record, repository writes, and emitted events together before resolving.\n * An existing record rejects with `MessageAlreadyProcessedError`; external effects must run through the outbox after commit.\n * Nested calls reject with `UnitOfWorkAlreadyActiveError`; independent concurrent calls are allowed.\n */\nexport interface IUnitOfWork<Repos> {\n run<T>(messageId: MessageId, work: (repos: Repos) => Promise<T>): Promise<T>\n}\n\nexport type UnitOfWorkDeps<Tx, Repos> = {\n readonly transaction: ITransaction<Tx>\n /** The processed-messages and outbox repository for this transaction. */\n readonly messageStore: (tx: Tx) => IMessageStore\n /** Repositories participate in the current transaction. */\n readonly createRepositories: (tx: Tx) => Repos\n readonly eventCollector: IEventCollector\n readonly relay: IOutboxRelay\n}\n\n/** The unit of work: the boundary, the repositories, the events. Every database call is a port. */\nexport class UnitOfWork<Tx, Repos> implements IUnitOfWork<Repos> {\n constructor(private readonly deps: UnitOfWorkDeps<Tx, Repos>) {}\n\n async run<T>(messageId: MessageId, work: (repos: Repos) => Promise<T>): Promise<T> {\n const { transaction, messageStore, createRepositories, eventCollector, relay } = this.deps\n let written = 0\n\n let value: T\n try {\n value = await transaction.transaction(async (tx) => {\n const store = messageStore(tx)\n await store.claim(messageId)\n const repos = createRepositories(tx)\n const result = await work(repos)\n const pending = eventCollector.collect()\n await store.persistEvents(pending)\n written = pending.length\n return result\n })\n } catch (error) {\n throw MessageAlreadyProcessedError.from(error, messageId) ?? error\n }\n\n if (written > 0) relay.wake()\n return value\n }\n}\n","import { AppError } from '@/core'\n\n/** A boundary was opened inside another. One message, one atomic scope. */\nexport class UnitOfWorkAlreadyActiveError extends AppError {\n readonly _tag = 'UnitOfWorkAlreadyActiveError'\n readonly classification = 'terminal'\n\n constructor() {\n super('a unit of work is already active')\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { C as Event, E as Query, S as Command, T as MessageId, _ as RegistryMessage, a as UnitOfWorkDeps, b as EventHandler, c as IOutboxRelay, d as CommandRegistry, f as EventRegistry, g as QueryRegistry, h as MessageResult, i as UnitOfWork, l as IMessageBus, m as MessageRegistry, n as MessageAlreadyProcessedError, o as IEventCollector, p as Handlers, r as IUnitOfWork, s as ITransaction, t as UnitOfWorkAlreadyActiveError, u as MessageBus, v as IMessageStore, w as Message, x as QueryHandler, y as CommandHandler } from "./index-CkaFqbbV.js";
1
+ import { C as Event, E as Query, S as Command, T as MessageId, _ as RegistryMessage, a as UnitOfWorkDeps, b as EventHandler, c as IOutboxRelay, d as CommandRegistry, f as EventRegistry, g as QueryRegistry, h as MessageResult, i as UnitOfWork, l as IMessageBus, m as MessageRegistry, n as MessageAlreadyProcessedError, o as IEventCollector, p as Handlers, r as IUnitOfWork, s as ITransaction, t as UnitOfWorkAlreadyActiveError, u as MessageBus, v as IMessageStore, w as Message, x as QueryHandler, y as CommandHandler } from "./index-Bdyh3_xC.js";
2
2
  export { type Command, type CommandHandler, type CommandRegistry, type Event, type EventHandler, type EventRegistry, type Handlers, type IEventCollector, type IMessageBus, type IMessageStore, type IOutboxRelay, type ITransaction, type IUnitOfWork, type Message, MessageAlreadyProcessedError, MessageBus, type MessageId, type MessageRegistry, type MessageResult, type Query, type QueryHandler, type QueryRegistry, type RegistryMessage, UnitOfWork, UnitOfWorkAlreadyActiveError, type UnitOfWorkDeps };
@@ -1,3 +1,3 @@
1
- import { i as MessageAlreadyProcessedError, n as UnitOfWork, r as MessageBus, t as UnitOfWorkAlreadyActiveError } from "./application-BpSwCKJ5.js";
1
+ import { i as MessageAlreadyProcessedError, n as UnitOfWork, r as MessageBus, t as UnitOfWorkAlreadyActiveError } from "./application-DEerezCl.js";
2
2
 
3
3
  export { MessageAlreadyProcessedError, MessageBus, UnitOfWork, UnitOfWorkAlreadyActiveError };
@@ -0,0 +1,21 @@
1
+ import { $ as AppError } from "../index-B9Y1hVh6.js";
2
+ //#region src/infrastructure/better-auth/apple-client-secret.d.ts
3
+ /** Apple sends its OAuth callback from this origin. Include it in trustedOrigins when enabling Apple. */
4
+ export declare const APPLE_ORIGIN = "https://appleid.apple.com";
5
+ /** Apple credentials used to sign a client secret. Ported from Porte's apple-client-secret.ts. */
6
+ export type AppleKeyConfig = {
7
+ readonly appleClientId: string;
8
+ readonly appleTeamId: string;
9
+ readonly appleKeyId: string;
10
+ readonly applePrivateKey: string;
11
+ };
12
+ /** Apple signing failed. The application's error boundary owns reporting. */
13
+ export declare class AppleClientSecretError extends AppError {
14
+ readonly _tag = "AppleClientSecretError";
15
+ readonly classification = "terminal";
16
+ constructor(cause: unknown);
17
+ }
18
+ /** Sign Apple's six-month client secret. Invalid private keys return an empty secret and log once per isolate, as in Typist. */
19
+ export declare function generateAppleClientSecret(config: AppleKeyConfig): Promise<string>;
20
+ //#endregion
21
+ //# sourceMappingURL=apple-client-secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-client-secret.d.ts","names":[],"sources":["../../src/infrastructure/better-auth/apple-client-secret.ts"],"mappings":";;;qBASa;;YAGD;WACD;WACA;WACA;WACA;;;qBAIE,+BAA+B;WACjC;WACA;EAET,YAAY;;;wBAMQ,0BAA0B,QAAQ,iBAAiB"}
@@ -0,0 +1,44 @@
1
+ import { A as AppError, a as createLogger } from "../core-CnwWj4MY.js";
2
+ import { SignJWT, importPKCS8 } from "jose";
3
+
4
+ //#region src/infrastructure/better-auth/apple-client-secret.ts
5
+ const logger = createLogger("auth");
6
+ let appleKeyFailureLogged = false;
7
+ /** Apple sends its OAuth callback from this origin. Include it in trustedOrigins when enabling Apple. */
8
+ const APPLE_ORIGIN = "https://appleid.apple.com";
9
+ /** Apple signing failed. The application's error boundary owns reporting. */
10
+ var AppleClientSecretError = class extends AppError {
11
+ _tag = "AppleClientSecretError";
12
+ classification = "terminal";
13
+ constructor(cause) {
14
+ super("Apple client secret signing failed", { cause });
15
+ }
16
+ };
17
+ /** Sign Apple's six-month client secret. Invalid private keys return an empty secret and log once per isolate, as in Typist. */
18
+ async function generateAppleClientSecret(config) {
19
+ let key;
20
+ try {
21
+ key = await importPKCS8(config.applePrivateKey.replace(/\\n/g, "\n"), "ES256");
22
+ } catch (error) {
23
+ if (!appleKeyFailureLogged) {
24
+ appleKeyFailureLogged = true;
25
+ logger.error("apple_private_key_invalid", {
26
+ error,
27
+ details: { keyId: config.appleKeyId }
28
+ });
29
+ }
30
+ return "";
31
+ }
32
+ try {
33
+ return await new SignJWT({}).setProtectedHeader({
34
+ alg: "ES256",
35
+ kid: config.appleKeyId
36
+ }).setIssuer(config.appleTeamId).setSubject(config.appleClientId).setAudience(APPLE_ORIGIN).setIssuedAt().setExpirationTime("180d").sign(key);
37
+ } catch (cause) {
38
+ throw new AppleClientSecretError(cause);
39
+ }
40
+ }
41
+
42
+ //#endregion
43
+ export { APPLE_ORIGIN, AppleClientSecretError, generateAppleClientSecret };
44
+ //# sourceMappingURL=apple-client-secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-client-secret.js","names":[],"sources":["../../src/infrastructure/better-auth/apple-client-secret.ts"],"sourcesContent":["import { importPKCS8, SignJWT } from 'jose'\n\nimport { AppError, createLogger } from '@/core'\n\nconst logger = createLogger('auth')\n// Typist reports an invalid key once per isolate because session checks repeatedly construct auth options.\nlet appleKeyFailureLogged = false\n\n/** Apple sends its OAuth callback from this origin. Include it in trustedOrigins when enabling Apple. */\nexport const APPLE_ORIGIN = 'https://appleid.apple.com'\n\n/** Apple credentials used to sign a client secret. Ported from Porte's apple-client-secret.ts. */\nexport type AppleKeyConfig = {\n readonly appleClientId: string\n readonly appleTeamId: string\n readonly appleKeyId: string\n readonly applePrivateKey: string\n}\n\n/** Apple signing failed. The application's error boundary owns reporting. */\nexport class AppleClientSecretError extends AppError {\n readonly _tag = 'AppleClientSecretError'\n readonly classification = 'terminal'\n\n constructor(cause: unknown) {\n super('Apple client secret signing failed', { cause })\n }\n}\n\n/** Sign Apple's six-month client secret. Invalid private keys return an empty secret and log once per isolate, as in Typist. */\nexport async function generateAppleClientSecret(config: AppleKeyConfig): Promise<string> {\n let key: Awaited<ReturnType<typeof importPKCS8>>\n try {\n key = await importPKCS8(config.applePrivateKey.replace(/\\\\n/g, '\\n'), 'ES256')\n } catch (error) {\n if (!appleKeyFailureLogged) {\n appleKeyFailureLogged = true\n logger.error('apple_private_key_invalid', { error, details: { keyId: config.appleKeyId } })\n }\n return ''\n }\n try {\n return await new SignJWT({})\n .setProtectedHeader({ alg: 'ES256', kid: config.appleKeyId })\n .setIssuer(config.appleTeamId)\n .setSubject(config.appleClientId)\n .setAudience(APPLE_ORIGIN)\n .setIssuedAt()\n .setExpirationTime('180d')\n .sign(key)\n } catch (cause) {\n throw new AppleClientSecretError(cause)\n }\n}\n"],"mappings":";;;;AAIA,MAAM,SAAS,aAAa,MAAM;AAElC,IAAI,wBAAwB;;AAG5B,MAAa,eAAe;;AAW5B,IAAa,yBAAb,cAA4C,SAAS;CACnD,AAAS,OAAO;CAChB,AAAS,iBAAiB;CAE1B,YAAY,OAAgB;EAC1B,MAAM,sCAAsC,EAAE,MAAM,CAAC;CACvD;AACF;;AAGA,eAAsB,0BAA0B,QAAyC;CACvF,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,YAAY,OAAO,gBAAgB,QAAQ,QAAQ,IAAI,GAAG,OAAO;CAC/E,SAAS,OAAO;EACd,IAAI,CAAC,uBAAuB;GAC1B,wBAAwB;GACxB,OAAO,MAAM,6BAA6B;IAAE;IAAO,SAAS,EAAE,OAAO,OAAO,WAAW;GAAE,CAAC;EAC5F;EACA,OAAO;CACT;CACA,IAAI;EACF,OAAO,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,CACzB,mBAAmB;GAAE,KAAK;GAAS,KAAK,OAAO;EAAW,CAAC,CAAC,CAC5D,UAAU,OAAO,WAAW,CAAC,CAC7B,WAAW,OAAO,aAAa,CAAC,CAChC,YAAY,YAAY,CAAC,CACzB,YAAY,CAAC,CACb,kBAAkB,MAAM,CAAC,CACzB,KAAK,GAAG;CACb,SAAS,OAAO;EACd,MAAM,IAAI,uBAAuB,KAAK;CACxC;AACF"}
@@ -0,0 +1,16 @@
1
+ import { BetterAuthClientOptions } from "better-auth/client";
2
+ //#region src/infrastructure/better-auth/auth-client.d.ts
3
+ type AuthErrorHandler = NonNullable<NonNullable<BetterAuthClientOptions['fetchOptions']>['onError']>;
4
+ /**
5
+ * Pass to native fetchOptions.onError to reject HTTP 429 with RateLimitError.
6
+ * Other errors remain in the native result. This handler does not schedule retries or log errors.
7
+ */
8
+ export declare const handleAuthError: AuthErrorHandler;
9
+ /** Native client for same-origin /api/auth requests. HTTP 429 rejects with RateLimitError. */
10
+ export declare const authClient: import("better-auth/client").AuthClient<{
11
+ fetchOptions: {
12
+ onError: (context: import("better-auth/client").ErrorContext) => Promise<void> | void;
13
+ };
14
+ }>;
15
+ //#endregion
16
+ //# sourceMappingURL=auth-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-client.d.ts","names":[],"sources":["../../src/infrastructure/better-auth/auth-client.ts"],"mappings":";;KAIK,mBAAmB,YAAY,YAAY;;;;;qBAMnC,iBAAiB;;qBAMjB,yCAAU;;IACL,UAAO,sCAAA,iBAAA"}
@@ -0,0 +1,24 @@
1
+ import { O as RateLimitError } from "../core-CnwWj4MY.js";
2
+ import { createAuthClient } from "better-auth/client";
3
+
4
+ //#region src/infrastructure/better-auth/auth-client.ts
5
+ /**
6
+ * Pass to native fetchOptions.onError to reject HTTP 429 with RateLimitError.
7
+ * Other errors remain in the native result. This handler does not schedule retries or log errors.
8
+ */
9
+ const handleAuthError = ({ response }) => {
10
+ if (response.status !== 429) return;
11
+ throw new RateLimitError(retryAfterSeconds(response.headers));
12
+ };
13
+ /** Native client for same-origin /api/auth requests. HTTP 429 rejects with RateLimitError. */
14
+ const authClient = createAuthClient({ fetchOptions: { onError: handleAuthError } });
15
+ function retryAfterSeconds(headers) {
16
+ const value = headers.get("X-Retry-After");
17
+ if (value === null || !/^\d+$/.test(value)) return null;
18
+ const seconds = Number(value);
19
+ return Number.isSafeInteger(seconds) ? seconds : null;
20
+ }
21
+
22
+ //#endregion
23
+ export { authClient, handleAuthError };
24
+ //# sourceMappingURL=auth-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-client.js","names":[],"sources":["../../src/infrastructure/better-auth/auth-client.ts"],"sourcesContent":["import { createAuthClient, type BetterAuthClientOptions } from 'better-auth/client'\n\nimport { RateLimitError } from '@/core'\n\ntype AuthErrorHandler = NonNullable<NonNullable<BetterAuthClientOptions['fetchOptions']>['onError']>\n\n/**\n * Pass to native fetchOptions.onError to reject HTTP 429 with RateLimitError.\n * Other errors remain in the native result. This handler does not schedule retries or log errors.\n */\nexport const handleAuthError: AuthErrorHandler = ({ response }) => {\n if (response.status !== 429) return\n throw new RateLimitError(retryAfterSeconds(response.headers))\n}\n\n/** Native client for same-origin /api/auth requests. HTTP 429 rejects with RateLimitError. */\nexport const authClient = createAuthClient({\n fetchOptions: { onError: handleAuthError },\n})\n\nfunction retryAfterSeconds(headers: Headers): number | null {\n const value = headers.get('X-Retry-After')\n if (value === null || !/^\\d+$/.test(value)) return null\n const seconds = Number(value)\n return Number.isSafeInteger(seconds) ? seconds : null\n}\n"],"mappings":";;;;;;;;AAUA,MAAa,mBAAqC,EAAE,eAAe;CACjE,IAAI,SAAS,WAAW,KAAK;CAC7B,MAAM,IAAI,eAAe,kBAAkB,SAAS,OAAO,CAAC;AAC9D;;AAGA,MAAa,aAAa,iBAAiB,EACzC,cAAc,EAAE,SAAS,gBAAgB,EAC3C,CAAC;AAED,SAAS,kBAAkB,SAAiC;CAC1D,MAAM,QAAQ,QAAQ,IAAI,eAAe;CACzC,IAAI,UAAU,QAAQ,CAAC,QAAQ,KAAK,KAAK,GAAG,OAAO;CACnD,MAAM,UAAU,OAAO,KAAK;CAC5B,OAAO,OAAO,cAAc,OAAO,IAAI,UAAU;AACnD"}
@@ -0,0 +1,34 @@
1
+ import { $ as AppError } from "../index-B9Y1hVh6.js";
2
+ import { BetterAuthRateLimitStorage } from "better-auth";
3
+ //#region src/infrastructure/better-auth/rate-limit.d.ts
4
+ /** Each rule must match its binding's deployed limit and period. Cloudflare accepts only 10 or 60 seconds. */
5
+ export type AuthRateLimitBinding = {
6
+ readonly binding: Pick<RateLimit, 'limit'>;
7
+ readonly window: 10 | 60;
8
+ readonly max: number;
9
+ };
10
+ /** A binding failure with no known retry classification. The cause preserves the original failure. */
11
+ export declare class AuthRateLimitError extends AppError {
12
+ readonly _tag = "AuthRateLimitError";
13
+ readonly classification = "unknown";
14
+ constructor(cause: unknown);
15
+ }
16
+ /** No configured binding matches this Better Auth rule. Outage policy cannot bypass a configuration error. */
17
+ export declare class UnsupportedAuthRateLimitRuleError extends AppError {
18
+ readonly window: number;
19
+ readonly max: number;
20
+ readonly _tag = "UnsupportedAuthRateLimitRuleError";
21
+ readonly classification = "terminal";
22
+ constructor(window: number, max: number);
23
+ }
24
+ /** Typist allows requests during binding outages. Select block to propagate an error instead. */
25
+ export type AuthRateLimitOptions = {
26
+ readonly failureMode?: 'allow' | 'block';
27
+ };
28
+ /**
29
+ * Port Typist and Porte's binding adapters with explicit rule matching and configurable outage behavior.
30
+ * Bindings enforce approximate limits per Cloudflare location; retryAfter is a conservative full window.
31
+ */
32
+ export declare function createAuthRateLimitStorage(bindings: readonly AuthRateLimitBinding[], { failureMode }?: AuthRateLimitOptions): BetterAuthRateLimitStorage;
33
+ //#endregion
34
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","names":[],"sources":["../../src/infrastructure/better-auth/rate-limit.ts"],"mappings":";;;;YAKY;WACD,SAAS,KAAK;WACd;WACA;;;qBAIE,2BAA2B;WAC7B;WACA;EAET,YAAY;;;qBAMD,0CAA0C;WAK1C;WACA;WALF;WACA;EAET,YACW,gBACA;;;YAOD;WACD;;;;;;wBAOK,2BACd,mBAAmB,0BACjB,gBAAyB,uBAC1B"}
@@ -0,0 +1,59 @@
1
+ import { A as AppError, a as createLogger } from "../core-CnwWj4MY.js";
2
+
3
+ //#region src/infrastructure/better-auth/rate-limit.ts
4
+ /** A binding failure with no known retry classification. The cause preserves the original failure. */
5
+ var AuthRateLimitError = class extends AppError {
6
+ _tag = "AuthRateLimitError";
7
+ classification = "unknown";
8
+ constructor(cause) {
9
+ super("Authentication rate limiter is unavailable", { cause });
10
+ }
11
+ };
12
+ /** No configured binding matches this Better Auth rule. Outage policy cannot bypass a configuration error. */
13
+ var UnsupportedAuthRateLimitRuleError = class extends AppError {
14
+ window;
15
+ max;
16
+ _tag = "UnsupportedAuthRateLimitRuleError";
17
+ classification = "terminal";
18
+ constructor(window, max) {
19
+ super("No Cloudflare binding supports the authentication rate limit rule");
20
+ this.window = window;
21
+ this.max = max;
22
+ }
23
+ };
24
+ /**
25
+ * Port Typist and Porte's binding adapters with explicit rule matching and configurable outage behavior.
26
+ * Bindings enforce approximate limits per Cloudflare location; retryAfter is a conservative full window.
27
+ */
28
+ function createAuthRateLimitStorage(bindings, { failureMode = "allow" } = {}) {
29
+ const logger = createLogger("auth-rate-limit");
30
+ return { async consume(key, rule) {
31
+ const configured = bindings.find((entry) => entry.window === rule.window && entry.max === rule.max);
32
+ if (configured === void 0) throw new UnsupportedAuthRateLimitRuleError(rule.window, rule.max);
33
+ try {
34
+ const { success } = await configured.binding.limit({ key });
35
+ return {
36
+ allowed: success,
37
+ retryAfter: success ? null : rule.window
38
+ };
39
+ } catch (cause) {
40
+ if (failureMode === "block") throw new AuthRateLimitError(cause);
41
+ logger.error("rate_limit_binding_failed", {
42
+ error: cause,
43
+ details: {
44
+ window: rule.window,
45
+ max: rule.max,
46
+ failureMode
47
+ }
48
+ });
49
+ return {
50
+ allowed: true,
51
+ retryAfter: null
52
+ };
53
+ }
54
+ } };
55
+ }
56
+
57
+ //#endregion
58
+ export { AuthRateLimitError, UnsupportedAuthRateLimitRuleError, createAuthRateLimitStorage };
59
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","names":[],"sources":["../../src/infrastructure/better-auth/rate-limit.ts"],"sourcesContent":["import type { BetterAuthRateLimitStorage } from 'better-auth'\n\nimport { AppError, createLogger } from '@/core'\n\n/** Each rule must match its binding's deployed limit and period. Cloudflare accepts only 10 or 60 seconds. */\nexport type AuthRateLimitBinding = {\n readonly binding: Pick<RateLimit, 'limit'>\n readonly window: 10 | 60\n readonly max: number\n}\n\n/** A binding failure with no known retry classification. The cause preserves the original failure. */\nexport class AuthRateLimitError extends AppError {\n readonly _tag = 'AuthRateLimitError'\n readonly classification = 'unknown'\n\n constructor(cause: unknown) {\n super('Authentication rate limiter is unavailable', { cause })\n }\n}\n\n/** No configured binding matches this Better Auth rule. Outage policy cannot bypass a configuration error. */\nexport class UnsupportedAuthRateLimitRuleError extends AppError {\n readonly _tag = 'UnsupportedAuthRateLimitRuleError'\n readonly classification = 'terminal'\n\n constructor(\n readonly window: number,\n readonly max: number,\n ) {\n super('No Cloudflare binding supports the authentication rate limit rule')\n }\n}\n\n/** Typist allows requests during binding outages. Select block to propagate an error instead. */\nexport type AuthRateLimitOptions = {\n readonly failureMode?: 'allow' | 'block'\n}\n\n/**\n * Port Typist and Porte's binding adapters with explicit rule matching and configurable outage behavior.\n * Bindings enforce approximate limits per Cloudflare location; retryAfter is a conservative full window.\n */\nexport function createAuthRateLimitStorage(\n bindings: readonly AuthRateLimitBinding[],\n { failureMode = 'allow' }: AuthRateLimitOptions = {},\n): BetterAuthRateLimitStorage {\n const logger = createLogger('auth-rate-limit')\n return {\n async consume(key, rule) {\n const configured = bindings.find(\n (entry) => entry.window === rule.window && entry.max === rule.max,\n )\n if (configured === undefined)\n throw new UnsupportedAuthRateLimitRuleError(rule.window, rule.max)\n try {\n const { success } = await configured.binding.limit({ key })\n return { allowed: success, retryAfter: success ? null : rule.window }\n } catch (cause) {\n if (failureMode === 'block') throw new AuthRateLimitError(cause)\n logger.error('rate_limit_binding_failed', {\n error: cause,\n details: { window: rule.window, max: rule.max, failureMode },\n })\n return { allowed: true, retryAfter: null }\n }\n },\n }\n}\n"],"mappings":";;;;AAYA,IAAa,qBAAb,cAAwC,SAAS;CAC/C,AAAS,OAAO;CAChB,AAAS,iBAAiB;CAE1B,YAAY,OAAgB;EAC1B,MAAM,8CAA8C,EAAE,MAAM,CAAC;CAC/D;AACF;;AAGA,IAAa,oCAAb,cAAuD,SAAS;CAKnD;CACA;CALX,AAAS,OAAO;CAChB,AAAS,iBAAiB;CAE1B,YACE,AAAS,QACT,AAAS,KACT;EACA,MAAM,mEAAmE;EAHhE;EACA;CAGX;AACF;;;;;AAWA,SAAgB,2BACd,UACA,EAAE,cAAc,YAAkC,CAAC,GACvB;CAC5B,MAAM,SAAS,aAAa,iBAAiB;CAC7C,OAAO,EACL,MAAM,QAAQ,KAAK,MAAM;EACvB,MAAM,aAAa,SAAS,MACzB,UAAU,MAAM,WAAW,KAAK,UAAU,MAAM,QAAQ,KAAK,GAChE;EACA,IAAI,eAAe,QACjB,MAAM,IAAI,kCAAkC,KAAK,QAAQ,KAAK,GAAG;EACnE,IAAI;GACF,MAAM,EAAE,YAAY,MAAM,WAAW,QAAQ,MAAM,EAAE,IAAI,CAAC;GAC1D,OAAO;IAAE,SAAS;IAAS,YAAY,UAAU,OAAO,KAAK;GAAO;EACtE,SAAS,OAAO;GACd,IAAI,gBAAgB,SAAS,MAAM,IAAI,mBAAmB,KAAK;GAC/D,OAAO,MAAM,6BAA6B;IACxC,OAAO;IACP,SAAS;KAAE,QAAQ,KAAK;KAAQ,KAAK,KAAK;KAAK;IAAY;GAC7D,CAAC;GACD,OAAO;IAAE,SAAS;IAAM,YAAY;GAAK;EAC3C;CACF,EACF;AACF"}
@@ -0,0 +1,11 @@
1
+ import { AuthSecondaryStorage } from "../cloudflare/auth-secondary-storage.js";
2
+ import { BetterAuthOptions } from "better-auth";
3
+ //#region src/infrastructure/better-auth/secondary-storage.d.ts
4
+ type SecondaryStorage = NonNullable<BetterAuthOptions['secondaryStorage']>;
5
+ /**
6
+ * Implement Better Auth's SecondaryStorage interface through the application's AuthSecondaryStorage binding.
7
+ * Operations preserve TTL and atomic semantics; failures propagate without logging or retries.
8
+ */
9
+ export declare function createAuthSecondaryStorage(binding: Pick<DurableObjectNamespace<AuthSecondaryStorage>, 'getByName'>): SecondaryStorage;
10
+ //#endregion
11
+ //# sourceMappingURL=secondary-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secondary-storage.d.ts","names":[],"sources":["../../src/infrastructure/better-auth/secondary-storage.ts"],"mappings":";;;KAIK,mBAAmB,YAAY;;;;;wBAMpB,2BACd,SAAS,KAAK,uBAAuB,sCACpC"}
@@ -0,0 +1,18 @@
1
+ //#region src/infrastructure/better-auth/secondary-storage.ts
2
+ /**
3
+ * Implement Better Auth's SecondaryStorage interface through the application's AuthSecondaryStorage binding.
4
+ * Operations preserve TTL and atomic semantics; failures propagate without logging or retries.
5
+ */
6
+ function createAuthSecondaryStorage(binding) {
7
+ return {
8
+ get: (key) => binding.getByName(key).get(),
9
+ set: (key, value, ttl) => binding.getByName(key).set(value, ttl),
10
+ delete: (key) => binding.getByName(key).delete(),
11
+ getAndDelete: (key) => binding.getByName(key).getAndDelete(),
12
+ increment: (key, ttl) => binding.getByName(key).increment(ttl)
13
+ };
14
+ }
15
+
16
+ //#endregion
17
+ export { createAuthSecondaryStorage };
18
+ //# sourceMappingURL=secondary-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secondary-storage.js","names":[],"sources":["../../src/infrastructure/better-auth/secondary-storage.ts"],"sourcesContent":["import type { BetterAuthOptions } from 'better-auth'\n\nimport type { AuthSecondaryStorage } from '../cloudflare/auth-secondary-storage.ts'\n\ntype SecondaryStorage = NonNullable<BetterAuthOptions['secondaryStorage']>\n\n/**\n * Implement Better Auth's SecondaryStorage interface through the application's AuthSecondaryStorage binding.\n * Operations preserve TTL and atomic semantics; failures propagate without logging or retries.\n */\nexport function createAuthSecondaryStorage(\n binding: Pick<DurableObjectNamespace<AuthSecondaryStorage>, 'getByName'>,\n): SecondaryStorage {\n return {\n get: (key) => binding.getByName(key).get(),\n set: (key, value, ttl) => binding.getByName(key).set(value, ttl),\n delete: (key) => binding.getByName(key).delete(),\n getAndDelete: (key) => binding.getByName(key).getAndDelete(),\n increment: (key, ttl) => binding.getByName(key).increment(ttl),\n }\n}\n"],"mappings":";;;;;AAUA,SAAgB,2BACd,SACkB;CAClB,OAAO;EACL,MAAM,QAAQ,QAAQ,UAAU,GAAG,CAAC,CAAC,IAAI;EACzC,MAAM,KAAK,OAAO,QAAQ,QAAQ,UAAU,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG;EAC/D,SAAS,QAAQ,QAAQ,UAAU,GAAG,CAAC,CAAC,OAAO;EAC/C,eAAe,QAAQ,QAAQ,UAAU,GAAG,CAAC,CAAC,aAAa;EAC3D,YAAY,KAAK,QAAQ,QAAQ,UAAU,GAAG,CAAC,CAAC,UAAU,GAAG;CAC/D;AACF"}
@@ -0,0 +1,9 @@
1
+ import { stripe } from "@better-auth/stripe";
2
+ //#region src/infrastructure/better-auth/stripe.d.ts
3
+ /**
4
+ * Configure the official Stripe plugin with Typist's customer creation policy.
5
+ * Customers are created at checkout by default. Native plans, callbacks, and overrides remain available.
6
+ */
7
+ export declare function createStripePlugin<const Options extends Parameters<typeof stripe>[0]>(options: Options): ReturnType<typeof stripe<Options>>;
8
+ //#endregion
9
+ //# sourceMappingURL=stripe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.d.ts","names":[],"sources":["../../src/infrastructure/better-auth/stripe.ts"],"mappings":";;;;;;wBAMgB,yBAAyB,gBAAgB,kBAAkB,YACzE,SAAS,UACR,kBAAkB,OAAO"}
@@ -0,0 +1,17 @@
1
+ import { stripe } from "@better-auth/stripe";
2
+
3
+ //#region src/infrastructure/better-auth/stripe.ts
4
+ /**
5
+ * Configure the official Stripe plugin with Typist's customer creation policy.
6
+ * Customers are created at checkout by default. Native plans, callbacks, and overrides remain available.
7
+ */
8
+ function createStripePlugin(options) {
9
+ return stripe({
10
+ createCustomerOnSignUp: false,
11
+ ...options
12
+ });
13
+ }
14
+
15
+ //#endregion
16
+ export { createStripePlugin };
17
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.js","names":[],"sources":["../../src/infrastructure/better-auth/stripe.ts"],"sourcesContent":["import { stripe } from '@better-auth/stripe'\n\n/**\n * Configure the official Stripe plugin with Typist's customer creation policy.\n * Customers are created at checkout by default. Native plans, callbacks, and overrides remain available.\n */\nexport function createStripePlugin<const Options extends Parameters<typeof stripe>[0]>(\n options: Options,\n): ReturnType<typeof stripe<Options>> {\n return stripe({ createCustomerOnSignUp: false, ...options })\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,mBACd,SACoC;CACpC,OAAO,OAAO;EAAE,wBAAwB;EAAO,GAAG;CAAQ,CAAC;AAC7D"}
@@ -0,0 +1,100 @@
1
+ import { CloudflareTurnstileOptions, LastLoginMethodOptions, anonymous, bearer, captcha, deviceAuthorization, lastLoginMethod, magicLink, openAPI } from "better-auth/plugins";
2
+ import { BetterAuthOptions, BetterAuthPlugin, BetterAuthRateLimitStorage, DBAdapterInstance } from "better-auth";
3
+ //#region src/infrastructure/better-auth/options.d.ts
4
+ /**
5
+ * Shared policy from Porte and Typist. The factory merges application overrides into these defaults.
6
+ */
7
+ export declare const authDefaults: {
8
+ onAPIError: {
9
+ throw: false;
10
+ onError(error: unknown): void;
11
+ };
12
+ rateLimit: {
13
+ window: number;
14
+ max: number;
15
+ };
16
+ session: {
17
+ expiresIn: number;
18
+ updateAge: number;
19
+ freshAge: number;
20
+ cookieCache: {
21
+ enabled: true;
22
+ maxAge: number;
23
+ };
24
+ };
25
+ advanced: {
26
+ database: {
27
+ generateId: () => string;
28
+ joins: true;
29
+ };
30
+ ipAddress: {
31
+ ipAddressHeaders: string[];
32
+ };
33
+ };
34
+ };
35
+ /** Configure built-in plugins with native options. Runtime auth requires captcha; lastLoginMethod is enabled unless explicitly disabled. */
36
+ export type AuthPluginOptions = {
37
+ readonly magicLink?: Parameters<typeof magicLink>[0];
38
+ readonly anonymous?: NonNullable<Parameters<typeof anonymous>[0]>;
39
+ readonly captcha?: Omit<CloudflareTurnstileOptions, 'provider'>;
40
+ readonly lastLoginMethod?: false | NonNullable<Parameters<typeof lastLoginMethod>[0]>;
41
+ readonly openAPI?: NonNullable<Parameters<typeof openAPI>[0]>;
42
+ readonly bearer?: NonNullable<Parameters<typeof bearer>[0]>;
43
+ readonly deviceAuthorization?: NonNullable<Parameters<typeof deviceAuthorization>[0]>;
44
+ };
45
+ /**
46
+ * Runtime auth requires rate limiting and Turnstile credentials. Schema generation omits both.
47
+ * Both modes select plugins through `integrations`, so schema generation must repeat the runtime selection.
48
+ */
49
+ export type AuthConfig = Omit<BetterAuthOptions, 'database' | 'plugins' | 'rateLimit'> & {
50
+ readonly rateLimit?: Omit<NonNullable<BetterAuthOptions['rateLimit']>, 'enabled' | 'storage' | 'customStorage'>;
51
+ } & ({
52
+ readonly mode?: 'runtime';
53
+ readonly rateLimitStorage: BetterAuthRateLimitStorage;
54
+ readonly integrations: AuthPluginOptions & {
55
+ readonly captcha: Omit<CloudflareTurnstileOptions, 'provider'>;
56
+ };
57
+ } | {
58
+ readonly mode: 'schema';
59
+ readonly rateLimitStorage?: never;
60
+ readonly integrations: AuthPluginOptions;
61
+ });
62
+ type PluginTypes = {
63
+ magicLink: ReturnType<typeof magicLink>;
64
+ anonymous: ReturnType<typeof anonymous>;
65
+ captcha: ReturnType<typeof captcha>;
66
+ lastLoginMethod: ReturnType<typeof lastLoginMethod<{}>>;
67
+ openAPI: ReturnType<typeof openAPI>;
68
+ bearer: ReturnType<typeof bearer>;
69
+ deviceAuthorization: ReturnType<typeof deviceAuthorization>;
70
+ };
71
+ type LastLoginPlugin<Options extends AuthPluginOptions> = Options extends {
72
+ readonly lastLoginMethod: infer Settings extends LastLoginMethodOptions;
73
+ } ? ReturnType<typeof lastLoginMethod<Settings>> : PluginTypes['lastLoginMethod'];
74
+ type SelectedPlugin<Options extends AuthPluginOptions, Key extends keyof PluginTypes> = Options extends Record<Key, false | undefined> ? [] : Options extends Record<Key, NonNullable<AuthPluginOptions[Key]>> ? [Key extends 'lastLoginMethod' ? LastLoginPlugin<Options> : PluginTypes[Key]] : Key extends 'lastLoginMethod' ? [PluginTypes[Key]] : [];
75
+ type SelectedPlugins<Options extends AuthPluginOptions> = [...SelectedPlugin<Options, 'captcha'>, ...SelectedPlugin<Options, 'lastLoginMethod'>, ...SelectedPlugin<Options, 'openAPI'>, ...SelectedPlugin<Options, 'magicLink'>, ...SelectedPlugin<Options, 'anonymous'>, ...SelectedPlugin<Options, 'deviceAuthorization'>, ...SelectedPlugin<Options, 'bearer'>];
76
+ type ConfiguredPlugins<Config extends AuthConfig> = SelectedPlugins<Config['integrations']>;
77
+ type MergeOptions<Defaults, Overrides> = Omit<Defaults, keyof Overrides> & Overrides;
78
+ type SessionOptions<Config extends AuthConfig> = MergeOptions<typeof authDefaults.session, NonNullable<Config['session']>>;
79
+ type ConfiguredOptions<Config extends AuthConfig, Plugins extends BetterAuthPlugin[]> = Omit<Config, 'mode' | 'integrations' | 'rateLimitStorage' | 'rateLimit' | 'session' | 'advanced' | 'onAPIError'> & {
80
+ database: DBAdapterInstance;
81
+ onAPIError: MergeOptions<typeof authDefaults.onAPIError, NonNullable<Config['onAPIError']>>;
82
+ rateLimit: NonNullable<BetterAuthOptions['rateLimit']>;
83
+ session: Omit<SessionOptions<Config>, 'cookieCache'> & {
84
+ cookieCache: MergeOptions<typeof authDefaults.session.cookieCache, NonNullable<NonNullable<Config['session']>['cookieCache']>>;
85
+ };
86
+ advanced: MergeOptions<typeof authDefaults.advanced, NonNullable<Config['advanced']>> & {
87
+ database: MergeOptions<typeof authDefaults.advanced.database, NonNullable<NonNullable<Config['advanced']>['database']>>;
88
+ ipAddress: MergeOptions<typeof authDefaults.advanced.ipAddress, NonNullable<NonNullable<Config['advanced']>['ipAddress']>>;
89
+ };
90
+ plugins: [...ConfiguredPlugins<Config>, ...Plugins];
91
+ };
92
+ /** A required database adapter and application config produce options with inferred plugin and session types. */
93
+ export type BetterAuthOptionsFactory = <const Config extends AuthConfig, const Plugins extends BetterAuthPlugin[] = []>(database: DBAdapterInstance, config: Config, additionalPlugins?: Plugins) => ConfiguredOptions<Config, Plugins>;
94
+ /**
95
+ * Build native Better Auth options with shared defaults and configured plugins; both arguments are required.
96
+ * Pass Stripe and other plugins in the third argument. Put tanstackStartCookies() last.
97
+ */
98
+ export declare const createBetterAuthOptions: BetterAuthOptionsFactory;
99
+ //#endregion
100
+ //# sourceMappingURL=better-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"better-auth.d.ts","names":[],"sources":["../src/infrastructure/better-auth/options.ts"],"mappings":";;;;;;qBAmCa;;;;;;IAQE;IAAY;;;;;;;;;;;;;;;;;MASV;;;;;YAKL;WACD,YAAY,kBAAkB;WAC9B,YAAY,YAAY,kBAAkB;WAC1C,UAAU,KAAK;WACf,0BAA0B,YAAY,kBAAkB;WACxD,UAAU,YAAY,kBAAkB;WACxC,SAAS,YAAY,kBAAkB;WACvC,sBAAsB,YAAY,kBAAkB;;;;;;YAOnD,aAAa,KAAK;WACnB,YAAY,KACnB,YAAY;;WAKC;WACA,kBAAkB;WAClB,cAAc;aACZ,SAAS,KAAK;;;WAIhB;WACA;WACA,cAAc;;KAI1B;EACH,WAAW,kBAAkB;EAC7B,WAAW,kBAAkB;EAC7B,SAAS,kBAAkB;EAC3B,iBAAiB,kBAAkB;EACnC,SAAS,kBAAkB;EAC3B,QAAQ,kBAAkB;EAC1B,qBAAqB,kBAAkB;;KAGpC,gBAAgB,gBAAgB,qBAAqB;WAC/C,uBAAuB,iBAAiB;IAE/C,kBAAkB,gBAAgB,aAClC;KAEC,eAAe,gBAAgB,mBAAmB,kBAAkB,eACvE,gBAAgB,OAAO,+BAEnB,gBAAgB,OAAO,KAAK,YAAY,kBAAkB,UACvD,gCAAgC,gBAAgB,WAAW,YAAY,QACxE,iCACG,YAAY;KAGlB,gBAAgB,gBAAgB,yBAChC,eAAe,wBACf,eAAe,gCACf,eAAe,wBACf,eAAe,0BACf,eAAe,0BACf,eAAe,oCACf,eAAe;KAGf,kBAAkB,eAAe,cAAc,gBAAgB;KAE/D,aAAa,UAAU,aAAa,KAAK,gBAAgB,aAAa;KAEtE,eAAe,eAAe,cAAc,oBACxC,aAAa,SACpB,YAAY;KAGT,kBAAkB,eAAe,YAAY,gBAAgB,sBAAsB,KACtF;EAGA,UAAU;EACV,YAAY,oBAAoB,aAAa,YAAY,YAAY;EACrE,WAAW,YAAY;EACvB,SAAS,KAAK,eAAe;IAC3B,aAAa,oBACJ,aAAa,QAAQ,aAC5B,YAAY,YAAY;;EAG5B,UAAU,oBAAoB,aAAa,UAAU,YAAY;IAC/D,UAAU,oBACD,aAAa,SAAS,UAC7B,YAAY,YAAY;IAE1B,WAAW,oBACF,aAAa,SAAS,WAC7B,YAAY,YAAY;;EAG5B,aAAa,kBAAkB,YAAY;;;YAIjC,kCACJ,eAAe,kBACf,gBAAgB,yBAEtB,UAAU,mBACV,QAAQ,QACR,oBAAoB,YACjB,kBAAkB,QAAQ;;;;;qBA4BlB,yBAAyB"}
@@ -0,0 +1,112 @@
1
+ import { a as createLogger } from "./core-CnwWj4MY.js";
2
+ import { isAPIError } from "better-auth/api";
3
+ import { anonymous, bearer, captcha, deviceAuthorization, lastLoginMethod, magicLink, openAPI } from "better-auth/plugins";
4
+ import { v7 } from "uuid";
5
+
6
+ //#region src/infrastructure/better-auth/options.ts
7
+ const logger = createLogger("auth");
8
+ const MAGIC_LINK_CAPTCHA_ENDPOINTS = [
9
+ "/sign-up/email",
10
+ "/sign-in/email",
11
+ "/request-password-reset",
12
+ "/sign-in/magic-link"
13
+ ];
14
+ /**
15
+ * Shared policy from Porte and Typist. The factory merges application overrides into these defaults.
16
+ */
17
+ const authDefaults = {
18
+ onAPIError: {
19
+ throw: false,
20
+ onError(error) {
21
+ if (isAPIError(error) && error.statusCode < 500) return;
22
+ logger.error("auth_operation_failed", { error });
23
+ }
24
+ },
25
+ rateLimit: {
26
+ window: 60,
27
+ max: 100
28
+ },
29
+ session: {
30
+ expiresIn: 604800,
31
+ updateAge: 86400,
32
+ freshAge: 86400,
33
+ cookieCache: {
34
+ enabled: true,
35
+ maxAge: 300
36
+ }
37
+ },
38
+ advanced: {
39
+ database: {
40
+ generateId: () => v7(),
41
+ joins: true
42
+ },
43
+ ipAddress: { ipAddressHeaders: ["cf-connecting-ip", "x-forwarded-for"] }
44
+ }
45
+ };
46
+ function buildPlugins(options) {
47
+ const plugins = [];
48
+ if (options.captcha !== void 0) {
49
+ const captchaOptions = { ...options.captcha };
50
+ if (options.magicLink !== void 0 && captchaOptions.endpoints === void 0) captchaOptions.endpoints = [...MAGIC_LINK_CAPTCHA_ENDPOINTS];
51
+ plugins.push(captcha({
52
+ ...captchaOptions,
53
+ provider: "cloudflare-turnstile"
54
+ }));
55
+ }
56
+ if (options.lastLoginMethod !== false) plugins.push(lastLoginMethod(options.lastLoginMethod));
57
+ if (options.openAPI !== void 0) plugins.push(openAPI(options.openAPI));
58
+ if (options.magicLink !== void 0) plugins.push(magicLink({
59
+ expiresIn: 300,
60
+ ...options.magicLink
61
+ }));
62
+ if (options.anonymous !== void 0) plugins.push(anonymous(options.anonymous));
63
+ if (options.deviceAuthorization !== void 0) plugins.push(deviceAuthorization(options.deviceAuthorization));
64
+ if (options.bearer !== void 0) plugins.push(bearer(options.bearer));
65
+ return plugins;
66
+ }
67
+ /**
68
+ * Build native Better Auth options with shared defaults and configured plugins; both arguments are required.
69
+ * Pass Stripe and other plugins in the third argument. Put tanstackStartCookies() last.
70
+ */
71
+ const createBetterAuthOptions = (database, config, additionalPlugins) => {
72
+ const { mode, integrations, rateLimitStorage, ...options } = config;
73
+ return {
74
+ ...options,
75
+ database,
76
+ onAPIError: {
77
+ ...authDefaults.onAPIError,
78
+ ...options.onAPIError
79
+ },
80
+ rateLimit: mode === "schema" ? { enabled: false } : {
81
+ ...authDefaults.rateLimit,
82
+ ...options.rateLimit,
83
+ enabled: true,
84
+ customStorage: rateLimitStorage
85
+ },
86
+ session: {
87
+ ...authDefaults.session,
88
+ ...options.session,
89
+ cookieCache: {
90
+ ...authDefaults.session.cookieCache,
91
+ ...options.session?.cookieCache
92
+ }
93
+ },
94
+ advanced: {
95
+ ...authDefaults.advanced,
96
+ ...options.advanced,
97
+ database: {
98
+ ...authDefaults.advanced.database,
99
+ ...options.advanced?.database
100
+ },
101
+ ipAddress: {
102
+ ...authDefaults.advanced.ipAddress,
103
+ ...options.advanced?.ipAddress
104
+ }
105
+ },
106
+ plugins: [...buildPlugins(integrations), ...additionalPlugins ?? []]
107
+ };
108
+ };
109
+
110
+ //#endregion
111
+ export { authDefaults, createBetterAuthOptions };
112
+ //# sourceMappingURL=better-auth.js.map