@nanokajs/core 0.1.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js CHANGED
@@ -1,4 +1,4 @@
1
- export { t } from './chunk-F5OBRXXY.js';
1
+ export { t } from './chunk-UYXRKFAR.js';
2
2
 
3
3
  // src/config.ts
4
4
  function defineConfig(config) {
@@ -1,6 +1,7 @@
1
1
  import { SQLiteColumnBuilderBase } from 'drizzle-orm/sqlite-core';
2
2
  import { z } from 'zod';
3
3
 
4
+ type FieldPolicy = 'serverOnly' | 'writeOnly' | 'readOnly';
4
5
  interface FieldModifiers {
5
6
  optional?: boolean;
6
7
  primary?: boolean;
@@ -10,6 +11,7 @@ interface FieldModifiers {
10
11
  format?: 'email' | 'uuid';
11
12
  min?: number;
12
13
  max?: number;
14
+ policy?: FieldPolicy;
13
15
  }
14
16
  type FieldKind = 'string' | 'uuid' | 'number' | 'integer' | 'boolean' | 'timestamp' | 'json';
15
17
  /**
@@ -60,7 +62,7 @@ interface Field<TS = unknown, Mods extends FieldModifiers = {}, ZB extends z.Zod
60
62
  * type T1 = InferFieldType<typeof t.string()> // string
61
63
  * type T2 = InferFieldType<typeof t.string().optional()> // string | undefined
62
64
  */
63
- type InferFieldType<F> = F extends Field<infer T, FieldModifiers, z.ZodTypeAny> ? T : never;
65
+ type InferFieldType<F> = F extends Field<infer T, FieldModifiers, z.ZodTypeAny> ? T : F extends Field<infer T, FieldModifiers, any> ? T : never;
64
66
 
65
67
  /**
66
68
  * Abstract base class for field builders.
@@ -94,7 +96,7 @@ declare abstract class BaseFieldBuilder<TS, Mods extends FieldModifiers = {}, ZB
94
96
  protected applyModifiersToZod(baseSchema: z.ZodTypeAny): z.ZodTypeAny;
95
97
  }
96
98
 
97
- declare class StringFieldBuilder<TS = string, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, TS> = z.ZodType<TS, z.ZodTypeDef, TS>> extends BaseFieldBuilder<TS, Mods, ZB> {
99
+ declare class StringFieldBuilder<TS = string, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
98
100
  readonly kind: "string";
99
101
  get zodBase(): ZB;
100
102
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
@@ -141,8 +143,17 @@ declare class StringFieldBuilder<TS = string, Mods extends FieldModifiers = {},
141
143
  max(this: StringFieldBuilder<TS, Mods>, n: number): StringFieldBuilder<TS, Mods & {
142
144
  max: number;
143
145
  }>;
146
+ serverOnly(this: StringFieldBuilder<TS, Mods>): StringFieldBuilder<TS, Omit<Mods, 'policy'> & {
147
+ policy: 'serverOnly';
148
+ }>;
149
+ writeOnly(this: StringFieldBuilder<TS, Mods>): StringFieldBuilder<TS, Omit<Mods, 'policy'> & {
150
+ policy: 'writeOnly';
151
+ }>;
152
+ readOnly(this: StringFieldBuilder<TS, Mods>): StringFieldBuilder<TS, Omit<Mods, 'policy'> & {
153
+ policy: 'readOnly';
154
+ }>;
144
155
  }
145
- declare class UuidFieldBuilder<TS = string, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, TS> = z.ZodType<TS, z.ZodTypeDef, TS>> extends BaseFieldBuilder<TS, Mods, ZB> {
156
+ declare class UuidFieldBuilder<TS = string, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
146
157
  readonly kind: "uuid";
147
158
  get zodBase(): ZB;
148
159
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
@@ -183,8 +194,17 @@ declare class UuidFieldBuilder<TS = string, Mods extends FieldModifiers = {}, ZB
183
194
  max(this: UuidFieldBuilder<TS, Mods>, n: number): UuidFieldBuilder<TS, Mods & {
184
195
  max: number;
185
196
  }>;
197
+ serverOnly(this: UuidFieldBuilder<TS, Mods>): UuidFieldBuilder<TS, Omit<Mods, 'policy'> & {
198
+ policy: 'serverOnly';
199
+ }>;
200
+ writeOnly(this: UuidFieldBuilder<TS, Mods>): UuidFieldBuilder<TS, Omit<Mods, 'policy'> & {
201
+ policy: 'writeOnly';
202
+ }>;
203
+ readOnly(this: UuidFieldBuilder<TS, Mods>): UuidFieldBuilder<TS, Omit<Mods, 'policy'> & {
204
+ policy: 'readOnly';
205
+ }>;
186
206
  }
187
- declare class NumberFieldBuilder<TS = number, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, TS> = z.ZodType<TS, z.ZodTypeDef, TS>> extends BaseFieldBuilder<TS, Mods, ZB> {
207
+ declare class NumberFieldBuilder<TS = number, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
188
208
  readonly kind: "number";
189
209
  get zodBase(): ZB;
190
210
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
@@ -225,8 +245,17 @@ declare class NumberFieldBuilder<TS = number, Mods extends FieldModifiers = {},
225
245
  max(this: NumberFieldBuilder<TS, Mods>, n: number): NumberFieldBuilder<TS, Mods & {
226
246
  max: number;
227
247
  }>;
248
+ serverOnly(this: NumberFieldBuilder<TS, Mods>): NumberFieldBuilder<TS, Omit<Mods, 'policy'> & {
249
+ policy: 'serverOnly';
250
+ }>;
251
+ writeOnly(this: NumberFieldBuilder<TS, Mods>): NumberFieldBuilder<TS, Omit<Mods, 'policy'> & {
252
+ policy: 'writeOnly';
253
+ }>;
254
+ readOnly(this: NumberFieldBuilder<TS, Mods>): NumberFieldBuilder<TS, Omit<Mods, 'policy'> & {
255
+ policy: 'readOnly';
256
+ }>;
228
257
  }
229
- declare class IntegerFieldBuilder<TS = number, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, TS> = z.ZodType<TS, z.ZodTypeDef, TS>> extends BaseFieldBuilder<TS, Mods, ZB> {
258
+ declare class IntegerFieldBuilder<TS = number, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
230
259
  readonly kind: "integer";
231
260
  get zodBase(): ZB;
232
261
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
@@ -267,8 +296,17 @@ declare class IntegerFieldBuilder<TS = number, Mods extends FieldModifiers = {},
267
296
  max(this: IntegerFieldBuilder<TS, Mods>, n: number): IntegerFieldBuilder<TS, Mods & {
268
297
  max: number;
269
298
  }>;
299
+ serverOnly(this: IntegerFieldBuilder<TS, Mods>): IntegerFieldBuilder<TS, Omit<Mods, 'policy'> & {
300
+ policy: 'serverOnly';
301
+ }>;
302
+ writeOnly(this: IntegerFieldBuilder<TS, Mods>): IntegerFieldBuilder<TS, Omit<Mods, 'policy'> & {
303
+ policy: 'writeOnly';
304
+ }>;
305
+ readOnly(this: IntegerFieldBuilder<TS, Mods>): IntegerFieldBuilder<TS, Omit<Mods, 'policy'> & {
306
+ policy: 'readOnly';
307
+ }>;
270
308
  }
271
- declare class BooleanFieldBuilder<TS = boolean, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, TS> = z.ZodType<TS, z.ZodTypeDef, TS>> extends BaseFieldBuilder<TS, Mods, ZB> {
309
+ declare class BooleanFieldBuilder<TS = boolean, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
272
310
  readonly kind: "boolean";
273
311
  get zodBase(): ZB;
274
312
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
@@ -297,8 +335,17 @@ declare class BooleanFieldBuilder<TS = boolean, Mods extends FieldModifiers = {}
297
335
  default(this: BooleanFieldBuilder<TS, Mods>, value: TS | (() => TS)): BooleanFieldBuilder<TS, Mods & {
298
336
  hasDefault: true;
299
337
  }>;
338
+ serverOnly(this: BooleanFieldBuilder<TS, Mods>): BooleanFieldBuilder<TS, Omit<Mods, 'policy'> & {
339
+ policy: 'serverOnly';
340
+ }>;
341
+ writeOnly(this: BooleanFieldBuilder<TS, Mods>): BooleanFieldBuilder<TS, Omit<Mods, 'policy'> & {
342
+ policy: 'writeOnly';
343
+ }>;
344
+ readOnly(this: BooleanFieldBuilder<TS, Mods>): BooleanFieldBuilder<TS, Omit<Mods, 'policy'> & {
345
+ policy: 'readOnly';
346
+ }>;
300
347
  }
301
- declare class TimestampFieldBuilder<TS = Date, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, Date | string | number> = z.ZodType<TS, z.ZodTypeDef, Date | string | number>> extends BaseFieldBuilder<TS, Mods, ZB> {
348
+ declare class TimestampFieldBuilder<TS = Date, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
302
349
  readonly kind: "timestamp";
303
350
  get zodBase(): ZB;
304
351
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
@@ -327,15 +374,26 @@ declare class TimestampFieldBuilder<TS = Date, Mods extends FieldModifiers = {},
327
374
  default(this: TimestampFieldBuilder<TS, Mods>, value: TS | (() => TS)): TimestampFieldBuilder<TS, Mods & {
328
375
  hasDefault: true;
329
376
  }>;
377
+ serverOnly(this: TimestampFieldBuilder<TS, Mods>): TimestampFieldBuilder<TS, Omit<Mods, 'policy'> & {
378
+ policy: 'serverOnly';
379
+ }>;
380
+ writeOnly(this: TimestampFieldBuilder<TS, Mods>): TimestampFieldBuilder<TS, Omit<Mods, 'policy'> & {
381
+ policy: 'writeOnly';
382
+ }>;
383
+ readOnly(this: TimestampFieldBuilder<TS, Mods>): TimestampFieldBuilder<TS, Omit<Mods, 'policy'> & {
384
+ policy: 'readOnly';
385
+ }>;
330
386
  }
331
387
  /**
332
388
  * JSON フィールドビルダー。
333
389
  *
334
- * ⚠️ セキュリティ警告: zod ベースは `z.unknown()` のため、API に晒す際は
335
- * `validator()` で除外するか、別途検証が必須。
390
+ * `t.json(zodSchema)` 形式で Zod スキーマを渡すと runtime 検証が走る。
391
+ * スキーマ省略時は `z.unknown()` で後方互換を維持する。
336
392
  */
337
- declare class JsonFieldBuilder<T = unknown, TS = T, Mods extends FieldModifiers = {}, ZB extends z.ZodType<TS, z.ZodTypeDef, TS> = z.ZodType<TS, z.ZodTypeDef, TS>> extends BaseFieldBuilder<TS, Mods, ZB> {
393
+ declare class JsonFieldBuilder<T = unknown, TS = T, Mods extends FieldModifiers = {}, ZB extends z.ZodTypeAny = z.ZodTypeAny> extends BaseFieldBuilder<TS, Mods, ZB> {
338
394
  readonly kind: "json";
395
+ private readonly _zodSchema;
396
+ constructor(modifiers?: Mods, zodSchema?: z.ZodTypeAny);
339
397
  get zodBase(): ZB;
340
398
  drizzleColumn(name: string): SQLiteColumnBuilderBase;
341
399
  protected cloneWithModifiers<NewMods extends FieldModifiers>(mods: NewMods): JsonFieldBuilder<T, TS, NewMods>;
@@ -363,7 +421,18 @@ declare class JsonFieldBuilder<T = unknown, TS = T, Mods extends FieldModifiers
363
421
  default(this: JsonFieldBuilder<T, TS, Mods>, value: TS | (() => TS)): JsonFieldBuilder<T, TS, Mods & {
364
422
  hasDefault: true;
365
423
  }>;
424
+ serverOnly(this: JsonFieldBuilder<T, TS, Mods>): JsonFieldBuilder<T, TS, Omit<Mods, 'policy'> & {
425
+ policy: 'serverOnly';
426
+ }>;
427
+ writeOnly(this: JsonFieldBuilder<T, TS, Mods>): JsonFieldBuilder<T, TS, Omit<Mods, 'policy'> & {
428
+ policy: 'writeOnly';
429
+ }>;
430
+ readOnly(this: JsonFieldBuilder<T, TS, Mods>): JsonFieldBuilder<T, TS, Omit<Mods, 'policy'> & {
431
+ policy: 'readOnly';
432
+ }>;
366
433
  }
434
+ declare function jsonField<S extends z.ZodTypeAny>(schema: S): JsonFieldBuilder<z.infer<S>, z.infer<S>, {}>;
435
+ declare function jsonField<T = unknown>(): JsonFieldBuilder<T, T, {}>;
367
436
  /**
368
437
  * Field DSL factory for building type-safe database fields.
369
438
  *
@@ -374,6 +443,7 @@ declare class JsonFieldBuilder<T = unknown, TS = T, Mods extends FieldModifiers
374
443
  * const ageField = t.integer().min(0).max(150)
375
444
  * const createdAtField = t.timestamp().default(() => new Date())
376
445
  * const dataField = t.json<{ foo: string }>()
446
+ * const dataField2 = t.json(z.object({ foo: z.string() })) // runtime validation
377
447
  */
378
448
  declare const t: {
379
449
  string(): StringFieldBuilder<string, {}>;
@@ -383,20 +453,17 @@ declare const t: {
383
453
  boolean(): BooleanFieldBuilder<boolean, {}>;
384
454
  timestamp(): TimestampFieldBuilder<Date, {}>;
385
455
  /**
386
- * JSON フィールド。任意の JSON 値を受け入れる(Zod 内部表現は `z.unknown()`)。
456
+ * JSON フィールド。
457
+ *
458
+ * `t.json(zodSchema)` 形式で Zod スキーマを渡すと runtime 検証が走る。
459
+ * スキーマ省略時は `z.unknown()` で後方互換を維持する。
387
460
  *
388
- * ⚠️ セキュリティ警告:
389
- * このフィールドの zod ベースは `z.unknown()` のため、型パラメータ `T` で
390
- * 指定した制約は **コンパイル時のみ** 適用され、validator() 経由でも
391
- * runtime バリデーションは行われません。API 入力として晒す場合は:
392
- * - `model.validator(target, { omit: ['<json field name>'] })` で除外する、または
393
- * - ハンドラ側で別途 Zod スキーマを使って手動検証する、
394
- * のいずれかを必ず実施してください。型パラメータが `unknown` 以外の値を
395
- * runtime で保証することはありません。
461
+ * `t.json(z.object({ foo: z.string() }))` でオブジェクト形状の runtime 検証が可能。
396
462
  *
397
- * Phase 2 以降で `t.json(zodSchema)` 形式の引数追加を検討中。
463
+ * @note codegen (`nanoka generate`) は `t.json(zodSchema)` を渡しても `$type<unknown>()` のまま出力する。
464
+ * Drizzle 型が欲しい場合は生成後に手動で書き換えるか `as` を使うこと。
398
465
  */
399
- json<T = unknown>(): JsonFieldBuilder<T, T, {}>;
466
+ json: typeof jsonField;
400
467
  };
401
468
 
402
- export { type Field as F, type InferFieldType as I, type FieldModifiers as a, t };
469
+ export { type Field as F, type InferFieldType as I, type FieldPolicy as a, type FieldModifiers as b, t };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { D1Database } from '@cloudflare/workers-types';
2
2
  import { BatchItem, BatchResponse } from 'drizzle-orm/batch';
3
3
  import { BaseSQLiteDatabase, SQLiteTable } from 'drizzle-orm/sqlite-core';
4
- import { F as Field, I as InferFieldType } from './factories-CDsk1Hdt.js';
5
- export { a as FieldModifiers, t } from './factories-CDsk1Hdt.js';
4
+ import { F as Field, I as InferFieldType, a as FieldPolicy } from './factories-Cmvx0495.js';
5
+ export { b as FieldModifiers, t } from './factories-Cmvx0495.js';
6
6
  import { Hook } from '@hono/zod-validator';
7
7
  import * as hono from 'hono';
8
8
  import { ValidationTargets, Env, MiddlewareHandler, Hono } from 'hono';
@@ -40,6 +40,14 @@ interface Adapter<TSchema extends Record<string, unknown> = Record<string, never
40
40
  */
41
41
  declare function d1Adapter<TSchema extends Record<string, unknown> = Record<string, never>>(d1: D1Database): Adapter<TSchema>;
42
42
 
43
+ type OpenAPISchemaObject = Record<string, unknown>;
44
+ interface OpenAPIModelComponent {
45
+ create: OpenAPISchemaObject;
46
+ update: OpenAPISchemaObject;
47
+ output: OpenAPISchemaObject;
48
+ }
49
+ type OpenAPIUsage = 'create' | 'update' | 'output';
50
+
43
51
  /**
44
52
  * Options for findMany query.
45
53
  * `limit` is required (no default).
@@ -78,13 +86,84 @@ type RowType<Fields extends Record<string, Field<any, any, any>>> = {
78
86
  [K in keyof Fields]: InferFieldType<Fields[K]>;
79
87
  };
80
88
  /**
81
- * Create input: partial row (most fields optional for insert).
82
- * Phase 1 では `Partial<RowType>` のままとし、必須/任意フィールドの型区別は Phase 2 で扱う。
89
+ * @internal
90
+ * Extracts the policy from a Field's modifiers.
91
+ *
92
+ * Uses `Field<any, { policy: infer P }, any>` rather than a two-step
93
+ * `infer M` + `M extends { policy: infer P }` pattern.
94
+ * The two-step pattern is broken when TypeScript widens `M` to the constraint
95
+ * `FieldModifiers` (which has `policy?: FieldPolicy` as optional), causing
96
+ * `M extends { policy: infer P }` to be true for ALL fields regardless of their
97
+ * actual policy value. Directly matching `{ policy: infer P }` avoids this.
98
+ */
99
+ type PolicyOf<F> = F extends Field<any, {
100
+ policy: infer P extends FieldPolicy;
101
+ }, any> ? P : never;
102
+ /**
103
+ * @internal
104
+ * Computes which field keys should be omitted based on policy and usage.
105
+ * Mirrors the runtime logic in derivePolicyOptions() in schema.ts.
106
+ *
107
+ * NOTE: `[PolicyOf<Fields[K]>] extends [never]` (non-distributive form) is used to
108
+ * short-circuit fields with no policy to `never` (do not omit). Without this guard,
109
+ * `never extends 'serverOnly'` evaluates to `true` (vacuously), causing every field
110
+ * without a policy to be incorrectly included in the omit set.
111
+ */
112
+ type PolicyOmitKeys<Fields extends Record<string, Field<any, any, any>>, Usage extends 'input-create' | 'input-update' | 'output'> = {
113
+ [K in keyof Fields]: [PolicyOf<Fields[K]>] extends [never] ? never : PolicyOf<Fields[K]> extends 'serverOnly' ? K : Usage extends 'output' ? PolicyOf<Fields[K]> extends 'writeOnly' ? K : never : Usage extends 'input-create' | 'input-update' ? PolicyOf<Fields[K]> extends 'readOnly' ? K : never : never;
114
+ }[keyof Fields];
115
+ /**
116
+ * @internal
117
+ * Computes which field keys have the serverOnly policy.
118
+ * serverOnly fields are completely excluded from CreateInput
119
+ * (cannot be passed even from library internals — they are server-side only).
120
+ *
121
+ * Uses `[PolicyOf<Fields[K]>] extends [never]` (non-distributive form) to short-circuit
122
+ * fields with no policy to `never`. Without this guard, `never extends 'serverOnly'`
123
+ * evaluates to `true` (vacuously), causing every field without a policy to be included.
124
+ */
125
+ type ServerOnlyKeys<Fields extends Record<string, Field<any, any, any>>> = {
126
+ [K in keyof Fields]: [PolicyOf<Fields[K]>] extends [never] ? never : PolicyOf<Fields[K]> extends 'serverOnly' ? K : never;
127
+ }[keyof Fields];
128
+ /**
129
+ * @internal
130
+ * Determines if a field is required for create input.
131
+ * A field is optional if it has a default, is optional, or is readOnly.
132
+ *
133
+ * Uses direct shape matching (`Field<any, { hasDefault: true }, any>`) to avoid
134
+ * the TypeScript infer-widening issue where `infer M` resolves to `FieldModifiers`
135
+ * (the constraint bound) instead of the concrete Mods type, causing `M extends
136
+ * { policy: 'readOnly' }` to be false even for readOnly fields.
137
+ */
138
+ type IsRequired<F extends Field<any, any, any>> = F extends Field<any, {
139
+ hasDefault: true;
140
+ }, any> ? false : F extends Field<any, {
141
+ optional: true;
142
+ }, any> ? false : F extends Field<any, {
143
+ policy: 'readOnly';
144
+ }, any> ? false : true;
145
+ /**
146
+ * Create input with precise types:
147
+ * - `readOnly` fields are optional (library internals can pass them; API callers
148
+ * are excluded via inputSchema — but CreateInput itself keeps them as optional
149
+ * so handlers can supply generated values like `id` or `createdAt`)
150
+ * - fields with `default` are optional
151
+ * - fields marked `optional` are optional
152
+ * - all other fields are required
153
+ * - `serverOnly` fields are completely excluded (cannot be passed even internally)
154
+ * - `writeOnly` fields are included as required (they are input fields by design)
83
155
  */
84
- type CreateInput<Fields extends Record<string, Field<any, any, any>>> = Partial<RowType<Fields>>;
156
+ type CreateInput<Fields extends Record<string, Field<any, any, any>>> = {
157
+ [K in keyof Fields as IsRequired<Fields[K]> extends true ? K extends ServerOnlyKeys<Fields> ? never : K : never]: InferFieldType<Fields[K]>;
158
+ } & {
159
+ [K in keyof Fields as IsRequired<Fields[K]> extends true ? never : K extends ServerOnlyKeys<Fields> ? never : K]?: InferFieldType<Fields[K]>;
160
+ };
161
+ type FieldAccessor<K extends string> = {
162
+ readonly [Key in K]: Key;
163
+ };
85
164
  interface SchemaOptions<K extends string = string> {
86
- readonly pick?: readonly K[];
87
- readonly omit?: readonly K[];
165
+ readonly pick?: readonly K[] | ((f: FieldAccessor<K>) => readonly K[]);
166
+ readonly omit?: readonly K[] | ((f: FieldAccessor<K>) => readonly K[]);
88
167
  readonly partial?: boolean;
89
168
  }
90
169
  /**
@@ -101,16 +180,22 @@ type FieldsToZodShape<Fields extends Record<string, Field<any, any, any>>> = {
101
180
  * Utility to narrow array literal types to union of strings.
102
181
  */
103
182
  type ArrayKeys<A> = A extends readonly (infer U)[] ? U : never;
183
+ /**
184
+ * @internal
185
+ * Resolves pick/omit option type to the array type.
186
+ * Handles both array form and accessor function form.
187
+ */
188
+ type ResolveOptKeys<Opt> = Opt extends readonly string[] ? Opt : Opt extends (f: FieldAccessor<infer _K>) => readonly (infer R)[] ? readonly R[] : never;
104
189
  /**
105
190
  * @internal
106
191
  * Apply pick transformation to a shape.
107
192
  */
108
- type ApplyPickToShape<Shape extends z.ZodRawShape, PickKeys> = PickKeys extends readonly string[] ? Pick<Shape, ArrayKeys<PickKeys> & keyof Shape> : Shape;
193
+ type ApplyPickToShape<Shape extends z.ZodRawShape, PickOpt> = ResolveOptKeys<PickOpt> extends readonly string[] ? Pick<Shape, ArrayKeys<ResolveOptKeys<PickOpt>> & keyof Shape> : Shape;
109
194
  /**
110
195
  * @internal
111
196
  * Apply omit transformation to a shape.
112
197
  */
113
- type ApplyOmitToShape<Shape extends z.ZodRawShape, OmitKeys> = OmitKeys extends readonly string[] ? Omit<Shape, ArrayKeys<OmitKeys> & keyof Shape> : Shape;
198
+ type ApplyOmitToShape<Shape extends z.ZodRawShape, OmitOpt> = ResolveOptKeys<OmitOpt> extends readonly string[] ? Omit<Shape, ArrayKeys<ResolveOptKeys<OmitOpt>> & keyof Shape> : Shape;
114
199
  /**
115
200
  * @internal
116
201
  * Apply partial transformation to a shape.
@@ -118,38 +203,77 @@ type ApplyOmitToShape<Shape extends z.ZodRawShape, OmitKeys> = OmitKeys extends
118
203
  type ApplyPartialToShape<Shape extends z.ZodRawShape> = {
119
204
  [K in keyof Shape]: z.ZodOptional<Shape[K]>;
120
205
  };
206
+ /**
207
+ * @internal
208
+ * Checks if an option value (array or function) is present (i.e. not undefined and not never).
209
+ * Uses distributive conditional to handle union types like `T | undefined`.
210
+ */
211
+ type HasOpt<Opt> = undefined extends Opt ? false : [Opt] extends [never] ? false : true;
121
212
  /**
122
213
  * @internal
123
214
  * Apply pick/omit/partial transformations to a Zod raw shape.
124
215
  * Order: pick → omit → partial
216
+ * Handles both array form and accessor function form for pick/omit.
125
217
  */
126
- type ApplyShape<Shape extends z.ZodRawShape, Opts extends SchemaOptions | undefined> = Opts extends undefined ? Shape : Opts extends {
127
- pick: readonly string[];
128
- } ? Opts extends {
129
- omit: readonly string[];
130
- } ? Opts extends {
218
+ type ApplyShape<Shape extends z.ZodRawShape, Opts extends SchemaOptions | undefined> = Opts extends SchemaOptions ? HasOpt<Opts['pick']> extends true ? HasOpt<Opts['omit']> extends true ? Opts extends {
131
219
  partial: true;
132
220
  } ? ApplyPartialToShape<ApplyOmitToShape<ApplyPickToShape<Shape, Opts['pick']>, Opts['omit']>> : ApplyOmitToShape<ApplyPickToShape<Shape, Opts['pick']>, Opts['omit']> : Opts extends {
133
221
  partial: true;
134
- } ? ApplyPartialToShape<ApplyPickToShape<Shape, Opts['pick']>> : ApplyPickToShape<Shape, Opts['pick']> : Opts extends {
135
- omit: readonly string[];
136
- } ? Opts extends {
222
+ } ? ApplyPartialToShape<ApplyPickToShape<Shape, Opts['pick']>> : ApplyPickToShape<Shape, Opts['pick']> : HasOpt<Opts['omit']> extends true ? Opts extends {
137
223
  partial: true;
138
224
  } ? ApplyPartialToShape<ApplyOmitToShape<Shape, Opts['omit']>> : ApplyOmitToShape<Shape, Opts['omit']> : Opts extends {
139
225
  partial: true;
140
- } ? ApplyPartialToShape<Shape> : Shape;
226
+ } ? ApplyPartialToShape<Shape> : Shape : Shape;
141
227
  /**
142
228
  * @internal
143
229
  * Returns the correct Zod object type after applying pick/omit/partial transformations.
144
230
  */
145
231
  type Apply<Shape extends z.ZodRawShape, Opts extends SchemaOptions | undefined> = z.ZodObject<ApplyShape<Shape, Opts>>;
232
+ /**
233
+ * @internal
234
+ * Apply pick transformation to a TS record type.
235
+ */
236
+ type ApplyPickToRecord<R, PickOpt> = ResolveOptKeys<PickOpt> extends readonly string[] ? Pick<R, ArrayKeys<ResolveOptKeys<PickOpt>> & keyof R> : R;
237
+ /**
238
+ * @internal
239
+ * Apply omit transformation to a TS record type.
240
+ */
241
+ type ApplyOmitToRecord<R, OmitOpt> = ResolveOptKeys<OmitOpt> extends readonly string[] ? Omit<R, ArrayKeys<ResolveOptKeys<OmitOpt>> & keyof R> : R;
242
+ /**
243
+ * @internal
244
+ * Apply pick/omit/partial transformations to a TypeScript record type.
245
+ * Mirrors ApplyShape but operates on TS types directly, not Zod shapes.
246
+ * Used for Zod v3/v4 compatible middleware input/output type computation.
247
+ */
248
+ type ApplyToRecord<R, Opts extends SchemaOptions | undefined> = Opts extends SchemaOptions ? HasOpt<Opts['pick']> extends true ? HasOpt<Opts['omit']> extends true ? Opts extends {
249
+ partial: true;
250
+ } ? Partial<ApplyOmitToRecord<ApplyPickToRecord<R, Opts['pick']>, Opts['omit']>> : ApplyOmitToRecord<ApplyPickToRecord<R, Opts['pick']>, Opts['omit']> : Opts extends {
251
+ partial: true;
252
+ } ? Partial<ApplyPickToRecord<R, Opts['pick']>> : ApplyPickToRecord<R, Opts['pick']> : HasOpt<Opts['omit']> extends true ? Opts extends {
253
+ partial: true;
254
+ } ? Partial<ApplyOmitToRecord<R, Opts['omit']>> : ApplyOmitToRecord<R, Opts['omit']> : Opts extends {
255
+ partial: true;
256
+ } ? Partial<R> : R : R;
146
257
  /**
147
258
  * @internal
148
259
  * Mirrors @hono/zod-validator's middleware Input type.
149
260
  * Aligned with @hono/zod-validator@0.4.x; revisit on version bump.
150
261
  */
151
262
  type HasUndefined<T> = undefined extends T ? true : false;
152
- type ValidatorInput<Target extends keyof ValidationTargets, Schema extends z.ZodType<any, z.ZodTypeDef, any>, In = z.input<Schema>, Out = z.output<Schema>> = {
263
+ /**
264
+ * @internal
265
+ * Hono middleware input/output type for validator middleware.
266
+ * Aligned with @hono/zod-validator@0.4.x; revisit on version bump.
267
+ *
268
+ * WARNING (Zod 4): `z.input<ZodTypeAny>` resolves to `unknown` in Zod 4, making
269
+ * `c.req.valid(target)` return `unknown` if `In`/`Out` are left as defaults.
270
+ * When using this type directly (outside of `ModelValidatorReturn`), always pass
271
+ * explicit `In` and `Out` type arguments — do NOT rely on the defaults.
272
+ *
273
+ * Internal usages in this file (ModelValidatorReturn and preset overloads) already
274
+ * pass `In`/`Out` explicitly via `ApplyToRecord` or `Omit<RowType<Fields>, ...>`.
275
+ */
276
+ type ValidatorInput<Target extends keyof ValidationTargets, Schema extends z.ZodTypeAny, In = z.input<Schema>, Out = z.output<Schema>> = {
153
277
  in: HasUndefined<In> extends true ? {
154
278
  [K in Target]?: (In extends ValidationTargets[K] ? In : {
155
279
  [K2 in keyof In]?: ValidationTargets[K][K2] | undefined;
@@ -167,8 +291,11 @@ type ValidatorInput<Target extends keyof ValidationTargets, Schema extends z.Zod
167
291
  * @internal
168
292
  * Type-safe Hono middleware handler return for Model.validator().
169
293
  * Provides proper type narrowing for c.req.valid(target).
294
+ *
295
+ * Uses RowType<Fields> directly for input/output type computation
296
+ * to ensure Zod v3/v4 compatibility (z.input<ZodTypeAny> returns unknown in Zod v4).
170
297
  */
171
- type ModelValidatorReturn<Fields extends Record<string, Field<any, any, any>>, Target extends keyof ValidationTargets, Opts extends SchemaOptions<keyof Fields & string> | undefined> = MiddlewareHandler<any, string, ValidatorInput<Target, Apply<FieldsToZodShape<Fields>, Opts>>>;
298
+ type ModelValidatorReturn<Fields extends Record<string, Field<any, any, any>>, Target extends keyof ValidationTargets, Opts extends SchemaOptions<keyof Fields & string> | undefined> = MiddlewareHandler<any, string, ValidatorInput<Target, Apply<FieldsToZodShape<Fields>, Opts>, ApplyToRecord<RowType<Fields>, Opts>, ApplyToRecord<RowType<Fields>, Opts>>>;
172
299
 
173
300
  /**
174
301
  * Represents a database model with type-safe schema derivation and CRUD operations.
@@ -185,22 +312,60 @@ interface Model<Fields extends Record<string, Field<any, any, any>>> {
185
312
  * const CreateSchema = User.schema({ omit: ['passwordHash'] })
186
313
  * const UpdateSchema = User.schema({ partial: true, pick: ['name', 'email'] })
187
314
  */
188
- schema<Opts extends SchemaOptions<keyof Fields & string> | undefined = undefined>(opts?: Opts): Apply<FieldsToZodShape<Fields>, Opts>;
315
+ schema<Opts extends SchemaOptions<keyof Fields & string> | undefined>(opts?: Opts): Apply<FieldsToZodShape<Fields>, Opts>;
189
316
  /**
190
317
  * Returns a Hono middleware validator derived from this model's schema.
191
318
  * Integrates with @hono/zod-validator to validate request inputs.
192
319
  * The optional `hook` argument is passed through to @hono/zod-validator.
193
320
  *
321
+ * Accepts a preset ('create' | 'update') as second argument to use inputSchema.
322
+ * When a preset is used, the return type is a MiddlewareHandler (loosely typed in Phase 2A).
323
+ * Precise type narrowing for preset is deferred to Phase 2B M2.3.
324
+ *
194
325
  * @example
326
+ * app.post('/users', User.validator('json', 'create'), c => {
327
+ * const body = c.req.valid('json')
328
+ * })
195
329
  * app.post('/users', User.validator('json', { omit: ['passwordHash'] }), c => {
196
330
  * const body = c.req.valid('json')
197
331
  * })
198
- * // With hook for hardened error responses:
332
+ * // With hook:
199
333
  * app.post('/users', User.validator('json', opts, (result, c) => {
200
334
  * if (!result.success) return c.json({ error: 'Invalid request' }, 400)
201
335
  * }), handler)
202
336
  */
203
- validator<Target extends keyof ValidationTargets, Opts extends SchemaOptions<keyof Fields & string> | undefined = undefined, E extends Env = Env, P extends string = string>(target: Target, opts?: Opts, hook?: Hook<z.output<Apply<FieldsToZodShape<Fields>, Opts>>, E, P, Target>): ModelValidatorReturn<Fields, Target, Opts>;
337
+ validator<Target extends keyof ValidationTargets, E extends Env = Env, P extends string = string>(target: Target, preset: 'create', hook?: Hook<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>, E, P, Target>): MiddlewareHandler<E, P, ValidatorInput<Target, z.ZodObject<z.ZodRawShape>, Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>, Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>>>;
338
+ validator<Target extends keyof ValidationTargets, E extends Env = Env, P extends string = string>(target: Target, preset: 'update', hook?: Hook<Partial<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>>, E, P, Target>): MiddlewareHandler<E, P, ValidatorInput<Target, z.ZodObject<z.ZodRawShape>, Partial<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>>, Partial<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>>>>;
339
+ validator<Target extends keyof ValidationTargets, Opts extends SchemaOptions<keyof Fields & string> | undefined, E extends Env = Env, P extends string = string>(target: Target, opts?: Opts, hook?: Hook<z.output<Apply<FieldsToZodShape<Fields>, Opts>>, E, P, Target>): ModelValidatorReturn<Fields, Target, Opts>;
340
+ /**
341
+ * Returns a Zod schema for API input, with policy-based field exclusions applied.
342
+ * - 'create': serverOnly and readOnly fields are excluded
343
+ * - 'update': same exclusions + all fields become optional (partial: true)
344
+ *
345
+ * The return type precisely reflects policy-derived omissions using PolicyOmitKeys.
346
+ */
347
+ inputSchema<Opts extends SchemaOptions<keyof Fields & string> | undefined = undefined>(usage: 'create', opts?: Opts): z.ZodObject<ApplyShape<Omit<FieldsToZodShape<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>, Opts>>;
348
+ inputSchema<Opts extends SchemaOptions<keyof Fields & string> | undefined = undefined>(usage: 'update', opts?: Opts): z.ZodObject<ApplyShape<Omit<FieldsToZodShape<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>, Opts extends undefined ? {
349
+ partial: true;
350
+ } : Opts & {
351
+ partial: true;
352
+ }>>;
353
+ inputSchema(usage: 'create' | 'update', opts?: SchemaOptions<keyof Fields & string>): z.ZodObject<z.ZodRawShape>;
354
+ /**
355
+ * Returns a Zod schema for API output, with policy-based field exclusions applied.
356
+ * - serverOnly and writeOnly fields are excluded
357
+ * - readOnly fields remain present
358
+ *
359
+ * The return type precisely reflects policy-derived omissions using PolicyOmitKeys.
360
+ */
361
+ outputSchema<Opts extends SchemaOptions<keyof Fields & string> | undefined = undefined>(opts?: Opts): z.ZodObject<ApplyShape<Omit<FieldsToZodShape<Fields>, PolicyOmitKeys<Fields, 'output'> & string>, Opts>>;
362
+ /**
363
+ * Parses a DB row through outputSchema and returns the safe response object.
364
+ * Equivalent to `User.outputSchema().parse(row)`.
365
+ */
366
+ toResponse(row: RowType<Fields>): unknown;
367
+ toOpenAPIComponent(): OpenAPIModelComponent;
368
+ toOpenAPISchema(usage: OpenAPIUsage): OpenAPISchemaObject;
204
369
  /**
205
370
  * Fetches multiple rows with pagination and optional ordering.
206
371
  * `limit` is required (no default) to prevent accidental unbounded queries.
@@ -286,8 +451,25 @@ interface NanokaModel<Fields extends Record<string, Field<any, any, any>>> {
286
451
  /**
287
452
  * Returns a Hono middleware validator for this model.
288
453
  * The optional `hook` argument is passed through to @hono/zod-validator.
454
+ * Accepts a preset ('create' | 'update') as second argument to use inputSchema.
289
455
  */
456
+ validator<Target extends keyof hono.ValidationTargets, E extends Env$1 = Env$1, P extends string = string>(target: Target, preset: 'create', hook?: Hook<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>, E, P, Target>): MiddlewareHandler<E, P, ValidatorInput<Target, z.ZodObject<z.ZodRawShape>, Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>, Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-create'> & string>>>;
457
+ validator<Target extends keyof hono.ValidationTargets, E extends Env$1 = Env$1, P extends string = string>(target: Target, preset: 'update', hook?: Hook<Partial<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>>, E, P, Target>): MiddlewareHandler<E, P, ValidatorInput<Target, z.ZodObject<z.ZodRawShape>, Partial<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>>, Partial<Omit<RowType<Fields>, PolicyOmitKeys<Fields, 'input-update'> & string>>>>;
290
458
  validator<Target extends keyof hono.ValidationTargets, Opts extends SchemaOptions<keyof Fields & string> | undefined = undefined, E extends Env$1 = Env$1, P extends string = string>(target: Target, opts?: Opts, hook?: Hook<z.output<Apply<FieldsToZodShape<Fields>, Opts>>, E, P, Target>): ModelValidatorReturn<Fields, Target, Opts>;
459
+ /**
460
+ * Returns a Zod schema for API input, with policy-based field exclusions applied.
461
+ */
462
+ inputSchema(usage: 'create' | 'update', opts?: SchemaOptions<keyof Fields & string>): z.ZodObject<z.ZodRawShape>;
463
+ /**
464
+ * Returns a Zod schema for API output, with policy-based field exclusions applied.
465
+ */
466
+ outputSchema(opts?: SchemaOptions<keyof Fields & string>): z.ZodObject<z.ZodRawShape>;
467
+ /**
468
+ * Parses a DB row through outputSchema and returns the safe response object.
469
+ */
470
+ toResponse(row: RowType<Fields>): unknown;
471
+ toOpenAPIComponent(): OpenAPIModelComponent;
472
+ toOpenAPISchema(usage: OpenAPIUsage): OpenAPISchemaObject;
291
473
  /**
292
474
  * Fetches multiple rows with pagination and optional ordering.
293
475
  * `limit` is required (no default) to prevent accidental unbounded queries.
@@ -364,4 +546,4 @@ interface Nanoka<E extends Env$1 = BlankEnv> extends Hono<E> {
364
546
  */
365
547
  declare function nanoka<E extends Env$1 = BlankEnv>(adapter: Adapter): Nanoka<E>;
366
548
 
367
- export { type Adapter, type CreateInput, Field, type FindManyOptions, type IdOrWhere, InferFieldType, type Model, type Nanoka, type NanokaModel, type OrderBy, type RowType, type SchemaOptions, type Where, d1Adapter, defineModel, nanoka };
549
+ export { type Adapter, type CreateInput, Field, FieldPolicy, type FindManyOptions, type IdOrWhere, InferFieldType, type Model, type Nanoka, type NanokaModel, type OpenAPIModelComponent, type OpenAPISchemaObject, type OpenAPIUsage, type OrderBy, type RowType, type SchemaOptions, type Where, d1Adapter, defineModel, nanoka };