@lunora/values 1.0.0-alpha.6 → 1.0.0-alpha.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,15 +2,15 @@ import { LunoraError } from '@lunora/errors';
2
2
  import { StandardSchemaV1 } from '@standard-schema/spec';
3
3
  type ValidationPath = ReadonlyArray<number | string>;
4
4
  /**
5
- * Thrown by `validator.parse` (or returned inside `safeParse`) when input does
6
- * not match the validator's shape. `path` walks from the root to the offending
7
- * value, e.g. `["users", 0, "email"]`.
8
- *
9
- * A `LunoraError` subclass: carries `code: "VALIDATION_ERROR"` and `status: 400`
10
- * so the runtime/DO transport mappers surface it structurally (a request that
11
- * fails validation is a 400), while keeping the `name: "ValidationError"` and the
12
- * `path`/`expected`/`received` diagnostics.
13
- */
5
+ * Thrown by `validator.parse` (or returned inside `safeParse`) when input does
6
+ * not match the validator's shape. `path` walks from the root to the offending
7
+ * value, e.g. `["users", 0, "email"]`.
8
+ *
9
+ * A `LunoraError` subclass: carries `code: "VALIDATION_ERROR"` and `status: 400`
10
+ * so the runtime/DO transport mappers surface it structurally (a request that
11
+ * fails validation is a 400), while keeping the `name: "ValidationError"` and the
12
+ * `path`/`expected`/`received` diagnostics.
13
+ */
14
14
  declare class ValidationError extends LunoraError {
15
15
  readonly path: ValidationPath;
16
16
  readonly expected: string;
@@ -22,35 +22,43 @@ declare class ValidationError extends LunoraError {
22
22
  });
23
23
  }
24
24
  /**
25
- * Render a short, diagnostic description of a runtime value for the `received`
26
- * field of a {@link ValidationError}. Primitives carry their concrete (length-
27
- * capped) literal so messages distinguish `string "7"` from `number 7`;
28
- * non-plain objects carry their constructor name (e.g. `Date`) so a class
29
- * instance is not flattened to a bare `"object"`.
30
- */
31
- declare const describeValue: (value: unknown) => string;
25
+ * Render a short, diagnostic description of a runtime value for the `received`
26
+ * field of a {@link ValidationError}. Primitives carry their concrete (length-
27
+ * capped) literal so messages distinguish `string "7"` from `number 7`;
28
+ * non-plain objects carry their constructor name (e.g. `Date`) so a class
29
+ * instance is not flattened to a bare `"object"`.
30
+ *
31
+ * Pass `{ literal: false }` to suppress the concrete primitive literal and
32
+ * return only the type tag (`"string"`, `"number"`, `"bigint"`, …). This is used
33
+ * on `.check()` refinement failures — where the value already passed its type
34
+ * check — so a secret-bearing field (password, token) never surfaces its value
35
+ * in the `ValidationError.message`/`received` that goes to the wire and logs.
36
+ */
37
+ declare const describeValue: (value: unknown, options?: {
38
+ literal?: boolean;
39
+ }) => string;
32
40
  declare const formatPath: (path: ValidationPath) => string;
33
- /** Branded id type, e.g. `Id&lt;"users">`. */
41
+ /** Branded id type, e.g. `Id<"users">`. */
34
42
  type Id<TableName extends string> = string & {
35
43
  readonly __table: TableName;
36
44
  };
37
45
  /**
38
- * A JSON Schema fragment (Draft 2020-12 / OpenAPI 3.1 compatible). Intentionally
39
- * a loose bag — a `.check()`/`.meta()` caller contributes keywords like
40
- * `minLength`/`pattern`/`minimum` that `toJsonSchema` shallow-merges onto the
41
- * node for the enclosing validator. Mirrors the `JsonSchema` shape exported by
42
- * `./to-json-schema`; kept structurally identical and local so `v.ts` never
43
- * imports the converter and the two files stay decoupled.
44
- */
46
+ * A JSON Schema fragment (Draft 2020-12 / OpenAPI 3.1 compatible). Intentionally
47
+ * a loose bag — a `.check()`/`.meta()` caller contributes keywords like
48
+ * `minLength`/`pattern`/`minimum` that `toJsonSchema` shallow-merges onto the
49
+ * node for the enclosing validator. Mirrors the `JsonSchema` shape exported by
50
+ * `./to-json-schema`; kept structurally identical and local so `v.ts` never
51
+ * imports the converter and the two files stay decoupled.
52
+ */
45
53
  interface JsonSchemaFragment {
46
54
  [keyword: string]: unknown;
47
55
  }
48
56
  /**
49
- * Options for a {@link Validator.check} refinement. Lets a predicate carry both
50
- * a human-facing `message` and an introspectable JSON Schema `schema` fragment
51
- * (e.g. `{ minLength: 1 }`) so the constraint flows into `toJsonSchema`. The
52
- * legacy `.check(pred, "message")` string form remains supported.
53
- */
57
+ * Options for a {@link Validator.check} refinement. Lets a predicate carry both
58
+ * a human-facing `message` and an introspectable JSON Schema `schema` fragment
59
+ * (e.g. `{ minLength: 1 }`) so the constraint flows into `toJsonSchema`. The
60
+ * legacy `.check(pred, "message")` string form remains supported.
61
+ */
54
62
  interface CheckOptions {
55
63
  /** Failure message thrown on the `ValidationError` (default `"value matching refinement"`). */
56
64
  message?: string;
@@ -58,10 +66,10 @@ interface CheckOptions {
58
66
  schema?: JsonSchemaFragment;
59
67
  }
60
68
  /**
61
- * Options for {@link Validator.meta} — pure metadata with no runtime parsing
62
- * effect, used to enrich the emitted JSON Schema node (description + constraint
63
- * keywords) without attaching a predicate.
64
- */
69
+ * Options for {@link Validator.meta} — pure metadata with no runtime parsing
70
+ * effect, used to enrich the emitted JSON Schema node (description + constraint
71
+ * keywords) without attaching a predicate.
72
+ */
65
73
  interface MetaOptions {
66
74
  /** A human description merged onto this validator's JSON Schema node. */
67
75
  description?: string;
@@ -69,39 +77,47 @@ interface MetaOptions {
69
77
  schema?: JsonSchemaFragment;
70
78
  }
71
79
  /**
72
- * Runtime "kind" tag attached to every validator. Codegen and reflective tools
73
- * use this to inspect the shape without crawling the closure.
74
- */
75
- type ValidatorKind = "any" | "array" | "bigint" | "boolean" | "bytes" | "date" | "from" | "id" | "literal" | "null" | "number" | "object" | "optional" | "record" | "storage" | "string" | "timestamp" | "union";
80
+ * Runtime "kind" tag attached to every validator. Codegen and reflective tools
81
+ * use this to inspect the shape without crawling the closure.
82
+ */
83
+ type ValidatorKind = "any" | "array" | "bigint" | "boolean" | "bytes" | "date" | "from" | "geoPoint" | "id" | "literal" | "null" | "number" | "object" | "optional" | "record" | "storage" | "string" | "timestamp" | "union";
76
84
  interface Validator<T = unknown> extends StandardSchemaV1<T, T> {
77
85
  readonly __type: T;
78
86
  /**
79
- * Attach a refinement predicate. The returned validator parses with the
80
- * original rules first; if the result satisfies `predicate` it passes
81
- * through, otherwise it throws a {@link ValidationError} carrying
82
- * `message` (default: `"value matching refinement"`). Multiple `.check()`
83
- * calls chain — every predicate must return true.
84
- *
85
- * The second argument may be a plain message string (legacy form) or a
86
- * {@link CheckOptions} object that additionally carries a JSON Schema
87
- * `schema` fragment (e.g. `{ minLength: 1 }`) reflected by `toJsonSchema`.
88
- *
89
- * Works in any context — argument validators, column validators, or
90
- * standalone — so it can encode invariants like
91
- * `v.number().check(n => n >= 0)` or
92
- * `v.string().check(s => s.length > 0, { message: "non-empty", schema: { minLength: 1 } })`.
93
- */
87
+ * Attach a refinement predicate. The returned validator parses with the
88
+ * original rules first; if the result satisfies `predicate` it passes
89
+ * through, otherwise it throws a {@link ValidationError} carrying
90
+ * `message` (default: `"value matching refinement"`). Multiple `.check()`
91
+ * calls chain — every predicate must return true.
92
+ *
93
+ * The second argument may be a plain message string (legacy form) or a
94
+ * {@link CheckOptions} object that additionally carries a JSON Schema
95
+ * `schema` fragment (e.g. `{ minLength: 1 }`) reflected by `toJsonSchema`.
96
+ *
97
+ * Works in any context — argument validators, column validators, or
98
+ * standalone — so it can encode invariants like
99
+ * `v.number().check(n => n >= 0)`.
100
+ *
101
+ * For the common length/format/range cases, prefer the named shortcuts
102
+ * (`v.string().min(1)`, `.max(n)`, `.length(n)`, `.pattern(re)`, `.email()`,
103
+ * `.url()`; `v.number().min(n)`, `.max(n)`, `.int()`, `.positive()`;
104
+ * `v.array(...).min(n)`, `.max(n)`) — each is sugar over this same
105
+ * `.check(predicate, { schema })` path, so the predicate and the JSON Schema
106
+ * keyword are set together and can never drift apart. `.check()` remains the
107
+ * escape hatch for anything the shortcuts don't cover, e.g.
108
+ * `v.string().check(s => s.length > 0, { message: "non-empty", schema: { minLength: 1 } })`.
109
+ */
94
110
  check: (predicate: (value: T) => boolean, options?: CheckOptions | string) => Validator<T>;
95
111
  readonly kind: ValidatorKind;
96
112
  /**
97
- * Attach pure metadata (description + JSON Schema constraint fragment) with
98
- * no effect on runtime parsing. The fragment is shallow-merged onto this
99
- * validator's emitted JSON Schema node, composing with any `.check()`
100
- * `schema` fragments (later wins on conflicting keys).
101
- */
113
+ * Attach pure metadata (description + JSON Schema constraint fragment) with
114
+ * no effect on runtime parsing. The fragment is shallow-merged onto this
115
+ * validator's emitted JSON Schema node, composing with any `.check()`
116
+ * `schema` fragments (later wins on conflicting keys).
117
+ */
102
118
  meta: (options: MetaOptions) => Validator<T>;
103
- parse: (value: unknown) => T;
104
- safeParse: (value: unknown) => {
119
+ parse: (value: unknown, options?: ParseOptions) => T;
120
+ safeParse: (value: unknown, options?: ParseOptions) => {
105
121
  error: ValidationError;
106
122
  ok: false;
107
123
  } | {
@@ -112,10 +128,10 @@ interface Validator<T = unknown> extends StandardSchemaV1<T, T> {
112
128
  /** Extract the TS type a validator describes (the **select** type). */
113
129
  type Infer<V> = V extends Validator<infer T> ? T : never;
114
130
  /**
115
- * Column constraints/defaults collected from the `v.*` modifier chain used
116
- * inside `defineTable`. Inert in argument position. Persisted on the
117
- * validator's internal `_meta.column` and mirrored into codegen IR.
118
- */
131
+ * Column constraints/defaults collected from the `v.*` modifier chain used
132
+ * inside `defineTable`. Inert in argument position. Persisted on the
133
+ * validator's internal `_meta.column` and mirrored into codegen IR.
134
+ */
119
135
  interface ColumnMeta {
120
136
  /** `.$defaultFn(fn)` — default factory; field is optional on insert. */
121
137
  defaultFn?: () => unknown;
@@ -126,24 +142,24 @@ interface ColumnMeta {
126
142
  /** `.$onUpdateFn(fn)` — recomputed on every patch/replace. */
127
143
  onUpdateFn?: () => unknown;
128
144
  /**
129
- * `.serverDefault(fn)` — a SERVER-trusted value factory. Unlike
130
- * `.$defaultFn` (which only fills an absent field), this runs on every
131
- * insert/update and SILENTLY OVERWRITES any client-supplied value with
132
- * `fn({ auth })`, so the column is never client-controllable (e.g.
133
- * `ownerId`/`tenantId` stamped from `auth.userId`). Field is optional on
134
- * insert. The factory runs server-side with the resolved request auth.
135
- */
145
+ * `.serverDefault(fn)` — a SERVER-trusted value factory. Unlike
146
+ * `.$defaultFn` (which only fills an absent field), this runs on every
147
+ * insert/update and SILENTLY OVERWRITES any client-supplied value with
148
+ * `fn({ auth })`, so the column is never client-controllable (e.g.
149
+ * `ownerId`/`tenantId` stamped from `auth.userId`). Field is optional on
150
+ * insert. The factory runs server-side with the resolved request auth.
151
+ */
136
152
  serverDefault?: (context: ServerDefaultContext) => unknown;
137
153
  /** `.unique()` — synthesizes a UNIQUE index. */
138
154
  unique?: boolean;
139
155
  }
140
156
  /**
141
- * Context handed to a `.serverDefault(fn)` factory at write time. Carries the
142
- * resolved request identity so a column can be stamped from the caller
143
- * (`auth.userId`) rather than trusted from the client. Structurally mirrors the
144
- * `auth` slice of the server's procedure context without depending on
145
- * `@lunora/server`.
146
- */
157
+ * Context handed to a `.serverDefault(fn)` factory at write time. Carries the
158
+ * resolved request identity so a column can be stamped from the caller
159
+ * (`auth.userId`) rather than trusted from the client. Structurally mirrors the
160
+ * `auth` slice of the server's procedure context without depending on
161
+ * `@lunora/server`.
162
+ */
147
163
  interface ServerDefaultContext {
148
164
  readonly auth: {
149
165
  /** The raw identity claims, or `null` for the anonymous/no-resolver case. */
@@ -153,9 +169,9 @@ interface ServerDefaultContext {
153
169
  };
154
170
  }
155
171
  /**
156
- * Phantom carrier of a column's select/insert types. Never present at runtime;
157
- * `defineTable` reads it to derive `$inferSelect` / `$inferInsert`.
158
- */
172
+ * Phantom carrier of a column's select/insert types. Never present at runtime;
173
+ * `defineTable` reads it to derive `$inferSelect` / `$inferInsert`.
174
+ */
159
175
  interface Column<TSelect, TInsert> {
160
176
  /** Phantom carrier — type-only, never present at runtime. */
161
177
  readonly __column: {
@@ -164,17 +180,24 @@ interface Column<TSelect, TInsert> {
164
180
  };
165
181
  }
166
182
  /**
167
- * A {@link Validator} carrying the chainable column-modifier API. The factories
168
- * (`v.string()`, …) return this so modifiers are available inside `defineTable`.
169
- * `TSelect` is the read type; `TInsert` is the write type (modifiers may make it
170
- * `| undefined`, marking the field optional on insert).
171
- */
183
+ * A {@link Validator} carrying the chainable column-modifier API. The factories
184
+ * (`v.string()`, …) return this so modifiers are available inside `defineTable`.
185
+ * `TSelect` is the read type; `TInsert` is the write type (modifiers may make it
186
+ * `| undefined`, marking the field optional on insert).
187
+ */
172
188
  interface ColumnValidator<TSelect, TInsert> extends Column<TSelect, TInsert>, Validator<TSelect> {
173
189
  /** Default factory applied in the write layer; field becomes optional on insert. */
174
190
  $defaultFn: (function_: () => TSelect) => ColumnValidator<TSelect, TInsert | undefined>;
175
191
  /** Recompute the field on every patch/replace when not explicitly provided. */
176
192
  $onUpdateFn: (function_: () => TSelect) => ColumnValidator<TSelect, TInsert>;
177
- /** Override the inferred select/insert type without changing runtime parsing (e.g. `v.string().$type&lt;Id&lt;"users">>()`). */
193
+ /**
194
+ * Override the type `Infer<>` gives this validator without changing runtime
195
+ * parsing (e.g. `v.string().$type<Brand>()`). An unchecked assertion: the
196
+ * parser still accepts any value of the base kind, so codegen's `Doc_*` /
197
+ * `Insert_*` / procedure-arg types keep the base type (`string`) — the one
198
+ * the stored value is actually guaranteed to have. For a document id use
199
+ * `v.id("users")`, which is checked.
200
+ */
178
201
  $type: <TOverride>() => ColumnValidator<TOverride, TOverride>;
179
202
  /** Refinement predicate run after parsing — see {@link Validator.check}. Chainable; preserves column modifiers. */
180
203
  check: (predicate: (value: TSelect) => boolean, options?: CheckOptions | string) => ColumnValidator<TSelect, TInsert>;
@@ -185,38 +208,118 @@ interface ColumnValidator<TSelect, TInsert> extends Column<TSelect, TInsert>, Va
185
208
  /** Allow SQL NULL — widens the select type to `T | null`. */
186
209
  nullable: () => ColumnValidator<null | TSelect, null | TInsert>;
187
210
  /**
188
- * Stamp this column SERVER-side from the request auth on every write,
189
- * overwriting any client-supplied value. The field becomes optional on
190
- * insert (the server fills it). Use for owner/tenant columns that must never
191
- * be client-controllable — e.g. `v.string().serverDefault(({ auth }) => auth.userId)`.
192
- */
211
+ * Stamp this column SERVER-side from the request auth on every write,
212
+ * overwriting any client-supplied value. The field becomes optional on
213
+ * insert (the server fills it). Use for owner/tenant columns that must never
214
+ * be client-controllable — e.g. `v.string().serverDefault(({ auth }) => auth.userId)`.
215
+ */
193
216
  serverDefault: (function_: (context: ServerDefaultContext) => TSelect) => ColumnValidator<TSelect, TInsert | undefined>;
194
217
  /** Enforce a UNIQUE constraint (synthesizes a unique index). */
195
218
  unique: () => ColumnValidator<TSelect, TInsert>;
196
219
  }
197
220
  /**
198
- * A time-valued {@link ColumnValidator} (epoch milliseconds). Adds
199
- * {@link TimestampColumnValidator.defaultNow} so the field can default to the
200
- * insert-time clock.
201
- */
221
+ * A time-valued {@link ColumnValidator} (epoch milliseconds). Adds
222
+ * {@link TimestampColumnValidator.defaultNow} so the field can default to the
223
+ * insert-time clock.
224
+ */
202
225
  interface TimestampColumnValidator extends ColumnValidator<number, number> {
203
226
  /** Default to the current epoch-ms (`Date.now()`) at insert time; field becomes optional on insert. */
204
227
  defaultNow: () => ColumnValidator<number, number | undefined>;
205
228
  }
229
+ /**
230
+ * A {@link ColumnValidator} for `v.string()` with ergonomic refinement
231
+ * shortcuts. Each method is sugar over `.check(predicate, { schema })` — it
232
+ * sets the runtime predicate AND the matching JSON Schema keyword in one call
233
+ * so the two can never drift (see the module-level `.check()` docstring for
234
+ * the underlying two-part mechanism). Chainable among each other; `.check()`/
235
+ * `.meta()` compose after them but the return narrows to the base
236
+ * {@link ColumnValidator} (no further refinement shortcuts after that point).
237
+ */
238
+ interface StringColumnValidator extends ColumnValidator<string, string> {
239
+ /** Require a valid email address (`format: "email"`). Uses a pragmatic (non-RFC-5322-exhaustive) pattern. */
240
+ email: () => StringColumnValidator;
241
+ /** Require exactly `length` characters (`minLength`/`maxLength` both set to `length`). */
242
+ length: (length: number) => StringColumnValidator;
243
+ /** Require at most `max` characters (`maxLength`). */
244
+ max: (max: number) => StringColumnValidator;
245
+ /** Require at least `min` characters (`minLength`). */
246
+ min: (min: number) => StringColumnValidator;
247
+ /**
248
+ * Require the value to match `pattern` (JSON Schema `pattern` set from
249
+ * `pattern.source`; emitted `pattern` does not encode `pattern.flags` — a
250
+ * case-insensitive `/i` regex, for instance, emits a flag-less JSON Schema
251
+ * pattern). A `g`/`y`-flagged `pattern` is tested statelessly (its
252
+ * `lastIndex` is never consulted or advanced), so a single validator
253
+ * instance gives the same answer for the same input on every call.
254
+ */
255
+ pattern: (pattern: RegExp) => StringColumnValidator;
256
+ /**
257
+ * Require a valid `http:`/`https:` URL (`format: "uri"`). Parseable by the
258
+ * WHATWG `URL` constructor is necessary but not sufficient — schemes such as
259
+ * `javascript:`, `data:`, `file:`, and `vbscript:` all parse successfully but
260
+ * are rejected here, since accepting them lets a validated "link" field carry
261
+ * an XSS payload straight into an anchor's `href` or `window.location` at
262
+ * render time.
263
+ */
264
+ url: () => StringColumnValidator;
265
+ }
266
+ /**
267
+ * A {@link ColumnValidator} for `v.number()` with ergonomic refinement
268
+ * shortcuts — see {@link StringColumnValidator} for the delegation pattern.
269
+ */
270
+ interface NumberColumnValidator extends ColumnValidator<number, number> {
271
+ /** Require an integer value (`Number.isInteger`); JSON Schema `type` narrows to `"integer"`. */
272
+ int: () => NumberColumnValidator;
273
+ /** Require at most `max` (`maximum`). */
274
+ max: (max: number) => NumberColumnValidator;
275
+ /** Require at least `min` (`minimum`). */
276
+ min: (min: number) => NumberColumnValidator;
277
+ /** Require a value strictly greater than zero (`exclusiveMinimum: 0`). */
278
+ positive: () => NumberColumnValidator;
279
+ }
280
+ /**
281
+ * A {@link ColumnValidator} for `v.object(...)`, carrying the `.strip()` opt-out.
282
+ */
283
+ interface ObjectColumnValidator<T> extends ColumnValidator<T, T> {
284
+ /**
285
+ * Drop keys this shape does not declare, even under `.output()`.
286
+ *
287
+ * Only meaningful there — input parsing strips already. Say it when the
288
+ * narrowing is deliberate (trimming an internal field off a row before it
289
+ * reaches a client); without it, an undeclared key on the way OUT is an
290
+ * error, because the alternative is deleting server data silently.
291
+ */
292
+ strip: () => ObjectColumnValidator<T>;
293
+ }
294
+ /**
295
+ * A {@link ColumnValidator} for `v.array(...)` with ergonomic length-refinement
296
+ * shortcuts — see {@link StringColumnValidator} for the delegation pattern.
297
+ */
298
+ interface ArrayColumnValidator<TItem> extends ColumnValidator<TItem[], TItem[]> {
299
+ /** Require at most `max` items (`maxItems`). */
300
+ max: (max: number) => ArrayColumnValidator<TItem>;
301
+ /** Require at least `min` items (`minItems`). */
302
+ min: (min: number) => ArrayColumnValidator<TItem>;
303
+ }
206
304
  /** The type a validator/column presents on **select** (reads). */
207
305
  type InferSelect<V> = V extends Validator<infer T> ? T : never;
208
306
  /** The type a validator/column accepts on **insert** (writes). */
209
307
  type InferInsert<V> = V extends Column<unknown, infer I> ? I : V extends Validator<infer T> ? T : never;
210
308
  /** Derive the read shape of a table's column map. */
211
- type SelectShape<S extends Record<string, Validator>> = { [K in keyof S]: InferSelect<S[K]> };
212
- /**
213
- * Derive the write shape of a table's column map. Columns whose insert type
214
- * includes `undefined` (via `.default()` / `.$defaultFn()` / `v.optional`)
215
- * become optional keys.
216
- */
217
- type InsertShape<S extends Record<string, Validator>> = { [K in keyof S as undefined extends InferInsert<S[K]> ? K : never]?: Exclude<InferInsert<S[K]>, undefined> } & { [K in keyof S as undefined extends InferInsert<S[K]> ? never : K]: InferInsert<S[K]> };
218
- declare const string: () => ColumnValidator<string, string>;
219
- declare const number: () => ColumnValidator<number, number>;
309
+ type SelectShape<S extends Record<string, Validator>> = { [K in keyof S]: InferSelect<S[K]>; };
310
+ /**
311
+ * Derive the write shape of a table's column map. Columns whose insert type
312
+ * includes `undefined` (via `.default()` / `.$defaultFn()` / `v.optional`)
313
+ * become optional keys.
314
+ */
315
+ type InsertShape<S extends Record<string, Validator>> = { [K in keyof S as undefined extends InferInsert<S[K]> ? K : never]?: Exclude<InferInsert<S[K]>, undefined>; } & { [K in keyof S as undefined extends InferInsert<S[K]> ? never : K]: InferInsert<S[K]>; };
316
+ /** Options for {@link Validator.parse} / {@link Validator.safeParse}. */
317
+ interface ParseOptions {
318
+ /** See {@link ParseContext.rejectUnknownKeys}. */
319
+ rejectUnknownKeys?: boolean;
320
+ }
321
+ declare const string: () => StringColumnValidator;
322
+ declare const number: () => NumberColumnValidator;
220
323
  /** Epoch-millisecond timestamp (`number`). Pair with `.defaultNow()` for an insert-time clock. */
221
324
  declare const timestamp: () => TimestampColumnValidator;
222
325
  /** Calendar date stored as an epoch-millisecond `number`. Pair with `.defaultNow()` for an insert-time clock. */
@@ -227,68 +330,174 @@ declare const nullValidator: () => ColumnValidator<null, null>;
227
330
  declare const bytes: () => ColumnValidator<ArrayBuffer, ArrayBuffer>;
228
331
  declare const id: <TableName extends string>(tableName: TableName) => ColumnValidator<Id<TableName>, Id<TableName>>;
229
332
  /**
230
- * A reference to a stored R2 object: the column holds the object's **key** (a
231
- * string), the same key `@lunora/storage` puts/gets by. Functionally it parses
232
- * like `v.string()`, but the distinct `"storage"` kind lets codegen and the
233
- * studio join the data model to R2 — the file browser uses it to show which
234
- * record owns a file and to flag orphaned objects no row references. The
235
- * optional `bucket` names the typed bucket the key lives in (for app-context
236
- * signed URLs); omit it for the app's default bucket.
237
- */
333
+ * A reference to a stored R2 object: the column holds the object's **key** (a
334
+ * string), the same key `@lunora/storage` puts/gets by. Functionally it parses
335
+ * like `v.string()`, but the distinct `"storage"` kind lets codegen and the
336
+ * studio join the data model to R2 — the file browser uses it to show which
337
+ * record owns a file and to flag orphaned objects no row references. The
338
+ * optional `bucket` names the typed bucket the key lives in (for app-context
339
+ * signed URLs); omit it for the app's default bucket.
340
+ */
238
341
  declare const storage: (bucket?: string) => ColumnValidator<string, string>;
342
+ /**
343
+ * A geographic point — latitude/longitude in decimal degrees (WGS84). The value
344
+ * a `v.geoPoint()` column reads/writes. Stored as a JSON object alongside the
345
+ * row; a `.geoIndex(name, { field })` on the table maintains a geohash companion
346
+ * so `withGeoIndex(name, q => q.near(point, radius) | q.within(bbox))` can answer
347
+ * proximity/bounding-box reads.
348
+ */
349
+ interface GeoPoint {
350
+ /** Latitude in decimal degrees, `-90 … 90`. */
351
+ lat: number;
352
+ /** Longitude in decimal degrees, `-180 … 180`. */
353
+ lng: number;
354
+ }
355
+ /**
356
+ * A latitude/longitude point (WGS84 decimal degrees). Parses an object with
357
+ * finite `lat` ∈ `[-90, 90]` and `lng` ∈ `[-180, 180]`; any other shape or an
358
+ * out-of-range coordinate throws a {@link ValidationError}. Pair with a table's
359
+ * `.geoIndex(name, { field })` to enable `near` / `within` reads.
360
+ */
361
+ declare const geoPoint: () => ColumnValidator<GeoPoint, GeoPoint>;
239
362
  declare const literal: <T extends bigint | boolean | number | string | null>(literalValue: T) => ColumnValidator<T, T>;
240
- declare const array: <V extends Validator>(inner: V) => ColumnValidator<Infer<V>[], Infer<V>[]>;
363
+ declare const array: <V extends Validator>(inner: V) => ArrayColumnValidator<Infer<V>>;
364
+ /**
365
+ * Split a value-type map into optional + required keys: any member whose value
366
+ * type includes `undefined` becomes an optional key. The single optionality rule
367
+ * shared by object-shape inference ({@link ObjectShapeType}) and args-map
368
+ * inference (`InferValidatorMap` in `./validator-map`), so the two can never
369
+ * drift. (`InsertShape` stays separate — it additionally `Exclude`s `undefined`
370
+ * from the optional value, a deliberate insert-type difference.)
371
+ *
372
+ * `undefined extends M[K]` is the type-level spelling of "the parser accepts
373
+ * this field absent" — the same question `acceptsAbsent` in
374
+ * `./json-schema-core` answers structurally. It is broader than
375
+ * `v.optional(...)` on purpose: `v.any()` returns its input unchanged, so it
376
+ * parses an absent field happily, and a `v.union(...)` with an `any`/optional
377
+ * member does too. Anything narrower would mark a key required that the runtime
378
+ * does not require — which is how a generated `_generated/api.ts` came to reject
379
+ * args the server accepts (issue #688).
380
+ */
381
+ type OptionalizeShape<M> = { [K in keyof M as undefined extends M[K] ? K : never]?: M[K]; } & { [K in keyof M as undefined extends M[K] ? never : K]: M[K]; };
241
382
  type ObjectShape = Record<string, Validator>;
242
- type ObjectShapeType<S extends ObjectShape> = { [K in keyof S as undefined extends Infer<S[K]> ? K : never]?: Infer<S[K]> } & { [K in keyof S as undefined extends Infer<S[K]> ? never : K]: Infer<S[K]> };
243
- declare const objectValidator: <S extends ObjectShape>(shape: S) => ColumnValidator<ObjectShapeType<S>, ObjectShapeType<S>>;
383
+ type ObjectShapeType<S extends ObjectShape> = OptionalizeShape<{ [K in keyof S]: Infer<S[K]>; }>;
384
+ declare const objectValidator: <S extends ObjectShape>(shape: S, stripUnknown?: boolean) => ObjectColumnValidator<ObjectShapeType<S>>;
244
385
  declare const record: <K extends Validator<string>, V extends Validator>(keyValidator: K, valueValidator: V) => ColumnValidator<Record<Infer<K>, Infer<V>>, Record<Infer<K>, Infer<V>>>;
245
386
  declare const union: <Vs extends ReadonlyArray<Validator>>(...members: Vs) => ColumnValidator<Infer<Vs[number]>, Infer<Vs[number]>>;
246
387
  declare const optional: <V extends Validator>(inner: V) => ColumnValidator<Infer<V> | undefined, Infer<V> | undefined>;
388
+ /** Every member of a shape made optional — the result of {@link partial}. */
389
+ type PartialShape<S extends ObjectShape> = { [K in keyof S]: ColumnValidator<Infer<S[K]> | undefined, Infer<S[K]> | undefined>; };
390
+ /**
391
+ * Wrap every member of a shape in {@link optional}.
392
+ *
393
+ * A patch-style procedure takes "any subset of these columns" — the same shape
394
+ * the table declares, with nothing required. Spelling that out by hand means a
395
+ * second copy of the shape that has to be kept in step with the first, and the
396
+ * failure when it drifts is quiet: a column added to the table is simply not
397
+ * accepted by the patch, and nothing typechecks it against the table.
398
+ *
399
+ * Takes and returns a *shape record* rather than a `v.object(...)`, so the one
400
+ * function serves both positions — a procedure's args map (`.input(...)`) and a
401
+ * nested object (`v.object(v.partial(shape))`).
402
+ *
403
+ * A member that is already `v.optional(...)` is passed through rather than
404
+ * double-wrapped, so this is idempotent.
405
+ *
406
+ * **Pass the fields a caller may patch, not a whole table's shape.** Handing this
407
+ * `schema.tables.x.shape` makes every present and *future* column client-writable:
408
+ * add `ownerId`, `role`, or `isVerified` to the table later and it silently joins
409
+ * the patch, with no diff on the procedure to review. Row-level policies do not
410
+ * close that — they decide which ROW you may write, not which fields — so the
411
+ * allow-list has to be here.
412
+ * @example
413
+ * ```ts
414
+ * const editable = { body: v.string(), title: v.string() };
415
+ *
416
+ * mutation.input({ id: v.id("threads"), ...v.partial(editable) }).mutation(…)
417
+ * ```
418
+ */
419
+ declare const partial: <S extends ObjectShape>(shape: S) => PartialShape<S>;
247
420
  declare const any: () => ColumnValidator<unknown, unknown>;
248
421
  /**
249
- * Infer the output type of a Standard Schema v1 object. When the schema omits
250
- * `~standard.types` (it is optional in the spec), falls back to `unknown` so
251
- * callers always get a usable type rather than `never`.
252
- */
253
- type InferStandardOutput<S extends StandardSchemaV1> = S["~standard"]["types"] extends {
254
- output: infer O;
255
- } ? O : unknown;
256
- /**
257
- * Wrap any Standard Schema v1 validator (`zod`, `valibot`, `arktype`, …) so it
258
- * can be used as an **args** validator in `query`/`mutation`/`action`. The
259
- * wrapped validator's output type is inferred from `~standard.types.output`
260
- * when declared; falls back to `unknown` when the schema omits the types field.
261
- *
262
- * **Args-only.** `v.from(...)` validators must not be used as table columns —
263
- * `defineTable` checks the `kind` and throws a clear error if you try.
264
- *
265
- * **Sync-only.** Standard Schema allows async `validate`; Lunora args
266
- * validation is synchronous and throws when a Promise is returned.
267
- */
268
- declare const from: <S extends StandardSchemaV1>(schema: S) => ColumnValidator<InferStandardOutput<S>, InferStandardOutput<S>>;
269
- /**
270
- * True when `validator` is `v.from(...)` or structurally wraps one through
271
- * `v.optional` / `v.array` / `v.object` / `v.record` / `v.union`. `defineTable`
272
- * uses it to reject Standard-Schema-backed validators anywhere in a column —
273
- * not just at the top level — since they are args-only and have no SQL column
274
- * type. The nested children live on the validator's `_meta` (`inner`, `shape`,
275
- * `members`, `keyValidator`/`valueValidator`) and are themselves validators.
276
- */
422
+ * The type a Standard Schema v1 object validates TO — what a read gets back.
423
+ *
424
+ * Defer to the spec's own helper rather than matching `~standard.types`: the spec
425
+ * declares that property optional (it is a phantom that never exists at runtime),
426
+ * so every real library types it as a union with `undefined`, and a hand-written
427
+ * `extends { output: infer O }` misses all of them. A schema that declares no
428
+ * `types` still resolves through the constraint to `unknown`.
429
+ */
430
+ type InferStandardOutput<S extends StandardSchemaV1> = StandardSchemaV1.InferOutput<S>;
431
+ /**
432
+ * The type a Standard Schema v1 object validates FROM — what a write supplies.
433
+ *
434
+ * Distinct from {@link InferStandardOutput} only for a transforming schema
435
+ * (`z.string().transform(…)`, `z.coerce.number()`), where the value handed in is
436
+ * not the value stored. Identical for everything else.
437
+ */
438
+ type InferStandardInput<S extends StandardSchemaV1> = StandardSchemaV1.InferInput<S>;
439
+ /**
440
+ * Wrap any Standard Schema v1 validator (`zod`, `valibot`, `arktype`, …) so it
441
+ * can be used as an args validator in `query`/`mutation`/`action`, or as a table
442
+ * column. The wrapped validator's output type is inferred from
443
+ * `~standard.types.output` when declared; falls back to `unknown` when the
444
+ * schema omits the types field.
445
+ *
446
+ * **Columns are stored by the value's runtime type.** A shard row is a JSON
447
+ * document, so a `v.from()` column needs no SQL type of its own. For a
448
+ * `.global()` table it maps to a TEXT column holding whatever the encoder
449
+ * produces: a scalar is written verbatim (a `v.from(z.string())` column holds a
450
+ * bare `hello`, not `"hello"`), and an object or array is JSON-encoded. That is
451
+ * the same rule `v.union` and `v.any` follow, and it is why the column cannot be
452
+ * a Postgres/MySQL `JSON` column — a bare `hello` is not valid JSON.
453
+ *
454
+ * The consequence worth knowing: a stored *string* that itself looks like JSON
455
+ * (`'{"a":1}'`) is ambiguous on read and decodes to the parsed object. Declare
456
+ * the column with a concrete `v.*` type when the plain column type matters — for
457
+ * a comparison index, or to avoid that ambiguity.
458
+ *
459
+ * **Not seedable.** `@lunora/seed` cannot introspect an external schema to
460
+ * invent a valid value, so it refuses a `v.from()` column with an actionable
461
+ * error rather than generating one that fails validation on insert.
462
+ *
463
+ * **Sync-only.** Standard Schema allows async `validate`; Lunora validation is
464
+ * synchronous and throws when a Promise is returned.
465
+ *
466
+ * **Reads and writes can differ.** A write supplies the schema's INPUT and a read
467
+ * gets its OUTPUT back, because what is stored is `validate()`'s result. The two
468
+ * coincide for every non-transforming schema; they part for `z.coerce.number()`
469
+ * and friends, where typing the insert side as the output would demand the
470
+ * post-transform value from a caller whose value the validator is there to
471
+ * transform.
472
+ */
473
+ declare const from: <S extends StandardSchemaV1>(schema: S) => ColumnValidator<InferStandardOutput<S>, InferStandardInput<S>>;
474
+ /**
475
+ * True when `validator` is `v.from(...)` or structurally wraps one through
476
+ * `v.optional` / `v.array` / `v.object` / `v.record` / `v.union`. The nested
477
+ * children live on the validator's `_meta` (`inner`, `shape`, `members`,
478
+ * `keyValidator`/`valueValidator`) and are themselves validators.
479
+ *
480
+ * For tooling that must know whether a value is validated by an external
481
+ * Standard Schema rather than a concrete `v.*` type — a seeder that cannot
482
+ * invent a conforming value, a JSON Schema exporter that has nothing to
483
+ * describe. `defineTable` no longer calls it: a `v.from()` column is allowed
484
+ * and stores JSON, see {@link from}.
485
+ */
277
486
  declare const isOrWrapsFromValidator: (validator: Validator) => boolean;
278
487
  /**
279
- * The inner validator wrapped by `v.optional(inner)`, or `undefined` for any
280
- * other validator. The nested child lives on the validator's internal `_meta`
281
- * bag; this accessor keeps that knowledge inside `@lunora/values` (the package
282
- * that owns validator internals) so consumers don't reach into `_meta`
283
- * themselves. Used by `@lunora/server`'s `defineEnv` to coerce through a leading
284
- * `v.optional(...)`.
285
- * @returns The inner validator if `v.optional(...)`, otherwise `undefined`.
286
- */
488
+ * The inner validator wrapped by `v.optional(inner)`, or `undefined` for any
489
+ * other validator. The nested child lives on the validator's internal `_meta`
490
+ * bag; this accessor keeps that knowledge inside `@lunora/values` (the package
491
+ * that owns validator internals) so consumers don't reach into `_meta`
492
+ * themselves. Used by `@lunora/server`'s `defineEnv` to coerce through a leading
493
+ * `v.optional(...)`.
494
+ * @returns The inner validator if `v.optional(...)`, otherwise `undefined`.
495
+ */
287
496
  declare const optionalInner: (validator: Validator) => Validator | undefined;
288
497
  /**
289
- * Validator/codec namespace. Each factory returns a {@link Validator} with a
290
- * runtime `parse`/`safeParse` plus a phantom `__type` field for inference.
291
- */
498
+ * Validator/codec namespace. Each factory returns a {@link Validator} with a
499
+ * runtime `parse`/`safeParse` plus a phantom `__type` field for inference.
500
+ */
292
501
  declare const v: {
293
502
  any: typeof any;
294
503
  array: typeof array;
@@ -297,12 +506,14 @@ declare const v: {
297
506
  bytes: typeof bytes;
298
507
  date: typeof date;
299
508
  from: typeof from;
509
+ geoPoint: typeof geoPoint;
300
510
  id: typeof id;
301
511
  literal: typeof literal;
302
512
  null: typeof nullValidator;
303
513
  number: typeof number;
304
514
  object: typeof objectValidator;
305
515
  optional: typeof optional;
516
+ partial: typeof partial;
306
517
  record: typeof record;
307
518
  storage: typeof storage;
308
519
  string: typeof string;
@@ -310,47 +521,53 @@ declare const v: {
310
521
  union: typeof union;
311
522
  };
312
523
  /**
313
- * A JSON Schema node (Draft 2020-12 / OpenAPI 3.1 compatible). Intentionally a
314
- * loose bag — Lunora only emits a known subset, but consumers (OpenAPI/OpenRPC
315
- * builders, Swagger UI, form generators) treat it as an opaque schema object.
316
- */
524
+ * A JSON Schema node (Draft 2020-12 / OpenAPI 3.1 compatible). Intentionally a
525
+ * loose bag — Lunora only emits a known subset, but consumers (OpenAPI/OpenRPC
526
+ * builders, Swagger UI, form generators) treat it as an opaque schema object.
527
+ */
317
528
  interface JsonSchema {
318
529
  [keyword: string]: unknown;
319
530
  }
320
531
  /**
321
- * Structural reader over a validator-like node. The shared mapping algorithm
322
- * ({@link jsonSchemaFromNode}) is parameterized by this interface so the same
323
- * switch/recursion serves both inputs Lunora maps to JSON Schema: the runtime
324
- * `@lunora/values` validator (children + metadata live on `_meta`), and the
325
- * build-time validator IR consumed by codegen (children are plain fields, with
326
- * no runtime metadata — so `constraints`/`isNullable` may be inert there).
327
- *
328
- * A reader normalizes a `TNode` to the small set of children/leaves the mapper
329
- * recurses over. Composite accessors (`inner`/`shape`/`members`/`valueChild`)
330
- * return the same `TNode` type so the mapper can recurse uniformly; leaf concerns
331
- * that differ between the two sources — how a literal's `const` is computed,
332
- * whether a `.check()`/`.meta()` constraint fragment exists, whether `.nullable()`
333
- * was applied — are delegated wholesale to the reader.
334
- */
532
+ * Structural reader over a validator-like node. The shared mapping algorithm
533
+ * ({@link jsonSchemaFromNode}) is parameterized by this interface so the same
534
+ * switch/recursion serves both inputs Lunora maps to JSON Schema: the runtime
535
+ * `@lunora/values` validator (children + metadata live on `_meta`), and the
536
+ * build-time validator IR consumed by codegen (children are plain fields, with
537
+ * no runtime metadata — so `constraints`/`isNullable` may be inert there).
538
+ *
539
+ * A reader normalizes a `TNode` to the small set of children/leaves the mapper
540
+ * recurses over. Composite accessors (`inner`/`shape`/`members`/`keyChild`/`valueChild`)
541
+ * return the same `TNode` type so the mapper can recurse uniformly; leaf concerns
542
+ * that differ between the two sources — how a literal's `const` is computed,
543
+ * whether a `.check()`/`.meta()` constraint fragment exists, whether `.nullable()`
544
+ * was applied — are delegated wholesale to the reader.
545
+ */
335
546
  interface SchemaNodeReader<TNode> {
336
547
  /**
337
- * The `.check()`/`.meta()` JSON Schema fragment to shallow-merge onto the
338
- * node, or `undefined` when none (the IR side never carries one). Constraint
339
- * keys win over the base on conflict so a refinement can tighten — never
340
- * silently weaken — the schema.
341
- */
548
+ * The `.check()`/`.meta()` JSON Schema fragment to shallow-merge onto the
549
+ * node, or `undefined` when none (the IR side never carries one). Constraint
550
+ * keys win over the base on conflict so a refinement can tighten — never
551
+ * silently weaken — the schema.
552
+ */
342
553
  constraints: (node: TNode) => JsonSchema | undefined;
343
554
  /** Inner child of an `array`/`optional` node. May be absent on the IR side. */
344
555
  inner: (node: TNode) => TNode | undefined;
345
556
  /** Whether `.nullable()` was applied (runtime: `_meta.column.notNull === false`). */
346
557
  isNullable: (node: TNode) => boolean;
558
+ /**
559
+ * Key-child of a `record` node, mapped to `propertyNames`. May be absent on
560
+ * the IR side (and on a `v.record(v.string(), …)` whose key validator adds
561
+ * no constraint beyond `type: "string"` it is merely redundant, not wrong).
562
+ */
563
+ keyChild: (node: TNode) => TNode | undefined;
347
564
  /** Discriminating validator kind. */
348
565
  kind: (node: TNode) => ValidatorKind;
349
566
  /**
350
- * The JSON Schema `const` fragment for a `literal` node. Computed differently
351
- * per source — runtime reads the live `_meta.value`; the IR parses verbatim
352
- * source text — so the reader owns it entirely.
353
- */
567
+ * The JSON Schema `const` fragment for a `literal` node. Computed differently
568
+ * per source — runtime reads the live `_meta.value`; the IR parses verbatim
569
+ * source text — so the reader owns it entirely.
570
+ */
354
571
  literalSchema: (node: TNode) => JsonSchema;
355
572
  /** Member nodes of a `union`. */
356
573
  members: (node: TNode) => ReadonlyArray<TNode>;
@@ -362,105 +579,137 @@ interface SchemaNodeReader<TNode> {
362
579
  valueChild: (node: TNode) => TNode | undefined;
363
580
  }
364
581
  /**
365
- * The single validator→JSON-Schema mapping algorithm, shared by the runtime
366
- * `toJsonSchema` (over `@lunora/values` validators) and codegen's IR-backed
367
- * mapper. It walks a node recursively via the supplied {@link SchemaNodeReader},
368
- * so nested objects/arrays/unions/records are fully expanded (never collapsed to
369
- * one level).
370
- *
371
- * `date`/`timestamp` are epoch-millisecond numbers in Lunora (not ISO strings),
372
- * so they schema as integers; `bigint` schemas as an int64 (JSON has no bigint
373
- * type, so `format: int64` is the conventional OpenAPI carrier); `bytes` is an
374
- * `ArrayBuffer`, surfaced as base64 per JSON Schema 2020-12 content encoding.
375
- *
376
- * A `.check()`/`.meta()` JSON Schema fragment (when the reader exposes one) is
377
- * shallow-merged onto the node — constraint keys win on conflict. A `.nullable()`
378
- * node widens to also accept `null`; constraints describe the underlying value,
379
- * so they ride inside the non-null branch rather than on the wrapping `anyOf`.
380
- */
582
+ * The single validator→JSON-Schema mapping algorithm, shared by the runtime
583
+ * `toJsonSchema` (over `@lunora/values` validators) and codegen's IR-backed
584
+ * mapper. It walks a node recursively via the supplied {@link SchemaNodeReader},
585
+ * so nested objects/arrays/unions/records are fully expanded (never collapsed to
586
+ * one level).
587
+ *
588
+ * Each node names the JSON CARRIER, not the decoded JS type — the transport
589
+ * decodes before the parser sees a value, so describing the JS type would
590
+ * advertise shapes no JSON body can hold. `date`/`timestamp` are
591
+ * epoch-millisecond numbers in Lunora (not ISO strings), so they schema as
592
+ * integers; `bigint` rides as an int64 decimal string, the same carrier the
593
+ * wire codec and `v.literal(5n)` use; `bytes` is an `ArrayBuffer`, surfaced as
594
+ * base64 per JSON Schema 2020-12 content encoding.
595
+ *
596
+ * A `.check()`/`.meta()` JSON Schema fragment (when the reader exposes one) is
597
+ * shallow-merged onto the node — constraint keys win on conflict. A `.nullable()`
598
+ * node widens to also accept `null`; constraints describe the underlying value,
599
+ * so they ride inside the non-null branch rather than on the wrapping `anyOf`.
600
+ */
381
601
  declare const jsonSchemaFromNode: <TNode>(node: TNode, reader: SchemaNodeReader<TNode>) => JsonSchema;
382
602
  /**
383
- * Build `{ type: "object", properties, required, additionalProperties: false }`
384
- * from a node shape. A `v.optional(...)` property is the only thing that drops
385
- * out of `required`; every other property is required.
386
- */
603
+ * True when the parser accepts this node's field ABSENT — the only honest test
604
+ * for whether a key is optional.
605
+ *
606
+ * `kind === "optional"` used to be the whole test, which called two kinds
607
+ * required that parse `{}` happily: `v.any()` (its parser returns `undefined`
608
+ * unchanged) and a `v.union(...)` with an optional or `any` member (the union
609
+ * tries members, one of which accepts `undefined`). A contract that requires a
610
+ * field the server does not cannot be satisfied by a generated client.
611
+ *
612
+ * Shared beyond JSON Schema's `required`: `@lunora/codegen`'s api emitter runs it
613
+ * over the build-time validator IR to decide `key?: T` vs `key: T`, so the
614
+ * generated `_generated/api.ts` argument types, the emitted OpenAPI/OpenRPC
615
+ * specs, and `Infer`'s `undefined extends …` optionality rule all answer this
616
+ * one question the same way.
617
+ * @returns `true` when an absent value parses.
618
+ */
619
+ declare const acceptsAbsent: <TNode>(node: TNode, reader: SchemaNodeReader<TNode>) => boolean;
620
+ /**
621
+ * Build `{ type: "object", properties, required }` from a node shape.
622
+ *
623
+ * Deliberately NOT `additionalProperties: false`: `v.object` STRIPS an
624
+ * undeclared key rather than refusing it (only `.output()`'s
625
+ * `rejectUnknownKeys` mode refuses one, and it is not what this schema
626
+ * describes), so closing the object had a spec-validating client reject bodies
627
+ * the server accepts. Absent means allowed, which is what the parser does.
628
+ *
629
+ * `required` lists every key whose field cannot be absent — see
630
+ * {@link acceptsAbsent}.
631
+ */
387
632
  declare const objectSchemaFromNodes: <TNode>(shape: Record<string, TNode>, reader: SchemaNodeReader<TNode>) => JsonSchema;
388
633
  /**
389
- * Convert a single `@lunora/values` validator to a JSON Schema node (Draft
390
- * 2020-12 / OpenAPI 3.1). A thin wrapper over the shared {@link jsonSchemaFromNode}
391
- * core with the runtime {@link validatorReader}; see that core for the full
392
- * kind→schema mapping (date/timestamp → epoch-ms integer, bigint → int64, bytes →
393
- * base64, id → annotated string, literal → `const`, optionality via the parent
394
- * `required` list, `.nullable()` widening, `.check()`/`.meta()` constraint merge).
395
- */
634
+ * Convert a single `@lunora/values` validator to a JSON Schema node (Draft
635
+ * 2020-12 / OpenAPI 3.1). A thin wrapper over the shared {@link jsonSchemaFromNode}
636
+ * core with the runtime {@link validatorReader}; see that core for the full
637
+ * kind→schema mapping (date/timestamp → epoch-ms integer, bigint → int64, bytes →
638
+ * base64, id → annotated string, literal → `const`, optionality via the parent
639
+ * `required` list, `.nullable()` widening, `.check()`/`.meta()` constraint merge).
640
+ */
396
641
  declare const toJsonSchema: (validator: Validator) => JsonSchema;
397
642
  /**
398
- * Convert a function's argument validators (a name-to-validator map) into a
399
- * single JSON Schema object. Non-`optional` arguments are `required`; the result
400
- * is the request `params`/`args` schema an OpenAPI operation or OpenRPC method
401
- * advertises. An empty arg map yields an empty (but valid) object schema.
402
- */
643
+ * Convert a function's argument validators (a name-to-validator map) into a
644
+ * single JSON Schema object. Non-`optional` arguments are `required`; the result
645
+ * is the request `params`/`args` schema an OpenAPI operation or OpenRPC method
646
+ * advertises. An empty arg map yields an empty (but valid) object schema.
647
+ */
403
648
  declare const argsToJsonSchema: (args: Record<string, Validator>) => JsonSchema;
404
649
  /** Map of validators describing a record of named fields (a function's args, a step's args, an HTTP query/body/params). */
405
650
  type ValidatorMap = Record<string, Validator>;
406
- /** Infer the object type from a {@link ValidatorMap} — optional validators (`v.optional`) become optional keys. */
407
- type InferValidatorMap<A extends ValidatorMap> = { [K in keyof A as undefined extends Infer<A[K]> ? K : never]?: Infer<A[K]> } & { [K in keyof A as undefined extends Infer<A[K]> ? never : K]: Infer<A[K]> };
408
- /**
409
- * A precompiled fast-path parser for one {@link ValidatorMap}. Returns the fully
410
- * built, validated record on a confident success, or the {@link DEFER_VALIDATION}
411
- * sentinel to hand the input back to the interpreted parser.
412
- *
413
- * The contract is soundness, not completeness: a compiled parser may return
414
- * {@link DEFER_VALIDATION} for any input it is not certain about (the interpreted
415
- * path then runs and either succeeds or throws the canonical error), but it must
416
- * NEVER return a built record for input the interpreted parser would reject, and
417
- * the record it returns must be byte-for-byte what the interpreted parser would
418
- * have produced. This lets `@lunora/codegen` emit zero-allocation structural
419
- * checks (the common case) while every error message and every tricky validator
420
- * still flows through the single interpreted implementation below — so error
421
- * contracts can never drift.
422
- *
423
- * The `source` parameter is intentionally `any`: the codegen-emitted body is
424
- * plain JavaScript (no type annotations — it must also be loadable via
425
- * `new Function` in the compiler's differential tests) that index-walks the input
426
- * to arbitrary depth, which strict TypeScript forbids on `unknown`/`object`. An
427
- * `any` input lets the emitted structural checks type-check cleanly while the
428
- * RESULT stays strongly typed; soundness is enforced by the differential test
429
- * harness, not the input type.
430
- */
651
+ /**
652
+ * Infer the object type from a {@link ValidatorMap} — optional validators
653
+ * (`v.optional`) become optional keys. Shares the single optionality rule with
654
+ * `ObjectShapeType` via {@link OptionalizeShape}, so args-map and object-shape
655
+ * inference can never drift.
656
+ */
657
+ type InferValidatorMap<A extends ValidatorMap> = OptionalizeShape<{ [K in keyof A]: Infer<A[K]>; }>;
658
+ /**
659
+ * A precompiled fast-path parser for one {@link ValidatorMap}. Returns the fully
660
+ * built, validated record on a confident success, or the {@link DEFER_VALIDATION}
661
+ * sentinel to hand the input back to the interpreted parser.
662
+ *
663
+ * The contract is soundness, not completeness: a compiled parser may return
664
+ * {@link DEFER_VALIDATION} for any input it is not certain about (the interpreted
665
+ * path then runs and either succeeds or throws the canonical error), but it must
666
+ * NEVER return a built record for input the interpreted parser would reject, and
667
+ * the record it returns must be byte-for-byte what the interpreted parser would
668
+ * have produced. This lets `@lunora/codegen` emit zero-allocation structural
669
+ * checks (the common case) while every error message and every tricky validator
670
+ * still flows through the single interpreted implementation below — so error
671
+ * contracts can never drift.
672
+ *
673
+ * The `source` parameter is intentionally `any`: the codegen-emitted body is
674
+ * plain JavaScript (no type annotations — it must also be loadable via
675
+ * `new Function` in the compiler's differential tests) that index-walks the input
676
+ * to arbitrary depth, which strict TypeScript forbids on `unknown`/`object`. An
677
+ * `any` input lets the emitted structural checks type-check cleanly while the
678
+ * RESULT stays strongly typed; soundness is enforced by the differential test
679
+ * harness, not the input type.
680
+ */
431
681
  type CompiledValidatorMap = (source: any) => Record<string, unknown> | typeof DEFER_VALIDATION;
432
682
  /**
433
- * Sentinel a {@link CompiledValidatorMap} returns to defer to the interpreted
434
- * parser. A unique symbol (never a valid parse result — {@link parseValidatorMap}
435
- * always yields a record) so the seam can distinguish "compiled handled it" from
436
- * "compiled bailed" with a single identity check and no per-call allocation.
437
- */
683
+ * Sentinel a {@link CompiledValidatorMap} returns to defer to the interpreted
684
+ * parser. A unique symbol (never a valid parse result — {@link parseValidatorMap}
685
+ * always yields a record) so the seam can distinguish "compiled handled it" from
686
+ * "compiled bailed" with a single identity check and no per-call allocation.
687
+ */
438
688
  declare const DEFER_VALIDATION: unique symbol;
439
689
  /**
440
- * Install a compiled fast-path parser for `validators`. Idempotent-ish: a second
441
- * install overwrites the first (codegen emits each map once, so this only matters
442
- * if a host installs by hand). See {@link CompiledValidatorMap} for the contract
443
- * the parser must honour.
444
- */
690
+ * Install a compiled fast-path parser for `validators`. Idempotent-ish: a second
691
+ * install overwrites the first (codegen emits each map once, so this only matters
692
+ * if a host installs by hand). See {@link CompiledValidatorMap} for the contract
693
+ * the parser must honour.
694
+ */
445
695
  declare const installCompiledValidatorMap: (validators: object, compiled: CompiledValidatorMap) => void;
446
696
  /**
447
- * Validate each declared field of `source` through its validator, re-wrapping
448
- * any {@link ValidationError} with a `label.&lt;key>:` prefix and the rebuilt path
449
- * `[key, ...error.path]` so the failure points at the offending field. Optional
450
- * fields absent from the source are skipped (so `v.optional` passes and a
451
- * required validator fails on `undefined`).
452
- *
453
- * The single arg-/field-parsing implementation shared across the framework — the
454
- * procedure builder (label `args`), the HTTP route builder (`searchParams` /
455
- * `body` / `params`), and `@lunora/workflow`'s reusable steps (`step args`) — so
456
- * the error-prefixing and optional-skip semantics can't drift apart. The `label`
457
- * is the only thing each caller varies.
458
- *
459
- * When a codegen-emitted {@link CompiledValidatorMap} is installed for this exact
460
- * `validators` object, the fast path runs first; it either returns the finished
461
- * record (a confident success — the common case) or {@link DEFER_VALIDATION}, in
462
- * which case the interpreted loop below runs and owns the result (and any error).
463
- */
697
+ * Validate each declared field of `source` through its validator, re-wrapping
698
+ * any {@link ValidationError} with a `label.<key>:` prefix and the rebuilt path
699
+ * `[key, ...error.path]` so the failure points at the offending field. Optional
700
+ * fields absent from the source are skipped (so `v.optional` passes and a
701
+ * required validator fails on `undefined`).
702
+ *
703
+ * The single arg-/field-parsing implementation shared across the framework — the
704
+ * procedure builder (label `args`), the HTTP route builder (`searchParams` /
705
+ * `body` / `params`), and `@lunora/workflow`'s reusable steps (`step args`) — so
706
+ * the error-prefixing and optional-skip semantics can't drift apart. The `label`
707
+ * is the only thing each caller varies.
708
+ *
709
+ * When a codegen-emitted {@link CompiledValidatorMap} is installed for this exact
710
+ * `validators` object, the fast path runs first; it either returns the finished
711
+ * record (a confident success — the common case) or {@link DEFER_VALIDATION}, in
712
+ * which case the interpreted loop below runs and owns the result (and any error).
713
+ */
464
714
  declare const parseValidatorMap: (validators: ValidatorMap, source: Record<string, unknown>, label: string) => Record<string, unknown>;
465
- declare const VERSION = "0.0.0";
466
- export { type CheckOptions, type Column, type ColumnMeta, type ColumnValidator, type CompiledValidatorMap, DEFER_VALIDATION, type Id, type Infer, type InferInsert, type InferSelect, type InferStandardOutput, type InferValidatorMap, type InsertShape, type JsonSchema, type JsonSchemaFragment, type MetaOptions, type SchemaNodeReader, type SelectShape, type ServerDefaultContext, type TimestampColumnValidator, VERSION, ValidationError, type ValidationPath, type Validator, type ValidatorKind, type ValidatorMap, argsToJsonSchema, describeValue, formatPath, installCompiledValidatorMap, isOrWrapsFromValidator, jsonSchemaFromNode, objectSchemaFromNodes, optionalInner, parseValidatorMap, toJsonSchema, v };
715
+ export { type ArrayColumnValidator, type CheckOptions, type Column, type ColumnMeta, type ColumnValidator, type CompiledValidatorMap, DEFER_VALIDATION, type GeoPoint, type Id, type Infer, type InferInsert, type InferSelect, type InferStandardInput, type InferStandardOutput, type InferValidatorMap, type InsertShape, type JsonSchema, type JsonSchemaFragment, type MetaOptions, type NumberColumnValidator, type ObjectColumnValidator, type ParseOptions, type SchemaNodeReader, type SelectShape, type ServerDefaultContext, type StringColumnValidator, type TimestampColumnValidator, ValidationError, type ValidationPath, type Validator, type ValidatorKind, type ValidatorMap, acceptsAbsent, argsToJsonSchema, describeValue, formatPath, installCompiledValidatorMap, isOrWrapsFromValidator, jsonSchemaFromNode, objectSchemaFromNodes, optionalInner, parseValidatorMap, toJsonSchema, v };