@drzl/cli 4.13.0 → 4.14.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/{chunk-ZYJ4I2I7.js → chunk-HGD5CBM5.js} +11 -1
- package/dist/chunk-HGD5CBM5.js.map +1 -0
- package/dist/cli.cjs +334 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +10 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +10 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +6 -2
- package/dist/config.d.ts +6 -2
- package/dist/config.js +1 -1
- package/dist/dist-EVB4YOPK.js +313 -0
- package/dist/dist-EVB4YOPK.js.map +1 -0
- package/package.json +10 -8
- package/dist/chunk-ZYJ4I2I7.js.map +0 -1
|
@@ -73,6 +73,14 @@ var GeneratorSchema = z.object({
|
|
|
73
73
|
typedColumns: z.boolean().optional(),
|
|
74
74
|
// Reproduce literal column defaults in the insert schema, so parsing fills them in.
|
|
75
75
|
applyDefaults: z.boolean().optional(),
|
|
76
|
+
/**
|
|
77
|
+
* Emit `findDuplicate<Table>` beside the schemas: the rows in a batch that collide with an
|
|
78
|
+
* earlier row on a unique constraint.
|
|
79
|
+
*
|
|
80
|
+
* Uniqueness is the one constraint a per-row validator structurally cannot see, since it is a
|
|
81
|
+
* fact about the table rather than the row. This checks the half that needs no database.
|
|
82
|
+
*/
|
|
83
|
+
duplicateFinder: z.boolean().optional(),
|
|
76
84
|
naming: NamingSchema.optional(),
|
|
77
85
|
outputHeader: z.object({
|
|
78
86
|
enabled: z.boolean().default(true).optional(),
|
|
@@ -93,6 +101,8 @@ var GeneratorSchema = z.object({
|
|
|
93
101
|
* validates and then accepts the values the constraints exist to reject.
|
|
94
102
|
*/
|
|
95
103
|
target: z.enum(["draft-2020-12", "openapi-3.1", "openapi-3.0"]).optional(),
|
|
104
|
+
/** Also emit `components.ts` for the `json-schema` generator, ready for an OpenAPI document. */
|
|
105
|
+
components: z.boolean().optional(),
|
|
96
106
|
// service generator specific options
|
|
97
107
|
path: z.string().optional(),
|
|
98
108
|
dataAccess: z.enum(["stub", "drizzle"]).default("stub").optional(),
|
|
@@ -359,4 +369,4 @@ export {
|
|
|
359
369
|
filterTables,
|
|
360
370
|
computeWatchTargets
|
|
361
371
|
};
|
|
362
|
-
//# sourceMappingURL=chunk-
|
|
372
|
+
//# sourceMappingURL=chunk-HGD5CBM5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import type { AffixOptions } from '@drzl/validation-core';\nimport {\n AFFIX_PROBE_TABLE,\n DEFAULT_IMPORT_EXTENSION,\n IMPORT_EXTENSIONS,\n NAME_MODES,\n resolveAffix,\n schemaName,\n validateAffix,\n} from '@drzl/validation-core';\nimport * as fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\nexport const NamingSchema = z\n .object({\n routerSuffix: z.string().default('Router'),\n procedureCase: z.enum(['camel', 'kebab', 'snake']).default('camel'),\n })\n .partial();\n\n/** One affix for every mode, or a per-mode map. Keys match drzl's internal mode names. */\nconst AffixValueSchema = z.union(\n [\n z.string(),\n z\n .object({\n insert: z.string().optional(),\n update: z.string().optional(),\n select: z.string().optional(),\n })\n .strict(),\n ],\n {\n error:\n 'Expected a string to use for every mode, or an object with any of the keys \"insert\", ' +\n '\"update\" and \"select\". Those keys are lowercase, matching the mode names drzl uses ' +\n 'everywhere else.',\n }\n);\n\nconst AffixPartSchema = z\n .object({\n prefix: AffixValueSchema.optional(),\n suffix: AffixValueSchema.optional(),\n })\n .strict();\n\nexport const AffixSchema = z\n .object({\n /**\n * `preserve` (default) keeps today's output: the Drizzle export name goes into the\n * identifier verbatim, so `export const users` yields `InsertusersSchema`. `pascal`\n * upper-camels it first, yielding `InsertUsersSchema`.\n */\n tableCase: z.enum(['preserve', 'pascal']).optional(),\n schema: AffixPartSchema.optional(),\n type: AffixPartSchema.optional(),\n })\n .strict();\n\n/**\n * How every relative specifier drzl invents spells its extension.\n *\n * The generated files land in the consumer's own source tree, so the consumer's\n * `moduleResolution` decides which forms resolve. `js` is the only one that resolves under\n * all of `bundler`, `node10`, `node16` and `nodenext` with no compiler flag, so it is the\n * default. See the `ImportExtension` docs in `@drzl/validation-core` for the measured grid.\n */\nexport const ImportExtensionSchema = z.enum(IMPORT_EXTENSIONS);\n\nexport const GeneratorSchema = z.object({\n kind: z.enum(['orpc', 'service', 'zod', 'valibot', 'arktype', 'typebox', 'json-schema']),\n /**\n * Overrides the top-level `importExtension` for this generator alone, for a project whose\n * generated directories are compiled by different tsconfigs.\n */\n importExtension: ImportExtensionSchema.optional(),\n template: z.string().optional(),\n includeRelations: z.boolean().optional(),\n /**\n * Type `json` and `jsonb` columns from the schema rather than leaving them wide.\n *\n * `.$type<T>()` is a compile-time cast, so no runtime-derived validator can see it and\n * `drizzle-orm/zod` types every json column as its generic `Json`. A generator can reference\n * `typeof <table>.$inferSelect['<column>']` instead, which is the declared type resolved by\n * TypeScript itself, so generics, unions and imported interfaces all work.\n *\n * Off by default because it makes the generated file import your schema module, as a\n * type-only import that disappears at build time.\n */\n // What a date column accepts. Documented on the zod generator and, until now, accepted by the\n // config parser and then dropped on the floor: the generators default it to 'input' themselves,\n // so setting it here changed nothing.\n coerceDates: z.enum(['input', 'all', 'none']).optional(),\n typedJson: z.boolean().optional(),\n // The wider form: every column's static type comes from Drizzle, not just the untyped ones.\n typedColumns: z.boolean().optional(),\n // Reproduce literal column defaults in the insert schema, so parsing fills them in.\n applyDefaults: z.boolean().optional(),\n /**\n * Emit `findDuplicate<Table>` beside the schemas: the rows in a batch that collide with an\n * earlier row on a unique constraint.\n *\n * Uniqueness is the one constraint a per-row validator structurally cannot see, since it is a\n * fact about the table rather than the row. This checks the half that needs no database.\n */\n duplicateFinder: z.boolean().optional(),\n naming: NamingSchema.optional(),\n outputHeader: z\n .object({\n enabled: z.boolean().default(true).optional(),\n text: z.string().optional(),\n })\n .optional(),\n format: z\n .object({\n enabled: z.boolean().default(true).optional(),\n engine: z.enum(['auto', 'prettier', 'biome']).default('auto').optional(),\n configPath: z.string().optional(),\n })\n .optional(),\n /**\n * Which spelling of JSON Schema the `json-schema` generator emits.\n *\n * OpenAPI 3.0 is not an older superset of the 2020-12 draft, it is a different dialect: a\n * nullable type is `nullable: true` rather than a type array, and an exclusive bound is a\n * boolean flag beside the bound rather than its own keyword. An unknown keyword is not an error\n * in JSON Schema, it is ignored, so emitting the wrong dialect produces a document that\n * validates and then accepts the values the constraints exist to reject.\n */\n target: z.enum(['draft-2020-12', 'openapi-3.1', 'openapi-3.0']).optional(),\n /** Also emit `components.ts` for the `json-schema` generator, ready for an OpenAPI document. */\n components: z.boolean().optional(),\n // service generator specific options\n path: z.string().optional(),\n dataAccess: z.enum(['stub', 'drizzle']).default('stub').optional(),\n dbImportPath: z.string().optional(),\n schemaImportPath: z.string().optional(),\n // zod/valibot/arktype generator specific options\n schemaSuffix: z.string().optional(),\n fileSuffix: z.string().optional(),\n /**\n * Prefixes, suffixes and table casing for generated identifiers (zod/valibot/arktype).\n * Omitting it reproduces the output of every previous release exactly.\n */\n affix: AffixSchema.optional(),\n // orpc validation sharing\n validation: z\n .object({\n useShared: z.boolean().default(false).optional(),\n library: z.enum(['zod', 'valibot', 'arktype']).default('zod').optional(),\n importPath: z.string().optional(),\n schemaSuffix: z.string().optional(),\n /**\n * How the validation generator named its exports. Usually left unset: the CLI copies\n * it from the sibling generator whose `kind` matches `library`.\n */\n affix: AffixSchema.optional(),\n })\n .optional(),\n // template options\n templateOptions: z.record(z.string(), z.any()).optional(),\n});\n\nexport const AnalyzerSchema = z.object({\n includeRelations: z.boolean().default(true),\n validateConstraints: z.boolean().default(true),\n includeHeuristicRelations: z.boolean().default(false),\n});\n\nexport const ConfigSchema = z\n .object({\n schema: z.string(),\n outDir: z.string().default('src/api'),\n /**\n * Which tables to generate for, matched against the database table name.\n *\n * There was no way to say this, and every generator loops over every table it finds, so\n * DRZL emitted unauthenticated CRUD over whatever shared the schema file. That is noise for\n * a migrations table and a genuine leak for an auth one: Better Auth puts `user`, `session`,\n * `account` and `verification` alongside your own tables, and `account` holds\n * `accessToken`, `refreshToken`, `idToken` and `password`.\n *\n * Deliberately name-based and explicit rather than detecting any particular library. Auth\n * table names are all renameable, so a built-in list would miss renamed tables and, worse,\n * silently skip an ordinary table that happened to be called `user`, which is usually the\n * application's main entity.\n *\n * `exclude` wins over `include`. Patterns support `*`, matching within a name.\n */\n include: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n /**\n * How every relative specifier drzl invents spells its extension, for every generator.\n * A generator may override it. Defaults to `js`, which is the only form that resolves\n * under every `moduleResolution` without a compiler flag.\n */\n importExtension: ImportExtensionSchema.default(DEFAULT_IMPORT_EXTENSION),\n analyzer: AnalyzerSchema.default({\n includeRelations: true,\n validateConstraints: true,\n includeHeuristicRelations: false,\n }),\n generators: z\n .array(GeneratorSchema)\n .min(1)\n .default([{ kind: 'orpc' } as any]),\n })\n // Reject an affix before anything is written, rather than emitting a file that cannot\n // compile. Only `affix` is inspected; the legacy flat `schemaSuffix` is left alone so\n // configs that parse today keep parsing.\n .superRefine((cfg, ctx) => {\n cfg.generators.forEach((g, i) => {\n const report = (base: (string | number)[], affix?: AffixOptions, schemaSuffix?: string) => {\n for (const issue of validateAffix(affix, schemaSuffix)) {\n ctx.addIssue({\n code: 'custom',\n path: ['generators', i, ...base, ...issue.path],\n message: issue.message,\n });\n }\n };\n report(['affix'], g.affix as AffixOptions | undefined, g.schemaSuffix);\n report(\n ['validation', 'affix'],\n g.validation?.affix as AffixOptions | undefined,\n g.validation?.schemaSuffix\n );\n });\n });\n\n// ✨ Separate input vs output types\nexport type DrzlConfigInput = z.input<typeof ConfigSchema>;\nexport type DrzlConfig = z.output<typeof ConfigSchema>;\n\nexport function defineConfig<T extends DrzlConfigInput>(cfg: T): T {\n return cfg;\n}\n\ntype GeneratorConfig = DrzlConfig['generators'][number];\n\nfunction sharedSchemaNames(opts: { affix?: AffixOptions; schemaSuffix?: string }): string[] {\n const resolved = resolveAffix(opts);\n return NAME_MODES.map((mode) => schemaName(mode, AFFIX_PROBE_TABLE, resolved));\n}\n\n/**\n * Fill in cross-generator defaults and refuse configs whose generators would disagree.\n *\n * An oRPC router that imports shared schemas has to spell the exact names the validation\n * generator exported. Both sides used to be configured independently, so they could silently\n * drift into a router that does not compile. When an oRPC generator uses shared validation\n * and exactly one sibling generator produces that library, its `affix` is copied across.\n *\n * Deliberately conservative about the pre-existing flat `schemaSuffix`: a disagreement there\n * is only reported, never repaired, because repairing it would change the bytes an existing\n * config emits.\n *\n * `importExtension` is pushed down here too. A consumer compiles the whole generated tree\n * with one tsconfig, so the setting that has to hold is the same for every generator, and\n * every call site downstream can then read it off the generator without knowing about the\n * top-level default.\n */\nexport function resolveConfig(cfg: DrzlConfig): { config: DrzlConfig; warnings: string[] } {\n const warnings: string[] = [];\n const generators: GeneratorConfig[] = cfg.generators.map((g) => ({\n ...g,\n importExtension: g.importExtension ?? cfg.importExtension,\n }));\n\n for (const g of generators) {\n if (g.kind !== 'orpc') continue;\n const v = g.validation;\n if (!v?.useShared) continue;\n\n const library = v.library ?? 'zod';\n const siblings = generators.filter((s) => s.kind === library);\n // Zero siblings means the user points at a barrel drzl does not generate; more than one\n // means there is no single source of truth. Either way, leave the config alone.\n if (siblings.length !== 1) continue;\n const sibling = siblings[0];\n\n const theirs = sharedSchemaNames({\n affix: sibling.affix as AffixOptions | undefined,\n schemaSuffix: sibling.schemaSuffix,\n });\n\n if (!v.affix) {\n if (sibling.affix) {\n // Bake the sibling's fully resolved naming in, so its own schemaSuffix fallback\n // travels with it and cannot be re-interpreted on the oRPC side.\n g.validation = {\n ...v,\n affix: resolveAffix({\n affix: sibling.affix as AffixOptions,\n schemaSuffix: sibling.schemaSuffix,\n }),\n };\n continue;\n }\n const mine = sharedSchemaNames({ schemaSuffix: v.schemaSuffix });\n if (mine.join(',') !== theirs.join(',')) {\n warnings.push(\n `drzl config: the \"orpc\" generator's validation.schemaSuffix ` +\n `(${JSON.stringify(v.schemaSuffix ?? 'Schema')}) does not match the \"${library}\" ` +\n `generator's schemaSuffix (${JSON.stringify(sibling.schemaSuffix ?? 'Schema')}). ` +\n `The router will import ${mine.join(', ')} but the \"${library}\" generator exports ` +\n `${theirs.join(', ')}, so the generated router will not compile. Set both to the ` +\n `same value, or move to \"affix\", which is inherited automatically.`\n );\n }\n continue;\n }\n\n const mine = sharedSchemaNames({\n affix: v.affix as AffixOptions,\n schemaSuffix: v.schemaSuffix,\n });\n if (mine.join(',') !== theirs.join(',')) {\n throw new Error(\n `drzl config: the \"orpc\" generator imports shared ${library} schemas, but its ` +\n `validation.affix disagrees with the \"${library}\" generator's own naming. The router ` +\n `would import ${mine.join(', ')} while the \"${library}\" generator exports ` +\n `${theirs.join(', ')}. Make them match, or drop validation.affix and let it be ` +\n `inherited from the \"${library}\" generator.`\n );\n }\n }\n\n return { config: { ...cfg, generators }, warnings };\n}\n\n/**\n * Parse, then resolve cross-generator defaults. Both `generate` and `watch` go through\n * loadConfig, so putting the resolution here is what keeps the two duplicated generator\n * dispatch blocks in cli.ts from needing the logic twice.\n */\nfunction finalize(raw: unknown): DrzlConfig {\n const { config, warnings } = resolveConfig(ConfigSchema.parse(raw));\n for (const w of warnings) console.warn(w);\n return config;\n}\n\nexport async function loadConfig(customPath?: string): Promise<DrzlConfig | null> {\n const fsp = await import('node:fs/promises');\n\n const candidates = customPath\n ? [customPath]\n : [\n 'drzl.config.ts',\n 'drzl.config.mjs',\n 'drzl.config.js',\n 'drzl.config.cjs',\n 'drzl.config.json',\n ];\n\n for (const c of candidates) {\n const p = path.resolve(process.cwd(), c);\n try {\n await fsp.access(p);\n } catch {\n continue;\n }\n\n const ext = path.extname(p).toLowerCase();\n\n // JSON: read directly\n if (ext === '.json') {\n const raw = JSON.parse(await fsp.readFile(p, 'utf8'));\n return finalize(raw);\n }\n\n // Everything else (TS/JS/MJS/CJS) -> Jiti with cache-busting\n const { createJiti } = await import('jiti');\n const stat = await fsp.stat(p);\n\n // Passing __filename is safe in CJS; fallback to cwd if not defined.\n const base =\n typeof __filename !== 'undefined' ? __filename : path.join(process.cwd(), 'index.js');\n\n const jiti = createJiti(base, {\n moduleCache: false, // re-evaluate each time\n fsCache: true, // keep transform cache\n cacheVersion: String(stat.mtimeMs), // bump on edit\n interopDefault: true,\n tryNative: false, // <-- prevent native import of .ts\n // debug: true,\n }) as any;\n\n const mod = await jiti.import(p);\n const raw = mod?.default ?? mod;\n return finalize(raw);\n }\n\n return null;\n}\n\n/** Absolute output dirs for all generators (to ignore in watcher). */\nexport function computeGeneratorOutputDirs(cfg: DrzlConfig, cwd = process.cwd()): string[] {\n const abs = (p: string) => path.resolve(cwd, p);\n const dirs = new Set<string>();\n dirs.add(abs(cfg.outDir)); // orpc\n for (const g of cfg.generators) {\n if (g.kind === 'service') dirs.add(abs(g.path ?? 'src/services'));\n if (g.kind === 'zod') dirs.add(abs(g.path ?? 'src/validators/zod'));\n if (g.kind === 'valibot') dirs.add(abs(g.path ?? 'src/validators/valibot'));\n if (g.kind === 'arktype') dirs.add(abs(g.path ?? 'src/validators/arktype'));\n if (g.kind === 'typebox') dirs.add(abs(g.path ?? 'src/validators/typebox'));\n if (g.kind === 'json-schema') dirs.add(abs(g.path ?? 'src/validators/json-schema'));\n }\n return [...dirs];\n}\n\n/** Resolve custom template directories (local path or installed package). */\nexport function resolveTemplateDirsSync(cfg: DrzlConfig, cwd = process.cwd()): string[] {\n const results: string[] = [];\n const req = createRequire(\n typeof __filename !== 'undefined' ? __filename : path.join(process.cwd(), 'index.js')\n );\n\n for (const g of cfg.generators) {\n const t = g.template;\n if (!t || t === 'standard' || t === 'minimal') continue;\n\n // Try package resolution relative to cwd\n let pkgDir: string | null = null;\n try {\n const pkg = req.resolve(`${t}/package.json`, { paths: [cwd] as any });\n pkgDir = path.dirname(pkg);\n } catch {}\n\n if (pkgDir) {\n results.push(pkgDir);\n continue;\n }\n\n // Local path-like template\n if (/[./\\\\]/.test(t)) {\n const abs = path.resolve(cwd, t);\n if (fs.existsSync(abs)) results.push(abs);\n }\n }\n\n return Array.from(new Set(results));\n}\n\n/** Build watch targets (exclude output dirs; watcher will ignore those). */\n/**\n * Narrow an analysis's tables to the ones the config asked for.\n *\n * Matching is on the database table name, anchored, with `*` as the only metacharacter. Anchored\n * matters: `user` must not also drop `users`, and a substring match would. `exclude` is applied\n * after `include`, so the safer direction wins when both name the same table.\n */\nexport function filterTables<T extends { name: string }>(\n tables: T[],\n opts: { include?: string[]; exclude?: string[] }\n): T[] {\n const toRegExp = (pattern: string) =>\n new RegExp(\n '^' +\n pattern\n .split('*')\n .map((part) => part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*') +\n '$'\n );\n\n const matches = (patterns: string[], name: string) =>\n patterns.some((p) => toRegExp(p).test(name));\n\n let out = tables;\n if (opts.include?.length) out = out.filter((t) => matches(opts.include!, t.name));\n if (opts.exclude?.length) out = out.filter((t) => !matches(opts.exclude!, t.name));\n return out;\n}\n\nexport function computeWatchTargets(cfg: DrzlConfig, cwd = process.cwd()): string[] {\n const abs = (p: string) => path.resolve(cwd, p);\n const schemaAbs = abs(cfg.schema);\n // The schema's directory, not a glob under it. Chokidar removed glob support in v4 and treats\n // `<dir>/**/*.{ts,tsx,js}` as a literal path, so it watched a directory named `**` that does\n // not exist: no event ever fired and `drzl watch` did its initial build and then sat inert.\n // A directory is watched recursively by chokidar itself, and the extension filtering that the\n // glob was doing now happens on the event instead.\n const targets = new Set<string>([\n path.dirname(schemaAbs),\n abs('drzl.config.ts'),\n abs('drzl.config.js'),\n abs('drzl.config.mjs'),\n abs('drzl.config.cjs'),\n ]);\n for (const t of resolveTemplateDirsSync(cfg, cwd)) targets.add(t);\n return [...targets];\n}\n"],"mappings":"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","names":["mine","raw"]}
|
package/dist/cli.cjs
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,6 +30,313 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
25
32
|
|
|
33
|
+
// ../generator-json-schema/dist/index.js
|
|
34
|
+
var dist_exports = {};
|
|
35
|
+
__export(dist_exports, {
|
|
36
|
+
JsonSchemaGenerator: () => JsonSchemaGenerator,
|
|
37
|
+
componentsDocument: () => componentsDocument,
|
|
38
|
+
default: () => index_default,
|
|
39
|
+
tableSchemas: () => tableSchemas
|
|
40
|
+
});
|
|
41
|
+
function baseSchema(c, mode, target, checks, sets, lengths) {
|
|
42
|
+
const s = c.shape;
|
|
43
|
+
if (s) {
|
|
44
|
+
switch (s.kind) {
|
|
45
|
+
case "json":
|
|
46
|
+
return {};
|
|
47
|
+
case "custom":
|
|
48
|
+
return {};
|
|
49
|
+
case "buffer":
|
|
50
|
+
return { type: "string", contentEncoding: "base64" };
|
|
51
|
+
case "tuple":
|
|
52
|
+
return target === "openapi-3.0" ? { type: "array", items: { type: "number" }, minItems: s.length, maxItems: s.length } : {
|
|
53
|
+
type: "array",
|
|
54
|
+
prefixItems: Array.from({ length: s.length }, () => ({ type: "number" })),
|
|
55
|
+
minItems: s.length,
|
|
56
|
+
maxItems: s.length
|
|
57
|
+
};
|
|
58
|
+
case "numberVector":
|
|
59
|
+
return {
|
|
60
|
+
type: "array",
|
|
61
|
+
items: { type: "number" },
|
|
62
|
+
...s.length ? { minItems: s.length, maxItems: s.length } : {}
|
|
63
|
+
};
|
|
64
|
+
case "bitstring":
|
|
65
|
+
return {
|
|
66
|
+
type: "string",
|
|
67
|
+
pattern: "^[01]*$",
|
|
68
|
+
...s.length ? s.exact ? { minLength: s.length, maxLength: s.length } : { maxLength: s.length } : {}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const set = sets.find((x) => x.column === c.name);
|
|
73
|
+
if (set) return { enum: set.values.map((v) => set.kind === "string" ? v : Number(v)) };
|
|
74
|
+
if (c.enumValues && c.enumValues.length) return { enum: [...c.enumValues] };
|
|
75
|
+
const mine = c.arrayDimensions ? [] : checks.filter((k) => k.column === c.name);
|
|
76
|
+
const eq = mine.find((k) => k.operator === "=");
|
|
77
|
+
if (eq) return { const: eq.kind === "string" ? eq.value : Number(eq.value) };
|
|
78
|
+
switch (c.tsType) {
|
|
79
|
+
case "string": {
|
|
80
|
+
const out = { type: "string" };
|
|
81
|
+
if (c.format === "uuid") out.format = UUID_FORMAT;
|
|
82
|
+
else if (c.format && import_validation_core2.COLUMN_FORMATS[c.format]) out.pattern = import_validation_core2.COLUMN_FORMATS[c.format];
|
|
83
|
+
if (c.maxLength !== void 0) out.maxLength = c.maxLength;
|
|
84
|
+
applyLengths(out, c, lengths);
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
case "number": {
|
|
88
|
+
const out = { type: (0, import_validation_core2.isIntegerColumn)(c) ? "integer" : "number" };
|
|
89
|
+
if (!c.arrayDimensions) applyNumericBounds(out, c, checks, target);
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
case "bigint":
|
|
93
|
+
return { type: "string", pattern: "^-?\\d+$" };
|
|
94
|
+
case "boolean":
|
|
95
|
+
return { type: "boolean" };
|
|
96
|
+
case "Date":
|
|
97
|
+
return { type: "string", format: "date-time" };
|
|
98
|
+
case "Uint8Array":
|
|
99
|
+
return { type: "string", contentEncoding: "base64" };
|
|
100
|
+
default:
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function applyLengths(out, c, lengths) {
|
|
105
|
+
for (const k of lengths.filter((x) => x.column === c.name)) {
|
|
106
|
+
const n = Number(k.value);
|
|
107
|
+
if (k.operator === ">=") out.minLength = Math.max(Number(out.minLength ?? 0), n);
|
|
108
|
+
else if (k.operator === ">") out.minLength = Math.max(Number(out.minLength ?? 0), n + 1);
|
|
109
|
+
else if (k.operator === "<=") out.maxLength = Math.min(Number(out.maxLength ?? Infinity), n);
|
|
110
|
+
else if (k.operator === "<") out.maxLength = Math.min(Number(out.maxLength ?? Infinity), n - 1);
|
|
111
|
+
else if (k.operator === "=") {
|
|
112
|
+
out.minLength = n;
|
|
113
|
+
out.maxLength = n;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function applyNumericBounds(out, c, checks, target) {
|
|
118
|
+
let min = c.min !== void 0 ? { value: Number(c.min), exclusive: false } : void 0;
|
|
119
|
+
let max = c.max !== void 0 ? { value: Number(c.max), exclusive: false } : void 0;
|
|
120
|
+
for (const k of checks.filter((x) => x.column === c.name && x.kind === "number")) {
|
|
121
|
+
if (k.operator === ">=") min = { value: Number(k.value), exclusive: false };
|
|
122
|
+
else if (k.operator === ">") min = { value: Number(k.value), exclusive: true };
|
|
123
|
+
else if (k.operator === "<=") max = { value: Number(k.value), exclusive: false };
|
|
124
|
+
else if (k.operator === "<") max = { value: Number(k.value), exclusive: true };
|
|
125
|
+
}
|
|
126
|
+
const old = target === "openapi-3.0";
|
|
127
|
+
if (min) {
|
|
128
|
+
if (min.exclusive && !old) out.exclusiveMinimum = min.value;
|
|
129
|
+
else {
|
|
130
|
+
out.minimum = min.value;
|
|
131
|
+
if (min.exclusive) out.exclusiveMinimum = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (max) {
|
|
135
|
+
if (max.exclusive && !old) out.exclusiveMaximum = max.value;
|
|
136
|
+
else {
|
|
137
|
+
out.maximum = max.value;
|
|
138
|
+
if (max.exclusive) out.exclusiveMaximum = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function cardinalityBounds(c, cardinalities) {
|
|
143
|
+
if (!c.arrayDimensions) return {};
|
|
144
|
+
const out = {};
|
|
145
|
+
for (const k of cardinalities.filter((x) => x.column === c.name)) {
|
|
146
|
+
const n = Number(k.value);
|
|
147
|
+
if (k.operator === ">=") out.minItems = n;
|
|
148
|
+
else if (k.operator === ">") out.minItems = n + 1;
|
|
149
|
+
else if (k.operator === "<=") out.maxItems = n;
|
|
150
|
+
else if (k.operator === "<") out.maxItems = n - 1;
|
|
151
|
+
else if (k.operator === "=") {
|
|
152
|
+
out.minItems = n;
|
|
153
|
+
out.maxItems = n;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
function makeNullable(s, target) {
|
|
159
|
+
if (target === "openapi-3.0") return { ...s, nullable: true };
|
|
160
|
+
if (s.type === void 0) {
|
|
161
|
+
if (Array.isArray(s.enum)) return { ...s, enum: [...s.enum, null] };
|
|
162
|
+
if ("const" in s) {
|
|
163
|
+
const { const: k, ...rest } = s;
|
|
164
|
+
return { ...rest, enum: [k, null] };
|
|
165
|
+
}
|
|
166
|
+
return s;
|
|
167
|
+
}
|
|
168
|
+
return { ...s, type: [s.type, "null"] };
|
|
169
|
+
}
|
|
170
|
+
function columnSchema(c, mode, target, checks, sets, lengths, cardinalities, applyDefault) {
|
|
171
|
+
let s = baseSchema(c, mode, target, checks, sets, lengths);
|
|
172
|
+
const dims = c.arrayDimensions ?? 0;
|
|
173
|
+
for (let i = 0; i < dims; i++) {
|
|
174
|
+
s = { type: "array", items: s, ...i === dims - 1 ? cardinalityBounds(c, cardinalities) : {} };
|
|
175
|
+
}
|
|
176
|
+
if (c.nullable) s = makeNullable(s, target);
|
|
177
|
+
if (mode === "insert" && applyDefault && c.defaultValue !== void 0) {
|
|
178
|
+
s = { ...s, default: c.defaultValue };
|
|
179
|
+
}
|
|
180
|
+
return s;
|
|
181
|
+
}
|
|
182
|
+
function rowDescription(rows, cols) {
|
|
183
|
+
const present = new Set(cols.map((c) => c.name));
|
|
184
|
+
const applicable = rows.filter((r) => present.has(r.left) && present.has(r.right));
|
|
185
|
+
if (!applicable.length) return void 0;
|
|
186
|
+
const list = applicable.map((r) => `${r.name ? `${r.name}: ` : ""}${r.left} ${r.operator} ${r.right}`).join("; ");
|
|
187
|
+
return `Row constraints not expressible in JSON Schema: ${list}`;
|
|
188
|
+
}
|
|
189
|
+
function tableSchema(table, cols, mode, target, applyDefaults, parsed) {
|
|
190
|
+
const properties = {};
|
|
191
|
+
const required = [];
|
|
192
|
+
for (const c of cols) {
|
|
193
|
+
properties[c.name] = columnSchema(
|
|
194
|
+
c,
|
|
195
|
+
mode,
|
|
196
|
+
target,
|
|
197
|
+
parsed.checks,
|
|
198
|
+
parsed.sets,
|
|
199
|
+
parsed.lengths,
|
|
200
|
+
parsed.cardinalities,
|
|
201
|
+
applyDefaults
|
|
202
|
+
);
|
|
203
|
+
const suppliedOnInsert = c.hasDefault || applyDefaults && c.defaultValue !== void 0 || c.isGenerated;
|
|
204
|
+
const optional = mode === "update" || mode === "insert" && suppliedOnInsert;
|
|
205
|
+
if (!optional) required.push(c.name);
|
|
206
|
+
}
|
|
207
|
+
const desc = rowDescription(parsed.rows, cols);
|
|
208
|
+
return {
|
|
209
|
+
...target === "draft-2020-12" ? { $schema: DRAFT } : {},
|
|
210
|
+
$id: `${table.tsName}.${mode}`,
|
|
211
|
+
title: `${mode} ${table.tsName}`,
|
|
212
|
+
...desc ? { description: desc } : {},
|
|
213
|
+
type: "object",
|
|
214
|
+
properties,
|
|
215
|
+
...required.length ? { required } : {},
|
|
216
|
+
additionalProperties: false
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function collect(table) {
|
|
220
|
+
const parsed = (table.checks ?? []).map((k) => (0, import_validation_core2.parseCheck)(k.expression, k.name));
|
|
221
|
+
return {
|
|
222
|
+
checks: parsed.flatMap((p) => p.ok ? p.checks : []),
|
|
223
|
+
sets: parsed.flatMap((p) => p.ok ? p.sets ?? [] : []),
|
|
224
|
+
rows: parsed.flatMap((p) => p.ok ? p.rows ?? [] : []),
|
|
225
|
+
lengths: parsed.flatMap((p) => p.ok ? p.lengths ?? [] : []),
|
|
226
|
+
cardinalities: parsed.flatMap((p) => p.ok ? p.cardinalities ?? [] : [])
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function tableSchemas(table, opts = {}) {
|
|
230
|
+
const target = opts.target ?? "draft-2020-12";
|
|
231
|
+
const parsed = collect(table);
|
|
232
|
+
const build = (cols, mode) => tableSchema(table, cols, mode, target, !!opts.applyDefaults, parsed);
|
|
233
|
+
return {
|
|
234
|
+
insert: build((0, import_validation_core2.insertColumns)(table), "insert"),
|
|
235
|
+
update: build((0, import_validation_core2.updateColumns)(table), "update"),
|
|
236
|
+
select: build((0, import_validation_core2.selectColumns)(table), "select")
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function componentsDocument(tables, opts = {}) {
|
|
240
|
+
const schemas = {};
|
|
241
|
+
for (const table of tables) {
|
|
242
|
+
const built = tableSchemas(table, opts);
|
|
243
|
+
for (const mode of ["insert", "update", "select"]) {
|
|
244
|
+
const name = `${table.tsName}${mode[0].toUpperCase()}${mode.slice(1)}`;
|
|
245
|
+
const { $schema: _dialect, $id: _id, ...rest } = built[mode];
|
|
246
|
+
schemas[name] = rest;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return { schemas };
|
|
250
|
+
}
|
|
251
|
+
function renderTableModule(table, affix, target, applyDefaults) {
|
|
252
|
+
const T = table.tsName;
|
|
253
|
+
const schemas = tableSchemas(table, { target, applyDefaults });
|
|
254
|
+
const decl = (mode) => `export const ${(0, import_validation_core2.schemaName)(mode, T, affix)} = ${JSON.stringify(schemas[mode], null, 2)} as const;
|
|
255
|
+
|
|
256
|
+
export type ${(0, import_validation_core2.typeName)(mode, T, affix)} = typeof ${(0, import_validation_core2.schemaName)(mode, T, affix)};`;
|
|
257
|
+
return [decl("insert"), decl("update"), decl("select")].join("\n\n") + "\n";
|
|
258
|
+
}
|
|
259
|
+
function buildHeader(h) {
|
|
260
|
+
if (h?.enabled === false) return "";
|
|
261
|
+
const text = h?.text ?? "// Generated by DRZL. Do not edit by hand.";
|
|
262
|
+
return `${text}
|
|
263
|
+
|
|
264
|
+
`;
|
|
265
|
+
}
|
|
266
|
+
var import_validation_core2, DEFAULT_FILE_SUFFIX, DRAFT, UUID_FORMAT, JsonSchemaGenerator, index_default;
|
|
267
|
+
var init_dist = __esm({
|
|
268
|
+
"../generator-json-schema/dist/index.js"() {
|
|
269
|
+
"use strict";
|
|
270
|
+
import_validation_core2 = require("@drzl/validation-core");
|
|
271
|
+
DEFAULT_FILE_SUFFIX = ".schema.ts";
|
|
272
|
+
DRAFT = "https://json-schema.org/draft/2020-12/schema";
|
|
273
|
+
UUID_FORMAT = "uuid";
|
|
274
|
+
JsonSchemaGenerator = class {
|
|
275
|
+
constructor(analysis) {
|
|
276
|
+
this.analysis = analysis;
|
|
277
|
+
this.library = "json-schema";
|
|
278
|
+
}
|
|
279
|
+
async generate(opts) {
|
|
280
|
+
const fs3 = await import("fs/promises");
|
|
281
|
+
const path5 = await import("path");
|
|
282
|
+
const out = path5.resolve(process.cwd(), opts.outDir);
|
|
283
|
+
const files = [];
|
|
284
|
+
await fs3.mkdir(out, { recursive: true });
|
|
285
|
+
const affix = (0, import_validation_core2.resolveAffix)(opts);
|
|
286
|
+
const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX;
|
|
287
|
+
const target = opts.target ?? "draft-2020-12";
|
|
288
|
+
for (const table of this.analysis.tables) {
|
|
289
|
+
const filePath = path5.join(out, (0, import_validation_core2.moduleFileName)(table.tsName, fileSuffix));
|
|
290
|
+
const code = renderTableModule(table, affix, target, !!opts.applyDefaults);
|
|
291
|
+
const formatted = await (0, import_validation_core2.formatCode)(
|
|
292
|
+
buildHeader(opts.outputHeader) + code,
|
|
293
|
+
filePath,
|
|
294
|
+
opts.format
|
|
295
|
+
);
|
|
296
|
+
await fs3.writeFile(filePath, formatted, "utf8");
|
|
297
|
+
files.push(filePath);
|
|
298
|
+
}
|
|
299
|
+
if (opts.components) {
|
|
300
|
+
const doc = componentsDocument(this.analysis.tables, {
|
|
301
|
+
target,
|
|
302
|
+
applyDefaults: !!opts.applyDefaults
|
|
303
|
+
});
|
|
304
|
+
const componentsPath = path5.join(out, "components.ts");
|
|
305
|
+
const code = `export const components = ${JSON.stringify(doc, null, 2)} as const;
|
|
306
|
+
`;
|
|
307
|
+
await fs3.writeFile(
|
|
308
|
+
componentsPath,
|
|
309
|
+
await (0, import_validation_core2.formatCode)(buildHeader(opts.outputHeader) + code, componentsPath, opts.format),
|
|
310
|
+
"utf8"
|
|
311
|
+
);
|
|
312
|
+
files.push(componentsPath);
|
|
313
|
+
}
|
|
314
|
+
const indexPath = path5.join(out, "index.ts");
|
|
315
|
+
const index = this.analysis.tables.map((t) => `export * from '${(0, import_validation_core2.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`).concat(
|
|
316
|
+
opts.components ? [`export * from './components${opts.importExtension === "none" ? "" : ".js"}';`] : []
|
|
317
|
+
).join("\n") + "\n";
|
|
318
|
+
const indexFormatted = await (0, import_validation_core2.formatCode)(
|
|
319
|
+
buildHeader(opts.outputHeader) + index,
|
|
320
|
+
indexPath,
|
|
321
|
+
opts.format
|
|
322
|
+
);
|
|
323
|
+
await fs3.writeFile(indexPath, indexFormatted, "utf8");
|
|
324
|
+
files.push(indexPath);
|
|
325
|
+
return files;
|
|
326
|
+
}
|
|
327
|
+
renderTable(table, opts) {
|
|
328
|
+
return renderTableModule(
|
|
329
|
+
table,
|
|
330
|
+
(0, import_validation_core2.resolveAffix)(opts),
|
|
331
|
+
opts?.target ?? "draft-2020-12",
|
|
332
|
+
!!opts?.applyDefaults
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
index_default = JsonSchemaGenerator;
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
26
340
|
// src/cli.ts
|
|
27
341
|
var import_analyzer = require("@drzl/analyzer");
|
|
28
342
|
var import_generator_orpc = require("@drzl/generator-orpc");
|
|
@@ -45,6 +359,7 @@ function validationOptions(g, cfg, outDir, caps = {}) {
|
|
|
45
359
|
affix: g.affix,
|
|
46
360
|
coerceDates: g.coerceDates,
|
|
47
361
|
applyDefaults: g.applyDefaults,
|
|
362
|
+
duplicateFinder: g.duplicateFinder,
|
|
48
363
|
// Only where the generator can act on them, so an unsupported option is absent rather than
|
|
49
364
|
// present and ignored.
|
|
50
365
|
...caps.schemaTypes ? {
|
|
@@ -123,6 +438,14 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
123
438
|
typedColumns: import_zod.z.boolean().optional(),
|
|
124
439
|
// Reproduce literal column defaults in the insert schema, so parsing fills them in.
|
|
125
440
|
applyDefaults: import_zod.z.boolean().optional(),
|
|
441
|
+
/**
|
|
442
|
+
* Emit `findDuplicate<Table>` beside the schemas: the rows in a batch that collide with an
|
|
443
|
+
* earlier row on a unique constraint.
|
|
444
|
+
*
|
|
445
|
+
* Uniqueness is the one constraint a per-row validator structurally cannot see, since it is a
|
|
446
|
+
* fact about the table rather than the row. This checks the half that needs no database.
|
|
447
|
+
*/
|
|
448
|
+
duplicateFinder: import_zod.z.boolean().optional(),
|
|
126
449
|
naming: NamingSchema.optional(),
|
|
127
450
|
outputHeader: import_zod.z.object({
|
|
128
451
|
enabled: import_zod.z.boolean().default(true).optional(),
|
|
@@ -143,6 +466,8 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
143
466
|
* validates and then accepts the values the constraints exist to reject.
|
|
144
467
|
*/
|
|
145
468
|
target: import_zod.z.enum(["draft-2020-12", "openapi-3.1", "openapi-3.0"]).optional(),
|
|
469
|
+
/** Also emit `components.ts` for the `json-schema` generator, ready for an OpenAPI document. */
|
|
470
|
+
components: import_zod.z.boolean().optional(),
|
|
146
471
|
// service generator specific options
|
|
147
472
|
path: import_zod.z.string().optional(),
|
|
148
473
|
dataAccess: import_zod.z.enum(["stub", "drizzle"]).default("stub").optional(),
|
|
@@ -695,13 +1020,14 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
695
1020
|
}
|
|
696
1021
|
} else if (g.kind === "json-schema") {
|
|
697
1022
|
try {
|
|
698
|
-
const { JsonSchemaGenerator } = await
|
|
699
|
-
const gen = new
|
|
1023
|
+
const { JsonSchemaGenerator: JsonSchemaGenerator2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
1024
|
+
const gen = new JsonSchemaGenerator2(analysis);
|
|
700
1025
|
const target = g.path ?? "src/validators/json-schema";
|
|
701
1026
|
const files = await gen.generate({
|
|
702
1027
|
// JSON Schema is data, so nothing here references a type from the schema module.
|
|
703
1028
|
...validationOptions(g, cfg, target, { schemaTypes: false }),
|
|
704
|
-
target: g.target
|
|
1029
|
+
target: g.target,
|
|
1030
|
+
components: g.components
|
|
705
1031
|
});
|
|
706
1032
|
progress.stop();
|
|
707
1033
|
(0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (json-schema): ${files.length} files`));
|
|
@@ -710,7 +1036,11 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
710
1036
|
progress.stop();
|
|
711
1037
|
console.error(
|
|
712
1038
|
import_chalk2.default.red("JSON Schema generator missing."),
|
|
713
|
-
import_chalk2.default.yellow("\nInstall with: npm install @drzl/generator-json-schema")
|
|
1039
|
+
import_chalk2.default.yellow("\nInstall with: npm install @drzl/generator-json-schema"),
|
|
1040
|
+
// An optional dependency, unlike the other generators, until its npm trusted
|
|
1041
|
+
// publisher exists. A missing optional dependency is skipped rather than failing
|
|
1042
|
+
// the install, which is what keeps `npm i @drzl/cli` working meanwhile.
|
|
1043
|
+
""
|
|
714
1044
|
);
|
|
715
1045
|
console.error(import_chalk2.default.gray("Error details:"), e?.message ?? e);
|
|
716
1046
|
process.exit(1);
|