@spfn/core 0.2.0-beta.68 → 0.2.0-beta.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { f as RouteAuthProfile, a as Router } from '../router-DhvbMhef.js';
1
+ import { f as RouteAuthProfile, a as Router } from '../router-HDVa6gfc.js';
2
2
  import { HttpMethod } from '../route/types.js';
3
3
  import '../define-middleware-B9bFuXVU.js';
4
4
  import 'hono';
@@ -24,6 +24,11 @@ interface ContractRequest {
24
24
  params?: JsonSchema;
25
25
  query?: JsonSchema;
26
26
  body?: JsonSchema;
27
+ /**
28
+ * Never produced: a contracted route carrying multipart is refused at
29
+ * collection. Kept because a snapshot released before that rule may still
30
+ * have one, and the compatibility comparison has to read it.
31
+ */
27
32
  formData?: JsonSchema;
28
33
  headers?: JsonSchema;
29
34
  cookies?: JsonSchema;
@@ -40,6 +45,14 @@ interface ContractOperation {
40
45
  requiresSession: boolean;
41
46
  /** Present only when the operation is announced for removal. */
42
47
  deprecatedIn?: string;
48
+ /**
49
+ * Present only when the operation is gone.
50
+ *
51
+ * A client generated before this version may still call it, so the record
52
+ * outlives the route: without it a caller sees an operation that simply
53
+ * stopped existing, with nothing saying when or that it was announced.
54
+ */
55
+ removedIn?: string;
43
56
  /** What the client sends. */
44
57
  request: ContractRequest;
45
58
  /**
@@ -53,10 +66,33 @@ interface ContractOperation {
53
66
  /** What the client reads. */
54
67
  response: JsonSchema;
55
68
  }
69
+ /**
70
+ * How a client's version is judged against the server's.
71
+ *
72
+ * - `allOrNothing` — one contract version is the whole surface's pass or
73
+ * refusal. Right for an auth primitive, where admitting a client that agrees
74
+ * about part of the admission sequence and not the rest is not a safe middle.
75
+ * - `perOperation` — availability is recorded per operation, so the verdict
76
+ * narrows to the operations a client actually calls. Deleting a response field
77
+ * from one route then stops blocking a client that never calls it.
78
+ *
79
+ * Stated rather than inferred: an app contract and @spfn/auth's mobile contract
80
+ * share this format under different rules, and a consumer must not have to guess
81
+ * which one it is holding.
82
+ */
83
+ type CompatibilityPolicy = 'allOrNothing' | 'perOperation';
56
84
  /** The generated contract. */
57
85
  interface ContractDocument {
58
86
  /** Shape version of this document, not of the API it describes. */
59
87
  documentVersion: 1;
88
+ /**
89
+ * The version this document publishes, from `.contractVersion()` on the
90
+ * router. Absent when the router declares none — the gate still runs, but
91
+ * nothing can be released or announced.
92
+ */
93
+ contractVersion?: string;
94
+ /** Always `perOperation` for an app contract. See the type. */
95
+ compatibilityPolicy: CompatibilityPolicy;
60
96
  /** Sorted by name, so the file does not churn on router reordering. */
61
97
  operations: ContractOperation[];
62
98
  }
@@ -238,10 +274,15 @@ declare function readSnapshot(file: string): ContractSnapshot;
238
274
  /**
239
275
  * Write the snapshot for a release.
240
276
  *
277
+ * The version comes from the document, which took it from `.contractVersion()`
278
+ * on the router. The filename follows the declaration rather than being told
279
+ * separately what to say — a second place to state the version is a second place
280
+ * for it to be wrong.
281
+ *
241
282
  * Refuses to overwrite: a published version's promise does not change, a
242
283
  * mistake becomes a new version.
243
284
  */
244
- declare function writeSnapshot(contractsDir: string, version: string, document: ContractDocument): string;
285
+ declare function writeSnapshot(contractsDir: string, document: ContractDocument): string;
245
286
  /** Read `contracts/current.json`. */
246
287
  declare function readCurrentDocument(contractsDir: string): ContractDocument;
247
288
  /** Write `contracts/current.json`. Returns true when the file changed. */
@@ -285,4 +326,4 @@ declare function readUsageRecords(usageDir: string): UsageReadResult;
285
326
  /** Which released clients still call `operation`. */
286
327
  declare function callersOf(operation: string, records: UsageRecord[]): UsageRecord[];
287
328
 
288
- export { CURRENT_FILENAME, type ContractCheckResult, ContractCollectionError, type ContractDocument, type ContractOperation, type ContractRequest, type ContractSnapshot, ContractSnapshotError, type ContractViolation, type ContractViolationKind, type DocumentComparison, type JsonSchema, RELEASED_DIRNAME, type SnapshotFile, USAGE_DIRNAME, type UsageReadResult, type UsageRecord, callersOf, canonicalize, checkContract, collectContractDocument, compareDocuments, compareOperation, compareVersions, currentPath, formatViolations, listSnapshots, newestSnapshot, readCurrentDocument, readSnapshot, readUsageRecords, releasedDir, stableDigest, stableStringify, stableStringifyPretty, usageDir, writeCurrentDocument, writeSnapshot };
329
+ export { CURRENT_FILENAME, type CompatibilityPolicy, type ContractCheckResult, ContractCollectionError, type ContractDocument, type ContractOperation, type ContractRequest, type ContractSnapshot, ContractSnapshotError, type ContractViolation, type ContractViolationKind, type DocumentComparison, type JsonSchema, RELEASED_DIRNAME, type SnapshotFile, USAGE_DIRNAME, type UsageReadResult, type UsageRecord, callersOf, canonicalize, checkContract, collectContractDocument, compareDocuments, compareOperation, compareVersions, currentPath, formatViolations, listSnapshots, newestSnapshot, readCurrentDocument, readSnapshot, readUsageRecords, releasedDir, stableDigest, stableStringify, stableStringifyPretty, usageDir, writeCurrentDocument, writeSnapshot };
@@ -18,7 +18,7 @@ function isRouteDef(value) {
18
18
  function toJsonSchema(schema) {
19
19
  return JSON.parse(JSON.stringify(schema));
20
20
  }
21
- var REQUEST_SECTIONS = ["params", "query", "body", "formData", "headers", "cookies"];
21
+ var REQUEST_SECTIONS = ["params", "query", "body", "headers", "cookies"];
22
22
  function toContractRequest(input) {
23
23
  const request = {};
24
24
  if (!input) {
@@ -51,9 +51,21 @@ function visitRouter(router, trail, found) {
51
51
  visitRouter(packageRouter, [...trail, "(package)"], found);
52
52
  }
53
53
  }
54
+ function assertNoFormData(where, routeDef) {
55
+ const input = routeDef.input;
56
+ const interceptor = routeDef.interceptor;
57
+ const section = input?.formData ? "input" : interceptor?.formData ? "interceptor" : void 0;
58
+ if (!section) {
59
+ return;
60
+ }
61
+ throw new ContractCollectionError(
62
+ `Contracted route "${where}" declares ${section}.formData, which a contract cannot describe. The contract grammar covers JSON values, and multipart carries file parts that have no spelling in it. Drop .contract() from this route, or move the operation to a JSON body.`
63
+ );
64
+ }
54
65
  function addOperation(name, routeDef, trail, found) {
55
66
  const where = trail.join(".");
56
67
  const contract = routeDef.contract;
68
+ assertNoFormData(where, routeDef);
57
69
  if (!routeDef.method || !routeDef.path) {
58
70
  throw new ContractCollectionError(
59
71
  `Contracted route "${where}" has no method or path. A contract describes an operation on the wire, so both are required.`
@@ -85,6 +97,7 @@ function addOperation(name, routeDef, trail, found) {
85
97
  auth: contract.auth ?? "none",
86
98
  requiresSession: contract.requiresSession ?? false,
87
99
  ...contract.deprecatedIn ? { deprecatedIn: contract.deprecatedIn } : {},
100
+ ...contract.removedIn ? { removedIn: contract.removedIn } : {},
88
101
  request: toContractRequest(routeDef.input),
89
102
  interceptor: toContractRequest(routeDef.interceptor),
90
103
  response: toJsonSchema(contract.response)
@@ -95,7 +108,12 @@ function collectContractDocument(router) {
95
108
  const found = /* @__PURE__ */ new Map();
96
109
  visitRouter(router, ["router"], found);
97
110
  const operations = [...found.values()].map((entry) => entry.operation).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
98
- return { documentVersion: 1, operations };
111
+ return {
112
+ documentVersion: 1,
113
+ ...router._contractVersion ? { contractVersion: router._contractVersion } : {},
114
+ compatibilityPolicy: "perOperation",
115
+ operations
116
+ };
99
117
  }
100
118
  function canonicalize(value) {
101
119
  if (Array.isArray(value)) {
@@ -377,7 +395,13 @@ function readSnapshot(file) {
377
395
  }
378
396
  return { version: parsed.version, sha256: parsed.sha256, document: parsed.document };
379
397
  }
380
- function writeSnapshot(contractsDir, version, document) {
398
+ function writeSnapshot(contractsDir, document) {
399
+ const version = document.contractVersion;
400
+ if (!version) {
401
+ throw new ContractSnapshotError(
402
+ 'The contract declares no version, so a release cannot be named. Add .contractVersion("x.y.z") to the router before cutting one.'
403
+ );
404
+ }
381
405
  compareVersions(version, version);
382
406
  const dir = releasedDir(contractsDir);
383
407
  const file = join(dir, `${version}.json`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/contract/collect.ts","../../src/contract/stable-json.ts","../../src/contract/compare.ts","../../src/contract/snapshot.ts","../../src/contract/usage.ts","../../src/contract/check.ts"],"names":["REQUEST_SECTIONS","usageDir","readdirSync","join","readFileSync"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Contract Collection\n *\n * Walks a loaded router and turns every route carrying `.contract()` into a\n * contract operation.\n *\n * The router is loaded and walked rather than parsed from source. Real routes\n * build their schemas from imported values — `EmailSchema`, `FileSchema()`,\n * `KEY_DEVICE_NAME_MAX_LENGTH` — which a source parser cannot resolve. Loading\n * costs a module import and no infrastructure: route modules have no\n * import-time side effects, so nothing here opens a database connection.\n */\n\nimport type { RouteDef } from '../route/route-builder';\nimport type { RouteInput } from '../route/route-input';\nimport type { Router } from '../route/router';\nimport type { ContractDocument, ContractOperation, ContractRequest, JsonSchema } from './types';\n\n/** Thrown when the router cannot produce a contract at all. */\nexport class ContractCollectionError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractCollectionError';\n }\n}\n\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'routes' in value\n && '_routes' in value;\n}\n\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'handler' in value;\n}\n\n/**\n * Strip TypeBox's symbol-keyed metadata and hand back plain JSON.\n *\n * The symbols carry no information the wire shape depends on, and they cannot\n * survive the round trip to a committed file.\n */\nfunction toJsonSchema(schema: unknown): JsonSchema\n{\n return JSON.parse(JSON.stringify(schema)) as JsonSchema;\n}\n\nconst REQUEST_SECTIONS = ['params', 'query', 'body', 'formData', 'headers', 'cookies'] as const;\n\nfunction toContractRequest(input: RouteInput | undefined): ContractRequest\n{\n const request: ContractRequest = {};\n\n if (!input)\n {\n return request;\n }\n\n for (const section of REQUEST_SECTIONS)\n {\n const schema = input[section];\n if (schema)\n {\n request[section] = toJsonSchema(schema);\n }\n }\n\n return request;\n}\n\ninterface Found\n{\n operation: ContractOperation;\n\n /** Where the route was found, for a collision message. */\n trail: string;\n}\n\nfunction visitRouter(router: Router<any>, trail: string[], found: Map<string, Found>): void\n{\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n visitRouter(entry, [...trail, name], found);\n continue;\n }\n\n if (!isRouteDef(entry))\n {\n continue;\n }\n\n const routeDef = entry as RouteDef<any>;\n if (!routeDef.contract)\n {\n continue;\n }\n\n addOperation(name, routeDef, [...trail, name], found);\n }\n\n for (const packageRouter of router._packageRouters ?? [])\n {\n visitRouter(packageRouter, [...trail, '(package)'], found);\n }\n}\n\nfunction addOperation(name: string, routeDef: RouteDef<any>, trail: string[], found: Map<string, Found>): void\n{\n const where = trail.join('.');\n const contract = routeDef.contract!;\n\n if (!routeDef.method || !routeDef.path)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" has no method or path. `\n + 'A contract describes an operation on the wire, so both are required.',\n );\n }\n\n if (!contract.since)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" has no \"since\" version. `\n + 'The version an operation first appeared in is part of the promise.',\n );\n }\n\n if (!contract.response)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" declares no response schema. `\n + 'An operation with no body declares Type.Null().',\n );\n }\n\n const existing = found.get(name);\n if (existing)\n {\n throw new ContractCollectionError(\n `Two contracted routes are both named \"${name}\" (${existing.trail} and ${where}). `\n + 'An operation is identified by its name across versions, so names must be unique.',\n );\n }\n\n found.set(name, {\n trail: where,\n operation: {\n name,\n method: routeDef.method,\n path: routeDef.path,\n since: contract.since,\n auth: contract.auth ?? 'none',\n requiresSession: contract.requiresSession ?? false,\n ...(contract.deprecatedIn ? { deprecatedIn: contract.deprecatedIn } : {}),\n request: toContractRequest(routeDef.input),\n interceptor: toContractRequest(routeDef.interceptor),\n response: toJsonSchema(contract.response),\n },\n });\n}\n\n/**\n * Build the contract document from a loaded router.\n *\n * Routes without `.contract()` are skipped — they are not part of the promise.\n */\nexport function collectContractDocument(router: Router<any>): ContractDocument\n{\n const found = new Map<string, Found>();\n visitRouter(router, ['router'], found);\n\n const operations = [...found.values()]\n .map(entry => entry.operation)\n .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n\n return { documentVersion: 1, operations };\n}\n","/**\n * Deterministic JSON\n *\n * The generated contract is committed and compared against what the generator\n * produces. If key order moved between runs, \"the committed file differs from\n * the generated one\" would flicker and the check would train people to ignore\n * it. So every emitted document is written with its object keys sorted, and the\n * digest that pins a released snapshot is taken over the same ordering with no\n * whitespace — formatting choices never move a digest.\n */\n\nimport { createHash } from 'node:crypto';\n\n/** Recursively sort object keys and drop `undefined` members. */\nexport function canonicalize(value: unknown): unknown\n{\n if (Array.isArray(value))\n {\n return value.map(canonicalize);\n }\n\n if (value === null || typeof value !== 'object')\n {\n return value;\n }\n\n const source = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(source).sort())\n {\n if (source[key] === undefined)\n {\n continue;\n }\n\n sorted[key] = canonicalize(source[key]);\n }\n\n return sorted;\n}\n\n/** Compact canonical encoding — what a digest is taken over. */\nexport function stableStringify(value: unknown): string\n{\n return JSON.stringify(canonicalize(value));\n}\n\n/** Indented canonical encoding — what gets written to disk and reviewed. */\nexport function stableStringifyPretty(value: unknown): string\n{\n return `${JSON.stringify(canonicalize(value), null, 4)}\\n`;\n}\n\n/** SHA-256 of the compact canonical encoding, lowercase hex. */\nexport function stableDigest(value: unknown): string\n{\n return createHash('sha256').update(stableStringify(value), 'utf8').digest('hex');\n}\n","/**\n * Backward-compatibility comparison\n *\n * Compares the contract a build produces against the newest released snapshot.\n *\n * The rule that shapes everything here: **optional runs in opposite directions\n * on the two sides.** A request is safe when the server grows more tolerant —\n * a new optional field, a dropped field, a requirement relaxed. A response is\n * safe when the server grows more certain — a new field, a value that used to\n * be optional now always present. Collapsing both into one rule would\n * necessarily get one of them backwards.\n *\n * | change | request | response |\n * |-------------------------|---------|----------|\n * | field added (optional) | pass | pass |\n * | field added (required) | refuse | pass |\n * | field removed | pass | refuse |\n * | required → optional | pass | refuse |\n * | optional → required | refuse | pass |\n * | type changed | refuse | refuse |\n */\n\nimport { stableStringify } from './stable-json';\nimport type {\n ContractDocument,\n ContractOperation,\n ContractRequest,\n ContractViolation,\n JsonSchema,\n} from './types';\n\n/** Which side of the wire a schema sits on. The direction optional runs. */\ntype Side = 'request' | 'response';\n\nexport interface DocumentComparison\n{\n violations: ContractViolation[];\n\n /**\n * Operations present in the baseline and gone from the current contract.\n *\n * Removal is not decided here: whether an operation may go depends on\n * whether any released app still calls it, which is a separate check.\n */\n removedOperations: string[];\n}\n\nconst REQUEST_SECTIONS = ['params', 'query', 'body', 'formData', 'headers', 'cookies'] as const;\n\n/**\n * The schema's own keywords, with the structural ones removed.\n *\n * Everything left — `type`, `format`, `enum`, `anyOf`, `minLength` — is\n * compared verbatim, so a narrowed constraint counts as a type change. That\n * refuses more than the table's \"type changed\" row strictly requires, and it\n * refuses in the recoverable direction: a build that stops is fixed by cutting\n * a new contract version, while a break that passes reaches a shipped app.\n */\nfunction leafSignature(schema: JsonSchema): string\n{\n const { properties, required, items, ...rest } = schema;\n\n return stableStringify(rest);\n}\n\nfunction propertiesOf(schema: JsonSchema): Record<string, JsonSchema>\n{\n const properties = schema.properties;\n\n if (!properties || typeof properties !== 'object')\n {\n return {};\n }\n\n return properties as Record<string, JsonSchema>;\n}\n\nfunction requiredOf(schema: JsonSchema): Set<string>\n{\n const required = schema.required;\n\n if (!Array.isArray(required))\n {\n return new Set();\n }\n\n return new Set(required.filter((name): name is string => typeof name === 'string'));\n}\n\n/**\n * Would a client that sends nothing here now be refused?\n *\n * Only this level counts. A required field nested inside an optional object is\n * required *if* that object is sent, and a client that never sends it is fine.\n */\nfunction requiresClientToSend(schema: JsonSchema): boolean\n{\n if (schema.type !== 'object')\n {\n return true;\n }\n\n return requiredOf(schema).size > 0;\n}\n\ninterface CompareContext\n{\n operation: string;\n side: Side;\n violations: ContractViolation[];\n}\n\nfunction report(\n ctx: CompareContext,\n kind: ContractViolation['kind'],\n location: string,\n detail: string,\n): void\n{\n ctx.violations.push({ kind, operation: ctx.operation, location, detail });\n}\n\n/**\n * Compare one schema position across versions.\n *\n * `before`/`after` may be absent: a whole request section can appear or vanish,\n * and so can a nested field.\n */\nfunction compareSchema(\n before: JsonSchema | undefined,\n after: JsonSchema | undefined,\n location: string,\n ctx: CompareContext,\n): void\n{\n if (!before && !after)\n {\n return;\n }\n\n if (before && !after)\n {\n if (ctx.side === 'response')\n {\n report(ctx, 'response.field-removed', location, 'the response no longer carries this, and old clients read it');\n }\n\n // A request the server stopped reading is a request old clients may still send.\n return;\n }\n\n if (!before && after)\n {\n if (ctx.side === 'request' && requiresClientToSend(after))\n {\n report(\n ctx,\n 'request.required-field-added',\n location,\n 'this is new and mandatory, so every already-released client is refused',\n );\n }\n\n // A response that carries more is a response old clients ignore the rest of.\n return;\n }\n\n const from = before!;\n const to = after!;\n\n if (leafSignature(from) !== leafSignature(to))\n {\n report(\n ctx,\n ctx.side === 'request' ? 'request.type-changed' : 'response.type-changed',\n location,\n `the declared type changed: ${leafSignature(from)} → ${leafSignature(to)}`,\n );\n\n return;\n }\n\n compareProperties(from, to, location, ctx);\n compareSchema(\n from.items as JsonSchema | undefined,\n to.items as JsonSchema | undefined,\n `${location}[]`,\n ctx,\n );\n}\n\nfunction compareProperties(from: JsonSchema, to: JsonSchema, location: string, ctx: CompareContext): void\n{\n const beforeProperties = propertiesOf(from);\n const afterProperties = propertiesOf(to);\n const beforeRequired = requiredOf(from);\n const afterRequired = requiredOf(to);\n\n for (const [name, beforeSchema] of Object.entries(beforeProperties))\n {\n const where = `${location}.${name}`;\n const afterSchema = afterProperties[name];\n\n compareSchema(beforeSchema, afterSchema, where, ctx);\n\n if (!afterSchema)\n {\n continue;\n }\n\n const wasRequired = beforeRequired.has(name);\n const isRequired = afterRequired.has(name);\n\n if (ctx.side === 'request' && !wasRequired && isRequired)\n {\n report(ctx, 'request.field-became-required', where, 'clients that never sent this are now refused');\n }\n\n if (ctx.side === 'response' && wasRequired && !isRequired)\n {\n report(ctx, 'response.field-became-optional', where, 'clients that counted on this always arriving now break');\n }\n }\n\n // An added field is judged at its own level only. Optional means an old\n // client that omits it still passes, whatever the field contains.\n for (const name of Object.keys(afterProperties))\n {\n if (beforeProperties[name])\n {\n continue;\n }\n\n if (ctx.side === 'request' && afterRequired.has(name))\n {\n report(\n ctx,\n 'request.required-field-added',\n `${location}.${name}`,\n 'this is new and mandatory, so every already-released client is refused',\n );\n }\n }\n}\n\nfunction compareRequestSections(\n before: ContractRequest,\n after: ContractRequest,\n prefix: string,\n ctx: CompareContext,\n): void\n{\n for (const section of REQUEST_SECTIONS)\n {\n compareSchema(before[section], after[section], `${prefix}.${section}`, ctx);\n }\n}\n\n/** Compare one operation that exists on both sides. */\nexport function compareOperation(before: ContractOperation, after: ContractOperation): ContractViolation[]\n{\n const violations: ContractViolation[] = [];\n\n if (before.path !== after.path)\n {\n violations.push({\n kind: 'operation.path-changed',\n operation: before.name,\n detail: `path moved ${before.path} → ${after.path}; released clients call the old one`,\n });\n }\n\n if (before.method !== after.method)\n {\n violations.push({\n kind: 'operation.method-changed',\n operation: before.name,\n detail: `method changed ${before.method} → ${after.method}; released clients send the old one`,\n });\n }\n\n const requestCtx: CompareContext = { operation: before.name, side: 'request', violations };\n compareRequestSections(before.request, after.request, 'request', requestCtx);\n compareRequestSections(before.interceptor, after.interceptor, 'interceptor', requestCtx);\n\n compareSchema(\n before.response,\n after.response,\n 'response',\n { operation: before.name, side: 'response', violations },\n );\n\n return violations;\n}\n\n/**\n * Compare a released contract against the one this build produced.\n *\n * Operations that are new in `after` need no check — nothing has been promised\n * about them yet.\n */\nexport function compareDocuments(before: ContractDocument, after: ContractDocument): DocumentComparison\n{\n const current = new Map(after.operations.map(operation => [operation.name, operation]));\n const violations: ContractViolation[] = [];\n const removedOperations: string[] = [];\n\n for (const operation of before.operations)\n {\n const now = current.get(operation.name);\n\n if (!now)\n {\n removedOperations.push(operation.name);\n continue;\n }\n\n violations.push(...compareOperation(operation, now));\n }\n\n return { violations, removedOperations };\n}\n","/**\n * Released snapshots\n *\n * `contracts/released/<version>.json` is what a version actually promised. It is\n * written once, at release, and never touched again — the gate compares against\n * the newest one.\n *\n * Comparing against the newest one alone is only sound if no release is missing\n * a snapshot: compatibility is transitive through the chain, and a gap in the\n * chain silently widens what passes. That is why cutting a release writes a\n * snapshot rather than offering to.\n *\n * Each snapshot carries the SHA-256 of its own document, so a hand-edited\n * snapshot fails the gate instead of quietly moving the baseline.\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { stableDigest, stableStringifyPretty } from './stable-json';\nimport type { ContractDocument, ContractSnapshot } from './types';\n\nexport class ContractSnapshotError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractSnapshotError';\n }\n}\n\nexport const CURRENT_FILENAME = 'current.json';\nexport const RELEASED_DIRNAME = 'released';\nexport const USAGE_DIRNAME = 'usage';\n\nexport function currentPath(contractsDir: string): string\n{\n return join(contractsDir, CURRENT_FILENAME);\n}\n\nexport function releasedDir(contractsDir: string): string\n{\n return join(contractsDir, RELEASED_DIRNAME);\n}\n\nexport function usageDir(contractsDir: string): string\n{\n return join(contractsDir, USAGE_DIRNAME);\n}\n\n/**\n * Order two versions the way a release line runs.\n *\n * Numeric identifiers compare numerically (so 1.10.0 follows 1.9.0), and a\n * pre-release sorts before the release it leads to.\n */\nexport function compareVersions(a: string, b: string): number\n{\n const [aCore, aPre] = splitVersion(a);\n const [bCore, bPre] = splitVersion(b);\n\n for (let i = 0; i < 3; i++)\n {\n if (aCore[i] !== bCore[i])\n {\n return aCore[i] - bCore[i];\n }\n }\n\n if (aPre === bPre)\n {\n return 0;\n }\n\n if (aPre === undefined)\n {\n return 1;\n }\n\n if (bPre === undefined)\n {\n return -1;\n }\n\n return comparePreRelease(aPre, bPre);\n}\n\nfunction splitVersion(version: string): [number[], string | undefined]\n{\n const [core, ...rest] = version.split('-');\n const parts = core.split('.').map(part => Number.parseInt(part, 10));\n\n if (parts.length !== 3 || parts.some(part => !Number.isInteger(part) || part < 0))\n {\n throw new ContractSnapshotError(`\"${version}\" is not a version of the form major.minor.patch`);\n }\n\n return [parts, rest.length > 0 ? rest.join('-') : undefined];\n}\n\nfunction comparePreRelease(a: string, b: string): number\n{\n const aParts = a.split('.');\n const bParts = b.split('.');\n\n for (let i = 0; i < Math.max(aParts.length, bParts.length); i++)\n {\n const left = aParts[i];\n const right = bParts[i];\n\n if (left === undefined)\n {\n return -1;\n }\n\n if (right === undefined)\n {\n return 1;\n }\n\n if (left === right)\n {\n continue;\n }\n\n const leftNumeric = /^\\d+$/.test(left);\n const rightNumeric = /^\\d+$/.test(right);\n\n if (leftNumeric && rightNumeric)\n {\n return Number(left) - Number(right);\n }\n\n if (leftNumeric !== rightNumeric)\n {\n return leftNumeric ? -1 : 1;\n }\n\n return left < right ? -1 : 1;\n }\n\n return 0;\n}\n\nexport interface SnapshotFile\n{\n version: string;\n file: string;\n}\n\n/** Every released snapshot, oldest first. */\nexport function listSnapshots(contractsDir: string): SnapshotFile[]\n{\n const dir = releasedDir(contractsDir);\n\n if (!existsSync(dir))\n {\n return [];\n }\n\n return readdirSync(dir)\n .filter(name => name.endsWith('.json'))\n .map(name => ({ version: name.slice(0, -'.json'.length), file: join(dir, name) }))\n .sort((a, b) => compareVersions(a.version, b.version));\n}\n\n/** The snapshot the gate compares against, or undefined when nothing is released yet. */\nexport function newestSnapshot(contractsDir: string): SnapshotFile | undefined\n{\n const snapshots = listSnapshots(contractsDir);\n\n return snapshots[snapshots.length - 1];\n}\n\n/**\n * Read a snapshot and check its digest.\n *\n * A mismatch means the file was edited after release. The baseline is the whole\n * point of the gate, so an edited baseline is refused rather than trusted.\n */\nexport function readSnapshot(file: string): ContractSnapshot\n{\n let parsed: Partial<ContractSnapshot>;\n\n try\n {\n parsed = JSON.parse(readFileSync(file, 'utf-8')) as Partial<ContractSnapshot>;\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n throw new ContractSnapshotError(`${file} could not be read: ${message}`);\n }\n\n if (typeof parsed.version !== 'string' || typeof parsed.sha256 !== 'string' || !parsed.document)\n {\n throw new ContractSnapshotError(`${file} is not a contract snapshot: expected version, sha256 and document`);\n }\n\n const digest = stableDigest(parsed.document);\n\n if (digest !== parsed.sha256)\n {\n throw new ContractSnapshotError(\n `${file} was edited after release: it records sha256 ${parsed.sha256} but its document hashes to ${digest}`,\n );\n }\n\n return { version: parsed.version, sha256: parsed.sha256, document: parsed.document as ContractDocument };\n}\n\n/**\n * Write the snapshot for a release.\n *\n * Refuses to overwrite: a published version's promise does not change, a\n * mistake becomes a new version.\n */\nexport function writeSnapshot(contractsDir: string, version: string, document: ContractDocument): string\n{\n compareVersions(version, version);\n\n const dir = releasedDir(contractsDir);\n const file = join(dir, `${version}.json`);\n\n if (existsSync(file))\n {\n throw new ContractSnapshotError(\n `${file} already exists. A released version's contract is never rewritten — cut a new version instead.`,\n );\n }\n\n // Releases have to reach the newest snapshot in order. The gate compares\n // against the newest one alone, which is only sound while the chain has no\n // gaps — a snapshot filled in behind the newest one is never compared to\n // anything, and quietly widens what passes.\n const newest = newestSnapshot(contractsDir);\n\n if (newest && compareVersions(version, newest.version) <= 0)\n {\n throw new ContractSnapshotError(\n `${version} is not newer than the released ${newest.version}. `\n + 'The gate compares against the newest snapshot, so filling one in behind it would never be checked.',\n );\n }\n\n mkdirSync(dir, { recursive: true });\n\n const snapshot: ContractSnapshot = { version, sha256: stableDigest(document), document };\n writeFileSync(file, stableStringifyPretty(snapshot), 'utf-8');\n\n return file;\n}\n\n/** Read `contracts/current.json`. */\nexport function readCurrentDocument(contractsDir: string): ContractDocument\n{\n const file = currentPath(contractsDir);\n\n try\n {\n return JSON.parse(readFileSync(file, 'utf-8')) as ContractDocument;\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n throw new ContractSnapshotError(\n `${file} could not be read: ${message}. Run the @spfn/core:contract generator first.`,\n );\n }\n}\n\n/** Write `contracts/current.json`. Returns true when the file changed. */\nexport function writeCurrentDocument(contractsDir: string, document: ContractDocument): boolean\n{\n const file = currentPath(contractsDir);\n const content = stableStringifyPretty(document);\n const existing = existsSync(file) ? readFileSync(file, 'utf-8') : undefined;\n\n if (existing === content)\n {\n return false;\n }\n\n mkdirSync(contractsDir, { recursive: true });\n writeFileSync(file, content, 'utf-8');\n\n return true;\n}\n","/**\n * Usage files — who still calls an operation\n *\n * A released app is compiled and shipped; the server cannot ask it what it\n * calls. So each released client writes down the operations it uses, and those\n * files are what a removal is judged against:\n * `contracts/usage/<platform>-<appVersion>.json`.\n *\n * The one rule this file exists to hold: **an unreadable file and \"nobody calls\n * it\" are not the same answer.** An empty scan result reading as a pass is how\n * a removal check quietly stops checking anything. Every failure to read is a\n * refusal that names the file.\n */\n\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** One released client's declared call list. */\nexport interface UsageRecord\n{\n platform: string;\n appVersion: string;\n operations: string[];\n\n /** File this came from, for messages. */\n file: string;\n}\n\nexport type UsageReadResult =\n | { decidable: true; records: UsageRecord[] }\n | { decidable: false; reason: string };\n\nfunction parseRecord(file: string, raw: string): UsageRecord\n{\n const parsed = JSON.parse(raw) as Partial<UsageRecord>;\n\n if (typeof parsed.platform !== 'string' || parsed.platform.length === 0)\n {\n throw new Error('\"platform\" must be a non-empty string');\n }\n\n if (typeof parsed.appVersion !== 'string' || parsed.appVersion.length === 0)\n {\n throw new Error('\"appVersion\" must be a non-empty string');\n }\n\n if (!Array.isArray(parsed.operations) || parsed.operations.some(name => typeof name !== 'string'))\n {\n throw new Error('\"operations\" must be an array of operation names');\n }\n\n return {\n platform: parsed.platform,\n appVersion: parsed.appVersion,\n operations: parsed.operations,\n file,\n };\n}\n\n/**\n * Read every usage file under `usageDir`.\n *\n * Returns undecidable — never an empty pass — when the directory is missing,\n * holds no usage file, or holds one that cannot be read.\n */\nexport function readUsageRecords(usageDir: string): UsageReadResult\n{\n let entries: string[];\n\n try\n {\n if (!statSync(usageDir).isDirectory())\n {\n return { decidable: false, reason: `${usageDir} is not a directory` };\n }\n\n entries = readdirSync(usageDir).filter(name => name.endsWith('.json')).sort();\n }\n catch\n {\n return { decidable: false, reason: `${usageDir} does not exist` };\n }\n\n if (entries.length === 0)\n {\n return { decidable: false, reason: `${usageDir} holds no usage file` };\n }\n\n const records: UsageRecord[] = [];\n\n for (const entry of entries)\n {\n const file = join(usageDir, entry);\n\n try\n {\n records.push(parseRecord(file, readFileSync(file, 'utf-8')));\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n return { decidable: false, reason: `${file} could not be read: ${message}` };\n }\n }\n\n return { decidable: true, records };\n}\n\n/** Which released clients still call `operation`. */\nexport function callersOf(operation: string, records: UsageRecord[]): UsageRecord[]\n{\n return records.filter(record => record.operations.includes(operation));\n}\n","/**\n * The gate\n *\n * Puts the pieces together: read the newest released snapshot, compare this\n * build's contract against it, and decide removals against what released\n * clients still call.\n *\n * Nothing released yet is a pass — with a warning. \"This is the first contract\"\n * and \"the release that should have written a snapshot didn't\" produce the same\n * empty directory, and only a person can tell them apart.\n */\n\nimport { compareDocuments } from './compare';\nimport { newestSnapshot, readSnapshot, usageDir } from './snapshot';\nimport { callersOf, readUsageRecords } from './usage';\nimport type { ContractDocument, ContractViolation } from './types';\n\nexport interface ContractCheckResult\n{\n /** Version compared against, absent when nothing is released yet. */\n baselineVersion?: string;\n\n violations: ContractViolation[];\n\n /** Things a person should look at that do not stop the build. */\n warnings: string[];\n}\n\nexport function checkContract(contractsDir: string, current: ContractDocument): ContractCheckResult\n{\n const baseline = newestSnapshot(contractsDir);\n\n if (!baseline)\n {\n return {\n violations: [],\n warnings: [\n 'No released contract snapshot found, so nothing was compared. '\n + 'This is expected for a first contract, and a mistake if a release forgot to write one.',\n ],\n };\n }\n\n let previous: ContractDocument;\n\n try\n {\n previous = readSnapshot(baseline.file).document;\n }\n catch (error)\n {\n return {\n baselineVersion: baseline.version,\n warnings: [],\n violations: [{\n kind: 'snapshot.digest-mismatch',\n detail: error instanceof Error ? error.message : String(error),\n }],\n };\n }\n\n const { violations, removedOperations } = compareDocuments(previous, current);\n\n return {\n baselineVersion: baseline.version,\n warnings: [],\n violations: [...violations, ...judgeRemovals(contractsDir, removedOperations)],\n };\n}\n\n/**\n * Decide whether removed operations may go.\n *\n * Only reached when something was actually removed — an app that removes\n * nothing never needs a usage file to exist.\n */\nfunction judgeRemovals(contractsDir: string, removedOperations: string[]): ContractViolation[]\n{\n if (removedOperations.length === 0)\n {\n return [];\n }\n\n const usage = readUsageRecords(usageDir(contractsDir));\n\n if (!usage.decidable)\n {\n return [{\n kind: 'usage.undecidable',\n detail:\n `${removedOperations.join(', ')} would be removed, but no released client's call list could be read `\n + `(${usage.reason}). Not knowing who calls an operation is not the same as knowing nobody does.`,\n }];\n }\n\n const violations: ContractViolation[] = [];\n\n for (const operation of removedOperations)\n {\n const callers = callersOf(operation, usage.records);\n\n if (callers.length === 0)\n {\n continue;\n }\n\n violations.push({\n kind: 'usage.still-called',\n operation,\n detail:\n 'still called by '\n + callers.map(caller => `${caller.platform} ${caller.appVersion}`).join(', '),\n });\n }\n\n return violations;\n}\n\n/** Render violations as the message a failing build prints. */\nexport function formatViolations(violations: ContractViolation[]): string\n{\n return violations\n .map((violation) =>\n {\n const where = [violation.operation, violation.location].filter(Boolean).join(' ');\n\n return ` - [${violation.kind}]${where ? ` ${where}` : ''}: ${violation.detail}`;\n })\n .join('\\n');\n}\n"]}
1
+ {"version":3,"sources":["../../src/contract/collect.ts","../../src/contract/stable-json.ts","../../src/contract/compare.ts","../../src/contract/snapshot.ts","../../src/contract/usage.ts","../../src/contract/check.ts"],"names":["REQUEST_SECTIONS","usageDir","readdirSync","join","readFileSync"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Contract Collection\n *\n * Walks a loaded router and turns every route carrying `.contract()` into a\n * contract operation.\n *\n * The router is loaded and walked rather than parsed from source. Real routes\n * build their schemas from imported values — `EmailSchema`, `FileSchema()`,\n * `KEY_DEVICE_NAME_MAX_LENGTH` — which a source parser cannot resolve. Loading\n * costs a module import and no infrastructure: route modules have no\n * import-time side effects, so nothing here opens a database connection.\n */\n\nimport type { RouteDef } from '../route/route-builder';\nimport type { RouteInput } from '../route/route-input';\nimport type { Router } from '../route/router';\nimport type { ContractDocument, ContractOperation, ContractRequest, JsonSchema } from './types';\n\n/** Thrown when the router cannot produce a contract at all. */\nexport class ContractCollectionError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractCollectionError';\n }\n}\n\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'routes' in value\n && '_routes' in value;\n}\n\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'handler' in value;\n}\n\n/**\n * Strip TypeBox's symbol-keyed metadata and hand back plain JSON.\n *\n * The symbols carry no information the wire shape depends on, and they cannot\n * survive the round trip to a committed file.\n */\nfunction toJsonSchema(schema: unknown): JsonSchema\n{\n return JSON.parse(JSON.stringify(schema)) as JsonSchema;\n}\n\n/**\n * `formData` is absent on purpose — a contracted route carrying it is refused\n * before it gets here (see `assertNoFormData`). Emitting its schema alongside\n * the JSON sections described a multipart route as if it were JSON-shaped, and a\n * generated client built from that reads as correct right up to the request.\n */\nconst REQUEST_SECTIONS = ['params', 'query', 'body', 'headers', 'cookies'] as const;\n\nfunction toContractRequest(input: RouteInput | undefined): ContractRequest\n{\n const request: ContractRequest = {};\n\n if (!input)\n {\n return request;\n }\n\n for (const section of REQUEST_SECTIONS)\n {\n const schema = input[section];\n if (schema)\n {\n request[section] = toJsonSchema(schema);\n }\n }\n\n return request;\n}\n\ninterface Found\n{\n operation: ContractOperation;\n\n /** Where the route was found, for a collision message. */\n trail: string;\n}\n\nfunction visitRouter(router: Router<any>, trail: string[], found: Map<string, Found>): void\n{\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n visitRouter(entry, [...trail, name], found);\n continue;\n }\n\n if (!isRouteDef(entry))\n {\n continue;\n }\n\n const routeDef = entry as RouteDef<any>;\n if (!routeDef.contract)\n {\n continue;\n }\n\n addOperation(name, routeDef, [...trail, name], found);\n }\n\n for (const packageRouter of router._packageRouters ?? [])\n {\n visitRouter(packageRouter, [...trail, '(package)'], found);\n }\n}\n\n/**\n * Refuse a contracted route that takes multipart form data.\n *\n * Multipart is a transport-format problem, not a type problem: the contract's\n * grammar describes JSON values, and a file part has no spelling in it. The\n * refusal is loud rather than a quiet omission — a contract that silently\n * dropped the section would still claim to describe the operation, and an app\n * generated from it would break against the running server instead of at build.\n */\nfunction assertNoFormData(where: string, routeDef: RouteDef<any>): void\n{\n const input = routeDef.input as RouteInput | undefined;\n const interceptor = routeDef.interceptor as RouteInput | undefined;\n const section = input?.formData ? 'input' : interceptor?.formData ? 'interceptor' : undefined;\n\n if (!section)\n {\n return;\n }\n\n throw new ContractCollectionError(\n `Contracted route \"${where}\" declares ${section}.formData, which a contract cannot describe. `\n + 'The contract grammar covers JSON values, and multipart carries file parts that have no spelling in it. '\n + 'Drop .contract() from this route, or move the operation to a JSON body.',\n );\n}\n\nfunction addOperation(name: string, routeDef: RouteDef<any>, trail: string[], found: Map<string, Found>): void\n{\n const where = trail.join('.');\n const contract = routeDef.contract!;\n\n assertNoFormData(where, routeDef);\n\n if (!routeDef.method || !routeDef.path)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" has no method or path. `\n + 'A contract describes an operation on the wire, so both are required.',\n );\n }\n\n if (!contract.since)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" has no \"since\" version. `\n + 'The version an operation first appeared in is part of the promise.',\n );\n }\n\n if (!contract.response)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" declares no response schema. `\n + 'An operation with no body declares Type.Null().',\n );\n }\n\n const existing = found.get(name);\n if (existing)\n {\n throw new ContractCollectionError(\n `Two contracted routes are both named \"${name}\" (${existing.trail} and ${where}). `\n + 'An operation is identified by its name across versions, so names must be unique.',\n );\n }\n\n found.set(name, {\n trail: where,\n operation: {\n name,\n method: routeDef.method,\n path: routeDef.path,\n since: contract.since,\n auth: contract.auth ?? 'none',\n requiresSession: contract.requiresSession ?? false,\n ...(contract.deprecatedIn ? { deprecatedIn: contract.deprecatedIn } : {}),\n ...(contract.removedIn ? { removedIn: contract.removedIn } : {}),\n request: toContractRequest(routeDef.input),\n interceptor: toContractRequest(routeDef.interceptor),\n response: toJsonSchema(contract.response),\n },\n });\n}\n\n/**\n * Build the contract document from a loaded router.\n *\n * Routes without `.contract()` are skipped — they are not part of the promise.\n */\nexport function collectContractDocument(router: Router<any>): ContractDocument\n{\n const found = new Map<string, Found>();\n visitRouter(router, ['router'], found);\n\n const operations = [...found.values()]\n .map(entry => entry.operation)\n .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n\n return {\n documentVersion: 1,\n ...(router._contractVersion ? { contractVersion: router._contractVersion } : {}),\n compatibilityPolicy: 'perOperation',\n operations,\n };\n}\n","/**\n * Deterministic JSON\n *\n * The generated contract is committed and compared against what the generator\n * produces. If key order moved between runs, \"the committed file differs from\n * the generated one\" would flicker and the check would train people to ignore\n * it. So every emitted document is written with its object keys sorted, and the\n * digest that pins a released snapshot is taken over the same ordering with no\n * whitespace — formatting choices never move a digest.\n */\n\nimport { createHash } from 'node:crypto';\n\n/** Recursively sort object keys and drop `undefined` members. */\nexport function canonicalize(value: unknown): unknown\n{\n if (Array.isArray(value))\n {\n return value.map(canonicalize);\n }\n\n if (value === null || typeof value !== 'object')\n {\n return value;\n }\n\n const source = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(source).sort())\n {\n if (source[key] === undefined)\n {\n continue;\n }\n\n sorted[key] = canonicalize(source[key]);\n }\n\n return sorted;\n}\n\n/** Compact canonical encoding — what a digest is taken over. */\nexport function stableStringify(value: unknown): string\n{\n return JSON.stringify(canonicalize(value));\n}\n\n/** Indented canonical encoding — what gets written to disk and reviewed. */\nexport function stableStringifyPretty(value: unknown): string\n{\n return `${JSON.stringify(canonicalize(value), null, 4)}\\n`;\n}\n\n/** SHA-256 of the compact canonical encoding, lowercase hex. */\nexport function stableDigest(value: unknown): string\n{\n return createHash('sha256').update(stableStringify(value), 'utf8').digest('hex');\n}\n","/**\n * Backward-compatibility comparison\n *\n * Compares the contract a build produces against the newest released snapshot.\n *\n * The rule that shapes everything here: **optional runs in opposite directions\n * on the two sides.** A request is safe when the server grows more tolerant —\n * a new optional field, a dropped field, a requirement relaxed. A response is\n * safe when the server grows more certain — a new field, a value that used to\n * be optional now always present. Collapsing both into one rule would\n * necessarily get one of them backwards.\n *\n * | change | request | response |\n * |-------------------------|---------|----------|\n * | field added (optional) | pass | pass |\n * | field added (required) | refuse | pass |\n * | field removed | pass | refuse |\n * | required → optional | pass | refuse |\n * | optional → required | refuse | pass |\n * | type changed | refuse | refuse |\n */\n\nimport { stableStringify } from './stable-json';\nimport type {\n ContractDocument,\n ContractOperation,\n ContractRequest,\n ContractViolation,\n JsonSchema,\n} from './types';\n\n/** Which side of the wire a schema sits on. The direction optional runs. */\ntype Side = 'request' | 'response';\n\nexport interface DocumentComparison\n{\n violations: ContractViolation[];\n\n /**\n * Operations present in the baseline and gone from the current contract.\n *\n * Removal is not decided here: whether an operation may go depends on\n * whether any released app still calls it, which is a separate check.\n */\n removedOperations: string[];\n}\n\nconst REQUEST_SECTIONS = ['params', 'query', 'body', 'formData', 'headers', 'cookies'] as const;\n\n/**\n * The schema's own keywords, with the structural ones removed.\n *\n * Everything left — `type`, `format`, `enum`, `anyOf`, `minLength` — is\n * compared verbatim, so a narrowed constraint counts as a type change. That\n * refuses more than the table's \"type changed\" row strictly requires, and it\n * refuses in the recoverable direction: a build that stops is fixed by cutting\n * a new contract version, while a break that passes reaches a shipped app.\n */\nfunction leafSignature(schema: JsonSchema): string\n{\n const { properties, required, items, ...rest } = schema;\n\n return stableStringify(rest);\n}\n\nfunction propertiesOf(schema: JsonSchema): Record<string, JsonSchema>\n{\n const properties = schema.properties;\n\n if (!properties || typeof properties !== 'object')\n {\n return {};\n }\n\n return properties as Record<string, JsonSchema>;\n}\n\nfunction requiredOf(schema: JsonSchema): Set<string>\n{\n const required = schema.required;\n\n if (!Array.isArray(required))\n {\n return new Set();\n }\n\n return new Set(required.filter((name): name is string => typeof name === 'string'));\n}\n\n/**\n * Would a client that sends nothing here now be refused?\n *\n * Only this level counts. A required field nested inside an optional object is\n * required *if* that object is sent, and a client that never sends it is fine.\n */\nfunction requiresClientToSend(schema: JsonSchema): boolean\n{\n if (schema.type !== 'object')\n {\n return true;\n }\n\n return requiredOf(schema).size > 0;\n}\n\ninterface CompareContext\n{\n operation: string;\n side: Side;\n violations: ContractViolation[];\n}\n\nfunction report(\n ctx: CompareContext,\n kind: ContractViolation['kind'],\n location: string,\n detail: string,\n): void\n{\n ctx.violations.push({ kind, operation: ctx.operation, location, detail });\n}\n\n/**\n * Compare one schema position across versions.\n *\n * `before`/`after` may be absent: a whole request section can appear or vanish,\n * and so can a nested field.\n */\nfunction compareSchema(\n before: JsonSchema | undefined,\n after: JsonSchema | undefined,\n location: string,\n ctx: CompareContext,\n): void\n{\n if (!before && !after)\n {\n return;\n }\n\n if (before && !after)\n {\n if (ctx.side === 'response')\n {\n report(ctx, 'response.field-removed', location, 'the response no longer carries this, and old clients read it');\n }\n\n // A request the server stopped reading is a request old clients may still send.\n return;\n }\n\n if (!before && after)\n {\n if (ctx.side === 'request' && requiresClientToSend(after))\n {\n report(\n ctx,\n 'request.required-field-added',\n location,\n 'this is new and mandatory, so every already-released client is refused',\n );\n }\n\n // A response that carries more is a response old clients ignore the rest of.\n return;\n }\n\n const from = before!;\n const to = after!;\n\n if (leafSignature(from) !== leafSignature(to))\n {\n report(\n ctx,\n ctx.side === 'request' ? 'request.type-changed' : 'response.type-changed',\n location,\n `the declared type changed: ${leafSignature(from)} → ${leafSignature(to)}`,\n );\n\n return;\n }\n\n compareProperties(from, to, location, ctx);\n compareSchema(\n from.items as JsonSchema | undefined,\n to.items as JsonSchema | undefined,\n `${location}[]`,\n ctx,\n );\n}\n\nfunction compareProperties(from: JsonSchema, to: JsonSchema, location: string, ctx: CompareContext): void\n{\n const beforeProperties = propertiesOf(from);\n const afterProperties = propertiesOf(to);\n const beforeRequired = requiredOf(from);\n const afterRequired = requiredOf(to);\n\n for (const [name, beforeSchema] of Object.entries(beforeProperties))\n {\n const where = `${location}.${name}`;\n const afterSchema = afterProperties[name];\n\n compareSchema(beforeSchema, afterSchema, where, ctx);\n\n if (!afterSchema)\n {\n continue;\n }\n\n const wasRequired = beforeRequired.has(name);\n const isRequired = afterRequired.has(name);\n\n if (ctx.side === 'request' && !wasRequired && isRequired)\n {\n report(ctx, 'request.field-became-required', where, 'clients that never sent this are now refused');\n }\n\n if (ctx.side === 'response' && wasRequired && !isRequired)\n {\n report(ctx, 'response.field-became-optional', where, 'clients that counted on this always arriving now break');\n }\n }\n\n // An added field is judged at its own level only. Optional means an old\n // client that omits it still passes, whatever the field contains.\n for (const name of Object.keys(afterProperties))\n {\n if (beforeProperties[name])\n {\n continue;\n }\n\n if (ctx.side === 'request' && afterRequired.has(name))\n {\n report(\n ctx,\n 'request.required-field-added',\n `${location}.${name}`,\n 'this is new and mandatory, so every already-released client is refused',\n );\n }\n }\n}\n\nfunction compareRequestSections(\n before: ContractRequest,\n after: ContractRequest,\n prefix: string,\n ctx: CompareContext,\n): void\n{\n for (const section of REQUEST_SECTIONS)\n {\n compareSchema(before[section], after[section], `${prefix}.${section}`, ctx);\n }\n}\n\n/** Compare one operation that exists on both sides. */\nexport function compareOperation(before: ContractOperation, after: ContractOperation): ContractViolation[]\n{\n const violations: ContractViolation[] = [];\n\n if (before.path !== after.path)\n {\n violations.push({\n kind: 'operation.path-changed',\n operation: before.name,\n detail: `path moved ${before.path} → ${after.path}; released clients call the old one`,\n });\n }\n\n if (before.method !== after.method)\n {\n violations.push({\n kind: 'operation.method-changed',\n operation: before.name,\n detail: `method changed ${before.method} → ${after.method}; released clients send the old one`,\n });\n }\n\n const requestCtx: CompareContext = { operation: before.name, side: 'request', violations };\n compareRequestSections(before.request, after.request, 'request', requestCtx);\n compareRequestSections(before.interceptor, after.interceptor, 'interceptor', requestCtx);\n\n compareSchema(\n before.response,\n after.response,\n 'response',\n { operation: before.name, side: 'response', violations },\n );\n\n return violations;\n}\n\n/**\n * Compare a released contract against the one this build produced.\n *\n * Operations that are new in `after` need no check — nothing has been promised\n * about them yet.\n */\nexport function compareDocuments(before: ContractDocument, after: ContractDocument): DocumentComparison\n{\n const current = new Map(after.operations.map(operation => [operation.name, operation]));\n const violations: ContractViolation[] = [];\n const removedOperations: string[] = [];\n\n for (const operation of before.operations)\n {\n const now = current.get(operation.name);\n\n if (!now)\n {\n removedOperations.push(operation.name);\n continue;\n }\n\n violations.push(...compareOperation(operation, now));\n }\n\n return { violations, removedOperations };\n}\n","/**\n * Released snapshots\n *\n * `contracts/released/<version>.json` is what a version actually promised. It is\n * written once, at release, and never touched again — the gate compares against\n * the newest one.\n *\n * Comparing against the newest one alone is only sound if no release is missing\n * a snapshot: compatibility is transitive through the chain, and a gap in the\n * chain silently widens what passes. That is why cutting a release writes a\n * snapshot rather than offering to.\n *\n * Each snapshot carries the SHA-256 of its own document, so a hand-edited\n * snapshot fails the gate instead of quietly moving the baseline.\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { stableDigest, stableStringifyPretty } from './stable-json';\nimport type { ContractDocument, ContractSnapshot } from './types';\n\nexport class ContractSnapshotError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractSnapshotError';\n }\n}\n\nexport const CURRENT_FILENAME = 'current.json';\nexport const RELEASED_DIRNAME = 'released';\nexport const USAGE_DIRNAME = 'usage';\n\nexport function currentPath(contractsDir: string): string\n{\n return join(contractsDir, CURRENT_FILENAME);\n}\n\nexport function releasedDir(contractsDir: string): string\n{\n return join(contractsDir, RELEASED_DIRNAME);\n}\n\nexport function usageDir(contractsDir: string): string\n{\n return join(contractsDir, USAGE_DIRNAME);\n}\n\n/**\n * Order two versions the way a release line runs.\n *\n * Numeric identifiers compare numerically (so 1.10.0 follows 1.9.0), and a\n * pre-release sorts before the release it leads to.\n */\nexport function compareVersions(a: string, b: string): number\n{\n const [aCore, aPre] = splitVersion(a);\n const [bCore, bPre] = splitVersion(b);\n\n for (let i = 0; i < 3; i++)\n {\n if (aCore[i] !== bCore[i])\n {\n return aCore[i] - bCore[i];\n }\n }\n\n if (aPre === bPre)\n {\n return 0;\n }\n\n if (aPre === undefined)\n {\n return 1;\n }\n\n if (bPre === undefined)\n {\n return -1;\n }\n\n return comparePreRelease(aPre, bPre);\n}\n\nfunction splitVersion(version: string): [number[], string | undefined]\n{\n const [core, ...rest] = version.split('-');\n const parts = core.split('.').map(part => Number.parseInt(part, 10));\n\n if (parts.length !== 3 || parts.some(part => !Number.isInteger(part) || part < 0))\n {\n throw new ContractSnapshotError(`\"${version}\" is not a version of the form major.minor.patch`);\n }\n\n return [parts, rest.length > 0 ? rest.join('-') : undefined];\n}\n\nfunction comparePreRelease(a: string, b: string): number\n{\n const aParts = a.split('.');\n const bParts = b.split('.');\n\n for (let i = 0; i < Math.max(aParts.length, bParts.length); i++)\n {\n const left = aParts[i];\n const right = bParts[i];\n\n if (left === undefined)\n {\n return -1;\n }\n\n if (right === undefined)\n {\n return 1;\n }\n\n if (left === right)\n {\n continue;\n }\n\n const leftNumeric = /^\\d+$/.test(left);\n const rightNumeric = /^\\d+$/.test(right);\n\n if (leftNumeric && rightNumeric)\n {\n return Number(left) - Number(right);\n }\n\n if (leftNumeric !== rightNumeric)\n {\n return leftNumeric ? -1 : 1;\n }\n\n return left < right ? -1 : 1;\n }\n\n return 0;\n}\n\nexport interface SnapshotFile\n{\n version: string;\n file: string;\n}\n\n/** Every released snapshot, oldest first. */\nexport function listSnapshots(contractsDir: string): SnapshotFile[]\n{\n const dir = releasedDir(contractsDir);\n\n if (!existsSync(dir))\n {\n return [];\n }\n\n return readdirSync(dir)\n .filter(name => name.endsWith('.json'))\n .map(name => ({ version: name.slice(0, -'.json'.length), file: join(dir, name) }))\n .sort((a, b) => compareVersions(a.version, b.version));\n}\n\n/** The snapshot the gate compares against, or undefined when nothing is released yet. */\nexport function newestSnapshot(contractsDir: string): SnapshotFile | undefined\n{\n const snapshots = listSnapshots(contractsDir);\n\n return snapshots[snapshots.length - 1];\n}\n\n/**\n * Read a snapshot and check its digest.\n *\n * A mismatch means the file was edited after release. The baseline is the whole\n * point of the gate, so an edited baseline is refused rather than trusted.\n */\nexport function readSnapshot(file: string): ContractSnapshot\n{\n let parsed: Partial<ContractSnapshot>;\n\n try\n {\n parsed = JSON.parse(readFileSync(file, 'utf-8')) as Partial<ContractSnapshot>;\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n throw new ContractSnapshotError(`${file} could not be read: ${message}`);\n }\n\n if (typeof parsed.version !== 'string' || typeof parsed.sha256 !== 'string' || !parsed.document)\n {\n throw new ContractSnapshotError(`${file} is not a contract snapshot: expected version, sha256 and document`);\n }\n\n const digest = stableDigest(parsed.document);\n\n if (digest !== parsed.sha256)\n {\n throw new ContractSnapshotError(\n `${file} was edited after release: it records sha256 ${parsed.sha256} but its document hashes to ${digest}`,\n );\n }\n\n return { version: parsed.version, sha256: parsed.sha256, document: parsed.document as ContractDocument };\n}\n\n/**\n * Write the snapshot for a release.\n *\n * The version comes from the document, which took it from `.contractVersion()`\n * on the router. The filename follows the declaration rather than being told\n * separately what to say — a second place to state the version is a second place\n * for it to be wrong.\n *\n * Refuses to overwrite: a published version's promise does not change, a\n * mistake becomes a new version.\n */\nexport function writeSnapshot(contractsDir: string, document: ContractDocument): string\n{\n const version = document.contractVersion;\n\n if (!version)\n {\n throw new ContractSnapshotError(\n 'The contract declares no version, so a release cannot be named. '\n + 'Add .contractVersion(\"x.y.z\") to the router before cutting one.',\n );\n }\n compareVersions(version, version);\n\n const dir = releasedDir(contractsDir);\n const file = join(dir, `${version}.json`);\n\n if (existsSync(file))\n {\n throw new ContractSnapshotError(\n `${file} already exists. A released version's contract is never rewritten — cut a new version instead.`,\n );\n }\n\n // Releases have to reach the newest snapshot in order. The gate compares\n // against the newest one alone, which is only sound while the chain has no\n // gaps — a snapshot filled in behind the newest one is never compared to\n // anything, and quietly widens what passes.\n const newest = newestSnapshot(contractsDir);\n\n if (newest && compareVersions(version, newest.version) <= 0)\n {\n throw new ContractSnapshotError(\n `${version} is not newer than the released ${newest.version}. `\n + 'The gate compares against the newest snapshot, so filling one in behind it would never be checked.',\n );\n }\n\n mkdirSync(dir, { recursive: true });\n\n const snapshot: ContractSnapshot = { version, sha256: stableDigest(document), document };\n writeFileSync(file, stableStringifyPretty(snapshot), 'utf-8');\n\n return file;\n}\n\n/** Read `contracts/current.json`. */\nexport function readCurrentDocument(contractsDir: string): ContractDocument\n{\n const file = currentPath(contractsDir);\n\n try\n {\n return JSON.parse(readFileSync(file, 'utf-8')) as ContractDocument;\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n throw new ContractSnapshotError(\n `${file} could not be read: ${message}. Run the @spfn/core:contract generator first.`,\n );\n }\n}\n\n/** Write `contracts/current.json`. Returns true when the file changed. */\nexport function writeCurrentDocument(contractsDir: string, document: ContractDocument): boolean\n{\n const file = currentPath(contractsDir);\n const content = stableStringifyPretty(document);\n const existing = existsSync(file) ? readFileSync(file, 'utf-8') : undefined;\n\n if (existing === content)\n {\n return false;\n }\n\n mkdirSync(contractsDir, { recursive: true });\n writeFileSync(file, content, 'utf-8');\n\n return true;\n}\n","/**\n * Usage files — who still calls an operation\n *\n * A released app is compiled and shipped; the server cannot ask it what it\n * calls. So each released client writes down the operations it uses, and those\n * files are what a removal is judged against:\n * `contracts/usage/<platform>-<appVersion>.json`.\n *\n * The one rule this file exists to hold: **an unreadable file and \"nobody calls\n * it\" are not the same answer.** An empty scan result reading as a pass is how\n * a removal check quietly stops checking anything. Every failure to read is a\n * refusal that names the file.\n */\n\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** One released client's declared call list. */\nexport interface UsageRecord\n{\n platform: string;\n appVersion: string;\n operations: string[];\n\n /** File this came from, for messages. */\n file: string;\n}\n\nexport type UsageReadResult =\n | { decidable: true; records: UsageRecord[] }\n | { decidable: false; reason: string };\n\nfunction parseRecord(file: string, raw: string): UsageRecord\n{\n const parsed = JSON.parse(raw) as Partial<UsageRecord>;\n\n if (typeof parsed.platform !== 'string' || parsed.platform.length === 0)\n {\n throw new Error('\"platform\" must be a non-empty string');\n }\n\n if (typeof parsed.appVersion !== 'string' || parsed.appVersion.length === 0)\n {\n throw new Error('\"appVersion\" must be a non-empty string');\n }\n\n if (!Array.isArray(parsed.operations) || parsed.operations.some(name => typeof name !== 'string'))\n {\n throw new Error('\"operations\" must be an array of operation names');\n }\n\n return {\n platform: parsed.platform,\n appVersion: parsed.appVersion,\n operations: parsed.operations,\n file,\n };\n}\n\n/**\n * Read every usage file under `usageDir`.\n *\n * Returns undecidable — never an empty pass — when the directory is missing,\n * holds no usage file, or holds one that cannot be read.\n */\nexport function readUsageRecords(usageDir: string): UsageReadResult\n{\n let entries: string[];\n\n try\n {\n if (!statSync(usageDir).isDirectory())\n {\n return { decidable: false, reason: `${usageDir} is not a directory` };\n }\n\n entries = readdirSync(usageDir).filter(name => name.endsWith('.json')).sort();\n }\n catch\n {\n return { decidable: false, reason: `${usageDir} does not exist` };\n }\n\n if (entries.length === 0)\n {\n return { decidable: false, reason: `${usageDir} holds no usage file` };\n }\n\n const records: UsageRecord[] = [];\n\n for (const entry of entries)\n {\n const file = join(usageDir, entry);\n\n try\n {\n records.push(parseRecord(file, readFileSync(file, 'utf-8')));\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n return { decidable: false, reason: `${file} could not be read: ${message}` };\n }\n }\n\n return { decidable: true, records };\n}\n\n/** Which released clients still call `operation`. */\nexport function callersOf(operation: string, records: UsageRecord[]): UsageRecord[]\n{\n return records.filter(record => record.operations.includes(operation));\n}\n","/**\n * The gate\n *\n * Puts the pieces together: read the newest released snapshot, compare this\n * build's contract against it, and decide removals against what released\n * clients still call.\n *\n * Nothing released yet is a pass — with a warning. \"This is the first contract\"\n * and \"the release that should have written a snapshot didn't\" produce the same\n * empty directory, and only a person can tell them apart.\n */\n\nimport { compareDocuments } from './compare';\nimport { newestSnapshot, readSnapshot, usageDir } from './snapshot';\nimport { callersOf, readUsageRecords } from './usage';\nimport type { ContractDocument, ContractViolation } from './types';\n\nexport interface ContractCheckResult\n{\n /** Version compared against, absent when nothing is released yet. */\n baselineVersion?: string;\n\n violations: ContractViolation[];\n\n /** Things a person should look at that do not stop the build. */\n warnings: string[];\n}\n\nexport function checkContract(contractsDir: string, current: ContractDocument): ContractCheckResult\n{\n const baseline = newestSnapshot(contractsDir);\n\n if (!baseline)\n {\n return {\n violations: [],\n warnings: [\n 'No released contract snapshot found, so nothing was compared. '\n + 'This is expected for a first contract, and a mistake if a release forgot to write one.',\n ],\n };\n }\n\n let previous: ContractDocument;\n\n try\n {\n previous = readSnapshot(baseline.file).document;\n }\n catch (error)\n {\n return {\n baselineVersion: baseline.version,\n warnings: [],\n violations: [{\n kind: 'snapshot.digest-mismatch',\n detail: error instanceof Error ? error.message : String(error),\n }],\n };\n }\n\n const { violations, removedOperations } = compareDocuments(previous, current);\n\n return {\n baselineVersion: baseline.version,\n warnings: [],\n violations: [...violations, ...judgeRemovals(contractsDir, removedOperations)],\n };\n}\n\n/**\n * Decide whether removed operations may go.\n *\n * Only reached when something was actually removed — an app that removes\n * nothing never needs a usage file to exist.\n */\nfunction judgeRemovals(contractsDir: string, removedOperations: string[]): ContractViolation[]\n{\n if (removedOperations.length === 0)\n {\n return [];\n }\n\n const usage = readUsageRecords(usageDir(contractsDir));\n\n if (!usage.decidable)\n {\n return [{\n kind: 'usage.undecidable',\n detail:\n `${removedOperations.join(', ')} would be removed, but no released client's call list could be read `\n + `(${usage.reason}). Not knowing who calls an operation is not the same as knowing nobody does.`,\n }];\n }\n\n const violations: ContractViolation[] = [];\n\n for (const operation of removedOperations)\n {\n const callers = callersOf(operation, usage.records);\n\n if (callers.length === 0)\n {\n continue;\n }\n\n violations.push({\n kind: 'usage.still-called',\n operation,\n detail:\n 'still called by '\n + callers.map(caller => `${caller.platform} ${caller.appVersion}`).join(', '),\n });\n }\n\n return violations;\n}\n\n/** Render violations as the message a failing build prints. */\nexport function formatViolations(violations: ContractViolation[]): string\n{\n return violations\n .map((violation) =>\n {\n const where = [violation.operation, violation.location].filter(Boolean).join(' ');\n\n return ` - [${violation.kind}]${where ? ` ${where}` : ''}: ${violation.detail}`;\n })\n .join('\\n');\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { R as RouteDef, a as Router } from '../router-DhvbMhef.js';
2
- export { M as MergedInput, P as PaginatedResult, f as RouteAuthProfile, c as RouteBuilderContext, e as RouteContract, d as RouteHandlerFn, b as RouteInput, g as defineRouter, r as route } from '../router-DhvbMhef.js';
1
+ import { R as RouteDef, a as Router } from '../router-HDVa6gfc.js';
2
+ export { M as MergedInput, P as PaginatedResult, f as RouteAuthProfile, c as RouteBuilderContext, e as RouteContract, d as RouteHandlerFn, b as RouteInput, g as defineRouter, r as route } from '../router-HDVa6gfc.js';
3
3
  import { Hono, MiddlewareHandler } from 'hono';
4
4
  import { HttpMethod } from './types.js';
5
5
  export { E as ExtractMiddlewareNames, b as NamedMiddleware, N as NamedMiddlewareFactory, d as defineMiddleware, a as defineMiddlewareFactory } from '../define-middleware-B9bFuXVU.js';
@@ -273,12 +273,13 @@ var route = {
273
273
  };
274
274
 
275
275
  // src/route/router.ts
276
- function createRouterInstance(routes, packageRouters = [], globalMiddlewares = []) {
276
+ function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [], contractVersion = null) {
277
277
  return {
278
278
  routes,
279
279
  _routes: routes,
280
280
  _packageRouters: packageRouters,
281
281
  _globalMiddlewares: globalMiddlewares,
282
+ _contractVersion: contractVersion,
282
283
  packages(routers) {
283
284
  const newPackageRouters = [...this._packageRouters, ...routers];
284
285
  for (const pkgRouter of routers) {
@@ -286,13 +287,39 @@ function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [
286
287
  newPackageRouters.push(...pkgRouter._packageRouters);
287
288
  }
288
289
  }
289
- return createRouterInstance(this.routes, newPackageRouters, this._globalMiddlewares);
290
+ return createRouterInstance(
291
+ this.routes,
292
+ newPackageRouters,
293
+ this._globalMiddlewares,
294
+ this._contractVersion
295
+ );
290
296
  },
291
297
  use(middlewares) {
292
- return createRouterInstance(this.routes, this._packageRouters, [...this._globalMiddlewares, ...middlewares]);
298
+ return createRouterInstance(
299
+ this.routes,
300
+ this._packageRouters,
301
+ [...this._globalMiddlewares, ...middlewares],
302
+ this._contractVersion
303
+ );
304
+ },
305
+ contractVersion(version) {
306
+ assertContractVersion(version);
307
+ return createRouterInstance(
308
+ this.routes,
309
+ this._packageRouters,
310
+ this._globalMiddlewares,
311
+ version
312
+ );
293
313
  }
294
314
  };
295
315
  }
316
+ function assertContractVersion(version) {
317
+ if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(version)) {
318
+ throw new Error(
319
+ `contractVersion("${version}") is not a version of the form major.minor.patch. The released snapshot is named from this value and releases are compared by it.`
320
+ );
321
+ }
322
+ }
296
323
  function defineRouter(routes) {
297
324
  return createRouterInstance(routes);
298
325
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/route/route-builder.ts","../../src/route/router.ts","../../src/route/file-schema.ts","../../src/route/validation.ts","../../src/route/register-routes.ts","../../src/route/define-middleware.ts","../../src/route/helpers.ts"],"names":["Type"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Route Builder\n *\n * Provides tRPC-style chainable API for route definition\n */\n\nimport type { MiddlewareHandler } from 'hono';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { RouteContract } from './contract';\nimport type { HttpMethod } from './types';\n\n/**\n * Route handler function\n */\nexport type RouteHandlerFn<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown,\n> = (c: RouteBuilderContext<TInput, TInterceptor>) => Response | Promise<Response> | TResponse | Promise<TResponse>;\n\n/**\n * Route definition result\n *\n * Contains all information needed for type inference and registration\n */\nexport type RouteDef<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown,\n> = {\n method?: HttpMethod;\n path?: string;\n input?: TInput;\n interceptor?: TInterceptor;\n middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares?: string[] | '*';\n\n /**\n * Public promise this route makes to separately deployed clients.\n *\n * Present as a runtime value, unlike `_response`: the contract generator and\n * the compatibility gate read it.\n */\n contract?: RouteContract;\n\n handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;\n\n // Type inference helpers\n _input: TInput;\n _interceptor: TInterceptor;\n _response: TResponse;\n};\n\n/**\n * Route builder with chainable API (tRPC-style)\n */\nexport class RouteBuilder<\n TInput extends RouteInput = {},\n TInterceptor extends RouteInput = {},\n TResponse = never,\n>\n{\n public _method?: HttpMethod;\n public _path?: string;\n public _input?: TInput;\n public _interceptor?: TInterceptor;\n public _middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n public _skipMiddlewares?: string[] | '*';\n public _contract?: RouteContract;\n\n /**\n * Create a new RouteBuilder with copied properties and optional overrides\n */\n private clone<\n TNewInput extends RouteInput = TInput,\n TNewInterceptor extends RouteInput = TInterceptor,\n >(\n overrides?: Partial<{\n input: TNewInput;\n interceptor: TNewInterceptor;\n middlewares: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares: string[] | '*';\n contract: RouteContract;\n }>,\n ): RouteBuilder<TNewInput, TNewInterceptor, TResponse>\n {\n const builder = new RouteBuilder<TNewInput, TNewInterceptor, TResponse>();\n builder._method = this._method;\n builder._path = this._path;\n builder._input = (overrides?.input ?? this._input) as TNewInput | undefined;\n builder._interceptor = (overrides?.interceptor ?? this._interceptor) as TNewInterceptor | undefined;\n builder._middlewares = overrides?.middlewares ?? this._middlewares;\n builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;\n builder._contract = overrides?.contract ?? this._contract;\n\n return builder;\n }\n\n /**\n * Define input schemas\n *\n * @example\n * ```ts\n * route.get('/users/:id')\n * .input({\n * params: Type.Object({ id: Type.String() }),\n * query: Type.Object({ page: Type.Number() }),\n * headers: Type.Object({ authorization: Type.String() })\n * })\n * .handler(async (c) => {\n * const { params, query, headers } = await c.data();\n * // params = { id: string }\n * // query = { page: number }\n * // headers = { authorization: string }\n * })\n * ```\n */\n input<TNewInput extends RouteInput>(input: TNewInput): RouteBuilder<TNewInput, TInterceptor, TResponse>\n {\n return this.clone({ input });\n }\n\n /**\n * Define fields injected by interceptors\n *\n * These fields are:\n * - Available in the handler (merged with input)\n * - Excluded from client types (codegen uses only input)\n * - Not validated by route input schema (injected by middleware)\n *\n * Use this when middleware/interceptors add fields to the request\n * before it reaches the handler.\n *\n * @example\n * ```ts\n * // Auth interceptor injects crypto key fields\n * route.post('/_auth/login')\n * .input({\n * body: Type.Object({\n * email: Type.String(),\n * password: Type.String()\n * })\n * })\n * .interceptor({\n * body: Type.Object({\n * publicKey: Type.String(),\n * keyId: Type.String(),\n * fingerprint: Type.String()\n * })\n * })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * // body type: { email, password, publicKey, keyId, fingerprint }\n * // Client only sees: { email, password }\n * return loginService(body);\n * });\n * ```\n */\n interceptor<TNewInterceptor extends RouteInput>(\n interceptor: TNewInterceptor,\n ): RouteBuilder<TInput, TNewInterceptor, TResponse>\n {\n return this.clone({ interceptor });\n }\n\n /**\n * Add middlewares to the route\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n middleware(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ middlewares });\n }\n\n /**\n * Add middlewares to the route (alias for `.middleware()`)\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.middleware(middlewares);\n }\n\n /**\n * Skip server-level named middlewares\n *\n * Useful for public endpoints that should bypass auth or rate limiting\n *\n * @param middlewareNames - Array of middleware names to skip, or '*' to skip all\n *\n * @example\n * ```ts\n * // Skip specific middlewares\n * route.get('/health')\n * .skip(['auth', 'rateLimit'])\n * .handler(async (c) => c.json({ status: 'ok' }));\n *\n * // Skip only auth (still apply rate limiting)\n * route.get('/public-data')\n * .skip(['auth'])\n * .handler(async (c) => { ... });\n *\n * // Skip all middlewares\n * route.get('/public-health')\n * .skip('*')\n * .handler(async (c) => c.json({ status: 'ok' }));\n * ```\n */\n skip(middlewareNames: string[] | '*'): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ skipMiddlewares: middlewareNames });\n }\n\n /**\n * Publish this route as a versioned contract operation\n *\n * Marks the route as a promise to clients that are compiled and deployed\n * separately from the server — a mobile app, an external API consumer.\n * The `@spfn/core:contract` generator writes every contracted route into\n * `contracts/current.json`, and the build refuses a change that would break\n * an already-released client.\n *\n * Routes without `.contract()` are unaffected: they simply do not appear in\n * the contract. A web client needs nothing here — it derives its types from\n * the router in the same build.\n *\n * @example\n * ```ts\n * export const getUser = route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .contract({\n * since: '1.2.0',\n * auth: 'clientProofV1',\n * requiresSession: true,\n * response: Type.Object({\n * id: Type.String(),\n * name: Type.String(),\n * email: Type.Optional(Type.String()),\n * }),\n * })\n * .handler(async (c) => { ... });\n * ```\n */\n contract(contract: RouteContract): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ contract });\n }\n\n /**\n * Define handler function\n *\n * Response type is automatically inferred from the return value.\n * Use helper methods like `c.created()`, `c.paginated()` for proper type inference.\n *\n * @example\n * ```ts\n * // Direct return - type inferred from data\n * route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await getUser(params.id); // Type: User\n * })\n *\n * // Using c.created() - returns data with 201 status, type preserved\n * route.post('/users')\n * .input({ body: Type.Object({ name: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await createUser(body)); // Type: User\n * })\n *\n * // Using c.paginated() - returns PaginatedResult<T>\n * route.get('/users')\n * .handler(async (c) => {\n * const users = await getUsers();\n * return c.paginated(users, 1, 20, 100); // Type: PaginatedResult<User>\n * })\n *\n * // Using c.noContent() - returns void\n * route.delete('/users/:id')\n * .handler(async (c) => {\n * await deleteUser(params.id);\n * return c.noContent(); // Type: void\n * })\n *\n * // Using c.json() - returns Response (type inference lost)\n * // Use only when you need custom status codes not covered by helpers\n * route.get('/custom')\n * .handler(async (c) => {\n * return c.json({ data }, 418); // Type: Response\n * })\n * ```\n */\n handler<THandlerResponse>(\n fn: RouteHandlerFn<TInput, TInterceptor, THandlerResponse>,\n ): RouteDef<TInput, TInterceptor, THandlerResponse>\n {\n return {\n method: this._method,\n path: this._path,\n input: this._input,\n interceptor: this._interceptor,\n middlewares: this._middlewares,\n skipMiddlewares: this._skipMiddlewares,\n contract: this._contract,\n handler: fn,\n _input: {} as TInput,\n _interceptor: {} as TInterceptor,\n _response: {} as THandlerResponse,\n };\n }\n}\n\n/**\n * Create a route definition with HTTP method shortcuts\n */\nfunction createMethodRoute(method: HttpMethod): (path: string) => RouteBuilder\n{\n return (path: string) =>\n {\n const builder = new RouteBuilder();\n builder._method = method;\n builder._path = path;\n\n return builder;\n };\n}\n\n/**\n * Route builder entry point\n *\n * @example\n * ```ts\n * // GET request\n * export const getUser = route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await db.user.findUnique({ where: { id: params.id } });\n * });\n *\n * // POST request\n * export const createUser = route.post('/users')\n * .input({ body: Type.Object({ name: Type.String(), email: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await db.user.create({ data: body }));\n * });\n * ```\n */\nexport const route = {\n get: createMethodRoute('GET'),\n post: createMethodRoute('POST'),\n put: createMethodRoute('PUT'),\n patch: createMethodRoute('PATCH'),\n delete: createMethodRoute('DELETE'),\n};\n","/**\n * Router Definition\n *\n * Provides router composition and middleware management\n */\n\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteDef } from './route-builder';\n\n/**\n * Router definition - holds all routes\n */\nexport interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {\n routes: TRoutes;\n _routes: TRoutes;\n _packageRouters: Router<any>[];\n _globalMiddlewares: NamedMiddleware<string>[];\n\n /**\n * Register package routers (type-hidden)\n *\n * Package routes are:\n * - Recognized by RPC proxy and backend\n * - NOT exposed in client types (use package's own API like authApi, cmsApi)\n *\n * @example\n * ```ts\n * import { authRouter } from '@spfn/auth/server';\n * import { cmsAppRouter } from '@spfn/cms/server';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // Client usage:\n * // api.getRoot.call({}) - app routes\n * // authApi.login.call({}) - package API\n * ```\n */\n packages(routers: Router<any>[]): Router<TRoutes>;\n\n /**\n * Register global middlewares\n *\n * Applied to all routes unless explicitly skipped via .skip()\n *\n * @example\n * ```ts\n * import { authMiddleware, loggingMiddleware } from './middlewares';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n * ```\n */\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;\n}\n\n/**\n * Create a Router instance with chainable methods\n */\nfunction createRouterInstance<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes,\n packageRouters: Router<any>[] = [],\n globalMiddlewares: NamedMiddleware<string>[] = [],\n): Router<TRoutes>\n{\n return {\n routes,\n _routes: routes,\n _packageRouters: packageRouters,\n _globalMiddlewares: globalMiddlewares,\n\n packages(routers: Router<any>[]): Router<TRoutes>\n {\n const newPackageRouters = [...this._packageRouters, ...routers];\n\n // Also include nested package routers if any\n for (const pkgRouter of routers)\n {\n if (pkgRouter._packageRouters?.length > 0)\n {\n newPackageRouters.push(...pkgRouter._packageRouters);\n }\n }\n\n return createRouterInstance(this.routes, newPackageRouters, this._globalMiddlewares);\n },\n\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>\n {\n return createRouterInstance(this.routes, this._packageRouters, [...this._globalMiddlewares, ...middlewares]);\n },\n };\n}\n\n/**\n * Define a router with multiple routes (tRPC-style)\n *\n * Supports chainable API for packages and middlewares:\n *\n * @example\n * ```ts\n * // Basic usage\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * listExamples,\n * });\n *\n * // With package routers (type-hidden)\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // With global middlewares\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n *\n * export type AppRouter = typeof appRouter;\n * ```\n *\n * Package routes:\n * - Recognized by RPC proxy and backend for routing\n * - NOT included in AppRouter type (use authApi, cmsApi instead)\n * - Prevents confusion between app API and package APIs\n */\nexport function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes,\n): Router<TRoutes>\n{\n return createRouterInstance(routes);\n}\n","/**\n * File Schema Helpers for TypeBox\n *\n * Provides TypeBox schema definitions for file upload handling\n * with optional validation constraints.\n */\n\nimport { Kind, Type, type TSchema } from '@sinclair/typebox';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * File validation options\n */\nexport interface FileSchemaOptions\n{\n /**\n * Maximum file size in bytes\n *\n * @example 5 * 1024 * 1024 // 5MB\n */\n maxSize?: number;\n\n /**\n * Allowed MIME types\n *\n * @example ['image/jpeg', 'image/png', 'image/webp']\n */\n allowedTypes?: string[];\n\n /**\n * Minimum file size in bytes (optional)\n *\n * @example 1024 // 1KB minimum\n */\n minSize?: number;\n}\n\n/**\n * File array validation options\n */\nexport interface FileArraySchemaOptions extends FileSchemaOptions\n{\n /**\n * Maximum number of files\n *\n * @example 5\n */\n maxFiles?: number;\n\n /**\n * Minimum number of files (optional)\n *\n * @example 1\n */\n minFiles?: number;\n}\n\n/**\n * Internal schema type with file validation metadata\n */\nexport interface FileSchemaType extends TSchema\n{\n [Kind]: 'File';\n fileOptions?: FileSchemaOptions;\n}\n\nexport interface FileArraySchemaType extends TSchema\n{\n [Kind]: 'FileArray';\n fileOptions?: FileArraySchemaOptions;\n}\n\n// ============================================================================\n// Schema Creators\n// ============================================================================\n\n/**\n * Create a File schema with optional validation\n *\n * @example\n * ```ts\n * // Basic usage (no validation)\n * formData: Type.Object({\n * file: FileSchema()\n * })\n *\n * // With validation\n * formData: Type.Object({\n * avatar: FileSchema({\n * maxSize: 5 * 1024 * 1024, // 5MB\n * allowedTypes: ['image/jpeg', 'image/png', 'image/webp']\n * })\n * })\n * ```\n */\nexport function FileSchema(options?: FileSchemaOptions): FileSchemaType\n{\n return Type.Unsafe<File>({\n [Kind]: 'File',\n type: 'object',\n fileOptions: options,\n }) as FileSchemaType;\n}\n\n/**\n * Create a File array schema with optional validation\n *\n * @example\n * ```ts\n * // Basic usage (no validation)\n * formData: Type.Object({\n * files: FileArraySchema()\n * })\n *\n * // With validation\n * formData: Type.Object({\n * documents: FileArraySchema({\n * maxSize: 10 * 1024 * 1024, // 10MB per file\n * maxFiles: 5,\n * allowedTypes: ['application/pdf', 'application/msword']\n * })\n * })\n * ```\n */\nexport function FileArraySchema(options?: FileArraySchemaOptions): FileArraySchemaType\n{\n return Type.Unsafe<File[]>({\n [Kind]: 'FileArray',\n type: 'array',\n items: { [Kind]: 'File', type: 'object' },\n fileOptions: options,\n }) as FileArraySchemaType;\n}\n\n/**\n * Create an optional File schema with validation\n *\n * @example\n * ```ts\n * formData: Type.Object({\n * name: Type.String(),\n * avatar: OptionalFileSchema({\n * maxSize: 2 * 1024 * 1024,\n * allowedTypes: ['image/jpeg', 'image/png']\n * })\n * })\n * ```\n */\nexport function OptionalFileSchema(options?: FileSchemaOptions): TSchema\n{\n return Type.Optional(FileSchema(options));\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Check if a schema is a File schema\n */\nexport function isFileSchema(schema: TSchema): schema is FileSchemaType\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'File';\n}\n\n/**\n * Check if a schema is a FileArray schema\n */\nexport function isFileArraySchema(schema: TSchema): schema is FileArraySchemaType\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'FileArray';\n}\n\n/**\n * Get file options from schema\n */\nexport function getFileOptions(schema: TSchema): FileSchemaOptions | FileArraySchemaOptions | undefined\n{\n return (schema as any).fileOptions;\n}\n\n/**\n * Format file size for error messages\n */\nexport function formatFileSize(bytes: number): string\n{\n if (bytes >= 1024 * 1024 * 1024)\n {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;\n }\n if (bytes >= 1024 * 1024)\n {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n }\n if (bytes >= 1024)\n {\n return `${(bytes / 1024).toFixed(1)}KB`;\n }\n\n return `${bytes}B`;\n}\n","/**\n * Route Input Validation\n *\n * Provides unified validation logic for route input fields\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { FormatRegistry } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport type { Context } from 'hono';\nimport { ValidationError } from '@spfn/core/errors';\nimport { formatFileSize } from './file-schema';\n\n// ============================================\n// Format Registry\n// ============================================\n\nFormatRegistry.Set('email', (value: string) =>\n /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value),\n);\n\nFormatRegistry.Set('uri', (value: string) =>\n /^https?:\\/\\/.+/.test(value),\n);\n\nFormatRegistry.Set('uuid', (value: string) =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value),\n);\n\nFormatRegistry.Set('date', (value: string) =>\n /^\\d{4}-\\d{2}-\\d{2}$/.test(value),\n);\n\nFormatRegistry.Set('date-time', (value: string) =>\n !isNaN(Date.parse(value)),\n);\n\n/**\n * Validation error field info\n */\ninterface ValidationFieldError\n{\n path: string;\n message: string;\n value: unknown;\n}\n\n/**\n * Check if a value is a File object\n */\nfunction isFile(value: unknown): value is File\n{\n return value instanceof File ||\n (typeof value === 'object' &&\n value !== null &&\n 'name' in value &&\n 'size' in value &&\n 'type' in value &&\n typeof (value as any).arrayBuffer === 'function');\n}\n\n/**\n * Check if a TypeBox schema represents a File type\n */\nfunction isFileSchemaDef(schema: TSchema): boolean\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'File';\n}\n\n/**\n * Check if a TypeBox schema represents a FileArray type\n */\nfunction isFileArraySchemaDef(schema: TSchema): boolean\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'FileArray';\n}\n\n/**\n * Get file options from schema\n */\nfunction getSchemaFileOptions(schema: TSchema): any | undefined\n{\n return (schema as any).fileOptions;\n}\n\n/**\n * Validate a single file against schema options\n */\nfunction validateSingleFile(\n file: File,\n fieldPath: string,\n options: { maxSize?: number; minSize?: number; allowedTypes?: string[] } | undefined,\n errors: ValidationFieldError[],\n): void\n{\n if (!options) return;\n\n const { maxSize, minSize, allowedTypes } = options;\n\n if (maxSize !== undefined && file.size > maxSize)\n {\n errors.push({\n path: fieldPath,\n message: `File size ${formatFileSize(file.size)} exceeds maximum ${formatFileSize(maxSize)}`,\n value: file.size,\n });\n }\n\n if (minSize !== undefined && file.size < minSize)\n {\n errors.push({\n path: fieldPath,\n message: `File size ${formatFileSize(file.size)} is below minimum ${formatFileSize(minSize)}`,\n value: file.size,\n });\n }\n\n if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(file.type))\n {\n errors.push({\n path: fieldPath,\n message: `File type \"${file.type}\" is not allowed. Allowed: ${allowedTypes.join(', ')}`,\n value: file.type,\n });\n }\n}\n\n/**\n * Validate a value against a TypeBox schema\n *\n * @param schema - TypeBox schema to validate against\n * @param rawValue - Raw value to validate\n * @param fieldName - Field name for error messages\n * @returns Validated and converted value\n * @throws ValidationError if validation fails\n */\nexport function validateField<T extends Record<string, unknown>>(\n schema: TSchema | undefined,\n rawValue: Record<string, unknown>,\n fieldName: string,\n): T\n{\n if (!schema)\n {\n return {} as T;\n }\n\n const converted = Value.Convert(schema, rawValue);\n const errors = [...Value.Errors(schema, converted)];\n\n if (errors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: errors.map((e): ValidationFieldError => ({\n path: e.path,\n message: e.message,\n value: e.value,\n })),\n });\n }\n\n return converted as T;\n}\n\n/**\n * Validate form data against a TypeBox schema\n *\n * Handles File fields specially:\n * - File/FileArray schemas are validated using fileOptions (maxSize, allowedTypes, etc.)\n * - Non-file fields are validated using TypeBox\n *\n * @param schema - TypeBox schema to validate against\n * @param rawValue - Raw form data to validate\n * @param fieldName - Field name for error messages\n * @returns Validated form data\n * @throws ValidationError if validation fails\n */\nexport function validateFormData<T extends Record<string, unknown>>(\n schema: TSchema | undefined,\n rawValue: Record<string, unknown>,\n fieldName: string,\n): T\n{\n if (!schema)\n {\n return {} as T;\n }\n\n // For Object schemas, validate non-File fields and validate File fields with fileOptions\n const schemaProps = (schema as any).properties;\n if (!schemaProps)\n {\n // Not an object schema, just return raw value\n return rawValue as T;\n }\n\n const result: Record<string, unknown> = {};\n const nonFileData: Record<string, unknown> = {};\n const nonFileSchema: Record<string, TSchema> = {};\n const fileErrors: ValidationFieldError[] = [];\n\n // Process each field\n for (const [key, value] of Object.entries(rawValue))\n {\n const propSchema = schemaProps[key];\n\n if (propSchema && isFileSchemaDef(propSchema))\n {\n // Single File field\n result[key] = value;\n\n if (isFile(value))\n {\n const fileOptions = getSchemaFileOptions(propSchema);\n validateSingleFile(value, `/${key}`, fileOptions, fileErrors);\n }\n }\n else if (propSchema && isFileArraySchemaDef(propSchema))\n {\n // File array field\n result[key] = value;\n\n const fileOptions = getSchemaFileOptions(propSchema);\n const files = Array.isArray(value) ? value : [value];\n const fileArray = files.filter(isFile);\n\n // Validate file count\n if (fileOptions?.maxFiles !== undefined && fileArray.length > fileOptions.maxFiles)\n {\n fileErrors.push({\n path: `/${key}`,\n message: `Too many files. Maximum: ${fileOptions.maxFiles}, received: ${fileArray.length}`,\n value: fileArray.length,\n });\n }\n\n if (fileOptions?.minFiles !== undefined && fileArray.length < fileOptions.minFiles)\n {\n fileErrors.push({\n path: `/${key}`,\n message: `Too few files. Minimum: ${fileOptions.minFiles}, received: ${fileArray.length}`,\n value: fileArray.length,\n });\n }\n\n // Validate each file\n fileArray.forEach((file, index) =>\n {\n validateSingleFile(file, `/${key}/${index}`, fileOptions, fileErrors);\n });\n }\n else if (isFile(value) || (Array.isArray(value) && value.some(isFile)))\n {\n // Value is a File but schema doesn't indicate it - pass through without validation\n result[key] = value;\n }\n else\n {\n // Non-file field - collect for TypeBox validation\n nonFileData[key] = value;\n if (propSchema)\n {\n nonFileSchema[key] = propSchema;\n }\n }\n }\n\n // Throw file validation errors if any\n if (fileErrors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: fileErrors,\n });\n }\n\n // Validate non-file fields if any\n if (Object.keys(nonFileSchema).length > 0)\n {\n const tempSchema = {\n ...schema,\n properties: nonFileSchema,\n required: (schema as any).required?.filter((r: string) => r in nonFileSchema) ?? [],\n };\n\n const converted = Value.Convert(tempSchema, nonFileData);\n const errors = [...Value.Errors(tempSchema, converted)];\n\n if (errors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: errors.map((e): ValidationFieldError => ({\n path: e.path,\n message: e.message,\n value: e.value,\n })),\n });\n }\n\n // Merge validated non-file fields with file fields\n Object.assign(result, converted);\n }\n else\n {\n // No non-file fields to validate\n Object.assign(result, nonFileData);\n }\n\n return result as T;\n}\n\n/**\n * Extract query parameters from request URL\n *\n * Handles array values (multiple params with same key)\n */\nexport function extractQueryParams(c: Context): Record<string, string | string[]>\n{\n const url = new URL(c.req.url);\n const queryObj: Record<string, string | string[]> = {};\n\n url.searchParams.forEach((v, k) =>\n {\n const existing = queryObj[k];\n if (existing)\n {\n queryObj[k] = Array.isArray(existing) ? [...existing, v] : [existing, v];\n }\n else\n {\n queryObj[k] = v;\n }\n });\n\n return queryObj;\n}\n\n/**\n * Extract headers from request (lowercase keys)\n */\nexport function extractHeaders(c: Context): Record<string, string>\n{\n const rawHeaders: Record<string, string> = {};\n\n c.req.raw.headers.forEach((value, key) =>\n {\n rawHeaders[key.toLowerCase()] = value;\n });\n\n return rawHeaders;\n}\n\n/**\n * Extract and parse cookies from request\n */\nexport function extractCookies(c: Context): Record<string, string>\n{\n const cookieHeader = c.req.header('cookie');\n const rawCookies: Record<string, string> = {};\n\n if (cookieHeader)\n {\n cookieHeader.split(';').forEach(cookie =>\n {\n const [key, value] = cookie.trim().split('=');\n if (key && value)\n {\n rawCookies[key] = decodeURIComponent(value);\n }\n });\n }\n\n return rawCookies;\n}\n\n/**\n * Parse JSON body from request\n *\n * @throws ValidationError if JSON parsing fails\n */\nexport async function parseJsonBody(c: Context): Promise<Record<string, unknown>>\n{\n try\n {\n return await c.req.json();\n }\n catch (error)\n {\n throw new ValidationError({\n message: 'Invalid JSON body',\n fields: [{\n path: '/',\n message: 'Failed to parse JSON',\n value: error instanceof Error ? error.message : 'Unknown error',\n }],\n });\n }\n}\n\n/**\n * Parse multipart/form-data from request\n *\n * Handles file uploads and form fields.\n * Multiple files with same key are collected into arrays.\n *\n * @throws ValidationError if form data parsing fails\n */\nexport async function parseFormData(c: Context): Promise<Record<string, unknown>>\n{\n try\n {\n const formData = await c.req.formData();\n const result: Record<string, unknown> = {};\n\n formData.forEach((value, key) =>\n {\n const existing = result[key];\n\n if (existing !== undefined)\n {\n // Multiple values with same key - convert to array\n if (Array.isArray(existing))\n {\n existing.push(value);\n }\n else\n {\n result[key] = [existing, value];\n }\n }\n else\n {\n result[key] = value;\n }\n });\n\n return result;\n }\n catch (error)\n {\n throw new ValidationError({\n message: 'Invalid form data',\n fields: [{\n path: '/',\n message: 'Failed to parse form data',\n value: error instanceof Error ? error.message : 'Unknown error',\n }],\n });\n }\n}\n","/**\n * Route Registration for define-route style routing\n *\n * Registers routes defined with route.get()...handler() to Hono app\n */\n\nimport type { Context, Hono, MiddlewareHandler } from 'hono';\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';\nimport { logger } from '@spfn/core/logger';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { RouteDef } from './route-builder';\nimport type { Router } from './router';\nimport type { HttpMethod } from './types';\nimport {\n validateField,\n validateFormData,\n extractQueryParams,\n extractHeaders,\n extractCookies,\n parseJsonBody,\n parseFormData,\n} from './validation';\n\n/**\n * Registered route information for logging\n */\nexport interface RegisteredRoute\n{\n method: HttpMethod;\n path: string;\n name: string;\n}\n\n/**\n * Type guard to check if value is a Router\n */\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'routes' in value &&\n '_routes' in value;\n}\n\n/**\n * Type guard to check if value is a RouteDef\n */\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'handler' in value;\n}\n\n/**\n * Type guard to check if value is a NamedMiddleware\n */\nfunction isNamedMiddleware(value: unknown): value is NamedMiddleware<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n '_name' in value;\n}\n\n/**\n * Register routes from defineRouter() to Hono app\n *\n * @param app - Hono app instance\n * @param router - Router definition\n * @param namedMiddlewares - Optional server-level named middlewares\n *\n * @param collectedRoutes\n * @example\n * ```ts\n * const appRouter = defineRouter({\n * getUser: route.get('/users/:id')...\n * createUser: route.post('/users')...\n * });\n *\n * const app = new Hono();\n * const namedMiddlewares = [\n * { name: 'auth', handler: AuthMiddleware() },\n * { name: 'rateLimit', handler: RateLimitMiddleware() },\n * ];\n * registerRoutes(app, appRouter, namedMiddlewares);\n * ```\n */\nexport function registerRoutes<TRoutes extends Record<string, RouteDef<any> | Router<any>>>(\n app: Hono,\n router: Router<TRoutes>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>,\n collectedRoutes?: RegisteredRoute[],\n): RegisteredRoute[]\n{\n // Use provided array or create new one (top-level call)\n const routes = collectedRoutes ?? [];\n\n // Merge router's global middlewares with provided named middlewares\n const allNamedMiddlewares = [\n ...(namedMiddlewares ?? []),\n ...router._globalMiddlewares.map(mw => ({ name: mw.name, handler: mw.handler })),\n ];\n\n // 1. Register routes from router.routes\n for (const [name, routeOrRouter] of Object.entries(router.routes))\n {\n if (isRouter(routeOrRouter))\n {\n // Nested router - recursively register\n registerRoutes(app, routeOrRouter, allNamedMiddlewares, routes);\n }\n else if (isRouteDef(routeOrRouter))\n {\n // Single route - register\n const registered = registerRoute(app, name, routeOrRouter, allNamedMiddlewares);\n if (registered)\n {\n routes.push(registered);\n }\n }\n else\n {\n logger.warn(`Unknown route type for \"${name}\" - skipping`, {\n type: typeof routeOrRouter,\n });\n }\n }\n\n // 2. Register routes from package routers (_packageRouters)\n if (router._packageRouters && router._packageRouters.length > 0)\n {\n for (const pkgRouter of router._packageRouters)\n {\n registerRoutes(app, pkgRouter, allNamedMiddlewares, routes);\n }\n }\n\n return routes;\n}\n\n/**\n * Register a single route\n */\nfunction registerRoute(\n app: Hono,\n name: string,\n routeDef: RouteDef<any>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>,\n): RegisteredRoute | null\n{\n const { method, path, input, middlewares = [], skipMiddlewares, handler } = routeDef;\n\n if (!method || !path)\n {\n logger.warn(`Route \"${name}\" is missing method or path - skipping`, {\n method,\n path,\n });\n\n return null;\n }\n\n // Create wrapped handler with validation\n const wrappedHandler = async (c: Context) =>\n {\n // Create RouteBuilderContext with validation\n const { context, responseMeta } = await createRouteBuilderContext(c, input || {});\n\n // Call user handler\n const result = await handler(context);\n\n // If handler returns Response, use it directly (e.g., c.json(), c.redirect())\n if (result instanceof Response)\n {\n return result;\n }\n\n // Handle empty responses (noContent, notModified, accepted without data)\n if (responseMeta.isEmpty)\n {\n return c.body(null, responseMeta.status);\n }\n\n // Return data as JSON with status and headers from helper methods\n const hasCustomHeaders = Object.keys(responseMeta.headers).length > 0;\n\n if (hasCustomHeaders)\n {\n return c.json(result, responseMeta.status, responseMeta.headers);\n }\n\n return c.json(result, responseMeta.status);\n };\n\n // Collect all middlewares: server-level (filtered) + route-level\n const allMiddlewares: MiddlewareHandler[] = [];\n\n // Track registered middlewares for deduplication\n const registeredNames = new Set<string>();\n const registeredHandlers = new Set<MiddlewareHandler>();\n\n // Check if skipping all middlewares\n const skipAll = skipMiddlewares === '*';\n\n // Collect auto-skips from route-level middlewares (e.g., optionalAuth skips 'auth')\n const autoSkips = new Set<string>();\n for (const mw of middlewares)\n {\n if (isNamedMiddleware(mw) && mw.skips)\n {\n for (const skipName of mw.skips)\n {\n autoSkips.add(skipName);\n }\n }\n }\n\n // Add server-level named middlewares (skip those in skipMiddlewares, autoSkips, or if '*')\n if (namedMiddlewares && namedMiddlewares.length > 0)\n {\n if (skipAll)\n {\n logger.debug(`⏭️ Skipping all middlewares (*) for route: ${method} ${path}`, { name });\n }\n else\n {\n const skipSet = new Set(Array.isArray(skipMiddlewares) ? skipMiddlewares : []);\n for (const middleware of namedMiddlewares)\n {\n if (skipSet.has(middleware.name))\n {\n logger.debug(`⏭️ Skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });\n }\n else if (autoSkips.has(middleware.name))\n {\n logger.debug(`⏭️ Auto-skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });\n }\n else\n {\n allMiddlewares.push(middleware.handler);\n registeredNames.add(middleware.name);\n registeredHandlers.add(middleware.handler);\n }\n }\n }\n }\n\n // Add route-level middlewares (with deduplication by name or handler reference)\n for (const mw of middlewares)\n {\n if (isNamedMiddleware(mw))\n {\n // Named middleware: deduplicate by name\n if (registeredNames.has(mw.name))\n {\n logger.debug(`🔄 Skipping duplicate middleware '${mw.name}' for route: ${method} ${path}`, { name });\n continue;\n }\n registeredNames.add(mw.name);\n allMiddlewares.push(mw.handler);\n }\n else\n {\n // Regular middleware: deduplicate by handler reference\n if (registeredHandlers.has(mw))\n {\n logger.debug(`🔄 Skipping duplicate middleware handler for route: ${method} ${path}`, { name });\n continue;\n }\n registeredHandlers.add(mw);\n allMiddlewares.push(mw);\n }\n }\n\n // Register to Hono with correct HTTP method\n const methodLower = method.toLowerCase() as Lowercase<HttpMethod>;\n\n // hono 4.12+ overload resolution fails with spread middleware arrays\n const handlers: MiddlewareHandler[] = [...allMiddlewares, wrappedHandler as unknown as MiddlewareHandler];\n app.on([methodLower], [path], ...handlers);\n\n logger.debug(`Registered route: ${method} ${path}`, { name });\n\n return { method, path, name };\n}\n\n/**\n * Response metadata set by helper methods\n */\ninterface ResponseMeta\n{\n status: ContentfulStatusCode;\n headers: Record<string, string>;\n isEmpty: boolean;\n}\n\n/**\n * Create RouteBuilderContext from Hono Context\n *\n * Validates params, query, body, headers, cookies and returns structured input.\n * Helper methods (created, accepted, etc.) return data directly for type inference,\n * while storing response metadata internally for later use.\n */\nasync function createRouteBuilderContext<TInput extends RouteInput>(\n c: Context,\n input: TInput,\n): Promise<{ context: RouteBuilderContext<TInput>; responseMeta: ResponseMeta }>\n{\n // Validate and extract all input fields\n const params = validateField(input.params, c.req.param(), 'path parameters');\n const query = validateField(input.query, extractQueryParams(c), 'query parameters');\n const headers = validateField(input.headers, extractHeaders(c), 'headers');\n const cookies = validateField(input.cookies, extractCookies(c), 'cookies');\n\n // Body/FormData requires async parsing - determine by Content-Type\n let body: Record<string, unknown> = {};\n let formData: Record<string, unknown> = {};\n\n if (input.body || input.formData)\n {\n const contentType = c.req.header('content-type') || '';\n\n if (contentType.includes('multipart/form-data') && input.formData)\n {\n const rawFormData = await parseFormData(c);\n formData = validateFormData(input.formData, rawFormData, 'form data');\n }\n else if (input.body)\n {\n const rawBody = await parseJsonBody(c);\n body = validateField(input.body, rawBody, 'request body');\n }\n }\n\n // Cache for data() - avoid creating new object on each call\n let cachedData: any = null;\n\n // Response metadata - set by helper methods, used when building final Response\n const responseMeta: ResponseMeta = {\n status: 200 as ContentfulStatusCode,\n headers: {},\n isEmpty: false,\n };\n\n // Create context with structured data()\n const context: RouteBuilderContext<TInput> = {\n data: async () =>\n {\n if (!cachedData)\n {\n cachedData = { params, query, body, formData, headers, cookies };\n }\n\n return cachedData;\n },\n\n json: (data, status, resHeaders) =>\n {\n return c.json(data, status, resHeaders);\n },\n\n created: <T>(data: T, location?: string): T =>\n {\n responseMeta.status = 201 as ContentfulStatusCode;\n if (location)\n {\n responseMeta.headers['Location'] = location;\n }\n\n return data;\n },\n\n accepted: <T>(data?: T): any =>\n {\n responseMeta.status = 202 as ContentfulStatusCode;\n if (data === undefined)\n {\n responseMeta.isEmpty = true;\n\n return undefined;\n }\n\n return data;\n },\n\n noContent: (): void =>\n {\n responseMeta.status = 204 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n notModified: (): void =>\n {\n responseMeta.status = 304 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n paginated: <T>(data: T[], page: number, limit: number, total: number) =>\n {\n return {\n items: data,\n pagination: {\n page,\n limit,\n total,\n totalPages: Math.ceil(total / limit),\n },\n };\n },\n\n redirect: (url, status) =>\n {\n return c.redirect(url, status as RedirectStatusCode);\n },\n\n raw: c,\n };\n\n return { context, responseMeta };\n}\n","/**\n * Middleware Definition Helper\n *\n * Provides type-safe middleware definition with name inference\n */\n\nimport type { MiddlewareHandler } from 'hono';\n\n/**\n * Named middleware with type inference\n *\n * @example\n * ```ts\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * // auth logic\n * c.set('user', { id: 1 });\n * await next();\n * });\n *\n * export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {\n * // rate limit logic\n * await next();\n * });\n * ```\n */\nexport type NamedMiddleware<TName extends string = string> = {\n name: TName;\n handler: MiddlewareHandler;\n _name: TName; // Type inference helper\n skips?: string[]; // Server-level middlewares to auto-skip when this middleware is used\n};\n\n/**\n * Named middleware factory with type inference\n *\n * Factory function that creates middleware instances with parameters\n *\n * @example\n * ```ts\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * // permission check logic\n * await next();\n * }\n * );\n * ```\n */\nexport type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {\n name: TName;\n _name: TName; // Type inference helper\n} & ((...args: TArgs) => MiddlewareHandler);\n\n/**\n * Define a named middleware\n *\n * Creates a middleware with a unique name that can be referenced\n * in route-level skip() calls with full type safety.\n *\n * Supports two patterns:\n * 1. Regular middleware: Direct middleware handler\n * 2. Factory middleware: Function that creates middleware with parameters\n *\n * @param name - Unique middleware name\n * @param handler - Middleware handler function\n * @returns Named middleware with type inference\n *\n * @example\n * ```ts\n * // Regular middleware\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * const token = c.req.header('authorization');\n * if (!token) {\n * return c.json({ error: 'Unauthorized' }, 401);\n * }\n * c.set('user', await verifyToken(token));\n * await next();\n * });\n *\n * // Factory middleware\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * const user = c.get('user');\n * if (!hasPermissions(user, permissions)) {\n * return c.json({ error: 'Forbidden' }, 403);\n * }\n * await next();\n * }\n * );\n *\n * // server.config.ts\n * export default defineServerConfig()\n * .middlewares([authMiddleware])\n * .routes(appRouter)\n * .build();\n *\n * // routes.ts - skip by name\n * export const publicRoute = route.get('/health')\n * .skip(['auth']) // ✅ Type-safe! Autocomplete!\n * .handler(async (c) => c.success({ status: 'ok' }));\n *\n * // Use factory middleware inline\n * export const protectedRoute = route.get('/admin')\n * .use([requirePermissions('admin:write')])\n * .handler(async (c) => { ... });\n * ```\n */\n/**\n * Options for defineMiddleware\n */\nexport interface DefineMiddlewareOptions\n{\n /**\n * Server-level middleware names to auto-skip when this middleware is used at route level.\n *\n * @example\n * ```ts\n * // optionalAuth auto-skips the global 'auth' middleware\n * export const optionalAuth = defineMiddleware('optionalAuth', handler, {\n * skips: ['auth']\n * });\n *\n * // Usage: .use([optionalAuth]) — no need for .skip(['auth'])\n * ```\n */\n skips?: string[];\n}\n\nexport function defineMiddleware<TName extends string>(\n name: TName,\n handler: MiddlewareHandler,\n options?: DefineMiddlewareOptions,\n): NamedMiddleware<TName>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler,\n options?: DefineMiddlewareOptions,\n): NamedMiddlewareFactory<TName, TArgs>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[] = []>(\n name: TName,\n handlerOrFactory: MiddlewareHandler | ((...args: TArgs) => MiddlewareHandler),\n options?: DefineMiddlewareOptions,\n): NamedMiddleware<TName> | NamedMiddlewareFactory<TName, TArgs>\n{\n const skips = options?.skips;\n\n // Distinguish between regular middleware and factory by parameter count\n // MiddlewareHandler always has exactly 2 parameters: (c, next)\n // Factory has any other number of parameters\n if (typeof handlerOrFactory === 'function')\n {\n const paramCount = handlerOrFactory.length;\n\n // Regular middleware handler (c, next) => ...\n if (paramCount === 2)\n {\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n ...(skips && { skips }),\n };\n }\n // Factory (...args) => (c, next) => ...\n else\n {\n // Create a new wrapper function to avoid \"Cannot assign to read only property 'name'\" error\n const factory = handlerOrFactory as (...args: TArgs) => MiddlewareHandler;\n const wrapper = (...args: TArgs) => factory(...args);\n\n // Use Object.defineProperty to set name property (which is read-only by default)\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n if (skips)\n {\n Object.defineProperty(wrapper, 'skips', {\n value: skips,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n }\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n }\n }\n\n // Fallback: treat as regular middleware\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n ...(skips && { skips }),\n };\n}\n\n/**\n * Define a middleware factory explicitly\n *\n * Use this when your factory function has exactly 2 parameters,\n * which would be incorrectly detected as a regular middleware handler.\n *\n * @param name - Unique middleware name\n * @param factory - Factory function that returns a middleware handler\n * @returns Named middleware factory with type inference\n *\n * @example\n * ```ts\n * // Factory with 2 params (would be misdetected by defineMiddleware)\n * export const rateLimiter = defineMiddlewareFactory('rateLimit',\n * (limit: number, window: number) => async (c, next) => {\n * // rate limit logic using limit and window\n * await next();\n * }\n * );\n *\n * // Usage\n * route.get('/api')\n * .use([rateLimiter(100, 60000)]) // 100 requests per minute\n * .handler(...)\n * ```\n */\nexport function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler,\n): NamedMiddlewareFactory<TName, TArgs>\n{\n const wrapper = (...args: TArgs) => factory(...args);\n\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n}\n\n/**\n * Extract middleware names from an array of named middlewares\n *\n * @example\n * ```ts\n * type Middlewares = [\n * NamedMiddleware<'auth'>,\n * NamedMiddleware<'rateLimit'>\n * ];\n * type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'\n * ```\n */\nexport type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> =\n T[number]['_name'];\n","/**\n * Route Helper Functions\n *\n * Type guards and TypeBox utilities\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { Type } from '@sinclair/typebox';\nimport type { HttpMethod } from './types';\n\n/**\n * Type guard for HttpMethod\n */\nexport function isHttpMethod(value: unknown): value is HttpMethod\n{\n return (\n typeof value === 'string' &&\n ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(value)\n );\n}\n\n/**\n * Nullable - Creates a union of T | null\n *\n * @example\n * ```typescript\n * // string | null\n * firstName: Nullable(Type.String())\n * ```\n */\nexport const Nullable = <T extends TSchema>(schema: T) =>\n Type.Union([schema, Type.Null()]);\n\n/**\n * OptionalNullable - Creates a union of T | null | undefined\n *\n * @example\n * ```typescript\n * // string | null | undefined\n * lastName: OptionalNullable(Type.String())\n * ```\n */\nexport const OptionalNullable = <T extends TSchema>(schema: T) =>\n Type.Optional(Type.Union([schema, Type.Null()]));\n"]}
1
+ {"version":3,"sources":["../../src/route/route-builder.ts","../../src/route/router.ts","../../src/route/file-schema.ts","../../src/route/validation.ts","../../src/route/register-routes.ts","../../src/route/define-middleware.ts","../../src/route/helpers.ts"],"names":["Type"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Route Builder\n *\n * Provides tRPC-style chainable API for route definition\n */\n\nimport type { MiddlewareHandler } from 'hono';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { RouteContract } from './contract';\nimport type { HttpMethod } from './types';\n\n/**\n * Route handler function\n */\nexport type RouteHandlerFn<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown,\n> = (c: RouteBuilderContext<TInput, TInterceptor>) => Response | Promise<Response> | TResponse | Promise<TResponse>;\n\n/**\n * Route definition result\n *\n * Contains all information needed for type inference and registration\n */\nexport type RouteDef<\n TInput extends RouteInput = RouteInput,\n TInterceptor extends RouteInput = {},\n TResponse = unknown,\n> = {\n method?: HttpMethod;\n path?: string;\n input?: TInput;\n interceptor?: TInterceptor;\n middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares?: string[] | '*';\n\n /**\n * Public promise this route makes to separately deployed clients.\n *\n * Present as a runtime value, unlike `_response`: the contract generator and\n * the compatibility gate read it.\n */\n contract?: RouteContract;\n\n handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;\n\n // Type inference helpers\n _input: TInput;\n _interceptor: TInterceptor;\n _response: TResponse;\n};\n\n/**\n * Route builder with chainable API (tRPC-style)\n */\nexport class RouteBuilder<\n TInput extends RouteInput = {},\n TInterceptor extends RouteInput = {},\n TResponse = never,\n>\n{\n public _method?: HttpMethod;\n public _path?: string;\n public _input?: TInput;\n public _interceptor?: TInterceptor;\n public _middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];\n public _skipMiddlewares?: string[] | '*';\n public _contract?: RouteContract;\n\n /**\n * Create a new RouteBuilder with copied properties and optional overrides\n */\n private clone<\n TNewInput extends RouteInput = TInput,\n TNewInterceptor extends RouteInput = TInterceptor,\n >(\n overrides?: Partial<{\n input: TNewInput;\n interceptor: TNewInterceptor;\n middlewares: (MiddlewareHandler | NamedMiddleware<string>)[];\n skipMiddlewares: string[] | '*';\n contract: RouteContract;\n }>,\n ): RouteBuilder<TNewInput, TNewInterceptor, TResponse>\n {\n const builder = new RouteBuilder<TNewInput, TNewInterceptor, TResponse>();\n builder._method = this._method;\n builder._path = this._path;\n builder._input = (overrides?.input ?? this._input) as TNewInput | undefined;\n builder._interceptor = (overrides?.interceptor ?? this._interceptor) as TNewInterceptor | undefined;\n builder._middlewares = overrides?.middlewares ?? this._middlewares;\n builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;\n builder._contract = overrides?.contract ?? this._contract;\n\n return builder;\n }\n\n /**\n * Define input schemas\n *\n * @example\n * ```ts\n * route.get('/users/:id')\n * .input({\n * params: Type.Object({ id: Type.String() }),\n * query: Type.Object({ page: Type.Number() }),\n * headers: Type.Object({ authorization: Type.String() })\n * })\n * .handler(async (c) => {\n * const { params, query, headers } = await c.data();\n * // params = { id: string }\n * // query = { page: number }\n * // headers = { authorization: string }\n * })\n * ```\n */\n input<TNewInput extends RouteInput>(input: TNewInput): RouteBuilder<TNewInput, TInterceptor, TResponse>\n {\n return this.clone({ input });\n }\n\n /**\n * Define fields injected by interceptors\n *\n * These fields are:\n * - Available in the handler (merged with input)\n * - Excluded from client types (codegen uses only input)\n * - Not validated by route input schema (injected by middleware)\n *\n * Use this when middleware/interceptors add fields to the request\n * before it reaches the handler.\n *\n * @example\n * ```ts\n * // Auth interceptor injects crypto key fields\n * route.post('/_auth/login')\n * .input({\n * body: Type.Object({\n * email: Type.String(),\n * password: Type.String()\n * })\n * })\n * .interceptor({\n * body: Type.Object({\n * publicKey: Type.String(),\n * keyId: Type.String(),\n * fingerprint: Type.String()\n * })\n * })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * // body type: { email, password, publicKey, keyId, fingerprint }\n * // Client only sees: { email, password }\n * return loginService(body);\n * });\n * ```\n */\n interceptor<TNewInterceptor extends RouteInput>(\n interceptor: TNewInterceptor,\n ): RouteBuilder<TInput, TNewInterceptor, TResponse>\n {\n return this.clone({ interceptor });\n }\n\n /**\n * Add middlewares to the route\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n middleware(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ middlewares });\n }\n\n /**\n * Add middlewares to the route (alias for `.middleware()`)\n *\n * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).\n * Named middlewares that are already registered globally will be automatically\n * deduplicated to prevent double execution.\n *\n * @example\n * ```ts\n * import { authenticate } from '@spfn/auth/server/middleware';\n *\n * // With NamedMiddleware (auto-deduped if registered globally)\n * route.get('/users')\n * .use([authenticate, RateLimitMiddleware()])\n *\n * // With regular middleware handlers\n * route.get('/users')\n * .use([AuthMiddleware(), RateLimitMiddleware()])\n * ```\n */\n use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.middleware(middlewares);\n }\n\n /**\n * Skip server-level named middlewares\n *\n * Useful for public endpoints that should bypass auth or rate limiting\n *\n * @param middlewareNames - Array of middleware names to skip, or '*' to skip all\n *\n * @example\n * ```ts\n * // Skip specific middlewares\n * route.get('/health')\n * .skip(['auth', 'rateLimit'])\n * .handler(async (c) => c.json({ status: 'ok' }));\n *\n * // Skip only auth (still apply rate limiting)\n * route.get('/public-data')\n * .skip(['auth'])\n * .handler(async (c) => { ... });\n *\n * // Skip all middlewares\n * route.get('/public-health')\n * .skip('*')\n * .handler(async (c) => c.json({ status: 'ok' }));\n * ```\n */\n skip(middlewareNames: string[] | '*'): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ skipMiddlewares: middlewareNames });\n }\n\n /**\n * Publish this route as a versioned contract operation\n *\n * Marks the route as a promise to clients that are compiled and deployed\n * separately from the server — a mobile app, an external API consumer.\n * The `@spfn/core:contract` generator writes every contracted route into\n * `contracts/current.json`, and the build refuses a change that would break\n * an already-released client.\n *\n * Routes without `.contract()` are unaffected: they simply do not appear in\n * the contract. A web client needs nothing here — it derives its types from\n * the router in the same build.\n *\n * @example\n * ```ts\n * export const getUser = route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .contract({\n * since: '1.2.0',\n * auth: 'clientProofV1',\n * requiresSession: true,\n * response: Type.Object({\n * id: Type.String(),\n * name: Type.String(),\n * email: Type.Optional(Type.String()),\n * }),\n * })\n * .handler(async (c) => { ... });\n * ```\n */\n contract(contract: RouteContract): RouteBuilder<TInput, TInterceptor, TResponse>\n {\n return this.clone({ contract });\n }\n\n /**\n * Define handler function\n *\n * Response type is automatically inferred from the return value.\n * Use helper methods like `c.created()`, `c.paginated()` for proper type inference.\n *\n * @example\n * ```ts\n * // Direct return - type inferred from data\n * route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await getUser(params.id); // Type: User\n * })\n *\n * // Using c.created() - returns data with 201 status, type preserved\n * route.post('/users')\n * .input({ body: Type.Object({ name: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await createUser(body)); // Type: User\n * })\n *\n * // Using c.paginated() - returns PaginatedResult<T>\n * route.get('/users')\n * .handler(async (c) => {\n * const users = await getUsers();\n * return c.paginated(users, 1, 20, 100); // Type: PaginatedResult<User>\n * })\n *\n * // Using c.noContent() - returns void\n * route.delete('/users/:id')\n * .handler(async (c) => {\n * await deleteUser(params.id);\n * return c.noContent(); // Type: void\n * })\n *\n * // Using c.json() - returns Response (type inference lost)\n * // Use only when you need custom status codes not covered by helpers\n * route.get('/custom')\n * .handler(async (c) => {\n * return c.json({ data }, 418); // Type: Response\n * })\n * ```\n */\n handler<THandlerResponse>(\n fn: RouteHandlerFn<TInput, TInterceptor, THandlerResponse>,\n ): RouteDef<TInput, TInterceptor, THandlerResponse>\n {\n return {\n method: this._method,\n path: this._path,\n input: this._input,\n interceptor: this._interceptor,\n middlewares: this._middlewares,\n skipMiddlewares: this._skipMiddlewares,\n contract: this._contract,\n handler: fn,\n _input: {} as TInput,\n _interceptor: {} as TInterceptor,\n _response: {} as THandlerResponse,\n };\n }\n}\n\n/**\n * Create a route definition with HTTP method shortcuts\n */\nfunction createMethodRoute(method: HttpMethod): (path: string) => RouteBuilder\n{\n return (path: string) =>\n {\n const builder = new RouteBuilder();\n builder._method = method;\n builder._path = path;\n\n return builder;\n };\n}\n\n/**\n * Route builder entry point\n *\n * @example\n * ```ts\n * // GET request\n * export const getUser = route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => {\n * const { params } = await c.data();\n * return await db.user.findUnique({ where: { id: params.id } });\n * });\n *\n * // POST request\n * export const createUser = route.post('/users')\n * .input({ body: Type.Object({ name: Type.String(), email: Type.String() }) })\n * .handler(async (c) => {\n * const { body } = await c.data();\n * return c.created(await db.user.create({ data: body }));\n * });\n * ```\n */\nexport const route = {\n get: createMethodRoute('GET'),\n post: createMethodRoute('POST'),\n put: createMethodRoute('PUT'),\n patch: createMethodRoute('PATCH'),\n delete: createMethodRoute('DELETE'),\n};\n","/**\n * Router Definition\n *\n * Provides router composition and middleware management\n */\n\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteDef } from './route-builder';\n\n/**\n * Router definition - holds all routes\n */\nexport interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {\n routes: TRoutes;\n _routes: TRoutes;\n _packageRouters: Router<any>[];\n _globalMiddlewares: NamedMiddleware<string>[];\n\n /** The contract version these routes publish, or null when uncontracted. */\n _contractVersion: string | null;\n\n /**\n * Register package routers (type-hidden)\n *\n * Package routes are:\n * - Recognized by RPC proxy and backend\n * - NOT exposed in client types (use package's own API like authApi, cmsApi)\n *\n * @example\n * ```ts\n * import { authRouter } from '@spfn/auth/server';\n * import { cmsAppRouter } from '@spfn/cms/server';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // Client usage:\n * // api.getRoot.call({}) - app routes\n * // authApi.login.call({}) - package API\n * ```\n */\n packages(routers: Router<any>[]): Router<TRoutes>;\n\n /**\n * Register global middlewares\n *\n * Applied to all routes unless explicitly skipped via .skip()\n *\n * @example\n * ```ts\n * import { authMiddleware, loggingMiddleware } from './middlewares';\n *\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n * ```\n */\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;\n\n /**\n * Declare the contract version these routes publish.\n *\n * A client compiled against this server — a mobile app in a store — is\n * generated from one version of the contract and cannot be updated when the\n * server changes. The server announces this version on every response so\n * that client can tell whether the two ends still agree.\n *\n * This is the version's source. A released snapshot is written to\n * `contracts/released/<version>.json` from what is declared here, so the\n * filename follows the code rather than the code having to be told what the\n * filename said.\n *\n * Only a server with contracted routes needs it. Without it the contract\n * generator still writes `current.json` and still runs the compatibility\n * gate; what it cannot do is cut a release or announce a version.\n *\n * @example\n * ```ts\n * export const appRouter = defineRouter({ getRoot, listItems })\n * .contractVersion('1.2.0')\n * .packages([authRouter]);\n * ```\n */\n contractVersion(version: string): Router<TRoutes>;\n}\n\n/**\n * Create a Router instance with chainable methods\n */\nfunction createRouterInstance<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes,\n packageRouters: Router<any>[] = [],\n globalMiddlewares: NamedMiddleware<string>[] = [],\n contractVersion: string | null = null,\n): Router<TRoutes>\n{\n return {\n routes,\n _routes: routes,\n _packageRouters: packageRouters,\n _globalMiddlewares: globalMiddlewares,\n _contractVersion: contractVersion,\n\n packages(routers: Router<any>[]): Router<TRoutes>\n {\n const newPackageRouters = [...this._packageRouters, ...routers];\n\n // Also include nested package routers if any\n for (const pkgRouter of routers)\n {\n if (pkgRouter._packageRouters?.length > 0)\n {\n newPackageRouters.push(...pkgRouter._packageRouters);\n }\n }\n\n return createRouterInstance(\n this.routes,\n newPackageRouters,\n this._globalMiddlewares,\n this._contractVersion,\n );\n },\n\n use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>\n {\n return createRouterInstance(\n this.routes,\n this._packageRouters,\n [...this._globalMiddlewares, ...middlewares],\n this._contractVersion,\n );\n },\n\n contractVersion(version: string): Router<TRoutes>\n {\n assertContractVersion(version);\n\n return createRouterInstance(\n this.routes,\n this._packageRouters,\n this._globalMiddlewares,\n version,\n );\n },\n };\n}\n\n/**\n * A version that cannot be ordered cannot gate a release.\n *\n * Checked when it is declared rather than when a snapshot is cut: the failure\n * belongs next to the typo, not in a build step that runs much later.\n */\nfunction assertContractVersion(version: string): void\n{\n if (!/^\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(version))\n {\n throw new Error(\n `contractVersion(\"${version}\") is not a version of the form major.minor.patch. `\n + 'The released snapshot is named from this value and releases are compared by it.',\n );\n }\n}\n\n/**\n * Define a router with multiple routes (tRPC-style)\n *\n * Supports chainable API for packages and middlewares:\n *\n * @example\n * ```ts\n * // Basic usage\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * listExamples,\n * });\n *\n * // With package routers (type-hidden)\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter, cmsAppRouter]);\n *\n * // With global middlewares\n * export const appRouter = defineRouter({\n * getRoot,\n * getHealth,\n * })\n * .packages([authRouter])\n * .use([authMiddleware, loggingMiddleware]);\n *\n * export type AppRouter = typeof appRouter;\n * ```\n *\n * Package routes:\n * - Recognized by RPC proxy and backend for routing\n * - NOT included in AppRouter type (use authApi, cmsApi instead)\n * - Prevents confusion between app API and package APIs\n */\nexport function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(\n routes: TRoutes,\n): Router<TRoutes>\n{\n return createRouterInstance(routes);\n}\n","/**\n * File Schema Helpers for TypeBox\n *\n * Provides TypeBox schema definitions for file upload handling\n * with optional validation constraints.\n */\n\nimport { Kind, Type, type TSchema } from '@sinclair/typebox';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * File validation options\n */\nexport interface FileSchemaOptions\n{\n /**\n * Maximum file size in bytes\n *\n * @example 5 * 1024 * 1024 // 5MB\n */\n maxSize?: number;\n\n /**\n * Allowed MIME types\n *\n * @example ['image/jpeg', 'image/png', 'image/webp']\n */\n allowedTypes?: string[];\n\n /**\n * Minimum file size in bytes (optional)\n *\n * @example 1024 // 1KB minimum\n */\n minSize?: number;\n}\n\n/**\n * File array validation options\n */\nexport interface FileArraySchemaOptions extends FileSchemaOptions\n{\n /**\n * Maximum number of files\n *\n * @example 5\n */\n maxFiles?: number;\n\n /**\n * Minimum number of files (optional)\n *\n * @example 1\n */\n minFiles?: number;\n}\n\n/**\n * Internal schema type with file validation metadata\n */\nexport interface FileSchemaType extends TSchema\n{\n [Kind]: 'File';\n fileOptions?: FileSchemaOptions;\n}\n\nexport interface FileArraySchemaType extends TSchema\n{\n [Kind]: 'FileArray';\n fileOptions?: FileArraySchemaOptions;\n}\n\n// ============================================================================\n// Schema Creators\n// ============================================================================\n\n/**\n * Create a File schema with optional validation\n *\n * @example\n * ```ts\n * // Basic usage (no validation)\n * formData: Type.Object({\n * file: FileSchema()\n * })\n *\n * // With validation\n * formData: Type.Object({\n * avatar: FileSchema({\n * maxSize: 5 * 1024 * 1024, // 5MB\n * allowedTypes: ['image/jpeg', 'image/png', 'image/webp']\n * })\n * })\n * ```\n */\nexport function FileSchema(options?: FileSchemaOptions): FileSchemaType\n{\n return Type.Unsafe<File>({\n [Kind]: 'File',\n type: 'object',\n fileOptions: options,\n }) as FileSchemaType;\n}\n\n/**\n * Create a File array schema with optional validation\n *\n * @example\n * ```ts\n * // Basic usage (no validation)\n * formData: Type.Object({\n * files: FileArraySchema()\n * })\n *\n * // With validation\n * formData: Type.Object({\n * documents: FileArraySchema({\n * maxSize: 10 * 1024 * 1024, // 10MB per file\n * maxFiles: 5,\n * allowedTypes: ['application/pdf', 'application/msword']\n * })\n * })\n * ```\n */\nexport function FileArraySchema(options?: FileArraySchemaOptions): FileArraySchemaType\n{\n return Type.Unsafe<File[]>({\n [Kind]: 'FileArray',\n type: 'array',\n items: { [Kind]: 'File', type: 'object' },\n fileOptions: options,\n }) as FileArraySchemaType;\n}\n\n/**\n * Create an optional File schema with validation\n *\n * @example\n * ```ts\n * formData: Type.Object({\n * name: Type.String(),\n * avatar: OptionalFileSchema({\n * maxSize: 2 * 1024 * 1024,\n * allowedTypes: ['image/jpeg', 'image/png']\n * })\n * })\n * ```\n */\nexport function OptionalFileSchema(options?: FileSchemaOptions): TSchema\n{\n return Type.Optional(FileSchema(options));\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Check if a schema is a File schema\n */\nexport function isFileSchema(schema: TSchema): schema is FileSchemaType\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'File';\n}\n\n/**\n * Check if a schema is a FileArray schema\n */\nexport function isFileArraySchema(schema: TSchema): schema is FileArraySchemaType\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'FileArray';\n}\n\n/**\n * Get file options from schema\n */\nexport function getFileOptions(schema: TSchema): FileSchemaOptions | FileArraySchemaOptions | undefined\n{\n return (schema as any).fileOptions;\n}\n\n/**\n * Format file size for error messages\n */\nexport function formatFileSize(bytes: number): string\n{\n if (bytes >= 1024 * 1024 * 1024)\n {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;\n }\n if (bytes >= 1024 * 1024)\n {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n }\n if (bytes >= 1024)\n {\n return `${(bytes / 1024).toFixed(1)}KB`;\n }\n\n return `${bytes}B`;\n}\n","/**\n * Route Input Validation\n *\n * Provides unified validation logic for route input fields\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { FormatRegistry } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport type { Context } from 'hono';\nimport { ValidationError } from '@spfn/core/errors';\nimport { formatFileSize } from './file-schema';\n\n// ============================================\n// Format Registry\n// ============================================\n\nFormatRegistry.Set('email', (value: string) =>\n /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value),\n);\n\nFormatRegistry.Set('uri', (value: string) =>\n /^https?:\\/\\/.+/.test(value),\n);\n\nFormatRegistry.Set('uuid', (value: string) =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value),\n);\n\nFormatRegistry.Set('date', (value: string) =>\n /^\\d{4}-\\d{2}-\\d{2}$/.test(value),\n);\n\nFormatRegistry.Set('date-time', (value: string) =>\n !isNaN(Date.parse(value)),\n);\n\n/**\n * Validation error field info\n */\ninterface ValidationFieldError\n{\n path: string;\n message: string;\n value: unknown;\n}\n\n/**\n * Check if a value is a File object\n */\nfunction isFile(value: unknown): value is File\n{\n return value instanceof File ||\n (typeof value === 'object' &&\n value !== null &&\n 'name' in value &&\n 'size' in value &&\n 'type' in value &&\n typeof (value as any).arrayBuffer === 'function');\n}\n\n/**\n * Check if a TypeBox schema represents a File type\n */\nfunction isFileSchemaDef(schema: TSchema): boolean\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'File';\n}\n\n/**\n * Check if a TypeBox schema represents a FileArray type\n */\nfunction isFileArraySchemaDef(schema: TSchema): boolean\n{\n const kind = (schema as any)[Symbol.for('TypeBox.Kind')];\n\n return kind === 'FileArray';\n}\n\n/**\n * Get file options from schema\n */\nfunction getSchemaFileOptions(schema: TSchema): any | undefined\n{\n return (schema as any).fileOptions;\n}\n\n/**\n * Validate a single file against schema options\n */\nfunction validateSingleFile(\n file: File,\n fieldPath: string,\n options: { maxSize?: number; minSize?: number; allowedTypes?: string[] } | undefined,\n errors: ValidationFieldError[],\n): void\n{\n if (!options) return;\n\n const { maxSize, minSize, allowedTypes } = options;\n\n if (maxSize !== undefined && file.size > maxSize)\n {\n errors.push({\n path: fieldPath,\n message: `File size ${formatFileSize(file.size)} exceeds maximum ${formatFileSize(maxSize)}`,\n value: file.size,\n });\n }\n\n if (minSize !== undefined && file.size < minSize)\n {\n errors.push({\n path: fieldPath,\n message: `File size ${formatFileSize(file.size)} is below minimum ${formatFileSize(minSize)}`,\n value: file.size,\n });\n }\n\n if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(file.type))\n {\n errors.push({\n path: fieldPath,\n message: `File type \"${file.type}\" is not allowed. Allowed: ${allowedTypes.join(', ')}`,\n value: file.type,\n });\n }\n}\n\n/**\n * Validate a value against a TypeBox schema\n *\n * @param schema - TypeBox schema to validate against\n * @param rawValue - Raw value to validate\n * @param fieldName - Field name for error messages\n * @returns Validated and converted value\n * @throws ValidationError if validation fails\n */\nexport function validateField<T extends Record<string, unknown>>(\n schema: TSchema | undefined,\n rawValue: Record<string, unknown>,\n fieldName: string,\n): T\n{\n if (!schema)\n {\n return {} as T;\n }\n\n const converted = Value.Convert(schema, rawValue);\n const errors = [...Value.Errors(schema, converted)];\n\n if (errors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: errors.map((e): ValidationFieldError => ({\n path: e.path,\n message: e.message,\n value: e.value,\n })),\n });\n }\n\n return converted as T;\n}\n\n/**\n * Validate form data against a TypeBox schema\n *\n * Handles File fields specially:\n * - File/FileArray schemas are validated using fileOptions (maxSize, allowedTypes, etc.)\n * - Non-file fields are validated using TypeBox\n *\n * @param schema - TypeBox schema to validate against\n * @param rawValue - Raw form data to validate\n * @param fieldName - Field name for error messages\n * @returns Validated form data\n * @throws ValidationError if validation fails\n */\nexport function validateFormData<T extends Record<string, unknown>>(\n schema: TSchema | undefined,\n rawValue: Record<string, unknown>,\n fieldName: string,\n): T\n{\n if (!schema)\n {\n return {} as T;\n }\n\n // For Object schemas, validate non-File fields and validate File fields with fileOptions\n const schemaProps = (schema as any).properties;\n if (!schemaProps)\n {\n // Not an object schema, just return raw value\n return rawValue as T;\n }\n\n const result: Record<string, unknown> = {};\n const nonFileData: Record<string, unknown> = {};\n const nonFileSchema: Record<string, TSchema> = {};\n const fileErrors: ValidationFieldError[] = [];\n\n // Process each field\n for (const [key, value] of Object.entries(rawValue))\n {\n const propSchema = schemaProps[key];\n\n if (propSchema && isFileSchemaDef(propSchema))\n {\n // Single File field\n result[key] = value;\n\n if (isFile(value))\n {\n const fileOptions = getSchemaFileOptions(propSchema);\n validateSingleFile(value, `/${key}`, fileOptions, fileErrors);\n }\n }\n else if (propSchema && isFileArraySchemaDef(propSchema))\n {\n // File array field\n result[key] = value;\n\n const fileOptions = getSchemaFileOptions(propSchema);\n const files = Array.isArray(value) ? value : [value];\n const fileArray = files.filter(isFile);\n\n // Validate file count\n if (fileOptions?.maxFiles !== undefined && fileArray.length > fileOptions.maxFiles)\n {\n fileErrors.push({\n path: `/${key}`,\n message: `Too many files. Maximum: ${fileOptions.maxFiles}, received: ${fileArray.length}`,\n value: fileArray.length,\n });\n }\n\n if (fileOptions?.minFiles !== undefined && fileArray.length < fileOptions.minFiles)\n {\n fileErrors.push({\n path: `/${key}`,\n message: `Too few files. Minimum: ${fileOptions.minFiles}, received: ${fileArray.length}`,\n value: fileArray.length,\n });\n }\n\n // Validate each file\n fileArray.forEach((file, index) =>\n {\n validateSingleFile(file, `/${key}/${index}`, fileOptions, fileErrors);\n });\n }\n else if (isFile(value) || (Array.isArray(value) && value.some(isFile)))\n {\n // Value is a File but schema doesn't indicate it - pass through without validation\n result[key] = value;\n }\n else\n {\n // Non-file field - collect for TypeBox validation\n nonFileData[key] = value;\n if (propSchema)\n {\n nonFileSchema[key] = propSchema;\n }\n }\n }\n\n // Throw file validation errors if any\n if (fileErrors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: fileErrors,\n });\n }\n\n // Validate non-file fields if any\n if (Object.keys(nonFileSchema).length > 0)\n {\n const tempSchema = {\n ...schema,\n properties: nonFileSchema,\n required: (schema as any).required?.filter((r: string) => r in nonFileSchema) ?? [],\n };\n\n const converted = Value.Convert(tempSchema, nonFileData);\n const errors = [...Value.Errors(tempSchema, converted)];\n\n if (errors.length > 0)\n {\n throw new ValidationError({\n message: `Invalid ${fieldName}`,\n fields: errors.map((e): ValidationFieldError => ({\n path: e.path,\n message: e.message,\n value: e.value,\n })),\n });\n }\n\n // Merge validated non-file fields with file fields\n Object.assign(result, converted);\n }\n else\n {\n // No non-file fields to validate\n Object.assign(result, nonFileData);\n }\n\n return result as T;\n}\n\n/**\n * Extract query parameters from request URL\n *\n * Handles array values (multiple params with same key)\n */\nexport function extractQueryParams(c: Context): Record<string, string | string[]>\n{\n const url = new URL(c.req.url);\n const queryObj: Record<string, string | string[]> = {};\n\n url.searchParams.forEach((v, k) =>\n {\n const existing = queryObj[k];\n if (existing)\n {\n queryObj[k] = Array.isArray(existing) ? [...existing, v] : [existing, v];\n }\n else\n {\n queryObj[k] = v;\n }\n });\n\n return queryObj;\n}\n\n/**\n * Extract headers from request (lowercase keys)\n */\nexport function extractHeaders(c: Context): Record<string, string>\n{\n const rawHeaders: Record<string, string> = {};\n\n c.req.raw.headers.forEach((value, key) =>\n {\n rawHeaders[key.toLowerCase()] = value;\n });\n\n return rawHeaders;\n}\n\n/**\n * Extract and parse cookies from request\n */\nexport function extractCookies(c: Context): Record<string, string>\n{\n const cookieHeader = c.req.header('cookie');\n const rawCookies: Record<string, string> = {};\n\n if (cookieHeader)\n {\n cookieHeader.split(';').forEach(cookie =>\n {\n const [key, value] = cookie.trim().split('=');\n if (key && value)\n {\n rawCookies[key] = decodeURIComponent(value);\n }\n });\n }\n\n return rawCookies;\n}\n\n/**\n * Parse JSON body from request\n *\n * @throws ValidationError if JSON parsing fails\n */\nexport async function parseJsonBody(c: Context): Promise<Record<string, unknown>>\n{\n try\n {\n return await c.req.json();\n }\n catch (error)\n {\n throw new ValidationError({\n message: 'Invalid JSON body',\n fields: [{\n path: '/',\n message: 'Failed to parse JSON',\n value: error instanceof Error ? error.message : 'Unknown error',\n }],\n });\n }\n}\n\n/**\n * Parse multipart/form-data from request\n *\n * Handles file uploads and form fields.\n * Multiple files with same key are collected into arrays.\n *\n * @throws ValidationError if form data parsing fails\n */\nexport async function parseFormData(c: Context): Promise<Record<string, unknown>>\n{\n try\n {\n const formData = await c.req.formData();\n const result: Record<string, unknown> = {};\n\n formData.forEach((value, key) =>\n {\n const existing = result[key];\n\n if (existing !== undefined)\n {\n // Multiple values with same key - convert to array\n if (Array.isArray(existing))\n {\n existing.push(value);\n }\n else\n {\n result[key] = [existing, value];\n }\n }\n else\n {\n result[key] = value;\n }\n });\n\n return result;\n }\n catch (error)\n {\n throw new ValidationError({\n message: 'Invalid form data',\n fields: [{\n path: '/',\n message: 'Failed to parse form data',\n value: error instanceof Error ? error.message : 'Unknown error',\n }],\n });\n }\n}\n","/**\n * Route Registration for define-route style routing\n *\n * Registers routes defined with route.get()...handler() to Hono app\n */\n\nimport type { Context, Hono, MiddlewareHandler } from 'hono';\nimport type { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';\nimport { logger } from '@spfn/core/logger';\nimport type { NamedMiddleware } from './define-middleware';\nimport type { RouteInput } from './route-input';\nimport type { RouteBuilderContext } from './context';\nimport type { RouteDef } from './route-builder';\nimport type { Router } from './router';\nimport type { HttpMethod } from './types';\nimport {\n validateField,\n validateFormData,\n extractQueryParams,\n extractHeaders,\n extractCookies,\n parseJsonBody,\n parseFormData,\n} from './validation';\n\n/**\n * Registered route information for logging\n */\nexport interface RegisteredRoute\n{\n method: HttpMethod;\n path: string;\n name: string;\n}\n\n/**\n * Type guard to check if value is a Router\n */\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'routes' in value &&\n '_routes' in value;\n}\n\n/**\n * Type guard to check if value is a RouteDef\n */\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'handler' in value;\n}\n\n/**\n * Type guard to check if value is a NamedMiddleware\n */\nfunction isNamedMiddleware(value: unknown): value is NamedMiddleware<any>\n{\n return value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n '_name' in value;\n}\n\n/**\n * Register routes from defineRouter() to Hono app\n *\n * @param app - Hono app instance\n * @param router - Router definition\n * @param namedMiddlewares - Optional server-level named middlewares\n *\n * @param collectedRoutes\n * @example\n * ```ts\n * const appRouter = defineRouter({\n * getUser: route.get('/users/:id')...\n * createUser: route.post('/users')...\n * });\n *\n * const app = new Hono();\n * const namedMiddlewares = [\n * { name: 'auth', handler: AuthMiddleware() },\n * { name: 'rateLimit', handler: RateLimitMiddleware() },\n * ];\n * registerRoutes(app, appRouter, namedMiddlewares);\n * ```\n */\nexport function registerRoutes<TRoutes extends Record<string, RouteDef<any> | Router<any>>>(\n app: Hono,\n router: Router<TRoutes>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>,\n collectedRoutes?: RegisteredRoute[],\n): RegisteredRoute[]\n{\n // Use provided array or create new one (top-level call)\n const routes = collectedRoutes ?? [];\n\n // Merge router's global middlewares with provided named middlewares\n const allNamedMiddlewares = [\n ...(namedMiddlewares ?? []),\n ...router._globalMiddlewares.map(mw => ({ name: mw.name, handler: mw.handler })),\n ];\n\n // 1. Register routes from router.routes\n for (const [name, routeOrRouter] of Object.entries(router.routes))\n {\n if (isRouter(routeOrRouter))\n {\n // Nested router - recursively register\n registerRoutes(app, routeOrRouter, allNamedMiddlewares, routes);\n }\n else if (isRouteDef(routeOrRouter))\n {\n // Single route - register\n const registered = registerRoute(app, name, routeOrRouter, allNamedMiddlewares);\n if (registered)\n {\n routes.push(registered);\n }\n }\n else\n {\n logger.warn(`Unknown route type for \"${name}\" - skipping`, {\n type: typeof routeOrRouter,\n });\n }\n }\n\n // 2. Register routes from package routers (_packageRouters)\n if (router._packageRouters && router._packageRouters.length > 0)\n {\n for (const pkgRouter of router._packageRouters)\n {\n registerRoutes(app, pkgRouter, allNamedMiddlewares, routes);\n }\n }\n\n return routes;\n}\n\n/**\n * Register a single route\n */\nfunction registerRoute(\n app: Hono,\n name: string,\n routeDef: RouteDef<any>,\n namedMiddlewares?: ReadonlyArray<{ name: string; handler: MiddlewareHandler }>,\n): RegisteredRoute | null\n{\n const { method, path, input, middlewares = [], skipMiddlewares, handler } = routeDef;\n\n if (!method || !path)\n {\n logger.warn(`Route \"${name}\" is missing method or path - skipping`, {\n method,\n path,\n });\n\n return null;\n }\n\n // Create wrapped handler with validation\n const wrappedHandler = async (c: Context) =>\n {\n // Create RouteBuilderContext with validation\n const { context, responseMeta } = await createRouteBuilderContext(c, input || {});\n\n // Call user handler\n const result = await handler(context);\n\n // If handler returns Response, use it directly (e.g., c.json(), c.redirect())\n if (result instanceof Response)\n {\n return result;\n }\n\n // Handle empty responses (noContent, notModified, accepted without data)\n if (responseMeta.isEmpty)\n {\n return c.body(null, responseMeta.status);\n }\n\n // Return data as JSON with status and headers from helper methods\n const hasCustomHeaders = Object.keys(responseMeta.headers).length > 0;\n\n if (hasCustomHeaders)\n {\n return c.json(result, responseMeta.status, responseMeta.headers);\n }\n\n return c.json(result, responseMeta.status);\n };\n\n // Collect all middlewares: server-level (filtered) + route-level\n const allMiddlewares: MiddlewareHandler[] = [];\n\n // Track registered middlewares for deduplication\n const registeredNames = new Set<string>();\n const registeredHandlers = new Set<MiddlewareHandler>();\n\n // Check if skipping all middlewares\n const skipAll = skipMiddlewares === '*';\n\n // Collect auto-skips from route-level middlewares (e.g., optionalAuth skips 'auth')\n const autoSkips = new Set<string>();\n for (const mw of middlewares)\n {\n if (isNamedMiddleware(mw) && mw.skips)\n {\n for (const skipName of mw.skips)\n {\n autoSkips.add(skipName);\n }\n }\n }\n\n // Add server-level named middlewares (skip those in skipMiddlewares, autoSkips, or if '*')\n if (namedMiddlewares && namedMiddlewares.length > 0)\n {\n if (skipAll)\n {\n logger.debug(`⏭️ Skipping all middlewares (*) for route: ${method} ${path}`, { name });\n }\n else\n {\n const skipSet = new Set(Array.isArray(skipMiddlewares) ? skipMiddlewares : []);\n for (const middleware of namedMiddlewares)\n {\n if (skipSet.has(middleware.name))\n {\n logger.debug(`⏭️ Skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });\n }\n else if (autoSkips.has(middleware.name))\n {\n logger.debug(`⏭️ Auto-skipping middleware '${middleware.name}' for route: ${method} ${path}`, { name });\n }\n else\n {\n allMiddlewares.push(middleware.handler);\n registeredNames.add(middleware.name);\n registeredHandlers.add(middleware.handler);\n }\n }\n }\n }\n\n // Add route-level middlewares (with deduplication by name or handler reference)\n for (const mw of middlewares)\n {\n if (isNamedMiddleware(mw))\n {\n // Named middleware: deduplicate by name\n if (registeredNames.has(mw.name))\n {\n logger.debug(`🔄 Skipping duplicate middleware '${mw.name}' for route: ${method} ${path}`, { name });\n continue;\n }\n registeredNames.add(mw.name);\n allMiddlewares.push(mw.handler);\n }\n else\n {\n // Regular middleware: deduplicate by handler reference\n if (registeredHandlers.has(mw))\n {\n logger.debug(`🔄 Skipping duplicate middleware handler for route: ${method} ${path}`, { name });\n continue;\n }\n registeredHandlers.add(mw);\n allMiddlewares.push(mw);\n }\n }\n\n // Register to Hono with correct HTTP method\n const methodLower = method.toLowerCase() as Lowercase<HttpMethod>;\n\n // hono 4.12+ overload resolution fails with spread middleware arrays\n const handlers: MiddlewareHandler[] = [...allMiddlewares, wrappedHandler as unknown as MiddlewareHandler];\n app.on([methodLower], [path], ...handlers);\n\n logger.debug(`Registered route: ${method} ${path}`, { name });\n\n return { method, path, name };\n}\n\n/**\n * Response metadata set by helper methods\n */\ninterface ResponseMeta\n{\n status: ContentfulStatusCode;\n headers: Record<string, string>;\n isEmpty: boolean;\n}\n\n/**\n * Create RouteBuilderContext from Hono Context\n *\n * Validates params, query, body, headers, cookies and returns structured input.\n * Helper methods (created, accepted, etc.) return data directly for type inference,\n * while storing response metadata internally for later use.\n */\nasync function createRouteBuilderContext<TInput extends RouteInput>(\n c: Context,\n input: TInput,\n): Promise<{ context: RouteBuilderContext<TInput>; responseMeta: ResponseMeta }>\n{\n // Validate and extract all input fields\n const params = validateField(input.params, c.req.param(), 'path parameters');\n const query = validateField(input.query, extractQueryParams(c), 'query parameters');\n const headers = validateField(input.headers, extractHeaders(c), 'headers');\n const cookies = validateField(input.cookies, extractCookies(c), 'cookies');\n\n // Body/FormData requires async parsing - determine by Content-Type\n let body: Record<string, unknown> = {};\n let formData: Record<string, unknown> = {};\n\n if (input.body || input.formData)\n {\n const contentType = c.req.header('content-type') || '';\n\n if (contentType.includes('multipart/form-data') && input.formData)\n {\n const rawFormData = await parseFormData(c);\n formData = validateFormData(input.formData, rawFormData, 'form data');\n }\n else if (input.body)\n {\n const rawBody = await parseJsonBody(c);\n body = validateField(input.body, rawBody, 'request body');\n }\n }\n\n // Cache for data() - avoid creating new object on each call\n let cachedData: any = null;\n\n // Response metadata - set by helper methods, used when building final Response\n const responseMeta: ResponseMeta = {\n status: 200 as ContentfulStatusCode,\n headers: {},\n isEmpty: false,\n };\n\n // Create context with structured data()\n const context: RouteBuilderContext<TInput> = {\n data: async () =>\n {\n if (!cachedData)\n {\n cachedData = { params, query, body, formData, headers, cookies };\n }\n\n return cachedData;\n },\n\n json: (data, status, resHeaders) =>\n {\n return c.json(data, status, resHeaders);\n },\n\n created: <T>(data: T, location?: string): T =>\n {\n responseMeta.status = 201 as ContentfulStatusCode;\n if (location)\n {\n responseMeta.headers['Location'] = location;\n }\n\n return data;\n },\n\n accepted: <T>(data?: T): any =>\n {\n responseMeta.status = 202 as ContentfulStatusCode;\n if (data === undefined)\n {\n responseMeta.isEmpty = true;\n\n return undefined;\n }\n\n return data;\n },\n\n noContent: (): void =>\n {\n responseMeta.status = 204 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n notModified: (): void =>\n {\n responseMeta.status = 304 as ContentfulStatusCode;\n responseMeta.isEmpty = true;\n },\n\n paginated: <T>(data: T[], page: number, limit: number, total: number) =>\n {\n return {\n items: data,\n pagination: {\n page,\n limit,\n total,\n totalPages: Math.ceil(total / limit),\n },\n };\n },\n\n redirect: (url, status) =>\n {\n return c.redirect(url, status as RedirectStatusCode);\n },\n\n raw: c,\n };\n\n return { context, responseMeta };\n}\n","/**\n * Middleware Definition Helper\n *\n * Provides type-safe middleware definition with name inference\n */\n\nimport type { MiddlewareHandler } from 'hono';\n\n/**\n * Named middleware with type inference\n *\n * @example\n * ```ts\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * // auth logic\n * c.set('user', { id: 1 });\n * await next();\n * });\n *\n * export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {\n * // rate limit logic\n * await next();\n * });\n * ```\n */\nexport type NamedMiddleware<TName extends string = string> = {\n name: TName;\n handler: MiddlewareHandler;\n _name: TName; // Type inference helper\n skips?: string[]; // Server-level middlewares to auto-skip when this middleware is used\n};\n\n/**\n * Named middleware factory with type inference\n *\n * Factory function that creates middleware instances with parameters\n *\n * @example\n * ```ts\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * // permission check logic\n * await next();\n * }\n * );\n * ```\n */\nexport type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {\n name: TName;\n _name: TName; // Type inference helper\n} & ((...args: TArgs) => MiddlewareHandler);\n\n/**\n * Define a named middleware\n *\n * Creates a middleware with a unique name that can be referenced\n * in route-level skip() calls with full type safety.\n *\n * Supports two patterns:\n * 1. Regular middleware: Direct middleware handler\n * 2. Factory middleware: Function that creates middleware with parameters\n *\n * @param name - Unique middleware name\n * @param handler - Middleware handler function\n * @returns Named middleware with type inference\n *\n * @example\n * ```ts\n * // Regular middleware\n * export const authMiddleware = defineMiddleware('auth', async (c, next) => {\n * const token = c.req.header('authorization');\n * if (!token) {\n * return c.json({ error: 'Unauthorized' }, 401);\n * }\n * c.set('user', await verifyToken(token));\n * await next();\n * });\n *\n * // Factory middleware\n * export const requirePermissions = defineMiddleware('permission',\n * (...permissions: string[]) => async (c, next) => {\n * const user = c.get('user');\n * if (!hasPermissions(user, permissions)) {\n * return c.json({ error: 'Forbidden' }, 403);\n * }\n * await next();\n * }\n * );\n *\n * // server.config.ts\n * export default defineServerConfig()\n * .middlewares([authMiddleware])\n * .routes(appRouter)\n * .build();\n *\n * // routes.ts - skip by name\n * export const publicRoute = route.get('/health')\n * .skip(['auth']) // ✅ Type-safe! Autocomplete!\n * .handler(async (c) => c.success({ status: 'ok' }));\n *\n * // Use factory middleware inline\n * export const protectedRoute = route.get('/admin')\n * .use([requirePermissions('admin:write')])\n * .handler(async (c) => { ... });\n * ```\n */\n/**\n * Options for defineMiddleware\n */\nexport interface DefineMiddlewareOptions\n{\n /**\n * Server-level middleware names to auto-skip when this middleware is used at route level.\n *\n * @example\n * ```ts\n * // optionalAuth auto-skips the global 'auth' middleware\n * export const optionalAuth = defineMiddleware('optionalAuth', handler, {\n * skips: ['auth']\n * });\n *\n * // Usage: .use([optionalAuth]) — no need for .skip(['auth'])\n * ```\n */\n skips?: string[];\n}\n\nexport function defineMiddleware<TName extends string>(\n name: TName,\n handler: MiddlewareHandler,\n options?: DefineMiddlewareOptions,\n): NamedMiddleware<TName>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler,\n options?: DefineMiddlewareOptions,\n): NamedMiddlewareFactory<TName, TArgs>;\n\nexport function defineMiddleware<TName extends string, TArgs extends any[] = []>(\n name: TName,\n handlerOrFactory: MiddlewareHandler | ((...args: TArgs) => MiddlewareHandler),\n options?: DefineMiddlewareOptions,\n): NamedMiddleware<TName> | NamedMiddlewareFactory<TName, TArgs>\n{\n const skips = options?.skips;\n\n // Distinguish between regular middleware and factory by parameter count\n // MiddlewareHandler always has exactly 2 parameters: (c, next)\n // Factory has any other number of parameters\n if (typeof handlerOrFactory === 'function')\n {\n const paramCount = handlerOrFactory.length;\n\n // Regular middleware handler (c, next) => ...\n if (paramCount === 2)\n {\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n ...(skips && { skips }),\n };\n }\n // Factory (...args) => (c, next) => ...\n else\n {\n // Create a new wrapper function to avoid \"Cannot assign to read only property 'name'\" error\n const factory = handlerOrFactory as (...args: TArgs) => MiddlewareHandler;\n const wrapper = (...args: TArgs) => factory(...args);\n\n // Use Object.defineProperty to set name property (which is read-only by default)\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n if (skips)\n {\n Object.defineProperty(wrapper, 'skips', {\n value: skips,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n }\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n }\n }\n\n // Fallback: treat as regular middleware\n return {\n name,\n handler: handlerOrFactory as MiddlewareHandler,\n _name: name as TName,\n ...(skips && { skips }),\n };\n}\n\n/**\n * Define a middleware factory explicitly\n *\n * Use this when your factory function has exactly 2 parameters,\n * which would be incorrectly detected as a regular middleware handler.\n *\n * @param name - Unique middleware name\n * @param factory - Factory function that returns a middleware handler\n * @returns Named middleware factory with type inference\n *\n * @example\n * ```ts\n * // Factory with 2 params (would be misdetected by defineMiddleware)\n * export const rateLimiter = defineMiddlewareFactory('rateLimit',\n * (limit: number, window: number) => async (c, next) => {\n * // rate limit logic using limit and window\n * await next();\n * }\n * );\n *\n * // Usage\n * route.get('/api')\n * .use([rateLimiter(100, 60000)]) // 100 requests per minute\n * .handler(...)\n * ```\n */\nexport function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(\n name: TName,\n factory: (...args: TArgs) => MiddlewareHandler,\n): NamedMiddlewareFactory<TName, TArgs>\n{\n const wrapper = (...args: TArgs) => factory(...args);\n\n Object.defineProperty(wrapper, 'name', {\n value: name,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n Object.defineProperty(wrapper, '_name', {\n value: name as TName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n\n return wrapper as NamedMiddlewareFactory<TName, TArgs>;\n}\n\n/**\n * Extract middleware names from an array of named middlewares\n *\n * @example\n * ```ts\n * type Middlewares = [\n * NamedMiddleware<'auth'>,\n * NamedMiddleware<'rateLimit'>\n * ];\n * type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'\n * ```\n */\nexport type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> =\n T[number]['_name'];\n","/**\n * Route Helper Functions\n *\n * Type guards and TypeBox utilities\n */\n\nimport type { TSchema } from '@sinclair/typebox';\nimport { Type } from '@sinclair/typebox';\nimport type { HttpMethod } from './types';\n\n/**\n * Type guard for HttpMethod\n */\nexport function isHttpMethod(value: unknown): value is HttpMethod\n{\n return (\n typeof value === 'string' &&\n ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(value)\n );\n}\n\n/**\n * Nullable - Creates a union of T | null\n *\n * @example\n * ```typescript\n * // string | null\n * firstName: Nullable(Type.String())\n * ```\n */\nexport const Nullable = <T extends TSchema>(schema: T) =>\n Type.Union([schema, Type.Null()]);\n\n/**\n * OptionalNullable - Creates a union of T | null | undefined\n *\n * @example\n * ```typescript\n * // string | null | undefined\n * lastName: OptionalNullable(Type.String())\n * ```\n */\nexport const OptionalNullable = <T extends TSchema>(schema: T) =>\n Type.Optional(Type.Union([schema, Type.Null()]));\n"]}
@@ -257,6 +257,14 @@ interface RouteContract {
257
257
  requiresSession?: boolean;
258
258
  /** Contract version this operation was announced for removal in. */
259
259
  deprecatedIn?: string;
260
+ /**
261
+ * Contract version this operation was removed in.
262
+ *
263
+ * Set on a route kept alive only to carry the record. A client generated
264
+ * before the removal still calls it, and this is what tells that client the
265
+ * operation went and when — a route that simply disappears says nothing.
266
+ */
267
+ removedIn?: string;
260
268
  }
261
269
 
262
270
  /**
@@ -556,6 +564,8 @@ interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router
556
564
  _routes: TRoutes;
557
565
  _packageRouters: Router<any>[];
558
566
  _globalMiddlewares: NamedMiddleware<string>[];
567
+ /** The contract version these routes publish, or null when uncontracted. */
568
+ _contractVersion: string | null;
559
569
  /**
560
570
  * Register package routers (type-hidden)
561
571
  *
@@ -598,6 +608,31 @@ interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router
598
608
  * ```
599
609
  */
600
610
  use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;
611
+ /**
612
+ * Declare the contract version these routes publish.
613
+ *
614
+ * A client compiled against this server — a mobile app in a store — is
615
+ * generated from one version of the contract and cannot be updated when the
616
+ * server changes. The server announces this version on every response so
617
+ * that client can tell whether the two ends still agree.
618
+ *
619
+ * This is the version's source. A released snapshot is written to
620
+ * `contracts/released/<version>.json` from what is declared here, so the
621
+ * filename follows the code rather than the code having to be told what the
622
+ * filename said.
623
+ *
624
+ * Only a server with contracted routes needs it. Without it the contract
625
+ * generator still writes `current.json` and still runs the compatibility
626
+ * gate; what it cannot do is cut a release or announce a version.
627
+ *
628
+ * @example
629
+ * ```ts
630
+ * export const appRouter = defineRouter({ getRoot, listItems })
631
+ * .contractVersion('1.2.0')
632
+ * .packages([authRouter]);
633
+ * ```
634
+ */
635
+ contractVersion(version: string): Router<TRoutes>;
601
636
  }
602
637
  /**
603
638
  * Define a router with multiple routes (tRPC-style)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spfn/core",
3
- "version": "0.2.0-beta.68",
3
+ "version": "0.2.0-beta.69",
4
4
  "description": "SPFN Framework Core - File-based routing, transactions, repository pattern",
5
5
  "type": "module",
6
6
  "exports": {