@syncular/typegen 0.15.0 → 0.15.1
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 +4 -2
- package/dist/syql-validator.js +19 -4
- package/package.json +3 -3
- package/src/syql-validator.ts +33 -4
package/README.md
CHANGED
|
@@ -470,8 +470,10 @@ For FTS-backed named queries the synthesized type-check database creates the
|
|
|
470
470
|
same declared columns plus `_syncular_source_id UNINDEXED`. `MATCH :query`
|
|
471
471
|
infers a string parameter, and the FTS table reference is folded into its
|
|
472
472
|
owning synced table for dependency and scope-coverage metadata. `bm25`,
|
|
473
|
-
`highlight`, and `snippet`
|
|
474
|
-
|
|
473
|
+
`highlight`, and `snippet` are accepted deterministic FTS5 auxiliary
|
|
474
|
+
functions only when the query references a schema-declared FTS projection;
|
|
475
|
+
they use the normal computed-expression fidelity rules. Arbitrary extension
|
|
476
|
+
functions remain outside the portable profile.
|
|
475
477
|
|
|
476
478
|
**Typing fidelity** (what `bun:sqlite` actually exposes, and its honest
|
|
477
479
|
boundary):
|
package/dist/syql-validator.js
CHANGED
|
@@ -121,6 +121,16 @@ const SQLITE_346_PORTABLE_FUNCTIONS = new Set([
|
|
|
121
121
|
'jsonb_replace',
|
|
122
122
|
'jsonb_set',
|
|
123
123
|
]);
|
|
124
|
+
/**
|
|
125
|
+
* Deterministic FTS5 auxiliary functions available only when the query reads
|
|
126
|
+
* a schema-declared FTS projection. FTS5 is an explicit Syncular client
|
|
127
|
+
* requirement in that case, not an arbitrary host extension.
|
|
128
|
+
*/
|
|
129
|
+
const SQLITE_FTS5_AUXILIARY_FUNCTIONS = new Set([
|
|
130
|
+
'bm25',
|
|
131
|
+
'highlight',
|
|
132
|
+
'snippet',
|
|
133
|
+
]);
|
|
124
134
|
const SQL_PAREN_KEYWORDS = new Set([
|
|
125
135
|
'and',
|
|
126
136
|
'as',
|
|
@@ -248,8 +258,8 @@ class Validator {
|
|
|
248
258
|
const activeStructure = this.#inspect(activeSql, location);
|
|
249
259
|
this.#validateStatementShape(activeSql, activeStructure, logical.declaration, location);
|
|
250
260
|
this.#validateDeterminism(activeSql, logical.declaration.statement.span);
|
|
251
|
-
this.#validatePortableProfile(activeSql, logical.declaration.statement.span);
|
|
252
261
|
const refs = scanTableRefs(activeSql, this.#ir);
|
|
262
|
+
this.#validatePortableProfile(activeSql, logical.declaration.statement.span, refs);
|
|
253
263
|
const bindSymbols = this.#bindSymbols(logical.declaration);
|
|
254
264
|
const bindTypes = this.#resolveBindTypes(logical, activeSql, refs, bindSymbols);
|
|
255
265
|
const sort = this.#validateSort(logical.declaration, activeStructure, location);
|
|
@@ -512,8 +522,11 @@ class Validator {
|
|
|
512
522
|
}
|
|
513
523
|
}
|
|
514
524
|
}
|
|
515
|
-
#validatePortableProfile(sql, span) {
|
|
525
|
+
#validatePortableProfile(sql, span, refs) {
|
|
516
526
|
const tokens = significant(lexSyqlSqlSource(span.file, sql));
|
|
527
|
+
const declaredFtsTables = new Set(this.#ir.tables.flatMap((table) => table.ftsIndexes.map((index) => index.name)));
|
|
528
|
+
const queryRefs = refs ?? scanTableRefs(sql, this.#ir);
|
|
529
|
+
const readsDeclaredFts = queryRefs.some((ref) => declaredFtsTables.has(ref.table));
|
|
517
530
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
518
531
|
const token = tokens[index];
|
|
519
532
|
const lower = tokenLower(token);
|
|
@@ -537,8 +550,10 @@ class Validator {
|
|
|
537
550
|
tokens[closeIndex + 2]?.text === '(') {
|
|
538
551
|
continue;
|
|
539
552
|
}
|
|
540
|
-
|
|
541
|
-
|
|
553
|
+
const allowedFtsAuxiliary = readsDeclaredFts && SQLITE_FTS5_AUXILIARY_FUNCTIONS.has(lower ?? '');
|
|
554
|
+
if (!SQLITE_346_PORTABLE_FUNCTIONS.has(lower ?? '') &&
|
|
555
|
+
!allowedFtsAuxiliary) {
|
|
556
|
+
this.#fail('SYQL6002_INVALID_SQL', token.span, `${token.text}() is not a core built-in in the portable SQLite 3.46.0 profile or an auxiliary function of a referenced schema-declared FTS5 projection`);
|
|
542
557
|
}
|
|
543
558
|
const argumentCount = functionArgumentCount(tokens, index + 1);
|
|
544
559
|
if (lower === 'iif' && argumentCount !== 3) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/typegen",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
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.
|
|
52
|
-
"@syncular/server": "0.15.
|
|
51
|
+
"@syncular/core": "0.15.1",
|
|
52
|
+
"@syncular/server": "0.15.1"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/syql-validator.ts
CHANGED
|
@@ -221,6 +221,16 @@ const SQLITE_346_PORTABLE_FUNCTIONS = new Set([
|
|
|
221
221
|
'jsonb_replace',
|
|
222
222
|
'jsonb_set',
|
|
223
223
|
]);
|
|
224
|
+
/**
|
|
225
|
+
* Deterministic FTS5 auxiliary functions available only when the query reads
|
|
226
|
+
* a schema-declared FTS projection. FTS5 is an explicit Syncular client
|
|
227
|
+
* requirement in that case, not an arbitrary host extension.
|
|
228
|
+
*/
|
|
229
|
+
const SQLITE_FTS5_AUXILIARY_FUNCTIONS = new Set([
|
|
230
|
+
'bm25',
|
|
231
|
+
'highlight',
|
|
232
|
+
'snippet',
|
|
233
|
+
]);
|
|
224
234
|
const SQL_PAREN_KEYWORDS = new Set([
|
|
225
235
|
'and',
|
|
226
236
|
'as',
|
|
@@ -376,12 +386,13 @@ class Validator {
|
|
|
376
386
|
location,
|
|
377
387
|
);
|
|
378
388
|
this.#validateDeterminism(activeSql, logical.declaration.statement.span);
|
|
389
|
+
const refs = scanTableRefs(activeSql, this.#ir);
|
|
379
390
|
this.#validatePortableProfile(
|
|
380
391
|
activeSql,
|
|
381
392
|
logical.declaration.statement.span,
|
|
393
|
+
refs,
|
|
382
394
|
);
|
|
383
395
|
|
|
384
|
-
const refs = scanTableRefs(activeSql, this.#ir);
|
|
385
396
|
const bindSymbols = this.#bindSymbols(logical.declaration);
|
|
386
397
|
const bindTypes = this.#resolveBindTypes(
|
|
387
398
|
logical,
|
|
@@ -792,8 +803,21 @@ class Validator {
|
|
|
792
803
|
}
|
|
793
804
|
}
|
|
794
805
|
|
|
795
|
-
#validatePortableProfile(
|
|
806
|
+
#validatePortableProfile(
|
|
807
|
+
sql: string,
|
|
808
|
+
span: SyqlSourceSpan,
|
|
809
|
+
refs?: readonly TableRef[],
|
|
810
|
+
): void {
|
|
796
811
|
const tokens = significant(lexSyqlSqlSource(span.file, sql));
|
|
812
|
+
const declaredFtsTables = new Set(
|
|
813
|
+
this.#ir.tables.flatMap((table) =>
|
|
814
|
+
table.ftsIndexes.map((index) => index.name),
|
|
815
|
+
),
|
|
816
|
+
);
|
|
817
|
+
const queryRefs = refs ?? scanTableRefs(sql, this.#ir);
|
|
818
|
+
const readsDeclaredFts = queryRefs.some((ref) =>
|
|
819
|
+
declaredFtsTables.has(ref.table),
|
|
820
|
+
);
|
|
797
821
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
798
822
|
const token = tokens[index] as SyqlToken;
|
|
799
823
|
const lower = tokenLower(token);
|
|
@@ -828,11 +852,16 @@ class Validator {
|
|
|
828
852
|
) {
|
|
829
853
|
continue;
|
|
830
854
|
}
|
|
831
|
-
|
|
855
|
+
const allowedFtsAuxiliary =
|
|
856
|
+
readsDeclaredFts && SQLITE_FTS5_AUXILIARY_FUNCTIONS.has(lower ?? '');
|
|
857
|
+
if (
|
|
858
|
+
!SQLITE_346_PORTABLE_FUNCTIONS.has(lower ?? '') &&
|
|
859
|
+
!allowedFtsAuxiliary
|
|
860
|
+
) {
|
|
832
861
|
this.#fail(
|
|
833
862
|
'SYQL6002_INVALID_SQL',
|
|
834
863
|
token.span,
|
|
835
|
-
`${token.text}() is not a core built-in in the portable SQLite 3.46.0 profile`,
|
|
864
|
+
`${token.text}() is not a core built-in in the portable SQLite 3.46.0 profile or an auxiliary function of a referenced schema-declared FTS5 projection`,
|
|
836
865
|
);
|
|
837
866
|
}
|
|
838
867
|
|