stratal 0.0.14 → 0.0.16
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.
- package/dist/application-zG8b-pol.d.mts +116 -0
- package/dist/application-zG8b-pol.d.mts.map +1 -0
- package/dist/{base-email.provider-bzdAYp8Z.mjs → base-email.provider-Cuw4OAB0.mjs} +1 -1
- package/dist/{base-email.provider-bzdAYp8Z.mjs.map → base-email.provider-Cuw4OAB0.mjs.map} +1 -1
- package/dist/bin/cloudflare-workers-loader.mjs +34 -0
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -0
- package/dist/bin/quarry.mjs +168 -0
- package/dist/bin/quarry.mjs.map +1 -0
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +11 -6
- package/dist/cache/index.mjs.map +1 -1
- package/dist/colors-DJaRDXoS.mjs +16 -0
- package/dist/colors-DJaRDXoS.mjs.map +1 -0
- package/dist/command-B-QH-Vu3.d.mts +120 -0
- package/dist/command-B-QH-Vu3.d.mts.map +1 -0
- package/dist/command-BvCOD6df.mjs +193 -0
- package/dist/command-BvCOD6df.mjs.map +1 -0
- package/dist/config/index.d.mts +2 -2
- package/dist/config/index.mjs +11 -6
- package/dist/config/index.mjs.map +1 -1
- package/dist/cron/index.d.mts +1 -1
- package/dist/cron/index.mjs +4 -3
- package/dist/{cron-manager-CpS_hrDD.mjs → cron-manager-DR7fiG6o.mjs} +3 -3
- package/dist/{cron-manager-CpS_hrDD.mjs.map → cron-manager-DR7fiG6o.mjs.map} +1 -1
- package/dist/decorate-D5j-d9_z.mjs +171 -0
- package/dist/decorate-D5j-d9_z.mjs.map +1 -0
- package/dist/di/index.d.mts +1 -1
- package/dist/di/index.mjs +3 -2
- package/dist/email/index.d.mts +3 -3
- package/dist/email/index.mjs +17 -11
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-C9U5-ETs.mjs → en-DaewN8hc.mjs} +3 -1
- package/dist/en-DaewN8hc.mjs.map +1 -0
- package/dist/errors/index.d.mts +1 -1
- package/dist/errors/index.mjs +3 -2
- package/dist/{errors-BRJgVd5-.mjs → errors-CtCi1wn6.mjs} +6 -3
- package/dist/errors-CtCi1wn6.mjs.map +1 -0
- package/dist/errors-H3TZnVeX.mjs +67 -0
- package/dist/errors-H3TZnVeX.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +3 -2
- package/dist/{events-CQyvSyrQ.mjs → events-CXl-o1Ad.mjs} +3 -2
- package/dist/{events-CQyvSyrQ.mjs.map → events-CXl-o1Ad.mjs.map} +1 -1
- package/dist/{gateway-context-D7TFPLi5.mjs → gateway-context-BkZ4UKaX.mjs} +4 -4
- package/dist/{gateway-context-D7TFPLi5.mjs.map → gateway-context-BkZ4UKaX.mjs.map} +1 -1
- package/dist/guards/index.d.mts +3 -3
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-B5o618bL.mjs → guards-DUk_Kzst.mjs} +1 -1
- package/dist/{guards-B5o618bL.mjs.map → guards-DUk_Kzst.mjs.map} +1 -1
- package/dist/i18n/index.d.mts +2 -2
- package/dist/i18n/index.mjs +15 -10
- package/dist/i18n/messages/en/index.d.mts +1 -1
- package/dist/i18n/messages/en/index.mjs +1 -1
- package/dist/i18n/validation/index.d.mts +1 -1
- package/dist/i18n/validation/index.mjs +1 -1
- package/dist/{i18n.module-C9wQr_2k.mjs → i18n.module-W8OJxg3d.mjs} +10 -11
- package/dist/i18n.module-W8OJxg3d.mjs.map +1 -0
- package/dist/{index-C9bIk5tt.d.mts → index-BJWm863C.d.mts} +9 -6
- package/dist/index-BJWm863C.d.mts.map +1 -0
- package/dist/{index-zKURVFOC.d.mts → index-D9iYu2Yc.d.mts} +3 -3
- package/dist/{index-zKURVFOC.d.mts.map → index-D9iYu2Yc.d.mts.map} +1 -1
- package/dist/{index-Dl4RvzNp.d.mts → index-DVhdhLvE.d.mts} +2 -2
- package/dist/{index-Dl4RvzNp.d.mts.map → index-DVhdhLvE.d.mts.map} +1 -1
- package/dist/{index-BWEwA_XK.d.mts → index-D_w_Rmtd.d.mts} +3 -1
- package/dist/{index-BWEwA_XK.d.mts.map → index-D_w_Rmtd.d.mts.map} +1 -1
- package/dist/{index-3TtGtYlJ.d.mts → index-Dp6A5ywM.d.mts} +1 -1
- package/dist/{index-3TtGtYlJ.d.mts.map → index-Dp6A5ywM.d.mts.map} +1 -1
- package/dist/index.d.mts +3 -106
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +20 -13
- package/dist/is-command-BfCgWAcQ.mjs +14 -0
- package/dist/is-command-BfCgWAcQ.mjs.map +1 -0
- package/dist/is-seeder-CebjZCDn.mjs +28 -0
- package/dist/is-seeder-CebjZCDn.mjs.map +1 -0
- package/dist/logger/index.d.mts +1 -1
- package/dist/logger/index.mjs +2 -1
- package/dist/{logger-Bg-CuidS.mjs → logger-BR1-s1Um.mjs} +4 -169
- package/dist/logger-BR1-s1Um.mjs.map +1 -0
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +5 -4
- package/dist/{middleware-B3tx1u1K.mjs → middleware-C0Ebzswy.mjs} +3 -3
- package/dist/{middleware-B3tx1u1K.mjs.map → middleware-C0Ebzswy.mjs.map} +1 -1
- package/dist/module/index.d.mts +21 -3
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +11 -6
- package/dist/{module-Dvzm4dhS.mjs → module-BgdxxzBe.mjs} +44 -5
- package/dist/module-BgdxxzBe.mjs.map +1 -0
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.mjs +15 -10
- package/dist/quarry/index.d.mts +120 -0
- package/dist/quarry/index.d.mts.map +1 -0
- package/dist/quarry/index.mjs +7 -0
- package/dist/quarry-registry-DCwqVcRp.mjs +310 -0
- package/dist/quarry-registry-DCwqVcRp.mjs.map +1 -0
- package/dist/queue/index.d.mts +1 -1
- package/dist/queue/index.mjs +12 -7
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-ZqaZ2iY0.mjs → queue.module-BZvmeAMj.mjs} +4 -4
- package/dist/{queue.module-ZqaZ2iY0.mjs.map → queue.module-BZvmeAMj.mjs.map} +1 -1
- package/dist/{resend.provider-BFGt6fS4.mjs → resend.provider-BCCACQAU.mjs} +5 -4
- package/dist/{resend.provider-BFGt6fS4.mjs.map → resend.provider-BCCACQAU.mjs.map} +1 -1
- package/dist/router/index.d.mts +1 -1
- package/dist/router/index.mjs +15 -10
- package/dist/{router-context-DlTxpJUG.mjs → router-context-BEJe9HEB.mjs} +2 -2
- package/dist/{router-context-DlTxpJUG.mjs.map → router-context-BEJe9HEB.mjs.map} +1 -1
- package/dist/s3-storage.provider-BLlzQYiJ.mjs +336 -0
- package/dist/s3-storage.provider-BLlzQYiJ.mjs.map +1 -0
- package/dist/seeder/index.d.mts +77 -0
- package/dist/seeder/index.d.mts.map +1 -0
- package/dist/seeder/index.mjs +8 -0
- package/dist/seeder-Cupi5jl-.mjs +132 -0
- package/dist/seeder-Cupi5jl-.mjs.map +1 -0
- package/dist/{smtp.provider-BYY-AdmU.mjs → smtp.provider-B8XtOcHU.mjs} +5 -4
- package/dist/{smtp.provider-BYY-AdmU.mjs.map → smtp.provider-B8XtOcHU.mjs.map} +1 -1
- package/dist/storage/index.d.mts +6 -272
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +15 -9
- package/dist/{storage-dgi7MG6z.mjs → storage-By_ow2o_.mjs} +35 -411
- package/dist/storage-By_ow2o_.mjs.map +1 -0
- package/dist/{stratal-D4MS_7pI.mjs → stratal-CE0iTz4f.mjs} +44 -9
- package/dist/stratal-CE0iTz4f.mjs.map +1 -0
- package/dist/types-CLhOhYsQ.d.mts +64 -0
- package/dist/types-CLhOhYsQ.d.mts.map +1 -0
- package/dist/{types-JUIHSW_a.d.mts → types-DahElfUw.d.mts} +1 -1
- package/dist/types-DahElfUw.d.mts.map +1 -0
- package/dist/usage-generator-C9hWziY4.mjs +158 -0
- package/dist/usage-generator-C9hWziY4.mjs.map +1 -0
- package/dist/{validation-DA5nptIp.mjs → validation-Bh875Lyg.mjs} +1 -1
- package/dist/{validation-DA5nptIp.mjs.map → validation-Bh875Lyg.mjs.map} +1 -1
- package/dist/websocket/index.d.mts +2 -2
- package/dist/websocket/index.mjs +5 -4
- package/dist/workers/index.d.mts +1 -1
- package/dist/workers/index.mjs +20 -13
- package/dist/workers/index.mjs.map +1 -1
- package/package.json +17 -7
- package/dist/en-C9U5-ETs.mjs.map +0 -1
- package/dist/errors-BRJgVd5-.mjs.map +0 -1
- package/dist/i18n.module-C9wQr_2k.mjs.map +0 -1
- package/dist/index-C9bIk5tt.d.mts.map +0 -1
- package/dist/logger-Bg-CuidS.mjs.map +0 -1
- package/dist/module-Dvzm4dhS.mjs.map +0 -1
- package/dist/storage-dgi7MG6z.mjs.map +0 -1
- package/dist/stratal-D4MS_7pI.mjs.map +0 -1
- package/dist/types-JUIHSW_a.d.mts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quarry-registry-DCwqVcRp.mjs","names":[],"sources":["../src/quarry/command-internals.ts","../src/quarry/errors/command-not-found.error.ts","../src/quarry/signature-parser.ts","../src/quarry/quarry-registry.ts"],"sourcesContent":["import type { Command } from './command'\nimport { COMMAND_INTERNALS } from './constants'\nimport type { CommandInput, CommandInternals, CommandResult } from './types'\n\n/** @internal Set the flat input values before calling handle() */\nexport function setCommandInputs(command: Command, values: CommandInput): void {\n command[COMMAND_INTERNALS].inputs = { ...values }\n}\n\n/** @internal Set the Quarry reference for this.call() support */\nexport function setCommandQuarry(\n command: Command,\n quarry: { call(name: string, input?: CommandInput): Promise<CommandResult> },\n): void {\n command[COMMAND_INTERNALS].quarry = quarry\n}\n\n/** @internal Collect the result after handle() completes */\nexport function getCommandResult(command: Command): CommandResult {\n const internals: CommandInternals = command[COMMAND_INTERNALS]\n return {\n exitCode: internals.exitCode,\n output: [...internals.output],\n errors: [...internals.errors],\n }\n}\n\n/** @internal Reset state between invocations */\nexport function resetCommandState(command: Command): void {\n const internals: CommandInternals = command[COMMAND_INTERNALS]\n internals.inputs = {}\n internals.output = []\n internals.errors = []\n internals.exitCode = 0\n}\n","/**\n * Thrown when a command is not found in the Quarry registry.\n */\nexport class CommandNotFoundError extends Error {\n constructor(name: string) {\n super(`Command \"${name}\" is not registered.`)\n this.name = 'CommandNotFoundError'\n }\n}\n","import { CommandError } from './errors/command.error'\nimport type { ParsedArgument, ParsedOption, ParsedSignature } from './types'\n\n/**\n * Parse a Laravel-style command signature string.\n *\n * Signature syntax:\n * command-name {arg} ... — flat command\n * group subcommand {arg} ... — subcommand hierarchy (space-separated)\n * namespace:command {arg} ... — namespaced flat command (colon-separated)\n * {--flag} {--name=} {--name=default} {--name=*} {--A|name} {--name= : desc}\n *\n * Pure function, zero dependencies, edge-compatible.\n */\nexport function parseSignature(signature: string): ParsedSignature {\n const tokens = extractTokens(signature)\n const name = extractCommandName(signature)\n const args: ParsedArgument[] = []\n const options: ParsedOption[] = []\n\n for (const token of tokens) {\n const inner = token.slice(1, -1).trim() // strip { }\n\n if (inner.startsWith('--')) {\n options.push(parseOption(inner))\n } else {\n args.push(parseArgument(inner))\n }\n }\n\n return { name, arguments: args, options }\n}\n\nfunction extractCommandName(signature: string): string {\n const match = /^[\\w:.-]+(?:\\s+[\\w:.-]+)*/.exec(signature)\n if (!match) {\n throw new CommandError(`Invalid signature: cannot extract command name from \"${signature}\"`)\n }\n return match[0]\n}\n\nfunction extractTokens(signature: string): string[] {\n const tokens: string[] = []\n const regex = /\\{[^}]+\\}/g\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(signature)) !== null) {\n tokens.push(match[0])\n }\n\n return tokens\n}\n\nfunction parseArgument(inner: string): ParsedArgument {\n const { value, description } = splitDescription(inner)\n\n // {name*} — array/variadic argument\n if (value.endsWith('*')) {\n return {\n name: value.slice(0, -1).trim(),\n required: true,\n isArray: true,\n description,\n }\n }\n\n // {name=default} — argument with default value\n const eqIdx = value.indexOf('=')\n if (eqIdx !== -1) {\n return {\n name: value.slice(0, eqIdx).trim(),\n required: false,\n default: value.slice(eqIdx + 1).trim(),\n isArray: false,\n description,\n }\n }\n\n // {name?} — optional argument\n if (value.endsWith('?')) {\n return {\n name: value.slice(0, -1).trim(),\n required: false,\n isArray: false,\n description,\n }\n }\n\n // {name} — required argument\n return {\n name: value.trim(),\n required: true,\n isArray: false,\n description,\n }\n}\n\nfunction parseOption(inner: string): ParsedOption {\n // Remove leading --\n const withoutDashes = inner.slice(2)\n const { value, description } = splitDescription(withoutDashes)\n\n // Check for alias: {--A|name...}\n let alias: string | undefined\n let optBody = value\n\n const pipeIdx = optBody.indexOf('|')\n if (pipeIdx !== -1) {\n alias = optBody.slice(0, pipeIdx).trim()\n optBody = optBody.slice(pipeIdx + 1).trim()\n }\n\n // {--name=*} — array option\n if (optBody.endsWith('=*')) {\n return {\n name: optBody.slice(0, -2).trim(),\n alias,\n isFlag: false,\n isArray: true,\n description,\n }\n }\n\n // {--name=default} — option with default value\n const eqIdx = optBody.indexOf('=')\n if (eqIdx !== -1) {\n const name = optBody.slice(0, eqIdx).trim()\n const defaultValue = optBody.slice(eqIdx + 1).trim()\n\n return {\n name,\n alias,\n isFlag: false,\n isArray: false,\n default: defaultValue || undefined,\n description,\n }\n }\n\n // {--flag} — boolean flag\n return {\n name: optBody.trim(),\n alias,\n isFlag: true,\n isArray: false,\n description,\n }\n}\n\nfunction splitDescription(value: string): { value: string; description?: string } {\n const colonIdx = value.indexOf(' : ')\n if (colonIdx === -1) {\n return { value }\n }\n\n return {\n value: value.slice(0, colonIdx).trim(),\n description: value.slice(colonIdx + 3).trim(),\n }\n}\n","import { inject } from 'tsyringe'\nimport type { Container } from '../di/container'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { GlobalErrorHandler } from '../errors/global-error-handler'\nimport type { Constructor } from '../types'\nimport { Command } from './command'\nimport { getCommandResult, setCommandInputs, setCommandQuarry } from './command-internals'\nimport { CommandNotFoundError } from './errors/command-not-found.error'\nimport { CommandError } from './errors/command.error'\nimport { parseSignature } from './signature-parser'\nimport type { CommandInput, CommandResult, ParsedSignature, Quarry } from './types'\n\n/**\n * QuarryRegistry — edge-compatible programmatic API for running commands.\n *\n * Registered as a singleton via DI_TOKENS.Quarry.\n * Commands are auto-discovered from module providers and registered at bootstrap.\n * Command constructors are stored at bootstrap; fresh instances are resolved per `call()`.\n *\n * Users should inject and type as `Quarry` (the interface), which only exposes `call()`.\n */\n@Transient(DI_TOKENS.Quarry)\nexport class QuarryRegistry implements Quarry {\n private commands = new Map<string, Constructor<Command>>()\n private signatures = new Map<string, ParsedSignature>()\n private aliases = new Map<string, string>()\n\n constructor(@inject(DI_TOKENS.Container) private readonly container: Container) { }\n\n /**\n * Execute a command by name with optional flat input.\n * A fresh command instance is resolved from the container per invocation.\n */\n async call(name: string, input?: CommandInput): Promise<CommandResult> {\n const resolvedName = this.resolveName(name)\n const CommandClass = this.commands.get(resolvedName)\n\n if (!CommandClass) {\n throw new CommandNotFoundError(name)\n }\n\n const signature = this.signatures.get(resolvedName)!\n const mergedInput = this.applyDefaults(input ?? {}, signature)\n\n // Validate required arguments\n for (const arg of signature.arguments) {\n if (arg.required && (mergedInput[arg.name] === undefined || mergedInput[arg.name] === null)) {\n throw new CommandError(`Missing required argument: ${arg.name}`)\n }\n }\n\n let command: Command | undefined\n\n try {\n // Resolve a fresh instance per invocation to avoid shared mutable state\n command = this.container.resolve<Command>(CommandClass)\n\n setCommandQuarry(command, this)\n setCommandInputs(command, mergedInput)\n\n const exitCode = await command.handle()\n const result = getCommandResult(command)\n\n if (typeof exitCode === 'number') {\n return { ...result, exitCode }\n }\n\n return result\n } catch (error) {\n if (error instanceof CommandError) {\n if (command) {\n const result = getCommandResult(command)\n return {\n exitCode: result.exitCode === 0 ? 1 : result.exitCode,\n output: result.output,\n errors: [...result.errors, error.message],\n }\n }\n return { exitCode: 1, output: [], errors: [error.message] }\n }\n\n const errorMessage = this.handleError(error)\n\n if (command) {\n const result = getCommandResult(command)\n return {\n exitCode: result.exitCode === 0 ? 1 : result.exitCode,\n output: result.output,\n errors: [...result.errors, errorMessage],\n }\n }\n return { exitCode: 1, output: [], errors: [errorMessage] }\n }\n }\n\n /**\n * Check if a command exists by name or alias.\n */\n has(name: string): boolean {\n const resolved = this.resolveName(name)\n return this.commands.has(resolved)\n }\n\n /**\n * Get a command constructor by name or alias.\n */\n get(name: string): Constructor<Command> | undefined {\n const resolved = this.resolveName(name)\n return this.commands.get(resolved)\n }\n\n /**\n * Get all registered command constructors.\n */\n all(): Map<string, Constructor<Command>> {\n return new Map(this.commands)\n }\n\n /**\n * List all commands with their descriptions and aliases.\n */\n list(): { name: string; description?: string; aliases: string[] }[] {\n const result: { name: string; description?: string; aliases: string[] }[] = []\n\n for (const [name, CommandClass] of this.commands) {\n const staticCommand = CommandClass as unknown as typeof Command\n const commandAliases: string[] = []\n\n for (const [alias, target] of this.aliases) {\n if (target === name) {\n commandAliases.push(alias)\n }\n }\n\n result.push({\n name,\n description: staticCommand.description,\n aliases: commandAliases,\n })\n }\n\n return result.sort((a, b) => a.name.localeCompare(b.name))\n }\n\n /**\n * Generate a compact listing of all commands with visual hierarchy and colors.\n */\n async listUsage(options?: { binaryName?: string; binaryLabel?: string; binaryVersion?: string }): Promise<string> {\n const commands = this.list()\n\n // Dynamic import to keep usage-generator tree-shakeable\n const { generateListing } = await import('./usage-generator')\n return generateListing(commands, this.signatures, options)\n }\n\n /**\n * Get auto-generated usage text for a command.\n */\n async usage(name: string): Promise<string> {\n const resolvedName = this.resolveName(name)\n const CommandClass = this.commands.get(resolvedName)\n\n if (!CommandClass) {\n throw new CommandNotFoundError(name)\n }\n\n const signature = this.signatures.get(resolvedName)!\n const staticCommand = CommandClass as unknown as typeof Command\n\n // Dynamic import to keep usage-generator tree-shakeable\n const { generateUsage } = await import('./usage-generator')\n return generateUsage(signature, staticCommand.description)\n }\n\n /**\n * Register a command constructor with the registry.\n * @internal Called by Application during bootstrap.\n */\n register(commandClass: Constructor<Command>): void {\n const staticCommand = commandClass as unknown as typeof Command\n\n if (!staticCommand.command) {\n throw new CommandError(`Command class ${commandClass.name} is missing static \"command\" signature`)\n }\n\n const signature = parseSignature(staticCommand.command)\n const name = signature.name\n\n if (this.commands.has(name) || this.aliases.has(name)) {\n throw new CommandError(`Duplicate command name: \"${name}\" is already registered`)\n }\n\n // Validate all aliases before any mutation\n if (staticCommand.aliases) {\n for (const alias of staticCommand.aliases) {\n if (this.commands.has(alias) || this.aliases.has(alias)) {\n throw new CommandError(`Duplicate alias: \"${alias}\" conflicts with an existing command or alias`)\n }\n }\n }\n\n // All checks passed — safe to mutate\n this.commands.set(name, commandClass)\n this.signatures.set(name, signature)\n\n if (staticCommand.aliases) {\n for (const alias of staticCommand.aliases) {\n this.aliases.set(alias, name)\n }\n }\n }\n\n private handleError(error: unknown): string {\n const errorHandler = this.container.resolve<GlobalErrorHandler>(DI_TOKENS.ErrorHandler)\n const response = errorHandler.handle(error)\n return response.message\n }\n\n private resolveName(name: string): string {\n return this.aliases.get(name) ?? name\n }\n\n private applyDefaults(input: CommandInput, signature: ParsedSignature): CommandInput {\n const result = { ...input }\n\n // Apply argument defaults\n for (const arg of signature.arguments) {\n if (result[arg.name] === undefined && arg.default !== undefined) {\n result[arg.name] = arg.default\n }\n }\n\n // Apply option defaults\n for (const opt of signature.options) {\n if (result[opt.name] === undefined) {\n if (opt.default !== undefined) {\n result[opt.name] = opt.default\n } else if (opt.isFlag) {\n result[opt.name] = false\n }\n }\n }\n\n return result\n }\n}\n"],"mappings":"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"}
|
package/dist/queue/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as QueueNames, a as InjectQueue, b as IQueueConsumer, c as QueueModule, d as SyncQueueProvider, f as CloudflareQueueProvider, g as QueueName, h as IQueueSender, i as QueueToken, l as QueueModuleOptions, m as DispatchMessage, n as QueueBindingNotFoundError, o as QueueRegistry, p as IQueueProvider, r as QUEUE_TOKENS, s as QueueProviderFactory, t as QueueProviderNotSupportedError, u as QueueSender, v as QueueManager, x as QueueMessage, y as ConsumerRegistry } from "../index-
|
|
1
|
+
import { _ as QueueNames, a as InjectQueue, b as IQueueConsumer, c as QueueModule, d as SyncQueueProvider, f as CloudflareQueueProvider, g as QueueName, h as IQueueSender, i as QueueToken, l as QueueModuleOptions, m as DispatchMessage, n as QueueBindingNotFoundError, o as QueueRegistry, p as IQueueProvider, r as QUEUE_TOKENS, s as QueueProviderFactory, t as QueueProviderNotSupportedError, u as QueueSender, v as QueueManager, x as QueueMessage, y as ConsumerRegistry } from "../index-D9iYu2Yc.mjs";
|
|
2
2
|
export { CloudflareQueueProvider, ConsumerRegistry, DispatchMessage, IQueueConsumer, IQueueProvider, IQueueSender, InjectQueue, QUEUE_TOKENS, QueueBindingNotFoundError, QueueManager, QueueMessage, QueueModule, QueueModuleOptions, QueueName, QueueNames, QueueProviderFactory, QueueProviderNotSupportedError, QueueRegistry, QueueSender, QueueToken, SyncQueueProvider };
|
package/dist/queue/index.mjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import "../errors-
|
|
2
|
-
import {
|
|
3
|
-
import "../
|
|
4
|
-
import "../
|
|
5
|
-
import "../
|
|
6
|
-
import "../
|
|
7
|
-
import
|
|
1
|
+
import "../errors-CtCi1wn6.mjs";
|
|
2
|
+
import { i as Transient, l as DI_TOKENS, n as __decorateParam, r as __decorateMetadata, t as __decorate } from "../decorate-D5j-d9_z.mjs";
|
|
3
|
+
import "../logger-BR1-s1Um.mjs";
|
|
4
|
+
import "../module-BgdxxzBe.mjs";
|
|
5
|
+
import "../events-CXl-o1Ad.mjs";
|
|
6
|
+
import "../colors-DJaRDXoS.mjs";
|
|
7
|
+
import "../command-BvCOD6df.mjs";
|
|
8
|
+
import "../is-command-BfCgWAcQ.mjs";
|
|
9
|
+
import "../is-seeder-CebjZCDn.mjs";
|
|
10
|
+
import "../middleware-C0Ebzswy.mjs";
|
|
11
|
+
import "../router-context-BEJe9HEB.mjs";
|
|
12
|
+
import { a as CloudflareQueueProvider, c as QUEUE_TOKENS, i as SyncQueueProvider, l as QueueSender, n as QueueRegistry, o as QueueProviderNotSupportedError, r as QueueProviderFactory, s as QueueBindingNotFoundError, t as QueueModule, u as ConsumerRegistry } from "../queue.module-BZvmeAMj.mjs";
|
|
8
13
|
import { inject } from "tsyringe";
|
|
9
14
|
//#region src/queue/queue-manager.ts
|
|
10
15
|
var _ref;
|
package/dist/queue/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/queue/queue-manager.ts","../../src/queue/decorators/inject-queue.decorator.ts"],"sourcesContent":["import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport { ConsumerRegistry } from './consumer-registry'\nimport type { QueueMessage } from './queue-consumer'\n\n/**\n * Queue Manager\n *\n * Singleton service for processing queue message batches.\n * Routes messages to consumers based on message type.\n *\n * **Message Routing:**\n * - Consumers declare message types they handle (e.g., ['email.send'])\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n *\n * **Note:** For sending messages to queues, use IQueueSender instances\n * obtained via @InjectQueue('queue-name') or module bindings.\n *\n * @example Processing a queue batch\n * ```typescript\n * // In Cloudflare Worker queue handler\n * await queueManager.processBatch('notifications-queue', batch)\n * ```\n */\n@Transient(DI_TOKENS.Queue)\nexport class QueueManager {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a batch of queue messages\n *\n * Routes messages to registered consumers based on message type.\n * Uses ConsumerRegistry to find matching consumers.\n *\n * @param _queueName - Name of the queue (for logging, not used for routing)\n * @param batch - Batch of messages from Cloudflare Queue\n */\n async processBatch(_queueName: string, batch: MessageBatch): Promise<void> {\n for (const message of batch.messages) {\n const queueMessage = message.body as QueueMessage\n\n // Find consumers by message type (not by queue name)\n const consumers = this.registry.getConsumers(queueMessage.type)\n\n for (const consumer of consumers) {\n try {\n await consumer.handle(queueMessage)\n message.ack()\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n if (consumer.onError) {\n await consumer.onError(errorInstance, queueMessage)\n }\n message.retry()\n }\n }\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport type { QueueName } from '../queue-name'\n\n/**\n * Inject a queue sender by name.\n *\n * Queue names are typed via module augmentation of QueueNames interface.\n * The queue name serves as both the identifier and the DI injection token.\n *\n * @param name - Queue name (typed with autocomplete if QueueNames is augmented)\n * @returns Parameter decorator for constructor injection\n *\n * @example\n * ```typescript\n * // Direct injection by queue name\n * constructor(\n * @InjectQueue('notifications-queue') private queue: IQueueSender\n * ) {}\n *\n * // Usage\n * await this.queue.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n *\n * @remarks\n * The queue must be registered via `QueueModule.registerQueue(name)` before injection.\n * For module-internal queue bindings (e.g., EmailModule), use `@inject(TOKEN)` with\n * `useExisting` provider binding instead.\n */\nexport function InjectQueue(name: QueueName): ParameterDecorator {\n return inject(name)\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/queue/queue-manager.ts","../../src/queue/decorators/inject-queue.decorator.ts"],"sourcesContent":["import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport { ConsumerRegistry } from './consumer-registry'\nimport type { QueueMessage } from './queue-consumer'\n\n/**\n * Queue Manager\n *\n * Singleton service for processing queue message batches.\n * Routes messages to consumers based on message type.\n *\n * **Message Routing:**\n * - Consumers declare message types they handle (e.g., ['email.send'])\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n *\n * **Note:** For sending messages to queues, use IQueueSender instances\n * obtained via @InjectQueue('queue-name') or module bindings.\n *\n * @example Processing a queue batch\n * ```typescript\n * // In Cloudflare Worker queue handler\n * await queueManager.processBatch('notifications-queue', batch)\n * ```\n */\n@Transient(DI_TOKENS.Queue)\nexport class QueueManager {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a batch of queue messages\n *\n * Routes messages to registered consumers based on message type.\n * Uses ConsumerRegistry to find matching consumers.\n *\n * @param _queueName - Name of the queue (for logging, not used for routing)\n * @param batch - Batch of messages from Cloudflare Queue\n */\n async processBatch(_queueName: string, batch: MessageBatch): Promise<void> {\n for (const message of batch.messages) {\n const queueMessage = message.body as QueueMessage\n\n // Find consumers by message type (not by queue name)\n const consumers = this.registry.getConsumers(queueMessage.type)\n\n for (const consumer of consumers) {\n try {\n await consumer.handle(queueMessage)\n message.ack()\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n if (consumer.onError) {\n await consumer.onError(errorInstance, queueMessage)\n }\n message.retry()\n }\n }\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport type { QueueName } from '../queue-name'\n\n/**\n * Inject a queue sender by name.\n *\n * Queue names are typed via module augmentation of QueueNames interface.\n * The queue name serves as both the identifier and the DI injection token.\n *\n * @param name - Queue name (typed with autocomplete if QueueNames is augmented)\n * @returns Parameter decorator for constructor injection\n *\n * @example\n * ```typescript\n * // Direct injection by queue name\n * constructor(\n * @InjectQueue('notifications-queue') private queue: IQueueSender\n * ) {}\n *\n * // Usage\n * await this.queue.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n *\n * @remarks\n * The queue must be registered via `QueueModule.registerQueue(name)` before injection.\n * For module-internal queue bindings (e.g., EmailModule), use `@inject(TOKEN)` with\n * `useExisting` provider binding instead.\n */\nexport function InjectQueue(name: QueueName): ParameterDecorator {\n return inject(name)\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2BO,IAAA,eAAA,MAAM,aAAa;CACxB,YACE,UACA;AADqD,OAAA,WAAA;;;;;;;;;;;CAYvD,MAAM,aAAa,YAAoB,OAAoC;AACzE,OAAK,MAAM,WAAW,MAAM,UAAU;GACpC,MAAM,eAAe,QAAQ;GAG7B,MAAM,YAAY,KAAK,SAAS,aAAa,aAAa,KAAK;AAE/D,QAAK,MAAM,YAAY,UACrB,KAAI;AACF,UAAM,SAAS,OAAO,aAAa;AACnC,YAAQ,KAAK;YACN,OAAO;IACd,MAAM,gBAAgB,iBAAiB,QACnC,QACA,IAAI,MAAM,OAAO,MAAM,CAAC;AAE5B,QAAI,SAAS,QACX,OAAM,SAAS,QAAQ,eAAe,aAAa;AAErD,YAAQ,OAAO;;;;;;CAlCxB,UAAU,UAAU,MAAM;oBAGtB,OAAO,UAAU,iBAAiB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACEvC,SAAgB,YAAY,MAAqC;AAC/D,QAAO,OAAO,KAAK"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as ApplicationError, b as ERROR_CODES, o as I18N_TOKENS, s as Scope } from "./errors-
|
|
2
|
-
import {
|
|
3
|
-
import { r as Module } from "./module-
|
|
1
|
+
import { S as ApplicationError, b as ERROR_CODES, o as I18N_TOKENS, s as Scope } from "./errors-CtCi1wn6.mjs";
|
|
2
|
+
import { i as Transient, l as DI_TOKENS, n as __decorateParam, r as __decorateMetadata, t as __decorate } from "./decorate-D5j-d9_z.mjs";
|
|
3
|
+
import { r as Module } from "./module-BgdxxzBe.mjs";
|
|
4
4
|
import { inject } from "tsyringe";
|
|
5
5
|
//#region src/queue/consumer-registry.ts
|
|
6
6
|
let ConsumerRegistry = class ConsumerRegistry {
|
|
@@ -400,4 +400,4 @@ QueueModule = _QueueModule = __decorate([Module({ providers: [
|
|
|
400
400
|
//#endregion
|
|
401
401
|
export { CloudflareQueueProvider as a, QUEUE_TOKENS as c, SyncQueueProvider as i, QueueSender as l, QueueRegistry as n, QueueProviderNotSupportedError as o, QueueProviderFactory as r, QueueBindingNotFoundError as s, QueueModule as t, ConsumerRegistry as u };
|
|
402
402
|
|
|
403
|
-
//# sourceMappingURL=queue.module-
|
|
403
|
+
//# sourceMappingURL=queue.module-BZvmeAMj.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.module-ZqaZ2iY0.mjs","names":[],"sources":["../src/queue/consumer-registry.ts","../src/queue/queue-sender.ts","../src/queue/queue.tokens.ts","../src/queue/errors/queue-binding-not-found.error.ts","../src/queue/errors/queue-provider-not-supported.error.ts","../src/queue/providers/cloudflare-queue.provider.ts","../src/queue/providers/sync-queue.provider.ts","../src/queue/services/queue-provider-factory.ts","../src/queue/queue-registry.ts","../src/queue/queue.module.ts"],"sourcesContent":["import { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { IQueueConsumer } from './queue-consumer'\n\n/**\n * Consumer Registry\n *\n * Singleton service that holds all registered queue consumers indexed by message type.\n * Consumers declare the message types they handle, and this registry routes messages\n * to the appropriate consumers based on their types.\n *\n * **Message-Type Routing:**\n * - Consumers declare `messageTypes` array (e.g., `['email.send', 'email.batch.send']`)\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n * - Use `'*'` as a wildcard to handle all message types\n *\n * @example Consumer registration\n * ```typescript\n * // In consumer.ts\n * @Transient()\n * export class EmailConsumer implements IQueueConsumer {\n * readonly messageTypes = ['email.send', 'email.batch.send']\n * // ...\n * }\n *\n * // In module.ts\n * @Module({\n * consumers: [EmailConsumer]\n * })\n *\n * // Application auto-registers via ConsumerRegistry\n * this.consumerRegistry.register(consumer)\n * ```\n */\n@Transient(DI_TOKENS.ConsumerRegistry)\nexport class ConsumerRegistry {\n /** Map from message type to consumers handling that type */\n private consumersByType = new Map<string, IQueueConsumer[]>()\n\n /** Set of all registered consumers (for iteration) */\n private allConsumers = new Set<IQueueConsumer>()\n\n /**\n * Register a queue consumer\n *\n * Indexes the consumer by each of its declared message types.\n *\n * @param consumer - Queue consumer to register\n */\n register(consumer: IQueueConsumer): void {\n if (this.allConsumers.has(consumer)) {\n return // Already registered\n }\n\n this.allConsumers.add(consumer)\n\n for (const messageType of consumer.messageTypes) {\n const existing = this.consumersByType.get(messageType) ?? []\n existing.push(consumer)\n this.consumersByType.set(messageType, existing)\n }\n }\n\n /**\n * Get all consumers that can handle a specific message type\n *\n * Returns consumers that either:\n * - Explicitly declare the message type\n * - Use '*' wildcard to handle all types\n *\n * @param messageType - The message type to find consumers for\n * @returns Array of consumers that can handle this message type\n */\n getConsumers(messageType: string): IQueueConsumer[] {\n const exactMatch = this.consumersByType.get(messageType) ?? []\n const wildcardMatch = this.consumersByType.get('*') ?? []\n\n // Combine and dedupe\n const combined = new Set([...exactMatch, ...wildcardMatch])\n return Array.from(combined)\n }\n\n /**\n * Check if any consumers can handle a message type\n *\n * @param messageType - The message type to check\n * @returns true if at least one consumer can handle this type\n */\n hasConsumers(messageType: string): boolean {\n return this.getConsumers(messageType).length > 0\n }\n\n /**\n * Get all registered message types\n *\n * @returns Array of message types with registered consumers\n */\n getMessageTypes(): string[] {\n return Array.from(this.consumersByType.keys())\n }\n\n /**\n * Get all registered consumers\n *\n * @returns Array of all registered consumers\n */\n getAllConsumers(): IQueueConsumer[] {\n return Array.from(this.allConsumers)\n }\n}\n","import type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { QueueMessage } from './queue-consumer'\nimport type { DispatchMessage, IQueueSender } from './queue-sender.interface'\n\n/**\n * Queue Sender\n *\n * Implementation of IQueueSender bound to a specific queue name.\n * Created by QueueRegistry for each registered queue.\n *\n * Automatically enriches messages with:\n * - `id`: UUID generated via crypto.randomUUID()\n * - `timestamp`: Current time in milliseconds\n * - `metadata.locale`: Current locale from I18n context\n *\n * @example\n * ```typescript\n * // Created by QueueRegistry, not directly instantiated\n * const sender = registry.getQueue('notifications-queue')\n *\n * await sender.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n */\nexport class QueueSender implements IQueueSender {\n constructor(\n private readonly queueName: string,\n private readonly provider: IQueueProvider,\n private readonly i18n: II18nService\n ) {}\n\n /**\n * Dispatch a message to this queue.\n *\n * @param message - Message to dispatch (without id/timestamp)\n */\n async dispatch<T>(message: DispatchMessage<T>): Promise<void> {\n const metadata = { ...message.metadata }\n\n if (!metadata.locale) {\n const locale = this.i18n.getLocale()\n if (locale) {\n metadata.locale = locale\n }\n }\n\n const fullMessage: QueueMessage<T> = {\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n ...message,\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined\n }\n\n await this.provider.send(this.queueName, fullMessage)\n }\n}\n","export const QUEUE_TOKENS = {\n QueueProviderFactory: Symbol.for('stratal:queue:provider:factory'),\n QueueRegistry: Symbol.for('stratal:queue:registry'),\n QueueModuleOptions: Symbol.for('stratal:queue:options'),\n} as const\n\nexport type QueueToken = (typeof QUEUE_TOKENS)[keyof typeof QUEUE_TOKENS]\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * QueueBindingNotFoundError\n *\n * Thrown when attempting to access a Cloudflare Queue binding that hasn't been configured.\n * This typically indicates that the queue binding is missing from wrangler.jsonc\n * or the environment variables are not properly set.\n */\nexport class QueueBindingNotFoundError extends ApplicationError {\n constructor(queueName: string, bindingName: string) {\n super(\n 'errors.queueBindingNotFound',\n ERROR_CODES.SYSTEM.QUEUE_BINDING_NOT_FOUND,\n { queueName, bindingName }\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * QueueProviderNotSupportedError\n *\n * Thrown when attempting to use a queue provider that is not supported.\n * Valid providers are: 'cloudflare', 'sync'\n *\n * This typically indicates an invalid QUEUE_PROVIDER environment variable.\n */\nexport class QueueProviderNotSupportedError extends ApplicationError {\n constructor(provider: string) {\n super(\n 'errors.queueProviderNotSupported',\n ERROR_CODES.SYSTEM.QUEUE_PROVIDER_NOT_SUPPORTED,\n { provider }\n )\n }\n}\n","import { inject } from 'tsyringe'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { QueueBindingNotFoundError } from '../errors'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Cloudflare Queue Provider\n *\n * Sends messages to Cloudflare Queues by resolving bindings from the environment.\n * Used in production environments where Cloudflare Workers handle queue processing.\n *\n * **Binding Resolution:**\n * Queue names are converted to binding names:\n * - `notifications-queue` → `NOTIFICATIONS_QUEUE`\n *\n * @example\n * ```typescript\n * const provider = new CloudflareQueueProvider(env)\n * await provider.send('notifications-queue', message)\n * ```\n */\n@Transient()\nexport class CloudflareQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv\n ) { }\n\n /**\n * Send a message to a Cloudflare Queue\n *\n * @param queueName - Queue name (e.g., 'notifications-queue')\n * @param message - Complete message with id, timestamp, and payload\n * @throws {QueueBindingNotFoundError} If queue binding is not configured\n */\n async send<T>(queueName: string, message: QueueMessage<T>): Promise<void> {\n const queue = this.getQueue(queueName)\n await queue.send(message)\n }\n\n /**\n * Resolve queue binding from Cloudflare environment\n *\n * Converts kebab-case queue name to UPPER_SNAKE_CASE binding name.\n *\n * @param queueName - Queue name (e.g., 'notifications-queue')\n * @returns Cloudflare Queue binding\n * @throws {QueueBindingNotFoundError} If binding not found in env\n */\n private getQueue(queueName: string): Queue {\n const bindingName = queueName.toUpperCase().replace(/-/g, '_')\n const queue = (this.env as unknown as Record<string, unknown>)[bindingName] as Queue | undefined\n\n if (!queue) {\n throw new QueueBindingNotFoundError(queueName, bindingName)\n }\n\n return queue\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { ConsumerRegistry } from '../consumer-registry'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Sync Queue Provider\n *\n * Processes messages immediately by finding matching consumers and calling\n * their handle() method directly. Used for testing and development where\n * real queue infrastructure is not available.\n *\n * **Behavior:**\n * - Messages are processed synchronously when send() is called\n * - Matching consumers are found via ConsumerRegistry by message type\n * - All matching consumers are called sequentially\n * - Errors are re-thrown after onError() is called (fail-fast for testing)\n *\n * **Consumer Matching:**\n * - Consumers are matched by message type, not queue name\n * - Wildcard ('*') matches all message types\n *\n * @example Testing with sync provider\n * ```typescript\n * const provider = new SyncQueueProvider(registry)\n * await provider.send('notifications-queue', {\n * id: '123',\n * timestamp: Date.now(),\n * type: 'email.send',\n * payload: { to: 'test@example.com' }\n * })\n * // Consumer's handle() is called immediately!\n * ```\n */\n@Transient()\nexport class SyncQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a message synchronously\n *\n * Finds all matching consumers by message type and calls their handle() method.\n * If any consumer throws, onError() is called and the error is re-thrown.\n *\n * @param _queueName - Queue name (not used for routing, consumers match by message type)\n * @param message - Complete message with id, timestamp, and payload\n * @throws Re-throws any error from consumer.handle() after calling onError()\n */\n async send<T>(_queueName: string, message: QueueMessage<T>): Promise<void> {\n // Consumers are matched by message type, not queue name\n const consumers = this.registry.getConsumers(message.type)\n\n // Process synchronously - call each matching consumer\n for (const consumer of consumers) {\n try {\n await consumer.handle(message)\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n // Call onError hook if defined\n if (consumer.onError) {\n await consumer.onError(errorInstance, message)\n }\n\n // Re-throw for fail-fast behavior in tests\n throw errorInstance\n }\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { ConsumerRegistry } from '../consumer-registry'\nimport { QueueProviderNotSupportedError } from '../errors'\nimport { CloudflareQueueProvider, SyncQueueProvider, type IQueueProvider } from '../providers'\nimport type { QueueModuleOptions } from '../queue.module'\nimport { QUEUE_TOKENS } from '../queue.tokens'\n\n/**\n * Queue Provider Factory\n *\n * Creates the appropriate queue provider based on configuration provided\n * via QueueModule.forRootAsync().\n *\n * **Provider Selection:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n *\n * @example\n * ```typescript\n * // Configuration via QueueModule.forRootAsync()\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // Factory usage (internal)\n * const factory = container.resolve(QueueProviderFactory)\n * const provider = factory.create()\n * ```\n */\n@Transient(QUEUE_TOKENS.QueueProviderFactory)\nexport class QueueProviderFactory {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry,\n @inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }) private readonly options?: QueueModuleOptions,\n ) { }\n\n /**\n * Create a queue provider based on module configuration\n *\n * @returns Queue provider instance\n * @throws {QueueProviderNotSupportedError} If provider type is not supported\n */\n create(): IQueueProvider {\n const providerType = this.options?.provider ?? 'cloudflare'\n\n switch (providerType) {\n case 'cloudflare':\n return new CloudflareQueueProvider(this.env)\n\n case 'sync':\n return new SyncQueueProvider(this.registry)\n\n default:\n throw new QueueProviderNotSupportedError(providerType)\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { I18N_TOKENS } from '../i18n/i18n.tokens'\nimport type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QueueSender } from './queue-sender'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { QueueProviderFactory } from './services'\n\n/**\n * Queue Registry\n *\n * Request-scoped factory service for creating QueueSender instances.\n * Caches senders per queue name within the request scope.\n *\n * This service is used internally by QueueModule.registerQueue() to provide\n * IQueueSender instances for each registered queue.\n *\n * **Why request-scoped?**\n * - Needs access to I18nService for locale-aware message metadata\n * - Provider is created once per request for consistency\n * - Queue senders are cached per request to avoid recreating them\n *\n * @example\n * ```typescript\n * // Used internally by QueueModule.registerQueue()\n * QueueModule.registerQueue('notifications-queue')\n *\n * // The module creates a factory provider:\n * {\n * provide: 'notifications-queue',\n * useFactory: (registry: QueueRegistry) => registry.getQueue('notifications-queue'),\n * inject: [QUEUE_TOKENS.QueueRegistry],\n * }\n * ```\n */\n@Transient(QUEUE_TOKENS.QueueRegistry)\nexport class QueueRegistry {\n private readonly provider: IQueueProvider\n private readonly senders = new Map<string, IQueueSender>()\n\n constructor(\n @inject(QUEUE_TOKENS.QueueProviderFactory) providerFactory: QueueProviderFactory,\n @inject(I18N_TOKENS.I18nService) private readonly i18n: II18nService\n ) {\n this.provider = providerFactory.create()\n }\n\n /**\n * Get or create a QueueSender for the specified queue name.\n *\n * Senders are cached per queue name within the request scope.\n *\n * @param queueName - The queue name to get a sender for\n * @returns QueueSender bound to the specified queue\n */\n getQueue(queueName: string): IQueueSender {\n let sender = this.senders.get(queueName)\n\n if (!sender) {\n sender = new QueueSender(queueName, this.provider, this.i18n)\n this.senders.set(queueName, sender)\n }\n\n return sender\n }\n}\n","/**\n * Queue Module\n *\n * Provides declarative queue infrastructure with provider abstraction.\n *\n * **Usage:**\n * ```typescript\n * // 1. Configure provider (once, in app root)\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // 2. Register queues (queue name IS the injection token)\n * QueueModule.registerQueue('notifications-queue')\n * QueueModule.registerQueue('batch-notifications-queue')\n *\n * // 3. Inject and use\n * constructor(@InjectQueue('notifications-queue') private queue: IQueueSender) {}\n * await this.queue.dispatch({ type: 'email.send', payload: {...} })\n * ```\n *\n * **Providers:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n */\n\nimport { DI_TOKENS } from '../di/tokens'\nimport { Scope } from '../di/types'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule, InjectionToken } from '../module/types'\nimport { ConsumerRegistry } from './consumer-registry'\nimport type { QueueName } from './queue-name'\nimport { QueueRegistry } from './queue-registry'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { QueueProviderFactory } from './services'\n\n/**\n * Queue module configuration options\n */\nexport interface QueueModuleOptions {\n /**\n * Queue provider type\n * - 'cloudflare': Production provider using Cloudflare Queue bindings\n * - 'sync': Testing provider that processes messages immediately\n */\n provider: 'cloudflare' | 'sync'\n}\n\n@Module({\n providers: [\n { provide: DI_TOKENS.ConsumerRegistry, useClass: ConsumerRegistry, scope: Scope.Singleton },\n { provide: QUEUE_TOKENS.QueueProviderFactory, useClass: QueueProviderFactory, scope: Scope.Singleton },\n { provide: QUEUE_TOKENS.QueueRegistry, useClass: QueueRegistry },\n ],\n})\nexport class QueueModule {\n /**\n * Configure queue infrastructure with async factory.\n *\n * Use when provider configuration depends on other services like ConfigService.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with queue infrastructure\n *\n * @example\n * ```typescript\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config: IConfigService) => ({\n * provider: config.get('queue').provider\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<QueueModuleOptions>): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: QUEUE_TOKENS.QueueModuleOptions,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n /**\n * Register a queue for injection.\n *\n * The queue name serves as both the identifier and the DI injection token.\n * Queue names are typed via module augmentation of QueueNames interface.\n *\n * @param name - Queue name (typed with autocomplete if QueueNames is augmented)\n * @returns Dynamic module that provides the queue sender\n *\n * @example\n * ```typescript\n * // In AppModule imports\n * QueueModule.registerQueue('notifications-queue')\n *\n * // Then inject using the queue name\n * constructor(@InjectQueue('notifications-queue') private queue: IQueueSender) {}\n * ```\n */\n static registerQueue(name: QueueName): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: name as InjectionToken<IQueueSender>,\n useFactory: (registry: QueueRegistry) => registry.getQueue(name),\n inject: [QUEUE_TOKENS.QueueRegistry],\n },\n ],\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"queue.module-BZvmeAMj.mjs","names":[],"sources":["../src/queue/consumer-registry.ts","../src/queue/queue-sender.ts","../src/queue/queue.tokens.ts","../src/queue/errors/queue-binding-not-found.error.ts","../src/queue/errors/queue-provider-not-supported.error.ts","../src/queue/providers/cloudflare-queue.provider.ts","../src/queue/providers/sync-queue.provider.ts","../src/queue/services/queue-provider-factory.ts","../src/queue/queue-registry.ts","../src/queue/queue.module.ts"],"sourcesContent":["import { Transient } from '../di/decorators'\nimport { DI_TOKENS } from '../di/tokens'\nimport type { IQueueConsumer } from './queue-consumer'\n\n/**\n * Consumer Registry\n *\n * Singleton service that holds all registered queue consumers indexed by message type.\n * Consumers declare the message types they handle, and this registry routes messages\n * to the appropriate consumers based on their types.\n *\n * **Message-Type Routing:**\n * - Consumers declare `messageTypes` array (e.g., `['email.send', 'email.batch.send']`)\n * - When a message arrives, consumers matching the message type are invoked\n * - A consumer can handle messages from ANY queue (routing is by type, not queue)\n * - Use `'*'` as a wildcard to handle all message types\n *\n * @example Consumer registration\n * ```typescript\n * // In consumer.ts\n * @Transient()\n * export class EmailConsumer implements IQueueConsumer {\n * readonly messageTypes = ['email.send', 'email.batch.send']\n * // ...\n * }\n *\n * // In module.ts\n * @Module({\n * consumers: [EmailConsumer]\n * })\n *\n * // Application auto-registers via ConsumerRegistry\n * this.consumerRegistry.register(consumer)\n * ```\n */\n@Transient(DI_TOKENS.ConsumerRegistry)\nexport class ConsumerRegistry {\n /** Map from message type to consumers handling that type */\n private consumersByType = new Map<string, IQueueConsumer[]>()\n\n /** Set of all registered consumers (for iteration) */\n private allConsumers = new Set<IQueueConsumer>()\n\n /**\n * Register a queue consumer\n *\n * Indexes the consumer by each of its declared message types.\n *\n * @param consumer - Queue consumer to register\n */\n register(consumer: IQueueConsumer): void {\n if (this.allConsumers.has(consumer)) {\n return // Already registered\n }\n\n this.allConsumers.add(consumer)\n\n for (const messageType of consumer.messageTypes) {\n const existing = this.consumersByType.get(messageType) ?? []\n existing.push(consumer)\n this.consumersByType.set(messageType, existing)\n }\n }\n\n /**\n * Get all consumers that can handle a specific message type\n *\n * Returns consumers that either:\n * - Explicitly declare the message type\n * - Use '*' wildcard to handle all types\n *\n * @param messageType - The message type to find consumers for\n * @returns Array of consumers that can handle this message type\n */\n getConsumers(messageType: string): IQueueConsumer[] {\n const exactMatch = this.consumersByType.get(messageType) ?? []\n const wildcardMatch = this.consumersByType.get('*') ?? []\n\n // Combine and dedupe\n const combined = new Set([...exactMatch, ...wildcardMatch])\n return Array.from(combined)\n }\n\n /**\n * Check if any consumers can handle a message type\n *\n * @param messageType - The message type to check\n * @returns true if at least one consumer can handle this type\n */\n hasConsumers(messageType: string): boolean {\n return this.getConsumers(messageType).length > 0\n }\n\n /**\n * Get all registered message types\n *\n * @returns Array of message types with registered consumers\n */\n getMessageTypes(): string[] {\n return Array.from(this.consumersByType.keys())\n }\n\n /**\n * Get all registered consumers\n *\n * @returns Array of all registered consumers\n */\n getAllConsumers(): IQueueConsumer[] {\n return Array.from(this.allConsumers)\n }\n}\n","import type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { QueueMessage } from './queue-consumer'\nimport type { DispatchMessage, IQueueSender } from './queue-sender.interface'\n\n/**\n * Queue Sender\n *\n * Implementation of IQueueSender bound to a specific queue name.\n * Created by QueueRegistry for each registered queue.\n *\n * Automatically enriches messages with:\n * - `id`: UUID generated via crypto.randomUUID()\n * - `timestamp`: Current time in milliseconds\n * - `metadata.locale`: Current locale from I18n context\n *\n * @example\n * ```typescript\n * // Created by QueueRegistry, not directly instantiated\n * const sender = registry.getQueue('notifications-queue')\n *\n * await sender.dispatch({\n * type: 'email.send',\n * payload: { to: 'user@example.com', subject: 'Hello' }\n * })\n * ```\n */\nexport class QueueSender implements IQueueSender {\n constructor(\n private readonly queueName: string,\n private readonly provider: IQueueProvider,\n private readonly i18n: II18nService\n ) {}\n\n /**\n * Dispatch a message to this queue.\n *\n * @param message - Message to dispatch (without id/timestamp)\n */\n async dispatch<T>(message: DispatchMessage<T>): Promise<void> {\n const metadata = { ...message.metadata }\n\n if (!metadata.locale) {\n const locale = this.i18n.getLocale()\n if (locale) {\n metadata.locale = locale\n }\n }\n\n const fullMessage: QueueMessage<T> = {\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n ...message,\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined\n }\n\n await this.provider.send(this.queueName, fullMessage)\n }\n}\n","export const QUEUE_TOKENS = {\n QueueProviderFactory: Symbol.for('stratal:queue:provider:factory'),\n QueueRegistry: Symbol.for('stratal:queue:registry'),\n QueueModuleOptions: Symbol.for('stratal:queue:options'),\n} as const\n\nexport type QueueToken = (typeof QUEUE_TOKENS)[keyof typeof QUEUE_TOKENS]\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * QueueBindingNotFoundError\n *\n * Thrown when attempting to access a Cloudflare Queue binding that hasn't been configured.\n * This typically indicates that the queue binding is missing from wrangler.jsonc\n * or the environment variables are not properly set.\n */\nexport class QueueBindingNotFoundError extends ApplicationError {\n constructor(queueName: string, bindingName: string) {\n super(\n 'errors.queueBindingNotFound',\n ERROR_CODES.SYSTEM.QUEUE_BINDING_NOT_FOUND,\n { queueName, bindingName }\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * QueueProviderNotSupportedError\n *\n * Thrown when attempting to use a queue provider that is not supported.\n * Valid providers are: 'cloudflare', 'sync'\n *\n * This typically indicates an invalid QUEUE_PROVIDER environment variable.\n */\nexport class QueueProviderNotSupportedError extends ApplicationError {\n constructor(provider: string) {\n super(\n 'errors.queueProviderNotSupported',\n ERROR_CODES.SYSTEM.QUEUE_PROVIDER_NOT_SUPPORTED,\n { provider }\n )\n }\n}\n","import { inject } from 'tsyringe'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { QueueBindingNotFoundError } from '../errors'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Cloudflare Queue Provider\n *\n * Sends messages to Cloudflare Queues by resolving bindings from the environment.\n * Used in production environments where Cloudflare Workers handle queue processing.\n *\n * **Binding Resolution:**\n * Queue names are converted to binding names:\n * - `notifications-queue` → `NOTIFICATIONS_QUEUE`\n *\n * @example\n * ```typescript\n * const provider = new CloudflareQueueProvider(env)\n * await provider.send('notifications-queue', message)\n * ```\n */\n@Transient()\nexport class CloudflareQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv\n ) { }\n\n /**\n * Send a message to a Cloudflare Queue\n *\n * @param queueName - Queue name (e.g., 'notifications-queue')\n * @param message - Complete message with id, timestamp, and payload\n * @throws {QueueBindingNotFoundError} If queue binding is not configured\n */\n async send<T>(queueName: string, message: QueueMessage<T>): Promise<void> {\n const queue = this.getQueue(queueName)\n await queue.send(message)\n }\n\n /**\n * Resolve queue binding from Cloudflare environment\n *\n * Converts kebab-case queue name to UPPER_SNAKE_CASE binding name.\n *\n * @param queueName - Queue name (e.g., 'notifications-queue')\n * @returns Cloudflare Queue binding\n * @throws {QueueBindingNotFoundError} If binding not found in env\n */\n private getQueue(queueName: string): Queue {\n const bindingName = queueName.toUpperCase().replace(/-/g, '_')\n const queue = (this.env as unknown as Record<string, unknown>)[bindingName] as Queue | undefined\n\n if (!queue) {\n throw new QueueBindingNotFoundError(queueName, bindingName)\n }\n\n return queue\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { ConsumerRegistry } from '../consumer-registry'\nimport type { QueueMessage } from '../queue-consumer'\nimport type { IQueueProvider } from './queue-provider.interface'\n\n/**\n * Sync Queue Provider\n *\n * Processes messages immediately by finding matching consumers and calling\n * their handle() method directly. Used for testing and development where\n * real queue infrastructure is not available.\n *\n * **Behavior:**\n * - Messages are processed synchronously when send() is called\n * - Matching consumers are found via ConsumerRegistry by message type\n * - All matching consumers are called sequentially\n * - Errors are re-thrown after onError() is called (fail-fast for testing)\n *\n * **Consumer Matching:**\n * - Consumers are matched by message type, not queue name\n * - Wildcard ('*') matches all message types\n *\n * @example Testing with sync provider\n * ```typescript\n * const provider = new SyncQueueProvider(registry)\n * await provider.send('notifications-queue', {\n * id: '123',\n * timestamp: Date.now(),\n * type: 'email.send',\n * payload: { to: 'test@example.com' }\n * })\n * // Consumer's handle() is called immediately!\n * ```\n */\n@Transient()\nexport class SyncQueueProvider implements IQueueProvider {\n constructor(\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry\n ) {}\n\n /**\n * Process a message synchronously\n *\n * Finds all matching consumers by message type and calls their handle() method.\n * If any consumer throws, onError() is called and the error is re-thrown.\n *\n * @param _queueName - Queue name (not used for routing, consumers match by message type)\n * @param message - Complete message with id, timestamp, and payload\n * @throws Re-throws any error from consumer.handle() after calling onError()\n */\n async send<T>(_queueName: string, message: QueueMessage<T>): Promise<void> {\n // Consumers are matched by message type, not queue name\n const consumers = this.registry.getConsumers(message.type)\n\n // Process synchronously - call each matching consumer\n for (const consumer of consumers) {\n try {\n await consumer.handle(message)\n } catch (error) {\n const errorInstance = error instanceof Error\n ? error\n : new Error(String(error))\n\n // Call onError hook if defined\n if (consumer.onError) {\n await consumer.onError(errorInstance, message)\n }\n\n // Re-throw for fail-fast behavior in tests\n throw errorInstance\n }\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport { type StratalEnv } from '../../env'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { ConsumerRegistry } from '../consumer-registry'\nimport { QueueProviderNotSupportedError } from '../errors'\nimport { CloudflareQueueProvider, SyncQueueProvider, type IQueueProvider } from '../providers'\nimport type { QueueModuleOptions } from '../queue.module'\nimport { QUEUE_TOKENS } from '../queue.tokens'\n\n/**\n * Queue Provider Factory\n *\n * Creates the appropriate queue provider based on configuration provided\n * via QueueModule.forRootAsync().\n *\n * **Provider Selection:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n *\n * @example\n * ```typescript\n * // Configuration via QueueModule.forRootAsync()\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // Factory usage (internal)\n * const factory = container.resolve(QueueProviderFactory)\n * const provider = factory.create()\n * ```\n */\n@Transient(QUEUE_TOKENS.QueueProviderFactory)\nexport class QueueProviderFactory {\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(DI_TOKENS.ConsumerRegistry) private readonly registry: ConsumerRegistry,\n @inject(QUEUE_TOKENS.QueueModuleOptions, { isOptional: true }) private readonly options?: QueueModuleOptions,\n ) { }\n\n /**\n * Create a queue provider based on module configuration\n *\n * @returns Queue provider instance\n * @throws {QueueProviderNotSupportedError} If provider type is not supported\n */\n create(): IQueueProvider {\n const providerType = this.options?.provider ?? 'cloudflare'\n\n switch (providerType) {\n case 'cloudflare':\n return new CloudflareQueueProvider(this.env)\n\n case 'sync':\n return new SyncQueueProvider(this.registry)\n\n default:\n throw new QueueProviderNotSupportedError(providerType)\n }\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../di/decorators'\nimport { I18N_TOKENS } from '../i18n/i18n.tokens'\nimport type { II18nService } from '../i18n/i18n.types'\nimport type { IQueueProvider } from './providers'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QueueSender } from './queue-sender'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { QueueProviderFactory } from './services'\n\n/**\n * Queue Registry\n *\n * Request-scoped factory service for creating QueueSender instances.\n * Caches senders per queue name within the request scope.\n *\n * This service is used internally by QueueModule.registerQueue() to provide\n * IQueueSender instances for each registered queue.\n *\n * **Why request-scoped?**\n * - Needs access to I18nService for locale-aware message metadata\n * - Provider is created once per request for consistency\n * - Queue senders are cached per request to avoid recreating them\n *\n * @example\n * ```typescript\n * // Used internally by QueueModule.registerQueue()\n * QueueModule.registerQueue('notifications-queue')\n *\n * // The module creates a factory provider:\n * {\n * provide: 'notifications-queue',\n * useFactory: (registry: QueueRegistry) => registry.getQueue('notifications-queue'),\n * inject: [QUEUE_TOKENS.QueueRegistry],\n * }\n * ```\n */\n@Transient(QUEUE_TOKENS.QueueRegistry)\nexport class QueueRegistry {\n private readonly provider: IQueueProvider\n private readonly senders = new Map<string, IQueueSender>()\n\n constructor(\n @inject(QUEUE_TOKENS.QueueProviderFactory) providerFactory: QueueProviderFactory,\n @inject(I18N_TOKENS.I18nService) private readonly i18n: II18nService\n ) {\n this.provider = providerFactory.create()\n }\n\n /**\n * Get or create a QueueSender for the specified queue name.\n *\n * Senders are cached per queue name within the request scope.\n *\n * @param queueName - The queue name to get a sender for\n * @returns QueueSender bound to the specified queue\n */\n getQueue(queueName: string): IQueueSender {\n let sender = this.senders.get(queueName)\n\n if (!sender) {\n sender = new QueueSender(queueName, this.provider, this.i18n)\n this.senders.set(queueName, sender)\n }\n\n return sender\n }\n}\n","/**\n * Queue Module\n *\n * Provides declarative queue infrastructure with provider abstraction.\n *\n * **Usage:**\n * ```typescript\n * // 1. Configure provider (once, in app root)\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config) => ({ provider: config.get('queue').provider })\n * })\n *\n * // 2. Register queues (queue name IS the injection token)\n * QueueModule.registerQueue('notifications-queue')\n * QueueModule.registerQueue('batch-notifications-queue')\n *\n * // 3. Inject and use\n * constructor(@InjectQueue('notifications-queue') private queue: IQueueSender) {}\n * await this.queue.dispatch({ type: 'email.send', payload: {...} })\n * ```\n *\n * **Providers:**\n * - `cloudflare`: Production provider using Cloudflare Queue bindings\n * - `sync`: Testing provider that processes messages immediately\n */\n\nimport { DI_TOKENS } from '../di/tokens'\nimport { Scope } from '../di/types'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule, InjectionToken } from '../module/types'\nimport { ConsumerRegistry } from './consumer-registry'\nimport type { QueueName } from './queue-name'\nimport { QueueRegistry } from './queue-registry'\nimport type { IQueueSender } from './queue-sender.interface'\nimport { QUEUE_TOKENS } from './queue.tokens'\nimport { QueueProviderFactory } from './services'\n\n/**\n * Queue module configuration options\n */\nexport interface QueueModuleOptions {\n /**\n * Queue provider type\n * - 'cloudflare': Production provider using Cloudflare Queue bindings\n * - 'sync': Testing provider that processes messages immediately\n */\n provider: 'cloudflare' | 'sync'\n}\n\n@Module({\n providers: [\n { provide: DI_TOKENS.ConsumerRegistry, useClass: ConsumerRegistry, scope: Scope.Singleton },\n { provide: QUEUE_TOKENS.QueueProviderFactory, useClass: QueueProviderFactory, scope: Scope.Singleton },\n { provide: QUEUE_TOKENS.QueueRegistry, useClass: QueueRegistry },\n ],\n})\nexport class QueueModule {\n /**\n * Configure queue infrastructure with async factory.\n *\n * Use when provider configuration depends on other services like ConfigService.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with queue infrastructure\n *\n * @example\n * ```typescript\n * QueueModule.forRootAsync({\n * inject: [CONFIG_TOKENS.ConfigService],\n * useFactory: (config: IConfigService) => ({\n * provider: config.get('queue').provider\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<QueueModuleOptions>): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: QUEUE_TOKENS.QueueModuleOptions,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n /**\n * Register a queue for injection.\n *\n * The queue name serves as both the identifier and the DI injection token.\n * Queue names are typed via module augmentation of QueueNames interface.\n *\n * @param name - Queue name (typed with autocomplete if QueueNames is augmented)\n * @returns Dynamic module that provides the queue sender\n *\n * @example\n * ```typescript\n * // In AppModule imports\n * QueueModule.registerQueue('notifications-queue')\n *\n * // Then inject using the queue name\n * constructor(@InjectQueue('notifications-queue') private queue: IQueueSender) {}\n * ```\n */\n static registerQueue(name: QueueName): DynamicModule {\n return {\n module: QueueModule,\n providers: [\n {\n provide: name as InjectionToken<IQueueSender>,\n useFactory: (registry: QueueRegistry) => registry.getQueue(name),\n inject: [QUEUE_TOKENS.QueueRegistry],\n },\n ],\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import "./errors-
|
|
2
|
-
import "./
|
|
1
|
+
import "./errors-CtCi1wn6.mjs";
|
|
2
|
+
import "./decorate-D5j-d9_z.mjs";
|
|
3
|
+
import "./logger-BR1-s1Um.mjs";
|
|
3
4
|
import { EmailResendApiFailedError, ResendApiKeyMissingError } from "./email/index.mjs";
|
|
4
|
-
import { t as BaseEmailProvider } from "./base-email.provider-
|
|
5
|
+
import { t as BaseEmailProvider } from "./base-email.provider-Cuw4OAB0.mjs";
|
|
5
6
|
import { Resend } from "resend";
|
|
6
7
|
//#region src/email/providers/resend.provider.ts
|
|
7
8
|
/**
|
|
@@ -66,4 +67,4 @@ var ResendProvider = class extends BaseEmailProvider {
|
|
|
66
67
|
//#endregion
|
|
67
68
|
export { ResendProvider };
|
|
68
69
|
|
|
69
|
-
//# sourceMappingURL=resend.provider-
|
|
70
|
+
//# sourceMappingURL=resend.provider-BCCACQAU.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resend.provider-
|
|
1
|
+
{"version":3,"file":"resend.provider-BCCACQAU.mjs","names":[],"sources":["../src/email/providers/resend.provider.ts"],"sourcesContent":["import { Resend } from 'resend'\nimport type { ResolvedEmailAttachment, ResolvedEmailMessage } from '../contracts'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EmailResendApiFailedError, ResendApiKeyMissingError } from '../errors'\nimport { BaseEmailProvider } from './base-email.provider'\nimport type { EmailSendResult } from './email-provider.interface'\n\n/**\n * Resend Email Provider\n *\n * Implementation of IEmailProvider using Resend API\n * Docs: https://resend.com/docs\n */\nexport class ResendProvider extends BaseEmailProvider {\n private readonly client: Resend\n private readonly defaultFrom: { name: string; email: string }\n\n constructor(\n private readonly options: EmailModuleOptions\n ) {\n super()\n\n // Validate Resend API key\n if (!this.options.apiKey) {\n throw new ResendApiKeyMissingError()\n }\n\n this.client = new Resend(this.options.apiKey)\n this.defaultFrom = this.options.from\n }\n\n async send(message: ResolvedEmailMessage): Promise<EmailSendResult> {\n try {\n const from = message.from\n ? `${message.from.name} <${message.from.email}>`\n : `${this.defaultFrom.name} <${this.defaultFrom.email}>`\n\n const to = Array.isArray(message.to) ? message.to : [message.to]\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- Resend SDK types\n const response = await this.client.emails.send({\n from,\n to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment -- Resend template extension\n ...((message as any).template && { template: (message as any).template }),\n ...(message.replyTo && { replyTo: message.replyTo }),\n ...(message.cc && { cc: message.cc }),\n ...(message.bcc && { bcc: message.bcc }),\n ...(message.attachments && {\n attachments: await Promise.all(\n message.attachments.map(async attachment => ({\n filename: attachment.filename,\n content: await this.toBuffer(attachment.content),\n }))\n ),\n }),\n })\n\n if (response.error) {\n throw new EmailResendApiFailedError()\n }\n\n return {\n messageId: response.data.id,\n accepted: true,\n metadata: {\n provider: 'resend',\n },\n }\n } catch (error) {\n if (error instanceof EmailResendApiFailedError || error instanceof ResendApiKeyMissingError) {\n throw error\n }\n\n throw new EmailResendApiFailedError()\n }\n }\n\n /**\n * Convert attachment content to Buffer\n *\n * Resend SDK expects Buffer for attachment content.\n * If content is already a Buffer, return as-is.\n * If content is a ReadableStream, convert to Buffer.\n */\n private async toBuffer(content: ResolvedEmailAttachment['content']): Promise<Buffer> {\n if (Buffer.isBuffer(content)) {\n return content\n }\n // Convert ReadableStream to Buffer\n const response = new Response(content)\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,iBAAb,cAAoC,kBAAkB;CACpD;CACA;CAEA,YACE,SACA;AACA,SAAO;AAFU,OAAA,UAAA;AAKjB,MAAI,CAAC,KAAK,QAAQ,OAChB,OAAM,IAAI,0BAA0B;AAGtC,OAAK,SAAS,IAAI,OAAO,KAAK,QAAQ,OAAO;AAC7C,OAAK,cAAc,KAAK,QAAQ;;CAGlC,MAAM,KAAK,SAAyD;AAClE,MAAI;GACF,MAAM,OAAO,QAAQ,OACjB,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,KAC5C,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,MAAM;GAExD,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG,GAAG,QAAQ,KAAK,CAAC,QAAQ,GAAG;GAGhE,MAAM,WAAW,MAAM,KAAK,OAAO,OAAO,KAAK;IAC7C;IACA;IACA,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,MAAM,QAAQ;IAEd,GAAK,QAAgB,YAAY,EAAE,UAAW,QAAgB,UAAU;IACxE,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;IACnD,GAAI,QAAQ,MAAM,EAAE,IAAI,QAAQ,IAAI;IACpC,GAAI,QAAQ,OAAO,EAAE,KAAK,QAAQ,KAAK;IACvC,GAAI,QAAQ,eAAe,EACzB,aAAa,MAAM,QAAQ,IACzB,QAAQ,YAAY,IAAI,OAAM,gBAAe;KAC3C,UAAU,WAAW;KACrB,SAAS,MAAM,KAAK,SAAS,WAAW,QAAQ;KACjD,EAAE,CACJ,EACF;IACF,CAAC;AAEF,OAAI,SAAS,MACX,OAAM,IAAI,2BAA2B;AAGvC,UAAO;IACL,WAAW,SAAS,KAAK;IACzB,UAAU;IACV,UAAU,EACR,UAAU,UACX;IACF;WACM,OAAO;AACd,OAAI,iBAAiB,6BAA6B,iBAAiB,yBACjE,OAAM;AAGR,SAAM,IAAI,2BAA2B;;;;;;;;;;CAWzC,MAAc,SAAS,SAA8D;AACnF,MAAI,OAAO,SAAS,QAAQ,CAC1B,QAAO;EAIT,MAAM,cAAc,MADH,IAAI,SAAS,QAAQ,CACH,aAAa;AAChD,SAAO,OAAO,KAAK,YAAY"}
|
package/dist/router/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as Delete, At as RouteConfig, B as getControllerVersion, Bt as SECURITY_SCHEMES, C as successMessageSchema, Ct as IController, D as getDecoratedMethods, Dt as HttpRouteMetadata, E as Route, Et as ControllerOptions, F as getHttpDecoratedMethods, Ft as SecurityScheme, H as RouteRegistrationService, I as getHttpRouteMetadata, It as VersioningOptions, L as Controller, Lt as HTTP_METHODS, M as Patch, Mt as RouteResponseObject, N as Post, Nt as RouterEnv, O as getRouteConfig, Ot as RouteBody, P as Put, Pt as RouterVariables, R as getControllerOptions, Rt as ROUTER_CONTEXT_KEYS, S as paginationQuerySchema, St as Middleware, T as validationErrorResponseSchema, Tt as RouterContext, U as HonoApp, V as ROUTER_TOKENS, Vt as VERSION_NEUTRAL, _ as ControllerRegistrationError, b as errorResponseSchema, d as SSEMessage, f as SSEStreamingApi, g as OpenAPIRouteRegistrationError, h as OpenAPIValidationError, j as Get, jt as RouteResponse, k as All, kt as RouteBodyObject, m as RouteNotFoundError, p as StreamingApi, v as HonoAppAlreadyConfiguredError, w as uuidParamSchema, x as paginatedResponseSchema, y as commonErrorSchemas, z as getControllerRoute, zt as ROUTE_METADATA_KEYS } from "../index-
|
|
1
|
+
import { A as Delete, At as RouteConfig, B as getControllerVersion, Bt as SECURITY_SCHEMES, C as successMessageSchema, Ct as IController, D as getDecoratedMethods, Dt as HttpRouteMetadata, E as Route, Et as ControllerOptions, F as getHttpDecoratedMethods, Ft as SecurityScheme, H as RouteRegistrationService, I as getHttpRouteMetadata, It as VersioningOptions, L as Controller, Lt as HTTP_METHODS, M as Patch, Mt as RouteResponseObject, N as Post, Nt as RouterEnv, O as getRouteConfig, Ot as RouteBody, P as Put, Pt as RouterVariables, R as getControllerOptions, Rt as ROUTER_CONTEXT_KEYS, S as paginationQuerySchema, St as Middleware, T as validationErrorResponseSchema, Tt as RouterContext, U as HonoApp, V as ROUTER_TOKENS, Vt as VERSION_NEUTRAL, _ as ControllerRegistrationError, b as errorResponseSchema, d as SSEMessage, f as SSEStreamingApi, g as OpenAPIRouteRegistrationError, h as OpenAPIValidationError, j as Get, jt as RouteResponse, k as All, kt as RouteBodyObject, m as RouteNotFoundError, p as StreamingApi, v as HonoAppAlreadyConfiguredError, w as uuidParamSchema, x as paginatedResponseSchema, y as commonErrorSchemas, z as getControllerRoute, zt as ROUTE_METADATA_KEYS } from "../index-BJWm863C.mjs";
|
|
2
2
|
export { All, Controller, ControllerOptions, ControllerRegistrationError, Delete, Get, HTTP_METHODS, HonoApp, HonoAppAlreadyConfiguredError, HttpRouteMetadata, IController, Middleware, OpenAPIRouteRegistrationError, OpenAPIValidationError, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, Route, RouteBody, RouteBodyObject, RouteConfig, RouteNotFoundError, RouteRegistrationService, RouteResponse, RouteResponseObject, RouterContext, RouterEnv, RouterVariables, SECURITY_SCHEMES, SSEMessage, SSEStreamingApi, SecurityScheme, StreamingApi, VERSION_NEUTRAL, VersioningOptions, commonErrorSchemas, errorResponseSchema, getControllerOptions, getControllerRoute, getControllerVersion, getDecoratedMethods, getHttpDecoratedMethods, getHttpRouteMetadata, getRouteConfig, paginatedResponseSchema, paginationQuerySchema, successMessageSchema, uuidParamSchema, validationErrorResponseSchema };
|
package/dist/router/index.mjs
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { v as ROUTER_TOKENS } from "../errors-
|
|
2
|
-
import "../
|
|
3
|
-
import "../
|
|
4
|
-
import "../
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import "../
|
|
8
|
-
import
|
|
9
|
-
import "../
|
|
10
|
-
import "../
|
|
1
|
+
import { v as ROUTER_TOKENS } from "../errors-CtCi1wn6.mjs";
|
|
2
|
+
import "../decorate-D5j-d9_z.mjs";
|
|
3
|
+
import "../logger-BR1-s1Um.mjs";
|
|
4
|
+
import "../module-BgdxxzBe.mjs";
|
|
5
|
+
import "../events-CXl-o1Ad.mjs";
|
|
6
|
+
import "../colors-DJaRDXoS.mjs";
|
|
7
|
+
import "../command-BvCOD6df.mjs";
|
|
8
|
+
import "../is-command-BfCgWAcQ.mjs";
|
|
9
|
+
import "../is-seeder-CebjZCDn.mjs";
|
|
10
|
+
import { a as getControllerVersion, i as getControllerRoute, n as Controller, r as getControllerOptions } from "../middleware-C0Ebzswy.mjs";
|
|
11
|
+
import { a as ROUTER_CONTEXT_KEYS, c as VERSION_NEUTRAL, o as ROUTE_METADATA_KEYS, r as HTTP_METHODS, s as SECURITY_SCHEMES, t as RouterContext } from "../router-context-BEJe9HEB.mjs";
|
|
12
|
+
import "../validation-Bh875Lyg.mjs";
|
|
13
|
+
import { C as Get, D as getHttpDecoratedMethods, E as Put, O as getHttpRouteMetadata, S as Delete, T as Post, a as errorResponseSchema, b as getRouteConfig, c as successMessageSchema, d as RouteNotFoundError, f as OpenAPIValidationError, h as HonoAppAlreadyConfiguredError, i as commonErrorSchemas, l as uuidParamSchema, m as ControllerRegistrationError, n as HonoApp, o as paginatedResponseSchema, p as OpenAPIRouteRegistrationError, r as RouteRegistrationService, s as paginationQuerySchema, u as validationErrorResponseSchema, v as Route, w as Patch, x as All, y as getDecoratedMethods } from "../i18n.module-W8OJxg3d.mjs";
|
|
14
|
+
import "../guards-DUk_Kzst.mjs";
|
|
15
|
+
import "../gateway-context-BkZ4UKaX.mjs";
|
|
11
16
|
export { All, Controller, ControllerRegistrationError, Delete, Get, HTTP_METHODS, HonoApp, HonoAppAlreadyConfiguredError, OpenAPIRouteRegistrationError, OpenAPIValidationError, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, Route, RouteNotFoundError, RouteRegistrationService, RouterContext, SECURITY_SCHEMES, VERSION_NEUTRAL, commonErrorSchemas, errorResponseSchema, getControllerOptions, getControllerRoute, getControllerVersion, getDecoratedMethods, getHttpDecoratedMethods, getHttpRouteMetadata, getRouteConfig, paginatedResponseSchema, paginationQuerySchema, successMessageSchema, uuidParamSchema, validationErrorResponseSchema };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as RequestContainerNotInitializedError } from "./errors-
|
|
1
|
+
import { n as RequestContainerNotInitializedError } from "./errors-CtCi1wn6.mjs";
|
|
2
2
|
import { stream, streamSSE, streamText } from "hono/streaming";
|
|
3
3
|
//#region src/router/constants.ts
|
|
4
4
|
/**
|
|
@@ -261,4 +261,4 @@ var RouterContext = class {
|
|
|
261
261
|
//#endregion
|
|
262
262
|
export { ROUTER_CONTEXT_KEYS as a, VERSION_NEUTRAL as c, METHOD_STATUS_CODES as i, DEFAULT_CONTENT_TYPE as n, ROUTE_METADATA_KEYS as o, HTTP_METHODS as r, SECURITY_SCHEMES as s, RouterContext as t };
|
|
263
263
|
|
|
264
|
-
//# sourceMappingURL=router-context-
|
|
264
|
+
//# sourceMappingURL=router-context-BEJe9HEB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-context-DlTxpJUG.mjs","names":["honoStream","honoStreamText","honoStreamSSE"],"sources":["../src/router/constants.ts","../src/router/router-context.ts"],"sourcesContent":["/**\n * Type-safe context keys for Hono router variables\n * Using symbols to avoid string collisions\n */\nexport const ROUTER_CONTEXT_KEYS = {\n REQUEST_CONTAINER: 'requestContainer',\n LOCALE: 'locale'\n} as const satisfies Record<string, string>\n\n/**\n * Metadata keys for storing route and controller configuration\n * Using symbols to avoid collisions with other decorators\n */\nexport const ROUTE_METADATA_KEYS = {\n CONTROLLER_ROUTE: Symbol.for('stratal:controller:route'),\n CONTROLLER_OPTIONS: Symbol.for('stratal:controller:options'),\n CONTROLLER_MIDDLEWARES: Symbol.for('stratal:controller:middlewares'),\n ROUTE_CONFIG: Symbol.for('stratal:route:config'),\n DECORATED_METHODS: Symbol.for('stratal:decorated:methods'),\n HTTP_ROUTE_CONFIG: Symbol.for('stratal:http-route:config'),\n HTTP_DECORATED_METHODS: Symbol.for('stratal:http-decorated:methods'),\n AUTH_GUARD: Symbol.for('stratal:auth:guard'),\n GATEWAY_MARKER: Symbol.for('stratal:gateway:marker'),\n WS_ON_MESSAGE: Symbol.for('stratal:ws:on-message'),\n WS_ON_CLOSE: Symbol.for('stratal:ws:on-close'),\n WS_ON_ERROR: Symbol.for('stratal:ws:on-error'),\n} as const\n\n/**\n * Security scheme identifiers for OpenAPI\n * These reference the security scheme definitions in security.schemas.ts\n */\nexport const SECURITY_SCHEMES = {\n BEARER_AUTH: 'bearerAuth',\n API_KEY: 'apiKey',\n SESSION_COOKIE: 'sessionCookie'\n} as const\n\n/**\n * HTTP method mapping for RESTful controller methods\n * Maps controller method names to HTTP verbs and path patterns\n */\nexport const HTTP_METHODS = {\n index: { method: 'get', path: '' } as const,\n show: { method: 'get', path: '/:id' } as const,\n create: { method: 'post', path: '' } as const,\n update: { method: 'put', path: '/:id' } as const,\n patch: { method: 'patch', path: '/:id' } as const,\n destroy: { method: 'delete', path: '/:id' } as const\n} as const\n\n/**\n * Default success status codes for RESTful controller methods\n * Used by @Route() decorator to auto-derive response status\n */\nexport const METHOD_STATUS_CODES = {\n index: 200,\n show: 200,\n create: 201,\n update: 200,\n patch: 200,\n destroy: 200\n} as const\n\n/**\n * Sentinel symbol to opt a controller out of versioning.\n * When used as the version, no prefix is applied even when defaultVersion is set.\n */\nexport const VERSION_NEUTRAL = Symbol.for('stratal:version:neutral')\n\n/**\n * Default content type for request bodies and responses\n */\nexport const DEFAULT_CONTENT_TYPE = 'application/json'\n","import type { Context } from 'hono'\nimport type { SSEStreamingApi } from 'hono/streaming'\nimport { stream as honoStream, streamSSE as honoStreamSSE, streamText as honoStreamText } from 'hono/streaming'\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status'\nimport type { StreamingApi } from 'hono/utils/stream'\nimport type { Container } from '../di/container'\nimport { RequestContainerNotInitializedError } from '../errors'\nimport { ROUTER_CONTEXT_KEYS } from './constants'\nimport type { RouterEnv } from './types'\n\nexport type ContextQueryResult<R extends Record<string, unknown> | undefined, K extends string | undefined> = K extends string ? string : R extends undefined ? Record<string, unknown> : R\n\n/**\n * Router context wrapper with helper methods\n *\n * Provides convenient access to Hono's context and common request/response operations.\n * The native Hono context is available via the `c` property for advanced use cases.\n *\n * @example\n * ```typescript\n * async index(ctx: RouterContext): Promise<Response> {\n * // Use helper methods\n * const users = await this.service.findAll()\n * return ctx.json(users)\n * }\n *\n * async show(ctx: RouterContext): Promise<Response> {\n * // Access route params\n * const id = ctx.param('id')\n * const user = await this.service.findById(id)\n * return ctx.json(user)\n * }\n *\n * async create(ctx: RouterContext): Promise<Response> {\n * // Parse request body\n * const body = await ctx.body<CreateUserInput>()\n * const user = await this.service.create(body)\n * return ctx.json(user, 201)\n * }\n * ```\n */\nexport class RouterContext<T extends RouterEnv = RouterEnv> {\n /**\n * Native Hono context\n * Access for advanced use cases not covered by helper methods\n */\n constructor(\n public readonly c: Context<T>\n ) { }\n\n /**\n * Get request-scoped DI container\n * Contains request-specific services and context (AuthContext)\n *\n * @throws Error if container not initialized\n */\n getContainer(): Container {\n const container = this.c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n if (!container) {\n throw new RequestContainerNotInitializedError()\n }\n return container as Container\n }\n\n /**\n * Set locale for the current request\n * Locale is determined by X-Locale header or defaults to config\n *\n * @param locale - Locale code (e.g., 'en', 'fr')\n */\n setLocale(locale: string): void {\n this.c.set(ROUTER_CONTEXT_KEYS.LOCALE, locale)\n }\n\n /**\n * Get locale for the current request\n *\n * @returns Current locale code\n */\n getLocale(): string {\n const locale = this.c.get(ROUTER_CONTEXT_KEYS.LOCALE)\n return (locale as string) || 'en'\n }\n\n /**\n * Return JSON response\n *\n * @param data - Data to serialize as JSON\n * @param status - HTTP status code (default: 200)\n */\n json(data: object, status?: ContentfulStatusCode): Response {\n return this.c.json(data, status)\n }\n\n /**\n * Get route parameter value\n *\n * @param key - Parameter name (e.g., 'id' for /users/:id)\n */\n param(key: string): string {\n return (this.c.req as unknown as { valid(target: 'param'): Record<string, string> }).valid('param')[key]\n }\n\n /**\n * Get query parameter value\n *\n * @param key - Query parameter name\n */\n query<R extends Record<string, unknown> | undefined = undefined, K extends string | undefined = undefined>(key?: K): ContextQueryResult<R, K> {\n const validated = (this.c.req as unknown as { valid(target: 'query'): Record<string, unknown> }).valid('query')\n return key ? validated[key] as ContextQueryResult<R, K> : validated as ContextQueryResult<R, K>\n }\n\n /**\n * Get request header value\n *\n * @param name - Header name (case-insensitive)\n */\n header(name: string): string | undefined {\n return this.c.req.header(name)\n }\n\n /**\n * Get validated request body from OpenAPI route\n * Returns pre-validated data that has passed schema validation\n *\n * @returns Validated JSON body\n */\n body<T>(): Promise<T> {\n // Type assertion needed because req.valid() is type-safe per route\n // but this is a generic helper method that works across all routes\n return (this.c.req as unknown as { valid(target: 'json'): Promise<T> }).valid('json')\n }\n\n /**\n * Return text response\n *\n * @param text - Text content\n * @param status - HTTP status code (default: 200)\n */\n text(text: string, status?: ContentfulStatusCode): Response {\n return this.c.text(text, status)\n }\n\n /**\n * Return HTML response\n *\n * @param html - HTML content\n * @param status - HTTP status code (default: 200)\n */\n html(html: string, status?: ContentfulStatusCode): Response {\n return this.c.html(html, status)\n }\n\n /**\n * Redirect to another URL\n *\n * @param url - Target URL\n * @param status - HTTP status code (default: 302)\n */\n redirect(url: string, status?: RedirectStatusCode): Response {\n return this.c.redirect(url, status)\n }\n\n /**\n * Return a streaming response (binary/generic)\n *\n * @param callback - Async function that writes to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n stream(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response {\n return honoStream(this.c, callback, onError)\n }\n\n /**\n * Return a streaming text response\n *\n * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.\n *\n * @param callback - Async function that writes text to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n streamText(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response {\n this.c.header('Content-Encoding', 'Identity')\n return honoStreamText(this.c, callback, onError)\n }\n\n /**\n * Return a Server-Sent Events (SSE) streaming response\n *\n * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.\n *\n * @param callback - Async function that writes SSE events to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n streamSSE(callback: (stream: SSEStreamingApi) => Promise<void>, onError?: (err: Error, stream: SSEStreamingApi) => Promise<void>): Response {\n this.c.header('Content-Encoding', 'Identity')\n return honoStreamSSE(this.c, callback, onError)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router-context-BEJe9HEB.mjs","names":["honoStream","honoStreamText","honoStreamSSE"],"sources":["../src/router/constants.ts","../src/router/router-context.ts"],"sourcesContent":["/**\n * Type-safe context keys for Hono router variables\n * Using symbols to avoid string collisions\n */\nexport const ROUTER_CONTEXT_KEYS = {\n REQUEST_CONTAINER: 'requestContainer',\n LOCALE: 'locale'\n} as const satisfies Record<string, string>\n\n/**\n * Metadata keys for storing route and controller configuration\n * Using symbols to avoid collisions with other decorators\n */\nexport const ROUTE_METADATA_KEYS = {\n CONTROLLER_ROUTE: Symbol.for('stratal:controller:route'),\n CONTROLLER_OPTIONS: Symbol.for('stratal:controller:options'),\n CONTROLLER_MIDDLEWARES: Symbol.for('stratal:controller:middlewares'),\n ROUTE_CONFIG: Symbol.for('stratal:route:config'),\n DECORATED_METHODS: Symbol.for('stratal:decorated:methods'),\n HTTP_ROUTE_CONFIG: Symbol.for('stratal:http-route:config'),\n HTTP_DECORATED_METHODS: Symbol.for('stratal:http-decorated:methods'),\n AUTH_GUARD: Symbol.for('stratal:auth:guard'),\n GATEWAY_MARKER: Symbol.for('stratal:gateway:marker'),\n WS_ON_MESSAGE: Symbol.for('stratal:ws:on-message'),\n WS_ON_CLOSE: Symbol.for('stratal:ws:on-close'),\n WS_ON_ERROR: Symbol.for('stratal:ws:on-error'),\n} as const\n\n/**\n * Security scheme identifiers for OpenAPI\n * These reference the security scheme definitions in security.schemas.ts\n */\nexport const SECURITY_SCHEMES = {\n BEARER_AUTH: 'bearerAuth',\n API_KEY: 'apiKey',\n SESSION_COOKIE: 'sessionCookie'\n} as const\n\n/**\n * HTTP method mapping for RESTful controller methods\n * Maps controller method names to HTTP verbs and path patterns\n */\nexport const HTTP_METHODS = {\n index: { method: 'get', path: '' } as const,\n show: { method: 'get', path: '/:id' } as const,\n create: { method: 'post', path: '' } as const,\n update: { method: 'put', path: '/:id' } as const,\n patch: { method: 'patch', path: '/:id' } as const,\n destroy: { method: 'delete', path: '/:id' } as const\n} as const\n\n/**\n * Default success status codes for RESTful controller methods\n * Used by @Route() decorator to auto-derive response status\n */\nexport const METHOD_STATUS_CODES = {\n index: 200,\n show: 200,\n create: 201,\n update: 200,\n patch: 200,\n destroy: 200\n} as const\n\n/**\n * Sentinel symbol to opt a controller out of versioning.\n * When used as the version, no prefix is applied even when defaultVersion is set.\n */\nexport const VERSION_NEUTRAL = Symbol.for('stratal:version:neutral')\n\n/**\n * Default content type for request bodies and responses\n */\nexport const DEFAULT_CONTENT_TYPE = 'application/json'\n","import type { Context } from 'hono'\nimport type { SSEStreamingApi } from 'hono/streaming'\nimport { stream as honoStream, streamSSE as honoStreamSSE, streamText as honoStreamText } from 'hono/streaming'\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status'\nimport type { StreamingApi } from 'hono/utils/stream'\nimport type { Container } from '../di/container'\nimport { RequestContainerNotInitializedError } from '../errors'\nimport { ROUTER_CONTEXT_KEYS } from './constants'\nimport type { RouterEnv } from './types'\n\nexport type ContextQueryResult<R extends Record<string, unknown> | undefined, K extends string | undefined> = K extends string ? string : R extends undefined ? Record<string, unknown> : R\n\n/**\n * Router context wrapper with helper methods\n *\n * Provides convenient access to Hono's context and common request/response operations.\n * The native Hono context is available via the `c` property for advanced use cases.\n *\n * @example\n * ```typescript\n * async index(ctx: RouterContext): Promise<Response> {\n * // Use helper methods\n * const users = await this.service.findAll()\n * return ctx.json(users)\n * }\n *\n * async show(ctx: RouterContext): Promise<Response> {\n * // Access route params\n * const id = ctx.param('id')\n * const user = await this.service.findById(id)\n * return ctx.json(user)\n * }\n *\n * async create(ctx: RouterContext): Promise<Response> {\n * // Parse request body\n * const body = await ctx.body<CreateUserInput>()\n * const user = await this.service.create(body)\n * return ctx.json(user, 201)\n * }\n * ```\n */\nexport class RouterContext<T extends RouterEnv = RouterEnv> {\n /**\n * Native Hono context\n * Access for advanced use cases not covered by helper methods\n */\n constructor(\n public readonly c: Context<T>\n ) { }\n\n /**\n * Get request-scoped DI container\n * Contains request-specific services and context (AuthContext)\n *\n * @throws Error if container not initialized\n */\n getContainer(): Container {\n const container = this.c.get(ROUTER_CONTEXT_KEYS.REQUEST_CONTAINER)\n if (!container) {\n throw new RequestContainerNotInitializedError()\n }\n return container as Container\n }\n\n /**\n * Set locale for the current request\n * Locale is determined by X-Locale header or defaults to config\n *\n * @param locale - Locale code (e.g., 'en', 'fr')\n */\n setLocale(locale: string): void {\n this.c.set(ROUTER_CONTEXT_KEYS.LOCALE, locale)\n }\n\n /**\n * Get locale for the current request\n *\n * @returns Current locale code\n */\n getLocale(): string {\n const locale = this.c.get(ROUTER_CONTEXT_KEYS.LOCALE)\n return (locale as string) || 'en'\n }\n\n /**\n * Return JSON response\n *\n * @param data - Data to serialize as JSON\n * @param status - HTTP status code (default: 200)\n */\n json(data: object, status?: ContentfulStatusCode): Response {\n return this.c.json(data, status)\n }\n\n /**\n * Get route parameter value\n *\n * @param key - Parameter name (e.g., 'id' for /users/:id)\n */\n param(key: string): string {\n return (this.c.req as unknown as { valid(target: 'param'): Record<string, string> }).valid('param')[key]\n }\n\n /**\n * Get query parameter value\n *\n * @param key - Query parameter name\n */\n query<R extends Record<string, unknown> | undefined = undefined, K extends string | undefined = undefined>(key?: K): ContextQueryResult<R, K> {\n const validated = (this.c.req as unknown as { valid(target: 'query'): Record<string, unknown> }).valid('query')\n return key ? validated[key] as ContextQueryResult<R, K> : validated as ContextQueryResult<R, K>\n }\n\n /**\n * Get request header value\n *\n * @param name - Header name (case-insensitive)\n */\n header(name: string): string | undefined {\n return this.c.req.header(name)\n }\n\n /**\n * Get validated request body from OpenAPI route\n * Returns pre-validated data that has passed schema validation\n *\n * @returns Validated JSON body\n */\n body<T>(): Promise<T> {\n // Type assertion needed because req.valid() is type-safe per route\n // but this is a generic helper method that works across all routes\n return (this.c.req as unknown as { valid(target: 'json'): Promise<T> }).valid('json')\n }\n\n /**\n * Return text response\n *\n * @param text - Text content\n * @param status - HTTP status code (default: 200)\n */\n text(text: string, status?: ContentfulStatusCode): Response {\n return this.c.text(text, status)\n }\n\n /**\n * Return HTML response\n *\n * @param html - HTML content\n * @param status - HTTP status code (default: 200)\n */\n html(html: string, status?: ContentfulStatusCode): Response {\n return this.c.html(html, status)\n }\n\n /**\n * Redirect to another URL\n *\n * @param url - Target URL\n * @param status - HTTP status code (default: 302)\n */\n redirect(url: string, status?: RedirectStatusCode): Response {\n return this.c.redirect(url, status)\n }\n\n /**\n * Return a streaming response (binary/generic)\n *\n * @param callback - Async function that writes to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n stream(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response {\n return honoStream(this.c, callback, onError)\n }\n\n /**\n * Return a streaming text response\n *\n * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.\n *\n * @param callback - Async function that writes text to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n streamText(callback: (stream: StreamingApi) => Promise<void>, onError?: (err: Error, stream: StreamingApi) => Promise<void>): Response {\n this.c.header('Content-Encoding', 'Identity')\n return honoStreamText(this.c, callback, onError)\n }\n\n /**\n * Return a Server-Sent Events (SSE) streaming response\n *\n * Automatically sets `Content-Encoding: Identity` for Cloudflare Workers compatibility.\n *\n * @param callback - Async function that writes SSE events to the stream\n * @param onError - Optional error handler called if an error occurs during streaming\n */\n streamSSE(callback: (stream: SSEStreamingApi) => Promise<void>, onError?: (err: Error, stream: SSEStreamingApi) => Promise<void>): Response {\n this.c.header('Content-Encoding', 'Identity')\n return honoStreamSSE(this.c, callback, onError)\n }\n}\n"],"mappings":"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"}
|