@syncular/typegen 0.2.0 → 0.3.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 +25 -10
- package/dist/cli.js +155 -4
- package/dist/emit-dart.js +3 -2
- package/dist/emit-kotlin.js +3 -2
- package/dist/emit-queries-dart.js +87 -15
- package/dist/emit-queries-kotlin.js +89 -15
- package/dist/emit-queries-swift.js +92 -14
- package/dist/emit-queries.js +131 -22
- package/dist/emit-swift.js +3 -2
- package/dist/emit.d.ts +2 -1
- package/dist/emit.js +13 -27
- package/dist/fmt.d.ts +3 -0
- package/dist/fmt.js +356 -0
- package/dist/generate.d.ts +16 -6
- package/dist/generate.js +34 -9
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/lower.d.ts +54 -0
- package/dist/lower.js +283 -0
- package/dist/lsp.d.ts +20 -0
- package/dist/lsp.js +376 -0
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.js +20 -0
- package/dist/naming.d.ts +22 -0
- package/dist/naming.js +240 -0
- package/dist/query-ir.d.ts +14 -0
- package/dist/query-ir.js +69 -0
- package/dist/query.d.ts +102 -6
- package/dist/query.js +126 -34
- package/dist/syql.d.ts +83 -0
- package/dist/syql.js +945 -0
- package/package.json +4 -4
- package/src/cli.ts +155 -4
- package/src/emit-dart.ts +3 -2
- package/src/emit-kotlin.ts +3 -2
- package/src/emit-queries-dart.ts +109 -16
- package/src/emit-queries-kotlin.ts +111 -18
- package/src/emit-queries-swift.ts +106 -17
- package/src/emit-queries.ts +179 -28
- package/src/emit-swift.ts +3 -2
- package/src/emit.ts +18 -28
- package/src/fmt.ts +351 -0
- package/src/generate.ts +54 -11
- package/src/index.ts +6 -0
- package/src/lower.ts +331 -0
- package/src/lsp.ts +445 -0
- package/src/manifest.ts +38 -0
- package/src/naming.ts +271 -0
- package/src/query-ir.ts +82 -0
- package/src/query.ts +241 -34
- package/src/syql.ts +1171 -0
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* separate output). Header carries the IR hash for byte-exact `--check`.
|
|
12
12
|
*/
|
|
13
13
|
import type { IrColumnType } from './ir';
|
|
14
|
-
import
|
|
14
|
+
import { snakeToCamel } from './naming';
|
|
15
|
+
import type { AnalyzedQuery, QueryColumn, QueryParam } from './query';
|
|
15
16
|
|
|
16
17
|
const KOTLIN_TYPE: Readonly<Record<IrColumnType, string>> = {
|
|
17
18
|
string: 'String',
|
|
@@ -32,9 +33,9 @@ function pascalCase(name: string): string {
|
|
|
32
33
|
.join('');
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/** Language-facing field name — the pinned §12 naming map. */
|
|
35
37
|
function camelCase(name: string): string {
|
|
36
|
-
|
|
37
|
-
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
38
|
+
return snakeToCamel(name);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function typeName(name: string): string {
|
|
@@ -46,7 +47,7 @@ function quote(value: string): string {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
function rowAccessor(column: QueryColumn): string {
|
|
49
|
-
const key = `row[${quote(column.
|
|
50
|
+
const key = `row[${quote(column.langName)}]`;
|
|
50
51
|
switch (column.type) {
|
|
51
52
|
case 'string':
|
|
52
53
|
case 'json':
|
|
@@ -81,6 +82,45 @@ function paramValue(type: IrColumnType, name: string): string {
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
/** Bind for a §4 OPTIONAL param: `null` rides as JSON null (the §7
|
|
86
|
+
* neutralization guards make it a no-op). */
|
|
87
|
+
function optionalParamValue(type: IrColumnType, name: string): string {
|
|
88
|
+
switch (type) {
|
|
89
|
+
case 'integer':
|
|
90
|
+
return `${name}?.let { JsonValue.of(it.toDouble()) } ?: JsonValue.Null`;
|
|
91
|
+
case 'bytes':
|
|
92
|
+
case 'crdt':
|
|
93
|
+
return `${name}?.let { queryBindBytes(it) } ?: JsonValue.Null`;
|
|
94
|
+
default:
|
|
95
|
+
return `${name}?.let { JsonValue.of(it) } ?: JsonValue.Null`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isOptionalParam(query: AnalyzedQuery, p: QueryParam): boolean {
|
|
100
|
+
return (
|
|
101
|
+
p.optional === true ||
|
|
102
|
+
p.flag === true ||
|
|
103
|
+
(query.limit !== undefined && p.name === 'limit')
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Per-query orderBy allowlist enum (column = the checked SQL column). */
|
|
108
|
+
function emitOrderByEnum(query: AnalyzedQuery): string[] {
|
|
109
|
+
if (query.orderBy === undefined) return [];
|
|
110
|
+
const lines: string[] = [];
|
|
111
|
+
lines.push(
|
|
112
|
+
`/** §6 orderBy allowlist for ${query.name} — checked at generate time. */`,
|
|
113
|
+
);
|
|
114
|
+
lines.push(`enum class ${typeName(query.name)}OrderBy(val column: String) {`);
|
|
115
|
+
lines.push(
|
|
116
|
+
query.orderBy.allowed
|
|
117
|
+
.map((col) => ` ${camelCase(col.langName)}(${quote(col.name)})`)
|
|
118
|
+
.join(',\n') + ';',
|
|
119
|
+
);
|
|
120
|
+
lines.push('}');
|
|
121
|
+
return lines;
|
|
122
|
+
}
|
|
123
|
+
|
|
84
124
|
function emitDataClass(query: AnalyzedQuery): string[] {
|
|
85
125
|
const Row = `${typeName(query.name)}Row`;
|
|
86
126
|
const lines: string[] = [];
|
|
@@ -89,14 +129,14 @@ function emitDataClass(query: AnalyzedQuery): string[] {
|
|
|
89
129
|
for (const column of query.columns) {
|
|
90
130
|
const opt = column.nullable ? '?' : '';
|
|
91
131
|
lines.push(
|
|
92
|
-
` val ${camelCase(column.
|
|
132
|
+
` val ${camelCase(column.langName)}: ${KOTLIN_TYPE[column.type]}${opt},`,
|
|
93
133
|
);
|
|
94
134
|
}
|
|
95
135
|
lines.push(') {');
|
|
96
136
|
lines.push(' companion object {');
|
|
97
137
|
lines.push(` fun fromRow(row: JsonValue): ${Row}? {`);
|
|
98
138
|
for (const column of query.columns) {
|
|
99
|
-
const name = camelCase(column.
|
|
139
|
+
const name = camelCase(column.langName);
|
|
100
140
|
const accessor = rowAccessor(column);
|
|
101
141
|
if (column.nullable) {
|
|
102
142
|
lines.push(` val ${name} = ${accessor}`);
|
|
@@ -104,7 +144,7 @@ function emitDataClass(query: AnalyzedQuery): string[] {
|
|
|
104
144
|
lines.push(` val ${name} = ${accessor} ?: return null`);
|
|
105
145
|
}
|
|
106
146
|
}
|
|
107
|
-
const args = query.columns.map((c) => camelCase(c.
|
|
147
|
+
const args = query.columns.map((c) => camelCase(c.langName)).join(', ');
|
|
108
148
|
lines.push(` return ${Row}(${args})`);
|
|
109
149
|
lines.push(' }');
|
|
110
150
|
lines.push(' }');
|
|
@@ -118,30 +158,69 @@ function emitRunner(query: AnalyzedQuery): string[] {
|
|
|
118
158
|
lines.push(
|
|
119
159
|
` val ${query.name}Tables = listOf(${query.tables.map(quote).join(', ')})`,
|
|
120
160
|
);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
161
|
+
if (query.orderBy !== undefined) {
|
|
162
|
+
lines.push(
|
|
163
|
+
` private const val ${query.name}SqlBase = ${quote(query.positionalSqlBase ?? '')}`,
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
lines.push(
|
|
167
|
+
` private const val ${query.name}Sql = ${quote(query.positionalSql)}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
124
170
|
lines.push('');
|
|
125
171
|
lines.push(` /** Run the ${query.name} named query (SELECT-only). */`);
|
|
126
|
-
const args =
|
|
127
|
-
|
|
128
|
-
.
|
|
172
|
+
const args: string[] = [];
|
|
173
|
+
for (const p of query.params) {
|
|
174
|
+
const name = camelCase(p.langName);
|
|
175
|
+
if (isOptionalParam(query, p)) {
|
|
176
|
+
args.push(`${name}: ${KOTLIN_TYPE[p.type]}? = null`);
|
|
177
|
+
} else {
|
|
178
|
+
args.push(`${name}: ${KOTLIN_TYPE[p.type]}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (query.orderBy !== undefined) {
|
|
182
|
+
const defaultCase = camelCase(
|
|
183
|
+
query.orderBy.allowed.find((c) => c.name === query.orderBy?.defaultColumn)
|
|
184
|
+
?.langName ?? query.orderBy.defaultColumn,
|
|
185
|
+
);
|
|
186
|
+
args.push(
|
|
187
|
+
`orderBy: ${typeName(query.name)}OrderBy = ${typeName(query.name)}OrderBy.${defaultCase}`,
|
|
188
|
+
);
|
|
189
|
+
args.push(
|
|
190
|
+
`dir: SyncularQueryDir = SyncularQueryDir.${query.orderBy.defaultDir.toUpperCase()}`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
129
193
|
const signature =
|
|
130
|
-
|
|
131
|
-
? `client: SyncularClient, ${args}`
|
|
194
|
+
args.length > 0
|
|
195
|
+
? `client: SyncularClient, ${args.join(', ')}`
|
|
132
196
|
: 'client: SyncularClient';
|
|
133
197
|
lines.push(` fun ${query.name}(${signature}): List<${Row}> {`);
|
|
198
|
+
if (query.orderBy !== undefined) {
|
|
199
|
+
const limitTail =
|
|
200
|
+
query.positionalLimitTail !== undefined
|
|
201
|
+
? ` + ${quote(query.positionalLimitTail)}`
|
|
202
|
+
: '';
|
|
203
|
+
lines.push(
|
|
204
|
+
` val sql = ${query.name}SqlBase + " order by " + orderBy.column + " " + dir.sql${limitTail}`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const sqlRef = query.orderBy !== undefined ? 'sql' : `${query.name}Sql`;
|
|
134
208
|
if (query.params.length > 0) {
|
|
135
209
|
const binds = query.params
|
|
136
|
-
.map((p) =>
|
|
210
|
+
.map((p) => {
|
|
211
|
+
const name = camelCase(p.langName);
|
|
212
|
+
return isOptionalParam(query, p)
|
|
213
|
+
? optionalParamValue(p.type, name)
|
|
214
|
+
: paramValue(p.type, name);
|
|
215
|
+
})
|
|
137
216
|
.join(', ');
|
|
138
217
|
lines.push(` val params = listOf(${binds})`);
|
|
139
218
|
lines.push(
|
|
140
|
-
` return client.query(${
|
|
219
|
+
` return client.query(${sqlRef}, params).mapNotNull { ${Row}.fromRow(it) }`,
|
|
141
220
|
);
|
|
142
221
|
} else {
|
|
143
222
|
lines.push(
|
|
144
|
-
` return client.query(${
|
|
223
|
+
` return client.query(${sqlRef}).mapNotNull { ${Row}.fromRow(it) }`,
|
|
145
224
|
);
|
|
146
225
|
}
|
|
147
226
|
lines.push(' }');
|
|
@@ -194,8 +273,22 @@ export function emitQueriesKotlinModule(
|
|
|
194
273
|
].join('\n'),
|
|
195
274
|
);
|
|
196
275
|
|
|
276
|
+
if (queries.some((q) => q.orderBy !== undefined)) {
|
|
277
|
+
parts.push(
|
|
278
|
+
[
|
|
279
|
+
'/** §6 orderBy direction (shared by every orderBy-knob query). */',
|
|
280
|
+
'enum class SyncularQueryDir(val sql: String) {',
|
|
281
|
+
' ASC("asc"),',
|
|
282
|
+
' DESC("desc");',
|
|
283
|
+
'}',
|
|
284
|
+
].join('\n'),
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
197
288
|
for (const query of queries) {
|
|
198
289
|
parts.push(emitDataClass(query).join('\n'));
|
|
290
|
+
const orderByEnum = emitOrderByEnum(query);
|
|
291
|
+
if (orderByEnum.length > 0) parts.push(orderByEnum.join('\n'));
|
|
199
292
|
}
|
|
200
293
|
|
|
201
294
|
const objLines: string[] = [];
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* bytes decode). Header carries the IR hash for byte-exact `--check`.
|
|
14
14
|
*/
|
|
15
15
|
import type { IrColumnType } from './ir';
|
|
16
|
-
import
|
|
16
|
+
import { snakeToCamel } from './naming';
|
|
17
|
+
import type { AnalyzedQuery, QueryColumn, QueryParam } from './query';
|
|
17
18
|
|
|
18
19
|
const SWIFT_TYPE: Readonly<Record<IrColumnType, string>> = {
|
|
19
20
|
string: 'String',
|
|
@@ -34,9 +35,9 @@ function pascalCase(name: string): string {
|
|
|
34
35
|
.join('');
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/** Language-facing field name — the pinned §12 naming map. */
|
|
37
39
|
function camelCase(name: string): string {
|
|
38
|
-
|
|
39
|
-
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
40
|
+
return snakeToCamel(name);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
/** The query function/type name: already camelCase; PascalCase for the type. */
|
|
@@ -49,7 +50,7 @@ function quote(value: string): string {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
function rowAccessor(column: QueryColumn): string {
|
|
52
|
-
const key = `row[${quote(column.
|
|
53
|
+
const key = `row[${quote(column.langName)}]`;
|
|
53
54
|
switch (column.type) {
|
|
54
55
|
case 'string':
|
|
55
56
|
case 'json':
|
|
@@ -82,6 +83,32 @@ function paramValue(type: IrColumnType, name: string): string {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
/** Bind for a §4 OPTIONAL param: `nil` rides as JSON null (the §7
|
|
87
|
+
* neutralization guards make it a no-op). */
|
|
88
|
+
function optionalParamValue(type: IrColumnType, name: string): string {
|
|
89
|
+
switch (type) {
|
|
90
|
+
case 'integer':
|
|
91
|
+
return `${name}.map { JSONValue.number(Double($0)) } ?? .null`;
|
|
92
|
+
case 'float':
|
|
93
|
+
return `${name}.map { JSONValue.number($0) } ?? .null`;
|
|
94
|
+
case 'boolean':
|
|
95
|
+
return `${name}.map { JSONValue.bool($0) } ?? .null`;
|
|
96
|
+
case 'bytes':
|
|
97
|
+
case 'crdt':
|
|
98
|
+
return `${name}.map { SyncularSchemaQueryBind.bytes($0) } ?? .null`;
|
|
99
|
+
default:
|
|
100
|
+
return `${name}.map { JSONValue.string($0) } ?? .null`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isOptionalParam(query: AnalyzedQuery, p: QueryParam): boolean {
|
|
105
|
+
return (
|
|
106
|
+
p.optional === true ||
|
|
107
|
+
p.flag === true ||
|
|
108
|
+
(query.limit !== undefined && p.name === 'limit')
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
85
112
|
function emitRowStruct(query: AnalyzedQuery): string[] {
|
|
86
113
|
const Row = `${typeName(query.name)}Row`;
|
|
87
114
|
const lines: string[] = [];
|
|
@@ -90,13 +117,13 @@ function emitRowStruct(query: AnalyzedQuery): string[] {
|
|
|
90
117
|
for (const column of query.columns) {
|
|
91
118
|
const opt = column.nullable ? '?' : '';
|
|
92
119
|
lines.push(
|
|
93
|
-
` public let ${camelCase(column.
|
|
120
|
+
` public let ${camelCase(column.langName)}: ${SWIFT_TYPE[column.type]}${opt}`,
|
|
94
121
|
);
|
|
95
122
|
}
|
|
96
123
|
lines.push('');
|
|
97
124
|
lines.push(' public init?(row: [String: JSONValue]) {');
|
|
98
125
|
for (const column of query.columns) {
|
|
99
|
-
const name = camelCase(column.
|
|
126
|
+
const name = camelCase(column.langName);
|
|
100
127
|
const accessor = rowAccessor(column);
|
|
101
128
|
if (column.nullable) {
|
|
102
129
|
lines.push(` self.${name} = ${accessor}`);
|
|
@@ -110,38 +137,86 @@ function emitRowStruct(query: AnalyzedQuery): string[] {
|
|
|
110
137
|
return lines;
|
|
111
138
|
}
|
|
112
139
|
|
|
140
|
+
/** Per-query orderBy allowlist enum (rawValue = the checked SQL column). */
|
|
141
|
+
function emitOrderByEnum(query: AnalyzedQuery): string[] {
|
|
142
|
+
if (query.orderBy === undefined) return [];
|
|
143
|
+
const lines: string[] = [];
|
|
144
|
+
lines.push(
|
|
145
|
+
`/// §6 orderBy allowlist for ${query.name} — checked at generate time.`,
|
|
146
|
+
);
|
|
147
|
+
lines.push(`public enum ${typeName(query.name)}OrderBy: String, Sendable {`);
|
|
148
|
+
for (const col of query.orderBy.allowed) {
|
|
149
|
+
lines.push(` case ${camelCase(col.langName)} = ${quote(col.name)}`);
|
|
150
|
+
}
|
|
151
|
+
lines.push('}');
|
|
152
|
+
return lines;
|
|
153
|
+
}
|
|
154
|
+
|
|
113
155
|
function emitRunner(query: AnalyzedQuery): string[] {
|
|
114
156
|
const Row = `${typeName(query.name)}Row`;
|
|
115
157
|
const lines: string[] = [];
|
|
116
158
|
lines.push(
|
|
117
159
|
` public static let ${query.name}Tables = [${query.tables.map(quote).join(', ')}]`,
|
|
118
160
|
);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
161
|
+
if (query.orderBy !== undefined) {
|
|
162
|
+
lines.push(
|
|
163
|
+
` private static let ${query.name}SqlBase = ${quote(query.positionalSqlBase ?? '')}`,
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
lines.push(
|
|
167
|
+
` private static let ${query.name}Sql = ${quote(query.positionalSql)}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
122
170
|
lines.push('');
|
|
123
171
|
lines.push(` /// Run the ${query.name} named query (SELECT-only).`);
|
|
124
|
-
const args =
|
|
125
|
-
|
|
126
|
-
.
|
|
172
|
+
const args: string[] = [];
|
|
173
|
+
for (const p of query.params) {
|
|
174
|
+
const name = camelCase(p.langName);
|
|
175
|
+
if (isOptionalParam(query, p)) {
|
|
176
|
+
args.push(`${name}: ${SWIFT_TYPE[p.type]}? = nil`);
|
|
177
|
+
} else {
|
|
178
|
+
args.push(`${name}: ${SWIFT_TYPE[p.type]}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (query.orderBy !== undefined) {
|
|
182
|
+
const defaultCase = camelCase(
|
|
183
|
+
query.orderBy.allowed.find((c) => c.name === query.orderBy?.defaultColumn)
|
|
184
|
+
?.langName ?? query.orderBy.defaultColumn,
|
|
185
|
+
);
|
|
186
|
+
args.push(`orderBy: ${typeName(query.name)}OrderBy = .${defaultCase}`);
|
|
187
|
+
args.push(`dir: SyncularQueryDir = .${query.orderBy.defaultDir}`);
|
|
188
|
+
}
|
|
127
189
|
const signature =
|
|
128
|
-
|
|
129
|
-
? `client: SyncularClient, ${args}`
|
|
190
|
+
args.length > 0
|
|
191
|
+
? `client: SyncularClient, ${args.join(', ')}`
|
|
130
192
|
: 'client: SyncularClient';
|
|
131
193
|
lines.push(
|
|
132
194
|
` public static func ${query.name}(${signature}) throws -> [${Row}] {`,
|
|
133
195
|
);
|
|
196
|
+
const sqlExpr =
|
|
197
|
+
query.orderBy !== undefined
|
|
198
|
+
? `${query.name}SqlBase + " order by " + orderBy.rawValue + " " + dir.rawValue${query.positionalLimitTail !== undefined ? ` + ${quote(query.positionalLimitTail)}` : ''}`
|
|
199
|
+
: `${query.name}Sql`;
|
|
200
|
+
if (query.orderBy !== undefined) {
|
|
201
|
+
lines.push(` let sql = ${sqlExpr}`);
|
|
202
|
+
}
|
|
203
|
+
const sqlRef = query.orderBy !== undefined ? 'sql' : `${query.name}Sql`;
|
|
134
204
|
if (query.params.length > 0) {
|
|
135
205
|
const binds = query.params
|
|
136
|
-
.map((p) =>
|
|
206
|
+
.map((p) => {
|
|
207
|
+
const name = camelCase(p.langName);
|
|
208
|
+
return isOptionalParam(query, p)
|
|
209
|
+
? optionalParamValue(p.type, name)
|
|
210
|
+
: paramValue(p.type, name);
|
|
211
|
+
})
|
|
137
212
|
.join(', ');
|
|
138
213
|
lines.push(` let params: [JSONValue] = [${binds}]`);
|
|
139
214
|
lines.push(
|
|
140
|
-
` return try client.query(${
|
|
215
|
+
` return try client.query(${sqlRef}, params: params).compactMap { row in`,
|
|
141
216
|
);
|
|
142
217
|
} else {
|
|
143
218
|
lines.push(
|
|
144
|
-
` return try client.query(${
|
|
219
|
+
` return try client.query(${sqlRef}).compactMap { row in`,
|
|
145
220
|
);
|
|
146
221
|
}
|
|
147
222
|
lines.push(
|
|
@@ -183,8 +258,22 @@ export function emitQueriesSwiftModule(
|
|
|
183
258
|
].join('\n'),
|
|
184
259
|
);
|
|
185
260
|
|
|
261
|
+
if (queries.some((q) => q.orderBy !== undefined)) {
|
|
262
|
+
parts.push(
|
|
263
|
+
[
|
|
264
|
+
'/// §6 orderBy direction (shared by every orderBy-knob query).',
|
|
265
|
+
'public enum SyncularQueryDir: String, Sendable {',
|
|
266
|
+
' case asc = "asc"',
|
|
267
|
+
' case desc = "desc"',
|
|
268
|
+
'}',
|
|
269
|
+
].join('\n'),
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
186
273
|
for (const query of queries) {
|
|
187
274
|
parts.push(emitRowStruct(query).join('\n'));
|
|
275
|
+
const orderByEnum = emitOrderByEnum(query);
|
|
276
|
+
if (orderByEnum.length > 0) parts.push(orderByEnum.join('\n'));
|
|
188
277
|
}
|
|
189
278
|
|
|
190
279
|
const enumLines: string[] = [];
|