@drzl/analyzer 1.9.0 → 1.11.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.cjs +54 -2
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +51 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -32,7 +32,9 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
SchemaAnalyzer: () => SchemaAnalyzer,
|
|
34
34
|
default: () => index_default,
|
|
35
|
-
describeV1Column: () => describeV1Column
|
|
35
|
+
describeV1Column: () => describeV1Column,
|
|
36
|
+
isRelationsV2: () => isRelationsV2,
|
|
37
|
+
readRelationsV2: () => readRelationsV2
|
|
36
38
|
});
|
|
37
39
|
module.exports = __toCommonJS(index_exports);
|
|
38
40
|
var import_meta = {};
|
|
@@ -216,6 +218,47 @@ function declaredLength(column) {
|
|
|
216
218
|
const n = column?.length ?? column?.config?.length ?? column?.config?.dimensions;
|
|
217
219
|
return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
|
|
218
220
|
}
|
|
221
|
+
function getSymbolOf(target, key) {
|
|
222
|
+
if (!target) return void 0;
|
|
223
|
+
try {
|
|
224
|
+
for (const sym of Object.getOwnPropertySymbols(target)) {
|
|
225
|
+
if (sym.description === key) return target[sym];
|
|
226
|
+
}
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
return target[Symbol.for(key)];
|
|
230
|
+
}
|
|
231
|
+
function isRelationsV2(val) {
|
|
232
|
+
if (!val || typeof val !== "object" || Array.isArray(val)) return false;
|
|
233
|
+
const entries = Object.values(val);
|
|
234
|
+
if (!entries.length) return false;
|
|
235
|
+
return entries.every(
|
|
236
|
+
(e) => !!e && typeof e === "object" && !!e.table && !!e.relations && typeof e.relations === "object" && Object.values(e.relations).every(
|
|
237
|
+
(r) => r?.relationType === "one" || r?.relationType === "many"
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
function readRelationsV2(val, issues = []) {
|
|
242
|
+
const out = [];
|
|
243
|
+
for (const [tableKey, entry] of Object.entries(val)) {
|
|
244
|
+
const from = getSymbolOf(entry.table, "drizzle:Name") ?? entry.name ?? tableKey;
|
|
245
|
+
for (const [fieldName, r] of Object.entries(entry.relations ?? {})) {
|
|
246
|
+
const to = r?.targetTableName;
|
|
247
|
+
if (typeof to !== "string" || !to) {
|
|
248
|
+
issues.push({
|
|
249
|
+
code: "DRZL_ANL_REL_V2",
|
|
250
|
+
level: "warn",
|
|
251
|
+
message: `Relation "${fieldName}" on "${from}" names no target table and was skipped.`
|
|
252
|
+
});
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const via = getSymbolOf(r.throughTable, "drizzle:Name") ?? getSymbolOf(r.through?.sourceTable, "drizzle:Name") ?? void 0;
|
|
256
|
+
if (via) out.push({ kind: "manyToMany", from, to, via });
|
|
257
|
+
else out.push({ kind: r.relationType === "many" ? "many" : "one", from, to });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
}
|
|
219
262
|
var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
220
263
|
constructor(schemaPath) {
|
|
221
264
|
this.schemaPath = schemaPath;
|
|
@@ -608,6 +651,8 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
608
651
|
}
|
|
609
652
|
const ev = col?.enumValues;
|
|
610
653
|
const nullable = !col?.notNull && !col?.config?.notNull;
|
|
654
|
+
const rawDefault = col?.default;
|
|
655
|
+
const defaultValue = rawDefault !== void 0 && !(rawDefault && typeof rawDefault === "object" && "queryChunks" in rawDefault) ? rawDefault : void 0;
|
|
611
656
|
const generatedIdentity = col?.generatedIdentity;
|
|
612
657
|
const isGenerated = !!(col?.generated || generatedIdentity?.type === "always" || col?.isGenerated);
|
|
613
658
|
const hasDefault = col?.hasDefault === true || col?.default !== void 0 || col?.config?.default !== void 0 || col?.defaultFn !== void 0 || isGenerated;
|
|
@@ -635,6 +680,7 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
635
680
|
defaultExpression: void 0,
|
|
636
681
|
references,
|
|
637
682
|
enumValues: Array.isArray(ev) ? ev : void 0,
|
|
683
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
638
684
|
...constraints,
|
|
639
685
|
...v1 ?? {}
|
|
640
686
|
});
|
|
@@ -769,6 +815,10 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
769
815
|
if (opts.includeRelations) {
|
|
770
816
|
relations.push(...this.readRelationsObject(val, name, issues));
|
|
771
817
|
}
|
|
818
|
+
} else if (isRelationsV2(val)) {
|
|
819
|
+
if (opts.includeRelations) {
|
|
820
|
+
relations.push(...readRelationsV2(val, issues));
|
|
821
|
+
}
|
|
772
822
|
} else {
|
|
773
823
|
const ev = val?.enumValues;
|
|
774
824
|
if (Array.isArray(ev) && ev.every((x) => typeof x === "string")) {
|
|
@@ -906,5 +956,7 @@ var index_default = SchemaAnalyzer;
|
|
|
906
956
|
// Annotate the CommonJS export names for ESM import in node:
|
|
907
957
|
0 && (module.exports = {
|
|
908
958
|
SchemaAnalyzer,
|
|
909
|
-
describeV1Column
|
|
959
|
+
describeV1Column,
|
|
960
|
+
isRelationsV2,
|
|
961
|
+
readRelationsV2
|
|
910
962
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -72,6 +72,15 @@ interface Column {
|
|
|
72
72
|
* checking at all. See `COLUMN_FORMATS` in `@drzl/validation-core`.
|
|
73
73
|
*/
|
|
74
74
|
format?: 'uuid' | 'numeric';
|
|
75
|
+
/**
|
|
76
|
+
* The column's default, when it is a literal a schema can reproduce.
|
|
77
|
+
*
|
|
78
|
+
* `.default('GB')` stores a plain JS value. `defaultNow()`, `defaultRandom()` and any
|
|
79
|
+
* `sql` default store an object the database evaluates, and `$defaultFn` stores a function
|
|
80
|
+
* Drizzle calls at insert time. A schema guessing at either of those would produce a different
|
|
81
|
+
* value than the one actually stored, so only the literal case is carried.
|
|
82
|
+
*/
|
|
83
|
+
defaultValue?: unknown;
|
|
75
84
|
/**
|
|
76
85
|
* Array depth for a column declared with `.array()`, absent when the column is a scalar.
|
|
77
86
|
*
|
|
@@ -206,6 +215,25 @@ interface AnalyzeOptions {
|
|
|
206
215
|
* path below untouched.
|
|
207
216
|
*/
|
|
208
217
|
declare function describeV1Column(column: any): Partial<Column> | null;
|
|
218
|
+
/**
|
|
219
|
+
* Whether an export is a Relations v2 definition, from `defineRelations(schema, (r) => ...)`.
|
|
220
|
+
*
|
|
221
|
+
* v2 returns a plain object keyed by table name, each entry `{ table, name, relations }`, with
|
|
222
|
+
* no marker class or symbol to match on. So the shape is what identifies it: every value has a
|
|
223
|
+
* `relations` record whose entries carry a `relationType`. That is specific enough not to
|
|
224
|
+
* collide with a table or an enum, both of which are checked before this.
|
|
225
|
+
*/
|
|
226
|
+
declare function isRelationsV2(val: any): boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Read the relations declared by `defineRelations`.
|
|
229
|
+
*
|
|
230
|
+
* Simpler than the v1 reader, which has to invoke a callback with a stand-in builder to find
|
|
231
|
+
* out what was declared. v2 has already resolved everything: each descriptor states its
|
|
232
|
+
* `relationType`, its `targetTableName`, and for a many-to-many its `through` table, so nothing
|
|
233
|
+
* has to be inferred and the join table is stated rather than guessed at by the heuristic that
|
|
234
|
+
* covers v1.
|
|
235
|
+
*/
|
|
236
|
+
declare function readRelationsV2(val: any, issues?: Issue[]): Relation[];
|
|
209
237
|
declare class SchemaAnalyzer {
|
|
210
238
|
private readonly schemaPath;
|
|
211
239
|
constructor(schemaPath: string);
|
|
@@ -310,4 +338,4 @@ declare class SchemaAnalyzer {
|
|
|
310
338
|
analyze(opts?: AnalyzeOptions): Promise<Analysis>;
|
|
311
339
|
}
|
|
312
340
|
|
|
313
|
-
export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column };
|
|
341
|
+
export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column, isRelationsV2, readRelationsV2 };
|
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,15 @@ interface Column {
|
|
|
72
72
|
* checking at all. See `COLUMN_FORMATS` in `@drzl/validation-core`.
|
|
73
73
|
*/
|
|
74
74
|
format?: 'uuid' | 'numeric';
|
|
75
|
+
/**
|
|
76
|
+
* The column's default, when it is a literal a schema can reproduce.
|
|
77
|
+
*
|
|
78
|
+
* `.default('GB')` stores a plain JS value. `defaultNow()`, `defaultRandom()` and any
|
|
79
|
+
* `sql` default store an object the database evaluates, and `$defaultFn` stores a function
|
|
80
|
+
* Drizzle calls at insert time. A schema guessing at either of those would produce a different
|
|
81
|
+
* value than the one actually stored, so only the literal case is carried.
|
|
82
|
+
*/
|
|
83
|
+
defaultValue?: unknown;
|
|
75
84
|
/**
|
|
76
85
|
* Array depth for a column declared with `.array()`, absent when the column is a scalar.
|
|
77
86
|
*
|
|
@@ -206,6 +215,25 @@ interface AnalyzeOptions {
|
|
|
206
215
|
* path below untouched.
|
|
207
216
|
*/
|
|
208
217
|
declare function describeV1Column(column: any): Partial<Column> | null;
|
|
218
|
+
/**
|
|
219
|
+
* Whether an export is a Relations v2 definition, from `defineRelations(schema, (r) => ...)`.
|
|
220
|
+
*
|
|
221
|
+
* v2 returns a plain object keyed by table name, each entry `{ table, name, relations }`, with
|
|
222
|
+
* no marker class or symbol to match on. So the shape is what identifies it: every value has a
|
|
223
|
+
* `relations` record whose entries carry a `relationType`. That is specific enough not to
|
|
224
|
+
* collide with a table or an enum, both of which are checked before this.
|
|
225
|
+
*/
|
|
226
|
+
declare function isRelationsV2(val: any): boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Read the relations declared by `defineRelations`.
|
|
229
|
+
*
|
|
230
|
+
* Simpler than the v1 reader, which has to invoke a callback with a stand-in builder to find
|
|
231
|
+
* out what was declared. v2 has already resolved everything: each descriptor states its
|
|
232
|
+
* `relationType`, its `targetTableName`, and for a many-to-many its `through` table, so nothing
|
|
233
|
+
* has to be inferred and the join table is stated rather than guessed at by the heuristic that
|
|
234
|
+
* covers v1.
|
|
235
|
+
*/
|
|
236
|
+
declare function readRelationsV2(val: any, issues?: Issue[]): Relation[];
|
|
209
237
|
declare class SchemaAnalyzer {
|
|
210
238
|
private readonly schemaPath;
|
|
211
239
|
constructor(schemaPath: string);
|
|
@@ -310,4 +338,4 @@ declare class SchemaAnalyzer {
|
|
|
310
338
|
analyze(opts?: AnalyzeOptions): Promise<Analysis>;
|
|
311
339
|
}
|
|
312
340
|
|
|
313
|
-
export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column };
|
|
341
|
+
export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column, isRelationsV2, readRelationsV2 };
|
package/dist/index.js
CHANGED
|
@@ -179,6 +179,47 @@ function declaredLength(column) {
|
|
|
179
179
|
const n = column?.length ?? column?.config?.length ?? column?.config?.dimensions;
|
|
180
180
|
return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
|
|
181
181
|
}
|
|
182
|
+
function getSymbolOf(target, key) {
|
|
183
|
+
if (!target) return void 0;
|
|
184
|
+
try {
|
|
185
|
+
for (const sym of Object.getOwnPropertySymbols(target)) {
|
|
186
|
+
if (sym.description === key) return target[sym];
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
return target[Symbol.for(key)];
|
|
191
|
+
}
|
|
192
|
+
function isRelationsV2(val) {
|
|
193
|
+
if (!val || typeof val !== "object" || Array.isArray(val)) return false;
|
|
194
|
+
const entries = Object.values(val);
|
|
195
|
+
if (!entries.length) return false;
|
|
196
|
+
return entries.every(
|
|
197
|
+
(e) => !!e && typeof e === "object" && !!e.table && !!e.relations && typeof e.relations === "object" && Object.values(e.relations).every(
|
|
198
|
+
(r) => r?.relationType === "one" || r?.relationType === "many"
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
function readRelationsV2(val, issues = []) {
|
|
203
|
+
const out = [];
|
|
204
|
+
for (const [tableKey, entry] of Object.entries(val)) {
|
|
205
|
+
const from = getSymbolOf(entry.table, "drizzle:Name") ?? entry.name ?? tableKey;
|
|
206
|
+
for (const [fieldName, r] of Object.entries(entry.relations ?? {})) {
|
|
207
|
+
const to = r?.targetTableName;
|
|
208
|
+
if (typeof to !== "string" || !to) {
|
|
209
|
+
issues.push({
|
|
210
|
+
code: "DRZL_ANL_REL_V2",
|
|
211
|
+
level: "warn",
|
|
212
|
+
message: `Relation "${fieldName}" on "${from}" names no target table and was skipped.`
|
|
213
|
+
});
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const via = getSymbolOf(r.throughTable, "drizzle:Name") ?? getSymbolOf(r.through?.sourceTable, "drizzle:Name") ?? void 0;
|
|
217
|
+
if (via) out.push({ kind: "manyToMany", from, to, via });
|
|
218
|
+
else out.push({ kind: r.relationType === "many" ? "many" : "one", from, to });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
182
223
|
var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
183
224
|
constructor(schemaPath) {
|
|
184
225
|
this.schemaPath = schemaPath;
|
|
@@ -571,6 +612,8 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
571
612
|
}
|
|
572
613
|
const ev = col?.enumValues;
|
|
573
614
|
const nullable = !col?.notNull && !col?.config?.notNull;
|
|
615
|
+
const rawDefault = col?.default;
|
|
616
|
+
const defaultValue = rawDefault !== void 0 && !(rawDefault && typeof rawDefault === "object" && "queryChunks" in rawDefault) ? rawDefault : void 0;
|
|
574
617
|
const generatedIdentity = col?.generatedIdentity;
|
|
575
618
|
const isGenerated = !!(col?.generated || generatedIdentity?.type === "always" || col?.isGenerated);
|
|
576
619
|
const hasDefault = col?.hasDefault === true || col?.default !== void 0 || col?.config?.default !== void 0 || col?.defaultFn !== void 0 || isGenerated;
|
|
@@ -598,6 +641,7 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
598
641
|
defaultExpression: void 0,
|
|
599
642
|
references,
|
|
600
643
|
enumValues: Array.isArray(ev) ? ev : void 0,
|
|
644
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
601
645
|
...constraints,
|
|
602
646
|
...v1 ?? {}
|
|
603
647
|
});
|
|
@@ -732,6 +776,10 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
732
776
|
if (opts.includeRelations) {
|
|
733
777
|
relations.push(...this.readRelationsObject(val, name, issues));
|
|
734
778
|
}
|
|
779
|
+
} else if (isRelationsV2(val)) {
|
|
780
|
+
if (opts.includeRelations) {
|
|
781
|
+
relations.push(...readRelationsV2(val, issues));
|
|
782
|
+
}
|
|
735
783
|
} else {
|
|
736
784
|
const ev = val?.enumValues;
|
|
737
785
|
if (Array.isArray(ev) && ev.every((x) => typeof x === "string")) {
|
|
@@ -869,5 +917,7 @@ var index_default = SchemaAnalyzer;
|
|
|
869
917
|
export {
|
|
870
918
|
SchemaAnalyzer,
|
|
871
919
|
index_default as default,
|
|
872
|
-
describeV1Column
|
|
920
|
+
describeV1Column,
|
|
921
|
+
isRelationsV2,
|
|
922
|
+
readRelationsV2
|
|
873
923
|
};
|