{"version":3,"file":"index.cjs","names":["captureStack","RepositoryManager","Model"],"sources":["../../../../../../notifications/src/errors.ts","../../../../../../notifications/src/config.ts","../../../../../../notifications/src/dispatch/define-channel.ts","../../../../../../notifications/src/dispatch/dispatch-id.ts","../../../../../../notifications/src/dispatch/notifications-event-bus.ts","../../../../../../notifications/src/dispatch/dispatcher.ts","../../../../../../notifications/src/dispatch/define-notification.ts","../../../../../../notifications/src/dispatch/notify.ts","../../../../../../notifications/src/channels/database-channel.ts","../../../../../../notifications/src/channels/mail-channel.ts","../../../../../../notifications/src/in-app/column-map.ts","../../../../../../notifications/src/in-app/base-notifications-repository.ts","../../../../../../notifications/src/in-app/database-notification.ts","../../../../../../notifications/src/in-app/in-app.ts","../../../../../../notifications/src/migration/notification-columns.ts","../../../../../../notifications/src/queue/load-herald.ts","../../../../../../notifications/src/queue/herald-queue.ts","../../../../../../notifications/src/queue/notifications-worker.ts","../../../../../../notifications/src/queue/queue-package-not-installed.error.ts","../../../../../../notifications/src/queue/load-queue.ts","../../../../../../notifications/src/queue/bullmq-queue.ts"],"sourcesContent":["/**\n * Domain errors for `@warlock.js/notifications`. All extend `Error` directly\n * (no framework HttpError) so the package stays runtime-agnostic — the host\n * app maps them to its own error shape via `instanceof` if needed.\n *\n * Each captures a clean V8 stack (drops the Error-constructor frame) to match\n * the cascade convention.\n */\n\nfunction captureStack(target: object, ctor: new (...args: any[]) => unknown): void {\n  // V8 only; guarded for non-V8 runtimes.\n  (Error as unknown as { captureStackTrace?: (t: object, c: unknown) => void }).captureStackTrace?.(\n    target,\n    ctor,\n  );\n}\n\nexport class NotificationsNotConfiguredError extends Error {\n  public constructor() {\n    super(\n      \"Notifications not configured — add `src/config/notifications.ts` with a \" +\n        \"default-exported config; the notifications connector registers it at boot.\",\n    );\n    this.name = \"NotificationsNotConfiguredError\";\n    captureStack(this, NotificationsNotConfiguredError);\n  }\n}\n\nexport class ChannelNotFoundError extends Error {\n  public constructor(channelName: string) {\n    super(\n      `Channel \"${channelName}\" is not configured. Add it to the ` +\n        \"`channels` map in `src/config/notifications.ts`.\",\n    );\n    this.name = \"ChannelNotFoundError\";\n    captureStack(this, ChannelNotFoundError);\n  }\n}\n\nexport class NoQueueDispatcherError extends Error {\n  public constructor(channelName: string) {\n    super(\n      `Cannot queue to channel \"${channelName}\" — no queue dispatcher configured. ` +\n        \"Add a `queue` to `src/config/notifications.ts`, or call `.send()` instead of `.queue()`.\",\n    );\n    this.name = \"NoQueueDispatcherError\";\n    captureStack(this, NoQueueDispatcherError);\n  }\n}\n\nexport class MissingRendererError extends Error {\n  public constructor(notificationType: string, channelName: string) {\n    super(\n      `Notification \"${notificationType}\" routes to channel \"${channelName}\" via ` +\n        `\\`via\\` but defines no \\`${channelName}\\` renderer.`,\n    );\n    this.name = \"MissingRendererError\";\n    captureStack(this, MissingRendererError);\n  }\n}\n\n/**\n * Thrown when a channel's `route()` resolver runs but returns `undefined` for\n * a recipient (e.g. a mail recipient with no email). Distinct from \"the\n * channel declares no resolver\" — that case falls back to `{ id }`.\n */\nexport class UnresolvableRouteError extends Error {\n  public constructor(channelName: string, recipientId?: unknown) {\n    super(\n      `Channel \"${channelName}\" could not resolve a route for recipient ` +\n        `${recipientId ?? \"(unknown)\"}. The channel's \\`route()\\` returned undefined — ` +\n        \"the recipient is missing the address column this channel needs.\",\n    );\n    this.name = \"UnresolvableRouteError\";\n    captureStack(this, UnresolvableRouteError);\n  }\n}\n","/**\n * Notification configuration holder — the runtime singleton the dispatch core\n * reads from. In a Warlock app you do NOT call `setNotificationConfig`\n * directly: `src/config/notifications.ts` exports a `NotificationConfig` as its\n * default, and the notifications connector calls this at boot. Tests (and\n * non-Warlock embeddings) may call it directly.\n *\n * @example  src/config/notifications.ts — declarative; the connector registers it\n *   import { type NotificationConfig, mailChannel, inApp } from \"@warlock.js/notifications\";\n *   import { Notification } from \"app/notifications/notification.model\";\n *\n *   const config: NotificationConfig = {\n *     channels: {\n *       mail: mailChannel({ from: \"no-reply@store.com\" }),\n *       database: inApp.configure({ model: Notification }),\n *     },\n *     // preferences / rateLimit / queue all optional\n *   };\n *\n *   export default config;\n */\nimport type {\n  Channel,\n  PreferenceProvider,\n  QueueDispatcher,\n  RateLimiter,\n} from \"./contracts\";\nimport { NotificationsNotConfiguredError } from \"./errors\";\nimport type { ChannelName, NotificationChannels } from \"./types\";\n\n/**\n * Channel registry — each key is a registered channel name, each value the\n * channel whose payload type matches the registry. Typed as a partial map\n * over `ChannelName` so `setNotificationConfig` rejects a channel whose\n * payload doesn't line up with its registry entry, AND so a custom channel\n * (declaration-merged into `NotificationChannels`) is accepted with its real\n * payload type rather than `any`.\n */\nexport type ChannelMap = Partial<{\n  [C in ChannelName]: Channel<NotificationChannels[C]>;\n}>;\n\n/**\n * Runtime configuration the dispatcher reads on every send. `queue`,\n * `preferences`, and `rateLimit` are all OPTIONAL slots — the package\n * reserves their shape but ships no defaults.\n */\nexport type NotificationConfig = {\n  /** Channel registry — keys correlate to `NotificationChannels` payloads. */\n  channels: ChannelMap;\n  /** Async dispatcher backing `.queue()`. Phase 2 ships a herald impl. */\n  queue?: QueueDispatcher;\n  /** Pre-send gate: drops channels the recipient opted out of. */\n  preferences?: PreferenceProvider;\n  /** Pre-send gate: drops channels exceeding per-recipient budgets. */\n  rateLimit?: RateLimiter;\n};\n\nlet activeConfig: NotificationConfig | undefined;\n\n/**\n * Set the active notifications configuration. In a Warlock app the\n * notifications connector calls this at boot with the default export of\n * `src/config/notifications.ts`. Subsequent calls REPLACE the active config\n * (does not merge) — pass the complete config, not a partial.\n */\nexport function setNotificationConfig(config: NotificationConfig): void {\n  activeConfig = config;\n}\n\n/**\n * Get the active notifications configuration. Throws if not yet configured —\n * fail loudly at the first send rather than silently doing nothing.\n */\nexport function getNotificationConfig(): NotificationConfig {\n  if (!activeConfig) {\n    throw new NotificationsNotConfiguredError();\n  }\n  return activeConfig;\n}\n\n/**\n * Clear the active configuration. Intended for tests; not part of the runtime\n * surface. Production code should never call this.\n */\nexport function resetNotificationConfig(): void {\n  activeConfig = undefined;\n}\n","import type { Channel } from \"../contracts\";\n\n/**\n * Identity helper that narrows the channel type to its payload. Use it when\n * you want TypeScript to enforce the `send` payload shape against the\n * payload type you declared.\n *\n * Built-in channels (`mail`, `database`) ship as factories; custom channels\n * are usually defined with this helper.\n *\n * @example\n *   import { defineChannel } from \"@warlock.js/notifications\";\n *\n *   type DiscordPayload = { content: string };\n *\n *   export const discordChannel = () =>\n *     defineChannel<DiscordPayload>({\n *       name: \"discord\",\n *       route: (n) => n.get(\"discord_webhook\"),\n *       async send({ payload, route }) {\n *         await fetch(route as string, {\n *           method: \"POST\",\n *           headers: { \"content-type\": \"application/json\" },\n *           body: JSON.stringify(payload),\n *         });\n *       },\n *     });\n *\n *   // Then teach the registry:\n *   declare module \"@warlock.js/notifications\" {\n *     interface NotificationChannels { discord: DiscordPayload }\n *   }\n */\nexport function defineChannel<P>(channel: Channel<P>): Channel<P> {\n  return channel;\n}\n","import { randomUUID } from \"node:crypto\";\n\n/**\n * A unique id for one (channel, recipient) dispatch. `sending` and its terminal\n * `sent` / `failed` event share it so observers can pair them — for tracing\n * spans, latency, or spotting a `sending` with no terminal (a hung send).\n */\nexport function newDispatchId(): string {\n  return randomUUID();\n}\n","/**\n * Typed event bus for notifications observability. Emits `sent`, `failed`,\n * and `skipped` events per (channel, recipient) — fan-out emits N events.\n *\n * Intentionally minimal — a `Map<event, Set<handler>>` keeps tests trivial\n * and avoids pulling event-emitter machinery for what is fundamentally\n * pub/sub. Handler exceptions are logged-and-swallowed so one bad listener\n * cannot break the dispatcher.\n *\n * @example\n *   notifications.on(\"sent\",    ({ channel }) => metrics.inc(`notif.${channel}.sent`));\n *   notifications.on(\"skipped\", ({ reason })  => metrics.inc(`notif.skipped.${reason}`));\n *   const off = notifications.on(\"failed\", logErr);\n *   off(); // unsubscribe\n */\nimport { log } from \"@warlock.js/logger\";\nimport type { NotificationEvents } from \"../types\";\n\ntype EventName = keyof NotificationEvents;\ntype Handler<E extends EventName> = (data: NotificationEvents[E]) => void | Promise<void>;\n\nconst handlers: { [E in EventName]: Set<Handler<E>> } = {\n  sending: new Set(),\n  sent: new Set(),\n  failed: new Set(),\n  skipped: new Set(),\n};\n\n/**\n * Public observability surface.\n *\n * `on(event, handler)` returns an unsubscribe function. `off` exists for\n * symmetry with libraries that hold handler references for later removal.\n */\nexport const notifications = {\n  on<E extends EventName>(event: E, handler: Handler<E>): () => void {\n    handlers[event].add(handler as Handler<EventName>);\n    return () => {\n      handlers[event].delete(handler as Handler<EventName>);\n    };\n  },\n  off<E extends EventName>(event: E, handler: Handler<E>): void {\n    handlers[event].delete(handler as Handler<EventName>);\n  },\n};\n\n/**\n * Internal emit — used by the dispatcher. Not exported from the package.\n * Handlers run in registration order; an awaited Promise.allSettled isolates\n * each listener so one throw cannot block the others.\n */\nexport async function emit<E extends EventName>(\n  event: E,\n  data: NotificationEvents[E],\n): Promise<void> {\n  const set = handlers[event];\n  if (set.size === 0) return;\n\n  // The `async` wrapper turns SYNC handler throws into promise rejections so\n  // `Promise.allSettled` can isolate them. Without it, a `throw` inside the\n  // handler escapes the array map before allSettled gets a chance to catch.\n  const results = await Promise.allSettled(\n    Array.from(set).map(async (handler) => {\n      await (handler as Handler<E>)(data);\n    }),\n  );\n\n  for (const result of results) {\n    if (result.status === \"rejected\") {\n      log.error(\"notifications\", \"event-handler\", result.reason);\n    }\n  }\n}\n","/**\n * Internal dispatch core — shared by `defineNotification` and `notify.<channel>`.\n *\n * Responsibilities:\n * 1. Look up the channel in the configured registry (throws if missing).\n * 2. Run the rate-limit gate (if a notification type is known) — drop with a\n *    `skipped` event when the limiter refuses.\n * 3. Resolve the route: raw string > channel.route(notifiable) > { id }.\n * 4. Inject the per-send `idempotencyKey` into the database payload.\n * 5. Dispatch (sync via channel.send OR async via queue.dispatch).\n * 6. Emit `sent` on success, `failed` on throw.\n *\n * Per-channel failure isolation is the CALLER's job (it wraps a list of\n * `dispatchChannel` calls in `Promise.allSettled`).\n */\nimport { getNotificationConfig } from \"../config\";\nimport { ChannelNotFoundError, NoQueueDispatcherError, UnresolvableRouteError } from \"../errors\";\nimport type { ChannelName, Id, Notifiable, SendOptions } from \"../types\";\nimport { newDispatchId } from \"./dispatch-id\";\nimport { emit } from \"./notifications-event-bus\";\n\nexport type DispatchMode = \"send\" | \"queue\";\n\nexport type DispatchChannelArgs = {\n  channelName: string;\n  payload: unknown;\n  /** The recipient model — undefined when dispatching to a raw route string. */\n  to: Notifiable | undefined;\n  /** Raw route override (ad-hoc `notify.mail(\"x@y.com\", …)`). */\n  rawRoute?: string;\n  /** Notification type — drives the rate-limit gate. May be undefined. */\n  notificationType: string | undefined;\n  mode: DispatchMode;\n  options: SendOptions;\n};\n\n/**\n * Dispatch ONE rendered payload to ONE recipient through ONE channel.\n * Does NOT consult `PreferenceProvider` — that gate runs at the caller level.\n *\n * Throws `ChannelNotFoundError` (channel not registered) and\n * `UnresolvableRouteError` (channel's `route()` returned undefined) — both are\n * configuration/data errors that should surface loudly. Transport failures\n * (channel.send throwing) are emitted as `failed` AND rethrown.\n */\nexport async function dispatchChannel(args: DispatchChannelArgs): Promise<void> {\n  const config = getNotificationConfig();\n  const channel = config.channels[args.channelName as ChannelName];\n\n  if (!channel) {\n    throw new ChannelNotFoundError(args.channelName);\n  }\n\n  // One id for this (channel, recipient) dispatch — `sending` and its terminal\n  // `sent` / `failed` share it so observers can pair them.\n  const dispatchId = newDispatchId();\n\n  // Rate-limit gate. Only applies when we have a notifiable + a notification\n  // type (otherwise there's nothing to rate-limit on). `force` does NOT\n  // bypass — rate limits are a safety valve, not a UX preference.\n  //\n  // NOTE (Phase-2 seam): for mode \"queue\" the budget is consumed at ENQUEUE\n  // time here, not at delivery. When the herald worker lands it should run\n  // this gate at delivery instead; see RateLimiter docstring.\n  if (args.to && args.notificationType && config.rateLimit) {\n    const allowed = await config.rateLimit.allow(\n      args.to,\n      args.channelName as ChannelName,\n      args.notificationType,\n    );\n\n    if (!allowed) {\n      await emit(\"skipped\", {\n        dispatchId,\n        channel: args.channelName,\n        notifiable: args.to,\n        reason: \"rate-limit\",\n        options: args.options,\n      });\n      return;\n    }\n  }\n\n  const route = resolveRoute(args, channel.route);\n\n  // Inject idempotency key into the database payload (so the repo can dedupe).\n  let payload = args.payload;\n  if (\n    args.channelName === \"database\" &&\n    args.options.idempotencyKey &&\n    payload &&\n    typeof payload === \"object\"\n  ) {\n    payload = { ...(payload as object), idempotencyKey: args.options.idempotencyKey };\n  }\n\n  // `sending` fires after the gates pass + the route resolves, BEFORE the\n  // transport — awaited so it always precedes the send. Observers can't abort\n  // the send, but a slow handler delays it; keep them fast.\n  await emit(\"sending\", {\n    dispatchId,\n    channel: args.channelName,\n    notifiable: args.to,\n    payload,\n    options: args.options,\n  });\n\n  const startedAt = Date.now();\n\n  try {\n    if (args.mode === \"queue\") {\n      if (!config.queue) {\n        throw new NoQueueDispatcherError(args.channelName);\n      }\n      await config.queue.dispatch({\n        channel: args.channelName,\n        route,\n        payload,\n        options: args.options,\n      });\n    } else {\n      await channel.send({\n        payload: payload as never,\n        route,\n        notifiable: args.to,\n        options: args.options,\n      });\n    }\n\n    await emit(\"sent\", {\n      dispatchId,\n      channel: args.channelName,\n      notifiable: args.to,\n      payload,\n      options: args.options,\n      durationMs: Date.now() - startedAt,\n    });\n  } catch (error) {\n    await emit(\"failed\", {\n      dispatchId,\n      channel: args.channelName,\n      notifiable: args.to,\n      payload,\n      error: error as Error,\n      options: args.options,\n      durationMs: Date.now() - startedAt,\n    });\n    throw error;\n  }\n}\n\n/**\n * Route resolution priority:\n *   1. raw route string (ad-hoc `notify.mail(\"x@y.com\", …)`)\n *   2. the channel's `route(notifiable)` resolver\n *   3. `{ id: notifiable.id }` — ONLY when the channel declares no resolver\n *\n * When a channel HAS a resolver but it returns undefined (e.g. a mail\n * recipient with no email), we throw `UnresolvableRouteError` rather than\n * silently coercing to `{ id }` — that would hand a string-route channel an\n * object and fail deep inside the transport.\n */\nfunction resolveRoute(\n  args: DispatchChannelArgs,\n  resolver: ((notifiable: Notifiable) => string | { id: Id } | undefined) | undefined,\n): string | { id: Id } {\n  if (args.rawRoute !== undefined) {\n    return args.rawRoute;\n  }\n\n  if (!args.to) {\n    throw new UnresolvableRouteError(args.channelName);\n  }\n\n  // No resolver declared → the channel addresses by id (database/internal).\n  if (!resolver) {\n    return { id: args.to.id };\n  }\n\n  const resolved = resolver(args.to);\n\n  if (resolved === undefined) {\n    throw new UnresolvableRouteError(args.channelName, args.to.id);\n  }\n\n  return resolved;\n}\n","/**\n * `defineNotification` — reusable, type-safe, multi-channel notification.\n *\n * Functional, not class-based: pass `via` + a renderer per channel + a\n * stable `type`. The returned object exposes `.send` / `.queue` / `.only`.\n *\n * Renderer signature is `(data, to, ctx)` where `ctx` carries `locale` and\n * `meta` from `SendOptions`. The database renderer's `type` is OPTIONAL — the\n * dispatcher injects `def.type`, keeping the type defined in ONE place.\n *\n * ## Error policy\n * - **Config errors** (missing renderer, no queue dispatcher, unknown channel)\n *   are programmer mistakes → they REJECT `.send()`/`.queue()` (and a static\n *   `via` with a missing renderer throws at definition time). Consistent with\n *   `notify`, which rejects `ChannelNotFoundError`.\n * - **Transport errors** (a channel's `send` throwing) stay ISOLATED — emitted\n *   as a `failed` event, never aborting sibling channels/recipients.\n * - **`via()` / preferences throws** for one recipient are logged and that\n *   recipient is dropped (the rest of a fan-out still go).\n *\n * @example\n *   export const orderShipped = defineNotification<{ order: Order }>({\n *     type: \"order.shipped\",\n *     via: [\"mail\", \"database\"],\n *     mail: ({ order }, to) => ({ subject: `#${order.number} shipped`, html: \"…\" }),\n *     database: ({ order }) => ({ title: \"Your order shipped\", payload: { id: order.id } }),\n *   });\n *   await orderShipped.send(user, { order });\n *   await orderShipped.queue([buyer, salesRep], { order }, { delay: \"10m\" });\n *   await orderShipped.only(\"mail\").send(user, { order });\n */\nimport { log } from \"@warlock.js/logger\";\nimport { getNotificationConfig } from \"../config\";\nimport {\n  ChannelNotFoundError,\n  MissingRendererError,\n  NoQueueDispatcherError,\n} from \"../errors\";\nimport type {\n  ChannelName,\n  Notifiable,\n  NotificationChannels,\n  RenderContext,\n  SendOptions,\n} from \"../types\";\nimport { newDispatchId } from \"./dispatch-id\";\nimport { dispatchChannel, type DispatchMode } from \"./dispatcher\";\nimport { emit } from \"./notifications-event-bus\";\n\n/**\n * Per-channel renderer return type. The `database` renderer may OMIT `type`\n * — it inherits from `NotificationDef.type` at dispatch time.\n */\ntype RendererReturn<C extends ChannelName> = C extends \"database\"\n  ? Omit<NotificationChannels[\"database\"], \"type\">\n  : NotificationChannels[C];\n\ntype Renderer<Data, C extends ChannelName> = (\n  data: Data,\n  to: Notifiable,\n  ctx: RenderContext,\n) => RendererReturn<C>;\n\nexport type NotificationDef<Data> = {\n  type: string;\n  via: ChannelName[] | ((data: Data, to: Notifiable) => ChannelName[]);\n} & {\n  [C in ChannelName]?: Renderer<Data, C>;\n};\n\nexport interface DefinedNotification<Data> {\n  send(to: Notifiable | Notifiable[], data: Data, options?: SendOptions): Promise<void>;\n  queue(to: Notifiable | Notifiable[], data: Data, options?: SendOptions): Promise<void>;\n  only(...channels: ChannelName[]): DefinedNotification<Data>;\n}\n\n/** Errors that represent a misconfiguration and must surface (reject), not isolate. */\nfunction isConfigError(error: unknown): boolean {\n  return (\n    error instanceof MissingRendererError ||\n    error instanceof NoQueueDispatcherError ||\n    error instanceof ChannelNotFoundError\n  );\n}\n\nexport function defineNotification<Data>(def: NotificationDef<Data>): DefinedNotification<Data> {\n  const make = (restrict?: ChannelName[]): DefinedNotification<Data> => {\n    // Validate at definition time when the channel set is static — a missing\n    // renderer is a programmer error, surfaced loudly at import.\n    if (Array.isArray(def.via)) {\n      const channels = restrict ? def.via.filter((c) => restrict.includes(c)) : def.via;\n      for (const channel of channels) {\n        if (!def[channel]) {\n          throw new MissingRendererError(def.type, channel);\n        }\n      }\n    }\n\n    return {\n      send(to, data, options) {\n        return dispatchAll(def, to, data, options ?? {}, \"send\", restrict);\n      },\n      queue(to, data, options) {\n        return dispatchAll(def, to, data, options ?? {}, \"queue\", restrict);\n      },\n      only(...channels) {\n        return make(channels);\n      },\n    };\n  };\n\n  return make();\n}\n\n/**\n * Fan-out to all recipients. Each recipient dispatches independently; one\n * recipient's transport failure never aborts the others. CONFIG errors,\n * however, propagate — they reject the returned promise.\n */\nasync function dispatchAll<Data>(\n  def: NotificationDef<Data>,\n  to: Notifiable | Notifiable[],\n  data: Data,\n  options: SendOptions,\n  mode: DispatchMode,\n  restrict?: ChannelName[],\n): Promise<void> {\n  const recipients = Array.isArray(to) ? to : [to];\n\n  const results = await Promise.allSettled(\n    recipients.map((recipient) =>\n      dispatchToRecipient(def, recipient, data, options, mode, restrict),\n    ),\n  );\n\n  rethrowConfigErrors(results);\n}\n\nasync function dispatchToRecipient<Data>(\n  def: NotificationDef<Data>,\n  to: Notifiable,\n  data: Data,\n  options: SendOptions,\n  mode: DispatchMode,\n  restrict?: ChannelName[],\n): Promise<void> {\n  const config = getNotificationConfig();\n\n  // Resolve channels (via + preferences). A throw here drops THIS recipient\n  // with a log — it's a per-recipient data problem, not a config error.\n  let channels: ChannelName[];\n  try {\n    channels = typeof def.via === \"function\" ? def.via(data, to) : [...def.via];\n\n    if (restrict) {\n      channels = channels.filter((c) => restrict.includes(c));\n    }\n\n    if (!options.force && config.preferences) {\n      const allowed = await config.preferences.resolveChannels(to, def.type, channels);\n      for (const channel of channels.filter((c) => !allowed.includes(c))) {\n        await emit(\"skipped\", {\n          dispatchId: newDispatchId(),\n          channel,\n          notifiable: to,\n          reason: \"preference\",\n          options,\n        });\n      }\n      channels = channels.filter((c) => allowed.includes(c));\n    }\n  } catch (error) {\n    log.error(\"notifications\", `resolve.${def.type}`, error as Error);\n    return;\n  }\n\n  const renderCtx: RenderContext = { locale: options.locale, meta: options.meta };\n\n  const results = await Promise.allSettled(\n    channels.map((name) => {\n      const renderer = def[name] as Renderer<Data, ChannelName> | undefined;\n      if (!renderer) {\n        // Dynamic via chose a channel with no renderer — surface it.\n        return Promise.reject(new MissingRendererError(def.type, name));\n      }\n\n      let payload: unknown = renderer(data, to, renderCtx);\n\n      // Default the database channel's `type` from `def.type` when omitted.\n      if (name === \"database\" && payload && typeof payload === \"object\" && !(\"type\" in payload)) {\n        payload = { ...(payload as object), type: def.type };\n      }\n\n      return dispatchChannel({\n        channelName: name,\n        payload,\n        to,\n        notificationType: def.type,\n        mode,\n        options,\n      });\n    }),\n  );\n\n  rethrowConfigErrors(results);\n}\n\n/** Collect config-class rejections and rethrow (single or AggregateError). */\nfunction rethrowConfigErrors(results: PromiseSettledResult<unknown>[]): void {\n  const configErrors = results\n    .filter((r): r is PromiseRejectedResult => r.status === \"rejected\" && isConfigError(r.reason))\n    .map((r) => r.reason);\n\n  if (configErrors.length === 1) {\n    throw configErrors[0];\n  }\n  if (configErrors.length > 1) {\n    throw new AggregateError(configErrors, \"Multiple notification configuration errors\");\n  }\n}\n","/**\n * `notify` — ad-hoc, per-channel send facade.\n *\n * A Proxy keyed by the channel registry. `notify.<channel>(to, payload,\n * options?)` works for ANY channel name registered in\n * `NotificationChannels` — built-in or custom (via declaration merging).\n *\n * For multi-channel sends, use `defineNotification` — that's the reusable\n * pattern and the only mental model for \"send through several channels at\n * once\" (no inline multi-channel overload here on purpose).\n *\n * `notify.channel(name)` is the runtime escape when the channel name isn't a\n * compile-time literal.\n *\n * @example\n *   await notify.mail(user, { subject: \"Welcome\", html: \"<p>Hi!</p>\" });\n *   await notify.database(user, { type: \"welcome\", title: \"Welcome!\" });\n *   await notify.mail(\"guest@example.com\", { subject: \"…\", html: \"…\" });   // raw target\n *   await notify.channel(dynamicName).send(user, payload);                 // dynamic name\n */\nimport { getNotificationConfig } from \"../config\";\nimport type { ChannelName, Notifiable, NotificationChannels, SendOptions } from \"../types\";\nimport { newDispatchId } from \"./dispatch-id\";\nimport { dispatchChannel } from \"./dispatcher\";\nimport { emit } from \"./notifications-event-bus\";\n\n/**\n * Public shape of `notify`. The mapped portion gives `notify.<channel>` per\n * registered channel; `channel(name)` is the runtime escape.\n */\nexport type Notify = {\n  channel(name: string): {\n    send(\n      to: Notifiable | string,\n      payload: unknown,\n      options?: SendOptions,\n    ): Promise<void>;\n  };\n} & {\n  [C in ChannelName]: (\n    to: Notifiable | string,\n    payload: NotificationChannels[C],\n    options?: SendOptions,\n  ) => Promise<void>;\n};\n\n/**\n * Per-call dispatch for `notify.<channel>`. Mirrors `defineNotification`'s\n * pipeline but without rendering: payload is already concrete.\n *\n * Preferences gate runs ONLY when a notification `type` is known — explicit\n * via `options.type`, or implicit from a database payload's `type` field.\n * Without a type we have nothing to gate on, and ad-hoc sends bypass\n * preferences silently (use `defineNotification` if you want guaranteed\n * gating).\n */\nasync function notifyImpl(\n  channelName: string,\n  to: Notifiable | string,\n  payload: unknown,\n  options: SendOptions = {},\n): Promise<void> {\n  const isRawTarget = typeof to === \"string\";\n  const notifiable: Notifiable | undefined = isRawTarget ? undefined : to;\n\n  // Derive the type: explicit `options.type` wins, then the payload's\n  // `type` field (database channel convention).\n  let notificationType: string | undefined = options.type;\n  if (\n    notificationType === undefined &&\n    payload &&\n    typeof payload === \"object\" &&\n    \"type\" in payload &&\n    typeof (payload as { type: unknown }).type === \"string\"\n  ) {\n    notificationType = (payload as { type: string }).type;\n  }\n\n  // Preferences gate — only when notifiable + type are both available.\n  if (notifiable && notificationType && !options.force) {\n    const config = getNotificationConfig();\n\n    if (config.preferences) {\n      const allowed = await config.preferences.resolveChannels(\n        notifiable,\n        notificationType,\n        [channelName as ChannelName],\n      );\n\n      if (!allowed.includes(channelName as ChannelName)) {\n        await emit(\"skipped\", {\n          dispatchId: newDispatchId(),\n          channel: channelName,\n          notifiable,\n          reason: \"preference\",\n          options,\n        });\n        return;\n      }\n    }\n  }\n\n  await dispatchChannel({\n    channelName,\n    payload,\n    to: notifiable,\n    rawRoute: isRawTarget ? to : undefined,\n    notificationType,\n    mode: \"send\",\n    options,\n  });\n}\n\n/**\n * Build the `notify` proxy. `channel(name)` returns a `{ send }` object;\n * any other property access returns a one-shot dispatcher for that channel.\n */\nexport const notify: Notify = new Proxy({} as Notify, {\n  get(_target, prop) {\n    if (typeof prop === \"symbol\") {\n      return undefined;\n    }\n\n    // Don't masquerade as a thenable — awaiting or Promise-wrapping `notify`\n    // must NOT invoke notify.then(...) as a channel send. Also keep common\n    // serialization probes from being treated as channel names.\n    if (prop === \"then\" || prop === \"catch\" || prop === \"finally\" || prop === \"toJSON\") {\n      return undefined;\n    }\n\n    if (prop === \"channel\") {\n      return (name: string) => ({\n        send: (to: Notifiable | string, payload: unknown, options?: SendOptions) =>\n          notifyImpl(name, to, payload, options),\n      });\n    }\n\n    return (to: Notifiable | string, payload: unknown, options?: SendOptions) =>\n      notifyImpl(prop, to, payload, options);\n  },\n});\n","/**\n * `databaseChannel` — persists notifications via the bound repository.\n *\n * NOT created by the dev directly — `inApp.configure({ model })` builds it\n * and returns it. That way the read side (`inApp.listUnread`, etc.) and the\n * write side (this channel's `send`) share ONE repository instance, which\n * means cache invalidation just works.\n *\n * Route is `{ id: notifiable.id }` — the recipient's id, used to set the\n * recipient column on the persisted row.\n *\n * Multi-tenant: when the model declares a `tenant` column, this channel reads\n * the tenant value off the recipient model (`notifiable.get(tenantColumn)`)\n * and hands it to `createFor`. The recipient model is only present on the\n * synchronous send path; a tenant-scoped store should send (not queue) the\n * database channel so the recipient — and thus the tenant — is in scope.\n *\n * The dispatcher has already injected `SendOptions.idempotencyKey` into the\n * payload (when set), so `createFor` receives a payload that includes it.\n */\nimport type { NotificationContract } from \"../contracts\";\nimport type { Channel } from \"../contracts\";\nimport { defineChannel } from \"../dispatch/define-channel\";\n// Type-only import to avoid a runtime cycle with `in-app/in-app.ts`.\nimport type { BaseNotificationsRepository } from \"../in-app/base-notifications-repository\";\nimport type { DatabasePayload, Id } from \"../types\";\n\nexport function databaseChannel<TModel extends NotificationContract = NotificationContract>(\n  repo: BaseNotificationsRepository<TModel>,\n): Channel<DatabasePayload> {\n  return defineChannel<DatabasePayload>({\n    name: \"database\",\n    route: (notifiable) => ({ id: notifiable.id }),\n    async send({ payload, route, notifiable }) {\n      const id: Id = typeof route === \"object\" ? route.id : route;\n\n      const { tenantColumn } = repo;\n      const tenantId =\n        tenantColumn && notifiable ? (notifiable.get(tenantColumn) as Id) : undefined;\n\n      await repo.createFor(id, payload, tenantId);\n    },\n  });\n}\n","/**\n * `mailChannel` — wraps `@warlock.js/core` `sendMail`.\n *\n * Route is `notifiable.email` by convention; override via `config.route`\n * when the column is named differently or computed.\n *\n * The renderer's payload is forwarded verbatim (`subject`, `html`, `text`,\n * `cc`). `from` is set once at channel-config time; per-send overrides go\n * through the payload itself (you can include `from` in `MailPayload` if\n * you extend the registry — but the convention is one channel = one\n * sender identity).\n *\n * @example\n *   // src/config/notifications.ts\n *   channels: {\n *     mail: mailChannel({ from: \"no-reply@store.com\" }),\n *   }\n *\n *   // anywhere\n *   await notify.mail(user, { subject: \"Welcome\", html: \"<p>Hi!</p>\" });\n */\nimport { sendMail } from \"@warlock.js/core\";\nimport type { Channel } from \"../contracts\";\nimport { defineChannel } from \"../dispatch/define-channel\";\nimport type { MailPayload, Notifiable } from \"../types\";\n\nexport type MailChannelConfig = {\n  /** Default `from` address for every send through this channel. */\n  from?: string;\n  /** Route resolver. Defaults to `notifiable.get(\"email\")`. */\n  route?: (notifiable: Notifiable) => string;\n};\n\nexport function mailChannel(config: MailChannelConfig = {}): Channel<MailPayload> {\n  const resolveRoute = config.route ?? ((n) => n.get(\"email\") as string);\n\n  return defineChannel<MailPayload>({\n    name: \"mail\",\n    route: (notifiable) => resolveRoute(notifiable),\n    async send({ payload, route }) {\n      await sendMail({\n        to: route as string,\n        from: config.from,\n        ...payload,\n      });\n    },\n  });\n}\n","/**\n * `columnMap` — how a notification model's logical roles map to the physical\n * columns of ITS table. The ONE source the model getters, the repository\n * (read filter + write mapping), the database channel, and the migration\n * factory all read, so they can never drift apart.\n *\n * Declared as a `static columnMap` on the model and ejected to userland, so\n * the dev owns it:\n *\n *   @RegisterModel()\n *   export class Notification extends DatabaseNotification {\n *     public static table = \"notifications\";\n *     public static columnMap: NotificationColumnMap = {\n *       tenant: \"organization_id\", // omit → single-tenant\n *       readAt: \"read_at\",         // read-state (see below)\n *     };\n *   }\n *\n * ## Read-state is chosen by which keys are PRESENT\n *\n * - `readAt` only → unread = `read_at IS NULL`; marking read stamps it.\n * - `isRead` only → unread = `is_read = false`; no timestamp.\n * - both        → `is_read` is the indexed flag used for filtering, `read_at`\n *                 records WHEN; marking read sets both.\n *\n * Declare neither and you get the `readAt`-only default — so a model can omit\n * `columnMap` entirely and still work.\n */\n\n/**\n * The logical → physical column bindings a notification model may declare.\n * Every key is optional; `resolveColumnMap` fills the defaults. Values are the\n * app's real column names (rename-friendly: `readAt: \"seen_at\"`).\n */\nexport type NotificationColumnMap = {\n  /** Recipient FK column. Default: `\"user_id\"`. */\n  recipient?: string;\n  /** Multi-tenant scope column, written from the recipient. Omit → single-tenant. */\n  tenant?: string;\n  /** Read-timestamp column. Present → read-state records \"when\". */\n  readAt?: string;\n  /** Read-flag column (indexed → fast unread filter). Present → read-state is a boolean. */\n  isRead?: string;\n};\n\n/**\n * A `NotificationColumnMap` with the defaults applied. `recipient` is always\n * set, and at least one of `readAt` / `isRead` is always present (the resolver\n * falls back to `readAt`), so consumers never face a \"no read-state\" map.\n */\nexport type ResolvedNotificationColumnMap = {\n  recipient: string;\n  tenant?: string;\n  readAt?: string;\n  isRead?: string;\n};\n\n/** A model class that may carry the `columnMap` static. */\nexport type NotificationColumnMapHost = {\n  columnMap?: NotificationColumnMap;\n};\n\nconst DEFAULT_RECIPIENT_COLUMN = \"user_id\";\nconst DEFAULT_READ_AT_COLUMN = \"read_at\";\n\n/**\n * Resolve a model's declared `columnMap` into a complete map: default the\n * recipient column, and fall back to a `read_at` timestamp when the model\n * declares no read-state column. The fallback guarantees the result always\n * has a usable read-state representation, so no boot-time validation is needed.\n */\nexport function resolveColumnMap(\n  map: NotificationColumnMap | undefined,\n): ResolvedNotificationColumnMap {\n  const recipient = map?.recipient ?? DEFAULT_RECIPIENT_COLUMN;\n  const tenant = map?.tenant;\n\n  if (!map?.readAt && !map?.isRead) {\n    return { recipient, tenant, readAt: DEFAULT_READ_AT_COLUMN };\n  }\n\n  return {\n    recipient,\n    tenant,\n    readAt: map.readAt,\n    isRead: map.isRead,\n  };\n}\n","/**\n * Shipped behavior repository for the in-app store.\n *\n * Concrete (NOT abstract) so `inApp.configure({ model })` can\n * `new BaseNotificationsRepository(model)` without subclassing. The dev only\n * extends this when they need extra query methods.\n *\n * ## The single source of truth: the model's `columnMap`\n *\n * The model declares its physical columns ONCE via `static columnMap`. The\n * constructor resolves that map and derives BOTH sides from it:\n *\n *  - the READ path — `filterBy` maps logical keys (`recipientId`, `unread`,\n *    `type`, …) onto the model's columns; `RepositoryManager` applies it.\n *  - the WRITE / UPDATE / DELETE path — `createFor` / `markRead` / … translate\n *    logical keys through the same `filterBy` via `toRow`.\n *\n * Because both come from one `columnMap`, reads and writes can never target\n * different columns. `unread` is mode-agnostic: it filters `is_read = false`\n * when the model has that flag, otherwise `read_at IS NULL`.\n *\n * @example  default (SQL snake_case — no subclass needed)\n *   inApp.configure({ model: Notification })\n *\n * @example  custom columns — declare them on the model, not here\n *   class Notification extends DatabaseNotification {\n *     public static columnMap = { recipient: \"audience_id\", isRead: \"seen\" };\n *   }\n */\nimport { type FilterRules, RepositoryManager } from \"@warlock.js/core\";\nimport type { NotificationContract } from \"../contracts\";\nimport type { DatabasePayload, Id } from \"../types\";\nimport {\n  type NotificationColumnMapHost,\n  type ResolvedNotificationColumnMap,\n  resolveColumnMap,\n} from \"./column-map\";\nimport type { DatabaseNotification } from \"./database-notification\";\n\n/**\n * Filter shape — logical keys the repo understands. `filterBy` maps each to a\n * physical column (or a predicate) derived from the model's `columnMap`.\n */\nexport type NotificationsFilter = {\n  id?: Id;\n  type?: string;\n  /** Mode-agnostic unread filter — resolves to the model's read-state column. */\n  unread?: boolean;\n  recipientId?: Id;\n  idempotencyKey?: string;\n};\n\n/** Class form of a `DatabaseNotification` subclass — what `configure()` accepts. */\nexport type NotificationModelClass = (new (...args: any[]) => DatabaseNotification) &\n  NotificationColumnMapHost;\n\nexport class BaseNotificationsRepository<\n  TModel extends NotificationContract = NotificationContract,\n> extends RepositoryManager<TModel, NotificationsFilter> {\n  /** Resolved physical columns for the bound model — the single source. */\n  private readonly columns: ResolvedNotificationColumnMap;\n\n  public filterBy: FilterRules;\n\n  public constructor(model?: NotificationModelClass) {\n    super();\n\n    if (model) {\n      this.source = model;\n    }\n\n    this.columns = resolveColumnMap(model?.columnMap);\n    this.filterBy = this.buildFilterBy();\n  }\n\n  /** The tenant column for this model, or `undefined` for single-tenant. */\n  public get tenantColumn(): string | undefined {\n    return this.columns.tenant;\n  }\n\n  /**\n   * Build `filterBy` from the resolved column map. `unread` is the\n   * mode-agnostic read filter; `isRead` / `readAt` / `tenant` are exposed for\n   * direct filtering only when the model declares those columns.\n   */\n  private buildFilterBy(): FilterRules {\n    const { recipient, tenant, readAt, isRead } = this.columns;\n\n    const rules: FilterRules = {\n      id: \"=\",\n      type: \"=\",\n      recipientId: [\"=\", recipient],\n      idempotencyKey: [\"=\", \"idempotency_key\"],\n      unread: (value, query) => {\n        if (value !== true && value !== \"true\") {\n          return;\n        }\n\n        if (isRead) {\n          query.where(isRead, false);\n        } else if (readAt) {\n          query.whereNull(readAt);\n        }\n      },\n    };\n\n    if (isRead) {\n      rules.isRead = [\"=\", isRead];\n    }\n\n    if (readAt) {\n      rules.readAt = [\"=\", readAt];\n    }\n\n    if (tenant) {\n      rules.tenant = [\"=\", tenant];\n    }\n\n    return rules;\n  }\n\n  /**\n   * Translate an object keyed by LOGICAL names into one keyed by PHYSICAL\n   * columns, using `filterBy` as the lookup. Keys with no `[op, column]` tuple\n   * (e.g. `title`, `body`, `payload`, `type`, `id`) pass through unchanged —\n   * they're already physical. Used for every write / update / delete payload +\n   * filter (the paths `RepositoryManager` does NOT map).\n   */\n  protected toRow(obj: Record<string, unknown>): Record<string, unknown> {\n    const rules = this.filterBy as Record<string, unknown>;\n    const row: Record<string, unknown> = {};\n\n    for (const [logical, value] of Object.entries(obj)) {\n      const rule = rules[logical];\n      const column = Array.isArray(rule) ? (rule[1] as string) : logical;\n      row[column] = value;\n    }\n\n    return row;\n  }\n\n  /**\n   * Runtime backstop for the compile-time `DatabasePayload` `Omit`: untyped\n   * callers (`notify.channel(name).send`, payloads assembled from request\n   * JSON) can smuggle server-owned keys past the type system, and spreading\n   * them into the row would let a payload redirect the notification into\n   * another recipient's inbox (recipient spoofing), cross tenants, pre-set\n   * read-state, or clobber the primary key. Drops BOTH the logical names and\n   * the model's resolved physical columns — `toRow` passes unmapped keys\n   * through verbatim, so a payload carrying `user_id` would otherwise reach\n   * the recipient column directly.\n   */\n  private stripServerOwnedKeys(input: DatabasePayload): Record<string, unknown> {\n    const { recipient, tenant, readAt, isRead } = this.columns;\n    const serverOwned = new Set<string | undefined>([\n      \"id\",\n      \"recipientId\",\n      \"tenant\",\n      \"readAt\",\n      \"isRead\",\n      recipient,\n      tenant,\n      readAt,\n      isRead,\n    ]);\n\n    const safe: Record<string, unknown> = {};\n\n    for (const [key, value] of Object.entries(input)) {\n      if (!serverOwned.has(key)) {\n        safe[key] = value;\n      }\n    }\n\n    return safe;\n  }\n\n  /**\n   * Create one row for the recipient. New rows start unread; the tenant column\n   * (when the model declares one) is written from `tenantId`, which the\n   * database channel reads off the recipient. `recipientId` and the other\n   * server-owned keys always come from the trusted arguments — matching keys\n   * in `input` are stripped, never merged.\n   *\n   * Idempotency: when `input.idempotencyKey` is set, find-or-create — return\n   * the existing row instead of inserting a duplicate. A unique index on the\n   * key is the backstop for the rare insert race (we re-fetch on conflict).\n   */\n  public async createFor(\n    recipientId: Id,\n    input: DatabasePayload,\n    tenantId?: Id,\n  ): Promise<TModel> {\n    const { idempotencyKey } = input;\n\n    if (idempotencyKey) {\n      const existing = await this.firstByIdempotencyKey(recipientId, idempotencyKey);\n      if (existing) {\n        return existing;\n      }\n    }\n\n    const row: Record<string, unknown> = { ...this.stripServerOwnedKeys(input), recipientId };\n\n    if (this.columns.isRead) {\n      row.isRead = false;\n    }\n\n    if (this.columns.tenant && tenantId !== undefined) {\n      row.tenant = tenantId;\n    }\n\n    try {\n      return await this.create(this.toRow(row));\n    } catch (error) {\n      // Lost the insert race to a concurrent send with the same key → re-fetch.\n      if (idempotencyKey) {\n        const existing = await this.firstByIdempotencyKey(recipientId, idempotencyKey);\n        if (existing) {\n          return existing;\n        }\n      }\n\n      throw error;\n    }\n  }\n\n  /**\n   * Bulk-create one row per recipient sharing the same input + tenant. Reserved\n   * for the Phase-2 `Channel.sendMany` fan-out hook; not on the per-recipient\n   * send path yet.\n   */\n  public createManyFor(\n    recipientIds: Id[],\n    input: DatabasePayload,\n    tenantId?: Id,\n  ): Promise<TModel[]> {\n    return Promise.all(\n      recipientIds.map((recipientId) => this.createFor(recipientId, input, tenantId)),\n    );\n  }\n\n  /**\n   * Mark rows read — recipient-scoped. `id` omitted → all unread rows for the\n   * recipient; `id` given → that one row, still scoped to the recipient. Scoped\n   * to unread so a re-mark never overwrites an earlier `read_at` timestamp.\n   * Sets whichever read-state column(s) the model declares.\n   */\n  public markRead(recipientId: Id, id?: Id): Promise<number> {\n    const { isRead, readAt } = this.columns;\n    const unreadScope = isRead ? { isRead: false } : { readAt: null };\n\n    const data: Record<string, unknown> = {};\n\n    if (isRead) {\n      data.isRead = true;\n    }\n\n    if (readAt) {\n      data.readAt = new Date();\n    }\n\n    return this.updateMany(\n      this.toRow({ recipientId, ...unreadScope, ...(id !== undefined ? { id } : {}) }),\n      this.toRow(data),\n    );\n  }\n\n  /**\n   * Inverse of `markRead`, same recipient-scoping. No unread scope needed —\n   * clearing the read-state of an already-unread row is a no-op.\n   */\n  public markUnread(recipientId: Id, id?: Id): Promise<number> {\n    const { isRead, readAt } = this.columns;\n    const data: Record<string, unknown> = {};\n\n    if (isRead) {\n      data.isRead = false;\n    }\n\n    if (readAt) {\n      data.readAt = null;\n    }\n\n    return this.updateMany(\n      this.toRow({ recipientId, ...(id !== undefined ? { id } : {}) }),\n      this.toRow(data),\n    );\n  }\n\n  /** Find one row for a recipient (read path — `filterBy` maps the keys). */\n  public findFor(recipientId: Id, id: Id): Promise<TModel | null> {\n    return this.first({ recipientId, id } as NotificationsFilter);\n  }\n\n  /**\n   * Delete rows — recipient-scoped. `id` omitted → clear all for the\n   * recipient; `id` given → that one row.\n   */\n  public deleteFor(recipientId: Id, id?: Id): Promise<number> {\n    return this.deleteMany(this.toRow({ recipientId, ...(id !== undefined ? { id } : {}) }));\n  }\n\n  /** Read path — `filterBy` maps `recipientId`/`idempotencyKey` to columns. */\n  private firstByIdempotencyKey(recipientId: Id, idempotencyKey: string): Promise<TModel | null> {\n    return this.first({ recipientId, idempotencyKey } as NotificationsFilter);\n  }\n}\n","/**\n * Shipped BASE in-app notification model. The scaffolded user model\n * `extends DatabaseNotification`, sets `static table`, and declares its\n * physical columns ONCE via `static columnMap`. Every accessor below derives\n * from that map, so the model, the repository, and the migration agree by\n * construction.\n *\n * The package ships NO concrete table or migration — those eject to userland\n * (thin eject). See `notificationColumns()` for the matching migration factory.\n *\n * @example\n *   @RegisterModel()\n *   export class Notification extends DatabaseNotification {\n *     public static table = \"notifications\";\n *     public static columnMap: NotificationColumnMap = {\n *       tenant: \"organization_id\",\n *       readAt: \"read_at\",\n *     };\n *   }\n */\nimport { Model } from \"@warlock.js/cascade\";\nimport type { NotificationContract } from \"../contracts\";\nimport type { Id } from \"../types\";\nimport {\n  type NotificationColumnMap,\n  type ResolvedNotificationColumnMap,\n  resolveColumnMap,\n} from \"./column-map\";\n\nexport abstract class DatabaseNotification extends Model implements NotificationContract {\n  /**\n   * Physical column bindings for THIS model's table. Override in the subclass;\n   * the empty default resolves to `{ recipient: \"user_id\", readAt: \"read_at\" }`.\n   */\n  public static columnMap: NotificationColumnMap = {};\n\n  /** The resolved column map for this row's model — defaults applied. */\n  protected get columns(): ResolvedNotificationColumnMap {\n    return resolveColumnMap((this.constructor as typeof DatabaseNotification).columnMap);\n  }\n\n  public get recipientId(): Id {\n    return this.get(this.columns.recipient);\n  }\n\n  /** Tenant scope value, or `undefined` for single-tenant models. */\n  public get tenantId(): Id | undefined {\n    const { tenant } = this.columns;\n    return tenant ? this.get(tenant) : undefined;\n  }\n\n  public get type(): string {\n    return this.get(\"type\");\n  }\n\n  public get isRead(): boolean {\n    const { isRead, readAt } = this.columns;\n\n    if (isRead) {\n      return Boolean(this.get(isRead));\n    }\n\n    return readAt ? this.get(readAt) != null : false;\n  }\n\n  public get readAt(): Date | null {\n    const { readAt } = this.columns;\n    return readAt ? (this.get(readAt) ?? null) : null;\n  }\n\n  /**\n   * Mark this row read. Sets whichever read-state column(s) the model\n   * declares — the boolean (fast unread filtering) and/or the timestamp\n   * (when it was read). Persists via `save()`.\n   */\n  public async markRead(): Promise<void> {\n    const { isRead, readAt } = this.columns;\n\n    if (isRead) {\n      this.set(isRead, true);\n    }\n\n    if (readAt) {\n      this.set(readAt, new Date());\n    }\n\n    await this.save();\n  }\n}\n","/**\n * `inApp` — app-facing facade for the in-app/database channel.\n *\n * `inApp.configure({ model | repository })` binds the in-app store AND\n * returns the `database` channel — so the read side and the dispatched\n * write side share ONE repository instance.\n *   • `{ model: Notification }`  — 90% case; default repo built internally.\n *   • `{ repository: myRepo }`   — 10% case; custom column mapping / extras.\n *\n * No zero-arg form: the package ships no concrete model/table (thin eject),\n * so the dev MUST tell us which model to use.\n *\n * Every read + mutation is RECIPIENT-SCOPED by construction — `markAsRead(user,\n * id)` cannot flip a row belonging to a different recipient even if handed a\n * foreign id (the recipient id is forced into the filter → 0 rows match).\n *\n * @example\n *   // src/config/notifications.ts:\n *   inApp.configure({ model: Notification }),\n *\n *   // anywhere:\n *   const unread = await inApp.listUnread(user);\n *   const badge  = await inApp.countUnread(user);\n *   const one    = await inApp.find(user, \"ntf_123\");\n *   await inApp.markAsRead(user, \"ntf_123\");\n *   await inApp.markAsRead(user);            // mark ALL unread\n *   await inApp.dismiss(user, \"ntf_123\");    // delete one\n *   await inApp.dismiss(user);               // clear all for this recipient\n */\nimport type { TypedRepositoryOptionsWithPages } from \"@warlock.js/core\";\nimport { databaseChannel } from \"../channels/database-channel\";\nimport type { Channel } from \"../contracts\";\nimport type { DatabasePayload, Id, Notifiable } from \"../types\";\nimport {\n  BaseNotificationsRepository,\n  type NotificationModelClass,\n  type NotificationsFilter,\n} from \"./base-notifications-repository\";\n\n/**\n * List options for the read methods — the repository's paginated options\n * (`page` / `limit` / `orderBy` / filter keys). `recipientId` is always forced\n * from the recipient argument, so passing it here has no effect.\n */\nexport type NotificationsListOptions = TypedRepositoryOptionsWithPages<NotificationsFilter>;\n\n/**\n * Configure options — discriminated so `model` and `repository` are mutually\n * exclusive at the type level. Future knobs (cache / realtime / naming) slot\n * in here without breaking the signature.\n */\nexport type ConfigureOptions =\n  | { model: NotificationModelClass; repository?: never }\n  | { repository: BaseNotificationsRepository; model?: never };\n\nconst idOf = (recipient: Notifiable | Id): Id =>\n  typeof recipient === \"object\" ? recipient.id : recipient;\n\nclass InApp {\n  private repo?: BaseNotificationsRepository;\n\n  /**\n   * Bind the in-app store AND return the `database` channel. Called once\n   * from `config/notifications.ts`. Subsequent calls REPLACE the binding.\n   */\n  public configure(options: ConfigureOptions): Channel<DatabasePayload> {\n    this.repo =\n      \"repository\" in options && options.repository\n        ? options.repository\n        : new BaseNotificationsRepository(options.model);\n    return databaseChannel(this.repo);\n  }\n\n  private get repository(): BaseNotificationsRepository {\n    if (!this.repo) {\n      throw new Error(\n        \"In-app notifications not configured — add \" +\n          \"`database: inApp.configure({ model: Notification })` to config/notifications.ts\",\n      );\n    }\n    return this.repo;\n  }\n\n  /** General list — scoped to recipient; pass `options` for paging + filters. */\n  public list(recipient: Notifiable | Id, options?: NotificationsListOptions) {\n    return this.repository.list({ ...options, recipientId: idOf(recipient) });\n  }\n\n  /** Unread-only list — common case for badges + dashboards. */\n  public listUnread(recipient: Notifiable | Id, options?: NotificationsListOptions) {\n    return this.repository.list({ ...options, recipientId: idOf(recipient), unread: true });\n  }\n\n  /**\n   * Cached unread count — backs the badge. Auto-invalidated by the repo's\n   * model create/update events (`RepositoryManager.registerEvents`).\n   */\n  public countUnread(recipient: Notifiable | Id) {\n    return this.repository.countCached({ recipientId: idOf(recipient), unread: true });\n  }\n\n  /** Find one notification for a recipient — for a detail view. */\n  public find(recipient: Notifiable | Id, id: Id) {\n    return this.repository.findFor(idOf(recipient), id);\n  }\n\n  /** Mark read — `id` omitted = all unread for this recipient. */\n  public markAsRead(recipient: Notifiable | Id, id?: Id) {\n    return this.repository.markRead(idOf(recipient), id);\n  }\n\n  /** Mark unread — same recipient-scoping. */\n  public markAsUnread(recipient: Notifiable | Id, id?: Id) {\n    return this.repository.markUnread(idOf(recipient), id);\n  }\n\n  /** Delete/dismiss — `id` omitted = clear all for this recipient. */\n  public dismiss(recipient: Notifiable | Id, id?: Id) {\n    return this.repository.deleteFor(idOf(recipient), id);\n  }\n}\n\n/** Single in-app facade — bound at boot via `inApp.configure(...)`. */\nexport const inApp = new InApp();\n","/**\n * Column factory for the notifications table, driven by the model's\n * `columnMap`. The recipient / read-state / tenant columns take the NAMES the\n * model declares, so the table, the repository, and the model accessors all\n * agree by construction. The stable lexical columns (`type`, `title`, `body`,\n * `payload`, `idempotency_key`) keep fixed names.\n *\n * Read-state follows the map's presence rules (see `NotificationColumnMap`):\n * `readAt` → a nullable timestamp, `isRead` → an indexed boolean, both → both.\n *\n * Spread + extend for app-specific extras (FK references, composite indexes).\n *\n * @example\n *   import { Migration } from \"@warlock.js/cascade\";\n *   import { notificationColumns } from \"@warlock.js/notifications\";\n *   import { Notification } from \"../notification.model\";\n *\n *   export default Migration.create(Notification, notificationColumns(Notification));\n *\n * @example  // with extras (multi-tenant FK)\n *   export default Migration.create(Notification, {\n *     ...notificationColumns(Notification),\n *     organization_id: uuid().references(Organization.table).notNullable(),\n *   });\n */\nimport { boolCol, type ColumnMap, json, string, text, timestamp, uuid } from \"@warlock.js/cascade\";\nimport { type NotificationColumnMapHost, resolveColumnMap } from \"../in-app/column-map\";\n\n/**\n * Returns the column map for a notification table, named from the model's\n * `columnMap`. Without a model, falls back to the resolved defaults\n * (`user_id` recipient + `read_at` read-state).\n */\nexport function notificationColumns(model?: NotificationColumnMapHost): ColumnMap {\n  const { recipient, tenant, readAt, isRead } = resolveColumnMap(model?.columnMap);\n\n  const columns: ColumnMap = {\n    [recipient]: uuid().index().notNullable(),\n    type: string().index().notNullable(),\n    title: string().notNullable(),\n    body: text().nullable(),\n    payload: json().nullable(),\n  };\n\n  if (readAt) {\n    columns[readAt] = timestamp().nullable();\n  }\n\n  if (isRead) {\n    columns[isRead] = boolCol().default(false).index();\n  }\n\n  if (tenant) {\n    columns[tenant] = uuid().index().notNullable();\n  }\n\n  // Unique so a retried send can't insert a duplicate. Nullable: most rows\n  // have no key (NULLs are distinct in a unique index). `createFor` does\n  // find-or-create; this constraint is the race backstop.\n  columns.idempotency_key = string().nullable().unique();\n\n  return columns;\n}\n","/**\n * Lazily load `@warlock.js/herald` — the optional peer backing `.queue()`.\n *\n * Memoized: the dynamic import runs at most once. A missing package surfaces a\n * curated install message at use time rather than a boot-time resolution error\n * (the lazy-optional-peer pattern used across the framework's drivers).\n */\nlet heraldModule: typeof import(\"@warlock.js/herald\") | undefined;\n\nconst INSTALL_INSTRUCTIONS = `\nThe notifications queue requires the @warlock.js/herald package.\nInstall it with:\n\n  npm install @warlock.js/herald\n\nOr with your preferred package manager:\n\n  pnpm add @warlock.js/herald\n  yarn add @warlock.js/herald\n`.trim();\n\nexport async function loadHerald(): Promise<typeof import(\"@warlock.js/herald\")> {\n  if (heraldModule) {\n    return heraldModule;\n  }\n\n  try {\n    heraldModule = await import(\"@warlock.js/herald\");\n    return heraldModule;\n  } catch {\n    throw new Error(INSTALL_INSTRUCTIONS);\n  }\n}\n\n/** The herald channel notification jobs are published to / consumed from. */\nexport const DEFAULT_QUEUE_CHANNEL = \"notifications.dispatch\";\n","/**\n * Herald-backed `QueueDispatcher` — the production backend for `.queue()`.\n *\n * `defineNotification` renders payloads + resolves routes BEFORE handing a job\n * to the dispatcher, so the job is fully serializable (`{ channel, route,\n * payload, options }`) — no model re-hydration or closure serialization. This\n * dispatcher just publishes the job onto a herald channel; the worker\n * (`startNotificationsWorker`) consumes it and runs `channel.send`.\n *\n * Connection is the app's job — the `@warlock.js/herald` connector connects the\n * broker from `config/herald.ts` at boot; this dispatcher only calls `herald()`.\n *\n * @example  src/config/notifications.ts — declarative; the connector registers it\n *   import { type NotificationConfig, heraldQueue, inApp, mailChannel } from \"@warlock.js/notifications\";\n *\n *   const config: NotificationConfig = {\n *     channels: { mail: mailChannel(), database: inApp.configure({ model: Notification }) },\n *     queue: heraldQueue(),                 // → `.queue()` now works\n *   };\n *\n *   export default config;\n */\nimport type { QueueDispatcher } from \"../contracts\";\nimport { DEFAULT_QUEUE_CHANNEL, loadHerald } from \"./load-herald\";\n\nexport type HeraldQueueOptions = {\n  /** Herald channel to publish jobs to. Default `\"notifications.dispatch\"`. */\n  channel?: string;\n  /** Herald broker name (multi-broker setups). Default broker if omitted. */\n  broker?: string;\n};\n\nexport function heraldQueue(options: HeraldQueueOptions = {}): QueueDispatcher {\n  const channelName = options.channel ?? DEFAULT_QUEUE_CHANNEL;\n\n  return {\n    async dispatch(job) {\n      const { herald } = await loadHerald();\n      await herald(options.broker).channel(channelName).publish(job);\n    },\n  };\n}\n","/**\n * Notification queue worker — consumes the jobs `heraldQueue` publishes and\n * runs the actual `channel.send`. Call this once in a worker process (or the\n * web process) after the notifications config + herald broker are up.\n *\n * The job carries an ALREADY-RENDERED payload + resolved route, so the worker\n * only looks the channel up by name and dispatches — no notifiable needed.\n *\n * @example\n *   // in a worker entrypoint, after the notifications config + broker are up\n *   import { startNotificationsWorker } from \"@warlock.js/notifications\";\n *   await startNotificationsWorker();\n */\nimport { log } from \"@warlock.js/logger\";\nimport { getNotificationConfig } from \"../config\";\nimport type { ChannelName, SendOptions } from \"../types\";\nimport { DEFAULT_QUEUE_CHANNEL, loadHerald } from \"./load-herald\";\n\ntype NotificationJob = {\n  channel: string;\n  route: unknown;\n  payload: unknown;\n  options: SendOptions;\n};\n\nexport type WorkerOptions = {\n  /** Herald channel to consume from. Must match the dispatcher. Default `\"notifications.dispatch\"`. */\n  channel?: string;\n  /** Herald broker name. Default broker if omitted. */\n  broker?: string;\n};\n\nexport async function startNotificationsWorker(options: WorkerOptions = {}): Promise<void> {\n  const channelName = options.channel ?? DEFAULT_QUEUE_CHANNEL;\n  const { herald } = await loadHerald();\n\n  await herald(options.broker)\n    .channel(channelName)\n    .subscribe(async (message: { payload: unknown }, ctx: { ack(): Promise<void> }) => {\n      const job = message.payload as NotificationJob;\n\n      try {\n        const channel = getNotificationConfig().channels[job.channel as ChannelName];\n\n        if (!channel) {\n          // Unknown channel — log and ack to avoid a poison-message loop.\n          log.error(\"notifications\", \"queue.worker\", `Unknown channel \"${job.channel}\" — dropping job`);\n          await ctx.ack();\n          return;\n        }\n\n        await channel.send({\n          payload: job.payload as never,\n          route: job.route as never,\n          options: job.options,\n        });\n\n        await ctx.ack();\n      } catch (error) {\n        // Phase 2: log + ack (no retry/DLQ yet — that lands with the\n        // delay-aware worker). A dead-letter strategy is a follow-up.\n        log.error(\"notifications\", \"queue.worker\", error as Error);\n        await ctx.ack();\n      }\n    });\n}\n","/**\n * Thrown when the `bullmq` queue driver (`bullmqQueue()`) is configured but\n * `@warlock.js/queue` — an optional peer — isn't installed. Surfaced at USE\n * time (first `.queue()` call), not at boot, matching the lazy-optional-peer\n * pattern `loadHerald` already uses for `@warlock.js/herald`.\n */\n\nfunction captureStack(target: object, ctor: new (...args: any[]) => unknown): void {\n  // V8 only; guarded for non-V8 runtimes.\n  (Error as unknown as { captureStackTrace?: (t: object, c: unknown) => void }).captureStackTrace?.(\n    target,\n    ctor,\n  );\n}\n\nexport class QueuePackageNotInstalledError extends Error {\n  public constructor() {\n    super(\n      \"The bullmq notifications queue driver requires @warlock.js/queue, which isn't installed. \" +\n        \"Install it with:\\n\\n\" +\n        \"  warlock add queue\\n\\n\" +\n        \"Or with your preferred package manager:\\n\\n\" +\n        \"  pnpm add @warlock.js/queue\\n\" +\n        \"  npm install @warlock.js/queue\\n\" +\n        \"  yarn add @warlock.js/queue\",\n    );\n    this.name = \"QueuePackageNotInstalledError\";\n    captureStack(this, QueuePackageNotInstalledError);\n  }\n}\n","/**\n * Lazily load `@warlock.js/queue` — the optional peer backing the `bullmq`\n * queue driver (`bullmqQueue()`). Mirrors `loadHerald`'s lazy-optional-peer\n * pattern: the dynamic import runs at most once and is memoized, and a\n * missing package surfaces a curated, named error at USE time rather than a\n * boot-time resolution error.\n *\n * The `loader` parameter exists so specs can inject a failing import without\n * needing the real package installed or uninstalled.\n */\nimport { QueuePackageNotInstalledError } from \"./queue-package-not-installed.error\";\n\n/** A function that performs the dynamic import of `@warlock.js/queue`. */\nexport type QueuePackageLoader = () => Promise<typeof import(\"@warlock.js/queue\")>;\n\nconst defaultLoader: QueuePackageLoader = () => import(\"@warlock.js/queue\");\n\nlet queueModule: typeof import(\"@warlock.js/queue\") | undefined;\n\n/**\n * Resolve `@warlock.js/queue`, memoized after the first successful load.\n * Throws {@link QueuePackageNotInstalledError} when the import fails.\n */\nexport async function loadQueuePackage(\n  loader: QueuePackageLoader = defaultLoader,\n): Promise<typeof import(\"@warlock.js/queue\")> {\n  if (queueModule) {\n    return queueModule;\n  }\n\n  try {\n    queueModule = await loader();\n    return queueModule;\n  } catch {\n    throw new QueuePackageNotInstalledError();\n  }\n}\n","/**\n * BullMQ-backed `QueueDispatcher` — an alternative to `heraldQueue()` for apps\n * that already run `@warlock.js/queue` and would rather deliver notifications\n * through it than stand up a herald broker.\n *\n * `defineNotification` renders the payload + resolves the route BEFORE\n * handing a job to the dispatcher, so the job (`{ channel, route, payload,\n * options }`) is plain JSON — the same contract `heraldQueue` relies on.\n *\n * `@warlock.js/queue` is an OPTIONAL peer, lazy-loaded via `loadQueuePackage`\n * on first `.dispatch()` — never at import time — so notifications never pays\n * for `@warlock.js/queue` unless `bullmqQueue()` is actually configured.\n *\n * @example  src/config/notifications.ts — declarative; the connector registers it\n *   import { type NotificationConfig, bullmqQueue, mailChannel } from \"@warlock.js/notifications\";\n *\n *   const config: NotificationConfig = {\n *     channels: { mail: mailChannel() },\n *     queue: bullmqQueue({ attempts: 3, backoff: { type: \"exponential\", delay: 5000 } }),\n *   };\n *\n *   export default config;\n */\nimport type { JobBackoff } from \"@warlock.js/queue\";\nimport type { QueueDispatcher } from \"../contracts\";\nimport { getNotificationConfig } from \"../config\";\nimport type { ChannelName, SendOptions } from \"../types\";\nimport { loadQueuePackage } from \"./load-queue\";\n\n/** The job name notification deliveries run under. */\nexport const NOTIFICATION_JOB_NAME = \"warlock.notifications.deliver\";\n\nexport type BullmqQueueOptions = {\n  /** Queue to deliver on. Default: `@warlock.js/queue`'s default queue. */\n  queue?: string;\n  /** Attempts per delivery. Default: `queue.defaultJobOptions.attempts`, else `1`. */\n  attempts?: number;\n  /** Backoff between attempts. */\n  backoff?: JobBackoff;\n};\n\ntype NotificationJobPayload = {\n  channel: string;\n  route: unknown;\n  payload: unknown;\n  options: SendOptions;\n};\n\n/**\n * Create the `QueueDispatcher` for `NotificationConfig.queue` backed by\n * `@warlock.js/queue`.\n *\n * - `SendOptions.delay` is honoured: a number is SECONDS (notifications'\n *   convention), a string is a duration such as `\"10m\"` (`@warlock.js/queue`'s\n *   convention).\n * - A channel missing from the worker's notifications config throws — the\n *   delivery is retried per `attempts` / `backoff` like any other failure.\n */\nexport function bullmqQueue(options: BullmqQueueOptions): QueueDispatcher {\n  let jobPromise: ReturnType<typeof defineNotificationJob> | undefined;\n\n  return {\n    async dispatch(job) {\n      if (!jobPromise) {\n        jobPromise = defineNotificationJob(options);\n      }\n\n      const notificationJob = await jobPromise;\n\n      await notificationJob.dispatch(\n        { channel: job.channel, route: job.route, payload: job.payload, options: job.options },\n        { delay: job.options.delay === undefined ? undefined : notificationDelay(job.options.delay) },\n      );\n    },\n  };\n}\n\nasync function defineNotificationJob(options: BullmqQueueOptions) {\n  const { defineJob } = await loadQueuePackage();\n\n  return defineJob<NotificationJobPayload, void>({\n    name: NOTIFICATION_JOB_NAME,\n    queue: options.queue,\n    attempts: options.attempts,\n    backoff: options.backoff,\n    async handle(job) {\n      const channel = getNotificationConfig().channels[job.channel as ChannelName];\n\n      if (!channel) {\n        throw new Error(\n          `Notification channel \"${job.channel}\" is not configured in this worker's notifications config.`,\n        );\n      }\n\n      await channel.send({ payload: job.payload, route: job.route, options: job.options } as never);\n    },\n  });\n}\n\nfunction notificationDelay(delay: number | string): number {\n  return typeof delay === \"number\" ? delay * 1_000 : toMilliseconds(delay);\n}\n\n// `toMilliseconds` mirrors `@warlock.js/queue`'s duration parser but is\n// inlined here so this file doesn't need a non-lazy import from the optional\n// peer just to convert a string like \"10m\".\nfunction toMilliseconds(value: string): number {\n  const units: Record<string, number> = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 };\n  const match = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/.exec(value.trim());\n\n  if (!match) {\n    throw new Error(\n      `Invalid duration \"${value}\": expected a number of milliseconds or a string like ` +\n        `\"500ms\", \"30s\", \"10m\", \"2h\", \"1d\".`,\n    );\n  }\n\n  return Math.round(Number(match[1]) * units[match[2]!]!);\n}\n"],"mappings":";;;;;;;;;;;;;;;AASA,SAASA,eAAa,QAAgB,MAA6C;CAEjF,AAAC,MAA6E,oBAC5E,QACA,IACF;AACF;AAEA,IAAa,kCAAb,MAAa,wCAAwC,MAAM;CACzD,AAAO,cAAc;EACnB,MACE,oJAEF;EACA,KAAK,OAAO;EACZ,eAAa,MAAM,+BAA+B;CACpD;AACF;AAEA,IAAa,uBAAb,MAAa,6BAA6B,MAAM;CAC9C,AAAO,YAAY,aAAqB;EACtC,MACE,YAAY,YAAY,wFAE1B;EACA,KAAK,OAAO;EACZ,eAAa,MAAM,oBAAoB;CACzC;AACF;AAEA,IAAa,yBAAb,MAAa,+BAA+B,MAAM;CAChD,AAAO,YAAY,aAAqB;EACtC,MACE,4BAA4B,YAAY,qIAE1C;EACA,KAAK,OAAO;EACZ,eAAa,MAAM,sBAAsB;CAC3C;AACF;AAEA,IAAa,uBAAb,MAAa,6BAA6B,MAAM;CAC9C,AAAO,YAAY,kBAA0B,aAAqB;EAChE,MACE,iBAAiB,iBAAiB,uBAAuB,YAAY,iCACvC,YAAY,aAC5C;EACA,KAAK,OAAO;EACZ,eAAa,MAAM,oBAAoB;CACzC;AACF;;;;;;AAOA,IAAa,yBAAb,MAAa,+BAA+B,MAAM;CAChD,AAAO,YAAY,aAAqB,aAAuB;EAC7D,MACE,YAAY,YAAY,4CACnB,eAAe,YAAY,iHAElC;EACA,KAAK,OAAO;EACZ,eAAa,MAAM,sBAAsB;CAC3C;AACF;;;;AClBA,IAAI;;;;;;;AAQJ,SAAgB,sBAAsB,QAAkC;CACtE,eAAe;AACjB;;;;;AAMA,SAAgB,wBAA4C;CAC1D,IAAI,CAAC,cACH,MAAM,IAAI,gCAAgC;CAE5C,OAAO;AACT;;;;;AAMA,SAAgB,0BAAgC;CAC9C,eAAe;AACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtDA,SAAgB,cAAiB,SAAiC;CAChE,OAAO;AACT;;;;;;;;;AC5BA,SAAgB,gBAAwB;CACtC,mCAAkB;AACpB;;;;;;;;;;;;;;;;;;;ACYA,MAAM,WAAkD;CACtD,yBAAS,IAAI,IAAI;CACjB,sBAAM,IAAI,IAAI;CACd,wBAAQ,IAAI,IAAI;CAChB,yBAAS,IAAI,IAAI;AACnB;;;;;;;AAQA,MAAa,gBAAgB;CAC3B,GAAwB,OAAU,SAAiC;EACjE,SAAS,MAAM,CAAC,IAAI,OAA6B;EACjD,aAAa;GACX,SAAS,MAAM,CAAC,OAAO,OAA6B;EACtD;CACF;CACA,IAAyB,OAAU,SAA2B;EAC5D,SAAS,MAAM,CAAC,OAAO,OAA6B;CACtD;AACF;;;;;;AAOA,eAAsB,KACpB,OACA,MACe;CACf,MAAM,MAAM,SAAS;CACrB,IAAI,IAAI,SAAS,GAAG;CAKpB,MAAM,UAAU,MAAM,QAAQ,WAC5B,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,OAAO,YAAY;EACrC,MAAO,QAAuB,IAAI;CACpC,CAAC,CACH;CAEA,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,WAAW,YACpB,uBAAI,MAAM,iBAAiB,iBAAiB,OAAO,MAAM;AAG/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3BA,eAAsB,gBAAgB,MAA0C;CAC9E,MAAM,SAAS,sBAAsB;CACrC,MAAM,UAAU,OAAO,SAAS,KAAK;CAErC,IAAI,CAAC,SACH,MAAM,IAAI,qBAAqB,KAAK,WAAW;CAKjD,MAAM,aAAa,cAAc;CASjC,IAAI,KAAK,MAAM,KAAK,oBAAoB,OAAO,WAO7C;MAAI,CAAC,MANiB,OAAO,UAAU,MACrC,KAAK,IACL,KAAK,aACL,KAAK,gBACP,GAEc;GACZ,MAAM,KAAK,WAAW;IACpB;IACA,SAAS,KAAK;IACd,YAAY,KAAK;IACjB,QAAQ;IACR,SAAS,KAAK;GAChB,CAAC;GACD;EACF;;CAGF,MAAM,QAAQ,aAAa,MAAM,QAAQ,KAAK;CAG9C,IAAI,UAAU,KAAK;CACnB,IACE,KAAK,gBAAgB,cACrB,KAAK,QAAQ,kBACb,WACA,OAAO,YAAY,UAEnB,UAAU;EAAE,GAAI;EAAoB,gBAAgB,KAAK,QAAQ;CAAe;CAMlF,MAAM,KAAK,WAAW;EACpB;EACA,SAAS,KAAK;EACd,YAAY,KAAK;EACjB;EACA,SAAS,KAAK;CAChB,CAAC;CAED,MAAM,YAAY,KAAK,IAAI;CAE3B,IAAI;EACF,IAAI,KAAK,SAAS,SAAS;GACzB,IAAI,CAAC,OAAO,OACV,MAAM,IAAI,uBAAuB,KAAK,WAAW;GAEnD,MAAM,OAAO,MAAM,SAAS;IAC1B,SAAS,KAAK;IACd;IACA;IACA,SAAS,KAAK;GAChB,CAAC;EACH,OACE,MAAM,QAAQ,KAAK;GACR;GACT;GACA,YAAY,KAAK;GACjB,SAAS,KAAK;EAChB,CAAC;EAGH,MAAM,KAAK,QAAQ;GACjB;GACA,SAAS,KAAK;GACd,YAAY,KAAK;GACjB;GACA,SAAS,KAAK;GACd,YAAY,KAAK,IAAI,IAAI;EAC3B,CAAC;CACH,SAAS,OAAO;EACd,MAAM,KAAK,UAAU;GACnB;GACA,SAAS,KAAK;GACd,YAAY,KAAK;GACjB;GACO;GACP,SAAS,KAAK;GACd,YAAY,KAAK,IAAI,IAAI;EAC3B,CAAC;EACD,MAAM;CACR;AACF;;;;;;;;;;;;AAaA,SAAS,aACP,MACA,UACqB;CACrB,IAAI,KAAK,aAAa,QACpB,OAAO,KAAK;CAGd,IAAI,CAAC,KAAK,IACR,MAAM,IAAI,uBAAuB,KAAK,WAAW;CAInD,IAAI,CAAC,UACH,OAAO,EAAE,IAAI,KAAK,GAAG,GAAG;CAG1B,MAAM,WAAW,SAAS,KAAK,EAAE;CAEjC,IAAI,aAAa,QACf,MAAM,IAAI,uBAAuB,KAAK,aAAa,KAAK,GAAG,EAAE;CAG/D,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7GA,SAAS,cAAc,OAAyB;CAC9C,OACE,iBAAiB,wBACjB,iBAAiB,0BACjB,iBAAiB;AAErB;AAEA,SAAgB,mBAAyB,KAAuD;CAC9F,MAAM,QAAQ,aAAwD;EAGpE,IAAI,MAAM,QAAQ,IAAI,GAAG,GAAG;GAC1B,MAAM,WAAW,WAAW,IAAI,IAAI,QAAQ,MAAM,SAAS,SAAS,CAAC,CAAC,IAAI,IAAI;GAC9E,KAAK,MAAM,WAAW,UACpB,IAAI,CAAC,IAAI,UACP,MAAM,IAAI,qBAAqB,IAAI,MAAM,OAAO;EAGtD;EAEA,OAAO;GACL,KAAK,IAAI,MAAM,SAAS;IACtB,OAAO,YAAY,KAAK,IAAI,MAAM,WAAW,CAAC,GAAG,QAAQ,QAAQ;GACnE;GACA,MAAM,IAAI,MAAM,SAAS;IACvB,OAAO,YAAY,KAAK,IAAI,MAAM,WAAW,CAAC,GAAG,SAAS,QAAQ;GACpE;GACA,KAAK,GAAG,UAAU;IAChB,OAAO,KAAK,QAAQ;GACtB;EACF;CACF;CAEA,OAAO,KAAK;AACd;;;;;;AAOA,eAAe,YACb,KACA,IACA,MACA,SACA,MACA,UACe;CACf,MAAM,aAAa,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE;CAQ/C,oBAAoB,MANE,QAAQ,WAC5B,WAAW,KAAK,cACd,oBAAoB,KAAK,WAAW,MAAM,SAAS,MAAM,QAAQ,CACnE,CACF,CAE2B;AAC7B;AAEA,eAAe,oBACb,KACA,IACA,MACA,SACA,MACA,UACe;CACf,MAAM,SAAS,sBAAsB;CAIrC,IAAI;CACJ,IAAI;EACF,WAAW,OAAO,IAAI,QAAQ,aAAa,IAAI,IAAI,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG;EAE1E,IAAI,UACF,WAAW,SAAS,QAAQ,MAAM,SAAS,SAAS,CAAC,CAAC;EAGxD,IAAI,CAAC,QAAQ,SAAS,OAAO,aAAa;GACxC,MAAM,UAAU,MAAM,OAAO,YAAY,gBAAgB,IAAI,IAAI,MAAM,QAAQ;GAC/E,KAAK,MAAM,WAAW,SAAS,QAAQ,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC,GAC/D,MAAM,KAAK,WAAW;IACpB,YAAY,cAAc;IAC1B;IACA,YAAY;IACZ,QAAQ;IACR;GACF,CAAC;GAEH,WAAW,SAAS,QAAQ,MAAM,QAAQ,SAAS,CAAC,CAAC;EACvD;CACF,SAAS,OAAO;EACd,uBAAI,MAAM,iBAAiB,WAAW,IAAI,QAAQ,KAAc;EAChE;CACF;CAEA,MAAM,YAA2B;EAAE,QAAQ,QAAQ;EAAQ,MAAM,QAAQ;CAAK;CA4B9E,oBAAoB,MA1BE,QAAQ,WAC5B,SAAS,KAAK,SAAS;EACrB,MAAM,WAAW,IAAI;EACrB,IAAI,CAAC,UAEH,OAAO,QAAQ,OAAO,IAAI,qBAAqB,IAAI,MAAM,IAAI,CAAC;EAGhE,IAAI,UAAmB,SAAS,MAAM,IAAI,SAAS;EAGnD,IAAI,SAAS,cAAc,WAAW,OAAO,YAAY,YAAY,EAAE,UAAU,UAC/E,UAAU;GAAE,GAAI;GAAoB,MAAM,IAAI;EAAK;EAGrD,OAAO,gBAAgB;GACrB,aAAa;GACb;GACA;GACA,kBAAkB,IAAI;GACtB;GACA;EACF,CAAC;CACH,CAAC,CACH,CAE2B;AAC7B;;AAGA,SAAS,oBAAoB,SAAgD;CAC3E,MAAM,eAAe,QAClB,QAAQ,MAAkC,EAAE,WAAW,cAAc,cAAc,EAAE,MAAM,CAAC,CAAC,CAC7F,KAAK,MAAM,EAAE,MAAM;CAEtB,IAAI,aAAa,WAAW,GAC1B,MAAM,aAAa;CAErB,IAAI,aAAa,SAAS,GACxB,MAAM,IAAI,eAAe,cAAc,4CAA4C;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnKA,eAAe,WACb,aACA,IACA,SACA,UAAuB,CAAC,GACT;CACf,MAAM,cAAc,OAAO,OAAO;CAClC,MAAM,aAAqC,cAAc,SAAY;CAIrE,IAAI,mBAAuC,QAAQ;CACnD,IACE,qBAAqB,UACrB,WACA,OAAO,YAAY,YACnB,UAAU,WACV,OAAQ,QAA8B,SAAS,UAE/C,mBAAoB,QAA6B;CAInD,IAAI,cAAc,oBAAoB,CAAC,QAAQ,OAAO;EACpD,MAAM,SAAS,sBAAsB;EAErC,IAAI,OAAO,aAOT;OAAI,EAAC,MANiB,OAAO,YAAY,gBACvC,YACA,kBACA,CAAC,WAA0B,CAC7B,EAEY,CAAC,SAAS,WAA0B,GAAG;IACjD,MAAM,KAAK,WAAW;KACpB,YAAY,cAAc;KAC1B,SAAS;KACT;KACA,QAAQ;KACR;IACF,CAAC;IACD;GACF;;CAEJ;CAEA,MAAM,gBAAgB;EACpB;EACA;EACA,IAAI;EACJ,UAAU,cAAc,KAAK;EAC7B;EACA,MAAM;EACN;CACF,CAAC;AACH;;;;;AAMA,MAAa,SAAiB,IAAI,MAAM,CAAC,GAAa,EACpD,IAAI,SAAS,MAAM;CACjB,IAAI,OAAO,SAAS,UAClB;CAMF,IAAI,SAAS,UAAU,SAAS,WAAW,SAAS,aAAa,SAAS,UACxE;CAGF,IAAI,SAAS,WACX,QAAQ,UAAkB,EACxB,OAAO,IAAyB,SAAkB,YAChD,WAAW,MAAM,IAAI,SAAS,OAAO,EACzC;CAGF,QAAQ,IAAyB,SAAkB,YACjD,WAAW,MAAM,IAAI,SAAS,OAAO;AACzC,EACF,CAAC;;;;ACjHD,SAAgB,gBACd,MAC0B;CAC1B,OAAO,cAA+B;EACpC,MAAM;EACN,QAAQ,gBAAgB,EAAE,IAAI,WAAW,GAAG;EAC5C,MAAM,KAAK,EAAE,SAAS,OAAO,cAAc;GACzC,MAAM,KAAS,OAAO,UAAU,WAAW,MAAM,KAAK;GAEtD,MAAM,EAAE,iBAAiB;GACzB,MAAM,WACJ,gBAAgB,aAAc,WAAW,IAAI,YAAY,IAAW;GAEtE,MAAM,KAAK,UAAU,IAAI,SAAS,QAAQ;EAC5C;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;ACVA,SAAgB,YAAY,SAA4B,CAAC,GAAyB;CAChF,MAAM,eAAe,OAAO,WAAW,MAAM,EAAE,IAAI,OAAO;CAE1D,OAAO,cAA2B;EAChC,MAAM;EACN,QAAQ,eAAe,aAAa,UAAU;EAC9C,MAAM,KAAK,EAAE,SAAS,SAAS;GAC7B,qCAAe;IACb,IAAI;IACJ,MAAM,OAAO;IACb,GAAG;GACL,CAAC;EACH;CACF,CAAC;AACH;;;;ACeA,MAAM,2BAA2B;AACjC,MAAM,yBAAyB;;;;;;;AAQ/B,SAAgB,iBACd,KAC+B;CAC/B,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,KAAK;CAEpB,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,QACxB,OAAO;EAAE;EAAW;EAAQ,QAAQ;CAAuB;CAG7D,OAAO;EACL;EACA;EACA,QAAQ,IAAI;EACZ,QAAQ,IAAI;CACd;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/BA,IAAa,8BAAb,cAEUC,mCAA+C;;CAEvD,AAAiB;CAEjB,AAAO;CAEP,AAAO,YAAY,OAAgC;EACjD,MAAM;EAEN,IAAI,OACF,KAAK,SAAS;EAGhB,KAAK,UAAU,iBAAiB,OAAO,SAAS;EAChD,KAAK,WAAW,KAAK,cAAc;CACrC;;CAGA,IAAW,eAAmC;EAC5C,OAAO,KAAK,QAAQ;CACtB;;;;;;CAOA,AAAQ,gBAA6B;EACnC,MAAM,EAAE,WAAW,QAAQ,QAAQ,WAAW,KAAK;EAEnD,MAAM,QAAqB;GACzB,IAAI;GACJ,MAAM;GACN,aAAa,CAAC,KAAK,SAAS;GAC5B,gBAAgB,CAAC,KAAK,iBAAiB;GACvC,SAAS,OAAO,UAAU;IACxB,IAAI,UAAU,QAAQ,UAAU,QAC9B;IAGF,IAAI,QACF,MAAM,MAAM,QAAQ,KAAK;SACpB,IAAI,QACT,MAAM,UAAU,MAAM;GAE1B;EACF;EAEA,IAAI,QACF,MAAM,SAAS,CAAC,KAAK,MAAM;EAG7B,IAAI,QACF,MAAM,SAAS,CAAC,KAAK,MAAM;EAG7B,IAAI,QACF,MAAM,SAAS,CAAC,KAAK,MAAM;EAG7B,OAAO;CACT;;;;;;;;CASA,AAAU,MAAM,KAAuD;EACrE,MAAM,QAAQ,KAAK;EACnB,MAAM,MAA+B,CAAC;EAEtC,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,GAAG,GAAG;GAClD,MAAM,OAAO,MAAM;GACnB,MAAM,SAAS,MAAM,QAAQ,IAAI,IAAK,KAAK,KAAgB;GAC3D,IAAI,UAAU;EAChB;EAEA,OAAO;CACT;;;;;;;;;;;;CAaA,AAAQ,qBAAqB,OAAiD;EAC5E,MAAM,EAAE,WAAW,QAAQ,QAAQ,WAAW,KAAK;EACnD,MAAM,cAAc,IAAI,IAAwB;GAC9C;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,OAAgC,CAAC;EAEvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,CAAC,YAAY,IAAI,GAAG,GACtB,KAAK,OAAO;EAIhB,OAAO;CACT;;;;;;;;;;;;CAaA,MAAa,UACX,aACA,OACA,UACiB;EACjB,MAAM,EAAE,mBAAmB;EAE3B,IAAI,gBAAgB;GAClB,MAAM,WAAW,MAAM,KAAK,sBAAsB,aAAa,cAAc;GAC7E,IAAI,UACF,OAAO;EAEX;EAEA,MAAM,MAA+B;GAAE,GAAG,KAAK,qBAAqB,KAAK;GAAG;EAAY;EAExF,IAAI,KAAK,QAAQ,QACf,IAAI,SAAS;EAGf,IAAI,KAAK,QAAQ,UAAU,aAAa,QACtC,IAAI,SAAS;EAGf,IAAI;GACF,OAAO,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,CAAC;EAC1C,SAAS,OAAO;GAEd,IAAI,gBAAgB;IAClB,MAAM,WAAW,MAAM,KAAK,sBAAsB,aAAa,cAAc;IAC7E,IAAI,UACF,OAAO;GAEX;GAEA,MAAM;EACR;CACF;;;;;;CAOA,AAAO,cACL,cACA,OACA,UACmB;EACnB,OAAO,QAAQ,IACb,aAAa,KAAK,gBAAgB,KAAK,UAAU,aAAa,OAAO,QAAQ,CAAC,CAChF;CACF;;;;;;;CAQA,AAAO,SAAS,aAAiB,IAA0B;EACzD,MAAM,EAAE,QAAQ,WAAW,KAAK;EAChC,MAAM,cAAc,SAAS,EAAE,QAAQ,MAAM,IAAI,EAAE,QAAQ,KAAK;EAEhE,MAAM,OAAgC,CAAC;EAEvC,IAAI,QACF,KAAK,SAAS;EAGhB,IAAI,QACF,KAAK,yBAAS,IAAI,KAAK;EAGzB,OAAO,KAAK,WACV,KAAK,MAAM;GAAE;GAAa,GAAG;GAAa,GAAI,OAAO,SAAY,EAAE,GAAG,IAAI,CAAC;EAAG,CAAC,GAC/E,KAAK,MAAM,IAAI,CACjB;CACF;;;;;CAMA,AAAO,WAAW,aAAiB,IAA0B;EAC3D,MAAM,EAAE,QAAQ,WAAW,KAAK;EAChC,MAAM,OAAgC,CAAC;EAEvC,IAAI,QACF,KAAK,SAAS;EAGhB,IAAI,QACF,KAAK,SAAS;EAGhB,OAAO,KAAK,WACV,KAAK,MAAM;GAAE;GAAa,GAAI,OAAO,SAAY,EAAE,GAAG,IAAI,CAAC;EAAG,CAAC,GAC/D,KAAK,MAAM,IAAI,CACjB;CACF;;CAGA,AAAO,QAAQ,aAAiB,IAAgC;EAC9D,OAAO,KAAK,MAAM;GAAE;GAAa;EAAG,CAAwB;CAC9D;;;;;CAMA,AAAO,UAAU,aAAiB,IAA0B;EAC1D,OAAO,KAAK,WAAW,KAAK,MAAM;GAAE;GAAa,GAAI,OAAO,SAAY,EAAE,GAAG,IAAI,CAAC;EAAG,CAAC,CAAC;CACzF;;CAGA,AAAQ,sBAAsB,aAAiB,gBAAgD;EAC7F,OAAO,KAAK,MAAM;GAAE;GAAa;EAAe,CAAwB;CAC1E;AACF;;;;;;;;;;;;;;;;;;;;;;;;ACtRA,IAAsB,uBAAtB,cAAmDC,0BAAsC;;;;;CAKvF,OAAc,YAAmC,CAAC;;CAGlD,IAAc,UAAyC;EACrD,OAAO,iBAAkB,KAAK,YAA4C,SAAS;CACrF;CAEA,IAAW,cAAkB;EAC3B,OAAO,KAAK,IAAI,KAAK,QAAQ,SAAS;CACxC;;CAGA,IAAW,WAA2B;EACpC,MAAM,EAAE,WAAW,KAAK;EACxB,OAAO,SAAS,KAAK,IAAI,MAAM,IAAI;CACrC;CAEA,IAAW,OAAe;EACxB,OAAO,KAAK,IAAI,MAAM;CACxB;CAEA,IAAW,SAAkB;EAC3B,MAAM,EAAE,QAAQ,WAAW,KAAK;EAEhC,IAAI,QACF,OAAO,QAAQ,KAAK,IAAI,MAAM,CAAC;EAGjC,OAAO,SAAS,KAAK,IAAI,MAAM,KAAK,OAAO;CAC7C;CAEA,IAAW,SAAsB;EAC/B,MAAM,EAAE,WAAW,KAAK;EACxB,OAAO,SAAU,KAAK,IAAI,MAAM,KAAK,OAAQ;CAC/C;;;;;;CAOA,MAAa,WAA0B;EACrC,MAAM,EAAE,QAAQ,WAAW,KAAK;EAEhC,IAAI,QACF,KAAK,IAAI,QAAQ,IAAI;EAGvB,IAAI,QACF,KAAK,IAAI,wBAAQ,IAAI,KAAK,CAAC;EAG7B,MAAM,KAAK,KAAK;CAClB;AACF;;;;ACjCA,MAAM,QAAQ,cACZ,OAAO,cAAc,WAAW,UAAU,KAAK;AAEjD,IAAM,QAAN,MAAY;CACV,AAAQ;;;;;CAMR,AAAO,UAAU,SAAqD;EACpE,KAAK,OACH,gBAAgB,WAAW,QAAQ,aAC/B,QAAQ,aACR,IAAI,4BAA4B,QAAQ,KAAK;EACnD,OAAO,gBAAgB,KAAK,IAAI;CAClC;CAEA,IAAY,aAA0C;EACpD,IAAI,CAAC,KAAK,MACR,MAAM,IAAI,MACR,2HAEF;EAEF,OAAO,KAAK;CACd;;CAGA,AAAO,KAAK,WAA4B,SAAoC;EAC1E,OAAO,KAAK,WAAW,KAAK;GAAE,GAAG;GAAS,aAAa,KAAK,SAAS;EAAE,CAAC;CAC1E;;CAGA,AAAO,WAAW,WAA4B,SAAoC;EAChF,OAAO,KAAK,WAAW,KAAK;GAAE,GAAG;GAAS,aAAa,KAAK,SAAS;GAAG,QAAQ;EAAK,CAAC;CACxF;;;;;CAMA,AAAO,YAAY,WAA4B;EAC7C,OAAO,KAAK,WAAW,YAAY;GAAE,aAAa,KAAK,SAAS;GAAG,QAAQ;EAAK,CAAC;CACnF;;CAGA,AAAO,KAAK,WAA4B,IAAQ;EAC9C,OAAO,KAAK,WAAW,QAAQ,KAAK,SAAS,GAAG,EAAE;CACpD;;CAGA,AAAO,WAAW,WAA4B,IAAS;EACrD,OAAO,KAAK,WAAW,SAAS,KAAK,SAAS,GAAG,EAAE;CACrD;;CAGA,AAAO,aAAa,WAA4B,IAAS;EACvD,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,GAAG,EAAE;CACvD;;CAGA,AAAO,QAAQ,WAA4B,IAAS;EAClD,OAAO,KAAK,WAAW,UAAU,KAAK,SAAS,GAAG,EAAE;CACtD;AACF;;AAGA,MAAa,QAAQ,IAAI,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1F/B,SAAgB,oBAAoB,OAA8C;CAChF,MAAM,EAAE,WAAW,QAAQ,QAAQ,WAAW,iBAAiB,OAAO,SAAS;CAE/E,MAAM,UAAqB;GACxB,0CAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,YAAY;EACxC,sCAAa,CAAC,CAAC,MAAM,CAAC,CAAC,YAAY;EACnC,uCAAc,CAAC,CAAC,YAAY;EAC5B,oCAAW,CAAC,CAAC,SAAS;EACtB,uCAAc,CAAC,CAAC,SAAS;CAC3B;CAEA,IAAI,QACF,QAAQ,6CAAoB,CAAC,CAAC,SAAS;CAGzC,IAAI,QACF,QAAQ,2CAAkB,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,MAAM;CAGnD,IAAI,QACF,QAAQ,wCAAe,CAAC,CAAC,MAAM,CAAC,CAAC,YAAY;CAM/C,QAAQ,kDAAyB,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO;CAErD,OAAO;AACT;;;;;;;;;;;ACvDA,IAAI;AAEJ,MAAM,uBAAuB;;;;;;;;;;EAU3B,KAAK;AAEP,eAAsB,aAA2D;CAC/E,IAAI,cACF,OAAO;CAGT,IAAI;EACF,eAAe,2CAAM;EACrB,OAAO;CACT,QAAQ;EACN,MAAM,IAAI,MAAM,oBAAoB;CACtC;AACF;;AAGA,MAAa,wBAAwB;;;;ACHrC,SAAgB,YAAY,UAA8B,CAAC,GAAoB;CAC7E,MAAM,cAAc,QAAQ;CAE5B,OAAO,EACL,MAAM,SAAS,KAAK;EAClB,MAAM,EAAE,WAAW,MAAM,WAAW;EACpC,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC,QAAQ,WAAW,CAAC,CAAC,QAAQ,GAAG;CAC/D,EACF;AACF;;;;;;;;;;;;;;;;;ACTA,eAAsB,yBAAyB,UAAyB,CAAC,GAAkB;CACzF,MAAM,cAAc,QAAQ;CAC5B,MAAM,EAAE,WAAW,MAAM,WAAW;CAEpC,MAAM,OAAO,QAAQ,MAAM,CAAC,CACzB,QAAQ,WAAW,CAAC,CACpB,UAAU,OAAO,SAA+B,QAAkC;EACjF,MAAM,MAAM,QAAQ;EAEpB,IAAI;GACF,MAAM,UAAU,sBAAsB,CAAC,CAAC,SAAS,IAAI;GAErD,IAAI,CAAC,SAAS;IAEZ,uBAAI,MAAM,iBAAiB,gBAAgB,oBAAoB,IAAI,QAAQ,iBAAiB;IAC5F,MAAM,IAAI,IAAI;IACd;GACF;GAEA,MAAM,QAAQ,KAAK;IACjB,SAAS,IAAI;IACb,OAAO,IAAI;IACX,SAAS,IAAI;GACf,CAAC;GAED,MAAM,IAAI,IAAI;EAChB,SAAS,OAAO;GAGd,uBAAI,MAAM,iBAAiB,gBAAgB,KAAc;GACzD,MAAM,IAAI,IAAI;EAChB;CACF,CAAC;AACL;;;;;;;;;;AC1DA,SAAS,aAAa,QAAgB,MAA6C;CAEjF,AAAC,MAA6E,oBAC5E,QACA,IACF;AACF;AAEA,IAAa,gCAAb,MAAa,sCAAsC,MAAM;CACvD,AAAO,cAAc;EACnB,MACE,4QAOF;EACA,KAAK,OAAO;EACZ,aAAa,MAAM,6BAA6B;CAClD;AACF;;;;;;;;;;;;;;ACdA,MAAM,sBAA0C,OAAO;AAEvD,IAAI;;;;;AAMJ,eAAsB,iBACpB,SAA6B,eACgB;CAC7C,IAAI,aACF,OAAO;CAGT,IAAI;EACF,cAAc,MAAM,OAAO;EAC3B,OAAO;CACT,QAAQ;EACN,MAAM,IAAI,8BAA8B;CAC1C;AACF;;;;;ACNA,MAAa,wBAAwB;;;;;;;;;;;AA4BrC,SAAgB,YAAY,SAA8C;CACxE,IAAI;CAEJ,OAAO,EACL,MAAM,SAAS,KAAK;EAClB,IAAI,CAAC,YACH,aAAa,sBAAsB,OAAO;EAK5C,OAAM,MAFwB,WAET,CAAC,SACpB;GAAE,SAAS,IAAI;GAAS,OAAO,IAAI;GAAO,SAAS,IAAI;GAAS,SAAS,IAAI;EAAQ,GACrF,EAAE,OAAO,IAAI,QAAQ,UAAU,SAAY,SAAY,kBAAkB,IAAI,QAAQ,KAAK,EAAE,CAC9F;CACF,EACF;AACF;AAEA,eAAe,sBAAsB,SAA6B;CAChE,MAAM,EAAE,cAAc,MAAM,iBAAiB;CAE7C,OAAO,UAAwC;EAC7C,MAAM;EACN,OAAO,QAAQ;EACf,UAAU,QAAQ;EAClB,SAAS,QAAQ;EACjB,MAAM,OAAO,KAAK;GAChB,MAAM,UAAU,sBAAsB,CAAC,CAAC,SAAS,IAAI;GAErD,IAAI,CAAC,SACH,MAAM,IAAI,MACR,yBAAyB,IAAI,QAAQ,2DACvC;GAGF,MAAM,QAAQ,KAAK;IAAE,SAAS,IAAI;IAAS,OAAO,IAAI;IAAO,SAAS,IAAI;GAAQ,CAAU;EAC9F;CACF,CAAC;AACH;AAEA,SAAS,kBAAkB,OAAgC;CACzD,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAQ,eAAe,KAAK;AACzE;AAKA,SAAS,eAAe,OAAuB;CAC7C,MAAM,QAAgC;EAAE,IAAI;EAAG,GAAG;EAAO,GAAG;EAAQ,GAAG;EAAW,GAAG;CAAW;CAChG,MAAM,QAAQ,gCAAgC,KAAK,MAAM,KAAK,CAAC;CAE/D,IAAI,CAAC,OACH,MAAM,IAAI,MACR,qBAAqB,MAAM,yFAE7B;CAGF,OAAO,KAAK,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM,MAAM,GAAK;AACxD"}