@nanokajs/core 0.1.3 → 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/index.js CHANGED
@@ -1,9 +1,9 @@
1
- export { t } from './chunk-F5OBRXXY.js';
1
+ export { t } from './chunk-UYXRKFAR.js';
2
2
  import { drizzle } from 'drizzle-orm/d1';
3
3
  import { zValidator } from '@hono/zod-validator';
4
+ import { z } from 'zod';
4
5
  import { asc, desc, eq, and } from 'drizzle-orm';
5
6
  import { HTTPException } from 'hono/http-exception';
6
- import { z } from 'zod';
7
7
  import { sqliteTable } from 'drizzle-orm/sqlite-core';
8
8
  import { Hono } from 'hono';
9
9
 
@@ -14,6 +14,296 @@ function d1Adapter(d1) {
14
14
  batch: (queries) => db.batch(queries)
15
15
  };
16
16
  }
17
+
18
+ // src/model/types.ts
19
+ function resolveSchemaOptionKeys(opt, accessor) {
20
+ if (opt === void 0) return void 0;
21
+ if (typeof opt === "function") return opt(accessor);
22
+ return opt;
23
+ }
24
+ function buildFieldAccessor(fields) {
25
+ const acc = {};
26
+ for (const k of Object.keys(fields)) acc[k] = k;
27
+ return Object.freeze(acc);
28
+ }
29
+
30
+ // src/model/schema.ts
31
+ function computeForcedOmit(fields) {
32
+ const omit = [];
33
+ for (const [key, field] of Object.entries(fields)) {
34
+ if (field.modifiers.policy === "serverOnly") {
35
+ omit.push(key);
36
+ }
37
+ }
38
+ return omit;
39
+ }
40
+ function computeOptionalOmit(fields, target) {
41
+ const omit = [];
42
+ for (const [key, field] of Object.entries(fields)) {
43
+ const policy = field.modifiers.policy;
44
+ if (!policy) continue;
45
+ if (policy === "writeOnly" && target === "output") {
46
+ omit.push(key);
47
+ } else if (policy === "readOnly" && (target === "input-create" || target === "input-update")) {
48
+ omit.push(key);
49
+ }
50
+ }
51
+ return omit;
52
+ }
53
+ function derivePolicyOptions(fields, usage, userOpts, accessor) {
54
+ const forced = computeForcedOmit(fields);
55
+ const optional = computeOptionalOmit(fields, usage);
56
+ const acc = accessor ?? {};
57
+ const resolvedPick = resolveSchemaOptionKeys(userOpts?.pick, acc);
58
+ const resolvedOmit = resolveSchemaOptionKeys(userOpts?.omit, acc);
59
+ if (resolvedPick !== void 0) {
60
+ const partial2 = usage === "input-update" ? userOpts?.partial ?? true : userOpts?.partial;
61
+ const sanitizedPick = Array.from(resolvedPick).filter((k) => !forced.includes(k));
62
+ const combinedOmit2 = [.../* @__PURE__ */ new Set([...forced, ...resolvedOmit ?? []])];
63
+ return {
64
+ ...userOpts,
65
+ pick: sanitizedPick,
66
+ ...combinedOmit2.length > 0 ? { omit: combinedOmit2 } : {},
67
+ partial: partial2
68
+ };
69
+ }
70
+ const combinedOmit = [.../* @__PURE__ */ new Set([...forced, ...optional, ...resolvedOmit ?? []])];
71
+ const partial = usage === "input-update" ? userOpts?.partial ?? true : userOpts?.partial ?? void 0;
72
+ return {
73
+ ...userOpts ?? {},
74
+ pick: void 0,
75
+ ...combinedOmit.length > 0 ? { omit: combinedOmit } : {},
76
+ ...partial !== void 0 ? { partial } : {}
77
+ };
78
+ }
79
+ function buildBaseObject(fields) {
80
+ const shape = {};
81
+ for (const [key, field] of Object.entries(fields)) {
82
+ shape[key] = field.zodBase;
83
+ }
84
+ return z.object(shape);
85
+ }
86
+ function applySchemaOptions(baseSchema, opts, accessor) {
87
+ if (!opts) {
88
+ return baseSchema;
89
+ }
90
+ const acc = accessor ?? {};
91
+ const resolvedPick = resolveSchemaOptionKeys(opts.pick, acc);
92
+ const resolvedOmit = resolveSchemaOptionKeys(opts.omit, acc);
93
+ let schema = baseSchema;
94
+ if (resolvedPick !== void 0) {
95
+ const pickArray = Array.from(resolvedPick);
96
+ schema = schema.pick(
97
+ // biome-ignore lint/suspicious/noExplicitAny: Zod API
98
+ Object.fromEntries(pickArray.map((k) => [k, true]))
99
+ );
100
+ }
101
+ if (resolvedOmit !== void 0) {
102
+ const omitArray = Array.from(resolvedOmit);
103
+ schema = schema.omit(
104
+ // biome-ignore lint/suspicious/noExplicitAny: Zod API
105
+ Object.fromEntries(omitArray.map((k) => [k, true]))
106
+ );
107
+ }
108
+ if (opts.partial === true) {
109
+ schema = schema.partial();
110
+ }
111
+ return schema;
112
+ }
113
+
114
+ // src/openapi/generate.ts
115
+ function toOpenAPIComponent(fields, opts) {
116
+ return {
117
+ create: toOpenAPISchema(fields, "create", opts),
118
+ update: toOpenAPISchema(fields, "update", opts),
119
+ output: toOpenAPISchema(fields, "output", opts)
120
+ };
121
+ }
122
+ function toOpenAPISchema(fields, usage, opts) {
123
+ const policyUsage = usage === "create" ? "input-create" : usage === "update" ? "input-update" : "output";
124
+ const merged = derivePolicyOptions(fields, policyUsage, opts?.opts);
125
+ const schema = applySchemaOptions(buildBaseObject(fields), merged);
126
+ const shape = getZodObjectShape(schema);
127
+ const properties = {};
128
+ const required = [];
129
+ for (const [key, zodSchema] of Object.entries(shape)) {
130
+ const field = fields[key];
131
+ if (field === void 0) continue;
132
+ properties[key] = fieldToOpenAPISchema(field);
133
+ if (field.modifiers.policy === "writeOnly" && (usage === "create" || usage === "update")) {
134
+ properties[key].writeOnly = true;
135
+ }
136
+ if (field.modifiers.policy === "readOnly" && usage === "output") {
137
+ properties[key].readOnly = true;
138
+ }
139
+ if (usage !== "update" && isRequiredZodSchema(zodSchema)) {
140
+ required.push(key);
141
+ }
142
+ }
143
+ return {
144
+ type: "object",
145
+ properties,
146
+ ...required.length > 0 ? { required } : {}
147
+ };
148
+ }
149
+ function fieldToOpenAPISchema(field) {
150
+ const schema = field.kind === "json" ? zodToOpenAPISchema(field.zodBase) : field.kind === "string" ? { type: "string" } : field.kind === "uuid" ? { type: "string", format: "uuid" } : field.kind === "number" ? { type: "number" } : field.kind === "integer" ? { type: "integer" } : field.kind === "boolean" ? { type: "boolean" } : field.kind === "timestamp" ? { type: "string", format: "date-time" } : {};
151
+ if (field.modifiers.format === "email") {
152
+ schema.format = "email";
153
+ }
154
+ if (field.modifiers.min !== void 0) {
155
+ if (field.kind === "string" || field.kind === "uuid") {
156
+ schema.minLength = field.modifiers.min;
157
+ } else {
158
+ schema.minimum = field.modifiers.min;
159
+ }
160
+ }
161
+ if (field.modifiers.max !== void 0) {
162
+ if (field.kind === "string" || field.kind === "uuid") {
163
+ schema.maxLength = field.modifiers.max;
164
+ } else {
165
+ schema.maximum = field.modifiers.max;
166
+ }
167
+ }
168
+ return schema;
169
+ }
170
+ function zodToOpenAPISchema(schema) {
171
+ const def = getZodDef(schema);
172
+ if (def === void 0) return {};
173
+ const typeName = getZodTypeName(def);
174
+ if (typeName === "ZodOptional" || typeName === "optional") {
175
+ return zodToOpenAPISchema(def.innerType);
176
+ }
177
+ if (typeName === "ZodDefault" || typeName === "default") {
178
+ return zodToOpenAPISchema(def.innerType);
179
+ }
180
+ if (typeName === "ZodNullable" || typeName === "nullable") {
181
+ return { ...zodToOpenAPISchema(def.innerType), nullable: true };
182
+ }
183
+ if (typeName === "ZodObject" || typeName === "object") {
184
+ const shape = getZodObjectShape(schema);
185
+ const properties = {};
186
+ const required = [];
187
+ for (const [key, childSchema] of Object.entries(shape)) {
188
+ properties[key] = zodToOpenAPISchema(childSchema);
189
+ if (isRequiredZodSchema(childSchema)) {
190
+ required.push(key);
191
+ }
192
+ }
193
+ return {
194
+ type: "object",
195
+ properties,
196
+ ...required.length > 0 ? { required } : {}
197
+ };
198
+ }
199
+ if (typeName === "ZodString" || typeName === "string") {
200
+ return applyStringChecks({ type: "string" }, def);
201
+ }
202
+ if (typeName === "ZodNumber" || typeName === "number") {
203
+ return applyNumberChecks({ type: hasIntegerCheck(def) ? "integer" : "number" }, def);
204
+ }
205
+ if (typeName === "ZodBoolean" || typeName === "boolean") {
206
+ return { type: "boolean" };
207
+ }
208
+ if (typeName === "ZodArray" || typeName === "array") {
209
+ const itemSchema = def.element ?? def.type;
210
+ return {
211
+ type: "array",
212
+ items: zodToOpenAPISchema(itemSchema)
213
+ };
214
+ }
215
+ if (typeName === "ZodRecord" || typeName === "record") {
216
+ return {
217
+ type: "object",
218
+ additionalProperties: zodToOpenAPISchema(def.valueType)
219
+ };
220
+ }
221
+ return {};
222
+ }
223
+ function getZodObjectShape(schema) {
224
+ if (typeof schema === "object" && schema !== null && "shape" in schema && typeof schema.shape === "object" && schema.shape !== null) {
225
+ return schema.shape;
226
+ }
227
+ if (typeof schema === "object" && schema !== null && "_def" in schema && typeof schema._def === "object" && schema._def !== null && "shape" in schema._def) {
228
+ const shape = schema._def.shape;
229
+ return typeof shape === "function" ? shape() : shape;
230
+ }
231
+ return {};
232
+ }
233
+ function isRequiredZodSchema(schema) {
234
+ const def = getZodDef(schema);
235
+ if (def === void 0) {
236
+ return true;
237
+ }
238
+ const typeName = getZodTypeName(def);
239
+ return typeName !== "ZodOptional" && typeName !== "optional" && typeName !== "ZodDefault" && typeName !== "default";
240
+ }
241
+ function getZodDef(schema) {
242
+ if (typeof schema === "object" && schema !== null && "_def" in schema && typeof schema._def === "object" && schema._def !== null) {
243
+ return schema._def;
244
+ }
245
+ return void 0;
246
+ }
247
+ function getZodTypeName(def) {
248
+ return typeof def.typeName === "string" ? def.typeName : typeof def.type === "string" ? def.type : void 0;
249
+ }
250
+ function applyStringChecks(schema, def) {
251
+ for (const check of getZodChecks(def)) {
252
+ const checkDef = getZodCheckDef(check);
253
+ if (checkDef.kind === "email" || checkDef.format === "email") {
254
+ schema.format = "email";
255
+ }
256
+ if (checkDef.kind === "min" && typeof checkDef.value === "number") {
257
+ schema.minLength = checkDef.value;
258
+ }
259
+ if (checkDef.kind === "max" && typeof checkDef.value === "number") {
260
+ schema.maxLength = checkDef.value;
261
+ }
262
+ if (checkDef.check === "min_length" && typeof checkDef.minimum === "number") {
263
+ schema.minLength = checkDef.minimum;
264
+ }
265
+ if (checkDef.check === "max_length" && typeof checkDef.maximum === "number") {
266
+ schema.maxLength = checkDef.maximum;
267
+ }
268
+ }
269
+ return schema;
270
+ }
271
+ function applyNumberChecks(schema, def) {
272
+ for (const check of getZodChecks(def)) {
273
+ const checkDef = getZodCheckDef(check);
274
+ if (checkDef.kind === "min" && typeof checkDef.value === "number") {
275
+ schema.minimum = checkDef.value;
276
+ }
277
+ if (checkDef.kind === "max" && typeof checkDef.value === "number") {
278
+ schema.maximum = checkDef.value;
279
+ }
280
+ if ((checkDef.check === "greater_than" || checkDef.check === "number_format") && typeof checkDef.minimum === "number") {
281
+ schema.minimum = checkDef.minimum;
282
+ }
283
+ if ((checkDef.check === "less_than" || checkDef.check === "number_format") && typeof checkDef.maximum === "number") {
284
+ schema.maximum = checkDef.maximum;
285
+ }
286
+ }
287
+ return schema;
288
+ }
289
+ function hasIntegerCheck(def) {
290
+ return getZodChecks(def).some((check) => {
291
+ const checkDef = getZodCheckDef(check);
292
+ return checkDef.kind === "int" || checkDef.format === "safeint" || checkDef.format === "int32" || checkDef.format === "int64";
293
+ });
294
+ }
295
+ function getZodChecks(def) {
296
+ return Array.isArray(def.checks) ? def.checks : [];
297
+ }
298
+ function getZodCheckDef(check) {
299
+ if (typeof check !== "object" || check === null) {
300
+ return {};
301
+ }
302
+ if ("_zod" in check && typeof check._zod === "object" && check._zod !== null && "def" in check._zod && typeof check._zod.def === "object" && check._zod.def !== null) {
303
+ return check._zod.def;
304
+ }
305
+ return check;
306
+ }
17
307
  var MAX_LIMIT = 100;
18
308
  function guardLimit(limit) {
19
309
  if (!Number.isInteger(limit)) {
@@ -39,9 +329,9 @@ function guardOffset(offset) {
39
329
  return n;
40
330
  }
41
331
  function findPrimaryKey(fields) {
332
+ let pkKey;
333
+ let pkField;
42
334
  let pkCount = 0;
43
- let pkKey = null;
44
- let pkField = null;
45
335
  for (const [key, field] of Object.entries(fields)) {
46
336
  if (field.modifiers.primary === true) {
47
337
  pkCount++;
@@ -57,6 +347,9 @@ function findPrimaryKey(fields) {
57
347
  if (pkCount > 1) {
58
348
  throw new HTTPException(500, { message: "model has multiple primary keys" });
59
349
  }
350
+ if (pkKey === void 0 || pkField === void 0) {
351
+ return null;
352
+ }
60
353
  return [pkKey, pkField];
61
354
  }
62
355
  function buildWhereClause(table, fields, where) {
@@ -79,7 +372,7 @@ function buildWhereClause(table, fields, where) {
79
372
  }
80
373
  return and(...conditions);
81
374
  }
82
- async function findManyImpl(adapter, table, fields, options) {
375
+ async function findManyImpl(adapter, table, _fields, options) {
83
376
  const limit = guardLimit(options.limit);
84
377
  const offset = options.offset !== void 0 ? guardOffset(options.offset) : 0;
85
378
  let query = adapter.drizzle.select().from(table).limit(limit).offset(offset);
@@ -166,37 +459,6 @@ async function deleteImpl(adapter, table, fields, idOrWhere) {
166
459
  const result = await adapter.drizzle.delete(table).where(whereClause).returning();
167
460
  return { deleted: result.length };
168
461
  }
169
- function buildBaseObject(fields) {
170
- const shape = {};
171
- for (const [key, field] of Object.entries(fields)) {
172
- shape[key] = field.zodBase;
173
- }
174
- return z.object(shape);
175
- }
176
- function applySchemaOptions(baseSchema, opts) {
177
- if (!opts) {
178
- return baseSchema;
179
- }
180
- let schema = baseSchema;
181
- if (opts.pick !== void 0) {
182
- const pickArray = Array.from(opts.pick);
183
- schema = schema.pick(
184
- // biome-ignore lint/suspicious/noExplicitAny: Zod API
185
- Object.fromEntries(pickArray.map((k) => [k, true]))
186
- );
187
- }
188
- if (opts.omit !== void 0) {
189
- const omitArray = Array.from(opts.omit);
190
- schema = schema.omit(
191
- // biome-ignore lint/suspicious/noExplicitAny: Zod API
192
- Object.fromEntries(omitArray.map((k) => [k, true]))
193
- );
194
- }
195
- if (opts.partial === true) {
196
- schema = schema.partial();
197
- }
198
- return schema;
199
- }
200
462
  function buildTable(tableName, fields) {
201
463
  const columns = {};
202
464
  for (const [key, field] of Object.entries(fields)) {
@@ -208,19 +470,49 @@ function buildTable(tableName, fields) {
208
470
  // src/model/define.ts
209
471
  function defineModel(tableName, fields) {
210
472
  const table = buildTable(tableName, fields);
473
+ const accessor = buildFieldAccessor(fields);
211
474
  return {
212
475
  fields,
213
476
  tableName,
214
477
  table,
215
478
  schema(opts) {
216
479
  const baseSchema = buildBaseObject(fields);
217
- const schema = applySchemaOptions(baseSchema, opts);
480
+ const schema = applySchemaOptions(baseSchema, opts, accessor);
218
481
  return schema;
219
482
  },
220
- validator(target, opts, hook) {
221
- const schema = this.schema(opts);
483
+ validator(target, optsOrPreset, hook) {
484
+ let schema;
485
+ if (optsOrPreset === "create" || optsOrPreset === "update") {
486
+ schema = this.inputSchema(optsOrPreset);
487
+ } else {
488
+ schema = this.schema(optsOrPreset);
489
+ }
222
490
  return zValidator(target, schema, hook);
223
491
  },
492
+ inputSchema(usage, opts) {
493
+ const merged = derivePolicyOptions(
494
+ fields,
495
+ usage === "create" ? "input-create" : "input-update",
496
+ opts,
497
+ accessor
498
+ );
499
+ const baseSchema = buildBaseObject(fields);
500
+ return applySchemaOptions(baseSchema, merged);
501
+ },
502
+ outputSchema(opts) {
503
+ const merged = derivePolicyOptions(fields, "output", opts, accessor);
504
+ const baseSchema = buildBaseObject(fields);
505
+ return applySchemaOptions(baseSchema, merged);
506
+ },
507
+ toResponse(row) {
508
+ return this.outputSchema().parse(row);
509
+ },
510
+ toOpenAPIComponent() {
511
+ return toOpenAPIComponent(fields);
512
+ },
513
+ toOpenAPISchema(usage) {
514
+ return toOpenAPISchema(fields, usage);
515
+ },
224
516
  findMany(adapter, options) {
225
517
  return findManyImpl(adapter, table, fields, options);
226
518
  },
@@ -252,7 +544,14 @@ function nanoka(adapter) {
252
544
  tableName: inner.tableName,
253
545
  table: inner.table,
254
546
  schema: (opts) => inner.schema(opts),
255
- validator: (target, opts, hook) => inner.validator(target, opts, hook),
547
+ validator: (...args) => {
548
+ return inner.validator(...args);
549
+ },
550
+ inputSchema: (usage, opts) => inner.inputSchema(usage, opts),
551
+ outputSchema: (opts) => inner.outputSchema(opts),
552
+ toResponse: (row) => inner.toResponse(row),
553
+ toOpenAPIComponent: () => inner.toOpenAPIComponent(),
554
+ toOpenAPISchema: (usage) => inner.toOpenAPISchema(usage),
256
555
  findMany: (options) => inner.findMany(adapter, options),
257
556
  findOne: (idOrWhere) => inner.findOne(adapter, idOrWhere),
258
557
  create: (data) => inner.create(adapter, data),