@syncular/typegen 0.3.1 → 0.4.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/dist/emit-dart.js +29 -20
- package/dist/emit-kotlin.js +28 -18
- package/dist/emit-queries-dart.js +2 -2
- package/dist/emit-queries-kotlin.js +2 -9
- package/dist/emit-queries-swift.js +0 -7
- package/package.json +3 -3
- package/src/emit-dart.ts +34 -21
- package/src/emit-kotlin.ts +33 -19
- package/src/emit-queries-dart.ts +2 -2
- package/src/emit-queries-kotlin.ts +7 -14
- package/src/emit-queries-swift.ts +0 -8
package/dist/emit-dart.js
CHANGED
|
@@ -172,26 +172,35 @@ export function emitDartModule(ir, hash) {
|
|
|
172
172
|
for (const line of schemaValue)
|
|
173
173
|
schemaLines.push(line);
|
|
174
174
|
parts.push(schemaLines.join('\n'));
|
|
175
|
-
// Shared row-decode helpers
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
'
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
175
|
+
// Shared row-decode helpers — emitted only when a column type references
|
|
176
|
+
// them. An unused private helper is an ANALYZER diagnostic (unused_element),
|
|
177
|
+
// which the `type=lint` ignore above does not cover, so a schema without
|
|
178
|
+
// boolean or bytes/crdt columns would fail a fatal-warnings `dart analyze`.
|
|
179
|
+
const columnTypes = new Set(ir.tables.flatMap((table) => table.columns.map((column) => column.type)));
|
|
180
|
+
if (columnTypes.has('boolean')) {
|
|
181
|
+
parts.push([
|
|
182
|
+
'/// Lift a SQLite boolean: a real bool, or 0/1 as a number.',
|
|
183
|
+
'bool? _rowBool(Object? value) {',
|
|
184
|
+
' if (value is bool) return value;',
|
|
185
|
+
' if (value is num) return value != 0;',
|
|
186
|
+
' return null;',
|
|
187
|
+
'}',
|
|
188
|
+
].join('\n'));
|
|
189
|
+
}
|
|
190
|
+
if (columnTypes.has('bytes') || columnTypes.has('crdt')) {
|
|
191
|
+
parts.push([
|
|
192
|
+
"/// Decode the core's {'\\$bytes': '<hex>'} marshaling (bytes as hex).",
|
|
193
|
+
'List<int>? _rowBytes(Object? value) {',
|
|
194
|
+
' if (value is! Map) return null;',
|
|
195
|
+
" final hex = value[r'$bytes'];",
|
|
196
|
+
' if (hex is! String || hex.length % 2 != 0) return null;',
|
|
197
|
+
' return [',
|
|
198
|
+
' for (var i = 0; i < hex.length; i += 2)',
|
|
199
|
+
' int.parse(hex.substring(i, i + 2), radix: 16),',
|
|
200
|
+
' ];',
|
|
201
|
+
'}',
|
|
202
|
+
].join('\n'));
|
|
203
|
+
}
|
|
195
204
|
for (const table of ir.tables) {
|
|
196
205
|
parts.push(emitClass(table).join('\n'));
|
|
197
206
|
}
|
package/dist/emit-kotlin.js
CHANGED
|
@@ -177,24 +177,34 @@ export function emitKotlinModule(ir, hash, packageName, objectName) {
|
|
|
177
177
|
schemaLines.push(' }');
|
|
178
178
|
schemaLines.push('}');
|
|
179
179
|
parts.push(schemaLines.join('\n'));
|
|
180
|
-
// Shared row-decode helpers
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
180
|
+
// Shared row-decode helpers — emitted only when a column type references
|
|
181
|
+
// them (the Dart emitter's rule): an unused private fun is dead code in a
|
|
182
|
+
// generated file and an "is never used" inspection in every IDE. The
|
|
183
|
+
// query emitter is unaffected — it declares its own queryRowBool/
|
|
184
|
+
// queryRowBytes copies under distinct names.
|
|
185
|
+
const columnTypes = new Set(ir.tables.flatMap((table) => table.columns.map((column) => column.type)));
|
|
186
|
+
if (columnTypes.has('boolean')) {
|
|
187
|
+
parts.push([
|
|
188
|
+
'/** Lift a SQLite boolean: a real JSON bool, or 0/1 as a number. */',
|
|
189
|
+
'private fun rowBool(value: JsonValue?): Boolean? {',
|
|
190
|
+
' value?.bool?.let { return it }',
|
|
191
|
+
' value?.number?.let { return it != 0.0 }',
|
|
192
|
+
' return null',
|
|
193
|
+
'}',
|
|
194
|
+
].join('\n'));
|
|
195
|
+
}
|
|
196
|
+
if (columnTypes.has('bytes') || columnTypes.has('crdt')) {
|
|
197
|
+
parts.push([
|
|
198
|
+
'/** Decode the core\'s `{"$bytes":"<hex>"}` marshaling into a ByteArray. */',
|
|
199
|
+
'private fun rowBytes(value: JsonValue?): ByteArray? {',
|
|
200
|
+
' val hex = value?.get("\\$bytes")?.string ?: return null',
|
|
201
|
+
' if (hex.length % 2 != 0) return null',
|
|
202
|
+
' return ByteArray(hex.length / 2) { i ->',
|
|
203
|
+
' hex.substring(i * 2, i * 2 + 2).toInt(16).toByte()',
|
|
204
|
+
' }',
|
|
205
|
+
'}',
|
|
206
|
+
].join('\n'));
|
|
207
|
+
}
|
|
198
208
|
for (const table of ir.tables) {
|
|
199
209
|
parts.push(emitDataClass(table).join('\n'));
|
|
200
210
|
}
|
|
@@ -77,9 +77,9 @@ function emitOrderByEnum(query) {
|
|
|
77
77
|
const lines = [];
|
|
78
78
|
lines.push(`/// §6 orderBy allowlist for ${query.name} — checked at generate time.`);
|
|
79
79
|
lines.push(`enum ${typeName(query.name)}OrderBy {`);
|
|
80
|
-
lines.push(query.orderBy.allowed
|
|
80
|
+
lines.push(`${query.orderBy.allowed
|
|
81
81
|
.map((col) => ` ${camelCase(col.langName)}(${quote(col.name)})`)
|
|
82
|
-
.join(',\n')
|
|
82
|
+
.join(',\n')};`);
|
|
83
83
|
lines.push('');
|
|
84
84
|
lines.push(` const ${typeName(query.name)}OrderBy(this.column);`);
|
|
85
85
|
lines.push(' final String column;');
|
|
@@ -9,13 +9,6 @@ const KOTLIN_TYPE = {
|
|
|
9
9
|
blob_ref: 'String',
|
|
10
10
|
crdt: 'ByteArray',
|
|
11
11
|
};
|
|
12
|
-
function pascalCase(name) {
|
|
13
|
-
return name
|
|
14
|
-
.split(/[_-]+/)
|
|
15
|
-
.filter((p) => p.length > 0)
|
|
16
|
-
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
17
|
-
.join('');
|
|
18
|
-
}
|
|
19
12
|
/** Language-facing field name — the pinned §12 naming map. */
|
|
20
13
|
function camelCase(name) {
|
|
21
14
|
return snakeToCamel(name);
|
|
@@ -85,9 +78,9 @@ function emitOrderByEnum(query) {
|
|
|
85
78
|
const lines = [];
|
|
86
79
|
lines.push(`/** §6 orderBy allowlist for ${query.name} — checked at generate time. */`);
|
|
87
80
|
lines.push(`enum class ${typeName(query.name)}OrderBy(val column: String) {`);
|
|
88
|
-
lines.push(query.orderBy.allowed
|
|
81
|
+
lines.push(`${query.orderBy.allowed
|
|
89
82
|
.map((col) => ` ${camelCase(col.langName)}(${quote(col.name)})`)
|
|
90
|
-
.join(',\n')
|
|
83
|
+
.join(',\n')};`);
|
|
91
84
|
lines.push('}');
|
|
92
85
|
return lines;
|
|
93
86
|
}
|
|
@@ -9,13 +9,6 @@ const SWIFT_TYPE = {
|
|
|
9
9
|
blob_ref: 'String',
|
|
10
10
|
crdt: '[UInt8]',
|
|
11
11
|
};
|
|
12
|
-
function pascalCase(name) {
|
|
13
|
-
return name
|
|
14
|
-
.split(/[_-]+/)
|
|
15
|
-
.filter((p) => p.length > 0)
|
|
16
|
-
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
17
|
-
.join('');
|
|
18
|
-
}
|
|
19
12
|
/** Language-facing field name — the pinned §12 naming map. */
|
|
20
13
|
function camelCase(name) {
|
|
21
14
|
return snakeToCamel(name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/typegen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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.
|
|
52
|
-
"@syncular/server": "0.
|
|
51
|
+
"@syncular/core": "0.3.1",
|
|
52
|
+
"@syncular/server": "0.3.1"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/emit-dart.ts
CHANGED
|
@@ -212,28 +212,41 @@ export function emitDartModule(ir: IrDocument, hash: string): string {
|
|
|
212
212
|
for (const line of schemaValue) schemaLines.push(line);
|
|
213
213
|
parts.push(schemaLines.join('\n'));
|
|
214
214
|
|
|
215
|
-
// Shared row-decode helpers
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
' if (value is num) return value != 0;',
|
|
222
|
-
' return null;',
|
|
223
|
-
'}',
|
|
224
|
-
'',
|
|
225
|
-
"/// Decode the core's {'\\$bytes': '<hex>'} marshaling (bytes as hex).",
|
|
226
|
-
'List<int>? _rowBytes(Object? value) {',
|
|
227
|
-
' if (value is! Map) return null;',
|
|
228
|
-
" final hex = value[r'$bytes'];",
|
|
229
|
-
' if (hex is! String || hex.length % 2 != 0) return null;',
|
|
230
|
-
' return [',
|
|
231
|
-
' for (var i = 0; i < hex.length; i += 2)',
|
|
232
|
-
' int.parse(hex.substring(i, i + 2), radix: 16),',
|
|
233
|
-
' ];',
|
|
234
|
-
'}',
|
|
235
|
-
].join('\n'),
|
|
215
|
+
// Shared row-decode helpers — emitted only when a column type references
|
|
216
|
+
// them. An unused private helper is an ANALYZER diagnostic (unused_element),
|
|
217
|
+
// which the `type=lint` ignore above does not cover, so a schema without
|
|
218
|
+
// boolean or bytes/crdt columns would fail a fatal-warnings `dart analyze`.
|
|
219
|
+
const columnTypes = new Set(
|
|
220
|
+
ir.tables.flatMap((table) => table.columns.map((column) => column.type)),
|
|
236
221
|
);
|
|
222
|
+
if (columnTypes.has('boolean')) {
|
|
223
|
+
parts.push(
|
|
224
|
+
[
|
|
225
|
+
'/// Lift a SQLite boolean: a real bool, or 0/1 as a number.',
|
|
226
|
+
'bool? _rowBool(Object? value) {',
|
|
227
|
+
' if (value is bool) return value;',
|
|
228
|
+
' if (value is num) return value != 0;',
|
|
229
|
+
' return null;',
|
|
230
|
+
'}',
|
|
231
|
+
].join('\n'),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
if (columnTypes.has('bytes') || columnTypes.has('crdt')) {
|
|
235
|
+
parts.push(
|
|
236
|
+
[
|
|
237
|
+
"/// Decode the core's {'\\$bytes': '<hex>'} marshaling (bytes as hex).",
|
|
238
|
+
'List<int>? _rowBytes(Object? value) {',
|
|
239
|
+
' if (value is! Map) return null;',
|
|
240
|
+
" final hex = value[r'$bytes'];",
|
|
241
|
+
' if (hex is! String || hex.length % 2 != 0) return null;',
|
|
242
|
+
' return [',
|
|
243
|
+
' for (var i = 0; i < hex.length; i += 2)',
|
|
244
|
+
' int.parse(hex.substring(i, i + 2), radix: 16),',
|
|
245
|
+
' ];',
|
|
246
|
+
'}',
|
|
247
|
+
].join('\n'),
|
|
248
|
+
);
|
|
249
|
+
}
|
|
237
250
|
|
|
238
251
|
for (const table of ir.tables) {
|
|
239
252
|
parts.push(emitClass(table).join('\n'));
|
package/src/emit-kotlin.ts
CHANGED
|
@@ -229,26 +229,40 @@ export function emitKotlinModule(
|
|
|
229
229
|
schemaLines.push('}');
|
|
230
230
|
parts.push(schemaLines.join('\n'));
|
|
231
231
|
|
|
232
|
-
// Shared row-decode helpers
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
' return null',
|
|
240
|
-
'}',
|
|
241
|
-
'',
|
|
242
|
-
'/** Decode the core\'s `{"$bytes":"<hex>"}` marshaling into a ByteArray. */',
|
|
243
|
-
'private fun rowBytes(value: JsonValue?): ByteArray? {',
|
|
244
|
-
' val hex = value?.get("\\$bytes")?.string ?: return null',
|
|
245
|
-
' if (hex.length % 2 != 0) return null',
|
|
246
|
-
' return ByteArray(hex.length / 2) { i ->',
|
|
247
|
-
' hex.substring(i * 2, i * 2 + 2).toInt(16).toByte()',
|
|
248
|
-
' }',
|
|
249
|
-
'}',
|
|
250
|
-
].join('\n'),
|
|
232
|
+
// Shared row-decode helpers — emitted only when a column type references
|
|
233
|
+
// them (the Dart emitter's rule): an unused private fun is dead code in a
|
|
234
|
+
// generated file and an "is never used" inspection in every IDE. The
|
|
235
|
+
// query emitter is unaffected — it declares its own queryRowBool/
|
|
236
|
+
// queryRowBytes copies under distinct names.
|
|
237
|
+
const columnTypes = new Set(
|
|
238
|
+
ir.tables.flatMap((table) => table.columns.map((column) => column.type)),
|
|
251
239
|
);
|
|
240
|
+
if (columnTypes.has('boolean')) {
|
|
241
|
+
parts.push(
|
|
242
|
+
[
|
|
243
|
+
'/** Lift a SQLite boolean: a real JSON bool, or 0/1 as a number. */',
|
|
244
|
+
'private fun rowBool(value: JsonValue?): Boolean? {',
|
|
245
|
+
' value?.bool?.let { return it }',
|
|
246
|
+
' value?.number?.let { return it != 0.0 }',
|
|
247
|
+
' return null',
|
|
248
|
+
'}',
|
|
249
|
+
].join('\n'),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (columnTypes.has('bytes') || columnTypes.has('crdt')) {
|
|
253
|
+
parts.push(
|
|
254
|
+
[
|
|
255
|
+
'/** Decode the core\'s `{"$bytes":"<hex>"}` marshaling into a ByteArray. */',
|
|
256
|
+
'private fun rowBytes(value: JsonValue?): ByteArray? {',
|
|
257
|
+
' val hex = value?.get("\\$bytes")?.string ?: return null',
|
|
258
|
+
' if (hex.length % 2 != 0) return null',
|
|
259
|
+
' return ByteArray(hex.length / 2) { i ->',
|
|
260
|
+
' hex.substring(i * 2, i * 2 + 2).toInt(16).toByte()',
|
|
261
|
+
' }',
|
|
262
|
+
'}',
|
|
263
|
+
].join('\n'),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
252
266
|
|
|
253
267
|
for (const table of ir.tables) {
|
|
254
268
|
parts.push(emitDataClass(table).join('\n'));
|
package/src/emit-queries-dart.ts
CHANGED
|
@@ -102,9 +102,9 @@ function emitOrderByEnum(query: AnalyzedQuery): string[] {
|
|
|
102
102
|
);
|
|
103
103
|
lines.push(`enum ${typeName(query.name)}OrderBy {`);
|
|
104
104
|
lines.push(
|
|
105
|
-
query.orderBy.allowed
|
|
105
|
+
`${query.orderBy.allowed
|
|
106
106
|
.map((col) => ` ${camelCase(col.langName)}(${quote(col.name)})`)
|
|
107
|
-
.join(',\n')
|
|
107
|
+
.join(',\n')};`,
|
|
108
108
|
);
|
|
109
109
|
lines.push('');
|
|
110
110
|
lines.push(` const ${typeName(query.name)}OrderBy(this.column);`);
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
* `q(client, …): List<QRow>` runner + `qTables` constant. The runner binds
|
|
6
6
|
* named params positionally into the wrapper's `query(sql, params)`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Self-contained: declares its own `queryRowBool`/`queryRowBytes` copies of
|
|
9
|
+
* the schema file's row-decode helpers (distinct names — both files are
|
|
10
|
+
* file-`private` in the same package, and the schema file only emits its
|
|
11
|
+
* helpers when a column type needs them). Header carries the IR hash for
|
|
12
|
+
* byte-exact `--check`.
|
|
12
13
|
*/
|
|
13
14
|
import type { IrColumnType } from './ir';
|
|
14
15
|
import { snakeToCamel } from './naming';
|
|
@@ -25,14 +26,6 @@ const KOTLIN_TYPE: Readonly<Record<IrColumnType, string>> = {
|
|
|
25
26
|
crdt: 'ByteArray',
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
function pascalCase(name: string): string {
|
|
29
|
-
return name
|
|
30
|
-
.split(/[_-]+/)
|
|
31
|
-
.filter((p) => p.length > 0)
|
|
32
|
-
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
33
|
-
.join('');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
29
|
/** Language-facing field name — the pinned §12 naming map. */
|
|
37
30
|
function camelCase(name: string): string {
|
|
38
31
|
return snakeToCamel(name);
|
|
@@ -113,9 +106,9 @@ function emitOrderByEnum(query: AnalyzedQuery): string[] {
|
|
|
113
106
|
);
|
|
114
107
|
lines.push(`enum class ${typeName(query.name)}OrderBy(val column: String) {`);
|
|
115
108
|
lines.push(
|
|
116
|
-
query.orderBy.allowed
|
|
109
|
+
`${query.orderBy.allowed
|
|
117
110
|
.map((col) => ` ${camelCase(col.langName)}(${quote(col.name)})`)
|
|
118
|
-
.join(',\n')
|
|
111
|
+
.join(',\n')};`,
|
|
119
112
|
);
|
|
120
113
|
lines.push('}');
|
|
121
114
|
return lines;
|
|
@@ -27,14 +27,6 @@ const SWIFT_TYPE: Readonly<Record<IrColumnType, string>> = {
|
|
|
27
27
|
crdt: '[UInt8]',
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
function pascalCase(name: string): string {
|
|
31
|
-
return name
|
|
32
|
-
.split(/[_-]+/)
|
|
33
|
-
.filter((p) => p.length > 0)
|
|
34
|
-
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
35
|
-
.join('');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
30
|
/** Language-facing field name — the pinned §12 naming map. */
|
|
39
31
|
function camelCase(name: string): string {
|
|
40
32
|
return snakeToCamel(name);
|