@syncular/typegen 0.1.2 → 0.2.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/README.md +436 -94
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +159 -73
- package/dist/emit-dart.d.ts +17 -0
- package/dist/emit-dart.js +201 -0
- package/dist/emit-kotlin.d.ts +16 -0
- package/dist/emit-kotlin.js +201 -0
- package/dist/emit-queries-dart.d.ts +2 -0
- package/dist/emit-queries-dart.js +160 -0
- package/dist/emit-queries-kotlin.d.ts +2 -0
- package/dist/emit-queries-kotlin.js +162 -0
- package/dist/emit-queries-swift.d.ts +2 -0
- package/dist/emit-queries-swift.js +146 -0
- package/dist/emit-queries.d.ts +2 -0
- package/dist/emit-queries.js +129 -0
- package/dist/emit-swift.d.ts +18 -0
- package/dist/emit-swift.js +235 -0
- package/dist/emit.d.ts +16 -0
- package/dist/emit.js +191 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +10 -0
- package/dist/generate.d.ts +53 -18
- package/dist/generate.js +391 -77
- package/dist/index.d.ts +17 -14
- package/dist/index.js +16 -13
- package/dist/init.d.ts +12 -0
- package/dist/init.js +85 -0
- package/dist/ir.d.ts +90 -0
- package/dist/ir.js +94 -0
- package/dist/manifest.d.ts +76 -0
- package/dist/manifest.js +303 -0
- package/dist/query.d.ts +96 -0
- package/dist/query.js +719 -0
- package/dist/sql.d.ts +13 -0
- package/dist/sql.js +440 -0
- package/package.json +14 -34
- package/src/cli.ts +161 -82
- package/src/emit-dart.ts +245 -0
- package/src/emit-kotlin.ts +257 -0
- package/src/emit-queries-dart.ts +203 -0
- package/src/emit-queries-kotlin.ts +209 -0
- package/src/emit-queries-swift.ts +198 -0
- package/src/emit-queries.ts +183 -0
- package/src/emit-swift.ts +295 -0
- package/src/emit.ts +239 -0
- package/src/errors.ts +11 -0
- package/src/generate.ts +574 -105
- package/src/index.ts +16 -13
- package/src/init.ts +100 -0
- package/src/ir.ts +197 -0
- package/src/manifest.ts +445 -0
- package/src/query.ts +918 -0
- package/src/sql.ts +533 -0
- package/dist/app-contract.d.ts +0 -154
- package/dist/app-contract.d.ts.map +0 -1
- package/dist/app-contract.js +0 -250
- package/dist/app-contract.js.map +0 -1
- package/dist/checksums.d.ts +0 -6
- package/dist/checksums.d.ts.map +0 -1
- package/dist/checksums.js +0 -173
- package/dist/checksums.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/introspect-postgres.d.ts +0 -8
- package/dist/introspect-postgres.d.ts.map +0 -1
- package/dist/introspect-postgres.js +0 -94
- package/dist/introspect-postgres.js.map +0 -1
- package/dist/introspect-sqlite.d.ts +0 -10
- package/dist/introspect-sqlite.d.ts.map +0 -1
- package/dist/introspect-sqlite.js +0 -97
- package/dist/introspect-sqlite.js.map +0 -1
- package/dist/introspect.d.ts +0 -8
- package/dist/introspect.d.ts.map +0 -1
- package/dist/introspect.js +0 -18
- package/dist/introspect.js.map +0 -1
- package/dist/map-types.d.ts +0 -20
- package/dist/map-types.d.ts.map +0 -1
- package/dist/map-types.js +0 -151
- package/dist/map-types.js.map +0 -1
- package/dist/render.d.ts +0 -23
- package/dist/render.d.ts.map +0 -1
- package/dist/render.js +0 -140
- package/dist/render.js.map +0 -1
- package/dist/types.d.ts +0 -122
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
- package/src/app-contract.ts +0 -531
- package/src/checksums.ts +0 -257
- package/src/introspect-postgres.ts +0 -149
- package/src/introspect-sqlite.ts +0 -156
- package/src/introspect.ts +0 -36
- package/src/map-types.ts +0 -189
- package/src/render.ts +0 -196
- package/src/types.ts +0 -137
package/src/sql.ts
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL migration subset parser (REVISE B5).
|
|
3
|
+
*
|
|
4
|
+
* Parses exactly the subset v1-style migrations need:
|
|
5
|
+
*
|
|
6
|
+
* - `CREATE TABLE [IF NOT EXISTS] name (columns…, [PRIMARY KEY (col)])
|
|
7
|
+
* [WITHOUT ROWID]`
|
|
8
|
+
* - `ALTER TABLE name ADD [COLUMN] coldef`
|
|
9
|
+
* - `CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (col [, col…])`
|
|
10
|
+
* - column defs: `name TYPE [PRIMARY KEY] [NOT NULL] [NULL]
|
|
11
|
+
* [DEFAULT literal]`
|
|
12
|
+
* - `--` and C-style comments
|
|
13
|
+
*
|
|
14
|
+
* Anything else — other statements, table constraints, parameterized or
|
|
15
|
+
* unknown types, DEFAULT expressions, quoted identifiers, composite
|
|
16
|
+
* primary keys, ASC/DESC or expression index columns, partial (`WHERE`)
|
|
17
|
+
* indexes — is a hard error naming the unsupported construct.
|
|
18
|
+
*/
|
|
19
|
+
import { TypegenError } from './errors';
|
|
20
|
+
import type { IrColumn, IrColumnType, IrIndex } from './ir';
|
|
21
|
+
|
|
22
|
+
/** SQL type keyword → the §2.4 column types. Case-insensitive. */
|
|
23
|
+
const TYPE_MAP: Readonly<Record<string, IrColumnType>> = {
|
|
24
|
+
TEXT: 'string',
|
|
25
|
+
INTEGER: 'integer',
|
|
26
|
+
INT: 'integer',
|
|
27
|
+
BIGINT: 'integer',
|
|
28
|
+
SMALLINT: 'integer',
|
|
29
|
+
REAL: 'float',
|
|
30
|
+
FLOAT: 'float',
|
|
31
|
+
DOUBLE: 'float',
|
|
32
|
+
BOOLEAN: 'boolean',
|
|
33
|
+
BOOL: 'boolean',
|
|
34
|
+
JSON: 'json',
|
|
35
|
+
JSONB: 'json',
|
|
36
|
+
BLOB: 'bytes',
|
|
37
|
+
BYTEA: 'bytes',
|
|
38
|
+
// §5.9 tag 7: a stored-as-TEXT column carrying a canonical BlobRef
|
|
39
|
+
// document. The synthetic keyword designates the semantic type; the local
|
|
40
|
+
// column is TEXT-shaped like `json`.
|
|
41
|
+
BLOB_REF: 'blob_ref',
|
|
42
|
+
BLOBREF: 'blob_ref',
|
|
43
|
+
// §5.10 tag 8: a stored-as-BLOB column carrying opaque server-merged CRDT
|
|
44
|
+
// bytes. The synthetic keyword designates the semantic type; crdtType
|
|
45
|
+
// defaults to the one built-in merger (`yjs-doc`, §5.10.1).
|
|
46
|
+
CRDT: 'crdt',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Default `crdtType` for a bare `CRDT` keyword (§5.10.1). */
|
|
50
|
+
const DEFAULT_CRDT_TYPE = 'yjs-doc';
|
|
51
|
+
|
|
52
|
+
export interface ParsedTable {
|
|
53
|
+
readonly name: string;
|
|
54
|
+
primaryKey: string;
|
|
55
|
+
readonly columns: IrColumn[];
|
|
56
|
+
/** Local secondary indexes, in declaration order (CREATE INDEX subset). */
|
|
57
|
+
readonly indexes: IrIndex[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface Token {
|
|
61
|
+
readonly kind: 'word' | 'string' | 'number' | 'punct';
|
|
62
|
+
readonly text: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const WORD_START = /[A-Za-z_]/;
|
|
66
|
+
const WORD_PART = /[A-Za-z0-9_]/;
|
|
67
|
+
const DIGIT = /[0-9]/;
|
|
68
|
+
|
|
69
|
+
function tokenizeStatements(sql: string, source: string): Token[][] {
|
|
70
|
+
const statements: Token[][] = [];
|
|
71
|
+
let current: Token[] = [];
|
|
72
|
+
let i = 0;
|
|
73
|
+
while (i < sql.length) {
|
|
74
|
+
const ch = sql[i] as string;
|
|
75
|
+
if (/\s/.test(ch)) {
|
|
76
|
+
i += 1;
|
|
77
|
+
} else if (ch === '-' && sql[i + 1] === '-') {
|
|
78
|
+
const end = sql.indexOf('\n', i);
|
|
79
|
+
i = end === -1 ? sql.length : end + 1;
|
|
80
|
+
} else if (ch === '/' && sql[i + 1] === '*') {
|
|
81
|
+
const end = sql.indexOf('*/', i + 2);
|
|
82
|
+
if (end === -1) throw new TypegenError(source, 'unterminated comment');
|
|
83
|
+
i = end + 2;
|
|
84
|
+
} else if (ch === ';') {
|
|
85
|
+
if (current.length > 0) statements.push(current);
|
|
86
|
+
current = [];
|
|
87
|
+
i += 1;
|
|
88
|
+
} else if (ch === "'") {
|
|
89
|
+
let text = '';
|
|
90
|
+
i += 1;
|
|
91
|
+
for (;;) {
|
|
92
|
+
if (i >= sql.length) {
|
|
93
|
+
throw new TypegenError(source, 'unterminated string literal');
|
|
94
|
+
}
|
|
95
|
+
if (sql[i] === "'") {
|
|
96
|
+
if (sql[i + 1] === "'") {
|
|
97
|
+
text += "'";
|
|
98
|
+
i += 2;
|
|
99
|
+
} else {
|
|
100
|
+
i += 1;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
text += sql[i];
|
|
105
|
+
i += 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
current.push({ kind: 'string', text });
|
|
109
|
+
} else if (ch === '"' || ch === '`' || ch === '[') {
|
|
110
|
+
throw new TypegenError(
|
|
111
|
+
source,
|
|
112
|
+
`quoted identifiers are unsupported (found ${JSON.stringify(ch)})`,
|
|
113
|
+
);
|
|
114
|
+
} else if (WORD_START.test(ch)) {
|
|
115
|
+
let end = i + 1;
|
|
116
|
+
while (end < sql.length && WORD_PART.test(sql[end] as string)) end += 1;
|
|
117
|
+
current.push({ kind: 'word', text: sql.slice(i, end) });
|
|
118
|
+
i = end;
|
|
119
|
+
} else if (DIGIT.test(ch)) {
|
|
120
|
+
let end = i + 1;
|
|
121
|
+
while (end < sql.length && /[0-9.]/.test(sql[end] as string)) end += 1;
|
|
122
|
+
current.push({ kind: 'number', text: sql.slice(i, end) });
|
|
123
|
+
i = end;
|
|
124
|
+
} else if (ch === '(' || ch === ')' || ch === ',') {
|
|
125
|
+
current.push({ kind: 'punct', text: ch });
|
|
126
|
+
i += 1;
|
|
127
|
+
} else {
|
|
128
|
+
throw new TypegenError(
|
|
129
|
+
source,
|
|
130
|
+
`unsupported SQL syntax: unexpected character ${JSON.stringify(ch)}`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (current.length > 0) statements.push(current);
|
|
135
|
+
return statements;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class Cursor {
|
|
139
|
+
private index = 0;
|
|
140
|
+
|
|
141
|
+
constructor(
|
|
142
|
+
private readonly tokens: Token[],
|
|
143
|
+
private readonly source: string,
|
|
144
|
+
) {}
|
|
145
|
+
|
|
146
|
+
peek(): Token | undefined {
|
|
147
|
+
return this.tokens[this.index];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
next(): Token {
|
|
151
|
+
const token = this.tokens[this.index];
|
|
152
|
+
if (token === undefined) {
|
|
153
|
+
throw new TypegenError(this.source, 'unexpected end of SQL statement');
|
|
154
|
+
}
|
|
155
|
+
this.index += 1;
|
|
156
|
+
return token;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Consume a word token if it matches (case-insensitive). */
|
|
160
|
+
eatWord(word: string): boolean {
|
|
161
|
+
const token = this.peek();
|
|
162
|
+
if (token?.kind === 'word' && token.text.toUpperCase() === word) {
|
|
163
|
+
this.index += 1;
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
expectWord(word: string, context: string): void {
|
|
170
|
+
const token = this.next();
|
|
171
|
+
if (token.kind !== 'word' || token.text.toUpperCase() !== word) {
|
|
172
|
+
throw new TypegenError(
|
|
173
|
+
this.source,
|
|
174
|
+
`expected ${word} ${context}, found ${JSON.stringify(token.text)}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
expectPunct(punct: string, context: string): void {
|
|
180
|
+
const token = this.next();
|
|
181
|
+
if (token.kind !== 'punct' || token.text !== punct) {
|
|
182
|
+
throw new TypegenError(
|
|
183
|
+
this.source,
|
|
184
|
+
`expected ${JSON.stringify(punct)} ${context}, found ${JSON.stringify(token.text)}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
identifier(context: string): string {
|
|
190
|
+
const token = this.next();
|
|
191
|
+
if (token.kind !== 'word') {
|
|
192
|
+
throw new TypegenError(
|
|
193
|
+
this.source,
|
|
194
|
+
`expected ${context}, found ${JSON.stringify(token.text)}`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return token.text;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
expectEnd(): void {
|
|
201
|
+
const token = this.peek();
|
|
202
|
+
if (token !== undefined) {
|
|
203
|
+
throw new TypegenError(
|
|
204
|
+
this.source,
|
|
205
|
+
`unsupported trailing SQL ${JSON.stringify(token.text)}`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fail(message: string): never {
|
|
211
|
+
throw new TypegenError(this.source, message);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
interface ColumnDef {
|
|
216
|
+
readonly column: IrColumn;
|
|
217
|
+
readonly primaryKey: boolean;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function parseColumnType(cursor: Cursor, columnName: string): IrColumnType {
|
|
221
|
+
const typeWord = cursor.identifier(`a column type for ${columnName}`);
|
|
222
|
+
if (cursor.peek()?.text === '(') {
|
|
223
|
+
cursor.fail(
|
|
224
|
+
`column ${columnName}: parameterized column types are unsupported (${typeWord}(…))`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
const mapped = TYPE_MAP[typeWord.toUpperCase()];
|
|
228
|
+
if (mapped === undefined) {
|
|
229
|
+
cursor.fail(
|
|
230
|
+
`column ${columnName}: unsupported column type ${JSON.stringify(typeWord)}`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
return mapped;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function parseColumnDef(cursor: Cursor, allowPrimaryKey: boolean): ColumnDef {
|
|
237
|
+
const name = cursor.identifier('a column name');
|
|
238
|
+
const type = parseColumnType(cursor, name);
|
|
239
|
+
let nullable = true;
|
|
240
|
+
let primaryKey = false;
|
|
241
|
+
for (;;) {
|
|
242
|
+
const token = cursor.peek();
|
|
243
|
+
if (token === undefined || token.kind === 'punct') break;
|
|
244
|
+
const word = token.text.toUpperCase();
|
|
245
|
+
if (word === 'PRIMARY') {
|
|
246
|
+
cursor.next();
|
|
247
|
+
cursor.expectWord('KEY', 'after PRIMARY');
|
|
248
|
+
if (!allowPrimaryKey) {
|
|
249
|
+
cursor.fail(
|
|
250
|
+
`column ${name}: PRIMARY KEY is not supported on ADD COLUMN`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
primaryKey = true;
|
|
254
|
+
nullable = false;
|
|
255
|
+
} else if (word === 'NOT') {
|
|
256
|
+
cursor.next();
|
|
257
|
+
cursor.expectWord('NULL', 'after NOT');
|
|
258
|
+
nullable = false;
|
|
259
|
+
} else if (word === 'NULL') {
|
|
260
|
+
cursor.next();
|
|
261
|
+
} else if (word === 'DEFAULT') {
|
|
262
|
+
cursor.next();
|
|
263
|
+
const value = cursor.next();
|
|
264
|
+
if (value.kind === 'punct') {
|
|
265
|
+
cursor.fail(
|
|
266
|
+
`column ${name}: DEFAULT expressions are unsupported (only literals)`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
// Literal defaults are accepted and ignored: typegen extracts the
|
|
270
|
+
// schema shape; running migrations is the host's job.
|
|
271
|
+
} else {
|
|
272
|
+
cursor.fail(
|
|
273
|
+
`column ${name}: unsupported column constraint ${JSON.stringify(token.text)}`,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (primaryKey) nullable = false;
|
|
278
|
+
// §5.10.1: a crdt column carries a crdtType (default `yjs-doc`).
|
|
279
|
+
const column: IrColumn =
|
|
280
|
+
type === 'crdt'
|
|
281
|
+
? { name, type, nullable, crdtType: DEFAULT_CRDT_TYPE }
|
|
282
|
+
: { name, type, nullable };
|
|
283
|
+
return { column, primaryKey };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Dispatch a `CREATE …` statement: `CREATE TABLE`, `CREATE INDEX`, or
|
|
288
|
+
* `CREATE UNIQUE INDEX`. Anything else is a hard error naming the construct.
|
|
289
|
+
*/
|
|
290
|
+
function parseCreate(
|
|
291
|
+
cursor: Cursor,
|
|
292
|
+
tables: Map<string, ParsedTable>,
|
|
293
|
+
source: string,
|
|
294
|
+
): void {
|
|
295
|
+
if (cursor.eatWord('TABLE')) {
|
|
296
|
+
parseCreateTable(cursor, tables, source);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (cursor.eatWord('UNIQUE')) {
|
|
300
|
+
cursor.expectWord('INDEX', 'after CREATE UNIQUE');
|
|
301
|
+
parseCreateIndex(cursor, tables, true);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (cursor.eatWord('INDEX')) {
|
|
305
|
+
parseCreateIndex(cursor, tables, false);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const token = cursor.peek();
|
|
309
|
+
cursor.fail(
|
|
310
|
+
`unsupported CREATE statement (only CREATE TABLE and CREATE [UNIQUE] INDEX), found ${JSON.stringify(token?.text ?? '')}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function parseCreateTable(
|
|
315
|
+
cursor: Cursor,
|
|
316
|
+
tables: Map<string, ParsedTable>,
|
|
317
|
+
source: string,
|
|
318
|
+
): void {
|
|
319
|
+
if (cursor.eatWord('IF')) {
|
|
320
|
+
cursor.expectWord('NOT', 'after IF');
|
|
321
|
+
cursor.expectWord('EXISTS', 'after IF NOT');
|
|
322
|
+
}
|
|
323
|
+
const name = cursor.identifier('a table name');
|
|
324
|
+
if (tables.has(name)) {
|
|
325
|
+
cursor.fail(`table ${name} is created twice`);
|
|
326
|
+
}
|
|
327
|
+
cursor.expectPunct('(', `after CREATE TABLE ${name}`);
|
|
328
|
+
const columns: IrColumn[] = [];
|
|
329
|
+
const primaryKeys: string[] = [];
|
|
330
|
+
for (;;) {
|
|
331
|
+
const head = cursor.peek();
|
|
332
|
+
if (head?.kind === 'word') {
|
|
333
|
+
const word = head.text.toUpperCase();
|
|
334
|
+
if (word === 'PRIMARY') {
|
|
335
|
+
cursor.next();
|
|
336
|
+
cursor.expectWord('KEY', 'after PRIMARY');
|
|
337
|
+
cursor.expectPunct('(', 'after PRIMARY KEY');
|
|
338
|
+
primaryKeys.push(cursor.identifier('a primary-key column name'));
|
|
339
|
+
if (cursor.peek()?.text === ',') {
|
|
340
|
+
cursor.fail(`table ${name}: composite primary keys are unsupported`);
|
|
341
|
+
}
|
|
342
|
+
cursor.expectPunct(')', 'after the PRIMARY KEY column');
|
|
343
|
+
} else if (
|
|
344
|
+
word === 'FOREIGN' ||
|
|
345
|
+
word === 'UNIQUE' ||
|
|
346
|
+
word === 'CHECK' ||
|
|
347
|
+
word === 'CONSTRAINT'
|
|
348
|
+
) {
|
|
349
|
+
cursor.fail(
|
|
350
|
+
`table ${name}: unsupported table constraint ${JSON.stringify(head.text)}`,
|
|
351
|
+
);
|
|
352
|
+
} else {
|
|
353
|
+
const def = parseColumnDef(cursor, true);
|
|
354
|
+
if (columns.some((c) => c.name === def.column.name)) {
|
|
355
|
+
cursor.fail(
|
|
356
|
+
`table ${name}: duplicate column ${JSON.stringify(def.column.name)}`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
columns.push(def.column);
|
|
360
|
+
if (def.primaryKey) primaryKeys.push(def.column.name);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
cursor.fail(`table ${name}: expected a column definition`);
|
|
364
|
+
}
|
|
365
|
+
const sep = cursor.next();
|
|
366
|
+
if (sep.kind === 'punct' && sep.text === ',') continue;
|
|
367
|
+
if (sep.kind === 'punct' && sep.text === ')') break;
|
|
368
|
+
cursor.fail(
|
|
369
|
+
`table ${name}: expected "," or ")", found ${JSON.stringify(sep.text)}`,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
if (cursor.eatWord('WITHOUT')) {
|
|
373
|
+
cursor.expectWord('ROWID', 'after WITHOUT');
|
|
374
|
+
}
|
|
375
|
+
cursor.expectEnd();
|
|
376
|
+
if (primaryKeys.length === 0) {
|
|
377
|
+
throw new TypegenError(source, `table ${name} declares no primary key`);
|
|
378
|
+
}
|
|
379
|
+
if (primaryKeys.length > 1) {
|
|
380
|
+
throw new TypegenError(
|
|
381
|
+
source,
|
|
382
|
+
`table ${name}: composite primary keys are unsupported (${primaryKeys.join(', ')})`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const primaryKey = primaryKeys[0] as string;
|
|
386
|
+
const pkColumn = columns.find((c) => c.name === primaryKey);
|
|
387
|
+
if (pkColumn === undefined) {
|
|
388
|
+
throw new TypegenError(
|
|
389
|
+
source,
|
|
390
|
+
`table ${name}: primary key ${JSON.stringify(primaryKey)} is not a column`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
const finalColumns = columns.map((c) =>
|
|
394
|
+
c.name === primaryKey ? { ...c, nullable: false } : c,
|
|
395
|
+
);
|
|
396
|
+
tables.set(name, { name, primaryKey, columns: finalColumns, indexes: [] });
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* `CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (col [, col…])`.
|
|
401
|
+
* The `UNIQUE` keyword is already consumed by the dispatcher (it precedes
|
|
402
|
+
* `INDEX`), so `unique` is passed in. The subset keeps the column list to
|
|
403
|
+
* bare column names of the target table: ASC/DESC, expressions, and a `WHERE`
|
|
404
|
+
* (partial index) clause are hard errors naming the construct. Index names
|
|
405
|
+
* are unique per accumulated schema.
|
|
406
|
+
*/
|
|
407
|
+
function parseCreateIndex(
|
|
408
|
+
cursor: Cursor,
|
|
409
|
+
tables: Map<string, ParsedTable>,
|
|
410
|
+
unique: boolean,
|
|
411
|
+
): void {
|
|
412
|
+
// `INDEX` already matched by the dispatcher.
|
|
413
|
+
if (cursor.eatWord('IF')) {
|
|
414
|
+
cursor.expectWord('NOT', 'after IF');
|
|
415
|
+
cursor.expectWord('EXISTS', 'after IF NOT');
|
|
416
|
+
}
|
|
417
|
+
const name = cursor.identifier('an index name');
|
|
418
|
+
for (const table of tables.values()) {
|
|
419
|
+
if (table.indexes.some((i) => i.name === name)) {
|
|
420
|
+
cursor.fail(`index ${name} is created twice`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
cursor.expectWord('ON', `after CREATE INDEX ${name}`);
|
|
424
|
+
const tableName = cursor.identifier('a table name');
|
|
425
|
+
const table = tables.get(tableName);
|
|
426
|
+
if (table === undefined) {
|
|
427
|
+
cursor.fail(
|
|
428
|
+
`CREATE INDEX ${name}: table ${tableName} does not exist at this point`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
cursor.expectPunct('(', `after CREATE INDEX ${name} ON ${tableName}`);
|
|
432
|
+
const columns: string[] = [];
|
|
433
|
+
for (;;) {
|
|
434
|
+
const col = cursor.identifier(`an index column for ${name}`);
|
|
435
|
+
// Reject ASC/DESC and any expression tail: the IR stores bare column names
|
|
436
|
+
// only, so accepting a direction would silently drop it. A following word
|
|
437
|
+
// (not "," or ")") is such an unsupported construct.
|
|
438
|
+
const after = cursor.peek();
|
|
439
|
+
if (
|
|
440
|
+
after !== undefined &&
|
|
441
|
+
!(after.kind === 'punct' && (after.text === ',' || after.text === ')'))
|
|
442
|
+
) {
|
|
443
|
+
const dir = after.text.toUpperCase();
|
|
444
|
+
if (dir === 'ASC' || dir === 'DESC') {
|
|
445
|
+
cursor.fail(
|
|
446
|
+
`index ${name}: ASC/DESC index columns are unsupported (found ${JSON.stringify(after.text)} after ${JSON.stringify(col)})`,
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
cursor.fail(
|
|
450
|
+
`index ${name}: expression index columns are unsupported (found ${JSON.stringify(after.text)} after ${JSON.stringify(col)})`,
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (table.columns.every((c) => c.name !== col)) {
|
|
454
|
+
cursor.fail(
|
|
455
|
+
`index ${name}: column ${JSON.stringify(col)} does not exist on table ${tableName}`,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (columns.includes(col)) {
|
|
459
|
+
cursor.fail(`index ${name}: column ${JSON.stringify(col)} appears twice`);
|
|
460
|
+
}
|
|
461
|
+
columns.push(col);
|
|
462
|
+
const sep = cursor.next();
|
|
463
|
+
if (sep.kind === 'punct' && sep.text === ',') continue;
|
|
464
|
+
if (sep.kind === 'punct' && sep.text === ')') break;
|
|
465
|
+
cursor.fail(
|
|
466
|
+
`index ${name}: expected "," or ")", found ${JSON.stringify(sep.text)}`,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
// A trailing token here is a partial-index `WHERE`, `COLLATE`, etc.
|
|
470
|
+
const trailing = cursor.peek();
|
|
471
|
+
if (trailing !== undefined) {
|
|
472
|
+
if (trailing.kind === 'word' && trailing.text.toUpperCase() === 'WHERE') {
|
|
473
|
+
cursor.fail(`index ${name}: partial indexes (WHERE …) are unsupported`);
|
|
474
|
+
}
|
|
475
|
+
cursor.fail(
|
|
476
|
+
`index ${name}: unsupported trailing SQL ${JSON.stringify(trailing.text)}`,
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
table.indexes.push({ name, columns, unique });
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function parseAlterTable(
|
|
483
|
+
cursor: Cursor,
|
|
484
|
+
tables: Map<string, ParsedTable>,
|
|
485
|
+
): void {
|
|
486
|
+
cursor.expectWord('TABLE', 'after ALTER');
|
|
487
|
+
const name = cursor.identifier('a table name');
|
|
488
|
+
const table = tables.get(name);
|
|
489
|
+
if (table === undefined) {
|
|
490
|
+
cursor.fail(`ALTER TABLE ${name}: table does not exist at this point`);
|
|
491
|
+
}
|
|
492
|
+
const action = cursor.identifier('an ALTER TABLE action');
|
|
493
|
+
if (action.toUpperCase() !== 'ADD') {
|
|
494
|
+
cursor.fail(
|
|
495
|
+
`ALTER TABLE ${name}: unsupported action ${JSON.stringify(action)} (only ADD COLUMN)`,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
cursor.eatWord('COLUMN');
|
|
499
|
+
const def = parseColumnDef(cursor, false);
|
|
500
|
+
cursor.expectEnd();
|
|
501
|
+
if (table.columns.some((c) => c.name === def.column.name)) {
|
|
502
|
+
cursor.fail(
|
|
503
|
+
`ALTER TABLE ${name}: duplicate column ${JSON.stringify(def.column.name)}`,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
table.columns.push(def.column);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Apply one migration file's SQL to the accumulated table map. Columns
|
|
511
|
+
* accumulate in declaration order — the §2.4 row-codec positional order.
|
|
512
|
+
*/
|
|
513
|
+
export function applyMigrationSql(
|
|
514
|
+
tables: Map<string, ParsedTable>,
|
|
515
|
+
sql: string,
|
|
516
|
+
source: string,
|
|
517
|
+
): void {
|
|
518
|
+
for (const tokens of tokenizeStatements(sql, source)) {
|
|
519
|
+
const cursor = new Cursor(tokens, source);
|
|
520
|
+
const head = cursor.next();
|
|
521
|
+
const word = head.kind === 'word' ? head.text.toUpperCase() : '';
|
|
522
|
+
if (word === 'CREATE') {
|
|
523
|
+
parseCreate(cursor, tables, source);
|
|
524
|
+
} else if (word === 'ALTER') {
|
|
525
|
+
parseAlterTable(cursor, tables);
|
|
526
|
+
} else {
|
|
527
|
+
throw new TypegenError(
|
|
528
|
+
source,
|
|
529
|
+
`unsupported SQL statement starting with ${JSON.stringify(head.text)} (only CREATE TABLE, CREATE [UNIQUE] INDEX, and ALTER TABLE … ADD COLUMN)`,
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
package/dist/app-contract.d.ts
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import type { DefinedMigrations } from '@syncular/migrations';
|
|
2
|
-
import type { TableSchema, TypegenDialect } from './types';
|
|
3
|
-
export type SyncularScopeSource = 'actorId' | 'projectId';
|
|
4
|
-
export type SyncularCrdtYjsKind = 'text' | 'xml-fragment' | 'prosemirror';
|
|
5
|
-
export type SyncularCrdtYjsSyncMode = 'server-merge' | 'encrypted-update-log';
|
|
6
|
-
export interface SyncularScopeDefinition {
|
|
7
|
-
name?: string;
|
|
8
|
-
column: string;
|
|
9
|
-
source: SyncularScopeSource;
|
|
10
|
-
required?: boolean;
|
|
11
|
-
}
|
|
12
|
-
export interface SyncularCrdtYjsFieldDefinition {
|
|
13
|
-
field?: string;
|
|
14
|
-
stateColumn: string;
|
|
15
|
-
containerKey?: string;
|
|
16
|
-
rowIdField?: string;
|
|
17
|
-
kind?: SyncularCrdtYjsKind;
|
|
18
|
-
syncMode?: SyncularCrdtYjsSyncMode;
|
|
19
|
-
}
|
|
20
|
-
export interface SyncularEncryptedFieldDefinition {
|
|
21
|
-
field: string;
|
|
22
|
-
scope?: string;
|
|
23
|
-
rowIdField?: string;
|
|
24
|
-
}
|
|
25
|
-
export interface SyncedTableDefinition {
|
|
26
|
-
table: string;
|
|
27
|
-
subscriptionId?: string;
|
|
28
|
-
subscriptionParams?: Record<string, unknown>;
|
|
29
|
-
scopes?: readonly SyncularScopeDefinition[];
|
|
30
|
-
serverVersion: string;
|
|
31
|
-
blobColumns?: readonly string[];
|
|
32
|
-
crdt?: Record<string, SyncularCrdtYjsFieldDefinition>;
|
|
33
|
-
crdtYjsFields?: readonly SyncularCrdtYjsFieldDefinition[];
|
|
34
|
-
encryptedFields?: readonly SyncularEncryptedFieldDefinition[];
|
|
35
|
-
softDelete?: string;
|
|
36
|
-
sqliteWithoutRowid?: boolean;
|
|
37
|
-
}
|
|
38
|
-
export interface SyncularCountByReadModelDefinition {
|
|
39
|
-
name: string;
|
|
40
|
-
kind: 'countBy';
|
|
41
|
-
sourceTable: string;
|
|
42
|
-
outputTable: string;
|
|
43
|
-
dimensions: readonly string[];
|
|
44
|
-
countColumn?: string;
|
|
45
|
-
}
|
|
46
|
-
export type SyncularLocalReadModelDefinition = SyncularCountByReadModelDefinition;
|
|
47
|
-
export interface SyncularClientSchemaSupportDefinition {
|
|
48
|
-
minSupported?: number;
|
|
49
|
-
supported?: readonly number[];
|
|
50
|
-
}
|
|
51
|
-
export interface SyncularCodegenPathsDefinition {
|
|
52
|
-
schemaOutputPath?: string;
|
|
53
|
-
typescriptOutputPath?: string;
|
|
54
|
-
typescriptServerOutputPath?: string;
|
|
55
|
-
typescriptRuntimeImportPath?: string;
|
|
56
|
-
rustRuntimeCratePath?: string;
|
|
57
|
-
nativeSwiftOutputPath?: string;
|
|
58
|
-
nativeKotlinOutputPath?: string;
|
|
59
|
-
nativeAndroidKotlinOutputPath?: string;
|
|
60
|
-
nativeAndroidKotlinPackage?: string;
|
|
61
|
-
}
|
|
62
|
-
export interface DefineSyncularClientOptions<Tables extends Record<string, SyncedTableDefinition>> extends SyncularCodegenPathsDefinition {
|
|
63
|
-
migrations?: unknown;
|
|
64
|
-
tables: Tables;
|
|
65
|
-
localOnlyTables?: readonly string[];
|
|
66
|
-
localReadModels?: readonly SyncularLocalReadModelDefinition[];
|
|
67
|
-
clientSchemaSupport?: SyncularClientSchemaSupportDefinition;
|
|
68
|
-
}
|
|
69
|
-
export interface SyncularClientContract<Tables extends Record<string, SyncedTableDefinition> = Record<string, SyncedTableDefinition>> extends DefineSyncularClientOptions<Tables> {
|
|
70
|
-
readonly kind: 'syncular-client-contract';
|
|
71
|
-
}
|
|
72
|
-
export interface SyncularCodegenScopeConfig {
|
|
73
|
-
name?: string;
|
|
74
|
-
column: string;
|
|
75
|
-
source: SyncularScopeSource;
|
|
76
|
-
required?: boolean;
|
|
77
|
-
}
|
|
78
|
-
export interface SyncularCodegenCrdtYjsFieldConfig {
|
|
79
|
-
field: string;
|
|
80
|
-
stateColumn: string;
|
|
81
|
-
containerKey?: string;
|
|
82
|
-
rowIdField?: string;
|
|
83
|
-
kind?: SyncularCrdtYjsKind;
|
|
84
|
-
syncMode?: SyncularCrdtYjsSyncMode;
|
|
85
|
-
}
|
|
86
|
-
export interface SyncularCodegenEncryptedFieldConfig {
|
|
87
|
-
field: string;
|
|
88
|
-
scope?: string;
|
|
89
|
-
rowIdField?: string;
|
|
90
|
-
}
|
|
91
|
-
export interface SyncularCodegenTableConfig {
|
|
92
|
-
subscriptionId?: string;
|
|
93
|
-
subscriptionParams?: Record<string, unknown>;
|
|
94
|
-
scopes?: SyncularCodegenScopeConfig[];
|
|
95
|
-
serverVersionColumn: string;
|
|
96
|
-
blobColumns?: string[];
|
|
97
|
-
crdtYjsFields?: SyncularCodegenCrdtYjsFieldConfig[];
|
|
98
|
-
encryptedFields?: SyncularCodegenEncryptedFieldConfig[];
|
|
99
|
-
softDeleteColumn?: string;
|
|
100
|
-
sqliteWithoutRowid?: boolean;
|
|
101
|
-
}
|
|
102
|
-
export interface SyncularCodegenLocalReadModelConfig {
|
|
103
|
-
name: string;
|
|
104
|
-
kind: 'countBy';
|
|
105
|
-
sourceTable: string;
|
|
106
|
-
outputTable: string;
|
|
107
|
-
dimensions: string[];
|
|
108
|
-
countColumn: string;
|
|
109
|
-
}
|
|
110
|
-
export interface SyncularCodegenConfig extends SyncularCodegenPathsDefinition {
|
|
111
|
-
tables: Record<string, SyncularCodegenTableConfig>;
|
|
112
|
-
localOnlyTables?: string[];
|
|
113
|
-
localReadModels?: SyncularCodegenLocalReadModelConfig[];
|
|
114
|
-
clientSchemaSupport?: {
|
|
115
|
-
minSupported?: number;
|
|
116
|
-
supported?: number[];
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
export interface ScaffoldSyncularClientContractOptions<DB = unknown> extends SyncularCodegenPathsDefinition {
|
|
120
|
-
migrations: DefinedMigrations<DB>;
|
|
121
|
-
dialect?: TypegenDialect;
|
|
122
|
-
tables?: readonly string[];
|
|
123
|
-
scopes?: Record<string, readonly SyncularScopeDefinition[]>;
|
|
124
|
-
serverVersionColumn?: string | Record<string, string> | ((table: TableSchema) => string);
|
|
125
|
-
subscriptionId?: string | Record<string, string> | ((table: string) => string);
|
|
126
|
-
sqliteWithoutRowid?: boolean | Record<string, boolean> | ((table: TableSchema) => boolean);
|
|
127
|
-
clientSchemaSupport?: SyncularClientSchemaSupportDefinition;
|
|
128
|
-
}
|
|
129
|
-
export declare function defineSyncularClient<Tables extends Record<string, SyncedTableDefinition>>(options: DefineSyncularClientOptions<Tables>): SyncularClientContract<Tables>;
|
|
130
|
-
export declare function isSyncularClientContract(value: unknown): value is SyncularClientContract;
|
|
131
|
-
export declare function syncedTable(options: SyncedTableDefinition): SyncedTableDefinition;
|
|
132
|
-
export declare function scope(name: string, options: {
|
|
133
|
-
column?: string;
|
|
134
|
-
source: SyncularScopeSource;
|
|
135
|
-
required?: boolean;
|
|
136
|
-
}): SyncularScopeDefinition;
|
|
137
|
-
export declare function yjsText(options: Omit<SyncularCrdtYjsFieldDefinition, 'kind'>): SyncularCrdtYjsFieldDefinition;
|
|
138
|
-
export declare function encryptedField(field: string, options?: Omit<SyncularEncryptedFieldDefinition, 'field'>): SyncularEncryptedFieldDefinition;
|
|
139
|
-
export declare function countByReadModel(options: Omit<SyncularCountByReadModelDefinition, 'kind'>): SyncularCountByReadModelDefinition;
|
|
140
|
-
export declare function toSyncularCodegenConfig(contract: SyncularClientContract): SyncularCodegenConfig;
|
|
141
|
-
export declare function toSyncularCodegenJson(contract: SyncularClientContract, space?: number): string;
|
|
142
|
-
export declare function writeSyncularCodegenJson(contract: SyncularClientContract, outputPath?: string | URL, space?: number): Promise<void>;
|
|
143
|
-
export interface LoadSyncularClientContractOptions {
|
|
144
|
-
modulePath: string | URL;
|
|
145
|
-
exportName?: string;
|
|
146
|
-
}
|
|
147
|
-
export declare function loadSyncularClientContract(options: LoadSyncularClientContractOptions): Promise<SyncularClientContract>;
|
|
148
|
-
export interface WriteSyncularCodegenJsonFromModuleOptions extends LoadSyncularClientContractOptions {
|
|
149
|
-
outputPath?: string | URL;
|
|
150
|
-
space?: number;
|
|
151
|
-
}
|
|
152
|
-
export declare function writeSyncularCodegenJsonFromModule(options: WriteSyncularCodegenJsonFromModuleOptions): Promise<SyncularClientContract>;
|
|
153
|
-
export declare function scaffoldSyncularClientContract<DB = unknown>(options: ScaffoldSyncularClientContractOptions<DB>): Promise<SyncularClientContract<Record<string, SyncedTableDefinition>>>;
|
|
154
|
-
//# sourceMappingURL=app-contract.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-contract.d.ts","sourceRoot":"","sources":["../src/app-contract.ts"],"names":[],"mappings":"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"}
|