@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 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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drzl/analyzer",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",