@b9g/zen 0.1.4 → 0.1.6

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 CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.6] - 2026-01-05
9
+
10
+ ### Added
11
+
12
+ - `table.relations()` method for unified forward and reverse relation navigation
13
+ - `FieldMeta.schema` - raw Zod schema for direct introspection
14
+ - `FieldMeta.db` - raw database metadata from `.db.*()` methods
15
+ - `Relation` type export for typing relation navigators
16
+ - Chained relation navigation: `Users.relations().posts.fields().author.fields().email`
17
+
18
+ ### Fixed
19
+
20
+ - Validation no longer runs on reads, matching documented behavior (#16)
21
+
22
+ ### Deprecated
23
+
24
+ - `FieldMeta.type`, `FieldMeta.required`, and other cooked properties - use `schema` and `db` instead
25
+
26
+ ## [0.1.5] - 2025-12-28
27
+
28
+ ### Fixed
29
+
30
+ - `ensureTable()`, `ensureView()`, `ensureConstraints()`, `copyColumn()` no longer throw "cannot start a transaction within a transaction" when called inside `upgradeneeded` handler (#12)
31
+
8
32
  ## [0.1.4] - 2025-12-28
9
33
 
10
34
  ### Added
package/README.md CHANGED
@@ -110,7 +110,7 @@ const post = await db.get(Posts, posts[0].id);
110
110
  await db.update(Users, {name: "Alice Smith"}, user.id);
111
111
  ```
112
112
 
113
- ## Why Zen?
113
+ ## Why ZenDB?
114
114
 
115
115
  Zen is the missing link between SQL and typed data. By writing tables with Zod schema, you get idempotent migration helpers, typed CRUD, normalized object references, and many features other database clients cannot provide.
116
116
 
@@ -829,19 +829,29 @@ type NewUser = Insert<typeof Users>; // Insert type (respects defaults/.db.auto(
829
829
 
830
830
  ## Field Metadata
831
831
 
832
- Tables expose metadata for form generation:
832
+ Tables expose metadata for introspection and form generation:
833
833
 
834
834
  ```typescript
835
835
  const fields = Users.fields();
836
- // {
837
- // email: { name: "email", type: "email", required: true, unique: true },
838
- // name: { name: "name", type: "text", required: true, maxLength: 100 },
839
- // role: { name: "role", type: "select", options: ["user", "admin"], default: "user" },
840
- // }
841
836
 
842
- const pkName = Users.primaryKey(); // "id" (field name)
837
+ // Preferred: raw schema and db metadata
838
+ fields.email.name; // "email"
839
+ fields.email.schema; // ZodString - use Zod APIs (isOptional(), etc.)
840
+ fields.email.db; // FieldDBMeta object
841
+ fields.email.db.unique; // true
842
+ fields.email.db.primaryKey; // undefined
843
+
844
+ const pkName = Users.meta.primary; // "id" (field name)
843
845
  const pkFragment = Users.primary; // SQLTemplate: "users"."id"
844
- const refs = Posts.references(); // [{fieldName: "authorId", table: Users, as: "author"}]
846
+ const refs = Posts.meta.references; // [{fieldName: "authorId", table: Users, as: "author"}]
847
+
848
+ // Relation navigation (forward and reverse)
849
+ Posts.relations().author.table; // Users table
850
+ Posts.relations().author.fields(); // Users field metadata
851
+ Users.relations().posts.table; // Posts table (if reverseAs: "posts" defined)
852
+
853
+ // Chain navigation through relationships
854
+ Users.relations().posts.fields().author.fields().email; // Back to Users.email field
845
855
  ```
846
856
 
847
857
  ## Performance
@@ -1037,9 +1047,9 @@ import type {
1037
1047
  CompoundReference, // Compound foreign key reference
1038
1048
 
1039
1049
  // Field types
1040
- FieldMeta, // Field metadata for form generation
1041
- FieldType, // Field type enum
1050
+ FieldMeta, // Field metadata for introspection
1042
1051
  FieldDBMeta, // Database-specific field metadata
1052
+ Relation, // Relation navigator from table.relations()
1043
1053
 
1044
1054
  // Type inference
1045
1055
  Row, // Infer row type from Table (after read)
@@ -1094,10 +1104,9 @@ Users.primary; // SQLTemplate for primary key column
1094
1104
  // Metadata
1095
1105
  Users.name; // Table name string
1096
1106
  Users.schema; // Zod schema
1097
- Users.meta; // Table metadata (primary, indexes, etc.)
1098
- Users.primaryKey(); // Primary key field name or null
1107
+ Users.meta; // Table metadata (primary, references, indexes, etc.)
1099
1108
  Users.fields(); // Field metadata for form generation
1100
- Users.references(); // Foreign key references
1109
+ Users.relations(); // Forward and reverse relation navigators
1101
1110
 
1102
1111
  // Derived Tables
1103
1112
  Users.pick("id", "email"); // PartialTable with subset of fields
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/zen",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Define Zod tables. Write raw SQL. Get typed objects.",
5
5
  "keywords": [
6
6
  "database",
@@ -4,7 +4,7 @@ import {
4
4
  getViewMeta,
5
5
  ident,
6
6
  makeTemplate
7
- } from "./chunk-DKLSJISE.js";
7
+ } from "./chunk-NLI6YKQ6.js";
8
8
 
9
9
  // src/impl/ddl.ts
10
10
  import { z } from "zod";
@@ -665,6 +665,25 @@ function extendZod(zodModule) {
665
665
  extendZod(z);
666
666
  var TABLE_MARKER = Symbol.for("@b9g/zen:table");
667
667
  var TABLE_META = Symbol.for("@b9g/zen:table-meta");
668
+ var reverseRelationsRegistry = /* @__PURE__ */ new WeakMap();
669
+ function registerReverseRelation(targetTable, reverseName, getSourceTable, sourceTableName, fieldName) {
670
+ let relations = reverseRelationsRegistry.get(targetTable);
671
+ if (!relations) {
672
+ relations = /* @__PURE__ */ new Map();
673
+ reverseRelationsRegistry.set(targetTable, relations);
674
+ }
675
+ if (relations.has(reverseName)) {
676
+ throw new TableDefinitionError(
677
+ `Table "${sourceTableName}": reverse relation "${reverseName}" (from field "${fieldName}") collides with an existing reverse relation on target table "${targetTable.name}". Each reverseAs name must be unique per target table.`,
678
+ sourceTableName,
679
+ fieldName
680
+ );
681
+ }
682
+ relations.set(reverseName, getSourceTable);
683
+ }
684
+ function getReverseRelations(table2) {
685
+ return reverseRelationsRegistry.get(table2);
686
+ }
668
687
  function isTable(value) {
669
688
  return value !== null && typeof value === "object" && TABLE_MARKER in value && value[TABLE_MARKER] === true;
670
689
  }
@@ -907,6 +926,15 @@ function table(name, shape, options = {}) {
907
926
  key
908
927
  );
909
928
  }
929
+ const targetRefs = getTableMeta(ref.table).references;
930
+ const collidingRef = targetRefs.find((r) => r.as === ref.reverseAs);
931
+ if (collidingRef) {
932
+ throw new TableDefinitionError(
933
+ `Table "${name}": reverse relation "${ref.reverseAs}" (from field "${key}") collides with forward relation "${collidingRef.as}" in target table "${ref.table.name}". Choose a different 'reverseAs' name.`,
934
+ name,
935
+ key
936
+ );
937
+ }
910
938
  }
911
939
  meta.references.push({
912
940
  fieldName: key,
@@ -942,7 +970,19 @@ function table(name, shape, options = {}) {
942
970
  meta.fields[key] = dbMeta;
943
971
  }
944
972
  const schema = z.object(zodShape);
945
- return createTableObject(name, schema, zodShape, meta, options);
973
+ const tableObj = createTableObject(name, schema, zodShape, meta, options);
974
+ for (const ref of meta.references) {
975
+ if (ref.reverseAs) {
976
+ registerReverseRelation(
977
+ ref.table,
978
+ ref.reverseAs,
979
+ () => tableObj,
980
+ name,
981
+ ref.fieldName
982
+ );
983
+ }
984
+ }
985
+ return tableObj;
946
986
  }
947
987
  function createColsProxy(tableName, zodShape) {
948
988
  return new Proxy({}, {
@@ -1020,6 +1060,25 @@ function createTableObject(name, schema, zodShape, meta, options) {
1020
1060
  references() {
1021
1061
  return meta.references;
1022
1062
  },
1063
+ relations() {
1064
+ const result = {};
1065
+ for (const ref of meta.references) {
1066
+ result[ref.as] = {
1067
+ fields: () => ref.table.fields(),
1068
+ table: ref.table
1069
+ };
1070
+ }
1071
+ const reverseMap = getReverseRelations(table2);
1072
+ if (reverseMap) {
1073
+ for (const [reverseName, getSourceTable] of reverseMap) {
1074
+ result[reverseName] = {
1075
+ fields: () => getSourceTable().fields(),
1076
+ table: getSourceTable()
1077
+ };
1078
+ }
1079
+ }
1080
+ return result;
1081
+ },
1023
1082
  deleted() {
1024
1083
  const softDeleteField = meta.softDeleteField;
1025
1084
  if (!softDeleteField) {
@@ -1338,7 +1397,11 @@ function extractFieldMeta(name, zodType, dbMeta) {
1338
1397
  } = unwrapType(zodType);
1339
1398
  const { db: _db, ...userMeta } = collectedMeta;
1340
1399
  const meta = {
1400
+ // New: raw schema access (preferred)
1341
1401
  name,
1402
+ schema: zodType,
1403
+ db: dbMeta,
1404
+ // Deprecated: cooked properties for backwards compatibility
1342
1405
  type: "text",
1343
1406
  required: !isOptional && !isNullable && !hasDefault,
1344
1407
  ...userMeta
@@ -2,7 +2,7 @@ import {
2
2
  isSQLBuiltin,
3
3
  isSQLIdentifier,
4
4
  resolveSQLBuiltin
5
- } from "./chunk-DKLSJISE.js";
5
+ } from "./chunk-NLI6YKQ6.js";
6
6
 
7
7
  // src/impl/sql.ts
8
8
  function quoteIdent(name, dialect) {
@@ -2,8 +2,8 @@ import {
2
2
  generateColumnDDL,
3
3
  generateDDL,
4
4
  generateViewDDL
5
- } from "./chunk-2R6FDKLS.js";
6
- import "./chunk-DKLSJISE.js";
5
+ } from "./chunk-BI2NQCYN.js";
6
+ import "./chunk-NLI6YKQ6.js";
7
7
  export {
8
8
  generateColumnDDL,
9
9
  generateDDL,
package/src/bun.js CHANGED
@@ -3,15 +3,15 @@ import {
3
3
  placeholder,
4
4
  quoteIdent,
5
5
  renderDDL
6
- } from "./_chunks/chunk-2C6KOX4F.js";
6
+ } from "./_chunks/chunk-OT6OQQ6S.js";
7
7
  import {
8
8
  generateDDL,
9
9
  generateViewDDL
10
- } from "./_chunks/chunk-2R6FDKLS.js";
10
+ } from "./_chunks/chunk-BI2NQCYN.js";
11
11
  import {
12
12
  getTableMeta,
13
13
  resolveSQLBuiltin
14
- } from "./_chunks/chunk-DKLSJISE.js";
14
+ } from "./_chunks/chunk-NLI6YKQ6.js";
15
15
 
16
16
  // src/bun.ts
17
17
  import { SQL } from "bun";
@@ -753,7 +753,7 @@ var BunDriver = class {
753
753
  return applied;
754
754
  }
755
755
  async #addColumn(table, fieldName) {
756
- const { generateColumnDDL } = await import("./_chunks/ddl-32B7E53E.js");
756
+ const { generateColumnDDL } = await import("./_chunks/ddl-KUIXILN7.js");
757
757
  const zodType = table.schema.shape[fieldName];
758
758
  const fieldMeta = table.meta.fields[fieldName] || {};
759
759
  const colTemplate = generateColumnDDL(
@@ -125,46 +125,112 @@ export interface FieldDBMeta {
125
125
  export declare function extendZod(zodModule: typeof z): void;
126
126
  export type FieldType = "text" | "textarea" | "email" | "url" | "tel" | "password" | "number" | "integer" | "checkbox" | "select" | "date" | "datetime" | "time" | "json" | "hidden";
127
127
  export interface FieldMeta {
128
+ /** Field name (the key in the schema) */
128
129
  name: string;
130
+ /** Raw Zod schema - use Zod APIs for introspection (isOptional(), etc.) */
131
+ schema: z.ZodType;
132
+ /** Raw database metadata from .db.*() methods */
133
+ db: FieldDBMeta;
134
+ /**
135
+ * @deprecated Use `schema` with Zod APIs instead. This form-specific type
136
+ * inference will move to a separate @b9g/forms package.
137
+ */
129
138
  type: FieldType;
139
+ /**
140
+ * @deprecated Use `!schema.isOptional() && !db.autoIncrement && !db.inserted` instead.
141
+ */
130
142
  required: boolean;
143
+ /**
144
+ * @deprecated Use `db.primaryKey` instead.
145
+ */
131
146
  primaryKey?: boolean;
147
+ /**
148
+ * @deprecated Use `db.unique` instead.
149
+ */
132
150
  unique?: boolean;
151
+ /**
152
+ * @deprecated Use `db.indexed` instead.
153
+ */
133
154
  indexed?: boolean;
155
+ /**
156
+ * @deprecated Use `db.softDelete` instead.
157
+ */
134
158
  softDelete?: boolean;
159
+ /**
160
+ * @deprecated Use `db.reference` instead.
161
+ */
135
162
  reference?: {
136
163
  table: Table;
137
164
  field: string;
138
165
  as: string;
139
166
  onDelete?: "cascade" | "set null" | "restrict";
140
167
  };
168
+ /**
169
+ * @deprecated Use `db.encode` instead.
170
+ */
141
171
  encode?: (value: any) => any;
172
+ /**
173
+ * @deprecated Use `db.decode` instead.
174
+ */
142
175
  decode?: (value: any) => any;
176
+ /**
177
+ * @deprecated Use `db.columnType` instead.
178
+ */
143
179
  columnType?: string;
180
+ /**
181
+ * @deprecated Use `db.autoIncrement` instead.
182
+ */
144
183
  autoIncrement?: boolean;
184
+ /**
185
+ * @deprecated Use `db.inserted` instead.
186
+ */
145
187
  inserted?: {
146
188
  type: "sql" | "symbol" | "function";
147
189
  template?: SQLTemplate;
148
190
  symbol?: SQLBuiltin;
149
191
  fn?: () => unknown;
150
192
  };
193
+ /**
194
+ * @deprecated Use `db.updated` instead.
195
+ */
151
196
  updated?: {
152
197
  type: "sql" | "symbol" | "function";
153
198
  template?: SQLTemplate;
154
199
  symbol?: SQLBuiltin;
155
200
  fn?: () => unknown;
156
201
  };
202
+ /**
203
+ * @deprecated Use `db.upserted` instead.
204
+ */
157
205
  upserted?: {
158
206
  type: "sql" | "symbol" | "function";
159
207
  template?: SQLTemplate;
160
208
  symbol?: SQLBuiltin;
161
209
  fn?: () => unknown;
162
210
  };
211
+ /**
212
+ * @deprecated Extract from Zod schema directly.
213
+ */
163
214
  default?: unknown;
215
+ /**
216
+ * @deprecated Extract from Zod schema directly.
217
+ */
164
218
  maxLength?: number;
219
+ /**
220
+ * @deprecated Extract from Zod schema directly.
221
+ */
165
222
  minLength?: number;
223
+ /**
224
+ * @deprecated Extract from Zod schema directly.
225
+ */
166
226
  min?: number;
227
+ /**
228
+ * @deprecated Extract from Zod schema directly.
229
+ */
167
230
  max?: number;
231
+ /**
232
+ * @deprecated Extract from Zod schema directly (for ZodEnum).
233
+ */
168
234
  options?: readonly string[];
169
235
  /** Additional user-defined metadata from Zod's .meta() (label, helpText, widget, etc.) */
170
236
  [key: string]: unknown;
@@ -306,12 +372,13 @@ type FilterRefs<PickedShape extends ZodRawShape, OriginalRefs extends Record<str
306
372
  [Alias in keyof OriginalRefs as Alias extends keyof RowRefs<PickedShape> ? Alias : never]: OriginalRefs[Alias];
307
373
  };
308
374
  /**
309
- * A relationship jump point for navigating to a referenced table's fields.
375
+ * A relationship navigator for traversing table relations.
376
+ * Used for both forward (many-to-one) and reverse (one-to-many) navigation.
310
377
  */
311
378
  export interface Relation<TargetTable extends Table<any, any>> {
312
- /** Navigate to the target table's fields */
379
+ /** Navigate to the related table's fields */
313
380
  fields(): ReturnType<TargetTable["fields"]>;
314
- /** Direct access to the referenced table */
381
+ /** Direct access to the related table */
315
382
  readonly table: TargetTable;
316
383
  }
317
384
  /**
@@ -355,9 +422,10 @@ export interface Table<T extends ZodRawShape = ZodRawShape, Refs extends Record<
355
422
  * Get the primary key field name.
356
423
  *
357
424
  * @returns The primary key field name, or null if no primary key is defined.
425
+ * @deprecated Use `table.meta.primary` instead.
358
426
  *
359
427
  * @example
360
- * const pk = Users.primaryKey(); // "id"
428
+ * const pk = Users.meta.primary; // "id"
361
429
  */
362
430
  primaryKey(): string | null;
363
431
  /**
@@ -368,7 +436,10 @@ export interface Table<T extends ZodRawShape = ZodRawShape, Refs extends Record<
368
436
  * // → GROUP BY "posts"."id"
369
437
  */
370
438
  readonly primary: SQLTemplate | null;
371
- /** Get all foreign key references */
439
+ /**
440
+ * Get all foreign key references.
441
+ * @deprecated Use `table.meta.references` for raw FK metadata, or `table.relations()` for navigation.
442
+ */
372
443
  references(): ReferenceInfo[];
373
444
  /**
374
445
  * Generate SQL fragment to check if a row is soft-deleted.
@@ -518,6 +589,39 @@ export interface Table<T extends ZodRawShape = ZodRawShape, Refs extends Record<
518
589
  * // → INSERT INTO posts (id, title) VALUES (?, ?), (?, ?)
519
590
  */
520
591
  values(rows: Partial<z.infer<ZodObject<T>>>[]): SQLTemplate;
592
+ /**
593
+ * Navigate table relations (both forward and reverse).
594
+ *
595
+ * Returns an object with relation names as keys. Each key provides
596
+ * a `Relation` with `fields()` and `table` properties.
597
+ *
598
+ * - Forward relations come from `as` in `.db.references(Table, "as")`
599
+ * - Reverse relations come from `reverseAs` in `.db.references(Table, "as", { reverseAs: "..." })`
600
+ *
601
+ * @example
602
+ * const Users = table("users", {
603
+ * id: z.string().db.primary(),
604
+ * email: z.string().email(),
605
+ * });
606
+ *
607
+ * const Posts = table("posts", {
608
+ * id: z.string().db.primary(),
609
+ * title: z.string(),
610
+ * authorId: z.string().db.references(Users, "author", { reverseAs: "posts" }),
611
+ * });
612
+ *
613
+ * // Forward relation (many-to-one): Posts -> Users
614
+ * Posts.relations().author.fields().email // Users.email field metadata
615
+ * Posts.relations().author.table // Users table
616
+ *
617
+ * // Reverse relation (one-to-many): Users -> Posts
618
+ * Users.relations().posts.fields().title // Posts.title field metadata
619
+ * Users.relations().posts.table // Posts table
620
+ *
621
+ * // Chain navigation
622
+ * Users.relations().posts.fields().author.fields().email // Back to Users.email
623
+ */
624
+ relations(): Record<string, Relation<Table<any, any>>>;
521
625
  /**
522
626
  * Get a view that excludes soft-deleted rows.
523
627
  *
@@ -583,6 +687,7 @@ export interface View<T extends ZodRawShape = ZodRawShape, Refs extends Record<s
583
687
  /**
584
688
  * Get reference metadata for foreign key relationships.
585
689
  * Delegates to the base table's references.
690
+ * @deprecated Use `view.baseTable.meta.references` for raw FK metadata, or `view.baseTable.relations()` for navigation.
586
691
  */
587
692
  references(): ReferenceInfo[];
588
693
  /**
package/src/mysql.js CHANGED
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  quoteIdent,
4
4
  renderDDL
5
- } from "./_chunks/chunk-2C6KOX4F.js";
5
+ } from "./_chunks/chunk-OT6OQQ6S.js";
6
6
  import {
7
7
  generateColumnDDL,
8
8
  generateDDL,
9
9
  generateViewDDL
10
- } from "./_chunks/chunk-2R6FDKLS.js";
10
+ } from "./_chunks/chunk-BI2NQCYN.js";
11
11
  import {
12
12
  ConstraintPreflightError,
13
13
  EnsureError,
14
14
  SchemaDriftError,
15
15
  getTableMeta,
16
16
  resolveSQLBuiltin
17
- } from "./_chunks/chunk-DKLSJISE.js";
17
+ } from "./_chunks/chunk-NLI6YKQ6.js";
18
18
 
19
19
  // src/mysql.ts
20
20
  import {
package/src/postgres.js CHANGED
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  quoteIdent,
4
4
  renderDDL
5
- } from "./_chunks/chunk-2C6KOX4F.js";
5
+ } from "./_chunks/chunk-OT6OQQ6S.js";
6
6
  import {
7
7
  generateColumnDDL,
8
8
  generateDDL,
9
9
  generateViewDDL
10
- } from "./_chunks/chunk-2R6FDKLS.js";
10
+ } from "./_chunks/chunk-BI2NQCYN.js";
11
11
  import {
12
12
  ConstraintPreflightError,
13
13
  EnsureError,
14
14
  SchemaDriftError,
15
15
  getTableMeta,
16
16
  resolveSQLBuiltin
17
- } from "./_chunks/chunk-DKLSJISE.js";
17
+ } from "./_chunks/chunk-NLI6YKQ6.js";
18
18
 
19
19
  // src/postgres.ts
20
20
  import {
package/src/sqlite.js CHANGED
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  quoteIdent,
4
4
  renderDDL
5
- } from "./_chunks/chunk-2C6KOX4F.js";
5
+ } from "./_chunks/chunk-OT6OQQ6S.js";
6
6
  import {
7
7
  generateColumnDDL,
8
8
  generateDDL,
9
9
  generateViewDDL
10
- } from "./_chunks/chunk-2R6FDKLS.js";
10
+ } from "./_chunks/chunk-BI2NQCYN.js";
11
11
  import {
12
12
  ConstraintPreflightError,
13
13
  EnsureError,
14
14
  SchemaDriftError,
15
15
  getTableMeta,
16
16
  resolveSQLBuiltin
17
- } from "./_chunks/chunk-DKLSJISE.js";
17
+ } from "./_chunks/chunk-NLI6YKQ6.js";
18
18
 
19
19
  // src/sqlite.ts
20
20
  import {
package/src/zen.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference types="./zen.d.ts" />
2
- import "./_chunks/chunk-2C6KOX4F.js";
2
+ import "./_chunks/chunk-OT6OQQ6S.js";
3
3
  import {
4
4
  AlreadyExistsError,
5
5
  CURRENT_DATE,
@@ -39,7 +39,7 @@ import {
39
39
  table,
40
40
  validateWithStandardSchema,
41
41
  view
42
- } from "./_chunks/chunk-DKLSJISE.js";
42
+ } from "./_chunks/chunk-NLI6YKQ6.js";
43
43
 
44
44
  // src/zen.ts
45
45
  import { z as zod } from "zod";
@@ -87,11 +87,9 @@ function buildEntityMap(rows, tables, driver) {
87
87
  const key = entityKey(table2.name, pk);
88
88
  if (!entities.has(key)) {
89
89
  const decoded = decodeData(table2, data, driver);
90
- const parsed = validateWithStandardSchema(
91
- table2.schema,
92
- decoded
93
- );
94
- entities.set(key, parsed);
90
+ if (decoded) {
91
+ entities.set(key, decoded);
92
+ }
95
93
  }
96
94
  }
97
95
  }
@@ -1236,6 +1234,7 @@ var Database = class extends EventTarget {
1236
1234
  #version = 0;
1237
1235
  #opened = false;
1238
1236
  #tables = [];
1237
+ #inMigrationLock = false;
1239
1238
  constructor(driver, options) {
1240
1239
  super();
1241
1240
  this.#driver = driver;
@@ -1282,9 +1281,19 @@ var Database = class extends EventTarget {
1282
1281
  }
1283
1282
  };
1284
1283
  if (this.#driver.withMigrationLock) {
1285
- await this.#driver.withMigrationLock(runMigration);
1284
+ this.#inMigrationLock = true;
1285
+ try {
1286
+ await this.#driver.withMigrationLock(runMigration);
1287
+ } finally {
1288
+ this.#inMigrationLock = false;
1289
+ }
1286
1290
  } else {
1287
- await this.#driver.transaction(runMigration);
1291
+ this.#inMigrationLock = true;
1292
+ try {
1293
+ await this.#driver.transaction(runMigration);
1294
+ } finally {
1295
+ this.#inMigrationLock = false;
1296
+ }
1288
1297
  }
1289
1298
  this.#version = version;
1290
1299
  this.#opened = true;
@@ -2071,6 +2080,9 @@ var Database = class extends EventTarget {
2071
2080
  );
2072
2081
  }
2073
2082
  const doEnsure = () => this.#driver.ensureTable(table2);
2083
+ if (this.#inMigrationLock) {
2084
+ return await doEnsure();
2085
+ }
2074
2086
  if (this.#driver.withMigrationLock) {
2075
2087
  return await this.#driver.withMigrationLock(doEnsure);
2076
2088
  }
@@ -2094,6 +2106,9 @@ var Database = class extends EventTarget {
2094
2106
  );
2095
2107
  }
2096
2108
  const doEnsure = () => this.#driver.ensureView(viewObj);
2109
+ if (this.#inMigrationLock) {
2110
+ return await doEnsure();
2111
+ }
2097
2112
  if (this.#driver.withMigrationLock) {
2098
2113
  return await this.#driver.withMigrationLock(doEnsure);
2099
2114
  }
@@ -2128,6 +2143,9 @@ var Database = class extends EventTarget {
2128
2143
  );
2129
2144
  }
2130
2145
  const doEnsure = () => this.#driver.ensureConstraints(table2);
2146
+ if (this.#inMigrationLock) {
2147
+ return await doEnsure();
2148
+ }
2131
2149
  if (this.#driver.withMigrationLock) {
2132
2150
  return await this.#driver.withMigrationLock(doEnsure);
2133
2151
  }
@@ -2163,6 +2181,9 @@ var Database = class extends EventTarget {
2163
2181
  }
2164
2182
  if (this.#driver.copyColumn) {
2165
2183
  const doCopy2 = () => this.#driver.copyColumn(table2, fromField, toField);
2184
+ if (this.#inMigrationLock) {
2185
+ return await doCopy2();
2186
+ }
2166
2187
  if (this.#driver.withMigrationLock) {
2167
2188
  return await this.#driver.withMigrationLock(doCopy2);
2168
2189
  }
@@ -2200,6 +2221,9 @@ var Database = class extends EventTarget {
2200
2221
  );
2201
2222
  }
2202
2223
  };
2224
+ if (this.#inMigrationLock) {
2225
+ return await doCopy();
2226
+ }
2203
2227
  if (this.#driver.withMigrationLock) {
2204
2228
  return await this.#driver.withMigrationLock(doCopy);
2205
2229
  }