sqlcx-orm 0.1.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.
@@ -0,0 +1,9 @@
1
+ // @bun
2
+ import {
3
+ defineConfig,
4
+ loadConfig
5
+ } from "../chunk-3qq0zjsm.js";
6
+ export {
7
+ loadConfig,
8
+ defineConfig
9
+ };
@@ -0,0 +1,151 @@
1
+ // @bun
2
+ import {
3
+ camelCase,
4
+ pascalCase
5
+ } from "../../../chunk-49wq4032.js";
6
+
7
+ // src/generator/typescript/driver/bun-sql.ts
8
+ function splitWords(str) {
9
+ return str.replace(/([a-z])([A-Z])/g, "$1_$2");
10
+ }
11
+ function toCamel(str) {
12
+ return camelCase(splitWords(str));
13
+ }
14
+ function toPascal(str) {
15
+ return pascalCase(splitWords(str));
16
+ }
17
+ function tsType(type) {
18
+ if (type.elementType) {
19
+ return `${tsType(type.elementType)}[]`;
20
+ }
21
+ switch (type.category) {
22
+ case "string":
23
+ case "uuid":
24
+ case "enum":
25
+ return "string";
26
+ case "number":
27
+ return "number";
28
+ case "boolean":
29
+ return "boolean";
30
+ case "date":
31
+ return "Date";
32
+ case "json":
33
+ return "unknown";
34
+ case "binary":
35
+ return "Uint8Array";
36
+ case "unknown":
37
+ return "unknown";
38
+ }
39
+ }
40
+ function generateRowType(query) {
41
+ if (query.returns.length === 0)
42
+ return "";
43
+ const typeName = `${toPascal(query.name)}Row`;
44
+ const fields = query.returns.map((col) => {
45
+ const fieldName = col.alias ?? col.name;
46
+ const type = tsType(col.type);
47
+ const nullable = col.nullable ? " | null" : "";
48
+ return ` ${fieldName}: ${type}${nullable};`;
49
+ }).join(`
50
+ `);
51
+ return `export interface ${typeName} {
52
+ ${fields}
53
+ }`;
54
+ }
55
+ function generateParamsType(query) {
56
+ if (query.params.length === 0)
57
+ return "";
58
+ const typeName = `${toPascal(query.name)}Params`;
59
+ const fields = query.params.map((p) => ` ${p.name}: ${tsType(p.type)};`).join(`
60
+ `);
61
+ return `export interface ${typeName} {
62
+ ${fields}
63
+ }`;
64
+ }
65
+ function createBunSqlGenerator() {
66
+ return {
67
+ name: "bun-sql",
68
+ generateImports() {
69
+ return "";
70
+ },
71
+ generateClientAdapter() {
72
+ return `interface BunSqlDriver {
73
+ unsafe(query: string, values?: unknown[]): Promise<any[] & { count: number }>;
74
+ }
75
+
76
+ export class BunSqlClient implements DatabaseClient {
77
+ private sql: BunSqlDriver;
78
+
79
+ constructor(sql: BunSqlDriver) {
80
+ this.sql = sql;
81
+ }
82
+
83
+ async query<T>(text: string, values?: unknown[]): Promise<T[]> {
84
+ const result = await this.sql.unsafe(text, values);
85
+ return [...result] as T[];
86
+ }
87
+
88
+ async queryOne<T>(text: string, values?: unknown[]): Promise<T | null> {
89
+ const rows = await this.query<T>(text, values);
90
+ return rows[0] ?? null;
91
+ }
92
+
93
+ async execute(text: string, values?: unknown[]): Promise<{ rowsAffected: number }> {
94
+ const result = await this.sql.unsafe(text, values);
95
+ return { rowsAffected: result.count };
96
+ }
97
+ }`;
98
+ },
99
+ generateQueryFunction(query) {
100
+ const fnName = toCamel(query.name);
101
+ const rowType = generateRowType(query);
102
+ const hasParams = query.params.length > 0;
103
+ const paramsInterface = generateParamsType(query);
104
+ const paramsTypeName = `${toPascal(query.name)}Params`;
105
+ const sqlConst = `export const ${fnName}Sql = ${JSON.stringify(query.sql)};`;
106
+ const paramsSig = hasParams ? `, params: ${paramsTypeName}` : "";
107
+ const valuesArg = hasParams ? `[${query.params.map((p) => `params.${p.name}`).join(", ")}]` : "[]";
108
+ let returnType;
109
+ let body;
110
+ switch (query.command) {
111
+ case "one": {
112
+ const typeName = `${toPascal(query.name)}Row`;
113
+ returnType = `Promise<${typeName} | null>`;
114
+ body = ` return client.queryOne<${typeName}>(${fnName}Sql, ${valuesArg});`;
115
+ break;
116
+ }
117
+ case "many": {
118
+ const typeName = `${toPascal(query.name)}Row`;
119
+ returnType = `Promise<${typeName}[]>`;
120
+ body = ` return client.query<${typeName}>(${fnName}Sql, ${valuesArg});`;
121
+ break;
122
+ }
123
+ case "exec": {
124
+ returnType = "Promise<void>";
125
+ body = ` await client.execute(${fnName}Sql, ${valuesArg});`;
126
+ break;
127
+ }
128
+ case "execresult": {
129
+ returnType = "Promise<{ rowsAffected: number }>";
130
+ body = ` return client.execute(${fnName}Sql, ${valuesArg});`;
131
+ break;
132
+ }
133
+ }
134
+ const parts = [];
135
+ if (rowType)
136
+ parts.push(rowType);
137
+ if (paramsInterface)
138
+ parts.push(paramsInterface);
139
+ parts.push(sqlConst);
140
+ parts.push(`export async function ${fnName}(client: DatabaseClient${paramsSig}): ${returnType} {
141
+ ${body}
142
+ }`);
143
+ return parts.join(`
144
+
145
+ `);
146
+ }
147
+ };
148
+ }
149
+ export {
150
+ createBunSqlGenerator
151
+ };
@@ -0,0 +1,110 @@
1
+ // @bun
2
+ import {
3
+ pascalCase
4
+ } from "../../chunk-49wq4032.js";
5
+
6
+ // src/generator/typescript/index.ts
7
+ import path from "path";
8
+ function joinPath(base, filename) {
9
+ const joined = path.join(base, filename);
10
+ if (base.startsWith("./") && !joined.startsWith("./")) {
11
+ return "./" + joined;
12
+ }
13
+ return joined;
14
+ }
15
+ function generateSchemaFile(schema, ir) {
16
+ const parts = [];
17
+ parts.push(schema.generateImports());
18
+ for (const enumDef of ir.enums) {
19
+ parts.push(schema.generateEnumSchema(enumDef));
20
+ }
21
+ for (const table of ir.tables) {
22
+ parts.push(schema.generateSelectSchema(table, ir));
23
+ parts.push(schema.generateInsertSchema(table, ir));
24
+ }
25
+ for (const table of ir.tables) {
26
+ const selectName = `Select${pascalCase(table.name)}`;
27
+ const insertName = `Insert${pascalCase(table.name)}`;
28
+ parts.push(schema.generateTypeAlias(selectName, selectName));
29
+ parts.push(schema.generateTypeAlias(insertName, insertName));
30
+ }
31
+ for (const enumDef of ir.enums) {
32
+ const name = pascalCase(enumDef.name);
33
+ parts.push(schema.generateTypeAlias(name, name));
34
+ }
35
+ return parts.join(`
36
+
37
+ `) + `
38
+ `;
39
+ }
40
+ var DATABASE_CLIENT_INTERFACE = `export interface DatabaseClient {
41
+ query<T>(sql: string, params: unknown[]): Promise<T[]>;
42
+ queryOne<T>(sql: string, params: unknown[]): Promise<T | null>;
43
+ execute(sql: string, params: unknown[]): Promise<{ rowsAffected: number }>;
44
+ }`;
45
+ function generateClientFile(driver) {
46
+ const parts = [];
47
+ const driverImports = driver.generateImports();
48
+ if (driverImports) {
49
+ parts.push(driverImports);
50
+ }
51
+ parts.push(DATABASE_CLIENT_INTERFACE);
52
+ parts.push(driver.generateClientAdapter());
53
+ return parts.join(`
54
+
55
+ `) + `
56
+ `;
57
+ }
58
+ function generateQueryFiles(driver, ir, outDir) {
59
+ const grouped = new Map;
60
+ for (const query of ir.queries) {
61
+ const existing = grouped.get(query.sourceFile);
62
+ if (existing) {
63
+ existing.push(query);
64
+ } else {
65
+ grouped.set(query.sourceFile, [query]);
66
+ }
67
+ }
68
+ const files = [];
69
+ for (const [sourceFile, queries] of grouped) {
70
+ const basename = path.basename(sourceFile, path.extname(sourceFile));
71
+ const filename = `${basename}.queries.ts`;
72
+ const parts = [];
73
+ parts.push(`import type { DatabaseClient } from "./client";`);
74
+ for (const query of queries) {
75
+ parts.push(driver.generateQueryFunction(query));
76
+ }
77
+ files.push({
78
+ path: joinPath(outDir, filename),
79
+ content: parts.join(`
80
+
81
+ `) + `
82
+ `
83
+ });
84
+ }
85
+ return files;
86
+ }
87
+ function createTypeScriptPlugin(options) {
88
+ const { schema, driver } = options;
89
+ return {
90
+ language: "typescript",
91
+ fileExtension: ".ts",
92
+ generate(ir, langOptions) {
93
+ const outDir = langOptions.out;
94
+ const files = [];
95
+ files.push({
96
+ path: joinPath(outDir, "schema.ts"),
97
+ content: generateSchemaFile(schema, ir)
98
+ });
99
+ files.push({
100
+ path: joinPath(outDir, "client.ts"),
101
+ content: generateClientFile(driver)
102
+ });
103
+ files.push(...generateQueryFiles(driver, ir, outDir));
104
+ return files;
105
+ }
106
+ };
107
+ }
108
+ export {
109
+ createTypeScriptPlugin
110
+ };
@@ -0,0 +1,127 @@
1
+ // @bun
2
+ import {
3
+ pascalCase
4
+ } from "../../../chunk-49wq4032.js";
5
+
6
+ // src/generator/typescript/schema/typebox.ts
7
+ function escapeString(str) {
8
+ return JSON.stringify(str).slice(1, -1);
9
+ }
10
+ function jsonShapeToTypeBox(shape) {
11
+ switch (shape.kind) {
12
+ case "string":
13
+ return "Type.String()";
14
+ case "number":
15
+ return "Type.Number()";
16
+ case "boolean":
17
+ return "Type.Boolean()";
18
+ case "object": {
19
+ const fields = Object.entries(shape.fields).map(([key, val]) => `"${escapeString(key)}": ${jsonShapeToTypeBox(val)}`).join(", ");
20
+ return `Type.Object({ ${fields} })`;
21
+ }
22
+ case "array":
23
+ return `Type.Array(${jsonShapeToTypeBox(shape.element)})`;
24
+ case "nullable":
25
+ return `Type.Union([${jsonShapeToTypeBox(shape.inner)}, Type.Null()])`;
26
+ }
27
+ }
28
+ function typeBoxType(type) {
29
+ if (type.enumValues) {
30
+ const literals = type.enumValues.map((v) => `Type.Literal("${escapeString(v)}")`).join(", ");
31
+ return `Type.Union([${literals}])`;
32
+ }
33
+ if (type.jsonShape) {
34
+ return jsonShapeToTypeBox(type.jsonShape);
35
+ }
36
+ if (type.elementType) {
37
+ return `Type.Array(${typeBoxType(type.elementType)})`;
38
+ }
39
+ switch (type.category) {
40
+ case "string":
41
+ return "Type.String()";
42
+ case "number":
43
+ return "Type.Number()";
44
+ case "boolean":
45
+ return "Type.Boolean()";
46
+ case "date":
47
+ return "Type.Date()";
48
+ case "json":
49
+ return "Type.Any()";
50
+ case "uuid":
51
+ return "Type.String()";
52
+ case "binary":
53
+ return "Type.Uint8Array()";
54
+ case "enum": {
55
+ if (type.enumName) {
56
+ return pascalCase(type.enumName);
57
+ }
58
+ return "Type.String()";
59
+ }
60
+ case "unknown":
61
+ return "Type.Unknown()";
62
+ default: {
63
+ const _exhaustive = type.category;
64
+ return _exhaustive;
65
+ }
66
+ }
67
+ }
68
+ function selectColumn(col) {
69
+ const base = typeBoxType(col.type);
70
+ if (col.nullable) {
71
+ return `Type.Union([${base}, Type.Null()])`;
72
+ }
73
+ return base;
74
+ }
75
+ function insertColumn(col) {
76
+ const base = typeBoxType(col.type);
77
+ if (col.hasDefault) {
78
+ if (col.nullable) {
79
+ return `Type.Optional(Type.Union([${base}, Type.Null()]))`;
80
+ }
81
+ return `Type.Optional(${base})`;
82
+ }
83
+ if (col.nullable) {
84
+ return `Type.Optional(Type.Union([${base}, Type.Null()]))`;
85
+ }
86
+ return base;
87
+ }
88
+ function objectBody(columns, mapper) {
89
+ const fields = columns.map((col) => ` "${escapeString(col.name)}": ${mapper(col)}`).join(`,
90
+ `);
91
+ return `{
92
+ ${fields}
93
+ }`;
94
+ }
95
+ function createTypeBoxGenerator() {
96
+ return {
97
+ name: "typebox",
98
+ generateImports() {
99
+ return `import { Type, type Static } from "@sinclair/typebox";
100
+
101
+ // Requires @sinclair/typebox >= 0.31.0 (for Type.Date and Type.Uint8Array)
102
+
103
+ type Prettify<T> = { [K in keyof T]: T[K] } & {};`;
104
+ },
105
+ generateEnumSchema(enumDef) {
106
+ const name = pascalCase(enumDef.name);
107
+ const literals = enumDef.values.map((v) => `Type.Literal("${escapeString(v)}")`).join(", ");
108
+ return `export const ${name} = Type.Union([${literals}]);`;
109
+ },
110
+ generateSelectSchema(table, _ir) {
111
+ const name = `Select${pascalCase(table.name)}`;
112
+ const body = objectBody(table.columns, selectColumn);
113
+ return `export const ${name} = Type.Object(${body});`;
114
+ },
115
+ generateInsertSchema(table, _ir) {
116
+ const name = `Insert${pascalCase(table.name)}`;
117
+ const body = objectBody(table.columns, insertColumn);
118
+ return `export const ${name} = Type.Object(${body});`;
119
+ },
120
+ generateTypeAlias(name, schemaVarName) {
121
+ return `export type ${name} = Prettify<Static<typeof ${schemaVarName}>>;`;
122
+ }
123
+ };
124
+ }
125
+ export {
126
+ createTypeBoxGenerator
127
+ };
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ import {
3
+ defineConfig
4
+ } from "./chunk-3qq0zjsm.js";
5
+ export {
6
+ defineConfig
7
+ };