@palbase/backend 7.0.0 → 8.0.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.
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/{endpoint-BFgsOTiL.d.cts → endpoint-cDQyI6jH.d.cts} +33 -4
- package/dist/{endpoint-BFgsOTiL.d.ts → endpoint-cDQyI6jH.d.ts} +33 -4
- package/dist/{index-CE31P7Dt.d.cts → index-B-FNCK84.d.cts} +1 -1
- package/dist/{index-E7CMoir3.d.ts → index-BfnIO5G-.d.ts} +1 -1
- package/dist/index.cjs +18 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -7
- package/dist/index.d.ts +4 -7
- package/dist/index.js +18 -5
- package/dist/index.js.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db/env-gen.ts","../src/config/storage.ts","../src/config/notifications.ts","../src/config/flags.ts","../src/decorators/controller.ts","../src/decorators/registry.ts","../src/decorators/methods.ts","../src/decorators/params.ts","../src/middleware.ts","../src/errors.ts","../src/error-registry.ts","../src/worker.ts","../src/job.ts","../src/webhook.ts","../src/resource.ts","../src/hooks.ts","../src/index.ts"],"sourcesContent":["/**\n * env-gen.ts — generate the `palbase-env.d.ts` text from a `defineSchema()`\n * result.\n *\n * The CLI (`palbase serve` / codegen) and the deploy pipeline call\n * {@link makeEnvDts} with the project's schema and write the returned string to\n * `palbase-env.d.ts` at the project root. That file AUGMENTS the\n * `@palbase/backend/env` `Tables` interface (controlled global augmentation,\n * C5) so `Database.tables.<name>` is typed with no import and no generic.\n *\n * The output is FLAT: each table gets a `{ row: {...}; insert: {...} }` entry\n * with plain TypeScript object types. The phantom `ColumnBuilder<...>` type\n * NEVER appears in the generated `.d.ts` — that type only lives at authoring\n * time inside `db/schema.ts`.\n */\n\nimport type { ColumnDef } from \"./columns.js\";\nimport type { SchemaDef, TableDef } from \"./schema.js\";\n\n/** The TypeScript value type for a column, ignoring nullability (added by the\n * caller). Mirrors the `ColValue` mapped type in columns.ts exactly. */\nfunction baseTsType(def: ColumnDef): string {\n switch (def.type) {\n case \"uuid\":\n case \"text\":\n case \"timestamp\":\n return \"string\";\n case \"integer\":\n return \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"jsonb\":\n return \"unknown\";\n case \"enum\": {\n const values = def.enumValues ?? [];\n if (values.length === 0) return \"string\";\n return values.map((v) => JSON.stringify(v)).join(\" | \");\n }\n default:\n return \"unknown\";\n }\n}\n\n/** The full row type for a column: base type, `| null` when nullable. */\nfunction rowType(def: ColumnDef): string {\n const base = baseTsType(def);\n return def.nullable ? `${base} | null` : base;\n}\n\n/** True when a column may be omitted on INSERT — nullable OR has any default.\n * Mirrors `ColIsOptionalOnInsert` in columns.ts. */\nfunction optionalOnInsert(def: ColumnDef): boolean {\n return (\n def.nullable === true ||\n def.defaultRandom === true ||\n def.defaultNow === true ||\n def.defaultValue !== undefined\n );\n}\n\n/** Emit the `row: {...}` and `insert: {...}` blocks for one table at the given\n * base indentation. */\nfunction tableBlock(table: TableDef, indent: string): string {\n const cols = Object.entries(table.columns);\n const rowLines = cols.map(([col, builder]) => {\n return `${indent} ${col}: ${rowType(builder._def)};`;\n });\n const insertLines = cols.map(([col, builder]) => {\n const def = builder._def;\n const opt = optionalOnInsert(def) ? \"?\" : \"\";\n return `${indent} ${col}${opt}: ${rowType(def)};`;\n });\n return [\n `${indent}${table.name}: {`,\n `${indent} row: {`,\n ...rowLines,\n `${indent} };`,\n `${indent} insert: {`,\n ...insertLines,\n `${indent} };`,\n `${indent}};`,\n ].join(\"\\n\");\n}\n\n/**\n * Generate the full `palbase-env.d.ts` text for a schema.\n *\n * @example\n * import { makeEnvDts } from \"@palbase/backend\";\n * import schema from \"./db/schema.js\";\n * writeFileSync(\"palbase-env.d.ts\", makeEnvDts(schema));\n */\nexport function makeEnvDts(schema: SchemaDef): string {\n const tableNames = Object.keys(schema.tables);\n const blocks = tableNames.map((name) => tableBlock(schema.tables[name]!, \" \"));\n const body = blocks.length > 0 ? `\\n${blocks.join(\"\\n\")}\\n ` : \"\";\n return `// AUTO-GENERATED by @palbase/backend — DO NOT EDIT.\n// Regenerated from db/schema.ts on every \\`palbase serve\\` / deploy.\n// Augments the @palbase/backend/env \\`Tables\\` interface so \\`Database.tables.*\\`\n// is typed with no import and no generic.\n\ndeclare module \"@palbase/backend/env\" {\n interface Tables {${body}}\n}\n\nexport {};\n`;\n}\n","/**\n * storage.ts — the storage-buckets config-as-code DSL.\n *\n * `defineStorage({ buckets })` is the first MODULE config-as-code surface (the\n * sibling of `db/schema.ts`'s `defineSchema`). A `config/storage.ts` file\n * default-exports a `defineStorage(...)` result; on deploy the br-pod evaluates\n * it to JSON and reconciles the declared buckets against the tenant's live\n * buckets via the Storage admin API (create missing, update changed; never\n * auto-delete — dropping a bucket is destructive and warned only).\n *\n * Buckets are DECLARATIVE: name + access + size/mime limits. The FILES inside a\n * bucket are runtime state (uploaded via the SDK), never in git.\n *\n * @example\n * import { defineStorage, bucket } from \"@palbase/backend\";\n *\n * export default defineStorage({\n * buckets: {\n * avatars: bucket({\n * public: true,\n * fileSizeLimit: \"5MB\",\n * allowedMimeTypes: [\"image/png\", \"image/jpeg\", \"image/webp\"],\n * }),\n * invoices: bucket({\n * public: false,\n * fileSizeLimit: \"20MB\",\n * allowedMimeTypes: [\"application/pdf\"],\n * }),\n * },\n * });\n *\n * The returned value is the EXACT JSON shape the runtime's generic config\n * extractor (`config_extract.js`) emits and the Go apply step parses:\n * { __config: \"storage\", buckets: { avatars: { public, fileSizeLimit, allowedMimeTypes }, ... } }\n * `fileSizeLimit` is always normalized to a number of bytes (or null when\n * omitted, meaning \"no limit\"); the apply step maps it to the Storage API's\n * `file_size_limit` field. `allowedMimeTypes` is the allowlist or null (any).\n */\n\n/** The discriminant written under `__config` so the eval/apply can tell which\n * module config a `config/*.ts` file describes. Storage is `\"storage\"`. */\nexport const STORAGE_CONFIG_KIND = \"storage\" as const;\n\n/**\n * The author-facing options for a single bucket.\n *\n * - `public`: when true the bucket is served without a signed URL. Defaults to\n * `false` (private — signed URLs only).\n * - `fileSizeLimit`: the per-object upload cap. Accepts a human string\n * (`\"5MB\"`, `\"20MB\"`, `\"1GB\"`) or a bare number of BYTES. Omit for no limit.\n * - `allowedMimeTypes`: an allowlist of MIME types accepted on upload. Omit to\n * allow any type. Each entry must look like `type/subtype` (e.g. `image/png`,\n * `application/pdf`, or a wildcard `image/*`).\n */\nexport interface BucketOptions {\n public?: boolean;\n fileSizeLimit?: string | number;\n allowedMimeTypes?: string[];\n}\n\n/**\n * The compiled, serializable bucket definition — the EXACT shape emitted to\n * JSON and consumed by the Go apply step.\n *\n * - `public`: always present (defaulted to `false`).\n * - `fileSizeLimit`: bytes as a number, or `null` for \"no limit\".\n * - `allowedMimeTypes`: the MIME allowlist, or `null` for \"any type\".\n */\nexport interface BucketDef {\n public: boolean;\n fileSizeLimit: number | null;\n allowedMimeTypes: string[] | null;\n}\n\n/** A storage config definition: the discriminant + a map of bucket name →\n * {@link BucketDef}. This is the value `defineStorage` returns and the runtime\n * config extractor serializes. */\nexport interface StorageConfig {\n __config: typeof STORAGE_CONFIG_KIND;\n buckets: Record<string, BucketDef>;\n}\n\n/** The author-facing input to `defineStorage`: a `buckets` map whose keys are\n * the bucket names and whose values are `bucket({...})` builders. */\nexport interface StorageInput {\n buckets: Record<string, BucketDef>;\n}\n\n// Powers of 1024 (binary, IEC). Storage's `file_size_limit` string parser\n// (the `bytes` npm lib, used by Supabase Storage) treats \"MB\"/\"GB\" as binary\n// multiples, so \"5MB\" = 5 * 1024 * 1024. We match that exactly so the limit a\n// user declares is the limit the bucket enforces.\nconst UNIT_BYTES: Record<string, number> = {\n b: 1,\n kb: 1024,\n mb: 1024 ** 2,\n gb: 1024 ** 3,\n tb: 1024 ** 4,\n};\n\n/**\n * Parse a `fileSizeLimit` into a number of bytes.\n *\n * - A bare number is taken as bytes verbatim (must be a non-negative integer).\n * - A string is `<number><unit>` (e.g. `\"5MB\"`, `\"1.5GB\"`, `\"500kb\"`); the unit\n * is case-insensitive and optional (a bare numeric string = bytes). Binary\n * units (1 MB = 1024 squared bytes) to match the Storage module's parser.\n *\n * Throws on a negative value, a non-finite number, or an unrecognized unit so a\n * bad limit fails at config-author time, not silently at deploy.\n */\nexport function parseFileSizeLimit(input: string | number): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0 || !Number.isInteger(input)) {\n throw new Error(\n `bucket fileSizeLimit must be a non-negative integer number of bytes, got ${input}`,\n );\n }\n return input;\n }\n\n const trimmed = input.trim();\n // <number><optional-unit>, e.g. \"5MB\", \"1.5 gb\", \"1024\", \"500kb\".\n const match = /^(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)?$/.exec(trimmed);\n if (!match) {\n throw new Error(\n `bucket fileSizeLimit string must be \"<number><unit>\" like \"5MB\" or \"1GB\", got \"${input}\"`,\n );\n }\n const value = Number.parseFloat(match[1]!);\n const unit = (match[2] ?? \"b\").toLowerCase();\n const multiplier = UNIT_BYTES[unit];\n if (multiplier === undefined) {\n throw new Error(\n `bucket fileSizeLimit has an unknown unit \"${match[2]}\" — use B, KB, MB, GB, or TB (e.g. \"5MB\")`,\n );\n }\n const bytes = value * multiplier;\n if (!Number.isFinite(bytes) || bytes < 0) {\n throw new Error(`bucket fileSizeLimit resolved to an invalid byte count: ${bytes}`);\n }\n // Round to a whole byte — a fractional byte is meaningless to the Storage API.\n return Math.round(bytes);\n}\n\n// A loose MIME-type shape check: `type/subtype`, where each side is a token of\n// alphanumerics plus the usual punctuation, and the subtype may be a wildcard\n// `*`. This is intentionally permissive (not an IANA allowlist) but rejects the\n// obvious typos (\"image\", \"imagepng\", \" \").\nconst MIME_RE = /^[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]*\\/(?:\\*|[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]*)$/;\n\n/**\n * Define a single bucket. The bucket NAME is supplied by the key in\n * `defineStorage({ buckets: { <name>: bucket({...}) } })`, so `bucket()` takes\n * only the options.\n *\n * Validates eagerly (at config-author time):\n * - `fileSizeLimit` parses to a valid non-negative byte count.\n * - each `allowedMimeTypes` entry is a `type/subtype` MIME string.\n *\n * Returns a normalized {@link BucketDef}: `public` defaulted to `false`,\n * `fileSizeLimit` as bytes-or-null, `allowedMimeTypes` deduped-or-null.\n */\nexport function bucket(opts: BucketOptions = {}): BucketDef {\n const fileSizeLimit =\n opts.fileSizeLimit === undefined ? null : parseFileSizeLimit(opts.fileSizeLimit);\n\n let allowedMimeTypes: string[] | null = null;\n if (opts.allowedMimeTypes !== undefined) {\n if (!Array.isArray(opts.allowedMimeTypes)) {\n throw new Error(\"bucket allowedMimeTypes must be an array of MIME-type strings\");\n }\n for (const mime of opts.allowedMimeTypes) {\n if (typeof mime !== \"string\" || !MIME_RE.test(mime.trim())) {\n throw new Error(\n `bucket allowedMimeTypes entry \"${mime}\" is not a valid MIME type (expected \"type/subtype\", e.g. \"image/png\")`,\n );\n }\n }\n // Dedupe + trim. An empty array means \"explicitly no types\" — keep it as []\n // rather than null so the apply step can distinguish \"any\" (null) from a\n // caller that passed [].\n allowedMimeTypes = [...new Set(opts.allowedMimeTypes.map((m) => m.trim()))];\n }\n\n return {\n public: opts.public ?? false,\n fileSizeLimit,\n allowedMimeTypes,\n };\n}\n\n/**\n * Define the storage config for a project. The bucket NAME comes from each\n * object key (authors never repeat the name). Returns the discriminated\n * {@link StorageConfig} the runtime config extractor serializes and the Go\n * apply step reconciles.\n *\n * @example\n * export default defineStorage({\n * buckets: { avatars: bucket({ public: true, fileSizeLimit: \"5MB\" }) },\n * });\n */\nexport function defineStorage(input: StorageInput): StorageConfig {\n if (input === null || typeof input !== \"object\" || typeof input.buckets !== \"object\") {\n throw new Error(\"defineStorage expects { buckets: { <name>: bucket({...}) } }\");\n }\n const buckets: Record<string, BucketDef> = {};\n for (const name of Object.keys(input.buckets)) {\n if (name.length === 0) {\n throw new Error(\"bucket name must be a non-empty string\");\n }\n const def = input.buckets[name];\n if (def === undefined) continue;\n buckets[name] = def;\n }\n return { __config: STORAGE_CONFIG_KIND, buckets };\n}\n","/**\n * notifications.ts — the notification-providers config-as-code DSL.\n *\n * `defineNotifications({ push, email, sms })` is the second MODULE config-as-code\n * surface (the sibling of `config/storage.ts`'s `defineStorage`). A\n * `config/notifications.ts` file default-exports a `defineNotifications(...)`\n * result; on deploy the br-pod evaluates it to JSON and reconciles the declared\n * providers against the tenant's live providers via the PalNotify admin API\n * (create missing; never auto-delete — dropping a provider is destructive and\n * warned only).\n *\n * SECRETS ARE NEVER IN THE FILE. Each provider's NON-SECRET fields (team id, key\n * id, bundle id, region, host, port, from address, account sid, …) are literals\n * in the config. Each provider's SECRET material (the APNs .p8 key, the FCM\n * service-account JSON, an api_key, an auth token, the SMTP/ACS password, …) is\n * NOT a field here — it is bound BY CONVENTION to a RESERVED encrypted env var\n * and resolved at deploy from control-pg. The env-key convention is:\n *\n * PB_NOTIFICATIONS_<PROVIDER>_<FIELD> (UPPER_SNAKE_CASE)\n *\n * e.g. `PB_NOTIFICATIONS_APNS_P8`, `PB_NOTIFICATIONS_FCM_SERVICE_ACCOUNT`,\n * `PB_NOTIFICATIONS_TWILIO_AUTH_TOKEN`. The `PB_` namespace is reserved (the CLI\n * refuses a hand-set `palbase secret set PB_*`); the CLI's `palbase notifications\n * add <provider>` derives the key and uploads the secret for you, so an author\n * never types either the secret or the env-key name into git.\n *\n * @example\n * import { defineNotifications } from \"@palbase/backend\";\n *\n * export default defineNotifications({\n * push: {\n * apns: { enabled: true, teamId: \"ABCDE12345\", keyId: \"KEY1234567\", bundleId: \"com.acme.app\" },\n * fcm: { enabled: true },\n * },\n * email: {\n * sendgrid: { enabled: true, fromDomain: \"mail.acme.com\" },\n * },\n * sms: {\n * twilio: { enabled: true, accountSid: \"ACxxxxxxxx\", messagingServiceSid: \"MGxxxxxxxx\" },\n * },\n * });\n *\n * The returned value is the EXACT JSON shape the runtime's generic config\n * extractor (`config_extract.js`) emits and the Go apply step parses:\n * { __config: \"notifications\", push: {...}, email: {...}, sms: {...} }\n * Only ENABLED providers carry their non-secret fields; a disabled (or absent)\n * provider serializes as `{ enabled: false }` so the apply step skips it.\n */\n\n/** The `__config` discriminant the eval/apply reads to confirm a config/*.ts is\n * a notifications config. Notifications is `\"notifications\"`. */\nexport const NOTIFICATIONS_CONFIG_KIND = \"notifications\" as const;\n\n/** The reserved env-var prefix that backs provider secrets. A `palbase secret\n * set` of a key under this prefix is REFUSED by the CLI (it is managed by\n * `palbase notifications add`). Both the CLI and the br-pod apply step derive a\n * provider's secret env key as `${RESERVED_SECRET_PREFIX}_<PROVIDER>_<FIELD>`. */\nexport const RESERVED_SECRET_PREFIX = \"PB_NOTIFICATIONS\" as const;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Provider catalog — the SINGLE SOURCE of which providers exist + their fields.\n//\n// Ported from platform/studio/src/server/trpc/routers/notifications.ts (the\n// per-provider credential zod schemas). Here the fields are split into:\n// • nonSecret: literals that live in config/notifications.ts (this file).\n// • secret: the field name(s) bound to a reserved env var (NOT in config).\n// The Go apply step + the CLI both mirror this catalog, so adding a provider is\n// a three-line change kept in lockstep across the SDK / runtime / CLI.\n// ────────────────────────────────────────────────────────────────────────────\n\n/** One provider's catalog entry: which non-secret fields are required, which are\n * optional, and which secret field-name(s) bind to reserved env vars. */\nexport interface ProviderCatalogEntry {\n /** The channel this provider serves (`push` | `email` | `sms`). */\n readonly channel: \"push\" | \"email\" | \"sms\";\n /** Non-secret config fields the author MUST supply (validated eagerly). */\n readonly required: readonly string[];\n /** Non-secret config fields the author MAY supply. */\n readonly optional: readonly string[];\n /** Secret field name(s) — each backed by `PB_NOTIFICATIONS_<PROVIDER>_<FIELD>`\n * (FIELD is the UPPER_SNAKE of the name here). NOT a config field. */\n readonly secrets: readonly string[];\n}\n\n/**\n * The provider catalog: provider key → fields. The non-secret field names are\n * the camelCase keys an author writes in `config/notifications.ts`; the secret\n * names are the snake-ish tokens that become the reserved env-var suffix.\n *\n * apns: team_id / key_id / bundle_id (+ optional is_production); secret = p8.\n * fcm: no non-secret fields; secret = service_account (the full JSON).\n * sendgrid: from_domain; secret = api_key.\n * ses: region / from_domain / access_key_id; secret = secret_access_key.\n * smtp: host / port / from_email (+ optional username, use_starttls); secret = password.\n * acs: from_email (+ optional from_name); secret = connection_string.\n * twilio: account_sid + (from_number OR messaging_service_sid); secret = auth_token.\n */\nexport const PROVIDER_CATALOG = {\n apns: {\n channel: \"push\",\n required: [\"teamId\", \"keyId\", \"bundleId\"],\n optional: [\"isProduction\"],\n secrets: [\"p8\"],\n },\n fcm: {\n channel: \"push\",\n required: [],\n optional: [],\n secrets: [\"serviceAccount\"],\n },\n sendgrid: {\n channel: \"email\",\n required: [\"fromDomain\"],\n optional: [],\n secrets: [\"apiKey\"],\n },\n ses: {\n channel: \"email\",\n required: [\"region\", \"accessKeyId\", \"fromDomain\"],\n optional: [],\n secrets: [\"secretAccessKey\"],\n },\n smtp: {\n channel: \"email\",\n required: [\"host\", \"port\", \"fromEmail\"],\n optional: [\"username\", \"useStarttls\"],\n secrets: [\"password\"],\n },\n acs: {\n channel: \"email\",\n required: [\"fromEmail\"],\n optional: [\"fromName\"],\n secrets: [\"connectionString\"],\n },\n twilio: {\n channel: \"sms\",\n // account_sid required; one of from_number / messaging_service_sid required\n // (enforced by the refine in buildProvider, not by the flat `required` list).\n required: [\"accountSid\"],\n optional: [\"fromNumber\", \"messagingServiceSid\"],\n secrets: [\"authToken\"],\n },\n} as const satisfies Record<string, ProviderCatalogEntry>;\n\n/** A provider key (`\"apns\" | \"fcm\" | \"sendgrid\" | …`). */\nexport type ProviderName = keyof typeof PROVIDER_CATALOG;\n\n// ── author-facing per-provider option types (non-secret fields only) ─────────\n\n/** APNs (Apple Push) — non-secret fields. The `.p8` key is the reserved secret\n * `PB_NOTIFICATIONS_APNS_P8` (uploaded via `palbase notifications add apns`). */\nexport interface ApnsOptions {\n enabled?: boolean;\n teamId: string;\n keyId: string;\n bundleId: string;\n /** APNs production gateway vs sandbox. Defaults to true (production). */\n isProduction?: boolean;\n}\n\n/** FCM (Firebase Cloud Messaging) — no non-secret fields; the service-account\n * JSON is the reserved secret `PB_NOTIFICATIONS_FCM_SERVICE_ACCOUNT`. */\nexport interface FcmOptions {\n enabled?: boolean;\n}\n\n/** SendGrid email — non-secret `fromDomain`; api_key is the reserved secret. */\nexport interface SendgridOptions {\n enabled?: boolean;\n fromDomain: string;\n}\n\n/** Amazon SES email — secret_access_key is the reserved secret. */\nexport interface SesOptions {\n enabled?: boolean;\n region: string;\n accessKeyId: string;\n fromDomain: string;\n}\n\n/** SMTP email — password is the reserved secret. */\nexport interface SmtpOptions {\n enabled?: boolean;\n host: string;\n port: number;\n fromEmail: string;\n username?: string;\n useStarttls?: boolean;\n}\n\n/** Azure Communication Services email — connection_string is the reserved secret. */\nexport interface AcsOptions {\n enabled?: boolean;\n fromEmail: string;\n fromName?: string;\n}\n\n/** Twilio SMS — auth_token is the reserved secret. Exactly one of `fromNumber`\n * or `messagingServiceSid` must be supplied. */\nexport interface TwilioOptions {\n enabled?: boolean;\n accountSid: string;\n fromNumber?: string;\n messagingServiceSid?: string;\n}\n\n/** The union of every provider's author-facing options. `buildProvider` accepts\n * this so each provider's typed options pass without a cast. */\nexport type ProviderOptions =\n | ApnsOptions\n | FcmOptions\n | SendgridOptions\n | SesOptions\n | SmtpOptions\n | AcsOptions\n | TwilioOptions;\n\n/** The author-facing input to `defineNotifications`. Every provider + every\n * channel is optional — declare only what you use. */\nexport interface NotificationsInput {\n push?: {\n apns?: ApnsOptions;\n fcm?: FcmOptions;\n };\n email?: {\n sendgrid?: SendgridOptions;\n ses?: SesOptions;\n smtp?: SmtpOptions;\n acs?: AcsOptions;\n };\n sms?: {\n twilio?: TwilioOptions;\n };\n}\n\n// ── compiled (serializable) provider defs — the EXACT JSON the apply parses ──\n\n/** A compiled provider def. `enabled` is always present; the remaining keys are\n * the provider's non-secret fields (verbatim from the catalog). A disabled\n * provider is `{ enabled: false }` with no other fields. */\nexport type ProviderDef = { enabled: boolean } & Record<string, unknown>;\n\n/** The compiled notifications config — the discriminant + per-channel maps of\n * provider name → {@link ProviderDef}. This is what `defineNotifications`\n * returns and the runtime extractor serializes. */\nexport interface NotificationsConfig {\n __config: typeof NOTIFICATIONS_CONFIG_KIND;\n push: Record<string, ProviderDef>;\n email: Record<string, ProviderDef>;\n sms: Record<string, ProviderDef>;\n}\n\n// ── validation + compilation ────────────────────────────────────────────────\n\n/**\n * Compile + validate one provider's author options into a {@link ProviderDef}.\n *\n * - A provider with `enabled === false` (or omitted) compiles to `{ enabled:\n * false }` and its required fields are NOT enforced (you can declare a disabled\n * provider as a placeholder without filling it in).\n * - An ENABLED provider must supply every `required` non-secret field from the\n * catalog; a missing one throws at config-author time (not silently at deploy).\n * - Only the catalog's non-secret fields (required + optional) are copied into\n * the def — an unknown extra key is ignored (it would have no effect on the\n * live provider). Secrets are never read here.\n */\nexport function buildProvider(name: ProviderName, opts: ProviderOptions): ProviderDef {\n const entry = PROVIDER_CATALOG[name];\n // A fresh, index-signature-bearing copy so catalog-driven field reads are\n // typed without a cast (the author-facing option interfaces are closed).\n const src: Record<string, unknown> = { ...opts };\n const enabled = src.enabled === undefined ? false : Boolean(src.enabled);\n if (!enabled) {\n return { enabled: false };\n }\n\n const def: ProviderDef = { enabled: true };\n\n for (const field of entry.required) {\n const value = src[field];\n if (value === undefined || value === null || value === \"\") {\n throw new Error(\n `notifications: provider \"${name}\" is enabled but missing required field \"${field}\"`,\n );\n }\n def[field] = value;\n }\n for (const field of entry.optional) {\n if (src[field] !== undefined) {\n def[field] = src[field];\n }\n }\n\n // Twilio: exactly one of fromNumber / messagingServiceSid is required (mirrors\n // the studio router's refine + the module's ValidateTwilioConfig).\n if (name === \"twilio\") {\n const hasFrom = Boolean(def.fromNumber);\n const hasMsg = Boolean(def.messagingServiceSid);\n if (!hasFrom && !hasMsg) {\n throw new Error(\n 'notifications: provider \"twilio\" requires one of \"fromNumber\" or \"messagingServiceSid\"',\n );\n }\n }\n\n return def;\n}\n\n/**\n * Define the notification-provider config for a project. Returns the\n * discriminated {@link NotificationsConfig} the runtime config extractor\n * serializes and the Go apply step reconciles. Validates eagerly: an enabled\n * provider missing a required non-secret field throws here (config-author time),\n * not at deploy.\n *\n * @example\n * export default defineNotifications({\n * push: { apns: { enabled: true, teamId: \"T\", keyId: \"K\", bundleId: \"com.x\" } },\n * });\n */\nexport function defineNotifications(input: NotificationsInput): NotificationsConfig {\n if (input === null || typeof input !== \"object\") {\n throw new Error(\"defineNotifications expects { push?, email?, sms? }\");\n }\n\n const config: NotificationsConfig = {\n __config: NOTIFICATIONS_CONFIG_KIND,\n push: {},\n email: {},\n sms: {},\n };\n\n const push = input.push ?? {};\n if (push.apns !== undefined) config.push.apns = buildProvider(\"apns\", push.apns);\n if (push.fcm !== undefined) config.push.fcm = buildProvider(\"fcm\", push.fcm);\n\n const email = input.email ?? {};\n if (email.sendgrid !== undefined) config.email.sendgrid = buildProvider(\"sendgrid\", email.sendgrid);\n if (email.ses !== undefined) config.email.ses = buildProvider(\"ses\", email.ses);\n if (email.smtp !== undefined) config.email.smtp = buildProvider(\"smtp\", email.smtp);\n if (email.acs !== undefined) config.email.acs = buildProvider(\"acs\", email.acs);\n\n const sms = input.sms ?? {};\n if (sms.twilio !== undefined) config.sms.twilio = buildProvider(\"twilio\", sms.twilio);\n\n return config;\n}\n\n/**\n * Derive the RESERVED env-var key that backs a provider's secret field, e.g.\n * `reservedSecretKey(\"apns\", \"p8\")` → `\"PB_NOTIFICATIONS_APNS_P8\"`. The CLI uses\n * this to upload the secret and the br-pod apply step uses the same derivation\n * to resolve it — they never hand-type the key, so they cannot drift.\n */\nexport function reservedSecretKey(provider: ProviderName, secretField: string): string {\n return `${RESERVED_SECRET_PREFIX}_${camelToUpperSnake(provider)}_${camelToUpperSnake(secretField)}`;\n}\n\n/** camelCase → UPPER_SNAKE_CASE (`serviceAccount` → `SERVICE_ACCOUNT`). */\nfunction camelToUpperSnake(s: string): string {\n return s.replace(/([a-z0-9])([A-Z])/g, \"$1_$2\").toUpperCase();\n}\n","/**\n * flags.ts — the feature-flag-definitions config-as-code DSL.\n *\n * `defineFlags({ flags })` is the third MODULE config-as-code surface (a sibling\n * of `config/storage.ts`'s `defineStorage` and `config/notifications.ts`'s\n * `defineNotifications`). A `config/flags.ts` file default-exports a\n * `defineFlags(...)` result; on deploy the br-pod evaluates it to JSON and\n * UPSERTS the declared flag DEFINITIONS into PalFlags (the user-flags module's\n * system-flags admin API) — create-or-update, idempotent. A live flag NOT in\n * config is NEVER auto-deleted (an orphan flag is harmless; upsert-only).\n *\n * Flags are DECLARATIVE: a flag is a typed project-wide DEFAULT (its key, type,\n * default value, an optional description, and — for string flags — an optional\n * list of allowed `variants`). The VALUE of a flag for a specific USER (a\n * per-user override / A-B assignment) is runtime state set via the SDK, NEVER in\n * git. `variants` here is part of the DEFINITION (the allowed values a string\n * flag may take), not a per-user assignment.\n *\n * NO SECRETS. Unlike notifications, a flag carries no credentials — this is the\n * simplest module config (pure declarative data).\n *\n * @example\n * import { defineFlags, flag } from \"@palbase/backend\";\n *\n * export default defineFlags({\n * flags: {\n * new_dashboard: flag({ type: \"boolean\", default: false, description: \"Roll out the new dashboard\" }),\n * max_uploads: flag({ type: \"number\", default: 10 }),\n * theme: flag({ type: \"string\", default: \"light\", variants: [\"light\", \"dark\", \"system\"] }),\n * },\n * });\n *\n * The returned value is the EXACT JSON shape the runtime's generic config\n * extractor (`config_extract.js`) emits and the Go apply step parses:\n * { __config: \"flags\", flags: { <key>: { type, default, variants, description }, ... } }\n * `variants` is the allowed-values list for a string flag, or `null` (any\n * string). `description` is the doc string, or `null`. The apply step maps the\n * author-facing `type` (\"boolean\") to PalFlags' `value_type` (\"bool\").\n */\n\n/** The `__config` discriminant the eval/apply reads to confirm a config/*.ts is\n * a flags config. Flags is `\"flags\"`. */\nexport const FLAGS_CONFIG_KIND = \"flags\" as const;\n\n/**\n * A flag's type. The author-facing vocabulary is `\"boolean\" | \"number\" |\n * \"string\"`. (The br-pod maps `\"boolean\"` to PalFlags' `value_type: \"bool\"` on\n * apply — the SDK keeps the JS-natural `\"boolean\"` so the DSL reads cleanly.)\n *\n * PalFlags also supports an `object` value type, but config-as-code flags are\n * deliberately scoped to the three SCALAR types — an object default is awkward\n * to author on the CLI and rare for a feature flag, so it is intentionally\n * omitted here (set an object flag via the Studio / API directly if needed).\n */\nexport type FlagType = \"boolean\" | \"number\" | \"string\";\n\n/** The runtime JSON value a flag's `default` may hold (matches {@link FlagType}). */\nexport type FlagValue = boolean | number | string;\n\n/**\n * The author-facing options for a single flag.\n *\n * - `type`: the flag's type — `\"boolean\" | \"number\" | \"string\"`.\n * - `default`: the project-wide default value. MUST match `type` (a `number`\n * default with `type: \"boolean\"` throws).\n * - `variants`: ONLY valid for `type: \"string\"` — the allowed values the string\n * flag may take (a DEFINITION, not a per-user assignment). When given, the\n * `default` MUST be one of the variants. Supplying `variants` on a non-string\n * type throws.\n * - `description`: an optional human description of what the flag controls.\n */\nexport interface FlagOptions {\n type: FlagType;\n default: FlagValue;\n variants?: string[];\n description?: string;\n}\n\n/**\n * The compiled, serializable flag definition — the EXACT shape emitted to JSON\n * and consumed by the Go apply step.\n *\n * - `type`: the author-facing type verbatim (`\"boolean\" | \"number\" | \"string\"`).\n * - `default`: the default value (matches `type`).\n * - `variants`: the allowed-values list for a string flag, or `null` (any).\n * - `description`: the doc string, or `null`.\n */\nexport interface FlagDef {\n type: FlagType;\n default: FlagValue;\n variants: string[] | null;\n description: string | null;\n}\n\n/** A flags config definition: the discriminant + a map of flag key →\n * {@link FlagDef}. This is the value `defineFlags` returns and the runtime\n * config extractor serializes. */\nexport interface FlagsConfig {\n __config: typeof FLAGS_CONFIG_KIND;\n flags: Record<string, FlagDef>;\n}\n\n/** The author-facing input to `defineFlags`: a `flags` map whose keys are the\n * flag keys and whose values are `flag({...})` builders. */\nexport interface FlagsInput {\n flags: Record<string, FlagDef>;\n}\n\n// Flag keys mirror PalFlags' system-flag key rule (validate.keyNameRe): a\n// letter, then letters / digits / underscores. We validate eagerly so a key the\n// CLI/SDK accepts is a key PalFlags accepts (the apply step never has to reject).\nconst FLAG_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;\n\n/** Reports whether `value` matches the declared {@link FlagType}. */\nfunction valueMatchesType(type: FlagType, value: FlagValue): boolean {\n switch (type) {\n case \"boolean\":\n return typeof value === \"boolean\";\n case \"number\":\n // A finite number only — NaN / Infinity are not serializable as JSON.\n return typeof value === \"number\" && Number.isFinite(value);\n case \"string\":\n return typeof value === \"string\";\n }\n}\n\n/**\n * Define a single flag. The flag KEY is supplied by the object key in\n * `defineFlags({ flags: { <key>: flag({...}) } })`, so `flag()` takes only the\n * options.\n *\n * Validates eagerly (at config-author time):\n * - `type` is one of `\"boolean\" | \"number\" | \"string\"`.\n * - `default` matches `type` (e.g. a `number` default with `type: \"boolean\"`\n * throws).\n * - `variants` is ONLY allowed for a string flag (variants on a non-string\n * type throws); each entry is a non-empty string; the `default` must be one\n * of the variants.\n *\n * Returns a normalized {@link FlagDef}: `variants` deduped-or-null,\n * `description` trimmed-or-null.\n */\nexport function flag(opts: FlagOptions): FlagDef {\n if (opts === null || typeof opts !== \"object\") {\n throw new Error(\"flag() expects { type, default, variants?, description? }\");\n }\n const { type } = opts;\n if (type !== \"boolean\" && type !== \"number\" && type !== \"string\") {\n throw new Error(`flag type must be \"boolean\", \"number\", or \"string\", got ${JSON.stringify(type)}`);\n }\n if (!valueMatchesType(type, opts.default)) {\n throw new Error(\n `flag default ${JSON.stringify(opts.default)} does not match type \"${type}\"`,\n );\n }\n\n let variants: string[] | null = null;\n if (opts.variants !== undefined) {\n if (type !== \"string\") {\n throw new Error(`flag variants are only valid for type \"string\" (got type \"${type}\")`);\n }\n if (!Array.isArray(opts.variants)) {\n throw new Error(\"flag variants must be an array of strings\");\n }\n for (const v of opts.variants) {\n if (typeof v !== \"string\" || v.length === 0) {\n throw new Error(`flag variant ${JSON.stringify(v)} must be a non-empty string`);\n }\n }\n // Dedupe while preserving first-seen order. An empty array is meaningless\n // (no value would be allowed) — reject it rather than silently widening.\n variants = [...new Set(opts.variants)];\n if (variants.length === 0) {\n throw new Error(\"flag variants must be a non-empty list when supplied\");\n }\n // `default` is a string here (valueMatchesType passed for type \"string\").\n if (!variants.includes(opts.default as string)) {\n throw new Error(\n `flag default ${JSON.stringify(opts.default)} is not one of its variants [${variants\n .map((v) => JSON.stringify(v))\n .join(\", \")}]`,\n );\n }\n }\n\n let description: string | null = null;\n if (opts.description !== undefined) {\n if (typeof opts.description !== \"string\") {\n throw new Error(\"flag description must be a string\");\n }\n const trimmed = opts.description.trim();\n description = trimmed.length > 0 ? trimmed : null;\n }\n\n return {\n type,\n default: opts.default,\n variants,\n description,\n };\n}\n\n/**\n * Define the feature-flag definitions for a project. The flag KEY comes from\n * each object key (authors never repeat the key). Returns the discriminated\n * {@link FlagsConfig} the runtime config extractor serializes and the Go apply\n * step UPSERTS into PalFlags.\n *\n * @example\n * export default defineFlags({\n * flags: { dark_mode: flag({ type: \"boolean\", default: false }) },\n * });\n */\nexport function defineFlags(input: FlagsInput): FlagsConfig {\n if (input === null || typeof input !== \"object\" || typeof input.flags !== \"object\") {\n throw new Error(\"defineFlags expects { flags: { <key>: flag({...}) } }\");\n }\n const flags: Record<string, FlagDef> = {};\n for (const key of Object.keys(input.flags)) {\n if (!FLAG_KEY_RE.test(key)) {\n throw new Error(\n `flag key ${JSON.stringify(key)} is invalid — must start with a letter and contain only letters, digits, and underscores`,\n );\n }\n const def = input.flags[key];\n if (def === undefined) continue;\n flags[key] = def;\n }\n return { __config: FLAGS_CONFIG_KIND, flags };\n}\n","// `@Controller(basePath, options?)` — the class decorator that marks a class as\n// a Palbase backend controller. It stamps a non-enumerable `__palbase`\n// discriminant + the resolved controller metadata onto the class so the\n// deploy/dispatch pipeline (and `isController`/`resolveController`) can detect\n// and read it without `reflect-metadata`.\nimport type { AuthSpec } from \"../endpoint.js\";\n\n/** The controller metadata stamped onto a `@Controller`-decorated class. The\n * default export of a `controllers/*.controller.ts` file resolves to this via\n * {@link resolveController}. */\nexport interface ControllerMeta {\n /** Discriminant the runtime + tooling read. */\n readonly __palbase: \"controller\";\n /** The base path every route in this controller mounts under (e.g. \"/todos\"). */\n basePath: string;\n /** Controller-level default auth, applied to routes that don't set their own\n * (`@Get(\"/x\", { auth })` overrides this). `undefined` ⇒ secure-by-default. */\n defaultAuth?: AuthSpec;\n}\n\n/** Options accepted by `@Controller`. */\nexport interface ControllerOptions {\n /** Default auth for ALL routes in this controller (route-level overrides). */\n auth?: AuthSpec;\n}\n\n/** Symbol the controller metadata is stamped under. Symbol-keyed (not a string\n * property) so it never collides with an authored member and stays off the\n * structural surface. */\nexport const CONTROLLER_META: unique symbol = Symbol.for(\"palbase.backend.controllerMeta\");\n\n/** A class carrying the stamped controller metadata + discriminant. */\ninterface ControllerCarrier {\n __palbase?: \"controller\";\n [CONTROLLER_META]?: ControllerMeta;\n}\n\n/**\n * Mark a class as a Palbase backend controller. `basePath` is the mount path\n * for every route the class declares; `options.auth` sets the controller-level\n * default auth (a route's own `auth` overrides it; absent ⇒ secure-by-default).\n *\n * @example\n * \\@Controller(\"/todos\", { auth: false })\n * export class TodosController {\n * \\@Get(\"\") list(\\@Query(ListTodosQuery) q: ListTodosQuery): TodoSchema[] { … }\n * }\n */\nexport function Controller(basePath: string, options: ControllerOptions = {}) {\n return function <T extends abstract new (...args: never[]) => object>(ctor: T): T {\n const carrier = ctor as unknown as ControllerCarrier;\n const meta: ControllerMeta = {\n __palbase: \"controller\",\n basePath,\n ...(options.auth !== undefined ? { defaultAuth: options.auth } : {}),\n };\n // Non-enumerable so it doesn't leak onto instances / structural checks.\n Object.defineProperty(carrier, CONTROLLER_META, {\n value: meta,\n enumerable: false,\n configurable: true,\n writable: false,\n });\n // The bare `__palbase` discriminant is the cheap detection marker the\n // runtime/extractor checks; keep it readable but non-enumerable.\n Object.defineProperty(carrier, \"__palbase\", {\n value: \"controller\",\n enumerable: false,\n configurable: true,\n writable: false,\n });\n return ctor;\n };\n}\n\n/** True when `value` is a `@Controller`-decorated class (cheap discriminant\n * check). Accepts the class constructor (the default export of a controller\n * file). */\nexport function isController(value: unknown): boolean {\n if (typeof value !== \"function\" && (typeof value !== \"object\" || value === null)) {\n return false;\n }\n const carrier = value as ControllerCarrier;\n return carrier.__palbase === \"controller\" && carrier[CONTROLLER_META] !== undefined;\n}\n\n/** Read the resolved controller metadata off a `@Controller`-decorated class.\n * Throws if the class was not decorated — callers should gate with\n * {@link isController} first (the loader does). */\nexport function resolveController(ctor: unknown): ControllerMeta {\n if (typeof ctor !== \"function\" && (typeof ctor !== \"object\" || ctor === null)) {\n throw new TypeError(\"resolveController: value is not a class\");\n }\n const meta = (ctor as ControllerCarrier)[CONTROLLER_META];\n if (!meta) {\n throw new TypeError(\n \"resolveController: class is not a @Controller — every controller file must `export default` a @Controller-decorated class\",\n );\n }\n return meta;\n}\n","// The decorator registry — the single plain-data store the method + parameter\n// decorators write into, and the deploy/dispatch pipeline reads back. No\n// `reflect-metadata`, no `emitDecoratorMetadata`: the registry is built from the\n// decorator arguments + the parameter INDEX that esbuild/tsc preserve for legacy\n// parameter decorators (verified — see the design spec §0/§4.1).\n//\n// A controller class carries its route metadata on a symbol-keyed static\n// property (`ROUTES`). `@Get`/`@Post`/… append a {@link RouteMeta} entry;\n// `@Body`/`@User`/… append a {@link ParamMeta} entry onto the route for the\n// method they decorate. Because parameter decorators run BEFORE the method\n// decorator for the same member (TS evaluates innermost-first, params before the\n// method), the route entry may not exist yet when a param decorator fires — so\n// param metadata is buffered per method name and merged when the method\n// decorator creates the route entry.\nimport type { AuthSpec, RateLimitConfig } from \"../endpoint.js\";\nimport type { ZodTypeAny } from \"zod\";\n\n/** The HTTP verbs a route may declare, upper-cased (the runtime router +\n * OpenAPI lower-case on their own). */\nexport type HttpMethodUpper = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/** Route-level options accepted by the method decorators (`@Get`/`@Post`/…). */\nexport interface RouteOptions {\n /** OVERRIDES the controller-level default auth for this one route. */\n auth?: AuthSpec;\n /** Per-route rate limit. */\n rateLimit?: RateLimitConfig;\n}\n\n/** The kind of value a parameter decorator injects. Drives both dispatch\n * (which request slice to inject) and codegen (which OpenAPI parameter source a\n * schema-bearing kind maps to). */\nexport type ParamKind =\n | \"body\"\n | \"query\"\n | \"param\"\n | \"headers\"\n | \"user\"\n | \"optionalUser\"\n | \"client\"\n | \"requestId\"\n | \"traceId\"\n | \"req\";\n\n/** One parameter decorator's recorded metadata. `index` is the parameter\n * position esbuild/tsc preserve; `schema` is present for the schema-bearing\n * kinds (`body`/`query`/`headers`); `name` is the path-param name for `param`. */\nexport interface ParamMeta {\n index: number;\n kind: ParamKind;\n /** Zod schema for `body`/`query`/`headers` (validation + codegen source). */\n schema?: ZodTypeAny;\n /** Path-param name for `@Param(\"id\")`. */\n name?: string;\n}\n\n/** One inferred throw site: the error CLASS name (e.g. \"TodoLocked\") and its\n * wire code (e.g. \"todo_locked\"). `status`, `hasData`, and the data JSON schema\n * are NOT carried here — they resolve from the error registry by `code` at\n * extract/openapi time (single source of truth). */\nexport interface ThrowDescriptor {\n name: string;\n code: string;\n}\n\n/** One route's recorded metadata: the verb + subpath + method name + options,\n * the ordered parameter metas, and the resolved return schema (injected by the\n * codegen step — see `returnSchema`). */\nexport interface RouteMeta {\n method: HttpMethodUpper;\n subpath: string;\n fnName: string;\n options: RouteOptions;\n params: ParamMeta[];\n /** Response schema for the route, if any. Derived from the method's RETURN\n * TYPE by codegen and written here via `recordReturn` (a generated top-level\n * IIFE injected per controller), not by an author-written decorator. */\n returnSchema?: ZodTypeAny;\n /** Error classes this route can throw, if inferred. Derived from the method\n * body + service call graph by the deploy stager's throw analysis and written\n * here via `recordThrows` (a generated top-level IIFE injected per controller,\n * the `recordReturn` twin), not by an author-written decorator. */\n throws?: ThrowDescriptor[];\n}\n\n/** Symbol the route metadata list is stored under on a controller class. Using\n * a symbol (not a string key) keeps it off the public structural surface and\n * avoids any chance of an authored property collision. */\nexport const ROUTES: unique symbol = Symbol.for(\"palbase.backend.routes\");\n\n/** Symbol the per-method buffered parameter metas are stored under while a class\n * is being decorated. Parameter decorators fire before the method decorator, so\n * they buffer here keyed by method name; the method decorator drains the buffer\n * into the route entry it creates. */\nconst PARAM_BUFFER: unique symbol = Symbol.for(\"palbase.backend.paramBuffer\");\n\n/** Symbol the per-method buffered return-type schemas are stored under while a\n * class's registry is being populated. The codegen-injected `recordReturn` call\n * can fire before OR after the method decorator; it buffers here keyed by method\n * name and `recordRoute` drains it into the route entry (and `recordReturn`\n * writes through if the route already exists). Buffering on BOTH sides means a\n * fully-formed route entry always carries its return schema — a raw-symbol\n * reader (the runtime) never has to re-merge. */\nconst RETURN_BUFFER: unique symbol = Symbol.for(\"palbase.backend.returnBuffer\");\n\n/** Symbol the per-method buffered throw descriptors are stored under while a\n * class's registry is being populated. The stager-injected `recordThrows` call\n * can fire before OR after the method decorator; it buffers here keyed by method\n * name and `recordRoute` drains it into the route entry (and `recordThrows`\n * writes through if the route already exists). Buffering on BOTH sides means a\n * fully-formed route entry always carries its throw descriptors — a raw-symbol\n * reader (the runtime) never has to re-merge. */\nconst THROWS_BUFFER: unique symbol = Symbol.for(\"palbase.backend.throwsBuffer\");\n\n/** A class constructor carrying the symbol-keyed registry slots. We type the\n * registry-bearing class as this so the decorators can read/write the slots\n * without `any` — a plain `Function` does not carry index signatures. */\ninterface RegistryCarrier {\n [ROUTES]?: RouteMeta[];\n [PARAM_BUFFER]?: Record<string, ParamMeta[]>;\n [RETURN_BUFFER]?: Record<string, ZodTypeAny>;\n [THROWS_BUFFER]?: Record<string, ThrowDescriptor[]>;\n}\n\n/** Coerce a decorated target (class constructor or its prototype) into the\n * registry carrier that owns the slots. Method/param decorators receive the\n * PROTOTYPE as their target; the class decorator receives the constructor. We\n * always anchor the registry on the CONSTRUCTOR so `getRoutes(ctor)` finds it. */\nfunction carrierOf(target: object): RegistryCarrier {\n // For instance-member decorators, `target` is the prototype; its `.constructor`\n // is the class. For a static member or the class decorator, `target` is the\n // constructor already. Resolve to the constructor either way.\n const ctor =\n typeof target === \"function\"\n ? (target as unknown as RegistryCarrier)\n : (((target as { constructor?: unknown }).constructor ??\n target) as unknown as RegistryCarrier);\n return ctor;\n}\n\n/** Get (creating if absent) the own route list for a class constructor. Own —\n * not inherited — so a subclass does not mutate its base's routes. */\nfunction ownRoutes(carrier: RegistryCarrier): RouteMeta[] {\n if (!Object.prototype.hasOwnProperty.call(carrier, ROUTES)) {\n carrier[ROUTES] = [];\n }\n return carrier[ROUTES] as RouteMeta[];\n}\n\n/** Get (creating if absent) the own per-method param buffer for a class. */\nfunction ownParamBuffer(carrier: RegistryCarrier): Record<string, ParamMeta[]> {\n if (!Object.prototype.hasOwnProperty.call(carrier, PARAM_BUFFER)) {\n carrier[PARAM_BUFFER] = {};\n }\n return carrier[PARAM_BUFFER] as Record<string, ParamMeta[]>;\n}\n\n/** Record a route (called by the method decorators). Drains any parameter\n * metas already buffered for `fnName` into the new route entry, then sorts them\n * by parameter index so dispatch can inject positionally. */\nexport function recordRoute(\n target: object,\n fnName: string,\n method: HttpMethodUpper,\n subpath: string,\n options: RouteOptions,\n): void {\n const carrier = carrierOf(target);\n const routes = ownRoutes(carrier);\n const buffer = ownParamBuffer(carrier);\n const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);\n const route: RouteMeta = { method, subpath, fnName, options, params };\n // Drain a buffered return schema (the recordReturn-ran-first ordering) so the\n // route entry is complete the moment it's created — a raw-symbol consumer\n // (the runtime extractor/worker) sees the return schema without re-merging.\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer && returnBuffer[fnName] !== undefined) {\n route.returnSchema = returnBuffer[fnName];\n }\n // Same drain for buffered throw descriptors (the recordThrows-ran-first\n // ordering) — the route entry is complete the moment it's created.\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer && throwsBuffer[fnName] !== undefined) {\n route.throws = throwsBuffer[fnName];\n }\n routes.push(route);\n}\n\n/** Record one parameter decorator (called by `@Body`/`@User`/…). Buffers per\n * method name; the method decorator merges the buffer into the route entry. If\n * the route already exists (method decorator ran first — TS does evaluate the\n * method decorator AFTER its parameter decorators, but we stay order-robust),\n * the meta is also appended directly so neither ordering loses it. */\nexport function recordParam(target: object, fnName: string, meta: ParamMeta): void {\n const carrier = carrierOf(target);\n const buffer = ownParamBuffer(carrier);\n (buffer[fnName] ??= []).push(meta);\n\n // Order-robust: if the route already exists, merge in place + keep sorted.\n const routes = carrier[ROUTES];\n if (routes) {\n const route = routes.find((r) => r.fnName === fnName);\n if (route) {\n route.params.push(meta);\n route.params.sort((a, b) => a.index - b.index);\n }\n }\n}\n\n/** Attach a return schema to the route for `fnName` (called by the codegen\n * injection that reads the method's return type). If the route does not exist\n * yet, the schema is buffered (RETURN_BUFFER) and drained into the route by\n * `recordRoute` when the method decorator runs. */\nexport function recordReturn(target: object, fnName: string, schema: ZodTypeAny): void {\n const carrier = carrierOf(target);\n const routes = carrier[ROUTES];\n const route = routes?.find((r) => r.fnName === fnName);\n if (route) {\n route.returnSchema = schema;\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(carrier, RETURN_BUFFER)) {\n carrier[RETURN_BUFFER] = {};\n }\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer) returnBuffer[fnName] = schema;\n}\n\n/** Attach the inferred throw descriptors to the route for `fnName` (called by\n * the stager-injected IIFE that carries the throw analysis result — the\n * `recordReturn` twin). If the route does not exist yet, the descriptors are\n * buffered (THROWS_BUFFER) and drained into the route by `recordRoute` when the\n * method decorator runs. */\nexport function recordThrows(target: object, fnName: string, throws: ThrowDescriptor[]): void {\n const carrier = carrierOf(target);\n const routes = carrier[ROUTES];\n const route = routes?.find((r) => r.fnName === fnName);\n if (route) {\n route.throws = throws;\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(carrier, THROWS_BUFFER)) {\n carrier[THROWS_BUFFER] = {};\n }\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer) throwsBuffer[fnName] = throws;\n}\n\n/** Read the route metadata for a controller class (the deploy/dispatch entry\n * point). Applies any buffered return schemas + throw descriptors (for the\n * recordReturn/recordThrows-runs-before orderings) and returns a defensive copy\n * so callers cannot mutate the registry.\n */\nexport function getRoutes(ctor: object): RouteMeta[] {\n const carrier = carrierOf(ctor);\n const routes = carrier[ROUTES] ?? [];\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer) {\n for (const route of routes) {\n const buffered = returnBuffer[route.fnName];\n if (buffered && route.returnSchema === undefined) {\n route.returnSchema = buffered;\n }\n }\n }\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer) {\n for (const route of routes) {\n const buffered = throwsBuffer[route.fnName];\n if (buffered && route.throws === undefined) {\n route.throws = buffered;\n }\n }\n }\n return routes.map((r) => ({\n ...r,\n params: r.params.slice(),\n ...(r.throws !== undefined ? { throws: r.throws.slice() } : {}),\n }));\n}\n","// Method decorators: `@Get` / `@Post` / `@Put` / `@Patch` / `@Delete` declare a\n// route (verb + subpath + options) on a controller method. These are LEGACY\n// method decorators (`experimentalDecorators`), receiving\n// `(prototype, methodName, descriptor)`. They write into the per-class registry\n// (registry.ts). The success-response schema is NOT declared here: it is derived\n// from the method's RETURN TYPE by a codegen step and injected onto the route at\n// runtime via `recordReturn` (registry.ts).\nimport {\n recordRoute,\n type HttpMethodUpper,\n type RouteOptions,\n} from \"./registry.js\";\n\n/** A legacy method decorator. */\ntype MethodDecorator = (\n target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor,\n) => void;\n\n/** Build a method decorator for one HTTP verb. The decorated method's name is\n * the route `fnName` (authoring sugar — the operationId is derived flat from\n * method+path by the runtime, not from this name). */\nfunction makeMethodDecorator(method: HttpMethodUpper) {\n return function (subpath: string, options: RouteOptions = {}): MethodDecorator {\n return function (target, propertyKey) {\n recordRoute(target, String(propertyKey), method, subpath, options);\n };\n };\n}\n\n/** `@Get(subpath, options?)` — declare a GET route. */\nexport const Get = makeMethodDecorator(\"GET\");\n/** `@Post(subpath, options?)` — declare a POST route. */\nexport const Post = makeMethodDecorator(\"POST\");\n/** `@Put(subpath, options?)` — declare a PUT route. */\nexport const Put = makeMethodDecorator(\"PUT\");\n/** `@Patch(subpath, options?)` — declare a PATCH route. */\nexport const Patch = makeMethodDecorator(\"PATCH\");\n/** `@Delete(subpath, options?)` — declare a DELETE route. */\nexport const Delete = makeMethodDecorator(\"DELETE\");\n","// Parameter decorators: `@Body` / `@Query` / `@Param` / `@Headers` / `@User` /\n// `@OptionalUser` / `@Client` / `@RequestId` / `@TraceId` / `@Req`. Each records\n// `{ index, kind, schema?, name? }` into the per-class registry for the method\n// it decorates. These are LEGACY parameter decorators\n// (`experimentalDecorators`), receiving `(prototype, methodName, paramIndex)` —\n// esbuild/tsc preserve the param index at runtime (verified, design §0), which\n// is how dispatch injects positionally. No type reflection\n// (`emitDecoratorMetadata`) is used: validation comes from the zod schema, the\n// type annotation the developer writes is purely for autocomplete.\nimport type { ZodTypeAny } from \"zod\";\nimport { recordParam, type ParamKind } from \"./registry.js\";\n\n/** A legacy parameter decorator. */\ntype ParameterDecorator = (\n target: object,\n propertyKey: string | symbol,\n parameterIndex: number,\n) => void;\n\n/** Build a parameter decorator that records the given kind (+ optional schema /\n * name) at the decorated parameter's index. */\nfunction makeParamDecorator(\n kind: ParamKind,\n extra?: { schema?: ZodTypeAny; name?: string },\n): ParameterDecorator {\n return function (target, propertyKey, parameterIndex) {\n recordParam(target, String(propertyKey), {\n index: parameterIndex,\n kind,\n ...(extra?.schema !== undefined ? { schema: extra.schema } : {}),\n ...(extra?.name !== undefined ? { name: extra.name } : {}),\n });\n };\n}\n\n/** `@Body(schema)` — inject the request body, validated against `schema`. The\n * developer writes `: T` (= `z.infer<schema>`, same name) for autocomplete. */\nexport function Body(schema: ZodTypeAny): ParameterDecorator {\n return makeParamDecorator(\"body\", { schema });\n}\n\n/** `@Query(schema)` — inject the parsed query params, validated against\n * `schema`. */\nexport function Query(schema: ZodTypeAny): ParameterDecorator {\n return makeParamDecorator(\"query\", { schema });\n}\n\n/** `@Headers(schema?)` — inject the request headers (lowercase keys). With a\n * schema, headers are validated + the codegen emits header parameters. */\nexport function Headers(schema?: ZodTypeAny): ParameterDecorator {\n return makeParamDecorator(\"headers\", schema !== undefined ? { schema } : undefined);\n}\n\n/** `@Param(\"id\")` — inject one matched path param by name. */\nexport function Param(name: string): ParameterDecorator {\n return makeParamDecorator(\"param\", { name });\n}\n\n/** `@User()` — inject the authenticated user (`: User`, non-null for an\n * effective-required route). The runtime resolves the effective auth. */\nexport function User(): ParameterDecorator {\n return makeParamDecorator(\"user\");\n}\n\n/** `@OptionalUser()` — inject the user as `User | null` (for routes whose\n * effective auth is `false` / `{ required: false }`). */\nexport function OptionalUser(): ParameterDecorator {\n return makeParamDecorator(\"optionalUser\");\n}\n\n/** `@Client()` — inject the parsed calling-client metadata (`: ClientInfo`). */\nexport function Client(): ParameterDecorator {\n return makeParamDecorator(\"client\");\n}\n\n/** `@RequestId()` — inject the per-request id (`: string`). */\nexport function RequestId(): ParameterDecorator {\n return makeParamDecorator(\"requestId\");\n}\n\n/** `@TraceId()` — inject the W3C trace id (`: string`). */\nexport function TraceId(): ParameterDecorator {\n return makeParamDecorator(\"traceId\");\n}\n\n/** `@Req()` — inject the raw request object (escape hatch, `: PBRequest`). */\nexport function Req(): ParameterDecorator {\n return makeParamDecorator(\"req\");\n}\n","import type { DBClient, Logger, CacheClient, PalbaseModuleClients } from \"./endpoint.js\";\nimport type { User } from \"./types.js\";\n\n/** Middleware context — subset of EndpointContext without input (not yet validated). */\nexport interface MiddlewareContext extends PalbaseModuleClients {\n params: Record<string, string>;\n query: Record<string, string>;\n headers: Record<string, string>;\n user: User | null;\n db: DBClient;\n env: Record<string, string>;\n log: Logger;\n cache: CacheClient;\n requestId: string;\n projectId: string;\n environmentId: string;\n}\n\n/** Middleware function signature — receives context and next function. */\nexport type MiddlewareHandler = (\n ctx: MiddlewareContext,\n next: () => Promise<void>,\n) => Promise<void>;\n\n/**\n * Define a middleware function for use in the middleware/ directory or\n * as endpoint-specific middleware.\n *\n * Middleware runs before the handler. Call `next()` to pass control\n * to the next middleware or handler. If `next()` is not called, the\n * handler will not execute.\n *\n * Errors thrown in middleware are caught by the pipeline and returned\n * as error responses.\n */\nexport function defineMiddleware(fn: MiddlewareHandler): MiddlewareHandler {\n return fn;\n}\n","/** HTTP error with structured error response format.\n *\n * The base class for the throwable error classes (`PalError`, `Conflict`,\n * `NotFound`, …). Construct one directly with `throw new HttpError(404,\n * \"todo_not_found\", \"No such todo\")`, or throw a named subclass\n * (`throw new NotFound(\"todo not found\")`). The runtime catches any `HttpError`\n * and emits the standard envelope; on the wire (and to iOS) it surfaces as\n * `BackendError.server(code, status, message, requestId)`.\n *\n * The optional `data` field carries a structured payload alongside the\n * standard envelope — for errors that need to ship extra context\n * (e.g. `new Conflict(\"locked\", \"title_locked\", { retryAfter: 30 })`). It rides\n * through to the iOS typed enum's associated value.\n */\nexport class HttpError extends Error {\n public readonly status: number;\n public readonly error: string;\n public readonly errorDescription: string;\n public readonly data?: unknown;\n\n constructor(status: number, error: string, errorDescription: string, data?: unknown) {\n super(errorDescription);\n this.name = \"HttpError\";\n this.status = status;\n this.error = error;\n this.errorDescription = errorDescription;\n if (data !== undefined) {\n this.data = data;\n }\n }\n\n /**\n * Serialize to the standard Palbase error response format.\n * The `requestId` is injected by the runtime layer from the request context.\n * When called without arguments (e.g. JSON.stringify), request_id is omitted.\n * When `data` is set, it is appended as a strict-superset field.\n */\n toJSON(requestId?: string): {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } {\n const result: {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } = {\n error: this.error,\n error_description: this.errorDescription,\n status: this.status,\n };\n if (requestId) {\n result.request_id = requestId;\n }\n if (this.data !== undefined) {\n result.data = this.data;\n }\n return result;\n }\n}\n\n/**\n * Throw with a custom HTTP status + wire code. The general-purpose escape hatch\n * when none of the named classes (`Conflict`/`NotFound`/…) fits.\n *\n * @example\n * throw new PalError(418, \"teapot\", \"I'm a teapot\");\n */\nexport class PalError extends HttpError {\n constructor(status: number, code: string, description: string, data?: unknown) {\n super(status, code, description, data);\n this.name = \"PalError\";\n }\n}\n\n/** Base for the named status classes. Each subclass fixes its HTTP status; the\n * `code` defaults to the class's canonical wire code (overridable), and the\n * `message` defaults to a human-readable label (overridable). */\nabstract class NamedHttpError extends HttpError {\n protected constructor(\n status: number,\n defaultCode: string,\n name: string,\n message?: string,\n code?: string,\n data?: unknown,\n ) {\n super(status, code ?? defaultCode, message ?? defaultMessage(name), data);\n this.name = name;\n }\n}\n\n/** Derive a default human-readable message from a class name\n * (\"NotFound\" → \"Not found\", \"TooManyRequests\" → \"Too many requests\"). */\nfunction defaultMessage(name: string): string {\n const spaced = name.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();\n}\n\n/** 400 — the request was malformed or failed validation. */\nexport class BadRequest extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(400, \"bad_request\", \"BadRequest\", message, code, data);\n }\n}\n\n/** 401 — the caller is not authenticated. */\nexport class Unauthorized extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(401, \"unauthorized\", \"Unauthorized\", message, code, data);\n }\n}\n\n/** 403 — the caller is authenticated but not allowed. */\nexport class Forbidden extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(403, \"forbidden\", \"Forbidden\", message, code, data);\n }\n}\n\n/** 404 — the requested resource does not exist. */\nexport class NotFound extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(404, \"not_found\", \"NotFound\", message, code, data);\n }\n}\n\n/** 409 — the request conflicts with the current state. */\nexport class Conflict extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(409, \"conflict\", \"Conflict\", message, code, data);\n }\n}\n\n/** 429 — the caller has exceeded the rate limit. */\nexport class TooManyRequests extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(429, \"too_many_requests\", \"TooManyRequests\", message, code, data);\n }\n}\n","// defineError — project-defined typed error classes + the module-global error\n// registry the OpenAPI spec twins join against.\n//\n// `defineError(code, status, dataSchema?)` returns an `HttpError` subclass whose\n// constructor is typed by the zod schema (`new TodoLocked({ retryAfter: 30 })`).\n// Each call self-registers `{ code, status, className, dataSchema }` in a\n// registry anchored on `globalThis` under `Symbol.for(...)` — mirroring the\n// ROUTES pattern — so the deploy bundle's registrations land on the single SDK\n// instance the extractor `require()`s. The registry is pre-seeded with the named\n// built-in classes (`NotFound`, `Conflict`, …) so throw inference can resolve\n// ad-hoc `throw new NotFound(...)` sites by their canonical wire codes.\n//\n// Duplicate code with a DIFFERENT shape (status OR converted data schema) is a\n// hard error at registration time (deploy fails loudly — error codes are\n// project-unique); structurally-identical re-registration is idempotent (each\n// controller BUNDLE carries its own copy of a shared models/errors.ts, so the\n// same defineError line legitimately runs once per bundle in one process).\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z, type ZodTypeAny } from \"zod\";\nimport { HttpError } from \"./errors.js\";\n\nextendZodWithOpenApi(z);\n\n/** One registered error: the wire code, HTTP status, the class name the\n * analyzer sees, the optional data payload schema, and whether it is one of the\n * pre-seeded built-in named classes. */\nexport interface RegisteredError {\n code: string;\n status: number;\n className: string;\n dataSchema?: ZodTypeAny;\n /** JSON.stringify of the converted JSON schema — equal digest ⇔ the spec\n * twins would emit an identical `data` schema for this error. */\n dataDigest?: string;\n builtin: boolean;\n}\n\n/** The cross-module-instance registry anchor (the pinned contract symbol). */\nconst ERROR_REGISTRY: unique symbol = Symbol.for(\"palbase.backend.errorRegistry\");\n\ninterface RegistryCarrier {\n [ERROR_REGISTRY]?: Map<string, RegisteredError>;\n}\n\n/** Get (creating + pre-seeding if absent) the project-global error registry,\n * keyed by wire code. Anchored on `globalThis` so every copy of the SDK in a\n * process shares the one registry. */\nexport function getErrorRegistry(): Map<string, RegisteredError> {\n const g = globalThis as RegistryCarrier;\n if (!g[ERROR_REGISTRY]) {\n const m = new Map<string, RegisteredError>();\n for (const [code, status, className] of [\n [\"bad_request\", 400, \"BadRequest\"],\n [\"unauthorized\", 401, \"Unauthorized\"],\n [\"forbidden\", 403, \"Forbidden\"],\n [\"not_found\", 404, \"NotFound\"],\n [\"conflict\", 409, \"Conflict\"],\n [\"too_many_requests\", 429, \"TooManyRequests\"],\n ] as const) {\n m.set(code, { code, status, className, builtin: true });\n }\n g[ERROR_REGISTRY] = m;\n }\n return g[ERROR_REGISTRY];\n}\n\n/** Class returned for schema-bearing errors: ctor(data, message?). */\nexport interface DefinedErrorWithData<S extends ZodTypeAny> {\n new (data: z.input<S>, message?: string): HttpError;\n readonly code: string;\n readonly status: number;\n}\n\n/** Class returned for schema-less errors: ctor(message?). */\nexport interface DefinedError {\n new (message?: string): HttpError;\n readonly code: string;\n readonly status: number;\n}\n\n/**\n * Define a project error class. MUST be called with literal args (string\n * literal `code`, numeric literal `status`) at module top level — the deploy\n * stager's static analyzer reads those literals to resolve throw sites.\n *\n * @example\n * // models/todos/errors.ts\n * export const TodoLocked = defineError(\"todo_locked\", 409, z.object({ retryAfter: z.number() }));\n * // services/todo.service.ts\n * throw new TodoLocked({ retryAfter: 30 });\n */\nexport function defineError<S extends ZodTypeAny>(\n code: string,\n status: number,\n dataSchema: S,\n): DefinedErrorWithData<S>;\nexport function defineError(code: string, status: number): DefinedError;\nexport function defineError(code: string, status: number, dataSchema?: ZodTypeAny): unknown {\n if (!Number.isInteger(status) || status < 400 || status > 599) {\n throw new Error(\n `defineError: status for \"${code}\" must be a 4xx/5xx integer, got ${status} — ` +\n `error responses must not clobber success responses in the project spec.`,\n );\n }\n const registry = getErrorRegistry();\n const existing = registry.get(code);\n const className = defaultClassName(code);\n const dataDigest = dataSchema ? digestOf(code, dataSchema) : undefined;\n if (existing) {\n const sameShape =\n existing.status === status && !existing.builtin && existing.dataDigest === dataDigest;\n if (!sameShape) {\n throw new Error(\n `defineError: duplicate error code \"${code}\" with a different shape ` +\n `(existing: status ${existing.status}${existing.builtin ? \", built-in\" : \"\"}` +\n `${existing.status === status && !existing.builtin ? \", different data schema\" : \"\"}). ` +\n `Error codes are project-unique.`,\n );\n }\n }\n // Local factories so the class-constructor closures capture a NON-optional\n // schema binding (narrowing does not flow into closures created in a branch).\n const makeWithData = (schema: ZodTypeAny) =>\n class extends HttpError {\n static readonly code = code;\n static readonly status = status;\n constructor(data: unknown, message?: string) {\n super(status, code, message ?? humanize(code), schema.parse(data));\n this.name = className;\n }\n };\n const makeWithoutData = () =>\n class extends HttpError {\n static readonly code = code;\n static readonly status = status;\n constructor(message?: string) {\n super(status, code, message ?? humanize(code));\n this.name = className;\n }\n };\n const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();\n Object.defineProperty(cls, \"name\", { value: className });\n registry.set(code, {\n code,\n status,\n className,\n builtin: false,\n ...(dataSchema ? { dataSchema } : {}),\n ...(dataDigest !== undefined ? { dataDigest } : {}),\n });\n return cls;\n}\n\n/** Convert a data schema through the SAME library the spec twins use and\n * return its JSON-schema digest (the duplicate-shape comparison key). Doubles\n * as the self-containment guard: a schema carrying `.openapi(refId)` metadata\n * (top-level or nested) converts to `$ref` pointers into components the\n * project spec never emits — a dangling ref every spec consumer would choke\n * on — so it is rejected here, at the definition site, with a fixable message. */\nfunction digestOf(code: string, dataSchema: ZodTypeAny): string {\n const TMP_REF = \"__PalbaseErrorDataDigest\";\n const tmpRegistry = new OpenAPIRegistry();\n // Wrap the schema as a property — exactly how the envelope embeds it — so a\n // TOP-LEVEL .openapi(refId) is not masked by the registration refId and\n // surfaces as the same $ref the real emission would produce.\n tmpRegistry.register(TMP_REF, z.object({ data: dataSchema }).openapi(TMP_REF));\n const generated = new OpenApiGeneratorV31(tmpRegistry.definitions).generateComponents();\n const out = generated.components?.schemas?.[TMP_REF];\n const digest = JSON.stringify(out ?? null);\n if (digest.includes('\"$ref\"')) {\n throw new Error(\n `defineError: dataSchema for \"${code}\" carries .openapi(refId) metadata — ` +\n `it would emit dangling $ref pointers in the project spec. ` +\n `Use a plain zod schema (z.object({...})) without .openapi(...).`,\n );\n }\n return digest;\n}\n\n/** \"todo_locked\" → \"TodoLocked\" (used as Error.name; the source class NAME the\n * analyzer sees is whatever the user assigned the const to — they should match\n * by convention, and the descriptor carries the analyzed const name anyway). */\nfunction defaultClassName(code: string): string {\n return code\n .split(\"_\")\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join(\"\");\n}\n\n/** \"todo_locked\" → \"Todo locked\" — the default human-readable message. */\nfunction humanize(code: string): string {\n const s = code.replace(/_/g, \" \");\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n","import type { User } from \"./types.js\";\n\n/** Non-service, per-invocation data for background worker handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface WorkerMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n /** The user that enqueued the job, if any (background jobs are usually system-initiated). */\n user: User | null;\n /** Per-invocation id for correlation. */\n requestId: string;\n projectId: string;\n environmentId: string;\n}\n\n/** Backoff strategy for worker retries. */\nexport type BackoffStrategy = \"exponential\" | \"linear\" | \"fixed\";\n\n/** Configuration for a background worker. */\nexport interface WorkerConfig<TPayload = unknown> {\n /** Worker name — must be unique across the project. */\n name: string;\n /** Maximum number of retries before sending to dead letter queue. Defaults to 3. */\n retry?: number;\n /** Execution timeout in seconds. Defaults to 30. */\n timeout?: number;\n /** Backoff strategy between retries. Defaults to \"exponential\". */\n backoff?: BackoffStrategy;\n /** Handler function that processes the job payload. */\n handler: (\n payload: TPayload,\n meta: WorkerMeta,\n ) => Promise<void>;\n}\n\n/** Resolved worker configuration with defaults applied. */\nexport interface ResolvedWorkerConfig<TPayload = unknown> {\n name: string;\n retry: number;\n timeout: number;\n backoff: BackoffStrategy;\n handler: (\n payload: TPayload,\n meta: WorkerMeta,\n ) => Promise<void>;\n}\n\n/** Valid worker name pattern: alphanumeric, underscore, hyphen only.\n * Prevents Redis key injection via colons or path separators. */\nconst VALID_WORKER_NAME = /^[a-zA-Z0-9_-]+$/;\n\n/** Default values for worker configuration. */\nconst WORKER_DEFAULTS = {\n retry: 3,\n timeout: 30,\n backoff: \"exponential\" as BackoffStrategy,\n} as const;\n\n/**\n * Define a background worker that processes jobs from the queue.\n *\n * Workers are placed in the `workers/` directory and auto-discovered at deploy time.\n *\n * @example\n * ```ts\n * export default defineWorker({\n * name: \"send-email\",\n * retry: 5,\n * timeout: 60,\n * backoff: \"exponential\",\n * handler: async (payload: { to: string; subject: string }, meta) => {\n * await sendEmail(payload.to, payload.subject);\n * },\n * });\n * ```\n */\nexport function defineWorker<TPayload = unknown>(\n config: WorkerConfig<TPayload>,\n): ResolvedWorkerConfig<TPayload> {\n if (!config.name || config.name.trim() === \"\") {\n throw new Error(\"Worker name is required\");\n }\n\n if (!VALID_WORKER_NAME.test(config.name)) {\n throw new Error(\n `Invalid worker name \"${config.name}\": must match [a-zA-Z0-9_-]+`,\n );\n }\n\n if (config.retry !== undefined && (config.retry < 0 || !Number.isInteger(config.retry))) {\n throw new Error(\"Worker retry must be a non-negative integer\");\n }\n\n if (config.timeout !== undefined && config.timeout <= 0) {\n throw new Error(\"Worker timeout must be a positive number\");\n }\n\n if (\n config.backoff !== undefined &&\n ![\"exponential\", \"linear\", \"fixed\"].includes(config.backoff)\n ) {\n throw new Error(`Invalid backoff strategy: ${config.backoff}`);\n }\n\n return {\n name: config.name,\n retry: config.retry ?? WORKER_DEFAULTS.retry,\n timeout: config.timeout ?? WORKER_DEFAULTS.timeout,\n backoff: config.backoff ?? WORKER_DEFAULTS.backoff,\n handler: config.handler,\n };\n}\n","/** Non-service, per-invocation data for job handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface JobMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n projectId: string;\n environmentId: string;\n}\n\n/** Configuration for defining a scheduled job. */\nexport interface JobConfig {\n /** Unique job name — alphanumeric, underscore, hyphen only. */\n name: string;\n /** Cron expression (e.g., \"0 3 * * *\"). */\n schedule: string;\n /** Execution timeout in seconds. Defaults to 30. */\n timeout?: number;\n /** Job handler function. */\n handler: (meta: JobMeta) => Promise<void>;\n}\n\n/** Resolved job configuration with defaults applied. */\nexport interface ResolvedJobConfig {\n name: string;\n schedule: string;\n timeout: number;\n handler: (meta: JobMeta) => Promise<void>;\n}\n\n/** Valid job name pattern: alphanumeric, underscore, hyphen only. */\nconst VALID_JOB_NAME = /^[a-zA-Z0-9_-]+$/;\n\n/** Maximum allowed timeout in seconds (5 minutes, matching sandbox limits). */\nconst MAX_TIMEOUT_SECONDS = 300;\n\n/** Default values for job configuration. */\nconst JOB_DEFAULTS = {\n timeout: 30,\n} as const;\n\n/**\n * Cron expression validation.\n * Supports standard 5-field cron: minute hour day-of-month month day-of-week.\n * Each field allows: number, *, ranges (1-5), steps (star/2), lists (1,3,5).\n */\nfunction validateCronExpression(expression: string): string | null {\n const trimmed = expression.trim();\n if (trimmed === \"\") {\n return \"Cron expression is required\";\n }\n\n const parts = trimmed.split(/\\s+/);\n if (parts.length !== 5) {\n return `Invalid cron expression \"${trimmed}\": expected 5 fields (minute hour day month weekday), got ${parts.length}`;\n }\n\n const fieldNames = [\"minute\", \"hour\", \"day of month\", \"month\", \"day of week\"];\n const fieldRanges: [number, number][] = [\n [0, 59],\n [0, 23],\n [1, 31],\n [1, 12],\n [0, 7],\n ];\n\n for (let i = 0; i < 5; i++) {\n const field = parts[i]!;\n const name = fieldNames[i]!;\n const [min, max] = fieldRanges[i]!;\n\n const error = validateCronField(field, name, min, max);\n if (error !== null) {\n return error;\n }\n }\n\n return null;\n}\n\nfunction validateCronField(\n field: string,\n name: string,\n min: number,\n max: number,\n): string | null {\n // Split by comma for lists\n const listParts = field.split(\",\");\n for (const part of listParts) {\n // Check for step: */2, 1-5/2\n const stepParts = part.split(\"/\");\n if (stepParts.length > 2) {\n return `Invalid ${name} field: \"${field}\"`;\n }\n\n const base = stepParts[0]!;\n const step = stepParts[1];\n\n if (step !== undefined) {\n const stepNum = Number(step);\n if (!Number.isInteger(stepNum) || stepNum < 1) {\n return `Invalid step value in ${name} field: \"${field}\"`;\n }\n }\n\n if (base === \"*\") {\n continue;\n }\n\n // Check for range: 1-5\n if (base.includes(\"-\")) {\n const rangeParts = base.split(\"-\");\n if (rangeParts.length !== 2) {\n return `Invalid range in ${name} field: \"${field}\"`;\n }\n const rangeStart = Number(rangeParts[0]);\n const rangeEnd = Number(rangeParts[1]);\n if (\n !Number.isInteger(rangeStart) ||\n !Number.isInteger(rangeEnd) ||\n rangeStart < min ||\n rangeEnd > max ||\n rangeStart > rangeEnd\n ) {\n return `Invalid range in ${name} field: \"${field}\"`;\n }\n continue;\n }\n\n // Single number\n const num = Number(base);\n if (!Number.isInteger(num) || num < min || num > max) {\n return `Invalid value in ${name} field: \"${field}\"`;\n }\n }\n\n return null;\n}\n\n/**\n * Define a scheduled cron job.\n *\n * Jobs are placed in the `jobs/` directory and auto-discovered at deploy time.\n *\n * @example\n * ```ts\n * export default defineJob({\n * name: \"cleanup-expired\",\n * schedule: \"0 3 * * *\", // every day at 3 AM\n * timeout: 60,\n * handler: async (meta) => {\n * await Database.delete(\"sessions\", \"expired < NOW()\");\n * Log.info(\"Cleaned up expired sessions\");\n * },\n * });\n * ```\n */\nexport function defineJob(config: JobConfig): ResolvedJobConfig {\n if (!config.name || config.name.trim() === \"\") {\n throw new Error(\"Job name is required\");\n }\n\n if (!VALID_JOB_NAME.test(config.name)) {\n throw new Error(\n `Invalid job name \"${config.name}\": must match [a-zA-Z0-9_-]+`,\n );\n }\n\n if (!config.schedule || config.schedule.trim() === \"\") {\n throw new Error(\"Job schedule is required\");\n }\n\n const cronError = validateCronExpression(config.schedule);\n if (cronError !== null) {\n throw new Error(cronError);\n }\n\n if (!config.handler) {\n throw new Error(\"Job handler is required\");\n }\n\n if (config.timeout !== undefined && config.timeout <= 0) {\n throw new Error(\"Job timeout must be a positive number\");\n }\n\n if (config.timeout !== undefined && !Number.isInteger(config.timeout)) {\n throw new Error(\"Job timeout must be an integer\");\n }\n\n if (config.timeout !== undefined && config.timeout > MAX_TIMEOUT_SECONDS) {\n throw new Error(\n `Job timeout ${config.timeout}s exceeds maximum ${MAX_TIMEOUT_SECONDS}s`,\n );\n }\n\n return {\n name: config.name,\n schedule: config.schedule.trim(),\n timeout: config.timeout ?? JOB_DEFAULTS.timeout,\n handler: config.handler,\n };\n}\n","/** Supported webhook provider types. */\nexport type WebhookProvider =\n | \"stripe\"\n | \"github\"\n | \"twilio\"\n | \"sendgrid\"\n | \"slack\"\n | \"discord\"\n | \"livekit\";\n\n/** Non-service, per-invocation data for webhook handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface WebhookMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n /** Per-invocation id for correlation. */\n requestId: string;\n projectId: string;\n environmentId: string;\n}\n\n/** Secret reference — resolves from environment variables at runtime. */\nexport interface EnvSecretRef {\n env: string;\n}\n\n/** Provider-specific event maps for type-safe event handlers. */\nexport interface ProviderEventMap {\n stripe: {\n \"checkout.session.completed\": Record<string, unknown>;\n \"payment_intent.succeeded\": Record<string, unknown>;\n \"payment_intent.payment_failed\": Record<string, unknown>;\n \"customer.subscription.created\": Record<string, unknown>;\n \"customer.subscription.updated\": Record<string, unknown>;\n \"customer.subscription.deleted\": Record<string, unknown>;\n \"invoice.paid\": Record<string, unknown>;\n \"invoice.payment_failed\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n github: {\n push: Record<string, unknown>;\n pull_request: Record<string, unknown>;\n issues: Record<string, unknown>;\n \"pull_request.opened\": Record<string, unknown>;\n \"pull_request.closed\": Record<string, unknown>;\n \"pull_request.merged\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n twilio: {\n \"message.received\": Record<string, unknown>;\n \"message.sent\": Record<string, unknown>;\n \"call.completed\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n sendgrid: {\n delivered: Record<string, unknown>;\n bounce: Record<string, unknown>;\n open: Record<string, unknown>;\n click: Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n slack: {\n url_verification: Record<string, unknown>;\n event_callback: Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n discord: {\n PING: Record<string, unknown>;\n MESSAGE_CREATE: Record<string, unknown>;\n INTERACTION_CREATE: Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n livekit: {\n \"room.started\": Record<string, unknown>;\n \"room.finished\": Record<string, unknown>;\n \"participant.joined\": Record<string, unknown>;\n \"participant.left\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n}\n\n/** Event handler function type. */\nexport type WebhookEventHandler<TEvent = Record<string, unknown>> = (\n event: TEvent,\n meta: WebhookMeta,\n) => Promise<void>;\n\n/** Provider-based webhook configuration. */\nexport interface ProviderWebhookConfig<P extends WebhookProvider = WebhookProvider> {\n provider: P;\n secret: EnvSecretRef;\n events: {\n [K in keyof ProviderEventMap[P]]?: WebhookEventHandler<ProviderEventMap[P][K]>;\n };\n}\n\n/** Incoming request representation for custom verify functions. */\nexport interface WebhookRequest {\n headers: Record<string, string>;\n body: string;\n method: string;\n url: string;\n}\n\n/** Custom webhook configuration. */\nexport interface CustomWebhookConfig {\n path: string;\n verify?: (req: WebhookRequest) => Promise<boolean> | boolean;\n handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;\n}\n\n/** Resolved provider webhook (internal). */\nexport interface ResolvedProviderWebhook<P extends WebhookProvider = WebhookProvider> {\n type: \"provider\";\n provider: P;\n secret: EnvSecretRef;\n events: Record<string, WebhookEventHandler>;\n}\n\n/** Resolved custom webhook (internal). */\nexport interface ResolvedCustomWebhook {\n type: \"custom\";\n path: string;\n verify?: (req: WebhookRequest) => Promise<boolean> | boolean;\n handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;\n}\n\n/** Union of resolved webhook types. */\nexport type ResolvedWebhookConfig = ResolvedProviderWebhook | ResolvedCustomWebhook;\n\n/** Valid webhook path pattern: starts with /, alphanumeric + hyphen + slash. */\nconst VALID_WEBHOOK_PATH = /^\\/[a-zA-Z0-9/_-]+$/;\n\n/**\n * Define a provider-based webhook.\n *\n * @example\n * ```ts\n * export default defineWebhook({\n * provider: \"stripe\",\n * secret: { env: \"STRIPE_WEBHOOK_SECRET\" },\n * events: {\n * \"checkout.session.completed\": async (event, meta) => {\n * await Database.insert(\"orders\", { status: \"paid\" });\n * },\n * },\n * });\n * ```\n */\nexport function defineWebhook<P extends WebhookProvider>(\n config: ProviderWebhookConfig<P>,\n): ResolvedProviderWebhook<P>;\n\n/**\n * Define a custom webhook with optional verification.\n *\n * @example\n * ```ts\n * export default defineWebhook({\n * path: \"/webhooks/livekit\",\n * verify: async (req) => req.headers[\"x-api-key\"] === \"secret\",\n * handler: async (payload, meta) => {\n * Log.info(\"Received webhook\", payload);\n * },\n * });\n * ```\n */\nexport function defineWebhook(config: CustomWebhookConfig): ResolvedCustomWebhook;\n\nexport function defineWebhook(\n config: ProviderWebhookConfig | CustomWebhookConfig,\n): ResolvedWebhookConfig {\n if (\"provider\" in config) {\n return validateProviderWebhook(config);\n }\n return validateCustomWebhook(config);\n}\n\nfunction validateProviderWebhook<P extends WebhookProvider>(\n config: ProviderWebhookConfig<P>,\n): ResolvedProviderWebhook<P> {\n if (!config.provider) {\n throw new Error(\"Webhook provider is required\");\n }\n\n const validProviders: WebhookProvider[] = [\n \"stripe\", \"github\", \"twilio\", \"sendgrid\", \"slack\", \"discord\", \"livekit\",\n ];\n if (!validProviders.includes(config.provider)) {\n throw new Error(\n `Invalid webhook provider \"${config.provider}\": must be one of ${validProviders.join(\", \")}`,\n );\n }\n\n if (!config.secret) {\n throw new Error(\"Webhook secret is required (use { env: \\\"SECRET_NAME\\\" })\");\n }\n\n if (typeof config.secret.env !== \"string\" || config.secret.env.trim() === \"\") {\n throw new Error(\"Webhook secret env name must be a non-empty string\");\n }\n\n if (!config.events || Object.keys(config.events).length === 0) {\n throw new Error(\"At least one event handler is required\");\n }\n\n for (const [eventName, handler] of Object.entries(config.events)) {\n if (typeof handler !== \"function\") {\n throw new Error(`Event handler for \"${eventName}\" must be a function`);\n }\n }\n\n return {\n type: \"provider\",\n provider: config.provider,\n secret: config.secret,\n events: config.events as Record<string, WebhookEventHandler>,\n };\n}\n\nfunction validateCustomWebhook(config: CustomWebhookConfig): ResolvedCustomWebhook {\n if (!config.path || config.path.trim() === \"\") {\n throw new Error(\"Webhook path is required\");\n }\n\n if (!VALID_WEBHOOK_PATH.test(config.path)) {\n throw new Error(\n `Invalid webhook path \"${config.path}\": must start with / and contain only alphanumeric, hyphen, underscore, slash`,\n );\n }\n\n if (!config.handler) {\n throw new Error(\"Webhook handler is required\");\n }\n\n if (typeof config.handler !== \"function\") {\n throw new Error(\"Webhook handler must be a function\");\n }\n\n if (config.verify !== undefined && typeof config.verify !== \"function\") {\n throw new Error(\"Webhook verify must be a function\");\n }\n\n return {\n type: \"custom\",\n path: config.path,\n verify: config.verify,\n handler: config.handler,\n };\n}\n","/**\n * resource.ts — external connections as lifecycle-managed classes.\n *\n * A `Resource` subclass models one external connection (a pooled datastore, a\n * stateless API client, or a per-user factory). The framework discovers each\n * instance, calls `init(env)` ONCE at boot with the declared secret subset, and\n * `shutdown()` (reverse order) on SIGTERM. On top of that lifecycle the author\n * exposes their own clean facade methods.\n *\n * // resources/neo4j.ts\n * import { Resource } from \"@palbase/backend\";\n * import neo4j, { type Driver, type Session } from \"neo4j-driver\";\n *\n * export class Neo4jResource extends Resource {\n * static secrets = [\"NEO4J_URL\", \"NEO4J_USER\", \"NEO4J_PASSWORD\"] as const;\n * private driver!: Driver;\n * async init(env: { NEO4J_URL: string; NEO4J_USER: string; NEO4J_PASSWORD: string }) {\n * this.driver = neo4j.driver(env.NEO4J_URL, neo4j.auth.basic(env.NEO4J_USER, env.NEO4J_PASSWORD));\n * }\n * async shutdown() { await this.driver.close(); }\n * session(): Session { return this.driver.session(); }\n * }\n * export const neo4j = new Neo4jResource(); // framework finds + manages it\n *\n * # Boot scope (NOT request-ALS)\n *\n * Resources are instantiated once at process boot. This is deliberately NOT the\n * per-request {@link AsyncLocalStorage} scope used for `Database`/`Cache`/… — a\n * connection pool must outlive a single request. The runtime discovers\n * resources from the project's `resources/` directory, registers each via\n * {@link __registerResource}, then calls {@link __runResourceBoot} before\n * serving and {@link __shutdownResources} on SIGTERM. Discovery is a runtime\n * concern; the SDK provides the base class + registry + boot/shutdown hooks.\n */\n\n/** Map a declared `secrets` tuple to the `init(env)` argument type — a record\n * over the secret names, each `string`. An empty tuple maps to an empty\n * record. */\nexport type ResourceEnv<Secrets extends readonly string[]> = {\n [K in Secrets[number]]: string;\n};\n\n/**\n * Base class for external connections.\n *\n * - `static secrets` — OPTIONAL readonly tuple of env-var names this resource\n * needs. Drives (a) the `env` type passed to `init` and (b) the\n * missing-secret check at boot (deploy fails naming the absent secret).\n * - `init(env)` — called ONCE at boot with the declared secret subset. May be\n * sync (`void`) or async (`Promise<void>`).\n * - `shutdown()` — OPTIONAL drain hook, called on SIGTERM in reverse boot\n * order.\n *\n * @example\n * import { Resource } from \"@palbase/backend\";\n * import { Client } from \"@googlemaps/google-maps-services-js\";\n *\n * export class GoogleResource extends Resource {\n * static secrets = [\"GOOGLE_MAPS_KEY\"] as const;\n * private client = new Client();\n * private key = \"\";\n * init(env: { GOOGLE_MAPS_KEY: string }) { this.key = env.GOOGLE_MAPS_KEY; }\n * }\n * export const google = new GoogleResource();\n */\nexport abstract class Resource {\n /** The env-var names this resource needs. Optional; omit for none. */\n static secrets?: readonly string[];\n\n /** Set up the connection from the declared secrets. Called once at boot. */\n abstract init(env: Record<string, string>): void | Promise<void>;\n\n /** Drain/close the connection on SIGTERM. Optional. */\n shutdown?(): void | Promise<void>;\n}\n\n/** A value carrying an optional readonly `secrets` tuple — the constructor side\n * of a Resource subclass. Narrowed from `unknown` via {@link hasSecrets}. */\ninterface WithSecrets {\n secrets?: readonly string[];\n}\n\n/** Guard: does this value carry a `secrets` array (the static on a Resource\n * subclass)? Lets us read `secrets` off `resource.constructor` (typed\n * `Function`) without a cast — narrows from `unknown` instead. */\nfunction hasSecrets(value: unknown): value is WithSecrets {\n if (typeof value !== \"object\" && typeof value !== \"function\") return false;\n const secrets = (value as { secrets?: unknown }).secrets;\n return secrets === undefined || Array.isArray(secrets);\n}\n\n/** A registered resource plus whether its `init` has already run. The\n * `booted` flag makes {@link __runResourceBoot} idempotent (re-running boot,\n * e.g. across a re-entrant deploy path, never re-inits). */\ninterface RegistryEntry {\n resource: Resource;\n booted: boolean;\n}\n\n/** Module-level boot registry. Single per process (the br-pod is\n * single-project). Order is registration order; shutdown reverses it. */\nconst registry: RegistryEntry[] = [];\n\n/**\n * Register a resource instance with the boot registry. The runtime calls this\n * for each instance discovered under the project's `resources/` directory.\n * NOT part of the public author-facing API (prefixed `__`).\n */\nexport function __registerResource(resource: Resource): void {\n registry.push({ resource, booted: false });\n}\n\n/** Read the declared `secrets` tuple off a resource instance's constructor. */\nfunction declaredSecrets(resource: Resource): readonly string[] {\n const ctor: unknown = resource.constructor;\n return hasSecrets(ctor) ? (ctor.secrets ?? []) : [];\n}\n\n/**\n * Boot every registered resource that has not yet been booted: resolve its\n * declared secret subset from `envMap`, then await its `init(env)`. Idempotent\n * — an already-booted resource is skipped. Throws (failing deploy/boot) when a\n * declared secret is absent, naming the missing secret. NOT part of the public\n * author-facing API.\n */\nexport async function __runResourceBoot(envMap: Record<string, string>): Promise<void> {\n for (const entry of registry) {\n if (entry.booted) continue;\n const secrets = declaredSecrets(entry.resource);\n const env: Record<string, string> = {};\n for (const name of secrets) {\n const value = envMap[name];\n if (value === undefined) {\n throw new Error(\n `Resource ${entry.resource.constructor.name} requires secret \"${name}\" but it is not set. ` +\n `Set it with \\`palbase secret set ${name} ...\\` (or in Studio) and redeploy.`,\n );\n }\n env[name] = value;\n }\n await entry.resource.init(env);\n entry.booted = true;\n }\n}\n\n/**\n * Shut down every booted resource in REVERSE registration order, awaiting each\n * `shutdown()` (a no-op when undefined). Clears the registry afterwards so a\n * second call is a no-op. NOT part of the public author-facing API.\n */\nexport async function __shutdownResources(): Promise<void> {\n for (let i = registry.length - 1; i >= 0; i -= 1) {\n const entry = registry[i]!;\n if (entry.booted && entry.resource.shutdown) {\n await entry.resource.shutdown();\n }\n }\n registry.length = 0;\n}\n\n/** Clear the registry without running shutdown. TEST-only helper so each test\n * starts from a clean registry. NOT part of the public author-facing API. */\nexport function __resetResources(): void {\n registry.length = 0;\n}\n","/** Non-service, per-invocation data for hook handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface HookMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n projectId: string;\n environmentId: string;\n}\n\n// --- Auth Event Payloads ---\n\n/** Payload for auth.onUserCreated hook. */\nexport interface UserCreatedEvent {\n user: {\n id: string;\n /** User's email, if they signed up with one (absent for phone-only users). */\n email?: string;\n role: string;\n metadata: Record<string, unknown>;\n createdAt: string;\n };\n}\n\n/** Payload for auth.onSignIn hook. */\nexport interface SignInEvent {\n user: {\n id: string;\n /** User's email, if they have one (absent for phone-only users). */\n email?: string;\n role: string;\n };\n provider: string;\n timestamp: string;\n}\n\n/** Payload for auth.onSignOut hook. */\nexport interface SignOutEvent {\n user: {\n id: string;\n /** User's email, if they have one (absent for phone-only users). */\n email?: string;\n };\n timestamp: string;\n}\n\n/** Payload for auth.onPasswordReset hook.\n * Password reset is inherently email-based, so `email` is always present here\n * (a phone-only passwordless user cannot trigger this event). */\nexport interface PasswordResetEvent {\n user: {\n id: string;\n email: string;\n };\n timestamp: string;\n}\n\n// --- Storage Event Payloads ---\n\n/** Payload for storage.onFileUploaded hook. */\nexport interface FileUploadedEvent {\n file: {\n id: string;\n name: string;\n bucket: string;\n path: string;\n size: number;\n contentType: string;\n };\n}\n\n/** Payload for storage.onFileDeleted hook. */\nexport interface FileDeletedEvent {\n file: {\n id: string;\n name: string;\n bucket: string;\n path: string;\n };\n}\n\n// --- Documents Event Payloads ---\n\n/** Payload for documents.onDocumentCreated hook. */\nexport interface DocumentCreatedEvent {\n document: {\n id: string;\n collection: string;\n data: Record<string, unknown>;\n };\n}\n\n/** Payload for documents.onDocumentUpdated hook. */\nexport interface DocumentUpdatedEvent {\n document: {\n id: string;\n collection: string;\n data: Record<string, unknown>;\n previousData: Record<string, unknown>;\n };\n}\n\n/** Payload for documents.onDocumentDeleted hook. */\nexport interface DocumentDeletedEvent {\n document: {\n id: string;\n collection: string;\n data: Record<string, unknown>;\n };\n}\n\n// --- Hook Handler Types ---\n\nexport type HookHandler<TEvent> = (event: TEvent, meta: HookMeta) => Promise<void>;\n\n/** Resolved hook configuration (internal). */\nexport interface ResolvedHook<TEvent = unknown> {\n module: string;\n event: string;\n handler: HookHandler<TEvent>;\n}\n\n// --- Auth Hooks ---\n\nexport const auth = {\n onUserCreated(handler: HookHandler<UserCreatedEvent>): ResolvedHook<UserCreatedEvent> {\n return { module: \"auth\", event: \"user.created\", handler };\n },\n\n onSignIn(handler: HookHandler<SignInEvent>): ResolvedHook<SignInEvent> {\n return { module: \"auth\", event: \"user.sign_in\", handler };\n },\n\n onSignOut(handler: HookHandler<SignOutEvent>): ResolvedHook<SignOutEvent> {\n return { module: \"auth\", event: \"user.sign_out\", handler };\n },\n\n onPasswordReset(handler: HookHandler<PasswordResetEvent>): ResolvedHook<PasswordResetEvent> {\n return { module: \"auth\", event: \"user.password_reset\", handler };\n },\n};\n\n// --- Storage Hooks ---\n\nexport const storage = {\n onFileUploaded(handler: HookHandler<FileUploadedEvent>): ResolvedHook<FileUploadedEvent> {\n return { module: \"storage\", event: \"file.uploaded\", handler };\n },\n\n onFileDeleted(handler: HookHandler<FileDeletedEvent>): ResolvedHook<FileDeletedEvent> {\n return { module: \"storage\", event: \"file.deleted\", handler };\n },\n};\n\n// --- Documents Hooks ---\n\nexport const documents = {\n onDocumentCreated(handler: HookHandler<DocumentCreatedEvent>): ResolvedHook<DocumentCreatedEvent> {\n return { module: \"documents\", event: \"document.created\", handler };\n },\n\n onDocumentUpdated(handler: HookHandler<DocumentUpdatedEvent>): ResolvedHook<DocumentUpdatedEvent> {\n return { module: \"documents\", event: \"document.updated\", handler };\n },\n\n onDocumentDeleted(handler: HookHandler<DocumentDeletedEvent>): ResolvedHook<DocumentDeletedEvent> {\n return { module: \"documents\", event: \"document.deleted\", handler };\n },\n};\n","export type {\n PBRequest,\n ClientInfo,\n RateLimitConfig,\n DBClient,\n DBOps,\n TxClient,\n FileContext,\n QueueClient,\n Logger,\n CacheClient,\n PalbaseDocsClient,\n PalbaseCollectionRef,\n PalbaseDocumentRef,\n PalbaseDocumentSnapshot,\n PalbaseQuerySnapshot,\n PalbaseWhereOperator,\n PalbaseResult,\n Middleware,\n ErrorDef,\n ErrorMap,\n ErrorThrowers,\n} from \"./endpoint.js\";\nexport {\n Database,\n Documents,\n Storage,\n Cache,\n Queue,\n Log,\n Notifications,\n Flags,\n Realtime,\n __setRuntime,\n __runWithRuntime,\n __requestALS,\n __getRuntime,\n} from \"./runtime.js\";\nexport type { RuntimeServices } from \"./runtime.js\";\nexport type {\n PalbaseAuthClient,\n PalbaseStorageClient,\n PalbaseBucketClient,\n PalbaseRealtimeClient,\n PalbaseFunctionsClient,\n PalbaseInvokeOptions,\n PalbaseFlagsClient,\n PalbaseFlagsServiceClient,\n PalbaseFlagContext,\n PalbaseFlagVariant,\n PalbaseFlag,\n PalbaseFlagValue,\n PalbaseFlagSource,\n PalbaseSetOverrideResult,\n PalbaseSetOverridesResult,\n PalbaseClearOverrideResult,\n PalbaseClearAllOverridesResult,\n PalbaseBatchOverrideOperation,\n PalbaseBatchSetOverridesResult,\n PalbaseNotificationsClient,\n PalbasePushClient,\n PalbaseEmailClient,\n PalbaseSmsClient,\n PalbaseInboxClient,\n PalbasePreferencesClient,\n PalbaseAnalyticsClient,\n PalbaseAnalyticsQueryNamespace,\n PalbaseAnalyticsManagementNamespace,\n PalbaseLinksClient,\n PalbaseCmsClient,\n // Shared local types\n PalbaseUser,\n PalbaseSession,\n PalbaseDeviceInfo,\n PalbaseAttestAndroidParams,\n PalbaseAttestAndroidResult,\n PalbaseAttestiOSParams,\n PalbaseAttestiOSResult,\n PalbaseBindDeviceParams,\n PalbaseVerifyRequestSignatureParams,\n PalbaseFileObject,\n PalbaseSignedUrlResponse,\n PalbasePublicUrlResponse,\n PalbaseUploadOptions,\n PalbaseTransformOptions,\n PalbaseListOptions,\n PalbasePushSendParams,\n PalbasePushSendResponse,\n PalbaseEmailSendParams,\n PalbaseEmailSendResponse,\n PalbaseSmsSendParams,\n PalbaseSmsSendResponse,\n PalbaseInboxSendParams,\n PalbaseInboxSendResponse,\n PalbaseInboxMessage,\n PalbaseInboxListOptions,\n PalbaseInboxListResult,\n PalbasePreferences,\n PalbaseRegisterDeviceParams,\n PalbaseDeviceTokenView,\n PalbaseMultiChannelResponse,\n PalbaseAnalyticsProperties,\n PalbaseIdentifyTraits,\n PalbaseCountQueryInput,\n PalbaseCountResult,\n PalbaseEventsQueryInput,\n PalbaseEventsResult,\n PalbaseUsersQueryInput,\n PalbaseUsersResult,\n PalbaseFunnelQueryInput,\n PalbaseFunnelResult,\n PalbaseRetentionQueryInput,\n PalbaseRetentionResult,\n PalbaseCohortQueryInput,\n PalbaseCohortResult,\n PalbaseOverviewResult,\n PalbaseEventNamesResult,\n PalbaseUserDetailResult,\n PalbaseCreateLinkParams,\n PalbaseUpdateLinkParams,\n PalbaseLink,\n PalbaseLinkDetails,\n PalbaseLinkAnalytics,\n PalbaseQrCodeOptions,\n PalbaseMatchParams,\n PalbaseInitialLink,\n PalbaseListLinksOptions,\n PalbaseListLinksResult,\n PalbaseCmsFindOptions,\n PalbaseCmsFindOneOptions,\n} from \"./clients.js\";\nexport { defineSchema } from \"./db/schema.js\";\nexport type { SchemaDef, TableDef, TableInput, ColumnMap, SchemaInput } from \"./db/schema.js\";\nexport { policy, PolicyBuilder } from \"./db/policy.js\";\nexport type { PolicyDef, PolicyCommand, PolicyMode } from \"./db/policy.js\";\nexport { PALBASE_EXTENSIONS, EXTENSION_DEPENDENCIES, isPalbaseExtension } from \"./db/extensions.js\";\nexport type { PalbaseExtension } from \"./db/extensions.js\";\nexport { uuid, text, integer, boolean, timestamp, jsonb, enumType } from \"./db/columns.js\";\nexport type { ColumnBuilder, ColumnDef, ColumnType, OnDeleteAction } from \"./db/columns.js\";\nexport { makeTypedDB } from \"./db/typed-db.js\";\nexport type {\n TypedDB,\n TypedTx,\n TypedTable,\n InsertShape,\n RowShape,\n EnvTypedDatabase,\n EnvServiceDatabase,\n EnvTypedTx,\n EnvTypedTable,\n EnvTables,\n} from \"./db/typed-db.js\";\nexport type { Tables, TableTypes } from \"./db/env.js\";\nexport { makeEnvDts } from \"./db/env-gen.js\";\n// Module config-as-code DSLs (sibling of defineSchema). config/storage.ts\n// default-exports defineStorage(...); the deploy evals + applies the buckets.\nexport { defineStorage, bucket, parseFileSizeLimit, STORAGE_CONFIG_KIND } from \"./config/storage.js\";\nexport type { BucketDef, BucketOptions, StorageConfig, StorageInput } from \"./config/storage.js\";\n// config/notifications.ts default-exports defineNotifications(...); the deploy\n// evals + applies the providers (non-secret fields here, cert/key secrets bound\n// by convention to reserved PB_NOTIFICATIONS_* env vars resolved at deploy).\nexport {\n defineNotifications,\n buildProvider,\n reservedSecretKey,\n PROVIDER_CATALOG,\n NOTIFICATIONS_CONFIG_KIND,\n RESERVED_SECRET_PREFIX,\n} from \"./config/notifications.js\";\nexport type {\n NotificationsConfig,\n NotificationsInput,\n ProviderDef,\n ProviderName,\n ProviderOptions,\n ProviderCatalogEntry,\n ApnsOptions,\n FcmOptions,\n SendgridOptions,\n SesOptions,\n SmtpOptions,\n AcsOptions,\n TwilioOptions,\n} from \"./config/notifications.js\";\n// config/flags.ts default-exports defineFlags(...); the deploy evals + UPSERTS\n// the declared flag DEFINITIONS into PalFlags (upsert-only, never auto-deleted).\n// The simplest module config — pure declarative data, no secrets.\nexport { defineFlags, flag, FLAGS_CONFIG_KIND } from \"./config/flags.js\";\nexport type {\n FlagDef,\n FlagOptions,\n FlagsConfig,\n FlagsInput,\n FlagType,\n FlagValue,\n} from \"./config/flags.js\";\n// Class-controller decorator model (replaces defineController/defineHandler/route).\nexport { Controller } from \"./decorators/controller.js\";\nexport type { ControllerOptions } from \"./decorators/controller.js\";\nexport { Get, Post, Put, Patch, Delete } from \"./decorators/methods.js\";\nexport {\n Body,\n Query,\n Headers,\n Param,\n User,\n OptionalUser,\n Client,\n RequestId,\n TraceId,\n Req,\n} from \"./decorators/params.js\";\nexport type { RouteOptions, HttpMethodUpper, ThrowDescriptor } from \"./decorators/registry.js\";\n// recordThrows is the stager-injected carrier for inferred throw descriptors\n// (the recordReturn twin) — public so the injected IIFE in a deployed bundle\n// can call it via `require(\"@palbase/backend\").recordThrows(...)`.\nexport { recordThrows } from \"./decorators/registry.js\";\nexport { defineMiddleware } from \"./middleware.js\";\nexport type { MiddlewareContext, MiddlewareHandler } from \"./middleware.js\";\n// The authenticated-user TYPE is exported as `UserT` (not `User`) because the\n// value name `User` is the @User() parameter decorator (exported above). A\n// controller annotates `@User() user: UserT` — decorator for the value\n// position, `UserT` for the type. (NestJS-style: same name as the decorator\n// would collide in the value+type namespaces.)\nexport type { User as UserT, HttpMethod, AuthConfig } from \"./types.js\";\nexport {\n HttpError,\n PalError,\n BadRequest,\n Unauthorized,\n Forbidden,\n NotFound,\n Conflict,\n TooManyRequests,\n} from \"./errors.js\";\n// Typed project errors — defineError returns an HttpError subclass and\n// self-registers {code, status, dataSchema} in the project-global error\n// registry (the OpenAPI spec twins join `RouteMeta.throws` against it).\nexport { defineError, getErrorRegistry } from \"./error-registry.js\";\nexport type { RegisteredError, DefinedError, DefinedErrorWithData } from \"./error-registry.js\";\nexport { defineWorker } from \"./worker.js\";\nexport type {\n WorkerConfig,\n ResolvedWorkerConfig,\n WorkerMeta,\n BackoffStrategy,\n} from \"./worker.js\";\nexport { defineJob } from \"./job.js\";\nexport type { JobConfig, ResolvedJobConfig, JobMeta } from \"./job.js\";\nexport { defineWebhook } from \"./webhook.js\";\nexport type {\n WebhookProvider,\n WebhookMeta,\n EnvSecretRef,\n ProviderWebhookConfig,\n CustomWebhookConfig,\n ResolvedProviderWebhook,\n ResolvedCustomWebhook,\n ResolvedWebhookConfig,\n WebhookEventHandler,\n WebhookRequest,\n ProviderEventMap,\n} from \"./webhook.js\";\nexport {\n Resource,\n __registerResource,\n __runResourceBoot,\n __shutdownResources,\n} from \"./resource.js\";\nexport type { ResourceEnv } from \"./resource.js\";\nexport { auth, storage, documents } from \"./hooks.js\";\nexport type {\n HookMeta,\n HookHandler,\n ResolvedHook,\n UserCreatedEvent,\n SignInEvent,\n SignOutEvent,\n PasswordResetEvent,\n FileUploadedEvent,\n FileDeletedEvent,\n DocumentCreatedEvent,\n DocumentUpdatedEvent,\n DocumentDeletedEvent,\n} from \"./hooks.js\";\nexport { z } from \"zod\";\n"],"mappings":"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","names":["registry","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/db/env-gen.ts","../src/config/storage.ts","../src/config/notifications.ts","../src/config/flags.ts","../src/decorators/controller.ts","../src/decorators/registry.ts","../src/decorators/methods.ts","../src/decorators/params.ts","../src/middleware.ts","../src/errors.ts","../src/error-registry.ts","../src/worker.ts","../src/job.ts","../src/webhook.ts","../src/resource.ts","../src/hooks.ts","../src/index.ts"],"sourcesContent":["/**\n * env-gen.ts — generate the `palbase-env.d.ts` text from a `defineSchema()`\n * result.\n *\n * The CLI (`palbase serve` / codegen) and the deploy pipeline call\n * {@link makeEnvDts} with the project's schema and write the returned string to\n * `palbase-env.d.ts` at the project root. That file AUGMENTS the\n * `@palbase/backend/env` `Tables` interface (controlled global augmentation,\n * C5) so `Database.tables.<name>` is typed with no import and no generic.\n *\n * The output is FLAT: each table gets a `{ row: {...}; insert: {...} }` entry\n * with plain TypeScript object types. The phantom `ColumnBuilder<...>` type\n * NEVER appears in the generated `.d.ts` — that type only lives at authoring\n * time inside `db/schema.ts`.\n */\n\nimport type { ColumnDef } from \"./columns.js\";\nimport type { SchemaDef, TableDef } from \"./schema.js\";\n\n/** The TypeScript value type for a column, ignoring nullability (added by the\n * caller). Mirrors the `ColValue` mapped type in columns.ts exactly. */\nfunction baseTsType(def: ColumnDef): string {\n switch (def.type) {\n case \"uuid\":\n case \"text\":\n case \"timestamp\":\n return \"string\";\n case \"integer\":\n return \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"jsonb\":\n return \"unknown\";\n case \"enum\": {\n const values = def.enumValues ?? [];\n if (values.length === 0) return \"string\";\n return values.map((v) => JSON.stringify(v)).join(\" | \");\n }\n default:\n return \"unknown\";\n }\n}\n\n/** The full row type for a column: base type, `| null` when nullable. */\nfunction rowType(def: ColumnDef): string {\n const base = baseTsType(def);\n return def.nullable ? `${base} | null` : base;\n}\n\n/** True when a column may be omitted on INSERT — nullable OR has any default.\n * Mirrors `ColIsOptionalOnInsert` in columns.ts. */\nfunction optionalOnInsert(def: ColumnDef): boolean {\n return (\n def.nullable === true ||\n def.defaultRandom === true ||\n def.defaultNow === true ||\n def.defaultValue !== undefined\n );\n}\n\n/** Emit the `row: {...}` and `insert: {...}` blocks for one table at the given\n * base indentation. */\nfunction tableBlock(table: TableDef, indent: string): string {\n const cols = Object.entries(table.columns);\n const rowLines = cols.map(([col, builder]) => {\n return `${indent} ${col}: ${rowType(builder._def)};`;\n });\n const insertLines = cols.map(([col, builder]) => {\n const def = builder._def;\n const opt = optionalOnInsert(def) ? \"?\" : \"\";\n return `${indent} ${col}${opt}: ${rowType(def)};`;\n });\n return [\n `${indent}${table.name}: {`,\n `${indent} row: {`,\n ...rowLines,\n `${indent} };`,\n `${indent} insert: {`,\n ...insertLines,\n `${indent} };`,\n `${indent}};`,\n ].join(\"\\n\");\n}\n\n/**\n * Generate the full `palbase-env.d.ts` text for a schema.\n *\n * @example\n * import { makeEnvDts } from \"@palbase/backend\";\n * import schema from \"./db/schema.js\";\n * writeFileSync(\"palbase-env.d.ts\", makeEnvDts(schema));\n */\nexport function makeEnvDts(schema: SchemaDef): string {\n const tableNames = Object.keys(schema.tables);\n const blocks = tableNames.map((name) => tableBlock(schema.tables[name]!, \" \"));\n const body = blocks.length > 0 ? `\\n${blocks.join(\"\\n\")}\\n ` : \"\";\n return `// AUTO-GENERATED by @palbase/backend — DO NOT EDIT.\n// Regenerated from db/schema.ts on every \\`palbase serve\\` / deploy.\n// Augments the @palbase/backend/env \\`Tables\\` interface so \\`Database.tables.*\\`\n// is typed with no import and no generic.\n\ndeclare module \"@palbase/backend/env\" {\n interface Tables {${body}}\n}\n\nexport {};\n`;\n}\n","/**\n * storage.ts — the storage-buckets config-as-code DSL.\n *\n * `defineStorage({ buckets })` is the first MODULE config-as-code surface (the\n * sibling of `db/schema.ts`'s `defineSchema`). A `config/storage.ts` file\n * default-exports a `defineStorage(...)` result; on deploy the br-pod evaluates\n * it to JSON and reconciles the declared buckets against the tenant's live\n * buckets via the Storage admin API (create missing, update changed; never\n * auto-delete — dropping a bucket is destructive and warned only).\n *\n * Buckets are DECLARATIVE: name + access + size/mime limits. The FILES inside a\n * bucket are runtime state (uploaded via the SDK), never in git.\n *\n * @example\n * import { defineStorage, bucket } from \"@palbase/backend\";\n *\n * export default defineStorage({\n * buckets: {\n * avatars: bucket({\n * public: true,\n * fileSizeLimit: \"5MB\",\n * allowedMimeTypes: [\"image/png\", \"image/jpeg\", \"image/webp\"],\n * }),\n * invoices: bucket({\n * public: false,\n * fileSizeLimit: \"20MB\",\n * allowedMimeTypes: [\"application/pdf\"],\n * }),\n * },\n * });\n *\n * The returned value is the EXACT JSON shape the runtime's generic config\n * extractor (`config_extract.js`) emits and the Go apply step parses:\n * { __config: \"storage\", buckets: { avatars: { public, fileSizeLimit, allowedMimeTypes }, ... } }\n * `fileSizeLimit` is always normalized to a number of bytes (or null when\n * omitted, meaning \"no limit\"); the apply step maps it to the Storage API's\n * `file_size_limit` field. `allowedMimeTypes` is the allowlist or null (any).\n */\n\n/** The discriminant written under `__config` so the eval/apply can tell which\n * module config a `config/*.ts` file describes. Storage is `\"storage\"`. */\nexport const STORAGE_CONFIG_KIND = \"storage\" as const;\n\n/**\n * The author-facing options for a single bucket.\n *\n * - `public`: when true the bucket is served without a signed URL. Defaults to\n * `false` (private — signed URLs only).\n * - `fileSizeLimit`: the per-object upload cap. Accepts a human string\n * (`\"5MB\"`, `\"20MB\"`, `\"1GB\"`) or a bare number of BYTES. Omit for no limit.\n * - `allowedMimeTypes`: an allowlist of MIME types accepted on upload. Omit to\n * allow any type. Each entry must look like `type/subtype` (e.g. `image/png`,\n * `application/pdf`, or a wildcard `image/*`).\n */\nexport interface BucketOptions {\n public?: boolean;\n fileSizeLimit?: string | number;\n allowedMimeTypes?: string[];\n}\n\n/**\n * The compiled, serializable bucket definition — the EXACT shape emitted to\n * JSON and consumed by the Go apply step.\n *\n * - `public`: always present (defaulted to `false`).\n * - `fileSizeLimit`: bytes as a number, or `null` for \"no limit\".\n * - `allowedMimeTypes`: the MIME allowlist, or `null` for \"any type\".\n */\nexport interface BucketDef {\n public: boolean;\n fileSizeLimit: number | null;\n allowedMimeTypes: string[] | null;\n}\n\n/** A storage config definition: the discriminant + a map of bucket name →\n * {@link BucketDef}. This is the value `defineStorage` returns and the runtime\n * config extractor serializes. */\nexport interface StorageConfig {\n __config: typeof STORAGE_CONFIG_KIND;\n buckets: Record<string, BucketDef>;\n}\n\n/** The author-facing input to `defineStorage`: a `buckets` map whose keys are\n * the bucket names and whose values are `bucket({...})` builders. */\nexport interface StorageInput {\n buckets: Record<string, BucketDef>;\n}\n\n// Powers of 1024 (binary, IEC). Storage's `file_size_limit` string parser\n// (the `bytes` npm lib, used by Supabase Storage) treats \"MB\"/\"GB\" as binary\n// multiples, so \"5MB\" = 5 * 1024 * 1024. We match that exactly so the limit a\n// user declares is the limit the bucket enforces.\nconst UNIT_BYTES: Record<string, number> = {\n b: 1,\n kb: 1024,\n mb: 1024 ** 2,\n gb: 1024 ** 3,\n tb: 1024 ** 4,\n};\n\n/**\n * Parse a `fileSizeLimit` into a number of bytes.\n *\n * - A bare number is taken as bytes verbatim (must be a non-negative integer).\n * - A string is `<number><unit>` (e.g. `\"5MB\"`, `\"1.5GB\"`, `\"500kb\"`); the unit\n * is case-insensitive and optional (a bare numeric string = bytes). Binary\n * units (1 MB = 1024 squared bytes) to match the Storage module's parser.\n *\n * Throws on a negative value, a non-finite number, or an unrecognized unit so a\n * bad limit fails at config-author time, not silently at deploy.\n */\nexport function parseFileSizeLimit(input: string | number): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0 || !Number.isInteger(input)) {\n throw new Error(\n `bucket fileSizeLimit must be a non-negative integer number of bytes, got ${input}`,\n );\n }\n return input;\n }\n\n const trimmed = input.trim();\n // <number><optional-unit>, e.g. \"5MB\", \"1.5 gb\", \"1024\", \"500kb\".\n const match = /^(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)?$/.exec(trimmed);\n if (!match) {\n throw new Error(\n `bucket fileSizeLimit string must be \"<number><unit>\" like \"5MB\" or \"1GB\", got \"${input}\"`,\n );\n }\n const value = Number.parseFloat(match[1]!);\n const unit = (match[2] ?? \"b\").toLowerCase();\n const multiplier = UNIT_BYTES[unit];\n if (multiplier === undefined) {\n throw new Error(\n `bucket fileSizeLimit has an unknown unit \"${match[2]}\" — use B, KB, MB, GB, or TB (e.g. \"5MB\")`,\n );\n }\n const bytes = value * multiplier;\n if (!Number.isFinite(bytes) || bytes < 0) {\n throw new Error(`bucket fileSizeLimit resolved to an invalid byte count: ${bytes}`);\n }\n // Round to a whole byte — a fractional byte is meaningless to the Storage API.\n return Math.round(bytes);\n}\n\n// A loose MIME-type shape check: `type/subtype`, where each side is a token of\n// alphanumerics plus the usual punctuation, and the subtype may be a wildcard\n// `*`. This is intentionally permissive (not an IANA allowlist) but rejects the\n// obvious typos (\"image\", \"imagepng\", \" \").\nconst MIME_RE = /^[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]*\\/(?:\\*|[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]*)$/;\n\n/**\n * Define a single bucket. The bucket NAME is supplied by the key in\n * `defineStorage({ buckets: { <name>: bucket({...}) } })`, so `bucket()` takes\n * only the options.\n *\n * Validates eagerly (at config-author time):\n * - `fileSizeLimit` parses to a valid non-negative byte count.\n * - each `allowedMimeTypes` entry is a `type/subtype` MIME string.\n *\n * Returns a normalized {@link BucketDef}: `public` defaulted to `false`,\n * `fileSizeLimit` as bytes-or-null, `allowedMimeTypes` deduped-or-null.\n */\nexport function bucket(opts: BucketOptions = {}): BucketDef {\n const fileSizeLimit =\n opts.fileSizeLimit === undefined ? null : parseFileSizeLimit(opts.fileSizeLimit);\n\n let allowedMimeTypes: string[] | null = null;\n if (opts.allowedMimeTypes !== undefined) {\n if (!Array.isArray(opts.allowedMimeTypes)) {\n throw new Error(\"bucket allowedMimeTypes must be an array of MIME-type strings\");\n }\n for (const mime of opts.allowedMimeTypes) {\n if (typeof mime !== \"string\" || !MIME_RE.test(mime.trim())) {\n throw new Error(\n `bucket allowedMimeTypes entry \"${mime}\" is not a valid MIME type (expected \"type/subtype\", e.g. \"image/png\")`,\n );\n }\n }\n // Dedupe + trim. An empty array means \"explicitly no types\" — keep it as []\n // rather than null so the apply step can distinguish \"any\" (null) from a\n // caller that passed [].\n allowedMimeTypes = [...new Set(opts.allowedMimeTypes.map((m) => m.trim()))];\n }\n\n return {\n public: opts.public ?? false,\n fileSizeLimit,\n allowedMimeTypes,\n };\n}\n\n/**\n * Define the storage config for a project. The bucket NAME comes from each\n * object key (authors never repeat the name). Returns the discriminated\n * {@link StorageConfig} the runtime config extractor serializes and the Go\n * apply step reconciles.\n *\n * @example\n * export default defineStorage({\n * buckets: { avatars: bucket({ public: true, fileSizeLimit: \"5MB\" }) },\n * });\n */\nexport function defineStorage(input: StorageInput): StorageConfig {\n if (input === null || typeof input !== \"object\" || typeof input.buckets !== \"object\") {\n throw new Error(\"defineStorage expects { buckets: { <name>: bucket({...}) } }\");\n }\n const buckets: Record<string, BucketDef> = {};\n for (const name of Object.keys(input.buckets)) {\n if (name.length === 0) {\n throw new Error(\"bucket name must be a non-empty string\");\n }\n const def = input.buckets[name];\n if (def === undefined) continue;\n buckets[name] = def;\n }\n return { __config: STORAGE_CONFIG_KIND, buckets };\n}\n","/**\n * notifications.ts — the notification-providers config-as-code DSL.\n *\n * `defineNotifications({ push, email, sms })` is the second MODULE config-as-code\n * surface (the sibling of `config/storage.ts`'s `defineStorage`). A\n * `config/notifications.ts` file default-exports a `defineNotifications(...)`\n * result; on deploy the br-pod evaluates it to JSON and reconciles the declared\n * providers against the tenant's live providers via the PalNotify admin API\n * (create missing; never auto-delete — dropping a provider is destructive and\n * warned only).\n *\n * SECRETS ARE NEVER IN THE FILE. Each provider's NON-SECRET fields (team id, key\n * id, bundle id, region, host, port, from address, account sid, …) are literals\n * in the config. Each provider's SECRET material (the APNs .p8 key, the FCM\n * service-account JSON, an api_key, an auth token, the SMTP/ACS password, …) is\n * NOT a field here — it is bound BY CONVENTION to a RESERVED encrypted env var\n * and resolved at deploy from control-pg. The env-key convention is:\n *\n * PB_NOTIFICATIONS_<PROVIDER>_<FIELD> (UPPER_SNAKE_CASE)\n *\n * e.g. `PB_NOTIFICATIONS_APNS_P8`, `PB_NOTIFICATIONS_FCM_SERVICE_ACCOUNT`,\n * `PB_NOTIFICATIONS_TWILIO_AUTH_TOKEN`. The `PB_` namespace is reserved (the CLI\n * refuses a hand-set `palbase secret set PB_*`); the CLI's `palbase notifications\n * add <provider>` derives the key and uploads the secret for you, so an author\n * never types either the secret or the env-key name into git.\n *\n * @example\n * import { defineNotifications } from \"@palbase/backend\";\n *\n * export default defineNotifications({\n * push: {\n * apns: { enabled: true, teamId: \"ABCDE12345\", keyId: \"KEY1234567\", bundleId: \"com.acme.app\" },\n * fcm: { enabled: true },\n * },\n * email: {\n * sendgrid: { enabled: true, fromDomain: \"mail.acme.com\" },\n * },\n * sms: {\n * twilio: { enabled: true, accountSid: \"ACxxxxxxxx\", messagingServiceSid: \"MGxxxxxxxx\" },\n * },\n * });\n *\n * The returned value is the EXACT JSON shape the runtime's generic config\n * extractor (`config_extract.js`) emits and the Go apply step parses:\n * { __config: \"notifications\", push: {...}, email: {...}, sms: {...} }\n * Only ENABLED providers carry their non-secret fields; a disabled (or absent)\n * provider serializes as `{ enabled: false }` so the apply step skips it.\n */\n\n/** The `__config` discriminant the eval/apply reads to confirm a config/*.ts is\n * a notifications config. Notifications is `\"notifications\"`. */\nexport const NOTIFICATIONS_CONFIG_KIND = \"notifications\" as const;\n\n/** The reserved env-var prefix that backs provider secrets. A `palbase secret\n * set` of a key under this prefix is REFUSED by the CLI (it is managed by\n * `palbase notifications add`). Both the CLI and the br-pod apply step derive a\n * provider's secret env key as `${RESERVED_SECRET_PREFIX}_<PROVIDER>_<FIELD>`. */\nexport const RESERVED_SECRET_PREFIX = \"PB_NOTIFICATIONS\" as const;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Provider catalog — the SINGLE SOURCE of which providers exist + their fields.\n//\n// Ported from platform/studio/src/server/trpc/routers/notifications.ts (the\n// per-provider credential zod schemas). Here the fields are split into:\n// • nonSecret: literals that live in config/notifications.ts (this file).\n// • secret: the field name(s) bound to a reserved env var (NOT in config).\n// The Go apply step + the CLI both mirror this catalog, so adding a provider is\n// a three-line change kept in lockstep across the SDK / runtime / CLI.\n// ────────────────────────────────────────────────────────────────────────────\n\n/** One provider's catalog entry: which non-secret fields are required, which are\n * optional, and which secret field-name(s) bind to reserved env vars. */\nexport interface ProviderCatalogEntry {\n /** The channel this provider serves (`push` | `email` | `sms`). */\n readonly channel: \"push\" | \"email\" | \"sms\";\n /** Non-secret config fields the author MUST supply (validated eagerly). */\n readonly required: readonly string[];\n /** Non-secret config fields the author MAY supply. */\n readonly optional: readonly string[];\n /** Secret field name(s) — each backed by `PB_NOTIFICATIONS_<PROVIDER>_<FIELD>`\n * (FIELD is the UPPER_SNAKE of the name here). NOT a config field. */\n readonly secrets: readonly string[];\n}\n\n/**\n * The provider catalog: provider key → fields. The non-secret field names are\n * the camelCase keys an author writes in `config/notifications.ts`; the secret\n * names are the snake-ish tokens that become the reserved env-var suffix.\n *\n * apns: team_id / key_id / bundle_id (+ optional is_production); secret = p8.\n * fcm: no non-secret fields; secret = service_account (the full JSON).\n * sendgrid: from_domain; secret = api_key.\n * ses: region / from_domain / access_key_id; secret = secret_access_key.\n * smtp: host / port / from_email (+ optional username, use_starttls); secret = password.\n * acs: from_email (+ optional from_name); secret = connection_string.\n * twilio: account_sid + (from_number OR messaging_service_sid); secret = auth_token.\n */\nexport const PROVIDER_CATALOG = {\n apns: {\n channel: \"push\",\n required: [\"teamId\", \"keyId\", \"bundleId\"],\n optional: [\"isProduction\"],\n secrets: [\"p8\"],\n },\n fcm: {\n channel: \"push\",\n required: [],\n optional: [],\n secrets: [\"serviceAccount\"],\n },\n sendgrid: {\n channel: \"email\",\n required: [\"fromDomain\"],\n optional: [],\n secrets: [\"apiKey\"],\n },\n ses: {\n channel: \"email\",\n required: [\"region\", \"accessKeyId\", \"fromDomain\"],\n optional: [],\n secrets: [\"secretAccessKey\"],\n },\n smtp: {\n channel: \"email\",\n required: [\"host\", \"port\", \"fromEmail\"],\n optional: [\"username\", \"useStarttls\"],\n secrets: [\"password\"],\n },\n acs: {\n channel: \"email\",\n required: [\"fromEmail\"],\n optional: [\"fromName\"],\n secrets: [\"connectionString\"],\n },\n twilio: {\n channel: \"sms\",\n // account_sid required; one of from_number / messaging_service_sid required\n // (enforced by the refine in buildProvider, not by the flat `required` list).\n required: [\"accountSid\"],\n optional: [\"fromNumber\", \"messagingServiceSid\"],\n secrets: [\"authToken\"],\n },\n} as const satisfies Record<string, ProviderCatalogEntry>;\n\n/** A provider key (`\"apns\" | \"fcm\" | \"sendgrid\" | …`). */\nexport type ProviderName = keyof typeof PROVIDER_CATALOG;\n\n// ── author-facing per-provider option types (non-secret fields only) ─────────\n\n/** APNs (Apple Push) — non-secret fields. The `.p8` key is the reserved secret\n * `PB_NOTIFICATIONS_APNS_P8` (uploaded via `palbase notifications add apns`). */\nexport interface ApnsOptions {\n enabled?: boolean;\n teamId: string;\n keyId: string;\n bundleId: string;\n /** APNs production gateway vs sandbox. Defaults to true (production). */\n isProduction?: boolean;\n}\n\n/** FCM (Firebase Cloud Messaging) — no non-secret fields; the service-account\n * JSON is the reserved secret `PB_NOTIFICATIONS_FCM_SERVICE_ACCOUNT`. */\nexport interface FcmOptions {\n enabled?: boolean;\n}\n\n/** SendGrid email — non-secret `fromDomain`; api_key is the reserved secret. */\nexport interface SendgridOptions {\n enabled?: boolean;\n fromDomain: string;\n}\n\n/** Amazon SES email — secret_access_key is the reserved secret. */\nexport interface SesOptions {\n enabled?: boolean;\n region: string;\n accessKeyId: string;\n fromDomain: string;\n}\n\n/** SMTP email — password is the reserved secret. */\nexport interface SmtpOptions {\n enabled?: boolean;\n host: string;\n port: number;\n fromEmail: string;\n username?: string;\n useStarttls?: boolean;\n}\n\n/** Azure Communication Services email — connection_string is the reserved secret. */\nexport interface AcsOptions {\n enabled?: boolean;\n fromEmail: string;\n fromName?: string;\n}\n\n/** Twilio SMS — auth_token is the reserved secret. Exactly one of `fromNumber`\n * or `messagingServiceSid` must be supplied. */\nexport interface TwilioOptions {\n enabled?: boolean;\n accountSid: string;\n fromNumber?: string;\n messagingServiceSid?: string;\n}\n\n/** The union of every provider's author-facing options. `buildProvider` accepts\n * this so each provider's typed options pass without a cast. */\nexport type ProviderOptions =\n | ApnsOptions\n | FcmOptions\n | SendgridOptions\n | SesOptions\n | SmtpOptions\n | AcsOptions\n | TwilioOptions;\n\n/** The author-facing input to `defineNotifications`. Every provider + every\n * channel is optional — declare only what you use. */\nexport interface NotificationsInput {\n push?: {\n apns?: ApnsOptions;\n fcm?: FcmOptions;\n };\n email?: {\n sendgrid?: SendgridOptions;\n ses?: SesOptions;\n smtp?: SmtpOptions;\n acs?: AcsOptions;\n };\n sms?: {\n twilio?: TwilioOptions;\n };\n}\n\n// ── compiled (serializable) provider defs — the EXACT JSON the apply parses ──\n\n/** A compiled provider def. `enabled` is always present; the remaining keys are\n * the provider's non-secret fields (verbatim from the catalog). A disabled\n * provider is `{ enabled: false }` with no other fields. */\nexport type ProviderDef = { enabled: boolean } & Record<string, unknown>;\n\n/** The compiled notifications config — the discriminant + per-channel maps of\n * provider name → {@link ProviderDef}. This is what `defineNotifications`\n * returns and the runtime extractor serializes. */\nexport interface NotificationsConfig {\n __config: typeof NOTIFICATIONS_CONFIG_KIND;\n push: Record<string, ProviderDef>;\n email: Record<string, ProviderDef>;\n sms: Record<string, ProviderDef>;\n}\n\n// ── validation + compilation ────────────────────────────────────────────────\n\n/**\n * Compile + validate one provider's author options into a {@link ProviderDef}.\n *\n * - A provider with `enabled === false` (or omitted) compiles to `{ enabled:\n * false }` and its required fields are NOT enforced (you can declare a disabled\n * provider as a placeholder without filling it in).\n * - An ENABLED provider must supply every `required` non-secret field from the\n * catalog; a missing one throws at config-author time (not silently at deploy).\n * - Only the catalog's non-secret fields (required + optional) are copied into\n * the def — an unknown extra key is ignored (it would have no effect on the\n * live provider). Secrets are never read here.\n */\nexport function buildProvider(name: ProviderName, opts: ProviderOptions): ProviderDef {\n const entry = PROVIDER_CATALOG[name];\n // A fresh, index-signature-bearing copy so catalog-driven field reads are\n // typed without a cast (the author-facing option interfaces are closed).\n const src: Record<string, unknown> = { ...opts };\n const enabled = src.enabled === undefined ? false : Boolean(src.enabled);\n if (!enabled) {\n return { enabled: false };\n }\n\n const def: ProviderDef = { enabled: true };\n\n for (const field of entry.required) {\n const value = src[field];\n if (value === undefined || value === null || value === \"\") {\n throw new Error(\n `notifications: provider \"${name}\" is enabled but missing required field \"${field}\"`,\n );\n }\n def[field] = value;\n }\n for (const field of entry.optional) {\n if (src[field] !== undefined) {\n def[field] = src[field];\n }\n }\n\n // Twilio: exactly one of fromNumber / messagingServiceSid is required (mirrors\n // the studio router's refine + the module's ValidateTwilioConfig).\n if (name === \"twilio\") {\n const hasFrom = Boolean(def.fromNumber);\n const hasMsg = Boolean(def.messagingServiceSid);\n if (!hasFrom && !hasMsg) {\n throw new Error(\n 'notifications: provider \"twilio\" requires one of \"fromNumber\" or \"messagingServiceSid\"',\n );\n }\n }\n\n return def;\n}\n\n/**\n * Define the notification-provider config for a project. Returns the\n * discriminated {@link NotificationsConfig} the runtime config extractor\n * serializes and the Go apply step reconciles. Validates eagerly: an enabled\n * provider missing a required non-secret field throws here (config-author time),\n * not at deploy.\n *\n * @example\n * export default defineNotifications({\n * push: { apns: { enabled: true, teamId: \"T\", keyId: \"K\", bundleId: \"com.x\" } },\n * });\n */\nexport function defineNotifications(input: NotificationsInput): NotificationsConfig {\n if (input === null || typeof input !== \"object\") {\n throw new Error(\"defineNotifications expects { push?, email?, sms? }\");\n }\n\n const config: NotificationsConfig = {\n __config: NOTIFICATIONS_CONFIG_KIND,\n push: {},\n email: {},\n sms: {},\n };\n\n const push = input.push ?? {};\n if (push.apns !== undefined) config.push.apns = buildProvider(\"apns\", push.apns);\n if (push.fcm !== undefined) config.push.fcm = buildProvider(\"fcm\", push.fcm);\n\n const email = input.email ?? {};\n if (email.sendgrid !== undefined) config.email.sendgrid = buildProvider(\"sendgrid\", email.sendgrid);\n if (email.ses !== undefined) config.email.ses = buildProvider(\"ses\", email.ses);\n if (email.smtp !== undefined) config.email.smtp = buildProvider(\"smtp\", email.smtp);\n if (email.acs !== undefined) config.email.acs = buildProvider(\"acs\", email.acs);\n\n const sms = input.sms ?? {};\n if (sms.twilio !== undefined) config.sms.twilio = buildProvider(\"twilio\", sms.twilio);\n\n return config;\n}\n\n/**\n * Derive the RESERVED env-var key that backs a provider's secret field, e.g.\n * `reservedSecretKey(\"apns\", \"p8\")` → `\"PB_NOTIFICATIONS_APNS_P8\"`. The CLI uses\n * this to upload the secret and the br-pod apply step uses the same derivation\n * to resolve it — they never hand-type the key, so they cannot drift.\n */\nexport function reservedSecretKey(provider: ProviderName, secretField: string): string {\n return `${RESERVED_SECRET_PREFIX}_${camelToUpperSnake(provider)}_${camelToUpperSnake(secretField)}`;\n}\n\n/** camelCase → UPPER_SNAKE_CASE (`serviceAccount` → `SERVICE_ACCOUNT`). */\nfunction camelToUpperSnake(s: string): string {\n return s.replace(/([a-z0-9])([A-Z])/g, \"$1_$2\").toUpperCase();\n}\n","/**\n * flags.ts — the feature-flag-definitions config-as-code DSL.\n *\n * `defineFlags({ flags })` is the third MODULE config-as-code surface (a sibling\n * of `config/storage.ts`'s `defineStorage` and `config/notifications.ts`'s\n * `defineNotifications`). A `config/flags.ts` file default-exports a\n * `defineFlags(...)` result; on deploy the br-pod evaluates it to JSON and\n * UPSERTS the declared flag DEFINITIONS into PalFlags (the user-flags module's\n * system-flags admin API) — create-or-update, idempotent. A live flag NOT in\n * config is NEVER auto-deleted (an orphan flag is harmless; upsert-only).\n *\n * Flags are DECLARATIVE: a flag is a typed project-wide DEFAULT (its key, type,\n * default value, an optional description, and — for string flags — an optional\n * list of allowed `variants`). The VALUE of a flag for a specific USER (a\n * per-user override / A-B assignment) is runtime state set via the SDK, NEVER in\n * git. `variants` here is part of the DEFINITION (the allowed values a string\n * flag may take), not a per-user assignment.\n *\n * NO SECRETS. Unlike notifications, a flag carries no credentials — this is the\n * simplest module config (pure declarative data).\n *\n * @example\n * import { defineFlags, flag } from \"@palbase/backend\";\n *\n * export default defineFlags({\n * flags: {\n * new_dashboard: flag({ type: \"boolean\", default: false, description: \"Roll out the new dashboard\" }),\n * max_uploads: flag({ type: \"number\", default: 10 }),\n * theme: flag({ type: \"string\", default: \"light\", variants: [\"light\", \"dark\", \"system\"] }),\n * },\n * });\n *\n * The returned value is the EXACT JSON shape the runtime's generic config\n * extractor (`config_extract.js`) emits and the Go apply step parses:\n * { __config: \"flags\", flags: { <key>: { type, default, variants, description }, ... } }\n * `variants` is the allowed-values list for a string flag, or `null` (any\n * string). `description` is the doc string, or `null`. The apply step maps the\n * author-facing `type` (\"boolean\") to PalFlags' `value_type` (\"bool\").\n */\n\n/** The `__config` discriminant the eval/apply reads to confirm a config/*.ts is\n * a flags config. Flags is `\"flags\"`. */\nexport const FLAGS_CONFIG_KIND = \"flags\" as const;\n\n/**\n * A flag's type. The author-facing vocabulary is `\"boolean\" | \"number\" |\n * \"string\"`. (The br-pod maps `\"boolean\"` to PalFlags' `value_type: \"bool\"` on\n * apply — the SDK keeps the JS-natural `\"boolean\"` so the DSL reads cleanly.)\n *\n * PalFlags also supports an `object` value type, but config-as-code flags are\n * deliberately scoped to the three SCALAR types — an object default is awkward\n * to author on the CLI and rare for a feature flag, so it is intentionally\n * omitted here (set an object flag via the Studio / API directly if needed).\n */\nexport type FlagType = \"boolean\" | \"number\" | \"string\";\n\n/** The runtime JSON value a flag's `default` may hold (matches {@link FlagType}). */\nexport type FlagValue = boolean | number | string;\n\n/**\n * The author-facing options for a single flag.\n *\n * - `type`: the flag's type — `\"boolean\" | \"number\" | \"string\"`.\n * - `default`: the project-wide default value. MUST match `type` (a `number`\n * default with `type: \"boolean\"` throws).\n * - `variants`: ONLY valid for `type: \"string\"` — the allowed values the string\n * flag may take (a DEFINITION, not a per-user assignment). When given, the\n * `default` MUST be one of the variants. Supplying `variants` on a non-string\n * type throws.\n * - `description`: an optional human description of what the flag controls.\n */\nexport interface FlagOptions {\n type: FlagType;\n default: FlagValue;\n variants?: string[];\n description?: string;\n}\n\n/**\n * The compiled, serializable flag definition — the EXACT shape emitted to JSON\n * and consumed by the Go apply step.\n *\n * - `type`: the author-facing type verbatim (`\"boolean\" | \"number\" | \"string\"`).\n * - `default`: the default value (matches `type`).\n * - `variants`: the allowed-values list for a string flag, or `null` (any).\n * - `description`: the doc string, or `null`.\n */\nexport interface FlagDef {\n type: FlagType;\n default: FlagValue;\n variants: string[] | null;\n description: string | null;\n}\n\n/** A flags config definition: the discriminant + a map of flag key →\n * {@link FlagDef}. This is the value `defineFlags` returns and the runtime\n * config extractor serializes. */\nexport interface FlagsConfig {\n __config: typeof FLAGS_CONFIG_KIND;\n flags: Record<string, FlagDef>;\n}\n\n/** The author-facing input to `defineFlags`: a `flags` map whose keys are the\n * flag keys and whose values are `flag({...})` builders. */\nexport interface FlagsInput {\n flags: Record<string, FlagDef>;\n}\n\n// Flag keys mirror PalFlags' system-flag key rule (validate.keyNameRe): a\n// letter, then letters / digits / underscores. We validate eagerly so a key the\n// CLI/SDK accepts is a key PalFlags accepts (the apply step never has to reject).\nconst FLAG_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;\n\n/** Reports whether `value` matches the declared {@link FlagType}. */\nfunction valueMatchesType(type: FlagType, value: FlagValue): boolean {\n switch (type) {\n case \"boolean\":\n return typeof value === \"boolean\";\n case \"number\":\n // A finite number only — NaN / Infinity are not serializable as JSON.\n return typeof value === \"number\" && Number.isFinite(value);\n case \"string\":\n return typeof value === \"string\";\n }\n}\n\n/**\n * Define a single flag. The flag KEY is supplied by the object key in\n * `defineFlags({ flags: { <key>: flag({...}) } })`, so `flag()` takes only the\n * options.\n *\n * Validates eagerly (at config-author time):\n * - `type` is one of `\"boolean\" | \"number\" | \"string\"`.\n * - `default` matches `type` (e.g. a `number` default with `type: \"boolean\"`\n * throws).\n * - `variants` is ONLY allowed for a string flag (variants on a non-string\n * type throws); each entry is a non-empty string; the `default` must be one\n * of the variants.\n *\n * Returns a normalized {@link FlagDef}: `variants` deduped-or-null,\n * `description` trimmed-or-null.\n */\nexport function flag(opts: FlagOptions): FlagDef {\n if (opts === null || typeof opts !== \"object\") {\n throw new Error(\"flag() expects { type, default, variants?, description? }\");\n }\n const { type } = opts;\n if (type !== \"boolean\" && type !== \"number\" && type !== \"string\") {\n throw new Error(`flag type must be \"boolean\", \"number\", or \"string\", got ${JSON.stringify(type)}`);\n }\n if (!valueMatchesType(type, opts.default)) {\n throw new Error(\n `flag default ${JSON.stringify(opts.default)} does not match type \"${type}\"`,\n );\n }\n\n let variants: string[] | null = null;\n if (opts.variants !== undefined) {\n if (type !== \"string\") {\n throw new Error(`flag variants are only valid for type \"string\" (got type \"${type}\")`);\n }\n if (!Array.isArray(opts.variants)) {\n throw new Error(\"flag variants must be an array of strings\");\n }\n for (const v of opts.variants) {\n if (typeof v !== \"string\" || v.length === 0) {\n throw new Error(`flag variant ${JSON.stringify(v)} must be a non-empty string`);\n }\n }\n // Dedupe while preserving first-seen order. An empty array is meaningless\n // (no value would be allowed) — reject it rather than silently widening.\n variants = [...new Set(opts.variants)];\n if (variants.length === 0) {\n throw new Error(\"flag variants must be a non-empty list when supplied\");\n }\n // `default` is a string here (valueMatchesType passed for type \"string\").\n if (!variants.includes(opts.default as string)) {\n throw new Error(\n `flag default ${JSON.stringify(opts.default)} is not one of its variants [${variants\n .map((v) => JSON.stringify(v))\n .join(\", \")}]`,\n );\n }\n }\n\n let description: string | null = null;\n if (opts.description !== undefined) {\n if (typeof opts.description !== \"string\") {\n throw new Error(\"flag description must be a string\");\n }\n const trimmed = opts.description.trim();\n description = trimmed.length > 0 ? trimmed : null;\n }\n\n return {\n type,\n default: opts.default,\n variants,\n description,\n };\n}\n\n/**\n * Define the feature-flag definitions for a project. The flag KEY comes from\n * each object key (authors never repeat the key). Returns the discriminated\n * {@link FlagsConfig} the runtime config extractor serializes and the Go apply\n * step UPSERTS into PalFlags.\n *\n * @example\n * export default defineFlags({\n * flags: { dark_mode: flag({ type: \"boolean\", default: false }) },\n * });\n */\nexport function defineFlags(input: FlagsInput): FlagsConfig {\n if (input === null || typeof input !== \"object\" || typeof input.flags !== \"object\") {\n throw new Error(\"defineFlags expects { flags: { <key>: flag({...}) } }\");\n }\n const flags: Record<string, FlagDef> = {};\n for (const key of Object.keys(input.flags)) {\n if (!FLAG_KEY_RE.test(key)) {\n throw new Error(\n `flag key ${JSON.stringify(key)} is invalid — must start with a letter and contain only letters, digits, and underscores`,\n );\n }\n const def = input.flags[key];\n if (def === undefined) continue;\n flags[key] = def;\n }\n return { __config: FLAGS_CONFIG_KIND, flags };\n}\n","// `@Controller(basePath, options?)` — the class decorator that marks a class as\n// a Palbase backend controller. It stamps a non-enumerable `__palbase`\n// discriminant + the resolved controller metadata onto the class so the\n// deploy/dispatch pipeline (and `isController`/`resolveController`) can detect\n// and read it without `reflect-metadata`.\nimport type { AuthSpec } from \"../endpoint.js\";\n\n/** The controller metadata stamped onto a `@Controller`-decorated class. The\n * default export of a `controllers/*.controller.ts` file resolves to this via\n * {@link resolveController}. */\nexport interface ControllerMeta {\n /** Discriminant the runtime + tooling read. */\n readonly __palbase: \"controller\";\n /** The base path every route in this controller mounts under (e.g. \"/todos\"). */\n basePath: string;\n /** Controller-level default auth, applied to routes that don't set their own\n * (`@Get(\"/x\", { auth })` overrides this). `undefined` ⇒ secure-by-default. */\n defaultAuth?: AuthSpec;\n}\n\n/** Options accepted by `@Controller`. */\nexport interface ControllerOptions {\n /** Default auth for ALL routes in this controller (route-level overrides). */\n auth?: AuthSpec;\n}\n\n/** Symbol the controller metadata is stamped under. Symbol-keyed (not a string\n * property) so it never collides with an authored member and stays off the\n * structural surface. */\nexport const CONTROLLER_META: unique symbol = Symbol.for(\"palbase.backend.controllerMeta\");\n\n/** A class carrying the stamped controller metadata + discriminant. */\ninterface ControllerCarrier {\n __palbase?: \"controller\";\n [CONTROLLER_META]?: ControllerMeta;\n}\n\n/**\n * Mark a class as a Palbase backend controller. `basePath` is the mount path\n * for every route the class declares; `options.auth` sets the controller-level\n * default auth (a route's own `auth` overrides it; absent ⇒ secure-by-default).\n *\n * @example\n * \\@Controller(\"/todos\", { auth: false })\n * export class TodosController {\n * \\@Get(\"\") list(\\@Query(ListTodosQuery) q: ListTodosQuery): TodoSchema[] { … }\n * }\n */\nexport function Controller(basePath: string, options: ControllerOptions = {}) {\n return function <T extends abstract new (...args: never[]) => object>(ctor: T): T {\n const carrier = ctor as unknown as ControllerCarrier;\n const meta: ControllerMeta = {\n __palbase: \"controller\",\n basePath,\n ...(options.auth !== undefined ? { defaultAuth: options.auth } : {}),\n };\n // Non-enumerable so it doesn't leak onto instances / structural checks.\n Object.defineProperty(carrier, CONTROLLER_META, {\n value: meta,\n enumerable: false,\n configurable: true,\n writable: false,\n });\n // The bare `__palbase` discriminant is the cheap detection marker the\n // runtime/extractor checks; keep it readable but non-enumerable.\n Object.defineProperty(carrier, \"__palbase\", {\n value: \"controller\",\n enumerable: false,\n configurable: true,\n writable: false,\n });\n return ctor;\n };\n}\n\n/** True when `value` is a `@Controller`-decorated class (cheap discriminant\n * check). Accepts the class constructor (the default export of a controller\n * file). */\nexport function isController(value: unknown): boolean {\n if (typeof value !== \"function\" && (typeof value !== \"object\" || value === null)) {\n return false;\n }\n const carrier = value as ControllerCarrier;\n return carrier.__palbase === \"controller\" && carrier[CONTROLLER_META] !== undefined;\n}\n\n/** Read the resolved controller metadata off a `@Controller`-decorated class.\n * Throws if the class was not decorated — callers should gate with\n * {@link isController} first (the loader does). */\nexport function resolveController(ctor: unknown): ControllerMeta {\n if (typeof ctor !== \"function\" && (typeof ctor !== \"object\" || ctor === null)) {\n throw new TypeError(\"resolveController: value is not a class\");\n }\n const meta = (ctor as ControllerCarrier)[CONTROLLER_META];\n if (!meta) {\n throw new TypeError(\n \"resolveController: class is not a @Controller — every controller file must `export default` a @Controller-decorated class\",\n );\n }\n return meta;\n}\n","// The decorator registry — the single plain-data store the method + parameter\n// decorators write into, and the deploy/dispatch pipeline reads back. No\n// `reflect-metadata`, no `emitDecoratorMetadata`: the registry is built from the\n// decorator arguments + the parameter INDEX that esbuild/tsc preserve for legacy\n// parameter decorators (verified — see the design spec §0/§4.1).\n//\n// A controller class carries its route metadata on a symbol-keyed static\n// property (`ROUTES`). `@Get`/`@Post`/… append a {@link RouteMeta} entry;\n// `@Body`/`@User`/… append a {@link ParamMeta} entry onto the route for the\n// method they decorate. Because parameter decorators run BEFORE the method\n// decorator for the same member (TS evaluates innermost-first, params before the\n// method), the route entry may not exist yet when a param decorator fires — so\n// param metadata is buffered per method name and merged when the method\n// decorator creates the route entry.\nimport type { AuthSpec, RateLimitConfig } from \"../endpoint.js\";\nimport type { ZodTypeAny } from \"zod\";\n\n/** The HTTP verbs a route may declare, upper-cased (the runtime router +\n * OpenAPI lower-case on their own). */\nexport type HttpMethodUpper = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/** Route-level options accepted by the method decorators (`@Get`/`@Post`/…). */\nexport interface RouteOptions {\n /** OVERRIDES the controller-level default auth for this one route. */\n auth?: AuthSpec;\n /** Per-route rate limit. */\n rateLimit?: RateLimitConfig;\n}\n\n/** The kind of value a parameter decorator injects. Drives both dispatch\n * (which request slice to inject) and codegen (which OpenAPI parameter source a\n * schema-bearing kind maps to). */\nexport type ParamKind =\n | \"body\"\n | \"query\"\n | \"param\"\n | \"headers\"\n | \"user\"\n | \"optionalUser\"\n | \"client\"\n | \"requestId\"\n | \"traceId\"\n | \"req\";\n\n/** One parameter decorator's recorded metadata. `index` is the parameter\n * position esbuild/tsc preserve; `schema` is present for the schema-bearing\n * kinds (`body`/`query`/`headers`); `name` is the path-param name for `param`. */\nexport interface ParamMeta {\n index: number;\n kind: ParamKind;\n /** Zod schema for `body`/`query`/`headers` (validation + codegen source). */\n schema?: ZodTypeAny;\n /** Path-param name for `@Param(\"id\")`. */\n name?: string;\n}\n\n/** One inferred throw site: the error CLASS name (e.g. \"TodoLocked\") and its\n * wire code (e.g. \"todo_locked\"). `status`, `hasData`, and the data JSON schema\n * are NOT carried here — they resolve from the error registry by `code` at\n * extract/openapi time (single source of truth). */\nexport interface ThrowDescriptor {\n name: string;\n code: string;\n}\n\n/** One route's recorded metadata: the verb + subpath + method name + options,\n * the ordered parameter metas, and the resolved return schema (injected by the\n * codegen step — see `returnSchema`). */\nexport interface RouteMeta {\n method: HttpMethodUpper;\n subpath: string;\n fnName: string;\n options: RouteOptions;\n params: ParamMeta[];\n /** Response schema for the route, if any. Derived from the method's RETURN\n * TYPE by codegen and written here via `recordReturn` (a generated top-level\n * IIFE injected per controller), not by an author-written decorator. */\n returnSchema?: ZodTypeAny;\n /** Error classes this route can throw, if inferred. Derived from the method\n * body + service call graph by the deploy stager's throw analysis and written\n * here via `recordThrows` (a generated top-level IIFE injected per controller,\n * the `recordReturn` twin), not by an author-written decorator. */\n throws?: ThrowDescriptor[];\n}\n\n/** Symbol the route metadata list is stored under on a controller class. Using\n * a symbol (not a string key) keeps it off the public structural surface and\n * avoids any chance of an authored property collision. */\nexport const ROUTES: unique symbol = Symbol.for(\"palbase.backend.routes\");\n\n/** Symbol the per-method buffered parameter metas are stored under while a class\n * is being decorated. Parameter decorators fire before the method decorator, so\n * they buffer here keyed by method name; the method decorator drains the buffer\n * into the route entry it creates. */\nconst PARAM_BUFFER: unique symbol = Symbol.for(\"palbase.backend.paramBuffer\");\n\n/** Symbol the per-method buffered return-type schemas are stored under while a\n * class's registry is being populated. The codegen-injected `recordReturn` call\n * can fire before OR after the method decorator; it buffers here keyed by method\n * name and `recordRoute` drains it into the route entry (and `recordReturn`\n * writes through if the route already exists). Buffering on BOTH sides means a\n * fully-formed route entry always carries its return schema — a raw-symbol\n * reader (the runtime) never has to re-merge. */\nconst RETURN_BUFFER: unique symbol = Symbol.for(\"palbase.backend.returnBuffer\");\n\n/** Symbol the per-method buffered throw descriptors are stored under while a\n * class's registry is being populated. The stager-injected `recordThrows` call\n * can fire before OR after the method decorator; it buffers here keyed by method\n * name and `recordRoute` drains it into the route entry (and `recordThrows`\n * writes through if the route already exists). Buffering on BOTH sides means a\n * fully-formed route entry always carries its throw descriptors — a raw-symbol\n * reader (the runtime) never has to re-merge. */\nconst THROWS_BUFFER: unique symbol = Symbol.for(\"palbase.backend.throwsBuffer\");\n\n/** A class constructor carrying the symbol-keyed registry slots. We type the\n * registry-bearing class as this so the decorators can read/write the slots\n * without `any` — a plain `Function` does not carry index signatures. */\ninterface RegistryCarrier {\n [ROUTES]?: RouteMeta[];\n [PARAM_BUFFER]?: Record<string, ParamMeta[]>;\n [RETURN_BUFFER]?: Record<string, ZodTypeAny>;\n [THROWS_BUFFER]?: Record<string, ThrowDescriptor[]>;\n}\n\n/** Coerce a decorated target (class constructor or its prototype) into the\n * registry carrier that owns the slots. Method/param decorators receive the\n * PROTOTYPE as their target; the class decorator receives the constructor. We\n * always anchor the registry on the CONSTRUCTOR so `getRoutes(ctor)` finds it. */\nfunction carrierOf(target: object): RegistryCarrier {\n // For instance-member decorators, `target` is the prototype; its `.constructor`\n // is the class. For a static member or the class decorator, `target` is the\n // constructor already. Resolve to the constructor either way.\n const ctor =\n typeof target === \"function\"\n ? (target as unknown as RegistryCarrier)\n : (((target as { constructor?: unknown }).constructor ??\n target) as unknown as RegistryCarrier);\n return ctor;\n}\n\n/** Get (creating if absent) the own route list for a class constructor. Own —\n * not inherited — so a subclass does not mutate its base's routes. */\nfunction ownRoutes(carrier: RegistryCarrier): RouteMeta[] {\n if (!Object.prototype.hasOwnProperty.call(carrier, ROUTES)) {\n carrier[ROUTES] = [];\n }\n return carrier[ROUTES] as RouteMeta[];\n}\n\n/** Get (creating if absent) the own per-method param buffer for a class. */\nfunction ownParamBuffer(carrier: RegistryCarrier): Record<string, ParamMeta[]> {\n if (!Object.prototype.hasOwnProperty.call(carrier, PARAM_BUFFER)) {\n carrier[PARAM_BUFFER] = {};\n }\n return carrier[PARAM_BUFFER] as Record<string, ParamMeta[]>;\n}\n\n/** Record a route (called by the method decorators). Drains any parameter\n * metas already buffered for `fnName` into the new route entry, then sorts them\n * by parameter index so dispatch can inject positionally. */\nexport function recordRoute(\n target: object,\n fnName: string,\n method: HttpMethodUpper,\n subpath: string,\n options: RouteOptions,\n): void {\n const carrier = carrierOf(target);\n const routes = ownRoutes(carrier);\n const buffer = ownParamBuffer(carrier);\n const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);\n const route: RouteMeta = { method, subpath, fnName, options, params };\n // Drain a buffered return schema (the recordReturn-ran-first ordering) so the\n // route entry is complete the moment it's created — a raw-symbol consumer\n // (the runtime extractor/worker) sees the return schema without re-merging.\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer && returnBuffer[fnName] !== undefined) {\n route.returnSchema = returnBuffer[fnName];\n }\n // Same drain for buffered throw descriptors (the recordThrows-ran-first\n // ordering) — the route entry is complete the moment it's created.\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer && throwsBuffer[fnName] !== undefined) {\n route.throws = throwsBuffer[fnName];\n }\n routes.push(route);\n}\n\n/** Record one parameter decorator (called by `@Body`/`@User`/…). Buffers per\n * method name; the method decorator merges the buffer into the route entry. If\n * the route already exists (method decorator ran first — TS does evaluate the\n * method decorator AFTER its parameter decorators, but we stay order-robust),\n * the meta is also appended directly so neither ordering loses it. */\nexport function recordParam(target: object, fnName: string, meta: ParamMeta): void {\n const carrier = carrierOf(target);\n const buffer = ownParamBuffer(carrier);\n (buffer[fnName] ??= []).push(meta);\n\n // Order-robust: if the route already exists, merge in place + keep sorted.\n const routes = carrier[ROUTES];\n if (routes) {\n const route = routes.find((r) => r.fnName === fnName);\n if (route) {\n route.params.push(meta);\n route.params.sort((a, b) => a.index - b.index);\n }\n }\n}\n\n/** Attach a return schema to the route for `fnName` (called by the codegen\n * injection that reads the method's return type). If the route does not exist\n * yet, the schema is buffered (RETURN_BUFFER) and drained into the route by\n * `recordRoute` when the method decorator runs. */\nexport function recordReturn(target: object, fnName: string, schema: ZodTypeAny): void {\n const carrier = carrierOf(target);\n const routes = carrier[ROUTES];\n const route = routes?.find((r) => r.fnName === fnName);\n if (route) {\n route.returnSchema = schema;\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(carrier, RETURN_BUFFER)) {\n carrier[RETURN_BUFFER] = {};\n }\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer) returnBuffer[fnName] = schema;\n}\n\n/** Attach the inferred throw descriptors to the route for `fnName` (called by\n * the stager-injected IIFE that carries the throw analysis result — the\n * `recordReturn` twin). If the route does not exist yet, the descriptors are\n * buffered (THROWS_BUFFER) and drained into the route by `recordRoute` when the\n * method decorator runs. */\nexport function recordThrows(target: object, fnName: string, throws: ThrowDescriptor[]): void {\n const carrier = carrierOf(target);\n const routes = carrier[ROUTES];\n const route = routes?.find((r) => r.fnName === fnName);\n if (route) {\n route.throws = throws;\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(carrier, THROWS_BUFFER)) {\n carrier[THROWS_BUFFER] = {};\n }\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer) throwsBuffer[fnName] = throws;\n}\n\n/** Read the route metadata for a controller class (the deploy/dispatch entry\n * point). Applies any buffered return schemas + throw descriptors (for the\n * recordReturn/recordThrows-runs-before orderings) and returns a defensive copy\n * so callers cannot mutate the registry.\n */\nexport function getRoutes(ctor: object): RouteMeta[] {\n const carrier = carrierOf(ctor);\n const routes = carrier[ROUTES] ?? [];\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer) {\n for (const route of routes) {\n const buffered = returnBuffer[route.fnName];\n if (buffered && route.returnSchema === undefined) {\n route.returnSchema = buffered;\n }\n }\n }\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer) {\n for (const route of routes) {\n const buffered = throwsBuffer[route.fnName];\n if (buffered && route.throws === undefined) {\n route.throws = buffered;\n }\n }\n }\n return routes.map((r) => ({\n ...r,\n params: r.params.slice(),\n ...(r.throws !== undefined ? { throws: r.throws.slice() } : {}),\n }));\n}\n","// Method decorators: `@Get` / `@Post` / `@Put` / `@Patch` / `@Delete` declare a\n// route (verb + subpath + options) on a controller method. These are LEGACY\n// method decorators (`experimentalDecorators`), receiving\n// `(prototype, methodName, descriptor)`. They write into the per-class registry\n// (registry.ts). The success-response schema is NOT declared here: it is derived\n// from the method's RETURN TYPE by a codegen step and injected onto the route at\n// runtime via `recordReturn` (registry.ts).\nimport {\n recordRoute,\n type HttpMethodUpper,\n type RouteOptions,\n} from \"./registry.js\";\n\n/** A legacy method decorator. */\ntype MethodDecorator = (\n target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor,\n) => void;\n\n/** Build a method decorator for one HTTP verb. The decorated method's name is\n * the route `fnName` (authoring sugar — the operationId is derived flat from\n * method+path by the runtime, not from this name). */\nfunction makeMethodDecorator(method: HttpMethodUpper) {\n return function (subpath: string, options: RouteOptions = {}): MethodDecorator {\n return function (target, propertyKey) {\n recordRoute(target, String(propertyKey), method, subpath, options);\n };\n };\n}\n\n/** `@Get(subpath, options?)` — declare a GET route. */\nexport const Get = makeMethodDecorator(\"GET\");\n/** `@Post(subpath, options?)` — declare a POST route. */\nexport const Post = makeMethodDecorator(\"POST\");\n/** `@Put(subpath, options?)` — declare a PUT route. */\nexport const Put = makeMethodDecorator(\"PUT\");\n/** `@Patch(subpath, options?)` — declare a PATCH route. */\nexport const Patch = makeMethodDecorator(\"PATCH\");\n/** `@Delete(subpath, options?)` — declare a DELETE route. */\nexport const Delete = makeMethodDecorator(\"DELETE\");\n","// Parameter decorators: `@Body` / `@Query` / `@Param` / `@Headers` / `@User` /\n// `@OptionalUser` / `@Client` / `@RequestId` / `@TraceId` / `@Req`. Each records\n// `{ index, kind, schema?, name? }` into the per-class registry for the method\n// it decorates. These are LEGACY parameter decorators\n// (`experimentalDecorators`), receiving `(prototype, methodName, paramIndex)` —\n// esbuild/tsc preserve the param index at runtime (verified, design §0), which\n// is how dispatch injects positionally. No type reflection\n// (`emitDecoratorMetadata`) is used: validation comes from the zod schema, the\n// type annotation the developer writes is purely for autocomplete.\nimport type { ZodTypeAny } from \"zod\";\nimport { recordParam, type ParamKind } from \"./registry.js\";\n\n/** A legacy parameter decorator. */\ntype ParameterDecorator = (\n target: object,\n propertyKey: string | symbol,\n parameterIndex: number,\n) => void;\n\n/** Build a parameter decorator that records the given kind (+ optional schema /\n * name) at the decorated parameter's index. */\nfunction makeParamDecorator(\n kind: ParamKind,\n extra?: { schema?: ZodTypeAny; name?: string },\n): ParameterDecorator {\n return function (target, propertyKey, parameterIndex) {\n recordParam(target, String(propertyKey), {\n index: parameterIndex,\n kind,\n ...(extra?.schema !== undefined ? { schema: extra.schema } : {}),\n ...(extra?.name !== undefined ? { name: extra.name } : {}),\n });\n };\n}\n\n/** `@Body(schema)` — inject the request body, validated against `schema`. The\n * developer writes `: T` (= `z.infer<schema>`, same name) for autocomplete. */\nexport function Body(schema: ZodTypeAny): ParameterDecorator {\n return makeParamDecorator(\"body\", { schema });\n}\n\n/** `@Query(schema)` — inject the parsed query params, validated against\n * `schema`. */\nexport function Query(schema: ZodTypeAny): ParameterDecorator {\n return makeParamDecorator(\"query\", { schema });\n}\n\n/** `@Headers(schema?)` — inject the request headers (lowercase keys). With a\n * schema, headers are validated + the codegen emits header parameters. */\nexport function Headers(schema?: ZodTypeAny): ParameterDecorator {\n return makeParamDecorator(\"headers\", schema !== undefined ? { schema } : undefined);\n}\n\n/** `@Param(\"id\")` — inject one matched path param by name. */\nexport function Param(name: string): ParameterDecorator {\n return makeParamDecorator(\"param\", { name });\n}\n\n/** `@User()` — inject the authenticated user (`: User`, non-null for an\n * effective-required route). The runtime resolves the effective auth. */\nexport function User(): ParameterDecorator {\n return makeParamDecorator(\"user\");\n}\n\n/** `@OptionalUser()` — inject the user as `User | null` (for routes whose\n * effective auth is `false` / `{ required: false }`). */\nexport function OptionalUser(): ParameterDecorator {\n return makeParamDecorator(\"optionalUser\");\n}\n\n/** `@Client()` — inject the parsed calling-client metadata (`: ClientInfo`). */\nexport function Client(): ParameterDecorator {\n return makeParamDecorator(\"client\");\n}\n\n/** `@RequestId()` — inject the per-request id (`: string`). */\nexport function RequestId(): ParameterDecorator {\n return makeParamDecorator(\"requestId\");\n}\n\n/** `@TraceId()` — inject the W3C trace id (`: string`). */\nexport function TraceId(): ParameterDecorator {\n return makeParamDecorator(\"traceId\");\n}\n\n/** `@Req()` — inject the raw request object (escape hatch, `: PBRequest`). */\nexport function Req(): ParameterDecorator {\n return makeParamDecorator(\"req\");\n}\n","import type { DBClient, Logger, CacheClient, PalbaseModuleClients } from \"./endpoint.js\";\nimport type { User } from \"./types.js\";\n\n/** Middleware context — subset of EndpointContext without input (not yet validated). */\nexport interface MiddlewareContext extends PalbaseModuleClients {\n params: Record<string, string>;\n query: Record<string, string>;\n headers: Record<string, string>;\n user: User | null;\n db: DBClient;\n env: Record<string, string>;\n log: Logger;\n cache: CacheClient;\n requestId: string;\n projectId: string;\n environmentId: string;\n}\n\n/** Middleware function signature — receives context and next function. */\nexport type MiddlewareHandler = (\n ctx: MiddlewareContext,\n next: () => Promise<void>,\n) => Promise<void>;\n\n/**\n * Define a middleware function for use in the middleware/ directory or\n * as endpoint-specific middleware.\n *\n * Middleware runs before the handler. Call `next()` to pass control\n * to the next middleware or handler. If `next()` is not called, the\n * handler will not execute.\n *\n * Errors thrown in middleware are caught by the pipeline and returned\n * as error responses.\n */\nexport function defineMiddleware(fn: MiddlewareHandler): MiddlewareHandler {\n return fn;\n}\n","/** HTTP error with structured error response format.\n *\n * The base class for the throwable error classes (`PalError`, `Conflict`,\n * `NotFound`, …). Construct one directly with `throw new HttpError(404,\n * \"todo_not_found\", \"No such todo\")`, or throw a named subclass\n * (`throw new NotFound(\"todo not found\")`). The runtime catches any `HttpError`\n * and emits the standard envelope; on the wire (and to iOS) it surfaces as\n * `BackendError.server(code, status, message, requestId)`.\n *\n * The optional `data` field carries a structured payload alongside the\n * standard envelope — for errors that need to ship extra context\n * (e.g. `new Conflict(\"locked\", \"title_locked\", { retryAfter: 30 })`). It rides\n * through to the iOS typed enum's associated value.\n */\nexport class HttpError extends Error {\n public readonly status: number;\n public readonly error: string;\n public readonly errorDescription: string;\n public readonly data?: unknown;\n\n constructor(status: number, error: string, errorDescription: string, data?: unknown) {\n super(errorDescription);\n this.name = \"HttpError\";\n this.status = status;\n this.error = error;\n this.errorDescription = errorDescription;\n if (data !== undefined) {\n this.data = data;\n }\n }\n\n /**\n * Serialize to the standard Palbase error response format.\n * The `requestId` is injected by the runtime layer from the request context.\n * When called without arguments (e.g. JSON.stringify), request_id is omitted.\n * When `data` is set, it is appended as a strict-superset field.\n */\n toJSON(requestId?: string): {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } {\n const result: {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } = {\n error: this.error,\n error_description: this.errorDescription,\n status: this.status,\n };\n if (requestId) {\n result.request_id = requestId;\n }\n if (this.data !== undefined) {\n result.data = this.data;\n }\n return result;\n }\n}\n\n/**\n * Throw with a custom HTTP status + wire code. The general-purpose escape hatch\n * when none of the named classes (`Conflict`/`NotFound`/…) fits.\n *\n * @example\n * throw new PalError(418, \"teapot\", \"I'm a teapot\");\n */\nexport class PalError extends HttpError {\n constructor(status: number, code: string, description: string, data?: unknown) {\n super(status, code, description, data);\n this.name = \"PalError\";\n }\n}\n\n/** Base for the named status classes. Each subclass fixes its HTTP status; the\n * `code` defaults to the class's canonical wire code (overridable), and the\n * `message` defaults to a human-readable label (overridable). */\nabstract class NamedHttpError extends HttpError {\n protected constructor(\n status: number,\n defaultCode: string,\n name: string,\n message?: string,\n code?: string,\n data?: unknown,\n ) {\n super(status, code ?? defaultCode, message ?? defaultMessage(name), data);\n this.name = name;\n }\n}\n\n/** Derive a default human-readable message from a class name\n * (\"NotFound\" → \"Not found\", \"TooManyRequests\" → \"Too many requests\"). */\nfunction defaultMessage(name: string): string {\n const spaced = name.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();\n}\n\n/**\n * 400 — the request was malformed or failed validation. Carries a fixed typed\n * payload: `new BadRequest({ fields: [{ field: \"email\", message: \"invalid\" }] })`.\n * The shape is declared once in the SDK so codegen surfaces `error.data.fields`\n * typed on the client.\n */\nexport class BadRequest extends NamedHttpError {\n public declare readonly data: BadRequestData;\n constructor(data: BadRequestData, message?: string) {\n super(400, \"bad_request\", \"BadRequest\", message, undefined, data);\n }\n}\n\n/** 401 — the caller is not authenticated. */\nexport class Unauthorized extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(401, \"unauthorized\", \"Unauthorized\", message, code, data);\n }\n}\n\n/** 403 — the caller is authenticated but not allowed. */\nexport class Forbidden extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(403, \"forbidden\", \"Forbidden\", message, code, data);\n }\n}\n\n/** 404 — the requested resource does not exist. */\nexport class NotFound extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(404, \"not_found\", \"NotFound\", message, code, data);\n }\n}\n\n/** 409 — the request conflicts with the current state. */\nexport class Conflict extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(409, \"conflict\", \"Conflict\", message, code, data);\n }\n}\n\n/** A single field-level validation failure carried by {@link BadRequest}. */\nexport interface FieldError {\n /** The offending field's name (dotted path for nested fields). */\n field: string;\n /** Human-readable reason the field failed. */\n message: string;\n}\n\n/** The fixed, typed payload {@link BadRequest} ships. */\nexport interface BadRequestData {\n /** The fields that failed validation. */\n fields: FieldError[];\n}\n\n/** The fixed, typed payload {@link TooManyRequests} ships. */\nexport interface TooManyRequestsData {\n /** Seconds the caller should wait before retrying. */\n retryAfter: number;\n}\n\n/**\n * 429 — the caller has exceeded the rate limit. Carries a fixed typed payload:\n * `new TooManyRequests({ retryAfter: 30 })`. The shape is declared once in the\n * SDK (error-registry pre-seed) so codegen surfaces `error.data.retryAfter`\n * typed on the client — no per-project definition needed.\n */\nexport class TooManyRequests extends NamedHttpError {\n public declare readonly data: TooManyRequestsData;\n constructor(data: TooManyRequestsData, message?: string) {\n super(429, \"too_many_requests\", \"TooManyRequests\", message, undefined, data);\n }\n}\n","// defineError — project-defined typed error classes + the module-global error\n// registry the OpenAPI spec twins join against.\n//\n// `defineError(code, status, dataSchema?)` returns an `HttpError` subclass whose\n// constructor is typed by the zod schema (`new TodoLocked({ retryAfter: 30 })`).\n// Each call self-registers `{ code, status, className, dataSchema }` in a\n// registry anchored on `globalThis` under `Symbol.for(...)` — mirroring the\n// ROUTES pattern — so the deploy bundle's registrations land on the single SDK\n// instance the extractor `require()`s. The registry is pre-seeded with the named\n// built-in classes (`NotFound`, `Conflict`, …) so throw inference can resolve\n// ad-hoc `throw new NotFound(...)` sites by their canonical wire codes.\n//\n// Duplicate code with a DIFFERENT shape (status OR converted data schema) is a\n// hard error at registration time (deploy fails loudly — error codes are\n// project-unique); structurally-identical re-registration is idempotent (each\n// controller BUNDLE carries its own copy of a shared models/errors.ts, so the\n// same defineError line legitimately runs once per bundle in one process).\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z, type ZodTypeAny } from \"zod\";\nimport { HttpError } from \"./errors.js\";\n\nextendZodWithOpenApi(z);\n\n/** One registered error: the wire code, HTTP status, the class name the\n * analyzer sees, the optional data payload schema, and whether it is one of the\n * pre-seeded built-in named classes. */\nexport interface RegisteredError {\n code: string;\n status: number;\n className: string;\n dataSchema?: ZodTypeAny;\n /** JSON.stringify of the converted JSON schema — equal digest ⇔ the spec\n * twins would emit an identical `data` schema for this error. */\n dataDigest?: string;\n builtin: boolean;\n}\n\n/** The cross-module-instance registry anchor (the pinned contract symbol). */\nconst ERROR_REGISTRY: unique symbol = Symbol.for(\"palbase.backend.errorRegistry\");\n\ninterface RegistryCarrier {\n [ERROR_REGISTRY]?: Map<string, RegisteredError>;\n}\n\n/** Get (creating + pre-seeding if absent) the project-global error registry,\n * keyed by wire code. Anchored on `globalThis` so every copy of the SDK in a\n * process shares the one registry. */\n/** The fixed data schemas the built-in named errors ship. Declared HERE (not in\n * errors.ts, which stays zod-free) because this is where the registry converts\n * them to JSON-schema for the spec — keying codegen's typed `error.data`. Only\n * the errors that semantically carry a payload have a schema; the rest are\n * data-less. `.int()` is preserved so the wire/codegen sees an integer. */\nconst BUILTIN_DATA_SCHEMAS: Record<string, ZodTypeAny> = {\n bad_request: z.object({\n fields: z.array(z.object({ field: z.string(), message: z.string() })),\n }),\n too_many_requests: z.object({ retryAfter: z.number().int() }),\n};\n\nexport function getErrorRegistry(): Map<string, RegisteredError> {\n const g = globalThis as RegistryCarrier;\n if (!g[ERROR_REGISTRY]) {\n const m = new Map<string, RegisteredError>();\n for (const [code, status, className] of [\n [\"bad_request\", 400, \"BadRequest\"],\n [\"unauthorized\", 401, \"Unauthorized\"],\n [\"forbidden\", 403, \"Forbidden\"],\n [\"not_found\", 404, \"NotFound\"],\n [\"conflict\", 409, \"Conflict\"],\n [\"too_many_requests\", 429, \"TooManyRequests\"],\n ] as const) {\n const dataSchema = BUILTIN_DATA_SCHEMAS[code];\n m.set(code, {\n code,\n status,\n className,\n builtin: true,\n ...(dataSchema ? { dataSchema, dataDigest: digestOf(code, dataSchema) } : {}),\n });\n }\n g[ERROR_REGISTRY] = m;\n }\n return g[ERROR_REGISTRY];\n}\n\n/** Class returned for schema-bearing errors: ctor(data, message?). */\nexport interface DefinedErrorWithData<S extends ZodTypeAny> {\n new (data: z.input<S>, message?: string): HttpError;\n readonly code: string;\n readonly status: number;\n}\n\n/** Class returned for schema-less errors: ctor(message?). */\nexport interface DefinedError {\n new (message?: string): HttpError;\n readonly code: string;\n readonly status: number;\n}\n\n/**\n * Define a project error class. MUST be called with literal args (string\n * literal `code`, numeric literal `status`) at module top level — the deploy\n * stager's static analyzer reads those literals to resolve throw sites.\n *\n * @example\n * // models/todos/errors.ts\n * export const TodoLocked = defineError(\"todo_locked\", 409, z.object({ retryAfter: z.number() }));\n * // services/todo.service.ts\n * throw new TodoLocked({ retryAfter: 30 });\n */\nexport function defineError<S extends ZodTypeAny>(\n code: string,\n status: number,\n dataSchema: S,\n): DefinedErrorWithData<S>;\nexport function defineError(code: string, status: number): DefinedError;\nexport function defineError(code: string, status: number, dataSchema?: ZodTypeAny): unknown {\n if (!Number.isInteger(status) || status < 400 || status > 599) {\n throw new Error(\n `defineError: status for \"${code}\" must be a 4xx/5xx integer, got ${status} — ` +\n `error responses must not clobber success responses in the project spec.`,\n );\n }\n const registry = getErrorRegistry();\n const existing = registry.get(code);\n const className = defaultClassName(code);\n const dataDigest = dataSchema ? digestOf(code, dataSchema) : undefined;\n if (existing) {\n const sameShape =\n existing.status === status && !existing.builtin && existing.dataDigest === dataDigest;\n if (!sameShape) {\n throw new Error(\n `defineError: duplicate error code \"${code}\" with a different shape ` +\n `(existing: status ${existing.status}${existing.builtin ? \", built-in\" : \"\"}` +\n `${existing.status === status && !existing.builtin ? \", different data schema\" : \"\"}). ` +\n `Error codes are project-unique.`,\n );\n }\n }\n // Local factories so the class-constructor closures capture a NON-optional\n // schema binding (narrowing does not flow into closures created in a branch).\n const makeWithData = (schema: ZodTypeAny) =>\n class extends HttpError {\n static readonly code = code;\n static readonly status = status;\n constructor(data: unknown, message?: string) {\n super(status, code, message ?? humanize(code), schema.parse(data));\n this.name = className;\n }\n };\n const makeWithoutData = () =>\n class extends HttpError {\n static readonly code = code;\n static readonly status = status;\n constructor(message?: string) {\n super(status, code, message ?? humanize(code));\n this.name = className;\n }\n };\n const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();\n Object.defineProperty(cls, \"name\", { value: className });\n registry.set(code, {\n code,\n status,\n className,\n builtin: false,\n ...(dataSchema ? { dataSchema } : {}),\n ...(dataDigest !== undefined ? { dataDigest } : {}),\n });\n return cls;\n}\n\n/** Convert a data schema through the SAME library the spec twins use and\n * return its JSON-schema digest (the duplicate-shape comparison key). Doubles\n * as the self-containment guard: a schema carrying `.openapi(refId)` metadata\n * (top-level or nested) converts to `$ref` pointers into components the\n * project spec never emits — a dangling ref every spec consumer would choke\n * on — so it is rejected here, at the definition site, with a fixable message. */\nfunction digestOf(code: string, dataSchema: ZodTypeAny): string {\n const TMP_REF = \"__PalbaseErrorDataDigest\";\n const tmpRegistry = new OpenAPIRegistry();\n // Wrap the schema as a property — exactly how the envelope embeds it — so a\n // TOP-LEVEL .openapi(refId) is not masked by the registration refId and\n // surfaces as the same $ref the real emission would produce.\n tmpRegistry.register(TMP_REF, z.object({ data: dataSchema }).openapi(TMP_REF));\n const generated = new OpenApiGeneratorV31(tmpRegistry.definitions).generateComponents();\n const out = generated.components?.schemas?.[TMP_REF];\n const digest = JSON.stringify(out ?? null);\n if (digest.includes('\"$ref\"')) {\n throw new Error(\n `defineError: dataSchema for \"${code}\" carries .openapi(refId) metadata — ` +\n `it would emit dangling $ref pointers in the project spec. ` +\n `Use a plain zod schema (z.object({...})) without .openapi(...).`,\n );\n }\n return digest;\n}\n\n/** \"todo_locked\" → \"TodoLocked\" (used as Error.name; the source class NAME the\n * analyzer sees is whatever the user assigned the const to — they should match\n * by convention, and the descriptor carries the analyzed const name anyway). */\nfunction defaultClassName(code: string): string {\n return code\n .split(\"_\")\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join(\"\");\n}\n\n/** \"todo_locked\" → \"Todo locked\" — the default human-readable message. */\nfunction humanize(code: string): string {\n const s = code.replace(/_/g, \" \");\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n","import type { User } from \"./types.js\";\n\n/** Non-service, per-invocation data for background worker handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface WorkerMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n /** The user that enqueued the job, if any (background jobs are usually system-initiated). */\n user: User | null;\n /** Per-invocation id for correlation. */\n requestId: string;\n projectId: string;\n environmentId: string;\n}\n\n/** Backoff strategy for worker retries. */\nexport type BackoffStrategy = \"exponential\" | \"linear\" | \"fixed\";\n\n/** Configuration for a background worker. */\nexport interface WorkerConfig<TPayload = unknown> {\n /** Worker name — must be unique across the project. */\n name: string;\n /** Maximum number of retries before sending to dead letter queue. Defaults to 3. */\n retry?: number;\n /** Execution timeout in seconds. Defaults to 30. */\n timeout?: number;\n /** Backoff strategy between retries. Defaults to \"exponential\". */\n backoff?: BackoffStrategy;\n /** Handler function that processes the job payload. */\n handler: (\n payload: TPayload,\n meta: WorkerMeta,\n ) => Promise<void>;\n}\n\n/** Resolved worker configuration with defaults applied. */\nexport interface ResolvedWorkerConfig<TPayload = unknown> {\n name: string;\n retry: number;\n timeout: number;\n backoff: BackoffStrategy;\n handler: (\n payload: TPayload,\n meta: WorkerMeta,\n ) => Promise<void>;\n}\n\n/** Valid worker name pattern: alphanumeric, underscore, hyphen only.\n * Prevents Redis key injection via colons or path separators. */\nconst VALID_WORKER_NAME = /^[a-zA-Z0-9_-]+$/;\n\n/** Default values for worker configuration. */\nconst WORKER_DEFAULTS = {\n retry: 3,\n timeout: 30,\n backoff: \"exponential\" as BackoffStrategy,\n} as const;\n\n/**\n * Define a background worker that processes jobs from the queue.\n *\n * Workers are placed in the `workers/` directory and auto-discovered at deploy time.\n *\n * @example\n * ```ts\n * export default defineWorker({\n * name: \"send-email\",\n * retry: 5,\n * timeout: 60,\n * backoff: \"exponential\",\n * handler: async (payload: { to: string; subject: string }, meta) => {\n * await sendEmail(payload.to, payload.subject);\n * },\n * });\n * ```\n */\nexport function defineWorker<TPayload = unknown>(\n config: WorkerConfig<TPayload>,\n): ResolvedWorkerConfig<TPayload> {\n if (!config.name || config.name.trim() === \"\") {\n throw new Error(\"Worker name is required\");\n }\n\n if (!VALID_WORKER_NAME.test(config.name)) {\n throw new Error(\n `Invalid worker name \"${config.name}\": must match [a-zA-Z0-9_-]+`,\n );\n }\n\n if (config.retry !== undefined && (config.retry < 0 || !Number.isInteger(config.retry))) {\n throw new Error(\"Worker retry must be a non-negative integer\");\n }\n\n if (config.timeout !== undefined && config.timeout <= 0) {\n throw new Error(\"Worker timeout must be a positive number\");\n }\n\n if (\n config.backoff !== undefined &&\n ![\"exponential\", \"linear\", \"fixed\"].includes(config.backoff)\n ) {\n throw new Error(`Invalid backoff strategy: ${config.backoff}`);\n }\n\n return {\n name: config.name,\n retry: config.retry ?? WORKER_DEFAULTS.retry,\n timeout: config.timeout ?? WORKER_DEFAULTS.timeout,\n backoff: config.backoff ?? WORKER_DEFAULTS.backoff,\n handler: config.handler,\n };\n}\n","/** Non-service, per-invocation data for job handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface JobMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n projectId: string;\n environmentId: string;\n}\n\n/** Configuration for defining a scheduled job. */\nexport interface JobConfig {\n /** Unique job name — alphanumeric, underscore, hyphen only. */\n name: string;\n /** Cron expression (e.g., \"0 3 * * *\"). */\n schedule: string;\n /** Execution timeout in seconds. Defaults to 30. */\n timeout?: number;\n /** Job handler function. */\n handler: (meta: JobMeta) => Promise<void>;\n}\n\n/** Resolved job configuration with defaults applied. */\nexport interface ResolvedJobConfig {\n name: string;\n schedule: string;\n timeout: number;\n handler: (meta: JobMeta) => Promise<void>;\n}\n\n/** Valid job name pattern: alphanumeric, underscore, hyphen only. */\nconst VALID_JOB_NAME = /^[a-zA-Z0-9_-]+$/;\n\n/** Maximum allowed timeout in seconds (5 minutes, matching sandbox limits). */\nconst MAX_TIMEOUT_SECONDS = 300;\n\n/** Default values for job configuration. */\nconst JOB_DEFAULTS = {\n timeout: 30,\n} as const;\n\n/**\n * Cron expression validation.\n * Supports standard 5-field cron: minute hour day-of-month month day-of-week.\n * Each field allows: number, *, ranges (1-5), steps (star/2), lists (1,3,5).\n */\nfunction validateCronExpression(expression: string): string | null {\n const trimmed = expression.trim();\n if (trimmed === \"\") {\n return \"Cron expression is required\";\n }\n\n const parts = trimmed.split(/\\s+/);\n if (parts.length !== 5) {\n return `Invalid cron expression \"${trimmed}\": expected 5 fields (minute hour day month weekday), got ${parts.length}`;\n }\n\n const fieldNames = [\"minute\", \"hour\", \"day of month\", \"month\", \"day of week\"];\n const fieldRanges: [number, number][] = [\n [0, 59],\n [0, 23],\n [1, 31],\n [1, 12],\n [0, 7],\n ];\n\n for (let i = 0; i < 5; i++) {\n const field = parts[i]!;\n const name = fieldNames[i]!;\n const [min, max] = fieldRanges[i]!;\n\n const error = validateCronField(field, name, min, max);\n if (error !== null) {\n return error;\n }\n }\n\n return null;\n}\n\nfunction validateCronField(\n field: string,\n name: string,\n min: number,\n max: number,\n): string | null {\n // Split by comma for lists\n const listParts = field.split(\",\");\n for (const part of listParts) {\n // Check for step: */2, 1-5/2\n const stepParts = part.split(\"/\");\n if (stepParts.length > 2) {\n return `Invalid ${name} field: \"${field}\"`;\n }\n\n const base = stepParts[0]!;\n const step = stepParts[1];\n\n if (step !== undefined) {\n const stepNum = Number(step);\n if (!Number.isInteger(stepNum) || stepNum < 1) {\n return `Invalid step value in ${name} field: \"${field}\"`;\n }\n }\n\n if (base === \"*\") {\n continue;\n }\n\n // Check for range: 1-5\n if (base.includes(\"-\")) {\n const rangeParts = base.split(\"-\");\n if (rangeParts.length !== 2) {\n return `Invalid range in ${name} field: \"${field}\"`;\n }\n const rangeStart = Number(rangeParts[0]);\n const rangeEnd = Number(rangeParts[1]);\n if (\n !Number.isInteger(rangeStart) ||\n !Number.isInteger(rangeEnd) ||\n rangeStart < min ||\n rangeEnd > max ||\n rangeStart > rangeEnd\n ) {\n return `Invalid range in ${name} field: \"${field}\"`;\n }\n continue;\n }\n\n // Single number\n const num = Number(base);\n if (!Number.isInteger(num) || num < min || num > max) {\n return `Invalid value in ${name} field: \"${field}\"`;\n }\n }\n\n return null;\n}\n\n/**\n * Define a scheduled cron job.\n *\n * Jobs are placed in the `jobs/` directory and auto-discovered at deploy time.\n *\n * @example\n * ```ts\n * export default defineJob({\n * name: \"cleanup-expired\",\n * schedule: \"0 3 * * *\", // every day at 3 AM\n * timeout: 60,\n * handler: async (meta) => {\n * await Database.delete(\"sessions\", \"expired < NOW()\");\n * Log.info(\"Cleaned up expired sessions\");\n * },\n * });\n * ```\n */\nexport function defineJob(config: JobConfig): ResolvedJobConfig {\n if (!config.name || config.name.trim() === \"\") {\n throw new Error(\"Job name is required\");\n }\n\n if (!VALID_JOB_NAME.test(config.name)) {\n throw new Error(\n `Invalid job name \"${config.name}\": must match [a-zA-Z0-9_-]+`,\n );\n }\n\n if (!config.schedule || config.schedule.trim() === \"\") {\n throw new Error(\"Job schedule is required\");\n }\n\n const cronError = validateCronExpression(config.schedule);\n if (cronError !== null) {\n throw new Error(cronError);\n }\n\n if (!config.handler) {\n throw new Error(\"Job handler is required\");\n }\n\n if (config.timeout !== undefined && config.timeout <= 0) {\n throw new Error(\"Job timeout must be a positive number\");\n }\n\n if (config.timeout !== undefined && !Number.isInteger(config.timeout)) {\n throw new Error(\"Job timeout must be an integer\");\n }\n\n if (config.timeout !== undefined && config.timeout > MAX_TIMEOUT_SECONDS) {\n throw new Error(\n `Job timeout ${config.timeout}s exceeds maximum ${MAX_TIMEOUT_SECONDS}s`,\n );\n }\n\n return {\n name: config.name,\n schedule: config.schedule.trim(),\n timeout: config.timeout ?? JOB_DEFAULTS.timeout,\n handler: config.handler,\n };\n}\n","/** Supported webhook provider types. */\nexport type WebhookProvider =\n | \"stripe\"\n | \"github\"\n | \"twilio\"\n | \"sendgrid\"\n | \"slack\"\n | \"discord\"\n | \"livekit\";\n\n/** Non-service, per-invocation data for webhook handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface WebhookMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n /** Per-invocation id for correlation. */\n requestId: string;\n projectId: string;\n environmentId: string;\n}\n\n/** Secret reference — resolves from environment variables at runtime. */\nexport interface EnvSecretRef {\n env: string;\n}\n\n/** Provider-specific event maps for type-safe event handlers. */\nexport interface ProviderEventMap {\n stripe: {\n \"checkout.session.completed\": Record<string, unknown>;\n \"payment_intent.succeeded\": Record<string, unknown>;\n \"payment_intent.payment_failed\": Record<string, unknown>;\n \"customer.subscription.created\": Record<string, unknown>;\n \"customer.subscription.updated\": Record<string, unknown>;\n \"customer.subscription.deleted\": Record<string, unknown>;\n \"invoice.paid\": Record<string, unknown>;\n \"invoice.payment_failed\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n github: {\n push: Record<string, unknown>;\n pull_request: Record<string, unknown>;\n issues: Record<string, unknown>;\n \"pull_request.opened\": Record<string, unknown>;\n \"pull_request.closed\": Record<string, unknown>;\n \"pull_request.merged\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n twilio: {\n \"message.received\": Record<string, unknown>;\n \"message.sent\": Record<string, unknown>;\n \"call.completed\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n sendgrid: {\n delivered: Record<string, unknown>;\n bounce: Record<string, unknown>;\n open: Record<string, unknown>;\n click: Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n slack: {\n url_verification: Record<string, unknown>;\n event_callback: Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n discord: {\n PING: Record<string, unknown>;\n MESSAGE_CREATE: Record<string, unknown>;\n INTERACTION_CREATE: Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n livekit: {\n \"room.started\": Record<string, unknown>;\n \"room.finished\": Record<string, unknown>;\n \"participant.joined\": Record<string, unknown>;\n \"participant.left\": Record<string, unknown>;\n [key: string]: Record<string, unknown>;\n };\n}\n\n/** Event handler function type. */\nexport type WebhookEventHandler<TEvent = Record<string, unknown>> = (\n event: TEvent,\n meta: WebhookMeta,\n) => Promise<void>;\n\n/** Provider-based webhook configuration. */\nexport interface ProviderWebhookConfig<P extends WebhookProvider = WebhookProvider> {\n provider: P;\n secret: EnvSecretRef;\n events: {\n [K in keyof ProviderEventMap[P]]?: WebhookEventHandler<ProviderEventMap[P][K]>;\n };\n}\n\n/** Incoming request representation for custom verify functions. */\nexport interface WebhookRequest {\n headers: Record<string, string>;\n body: string;\n method: string;\n url: string;\n}\n\n/** Custom webhook configuration. */\nexport interface CustomWebhookConfig {\n path: string;\n verify?: (req: WebhookRequest) => Promise<boolean> | boolean;\n handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;\n}\n\n/** Resolved provider webhook (internal). */\nexport interface ResolvedProviderWebhook<P extends WebhookProvider = WebhookProvider> {\n type: \"provider\";\n provider: P;\n secret: EnvSecretRef;\n events: Record<string, WebhookEventHandler>;\n}\n\n/** Resolved custom webhook (internal). */\nexport interface ResolvedCustomWebhook {\n type: \"custom\";\n path: string;\n verify?: (req: WebhookRequest) => Promise<boolean> | boolean;\n handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;\n}\n\n/** Union of resolved webhook types. */\nexport type ResolvedWebhookConfig = ResolvedProviderWebhook | ResolvedCustomWebhook;\n\n/** Valid webhook path pattern: starts with /, alphanumeric + hyphen + slash. */\nconst VALID_WEBHOOK_PATH = /^\\/[a-zA-Z0-9/_-]+$/;\n\n/**\n * Define a provider-based webhook.\n *\n * @example\n * ```ts\n * export default defineWebhook({\n * provider: \"stripe\",\n * secret: { env: \"STRIPE_WEBHOOK_SECRET\" },\n * events: {\n * \"checkout.session.completed\": async (event, meta) => {\n * await Database.insert(\"orders\", { status: \"paid\" });\n * },\n * },\n * });\n * ```\n */\nexport function defineWebhook<P extends WebhookProvider>(\n config: ProviderWebhookConfig<P>,\n): ResolvedProviderWebhook<P>;\n\n/**\n * Define a custom webhook with optional verification.\n *\n * @example\n * ```ts\n * export default defineWebhook({\n * path: \"/webhooks/livekit\",\n * verify: async (req) => req.headers[\"x-api-key\"] === \"secret\",\n * handler: async (payload, meta) => {\n * Log.info(\"Received webhook\", payload);\n * },\n * });\n * ```\n */\nexport function defineWebhook(config: CustomWebhookConfig): ResolvedCustomWebhook;\n\nexport function defineWebhook(\n config: ProviderWebhookConfig | CustomWebhookConfig,\n): ResolvedWebhookConfig {\n if (\"provider\" in config) {\n return validateProviderWebhook(config);\n }\n return validateCustomWebhook(config);\n}\n\nfunction validateProviderWebhook<P extends WebhookProvider>(\n config: ProviderWebhookConfig<P>,\n): ResolvedProviderWebhook<P> {\n if (!config.provider) {\n throw new Error(\"Webhook provider is required\");\n }\n\n const validProviders: WebhookProvider[] = [\n \"stripe\", \"github\", \"twilio\", \"sendgrid\", \"slack\", \"discord\", \"livekit\",\n ];\n if (!validProviders.includes(config.provider)) {\n throw new Error(\n `Invalid webhook provider \"${config.provider}\": must be one of ${validProviders.join(\", \")}`,\n );\n }\n\n if (!config.secret) {\n throw new Error(\"Webhook secret is required (use { env: \\\"SECRET_NAME\\\" })\");\n }\n\n if (typeof config.secret.env !== \"string\" || config.secret.env.trim() === \"\") {\n throw new Error(\"Webhook secret env name must be a non-empty string\");\n }\n\n if (!config.events || Object.keys(config.events).length === 0) {\n throw new Error(\"At least one event handler is required\");\n }\n\n for (const [eventName, handler] of Object.entries(config.events)) {\n if (typeof handler !== \"function\") {\n throw new Error(`Event handler for \"${eventName}\" must be a function`);\n }\n }\n\n return {\n type: \"provider\",\n provider: config.provider,\n secret: config.secret,\n events: config.events as Record<string, WebhookEventHandler>,\n };\n}\n\nfunction validateCustomWebhook(config: CustomWebhookConfig): ResolvedCustomWebhook {\n if (!config.path || config.path.trim() === \"\") {\n throw new Error(\"Webhook path is required\");\n }\n\n if (!VALID_WEBHOOK_PATH.test(config.path)) {\n throw new Error(\n `Invalid webhook path \"${config.path}\": must start with / and contain only alphanumeric, hyphen, underscore, slash`,\n );\n }\n\n if (!config.handler) {\n throw new Error(\"Webhook handler is required\");\n }\n\n if (typeof config.handler !== \"function\") {\n throw new Error(\"Webhook handler must be a function\");\n }\n\n if (config.verify !== undefined && typeof config.verify !== \"function\") {\n throw new Error(\"Webhook verify must be a function\");\n }\n\n return {\n type: \"custom\",\n path: config.path,\n verify: config.verify,\n handler: config.handler,\n };\n}\n","/**\n * resource.ts — external connections as lifecycle-managed classes.\n *\n * A `Resource` subclass models one external connection (a pooled datastore, a\n * stateless API client, or a per-user factory). The framework discovers each\n * instance, calls `init(env)` ONCE at boot with the declared secret subset, and\n * `shutdown()` (reverse order) on SIGTERM. On top of that lifecycle the author\n * exposes their own clean facade methods.\n *\n * // resources/neo4j.ts\n * import { Resource } from \"@palbase/backend\";\n * import neo4j, { type Driver, type Session } from \"neo4j-driver\";\n *\n * export class Neo4jResource extends Resource {\n * static secrets = [\"NEO4J_URL\", \"NEO4J_USER\", \"NEO4J_PASSWORD\"] as const;\n * private driver!: Driver;\n * async init(env: { NEO4J_URL: string; NEO4J_USER: string; NEO4J_PASSWORD: string }) {\n * this.driver = neo4j.driver(env.NEO4J_URL, neo4j.auth.basic(env.NEO4J_USER, env.NEO4J_PASSWORD));\n * }\n * async shutdown() { await this.driver.close(); }\n * session(): Session { return this.driver.session(); }\n * }\n * export const neo4j = new Neo4jResource(); // framework finds + manages it\n *\n * # Boot scope (NOT request-ALS)\n *\n * Resources are instantiated once at process boot. This is deliberately NOT the\n * per-request {@link AsyncLocalStorage} scope used for `Database`/`Cache`/… — a\n * connection pool must outlive a single request. The runtime discovers\n * resources from the project's `resources/` directory, registers each via\n * {@link __registerResource}, then calls {@link __runResourceBoot} before\n * serving and {@link __shutdownResources} on SIGTERM. Discovery is a runtime\n * concern; the SDK provides the base class + registry + boot/shutdown hooks.\n */\n\n/** Map a declared `secrets` tuple to the `init(env)` argument type — a record\n * over the secret names, each `string`. An empty tuple maps to an empty\n * record. */\nexport type ResourceEnv<Secrets extends readonly string[]> = {\n [K in Secrets[number]]: string;\n};\n\n/**\n * Base class for external connections.\n *\n * - `static secrets` — OPTIONAL readonly tuple of env-var names this resource\n * needs. Drives (a) the `env` type passed to `init` and (b) the\n * missing-secret check at boot (deploy fails naming the absent secret).\n * - `init(env)` — called ONCE at boot with the declared secret subset. May be\n * sync (`void`) or async (`Promise<void>`).\n * - `shutdown()` — OPTIONAL drain hook, called on SIGTERM in reverse boot\n * order.\n *\n * @example\n * import { Resource } from \"@palbase/backend\";\n * import { Client } from \"@googlemaps/google-maps-services-js\";\n *\n * export class GoogleResource extends Resource {\n * static secrets = [\"GOOGLE_MAPS_KEY\"] as const;\n * private client = new Client();\n * private key = \"\";\n * init(env: { GOOGLE_MAPS_KEY: string }) { this.key = env.GOOGLE_MAPS_KEY; }\n * }\n * export const google = new GoogleResource();\n */\nexport abstract class Resource {\n /** The env-var names this resource needs. Optional; omit for none. */\n static secrets?: readonly string[];\n\n /** Set up the connection from the declared secrets. Called once at boot. */\n abstract init(env: Record<string, string>): void | Promise<void>;\n\n /** Drain/close the connection on SIGTERM. Optional. */\n shutdown?(): void | Promise<void>;\n}\n\n/** A value carrying an optional readonly `secrets` tuple — the constructor side\n * of a Resource subclass. Narrowed from `unknown` via {@link hasSecrets}. */\ninterface WithSecrets {\n secrets?: readonly string[];\n}\n\n/** Guard: does this value carry a `secrets` array (the static on a Resource\n * subclass)? Lets us read `secrets` off `resource.constructor` (typed\n * `Function`) without a cast — narrows from `unknown` instead. */\nfunction hasSecrets(value: unknown): value is WithSecrets {\n if (typeof value !== \"object\" && typeof value !== \"function\") return false;\n const secrets = (value as { secrets?: unknown }).secrets;\n return secrets === undefined || Array.isArray(secrets);\n}\n\n/** A registered resource plus whether its `init` has already run. The\n * `booted` flag makes {@link __runResourceBoot} idempotent (re-running boot,\n * e.g. across a re-entrant deploy path, never re-inits). */\ninterface RegistryEntry {\n resource: Resource;\n booted: boolean;\n}\n\n/** Module-level boot registry. Single per process (the br-pod is\n * single-project). Order is registration order; shutdown reverses it. */\nconst registry: RegistryEntry[] = [];\n\n/**\n * Register a resource instance with the boot registry. The runtime calls this\n * for each instance discovered under the project's `resources/` directory.\n * NOT part of the public author-facing API (prefixed `__`).\n */\nexport function __registerResource(resource: Resource): void {\n registry.push({ resource, booted: false });\n}\n\n/** Read the declared `secrets` tuple off a resource instance's constructor. */\nfunction declaredSecrets(resource: Resource): readonly string[] {\n const ctor: unknown = resource.constructor;\n return hasSecrets(ctor) ? (ctor.secrets ?? []) : [];\n}\n\n/**\n * Boot every registered resource that has not yet been booted: resolve its\n * declared secret subset from `envMap`, then await its `init(env)`. Idempotent\n * — an already-booted resource is skipped. Throws (failing deploy/boot) when a\n * declared secret is absent, naming the missing secret. NOT part of the public\n * author-facing API.\n */\nexport async function __runResourceBoot(envMap: Record<string, string>): Promise<void> {\n for (const entry of registry) {\n if (entry.booted) continue;\n const secrets = declaredSecrets(entry.resource);\n const env: Record<string, string> = {};\n for (const name of secrets) {\n const value = envMap[name];\n if (value === undefined) {\n throw new Error(\n `Resource ${entry.resource.constructor.name} requires secret \"${name}\" but it is not set. ` +\n `Set it with \\`palbase secret set ${name} ...\\` (or in Studio) and redeploy.`,\n );\n }\n env[name] = value;\n }\n await entry.resource.init(env);\n entry.booted = true;\n }\n}\n\n/**\n * Shut down every booted resource in REVERSE registration order, awaiting each\n * `shutdown()` (a no-op when undefined). Clears the registry afterwards so a\n * second call is a no-op. NOT part of the public author-facing API.\n */\nexport async function __shutdownResources(): Promise<void> {\n for (let i = registry.length - 1; i >= 0; i -= 1) {\n const entry = registry[i]!;\n if (entry.booted && entry.resource.shutdown) {\n await entry.resource.shutdown();\n }\n }\n registry.length = 0;\n}\n\n/** Clear the registry without running shutdown. TEST-only helper so each test\n * starts from a clean registry. NOT part of the public author-facing API. */\nexport function __resetResources(): void {\n registry.length = 0;\n}\n","/** Non-service, per-invocation data for hook handlers.\n * Services (Database, Log, …) are imported as singletons, not passed here. */\nexport interface HookMeta {\n /** Branch-scoped env vars. */\n env: Record<string, string>;\n projectId: string;\n environmentId: string;\n}\n\n// --- Auth Event Payloads ---\n\n/** Payload for auth.onUserCreated hook. */\nexport interface UserCreatedEvent {\n user: {\n id: string;\n /** User's email, if they signed up with one (absent for phone-only users). */\n email?: string;\n role: string;\n metadata: Record<string, unknown>;\n createdAt: string;\n };\n}\n\n/** Payload for auth.onSignIn hook. */\nexport interface SignInEvent {\n user: {\n id: string;\n /** User's email, if they have one (absent for phone-only users). */\n email?: string;\n role: string;\n };\n provider: string;\n timestamp: string;\n}\n\n/** Payload for auth.onSignOut hook. */\nexport interface SignOutEvent {\n user: {\n id: string;\n /** User's email, if they have one (absent for phone-only users). */\n email?: string;\n };\n timestamp: string;\n}\n\n/** Payload for auth.onPasswordReset hook.\n * Password reset is inherently email-based, so `email` is always present here\n * (a phone-only passwordless user cannot trigger this event). */\nexport interface PasswordResetEvent {\n user: {\n id: string;\n email: string;\n };\n timestamp: string;\n}\n\n// --- Storage Event Payloads ---\n\n/** Payload for storage.onFileUploaded hook. */\nexport interface FileUploadedEvent {\n file: {\n id: string;\n name: string;\n bucket: string;\n path: string;\n size: number;\n contentType: string;\n };\n}\n\n/** Payload for storage.onFileDeleted hook. */\nexport interface FileDeletedEvent {\n file: {\n id: string;\n name: string;\n bucket: string;\n path: string;\n };\n}\n\n// --- Documents Event Payloads ---\n\n/** Payload for documents.onDocumentCreated hook. */\nexport interface DocumentCreatedEvent {\n document: {\n id: string;\n collection: string;\n data: Record<string, unknown>;\n };\n}\n\n/** Payload for documents.onDocumentUpdated hook. */\nexport interface DocumentUpdatedEvent {\n document: {\n id: string;\n collection: string;\n data: Record<string, unknown>;\n previousData: Record<string, unknown>;\n };\n}\n\n/** Payload for documents.onDocumentDeleted hook. */\nexport interface DocumentDeletedEvent {\n document: {\n id: string;\n collection: string;\n data: Record<string, unknown>;\n };\n}\n\n// --- Hook Handler Types ---\n\nexport type HookHandler<TEvent> = (event: TEvent, meta: HookMeta) => Promise<void>;\n\n/** Resolved hook configuration (internal). */\nexport interface ResolvedHook<TEvent = unknown> {\n module: string;\n event: string;\n handler: HookHandler<TEvent>;\n}\n\n// --- Auth Hooks ---\n\nexport const auth = {\n onUserCreated(handler: HookHandler<UserCreatedEvent>): ResolvedHook<UserCreatedEvent> {\n return { module: \"auth\", event: \"user.created\", handler };\n },\n\n onSignIn(handler: HookHandler<SignInEvent>): ResolvedHook<SignInEvent> {\n return { module: \"auth\", event: \"user.sign_in\", handler };\n },\n\n onSignOut(handler: HookHandler<SignOutEvent>): ResolvedHook<SignOutEvent> {\n return { module: \"auth\", event: \"user.sign_out\", handler };\n },\n\n onPasswordReset(handler: HookHandler<PasswordResetEvent>): ResolvedHook<PasswordResetEvent> {\n return { module: \"auth\", event: \"user.password_reset\", handler };\n },\n};\n\n// --- Storage Hooks ---\n\nexport const storage = {\n onFileUploaded(handler: HookHandler<FileUploadedEvent>): ResolvedHook<FileUploadedEvent> {\n return { module: \"storage\", event: \"file.uploaded\", handler };\n },\n\n onFileDeleted(handler: HookHandler<FileDeletedEvent>): ResolvedHook<FileDeletedEvent> {\n return { module: \"storage\", event: \"file.deleted\", handler };\n },\n};\n\n// --- Documents Hooks ---\n\nexport const documents = {\n onDocumentCreated(handler: HookHandler<DocumentCreatedEvent>): ResolvedHook<DocumentCreatedEvent> {\n return { module: \"documents\", event: \"document.created\", handler };\n },\n\n onDocumentUpdated(handler: HookHandler<DocumentUpdatedEvent>): ResolvedHook<DocumentUpdatedEvent> {\n return { module: \"documents\", event: \"document.updated\", handler };\n },\n\n onDocumentDeleted(handler: HookHandler<DocumentDeletedEvent>): ResolvedHook<DocumentDeletedEvent> {\n return { module: \"documents\", event: \"document.deleted\", handler };\n },\n};\n","export type {\n PBRequest,\n ClientInfo,\n RateLimitConfig,\n DBClient,\n DBOps,\n TxClient,\n FileContext,\n QueueClient,\n Logger,\n CacheClient,\n PalbaseDocsClient,\n PalbaseCollectionRef,\n PalbaseDocumentRef,\n PalbaseDocumentSnapshot,\n PalbaseQuerySnapshot,\n PalbaseWhereOperator,\n PalbaseResult,\n Middleware,\n ErrorDef,\n ErrorMap,\n ErrorThrowers,\n} from \"./endpoint.js\";\nexport {\n Database,\n Documents,\n Storage,\n Cache,\n Queue,\n Log,\n Notifications,\n Flags,\n Realtime,\n __setRuntime,\n __runWithRuntime,\n __requestALS,\n __getRuntime,\n} from \"./runtime.js\";\nexport type { RuntimeServices } from \"./runtime.js\";\nexport type {\n PalbaseAuthClient,\n PalbaseStorageClient,\n PalbaseBucketClient,\n PalbaseRealtimeClient,\n PalbaseFunctionsClient,\n PalbaseInvokeOptions,\n PalbaseFlagsClient,\n PalbaseFlagsServiceClient,\n PalbaseFlagContext,\n PalbaseFlagVariant,\n PalbaseFlag,\n PalbaseFlagValue,\n PalbaseFlagSource,\n PalbaseSetOverrideResult,\n PalbaseSetOverridesResult,\n PalbaseClearOverrideResult,\n PalbaseClearAllOverridesResult,\n PalbaseBatchOverrideOperation,\n PalbaseBatchSetOverridesResult,\n PalbaseNotificationsClient,\n PalbasePushClient,\n PalbaseEmailClient,\n PalbaseSmsClient,\n PalbaseInboxClient,\n PalbasePreferencesClient,\n PalbaseAnalyticsClient,\n PalbaseAnalyticsQueryNamespace,\n PalbaseAnalyticsManagementNamespace,\n PalbaseLinksClient,\n PalbaseCmsClient,\n // Shared local types\n PalbaseUser,\n PalbaseSession,\n PalbaseDeviceInfo,\n PalbaseAttestAndroidParams,\n PalbaseAttestAndroidResult,\n PalbaseAttestiOSParams,\n PalbaseAttestiOSResult,\n PalbaseBindDeviceParams,\n PalbaseVerifyRequestSignatureParams,\n PalbaseFileObject,\n PalbaseSignedUrlResponse,\n PalbasePublicUrlResponse,\n PalbaseUploadOptions,\n PalbaseTransformOptions,\n PalbaseListOptions,\n PalbasePushSendParams,\n PalbasePushSendResponse,\n PalbaseEmailSendParams,\n PalbaseEmailSendResponse,\n PalbaseSmsSendParams,\n PalbaseSmsSendResponse,\n PalbaseInboxSendParams,\n PalbaseInboxSendResponse,\n PalbaseInboxMessage,\n PalbaseInboxListOptions,\n PalbaseInboxListResult,\n PalbasePreferences,\n PalbaseRegisterDeviceParams,\n PalbaseDeviceTokenView,\n PalbaseMultiChannelResponse,\n PalbaseAnalyticsProperties,\n PalbaseIdentifyTraits,\n PalbaseCountQueryInput,\n PalbaseCountResult,\n PalbaseEventsQueryInput,\n PalbaseEventsResult,\n PalbaseUsersQueryInput,\n PalbaseUsersResult,\n PalbaseFunnelQueryInput,\n PalbaseFunnelResult,\n PalbaseRetentionQueryInput,\n PalbaseRetentionResult,\n PalbaseCohortQueryInput,\n PalbaseCohortResult,\n PalbaseOverviewResult,\n PalbaseEventNamesResult,\n PalbaseUserDetailResult,\n PalbaseCreateLinkParams,\n PalbaseUpdateLinkParams,\n PalbaseLink,\n PalbaseLinkDetails,\n PalbaseLinkAnalytics,\n PalbaseQrCodeOptions,\n PalbaseMatchParams,\n PalbaseInitialLink,\n PalbaseListLinksOptions,\n PalbaseListLinksResult,\n PalbaseCmsFindOptions,\n PalbaseCmsFindOneOptions,\n} from \"./clients.js\";\nexport { defineSchema } from \"./db/schema.js\";\nexport type { SchemaDef, TableDef, TableInput, ColumnMap, SchemaInput } from \"./db/schema.js\";\nexport { policy, PolicyBuilder } from \"./db/policy.js\";\nexport type { PolicyDef, PolicyCommand, PolicyMode } from \"./db/policy.js\";\nexport { PALBASE_EXTENSIONS, EXTENSION_DEPENDENCIES, isPalbaseExtension } from \"./db/extensions.js\";\nexport type { PalbaseExtension } from \"./db/extensions.js\";\nexport { uuid, text, integer, boolean, timestamp, jsonb, enumType } from \"./db/columns.js\";\nexport type { ColumnBuilder, ColumnDef, ColumnType, OnDeleteAction } from \"./db/columns.js\";\nexport { makeTypedDB } from \"./db/typed-db.js\";\nexport type {\n TypedDB,\n TypedTx,\n TypedTable,\n InsertShape,\n RowShape,\n EnvTypedDatabase,\n EnvServiceDatabase,\n EnvTypedTx,\n EnvTypedTable,\n EnvTables,\n} from \"./db/typed-db.js\";\nexport type { Tables, TableTypes } from \"./db/env.js\";\nexport { makeEnvDts } from \"./db/env-gen.js\";\n// Module config-as-code DSLs (sibling of defineSchema). config/storage.ts\n// default-exports defineStorage(...); the deploy evals + applies the buckets.\nexport { defineStorage, bucket, parseFileSizeLimit, STORAGE_CONFIG_KIND } from \"./config/storage.js\";\nexport type { BucketDef, BucketOptions, StorageConfig, StorageInput } from \"./config/storage.js\";\n// config/notifications.ts default-exports defineNotifications(...); the deploy\n// evals + applies the providers (non-secret fields here, cert/key secrets bound\n// by convention to reserved PB_NOTIFICATIONS_* env vars resolved at deploy).\nexport {\n defineNotifications,\n buildProvider,\n reservedSecretKey,\n PROVIDER_CATALOG,\n NOTIFICATIONS_CONFIG_KIND,\n RESERVED_SECRET_PREFIX,\n} from \"./config/notifications.js\";\nexport type {\n NotificationsConfig,\n NotificationsInput,\n ProviderDef,\n ProviderName,\n ProviderOptions,\n ProviderCatalogEntry,\n ApnsOptions,\n FcmOptions,\n SendgridOptions,\n SesOptions,\n SmtpOptions,\n AcsOptions,\n TwilioOptions,\n} from \"./config/notifications.js\";\n// config/flags.ts default-exports defineFlags(...); the deploy evals + UPSERTS\n// the declared flag DEFINITIONS into PalFlags (upsert-only, never auto-deleted).\n// The simplest module config — pure declarative data, no secrets.\nexport { defineFlags, flag, FLAGS_CONFIG_KIND } from \"./config/flags.js\";\nexport type {\n FlagDef,\n FlagOptions,\n FlagsConfig,\n FlagsInput,\n FlagType,\n FlagValue,\n} from \"./config/flags.js\";\n// Class-controller decorator model (replaces defineController/defineHandler/route).\nexport { Controller } from \"./decorators/controller.js\";\nexport type { ControllerOptions } from \"./decorators/controller.js\";\nexport { Get, Post, Put, Patch, Delete } from \"./decorators/methods.js\";\nexport {\n Body,\n Query,\n Headers,\n Param,\n User,\n OptionalUser,\n Client,\n RequestId,\n TraceId,\n Req,\n} from \"./decorators/params.js\";\nexport type { RouteOptions, HttpMethodUpper, ThrowDescriptor } from \"./decorators/registry.js\";\n// recordThrows is the stager-injected carrier for inferred throw descriptors\n// (the recordReturn twin) — public so the injected IIFE in a deployed bundle\n// can call it via `require(\"@palbase/backend\").recordThrows(...)`.\nexport { recordThrows } from \"./decorators/registry.js\";\nexport { defineMiddleware } from \"./middleware.js\";\nexport type { MiddlewareContext, MiddlewareHandler } from \"./middleware.js\";\n// The authenticated-user TYPE is exported as `UserT` (not `User`) because the\n// value name `User` is the @User() parameter decorator (exported above). A\n// controller annotates `@User() user: UserT` — decorator for the value\n// position, `UserT` for the type. (NestJS-style: same name as the decorator\n// would collide in the value+type namespaces.)\nexport type { User as UserT, HttpMethod, AuthConfig } from \"./types.js\";\nexport {\n HttpError,\n PalError,\n BadRequest,\n Unauthorized,\n Forbidden,\n NotFound,\n Conflict,\n TooManyRequests,\n} from \"./errors.js\";\n// Typed project errors — defineError returns an HttpError subclass and\n// self-registers {code, status, dataSchema} in the project-global error\n// registry (the OpenAPI spec twins join `RouteMeta.throws` against it).\nexport { defineError, getErrorRegistry } from \"./error-registry.js\";\nexport type { RegisteredError, DefinedError, DefinedErrorWithData } from \"./error-registry.js\";\nexport { defineWorker } from \"./worker.js\";\nexport type {\n WorkerConfig,\n ResolvedWorkerConfig,\n WorkerMeta,\n BackoffStrategy,\n} from \"./worker.js\";\nexport { defineJob } from \"./job.js\";\nexport type { JobConfig, ResolvedJobConfig, JobMeta } from \"./job.js\";\nexport { defineWebhook } from \"./webhook.js\";\nexport type {\n WebhookProvider,\n WebhookMeta,\n EnvSecretRef,\n ProviderWebhookConfig,\n CustomWebhookConfig,\n ResolvedProviderWebhook,\n ResolvedCustomWebhook,\n ResolvedWebhookConfig,\n WebhookEventHandler,\n WebhookRequest,\n ProviderEventMap,\n} from \"./webhook.js\";\nexport {\n Resource,\n __registerResource,\n __runResourceBoot,\n __shutdownResources,\n} from \"./resource.js\";\nexport type { ResourceEnv } from \"./resource.js\";\nexport { auth, storage, documents } from \"./hooks.js\";\nexport type {\n HookMeta,\n HookHandler,\n ResolvedHook,\n UserCreatedEvent,\n SignInEvent,\n SignOutEvent,\n PasswordResetEvent,\n FileUploadedEvent,\n FileDeletedEvent,\n DocumentCreatedEvent,\n DocumentUpdatedEvent,\n DocumentDeletedEvent,\n} from \"./hooks.js\";\nexport { z } from \"zod\";\n"],"mappings":"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","names":["registry","z"]}
|