@znight/messaging 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/broker.ts","../src/decorators.ts","../src/retry.ts","../src/consumers.ts","../src/tokens.ts","../src/runtime.ts","../src/plugin.ts","../src/standard-schema.ts","../src/validation.ts"],"sourcesContent":["import type { Hooks } from '@znight/core'\nimport type { MessageCodec } from './codec.ts'\nimport type { MessagingDriver } from './driver.ts'\nimport type { MessagePublishEvent } from './hooks.ts'\nimport type { OutboundMessage, PublishOptions, PublishResult } from './message.ts'\n\nimport { randomUUID } from 'node:crypto'\n\nimport { brokerUnavailableError } from './errors.ts'\nimport { headerRecord } from './headers.ts'\n\n/**\n * Publishes messages. Inject it in any provider (it is a singleton external provided by\n * `MessagingPlugin`):\n *\n * ```ts\n * @Injectable()\n * export class OrderService {\n * constructor(private readonly broker: MessageBroker) {}\n *\n * async place(order: Order) {\n * await this.broker.publish('orders', order, { type: 'order.placed', key: order.customerId })\n * }\n * }\n * ```\n *\n * `publish()` resolves once the broker confirmed the message (publisher confirms, SQS response,\n * Kafka acknowledgement). It is usable from `onBootstrap` of the messaging plugin until the\n * application is disposed (including providers' `onStart` / `onStop`).\n */\nexport abstract class MessageBroker {\n /**\n * Publishes `payload` to `queue` (a queue for RabbitMQ/SQS, a topic for Kafka). The payload is\n * encoded by the plugin codec (JSON by default; `Uint8Array` is sent as binary).\n */\n abstract publish(queue: string, payload: unknown, options?: PublishOptions): Promise<PublishResult>\n}\n\nconst HEADER_NAME = /^[\\x21-\\x7e]+$/\n\n/** The {@link MessageBroker} registered by `MessagingPlugin` (internal). */\nexport class DriverMessageBroker extends MessageBroker {\n #driver: MessagingDriver | undefined\n #codec: MessageCodec | undefined\n #hooks: Hooks | undefined\n #closed = false\n\n /** Connects the broker to the driver (plugin `onBootstrap`). */\n bind(driver: MessagingDriver, codec: MessageCodec, hooks: Hooks): void {\n this.#driver = driver\n this.#codec = codec\n this.#hooks = hooks\n this.#closed = false\n }\n\n /** Rejects further publications (plugin `onDispose`). */\n unbind(): void {\n this.#closed = true\n }\n\n override async publish(queue: string, payload: unknown, options: PublishOptions = {}): Promise<PublishResult> {\n const driver = this.#driver\n const codec = this.#codec\n if (driver === undefined || codec === undefined) {\n throw brokerUnavailableError(\n `MessageBroker.publish('${queue}'): the messaging plugin is not bootstrapped yet (publish from onStart or later)`,\n )\n }\n if (this.#closed) {\n throw brokerUnavailableError(`MessageBroker.publish('${queue}'): the messaging plugin has been disposed`)\n }\n const message = this.#prepare(queue, payload, options, codec, driver)\n const hooks = this.#hooks\n const wrapped = hooks?.hasWrappers('messaging:publish') ?? false\n const observed = hooks?.hasListeners('messaging:publish') ?? false\n if (hooks === undefined || (!wrapped && !observed)) {\n await driver.publish(queue, message.outbound())\n return { id: message.id }\n }\n const event: MessagePublishEvent = {\n queue,\n type: options.type,\n messageId: message.id,\n headers: message.headers,\n startTime: performance.now(),\n }\n const send = async (): Promise<void> => {\n try {\n await driver.publish(queue, message.outbound())\n } catch (error) {\n event.error = error // visible to the wrappers before the rejection reaches them\n throw error\n }\n }\n try {\n if (wrapped) await hooks.run('messaging:publish', event, send)\n else await send()\n } finally {\n if (observed) hooks.emit('messaging:publish', event)\n }\n return { id: message.id }\n }\n\n #prepare(\n queue: string,\n payload: unknown,\n options: PublishOptions,\n codec: MessageCodec,\n driver: MessagingDriver,\n ): { readonly id: string; readonly headers: Record<string, string>; outbound(): OutboundMessage } {\n if (typeof queue !== 'string' || queue === '') {\n throw new TypeError(\n `[znight] MessageBroker.publish(): the queue must be a non-empty string, got ${String(queue)}`,\n )\n }\n const delayMs = options.delay ?? 0\n if (typeof delayMs !== 'number' || !Number.isFinite(delayMs) || delayMs < 0) {\n throw new TypeError(`[znight] MessageBroker.publish('${queue}'): \"delay\" must be a number of milliseconds >= 0`)\n }\n if (delayMs > driver.capabilities.maxPublishDelayMs) {\n throw new RangeError(\n driver.capabilities.maxPublishDelayMs === 0\n ? `[znight] MessageBroker.publish('${queue}'): the ${driver.name} driver does not support delayed publishing`\n : `[znight] MessageBroker.publish('${queue}'): the ${driver.name} driver supports delays up to ${driver.capabilities.maxPublishDelayMs} ms (got ${delayMs})`,\n )\n }\n const headers = headerRecord()\n if (options.headers !== undefined) {\n for (const [name, value] of Object.entries(options.headers)) {\n if (!HEADER_NAME.test(name))\n throw new TypeError(`[znight] MessageBroker.publish('${queue}'): invalid header name \"${name}\"`)\n if (typeof value !== 'string') {\n throw new TypeError(`[znight] MessageBroker.publish('${queue}'): header \"${name}\" must be a string`)\n }\n headers[name] = value\n }\n }\n const id = options.id ?? randomUUID()\n const encoded = codec.encode(payload, options.contentType)\n const timestamp = Date.now()\n return {\n id,\n headers,\n outbound: () => ({\n id,\n type: options.type,\n key: options.key,\n headers: { ...headers },\n body: encoded.body,\n contentType: encoded.contentType,\n timestamp,\n delayMs,\n }),\n }\n }\n}\n","/**\n * Marker decorators of `@znight/messaging`. Like every Znight decorator they do nothing at runtime:\n * the compiler extension (`@znight/messaging/compiler`) reads them from the source code and generates\n * `metadata.extensions.messaging`. Arguments marked _static_ must be statically evaluable (literals,\n * `const` references); _value_ arguments (schemas) may also be references to exported bindings.\n */\nimport type { ClassMarker, MethodMarker, ParameterMarker, Scope } from '@znight/core'\nimport type { InboundMessage } from './message.ts'\nimport type { RetryOptions } from './retry.ts'\nimport type { StandardSchemaV1 } from './standard-schema.ts'\n\n/** Options of {@link Consumer} (static). */\nexport interface ConsumerOptions {\n /** Queue (RabbitMQ, SQS) or topic (Kafka) to consume. */\n readonly queue: string\n /** Scope of the consumer provider. Default: `singleton`; `request` creates one instance per message. */\n readonly scope?: Scope\n /** Maximum number of messages handled concurrently. Default: plugin `defaults.concurrency` (1). */\n readonly concurrency?: number\n /** Dead-letter queue/topic, or `false` to disable dead-lettering. Default: driver naming (`<queue>.dlq`). */\n readonly deadLetter?: string | false\n /** Driver-specific options (e.g. Kafka `groupId`). */\n readonly [driverOption: string]: unknown\n}\n\n/** Result of an `@OnError` method: `'ack'` acknowledges (swallows) the message, anything else dead-letters it. */\n// biome-ignore lint/suspicious/noConfusingVoidType: lets error handlers without return statement type-check\nexport type ErrorHandlerResult = 'ack' | undefined | void\n\n/**\n * Signature of an `@OnError` method. Declared as a method signature so that parameters may be\n * narrowed (`onError(error: Error, message: InboundMessage<Order>)`).\n */\nexport type ErrorHandlerMethod = ErrorHandlerSignature['onError']\n\ninterface ErrorHandlerSignature {\n onError(error: unknown, message: InboundMessage): ErrorHandlerResult | Promise<ErrorHandlerResult>\n}\n\n/** Marker usable on an `@OnError` method: checks its signature. */\nexport type ErrorHandlerMarker = <F extends ErrorHandlerMethod>(\n target: object,\n propertyKeyOrContext: string | symbol | ClassMethodDecoratorContext<unknown, F>,\n descriptor?: TypedPropertyDescriptor<F>,\n) => void\n\nfunction marker(): void {\n // intentionally empty: decorators are compile-time markers\n}\n\nconst classMarker: ClassMarker = marker\nconst methodMarker: MethodMarker = marker\nconst parameterMarker: ParameterMarker = marker\nconst errorHandlerMarker: ErrorHandlerMarker = marker\n\n/**\n * Declares a message consumer: a provider (singleton by default) whose `@OnMessage` methods handle\n * the messages of `queue`. Arguments are static.\n *\n * ```ts\n * @Consumer('orders')\n * export class OrderConsumer {\n * constructor(private readonly orders: OrderService) {}\n *\n * @OnMessage('order.created')\n * @Retry({ attempts: 5, backoff: 'exponential', delay: 500 })\n * async created(@Message(OrderCreated) event: OrderCreated) {\n * await this.orders.process(event)\n * }\n * }\n * ```\n */\nexport function Consumer(queue: string): ClassMarker\nexport function Consumer(options: ConsumerOptions): ClassMarker\nexport function Consumer(_queueOrOptions: string | ConsumerOptions): ClassMarker {\n return classMarker\n}\n\n/**\n * Declares a message handler. With `type` (static), it handles the messages of that type (exact\n * match, or a pattern where `*` matches any sequence: `'order.*'`); without type it is the consumer's\n * default handler (messages without type, or whose type no other handler matches). A message no\n * handler matches is dead-lettered (reason `no-handler`).\n */\nexport function OnMessage(_type?: string): MethodMarker {\n return methodMarker\n}\n\n/**\n * Retry policy of a handler (static options). `attempts` counts the first delivery:\n * `{ attempts: 3 }` means one delivery plus two retries. Without `@Retry`, the plugin's\n * `defaults.retry` applies (3 attempts, exponential from 1 s).\n */\nexport function Retry(_options: RetryOptions): MethodMarker {\n return methodMarker\n}\n\n/**\n * Declares the consumer's error handler, called once a message will not be retried anymore (last\n * attempt failed, `NonRetryableError`, invalid payload, no matching handler). Returning `'ack'`\n * acknowledges the message; returning anything else (or throwing) dead-letters it.\n *\n * ```ts\n * @OnError()\n * onError(error: unknown, message: InboundMessage) {\n * if (message.type?.startsWith('legacy.')) return 'ack'\n * }\n * ```\n */\nexport function OnError(): ErrorHandlerMarker {\n return errorHandlerMarker\n}\n\n/**\n * Injects the message payload. With a Standard Schema (Zod, Valibot...), the payload is validated and\n * the parameter receives the schema output; an invalid payload is dead-lettered without retry\n * (reason `validation`). The schema is a _value_ argument (inline, or an exported binding).\n */\nexport function Message(_schema?: StandardSchemaV1): ParameterMarker {\n return parameterMarker\n}\n\n/** Injects all the message headers, or the value of one header (`string | undefined`). `name` is static. */\nexport function Headers(_name?: string): ParameterMarker {\n return parameterMarker\n}\n\n/** Injects the {@link InboundMessage} (id, type, attempt, headers, raw message, abort signal...). */\nexport function Ctx(): ParameterMarker {\n return parameterMarker\n}\n","import type { DriverCapabilities } from './driver.ts'\n\nimport { consumerError } from './errors.ts'\n\n/** Backoff strategy between attempts. */\nexport type BackoffStrategy = 'fixed' | 'exponential'\n\n/** Options of `@Retry()` and of the plugin's `defaults.retry`. */\nexport interface RetryOptions {\n /** Total number of attempts, including the first delivery (`1`: no retry). */\n readonly attempts: number\n /** `fixed`: always `delay`; `exponential`: `delay * 2^(attempt - 1)`, capped by `maxDelay`. Default: `exponential`. */\n readonly backoff?: BackoffStrategy\n /** Base delay between attempts, in milliseconds. Default: 1000. */\n readonly delay?: number\n /** Upper bound of exponential delays, in milliseconds. */\n readonly maxDelay?: number\n}\n\n/** A normalized retry policy, as stored in the metadata (`MessageHandlerDefinition.retry`). */\nexport interface RetryPolicy {\n readonly attempts: number\n readonly backoff: BackoffStrategy\n readonly delay: number\n readonly maxDelay?: number\n}\n\n/** Default base delay of a retry policy, in milliseconds. */\nexport const DEFAULT_RETRY_DELAY_MS = 1000\n\n/** Default backoff strategy. */\nexport const DEFAULT_BACKOFF: BackoffStrategy = 'exponential'\n\n/** Policy applied to handlers without `@Retry` when the plugin sets no `defaults.retry`. */\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = Object.freeze({\n attempts: 3,\n backoff: 'exponential',\n delay: DEFAULT_RETRY_DELAY_MS,\n maxDelay: 30_000,\n})\n\n/** The policy of `retry: false` / `{ attempts: 1 }`. */\nexport const NO_RETRY_POLICY: RetryPolicy = Object.freeze({ attempts: 1, backoff: 'fixed', delay: 0 })\n\nconst MAX_ATTEMPTS = 10_000\n\n/**\n * Validates retry options and fills defaults.\n *\n * @throws ZnightError ZNIGHT011 for invalid values (`where` names the handler in the message).\n */\nexport function normalizeRetry(options: RetryOptions | RetryPolicy, where = '@Retry()'): RetryPolicy {\n if (options === null || typeof options !== 'object') {\n throw consumerError(`${where}: retry options must be an object, got ${String(options)}`)\n }\n const { attempts, backoff = DEFAULT_BACKOFF, delay = DEFAULT_RETRY_DELAY_MS, maxDelay } = options\n if (!Number.isInteger(attempts) || attempts < 1 || attempts > MAX_ATTEMPTS) {\n throw consumerError(\n `${where}: \"attempts\" must be an integer between 1 and ${MAX_ATTEMPTS}, got ${String(attempts)}`,\n ['attempts counts the first delivery: { attempts: 3 } means 1 delivery + 2 retries'],\n )\n }\n if (backoff !== 'fixed' && backoff !== 'exponential') {\n throw consumerError(`${where}: \"backoff\" must be 'fixed' or 'exponential', got ${String(backoff)}`)\n }\n if (!isDuration(delay)) {\n throw consumerError(`${where}: \"delay\" must be a finite number of milliseconds >= 0, got ${String(delay)}`)\n }\n if (maxDelay !== undefined && (!isDuration(maxDelay) || maxDelay < delay)) {\n throw consumerError(\n `${where}: \"maxDelay\" must be a finite number of milliseconds >= delay (${delay}), got ${String(maxDelay)}`,\n )\n }\n return maxDelay === undefined ? { attempts, backoff, delay } : { attempts, backoff, delay, maxDelay }\n}\n\nfunction isDuration(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n}\n\n/**\n * Delay before the next attempt once `attempt` (1-based) failed: `delay` for `fixed`,\n * `delay * 2^(attempt - 1)` capped by `maxDelay` for `exponential`.\n */\nexport function computeRetryDelay(policy: RetryPolicy, attempt: number): number {\n if (policy.backoff === 'fixed') return policy.delay\n const exponent = Math.max(0, attempt - 1)\n const raw = policy.delay * 2 ** exponent\n return Math.min(raw, policy.maxDelay ?? Number.MAX_SAFE_INTEGER)\n}\n\n/** Every delay the policy may request (one per retry), in attempt order. */\nexport function retryDelays(policy: RetryPolicy): number[] {\n const delays: number[] = []\n for (let attempt = 1; attempt < policy.attempts; attempt++) delays.push(computeRetryDelay(policy, attempt))\n return delays\n}\n\n/** Whether a delay is waited in-process (`true`) or delegated to the broker (`false`). */\nexport function isInProcessDelay(delayMs: number, capabilities: DriverCapabilities): boolean {\n return delayMs <= capabilities.maxInProcessDelayMs\n}\n\n/**\n * Checks that the driver can honour every delay of `policy`.\n *\n * @throws ZnightError ZNIGHT011 naming `where` and the offending delay.\n */\nexport function assertRetrySupported(\n policy: RetryPolicy,\n capabilities: DriverCapabilities,\n driver: string,\n where: string,\n): void {\n for (const delay of new Set(retryDelays(policy))) {\n if (isInProcessDelay(delay, capabilities)) continue\n if (capabilities.nativeDelay && delay <= capabilities.maxNativeDelayMs) continue\n const limits = capabilities.nativeDelay\n ? `in-process delays up to ${capabilities.maxInProcessDelayMs} ms and native delays up to ${capabilities.maxNativeDelayMs} ms`\n : `in-process delays up to ${capabilities.maxInProcessDelayMs} ms and no native delay`\n throw consumerError(\n `${where}: the ${driver} driver cannot wait ${delay} ms between attempts (it supports ${limits})`,\n [\n 'Lower \"delay\"/\"maxDelay\" of the retry policy',\n 'Or raise the driver budget (e.g. maxInProcessDelayMs) if holding messages that long is acceptable',\n ],\n )\n }\n}\n","/**\n * Preparation and validation of the consumers declared in the metadata (boot time, ZNIGHT011).\n */\nimport type { Container, Token } from '@znight/core'\nimport type { MessagingDriver } from './driver.ts'\nimport type { ConsumerDefinition, MessageHandlerDefinition, MessagingMetadata } from './metadata.ts'\nimport type { RetryOptions, RetryPolicy } from './retry.ts'\n\nimport { displayName } from '@znight/core'\n\nimport { consumerError } from './errors.ts'\nimport { isPositiveInteger } from './internal/async.ts'\nimport {\n assertRetrySupported,\n DEFAULT_RETRY_POLICY,\n isInProcessDelay,\n NO_RETRY_POLICY,\n normalizeRetry,\n retryDelays,\n} from './retry.ts'\n\n/** A handler ready to be dispatched to. */\nexport interface PreparedHandler {\n readonly definition: MessageHandlerDefinition\n readonly method: string\n readonly policy: RetryPolicy\n}\n\n/** A validated consumer. */\nexport interface PreparedConsumer {\n readonly definition: ConsumerDefinition\n readonly token: Token\n /** Display name of the consumer class. */\n readonly name: string\n readonly queue: string\n readonly concurrency: number\n readonly deadLetterQueue: string | undefined\n readonly errorHandler: string | undefined\n readonly maxAttempts: number\n /** Native delays the runtime may request (sorted, unique). */\n readonly retryDelays: readonly number[]\n readonly driverOptions: Readonly<Record<string, unknown>>\n /** Selects the handler of a message type (exact, then patterns, then the default handler). */\n select(type: string | undefined): PreparedHandler | undefined\n}\n\n/** Plugin-level defaults applied to every consumer. */\nexport interface ConsumerDefaults {\n readonly retry: RetryPolicy\n readonly concurrency: number\n readonly deadLetter: ((queue: string) => string) | false | undefined\n}\n\n/** Normalizes `defaults.retry` of the plugin. */\nexport function defaultRetryPolicy(retry: RetryOptions | false | undefined): RetryPolicy {\n if (retry === undefined) return DEFAULT_RETRY_POLICY\n if (retry === false) return NO_RETRY_POLICY\n return normalizeRetry(retry, 'MessagingPlugin defaults.retry')\n}\n\n/**\n * Validates `metadata.extensions.messaging` against the container and the driver capabilities.\n *\n * @throws ZnightError ZNIGHT011 describing the first invalid consumer.\n */\nexport function prepareConsumers(\n metadata: MessagingMetadata | undefined,\n container: Container,\n driver: MessagingDriver,\n defaults: ConsumerDefaults,\n): PreparedConsumer[] {\n if (metadata === undefined) return []\n if (metadata === null || typeof metadata !== 'object' || !Array.isArray(metadata.consumers)) {\n throw consumerError('Invalid messaging metadata: \"consumers\" must be an array', [\n 'Regenerate the metadata with the @znight/messaging compiler extension',\n ])\n }\n const byQueue = new Map<string, string>()\n const prepared: PreparedConsumer[] = []\n for (const definition of metadata.consumers) {\n const consumer = prepareConsumer(definition, container, driver, defaults)\n const previous = byQueue.get(consumer.queue)\n if (previous !== undefined) {\n throw consumerError(`${previous} and ${consumer.name} both consume \"${consumer.queue}\"`, [\n 'A queue is consumed by a single @Consumer class: merge the handlers, or route by type with @OnMessage(type)',\n ])\n }\n byQueue.set(consumer.queue, consumer.name)\n prepared.push(consumer)\n }\n return prepared\n}\n\nfunction prepareConsumer(\n definition: ConsumerDefinition,\n container: Container,\n driver: MessagingDriver,\n defaults: ConsumerDefaults,\n): PreparedConsumer {\n if (\n definition === null ||\n typeof definition !== 'object' ||\n typeof definition.token !== 'string' ||\n definition.token === ''\n ) {\n throw consumerError('Invalid consumer definition: missing provider token')\n }\n const token = definition.token\n const name = displayName(token)\n if (typeof definition.queue !== 'string' || definition.queue === '') {\n throw consumerError(`@Consumer ${name}: the queue must be a non-empty string`)\n }\n if (!container.has(token)) {\n throw consumerError(`@Consumer ${name}: no provider is registered for ${token}`, [\n 'Regenerate the metadata: the consumer class must be a compiled provider',\n ])\n }\n const options = definition.options ?? {}\n const { concurrency: rawConcurrency, deadLetter, ...driverOptions } = options\n const concurrency = rawConcurrency ?? defaults.concurrency\n if (!isPositiveInteger(concurrency)) {\n throw consumerError(`@Consumer ${name}: \"concurrency\" must be a positive integer, got ${String(concurrency)}`)\n }\n if (deadLetter !== undefined && deadLetter !== false && (typeof deadLetter !== 'string' || deadLetter === '')) {\n throw consumerError(`@Consumer ${name}: \"deadLetter\" must be a queue name or false, got ${String(deadLetter)}`)\n }\n if (deadLetter === definition.queue) {\n throw consumerError(`@Consumer ${name}: the dead-letter queue cannot be the consumed queue \"${definition.queue}\"`)\n }\n\n const handlers = definition.handlers\n if (!Array.isArray(handlers) || handlers.length === 0) {\n throw consumerError(`@Consumer ${name} has no @OnMessage handler`, [\n 'Decorate at least one method with @OnMessage()',\n ])\n }\n const exact = new Map<string, PreparedHandler>()\n const patterns: { readonly regex: RegExp; readonly handler: PreparedHandler }[] = []\n let fallback: PreparedHandler | undefined\n let maxAttempts = 1\n const nativeDelays = new Set<number>()\n for (const handlerDefinition of handlers) {\n const handler = prepareHandler(handlerDefinition, name, driver, defaults.retry)\n maxAttempts = Math.max(maxAttempts, handler.policy.attempts)\n for (const delay of retryDelays(handler.policy)) {\n if (!isInProcessDelay(delay, driver.capabilities)) nativeDelays.add(delay)\n }\n const type = handlerDefinition.type\n if (type === undefined) {\n if (fallback !== undefined) {\n throw consumerError(\n `@Consumer ${name}: ${fallback.method}() and ${handler.method}() are both default @OnMessage() handlers`,\n ['Give one of them a message type: @OnMessage(\"order.created\")'],\n )\n }\n fallback = handler\n } else if (type.includes('*')) {\n if (patterns.some(entry => entry.handler.definition.type === type)) {\n throw duplicateType(name, type)\n }\n patterns.push({ regex: patternToRegExp(type), handler })\n } else {\n if (exact.has(type)) throw duplicateType(name, type)\n exact.set(type, handler)\n }\n }\n\n const errorHandler = definition.errorHandler\n if (errorHandler !== undefined) {\n if (typeof errorHandler !== 'string' || errorHandler === '') {\n throw consumerError(`@Consumer ${name}: invalid @OnError method name ${String(errorHandler)}`)\n }\n const providers = container.metadata.providers\n const provider = Object.hasOwn(providers, token) ? providers[token] : undefined\n const prototype = provider?.class?.prototype as Record<string, unknown> | undefined\n if (prototype !== undefined && typeof prototype[errorHandler] !== 'function') {\n throw consumerError(`@Consumer ${name}: the @OnError method ${errorHandler}() does not exist`)\n }\n }\n\n return {\n definition,\n token,\n name,\n queue: definition.queue,\n concurrency,\n deadLetterQueue: resolveDeadLetter(definition.queue, deadLetter, defaults.deadLetter, driver),\n errorHandler,\n maxAttempts,\n retryDelays: [...nativeDelays].sort((a, b) => a - b),\n driverOptions,\n select(type: string | undefined): PreparedHandler | undefined {\n if (type === undefined) return fallback\n const handler = exact.get(type)\n if (handler !== undefined) return handler\n for (const entry of patterns) if (entry.regex.test(type)) return entry.handler\n return fallback\n },\n }\n}\n\nfunction prepareHandler(\n definition: MessageHandlerDefinition,\n consumer: string,\n driver: MessagingDriver,\n defaultPolicy: RetryPolicy,\n): PreparedHandler {\n if (\n definition === null ||\n typeof definition !== 'object' ||\n typeof definition.method !== 'string' ||\n definition.method === ''\n ) {\n throw consumerError(`@Consumer ${consumer}: invalid @OnMessage handler definition`)\n }\n const where = `${consumer}.${definition.method}()`\n if (typeof definition.invoke !== 'function') {\n throw consumerError(`${where}: the handler has no invoker`, ['Regenerate the metadata'])\n }\n if (definition.type !== undefined && (typeof definition.type !== 'string' || definition.type === '')) {\n throw consumerError(`${where}: the @OnMessage type must be a non-empty string`)\n }\n const policy = definition.retry === undefined ? defaultPolicy : normalizeRetry(definition.retry, `@Retry on ${where}`)\n assertRetrySupported(\n policy,\n driver.capabilities,\n driver.name,\n definition.retry === undefined ? `${where} (default retry policy)` : `@Retry on ${where}`,\n )\n return { definition, method: definition.method, policy }\n}\n\nfunction duplicateType(consumer: string, type: string): Error {\n return consumerError(`@Consumer ${consumer}: several @OnMessage handlers for type \"${type}\"`)\n}\n\n/** `order.*` → `/^order\\..*$/`: `*` matches any sequence of characters. */\nfunction patternToRegExp(pattern: string): RegExp {\n const source = pattern\n .split('*')\n .map(part => part.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*')\n return new RegExp(`^${source}$`, 's')\n}\n\nfunction resolveDeadLetter(\n queue: string,\n option: string | false | undefined,\n defaults: ((queue: string) => string) | false | undefined,\n driver: MessagingDriver,\n): string | undefined {\n if (option === false) return undefined\n if (typeof option === 'string') return option\n if (defaults === false) return undefined\n const name = defaults === undefined ? driver.deadLetterName(queue) : defaults(queue)\n if (typeof name !== 'string' || name === '' || name === queue) {\n throw consumerError(`Invalid dead-letter queue name for \"${queue}\": ${String(name)}`)\n }\n return name\n}\n","import type { TypedToken } from '@znight/core'\nimport type { MessageBroker } from './broker.ts'\nimport type { InboundMessage } from './message.ts'\n\n/** Tokens of the externals provided by `MessagingPlugin` (declared in the package manifest). */\nexport const MessagingTokens: {\n /** The {@link MessageBroker} singleton. */\n readonly MessageBroker: TypedToken<MessageBroker>\n /** The {@link InboundMessage} of the current delivery (request-scoped). */\n readonly InboundMessage: TypedToken<InboundMessage>\n} = Object.freeze({\n MessageBroker: '@znight/messaging#MessageBroker',\n InboundMessage: '@znight/messaging#InboundMessage',\n})\n","/**\n * The delivery state machine (docs/internals/architecture.md §9.2), one {@link ConsumerRuntime} per\n * `@Consumer` class.\n *\n * For each delivery: concurrency slot → request scope seeded with the `InboundMessage` →\n * `hooks.run('messaging:message')` → decode → select the handler by type → invoke →\n * ack | retry (in-process wait or native redelivery) | `@OnError` → dead-letter (publish then ack;\n * on failure the message is released for a later redelivery). Nothing is ever dropped: every path\n * ends with the broker owning the message again.\n */\nimport type { Container, CoreLogger, Hooks, MessageDeliveryEvent } from '@znight/core'\nimport type { MessageCodec } from './codec.ts'\nimport type { PreparedConsumer, PreparedHandler } from './consumers.ts'\nimport type { DriverDelivery, MessagingDriver, Subscription } from './driver.ts'\nimport type { DeadLetterReason, InboundMessage } from './message.ts'\n\nimport {\n AttemptsExhaustedError,\n isNonRetryable,\n MessageDecodeError,\n MessageValidationError,\n NoHandlerError,\n} from './errors.ts'\nimport { errorMessage, Semaphore, sleep } from './internal/async.ts'\nimport { computeRetryDelay, isInProcessDelay } from './retry.ts'\nimport { MessagingTokens } from './tokens.ts'\n\n/** Services shared by every consumer runtime. */\nexport interface RuntimeServices {\n readonly container: Container\n readonly hooks: Hooks\n readonly logger: CoreLogger\n readonly codec: MessageCodec\n readonly driver: MessagingDriver\n /** Pause before releasing a message whose settlement failed (avoids hot redelivery loops). */\n readonly requeueDelayMs: number\n}\n\n/** State of a {@link ConsumerRuntime}. */\nexport type ConsumerState = 'created' | 'starting' | 'running' | 'stopping' | 'stopped'\n\n/** What remains to do once the scope of a delivery closed. */\ntype Followup =\n | { readonly kind: 'retry'; readonly delayMs: number; readonly error: unknown }\n | { readonly kind: 'requeue' }\n | undefined\n\n/** Per-delivery working state. */\ninterface DeliveryContext {\n readonly delivery: DriverDelivery\n readonly message: InboundMessage\n readonly handler: PreparedHandler | undefined\n readonly event: MessageDeliveryEvent\n readonly decodeError: unknown\n executed: boolean\n followup: Followup\n}\n\n/** Runs the deliveries of one consumer. */\nexport class ConsumerRuntime {\n readonly consumer: PreparedConsumer\n readonly #services: RuntimeServices\n readonly #slots: Semaphore\n readonly #abort = new AbortController()\n readonly #inflight = new Set<Promise<void>>()\n #subscription: Subscription | undefined\n #state: ConsumerState = 'created'\n\n constructor(consumer: PreparedConsumer, services: RuntimeServices) {\n this.consumer = consumer\n this.#services = services\n this.#slots = new Semaphore(consumer.concurrency)\n }\n\n get state(): ConsumerState {\n return this.#state\n }\n\n /** Deliveries currently handled (including those waiting for a slot or an in-process retry delay). */\n get inFlight(): number {\n return this.#inflight.size\n }\n\n /** Subscribes to the queue. */\n async start(): Promise<void> {\n if (this.#state !== 'created') throw new Error(`[znight] Consumer ${this.consumer.name} was already started`)\n this.#state = 'starting'\n const { consumer } = this\n try {\n this.#subscription = await this.#services.driver.subscribe(consumer.queue, delivery => this.handle(delivery), {\n consumer: consumer.name,\n concurrency: consumer.concurrency,\n deadLetterQueue: consumer.deadLetterQueue,\n maxAttempts: consumer.maxAttempts,\n retryDelays: consumer.retryDelays,\n options: consumer.driverOptions,\n })\n } catch (error) {\n this.#state = 'stopped'\n throw error\n }\n this.#state = 'running'\n }\n\n /**\n * Graceful stop: stops receiving, aborts in-process retry delays (those messages are released to\n * the broker), awaits the in-flight handlers, then closes the subscription. Never throws.\n */\n async stop(): Promise<void> {\n if (this.#state === 'stopping' || this.#state === 'stopped') return\n this.#state = 'stopping'\n this.#abort.abort()\n const subscription = this.#subscription\n if (subscription !== undefined) {\n try {\n await subscription.cancel()\n } catch (error) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: failed to cancel the subscription`, { err: error })\n }\n }\n while (this.#inflight.size > 0) await Promise.allSettled([...this.#inflight])\n if (subscription !== undefined) {\n try {\n await subscription.close()\n } catch (error) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: failed to close the subscription`, { err: error })\n }\n }\n this.#state = 'stopped'\n }\n\n /** Handles one delivery (the driver's `DeliveryHandler`). Never rejects. */\n handle(delivery: DriverDelivery): Promise<void> {\n const promise = this.#handle(delivery)\n this.#inflight.add(promise)\n const done = (): void => {\n this.#inflight.delete(promise)\n }\n promise.then(done, done)\n return promise\n }\n\n async #handle(delivery: DriverDelivery): Promise<void> {\n const signal = this.#abort.signal\n if (signal.aborted) return this.#release(delivery)\n try {\n await this.#slots.acquire(signal)\n } catch {\n return this.#release(delivery)\n }\n try {\n await this.#process(delivery)\n } catch (error) {\n // defensive: #process handles its failures; never leave a message unsettled\n this.#services.logger.error(`Consumer ${this.consumer.name}: unexpected delivery failure`, { err: error })\n await this.#release(delivery)\n } finally {\n this.#slots.release()\n }\n }\n\n async #process(delivery: DriverDelivery): Promise<void> {\n const { container, hooks, codec, driver } = this.#services\n const { consumer } = this\n const handler = consumer.select(delivery.type)\n let payload: unknown\n let decodeError: unknown\n try {\n payload = codec.decode(delivery.body, delivery.contentType)\n } catch (error) {\n decodeError = isNonRetryable(error)\n ? error\n : new MessageDecodeError(`Cannot decode the message body: ${errorMessage(error)}`, { cause: error })\n }\n const message: InboundMessage = {\n id: delivery.id,\n queue: consumer.queue,\n type: delivery.type,\n payload,\n headers: delivery.headers,\n key: delivery.key,\n attempt: delivery.attempt,\n maxAttempts: handler?.policy.attempts ?? 1,\n redelivered: delivery.redelivered,\n timestamp: delivery.timestamp,\n receivedAt: Date.now(),\n contentType: delivery.contentType,\n body: delivery.body,\n driver: driver.name,\n raw: delivery.raw,\n signal: this.#abort.signal,\n }\n const event: MessageDeliveryEvent = {\n queue: consumer.queue,\n type: delivery.type,\n messageId: delivery.id,\n attempt: delivery.attempt,\n headers: delivery.headers,\n consumer: consumer.name,\n handler: handler?.method,\n message,\n startTime: performance.now(),\n }\n const context: DeliveryContext = {\n delivery,\n message,\n handler,\n event,\n decodeError,\n executed: false,\n followup: undefined,\n }\n\n let failure: unknown = new Error('A messaging:message hook wrapper did not call next()')\n try {\n await container.runInScope(\n () => hooks.run('messaging:message', event, () => this.#execute(context)),\n [[MessagingTokens.InboundMessage, message]],\n )\n } catch (error) {\n failure = error\n if (context.executed) {\n // the delivery was settled: a wrapper failed afterwards, or a request-scoped instance failed to dispose\n this.#services.logger.error(`Consumer ${consumer.name}: error after handling message ${delivery.id}`, {\n err: error,\n queue: consumer.queue,\n })\n }\n }\n // the handler never ran (a hooks wrapper threw or did not call next()): count it as a failed attempt\n if (!context.executed) await this.#failure(context, failure, false)\n if (hooks.hasListeners('messaging:message')) hooks.emit('messaging:message', event)\n await this.#followup(context)\n }\n\n /** Runs inside the request scope and the hooks: handler, then settlement. Never throws. */\n async #execute(context: DeliveryContext): Promise<void> {\n if (context.executed) return // a wrapper called next() twice\n context.executed = true\n const { handler, message } = context\n if (context.decodeError !== undefined) return this.#failure(context, context.decodeError, true)\n if (handler === undefined)\n return this.#terminal(context, new NoHandlerError(this.consumer.name, message.type), 'no-handler', true)\n if (message.attempt > message.maxAttempts) {\n return this.#terminal(\n context,\n new AttemptsExhaustedError(message.attempt, message.maxAttempts),\n 'max-attempts',\n false,\n )\n }\n try {\n const instance = this.#services.container.get(this.consumer.token)\n await handler.definition.invoke(instance, message)\n } catch (error) {\n return this.#failure(context, error, true)\n }\n return this.#ack(context)\n }\n\n /** A failed attempt: retry while attempts remain, dead-letter otherwise. */\n #failure(context: DeliveryContext, error: unknown, withErrorHandler: boolean): Promise<void> {\n if (isNonRetryable(error)) return this.#terminal(context, error, reasonOf(error), withErrorHandler)\n if (context.message.attempt < context.message.maxAttempts) return this.#retry(context, error)\n return this.#terminal(context, error, 'max-attempts', withErrorHandler)\n }\n\n async #retry(context: DeliveryContext, error: unknown): Promise<void> {\n const { message, delivery, event } = context\n const policy = context.handler?.policy\n const delayMs = policy === undefined ? 0 : computeRetryDelay(policy, message.attempt)\n event.outcome = 'retry'\n event.error = error\n this.#services.logger.warn(\n `Consumer ${this.consumer.name}: attempt ${message.attempt}/${message.maxAttempts} of message ${message.id} failed, retrying in ${delayMs} ms`,\n { queue: message.queue, messageId: message.id, type: message.type, ...this.#errorContext(error) },\n )\n if (isInProcessDelay(delayMs, this.#services.driver.capabilities)) {\n context.followup = { kind: 'retry', delayMs, error }\n return\n }\n try {\n await delivery.retry({ attempt: message.attempt + 1, delayMs, error })\n } catch (retryError) {\n this.#services.logger.error(\n `Consumer ${this.consumer.name}: failed to schedule the retry of message ${message.id}`,\n {\n err: retryError,\n queue: message.queue,\n },\n )\n context.followup = { kind: 'requeue' }\n }\n }\n\n /** No more attempts: `@OnError` may acknowledge, otherwise the message is dead-lettered. */\n async #terminal(\n context: DeliveryContext,\n error: unknown,\n reason: DeadLetterReason,\n withErrorHandler: boolean,\n ): Promise<void> {\n context.event.error = error\n const method = this.consumer.errorHandler\n if (withErrorHandler && method !== undefined) {\n try {\n const instance = this.#services.container.get(this.consumer.token) as Record<\n string,\n (...args: unknown[]) => unknown\n >\n const result = await instance[method]?.(error, context.message)\n if (result === 'ack') return this.#ack(context)\n } catch (handlerError) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: @OnError ${method}() failed`, {\n err: handlerError,\n queue: context.message.queue,\n messageId: context.message.id,\n })\n }\n }\n return this.#deadLetter(context, error, reason)\n }\n\n async #deadLetter(context: DeliveryContext, error: unknown, reason: DeadLetterReason): Promise<void> {\n const { message, delivery, event } = context\n const queue = this.consumer.deadLetterQueue\n event.outcome = 'dead-letter'\n try {\n await delivery.deadLetter({ queue, reason, error, attempt: message.attempt })\n } catch (deadLetterError) {\n event.outcome = 'retry'\n event.error = deadLetterError\n this.#services.logger.error(\n `Consumer ${this.consumer.name}: failed to dead-letter message ${message.id}, it will be redelivered`,\n { err: deadLetterError, cause: error, queue: message.queue, deadLetterQueue: queue },\n )\n context.followup = { kind: 'requeue' }\n return\n }\n this.#services.logger.error(\n queue === undefined\n ? `Consumer ${this.consumer.name}: message ${message.id} rejected (${reason})`\n : `Consumer ${this.consumer.name}: message ${message.id} dead-lettered to ${queue} (${reason})`,\n {\n queue: message.queue,\n messageId: message.id,\n type: message.type,\n attempt: message.attempt,\n reason,\n ...this.#errorContext(error),\n },\n )\n }\n\n /**\n * The error of a failed delivery in the retry / dead-letter log entries: the error itself (with its\n * stack), or only its message when a `messaging:message` wrapper is installed. Such a wrapper (the\n * ObservabilityPlugin delivery log) writes its own entry for the delivery with the error: the stack\n * is logged once. Settlement failures always log their error.\n */\n #errorContext(error: unknown): { readonly err: unknown } | { readonly error: string } {\n return this.#services.hooks.hasWrappers('messaging:message') ? { error: errorMessage(error) } : { err: error }\n }\n\n async #ack(context: DeliveryContext): Promise<void> {\n context.event.outcome = 'ack'\n try {\n await context.delivery.ack()\n } catch (error) {\n this.#services.logger.warn(\n `Consumer ${this.consumer.name}: failed to acknowledge message ${context.message.id}, the broker will redeliver it`,\n { err: error, queue: context.message.queue },\n )\n }\n }\n\n /** Work done after the scope closed: in-process retry delays and requeues. */\n async #followup(context: DeliveryContext): Promise<void> {\n const followup = context.followup\n if (followup === undefined) return\n const { delivery, message } = context\n if (followup.kind === 'retry') {\n try {\n await sleep(followup.delayMs, this.#abort.signal)\n } catch {\n return this.#release(delivery) // stopping: the broker redelivers it later\n }\n try {\n await delivery.retry({ attempt: message.attempt + 1, delayMs: 0, error: followup.error })\n return\n } catch (error) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: failed to redeliver message ${message.id}`, {\n err: error,\n queue: message.queue,\n })\n }\n }\n try {\n await sleep(this.#services.requeueDelayMs, this.#abort.signal)\n } catch {\n // stopping: release right away\n }\n return this.#release(delivery)\n }\n\n async #release(delivery: DriverDelivery): Promise<void> {\n try {\n await delivery.release()\n } catch (error) {\n this.#services.logger.error(\n `Consumer ${this.consumer.name}: failed to release message ${delivery.id}, the broker will redeliver it`,\n { err: error, queue: this.consumer.queue },\n )\n }\n }\n}\n\nfunction reasonOf(error: unknown): DeadLetterReason {\n if (error instanceof MessageValidationError) return 'validation'\n if (error instanceof MessageDecodeError) return 'decode'\n if (error instanceof NoHandlerError) return 'no-handler'\n if (error instanceof AttemptsExhaustedError) return 'max-attempts'\n return 'non-retryable'\n}\n","import type { Plugin, PluginContext, PluginOptions } from '@znight/core'\nimport type { MessageCodec } from './codec.ts'\nimport type { MessagingDriver } from './driver.ts'\nimport type { RetryOptions } from './retry.ts'\n\nimport { resolveOptions } from '@znight/core'\n\nimport { DriverMessageBroker, type MessageBroker } from './broker.ts'\nimport { jsonCodec } from './codec.ts'\nimport { type ConsumerDefaults, defaultRetryPolicy, prepareConsumers } from './consumers.ts'\nimport { consumerError } from './errors.ts'\nimport { isPositiveInteger } from './internal/async.ts'\nimport { ConsumerRuntime, type ConsumerState } from './runtime.ts'\nimport { MessagingTokens } from './tokens.ts'\n\n/** Defaults applied to every consumer (each `@Consumer` / `@Retry` may override them). */\nexport interface MessagingDefaults {\n /** Retry policy of handlers without `@Retry`. Default: 3 attempts, exponential from 1 s up to 30 s. `false`: no retry. */\n readonly retry?: RetryOptions | false\n /** Concurrency of consumers without `concurrency` option. Default: 1. */\n readonly concurrency?: number\n /**\n * Dead-letter destination of a queue, or `false` to disable dead-lettering (messages are then\n * rejected natively or discarded). Default: the driver naming (`orders.dlq`, SQS `orders-dlq`).\n */\n readonly deadLetter?: ((queue: string) => string) | false\n /**\n * Pause, in milliseconds, before releasing a message whose settlement failed (dead-letter queue\n * unavailable...), to avoid hot redelivery loops. Default: 1000.\n */\n readonly requeueDelay?: number\n}\n\n/** Options of {@link MessagingPlugin.configure}. */\nexport interface MessagingPluginOptions {\n /** The broker driver: `rabbitmq()`, `sqs()`, `kafka()` or `memory()` (from the matching subpath). */\n readonly driver: MessagingDriver\n readonly defaults?: MessagingDefaults\n /** Payload encoding. Default: {@link jsonCodec}. */\n readonly codec?: MessageCodec\n /** Subscribe the consumers on start. `false` for publish-only processes. Default: `true`. */\n readonly consume?: boolean\n}\n\n/** Runtime status of a consumer. */\nexport interface ConsumerStatus {\n /** Display name of the consumer class. */\n readonly consumer: string\n readonly queue: string\n readonly state: ConsumerState\n readonly concurrency: number\n readonly inFlight: number\n readonly deadLetterQueue: string | undefined\n}\n\n/** The plugin returned by {@link MessagingPlugin.configure}. */\nexport interface MessagingPluginInstance extends Plugin {\n readonly name: 'messaging'\n /** The broker (also injectable as `MessageBroker`). */\n readonly broker: MessageBroker\n /** The driver, once the plugin is bootstrapped. */\n readonly driver: MessagingDriver | undefined\n /** Status of every consumer, once the plugin is bootstrapped. */\n consumers(): ConsumerStatus[]\n}\n\nconst DEFAULT_REQUEUE_DELAY_MS = 1000\n/** Period of the keep-alive timer (it never does anything: it only keeps the event loop alive). */\nconst KEEP_ALIVE_INTERVAL_MS = 2 ** 30\n\n/**\n * Message consumers and publishing.\n *\n * ```ts\n * import { MessagingPlugin } from '@znight/messaging'\n * import { rabbitmq } from '@znight/messaging/rabbitmq'\n *\n * const app = await Application.create({\n * metadata: __ZNIGHT_METADATA__,\n * plugins: [MessagingPlugin.configure({ driver: rabbitmq({ url: 'amqp://localhost' }) })],\n * })\n * ```\n *\n * Options may be computed from the container (evaluated in `onBootstrap`):\n * `MessagingPlugin.configure(c => ({ driver: rabbitmq({ url: c.get(AmqpConfig).url }) }))`.\n */\nexport const MessagingPlugin: {\n configure(options: PluginOptions<MessagingPluginOptions>): MessagingPluginInstance\n} = Object.freeze({\n configure(options: PluginOptions<MessagingPluginOptions>): MessagingPluginInstance {\n return createPlugin(options)\n },\n})\n\nfunction createPlugin(rawOptions: PluginOptions<MessagingPluginOptions>): MessagingPluginInstance {\n const broker = new DriverMessageBroker()\n let driver: MessagingDriver | undefined\n let connected = false\n let consume = true\n let runtimes: ConsumerRuntime[] = []\n /**\n * Keeps the process alive while consumers run: they are its ingress, and some drivers hold no\n * handle of their own (the memory driver only uses microtasks and short timers), so a process\n * without other handle would exit right after `app.start()`, without graceful shutdown.\n */\n let keepAlive: ReturnType<typeof setInterval> | undefined\n const stopKeepAlive = (): void => {\n if (keepAlive !== undefined) clearInterval(keepAlive)\n keepAlive = undefined\n }\n\n return {\n name: 'messaging',\n broker,\n get driver(): MessagingDriver | undefined {\n return driver\n },\n consumers(): ConsumerStatus[] {\n return runtimes.map(runtime => ({\n consumer: runtime.consumer.name,\n queue: runtime.consumer.queue,\n state: runtime.state,\n concurrency: runtime.consumer.concurrency,\n inFlight: runtime.inFlight,\n deadLetterQueue: runtime.consumer.deadLetterQueue,\n }))\n },\n\n register(ctx: PluginContext): void {\n ctx.container.registerExternal({\n token: MessagingTokens.InboundMessage,\n scope: 'request',\n providedBy: 'MessagingPlugin',\n })\n ctx.container.registerValue(MessagingTokens.MessageBroker, broker)\n },\n\n async onBootstrap(ctx: PluginContext): Promise<void> {\n const options = validateOptions(resolveOptions(rawOptions, ctx.container))\n driver = options.driver\n consume = options.consume ?? true\n const defaults = options.defaults ?? {}\n const consumerDefaults: ConsumerDefaults = {\n retry: defaultRetryPolicy(defaults.retry),\n concurrency: defaults.concurrency ?? 1,\n deadLetter: defaults.deadLetter,\n }\n const prepared = prepareConsumers(ctx.metadata.extensions?.messaging, ctx.container, driver, consumerDefaults)\n const codec = options.codec ?? jsonCodec\n connected = true // close() must run even if connect() failed half-way\n await driver.connect({ logger: ctx.logger })\n broker.bind(driver, codec, ctx.hooks)\n const services = {\n container: ctx.container,\n hooks: ctx.hooks,\n logger: ctx.logger,\n codec,\n driver,\n requeueDelayMs: defaults.requeueDelay ?? DEFAULT_REQUEUE_DELAY_MS,\n }\n runtimes = prepared.map(consumer => new ConsumerRuntime(consumer, services))\n },\n\n async onStart(ctx: PluginContext): Promise<void> {\n if (!consume || runtimes.length === 0) return\n const results = await Promise.allSettled(runtimes.map(runtime => runtime.start()))\n const failure = results.find(result => result.status === 'rejected')\n if (failure !== undefined) {\n // the application only stops plugins whose onStart succeeded: undo the subscriptions made\n await Promise.all(runtimes.map(runtime => runtime.stop()))\n throw failure.reason\n }\n keepAlive ??= setInterval(() => undefined, KEEP_ALIVE_INTERVAL_MS)\n ctx.logger.info(`Messaging: ${runtimes.length} consumer(s) subscribed with the ${driver?.name} driver`, {\n queues: runtimes.map(runtime => runtime.consumer.queue),\n })\n },\n\n async onStop(): Promise<void> {\n try {\n await Promise.all(runtimes.map(runtime => runtime.stop()))\n } finally {\n stopKeepAlive()\n }\n },\n\n async onDispose(): Promise<void> {\n stopKeepAlive()\n broker.unbind()\n if (driver !== undefined && connected) {\n connected = false\n await driver.close()\n }\n },\n }\n}\n\nfunction validateOptions(options: MessagingPluginOptions): MessagingPluginOptions {\n if (options === null || typeof options !== 'object') {\n throw consumerError('MessagingPlugin.configure(): options must be an object')\n }\n const driver = options.driver as Partial<MessagingDriver> | undefined\n if (\n driver === undefined ||\n driver === null ||\n typeof driver.connect !== 'function' ||\n typeof driver.subscribe !== 'function' ||\n typeof driver.publish !== 'function' ||\n typeof driver.close !== 'function' ||\n typeof driver.deadLetterName !== 'function' ||\n typeof driver.capabilities !== 'object'\n ) {\n throw consumerError('MessagingPlugin.configure(): \"driver\" must be a messaging driver', [\n \"Use rabbitmq() from '@znight/messaging/rabbitmq', sqs(), kafka() or memory() from their subpaths\",\n ])\n }\n const defaults = options.defaults\n if (defaults?.concurrency !== undefined && !isPositiveInteger(defaults.concurrency)) {\n throw consumerError(\n `MessagingPlugin defaults.concurrency must be a positive integer, got ${String(defaults.concurrency)}`,\n )\n }\n if (defaults?.requeueDelay !== undefined && !(Number.isFinite(defaults.requeueDelay) && defaults.requeueDelay >= 0)) {\n throw consumerError(\n `MessagingPlugin defaults.requeueDelay must be a number of milliseconds >= 0, got ${String(defaults.requeueDelay)}`,\n )\n }\n if (\n defaults?.deadLetter !== undefined &&\n defaults.deadLetter !== false &&\n typeof defaults.deadLetter !== 'function'\n ) {\n throw consumerError('MessagingPlugin defaults.deadLetter must be a function (queue => name) or false')\n }\n return options\n}\n","/**\n * Copy of the [Standard Schema v1](https://standardschema.dev) interface (MIT). Zod, Valibot, ArkType,\n * Effect Schema... implement it: `@Message(schema)` accepts any of them without depending on one.\n *\n * The upstream declaration uses a namespace; the types are flattened here (`erasableSyntaxOnly`) but\n * remain structurally identical, so any Standard Schema implementation is assignable.\n */\n\n/** A schema implementing the Standard Schema v1 interface. */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1Props<Input, Output>\n}\n\n/** The `~standard` property of a {@link StandardSchemaV1}. */\nexport interface StandardSchemaV1Props<Input = unknown, Output = Input> {\n /** Version of the Standard Schema specification. */\n readonly version: 1\n /** Library implementing the schema (`zod`, `valibot`...). */\n readonly vendor: string\n /** Validates an unknown value, synchronously or asynchronously. */\n readonly validate: (value: unknown) => StandardSchemaV1Result<Output> | Promise<StandardSchemaV1Result<Output>>\n /** Inferred types (type-level only). */\n readonly types?: StandardSchemaV1Types<Input, Output> | undefined\n}\n\n/** Result of {@link StandardSchemaV1Props.validate}. */\nexport type StandardSchemaV1Result<Output> = StandardSchemaV1SuccessResult<Output> | StandardSchemaV1FailureResult\n\n/** Successful validation: `value` is the schema output (defaults, transforms applied). */\nexport interface StandardSchemaV1SuccessResult<Output> {\n readonly value: Output\n readonly issues?: undefined\n}\n\n/** Failed validation. */\nexport interface StandardSchemaV1FailureResult {\n readonly issues: readonly StandardSchemaV1Issue[]\n}\n\n/** One validation issue. */\nexport interface StandardSchemaV1Issue {\n readonly message: string\n readonly path?: readonly (PropertyKey | StandardSchemaV1PathSegment)[] | undefined\n}\n\n/** A segment of an issue path. */\nexport interface StandardSchemaV1PathSegment {\n readonly key: PropertyKey\n}\n\n/** Input and output types of a schema (type-level only). */\nexport interface StandardSchemaV1Types<Input = unknown, Output = Input> {\n readonly input: Input\n readonly output: Output\n}\n\n/** Input type of a Standard Schema. */\nexport type InferSchemaInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input']\n\n/** Output type of a Standard Schema (what handlers receive). */\nexport type InferSchemaOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output']\n\n/** Whether `value` looks like a Standard Schema v1 (has a `~standard.validate` function). */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return false\n const props = (value as { '~standard'?: unknown })['~standard']\n return props !== null && typeof props === 'object' && typeof (props as { validate?: unknown }).validate === 'function'\n}\n","import type {\n InferSchemaOutput,\n StandardSchemaV1,\n StandardSchemaV1Issue,\n StandardSchemaV1PathSegment,\n StandardSchemaV1Result,\n} from './standard-schema.ts'\n\nimport { MessageValidationError, type MessageValidationIssue } from './errors.ts'\nimport { isStandardSchema } from './standard-schema.ts'\n\n/**\n * Validates `value` with a Standard Schema (Zod, Valibot, ArkType...) and returns the schema\n * **output** (defaults and transforms applied). On failure it throws a {@link MessageValidationError},\n * a `NonRetryableError`: the message is dead-lettered without further attempts.\n *\n * Returns synchronously when the schema validates synchronously, a promise otherwise. Called by the\n * code generated for `@Message(schema)` parameters:\n *\n * ```ts\n * invoke: async (c, m) => c.handle(await validateMessage(OrderCreated, m.payload))\n * ```\n *\n * @param label Describes the value in the error message. Default: `'message payload'`.\n */\nexport function validateMessage<S extends StandardSchemaV1>(\n schema: S,\n value: unknown,\n label = 'message payload',\n): InferSchemaOutput<S> | Promise<InferSchemaOutput<S>> {\n if (!isStandardSchema(schema)) {\n throw new TypeError(`[znight] validateMessage(): the schema of the ${label} is not a Standard Schema v1`)\n }\n const result = schema['~standard'].validate(value) as\n | StandardSchemaV1Result<InferSchemaOutput<S>>\n | Promise<StandardSchemaV1Result<InferSchemaOutput<S>>>\n if (isPromiseLike(result)) return Promise.resolve(result).then(settled => unwrap(settled, label))\n return unwrap(result, label)\n}\n\nfunction unwrap<T>(result: StandardSchemaV1Result<T>, label: string): T {\n if (result.issues === undefined) return result.value\n throw new MessageValidationError(label, result.issues.map(toIssue))\n}\n\nfunction toIssue(issue: StandardSchemaV1Issue): MessageValidationIssue {\n return { path: formatPath(issue.path), message: String(issue.message) }\n}\n\nfunction formatPath(path: StandardSchemaV1Issue['path']): string {\n if (path === undefined || path.length === 0) return ''\n return path.map(segment => String(isSegment(segment) ? segment.key : segment)).join('.')\n}\n\nfunction isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {\n return typeof (value as { then?: unknown } | null)?.then === 'function'\n}\n\nfunction isSegment(segment: PropertyKey | StandardSchemaV1PathSegment): segment is StandardSchemaV1PathSegment {\n return typeof segment === 'object' && segment !== null && 'key' in segment\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/broker.ts","../src/decorators.ts","../src/retry.ts","../src/consumers.ts","../src/tokens.ts","../src/runtime.ts","../src/plugin.ts","../src/standard-schema.ts","../src/validation.ts"],"sourcesContent":["import type { Hooks } from '@znight/core'\nimport type { MessageCodec } from './codec.ts'\nimport type { MessagingDriver } from './driver.ts'\nimport type { MessagePublishEvent } from './hooks.ts'\nimport type { OutboundMessage, PublishOptions, PublishResult } from './message.ts'\n\nimport { randomUUID } from 'node:crypto'\n\nimport { brokerUnavailableError } from './errors.ts'\nimport { headerRecord } from './headers.ts'\n\n/**\n * Publishes messages. Inject it in any provider (it is a singleton external provided by\n * `MessagingPlugin`):\n *\n * ```ts\n * @Injectable()\n * export class OrderService {\n * constructor(private readonly broker: MessageBroker) {}\n *\n * async place(order: Order) {\n * await this.broker.publish('orders', order, { type: 'order.placed', key: order.customerId })\n * }\n * }\n * ```\n *\n * `publish()` resolves once the broker confirmed the message (publisher confirms, SQS response,\n * Kafka acknowledgement). It is usable from `onBootstrap` of the messaging plugin until the\n * application is disposed (including providers' `onStart` / `onStop`).\n */\nexport abstract class MessageBroker {\n /**\n * Publishes `payload` to `queue` (a queue for RabbitMQ/SQS, a topic for Kafka). The payload is\n * encoded by the plugin codec (JSON by default; `Uint8Array` is sent as binary).\n */\n abstract publish(queue: string, payload: unknown, options?: PublishOptions): Promise<PublishResult>\n}\n\nconst HEADER_NAME = /^[\\x21-\\x7e]+$/\n\n/** The {@link MessageBroker} registered by `MessagingPlugin` (internal). */\nexport class DriverMessageBroker extends MessageBroker {\n #driver: MessagingDriver | undefined\n #codec: MessageCodec | undefined\n #hooks: Hooks | undefined\n #closed = false\n\n /** Connects the broker to the driver (plugin `onBootstrap`). */\n bind(driver: MessagingDriver, codec: MessageCodec, hooks: Hooks): void {\n this.#driver = driver\n this.#codec = codec\n this.#hooks = hooks\n this.#closed = false\n }\n\n /** Rejects further publications (plugin `onDispose`). */\n unbind(): void {\n this.#closed = true\n }\n\n override async publish(queue: string, payload: unknown, options: PublishOptions = {}): Promise<PublishResult> {\n const driver = this.#driver\n const codec = this.#codec\n if (driver === undefined || codec === undefined) {\n throw brokerUnavailableError(\n `MessageBroker.publish('${queue}'): the messaging plugin is not bootstrapped yet (publish from onStart or later)`,\n )\n }\n if (this.#closed) {\n throw brokerUnavailableError(`MessageBroker.publish('${queue}'): the messaging plugin has been disposed`)\n }\n const message = this.#prepare(queue, payload, options, codec, driver)\n const hooks = this.#hooks\n const wrapped = hooks?.hasWrappers('messaging:publish') ?? false\n const observed = hooks?.hasListeners('messaging:publish') ?? false\n if (hooks === undefined || (!wrapped && !observed)) {\n await driver.publish(queue, message.outbound())\n return { id: message.id }\n }\n const event: MessagePublishEvent = {\n queue,\n type: options.type,\n messageId: message.id,\n headers: message.headers,\n startTime: performance.now(),\n }\n const send = async (): Promise<void> => {\n try {\n await driver.publish(queue, message.outbound())\n } catch (error) {\n event.error = error // visible to the wrappers before the rejection reaches them\n throw error\n }\n }\n try {\n if (wrapped) await hooks.run('messaging:publish', event, send)\n else await send()\n } finally {\n if (observed) hooks.emit('messaging:publish', event)\n }\n return { id: message.id }\n }\n\n #prepare(\n queue: string,\n payload: unknown,\n options: PublishOptions,\n codec: MessageCodec,\n driver: MessagingDriver,\n ): { readonly id: string; readonly headers: Record<string, string>; outbound(): OutboundMessage } {\n if (typeof queue !== 'string' || queue === '') {\n throw new TypeError(\n `[znight] MessageBroker.publish(): the queue must be a non-empty string, got ${String(queue)}`,\n )\n }\n const delayMs = options.delay ?? 0\n if (typeof delayMs !== 'number' || !Number.isFinite(delayMs) || delayMs < 0) {\n throw new TypeError(`[znight] MessageBroker.publish('${queue}'): \"delay\" must be a number of milliseconds >= 0`)\n }\n if (delayMs > driver.capabilities.maxPublishDelayMs) {\n throw new RangeError(\n driver.capabilities.maxPublishDelayMs === 0\n ? `[znight] MessageBroker.publish('${queue}'): the ${driver.name} driver does not support delayed publishing`\n : `[znight] MessageBroker.publish('${queue}'): the ${driver.name} driver supports delays up to ${driver.capabilities.maxPublishDelayMs} ms (got ${delayMs})`,\n )\n }\n const headers = headerRecord()\n if (options.headers !== undefined) {\n for (const [name, value] of Object.entries(options.headers)) {\n if (!HEADER_NAME.test(name))\n throw new TypeError(`[znight] MessageBroker.publish('${queue}'): invalid header name \"${name}\"`)\n if (typeof value !== 'string') {\n throw new TypeError(`[znight] MessageBroker.publish('${queue}'): header \"${name}\" must be a string`)\n }\n headers[name] = value\n }\n }\n const id = options.id ?? randomUUID()\n const encoded = codec.encode(payload, options.contentType)\n const timestamp = Date.now()\n return {\n id,\n headers,\n outbound: () => ({\n id,\n type: options.type,\n key: options.key,\n headers: { ...headers },\n body: encoded.body,\n contentType: encoded.contentType,\n timestamp,\n delayMs,\n }),\n }\n }\n}\n","/**\n * Marker decorators of `@znight/messaging`. Like every Znight decorator they do nothing at runtime:\n * the compiler extension (`@znight/messaging/compiler`) reads them from the source code and generates\n * `metadata.extensions.messaging`. Arguments marked _static_ must be statically evaluable (literals,\n * `const` references); _value_ arguments (schemas) may also be references to exported bindings.\n */\nimport type { ClassMarker, MethodMarker, ParameterMarker, Scope } from '@znight/core'\nimport type { InboundMessage } from './message.ts'\nimport type { RetryOptions } from './retry.ts'\nimport type { StandardSchemaV1 } from './standard-schema.ts'\n\n/** Options of {@link Consumer} (static). */\nexport interface ConsumerOptions {\n /** Queue (RabbitMQ, SQS) or topic (Kafka) to consume. */\n readonly queue: string\n /** Scope of the consumer provider. Default: `singleton`; `request` creates one instance per message. */\n readonly scope?: Scope\n /** Maximum number of messages handled concurrently. Default: plugin `defaults.concurrency` (1). */\n readonly concurrency?: number\n /** Dead-letter queue/topic, or `false` to disable dead-lettering. Default: driver naming (`<queue>.dlq`). */\n readonly deadLetter?: string | false\n /** Driver-specific options (e.g. Kafka `groupId`). */\n readonly [driverOption: string]: unknown\n}\n\n/** Result of an `@OnError` method: `'ack'` acknowledges (swallows) the message, anything else dead-letters it. */\n// biome-ignore lint/suspicious/noConfusingVoidType: lets error handlers without return statement type-check\nexport type ErrorHandlerResult = 'ack' | undefined | void\n\n/**\n * Signature of an `@OnError` method. Declared as a method signature so that parameters may be\n * narrowed (`onError(error: Error, message: InboundMessage<Order>)`).\n */\nexport type ErrorHandlerMethod = ErrorHandlerSignature['onError']\n\ninterface ErrorHandlerSignature {\n onError(error: unknown, message: InboundMessage): ErrorHandlerResult | Promise<ErrorHandlerResult>\n}\n\n/** Marker usable on an `@OnError` method: checks its signature. */\nexport type ErrorHandlerMarker = <F extends ErrorHandlerMethod>(\n target: object,\n propertyKeyOrContext: string | symbol | ClassMethodDecoratorContext<unknown, F>,\n descriptor?: TypedPropertyDescriptor<F>,\n) => void\n\nfunction marker(): void {\n // intentionally empty: decorators are compile-time markers\n}\n\nconst classMarker: ClassMarker = marker\nconst methodMarker: MethodMarker = marker\nconst parameterMarker: ParameterMarker = marker\nconst errorHandlerMarker: ErrorHandlerMarker = marker\n\n/**\n * Declares a message consumer: a provider (singleton by default) whose `@OnMessage` methods handle\n * the messages of `queue`. Arguments are static.\n *\n * ```ts\n * @Consumer('orders')\n * export class OrderConsumer {\n * constructor(private readonly orders: OrderService) {}\n *\n * @OnMessage('order.created')\n * @Retry({ attempts: 5, backoff: 'exponential', delay: 500 })\n * async created(@Message(OrderCreated) event: OrderCreated) {\n * await this.orders.process(event)\n * }\n * }\n * ```\n */\nexport function Consumer(queue: string): ClassMarker\nexport function Consumer(options: ConsumerOptions): ClassMarker\nexport function Consumer(_queueOrOptions: string | ConsumerOptions): ClassMarker {\n return classMarker\n}\n\n/**\n * Declares a message handler. With `type` (static), it handles the messages of that type (exact\n * match, or a pattern where `*` matches any sequence: `'order.*'`); without type it is the consumer's\n * default handler (messages without type, or whose type no other handler matches). A message no\n * handler matches is dead-lettered (reason `no-handler`).\n */\nexport function OnMessage(_type?: string): MethodMarker {\n return methodMarker\n}\n\n/**\n * Retry policy of a handler (static options). `attempts` counts the first delivery:\n * `{ attempts: 3 }` means one delivery plus two retries. Without `@Retry`, the plugin's\n * `defaults.retry` applies (3 attempts, exponential from 1 s).\n */\nexport function Retry(_options: RetryOptions): MethodMarker {\n return methodMarker\n}\n\n/**\n * Declares the consumer's error handler, called once a message will not be retried anymore (last\n * attempt failed, `NonRetryableError`, invalid payload, no matching handler). Returning `'ack'`\n * acknowledges the message; returning anything else (or throwing) dead-letters it.\n *\n * ```ts\n * @OnError()\n * onError(error: unknown, message: InboundMessage) {\n * if (message.type?.startsWith('legacy.')) return 'ack'\n * }\n * ```\n */\nexport function OnError(): ErrorHandlerMarker {\n return errorHandlerMarker\n}\n\n/**\n * Injects the message payload. With a Standard Schema (Zod, Valibot...), the payload is validated and\n * the parameter receives the schema output; an invalid payload is dead-lettered without retry\n * (reason `validation`). The schema is a _value_ argument (inline, or an exported binding).\n */\nexport function Message(_schema?: StandardSchemaV1): ParameterMarker {\n return parameterMarker\n}\n\n/** Injects all the message headers, or the value of one header (`string | undefined`). `name` is static. */\nexport function Headers(_name?: string): ParameterMarker {\n return parameterMarker\n}\n\n/** Injects the {@link InboundMessage} (id, type, attempt, headers, raw message, abort signal...). */\nexport function Ctx(): ParameterMarker {\n return parameterMarker\n}\n","import type { DriverCapabilities } from './driver.ts'\n\nimport { consumerError } from './errors.ts'\n\n/** Backoff strategy between attempts. */\nexport type BackoffStrategy = 'fixed' | 'exponential'\n\n/** Options of `@Retry()` and of the plugin's `defaults.retry`. */\nexport interface RetryOptions {\n /** Total number of attempts, including the first delivery (`1`: no retry). */\n readonly attempts: number\n /** `fixed`: always `delay`; `exponential`: `delay * 2^(attempt - 1)`, capped by `maxDelay`. Default: `exponential`. */\n readonly backoff?: BackoffStrategy\n /** Base delay between attempts, in milliseconds. Default: 1000. */\n readonly delay?: number\n /** Upper bound of exponential delays, in milliseconds. */\n readonly maxDelay?: number\n}\n\n/** A normalized retry policy, as stored in the metadata (`MessageHandlerDefinition.retry`). */\nexport interface RetryPolicy {\n readonly attempts: number\n readonly backoff: BackoffStrategy\n readonly delay: number\n readonly maxDelay?: number\n}\n\n/** Default base delay of a retry policy, in milliseconds. */\nexport const DEFAULT_RETRY_DELAY_MS = 1000\n\n/** Default backoff strategy. */\nexport const DEFAULT_BACKOFF: BackoffStrategy = 'exponential'\n\n/** Policy applied to handlers without `@Retry` when the plugin sets no `defaults.retry`. */\nexport const DEFAULT_RETRY_POLICY: RetryPolicy = Object.freeze({\n attempts: 3,\n backoff: 'exponential',\n delay: DEFAULT_RETRY_DELAY_MS,\n maxDelay: 30_000,\n})\n\n/** The policy of `retry: false` / `{ attempts: 1 }`. */\nexport const NO_RETRY_POLICY: RetryPolicy = Object.freeze({ attempts: 1, backoff: 'fixed', delay: 0 })\n\nconst MAX_ATTEMPTS = 10_000\n\n/**\n * Validates retry options and fills defaults.\n *\n * @throws ZnightError ZNIGHT011 for invalid values (`where` names the handler in the message).\n */\nexport function normalizeRetry(options: RetryOptions | RetryPolicy, where = '@Retry()'): RetryPolicy {\n if (options === null || typeof options !== 'object') {\n throw consumerError(`${where}: retry options must be an object, got ${String(options)}`)\n }\n const { attempts, backoff = DEFAULT_BACKOFF, delay = DEFAULT_RETRY_DELAY_MS, maxDelay } = options\n if (!Number.isInteger(attempts) || attempts < 1 || attempts > MAX_ATTEMPTS) {\n throw consumerError(\n `${where}: \"attempts\" must be an integer between 1 and ${MAX_ATTEMPTS}, got ${String(attempts)}`,\n ['attempts counts the first delivery: { attempts: 3 } means 1 delivery + 2 retries'],\n )\n }\n if (backoff !== 'fixed' && backoff !== 'exponential') {\n throw consumerError(`${where}: \"backoff\" must be 'fixed' or 'exponential', got ${String(backoff)}`)\n }\n if (!isDuration(delay)) {\n throw consumerError(`${where}: \"delay\" must be a finite number of milliseconds >= 0, got ${String(delay)}`)\n }\n if (maxDelay !== undefined && (!isDuration(maxDelay) || maxDelay < delay)) {\n throw consumerError(\n `${where}: \"maxDelay\" must be a finite number of milliseconds >= delay (${delay}), got ${String(maxDelay)}`,\n )\n }\n return maxDelay === undefined ? { attempts, backoff, delay } : { attempts, backoff, delay, maxDelay }\n}\n\nfunction isDuration(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n}\n\n/**\n * Delay before the next attempt once `attempt` (1-based) failed: `delay` for `fixed`,\n * `delay * 2^(attempt - 1)` capped by `maxDelay` for `exponential`.\n */\nexport function computeRetryDelay(policy: RetryPolicy, attempt: number): number {\n if (policy.backoff === 'fixed') return policy.delay\n const exponent = Math.max(0, attempt - 1)\n const raw = policy.delay * 2 ** exponent\n return Math.min(raw, policy.maxDelay ?? Number.MAX_SAFE_INTEGER)\n}\n\n/** Every delay the policy may request (one per retry), in attempt order. */\nexport function retryDelays(policy: RetryPolicy): number[] {\n const delays: number[] = []\n for (let attempt = 1; attempt < policy.attempts; attempt++) delays.push(computeRetryDelay(policy, attempt))\n return delays\n}\n\n/** Whether a delay is waited in-process (`true`) or delegated to the broker (`false`). */\nexport function isInProcessDelay(delayMs: number, capabilities: DriverCapabilities): boolean {\n return delayMs <= capabilities.maxInProcessDelayMs\n}\n\n/**\n * Checks that the driver can honour every delay of `policy`.\n *\n * @throws ZnightError ZNIGHT011 naming `where` and the offending delay.\n */\nexport function assertRetrySupported(\n policy: RetryPolicy,\n capabilities: DriverCapabilities,\n driver: string,\n where: string,\n): void {\n for (const delay of new Set(retryDelays(policy))) {\n if (isInProcessDelay(delay, capabilities)) continue\n if (capabilities.nativeDelay && delay <= capabilities.maxNativeDelayMs) continue\n const limits = capabilities.nativeDelay\n ? `in-process delays up to ${capabilities.maxInProcessDelayMs} ms and native delays up to ${capabilities.maxNativeDelayMs} ms`\n : `in-process delays up to ${capabilities.maxInProcessDelayMs} ms and no native delay`\n throw consumerError(\n `${where}: the ${driver} driver cannot wait ${delay} ms between attempts (it supports ${limits})`,\n [\n 'Lower \"delay\"/\"maxDelay\" of the retry policy',\n 'Or raise the driver budget (e.g. maxInProcessDelayMs) if holding messages that long is acceptable',\n ],\n )\n }\n}\n","/**\n * Preparation and validation of the consumers declared in the metadata (boot time, ZNIGHT011).\n */\nimport type { Container, Token } from '@znight/core'\nimport type { MessagingDriver } from './driver.ts'\nimport type { InboundMessage } from './message.ts'\nimport type { ConsumerDefinition, MessageHandlerDefinition, MessagingMetadata } from './metadata.ts'\nimport type { RetryOptions, RetryPolicy } from './retry.ts'\n\nimport { displayName } from '@znight/core'\n\nimport { consumerError } from './errors.ts'\nimport { isPositiveInteger } from './internal/async.ts'\nimport {\n assertRetrySupported,\n DEFAULT_RETRY_POLICY,\n isInProcessDelay,\n NO_RETRY_POLICY,\n normalizeRetry,\n retryDelays,\n} from './retry.ts'\n\n/** A handler ready to be dispatched to. */\nexport interface PreparedHandler {\n readonly definition: MessageHandlerDefinition\n /**\n * Calls the handler: `definition.invoke`, or for a handler with parameter resolvers (RFC 0010) a\n * function passing their instances, resolved in the delivery scope.\n */\n readonly invoke: (consumer: unknown, message: InboundMessage) => unknown\n readonly method: string\n readonly policy: RetryPolicy\n}\n\n/** A validated consumer. */\nexport interface PreparedConsumer {\n readonly definition: ConsumerDefinition\n readonly token: Token\n /** Display name of the consumer class. */\n readonly name: string\n readonly queue: string\n readonly concurrency: number\n readonly deadLetterQueue: string | undefined\n readonly errorHandler: string | undefined\n readonly maxAttempts: number\n /** Native delays the runtime may request (sorted, unique). */\n readonly retryDelays: readonly number[]\n readonly driverOptions: Readonly<Record<string, unknown>>\n /** Selects the handler of a message type (exact, then patterns, then the default handler). */\n select(type: string | undefined): PreparedHandler | undefined\n}\n\n/** Plugin-level defaults applied to every consumer. */\nexport interface ConsumerDefaults {\n readonly retry: RetryPolicy\n readonly concurrency: number\n readonly deadLetter: ((queue: string) => string) | false | undefined\n}\n\n/** Normalizes `defaults.retry` of the plugin. */\nexport function defaultRetryPolicy(retry: RetryOptions | false | undefined): RetryPolicy {\n if (retry === undefined) return DEFAULT_RETRY_POLICY\n if (retry === false) return NO_RETRY_POLICY\n return normalizeRetry(retry, 'MessagingPlugin defaults.retry')\n}\n\n/**\n * Validates `metadata.extensions.messaging` against the container and the driver capabilities.\n *\n * @throws ZnightError ZNIGHT011 describing the first invalid consumer.\n */\nexport function prepareConsumers(\n metadata: MessagingMetadata | undefined,\n container: Container,\n driver: MessagingDriver,\n defaults: ConsumerDefaults,\n): PreparedConsumer[] {\n if (metadata === undefined) return []\n if (metadata === null || typeof metadata !== 'object' || !Array.isArray(metadata.consumers)) {\n throw consumerError('Invalid messaging metadata: \"consumers\" must be an array', [\n 'Regenerate the metadata with the @znight/messaging compiler extension',\n ])\n }\n const byQueue = new Map<string, string>()\n const prepared: PreparedConsumer[] = []\n for (const definition of metadata.consumers) {\n const consumer = prepareConsumer(definition, container, driver, defaults)\n const previous = byQueue.get(consumer.queue)\n if (previous !== undefined) {\n throw consumerError(`${previous} and ${consumer.name} both consume \"${consumer.queue}\"`, [\n 'A queue is consumed by a single @Consumer class: merge the handlers, or route by type with @OnMessage(type)',\n ])\n }\n byQueue.set(consumer.queue, consumer.name)\n prepared.push(consumer)\n }\n return prepared\n}\n\nfunction prepareConsumer(\n definition: ConsumerDefinition,\n container: Container,\n driver: MessagingDriver,\n defaults: ConsumerDefaults,\n): PreparedConsumer {\n if (\n definition === null ||\n typeof definition !== 'object' ||\n typeof definition.token !== 'string' ||\n definition.token === ''\n ) {\n throw consumerError('Invalid consumer definition: missing provider token')\n }\n const token = definition.token\n const name = displayName(token)\n if (typeof definition.queue !== 'string' || definition.queue === '') {\n throw consumerError(`@Consumer ${name}: the queue must be a non-empty string`)\n }\n if (!container.has(token)) {\n throw consumerError(`@Consumer ${name}: no provider is registered for ${token}`, [\n 'Regenerate the metadata: the consumer class must be a compiled provider',\n ])\n }\n const options = definition.options ?? {}\n const { concurrency: rawConcurrency, deadLetter, ...driverOptions } = options\n const concurrency = rawConcurrency ?? defaults.concurrency\n if (!isPositiveInteger(concurrency)) {\n throw consumerError(`@Consumer ${name}: \"concurrency\" must be a positive integer, got ${String(concurrency)}`)\n }\n if (deadLetter !== undefined && deadLetter !== false && (typeof deadLetter !== 'string' || deadLetter === '')) {\n throw consumerError(`@Consumer ${name}: \"deadLetter\" must be a queue name or false, got ${String(deadLetter)}`)\n }\n if (deadLetter === definition.queue) {\n throw consumerError(`@Consumer ${name}: the dead-letter queue cannot be the consumed queue \"${definition.queue}\"`)\n }\n\n const handlers = definition.handlers\n if (!Array.isArray(handlers) || handlers.length === 0) {\n throw consumerError(`@Consumer ${name} has no @OnMessage handler`, [\n 'Decorate at least one method with @OnMessage()',\n ])\n }\n const exact = new Map<string, PreparedHandler>()\n const patterns: { readonly regex: RegExp; readonly handler: PreparedHandler }[] = []\n let fallback: PreparedHandler | undefined\n let maxAttempts = 1\n const nativeDelays = new Set<number>()\n for (const handlerDefinition of handlers) {\n const handler = prepareHandler(handlerDefinition, name, driver, defaults.retry, container)\n maxAttempts = Math.max(maxAttempts, handler.policy.attempts)\n for (const delay of retryDelays(handler.policy)) {\n if (!isInProcessDelay(delay, driver.capabilities)) nativeDelays.add(delay)\n }\n const type = handlerDefinition.type\n if (type === undefined) {\n if (fallback !== undefined) {\n throw consumerError(\n `@Consumer ${name}: ${fallback.method}() and ${handler.method}() are both default @OnMessage() handlers`,\n ['Give one of them a message type: @OnMessage(\"order.created\")'],\n )\n }\n fallback = handler\n } else if (type.includes('*')) {\n if (patterns.some(entry => entry.handler.definition.type === type)) {\n throw duplicateType(name, type)\n }\n patterns.push({ regex: patternToRegExp(type), handler })\n } else {\n if (exact.has(type)) throw duplicateType(name, type)\n exact.set(type, handler)\n }\n }\n\n const errorHandler = definition.errorHandler\n if (errorHandler !== undefined) {\n if (typeof errorHandler !== 'string' || errorHandler === '') {\n throw consumerError(`@Consumer ${name}: invalid @OnError method name ${String(errorHandler)}`)\n }\n const providers = container.metadata.providers\n const provider = Object.hasOwn(providers, token) ? providers[token] : undefined\n const prototype = provider?.class?.prototype as Record<string, unknown> | undefined\n if (prototype !== undefined && typeof prototype[errorHandler] !== 'function') {\n throw consumerError(`@Consumer ${name}: the @OnError method ${errorHandler}() does not exist`)\n }\n }\n\n return {\n definition,\n token,\n name,\n queue: definition.queue,\n concurrency,\n deadLetterQueue: resolveDeadLetter(definition.queue, deadLetter, defaults.deadLetter, driver),\n errorHandler,\n maxAttempts,\n retryDelays: [...nativeDelays].sort((a, b) => a - b),\n driverOptions,\n select(type: string | undefined): PreparedHandler | undefined {\n if (type === undefined) return fallback\n const handler = exact.get(type)\n if (handler !== undefined) return handler\n for (const entry of patterns) if (entry.regex.test(type)) return entry.handler\n return fallback\n },\n }\n}\n\nfunction prepareHandler(\n definition: MessageHandlerDefinition,\n consumer: string,\n driver: MessagingDriver,\n defaultPolicy: RetryPolicy,\n container: Container,\n): PreparedHandler {\n if (\n definition === null ||\n typeof definition !== 'object' ||\n typeof definition.method !== 'string' ||\n definition.method === ''\n ) {\n throw consumerError(`@Consumer ${consumer}: invalid @OnMessage handler definition`)\n }\n const where = `${consumer}.${definition.method}()`\n if (typeof definition.invoke !== 'function') {\n throw consumerError(`${where}: the handler has no invoker`, ['Regenerate the metadata'])\n }\n if (definition.type !== undefined && (typeof definition.type !== 'string' || definition.type === '')) {\n throw consumerError(`${where}: the @OnMessage type must be a non-empty string`)\n }\n const policy = definition.retry === undefined ? defaultPolicy : normalizeRetry(definition.retry, `@Retry on ${where}`)\n assertRetrySupported(\n policy,\n driver.capabilities,\n driver.name,\n definition.retry === undefined ? `${where} (default retry policy)` : `@Retry on ${where}`,\n )\n return { definition, invoke: handlerInvoke(definition, container, where), method: definition.method, policy }\n}\n\n/** The generated invoke, or a function passing the parameter resolver instances (resolved per delivery). */\nfunction handlerInvoke(\n definition: MessageHandlerDefinition,\n container: Container,\n where: string,\n): PreparedHandler['invoke'] {\n const invoke = definition.invoke\n const tokens = definition.paramResolvers\n if (tokens === undefined || tokens.length === 0) return invoke\n for (const token of tokens) {\n if (!container.has(token)) {\n throw consumerError(`${where}: the parameter resolver ${token} is not a provider`, ['Regenerate the metadata'])\n }\n }\n return (consumer, message) =>\n invoke(\n consumer,\n message,\n tokens.map(token => container.get(token)),\n )\n}\n\nfunction duplicateType(consumer: string, type: string): Error {\n return consumerError(`@Consumer ${consumer}: several @OnMessage handlers for type \"${type}\"`)\n}\n\n/** `order.*` → `/^order\\..*$/`: `*` matches any sequence of characters. */\nfunction patternToRegExp(pattern: string): RegExp {\n const source = pattern\n .split('*')\n .map(part => part.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*')\n return new RegExp(`^${source}$`, 's')\n}\n\nfunction resolveDeadLetter(\n queue: string,\n option: string | false | undefined,\n defaults: ((queue: string) => string) | false | undefined,\n driver: MessagingDriver,\n): string | undefined {\n if (option === false) return undefined\n if (typeof option === 'string') return option\n if (defaults === false) return undefined\n const name = defaults === undefined ? driver.deadLetterName(queue) : defaults(queue)\n if (typeof name !== 'string' || name === '' || name === queue) {\n throw consumerError(`Invalid dead-letter queue name for \"${queue}\": ${String(name)}`)\n }\n return name\n}\n","import type { TypedToken } from '@znight/core'\nimport type { MessageBroker } from './broker.ts'\nimport type { InboundMessage } from './message.ts'\n\n/** Tokens of the externals provided by `MessagingPlugin` (declared in the package manifest). */\nexport const MessagingTokens: {\n /** The {@link MessageBroker} singleton. */\n readonly MessageBroker: TypedToken<MessageBroker>\n /** The {@link InboundMessage} of the current delivery (request-scoped). */\n readonly InboundMessage: TypedToken<InboundMessage>\n} = Object.freeze({\n MessageBroker: '@znight/messaging#MessageBroker',\n InboundMessage: '@znight/messaging#InboundMessage',\n})\n","/**\n * The delivery state machine (docs/internals/architecture.md §9.2), one {@link ConsumerRuntime} per\n * `@Consumer` class.\n *\n * For each delivery: concurrency slot → request scope seeded with the `InboundMessage` →\n * `hooks.run('messaging:message')` → decode → select the handler by type → invoke →\n * ack | retry (in-process wait or native redelivery) | `@OnError` → dead-letter (publish then ack;\n * on failure the message is released for a later redelivery). Nothing is ever dropped: every path\n * ends with the broker owning the message again.\n */\nimport type { Container, CoreLogger, Hooks, MessageDeliveryEvent } from '@znight/core'\nimport type { MessageCodec } from './codec.ts'\nimport type { PreparedConsumer, PreparedHandler } from './consumers.ts'\nimport type { DriverDelivery, MessagingDriver, Subscription } from './driver.ts'\nimport type { DeadLetterReason, InboundMessage } from './message.ts'\n\nimport {\n AttemptsExhaustedError,\n isNonRetryable,\n MessageDecodeError,\n MessageValidationError,\n NoHandlerError,\n} from './errors.ts'\nimport { errorMessage, Semaphore, sleep } from './internal/async.ts'\nimport { computeRetryDelay, isInProcessDelay } from './retry.ts'\nimport { MessagingTokens } from './tokens.ts'\n\n/** Services shared by every consumer runtime. */\nexport interface RuntimeServices {\n readonly container: Container\n readonly hooks: Hooks\n readonly logger: CoreLogger\n readonly codec: MessageCodec\n readonly driver: MessagingDriver\n /** Pause before releasing a message whose settlement failed (avoids hot redelivery loops). */\n readonly requeueDelayMs: number\n}\n\n/** State of a {@link ConsumerRuntime}. */\nexport type ConsumerState = 'created' | 'starting' | 'running' | 'stopping' | 'stopped'\n\n/** What remains to do once the scope of a delivery closed. */\ntype Followup =\n | { readonly kind: 'retry'; readonly delayMs: number; readonly error: unknown }\n | { readonly kind: 'requeue' }\n | undefined\n\n/** Per-delivery working state. */\ninterface DeliveryContext {\n readonly delivery: DriverDelivery\n readonly message: InboundMessage\n readonly handler: PreparedHandler | undefined\n readonly event: MessageDeliveryEvent\n readonly decodeError: unknown\n executed: boolean\n followup: Followup\n}\n\n/** Runs the deliveries of one consumer. */\nexport class ConsumerRuntime {\n readonly consumer: PreparedConsumer\n readonly #services: RuntimeServices\n readonly #slots: Semaphore\n readonly #abort = new AbortController()\n readonly #inflight = new Set<Promise<void>>()\n #subscription: Subscription | undefined\n #state: ConsumerState = 'created'\n\n constructor(consumer: PreparedConsumer, services: RuntimeServices) {\n this.consumer = consumer\n this.#services = services\n this.#slots = new Semaphore(consumer.concurrency)\n }\n\n get state(): ConsumerState {\n return this.#state\n }\n\n /** Deliveries currently handled (including those waiting for a slot or an in-process retry delay). */\n get inFlight(): number {\n return this.#inflight.size\n }\n\n /** Subscribes to the queue. */\n async start(): Promise<void> {\n if (this.#state !== 'created') throw new Error(`[znight] Consumer ${this.consumer.name} was already started`)\n this.#state = 'starting'\n const { consumer } = this\n try {\n this.#subscription = await this.#services.driver.subscribe(consumer.queue, delivery => this.handle(delivery), {\n consumer: consumer.name,\n concurrency: consumer.concurrency,\n deadLetterQueue: consumer.deadLetterQueue,\n maxAttempts: consumer.maxAttempts,\n retryDelays: consumer.retryDelays,\n options: consumer.driverOptions,\n })\n } catch (error) {\n this.#state = 'stopped'\n throw error\n }\n this.#state = 'running'\n }\n\n /**\n * Graceful stop: stops receiving, aborts in-process retry delays (those messages are released to\n * the broker), awaits the in-flight handlers, then closes the subscription. Never throws.\n */\n async stop(): Promise<void> {\n if (this.#state === 'stopping' || this.#state === 'stopped') return\n this.#state = 'stopping'\n this.#abort.abort()\n const subscription = this.#subscription\n if (subscription !== undefined) {\n try {\n await subscription.cancel()\n } catch (error) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: failed to cancel the subscription`, { err: error })\n }\n }\n while (this.#inflight.size > 0) await Promise.allSettled([...this.#inflight])\n if (subscription !== undefined) {\n try {\n await subscription.close()\n } catch (error) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: failed to close the subscription`, { err: error })\n }\n }\n this.#state = 'stopped'\n }\n\n /** Handles one delivery (the driver's `DeliveryHandler`). Never rejects. */\n handle(delivery: DriverDelivery): Promise<void> {\n const promise = this.#handle(delivery)\n this.#inflight.add(promise)\n const done = (): void => {\n this.#inflight.delete(promise)\n }\n promise.then(done, done)\n return promise\n }\n\n async #handle(delivery: DriverDelivery): Promise<void> {\n const signal = this.#abort.signal\n if (signal.aborted) return this.#release(delivery)\n try {\n await this.#slots.acquire(signal)\n } catch {\n return this.#release(delivery)\n }\n try {\n await this.#process(delivery)\n } catch (error) {\n // defensive: #process handles its failures; never leave a message unsettled\n this.#services.logger.error(`Consumer ${this.consumer.name}: unexpected delivery failure`, { err: error })\n await this.#release(delivery)\n } finally {\n this.#slots.release()\n }\n }\n\n async #process(delivery: DriverDelivery): Promise<void> {\n const { container, hooks, codec, driver } = this.#services\n const { consumer } = this\n const handler = consumer.select(delivery.type)\n let payload: unknown\n let decodeError: unknown\n try {\n payload = codec.decode(delivery.body, delivery.contentType)\n } catch (error) {\n decodeError = isNonRetryable(error)\n ? error\n : new MessageDecodeError(`Cannot decode the message body: ${errorMessage(error)}`, { cause: error })\n }\n const message: InboundMessage = {\n id: delivery.id,\n queue: consumer.queue,\n type: delivery.type,\n payload,\n headers: delivery.headers,\n key: delivery.key,\n attempt: delivery.attempt,\n maxAttempts: handler?.policy.attempts ?? 1,\n redelivered: delivery.redelivered,\n timestamp: delivery.timestamp,\n receivedAt: Date.now(),\n contentType: delivery.contentType,\n body: delivery.body,\n driver: driver.name,\n raw: delivery.raw,\n signal: this.#abort.signal,\n }\n const event: MessageDeliveryEvent = {\n queue: consumer.queue,\n type: delivery.type,\n messageId: delivery.id,\n attempt: delivery.attempt,\n headers: delivery.headers,\n consumer: consumer.name,\n handler: handler?.method,\n message,\n startTime: performance.now(),\n }\n const context: DeliveryContext = {\n delivery,\n message,\n handler,\n event,\n decodeError,\n executed: false,\n followup: undefined,\n }\n\n let failure: unknown = new Error('A messaging:message hook wrapper did not call next()')\n try {\n await container.runInScope(\n () => hooks.run('messaging:message', event, () => this.#execute(context)),\n [[MessagingTokens.InboundMessage, message]],\n )\n } catch (error) {\n failure = error\n if (context.executed) {\n // the delivery was settled: a wrapper failed afterwards, or a request-scoped instance failed to dispose\n this.#services.logger.error(`Consumer ${consumer.name}: error after handling message ${delivery.id}`, {\n err: error,\n queue: consumer.queue,\n })\n }\n }\n // the handler never ran (a hooks wrapper threw or did not call next()): count it as a failed attempt\n if (!context.executed) await this.#failure(context, failure, false)\n if (hooks.hasListeners('messaging:message')) hooks.emit('messaging:message', event)\n await this.#followup(context)\n }\n\n /** Runs inside the request scope and the hooks: handler, then settlement. Never throws. */\n async #execute(context: DeliveryContext): Promise<void> {\n if (context.executed) return // a wrapper called next() twice\n context.executed = true\n const { handler, message } = context\n if (context.decodeError !== undefined) return this.#failure(context, context.decodeError, true)\n if (handler === undefined)\n return this.#terminal(context, new NoHandlerError(this.consumer.name, message.type), 'no-handler', true)\n if (message.attempt > message.maxAttempts) {\n return this.#terminal(\n context,\n new AttemptsExhaustedError(message.attempt, message.maxAttempts),\n 'max-attempts',\n false,\n )\n }\n try {\n const instance = this.#services.container.get(this.consumer.token)\n await handler.invoke(instance, message)\n } catch (error) {\n return this.#failure(context, error, true)\n }\n return this.#ack(context)\n }\n\n /** A failed attempt: retry while attempts remain, dead-letter otherwise. */\n #failure(context: DeliveryContext, error: unknown, withErrorHandler: boolean): Promise<void> {\n if (isNonRetryable(error)) return this.#terminal(context, error, reasonOf(error), withErrorHandler)\n if (context.message.attempt < context.message.maxAttempts) return this.#retry(context, error)\n return this.#terminal(context, error, 'max-attempts', withErrorHandler)\n }\n\n async #retry(context: DeliveryContext, error: unknown): Promise<void> {\n const { message, delivery, event } = context\n const policy = context.handler?.policy\n const delayMs = policy === undefined ? 0 : computeRetryDelay(policy, message.attempt)\n event.outcome = 'retry'\n event.error = error\n this.#services.logger.warn(\n `Consumer ${this.consumer.name}: attempt ${message.attempt}/${message.maxAttempts} of message ${message.id} failed, retrying in ${delayMs} ms`,\n { queue: message.queue, messageId: message.id, type: message.type, ...this.#errorContext(error) },\n )\n if (isInProcessDelay(delayMs, this.#services.driver.capabilities)) {\n context.followup = { kind: 'retry', delayMs, error }\n return\n }\n try {\n await delivery.retry({ attempt: message.attempt + 1, delayMs, error })\n } catch (retryError) {\n this.#services.logger.error(\n `Consumer ${this.consumer.name}: failed to schedule the retry of message ${message.id}`,\n {\n err: retryError,\n queue: message.queue,\n },\n )\n context.followup = { kind: 'requeue' }\n }\n }\n\n /** No more attempts: `@OnError` may acknowledge, otherwise the message is dead-lettered. */\n async #terminal(\n context: DeliveryContext,\n error: unknown,\n reason: DeadLetterReason,\n withErrorHandler: boolean,\n ): Promise<void> {\n context.event.error = error\n const method = this.consumer.errorHandler\n if (withErrorHandler && method !== undefined) {\n try {\n const instance = this.#services.container.get(this.consumer.token) as Record<\n string,\n (...args: unknown[]) => unknown\n >\n const result = await instance[method]?.(error, context.message)\n if (result === 'ack') return this.#ack(context)\n } catch (handlerError) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: @OnError ${method}() failed`, {\n err: handlerError,\n queue: context.message.queue,\n messageId: context.message.id,\n })\n }\n }\n return this.#deadLetter(context, error, reason)\n }\n\n async #deadLetter(context: DeliveryContext, error: unknown, reason: DeadLetterReason): Promise<void> {\n const { message, delivery, event } = context\n const queue = this.consumer.deadLetterQueue\n event.outcome = 'dead-letter'\n try {\n await delivery.deadLetter({ queue, reason, error, attempt: message.attempt })\n } catch (deadLetterError) {\n event.outcome = 'retry'\n event.error = deadLetterError\n this.#services.logger.error(\n `Consumer ${this.consumer.name}: failed to dead-letter message ${message.id}, it will be redelivered`,\n { err: deadLetterError, cause: error, queue: message.queue, deadLetterQueue: queue },\n )\n context.followup = { kind: 'requeue' }\n return\n }\n this.#services.logger.error(\n queue === undefined\n ? `Consumer ${this.consumer.name}: message ${message.id} rejected (${reason})`\n : `Consumer ${this.consumer.name}: message ${message.id} dead-lettered to ${queue} (${reason})`,\n {\n queue: message.queue,\n messageId: message.id,\n type: message.type,\n attempt: message.attempt,\n reason,\n ...this.#errorContext(error),\n },\n )\n }\n\n /**\n * The error of a failed delivery in the retry / dead-letter log entries: the error itself (with its\n * stack), or only its message when a `messaging:message` wrapper is installed. Such a wrapper (the\n * ObservabilityPlugin delivery log) writes its own entry for the delivery with the error: the stack\n * is logged once. Settlement failures always log their error.\n */\n #errorContext(error: unknown): { readonly err: unknown } | { readonly error: string } {\n return this.#services.hooks.hasWrappers('messaging:message') ? { error: errorMessage(error) } : { err: error }\n }\n\n async #ack(context: DeliveryContext): Promise<void> {\n context.event.outcome = 'ack'\n try {\n await context.delivery.ack()\n } catch (error) {\n this.#services.logger.warn(\n `Consumer ${this.consumer.name}: failed to acknowledge message ${context.message.id}, the broker will redeliver it`,\n { err: error, queue: context.message.queue },\n )\n }\n }\n\n /** Work done after the scope closed: in-process retry delays and requeues. */\n async #followup(context: DeliveryContext): Promise<void> {\n const followup = context.followup\n if (followup === undefined) return\n const { delivery, message } = context\n if (followup.kind === 'retry') {\n try {\n await sleep(followup.delayMs, this.#abort.signal)\n } catch {\n return this.#release(delivery) // stopping: the broker redelivers it later\n }\n try {\n await delivery.retry({ attempt: message.attempt + 1, delayMs: 0, error: followup.error })\n return\n } catch (error) {\n this.#services.logger.error(`Consumer ${this.consumer.name}: failed to redeliver message ${message.id}`, {\n err: error,\n queue: message.queue,\n })\n }\n }\n try {\n await sleep(this.#services.requeueDelayMs, this.#abort.signal)\n } catch {\n // stopping: release right away\n }\n return this.#release(delivery)\n }\n\n async #release(delivery: DriverDelivery): Promise<void> {\n try {\n await delivery.release()\n } catch (error) {\n this.#services.logger.error(\n `Consumer ${this.consumer.name}: failed to release message ${delivery.id}, the broker will redeliver it`,\n { err: error, queue: this.consumer.queue },\n )\n }\n }\n}\n\nfunction reasonOf(error: unknown): DeadLetterReason {\n if (error instanceof MessageValidationError) return 'validation'\n if (error instanceof MessageDecodeError) return 'decode'\n if (error instanceof NoHandlerError) return 'no-handler'\n if (error instanceof AttemptsExhaustedError) return 'max-attempts'\n return 'non-retryable'\n}\n","import type { Plugin, PluginContext, PluginOptions } from '@znight/core'\nimport type { MessageCodec } from './codec.ts'\nimport type { MessagingDriver } from './driver.ts'\nimport type { RetryOptions } from './retry.ts'\n\nimport { resolveOptions } from '@znight/core'\n\nimport { DriverMessageBroker, type MessageBroker } from './broker.ts'\nimport { jsonCodec } from './codec.ts'\nimport { type ConsumerDefaults, defaultRetryPolicy, prepareConsumers } from './consumers.ts'\nimport { consumerError } from './errors.ts'\nimport { isPositiveInteger } from './internal/async.ts'\nimport { ConsumerRuntime, type ConsumerState } from './runtime.ts'\nimport { MessagingTokens } from './tokens.ts'\n\n/** Defaults applied to every consumer (each `@Consumer` / `@Retry` may override them). */\nexport interface MessagingDefaults {\n /** Retry policy of handlers without `@Retry`. Default: 3 attempts, exponential from 1 s up to 30 s. `false`: no retry. */\n readonly retry?: RetryOptions | false\n /** Concurrency of consumers without `concurrency` option. Default: 1. */\n readonly concurrency?: number\n /**\n * Dead-letter destination of a queue, or `false` to disable dead-lettering (messages are then\n * rejected natively or discarded). Default: the driver naming (`orders.dlq`, SQS `orders-dlq`).\n */\n readonly deadLetter?: ((queue: string) => string) | false\n /**\n * Pause, in milliseconds, before releasing a message whose settlement failed (dead-letter queue\n * unavailable...), to avoid hot redelivery loops. Default: 1000.\n */\n readonly requeueDelay?: number\n}\n\n/** Options of {@link MessagingPlugin.configure}. */\nexport interface MessagingPluginOptions {\n /** The broker driver: `rabbitmq()`, `sqs()`, `kafka()` or `memory()` (from the matching subpath). */\n readonly driver: MessagingDriver\n readonly defaults?: MessagingDefaults\n /** Payload encoding. Default: {@link jsonCodec}. */\n readonly codec?: MessageCodec\n /** Subscribe the consumers on start. `false` for publish-only processes. Default: `true`. */\n readonly consume?: boolean\n}\n\n/** Runtime status of a consumer. */\nexport interface ConsumerStatus {\n /** Display name of the consumer class. */\n readonly consumer: string\n readonly queue: string\n readonly state: ConsumerState\n readonly concurrency: number\n readonly inFlight: number\n readonly deadLetterQueue: string | undefined\n}\n\n/** The plugin returned by {@link MessagingPlugin.configure}. */\nexport interface MessagingPluginInstance extends Plugin {\n readonly name: 'messaging'\n /** The broker (also injectable as `MessageBroker`). */\n readonly broker: MessageBroker\n /** The driver, once the plugin is bootstrapped. */\n readonly driver: MessagingDriver | undefined\n /** Status of every consumer, once the plugin is bootstrapped. */\n consumers(): ConsumerStatus[]\n}\n\nconst DEFAULT_REQUEUE_DELAY_MS = 1000\n/** Period of the keep-alive timer (it never does anything: it only keeps the event loop alive). */\nconst KEEP_ALIVE_INTERVAL_MS = 2 ** 30\n\n/**\n * Message consumers and publishing.\n *\n * ```ts\n * import { MessagingPlugin } from '@znight/messaging'\n * import { rabbitmq } from '@znight/messaging/rabbitmq'\n *\n * const app = await Application.create({\n * metadata: __ZNIGHT_METADATA__,\n * plugins: [MessagingPlugin.configure({ driver: rabbitmq({ url: 'amqp://localhost' }) })],\n * })\n * ```\n *\n * Options may be computed from the container (evaluated in `onBootstrap`):\n * `MessagingPlugin.configure(c => ({ driver: rabbitmq({ url: c.get(AmqpConfig).url }) }))`.\n */\nexport const MessagingPlugin: {\n configure(options: PluginOptions<MessagingPluginOptions>): MessagingPluginInstance\n} = Object.freeze({\n configure(options: PluginOptions<MessagingPluginOptions>): MessagingPluginInstance {\n return createPlugin(options)\n },\n})\n\nfunction createPlugin(rawOptions: PluginOptions<MessagingPluginOptions>): MessagingPluginInstance {\n const broker = new DriverMessageBroker()\n let driver: MessagingDriver | undefined\n let connected = false\n let consume = true\n let runtimes: ConsumerRuntime[] = []\n /**\n * Keeps the process alive while consumers run: they are its ingress, and some drivers hold no\n * handle of their own (the memory driver only uses microtasks and short timers), so a process\n * without other handle would exit right after `app.start()`, without graceful shutdown.\n */\n let keepAlive: ReturnType<typeof setInterval> | undefined\n const stopKeepAlive = (): void => {\n if (keepAlive !== undefined) clearInterval(keepAlive)\n keepAlive = undefined\n }\n\n return {\n name: 'messaging',\n broker,\n get driver(): MessagingDriver | undefined {\n return driver\n },\n consumers(): ConsumerStatus[] {\n return runtimes.map(runtime => ({\n consumer: runtime.consumer.name,\n queue: runtime.consumer.queue,\n state: runtime.state,\n concurrency: runtime.consumer.concurrency,\n inFlight: runtime.inFlight,\n deadLetterQueue: runtime.consumer.deadLetterQueue,\n }))\n },\n\n register(ctx: PluginContext): void {\n ctx.container.registerExternal({\n token: MessagingTokens.InboundMessage,\n scope: 'request',\n providedBy: 'MessagingPlugin',\n })\n ctx.container.registerValue(MessagingTokens.MessageBroker, broker)\n },\n\n async onBootstrap(ctx: PluginContext): Promise<void> {\n const options = validateOptions(resolveOptions(rawOptions, ctx.container))\n driver = options.driver\n consume = options.consume ?? true\n const defaults = options.defaults ?? {}\n const consumerDefaults: ConsumerDefaults = {\n retry: defaultRetryPolicy(defaults.retry),\n concurrency: defaults.concurrency ?? 1,\n deadLetter: defaults.deadLetter,\n }\n const prepared = prepareConsumers(ctx.metadata.extensions?.messaging, ctx.container, driver, consumerDefaults)\n const codec = options.codec ?? jsonCodec\n connected = true // close() must run even if connect() failed half-way\n await driver.connect({ logger: ctx.logger })\n broker.bind(driver, codec, ctx.hooks)\n const services = {\n container: ctx.container,\n hooks: ctx.hooks,\n logger: ctx.logger,\n codec,\n driver,\n requeueDelayMs: defaults.requeueDelay ?? DEFAULT_REQUEUE_DELAY_MS,\n }\n runtimes = prepared.map(consumer => new ConsumerRuntime(consumer, services))\n },\n\n async onStart(ctx: PluginContext): Promise<void> {\n if (!consume || runtimes.length === 0) return\n const results = await Promise.allSettled(runtimes.map(runtime => runtime.start()))\n const failure = results.find(result => result.status === 'rejected')\n if (failure !== undefined) {\n // the application only stops plugins whose onStart succeeded: undo the subscriptions made\n await Promise.all(runtimes.map(runtime => runtime.stop()))\n throw failure.reason\n }\n keepAlive ??= setInterval(() => undefined, KEEP_ALIVE_INTERVAL_MS)\n ctx.logger.info(`Messaging: ${runtimes.length} consumer(s) subscribed with the ${driver?.name} driver`, {\n queues: runtimes.map(runtime => runtime.consumer.queue),\n })\n },\n\n async onStop(): Promise<void> {\n try {\n await Promise.all(runtimes.map(runtime => runtime.stop()))\n } finally {\n stopKeepAlive()\n }\n },\n\n async onDispose(): Promise<void> {\n stopKeepAlive()\n broker.unbind()\n if (driver !== undefined && connected) {\n connected = false\n await driver.close()\n }\n },\n }\n}\n\nfunction validateOptions(options: MessagingPluginOptions): MessagingPluginOptions {\n if (options === null || typeof options !== 'object') {\n throw consumerError('MessagingPlugin.configure(): options must be an object')\n }\n const driver = options.driver as Partial<MessagingDriver> | undefined\n if (\n driver === undefined ||\n driver === null ||\n typeof driver.connect !== 'function' ||\n typeof driver.subscribe !== 'function' ||\n typeof driver.publish !== 'function' ||\n typeof driver.close !== 'function' ||\n typeof driver.deadLetterName !== 'function' ||\n typeof driver.capabilities !== 'object'\n ) {\n throw consumerError('MessagingPlugin.configure(): \"driver\" must be a messaging driver', [\n \"Use rabbitmq() from '@znight/messaging/rabbitmq', sqs(), kafka() or memory() from their subpaths\",\n ])\n }\n const defaults = options.defaults\n if (defaults?.concurrency !== undefined && !isPositiveInteger(defaults.concurrency)) {\n throw consumerError(\n `MessagingPlugin defaults.concurrency must be a positive integer, got ${String(defaults.concurrency)}`,\n )\n }\n if (defaults?.requeueDelay !== undefined && !(Number.isFinite(defaults.requeueDelay) && defaults.requeueDelay >= 0)) {\n throw consumerError(\n `MessagingPlugin defaults.requeueDelay must be a number of milliseconds >= 0, got ${String(defaults.requeueDelay)}`,\n )\n }\n if (\n defaults?.deadLetter !== undefined &&\n defaults.deadLetter !== false &&\n typeof defaults.deadLetter !== 'function'\n ) {\n throw consumerError('MessagingPlugin defaults.deadLetter must be a function (queue => name) or false')\n }\n return options\n}\n","/**\n * Copy of the [Standard Schema v1](https://standardschema.dev) interface (MIT). Zod, Valibot, ArkType,\n * Effect Schema... implement it: `@Message(schema)` accepts any of them without depending on one.\n *\n * The upstream declaration uses a namespace; the types are flattened here (`erasableSyntaxOnly`) but\n * remain structurally identical, so any Standard Schema implementation is assignable.\n */\n\n/** A schema implementing the Standard Schema v1 interface. */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1Props<Input, Output>\n}\n\n/** The `~standard` property of a {@link StandardSchemaV1}. */\nexport interface StandardSchemaV1Props<Input = unknown, Output = Input> {\n /** Version of the Standard Schema specification. */\n readonly version: 1\n /** Library implementing the schema (`zod`, `valibot`...). */\n readonly vendor: string\n /** Validates an unknown value, synchronously or asynchronously. */\n readonly validate: (value: unknown) => StandardSchemaV1Result<Output> | Promise<StandardSchemaV1Result<Output>>\n /** Inferred types (type-level only). */\n readonly types?: StandardSchemaV1Types<Input, Output> | undefined\n}\n\n/** Result of {@link StandardSchemaV1Props.validate}. */\nexport type StandardSchemaV1Result<Output> = StandardSchemaV1SuccessResult<Output> | StandardSchemaV1FailureResult\n\n/** Successful validation: `value` is the schema output (defaults, transforms applied). */\nexport interface StandardSchemaV1SuccessResult<Output> {\n readonly value: Output\n readonly issues?: undefined\n}\n\n/** Failed validation. */\nexport interface StandardSchemaV1FailureResult {\n readonly issues: readonly StandardSchemaV1Issue[]\n}\n\n/** One validation issue. */\nexport interface StandardSchemaV1Issue {\n readonly message: string\n readonly path?: readonly (PropertyKey | StandardSchemaV1PathSegment)[] | undefined\n}\n\n/** A segment of an issue path. */\nexport interface StandardSchemaV1PathSegment {\n readonly key: PropertyKey\n}\n\n/** Input and output types of a schema (type-level only). */\nexport interface StandardSchemaV1Types<Input = unknown, Output = Input> {\n readonly input: Input\n readonly output: Output\n}\n\n/** Input type of a Standard Schema. */\nexport type InferSchemaInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input']\n\n/** Output type of a Standard Schema (what handlers receive). */\nexport type InferSchemaOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output']\n\n/** Whether `value` looks like a Standard Schema v1 (has a `~standard.validate` function). */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return false\n const props = (value as { '~standard'?: unknown })['~standard']\n return props !== null && typeof props === 'object' && typeof (props as { validate?: unknown }).validate === 'function'\n}\n","import type {\n InferSchemaOutput,\n StandardSchemaV1,\n StandardSchemaV1Issue,\n StandardSchemaV1PathSegment,\n StandardSchemaV1Result,\n} from './standard-schema.ts'\n\nimport { MessageValidationError, type MessageValidationIssue } from './errors.ts'\nimport { isStandardSchema } from './standard-schema.ts'\n\n/**\n * Validates `value` with a Standard Schema (Zod, Valibot, ArkType...) and returns the schema\n * **output** (defaults and transforms applied). On failure it throws a {@link MessageValidationError},\n * a `NonRetryableError`: the message is dead-lettered without further attempts.\n *\n * Returns synchronously when the schema validates synchronously, a promise otherwise. Called by the\n * code generated for `@Message(schema)` parameters:\n *\n * ```ts\n * invoke: async (c, m) => c.handle(await validateMessage(OrderCreated, m.payload))\n * ```\n *\n * @param label Describes the value in the error message. Default: `'message payload'`.\n */\nexport function validateMessage<S extends StandardSchemaV1>(\n schema: S,\n value: unknown,\n label = 'message payload',\n): InferSchemaOutput<S> | Promise<InferSchemaOutput<S>> {\n if (!isStandardSchema(schema)) {\n throw new TypeError(`[znight] validateMessage(): the schema of the ${label} is not a Standard Schema v1`)\n }\n const result = schema['~standard'].validate(value) as\n | StandardSchemaV1Result<InferSchemaOutput<S>>\n | Promise<StandardSchemaV1Result<InferSchemaOutput<S>>>\n if (isPromiseLike(result)) return Promise.resolve(result).then(settled => unwrap(settled, label))\n return unwrap(result, label)\n}\n\nfunction unwrap<T>(result: StandardSchemaV1Result<T>, label: string): T {\n if (result.issues === undefined) return result.value\n throw new MessageValidationError(label, result.issues.map(toIssue))\n}\n\nfunction toIssue(issue: StandardSchemaV1Issue): MessageValidationIssue {\n return { path: formatPath(issue.path), message: String(issue.message) }\n}\n\nfunction formatPath(path: StandardSchemaV1Issue['path']): string {\n if (path === undefined || path.length === 0) return ''\n return path.map(segment => String(isSegment(segment) ? segment.key : segment)).join('.')\n}\n\nfunction isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {\n return typeof (value as { then?: unknown } | null)?.then === 'function'\n}\n\nfunction isSegment(segment: PropertyKey | StandardSchemaV1PathSegment): segment is StandardSchemaV1PathSegment {\n return typeof segment === 'object' && segment !== null && 'key' in segment\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAsB,gBAAtB,MAAoC,CAMpC;AAEA,MAAM,cAAc;;AAGpB,IAAa,sBAAb,cAAyC,cAAc;CACrD;CACA;CACA;CACA,UAAU;;CAGV,KAAK,QAAyB,OAAqB,OAAoB;EACrE,KAAK,UAAU;EACf,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,UAAU;CACjB;;CAGA,SAAe;EACb,KAAK,UAAU;CACjB;CAEA,MAAe,QAAQ,OAAe,SAAkB,UAA0B,CAAC,GAA2B;EAC5G,MAAM,SAAS,KAAK;EACpB,MAAM,QAAQ,KAAK;EACnB,IAAI,WAAW,KAAA,KAAa,UAAU,KAAA,GACpC,MAAM,uBACJ,0BAA0B,MAAM,iFAClC;EAEF,IAAI,KAAK,SACP,MAAM,uBAAuB,0BAA0B,MAAM,2CAA2C;EAE1G,MAAM,UAAU,KAAK,SAAS,OAAO,SAAS,SAAS,OAAO,MAAM;EACpE,MAAM,QAAQ,KAAK;EACnB,MAAM,UAAU,OAAO,YAAY,mBAAmB,KAAK;EAC3D,MAAM,WAAW,OAAO,aAAa,mBAAmB,KAAK;EAC7D,IAAI,UAAU,KAAA,KAAc,CAAC,WAAW,CAAC,UAAW;GAClD,MAAM,OAAO,QAAQ,OAAO,QAAQ,SAAS,CAAC;GAC9C,OAAO,EAAE,IAAI,QAAQ,GAAG;EAC1B;EACA,MAAM,QAA6B;GACjC;GACA,MAAM,QAAQ;GACd,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,WAAW,YAAY,IAAI;EAC7B;EACA,MAAM,OAAO,YAA2B;GACtC,IAAI;IACF,MAAM,OAAO,QAAQ,OAAO,QAAQ,SAAS,CAAC;GAChD,SAAS,OAAO;IACd,MAAM,QAAQ;IACd,MAAM;GACR;EACF;EACA,IAAI;GACF,IAAI,SAAS,MAAM,MAAM,IAAI,qBAAqB,OAAO,IAAI;QACxD,MAAM,KAAK;EAClB,UAAU;GACR,IAAI,UAAU,MAAM,KAAK,qBAAqB,KAAK;EACrD;EACA,OAAO,EAAE,IAAI,QAAQ,GAAG;CAC1B;CAEA,SACE,OACA,SACA,SACA,OACA,QACgG;EAChG,IAAI,OAAO,UAAU,YAAY,UAAU,IACzC,MAAM,IAAI,UACR,+EAA+E,OAAO,KAAK,GAC7F;EAEF,MAAM,UAAU,QAAQ,SAAS;EACjC,IAAI,OAAO,YAAY,YAAY,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,GACxE,MAAM,IAAI,UAAU,mCAAmC,MAAM,kDAAkD;EAEjH,IAAI,UAAU,OAAO,aAAa,mBAChC,MAAM,IAAI,WACR,OAAO,aAAa,sBAAsB,IACtC,mCAAmC,MAAM,UAAU,OAAO,KAAK,+CAC/D,mCAAmC,MAAM,UAAU,OAAO,KAAK,gCAAgC,OAAO,aAAa,kBAAkB,WAAW,QAAQ,EAC9J;EAEF,MAAM,UAAU,aAAa;EAC7B,IAAI,QAAQ,YAAY,KAAA,GACtB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GAAG;GAC3D,IAAI,CAAC,YAAY,KAAK,IAAI,GACxB,MAAM,IAAI,UAAU,mCAAmC,MAAM,2BAA2B,KAAK,EAAE;GACjG,IAAI,OAAO,UAAU,UACnB,MAAM,IAAI,UAAU,mCAAmC,MAAM,cAAc,KAAK,mBAAmB;GAErG,QAAQ,QAAQ;EAClB;EAEF,MAAM,KAAK,QAAQ,MAAM,WAAW;EACpC,MAAM,UAAU,MAAM,OAAO,SAAS,QAAQ,WAAW;EACzD,MAAM,YAAY,KAAK,IAAI;EAC3B,OAAO;GACL;GACA;GACA,iBAAiB;IACf;IACA,MAAM,QAAQ;IACd,KAAK,QAAQ;IACb,SAAS,EAAE,GAAG,QAAQ;IACtB,MAAM,QAAQ;IACd,aAAa,QAAQ;IACrB;IACA;GACF;EACF;CACF;AACF;;AC7GA,SAAS,SAAe,CAExB;AAEA,MAAM,cAA2B;AACjC,MAAM,eAA6B;AACnC,MAAM,kBAAmC;AACzC,MAAM,qBAAyC;AAqB/C,SAAgB,SAAS,iBAAwD;CAC/E,OAAO;AACT;;;;;;;AAQA,SAAgB,UAAU,OAA8B;CACtD,OAAO;AACT;;;;;;AAOA,SAAgB,MAAM,UAAsC;CAC1D,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,UAA8B;CAC5C,OAAO;AACT;;;;;;AAOA,SAAgB,QAAQ,SAA6C;CACnE,OAAO;AACT;;AAGA,SAAgB,QAAQ,OAAiC;CACvD,OAAO;AACT;;AAGA,SAAgB,MAAuB;CACrC,OAAO;AACT;;;ACtGA,MAAa,yBAAyB;;AAGtC,MAAa,kBAAmC;;AAGhD,MAAa,uBAAoC,OAAO,OAAO;CAC7D,UAAU;CACV,SAAS;CACT,OAAO;CACP,UAAU;AACZ,CAAC;;AAGD,MAAa,kBAA+B,OAAO,OAAO;CAAE,UAAU;CAAG,SAAS;CAAS,OAAO;AAAE,CAAC;AAErG,MAAM,eAAe;;;;;;AAOrB,SAAgB,eAAe,SAAqC,QAAQ,YAAyB;CACnG,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,cAAc,GAAG,MAAM,yCAAyC,OAAO,OAAO,GAAG;CAEzF,MAAM,EAAE,UAAU,UAAU,iBAAiB,QAAQ,wBAAwB,aAAa;CAC1F,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,WAAW,KAAK,WAAW,cAC5D,MAAM,cACJ,GAAG,MAAM,gDAAgD,aAAa,QAAQ,OAAO,QAAQ,KAC7F,CAAC,kFAAkF,CACrF;CAEF,IAAI,YAAY,WAAW,YAAY,eACrC,MAAM,cAAc,GAAG,MAAM,oDAAoD,OAAO,OAAO,GAAG;CAEpG,IAAI,CAAC,WAAW,KAAK,GACnB,MAAM,cAAc,GAAG,MAAM,8DAA8D,OAAO,KAAK,GAAG;CAE5G,IAAI,aAAa,KAAA,MAAc,CAAC,WAAW,QAAQ,KAAK,WAAW,QACjE,MAAM,cACJ,GAAG,MAAM,iEAAiE,MAAM,SAAS,OAAO,QAAQ,GAC1G;CAEF,OAAO,aAAa,KAAA,IAAY;EAAE;EAAU;EAAS;CAAM,IAAI;EAAE;EAAU;EAAS;EAAO;CAAS;AACtG;AAEA,SAAS,WAAW,OAAiC;CACnD,OAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS;AACzE;;;;;AAMA,SAAgB,kBAAkB,QAAqB,SAAyB;CAC9E,IAAI,OAAO,YAAY,SAAS,OAAO,OAAO;CAC9C,MAAM,WAAW,KAAK,IAAI,GAAG,UAAU,CAAC;CACxC,MAAM,MAAM,OAAO,QAAQ,KAAK;CAChC,OAAO,KAAK,IAAI,KAAK,OAAO,YAAY,OAAO,gBAAgB;AACjE;;AAGA,SAAgB,YAAY,QAA+B;CACzD,MAAM,SAAmB,CAAC;CAC1B,KAAK,IAAI,UAAU,GAAG,UAAU,OAAO,UAAU,WAAW,OAAO,KAAK,kBAAkB,QAAQ,OAAO,CAAC;CAC1G,OAAO;AACT;;AAGA,SAAgB,iBAAiB,SAAiB,cAA2C;CAC3F,OAAO,WAAW,aAAa;AACjC;;;;;;AAOA,SAAgB,qBACd,QACA,cACA,QACA,OACM;CACN,KAAK,MAAM,SAAS,IAAI,IAAI,YAAY,MAAM,CAAC,GAAG;EAChD,IAAI,iBAAiB,OAAO,YAAY,GAAG;EAC3C,IAAI,aAAa,eAAe,SAAS,aAAa,kBAAkB;EACxE,MAAM,SAAS,aAAa,cACxB,2BAA2B,aAAa,oBAAoB,8BAA8B,aAAa,iBAAiB,OACxH,2BAA2B,aAAa,oBAAoB;EAChE,MAAM,cACJ,GAAG,MAAM,QAAQ,OAAO,sBAAsB,MAAM,oCAAoC,OAAO,IAC/F,CACE,oDACA,mGACF,CACF;CACF;AACF;;;ACpEA,SAAgB,mBAAmB,OAAsD;CACvF,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,IAAI,UAAU,OAAO,OAAO;CAC5B,OAAO,eAAe,OAAO,gCAAgC;AAC/D;;;;;;AAOA,SAAgB,iBACd,UACA,WACA,QACA,UACoB;CACpB,IAAI,aAAa,KAAA,GAAW,OAAO,CAAC;CACpC,IAAI,aAAa,QAAQ,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,SAAS,SAAS,GACxF,MAAM,cAAc,8DAA4D,CAC9E,uEACF,CAAC;CAEH,MAAM,0BAAU,IAAI,IAAoB;CACxC,MAAM,WAA+B,CAAC;CACtC,KAAK,MAAM,cAAc,SAAS,WAAW;EAC3C,MAAM,WAAW,gBAAgB,YAAY,WAAW,QAAQ,QAAQ;EACxE,MAAM,WAAW,QAAQ,IAAI,SAAS,KAAK;EAC3C,IAAI,aAAa,KAAA,GACf,MAAM,cAAc,GAAG,SAAS,OAAO,SAAS,KAAK,iBAAiB,SAAS,MAAM,IAAI,CACvF,6GACF,CAAC;EAEH,QAAQ,IAAI,SAAS,OAAO,SAAS,IAAI;EACzC,SAAS,KAAK,QAAQ;CACxB;CACA,OAAO;AACT;AAEA,SAAS,gBACP,YACA,WACA,QACA,UACkB;CAClB,IACE,eAAe,QACf,OAAO,eAAe,YACtB,OAAO,WAAW,UAAU,YAC5B,WAAW,UAAU,IAErB,MAAM,cAAc,qDAAqD;CAE3E,MAAM,QAAQ,WAAW;CACzB,MAAM,OAAO,YAAY,KAAK;CAC9B,IAAI,OAAO,WAAW,UAAU,YAAY,WAAW,UAAU,IAC/D,MAAM,cAAc,aAAa,KAAK,uCAAuC;CAE/E,IAAI,CAAC,UAAU,IAAI,KAAK,GACtB,MAAM,cAAc,aAAa,KAAK,kCAAkC,SAAS,CAC/E,yEACF,CAAC;CAGH,MAAM,EAAE,aAAa,gBAAgB,YAAY,GAAG,kBADpC,WAAW,WAAW,CAAC;CAEvC,MAAM,cAAc,kBAAkB,SAAS;CAC/C,IAAI,CAAC,kBAAkB,WAAW,GAChC,MAAM,cAAc,aAAa,KAAK,kDAAkD,OAAO,WAAW,GAAG;CAE/G,IAAI,eAAe,KAAA,KAAa,eAAe,UAAU,OAAO,eAAe,YAAY,eAAe,KACxG,MAAM,cAAc,aAAa,KAAK,oDAAoD,OAAO,UAAU,GAAG;CAEhH,IAAI,eAAe,WAAW,OAC5B,MAAM,cAAc,aAAa,KAAK,wDAAwD,WAAW,MAAM,EAAE;CAGnH,MAAM,WAAW,WAAW;CAC5B,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,GAClD,MAAM,cAAc,aAAa,KAAK,6BAA6B,CACjE,gDACF,CAAC;CAEH,MAAM,wBAAQ,IAAI,IAA6B;CAC/C,MAAM,WAA4E,CAAC;CACnF,IAAI;CACJ,IAAI,cAAc;CAClB,MAAM,+BAAe,IAAI,IAAY;CACrC,KAAK,MAAM,qBAAqB,UAAU;EACxC,MAAM,UAAU,eAAe,mBAAmB,MAAM,QAAQ,SAAS,OAAO,SAAS;EACzF,cAAc,KAAK,IAAI,aAAa,QAAQ,OAAO,QAAQ;EAC3D,KAAK,MAAM,SAAS,YAAY,QAAQ,MAAM,GAC5C,IAAI,CAAC,iBAAiB,OAAO,OAAO,YAAY,GAAG,aAAa,IAAI,KAAK;EAE3E,MAAM,OAAO,kBAAkB;EAC/B,IAAI,SAAS,KAAA,GAAW;GACtB,IAAI,aAAa,KAAA,GACf,MAAM,cACJ,aAAa,KAAK,IAAI,SAAS,OAAO,SAAS,QAAQ,OAAO,4CAC9D,CAAC,gEAA8D,CACjE;GAEF,WAAW;EACb,OAAO,IAAI,KAAK,SAAS,GAAG,GAAG;GAC7B,IAAI,SAAS,MAAK,UAAS,MAAM,QAAQ,WAAW,SAAS,IAAI,GAC/D,MAAM,cAAc,MAAM,IAAI;GAEhC,SAAS,KAAK;IAAE,OAAO,gBAAgB,IAAI;IAAG;GAAQ,CAAC;EACzD,OAAO;GACL,IAAI,MAAM,IAAI,IAAI,GAAG,MAAM,cAAc,MAAM,IAAI;GACnD,MAAM,IAAI,MAAM,OAAO;EACzB;CACF;CAEA,MAAM,eAAe,WAAW;CAChC,IAAI,iBAAiB,KAAA,GAAW;EAC9B,IAAI,OAAO,iBAAiB,YAAY,iBAAiB,IACvD,MAAM,cAAc,aAAa,KAAK,iCAAiC,OAAO,YAAY,GAAG;EAE/F,MAAM,YAAY,UAAU,SAAS;EAErC,MAAM,aADW,OAAO,OAAO,WAAW,KAAK,IAAI,UAAU,SAAS,KAAA,EAAA,EAC1C,OAAO;EACnC,IAAI,cAAc,KAAA,KAAa,OAAO,UAAU,kBAAkB,YAChE,MAAM,cAAc,aAAa,KAAK,wBAAwB,aAAa,kBAAkB;CAEjG;CAEA,OAAO;EACL;EACA;EACA;EACA,OAAO,WAAW;EAClB;EACA,iBAAiB,kBAAkB,WAAW,OAAO,YAAY,SAAS,YAAY,MAAM;EAC5F;EACA;EACA,aAAa,CAAC,GAAG,YAAY,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;EACnD;EACA,OAAO,MAAuD;GAC5D,IAAI,SAAS,KAAA,GAAW,OAAO;GAC/B,MAAM,UAAU,MAAM,IAAI,IAAI;GAC9B,IAAI,YAAY,KAAA,GAAW,OAAO;GAClC,KAAK,MAAM,SAAS,UAAU,IAAI,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO,MAAM;GACvE,OAAO;EACT;CACF;AACF;AAEA,SAAS,eACP,YACA,UACA,QACA,eACA,WACiB;CACjB,IACE,eAAe,QACf,OAAO,eAAe,YACtB,OAAO,WAAW,WAAW,YAC7B,WAAW,WAAW,IAEtB,MAAM,cAAc,aAAa,SAAS,wCAAwC;CAEpF,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,OAAO;CAC/C,IAAI,OAAO,WAAW,WAAW,YAC/B,MAAM,cAAc,GAAG,MAAM,+BAA+B,CAAC,yBAAyB,CAAC;CAEzF,IAAI,WAAW,SAAS,KAAA,MAAc,OAAO,WAAW,SAAS,YAAY,WAAW,SAAS,KAC/F,MAAM,cAAc,GAAG,MAAM,iDAAiD;CAEhF,MAAM,SAAS,WAAW,UAAU,KAAA,IAAY,gBAAgB,eAAe,WAAW,OAAO,aAAa,OAAO;CACrH,qBACE,QACA,OAAO,cACP,OAAO,MACP,WAAW,UAAU,KAAA,IAAY,GAAG,MAAM,2BAA2B,aAAa,OACpF;CACA,OAAO;EAAE;EAAY,QAAQ,cAAc,YAAY,WAAW,KAAK;EAAG,QAAQ,WAAW;EAAQ;CAAO;AAC9G;;AAGA,SAAS,cACP,YACA,WACA,OAC2B;CAC3B,MAAM,SAAS,WAAW;CAC1B,MAAM,SAAS,WAAW;CAC1B,IAAI,WAAW,KAAA,KAAa,OAAO,WAAW,GAAG,OAAO;CACxD,KAAK,MAAM,SAAS,QAClB,IAAI,CAAC,UAAU,IAAI,KAAK,GACtB,MAAM,cAAc,GAAG,MAAM,2BAA2B,MAAM,qBAAqB,CAAC,yBAAyB,CAAC;CAGlH,QAAQ,UAAU,YAChB,OACE,UACA,SACA,OAAO,KAAI,UAAS,UAAU,IAAI,KAAK,CAAC,CAC1C;AACJ;AAEA,SAAS,cAAc,UAAkB,MAAqB;CAC5D,OAAO,cAAc,aAAa,SAAS,0CAA0C,KAAK,EAAE;AAC9F;;AAGA,SAAS,gBAAgB,SAAyB;CAChD,MAAM,SAAS,QACZ,MAAM,GAAG,CAAC,CACV,KAAI,SAAQ,KAAK,QAAQ,sBAAsB,MAAM,CAAC,CAAC,CACvD,KAAK,IAAI;CACZ,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,GAAG;AACtC;AAEA,SAAS,kBACP,OACA,QACA,UACA,QACoB;CACpB,IAAI,WAAW,OAAO,OAAO,KAAA;CAC7B,IAAI,OAAO,WAAW,UAAU,OAAO;CACvC,IAAI,aAAa,OAAO,OAAO,KAAA;CAC/B,MAAM,OAAO,aAAa,KAAA,IAAY,OAAO,eAAe,KAAK,IAAI,SAAS,KAAK;CACnF,IAAI,OAAO,SAAS,YAAY,SAAS,MAAM,SAAS,OACtD,MAAM,cAAc,uCAAuC,MAAM,KAAK,OAAO,IAAI,GAAG;CAEtF,OAAO;AACT;;;AC3RA,MAAa,kBAKT,OAAO,OAAO;CAChB,eAAe;CACf,gBAAgB;AAClB,CAAC;;;AC8CD,IAAa,kBAAb,MAA6B;CAC3B;CACA;CACA;CACA,SAAkB,IAAI,gBAAgB;CACtC,4BAAqB,IAAI,IAAmB;CAC5C;CACA,SAAwB;CAExB,YAAY,UAA4B,UAA2B;EACjE,KAAK,WAAW;EAChB,KAAK,YAAY;EACjB,KAAK,SAAS,IAAI,UAAU,SAAS,WAAW;CAClD;CAEA,IAAI,QAAuB;EACzB,OAAO,KAAK;CACd;;CAGA,IAAI,WAAmB;EACrB,OAAO,KAAK,UAAU;CACxB;;CAGA,MAAM,QAAuB;EAC3B,IAAI,KAAK,WAAW,WAAW,MAAM,IAAI,MAAM,qBAAqB,KAAK,SAAS,KAAK,qBAAqB;EAC5G,KAAK,SAAS;EACd,MAAM,EAAE,aAAa;EACrB,IAAI;GACF,KAAK,gBAAgB,MAAM,KAAK,UAAU,OAAO,UAAU,SAAS,QAAO,aAAY,KAAK,OAAO,QAAQ,GAAG;IAC5G,UAAU,SAAS;IACnB,aAAa,SAAS;IACtB,iBAAiB,SAAS;IAC1B,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,SAAS,SAAS;GACpB,CAAC;EACH,SAAS,OAAO;GACd,KAAK,SAAS;GACd,MAAM;EACR;EACA,KAAK,SAAS;CAChB;;;;;CAMA,MAAM,OAAsB;EAC1B,IAAI,KAAK,WAAW,cAAc,KAAK,WAAW,WAAW;EAC7D,KAAK,SAAS;EACd,KAAK,OAAO,MAAM;EAClB,MAAM,eAAe,KAAK;EAC1B,IAAI,iBAAiB,KAAA,GACnB,IAAI;GACF,MAAM,aAAa,OAAO;EAC5B,SAAS,OAAO;GACd,KAAK,UAAU,OAAO,MAAM,YAAY,KAAK,SAAS,KAAK,sCAAsC,EAAE,KAAK,MAAM,CAAC;EACjH;EAEF,OAAO,KAAK,UAAU,OAAO,GAAG,MAAM,QAAQ,WAAW,CAAC,GAAG,KAAK,SAAS,CAAC;EAC5E,IAAI,iBAAiB,KAAA,GACnB,IAAI;GACF,MAAM,aAAa,MAAM;EAC3B,SAAS,OAAO;GACd,KAAK,UAAU,OAAO,MAAM,YAAY,KAAK,SAAS,KAAK,qCAAqC,EAAE,KAAK,MAAM,CAAC;EAChH;EAEF,KAAK,SAAS;CAChB;;CAGA,OAAO,UAAyC;EAC9C,MAAM,UAAU,KAAK,QAAQ,QAAQ;EACrC,KAAK,UAAU,IAAI,OAAO;EAC1B,MAAM,aAAmB;GACvB,KAAK,UAAU,OAAO,OAAO;EAC/B;EACA,QAAQ,KAAK,MAAM,IAAI;EACvB,OAAO;CACT;CAEA,MAAM,QAAQ,UAAyC;EACrD,MAAM,SAAS,KAAK,OAAO;EAC3B,IAAI,OAAO,SAAS,OAAO,KAAK,SAAS,QAAQ;EACjD,IAAI;GACF,MAAM,KAAK,OAAO,QAAQ,MAAM;EAClC,QAAQ;GACN,OAAO,KAAK,SAAS,QAAQ;EAC/B;EACA,IAAI;GACF,MAAM,KAAK,SAAS,QAAQ;EAC9B,SAAS,OAAO;GAEd,KAAK,UAAU,OAAO,MAAM,YAAY,KAAK,SAAS,KAAK,gCAAgC,EAAE,KAAK,MAAM,CAAC;GACzG,MAAM,KAAK,SAAS,QAAQ;EAC9B,UAAU;GACR,KAAK,OAAO,QAAQ;EACtB;CACF;CAEA,MAAM,SAAS,UAAyC;EACtD,MAAM,EAAE,WAAW,OAAO,OAAO,WAAW,KAAK;EACjD,MAAM,EAAE,aAAa;EACrB,MAAM,UAAU,SAAS,OAAO,SAAS,IAAI;EAC7C,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,OAAO,SAAS,MAAM,SAAS,WAAW;EAC5D,SAAS,OAAO;GACd,cAAc,eAAe,KAAK,IAC9B,QACA,IAAI,mBAAmB,mCAAmC,aAAa,KAAK,KAAK,EAAE,OAAO,MAAM,CAAC;EACvG;EACA,MAAM,UAA0B;GAC9B,IAAI,SAAS;GACb,OAAO,SAAS;GAChB,MAAM,SAAS;GACf;GACA,SAAS,SAAS;GAClB,KAAK,SAAS;GACd,SAAS,SAAS;GAClB,aAAa,SAAS,OAAO,YAAY;GACzC,aAAa,SAAS;GACtB,WAAW,SAAS;GACpB,YAAY,KAAK,IAAI;GACrB,aAAa,SAAS;GACtB,MAAM,SAAS;GACf,QAAQ,OAAO;GACf,KAAK,SAAS;GACd,QAAQ,KAAK,OAAO;EACtB;EACA,MAAM,QAA8B;GAClC,OAAO,SAAS;GAChB,MAAM,SAAS;GACf,WAAW,SAAS;GACpB,SAAS,SAAS;GAClB,SAAS,SAAS;GAClB,UAAU,SAAS;GACnB,SAAS,SAAS;GAClB;GACA,WAAW,YAAY,IAAI;EAC7B;EACA,MAAM,UAA2B;GAC/B;GACA;GACA;GACA;GACA;GACA,UAAU;GACV,UAAU,KAAA;EACZ;EAEA,IAAI,0BAAmB,IAAI,MAAM,sDAAsD;EACvF,IAAI;GACF,MAAM,UAAU,iBACR,MAAM,IAAI,qBAAqB,aAAa,KAAK,SAAS,OAAO,CAAC,GACxE,CAAC,CAAC,gBAAgB,gBAAgB,OAAO,CAAC,CAC5C;EACF,SAAS,OAAO;GACd,UAAU;GACV,IAAI,QAAQ,UAEV,KAAK,UAAU,OAAO,MAAM,YAAY,SAAS,KAAK,iCAAiC,SAAS,MAAM;IACpG,KAAK;IACL,OAAO,SAAS;GAClB,CAAC;EAEL;EAEA,IAAI,CAAC,QAAQ,UAAU,MAAM,KAAK,SAAS,SAAS,SAAS,KAAK;EAClE,IAAI,MAAM,aAAa,mBAAmB,GAAG,MAAM,KAAK,qBAAqB,KAAK;EAClF,MAAM,KAAK,UAAU,OAAO;CAC9B;;CAGA,MAAM,SAAS,SAAyC;EACtD,IAAI,QAAQ,UAAU;EACtB,QAAQ,WAAW;EACnB,MAAM,EAAE,SAAS,YAAY;EAC7B,IAAI,QAAQ,gBAAgB,KAAA,GAAW,OAAO,KAAK,SAAS,SAAS,QAAQ,aAAa,IAAI;EAC9F,IAAI,YAAY,KAAA,GACd,OAAO,KAAK,UAAU,SAAS,IAAI,eAAe,KAAK,SAAS,MAAM,QAAQ,IAAI,GAAG,cAAc,IAAI;EACzG,IAAI,QAAQ,UAAU,QAAQ,aAC5B,OAAO,KAAK,UACV,SACA,IAAI,uBAAuB,QAAQ,SAAS,QAAQ,WAAW,GAC/D,gBACA,KACF;EAEF,IAAI;GACF,MAAM,WAAW,KAAK,UAAU,UAAU,IAAI,KAAK,SAAS,KAAK;GACjE,MAAM,QAAQ,OAAO,UAAU,OAAO;EACxC,SAAS,OAAO;GACd,OAAO,KAAK,SAAS,SAAS,OAAO,IAAI;EAC3C;EACA,OAAO,KAAK,KAAK,OAAO;CAC1B;;CAGA,SAAS,SAA0B,OAAgB,kBAA0C;EAC3F,IAAI,eAAe,KAAK,GAAG,OAAO,KAAK,UAAU,SAAS,OAAO,SAAS,KAAK,GAAG,gBAAgB;EAClG,IAAI,QAAQ,QAAQ,UAAU,QAAQ,QAAQ,aAAa,OAAO,KAAK,OAAO,SAAS,KAAK;EAC5F,OAAO,KAAK,UAAU,SAAS,OAAO,gBAAgB,gBAAgB;CACxE;CAEA,MAAM,OAAO,SAA0B,OAA+B;EACpE,MAAM,EAAE,SAAS,UAAU,UAAU;EACrC,MAAM,SAAS,QAAQ,SAAS;EAChC,MAAM,UAAU,WAAW,KAAA,IAAY,IAAI,kBAAkB,QAAQ,QAAQ,OAAO;EACpF,MAAM,UAAU;EAChB,MAAM,QAAQ;EACd,KAAK,UAAU,OAAO,KACpB,YAAY,KAAK,SAAS,KAAK,YAAY,QAAQ,QAAQ,GAAG,QAAQ,YAAY,cAAc,QAAQ,GAAG,uBAAuB,QAAQ,MAC1I;GAAE,OAAO,QAAQ;GAAO,WAAW,QAAQ;GAAI,MAAM,QAAQ;GAAM,GAAG,KAAK,cAAc,KAAK;EAAE,CAClG;EACA,IAAI,iBAAiB,SAAS,KAAK,UAAU,OAAO,YAAY,GAAG;GACjE,QAAQ,WAAW;IAAE,MAAM;IAAS;IAAS;GAAM;GACnD;EACF;EACA,IAAI;GACF,MAAM,SAAS,MAAM;IAAE,SAAS,QAAQ,UAAU;IAAG;IAAS;GAAM,CAAC;EACvE,SAAS,YAAY;GACnB,KAAK,UAAU,OAAO,MACpB,YAAY,KAAK,SAAS,KAAK,4CAA4C,QAAQ,MACnF;IACE,KAAK;IACL,OAAO,QAAQ;GACjB,CACF;GACA,QAAQ,WAAW,EAAE,MAAM,UAAU;EACvC;CACF;;CAGA,MAAM,UACJ,SACA,OACA,QACA,kBACe;EACf,QAAQ,MAAM,QAAQ;EACtB,MAAM,SAAS,KAAK,SAAS;EAC7B,IAAI,oBAAoB,WAAW,KAAA,GACjC,IAAI;GAMF,IAAI,MALa,KAAK,UAAU,UAAU,IAAI,KAAK,SAAS,KAIhC,CAAC,CAAC,OAAO,GAAG,OAAO,QAAQ,OAAO,MAC/C,OAAO,OAAO,KAAK,KAAK,OAAO;EAChD,SAAS,cAAc;GACrB,KAAK,UAAU,OAAO,MAAM,YAAY,KAAK,SAAS,KAAK,aAAa,OAAO,YAAY;IACzF,KAAK;IACL,OAAO,QAAQ,QAAQ;IACvB,WAAW,QAAQ,QAAQ;GAC7B,CAAC;EACH;EAEF,OAAO,KAAK,YAAY,SAAS,OAAO,MAAM;CAChD;CAEA,MAAM,YAAY,SAA0B,OAAgB,QAAyC;EACnG,MAAM,EAAE,SAAS,UAAU,UAAU;EACrC,MAAM,QAAQ,KAAK,SAAS;EAC5B,MAAM,UAAU;EAChB,IAAI;GACF,MAAM,SAAS,WAAW;IAAE;IAAO;IAAQ;IAAO,SAAS,QAAQ;GAAQ,CAAC;EAC9E,SAAS,iBAAiB;GACxB,MAAM,UAAU;GAChB,MAAM,QAAQ;GACd,KAAK,UAAU,OAAO,MACpB,YAAY,KAAK,SAAS,KAAK,kCAAkC,QAAQ,GAAG,2BAC5E;IAAE,KAAK;IAAiB,OAAO;IAAO,OAAO,QAAQ;IAAO,iBAAiB;GAAM,CACrF;GACA,QAAQ,WAAW,EAAE,MAAM,UAAU;GACrC;EACF;EACA,KAAK,UAAU,OAAO,MACpB,UAAU,KAAA,IACN,YAAY,KAAK,SAAS,KAAK,YAAY,QAAQ,GAAG,aAAa,OAAO,KAC1E,YAAY,KAAK,SAAS,KAAK,YAAY,QAAQ,GAAG,oBAAoB,MAAM,IAAI,OAAO,IAC/F;GACE,OAAO,QAAQ;GACf,WAAW,QAAQ;GACnB,MAAM,QAAQ;GACd,SAAS,QAAQ;GACjB;GACA,GAAG,KAAK,cAAc,KAAK;EAC7B,CACF;CACF;;;;;;;CAQA,cAAc,OAAwE;EACpF,OAAO,KAAK,UAAU,MAAM,YAAY,mBAAmB,IAAI,EAAE,OAAO,aAAa,KAAK,EAAE,IAAI,EAAE,KAAK,MAAM;CAC/G;CAEA,MAAM,KAAK,SAAyC;EAClD,QAAQ,MAAM,UAAU;EACxB,IAAI;GACF,MAAM,QAAQ,SAAS,IAAI;EAC7B,SAAS,OAAO;GACd,KAAK,UAAU,OAAO,KACpB,YAAY,KAAK,SAAS,KAAK,kCAAkC,QAAQ,QAAQ,GAAG,iCACpF;IAAE,KAAK;IAAO,OAAO,QAAQ,QAAQ;GAAM,CAC7C;EACF;CACF;;CAGA,MAAM,UAAU,SAAyC;EACvD,MAAM,WAAW,QAAQ;EACzB,IAAI,aAAa,KAAA,GAAW;EAC5B,MAAM,EAAE,UAAU,YAAY;EAC9B,IAAI,SAAS,SAAS,SAAS;GAC7B,IAAI;IACF,MAAM,MAAM,SAAS,SAAS,KAAK,OAAO,MAAM;GAClD,QAAQ;IACN,OAAO,KAAK,SAAS,QAAQ;GAC/B;GACA,IAAI;IACF,MAAM,SAAS,MAAM;KAAE,SAAS,QAAQ,UAAU;KAAG,SAAS;KAAG,OAAO,SAAS;IAAM,CAAC;IACxF;GACF,SAAS,OAAO;IACd,KAAK,UAAU,OAAO,MAAM,YAAY,KAAK,SAAS,KAAK,gCAAgC,QAAQ,MAAM;KACvG,KAAK;KACL,OAAO,QAAQ;IACjB,CAAC;GACH;EACF;EACA,IAAI;GACF,MAAM,MAAM,KAAK,UAAU,gBAAgB,KAAK,OAAO,MAAM;EAC/D,QAAQ,CAER;EACA,OAAO,KAAK,SAAS,QAAQ;CAC/B;CAEA,MAAM,SAAS,UAAyC;EACtD,IAAI;GACF,MAAM,SAAS,QAAQ;EACzB,SAAS,OAAO;GACd,KAAK,UAAU,OAAO,MACpB,YAAY,KAAK,SAAS,KAAK,8BAA8B,SAAS,GAAG,iCACzE;IAAE,KAAK;IAAO,OAAO,KAAK,SAAS;GAAM,CAC3C;EACF;CACF;AACF;AAEA,SAAS,SAAS,OAAkC;CAClD,IAAI,iBAAiB,wBAAwB,OAAO;CACpD,IAAI,iBAAiB,oBAAoB,OAAO;CAChD,IAAI,iBAAiB,gBAAgB,OAAO;CAC5C,IAAI,iBAAiB,wBAAwB,OAAO;CACpD,OAAO;AACT;;ACrWA,MAAM,2BAA2B;;AAEjC,MAAM,yBAAyB,KAAK;;;;;;;;;;;;;;;;;AAkBpC,MAAa,kBAET,OAAO,OAAO,EAChB,UAAU,SAAyE;CACjF,OAAO,aAAa,OAAO;AAC7B,EACF,CAAC;AAED,SAAS,aAAa,YAA4E;CAChG,MAAM,SAAS,IAAI,oBAAoB;CACvC,IAAI;CACJ,IAAI,YAAY;CAChB,IAAI,UAAU;CACd,IAAI,WAA8B,CAAC;;;;;;CAMnC,IAAI;CACJ,MAAM,sBAA4B;EAChC,IAAI,cAAc,KAAA,GAAW,cAAc,SAAS;EACpD,YAAY,KAAA;CACd;CAEA,OAAO;EACL,MAAM;EACN;EACA,IAAI,SAAsC;GACxC,OAAO;EACT;EACA,YAA8B;GAC5B,OAAO,SAAS,KAAI,aAAY;IAC9B,UAAU,QAAQ,SAAS;IAC3B,OAAO,QAAQ,SAAS;IACxB,OAAO,QAAQ;IACf,aAAa,QAAQ,SAAS;IAC9B,UAAU,QAAQ;IAClB,iBAAiB,QAAQ,SAAS;GACpC,EAAE;EACJ;EAEA,SAAS,KAA0B;GACjC,IAAI,UAAU,iBAAiB;IAC7B,OAAO,gBAAgB;IACvB,OAAO;IACP,YAAY;GACd,CAAC;GACD,IAAI,UAAU,cAAc,gBAAgB,eAAe,MAAM;EACnE;EAEA,MAAM,YAAY,KAAmC;GACnD,MAAM,UAAU,gBAAgB,eAAe,YAAY,IAAI,SAAS,CAAC;GACzE,SAAS,QAAQ;GACjB,UAAU,QAAQ,WAAW;GAC7B,MAAM,WAAW,QAAQ,YAAY,CAAC;GACtC,MAAM,mBAAqC;IACzC,OAAO,mBAAmB,SAAS,KAAK;IACxC,aAAa,SAAS,eAAe;IACrC,YAAY,SAAS;GACvB;GACA,MAAM,WAAW,iBAAiB,IAAI,SAAS,YAAY,WAAW,IAAI,WAAW,QAAQ,gBAAgB;GAC7G,MAAM,QAAQ,QAAQ,SAAS;GAC/B,YAAY;GACZ,MAAM,OAAO,QAAQ,EAAE,QAAQ,IAAI,OAAO,CAAC;GAC3C,OAAO,KAAK,QAAQ,OAAO,IAAI,KAAK;GACpC,MAAM,WAAW;IACf,WAAW,IAAI;IACf,OAAO,IAAI;IACX,QAAQ,IAAI;IACZ;IACA;IACA,gBAAgB,SAAS,gBAAgB;GAC3C;GACA,WAAW,SAAS,KAAI,aAAY,IAAI,gBAAgB,UAAU,QAAQ,CAAC;EAC7E;EAEA,MAAM,QAAQ,KAAmC;GAC/C,IAAI,CAAC,WAAW,SAAS,WAAW,GAAG;GAEvC,MAAM,WAAU,MADM,QAAQ,WAAW,SAAS,KAAI,YAAW,QAAQ,MAAM,CAAC,CAAC,EAAA,CACzD,MAAK,WAAU,OAAO,WAAW,UAAU;GACnE,IAAI,YAAY,KAAA,GAAW;IAEzB,MAAM,QAAQ,IAAI,SAAS,KAAI,YAAW,QAAQ,KAAK,CAAC,CAAC;IACzD,MAAM,QAAQ;GAChB;GACA,cAAc,kBAAkB,KAAA,GAAW,sBAAsB;GACjE,IAAI,OAAO,KAAK,cAAc,SAAS,OAAO,mCAAmC,QAAQ,KAAK,UAAU,EACtG,QAAQ,SAAS,KAAI,YAAW,QAAQ,SAAS,KAAK,EACxD,CAAC;EACH;EAEA,MAAM,SAAwB;GAC5B,IAAI;IACF,MAAM,QAAQ,IAAI,SAAS,KAAI,YAAW,QAAQ,KAAK,CAAC,CAAC;GAC3D,UAAU;IACR,cAAc;GAChB;EACF;EAEA,MAAM,YAA2B;GAC/B,cAAc;GACd,OAAO,OAAO;GACd,IAAI,WAAW,KAAA,KAAa,WAAW;IACrC,YAAY;IACZ,MAAM,OAAO,MAAM;GACrB;EACF;CACF;AACF;AAEA,SAAS,gBAAgB,SAAyD;CAChF,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,cAAc,wDAAwD;CAE9E,MAAM,SAAS,QAAQ;CACvB,IACE,WAAW,KAAA,KACX,WAAW,QACX,OAAO,OAAO,YAAY,cAC1B,OAAO,OAAO,cAAc,cAC5B,OAAO,OAAO,YAAY,cAC1B,OAAO,OAAO,UAAU,cACxB,OAAO,OAAO,mBAAmB,cACjC,OAAO,OAAO,iBAAiB,UAE/B,MAAM,cAAc,sEAAoE,CACtF,kGACF,CAAC;CAEH,MAAM,WAAW,QAAQ;CACzB,IAAI,UAAU,gBAAgB,KAAA,KAAa,CAAC,kBAAkB,SAAS,WAAW,GAChF,MAAM,cACJ,wEAAwE,OAAO,SAAS,WAAW,GACrG;CAEF,IAAI,UAAU,iBAAiB,KAAA,KAAa,EAAE,OAAO,SAAS,SAAS,YAAY,KAAK,SAAS,gBAAgB,IAC/G,MAAM,cACJ,oFAAoF,OAAO,SAAS,YAAY,GAClH;CAEF,IACE,UAAU,eAAe,KAAA,KACzB,SAAS,eAAe,SACxB,OAAO,SAAS,eAAe,YAE/B,MAAM,cAAc,iFAAiF;CAEvG,OAAO;AACT;;;AC5KA,SAAgB,iBAAiB,OAA2C;CAC1E,IAAI,UAAU,QAAS,OAAO,UAAU,YAAY,OAAO,UAAU,YAAa,OAAO;CACzF,MAAM,QAAS,MAAoC;CACnD,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,OAAQ,MAAiC,aAAa;AAC9G;;;;;;;;;;;;;;;;AC1CA,SAAgB,gBACd,QACA,OACA,QAAQ,mBAC8C;CACtD,IAAI,CAAC,iBAAiB,MAAM,GAC1B,MAAM,IAAI,UAAU,iDAAiD,MAAM,6BAA6B;CAE1G,MAAM,SAAS,OAAO,YAAY,CAAC,SAAS,KAAK;CAGjD,IAAI,cAAc,MAAM,GAAG,OAAO,QAAQ,QAAQ,MAAM,CAAC,CAAC,MAAK,YAAW,OAAO,SAAS,KAAK,CAAC;CAChG,OAAO,OAAO,QAAQ,KAAK;AAC7B;AAEA,SAAS,OAAU,QAAmC,OAAkB;CACtE,IAAI,OAAO,WAAW,KAAA,GAAW,OAAO,OAAO;CAC/C,MAAM,IAAI,uBAAuB,OAAO,OAAO,OAAO,IAAI,OAAO,CAAC;AACpE;AAEA,SAAS,QAAQ,OAAsD;CACrE,OAAO;EAAE,MAAM,WAAW,MAAM,IAAI;EAAG,SAAS,OAAO,MAAM,OAAO;CAAE;AACxE;AAEA,SAAS,WAAW,MAA6C;CAC/D,IAAI,SAAS,KAAA,KAAa,KAAK,WAAW,GAAG,OAAO;CACpD,OAAO,KAAK,KAAI,YAAW,OAAO,UAAU,OAAO,IAAI,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG;AACzF;AAEA,SAAS,cAAiB,OAAoD;CAC5E,OAAO,OAAQ,OAAqC,SAAS;AAC/D;AAEA,SAAS,UAAU,SAA4F;CAC7G,OAAO,OAAO,YAAY,YAAY,YAAY,QAAQ,SAAS;AACrE"}
package/dist/kafka.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities } from "./driver-ZxLqy_Ap.mjs";
1
+ import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities } from "./driver-Dv38b74F.mjs";
2
2
  import { ConsumerOptions, ProducerOptions } from "@platformatic/kafka";
3
3
  /** Topic creation settings. */
4
4
  export interface KafkaTopicOptions {
package/dist/memory.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities, u as DeadLetterReason } from "./driver-ZxLqy_Ap.mjs";
1
+ import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities, u as DeadLetterReason } from "./driver-Dv38b74F.mjs";
2
2
  /** Options of {@link memory}. */
3
3
  export interface MemoryDriverOptions {
4
4
  /** Longest retry delay waited in-process. Default: 1000 ms. */
@@ -1,4 +1,4 @@
1
- import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities } from "./driver-ZxLqy_Ap.mjs";
1
+ import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities } from "./driver-Dv38b74F.mjs";
2
2
  import { Options, SocketOptions } from "amqplib";
3
3
  /** Reconnection settings. */
4
4
  export interface RabbitMQReconnectOptions {
package/dist/sqs.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities } from "./driver-ZxLqy_Ap.mjs";
1
+ import { c as SubscribeOptions, f as OutboundMessage, i as DriverContext, l as Subscription, n as DeliveryHandler, o as MessagingDriver, r as DriverCapabilities } from "./driver-Dv38b74F.mjs";
2
2
  import { SQSClient, SQSClientConfig } from "@aws-sdk/client-sqs";
3
3
  /** Options of {@link sqs}. */
4
4
  export interface SqsDriverOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@znight/messaging",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Znight messaging plugin: consumers, retries and dead-letter queues with RabbitMQ, SQS or Kafka",
5
5
  "license": "MIT",
6
6
  "author": "Ugho STEPHAN",
@@ -73,13 +73,13 @@
73
73
  "access": "public"
74
74
  },
75
75
  "dependencies": {
76
- "@znight/core": "0.3.0"
76
+ "@znight/core": "0.4.0"
77
77
  },
78
78
  "peerDependencies": {
79
79
  "@aws-sdk/client-sqs": "^3.600.0",
80
80
  "@platformatic/kafka": "^2.0.0",
81
81
  "amqplib": "^2.0.0",
82
- "@znight/compiler": "^0.3.0"
82
+ "@znight/compiler": "^0.4.0"
83
83
  },
84
84
  "peerDependenciesMeta": {
85
85
  "@aws-sdk/client-sqs": {
@@ -104,7 +104,7 @@
104
104
  "amqplib": "2.2.0",
105
105
  "testcontainers": "12.2.1",
106
106
  "zod": "4.6.7",
107
- "@znight/compiler": "0.3.0"
107
+ "@znight/compiler": "0.4.0"
108
108
  },
109
109
  "znight": {
110
110
  "apiVersion": 1,
@@ -377,7 +377,7 @@ function analyzeMethod(cls: ClassInfo, method: MethodInfo, ctx: Context): Member
377
377
  if (onMessage.length > 0) {
378
378
  const policy: RetryPolicyData | undefined =
379
379
  retry[0] === undefined ? undefined : normalizeRetryDecorator(retry[0], where, ctx)
380
- const params = analyzeHandlerParams(method, where, ctx)
380
+ const params = analyzeHandlerParams(cls, method, where, ctx)
381
381
  handlers = onMessage.flatMap(decorator => {
382
382
  const type = messageType(decorator, where, ctx)
383
383
  if (type === false) return []
@@ -503,12 +503,43 @@ function checkErrorHandler(method: MethodInfo, decorator: DecoratorInfo, where:
503
503
 
504
504
  // ---------------------------------------------------------------------------------------- parameters
505
505
 
506
- function analyzeHandlerParams(method: MethodInfo, where: string, ctx: Context): readonly HandlerParamData[] {
507
- return Object.freeze(method.params.map(param => analyzeHandlerParam(param, where, ctx)))
506
+ function analyzeHandlerParams(
507
+ cls: ClassInfo,
508
+ method: MethodInfo,
509
+ where: string,
510
+ ctx: Context,
511
+ ): readonly HandlerParamData[] {
512
+ return Object.freeze(method.params.map(param => analyzeHandlerParam(cls, param, where, ctx)))
508
513
  }
509
514
 
510
- function analyzeHandlerParam(param: ParameterInfo, where: string, ctx: Context): HandlerParamData {
515
+ function analyzeHandlerParam(cls: ClassInfo, param: ParameterInfo, where: string, ctx: Context): HandlerParamData {
511
516
  const decorators = parameterDecorators(param)
517
+ // custom parameter decorators (RFC 0010); compilers before it have no paramDecorator
518
+ const custom = typeof ctx.paramDecorator === 'function' ? ctx.paramDecorator(param, 'messaging', cls) : undefined
519
+ if (custom !== undefined) {
520
+ for (const other of decorators) {
521
+ ctx.report({
522
+ code: INVALID_DECORATOR,
523
+ severity: 'error',
524
+ message: `Parameter '${param.name}' of ${where} has several parameter decorators (@${custom.decorator.name} and @${other.name})`,
525
+ location: other.location,
526
+ hints: ['Keep a single parameter decorator'],
527
+ })
528
+ }
529
+ if (!custom.ok || decorators.length > 0) return { kind: 'undefined' }
530
+ const data = custom.data
531
+ return {
532
+ kind: 'custom',
533
+ identity: custom.identity,
534
+ name: custom.name,
535
+ data: data === undefined ? undefined : { value: data.ref ?? (data.value as StaticValue) },
536
+ resolver: custom.resolver.kind === 'class' ? custom.resolver.ref : undefined,
537
+ ...(custom.resolver.kind === 'class' && custom.resolver.arity !== undefined
538
+ ? { arity: custom.resolver.arity }
539
+ : {}),
540
+ cast: custom.cast,
541
+ }
542
+ }
512
543
  const decorator = decorators[0]
513
544
  if (decorator === undefined) {
514
545
  const foreign = foreignParameterDecorator(param)
@@ -2,7 +2,7 @@
2
2
  * Data stored by the extension during analysis (`ctx.store()`), read back by `validate` and
3
3
  * `generate`. Plain data only (plus the `ValueRef`s of schemas), in source order.
4
4
  */
5
- import type { Location, StaticValue, ValueRef } from '@znight/compiler'
5
+ import type { ClassRef, Location, StaticValue, ValueRef } from '@znight/compiler'
6
6
  import type { Token } from '@znight/core'
7
7
 
8
8
  /** A normalized `@Retry` policy (`MessageHandlerDefinition.retry`). */
@@ -27,6 +27,27 @@ export type HandlerParamData =
27
27
  | { readonly kind: 'message'; readonly narrow: boolean }
28
28
  /** Optional parameter without messaging decorator, or invalid parameter (reported): `undefined`. */
29
29
  | { readonly kind: 'undefined' }
30
+ /**
31
+ * A custom parameter decorator (`createParamDecorator`, RFC 0010): `await
32
+ * (D.resolvers.messaging as ParamFunctionOf<typeof D, 'messaging'>)(m, data)` or `await x[i].resolve(m, data)`.
33
+ */
34
+ | {
35
+ readonly kind: 'custom'
36
+ /** Canonical identity of the decorator `const` (imported by the generated module). */
37
+ readonly identity: string
38
+ readonly name: string
39
+ /** The data argument (a value, possibly holding references, or a reference); `undefined` without argument. */
40
+ readonly data: { readonly value: StaticValue } | undefined
41
+ /** The class resolver; `undefined` for a function resolver. */
42
+ readonly resolver: ClassRef | undefined
43
+ /**
44
+ * Parameters of the class resolver's `resolve` (own, inherited or an arrow function property);
45
+ * `undefined` for a function, or when the compiler cannot read it (the call is cast).
46
+ */
47
+ readonly arity?: number
48
+ /** The value type could not be checked against the parameter. */
49
+ readonly cast: boolean
50
+ }
30
51
 
31
52
  /** One `@OnMessage` decorator of a method. */
32
53
  export interface HandlerData {