@syncular/typegen 0.15.26 → 0.15.28

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/README.md CHANGED
@@ -142,7 +142,18 @@ After that, a deployed migration is immutable. Restore an accidentally edited
142
142
  migration and express the repair in a new `NNNN_name/up.sql`; do not delete and
143
143
  re-baseline the lock. Existing table layouts are append-only and added columns
144
144
  must be nullable so stored payloads and every server backend can upgrade
145
- safely. `generate` appends a valid new migration to the lock, while
145
+ safely. A SQL `DEFAULT` does not make a required append safe: existing
146
+ Syncular row payloads remain the source of truth, and no storage backend may
147
+ silently invent a value for them. `baseline`, `migrations check`, and
148
+ `generate` therefore reject `ALTER TABLE … ADD COLUMN … NOT NULL`, including
149
+ one with a literal default.
150
+
151
+ Use an expand/backfill/validate rollout instead: add the trailing column as
152
+ nullable; deploy that schema; fill existing rows with versioned,
153
+ server-authoritative writes under a new idempotency key; then enforce the
154
+ required value in host validation. Do not tighten the synced column's
155
+ nullability in a later migration. `generate` appends a valid new migration to
156
+ the lock, while
146
157
  `generate --check` fails until that updated lock and generated outputs are
147
158
  committed. Drift diagnostics name the locked migration and the first affected
148
159
  table/column without printing SQL or local paths.
@@ -316,10 +327,11 @@ duplicate or unknown-column index; unsupported virtual-table modules or FTS5
316
327
  options; an FTS projection without an owner or with invalid columns;
317
328
  trailing clauses (`STRICT`).
318
329
 
319
- **`DEFAULT` literals are accepted and ignored**: typegen extracts the
320
- schema *shape*; executing migrations (where defaults matter) is the
321
- host's job. Rejecting them would make real-world migrations unusable
322
- as input; recording them is not needed by any emitter today.
330
+ **`DEFAULT` literals on `CREATE TABLE` are accepted and ignored**: typegen
331
+ extracts the schema *shape*; executing migrations (where defaults matter) is
332
+ the host's job. Recording them is not needed by any emitter today. On
333
+ `ALTER TABLE ADD COLUMN`, a literal default does not make a non-null append
334
+ safe and is rejected as described in the migration-lock section above.
323
335
 
324
336
  ## 4. Generated-module contract
325
337
 
package/dist/sql.js CHANGED
@@ -12,7 +12,8 @@
12
12
  * - `CREATE VIRTUAL TABLE name USING fts5(cols…, content=table,
13
13
  * [tokenize='allowlisted tokenizer'])` (RFC 0005 local projection)
14
14
  * - column defs: `name TYPE [PRIMARY KEY] [NOT NULL] [NULL]
15
- * [DEFAULT literal]`
15
+ * [DEFAULT literal]`; `ALTER TABLE … ADD COLUMN` is restricted to nullable
16
+ * columns because Syncular does not execute SQL-default backfills
16
17
  * - `--` and C-style comments
17
18
  *
18
19
  * Anything else — other statements, table constraints, parameterized or
@@ -542,6 +543,9 @@ function parseAlterTable(cursor, tables) {
542
543
  cursor.eatWord('COLUMN');
543
544
  const def = parseColumnDef(cursor, false);
544
545
  cursor.expectEnd();
546
+ if (!def.column.nullable) {
547
+ cursor.fail(`ALTER TABLE ${name}: added column ${JSON.stringify(def.column.name)} must be nullable — SQL defaults do not backfill Syncular row payloads; add the column nullable, backfill it through versioned server-authoritative writes, and enforce required values in application validation`);
548
+ }
545
549
  if (table.columns.some((c) => c.name === def.column.name)) {
546
550
  cursor.fail(`ALTER TABLE ${name}: duplicate column ${JSON.stringify(def.column.name)}`);
547
551
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/typegen",
3
- "version": "0.15.26",
3
+ "version": "0.15.28",
4
4
  "description": "Syncular schema-to-TypeScript type generator and the `syncular` CLI",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -48,7 +48,7 @@
48
48
  "!dist/**/*.test.d.ts"
49
49
  ],
50
50
  "devDependencies": {
51
- "@syncular/core": "0.15.26",
52
- "@syncular/server": "0.15.26"
51
+ "@syncular/core": "0.15.28",
52
+ "@syncular/server": "0.15.28"
53
53
  }
54
54
  }
package/src/sql.ts CHANGED
@@ -12,7 +12,8 @@
12
12
  * - `CREATE VIRTUAL TABLE name USING fts5(cols…, content=table,
13
13
  * [tokenize='allowlisted tokenizer'])` (RFC 0005 local projection)
14
14
  * - column defs: `name TYPE [PRIMARY KEY] [NOT NULL] [NULL]
15
- * [DEFAULT literal]`
15
+ * [DEFAULT literal]`; `ALTER TABLE … ADD COLUMN` is restricted to nullable
16
+ * columns because Syncular does not execute SQL-default backfills
16
17
  * - `--` and C-style comments
17
18
  *
18
19
  * Anything else — other statements, table constraints, parameterized or
@@ -667,6 +668,11 @@ function parseAlterTable(
667
668
  cursor.eatWord('COLUMN');
668
669
  const def = parseColumnDef(cursor, false);
669
670
  cursor.expectEnd();
671
+ if (!def.column.nullable) {
672
+ cursor.fail(
673
+ `ALTER TABLE ${name}: added column ${JSON.stringify(def.column.name)} must be nullable — SQL defaults do not backfill Syncular row payloads; add the column nullable, backfill it through versioned server-authoritative writes, and enforce required values in application validation`,
674
+ );
675
+ }
670
676
  if (table.columns.some((c) => c.name === def.column.name)) {
671
677
  cursor.fail(
672
678
  `ALTER TABLE ${name}: duplicate column ${JSON.stringify(def.column.name)}`,