@forinda/kickjs 5.16.0 → 5.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/{application-DnnqoDWx.mjs → application-CRmMytW1.mjs} +3 -3
  2. package/dist/{application-DnnqoDWx.mjs.map → application-CRmMytW1.mjs.map} +1 -1
  3. package/dist/{bootstrap-CFK2aAjA.mjs → bootstrap-CHmCh_cE.mjs} +3 -3
  4. package/dist/{bootstrap-CFK2aAjA.mjs.map → bootstrap-CHmCh_cE.mjs.map} +1 -1
  5. package/dist/config/index.mjs +2 -2
  6. package/dist/{config-BW5taOVX.mjs → config-BvyPboRZ.mjs} +3 -3
  7. package/dist/{config-BW5taOVX.mjs.map → config-BvyPboRZ.mjs.map} +1 -1
  8. package/dist/{container-D1gfs6__.mjs → container-CPz0l8-b.mjs} +3 -3
  9. package/dist/{container-D1gfs6__.mjs.map → container-CPz0l8-b.mjs.map} +1 -1
  10. package/dist/context-C6e-S0uc.mjs +12 -0
  11. package/dist/context-C6e-S0uc.mjs.map +1 -0
  12. package/dist/{context-Dnt2Di7p.d.mts → context-DV7RjM9F.d.mts} +14 -3
  13. package/dist/context-DV7RjM9F.d.mts.map +1 -0
  14. package/dist/core/adapter.mjs +1 -1
  15. package/dist/core/app-module.mjs +1 -1
  16. package/dist/core/cache.mjs +1 -1
  17. package/dist/core/container.mjs +2 -2
  18. package/dist/core/cron.mjs +2 -2
  19. package/dist/core/decorators.mjs +2 -2
  20. package/dist/core/errors.mjs +1 -1
  21. package/dist/core/interfaces.mjs +1 -1
  22. package/dist/core/logger.mjs +1 -1
  23. package/dist/core/path.mjs +1 -1
  24. package/dist/core/plugin.mjs +1 -1
  25. package/dist/core/reactivity.mjs +1 -1
  26. package/dist/{core-Db2uyqtM.mjs → core-C_gfh7JQ.mjs} +3 -3
  27. package/dist/{core-Db2uyqtM.mjs.map → core-C_gfh7JQ.mjs.map} +1 -1
  28. package/dist/{env-nwsfCpdy.mjs → env-LIrKUSAJ.mjs} +3 -3
  29. package/dist/{env-nwsfCpdy.mjs.map → env-LIrKUSAJ.mjs.map} +1 -1
  30. package/dist/http/application.mjs +2 -2
  31. package/dist/http/bootstrap.mjs +2 -2
  32. package/dist/http/context.d.mts +1 -1
  33. package/dist/http/context.mjs +2 -2
  34. package/dist/http/middleware/cors.mjs +1 -1
  35. package/dist/http/middleware/csrf.mjs +1 -1
  36. package/dist/http/middleware/error-handler.mjs +2 -2
  37. package/dist/http/middleware/helmet.mjs +1 -1
  38. package/dist/http/middleware/rate-limit.mjs +1 -1
  39. package/dist/http/middleware/request-id.mjs +1 -1
  40. package/dist/http/middleware/request-logger.mjs +2 -2
  41. package/dist/http/middleware/session.mjs +1 -1
  42. package/dist/http/middleware/spa.mjs +2 -2
  43. package/dist/http/middleware/upload.mjs +1 -1
  44. package/dist/http/middleware/validate.mjs +2 -2
  45. package/dist/http/middleware/views.d.mts +1 -1
  46. package/dist/http/middleware/views.mjs +2 -2
  47. package/dist/http/query/index.d.mts +2 -2
  48. package/dist/http/query/index.mjs +2 -2
  49. package/dist/http/router-builder.mjs +2 -2
  50. package/dist/{index-kEmG8nWU.d.mts → index-BtWJ9SLP.d.mts} +53 -7
  51. package/dist/{index-kEmG8nWU.d.mts.map → index-BtWJ9SLP.d.mts.map} +1 -1
  52. package/dist/index.d.mts +4 -4
  53. package/dist/index.mjs +2 -2
  54. package/dist/index.mjs.map +1 -1
  55. package/dist/{metadata-Cll3P0N0.mjs → metadata-D8hSFaux.mjs} +2 -2
  56. package/dist/{metadata-Cll3P0N0.mjs.map → metadata-D8hSFaux.mjs.map} +1 -1
  57. package/dist/query-NhwucLHS.mjs +12 -0
  58. package/dist/query-NhwucLHS.mjs.map +1 -0
  59. package/dist/{views-BCl-mETX.d.mts → views-DkllbVB3.d.mts} +2 -2
  60. package/dist/{views-BCl-mETX.d.mts.map → views-DkllbVB3.d.mts.map} +1 -1
  61. package/package.json +1 -1
  62. package/dist/context-Dnt2Di7p.d.mts.map +0 -1
  63. package/dist/context-MD7Mc_o_.mjs +0 -12
  64. package/dist/context-MD7Mc_o_.mjs.map +0 -1
  65. package/dist/query-D-FiLfmd.mjs +0 -12
  66. package/dist/query-D-FiLfmd.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"env-nwsfCpdy.mjs","names":[],"sources":["../src/config/env.ts"],"sourcesContent":["import type { z } from 'zod'\nimport { detectSchema, type InferSchemaOutput } from '@forinda/kickjs-schema'\nimport { Container } from '../core/container'\nimport type { EnvKey } from '../core/decorators'\n\n/**\n * Lazy `zod` accessor. `zod` is an **optional** peer of\n * `@forinda/kickjs` — only the Zod-based env helpers (`baseEnvSchema`,\n * `defineEnv`, `loadEnv`) need it. Apps that validate env via Valibot /\n * Yup / Standard Schema go through `loadEnvFromSchema` (which only\n * touches the validator-agnostic `detectSchema`) and never load zod.\n *\n * Importing `zod` at module top-level made it a *hard* dependency of\n * the whole framework: `import { anything } from '@forinda/kickjs'`\n * eagerly evaluated this module, so a non-Zod app with no `zod`\n * installed crashed at module load / build time (\"Cannot find module\n * 'zod'\"). Deferring the require to first use keeps the import out of\n * the eager graph.\n */\nlet _zod: typeof import('zod') | undefined\nfunction getZod(): typeof import('zod') {\n if (_zod) return _zod\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _zod = require('zod') as typeof import('zod')\n } catch {\n throw new Error(\n \"@forinda/kickjs: the 'zod' peer dependency is required for \" +\n 'baseEnvSchema / defineEnv / loadEnv but is not installed. ' +\n 'Install it (`pnpm add zod`), or define your env with a non-Zod ' +\n 'schema and call loadEnvFromSchema() instead — @forinda/kickjs-schema ' +\n 'supports Valibot, Yup, and any Standard Schema validator without zod.',\n )\n }\n return _zod\n}\n\n/**\n * Lazily load `.env` via dotenv if it is installed in the consumer app.\n * `dotenv` is an **optional peer dependency** of `@forinda/kickjs` —\n * apps that want `.env` file support install it themselves (the CLI\n * generators add it to every scaffolded `package.json`). Apps that\n * inject env via the shell, Docker, or a secret manager can skip it\n * entirely and `process.env` is read as-is.\n *\n * We call `dotenv.config()` directly rather than `require('dotenv/config')`\n * so that a re-evaluation of this module (e.g. Vite SSR full-reload after\n * editing `.env`) can re-read the file. `dotenv/config` is a side-effect\n * shim that CJS caches aggressively, so the second `require` is a no-op\n * and new keys never reach `process.env` — the root cause of the\n * \"DATABASE_URL undefined until restart\" bug on Windows dev.\n */\nfunction tryLoadDotenv(): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const dotenv = require('dotenv')\n // `quiet: true` suppresses dotenv 17's tip banner that goes to stdout.\n // Required for CLI tools whose stdout is parsed by callers (`kick explain\n // --json`, etc) — the banner would otherwise corrupt the JSON output.\n dotenv.config({ override: false, quiet: true })\n } catch {\n // dotenv not installed — fall through, env vars must come from\n // the environment directly. This is a valid deployment style.\n }\n}\ntryLoadDotenv()\n\n/**\n * Build the base env schema. Memoized so repeated calls reuse one\n * schema instance (matters for the `cachedSchema === s` identity check\n * in {@link loadEnv}). Loads zod lazily via {@link getZod}.\n */\n// Build the concrete base schema. This function is the single source of\n// the *type* of `baseEnvSchema` (via `ReturnType<typeof buildBaseEnvSchema>`)\n// AND its runtime value (via `getBaseEnvSchema`). It is NEVER called at\n// module load — only lazily through `getBaseEnvSchema()` — so referencing\n// its return type stays compile-time-only and pulls no eager zod import.\n// Spelling the type this way (rather than `z.ZodObject<any>`) preserves the\n// precise PORT/NODE_ENV/LOG_LEVEL shape, which `defineEnv` composition and\n// the `kick typegen` `KickEnv` inference depend on.\nfunction buildBaseEnvSchema() {\n const z = getZod()\n return z.object({\n // Server\n PORT: z.coerce.number().default(3000),\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n LOG_LEVEL: z.string().default('info'),\n })\n}\n\n/** Precise type of the base env schema — no runtime value, no eager zod. */\nexport type BaseEnvSchema = ReturnType<typeof buildBaseEnvSchema>\n\n/**\n * Memoized base schema. Reuses one instance so the `cachedSchema === s`\n * identity check in {@link loadEnv} holds. Loads zod lazily.\n */\nlet _baseEnvSchema: BaseEnvSchema | undefined\nexport function getBaseEnvSchema(): BaseEnvSchema {\n if (!_baseEnvSchema) _baseEnvSchema = buildBaseEnvSchema()\n return _baseEnvSchema\n}\n\n/**\n * Base environment schema with common server variables.\n * Users extend this with their own application-specific vars.\n *\n * Back-compat lazy view over {@link getBaseEnvSchema}: constructing the\n * Proxy does NOT load zod — zod is only required the moment a property\n * (`.extend`, `.parse`, `.shape`, …) is actually accessed. This keeps\n * `import { baseEnvSchema } from '@forinda/kickjs'` zero-cost for apps\n * that don't use the Zod env path, while keeping the precise schema type.\n */\nexport const baseEnvSchema: BaseEnvSchema = new Proxy({} as BaseEnvSchema, {\n get(_t, prop) {\n const real = getBaseEnvSchema() as unknown as Record<string | symbol, unknown>\n const value = real[prop]\n // Bind methods to the real schema so zod's internal `this` works.\n return typeof value === 'function' ? (value as (...a: unknown[]) => unknown).bind(real) : value\n },\n has(_t, prop) {\n return prop in (getBaseEnvSchema() as object)\n },\n}) as BaseEnvSchema\n\n/** Cached env config to avoid re-parsing — keyed by schema reference */\nlet cachedEnv: any = null\nlet cachedSchema: any = null\n\n/**\n * Define a custom env schema. The base schema (`PORT`, `NODE_ENV`,\n * `LOG_LEVEL`) is always merged in, so users never need to remember to\n * extend it themselves — both styles below produce the same result and\n * both surface the base keys in `KickEnv`.\n *\n * Returns a Zod schema that validates `process.env`.\n *\n * @example\n * ```ts\n * // Style A — return a fresh object; base fields are merged automatically\n * export default defineEnv(() =>\n * z.object({\n * DATABASE_URL: z.string().url(),\n * JWT_SECRET: z.string().min(32),\n * })\n * )\n *\n * // Style B — explicitly extend `base` (still works, identical result)\n * export default defineEnv((base) =>\n * base.extend({\n * DATABASE_URL: z.string().url(),\n * })\n * )\n * ```\n *\n * User-defined keys override base keys when they collide, so a project\n * can re-type `PORT` or tighten `NODE_ENV` without losing the rest of\n * the base shape.\n */\nexport function defineEnv<T extends z.ZodRawShape>(\n extend: (base: typeof baseEnvSchema) => z.ZodObject<T>,\n): z.ZodObject<typeof baseEnvSchema.shape & T> {\n const base = getBaseEnvSchema()\n const userSchema = extend(base as typeof baseEnvSchema)\n // Always merge the base in. `extend` lets the user's shape override\n // any colliding base keys, which preserves the escape hatch for\n // projects that want to re-type `PORT` etc.\n return base.extend(userSchema.shape) as z.ZodObject<typeof baseEnvSchema.shape & T>\n}\n\n/**\n * Parse and validate `process.env` against a Zod schema. Caches result per schema.\n *\n * **No-arg behaviour is sticky.** The first time `loadEnv(schema)` is called\n * with an extended schema, the cache holds that schema. Subsequent\n * `loadEnv()` calls (no arg) will reuse the cached extended schema rather\n * than falling back to `baseEnvSchema`.\n *\n * Stickiness matters because `ConfigService` and the `@Value()` decorator\n * both read through the no-arg form internally. Without it, resolving\n * either of them after a `loadEnv(extendedSchema)` call would silently\n * downgrade the env to the base shape and drop every user-defined key.\n *\n * To force a re-parse with a different schema, pass the new schema explicitly.\n * To start fresh, call `resetEnvCache()` (full reset) or `reloadEnv()`\n * (re-parse against the same schema after a `.env` file change).\n *\n * **Typing:**\n * - With an explicit schema → returns `z.infer<typeof schema>`.\n * - With no schema and `KickEnv` populated by typegen → returns\n * `KickEnv` (the project-wide typed env shape).\n * - With no schema and `KickEnv` empty → returns the base `Env`.\n */\nexport function loadEnv<T extends z.ZodObject<any>>(schema: T): z.infer<T>\nexport function loadEnv(): [EnvKey] extends [never] ? Env : KickEnv\nexport function loadEnv<T extends z.ZodObject<any>>(schema?: T): any {\n // Sticky: when called with no arg, prefer the most recently cached\n // schema over re-parsing with the base schema.\n const s = schema ?? cachedSchema ?? getBaseEnvSchema()\n // Re-parse if schema changed or no cache yet\n if (cachedEnv && cachedSchema === s) return cachedEnv\n cachedSchema = s\n cachedEnv = s.parse(process.env)\n\n // Register env resolver so @Value() reads validated values\n Container._envResolver = (key: string) => cachedEnv?.[key]\n\n return cachedEnv\n}\n\n/**\n * Get a single typed environment variable value.\n *\n * Three forms:\n * - **No-arg, KickEnv populated** (`kick typegen` has run) → key is\n * constrained to known `KickEnv` keys; return type inferred from\n * the schema.\n * - **With explicit schema** → key constrained to that schema's keys;\n * return type is `z.infer<typeof schema>[K]`.\n * - **No-arg, KickEnv empty** → loose `string` key, `any` return.\n *\n * @example\n * ```ts\n * // After `kick typegen` (KickEnv populated):\n * const port = getEnv('PORT') // typed as number\n * const url = getEnv('DATABASE_URL') // typed as string\n *\n * // With an inline schema (legacy / one-off):\n * const dbUrl = getEnv('DATABASE_URL', envSchema)\n * ```\n */\nexport function getEnv<K extends EnvKey>(key: K): KickEnv[K]\nexport function getEnv<T extends z.ZodObject<any>, K extends string & keyof z.infer<T>>(\n key: K,\n schema: T,\n): z.infer<T>[K]\nexport function getEnv(key: string, schema?: z.ZodObject<any>): any {\n const env = schema ? loadEnv(schema) : (loadEnv() as Record<string, any>)\n return env[key]\n}\n\n/**\n * Reload env from `process.env`. Re-reads `.env` via dotenv (if\n * installed), then re-parses the result against the **same** schema\n * the project registered at startup. Called during HMR rebuild and by\n * `envWatchPlugin` to pick up `.env` file changes mid-session.\n *\n * Crucially, this preserves the registered schema. Wiping it would\n * silently downgrade `ConfigService` and `@Value()` back to the base\n * schema on the next read, so every user-defined key would start\n * resolving as `undefined` after the first `.env` save — which was the\n * original \"config disappears in dev\" bug.\n *\n * If you need to fully forget the schema (e.g. tests that swap schemas\n * between cases), call {@link resetEnvCache} instead.\n */\nexport function reloadEnv(): void {\n // Re-read .env file into process.env if dotenv is installed.\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('dotenv').config({ override: true, quiet: true })\n } catch {\n // dotenv not installed — nothing to reload, env comes from the\n // environment directly.\n }\n\n // Drop the parsed snapshot but keep the schema. Re-parse eagerly so\n // existing consumers (including ConfigService getters and @Value\n // resolvers that may already hold references to the env state) see\n // the new values on their very next read instead of having to\n // re-trigger any cache-population step.\n cachedEnv = null\n if (cachedSchema) {\n if (typeof cachedSchema.parse === 'function') {\n loadEnv(cachedSchema)\n } else {\n loadEnvFromSchema(cachedSchema)\n }\n }\n}\n\n/**\n * Fully reset the env cache, including the registered schema. Use this\n * in tests that need to swap schemas between cases — `reloadEnv()`\n * deliberately preserves the schema to avoid the \"user keys vanish on\n * `.env` reload\" footgun.\n */\nexport function resetEnvCache(): void {\n cachedEnv = null\n cachedSchema = null\n}\n\n/**\n * Schema-agnostic env loader. Accepts any schema supported by\n * `@forinda/kickjs-schema` (Zod, Valibot, Yup, Joi, Standard Schema,\n * KickSchema adapters, or plain validator functions).\n *\n * Unlike `loadEnv()` which uses Zod's `.parse()` and throws a\n * `ZodError`, this function uses `detectSchema().safeParse()` and\n * throws a plain `Error` with structured issue messages on failure.\n *\n * @example\n * ```ts\n * import * as v from 'valibot'\n * import { fromValibot } from '@forinda/kickjs-schema/valibot'\n *\n * const envSchema = fromValibot(v.object({\n * PORT: v.pipe(v.string(), v.transform(Number)),\n * NODE_ENV: v.picklist(['development', 'production', 'test']),\n * }))\n *\n * export const env = loadEnvFromSchema(envSchema)\n * ```\n */\n/**\n * Single inferring signature. `TSchema` is inferred from the call site;\n * the conditional return type maps the unknown-input case (TSchema\n * resolves to `unknown` only when the argument is `unknown`-typed\n * because nothing on the value side carried a richer brand) to\n * `Record<string, unknown>` rather than letting `InferSchemaOutput`\n * fall through to `unknown`. The previous two-overload form had the\n * generic overload win for `unknown`-typed inputs (`unknown` is\n * assignable to any `TSchema`), so the documented \"Record\" fallback\n * never actually applied.\n */\nexport function loadEnvFromSchema<TSchema>(\n schema: TSchema,\n): unknown extends TSchema ? Record<string, unknown> : InferSchemaOutput<TSchema>\nexport function loadEnvFromSchema(schema: unknown): unknown {\n const wrapped = detectSchema(schema)\n const result = wrapped.safeParse(process.env)\n if (!result.success) {\n const details = result.issues\n .map((i) => ` ${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('\\n')\n throw new Error(`Environment validation failed:\\n${details}`)\n }\n cachedEnv = result.data\n cachedSchema = schema\n Container._envResolver = (key: string) => cachedEnv?.[key]\n return result.data\n}\n\n/**\n * Output type of {@link baseEnvSchema}. Hand-written (rather than\n * `z.infer<typeof baseEnvSchema>`) so the type is available without the\n * eager `zod` value import — `baseEnvSchema` is now a lazy Proxy typed\n * as `z.ZodObject<any>`, from which `z.infer` could not recover the\n * precise shape anyway.\n */\nexport type Env = {\n PORT: number\n NODE_ENV: 'development' | 'production' | 'test'\n LOG_LEVEL: string\n}\n"],"mappings":";;;;;;;;;;qHAmBA,IAAI,KACJ,SAAS,QAA+B,CACtC,GAAI,KAAM,OAAO,KACjB,GAAI,CAEF,KAAA,UAAe,KAAK,CACtB,MAAQ,CACN,MAAU,MACR,gUAKF,CACF,CACA,OAAO,IACT,CAiBA,SAAS,eAAsB,CAC7B,GAAI,CAMF,UAJuB,QAIlB,CAAC,CAAC,OAAO,CAAE,SAAU,GAAO,MAAO,EAAK,CAAC,CAChD,MAAQ,CAGR,CACF,CACA,cAAc,EAed,SAAS,oBAAqB,CAC5B,IAAM,EAAI,OAAO,EACjB,OAAO,EAAE,OAAO,CAEd,KAAM,EAAE,OAAO,OAAO,CAAC,CAAC,QAAQ,GAAI,EACpC,SAAU,EAAE,KAAK,CAAC,cAAe,aAAc,MAAM,CAAC,CAAC,CAAC,QAAQ,aAAa,EAC7E,UAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,MAAM,CACtC,CAAC,CACH,CASA,IAAI,eACJ,SAAgB,kBAAkC,CAEhD,MADA,CAAqB,iBAAiB,mBAAmB,EAClD,cACT,CAYA,MAAa,cAA+B,IAAI,MAAM,CAAC,EAAoB,CACzE,IAAI,GAAI,KAAM,CACZ,IAAM,KAAO,iBAAiB,EACxB,MAAQ,KAAK,MAEnB,OAAO,OAAO,OAAU,WAAc,MAAuC,KAAK,IAAI,EAAI,KAC5F,EACA,IAAI,GAAI,KAAM,CACZ,OAAO,QAAS,iBAAiB,CACnC,CACF,CAAC,EAGD,IAAI,UAAiB,KACjB,aAAoB,KAgCxB,SAAgB,UACd,OAC6C,CAC7C,IAAM,KAAO,iBAAiB,EACxB,WAAa,OAAO,IAA4B,EAItD,OAAO,KAAK,OAAO,WAAW,KAAK,CACrC,CA2BA,SAAgB,QAAoC,OAAiB,CAGnE,IAAM,EAAI,QAAU,cAAgB,iBAAiB,EASrD,OAPI,WAAa,eAAiB,EAAU,WAC5C,aAAe,EACf,UAAY,EAAE,MAAM,QAAQ,GAAG,EAG/B,UAAU,aAAgB,KAAgB,YAAY,KAE/C,UACT,CA4BA,SAAgB,OAAO,IAAa,OAAgC,CAElE,OADY,OAAS,QAAQ,MAAM,EAAK,QAAQ,EAAA,CACrC,IACb,CAiBA,SAAgB,WAAkB,CAEhC,GAAI,CAEF,UAAQ,QAAQ,CAAC,CAAC,OAAO,CAAE,SAAU,GAAM,MAAO,EAAK,CAAC,CAC1D,MAAQ,CAGR,CAOA,UAAY,KACR,eACE,OAAO,aAAa,OAAU,WAChC,QAAQ,YAAY,EAEpB,kBAAkB,YAAY,EAGpC,CAQA,SAAgB,eAAsB,CACpC,UAAY,KACZ,aAAe,IACjB,CAsCA,SAAgB,kBAAkB,OAA0B,CAE1D,IAAM,OADU,aAAa,MACR,CAAC,CAAC,UAAU,QAAQ,GAAG,EAC5C,GAAI,CAAC,OAAO,QAAS,CACnB,IAAM,QAAU,OAAO,OACpB,IAAK,GAAM,KAAK,EAAE,KAAK,KAAK,GAAG,GAAK,SAAS,IAAI,EAAE,SAAS,CAAC,CAC7D,KAAK;CAAI,EACZ,MAAU,MAAM,mCAAmC,SAAS,CAC9D,CAIA,MAHA,WAAY,OAAO,KACnB,aAAe,OACf,UAAU,aAAgB,KAAgB,YAAY,KAC/C,OAAO,IAChB"}
1
+ {"version":3,"file":"env-LIrKUSAJ.mjs","names":[],"sources":["../src/config/env.ts"],"sourcesContent":["import type { z } from 'zod'\nimport { detectSchema, type InferSchemaOutput } from '@forinda/kickjs-schema'\nimport { Container } from '../core/container'\nimport type { EnvKey } from '../core/decorators'\n\n/**\n * Lazy `zod` accessor. `zod` is an **optional** peer of\n * `@forinda/kickjs` — only the Zod-based env helpers (`baseEnvSchema`,\n * `defineEnv`, `loadEnv`) need it. Apps that validate env via Valibot /\n * Yup / Standard Schema go through `loadEnvFromSchema` (which only\n * touches the validator-agnostic `detectSchema`) and never load zod.\n *\n * Importing `zod` at module top-level made it a *hard* dependency of\n * the whole framework: `import { anything } from '@forinda/kickjs'`\n * eagerly evaluated this module, so a non-Zod app with no `zod`\n * installed crashed at module load / build time (\"Cannot find module\n * 'zod'\"). Deferring the require to first use keeps the import out of\n * the eager graph.\n */\nlet _zod: typeof import('zod') | undefined\nfunction getZod(): typeof import('zod') {\n if (_zod) return _zod\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _zod = require('zod') as typeof import('zod')\n } catch {\n throw new Error(\n \"@forinda/kickjs: the 'zod' peer dependency is required for \" +\n 'baseEnvSchema / defineEnv / loadEnv but is not installed. ' +\n 'Install it (`pnpm add zod`), or define your env with a non-Zod ' +\n 'schema and call loadEnvFromSchema() instead — @forinda/kickjs-schema ' +\n 'supports Valibot, Yup, and any Standard Schema validator without zod.',\n )\n }\n return _zod\n}\n\n/**\n * Lazily load `.env` via dotenv if it is installed in the consumer app.\n * `dotenv` is an **optional peer dependency** of `@forinda/kickjs` —\n * apps that want `.env` file support install it themselves (the CLI\n * generators add it to every scaffolded `package.json`). Apps that\n * inject env via the shell, Docker, or a secret manager can skip it\n * entirely and `process.env` is read as-is.\n *\n * We call `dotenv.config()` directly rather than `require('dotenv/config')`\n * so that a re-evaluation of this module (e.g. Vite SSR full-reload after\n * editing `.env`) can re-read the file. `dotenv/config` is a side-effect\n * shim that CJS caches aggressively, so the second `require` is a no-op\n * and new keys never reach `process.env` — the root cause of the\n * \"DATABASE_URL undefined until restart\" bug on Windows dev.\n */\nfunction tryLoadDotenv(): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const dotenv = require('dotenv')\n // `quiet: true` suppresses dotenv 17's tip banner that goes to stdout.\n // Required for CLI tools whose stdout is parsed by callers (`kick explain\n // --json`, etc) — the banner would otherwise corrupt the JSON output.\n dotenv.config({ override: false, quiet: true })\n } catch {\n // dotenv not installed — fall through, env vars must come from\n // the environment directly. This is a valid deployment style.\n }\n}\ntryLoadDotenv()\n\n/**\n * Build the base env schema. Memoized so repeated calls reuse one\n * schema instance (matters for the `cachedSchema === s` identity check\n * in {@link loadEnv}). Loads zod lazily via {@link getZod}.\n */\n// Build the concrete base schema. This function is the single source of\n// the *type* of `baseEnvSchema` (via `ReturnType<typeof buildBaseEnvSchema>`)\n// AND its runtime value (via `getBaseEnvSchema`). It is NEVER called at\n// module load — only lazily through `getBaseEnvSchema()` — so referencing\n// its return type stays compile-time-only and pulls no eager zod import.\n// Spelling the type this way (rather than `z.ZodObject<any>`) preserves the\n// precise PORT/NODE_ENV/LOG_LEVEL shape, which `defineEnv` composition and\n// the `kick typegen` `KickEnv` inference depend on.\nfunction buildBaseEnvSchema() {\n const z = getZod()\n return z.object({\n // Server\n PORT: z.coerce.number().default(3000),\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n LOG_LEVEL: z.string().default('info'),\n })\n}\n\n/** Precise type of the base env schema — no runtime value, no eager zod. */\nexport type BaseEnvSchema = ReturnType<typeof buildBaseEnvSchema>\n\n/**\n * Memoized base schema. Reuses one instance so the `cachedSchema === s`\n * identity check in {@link loadEnv} holds. Loads zod lazily.\n */\nlet _baseEnvSchema: BaseEnvSchema | undefined\nexport function getBaseEnvSchema(): BaseEnvSchema {\n if (!_baseEnvSchema) _baseEnvSchema = buildBaseEnvSchema()\n return _baseEnvSchema\n}\n\n/**\n * Base environment schema with common server variables.\n * Users extend this with their own application-specific vars.\n *\n * Back-compat lazy view over {@link getBaseEnvSchema}: constructing the\n * Proxy does NOT load zod — zod is only required the moment a property\n * (`.extend`, `.parse`, `.shape`, …) is actually accessed. This keeps\n * `import { baseEnvSchema } from '@forinda/kickjs'` zero-cost for apps\n * that don't use the Zod env path, while keeping the precise schema type.\n */\nexport const baseEnvSchema: BaseEnvSchema = new Proxy({} as BaseEnvSchema, {\n get(_t, prop) {\n const real = getBaseEnvSchema() as unknown as Record<string | symbol, unknown>\n const value = real[prop]\n // Bind methods to the real schema so zod's internal `this` works.\n return typeof value === 'function' ? (value as (...a: unknown[]) => unknown).bind(real) : value\n },\n has(_t, prop) {\n return prop in (getBaseEnvSchema() as object)\n },\n}) as BaseEnvSchema\n\n/** Cached env config to avoid re-parsing — keyed by schema reference */\nlet cachedEnv: any = null\nlet cachedSchema: any = null\n\n/**\n * Define a custom env schema. The base schema (`PORT`, `NODE_ENV`,\n * `LOG_LEVEL`) is always merged in, so users never need to remember to\n * extend it themselves — both styles below produce the same result and\n * both surface the base keys in `KickEnv`.\n *\n * Returns a Zod schema that validates `process.env`.\n *\n * @example\n * ```ts\n * // Style A — return a fresh object; base fields are merged automatically\n * export default defineEnv(() =>\n * z.object({\n * DATABASE_URL: z.string().url(),\n * JWT_SECRET: z.string().min(32),\n * })\n * )\n *\n * // Style B — explicitly extend `base` (still works, identical result)\n * export default defineEnv((base) =>\n * base.extend({\n * DATABASE_URL: z.string().url(),\n * })\n * )\n * ```\n *\n * User-defined keys override base keys when they collide, so a project\n * can re-type `PORT` or tighten `NODE_ENV` without losing the rest of\n * the base shape.\n */\nexport function defineEnv<T extends z.ZodRawShape>(\n extend: (base: typeof baseEnvSchema) => z.ZodObject<T>,\n): z.ZodObject<typeof baseEnvSchema.shape & T> {\n const base = getBaseEnvSchema()\n const userSchema = extend(base as typeof baseEnvSchema)\n // Always merge the base in. `extend` lets the user's shape override\n // any colliding base keys, which preserves the escape hatch for\n // projects that want to re-type `PORT` etc.\n return base.extend(userSchema.shape) as z.ZodObject<typeof baseEnvSchema.shape & T>\n}\n\n/**\n * Parse and validate `process.env` against a Zod schema. Caches result per schema.\n *\n * **No-arg behaviour is sticky.** The first time `loadEnv(schema)` is called\n * with an extended schema, the cache holds that schema. Subsequent\n * `loadEnv()` calls (no arg) will reuse the cached extended schema rather\n * than falling back to `baseEnvSchema`.\n *\n * Stickiness matters because `ConfigService` and the `@Value()` decorator\n * both read through the no-arg form internally. Without it, resolving\n * either of them after a `loadEnv(extendedSchema)` call would silently\n * downgrade the env to the base shape and drop every user-defined key.\n *\n * To force a re-parse with a different schema, pass the new schema explicitly.\n * To start fresh, call `resetEnvCache()` (full reset) or `reloadEnv()`\n * (re-parse against the same schema after a `.env` file change).\n *\n * **Typing:**\n * - With an explicit schema → returns `z.infer<typeof schema>`.\n * - With no schema and `KickEnv` populated by typegen → returns\n * `KickEnv` (the project-wide typed env shape).\n * - With no schema and `KickEnv` empty → returns the base `Env`.\n */\nexport function loadEnv<T extends z.ZodObject<any>>(schema: T): z.infer<T>\nexport function loadEnv(): [EnvKey] extends [never] ? Env : KickEnv\nexport function loadEnv<T extends z.ZodObject<any>>(schema?: T): any {\n // Sticky: when called with no arg, prefer the most recently cached\n // schema over re-parsing with the base schema.\n const s = schema ?? cachedSchema ?? getBaseEnvSchema()\n // Re-parse if schema changed or no cache yet\n if (cachedEnv && cachedSchema === s) return cachedEnv\n cachedSchema = s\n cachedEnv = s.parse(process.env)\n\n // Register env resolver so @Value() reads validated values\n Container._envResolver = (key: string) => cachedEnv?.[key]\n\n return cachedEnv\n}\n\n/**\n * Get a single typed environment variable value.\n *\n * Three forms:\n * - **No-arg, KickEnv populated** (`kick typegen` has run) → key is\n * constrained to known `KickEnv` keys; return type inferred from\n * the schema.\n * - **With explicit schema** → key constrained to that schema's keys;\n * return type is `z.infer<typeof schema>[K]`.\n * - **No-arg, KickEnv empty** → loose `string` key, `any` return.\n *\n * @example\n * ```ts\n * // After `kick typegen` (KickEnv populated):\n * const port = getEnv('PORT') // typed as number\n * const url = getEnv('DATABASE_URL') // typed as string\n *\n * // With an inline schema (legacy / one-off):\n * const dbUrl = getEnv('DATABASE_URL', envSchema)\n * ```\n */\nexport function getEnv<K extends EnvKey>(key: K): KickEnv[K]\nexport function getEnv<T extends z.ZodObject<any>, K extends string & keyof z.infer<T>>(\n key: K,\n schema: T,\n): z.infer<T>[K]\nexport function getEnv(key: string, schema?: z.ZodObject<any>): any {\n const env = schema ? loadEnv(schema) : (loadEnv() as Record<string, any>)\n return env[key]\n}\n\n/**\n * Reload env from `process.env`. Re-reads `.env` via dotenv (if\n * installed), then re-parses the result against the **same** schema\n * the project registered at startup. Called during HMR rebuild and by\n * `envWatchPlugin` to pick up `.env` file changes mid-session.\n *\n * Crucially, this preserves the registered schema. Wiping it would\n * silently downgrade `ConfigService` and `@Value()` back to the base\n * schema on the next read, so every user-defined key would start\n * resolving as `undefined` after the first `.env` save — which was the\n * original \"config disappears in dev\" bug.\n *\n * If you need to fully forget the schema (e.g. tests that swap schemas\n * between cases), call {@link resetEnvCache} instead.\n */\nexport function reloadEnv(): void {\n // Re-read .env file into process.env if dotenv is installed.\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('dotenv').config({ override: true, quiet: true })\n } catch {\n // dotenv not installed — nothing to reload, env comes from the\n // environment directly.\n }\n\n // Drop the parsed snapshot but keep the schema. Re-parse eagerly so\n // existing consumers (including ConfigService getters and @Value\n // resolvers that may already hold references to the env state) see\n // the new values on their very next read instead of having to\n // re-trigger any cache-population step.\n cachedEnv = null\n if (cachedSchema) {\n if (typeof cachedSchema.parse === 'function') {\n loadEnv(cachedSchema)\n } else {\n loadEnvFromSchema(cachedSchema)\n }\n }\n}\n\n/**\n * Fully reset the env cache, including the registered schema. Use this\n * in tests that need to swap schemas between cases — `reloadEnv()`\n * deliberately preserves the schema to avoid the \"user keys vanish on\n * `.env` reload\" footgun.\n */\nexport function resetEnvCache(): void {\n cachedEnv = null\n cachedSchema = null\n}\n\n/**\n * Schema-agnostic env loader. Accepts any schema supported by\n * `@forinda/kickjs-schema` (Zod, Valibot, Yup, Joi, Standard Schema,\n * KickSchema adapters, or plain validator functions).\n *\n * Unlike `loadEnv()` which uses Zod's `.parse()` and throws a\n * `ZodError`, this function uses `detectSchema().safeParse()` and\n * throws a plain `Error` with structured issue messages on failure.\n *\n * @example\n * ```ts\n * import * as v from 'valibot'\n * import { fromValibot } from '@forinda/kickjs-schema/valibot'\n *\n * const envSchema = fromValibot(v.object({\n * PORT: v.pipe(v.string(), v.transform(Number)),\n * NODE_ENV: v.picklist(['development', 'production', 'test']),\n * }))\n *\n * export const env = loadEnvFromSchema(envSchema)\n * ```\n */\n/**\n * Single inferring signature. `TSchema` is inferred from the call site;\n * the conditional return type maps the unknown-input case (TSchema\n * resolves to `unknown` only when the argument is `unknown`-typed\n * because nothing on the value side carried a richer brand) to\n * `Record<string, unknown>` rather than letting `InferSchemaOutput`\n * fall through to `unknown`. The previous two-overload form had the\n * generic overload win for `unknown`-typed inputs (`unknown` is\n * assignable to any `TSchema`), so the documented \"Record\" fallback\n * never actually applied.\n */\nexport function loadEnvFromSchema<TSchema>(\n schema: TSchema,\n): unknown extends TSchema ? Record<string, unknown> : InferSchemaOutput<TSchema>\nexport function loadEnvFromSchema(schema: unknown): unknown {\n const wrapped = detectSchema(schema)\n const result = wrapped.safeParse(process.env)\n if (!result.success) {\n const details = result.issues\n .map((i) => ` ${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('\\n')\n throw new Error(`Environment validation failed:\\n${details}`)\n }\n cachedEnv = result.data\n cachedSchema = schema\n Container._envResolver = (key: string) => cachedEnv?.[key]\n return result.data\n}\n\n/**\n * Output type of {@link baseEnvSchema}. Hand-written (rather than\n * `z.infer<typeof baseEnvSchema>`) so the type is available without the\n * eager `zod` value import — `baseEnvSchema` is now a lazy Proxy typed\n * as `z.ZodObject<any>`, from which `z.infer` could not recover the\n * precise shape anyway.\n */\nexport type Env = {\n PORT: number\n NODE_ENV: 'development' | 'production' | 'test'\n LOG_LEVEL: string\n}\n"],"mappings":";;;;;;;;;;qHAmBA,IAAI,KACJ,SAAS,QAA+B,CACtC,GAAI,KAAM,OAAO,KACjB,GAAI,CAEF,KAAA,UAAe,KAAK,CACtB,MAAQ,CACN,MAAU,MACR,gUAKF,CACF,CACA,OAAO,IACT,CAiBA,SAAS,eAAsB,CAC7B,GAAI,CAMF,UAJuB,QAIlB,CAAC,CAAC,OAAO,CAAE,SAAU,GAAO,MAAO,EAAK,CAAC,CAChD,MAAQ,CAGR,CACF,CACA,cAAc,EAed,SAAS,oBAAqB,CAC5B,IAAM,EAAI,OAAO,EACjB,OAAO,EAAE,OAAO,CAEd,KAAM,EAAE,OAAO,OAAO,CAAC,CAAC,QAAQ,GAAI,EACpC,SAAU,EAAE,KAAK,CAAC,cAAe,aAAc,MAAM,CAAC,CAAC,CAAC,QAAQ,aAAa,EAC7E,UAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,MAAM,CACtC,CAAC,CACH,CASA,IAAI,eACJ,SAAgB,kBAAkC,CAEhD,MADA,CAAqB,iBAAiB,mBAAmB,EAClD,cACT,CAYA,MAAa,cAA+B,IAAI,MAAM,CAAC,EAAoB,CACzE,IAAI,GAAI,KAAM,CACZ,IAAM,KAAO,iBAAiB,EACxB,MAAQ,KAAK,MAEnB,OAAO,OAAO,OAAU,WAAc,MAAuC,KAAK,IAAI,EAAI,KAC5F,EACA,IAAI,GAAI,KAAM,CACZ,OAAO,QAAS,iBAAiB,CACnC,CACF,CAAC,EAGD,IAAI,UAAiB,KACjB,aAAoB,KAgCxB,SAAgB,UACd,OAC6C,CAC7C,IAAM,KAAO,iBAAiB,EACxB,WAAa,OAAO,IAA4B,EAItD,OAAO,KAAK,OAAO,WAAW,KAAK,CACrC,CA2BA,SAAgB,QAAoC,OAAiB,CAGnE,IAAM,EAAI,QAAU,cAAgB,iBAAiB,EASrD,OAPI,WAAa,eAAiB,EAAU,WAC5C,aAAe,EACf,UAAY,EAAE,MAAM,QAAQ,GAAG,EAG/B,UAAU,aAAgB,KAAgB,YAAY,KAE/C,UACT,CA4BA,SAAgB,OAAO,IAAa,OAAgC,CAElE,OADY,OAAS,QAAQ,MAAM,EAAK,QAAQ,EAAA,CACrC,IACb,CAiBA,SAAgB,WAAkB,CAEhC,GAAI,CAEF,UAAQ,QAAQ,CAAC,CAAC,OAAO,CAAE,SAAU,GAAM,MAAO,EAAK,CAAC,CAC1D,MAAQ,CAGR,CAOA,UAAY,KACR,eACE,OAAO,aAAa,OAAU,WAChC,QAAQ,YAAY,EAEpB,kBAAkB,YAAY,EAGpC,CAQA,SAAgB,eAAsB,CACpC,UAAY,KACZ,aAAe,IACjB,CAsCA,SAAgB,kBAAkB,OAA0B,CAE1D,IAAM,OADU,aAAa,MACR,CAAC,CAAC,UAAU,QAAQ,GAAG,EAC5C,GAAI,CAAC,OAAO,QAAS,CACnB,IAAM,QAAU,OAAO,OACpB,IAAK,GAAM,KAAK,EAAE,KAAK,KAAK,GAAG,GAAK,SAAS,IAAI,EAAE,SAAS,CAAC,CAC7D,KAAK;CAAI,EACZ,MAAU,MAAM,mCAAmC,SAAS,CAC9D,CAIA,MAHA,WAAY,OAAO,KACnB,aAAe,OACf,UAAU,aAAgB,KAAgB,YAAY,KAC/C,OAAO,IAChB"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as Application}from"../application-DnnqoDWx.mjs";export{Application};
11
+ import{t as Application}from"../application-CRmMytW1.mjs";export{Application};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as bootstrap}from"../bootstrap-CFK2aAjA.mjs";export{bootstrap};
11
+ import{t as bootstrap}from"../bootstrap-CHmCh_cE.mjs";export{bootstrap};
@@ -1,4 +1,4 @@
1
1
 
2
2
  import { n as ContextMeta } from "../execution-context-B4cfhivz.mjs";
3
- import { i as RouteShape, n as DeepReadonly, r as RequestContext, t as Ctx } from "../context-Dnt2Di7p.mjs";
3
+ import { i as RouteShape, n as DeepReadonly, r as RequestContext, t as Ctx } from "../context-DV7RjM9F.mjs";
4
4
  export { type ContextMeta, Ctx, DeepReadonly, RequestContext, RouteShape };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as RequestContext}from"../context-MD7Mc_o_.mjs";export{RequestContext};
11
+ import{t as RequestContext}from"../context-C6e-S0uc.mjs";export{RequestContext};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{createLogger}from"../../core/logger.mjs";import"../../core-Db2uyqtM.mjs";import{HttpException,ProblemException,normalizeProblem}from"../../core/errors.mjs";const log=createLogger(`ErrorHandler`);function notFoundHandler(){return(_req,res,_next)=>{res.status(404).json({message:`Not Found`})}}function errorHandler(){let isProduction=process.env.NODE_ENV===`production`;return(err,req,res,_next)=>{if(res.headersSent){log.warn(`Error after headers sent: ${err?.message||`Unknown`}`);return}if(err instanceof ProblemException){if(err.problem.status>=500&&log.error(err,err.message),err.headers)for(let[k,v]of Object.entries(err.headers))res.setHeader(k,v);let body=normalizeProblem(err.problem);return res.setHeader(`Content-Type`,`application/problem+json`),res.status(body.status).json(body)}if(err?.name===`ZodError`){let firstIssue=err.issues?.[0];return res.status(422).json({message:firstIssue?.message||`Validation failed`,...isProduction?{}:{errors:err.issues}})}if(err instanceof HttpException){if(err.status>=500&&log.error(err,err.message),err.headers)for(let[k,v]of Object.entries(err.headers))res.setHeader(k,v);let exposeDetails=!isProduction&&err.details!==void 0;return res.status(err.status).json({message:err.message,...exposeDetails?{errors:err.details}:{}})}let status=err.status||err.statusCode||500;log.error(err,`${req.method} ${req.originalUrl} — ${err.message||`Unhandled error`}`);let message=status===500?`Internal Server Error`:err.message||`Error`;res.status(status).json({message})}}export{errorHandler,notFoundHandler};
11
+ import{createLogger}from"../../core/logger.mjs";import"../../core-C_gfh7JQ.mjs";import{HttpException,ProblemException,normalizeProblem}from"../../core/errors.mjs";const log=createLogger(`ErrorHandler`);function notFoundHandler(){return(_req,res,_next)=>{res.status(404).json({message:`Not Found`})}}function errorHandler(){let isProduction=process.env.NODE_ENV===`production`;return(err,req,res,_next)=>{if(res.headersSent){log.warn(`Error after headers sent: ${err?.message||`Unknown`}`);return}if(err instanceof ProblemException){if(err.problem.status>=500&&log.error(err,err.message),err.headers)for(let[k,v]of Object.entries(err.headers))res.setHeader(k,v);let body=normalizeProblem(err.problem);return res.setHeader(`Content-Type`,`application/problem+json`),res.status(body.status).json(body)}if(err?.name===`ZodError`){let firstIssue=err.issues?.[0];return res.status(422).json({message:firstIssue?.message||`Validation failed`,...isProduction?{}:{errors:err.issues}})}if(err instanceof HttpException){if(err.status>=500&&log.error(err,err.message),err.headers)for(let[k,v]of Object.entries(err.headers))res.setHeader(k,v);let exposeDetails=!isProduction&&err.details!==void 0;return res.status(err.status).json({message:err.message,...exposeDetails?{errors:err.details}:{}})}let status=err.status||err.statusCode||500;log.error(err,`${req.method} ${req.originalUrl} — ${err.message||`Unhandled error`}`);let message=status===500?`Internal Server Error`:err.message||`Error`;res.status(status).json({message})}}export{errorHandler,notFoundHandler};
12
12
  //# sourceMappingURL=error-handler.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{createLogger}from"../../core/logger.mjs";import"../../core-Db2uyqtM.mjs";function requestLogger(options={}){let log=createLogger(options.name??`HTTP`),level=options.level??`info`,skip=options.skip??[];return(req,res,next)=>{if(skip.some(prefix=>req.path.startsWith(prefix)))return next();let start=Date.now();res.on(`finish`,()=>{let duration=Date.now()-start,requestId=req.requestId||req.headers[`x-request-id`]||`-`,status=res.statusCode;log[level](`${req.method} ${req.originalUrl} ${status} ${duration}ms ${requestId}`)}),next()}}export{requestLogger};
11
+ import{createLogger}from"../../core/logger.mjs";import"../../core-C_gfh7JQ.mjs";function requestLogger(options={}){let log=createLogger(options.name??`HTTP`),level=options.level??`info`,skip=options.skip??[];return(req,res,next)=>{if(skip.some(prefix=>req.path.startsWith(prefix)))return next();let start=Date.now();res.on(`finish`,()=>{let duration=Date.now()-start,requestId=req.requestId||req.headers[`x-request-id`]||`-`,status=res.statusCode;log[level](`${req.method} ${req.originalUrl} ${status} ${duration}ms ${requestId}`)}),next()}}export{requestLogger};
12
12
  //# sourceMappingURL=request-logger.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{v as __require}from"../../container-D1gfs6__.mjs";import{Logger}from"../../core/logger.mjs";import"../../core-Db2uyqtM.mjs";import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";const log=Logger.for(`SpaAdapter`);var SpaAdapter=class{options;name=`SpaAdapter`;clientDir;apiPrefixes;excludePaths;cacheControl;indexCacheControl;indexHtml=null;constructor(options={}){this.options=options,this.clientDir=resolve(options.clientDir??`dist/client`),this.excludePaths=options.exclude??[],this.cacheControl=options.cacheControl??`public, max-age=31536000, immutable`,this.indexCacheControl=options.indexCacheControl??`no-cache`;let prefix=options.apiPrefix??`/api`;this.apiPrefixes=Array.isArray(prefix)?prefix:[prefix]}beforeMount({app}){if(!existsSync(this.clientDir)){log.warn(`SPA client directory not found: ${this.clientDir}`),log.warn(`Build your frontend first, or set clientDir to the correct path.`);return}let indexPath=join(this.clientDir,`index.html`);if(existsSync(indexPath))this.indexHtml=readFileSync(indexPath,`utf-8`);else{log.warn(`index.html not found in ${this.clientDir}`);return}try{let express=__require(`express`),staticOpts={};this.cacheControl&&(staticOpts.setHeaders=(res,filePath)=>{filePath.endsWith(`.html`)?res.setHeader(`Cache-Control`,this.indexCacheControl):res.setHeader(`Cache-Control`,this.cacheControl)}),app.use(express.static(this.clientDir,staticOpts))}catch{log.error(`express.static not available — SPA static file serving disabled`);return}log.debug(`Serving SPA from ${this.clientDir}`)}beforeStart({app}){this.indexHtml&&app.use((req,res,next)=>{for(let prefix of this.apiPrefixes)if(req.path.startsWith(prefix))return next();for(let path of this.excludePaths)if(req.path.startsWith(path))return next();if(req.path.includes(`.`)||req.method!==`GET`)return next();res.setHeader(`Content-Type`,`text/html`),res.setHeader(`Cache-Control`,this.indexCacheControl),res.send(this.indexHtml)})}};export{SpaAdapter};
11
+ import{v as __require}from"../../container-CPz0l8-b.mjs";import{Logger}from"../../core/logger.mjs";import"../../core-C_gfh7JQ.mjs";import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";const log=Logger.for(`SpaAdapter`);var SpaAdapter=class{options;name=`SpaAdapter`;clientDir;apiPrefixes;excludePaths;cacheControl;indexCacheControl;indexHtml=null;constructor(options={}){this.options=options,this.clientDir=resolve(options.clientDir??`dist/client`),this.excludePaths=options.exclude??[],this.cacheControl=options.cacheControl??`public, max-age=31536000, immutable`,this.indexCacheControl=options.indexCacheControl??`no-cache`;let prefix=options.apiPrefix??`/api`;this.apiPrefixes=Array.isArray(prefix)?prefix:[prefix]}beforeMount({app}){if(!existsSync(this.clientDir)){log.warn(`SPA client directory not found: ${this.clientDir}`),log.warn(`Build your frontend first, or set clientDir to the correct path.`);return}let indexPath=join(this.clientDir,`index.html`);if(existsSync(indexPath))this.indexHtml=readFileSync(indexPath,`utf-8`);else{log.warn(`index.html not found in ${this.clientDir}`);return}try{let express=__require(`express`),staticOpts={};this.cacheControl&&(staticOpts.setHeaders=(res,filePath)=>{filePath.endsWith(`.html`)?res.setHeader(`Cache-Control`,this.indexCacheControl):res.setHeader(`Cache-Control`,this.cacheControl)}),app.use(express.static(this.clientDir,staticOpts))}catch{log.error(`express.static not available — SPA static file serving disabled`);return}log.debug(`Serving SPA from ${this.clientDir}`)}beforeStart({app}){this.indexHtml&&app.use((req,res,next)=>{for(let prefix of this.apiPrefixes)if(req.path.startsWith(prefix))return next();for(let path of this.excludePaths)if(req.path.startsWith(path))return next();if(req.path.includes(`.`)||req.method!==`GET`)return next();res.setHeader(`Content-Type`,`text/html`),res.setHeader(`Cache-Control`,this.indexCacheControl),res.send(this.indexHtml)})}};export{SpaAdapter};
12
12
  //# sourceMappingURL=spa.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import"../../core-Db2uyqtM.mjs";import{HttpException,HttpStatus}from"../../core/errors.mjs";import{detectSchema}from"@forinda/kickjs-schema";function toValidationException(issues,message,useFirstIssueMessage){let details=issues.map(i=>({field:i.path.join(`.`)||``,message:i.message})),finalMessage=useFirstIssueMessage?issues[0]?.message??message:message;return new HttpException(HttpStatus.UNPROCESSABLE_ENTITY,finalMessage,details)}function assignReqProperty(req,key,value){Object.defineProperty(req,key,{value,writable:!0,configurable:!0,enumerable:!0})}function validate(schema){return(req,_res,next)=>{try{if(schema.body){let result=detectSchema(schema.body).safeParse(req.body);if(!result.success)return next(toValidationException(result.issues,`Validation failed`,!0));req.body=result.data}if(schema.query){let result=detectSchema(schema.query).safeParse(req.query);if(!result.success)return next(toValidationException(result.issues,`Invalid query parameters`,!1));assignReqProperty(req,`query`,result.data)}if(schema.params){let result=detectSchema(schema.params).safeParse(req.params);if(!result.success)return next(toValidationException(result.issues,`Invalid path parameters`,!1));assignReqProperty(req,`params`,result.data)}next()}catch(err){next(err)}}}export{validate};
11
+ import"../../core-C_gfh7JQ.mjs";import{HttpException,HttpStatus}from"../../core/errors.mjs";import{detectSchema}from"@forinda/kickjs-schema";function toValidationException(issues,message,useFirstIssueMessage){let details=issues.map(i=>({field:i.path.join(`.`)||``,message:i.message})),finalMessage=useFirstIssueMessage?issues[0]?.message??message:message;return new HttpException(HttpStatus.UNPROCESSABLE_ENTITY,finalMessage,details)}function assignReqProperty(req,key,value){Object.defineProperty(req,key,{value,writable:!0,configurable:!0,enumerable:!0})}function validate(schema){return(req,_res,next)=>{try{if(schema.body){let result=detectSchema(schema.body).safeParse(req.body);if(!result.success)return next(toValidationException(result.issues,`Validation failed`,!0));req.body=result.data}if(schema.query){let result=detectSchema(schema.query).safeParse(req.query);if(!result.success)return next(toValidationException(result.issues,`Invalid query parameters`,!1));assignReqProperty(req,`query`,result.data)}if(schema.params){let result=detectSchema(schema.params).safeParse(req.params);if(!result.success)return next(toValidationException(result.issues,`Invalid path parameters`,!1));assignReqProperty(req,`params`,result.data)}next()}catch(err){next(err)}}}export{validate};
12
12
  //# sourceMappingURL=validate.mjs.map
@@ -1,3 +1,3 @@
1
1
 
2
- import { n as ViewAdapterOptions, t as ViewAdapter } from "../../views-BCl-mETX.mjs";
2
+ import { n as ViewAdapterOptions, t as ViewAdapter } from "../../views-DkllbVB3.mjs";
3
3
  export { ViewAdapter, ViewAdapterOptions };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{Logger}from"../../core/logger.mjs";import{n as defineAdapter}from"../../core-Db2uyqtM.mjs";import{resolve}from"node:path";const log=Logger.for(`ViewEngine`),ViewAdapter=defineAdapter({name:`ViewAdapter`,build:options=>({beforeMount({app}){let{engine,ext,viewsDir=`src/views`}=options;engine.__express?app.engine(ext,engine.__express):typeof engine.renderFile==`function`?app.engine(ext,(path,opts,callback)=>{engine.renderFile(path,opts,callback)}):(typeof engine==`function`||log.warn(`Engine for .${ext} does not have __express or renderFile. Trying as-is.`),app.engine(ext,engine)),app.set(`view engine`,ext),app.set(`views`,resolve(viewsDir)),log.debug(`View engine: ${ext} (${resolve(viewsDir)})`)}})});export{ViewAdapter};
11
+ import{Logger}from"../../core/logger.mjs";import{n as defineAdapter}from"../../core-C_gfh7JQ.mjs";import{resolve}from"node:path";const log=Logger.for(`ViewEngine`),ViewAdapter=defineAdapter({name:`ViewAdapter`,build:options=>({beforeMount({app}){let{engine,ext,viewsDir=`src/views`}=options;engine.__express?app.engine(ext,engine.__express):typeof engine.renderFile==`function`?app.engine(ext,(path,opts,callback)=>{engine.renderFile(path,opts,callback)}):(typeof engine==`function`||log.warn(`Engine for .${ext} does not have __express or renderFile. Trying as-is.`),app.engine(ext,engine)),app.set(`view engine`,ext),app.set(`views`,resolve(viewsDir)),log.debug(`View engine: ${ext} (${resolve(viewsDir)})`)}})});export{ViewAdapter};
12
12
  //# sourceMappingURL=views.mjs.map
@@ -1,3 +1,3 @@
1
1
 
2
- import { _ as SortItem, a as parseSearchQuery, c as FILTER_OPERATORS, d as FilterOperator, f as PaginatedResponse, g as QueryFieldConfig, h as QueryBuilderAdapter, i as parseQuery, l as FieldsOf, m as ParsedQuery, n as parseFilters, o as parseSort, p as PaginationParams, r as parsePagination, s as ColumnQueryFieldConfig, t as buildQueryParams, u as FilterItem, v as StringQueryFieldConfig, y as TypedParsedQuery } from "../../index-kEmG8nWU.mjs";
3
- export { type ColumnQueryFieldConfig, FILTER_OPERATORS, type FieldsOf, type FilterItem, type FilterOperator, type PaginatedResponse, type PaginationParams, type ParsedQuery, type QueryBuilderAdapter, type QueryFieldConfig, type SortItem, type StringQueryFieldConfig, type TypedParsedQuery, buildQueryParams, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort };
2
+ import { C as SortItem, S as QueryFieldConfig, T as TypedParsedQuery, _ as FilterOperator, a as getQueryParsingDefaults, b as ParsedQuery, c as parseQuery, d as resetQueryParsingDefaults, f as setQueryParsingDefaults, g as FilterItem, h as FieldsOf, i as buildQueryParams, l as parseSearchQuery, m as FILTER_OPERATORS, n as QueryRejectReason, o as parseFilters, p as ColumnQueryFieldConfig, r as QueryRejection, s as parsePagination, t as ParseQueryOptions, u as parseSort, v as PaginatedResponse, w as StringQueryFieldConfig, x as QueryBuilderAdapter, y as PaginationParams } from "../../index-BtWJ9SLP.mjs";
3
+ export { type ColumnQueryFieldConfig, FILTER_OPERATORS, type FieldsOf, type FilterItem, type FilterOperator, type PaginatedResponse, type PaginationParams, type ParseQueryOptions, type ParsedQuery, type QueryBuilderAdapter, type QueryFieldConfig, type QueryRejectReason, type QueryRejection, type SortItem, type StringQueryFieldConfig, type TypedParsedQuery, buildQueryParams, getQueryParsingDefaults, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, resetQueryParsingDefaults, setQueryParsingDefaults };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{a as parseSearchQuery,i as parseQuery,n as parseFilters,o as parseSort,r as parsePagination,s as FILTER_OPERATORS,t as buildQueryParams}from"../../query-D-FiLfmd.mjs";export{FILTER_OPERATORS,buildQueryParams,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort};
11
+ import{a as parseQuery,c as resetQueryParsingDefaults,i as parsePagination,l as setQueryParsingDefaults,n as getQueryParsingDefaults,o as parseSearchQuery,r as parseFilters,s as parseSort,t as buildQueryParams,u as FILTER_OPERATORS}from"../../query-NhwucLHS.mjs";export{FILTER_OPERATORS,buildQueryParams,getQueryParsingDefaults,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,resetQueryParsingDefaults,setQueryParsingDefaults};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as Container}from"../container-D1gfs6__.mjs";import{METADATA}from"../core/interfaces.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"../metadata-Cll3P0N0.mjs";import{c as runContributors,l as buildPipeline}from"../core-Db2uyqtM.mjs";import{t as RequestContext}from"../context-MD7Mc_o_.mjs";import{validate}from"./middleware/validate.mjs";import{buildUploadMiddleware}from"./middleware/upload.mjs";import{Router}from"express";let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function buildRoutes(controllerClass,options={}){let router=Router(),container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]);for(let route of routes){let method=route.method.toLowerCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),handlers=[];route.validation&&handlers.push(validate(route.validation));let fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName);fileUploadConfig&&handlers.push(buildUploadMiddleware(fileUploadConfig));for(let mw of[...classMiddlewares,...methodMiddlewares])handlers.push((req,res,next)=>{let ctx=new RequestContext(req,res,next);Promise.resolve(mw(ctx,next)).catch(next)});let methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]);if(classContributors.length>0||methodContributors.length>0||externalSources.length>0){let pipeline=buildPipeline([...methodContributors.map((registration,i)=>({source:`method`,registration,label:`${controllerClass.name}.${String(route.handlerName)}#${i}(${registration.key})`})),...classContributors.map((registration,i)=>({source:`class`,registration,label:`${controllerClass.name}.@class#${i}(${registration.key})`})),...externalSources],{route:`${route.method.toUpperCase()} ${fullPath}`});handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await runContributors({pipeline,ctx,container}),next()}catch(err){next(err)}})}handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await container.resolve(controllerClass)[route.handlerName](ctx)}catch(err){next(err)}}),router[method](fullPath,...handlers)}return router}export{_setExternalContributorSources,buildRoutes};
11
+ import{t as Container}from"../container-CPz0l8-b.mjs";import{METADATA}from"../core/interfaces.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"../metadata-D8hSFaux.mjs";import{c as runContributors,l as buildPipeline}from"../core-C_gfh7JQ.mjs";import{t as RequestContext}from"../context-C6e-S0uc.mjs";import{validate}from"./middleware/validate.mjs";import{buildUploadMiddleware}from"./middleware/upload.mjs";import{Router}from"express";let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function buildRoutes(controllerClass,options={}){let router=Router(),container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]);for(let route of routes){let method=route.method.toLowerCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),handlers=[];route.validation&&handlers.push(validate(route.validation));let fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName);fileUploadConfig&&handlers.push(buildUploadMiddleware(fileUploadConfig));for(let mw of[...classMiddlewares,...methodMiddlewares])handlers.push((req,res,next)=>{let ctx=new RequestContext(req,res,next);Promise.resolve(mw(ctx,next)).catch(next)});let methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]);if(classContributors.length>0||methodContributors.length>0||externalSources.length>0){let pipeline=buildPipeline([...methodContributors.map((registration,i)=>({source:`method`,registration,label:`${controllerClass.name}.${String(route.handlerName)}#${i}(${registration.key})`})),...classContributors.map((registration,i)=>({source:`class`,registration,label:`${controllerClass.name}.@class#${i}(${registration.key})`})),...externalSources],{route:`${route.method.toUpperCase()} ${fullPath}`});handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await runContributors({pipeline,ctx,container}),next()}catch(err){next(err)}})}handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await container.resolve(controllerClass)[route.handlerName](ctx)}catch(err){next(err)}}),router[method](fullPath,...handlers)}return router}export{_setExternalContributorSources,buildRoutes};
12
12
  //# sourceMappingURL=router-builder.mjs.map
@@ -137,17 +137,62 @@ interface QueryBuilderAdapter<TResult = any, TConfig = any> {
137
137
  }
138
138
  //#endregion
139
139
  //#region src/http/query/parse-query.d.ts
140
+ /**
141
+ * Why a rejected field/value was dropped. Surfaced through
142
+ * {@link ParseQueryOptions.onReject} so a caller can warn, count, or
143
+ * return a 400 instead of the historical silent drop.
144
+ */
145
+ type QueryRejectReason = 'field-not-allowed' | 'unknown-operator' | 'truncated';
146
+ interface QueryRejection {
147
+ kind: 'filter' | 'sort' | 'search';
148
+ field: string;
149
+ reason: QueryRejectReason;
150
+ }
151
+ interface ParseQueryOptions {
152
+ /** Hard cap on `limit`. Default: global (100, override via {@link setQueryParsingDefaults}). */
153
+ maxLimit?: number;
154
+ /** `limit` when the query omits one. Default: global (20). */
155
+ defaultLimit?: number;
156
+ /** Max `q` length before truncation. Default: global (200). */
157
+ maxSearchLength?: number;
158
+ /**
159
+ * Called once per dropped filter/sort field or truncated search.
160
+ * The historical behaviour is a silent drop; wire this to a logger or
161
+ * a 400 to make over-broad / typo'd queries visible.
162
+ */
163
+ onReject?: (rejection: QueryRejection) => void;
164
+ }
165
+ interface QueryParsingDefaults {
166
+ maxLimit: number;
167
+ defaultLimit: number;
168
+ maxSearchLength: number;
169
+ defaultPage: number;
170
+ }
171
+ /**
172
+ * Globally override the query-parsing limits — call once at bootstrap.
173
+ * Per-call {@link ParseQueryOptions} still take precedence over these.
174
+ */
175
+ declare function setQueryParsingDefaults(partial: Partial<Omit<QueryParsingDefaults, 'defaultPage'>>): void;
176
+ /** Read the current global query-parsing defaults (a copy). */
177
+ declare function getQueryParsingDefaults(): Readonly<QueryParsingDefaults>;
178
+ /**
179
+ * Restore the built-in query-parsing defaults (maxLimit 100, defaultLimit
180
+ * 20, maxSearchLength 200). Useful in test teardown after a
181
+ * {@link setQueryParsingDefaults} override so global state can't leak
182
+ * between tests.
183
+ */
184
+ declare function resetQueryParsingDefaults(): void;
140
185
  /** Parse filter strings like "status:eq:active" into structured objects */
141
- declare function parseFilters(filterParam: string | string[] | undefined, allowedFields?: string[]): FilterItem[];
186
+ declare function parseFilters(filterParam: string | string[] | undefined, allowedFields?: string[], options?: Pick<ParseQueryOptions, 'onReject'>): FilterItem[];
142
187
  /** Parse sort strings like "firstName:asc" into structured objects */
143
- declare function parseSort(sortParam: string | string[] | undefined, allowedFields?: string[]): SortItem[];
188
+ declare function parseSort(sortParam: string | string[] | undefined, allowedFields?: string[], options?: Pick<ParseQueryOptions, 'onReject'>): SortItem[];
144
189
  /** Parse page/limit into pagination with computed offset */
145
190
  declare function parsePagination(params: {
146
191
  page?: string | number;
147
192
  limit?: string | number;
148
- }): PaginationParams;
193
+ }, options?: Pick<ParseQueryOptions, 'maxLimit' | 'defaultLimit'>): PaginationParams;
149
194
  /** Sanitize and truncate search query */
150
- declare function parseSearchQuery(q: string | undefined): string;
195
+ declare function parseSearchQuery(q: string | undefined, options?: Pick<ParseQueryOptions, 'maxSearchLength' | 'onReject'>): string;
151
196
  /**
152
197
  * Parse a raw Express query object into a structured, ORM-agnostic ParsedQuery.
153
198
  *
@@ -155,6 +200,7 @@ declare function parseSearchQuery(q: string | undefined): string;
155
200
  * @param fieldConfig - Optional field restrictions (whitelist filterable/sortable/searchable).
156
201
  * Accepts both string-based configs (`{ filterable, sortable, searchable }`) and
157
202
  * column-object configs (`{ columns, sortable, searchColumns }`).
203
+ * @param options - Limits + an `onReject` hook for dropped/truncated input.
158
204
  *
159
205
  * @example
160
206
  * ```ts
@@ -176,9 +222,9 @@ declare function parseSearchQuery(q: string | undefined): string;
176
222
  *
177
223
  * Filter operators: eq, neq, gt, gte, lt, lte, between, in, contains, starts, ends
178
224
  */
179
- declare function parseQuery<TConfig extends QueryFieldConfig | undefined = undefined>(query: Record<string, any>, fieldConfig?: TConfig): TypedParsedQuery<TConfig>;
225
+ declare function parseQuery<TConfig extends QueryFieldConfig | undefined = undefined>(query: Record<string, any>, fieldConfig?: TConfig, options?: ParseQueryOptions): TypedParsedQuery<TConfig>;
180
226
  /** Convert ParsedQuery back into query string parameters */
181
227
  declare function buildQueryParams(parsed: Partial<ParsedQuery>): Record<string, string | string[] | number>;
182
228
  //#endregion
183
- export { SortItem as _, parseSearchQuery as a, FILTER_OPERATORS as c, FilterOperator as d, PaginatedResponse as f, QueryFieldConfig as g, QueryBuilderAdapter as h, parseQuery as i, FieldsOf as l, ParsedQuery as m, parseFilters as n, parseSort as o, PaginationParams as p, parsePagination as r, ColumnQueryFieldConfig as s, buildQueryParams as t, FilterItem as u, StringQueryFieldConfig as v, TypedParsedQuery as y };
184
- //# sourceMappingURL=index-kEmG8nWU.d.mts.map
229
+ export { SortItem as C, QueryFieldConfig as S, TypedParsedQuery as T, FilterOperator as _, getQueryParsingDefaults as a, ParsedQuery as b, parseQuery as c, resetQueryParsingDefaults as d, setQueryParsingDefaults as f, FilterItem as g, FieldsOf as h, buildQueryParams as i, parseSearchQuery as l, FILTER_OPERATORS as m, QueryRejectReason as n, parseFilters as o, ColumnQueryFieldConfig as p, QueryRejection as r, parsePagination as s, ParseQueryOptions as t, parseSort as u, PaginatedResponse as v, StringQueryFieldConfig as w, QueryBuilderAdapter as x, PaginationParams as y };
230
+ //# sourceMappingURL=index-BtWJ9SLP.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-kEmG8nWU.d.mts","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"mappings":";;;KACY,cAAA;AAAA,cAaC,gBAAA,EAAgB,GAAA;AAAA,UAcZ,UAAA;EACf,KAAA;EACA,QAAA,EAAU,cAAc;EACxB,KAAA;AAAA;AAAA,UAGe,QAAA;EACf,KAAA;EACA,SAAS;AAAA;AAAA,UAGM,gBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;;;;AAXK;AAGP;UAgBiB,WAAA;EACf,OAAA,EAAS,UAAA;EACT,IAAA,EAAM,QAAA;EACN,UAAA,EAAY,gBAAA;EACZ,MAAA;AAAA;;;;;;;AAZM;AAQR;KAeY,QAAA,iBACM,gBAAA,oEAEd,OAAA,0BAAiC,CAAA,4BACjC,CAAA,kBACE,CAAA,YAEF,OAAA;EAAkB,OAAA;AAAA,IAChB,CAAA,8BACQ,CAAA;;;;;;;;;;;AApBN;AAWR;;;;;;UA8BiB,gBAAA,iBACC,gBAAA,sBACR,WAAA;EACR,OAAA,EAAS,KAAA,CAAM,UAAA;IAAe,KAAA,EAAO,QAAA,CAAS,OAAA;EAAA;EAC9C,IAAA,EAAM,KAAA,CAAM,QAAA;IAAa,KAAA,EAAO,QAAA,CAAS,OAAA;EAAA;AAAA;;;;;;;;;;;KAa/B,gBAAA,GAAmB,sBAAA,GAAyB,sBAAsB;AAAA,UAE7D,sBAAA;EACf,UAAA;EACA,QAAA;EACA,UAAA;AAAA;;;;;UAOe,sBAAA;EACf,OAAA,EAAS,MAAA;EACT,QAAA,GAAW,MAAM;EACjB,aAAA;EAAA,CACC,GAAA;AAAA;;UAIc,iBAAA;EACf,IAAA,EAAM,CAAC;EACP,IAAA;IACE,IAAA;IACA,KAAA;IACA,KAAA;IACA,UAAA;IACA,OAAA;IACA,OAAA;EAAA;AAAA;;;;;;;AAzC8C;AAalD;;;;AAA8E;AAE9E;;;;;;;;AAGY;AAOZ;;UA2CiB,mBAAA;EAzCE;EAAA,SA2CR,IAAA;EA5CA;;;;;EAmDT,KAAA,CAAM,MAAA,EAAQ,WAAA,EAAa,MAAA,EAAQ,OAAA,GAAU,OAAA;AAAA;;;AA/K/C;AAAA,iBCmBgB,YAAA,CACd,WAAA,iCACA,aAAA,cACC,UAAU;;iBA8BG,SAAA,CACd,SAAA,iCACA,aAAA,cACC,QAAQ;ADvDe;AAAA,iBCiFV,eAAA,CAAgB,MAAA;EAC9B,IAAA;EACA,KAAA;AAAA,IACE,gBAAgB;ADvES;AAAA,iBCyFb,gBAAA,CAAiB,CAAqB;;;;;;;;;ADxE/C;AAGP;;;;AAEW;AAGX;;;;;;;;AAGQ;AAQR;;;;;iBC+GgB,UAAA,iBAA2B,gBAAA,0BACzC,KAAA,EAAO,MAAA,eACP,WAAA,GAAc,OAAA,GACb,gBAAA,CAAiB,OAAA;;iBAaJ,gBAAA,CACd,MAAA,EAAQ,OAAA,CAAQ,WAAA,IACf,MAAA"}
1
+ {"version":3,"file":"index-BtWJ9SLP.d.mts","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"mappings":";;;KACY,cAAA;AAAA,cAaC,gBAAA,EAAgB,GAAA;AAAA,UAcZ,UAAA;EACf,KAAA;EACA,QAAA,EAAU,cAAc;EACxB,KAAA;AAAA;AAAA,UAGe,QAAA;EACf,KAAA;EACA,SAAS;AAAA;AAAA,UAGM,gBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;;;;AAXK;AAGP;UAgBiB,WAAA;EACf,OAAA,EAAS,UAAA;EACT,IAAA,EAAM,QAAA;EACN,UAAA,EAAY,gBAAA;EACZ,MAAA;AAAA;;;;;;;AAZM;AAQR;KAeY,QAAA,iBACM,gBAAA,oEAEd,OAAA,0BAAiC,CAAA,4BACjC,CAAA,kBACE,CAAA,YAEF,OAAA;EAAkB,OAAA;AAAA,IAChB,CAAA,8BACQ,CAAA;;;;;;;;;;;AApBN;AAWR;;;;;;UA8BiB,gBAAA,iBACC,gBAAA,sBACR,WAAA;EACR,OAAA,EAAS,KAAA,CAAM,UAAA;IAAe,KAAA,EAAO,QAAA,CAAS,OAAA;EAAA;EAC9C,IAAA,EAAM,KAAA,CAAM,QAAA;IAAa,KAAA,EAAO,QAAA,CAAS,OAAA;EAAA;AAAA;;;;;;;;;;;KAa/B,gBAAA,GAAmB,sBAAA,GAAyB,sBAAsB;AAAA,UAE7D,sBAAA;EACf,UAAA;EACA,QAAA;EACA,UAAA;AAAA;;;;;UAOe,sBAAA;EACf,OAAA,EAAS,MAAA;EACT,QAAA,GAAW,MAAM;EACjB,aAAA;EAAA,CACC,GAAA;AAAA;;UAIc,iBAAA;EACf,IAAA,EAAM,CAAC;EACP,IAAA;IACE,IAAA;IACA,KAAA;IACA,KAAA;IACA,UAAA;IACA,OAAA;IACA,OAAA;EAAA;AAAA;;;;;;;AAzC8C;AAalD;;;;AAA8E;AAE9E;;;;;;;;AAGY;AAOZ;;UA2CiB,mBAAA;EAzCE;EAAA,SA2CR,IAAA;EA5CA;;;;;EAmDT,KAAA,CAAM,MAAA,EAAQ,WAAA,EAAa,MAAA,EAAQ,OAAA,GAAU,OAAA;AAAA;;;AA/K/C;;;;AAA0B;AAA1B,KCkBY,iBAAA;AAAA,UAEK,cAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA,EAAQ,iBAAiB;AAAA;AAAA,UAGV,iBAAA;;EAEf,QAAA;EDAA;ECEA,YAAA;EDDU;ECGV,eAAA;EDFK;AAAA;AAGP;;;ECKE,QAAA,IAAY,SAAA,EAAW,cAAc;AAAA;AAAA,UAG7B,oBAAA;EACR,QAAA;EACA,YAAA;EACA,eAAA;EACA,WAAA;AAAA;;;ADJM;AAQR;iBCYgB,uBAAA,CACd,OAAA,EAAS,OAAA,CAAQ,IAAA,CAAK,oBAAA;;iBAQR,uBAAA,IAA2B,QAAQ,CAAC,oBAAA;;;;;;;iBAUpC,yBAAA;;iBAOA,YAAA,CACd,WAAA,iCACA,aAAA,aACA,OAAA,GAAU,IAAA,CAAK,iBAAA,gBACd,UAAA;;iBAqCa,SAAA,CACd,SAAA,iCACA,aAAA,aACA,OAAA,GAAU,IAAA,CAAK,iBAAA,gBACd,QAAA;;iBA8Ba,eAAA,CACd,MAAA;EACE,IAAA;EACA,KAAA;AAAA,GAEF,OAAA,GAAU,IAAA,CAAK,iBAAA,iCACd,gBAAA;;iBAqBa,gBAAA,CACd,CAAA,sBACA,OAAA,GAAU,IAAI,CAAC,iBAAA;;;;;;;;;;;;;;;;;;;;;;ADtHF;AAqBf;;;;;;;iBCmKgB,UAAA,iBAA2B,gBAAA,0BACzC,KAAA,EAAO,MAAA,eACP,WAAA,GAAc,OAAA,EACd,OAAA,GAAU,iBAAA,GACT,gBAAA,CAAiB,OAAA;;iBAcJ,gBAAA,CACd,MAAA,EAAQ,OAAA,CAAQ,WAAA,IACf,MAAA"}
package/dist/index.d.mts CHANGED
@@ -17,8 +17,8 @@ import { a as isRef, c as toRefs, i as computed, l as unref, n as Ref, o as reac
17
17
  import { $ as defineModule, A as CircuitBreakerStats, B as pushMethodMeta, C as buildPipeline, D as CircuitBreaker, E as MissingContributorError, F as getMetaRecord, G as FormatKickErrorOptions, H as setInMetaMap, I as getMethodMeta, J as formatKickError, K as KickError, L as getMethodMetaOrUndefined, M as getClassMeta, N as getClassMetaOrUndefined, O as CircuitBreakerOptions, P as getMetaMap, Q as ModuleFactory, R as hasClassMeta, S as SourcedRegistration, T as DuplicateContributorError, U as setInMetaRecord, V as setClassMeta, W as setMethodMeta, X as defineModules, Y as ModuleList, Z as DefineModuleOptions, _ as RunContributorsOptions, a as groupAssetKeys, b as ContributorPipeline, c as AdapterFactory, d as DuplicateMountNameError, et as BuildContext, f as MissingMountDepError, g as mountSort, h as MountSortItem, i as GroupAssetKeysResult, it as definePlugin, j as CircuitOpenError, k as CircuitBreakerState, l as DefineAdapterOptions, m as MountKind, n as AssetKeyStrategy, nt as PluginAsyncOptions, o as stripExt, p as MountCycleError, q as KickErrorInit, r as GroupAssetKeysOptions, rt as PluginFactory, s as AdapterAsyncOptions, t as AssetKeyPair, tt as DefinePluginOptions, u as defineAdapter, v as runContributors, w as ContributorCycleError, x as ContributorSource, y as BuildPipelineOptions, z as pushClassMeta } from "./index-frSEHiL_.mjs";
18
18
  import { n as ApplicationOptions, r as MiddlewareEntry, t as Application } from "./application-CAnpDQ8d.mjs";
19
19
  import { t as bootstrap } from "./bootstrap-8DDzBjhx.mjs";
20
- import { _ as SortItem, a as parseSearchQuery, c as FILTER_OPERATORS, d as FilterOperator, f as PaginatedResponse, g as QueryFieldConfig, h as QueryBuilderAdapter, i as parseQuery, l as FieldsOf, m as ParsedQuery, n as parseFilters, o as parseSort, p as PaginationParams, r as parsePagination, t as buildQueryParams, u as FilterItem, y as TypedParsedQuery } from "./index-kEmG8nWU.mjs";
21
- import { i as RouteShape, n as DeepReadonly, r as RequestContext, t as Ctx } from "./context-Dnt2Di7p.mjs";
20
+ import { C as SortItem, S as QueryFieldConfig, T as TypedParsedQuery, _ as FilterOperator, a as getQueryParsingDefaults, b as ParsedQuery, c as parseQuery, d as resetQueryParsingDefaults, f as setQueryParsingDefaults, g as FilterItem, h as FieldsOf, i as buildQueryParams, l as parseSearchQuery, m as FILTER_OPERATORS, n as QueryRejectReason, o as parseFilters, r as QueryRejection, s as parsePagination, t as ParseQueryOptions, u as parseSort, v as PaginatedResponse, x as QueryBuilderAdapter, y as PaginationParams } from "./index-BtWJ9SLP.mjs";
21
+ import { i as RouteShape, n as DeepReadonly, r as RequestContext, t as Ctx } from "./context-DV7RjM9F.mjs";
22
22
  import { n as cors, t as CorsOptions } from "./cors-C4aSCD3J.mjs";
23
23
  import { n as csrf, t as CsrfOptions } from "./csrf-7nUXGTuz.mjs";
24
24
  import { n as notFoundHandler, t as errorHandler } from "./error-handler-3ih4E-QZ.mjs";
@@ -30,6 +30,6 @@ import { a as session, i as SessionStore, n as SessionData, r as SessionOptions,
30
30
  import { n as SpaAdapterOptions, t as SpaAdapter } from "./spa-IRFC2eMN.mjs";
31
31
  import { a as upload, i as resolveMimeTypes, n as buildUploadMiddleware, r as cleanupFiles, t as UploadOptions } from "./upload-D6edAELH.mjs";
32
32
  import { n as validate } from "./validate-DoZDOhCi.mjs";
33
- import { a as TraceContextOptions, c as requestScopeMiddleware, d as getRequestValue, f as requestStore, h as isClusterPrimary, i as TraceContext, l as RequestStore, m as ClusterOptions, n as ViewAdapterOptions, o as parseTraceparent, p as defineHttpContextDecorator, r as TRACEPARENT_HEADER, s as traceContext, t as ViewAdapter, u as getRequestStore } from "./views-BCl-mETX.mjs";
33
+ import { a as TraceContextOptions, c as requestScopeMiddleware, d as getRequestValue, f as requestStore, h as isClusterPrimary, i as TraceContext, l as RequestStore, m as ClusterOptions, n as ViewAdapterOptions, o as parseTraceparent, p as defineHttpContextDecorator, r as TRACEPARENT_HEADER, s as traceContext, t as ViewAdapter, u as getRequestStore } from "./views-DkllbVB3.mjs";
34
34
  import { r as buildRoutes } from "./router-builder-oQpcQMWl.mjs";
35
- export { ASSETS, ASSET_MANIFEST_VERSION, type AdapterAsyncOptions, type AdapterContext, type AdapterFactory, type AdapterMiddleware, type AnyContributorRegistration, ApiQueryParams, type ApiQueryParamsConfig, type ApiQueryParamsConfig as QueryParamsConfig, type AppAdapter, type AppAdapterClass, type AppModule, type AppModuleClass, type AppModuleEntry, Application, type ApplicationOptions, Asset, type AssetKey, type AssetKeyPair, type AssetKeyStrategy, type AugmentationMeta, Autowired, type BaseUploadOptions, type BuildContext, type BuildPipelineOptions, type Buildable, Builder, type BuilderOf, CRON_META, CacheEvict, type CacheOptions, type CacheProvider, Cacheable, CircuitBreaker, type CircuitBreakerOptions, type CircuitBreakerState, type CircuitBreakerStats, CircuitOpenError, type ClassKind, type ClusterOptions, type ColumnApiQueryParamsConfig, Component, type ComputedRef, ConfigService, ConsoleLoggerProvider, type Constructor, Container, type ContainerChangeEvent, type ContainerChangeListener, type ContextDecorator, type ContextDecoratorSpec, type ContextDecoratorTarget, type ContextKeys, type ContextMeta, type ContextMetaKey, ContributorCycleError, type ContributorPipeline, type ContributorRegistration, type ContributorRegistrations, type ContributorSource, Controller, type CorsOptions, Cron, type CronJobMeta, type CsrfOptions, type Ctx, type DeepReadonly, type DefineAdapterOptions, type DefineModuleOptions, type DefinePluginOptions, Delete, type DepValue, DuplicateContributorError, DuplicateMountNameError, type Env, type EnvKey, type ExecutionContext, FILTER_OPERATORS, type FieldsOf, type FileTypeFilter, FileUpload, type FileUploadConfig, type FilterItem, type FilterOperator, type FormatKickErrorOptions, Get, type GroupAssetKeysOptions, type GroupAssetKeysResult, type HelmetOptions, HttpException, HttpStatus, type HttpStatusCode, INJECTION_TOKEN, Inject, Injectable, type InjectionToken, type IntrospectFn, type IntrospectionKind, type IntrospectionSnapshot, type KickAssets, KickError, type KickErrorInit, type KickJsPluginName, type KickJsPluginRegistry, type KickJsRegistry, type KickPlugin, Logger, type LoggerProvider, METADATA, type MaybePromise, MemoryCacheProvider, type MetaValue, Middleware, type MiddlewareEntry, type MiddlewareHandler, type MiddlewarePath, type MiddlewarePhase, MissingContributorError, MissingMountDepError, type ModuleFactory, ModuleList, type ModuleRegistry, type ModuleRoutes, MountCycleError, type MountKind, type MountSortItem, MutableModuleRegistry, type PaginatedResponse, type PaginationParams, type ParsedQuery, Patch, type PluginAsyncOptions, type PluginFactory, Post, PostConstruct, type PostConstructStatus, type ProblemDetails, ProblemException, Problems, Put, type QueryBuilderAdapter, type QueryFieldConfig, REQUEST_ID_HEADER, type RateLimitOptions, type RateLimitStore, type Ref, Repository, RequestContext, type RequestLoggerOptions, type RequestStore, type ResolvedDeps, type RouteDefinition, type RouteShape, type RunContributorsOptions, Scope, Service, type ServiceOptions, type Session, type SessionData, type SessionOptions, type SessionStore, type SortItem, type SourcedRegistration, SpaAdapter, type SpaAdapterOptions, TRACEPARENT_HEADER, type TraceContext, type TraceContextOptions, type TypedConfigService, type TypedParsedQuery, UnknownAssetError, type UploadOptions, type ValidationError, Value, ViewAdapter, type ViewAdapterOptions, type WatchOptions, assets, baseEnvSchema, bootstrap, buildPipeline, buildQueryParams, buildRoutes, buildUploadMiddleware, cleanupFiles, clearAssetCache, computed, cors, createConfigService, createLogger, createToken, csrf, defaultProblemTitle, defineAdapter, defineAugmentation, defineContextDecorator, defineEnv, defineHttpContextDecorator, defineModule, defineModules, definePlugin, errorHandler, formatKickError, getCacheProvider, getClassMeta, getClassMetaOrUndefined, getCronJobs, getEnv, getMetaMap, getMetaRecord, getMethodMeta, getMethodMetaOrUndefined, getRequestStore, getRequestValue, groupAssetKeys, hasClassMeta, helmet, isClusterPrimary, isInjectionToken, isRef, joinPaths, loadEnv, loadEnvFromSchema, mountSort, normalizeApiQueryParamsConfig, normalizePath, normalizeProblem, notFoundHandler, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, parseTraceparent, pushClassMeta, pushMethodMeta, rateLimit, reactive, ref, reloadEnv, requestId, requestLogger, requestScopeMiddleware, requestStore, resetEnvCache, resolveAsset, resolveMimeTypes, runContributors, session, setCacheProvider, setClassMeta, setInMetaMap, setInMetaRecord, setMethodMeta, stripExt, toRefs, tokenName, traceContext, unref, upload, useAssets, validate, watch, withBuilder };
35
+ export { ASSETS, ASSET_MANIFEST_VERSION, type AdapterAsyncOptions, type AdapterContext, type AdapterFactory, type AdapterMiddleware, type AnyContributorRegistration, ApiQueryParams, type ApiQueryParamsConfig, type ApiQueryParamsConfig as QueryParamsConfig, type AppAdapter, type AppAdapterClass, type AppModule, type AppModuleClass, type AppModuleEntry, Application, type ApplicationOptions, Asset, type AssetKey, type AssetKeyPair, type AssetKeyStrategy, type AugmentationMeta, Autowired, type BaseUploadOptions, type BuildContext, type BuildPipelineOptions, type Buildable, Builder, type BuilderOf, CRON_META, CacheEvict, type CacheOptions, type CacheProvider, Cacheable, CircuitBreaker, type CircuitBreakerOptions, type CircuitBreakerState, type CircuitBreakerStats, CircuitOpenError, type ClassKind, type ClusterOptions, type ColumnApiQueryParamsConfig, Component, type ComputedRef, ConfigService, ConsoleLoggerProvider, type Constructor, Container, type ContainerChangeEvent, type ContainerChangeListener, type ContextDecorator, type ContextDecoratorSpec, type ContextDecoratorTarget, type ContextKeys, type ContextMeta, type ContextMetaKey, ContributorCycleError, type ContributorPipeline, type ContributorRegistration, type ContributorRegistrations, type ContributorSource, Controller, type CorsOptions, Cron, type CronJobMeta, type CsrfOptions, type Ctx, type DeepReadonly, type DefineAdapterOptions, type DefineModuleOptions, type DefinePluginOptions, Delete, type DepValue, DuplicateContributorError, DuplicateMountNameError, type Env, type EnvKey, type ExecutionContext, FILTER_OPERATORS, type FieldsOf, type FileTypeFilter, FileUpload, type FileUploadConfig, type FilterItem, type FilterOperator, type FormatKickErrorOptions, Get, type GroupAssetKeysOptions, type GroupAssetKeysResult, type HelmetOptions, HttpException, HttpStatus, type HttpStatusCode, INJECTION_TOKEN, Inject, Injectable, type InjectionToken, type IntrospectFn, type IntrospectionKind, type IntrospectionSnapshot, type KickAssets, KickError, type KickErrorInit, type KickJsPluginName, type KickJsPluginRegistry, type KickJsRegistry, type KickPlugin, Logger, type LoggerProvider, METADATA, type MaybePromise, MemoryCacheProvider, type MetaValue, Middleware, type MiddlewareEntry, type MiddlewareHandler, type MiddlewarePath, type MiddlewarePhase, MissingContributorError, MissingMountDepError, type ModuleFactory, ModuleList, type ModuleRegistry, type ModuleRoutes, MountCycleError, type MountKind, type MountSortItem, MutableModuleRegistry, type PaginatedResponse, type PaginationParams, type ParseQueryOptions, type ParsedQuery, Patch, type PluginAsyncOptions, type PluginFactory, Post, PostConstruct, type PostConstructStatus, type ProblemDetails, ProblemException, Problems, Put, type QueryBuilderAdapter, type QueryFieldConfig, type QueryRejectReason, type QueryRejection, REQUEST_ID_HEADER, type RateLimitOptions, type RateLimitStore, type Ref, Repository, RequestContext, type RequestLoggerOptions, type RequestStore, type ResolvedDeps, type RouteDefinition, type RouteShape, type RunContributorsOptions, Scope, Service, type ServiceOptions, type Session, type SessionData, type SessionOptions, type SessionStore, type SortItem, type SourcedRegistration, SpaAdapter, type SpaAdapterOptions, TRACEPARENT_HEADER, type TraceContext, type TraceContextOptions, type TypedConfigService, type TypedParsedQuery, UnknownAssetError, type UploadOptions, type ValidationError, Value, ViewAdapter, type ViewAdapterOptions, type WatchOptions, assets, baseEnvSchema, bootstrap, buildPipeline, buildQueryParams, buildRoutes, buildUploadMiddleware, cleanupFiles, clearAssetCache, computed, cors, createConfigService, createLogger, createToken, csrf, defaultProblemTitle, defineAdapter, defineAugmentation, defineContextDecorator, defineEnv, defineHttpContextDecorator, defineModule, defineModules, definePlugin, errorHandler, formatKickError, getCacheProvider, getClassMeta, getClassMetaOrUndefined, getCronJobs, getEnv, getMetaMap, getMetaRecord, getMethodMeta, getMethodMetaOrUndefined, getQueryParsingDefaults, getRequestStore, getRequestValue, groupAssetKeys, hasClassMeta, helmet, isClusterPrimary, isInjectionToken, isRef, joinPaths, loadEnv, loadEnvFromSchema, mountSort, normalizeApiQueryParamsConfig, normalizePath, normalizeProblem, notFoundHandler, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, parseTraceparent, pushClassMeta, pushMethodMeta, rateLimit, reactive, ref, reloadEnv, requestId, requestLogger, requestScopeMiddleware, requestStore, resetEnvCache, resetQueryParsingDefaults, resolveAsset, resolveMimeTypes, runContributors, session, setCacheProvider, setClassMeta, setInMetaMap, setInMetaRecord, setMethodMeta, setQueryParsingDefaults, stripExt, toRefs, tokenName, traceContext, unref, upload, useAssets, validate, watch, withBuilder };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.16.0
2
+ * @forinda/kickjs v5.17.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{_ as isInjectionToken,a as formatKickError,c as UnknownAssetError,d as resolveAsset,f as useAssets,g as createToken,h as INJECTION_TOKEN,i as KickError,l as assets,m as stripExt,n as tokenName,o as ASSETS,p as groupAssetKeys,s as ASSET_MANIFEST_VERSION,t as Container,u as clearAssetCache}from"./container-D1gfs6__.mjs";import{METADATA,Scope}from"./core/interfaces.mjs";import{ConsoleLoggerProvider,Logger,createLogger}from"./core/logger.mjs";import{a as getMethodMeta,c as pushClassMeta,d as setInMetaMap,f as setInMetaRecord,i as getMetaRecord,l as pushMethodMeta,n as getClassMetaOrUndefined,o as getMethodMetaOrUndefined,p as setMethodMeta,r as getMetaMap,s as hasClassMeta,t as getClassMeta,u as setClassMeta}from"./metadata-Cll3P0N0.mjs";import{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder}from"./core/decorators.mjs";import{_ as ModuleList,a as MissingMountDepError,c as runContributors,d as DuplicateContributorError,f as MissingContributorError,g as MutableModuleRegistry,h as CircuitOpenError,i as DuplicateMountNameError,l as buildPipeline,m as CircuitBreaker,n as defineAdapter,o as MountCycleError,p as defineContextDecorator,r as definePlugin,s as mountSort,t as defineAugmentation,u as ContributorCycleError,v as defineModules,y as defineModule}from"./core-Db2uyqtM.mjs";import{CRON_META,Cron,getCronJobs}from"./core/cron.mjs";import{CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider}from"./core/cache.mjs";import{HttpException,HttpStatus,ProblemException,Problems,defaultProblemTitle,normalizeProblem}from"./core/errors.mjs";import{joinPaths,normalizePath}from"./core/path.mjs";import{computed,isRef,reactive,ref,toRefs,unref,watch}from"./core/reactivity.mjs";import{REQUEST_ID_HEADER,requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore,n as getRequestStore,r as getRequestValue,t as RequestContext}from"./context-MD7Mc_o_.mjs";import{n as requestScopeMiddleware,t as Application}from"./application-DnnqoDWx.mjs";import{a as parseSearchQuery,i as parseQuery,n as parseFilters,o as parseSort,r as parsePagination,s as FILTER_OPERATORS,t as buildQueryParams}from"./query-D-FiLfmd.mjs";import{validate}from"./http/middleware/validate.mjs";import{buildUploadMiddleware,cleanupFiles,resolveMimeTypes,upload}from"./http/middleware/upload.mjs";import{buildRoutes}from"./http/router-builder.mjs";import{a as loadEnvFromSchema,i as loadEnv,n as defineEnv,o as reloadEnv,r as getEnv,s as resetEnvCache,t as baseEnvSchema}from"./env-nwsfCpdy.mjs";import{n as isClusterPrimary,t as bootstrap}from"./bootstrap-CFK2aAjA.mjs";import{csrf}from"./http/middleware/csrf.mjs";import{requestLogger}from"./http/middleware/request-logger.mjs";import{helmet}from"./http/middleware/helmet.mjs";import{cors}from"./http/middleware/cors.mjs";import{rateLimit}from"./http/middleware/rate-limit.mjs";import{session}from"./http/middleware/session.mjs";import{ViewAdapter}from"./http/middleware/views.mjs";import{SpaAdapter}from"./http/middleware/spa.mjs";import{n as createConfigService,t as ConfigService}from"./config-BW5taOVX.mjs";import crypto from"node:crypto";function defineHttpContextDecoratorImpl(spec){return defineContextDecorator(spec)}const defineHttpContextDecorator=Object.freeze(Object.assign(defineHttpContextDecoratorImpl,{withParams:()=>spec=>defineHttpContextDecoratorImpl(spec)})),TRACEPARENT_HEADER=`traceparent`,TRACEPARENT_RE=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;function generateTraceId(){return crypto.randomBytes(16).toString(`hex`)}function generateSpanId(){return crypto.randomBytes(8).toString(`hex`)}function parseTraceparent(header){let match=TRACEPARENT_RE.exec(header.trim().toLowerCase());if(!match)return null;let version=match[1],traceId=match[2],parentSpanId=match[3],flags=match[4];return version===`ff`||traceId===`00000000000000000000000000000000`||parentSpanId===`0000000000000000`?null:{version,traceId,parentSpanId,flags}}function traceContext(options={}){return(req,res,next)=>{let store=requestStore.getStore(),traceparentHeader=req.headers[TRACEPARENT_HEADER],raw=Array.isArray(traceparentHeader)?traceparentHeader[0]:traceparentHeader,parsed=raw?parseTraceparent(raw):null,traceId=parsed?.traceId??generateTraceId(),spanId=generateSpanId();store&&(store.values.set(`traceId`,traceId),store.values.set(`spanId`,spanId),parsed&&(store.values.set(`parentSpanId`,parsed.parentSpanId),store.values.set(`traceFlags`,parsed.flags),store.values.set(`traceVersion`,parsed.version))),req.traceId=traceId,req.spanId=spanId,options.propagateResponse&&res.setHeader(`traceresponse`,traceId),next()}}export{ASSETS,ASSET_MANIFEST_VERSION,ApiQueryParams,Application,Asset,Autowired,Builder,CRON_META,CacheEvict,Cacheable,CircuitBreaker,CircuitOpenError,Component,ConfigService,ConsoleLoggerProvider,Container,ContributorCycleError,Controller,Cron,Delete,DuplicateContributorError,DuplicateMountNameError,FILTER_OPERATORS,FileUpload,Get,HttpException,HttpStatus,INJECTION_TOKEN,Inject,Injectable,KickError,Logger,METADATA,MemoryCacheProvider,Middleware,MissingContributorError,MissingMountDepError,ModuleList,MountCycleError,MutableModuleRegistry,Patch,Post,PostConstruct,ProblemException,Problems,Put,REQUEST_ID_HEADER,Repository,RequestContext,Scope,Service,SpaAdapter,TRACEPARENT_HEADER,UnknownAssetError,Value,ViewAdapter,assets,baseEnvSchema,bootstrap,buildPipeline,buildQueryParams,buildRoutes,buildUploadMiddleware,cleanupFiles,clearAssetCache,computed,cors,createConfigService,createLogger,createToken,csrf,defaultProblemTitle,defineAdapter,defineAugmentation,defineContextDecorator,defineEnv,defineHttpContextDecorator,defineModule,defineModules,definePlugin,errorHandler,formatKickError,getCacheProvider,getClassMeta,getClassMetaOrUndefined,getCronJobs,getEnv,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,getRequestStore,getRequestValue,groupAssetKeys,hasClassMeta,helmet,isClusterPrimary,isInjectionToken,isRef,joinPaths,loadEnv,loadEnvFromSchema,mountSort,normalizeApiQueryParamsConfig,normalizePath,normalizeProblem,notFoundHandler,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,parseTraceparent,pushClassMeta,pushMethodMeta,rateLimit,reactive,ref,reloadEnv,requestId,requestLogger,requestScopeMiddleware,requestStore,resetEnvCache,resolveAsset,resolveMimeTypes,runContributors,session,setCacheProvider,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,stripExt,toRefs,tokenName,traceContext,unref,upload,useAssets,validate,watch,withBuilder};
11
+ import{_ as isInjectionToken,a as formatKickError,c as UnknownAssetError,d as resolveAsset,f as useAssets,g as createToken,h as INJECTION_TOKEN,i as KickError,l as assets,m as stripExt,n as tokenName,o as ASSETS,p as groupAssetKeys,s as ASSET_MANIFEST_VERSION,t as Container,u as clearAssetCache}from"./container-CPz0l8-b.mjs";import{METADATA,Scope}from"./core/interfaces.mjs";import{ConsoleLoggerProvider,Logger,createLogger}from"./core/logger.mjs";import{a as getMethodMeta,c as pushClassMeta,d as setInMetaMap,f as setInMetaRecord,i as getMetaRecord,l as pushMethodMeta,n as getClassMetaOrUndefined,o as getMethodMetaOrUndefined,p as setMethodMeta,r as getMetaMap,s as hasClassMeta,t as getClassMeta,u as setClassMeta}from"./metadata-D8hSFaux.mjs";import{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder}from"./core/decorators.mjs";import{_ as ModuleList,a as MissingMountDepError,c as runContributors,d as DuplicateContributorError,f as MissingContributorError,g as MutableModuleRegistry,h as CircuitOpenError,i as DuplicateMountNameError,l as buildPipeline,m as CircuitBreaker,n as defineAdapter,o as MountCycleError,p as defineContextDecorator,r as definePlugin,s as mountSort,t as defineAugmentation,u as ContributorCycleError,v as defineModules,y as defineModule}from"./core-C_gfh7JQ.mjs";import{CRON_META,Cron,getCronJobs}from"./core/cron.mjs";import{CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider}from"./core/cache.mjs";import{HttpException,HttpStatus,ProblemException,Problems,defaultProblemTitle,normalizeProblem}from"./core/errors.mjs";import{joinPaths,normalizePath}from"./core/path.mjs";import{computed,isRef,reactive,ref,toRefs,unref,watch}from"./core/reactivity.mjs";import{REQUEST_ID_HEADER,requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore,n as getRequestStore,r as getRequestValue,t as RequestContext}from"./context-C6e-S0uc.mjs";import{n as requestScopeMiddleware,t as Application}from"./application-CRmMytW1.mjs";import{a as parseQuery,c as resetQueryParsingDefaults,i as parsePagination,l as setQueryParsingDefaults,n as getQueryParsingDefaults,o as parseSearchQuery,r as parseFilters,s as parseSort,t as buildQueryParams,u as FILTER_OPERATORS}from"./query-NhwucLHS.mjs";import{validate}from"./http/middleware/validate.mjs";import{buildUploadMiddleware,cleanupFiles,resolveMimeTypes,upload}from"./http/middleware/upload.mjs";import{buildRoutes}from"./http/router-builder.mjs";import{a as loadEnvFromSchema,i as loadEnv,n as defineEnv,o as reloadEnv,r as getEnv,s as resetEnvCache,t as baseEnvSchema}from"./env-LIrKUSAJ.mjs";import{n as isClusterPrimary,t as bootstrap}from"./bootstrap-CHmCh_cE.mjs";import{csrf}from"./http/middleware/csrf.mjs";import{requestLogger}from"./http/middleware/request-logger.mjs";import{helmet}from"./http/middleware/helmet.mjs";import{cors}from"./http/middleware/cors.mjs";import{rateLimit}from"./http/middleware/rate-limit.mjs";import{session}from"./http/middleware/session.mjs";import{ViewAdapter}from"./http/middleware/views.mjs";import{SpaAdapter}from"./http/middleware/spa.mjs";import{n as createConfigService,t as ConfigService}from"./config-BvyPboRZ.mjs";import crypto from"node:crypto";function defineHttpContextDecoratorImpl(spec){return defineContextDecorator(spec)}const defineHttpContextDecorator=Object.freeze(Object.assign(defineHttpContextDecoratorImpl,{withParams:()=>spec=>defineHttpContextDecoratorImpl(spec)})),TRACEPARENT_HEADER=`traceparent`,TRACEPARENT_RE=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;function generateTraceId(){return crypto.randomBytes(16).toString(`hex`)}function generateSpanId(){return crypto.randomBytes(8).toString(`hex`)}function parseTraceparent(header){let match=TRACEPARENT_RE.exec(header.trim().toLowerCase());if(!match)return null;let version=match[1],traceId=match[2],parentSpanId=match[3],flags=match[4];return version===`ff`||traceId===`00000000000000000000000000000000`||parentSpanId===`0000000000000000`?null:{version,traceId,parentSpanId,flags}}function traceContext(options={}){return(req,res,next)=>{let store=requestStore.getStore(),traceparentHeader=req.headers[TRACEPARENT_HEADER],raw=Array.isArray(traceparentHeader)?traceparentHeader[0]:traceparentHeader,parsed=raw?parseTraceparent(raw):null,traceId=parsed?.traceId??generateTraceId(),spanId=generateSpanId();store&&(store.values.set(`traceId`,traceId),store.values.set(`spanId`,spanId),parsed&&(store.values.set(`parentSpanId`,parsed.parentSpanId),store.values.set(`traceFlags`,parsed.flags),store.values.set(`traceVersion`,parsed.version))),req.traceId=traceId,req.spanId=spanId,options.propagateResponse&&res.setHeader(`traceresponse`,traceId),next()}}export{ASSETS,ASSET_MANIFEST_VERSION,ApiQueryParams,Application,Asset,Autowired,Builder,CRON_META,CacheEvict,Cacheable,CircuitBreaker,CircuitOpenError,Component,ConfigService,ConsoleLoggerProvider,Container,ContributorCycleError,Controller,Cron,Delete,DuplicateContributorError,DuplicateMountNameError,FILTER_OPERATORS,FileUpload,Get,HttpException,HttpStatus,INJECTION_TOKEN,Inject,Injectable,KickError,Logger,METADATA,MemoryCacheProvider,Middleware,MissingContributorError,MissingMountDepError,ModuleList,MountCycleError,MutableModuleRegistry,Patch,Post,PostConstruct,ProblemException,Problems,Put,REQUEST_ID_HEADER,Repository,RequestContext,Scope,Service,SpaAdapter,TRACEPARENT_HEADER,UnknownAssetError,Value,ViewAdapter,assets,baseEnvSchema,bootstrap,buildPipeline,buildQueryParams,buildRoutes,buildUploadMiddleware,cleanupFiles,clearAssetCache,computed,cors,createConfigService,createLogger,createToken,csrf,defaultProblemTitle,defineAdapter,defineAugmentation,defineContextDecorator,defineEnv,defineHttpContextDecorator,defineModule,defineModules,definePlugin,errorHandler,formatKickError,getCacheProvider,getClassMeta,getClassMetaOrUndefined,getCronJobs,getEnv,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,getQueryParsingDefaults,getRequestStore,getRequestValue,groupAssetKeys,hasClassMeta,helmet,isClusterPrimary,isInjectionToken,isRef,joinPaths,loadEnv,loadEnvFromSchema,mountSort,normalizeApiQueryParamsConfig,normalizePath,normalizeProblem,notFoundHandler,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,parseTraceparent,pushClassMeta,pushMethodMeta,rateLimit,reactive,ref,reloadEnv,requestId,requestLogger,requestScopeMiddleware,requestStore,resetEnvCache,resetQueryParsingDefaults,resolveAsset,resolveMimeTypes,runContributors,session,setCacheProvider,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,setQueryParsingDefaults,stripExt,toRefs,tokenName,traceContext,unref,upload,useAssets,validate,watch,withBuilder};
12
12
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/http/define-http-context-decorator.ts","../src/http/middleware/trace-context.ts"],"sourcesContent":["/**\n * HTTP-flavoured wrapper around {@link defineContextDecorator}.\n *\n * The core factory leaves `Ctx` defaulted to {@link ExecutionContext} so the\n * Context Contributor pipeline stays transport-agnostic — future WebSocket,\n * queue, and cron contexts share the same primitive. The cost is that the\n * vast majority of contributors (which are HTTP) have to spell the fourth\n * generic by hand to get `ctx.req` / `ctx.headers` typing:\n *\n * ```ts\n * // Verbose: spell every generic, including the params shape `P`.\n * defineContextDecorator<'tenant', Record<string, never>, Record<string, never>, RequestContext>({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n * ```\n *\n * `defineHttpContextDecorator` removes that ceremony — the spec is typed\n * against `RequestContext` from the start, so resolvers can read `ctx.req`,\n * `ctx.headers`, `ctx.params`, etc. without a cast or generic dance:\n *\n * ```ts\n * import { defineHttpContextDecorator } from '@forinda/kickjs'\n *\n * const LoadTenant = defineHttpContextDecorator({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n *\n * // Parameterised — the positional form forces you to also spell `K`\n * // and `D`, which loses automatic `deps` inference. Prefer the curried\n * // `.withParams<P>()` entry point below.\n * const LoadTenantParameterised = defineHttpContextDecorator<\n * 'tenant',\n * Record<string, never>,\n * { source: 'header' | 'subdomain' }\n * >({\n * key: 'tenant',\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, _deps, params) => {\n * // params.source narrows inside `if` branches.\n * },\n * })\n *\n * // Parameterised + deps — only `P` is spelled; `K` and `D` infer from\n * // the spec, so `deps.repo` is fully typed in the resolver.\n * const LoadTenantWithRepo = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO },\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) =>\n * repo.findFor(ctx.req.headers['x-tenant-id'] as string, params.source),\n * })\n * ```\n *\n * Use {@link defineContextDecorator} directly when authoring contributors\n * for non-HTTP transports or when you want to share one contributor across\n * HTTP + WS + queue (the resolver then has to stick to the\n * `ExecutionContext` surface — `ctx.get` / `ctx.set` / `ctx.requestId`).\n */\n\nimport {\n defineContextDecorator,\n type ContextDecorator,\n type ContextDecoratorSpec,\n type DepValue,\n} from '../core/context-decorator'\nimport type { RequestContext } from './context'\n\n/**\n * Curried form returned by {@link DefineHttpContextDecoratorFn.withParams}.\n * Spell only the per-call params shape `P`; `K` and `D` are inferred\n * from the spec, and `Ctx` is locked to `RequestContext`.\n */\nexport interface DefineHttpContextDecoratorWithParams<P extends Record<string, unknown>> {\n <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n}\n\n/**\n * Public shape of {@link defineHttpContextDecorator} — the positional\n * call signature plus the curried `.withParams<P>()` helper that fixes\n * partial-inference for the parameterised case.\n */\nexport interface DefineHttpContextDecoratorFn {\n <\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n\n /**\n * Curried entry point. Spell only `P`; `K` and `D` infer from the\n * spec. `Ctx` is locked to `RequestContext`.\n *\n * ```ts\n * const LoadTenant = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO }, // D inferred\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) => repo.findFor(ctx, params),\n * })\n * ```\n */\n withParams<P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P>\n}\n\nfunction defineHttpContextDecoratorImpl<\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n>(spec: ContextDecoratorSpec<K, D, P, RequestContext>): ContextDecorator<K, D, P, RequestContext> {\n return defineContextDecorator<K, D, P, RequestContext>(spec)\n}\n\nexport const defineHttpContextDecorator: DefineHttpContextDecoratorFn = Object.freeze(\n Object.assign(defineHttpContextDecoratorImpl, {\n withParams: <P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P> => {\n return <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext> => defineHttpContextDecoratorImpl<K, D, P>(spec)\n },\n }),\n) as DefineHttpContextDecoratorFn\n","import crypto from 'node:crypto'\nimport type { Request, Response, NextFunction } from 'express'\nimport { requestStore } from '../request-store'\n\n/**\n * W3C Trace Context header name.\n * @see https://www.w3.org/TR/trace-context/#traceparent-header\n */\nexport const TRACEPARENT_HEADER = 'traceparent'\n\n/** Regex for a valid W3C traceparent: {version}-{trace-id}-{parent-id}-{flags} */\nconst TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/\n\nexport interface TraceContextOptions {\n /**\n * When true, respond with a `traceresponse` header containing the trace ID.\n * Useful for debugging. Default: false.\n */\n propagateResponse?: boolean\n}\n\n/**\n * Parsed W3C trace context fields.\n */\nexport interface TraceContext {\n /** W3C traceparent version (usually '00') */\n version: string\n /** 32-hex-char distributed trace ID */\n traceId: string\n /** 16-hex-char parent span ID */\n parentSpanId: string\n /** 2-hex-char trace flags */\n flags: string\n}\n\n/**\n * Generate a random 32-hex-character trace ID.\n */\nfunction generateTraceId(): string {\n return crypto.randomBytes(16).toString('hex')\n}\n\n/**\n * Generate a random 16-hex-character span ID.\n */\nfunction generateSpanId(): string {\n return crypto.randomBytes(8).toString('hex')\n}\n\n/**\n * Parse a W3C traceparent header value.\n * Returns null if the format is invalid.\n */\nexport function parseTraceparent(header: string): TraceContext | null {\n const match = TRACEPARENT_RE.exec(header.trim().toLowerCase())\n if (!match) return null\n\n const version = match[1]\n const traceId = match[2]\n const parentSpanId = match[3]\n const flags = match[4]\n\n // W3C spec: version ff is invalid, all-zero trace-id and parent-id are invalid\n if (\n version === 'ff' ||\n traceId === '00000000000000000000000000000000' ||\n parentSpanId === '0000000000000000'\n ) {\n return null\n }\n\n return { version, traceId, parentSpanId, flags }\n}\n\n/**\n * Middleware that extracts W3C `traceparent` header and stores trace context\n * in the request's AsyncLocalStorage store.\n *\n * If no valid `traceparent` header is present, a new traceId is generated\n * so that every request always has a trace ID available for correlation.\n *\n * Must be mounted **after** `requestScopeMiddleware()` (which creates the store).\n *\n * @example\n * ```ts\n * bootstrap({\n * middleware: [\n * requestScopeMiddleware(),\n * traceContext(), // extracts or generates traceId\n * requestLogger(),\n * express.json(),\n * ],\n * })\n * ```\n */\nexport function traceContext(options: TraceContextOptions = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n const store = requestStore.getStore()\n\n const traceparentHeader = req.headers[TRACEPARENT_HEADER]\n const raw = Array.isArray(traceparentHeader) ? traceparentHeader[0] : traceparentHeader\n const parsed = raw ? parseTraceparent(raw) : null\n\n const traceId = parsed?.traceId ?? generateTraceId()\n const spanId = generateSpanId()\n\n // Attach to the request store so logger + downstream code can access it\n if (store) {\n store.values.set('traceId', traceId)\n store.values.set('spanId', spanId)\n if (parsed) {\n store.values.set('parentSpanId', parsed.parentSpanId)\n store.values.set('traceFlags', parsed.flags)\n store.values.set('traceVersion', parsed.version)\n }\n }\n\n // Also expose on req for convenience\n ;(req as any).traceId = traceId\n ;(req as any).spanId = spanId\n\n if (options.propagateResponse) {\n res.setHeader('traceresponse', traceId)\n }\n\n next()\n }\n}\n"],"mappings":";;;;;;;;;;ixGAkHA,SAAS,+BAIP,KAAgG,CAChG,OAAO,uBAAgD,IAAI,CAC7D,CAEA,MAAa,2BAA2D,OAAO,OAC7E,OAAO,OAAO,+BAAgC,CAC5C,eAEI,MAC8C,+BAAwC,IAAI,CAEhG,CAAC,CACH,EC1Ha,mBAAqB,cAG5B,eAAiB,8DA2BvB,SAAS,iBAA0B,CACjC,OAAO,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK,CAC9C,CAKA,SAAS,gBAAyB,CAChC,OAAO,OAAO,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK,CAC7C,CAMA,SAAgB,iBAAiB,OAAqC,CACpE,IAAM,MAAQ,eAAe,KAAK,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,EAC7D,GAAI,CAAC,MAAO,OAAO,KAEnB,IAAM,QAAU,MAAM,GAChB,QAAU,MAAM,GAChB,aAAe,MAAM,GACrB,MAAQ,MAAM,GAWpB,OAPE,UAAY,MACZ,UAAY,oCACZ,eAAiB,mBAEV,KAGF,CAAE,QAAS,QAAS,aAAc,KAAM,CACjD,CAuBA,SAAgB,aAAa,QAA+B,CAAC,EAAG,CAC9D,OAAQ,IAAc,IAAe,OAAuB,CAC1D,IAAM,MAAQ,aAAa,SAAS,EAE9B,kBAAoB,IAAI,QAAQ,oBAChC,IAAM,MAAM,QAAQ,iBAAiB,EAAI,kBAAkB,GAAK,kBAChE,OAAS,IAAM,iBAAiB,GAAG,EAAI,KAEvC,QAAU,QAAQ,SAAW,gBAAgB,EAC7C,OAAS,eAAe,EAG1B,QACF,MAAM,OAAO,IAAI,UAAW,OAAO,EACnC,MAAM,OAAO,IAAI,SAAU,MAAM,EAC7B,SACF,MAAM,OAAO,IAAI,eAAgB,OAAO,YAAY,EACpD,MAAM,OAAO,IAAI,aAAc,OAAO,KAAK,EAC3C,MAAM,OAAO,IAAI,eAAgB,OAAO,OAAO,IAKlD,IAAa,QAAU,QACvB,IAAa,OAAS,OAEnB,QAAQ,mBACV,IAAI,UAAU,gBAAiB,OAAO,EAGxC,KAAK,CACP,CACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/http/define-http-context-decorator.ts","../src/http/middleware/trace-context.ts"],"sourcesContent":["/**\n * HTTP-flavoured wrapper around {@link defineContextDecorator}.\n *\n * The core factory leaves `Ctx` defaulted to {@link ExecutionContext} so the\n * Context Contributor pipeline stays transport-agnostic — future WebSocket,\n * queue, and cron contexts share the same primitive. The cost is that the\n * vast majority of contributors (which are HTTP) have to spell the fourth\n * generic by hand to get `ctx.req` / `ctx.headers` typing:\n *\n * ```ts\n * // Verbose: spell every generic, including the params shape `P`.\n * defineContextDecorator<'tenant', Record<string, never>, Record<string, never>, RequestContext>({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n * ```\n *\n * `defineHttpContextDecorator` removes that ceremony — the spec is typed\n * against `RequestContext` from the start, so resolvers can read `ctx.req`,\n * `ctx.headers`, `ctx.params`, etc. without a cast or generic dance:\n *\n * ```ts\n * import { defineHttpContextDecorator } from '@forinda/kickjs'\n *\n * const LoadTenant = defineHttpContextDecorator({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n *\n * // Parameterised — the positional form forces you to also spell `K`\n * // and `D`, which loses automatic `deps` inference. Prefer the curried\n * // `.withParams<P>()` entry point below.\n * const LoadTenantParameterised = defineHttpContextDecorator<\n * 'tenant',\n * Record<string, never>,\n * { source: 'header' | 'subdomain' }\n * >({\n * key: 'tenant',\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, _deps, params) => {\n * // params.source narrows inside `if` branches.\n * },\n * })\n *\n * // Parameterised + deps — only `P` is spelled; `K` and `D` infer from\n * // the spec, so `deps.repo` is fully typed in the resolver.\n * const LoadTenantWithRepo = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO },\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) =>\n * repo.findFor(ctx.req.headers['x-tenant-id'] as string, params.source),\n * })\n * ```\n *\n * Use {@link defineContextDecorator} directly when authoring contributors\n * for non-HTTP transports or when you want to share one contributor across\n * HTTP + WS + queue (the resolver then has to stick to the\n * `ExecutionContext` surface — `ctx.get` / `ctx.set` / `ctx.requestId`).\n */\n\nimport {\n defineContextDecorator,\n type ContextDecorator,\n type ContextDecoratorSpec,\n type DepValue,\n} from '../core/context-decorator'\nimport type { RequestContext } from './context'\n\n/**\n * Curried form returned by {@link DefineHttpContextDecoratorFn.withParams}.\n * Spell only the per-call params shape `P`; `K` and `D` are inferred\n * from the spec, and `Ctx` is locked to `RequestContext`.\n */\nexport interface DefineHttpContextDecoratorWithParams<P extends Record<string, unknown>> {\n <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n}\n\n/**\n * Public shape of {@link defineHttpContextDecorator} — the positional\n * call signature plus the curried `.withParams<P>()` helper that fixes\n * partial-inference for the parameterised case.\n */\nexport interface DefineHttpContextDecoratorFn {\n <\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n\n /**\n * Curried entry point. Spell only `P`; `K` and `D` infer from the\n * spec. `Ctx` is locked to `RequestContext`.\n *\n * ```ts\n * const LoadTenant = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO }, // D inferred\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) => repo.findFor(ctx, params),\n * })\n * ```\n */\n withParams<P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P>\n}\n\nfunction defineHttpContextDecoratorImpl<\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n>(spec: ContextDecoratorSpec<K, D, P, RequestContext>): ContextDecorator<K, D, P, RequestContext> {\n return defineContextDecorator<K, D, P, RequestContext>(spec)\n}\n\nexport const defineHttpContextDecorator: DefineHttpContextDecoratorFn = Object.freeze(\n Object.assign(defineHttpContextDecoratorImpl, {\n withParams: <P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P> => {\n return <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext> => defineHttpContextDecoratorImpl<K, D, P>(spec)\n },\n }),\n) as DefineHttpContextDecoratorFn\n","import crypto from 'node:crypto'\nimport type { Request, Response, NextFunction } from 'express'\nimport { requestStore } from '../request-store'\n\n/**\n * W3C Trace Context header name.\n * @see https://www.w3.org/TR/trace-context/#traceparent-header\n */\nexport const TRACEPARENT_HEADER = 'traceparent'\n\n/** Regex for a valid W3C traceparent: {version}-{trace-id}-{parent-id}-{flags} */\nconst TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/\n\nexport interface TraceContextOptions {\n /**\n * When true, respond with a `traceresponse` header containing the trace ID.\n * Useful for debugging. Default: false.\n */\n propagateResponse?: boolean\n}\n\n/**\n * Parsed W3C trace context fields.\n */\nexport interface TraceContext {\n /** W3C traceparent version (usually '00') */\n version: string\n /** 32-hex-char distributed trace ID */\n traceId: string\n /** 16-hex-char parent span ID */\n parentSpanId: string\n /** 2-hex-char trace flags */\n flags: string\n}\n\n/**\n * Generate a random 32-hex-character trace ID.\n */\nfunction generateTraceId(): string {\n return crypto.randomBytes(16).toString('hex')\n}\n\n/**\n * Generate a random 16-hex-character span ID.\n */\nfunction generateSpanId(): string {\n return crypto.randomBytes(8).toString('hex')\n}\n\n/**\n * Parse a W3C traceparent header value.\n * Returns null if the format is invalid.\n */\nexport function parseTraceparent(header: string): TraceContext | null {\n const match = TRACEPARENT_RE.exec(header.trim().toLowerCase())\n if (!match) return null\n\n const version = match[1]\n const traceId = match[2]\n const parentSpanId = match[3]\n const flags = match[4]\n\n // W3C spec: version ff is invalid, all-zero trace-id and parent-id are invalid\n if (\n version === 'ff' ||\n traceId === '00000000000000000000000000000000' ||\n parentSpanId === '0000000000000000'\n ) {\n return null\n }\n\n return { version, traceId, parentSpanId, flags }\n}\n\n/**\n * Middleware that extracts W3C `traceparent` header and stores trace context\n * in the request's AsyncLocalStorage store.\n *\n * If no valid `traceparent` header is present, a new traceId is generated\n * so that every request always has a trace ID available for correlation.\n *\n * Must be mounted **after** `requestScopeMiddleware()` (which creates the store).\n *\n * @example\n * ```ts\n * bootstrap({\n * middleware: [\n * requestScopeMiddleware(),\n * traceContext(), // extracts or generates traceId\n * requestLogger(),\n * express.json(),\n * ],\n * })\n * ```\n */\nexport function traceContext(options: TraceContextOptions = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n const store = requestStore.getStore()\n\n const traceparentHeader = req.headers[TRACEPARENT_HEADER]\n const raw = Array.isArray(traceparentHeader) ? traceparentHeader[0] : traceparentHeader\n const parsed = raw ? parseTraceparent(raw) : null\n\n const traceId = parsed?.traceId ?? generateTraceId()\n const spanId = generateSpanId()\n\n // Attach to the request store so logger + downstream code can access it\n if (store) {\n store.values.set('traceId', traceId)\n store.values.set('spanId', spanId)\n if (parsed) {\n store.values.set('parentSpanId', parsed.parentSpanId)\n store.values.set('traceFlags', parsed.flags)\n store.values.set('traceVersion', parsed.version)\n }\n }\n\n // Also expose on req for convenience\n ;(req as any).traceId = traceId\n ;(req as any).spanId = spanId\n\n if (options.propagateResponse) {\n res.setHeader('traceresponse', traceId)\n }\n\n next()\n }\n}\n"],"mappings":";;;;;;;;;;02GAkHA,SAAS,+BAIP,KAAgG,CAChG,OAAO,uBAAgD,IAAI,CAC7D,CAEA,MAAa,2BAA2D,OAAO,OAC7E,OAAO,OAAO,+BAAgC,CAC5C,eAEI,MAC8C,+BAAwC,IAAI,CAEhG,CAAC,CACH,EC1Ha,mBAAqB,cAG5B,eAAiB,8DA2BvB,SAAS,iBAA0B,CACjC,OAAO,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK,CAC9C,CAKA,SAAS,gBAAyB,CAChC,OAAO,OAAO,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK,CAC7C,CAMA,SAAgB,iBAAiB,OAAqC,CACpE,IAAM,MAAQ,eAAe,KAAK,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,EAC7D,GAAI,CAAC,MAAO,OAAO,KAEnB,IAAM,QAAU,MAAM,GAChB,QAAU,MAAM,GAChB,aAAe,MAAM,GACrB,MAAQ,MAAM,GAWpB,OAPE,UAAY,MACZ,UAAY,oCACZ,eAAiB,mBAEV,KAGF,CAAE,QAAS,QAAS,aAAc,KAAM,CACjD,CAuBA,SAAgB,aAAa,QAA+B,CAAC,EAAG,CAC9D,OAAQ,IAAc,IAAe,OAAuB,CAC1D,IAAM,MAAQ,aAAa,SAAS,EAE9B,kBAAoB,IAAI,QAAQ,oBAChC,IAAM,MAAM,QAAQ,iBAAiB,EAAI,kBAAkB,GAAK,kBAChE,OAAS,IAAM,iBAAiB,GAAG,EAAI,KAEvC,QAAU,QAAQ,SAAW,gBAAgB,EAC7C,OAAS,eAAe,EAG1B,QACF,MAAM,OAAO,IAAI,UAAW,OAAO,EACnC,MAAM,OAAO,IAAI,SAAU,MAAM,EAC7B,SACF,MAAM,OAAO,IAAI,eAAgB,OAAO,YAAY,EACpD,MAAM,OAAO,IAAI,aAAc,OAAO,KAAK,EAC3C,MAAM,OAAO,IAAI,eAAgB,OAAO,OAAO,IAKlD,IAAa,QAAU,QACvB,IAAa,OAAS,OAEnB,QAAQ,mBACV,IAAI,UAAU,gBAAiB,OAAO,EAGxC,KAAK,CACP,CACF"}