@tsqx/kit 0.0.4 → 0.0.5
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/sqlite/d1/index.cjs +389 -0
- package/dist/sqlite/d1/index.d.cts +6 -0
- package/dist/sqlite/d1/index.d.mts +6 -0
- package/dist/sqlite/d1/index.mjs +388 -0
- package/package.json +7 -2
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _tsqx_core = require("@tsqx/core");
|
|
3
|
+
let neverthrow = require("neverthrow");
|
|
4
|
+
//#region src/sqlite/d1/parser.ts
|
|
5
|
+
function stripComments(sql) {
|
|
6
|
+
sql = sql.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
7
|
+
sql = sql.replace(/--.*$/gm, "");
|
|
8
|
+
return sql;
|
|
9
|
+
}
|
|
10
|
+
function parseColumnDef(raw) {
|
|
11
|
+
const trimmed = raw.trim();
|
|
12
|
+
if (!trimmed) return null;
|
|
13
|
+
const match = trimmed.match(/^("(\w+)"|(\w+))\s+(.+)$/is);
|
|
14
|
+
if (!match) return null;
|
|
15
|
+
const name = match[2] ?? match[3].toLowerCase();
|
|
16
|
+
const rest = match[4].trim();
|
|
17
|
+
const typeMatch = rest.match(/^(\w+(?:\s+(?:PRECISION|VARYING))?(?:\(\s*\d+(?:\s*,\s*\d+)?\s*\))?)/i);
|
|
18
|
+
if (!typeMatch) return null;
|
|
19
|
+
const type = typeMatch[1].toUpperCase();
|
|
20
|
+
const modifiers = rest.slice(typeMatch[0].length).trim().toUpperCase();
|
|
21
|
+
const nullable = !modifiers.includes("NOT NULL");
|
|
22
|
+
const primaryKey = modifiers.includes("PRIMARY KEY");
|
|
23
|
+
const autoincrement = modifiers.includes("AUTOINCREMENT");
|
|
24
|
+
const unique = modifiers.includes("UNIQUE");
|
|
25
|
+
let defaultValue;
|
|
26
|
+
const defaultMatch = rest.slice(typeMatch[0].length).match(/DEFAULT\s+(.+?)(?:\s+(?:NOT\s+NULL|NULL|PRIMARY\s+KEY|UNIQUE|REFERENCES|CHECK|CONSTRAINT|AUTOINCREMENT)|\s*$)/i);
|
|
27
|
+
if (defaultMatch) defaultValue = defaultMatch[1].trim();
|
|
28
|
+
let references;
|
|
29
|
+
const restModifiers = rest.slice(typeMatch[0].length).trim();
|
|
30
|
+
const refMatch = restModifiers.match(/REFERENCES\s+"?(\w+)"?\s*\(\s*"?(\w+)"?\s*\)/i);
|
|
31
|
+
if (refMatch) references = {
|
|
32
|
+
table: restModifiers.includes(`"${refMatch[1]}"`) ? refMatch[1] : refMatch[1].toLowerCase(),
|
|
33
|
+
column: restModifiers.includes(`"${refMatch[2]}"`) ? refMatch[2] : refMatch[2].toLowerCase()
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
name,
|
|
37
|
+
type: autoincrement ? "INTEGER" : type,
|
|
38
|
+
nullable: primaryKey ? false : nullable,
|
|
39
|
+
...defaultValue !== void 0 && { default: defaultValue },
|
|
40
|
+
primaryKey,
|
|
41
|
+
unique,
|
|
42
|
+
...references && { references }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function isTableConstraint(line) {
|
|
46
|
+
const upper = line.trim().toUpperCase();
|
|
47
|
+
return upper.startsWith("PRIMARY KEY") || upper.startsWith("UNIQUE") || upper.startsWith("FOREIGN KEY") || upper.startsWith("CONSTRAINT") || upper.startsWith("CHECK");
|
|
48
|
+
}
|
|
49
|
+
function parseTableConstraint(raw) {
|
|
50
|
+
let working = raw.trim().toUpperCase();
|
|
51
|
+
let name;
|
|
52
|
+
const constraintMatch = raw.trim().match(/^CONSTRAINT\s+"?(\w+)"?\s+(.+)$/is);
|
|
53
|
+
if (constraintMatch) {
|
|
54
|
+
name = constraintMatch[1].toLowerCase();
|
|
55
|
+
working = constraintMatch[2].trim().toUpperCase();
|
|
56
|
+
}
|
|
57
|
+
const colsMatch = working.match(/\(\s*(.+?)\s*\)/);
|
|
58
|
+
if (!colsMatch) return null;
|
|
59
|
+
const columns = colsMatch[1].split(",").map((c) => {
|
|
60
|
+
const t = c.trim();
|
|
61
|
+
if (t.startsWith("\"") && t.endsWith("\"")) return t.slice(1, -1);
|
|
62
|
+
return t.replace(/"/g, "").toLowerCase();
|
|
63
|
+
});
|
|
64
|
+
if (working.startsWith("PRIMARY KEY")) return {
|
|
65
|
+
type: "primary_key",
|
|
66
|
+
...name && { name },
|
|
67
|
+
columns
|
|
68
|
+
};
|
|
69
|
+
if (working.startsWith("UNIQUE")) return {
|
|
70
|
+
type: "unique",
|
|
71
|
+
...name && { name },
|
|
72
|
+
columns
|
|
73
|
+
};
|
|
74
|
+
if (working.startsWith("FOREIGN KEY")) {
|
|
75
|
+
const refMatch = working.match(/REFERENCES\s+"?(\w+)"?\s*\(\s*(.+?)\s*\)/i);
|
|
76
|
+
if (!refMatch) return null;
|
|
77
|
+
const refColumns = refMatch[2].split(",").map((c) => {
|
|
78
|
+
const t = c.trim();
|
|
79
|
+
if (t.startsWith("\"") && t.endsWith("\"")) return t.slice(1, -1);
|
|
80
|
+
return t.replace(/"/g, "").toLowerCase();
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
type: "foreign_key",
|
|
84
|
+
...name && { name },
|
|
85
|
+
columns,
|
|
86
|
+
references: {
|
|
87
|
+
table: refMatch[1].toLowerCase(),
|
|
88
|
+
columns: refColumns
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function parseCreateTable(sql) {
|
|
95
|
+
const tableMatch = sql.match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?("(\w+)"|(\w+))\s*\(([\s\S]+)\)/i);
|
|
96
|
+
if (!tableMatch) return (0, neverthrow.err)(new _tsqx_core.SchemaError("Failed to parse CREATE TABLE statement"));
|
|
97
|
+
const tableName = tableMatch[2] ?? tableMatch[3].toLowerCase();
|
|
98
|
+
const body = tableMatch[4];
|
|
99
|
+
const lines = [];
|
|
100
|
+
let depth = 0;
|
|
101
|
+
let current = "";
|
|
102
|
+
for (const char of body) {
|
|
103
|
+
if (char === "(") depth++;
|
|
104
|
+
else if (char === ")") depth--;
|
|
105
|
+
if (char === "," && depth === 0) {
|
|
106
|
+
lines.push(current.trim());
|
|
107
|
+
current = "";
|
|
108
|
+
} else current += char;
|
|
109
|
+
}
|
|
110
|
+
if (current.trim()) lines.push(current.trim());
|
|
111
|
+
const columns = [];
|
|
112
|
+
const constraints = [];
|
|
113
|
+
for (const line of lines) if (isTableConstraint(line)) {
|
|
114
|
+
const constraint = parseTableConstraint(line);
|
|
115
|
+
if (constraint) constraints.push(constraint);
|
|
116
|
+
} else {
|
|
117
|
+
const column = parseColumnDef(line);
|
|
118
|
+
if (column) columns.push(column);
|
|
119
|
+
}
|
|
120
|
+
const pkConstraint = constraints.find((c) => c.type === "primary_key");
|
|
121
|
+
if (pkConstraint) {
|
|
122
|
+
for (const col of columns) if (pkConstraint.columns.includes(col.name)) {
|
|
123
|
+
col.primaryKey = true;
|
|
124
|
+
col.nullable = false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return (0, neverthrow.ok)({
|
|
128
|
+
name: tableName,
|
|
129
|
+
columns,
|
|
130
|
+
constraints
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function parseSchemaFiles(files) {
|
|
134
|
+
const snapshot = {};
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
const matches = stripComments(file.content).match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?"?\w+"?\s*\([\s\S]*?\);/gi);
|
|
137
|
+
if (!matches) continue;
|
|
138
|
+
for (const statement of matches) {
|
|
139
|
+
const result = parseCreateTable(statement);
|
|
140
|
+
if (result.isErr()) return (0, neverthrow.err)(new _tsqx_core.SchemaError(`Error in ${file.filename}: ${result.error.message}`));
|
|
141
|
+
const table = result.value;
|
|
142
|
+
if (snapshot[table.name]) return (0, neverthrow.err)(new _tsqx_core.SchemaError(`Duplicate table "${table.name}" found in ${file.filename}`));
|
|
143
|
+
snapshot[table.name] = table;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return (0, neverthrow.ok)(snapshot);
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/sqlite/d1/generator.ts
|
|
150
|
+
function quoteIdent(name) {
|
|
151
|
+
if (name !== name.toLowerCase()) return `"${name}"`;
|
|
152
|
+
return name;
|
|
153
|
+
}
|
|
154
|
+
function columnToSQL(col) {
|
|
155
|
+
const parts = [quoteIdent(col.name), col.type];
|
|
156
|
+
if (col.primaryKey) parts.push("PRIMARY KEY");
|
|
157
|
+
if (!col.nullable && !col.primaryKey) parts.push("NOT NULL");
|
|
158
|
+
if (col.unique) parts.push("UNIQUE");
|
|
159
|
+
if (col.default !== void 0) parts.push(`DEFAULT ${col.default}`);
|
|
160
|
+
if (col.references) parts.push(`REFERENCES ${quoteIdent(col.references.table)}(${quoteIdent(col.references.column)})`);
|
|
161
|
+
return parts.join(" ");
|
|
162
|
+
}
|
|
163
|
+
function createTableSQL(table) {
|
|
164
|
+
const cols = table.columns.map((c) => ` ${columnToSQL(c)}`);
|
|
165
|
+
for (const constraint of table.constraints) {
|
|
166
|
+
const name = constraint.name ? `CONSTRAINT ${constraint.name} ` : "";
|
|
167
|
+
if (constraint.type === "primary_key") {
|
|
168
|
+
if (constraint.columns.length > 1) cols.push(` ${name}PRIMARY KEY (${constraint.columns.map(quoteIdent).join(", ")})`);
|
|
169
|
+
} else if (constraint.type === "unique") {
|
|
170
|
+
if (constraint.columns.length > 1) cols.push(` ${name}UNIQUE (${constraint.columns.map(quoteIdent).join(", ")})`);
|
|
171
|
+
} else if (constraint.type === "foreign_key" && constraint.references) cols.push(` ${name}FOREIGN KEY (${constraint.columns.map(quoteIdent).join(", ")}) REFERENCES ${quoteIdent(constraint.references.table)}(${constraint.references.columns.map(quoteIdent).join(", ")})`);
|
|
172
|
+
}
|
|
173
|
+
return `CREATE TABLE ${quoteIdent(table.name)} (\n${cols.join(",\n")}\n);`;
|
|
174
|
+
}
|
|
175
|
+
function operationToSQL(op) {
|
|
176
|
+
switch (op.type) {
|
|
177
|
+
case "create_table": return createTableSQL(op.table);
|
|
178
|
+
case "drop_table": return `DROP TABLE IF EXISTS ${quoteIdent(op.tableName)};`;
|
|
179
|
+
case "add_column": return `ALTER TABLE ${quoteIdent(op.tableName)} ADD COLUMN ${columnToSQL(op.column)};`;
|
|
180
|
+
case "drop_column": return `ALTER TABLE ${quoteIdent(op.tableName)} DROP COLUMN ${quoteIdent(op.columnName)};`;
|
|
181
|
+
case "alter_column": {
|
|
182
|
+
const qt = quoteIdent(op.tableName);
|
|
183
|
+
const qc = quoteIdent(op.columnName);
|
|
184
|
+
return `-- SQLite does not support ALTER COLUMN. To change column "${op.columnName}" in "${op.tableName}", recreate the table.\n-- ALTER TABLE ${qt} ALTER COLUMN ${qc} (not supported);`;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function generateSQL(operations) {
|
|
189
|
+
if (operations.length === 0) return "";
|
|
190
|
+
return `${operations.map(operationToSQL).filter(Boolean).join("\n\n")}\n`;
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/sqlite/d1/types.ts
|
|
194
|
+
function sqlTypeToJsonSchema(sqlType) {
|
|
195
|
+
switch (sqlType.toUpperCase().replace(/\(.+\)/, "").trim()) {
|
|
196
|
+
case "INTEGER":
|
|
197
|
+
case "INT":
|
|
198
|
+
case "TINYINT":
|
|
199
|
+
case "SMALLINT":
|
|
200
|
+
case "MEDIUMINT":
|
|
201
|
+
case "BIGINT": return { type: "integer" };
|
|
202
|
+
case "REAL":
|
|
203
|
+
case "DOUBLE":
|
|
204
|
+
case "DOUBLE PRECISION":
|
|
205
|
+
case "FLOAT":
|
|
206
|
+
case "NUMERIC":
|
|
207
|
+
case "DECIMAL": return { type: "number" };
|
|
208
|
+
case "BOOLEAN": return { type: "integer" };
|
|
209
|
+
case "TEXT":
|
|
210
|
+
case "CLOB": return { type: "string" };
|
|
211
|
+
case "VARCHAR":
|
|
212
|
+
case "CHARACTER VARYING":
|
|
213
|
+
case "NVARCHAR":
|
|
214
|
+
case "CHARACTER": {
|
|
215
|
+
const lenMatch = sqlType.match(/\((\d+)\)/);
|
|
216
|
+
return lenMatch ? {
|
|
217
|
+
type: "string",
|
|
218
|
+
maxLength: parseInt(lenMatch[1], 10)
|
|
219
|
+
} : { type: "string" };
|
|
220
|
+
}
|
|
221
|
+
case "BLOB": return {
|
|
222
|
+
type: "string",
|
|
223
|
+
format: "byte"
|
|
224
|
+
};
|
|
225
|
+
case "DATE": return {
|
|
226
|
+
type: "string",
|
|
227
|
+
format: "date"
|
|
228
|
+
};
|
|
229
|
+
case "DATETIME":
|
|
230
|
+
case "TIMESTAMP": return {
|
|
231
|
+
type: "string",
|
|
232
|
+
format: "date-time"
|
|
233
|
+
};
|
|
234
|
+
default: return { type: "string" };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function sqlTypeToTsType(sqlType) {
|
|
238
|
+
switch (sqlType.toUpperCase().replace(/\(.+\)/, "").trim()) {
|
|
239
|
+
case "INTEGER":
|
|
240
|
+
case "INT":
|
|
241
|
+
case "TINYINT":
|
|
242
|
+
case "SMALLINT":
|
|
243
|
+
case "MEDIUMINT":
|
|
244
|
+
case "BIGINT":
|
|
245
|
+
case "REAL":
|
|
246
|
+
case "DOUBLE":
|
|
247
|
+
case "DOUBLE PRECISION":
|
|
248
|
+
case "FLOAT":
|
|
249
|
+
case "NUMERIC":
|
|
250
|
+
case "DECIMAL": return "number";
|
|
251
|
+
case "BOOLEAN": return "number";
|
|
252
|
+
case "BLOB": return "ArrayBuffer";
|
|
253
|
+
default: return "string";
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/sqlite/d1/query-codegen.ts
|
|
258
|
+
function camelCase(str) {
|
|
259
|
+
return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
260
|
+
}
|
|
261
|
+
function generateParamInterface(query) {
|
|
262
|
+
if (query.params.length === 0) return null;
|
|
263
|
+
return `export interface ${`${(0, _tsqx_core.pascalCase)(query.name)}Params`} {\n${query.params.map((p) => {
|
|
264
|
+
const base = sqlTypeToTsType(p.sqlType);
|
|
265
|
+
const type = p.nullable ? `${base} | null` : base;
|
|
266
|
+
return ` ${p.name}: ${type};`;
|
|
267
|
+
}).join("\n")}\n}`;
|
|
268
|
+
}
|
|
269
|
+
function generateParamSchema(query) {
|
|
270
|
+
if (query.params.length === 0) return null;
|
|
271
|
+
const name = `${(0, _tsqx_core.pascalCase)(query.name)}ParamsSchema`;
|
|
272
|
+
const properties = {};
|
|
273
|
+
const required = [];
|
|
274
|
+
for (const param of query.params) {
|
|
275
|
+
const jsonType = sqlTypeToJsonSchema(param.sqlType);
|
|
276
|
+
if (param.nullable) properties[param.name] = {
|
|
277
|
+
...jsonType,
|
|
278
|
+
type: Array.isArray(jsonType.type) ? [...jsonType.type, "null"] : [jsonType.type, "null"]
|
|
279
|
+
};
|
|
280
|
+
else {
|
|
281
|
+
properties[param.name] = jsonType;
|
|
282
|
+
required.push(param.name);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const schema = {
|
|
286
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
287
|
+
title: `${(0, _tsqx_core.pascalCase)(query.name)}Params`,
|
|
288
|
+
type: "object",
|
|
289
|
+
properties,
|
|
290
|
+
required,
|
|
291
|
+
additionalProperties: false
|
|
292
|
+
};
|
|
293
|
+
return `export const ${name} = ${JSON.stringify(schema, null, 2)} as const;`;
|
|
294
|
+
}
|
|
295
|
+
function generateResultType(query, snapshot) {
|
|
296
|
+
if (query.command === "exec" || query.command === "execrows" || query.command === "execresult") return null;
|
|
297
|
+
if (!query.returnsTable || !snapshot[query.returnsTable]) return null;
|
|
298
|
+
const table = snapshot[query.returnsTable];
|
|
299
|
+
const typeName = (0, _tsqx_core.pascalCase)(query.returnsTable);
|
|
300
|
+
if (query.returnsColumns.length === table.columns.length && query.returnsColumns.every((c) => table.columns.some((tc) => tc.name === c))) return typeName;
|
|
301
|
+
return `Pick<${typeName}, ${query.returnsColumns.map((c) => `"${c}"`).join(" | ")}>`;
|
|
302
|
+
}
|
|
303
|
+
function generateReturnType(query, resultType) {
|
|
304
|
+
switch (query.command) {
|
|
305
|
+
case "one": return `Promise<${resultType ?? "unknown"} | null>`;
|
|
306
|
+
case "many": return `Promise<${resultType ?? "unknown"}[]>`;
|
|
307
|
+
case "exec": return "Promise<void>";
|
|
308
|
+
case "execrows": return "Promise<number>";
|
|
309
|
+
case "execresult": return "Promise<D1Result<unknown>>";
|
|
310
|
+
default: return "Promise<void>";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function generateFunctionBody(query) {
|
|
314
|
+
const paramArgs = query.params.map((p) => `params.${p.name}`).join(", ");
|
|
315
|
+
const bindCall = query.params.length > 0 ? `.bind(${paramArgs})` : "";
|
|
316
|
+
switch (query.command) {
|
|
317
|
+
case "one": return ` const result = await db.prepare(sql)${bindCall}.first<${generateResultType(query, {}) ?? "unknown"}>();\n return result ?? null;`;
|
|
318
|
+
case "many": return ` const result = await db.prepare(sql)${bindCall}.all();\n return result.results;`;
|
|
319
|
+
case "exec": return ` await db.prepare(sql)${bindCall}.run();`;
|
|
320
|
+
case "execrows": return ` const result = await db.prepare(sql)${bindCall}.run();\n return result.meta.changes;`;
|
|
321
|
+
case "execresult": return ` return await db.prepare(sql)${bindCall}.run();`;
|
|
322
|
+
default: return ` await db.prepare(sql)${bindCall}.run();`;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function generateFunction(query, snapshot) {
|
|
326
|
+
const fnName = camelCase(query.name);
|
|
327
|
+
const returnType = generateReturnType(query, generateResultType(query, snapshot));
|
|
328
|
+
const hasParams = query.params.length > 0;
|
|
329
|
+
const paramsType = hasParams ? `${(0, _tsqx_core.pascalCase)(query.name)}Params` : null;
|
|
330
|
+
const args = hasParams ? `db: D1Database, params: ${paramsType}` : "db: D1Database";
|
|
331
|
+
const sql = query.expandedSql.replace(/'/g, "\\'").replace(/\n/g, "\\n");
|
|
332
|
+
const lines = [];
|
|
333
|
+
lines.push(`export async function ${fnName}(${args}): ${returnType} {`);
|
|
334
|
+
lines.push(` const sql = '${sql}';`);
|
|
335
|
+
lines.push(generateFunctionBody(query));
|
|
336
|
+
lines.push("}");
|
|
337
|
+
return lines.join("\n");
|
|
338
|
+
}
|
|
339
|
+
function generateQueryFiles(queries, snapshot) {
|
|
340
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
341
|
+
for (const query of queries) {
|
|
342
|
+
const key = query.sourceFile.replace(/\.sql$/, "");
|
|
343
|
+
if (!grouped.has(key)) grouped.set(key, []);
|
|
344
|
+
grouped.get(key).push(query);
|
|
345
|
+
}
|
|
346
|
+
const files = {};
|
|
347
|
+
for (const [basename, fileQueries] of grouped) {
|
|
348
|
+
const lines = ["// This file is auto-generated by tsqx. Do not edit manually."];
|
|
349
|
+
lines.push("// D1Database type is available globally in Cloudflare Workers");
|
|
350
|
+
lines.push("");
|
|
351
|
+
const tableImports = /* @__PURE__ */ new Set();
|
|
352
|
+
for (const query of fileQueries) if (query.returnsTable && snapshot[query.returnsTable]) tableImports.add(query.returnsTable);
|
|
353
|
+
if (tableImports.size > 0) {
|
|
354
|
+
const imports = [...tableImports].map((t) => `type ${(0, _tsqx_core.pascalCase)(t)}`).join(", ");
|
|
355
|
+
lines.push(`import { ${imports} } from "../../generated";`);
|
|
356
|
+
}
|
|
357
|
+
lines.push("");
|
|
358
|
+
for (const query of fileQueries) {
|
|
359
|
+
const paramInterface = generateParamInterface(query);
|
|
360
|
+
if (paramInterface) {
|
|
361
|
+
lines.push(paramInterface);
|
|
362
|
+
lines.push("");
|
|
363
|
+
}
|
|
364
|
+
const paramSchema = generateParamSchema(query);
|
|
365
|
+
if (paramSchema) {
|
|
366
|
+
lines.push(paramSchema);
|
|
367
|
+
lines.push("");
|
|
368
|
+
}
|
|
369
|
+
lines.push(generateFunction(query, snapshot));
|
|
370
|
+
lines.push("");
|
|
371
|
+
}
|
|
372
|
+
files[`${basename}.ts`] = lines.join("\n");
|
|
373
|
+
}
|
|
374
|
+
return files;
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/sqlite/d1/index.ts
|
|
378
|
+
function d1Dialect() {
|
|
379
|
+
return {
|
|
380
|
+
name: "d1",
|
|
381
|
+
parseSchema: parseSchemaFiles,
|
|
382
|
+
generateSQL,
|
|
383
|
+
sqlTypeToJsonSchema,
|
|
384
|
+
sqlTypeToTsType,
|
|
385
|
+
generateQueryCode: generateQueryFiles
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
//#endregion
|
|
389
|
+
exports.d1Dialect = d1Dialect;
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { SchemaError, pascalCase } from "@tsqx/core";
|
|
2
|
+
import { err, ok } from "neverthrow";
|
|
3
|
+
//#region src/sqlite/d1/parser.ts
|
|
4
|
+
function stripComments(sql) {
|
|
5
|
+
sql = sql.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
6
|
+
sql = sql.replace(/--.*$/gm, "");
|
|
7
|
+
return sql;
|
|
8
|
+
}
|
|
9
|
+
function parseColumnDef(raw) {
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed) return null;
|
|
12
|
+
const match = trimmed.match(/^("(\w+)"|(\w+))\s+(.+)$/is);
|
|
13
|
+
if (!match) return null;
|
|
14
|
+
const name = match[2] ?? match[3].toLowerCase();
|
|
15
|
+
const rest = match[4].trim();
|
|
16
|
+
const typeMatch = rest.match(/^(\w+(?:\s+(?:PRECISION|VARYING))?(?:\(\s*\d+(?:\s*,\s*\d+)?\s*\))?)/i);
|
|
17
|
+
if (!typeMatch) return null;
|
|
18
|
+
const type = typeMatch[1].toUpperCase();
|
|
19
|
+
const modifiers = rest.slice(typeMatch[0].length).trim().toUpperCase();
|
|
20
|
+
const nullable = !modifiers.includes("NOT NULL");
|
|
21
|
+
const primaryKey = modifiers.includes("PRIMARY KEY");
|
|
22
|
+
const autoincrement = modifiers.includes("AUTOINCREMENT");
|
|
23
|
+
const unique = modifiers.includes("UNIQUE");
|
|
24
|
+
let defaultValue;
|
|
25
|
+
const defaultMatch = rest.slice(typeMatch[0].length).match(/DEFAULT\s+(.+?)(?:\s+(?:NOT\s+NULL|NULL|PRIMARY\s+KEY|UNIQUE|REFERENCES|CHECK|CONSTRAINT|AUTOINCREMENT)|\s*$)/i);
|
|
26
|
+
if (defaultMatch) defaultValue = defaultMatch[1].trim();
|
|
27
|
+
let references;
|
|
28
|
+
const restModifiers = rest.slice(typeMatch[0].length).trim();
|
|
29
|
+
const refMatch = restModifiers.match(/REFERENCES\s+"?(\w+)"?\s*\(\s*"?(\w+)"?\s*\)/i);
|
|
30
|
+
if (refMatch) references = {
|
|
31
|
+
table: restModifiers.includes(`"${refMatch[1]}"`) ? refMatch[1] : refMatch[1].toLowerCase(),
|
|
32
|
+
column: restModifiers.includes(`"${refMatch[2]}"`) ? refMatch[2] : refMatch[2].toLowerCase()
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
name,
|
|
36
|
+
type: autoincrement ? "INTEGER" : type,
|
|
37
|
+
nullable: primaryKey ? false : nullable,
|
|
38
|
+
...defaultValue !== void 0 && { default: defaultValue },
|
|
39
|
+
primaryKey,
|
|
40
|
+
unique,
|
|
41
|
+
...references && { references }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function isTableConstraint(line) {
|
|
45
|
+
const upper = line.trim().toUpperCase();
|
|
46
|
+
return upper.startsWith("PRIMARY KEY") || upper.startsWith("UNIQUE") || upper.startsWith("FOREIGN KEY") || upper.startsWith("CONSTRAINT") || upper.startsWith("CHECK");
|
|
47
|
+
}
|
|
48
|
+
function parseTableConstraint(raw) {
|
|
49
|
+
let working = raw.trim().toUpperCase();
|
|
50
|
+
let name;
|
|
51
|
+
const constraintMatch = raw.trim().match(/^CONSTRAINT\s+"?(\w+)"?\s+(.+)$/is);
|
|
52
|
+
if (constraintMatch) {
|
|
53
|
+
name = constraintMatch[1].toLowerCase();
|
|
54
|
+
working = constraintMatch[2].trim().toUpperCase();
|
|
55
|
+
}
|
|
56
|
+
const colsMatch = working.match(/\(\s*(.+?)\s*\)/);
|
|
57
|
+
if (!colsMatch) return null;
|
|
58
|
+
const columns = colsMatch[1].split(",").map((c) => {
|
|
59
|
+
const t = c.trim();
|
|
60
|
+
if (t.startsWith("\"") && t.endsWith("\"")) return t.slice(1, -1);
|
|
61
|
+
return t.replace(/"/g, "").toLowerCase();
|
|
62
|
+
});
|
|
63
|
+
if (working.startsWith("PRIMARY KEY")) return {
|
|
64
|
+
type: "primary_key",
|
|
65
|
+
...name && { name },
|
|
66
|
+
columns
|
|
67
|
+
};
|
|
68
|
+
if (working.startsWith("UNIQUE")) return {
|
|
69
|
+
type: "unique",
|
|
70
|
+
...name && { name },
|
|
71
|
+
columns
|
|
72
|
+
};
|
|
73
|
+
if (working.startsWith("FOREIGN KEY")) {
|
|
74
|
+
const refMatch = working.match(/REFERENCES\s+"?(\w+)"?\s*\(\s*(.+?)\s*\)/i);
|
|
75
|
+
if (!refMatch) return null;
|
|
76
|
+
const refColumns = refMatch[2].split(",").map((c) => {
|
|
77
|
+
const t = c.trim();
|
|
78
|
+
if (t.startsWith("\"") && t.endsWith("\"")) return t.slice(1, -1);
|
|
79
|
+
return t.replace(/"/g, "").toLowerCase();
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
type: "foreign_key",
|
|
83
|
+
...name && { name },
|
|
84
|
+
columns,
|
|
85
|
+
references: {
|
|
86
|
+
table: refMatch[1].toLowerCase(),
|
|
87
|
+
columns: refColumns
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function parseCreateTable(sql) {
|
|
94
|
+
const tableMatch = sql.match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?("(\w+)"|(\w+))\s*\(([\s\S]+)\)/i);
|
|
95
|
+
if (!tableMatch) return err(new SchemaError("Failed to parse CREATE TABLE statement"));
|
|
96
|
+
const tableName = tableMatch[2] ?? tableMatch[3].toLowerCase();
|
|
97
|
+
const body = tableMatch[4];
|
|
98
|
+
const lines = [];
|
|
99
|
+
let depth = 0;
|
|
100
|
+
let current = "";
|
|
101
|
+
for (const char of body) {
|
|
102
|
+
if (char === "(") depth++;
|
|
103
|
+
else if (char === ")") depth--;
|
|
104
|
+
if (char === "," && depth === 0) {
|
|
105
|
+
lines.push(current.trim());
|
|
106
|
+
current = "";
|
|
107
|
+
} else current += char;
|
|
108
|
+
}
|
|
109
|
+
if (current.trim()) lines.push(current.trim());
|
|
110
|
+
const columns = [];
|
|
111
|
+
const constraints = [];
|
|
112
|
+
for (const line of lines) if (isTableConstraint(line)) {
|
|
113
|
+
const constraint = parseTableConstraint(line);
|
|
114
|
+
if (constraint) constraints.push(constraint);
|
|
115
|
+
} else {
|
|
116
|
+
const column = parseColumnDef(line);
|
|
117
|
+
if (column) columns.push(column);
|
|
118
|
+
}
|
|
119
|
+
const pkConstraint = constraints.find((c) => c.type === "primary_key");
|
|
120
|
+
if (pkConstraint) {
|
|
121
|
+
for (const col of columns) if (pkConstraint.columns.includes(col.name)) {
|
|
122
|
+
col.primaryKey = true;
|
|
123
|
+
col.nullable = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return ok({
|
|
127
|
+
name: tableName,
|
|
128
|
+
columns,
|
|
129
|
+
constraints
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function parseSchemaFiles(files) {
|
|
133
|
+
const snapshot = {};
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
const matches = stripComments(file.content).match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?"?\w+"?\s*\([\s\S]*?\);/gi);
|
|
136
|
+
if (!matches) continue;
|
|
137
|
+
for (const statement of matches) {
|
|
138
|
+
const result = parseCreateTable(statement);
|
|
139
|
+
if (result.isErr()) return err(new SchemaError(`Error in ${file.filename}: ${result.error.message}`));
|
|
140
|
+
const table = result.value;
|
|
141
|
+
if (snapshot[table.name]) return err(new SchemaError(`Duplicate table "${table.name}" found in ${file.filename}`));
|
|
142
|
+
snapshot[table.name] = table;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return ok(snapshot);
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/sqlite/d1/generator.ts
|
|
149
|
+
function quoteIdent(name) {
|
|
150
|
+
if (name !== name.toLowerCase()) return `"${name}"`;
|
|
151
|
+
return name;
|
|
152
|
+
}
|
|
153
|
+
function columnToSQL(col) {
|
|
154
|
+
const parts = [quoteIdent(col.name), col.type];
|
|
155
|
+
if (col.primaryKey) parts.push("PRIMARY KEY");
|
|
156
|
+
if (!col.nullable && !col.primaryKey) parts.push("NOT NULL");
|
|
157
|
+
if (col.unique) parts.push("UNIQUE");
|
|
158
|
+
if (col.default !== void 0) parts.push(`DEFAULT ${col.default}`);
|
|
159
|
+
if (col.references) parts.push(`REFERENCES ${quoteIdent(col.references.table)}(${quoteIdent(col.references.column)})`);
|
|
160
|
+
return parts.join(" ");
|
|
161
|
+
}
|
|
162
|
+
function createTableSQL(table) {
|
|
163
|
+
const cols = table.columns.map((c) => ` ${columnToSQL(c)}`);
|
|
164
|
+
for (const constraint of table.constraints) {
|
|
165
|
+
const name = constraint.name ? `CONSTRAINT ${constraint.name} ` : "";
|
|
166
|
+
if (constraint.type === "primary_key") {
|
|
167
|
+
if (constraint.columns.length > 1) cols.push(` ${name}PRIMARY KEY (${constraint.columns.map(quoteIdent).join(", ")})`);
|
|
168
|
+
} else if (constraint.type === "unique") {
|
|
169
|
+
if (constraint.columns.length > 1) cols.push(` ${name}UNIQUE (${constraint.columns.map(quoteIdent).join(", ")})`);
|
|
170
|
+
} else if (constraint.type === "foreign_key" && constraint.references) cols.push(` ${name}FOREIGN KEY (${constraint.columns.map(quoteIdent).join(", ")}) REFERENCES ${quoteIdent(constraint.references.table)}(${constraint.references.columns.map(quoteIdent).join(", ")})`);
|
|
171
|
+
}
|
|
172
|
+
return `CREATE TABLE ${quoteIdent(table.name)} (\n${cols.join(",\n")}\n);`;
|
|
173
|
+
}
|
|
174
|
+
function operationToSQL(op) {
|
|
175
|
+
switch (op.type) {
|
|
176
|
+
case "create_table": return createTableSQL(op.table);
|
|
177
|
+
case "drop_table": return `DROP TABLE IF EXISTS ${quoteIdent(op.tableName)};`;
|
|
178
|
+
case "add_column": return `ALTER TABLE ${quoteIdent(op.tableName)} ADD COLUMN ${columnToSQL(op.column)};`;
|
|
179
|
+
case "drop_column": return `ALTER TABLE ${quoteIdent(op.tableName)} DROP COLUMN ${quoteIdent(op.columnName)};`;
|
|
180
|
+
case "alter_column": {
|
|
181
|
+
const qt = quoteIdent(op.tableName);
|
|
182
|
+
const qc = quoteIdent(op.columnName);
|
|
183
|
+
return `-- SQLite does not support ALTER COLUMN. To change column "${op.columnName}" in "${op.tableName}", recreate the table.\n-- ALTER TABLE ${qt} ALTER COLUMN ${qc} (not supported);`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function generateSQL(operations) {
|
|
188
|
+
if (operations.length === 0) return "";
|
|
189
|
+
return `${operations.map(operationToSQL).filter(Boolean).join("\n\n")}\n`;
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/sqlite/d1/types.ts
|
|
193
|
+
function sqlTypeToJsonSchema(sqlType) {
|
|
194
|
+
switch (sqlType.toUpperCase().replace(/\(.+\)/, "").trim()) {
|
|
195
|
+
case "INTEGER":
|
|
196
|
+
case "INT":
|
|
197
|
+
case "TINYINT":
|
|
198
|
+
case "SMALLINT":
|
|
199
|
+
case "MEDIUMINT":
|
|
200
|
+
case "BIGINT": return { type: "integer" };
|
|
201
|
+
case "REAL":
|
|
202
|
+
case "DOUBLE":
|
|
203
|
+
case "DOUBLE PRECISION":
|
|
204
|
+
case "FLOAT":
|
|
205
|
+
case "NUMERIC":
|
|
206
|
+
case "DECIMAL": return { type: "number" };
|
|
207
|
+
case "BOOLEAN": return { type: "integer" };
|
|
208
|
+
case "TEXT":
|
|
209
|
+
case "CLOB": return { type: "string" };
|
|
210
|
+
case "VARCHAR":
|
|
211
|
+
case "CHARACTER VARYING":
|
|
212
|
+
case "NVARCHAR":
|
|
213
|
+
case "CHARACTER": {
|
|
214
|
+
const lenMatch = sqlType.match(/\((\d+)\)/);
|
|
215
|
+
return lenMatch ? {
|
|
216
|
+
type: "string",
|
|
217
|
+
maxLength: parseInt(lenMatch[1], 10)
|
|
218
|
+
} : { type: "string" };
|
|
219
|
+
}
|
|
220
|
+
case "BLOB": return {
|
|
221
|
+
type: "string",
|
|
222
|
+
format: "byte"
|
|
223
|
+
};
|
|
224
|
+
case "DATE": return {
|
|
225
|
+
type: "string",
|
|
226
|
+
format: "date"
|
|
227
|
+
};
|
|
228
|
+
case "DATETIME":
|
|
229
|
+
case "TIMESTAMP": return {
|
|
230
|
+
type: "string",
|
|
231
|
+
format: "date-time"
|
|
232
|
+
};
|
|
233
|
+
default: return { type: "string" };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function sqlTypeToTsType(sqlType) {
|
|
237
|
+
switch (sqlType.toUpperCase().replace(/\(.+\)/, "").trim()) {
|
|
238
|
+
case "INTEGER":
|
|
239
|
+
case "INT":
|
|
240
|
+
case "TINYINT":
|
|
241
|
+
case "SMALLINT":
|
|
242
|
+
case "MEDIUMINT":
|
|
243
|
+
case "BIGINT":
|
|
244
|
+
case "REAL":
|
|
245
|
+
case "DOUBLE":
|
|
246
|
+
case "DOUBLE PRECISION":
|
|
247
|
+
case "FLOAT":
|
|
248
|
+
case "NUMERIC":
|
|
249
|
+
case "DECIMAL": return "number";
|
|
250
|
+
case "BOOLEAN": return "number";
|
|
251
|
+
case "BLOB": return "ArrayBuffer";
|
|
252
|
+
default: return "string";
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/sqlite/d1/query-codegen.ts
|
|
257
|
+
function camelCase(str) {
|
|
258
|
+
return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
259
|
+
}
|
|
260
|
+
function generateParamInterface(query) {
|
|
261
|
+
if (query.params.length === 0) return null;
|
|
262
|
+
return `export interface ${`${pascalCase(query.name)}Params`} {\n${query.params.map((p) => {
|
|
263
|
+
const base = sqlTypeToTsType(p.sqlType);
|
|
264
|
+
const type = p.nullable ? `${base} | null` : base;
|
|
265
|
+
return ` ${p.name}: ${type};`;
|
|
266
|
+
}).join("\n")}\n}`;
|
|
267
|
+
}
|
|
268
|
+
function generateParamSchema(query) {
|
|
269
|
+
if (query.params.length === 0) return null;
|
|
270
|
+
const name = `${pascalCase(query.name)}ParamsSchema`;
|
|
271
|
+
const properties = {};
|
|
272
|
+
const required = [];
|
|
273
|
+
for (const param of query.params) {
|
|
274
|
+
const jsonType = sqlTypeToJsonSchema(param.sqlType);
|
|
275
|
+
if (param.nullable) properties[param.name] = {
|
|
276
|
+
...jsonType,
|
|
277
|
+
type: Array.isArray(jsonType.type) ? [...jsonType.type, "null"] : [jsonType.type, "null"]
|
|
278
|
+
};
|
|
279
|
+
else {
|
|
280
|
+
properties[param.name] = jsonType;
|
|
281
|
+
required.push(param.name);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const schema = {
|
|
285
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
286
|
+
title: `${pascalCase(query.name)}Params`,
|
|
287
|
+
type: "object",
|
|
288
|
+
properties,
|
|
289
|
+
required,
|
|
290
|
+
additionalProperties: false
|
|
291
|
+
};
|
|
292
|
+
return `export const ${name} = ${JSON.stringify(schema, null, 2)} as const;`;
|
|
293
|
+
}
|
|
294
|
+
function generateResultType(query, snapshot) {
|
|
295
|
+
if (query.command === "exec" || query.command === "execrows" || query.command === "execresult") return null;
|
|
296
|
+
if (!query.returnsTable || !snapshot[query.returnsTable]) return null;
|
|
297
|
+
const table = snapshot[query.returnsTable];
|
|
298
|
+
const typeName = pascalCase(query.returnsTable);
|
|
299
|
+
if (query.returnsColumns.length === table.columns.length && query.returnsColumns.every((c) => table.columns.some((tc) => tc.name === c))) return typeName;
|
|
300
|
+
return `Pick<${typeName}, ${query.returnsColumns.map((c) => `"${c}"`).join(" | ")}>`;
|
|
301
|
+
}
|
|
302
|
+
function generateReturnType(query, resultType) {
|
|
303
|
+
switch (query.command) {
|
|
304
|
+
case "one": return `Promise<${resultType ?? "unknown"} | null>`;
|
|
305
|
+
case "many": return `Promise<${resultType ?? "unknown"}[]>`;
|
|
306
|
+
case "exec": return "Promise<void>";
|
|
307
|
+
case "execrows": return "Promise<number>";
|
|
308
|
+
case "execresult": return "Promise<D1Result<unknown>>";
|
|
309
|
+
default: return "Promise<void>";
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function generateFunctionBody(query) {
|
|
313
|
+
const paramArgs = query.params.map((p) => `params.${p.name}`).join(", ");
|
|
314
|
+
const bindCall = query.params.length > 0 ? `.bind(${paramArgs})` : "";
|
|
315
|
+
switch (query.command) {
|
|
316
|
+
case "one": return ` const result = await db.prepare(sql)${bindCall}.first<${generateResultType(query, {}) ?? "unknown"}>();\n return result ?? null;`;
|
|
317
|
+
case "many": return ` const result = await db.prepare(sql)${bindCall}.all();\n return result.results;`;
|
|
318
|
+
case "exec": return ` await db.prepare(sql)${bindCall}.run();`;
|
|
319
|
+
case "execrows": return ` const result = await db.prepare(sql)${bindCall}.run();\n return result.meta.changes;`;
|
|
320
|
+
case "execresult": return ` return await db.prepare(sql)${bindCall}.run();`;
|
|
321
|
+
default: return ` await db.prepare(sql)${bindCall}.run();`;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function generateFunction(query, snapshot) {
|
|
325
|
+
const fnName = camelCase(query.name);
|
|
326
|
+
const returnType = generateReturnType(query, generateResultType(query, snapshot));
|
|
327
|
+
const hasParams = query.params.length > 0;
|
|
328
|
+
const paramsType = hasParams ? `${pascalCase(query.name)}Params` : null;
|
|
329
|
+
const args = hasParams ? `db: D1Database, params: ${paramsType}` : "db: D1Database";
|
|
330
|
+
const sql = query.expandedSql.replace(/'/g, "\\'").replace(/\n/g, "\\n");
|
|
331
|
+
const lines = [];
|
|
332
|
+
lines.push(`export async function ${fnName}(${args}): ${returnType} {`);
|
|
333
|
+
lines.push(` const sql = '${sql}';`);
|
|
334
|
+
lines.push(generateFunctionBody(query));
|
|
335
|
+
lines.push("}");
|
|
336
|
+
return lines.join("\n");
|
|
337
|
+
}
|
|
338
|
+
function generateQueryFiles(queries, snapshot) {
|
|
339
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
340
|
+
for (const query of queries) {
|
|
341
|
+
const key = query.sourceFile.replace(/\.sql$/, "");
|
|
342
|
+
if (!grouped.has(key)) grouped.set(key, []);
|
|
343
|
+
grouped.get(key).push(query);
|
|
344
|
+
}
|
|
345
|
+
const files = {};
|
|
346
|
+
for (const [basename, fileQueries] of grouped) {
|
|
347
|
+
const lines = ["// This file is auto-generated by tsqx. Do not edit manually."];
|
|
348
|
+
lines.push("// D1Database type is available globally in Cloudflare Workers");
|
|
349
|
+
lines.push("");
|
|
350
|
+
const tableImports = /* @__PURE__ */ new Set();
|
|
351
|
+
for (const query of fileQueries) if (query.returnsTable && snapshot[query.returnsTable]) tableImports.add(query.returnsTable);
|
|
352
|
+
if (tableImports.size > 0) {
|
|
353
|
+
const imports = [...tableImports].map((t) => `type ${pascalCase(t)}`).join(", ");
|
|
354
|
+
lines.push(`import { ${imports} } from "../../generated";`);
|
|
355
|
+
}
|
|
356
|
+
lines.push("");
|
|
357
|
+
for (const query of fileQueries) {
|
|
358
|
+
const paramInterface = generateParamInterface(query);
|
|
359
|
+
if (paramInterface) {
|
|
360
|
+
lines.push(paramInterface);
|
|
361
|
+
lines.push("");
|
|
362
|
+
}
|
|
363
|
+
const paramSchema = generateParamSchema(query);
|
|
364
|
+
if (paramSchema) {
|
|
365
|
+
lines.push(paramSchema);
|
|
366
|
+
lines.push("");
|
|
367
|
+
}
|
|
368
|
+
lines.push(generateFunction(query, snapshot));
|
|
369
|
+
lines.push("");
|
|
370
|
+
}
|
|
371
|
+
files[`${basename}.ts`] = lines.join("\n");
|
|
372
|
+
}
|
|
373
|
+
return files;
|
|
374
|
+
}
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/sqlite/d1/index.ts
|
|
377
|
+
function d1Dialect() {
|
|
378
|
+
return {
|
|
379
|
+
name: "d1",
|
|
380
|
+
parseSchema: parseSchemaFiles,
|
|
381
|
+
generateSQL,
|
|
382
|
+
sqlTypeToJsonSchema,
|
|
383
|
+
sqlTypeToTsType,
|
|
384
|
+
generateQueryCode: generateQueryFiles
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
//#endregion
|
|
388
|
+
export { d1Dialect };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsqx/kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"import": "./dist/postgres/pg/index.mjs",
|
|
14
14
|
"require": "./dist/postgres/pg/index.cjs",
|
|
15
15
|
"types": "./dist/postgres/pg/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./sqlite/d1": {
|
|
18
|
+
"import": "./dist/sqlite/d1/index.mjs",
|
|
19
|
+
"require": "./dist/sqlite/d1/index.cjs",
|
|
20
|
+
"types": "./dist/sqlite/d1/index.d.ts"
|
|
16
21
|
}
|
|
17
22
|
},
|
|
18
23
|
"main": "./dist/index.cjs",
|
|
@@ -23,7 +28,7 @@
|
|
|
23
28
|
],
|
|
24
29
|
"dependencies": {
|
|
25
30
|
"neverthrow": "^8.2.0",
|
|
26
|
-
"@tsqx/core": "^0.0.
|
|
31
|
+
"@tsqx/core": "^0.0.5"
|
|
27
32
|
},
|
|
28
33
|
"repository": {
|
|
29
34
|
"type": "git",
|