@nanokajs/core 0.1.4 → 1.0.1
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/CHANGELOG.md +72 -0
- package/README.md +38 -20
- package/dist/{chunk-F5OBRXXY.js → chunk-UYXRKFAR.js} +108 -18
- package/dist/chunk-UYXRKFAR.js.map +1 -0
- package/dist/cli.js +6 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +1 -1
- package/dist/{factories-CDsk1Hdt.d.ts → factories-Cmvx0495.d.ts} +89 -22
- package/dist/index.d.ts +277 -50
- package/dist/index.js +426 -40
- package/dist/index.js.map +1 -1
- package/dist/turso.d.ts +15 -0
- package/dist/turso.js +14 -0
- package/dist/turso.js.map +1 -0
- package/dist/types-D2QDVyks.d.ts +28 -0
- package/package.json +17 -5
- package/dist/chunk-F5OBRXXY.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { t } from './chunk-
|
|
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,305 @@ 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, opts?.strict);
|
|
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, strict = false) {
|
|
150
|
+
const schema = field.kind === "json" ? zodToOpenAPISchema(field.zodBase, strict) : 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, strict = false) {
|
|
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, strict);
|
|
176
|
+
}
|
|
177
|
+
if (typeName === "ZodDefault" || typeName === "default") {
|
|
178
|
+
return zodToOpenAPISchema(def.innerType, strict);
|
|
179
|
+
}
|
|
180
|
+
if (typeName === "ZodNullable" || typeName === "nullable") {
|
|
181
|
+
return { ...zodToOpenAPISchema(def.innerType, strict), 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, strict);
|
|
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, strict)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (typeName === "ZodRecord" || typeName === "record") {
|
|
216
|
+
return {
|
|
217
|
+
type: "object",
|
|
218
|
+
additionalProperties: zodToOpenAPISchema(def.valueType, strict)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (typeName === "ZodUnknown" || typeName === "unknown") {
|
|
222
|
+
return {};
|
|
223
|
+
}
|
|
224
|
+
if (strict) {
|
|
225
|
+
throw new Error(`Unsupported Zod type for strict OpenAPI generation: ${typeName ?? "unknown"}`);
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
"x-nanoka-zod-unsupported": true,
|
|
229
|
+
...typeName ? { "x-nanoka-zod-type": typeName } : {}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function getZodObjectShape(schema) {
|
|
233
|
+
if (typeof schema === "object" && schema !== null && "shape" in schema && typeof schema.shape === "object" && schema.shape !== null) {
|
|
234
|
+
return schema.shape;
|
|
235
|
+
}
|
|
236
|
+
if (typeof schema === "object" && schema !== null && "_def" in schema && typeof schema._def === "object" && schema._def !== null && "shape" in schema._def) {
|
|
237
|
+
const shape = schema._def.shape;
|
|
238
|
+
return typeof shape === "function" ? shape() : shape;
|
|
239
|
+
}
|
|
240
|
+
return {};
|
|
241
|
+
}
|
|
242
|
+
function isRequiredZodSchema(schema) {
|
|
243
|
+
const def = getZodDef(schema);
|
|
244
|
+
if (def === void 0) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
const typeName = getZodTypeName(def);
|
|
248
|
+
return typeName !== "ZodOptional" && typeName !== "optional" && typeName !== "ZodDefault" && typeName !== "default";
|
|
249
|
+
}
|
|
250
|
+
function getZodDef(schema) {
|
|
251
|
+
if (typeof schema === "object" && schema !== null && "_def" in schema && typeof schema._def === "object" && schema._def !== null) {
|
|
252
|
+
return schema._def;
|
|
253
|
+
}
|
|
254
|
+
return void 0;
|
|
255
|
+
}
|
|
256
|
+
function getZodTypeName(def) {
|
|
257
|
+
return typeof def.typeName === "string" ? def.typeName : typeof def.type === "string" ? def.type : void 0;
|
|
258
|
+
}
|
|
259
|
+
function applyStringChecks(schema, def) {
|
|
260
|
+
for (const check of getZodChecks(def)) {
|
|
261
|
+
const checkDef = getZodCheckDef(check);
|
|
262
|
+
if (checkDef.kind === "email" || checkDef.format === "email") {
|
|
263
|
+
schema.format = "email";
|
|
264
|
+
}
|
|
265
|
+
if (checkDef.kind === "min" && typeof checkDef.value === "number") {
|
|
266
|
+
schema.minLength = checkDef.value;
|
|
267
|
+
}
|
|
268
|
+
if (checkDef.kind === "max" && typeof checkDef.value === "number") {
|
|
269
|
+
schema.maxLength = checkDef.value;
|
|
270
|
+
}
|
|
271
|
+
if (checkDef.check === "min_length" && typeof checkDef.minimum === "number") {
|
|
272
|
+
schema.minLength = checkDef.minimum;
|
|
273
|
+
}
|
|
274
|
+
if (checkDef.check === "max_length" && typeof checkDef.maximum === "number") {
|
|
275
|
+
schema.maxLength = checkDef.maximum;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return schema;
|
|
279
|
+
}
|
|
280
|
+
function applyNumberChecks(schema, def) {
|
|
281
|
+
for (const check of getZodChecks(def)) {
|
|
282
|
+
const checkDef = getZodCheckDef(check);
|
|
283
|
+
if (checkDef.kind === "min" && typeof checkDef.value === "number") {
|
|
284
|
+
schema.minimum = checkDef.value;
|
|
285
|
+
}
|
|
286
|
+
if (checkDef.kind === "max" && typeof checkDef.value === "number") {
|
|
287
|
+
schema.maximum = checkDef.value;
|
|
288
|
+
}
|
|
289
|
+
if ((checkDef.check === "greater_than" || checkDef.check === "number_format") && typeof checkDef.minimum === "number") {
|
|
290
|
+
schema.minimum = checkDef.minimum;
|
|
291
|
+
}
|
|
292
|
+
if ((checkDef.check === "less_than" || checkDef.check === "number_format") && typeof checkDef.maximum === "number") {
|
|
293
|
+
schema.maximum = checkDef.maximum;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return schema;
|
|
297
|
+
}
|
|
298
|
+
function hasIntegerCheck(def) {
|
|
299
|
+
return getZodChecks(def).some((check) => {
|
|
300
|
+
const checkDef = getZodCheckDef(check);
|
|
301
|
+
return checkDef.kind === "int" || checkDef.format === "safeint" || checkDef.format === "int32" || checkDef.format === "int64";
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function getZodChecks(def) {
|
|
305
|
+
return Array.isArray(def.checks) ? def.checks : [];
|
|
306
|
+
}
|
|
307
|
+
function getZodCheckDef(check) {
|
|
308
|
+
if (typeof check !== "object" || check === null) {
|
|
309
|
+
return {};
|
|
310
|
+
}
|
|
311
|
+
if ("_zod" in check && typeof check._zod === "object" && check._zod !== null && "def" in check._zod && typeof check._zod.def === "object" && check._zod.def !== null) {
|
|
312
|
+
return check._zod.def;
|
|
313
|
+
}
|
|
314
|
+
return check;
|
|
315
|
+
}
|
|
17
316
|
var MAX_LIMIT = 100;
|
|
18
317
|
function guardLimit(limit) {
|
|
19
318
|
if (!Number.isInteger(limit)) {
|
|
@@ -39,9 +338,9 @@ function guardOffset(offset) {
|
|
|
39
338
|
return n;
|
|
40
339
|
}
|
|
41
340
|
function findPrimaryKey(fields) {
|
|
341
|
+
let pkKey;
|
|
342
|
+
let pkField;
|
|
42
343
|
let pkCount = 0;
|
|
43
|
-
let pkKey = null;
|
|
44
|
-
let pkField = null;
|
|
45
344
|
for (const [key, field] of Object.entries(fields)) {
|
|
46
345
|
if (field.modifiers.primary === true) {
|
|
47
346
|
pkCount++;
|
|
@@ -57,6 +356,9 @@ function findPrimaryKey(fields) {
|
|
|
57
356
|
if (pkCount > 1) {
|
|
58
357
|
throw new HTTPException(500, { message: "model has multiple primary keys" });
|
|
59
358
|
}
|
|
359
|
+
if (pkKey === void 0 || pkField === void 0) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
60
362
|
return [pkKey, pkField];
|
|
61
363
|
}
|
|
62
364
|
function buildWhereClause(table, fields, where) {
|
|
@@ -166,37 +468,6 @@ async function deleteImpl(adapter, table, fields, idOrWhere) {
|
|
|
166
468
|
const result = await adapter.drizzle.delete(table).where(whereClause).returning();
|
|
167
469
|
return { deleted: result.length };
|
|
168
470
|
}
|
|
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
471
|
function buildTable(tableName, fields) {
|
|
201
472
|
const columns = {};
|
|
202
473
|
for (const [key, field] of Object.entries(fields)) {
|
|
@@ -208,19 +479,49 @@ function buildTable(tableName, fields) {
|
|
|
208
479
|
// src/model/define.ts
|
|
209
480
|
function defineModel(tableName, fields) {
|
|
210
481
|
const table = buildTable(tableName, fields);
|
|
482
|
+
const accessor = buildFieldAccessor(fields);
|
|
211
483
|
return {
|
|
212
484
|
fields,
|
|
213
485
|
tableName,
|
|
214
486
|
table,
|
|
215
487
|
schema(opts) {
|
|
216
488
|
const baseSchema = buildBaseObject(fields);
|
|
217
|
-
const schema = applySchemaOptions(baseSchema, opts);
|
|
489
|
+
const schema = applySchemaOptions(baseSchema, opts, accessor);
|
|
218
490
|
return schema;
|
|
219
491
|
},
|
|
220
|
-
validator(target,
|
|
221
|
-
|
|
492
|
+
validator(target, optsOrPreset, hook) {
|
|
493
|
+
let schema;
|
|
494
|
+
if (optsOrPreset === "create" || optsOrPreset === "update") {
|
|
495
|
+
schema = this.inputSchema(optsOrPreset);
|
|
496
|
+
} else {
|
|
497
|
+
schema = this.schema(optsOrPreset);
|
|
498
|
+
}
|
|
222
499
|
return zValidator(target, schema, hook);
|
|
223
500
|
},
|
|
501
|
+
inputSchema(usage, opts) {
|
|
502
|
+
const merged = derivePolicyOptions(
|
|
503
|
+
fields,
|
|
504
|
+
usage === "create" ? "input-create" : "input-update",
|
|
505
|
+
opts,
|
|
506
|
+
accessor
|
|
507
|
+
);
|
|
508
|
+
const baseSchema = buildBaseObject(fields);
|
|
509
|
+
return applySchemaOptions(baseSchema, merged);
|
|
510
|
+
},
|
|
511
|
+
outputSchema(opts) {
|
|
512
|
+
const merged = derivePolicyOptions(fields, "output", opts, accessor);
|
|
513
|
+
const baseSchema = buildBaseObject(fields);
|
|
514
|
+
return applySchemaOptions(baseSchema, merged);
|
|
515
|
+
},
|
|
516
|
+
toResponse(row) {
|
|
517
|
+
return this.outputSchema().parse(row);
|
|
518
|
+
},
|
|
519
|
+
toOpenAPIComponent() {
|
|
520
|
+
return toOpenAPIComponent(fields);
|
|
521
|
+
},
|
|
522
|
+
toOpenAPISchema(usage) {
|
|
523
|
+
return toOpenAPISchema(fields, usage);
|
|
524
|
+
},
|
|
224
525
|
findMany(adapter, options) {
|
|
225
526
|
return findManyImpl(adapter, table, fields, options);
|
|
226
527
|
},
|
|
@@ -238,13 +539,91 @@ function defineModel(tableName, fields) {
|
|
|
238
539
|
}
|
|
239
540
|
};
|
|
240
541
|
}
|
|
542
|
+
|
|
543
|
+
// src/openapi/route.ts
|
|
544
|
+
function honoPathToOpenAPI(path) {
|
|
545
|
+
return path.replace(/:([^/]+)/g, "{$1}");
|
|
546
|
+
}
|
|
547
|
+
function buildOpenAPIDocument(routes, options) {
|
|
548
|
+
const paths = {};
|
|
549
|
+
for (const route of routes) {
|
|
550
|
+
const openAPIPath = honoPathToOpenAPI(route.path);
|
|
551
|
+
if (!paths[openAPIPath]) {
|
|
552
|
+
paths[openAPIPath] = {};
|
|
553
|
+
}
|
|
554
|
+
const operation = {};
|
|
555
|
+
if (route.operationId) operation.operationId = route.operationId;
|
|
556
|
+
if (route.summary) operation.summary = route.summary;
|
|
557
|
+
if (route.description) operation.description = route.description;
|
|
558
|
+
if (route.tags) operation.tags = route.tags;
|
|
559
|
+
const parameters = [];
|
|
560
|
+
const pathParams = [...route.path.matchAll(/:([^/]+)/g)].map((m) => m[1]);
|
|
561
|
+
for (const param of pathParams) {
|
|
562
|
+
const alreadyDefined = route.params?.some((p) => p.name === param && p.in === "path");
|
|
563
|
+
if (!alreadyDefined) {
|
|
564
|
+
parameters.push({ name: param, in: "path", required: true, schema: { type: "string" } });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (route.params) {
|
|
568
|
+
parameters.push(...route.params);
|
|
569
|
+
}
|
|
570
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
571
|
+
if (route.requestBody) operation.requestBody = route.requestBody;
|
|
572
|
+
operation.responses = route.responses;
|
|
573
|
+
paths[openAPIPath][route.method] = operation;
|
|
574
|
+
}
|
|
575
|
+
const doc = {
|
|
576
|
+
openapi: "3.1.0",
|
|
577
|
+
info: options.info,
|
|
578
|
+
...options.servers ? { servers: options.servers } : {},
|
|
579
|
+
paths
|
|
580
|
+
};
|
|
581
|
+
return doc;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/openapi/swagger-ui.ts
|
|
585
|
+
function escapeHtml(str) {
|
|
586
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
587
|
+
}
|
|
588
|
+
function swaggerUI(options) {
|
|
589
|
+
const title = escapeHtml(options.title ?? "API Documentation");
|
|
590
|
+
const html = `<!DOCTYPE html>
|
|
591
|
+
<html lang="en">
|
|
592
|
+
<head>
|
|
593
|
+
<meta charset="UTF-8" />
|
|
594
|
+
<title>${title}</title>
|
|
595
|
+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
|
|
596
|
+
</head>
|
|
597
|
+
<body>
|
|
598
|
+
<div id="swagger-ui"></div>
|
|
599
|
+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
|
600
|
+
<script>
|
|
601
|
+
SwaggerUIBundle({
|
|
602
|
+
url: ${JSON.stringify(options.url)},
|
|
603
|
+
dom_id: '#swagger-ui',
|
|
604
|
+
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
|
|
605
|
+
layout: 'BaseLayout',
|
|
606
|
+
})
|
|
607
|
+
</script>
|
|
608
|
+
</body>
|
|
609
|
+
</html>`;
|
|
610
|
+
return async (c) => {
|
|
611
|
+
return c.html(html);
|
|
612
|
+
};
|
|
613
|
+
}
|
|
241
614
|
function nanoka(adapter) {
|
|
242
615
|
const app = new Hono();
|
|
616
|
+
const openapiRoutes = [];
|
|
243
617
|
Object.defineProperty(app, "db", {
|
|
244
618
|
get: () => adapter.drizzle,
|
|
245
619
|
enumerable: true
|
|
246
620
|
});
|
|
247
621
|
app.batch = ((queries) => adapter.batch(queries));
|
|
622
|
+
app.openapi = (metadata) => {
|
|
623
|
+
openapiRoutes.push(metadata);
|
|
624
|
+
return app;
|
|
625
|
+
};
|
|
626
|
+
app.generateOpenAPISpec = (options) => buildOpenAPIDocument(openapiRoutes, options);
|
|
248
627
|
app.model = function model(name, fields) {
|
|
249
628
|
const inner = defineModel(name, fields);
|
|
250
629
|
return {
|
|
@@ -252,7 +631,14 @@ function nanoka(adapter) {
|
|
|
252
631
|
tableName: inner.tableName,
|
|
253
632
|
table: inner.table,
|
|
254
633
|
schema: (opts) => inner.schema(opts),
|
|
255
|
-
validator: (
|
|
634
|
+
validator: (...args) => {
|
|
635
|
+
return inner.validator(...args);
|
|
636
|
+
},
|
|
637
|
+
inputSchema: (usage, opts) => inner.inputSchema(usage, opts),
|
|
638
|
+
outputSchema: (opts) => inner.outputSchema(opts),
|
|
639
|
+
toResponse: (row) => inner.toResponse(row),
|
|
640
|
+
toOpenAPIComponent: () => inner.toOpenAPIComponent(),
|
|
641
|
+
toOpenAPISchema: (usage) => inner.toOpenAPISchema(usage),
|
|
256
642
|
findMany: (options) => inner.findMany(adapter, options),
|
|
257
643
|
findOne: (idOrWhere) => inner.findOne(adapter, idOrWhere),
|
|
258
644
|
create: (data) => inner.create(adapter, data),
|
|
@@ -263,6 +649,6 @@ function nanoka(adapter) {
|
|
|
263
649
|
return app;
|
|
264
650
|
}
|
|
265
651
|
|
|
266
|
-
export { d1Adapter, defineModel, nanoka };
|
|
652
|
+
export { d1Adapter, defineModel, nanoka, swaggerUI };
|
|
267
653
|
//# sourceMappingURL=index.js.map
|
|
268
654
|
//# sourceMappingURL=index.js.map
|