@syncular/typegen 0.15.11 → 0.15.12

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
@@ -184,6 +184,7 @@ duplicate ordinals are errors). The parser accepts exactly:
184
184
  - `CREATE TABLE [IF NOT EXISTS] name ( column-defs…, [PRIMARY KEY (col)] ) [WITHOUT ROWID]`
185
185
  - `ALTER TABLE name ADD [COLUMN] column-def`
186
186
  - `CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table ( col [, col…] )`
187
+ - `DROP INDEX [IF EXISTS] name`
187
188
  - `DROP TABLE [IF EXISTS] name`
188
189
  - `CREATE VIRTUAL TABLE [IF NOT EXISTS] name USING fts5( text-col [, text-col…], content = table [, tokenize = 'allowlisted tokenizer'] )`
189
190
  - column-def: `name TYPE [PRIMARY KEY] [NOT NULL] [NULL] [DEFAULT literal]`
@@ -217,6 +218,15 @@ is bare column names: **ASC/DESC**, **expression** columns (`lower(a)`), and
217
218
  **partial** (`WHERE …`) indexes are hard errors (the IR models column names
218
219
  only, so accepting a direction/expression would silently drop it).
219
220
 
221
+ **Index replacement** (`DROP INDEX`). A previously declared secondary index
222
+ may be removed with `DROP INDEX name` or conditionally with `IF EXISTS`.
223
+ Generation removes it from the head IR; a later `CREATE INDEX` may reuse the
224
+ name with a different uniqueness or column definition. On a server schema
225
+ bump, Syncular rebuilds the declared secondary indexes for its owned
226
+ relational projection tables before advancing the version marker. Client
227
+ mirrors already wipe and re-bootstrap application tables on every schema
228
+ version change.
229
+
220
230
  **Table retirement** (`DROP TABLE`). A table created earlier in migration
221
231
  history and dropped before the head version is absent from the IR and must be
222
232
  omitted from the manifest's `tables` list. `IF EXISTS` is supported. Reusing a
@@ -246,7 +256,7 @@ There is no automatic `LIKE` fallback when FTS5 is unavailable; local schema
246
256
  creation fails loudly.
247
257
 
248
258
  **Hard errors** (each names the construct and source file): any other
249
- statement (`CREATE TRIGGER/VIEW`, `DROP INDEX`, DML, `ALTER … RENAME`, …); unknown
259
+ statement (`CREATE TRIGGER/VIEW`, DML, `ALTER … RENAME`, …); unknown
250
260
  or parameterized types (`VARCHAR(36)`); quoted identifiers
251
261
  (`"t"`, `` `t` ``, `[t]`); table constraints (`FOREIGN KEY`, `UNIQUE`,
252
262
  `CHECK`, `CONSTRAINT`); column constraints beyond the list above
package/dist/sql.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * [WITHOUT ROWID]`
8
8
  * - `ALTER TABLE name ADD [COLUMN] coldef`
9
9
  * - `CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (col [, col…])`
10
+ * - `DROP INDEX [IF EXISTS] name`
10
11
  * - `DROP TABLE [IF EXISTS] name`
11
12
  * - `CREATE VIRTUAL TABLE name USING fts5(cols…, content=table,
12
13
  * [tokenize='allowlisted tokenizer'])` (RFC 0005 local projection)
@@ -548,7 +549,6 @@ function parseAlterTable(cursor, tables) {
548
549
  }
549
550
  /** `DROP TABLE [IF EXISTS] name`; table-name reuse remains unsupported. */
550
551
  function parseDropTable(cursor, tables, droppedTables) {
551
- cursor.expectWord('TABLE', 'after DROP');
552
552
  let ifExists = false;
553
553
  if (cursor.eatWord('IF')) {
554
554
  cursor.expectWord('EXISTS', 'after IF');
@@ -564,6 +564,38 @@ function parseDropTable(cursor, tables, droppedTables) {
564
564
  tables.delete(name);
565
565
  droppedTables.add(name);
566
566
  }
567
+ /** Remove one declared secondary index from the accumulated head schema. */
568
+ function parseDropIndex(cursor, tables) {
569
+ let ifExists = false;
570
+ if (cursor.eatWord('IF')) {
571
+ cursor.expectWord('EXISTS', 'after IF');
572
+ ifExists = true;
573
+ }
574
+ const name = cursor.identifier('an index name');
575
+ cursor.expectEnd();
576
+ for (const table of tables.values()) {
577
+ const index = table.indexes.findIndex((candidate) => candidate.name === name);
578
+ if (index < 0)
579
+ continue;
580
+ table.indexes.splice(index, 1);
581
+ return;
582
+ }
583
+ if (!ifExists) {
584
+ cursor.fail(`DROP INDEX ${name}: index does not exist at this point`);
585
+ }
586
+ }
587
+ function parseDrop(cursor, tables, droppedTables) {
588
+ if (cursor.eatWord('TABLE')) {
589
+ parseDropTable(cursor, tables, droppedTables);
590
+ return;
591
+ }
592
+ if (cursor.eatWord('INDEX')) {
593
+ parseDropIndex(cursor, tables);
594
+ return;
595
+ }
596
+ const token = cursor.peek();
597
+ cursor.fail(`unsupported DROP statement (only DROP TABLE and DROP INDEX), found ${JSON.stringify(token?.text ?? '')}`);
598
+ }
567
599
  /**
568
600
  * Apply one migration file's SQL to the accumulated table map. Columns
569
601
  * accumulate in declaration order — the §2.4 row-codec positional order.
@@ -580,10 +612,10 @@ export function applyMigrationSql(tables, sql, source, droppedTables = new Set()
580
612
  parseAlterTable(cursor, tables);
581
613
  }
582
614
  else if (word === 'DROP') {
583
- parseDropTable(cursor, tables, droppedTables);
615
+ parseDrop(cursor, tables, droppedTables);
584
616
  }
585
617
  else {
586
- throw new TypegenError(source, `unsupported SQL statement starting with ${JSON.stringify(head.text)} (only CREATE TABLE, CREATE [UNIQUE] INDEX, CREATE VIRTUAL TABLE … USING fts5, ALTER TABLE … ADD COLUMN, and DROP TABLE)`);
618
+ throw new TypegenError(source, `unsupported SQL statement starting with ${JSON.stringify(head.text)} (only CREATE TABLE, CREATE [UNIQUE] INDEX, CREATE VIRTUAL TABLE … USING fts5, ALTER TABLE … ADD COLUMN, DROP INDEX, and DROP TABLE)`);
587
619
  }
588
620
  }
589
621
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/typegen",
3
- "version": "0.15.11",
3
+ "version": "0.15.12",
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.11",
52
- "@syncular/server": "0.15.11"
51
+ "@syncular/core": "0.15.12",
52
+ "@syncular/server": "0.15.12"
53
53
  }
54
54
  }
package/src/sql.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * [WITHOUT ROWID]`
8
8
  * - `ALTER TABLE name ADD [COLUMN] coldef`
9
9
  * - `CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (col [, col…])`
10
+ * - `DROP INDEX [IF EXISTS] name`
10
11
  * - `DROP TABLE [IF EXISTS] name`
11
12
  * - `CREATE VIRTUAL TABLE name USING fts5(cols…, content=table,
12
13
  * [tokenize='allowlisted tokenizer'])` (RFC 0005 local projection)
@@ -680,7 +681,6 @@ function parseDropTable(
680
681
  tables: Map<string, ParsedTable>,
681
682
  droppedTables: Set<string>,
682
683
  ): void {
683
- cursor.expectWord('TABLE', 'after DROP');
684
684
  let ifExists = false;
685
685
  if (cursor.eatWord('IF')) {
686
686
  cursor.expectWord('EXISTS', 'after IF');
@@ -696,6 +696,50 @@ function parseDropTable(
696
696
  droppedTables.add(name);
697
697
  }
698
698
 
699
+ /** Remove one declared secondary index from the accumulated head schema. */
700
+ function parseDropIndex(
701
+ cursor: Cursor,
702
+ tables: Map<string, ParsedTable>,
703
+ ): void {
704
+ let ifExists = false;
705
+ if (cursor.eatWord('IF')) {
706
+ cursor.expectWord('EXISTS', 'after IF');
707
+ ifExists = true;
708
+ }
709
+ const name = cursor.identifier('an index name');
710
+ cursor.expectEnd();
711
+ for (const table of tables.values()) {
712
+ const index = table.indexes.findIndex(
713
+ (candidate) => candidate.name === name,
714
+ );
715
+ if (index < 0) continue;
716
+ table.indexes.splice(index, 1);
717
+ return;
718
+ }
719
+ if (!ifExists) {
720
+ cursor.fail(`DROP INDEX ${name}: index does not exist at this point`);
721
+ }
722
+ }
723
+
724
+ function parseDrop(
725
+ cursor: Cursor,
726
+ tables: Map<string, ParsedTable>,
727
+ droppedTables: Set<string>,
728
+ ): void {
729
+ if (cursor.eatWord('TABLE')) {
730
+ parseDropTable(cursor, tables, droppedTables);
731
+ return;
732
+ }
733
+ if (cursor.eatWord('INDEX')) {
734
+ parseDropIndex(cursor, tables);
735
+ return;
736
+ }
737
+ const token = cursor.peek();
738
+ cursor.fail(
739
+ `unsupported DROP statement (only DROP TABLE and DROP INDEX), found ${JSON.stringify(token?.text ?? '')}`,
740
+ );
741
+ }
742
+
699
743
  /**
700
744
  * Apply one migration file's SQL to the accumulated table map. Columns
701
745
  * accumulate in declaration order — the §2.4 row-codec positional order.
@@ -715,11 +759,11 @@ export function applyMigrationSql(
715
759
  } else if (word === 'ALTER') {
716
760
  parseAlterTable(cursor, tables);
717
761
  } else if (word === 'DROP') {
718
- parseDropTable(cursor, tables, droppedTables);
762
+ parseDrop(cursor, tables, droppedTables);
719
763
  } else {
720
764
  throw new TypegenError(
721
765
  source,
722
- `unsupported SQL statement starting with ${JSON.stringify(head.text)} (only CREATE TABLE, CREATE [UNIQUE] INDEX, CREATE VIRTUAL TABLE … USING fts5, ALTER TABLE … ADD COLUMN, and DROP TABLE)`,
766
+ `unsupported SQL statement starting with ${JSON.stringify(head.text)} (only CREATE TABLE, CREATE [UNIQUE] INDEX, CREATE VIRTUAL TABLE … USING fts5, ALTER TABLE … ADD COLUMN, DROP INDEX, and DROP TABLE)`,
723
767
  );
724
768
  }
725
769
  }