@zauso-ai/capstan-db 0.1.2

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,22 @@
1
+ import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
+ import type { DatabaseConfig } from "./types.js";
3
+ export interface DatabaseInstance {
4
+ /** The Drizzle ORM database instance. */
5
+ db: BetterSQLite3Database;
6
+ /** Close the underlying database connection. */
7
+ close: () => void;
8
+ }
9
+ /**
10
+ * Create a Drizzle database instance backed by better-sqlite3.
11
+ *
12
+ * @param config - Database configuration. Currently only `sqlite` provider is
13
+ * supported. The `url` field should be a file path (or `:memory:` for an
14
+ * in-memory database).
15
+ *
16
+ * @example
17
+ * const { db, close } = createDatabase({ provider: "sqlite", url: "./data.db" });
18
+ * // ... use db ...
19
+ * close();
20
+ */
21
+ export declare function createDatabase(config: DatabaseConfig): DatabaseInstance;
22
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,EAAE,EAAE,qBAAqB,CAAC;IAC1B,gDAAgD;IAChD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,gBAAgB,CAoBvE"}
package/dist/client.js ADDED
@@ -0,0 +1,30 @@
1
+ import { drizzle } from "drizzle-orm/better-sqlite3";
2
+ import Database from "better-sqlite3";
3
+ /**
4
+ * Create a Drizzle database instance backed by better-sqlite3.
5
+ *
6
+ * @param config - Database configuration. Currently only `sqlite` provider is
7
+ * supported. The `url` field should be a file path (or `:memory:` for an
8
+ * in-memory database).
9
+ *
10
+ * @example
11
+ * const { db, close } = createDatabase({ provider: "sqlite", url: "./data.db" });
12
+ * // ... use db ...
13
+ * close();
14
+ */
15
+ export function createDatabase(config) {
16
+ if (config.provider !== "sqlite") {
17
+ throw new Error(`@zauso-ai/capstan-db: Unsupported database provider "${config.provider}". Only "sqlite" is currently supported.`);
18
+ }
19
+ const sqlite = new Database(config.url);
20
+ // Enable WAL mode for better concurrent read performance.
21
+ sqlite.pragma("journal_mode = WAL");
22
+ const db = drizzle({ client: sqlite });
23
+ return {
24
+ db,
25
+ close() {
26
+ sqlite.close();
27
+ },
28
+ };
29
+ }
30
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAErD,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAUtC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,wDAAwD,MAAM,CAAC,QAAQ,0CAA0C,CAClH,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAExC,0DAA0D;IAC1D,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAEpC,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAEvC,OAAO;QACL,EAAE;QACF,KAAK;YACH,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/crud.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { ModelDefinition } from "./types.js";
2
+ export interface CrudRouteFiles {
3
+ /** File path relative to app/routes/, e.g. "tickets/index.api.ts" */
4
+ path: string;
5
+ content: string;
6
+ }
7
+ /**
8
+ * Naive English pluraliser — sufficient for model-name pluralisation.
9
+ *
10
+ * ticket -> tickets
11
+ * company -> companies
12
+ * status -> statuses
13
+ * bus -> buses
14
+ */
15
+ export declare function pluralize(word: string): string;
16
+ /**
17
+ * Generate CRUD API route handler files for a given model definition.
18
+ *
19
+ * Returns an array of `{ path, content }` objects where `path` is relative
20
+ * to the app's `app/routes/` directory.
21
+ *
22
+ * Two files are produced:
23
+ * 1. `<plural>/index.api.ts` — GET (list) + POST (create)
24
+ * 2. `<plural>/[id].api.ts` — GET (by id) + PUT (update) + DELETE
25
+ */
26
+ export declare function generateCrudRoutes(model: ModelDefinition): CrudRouteFiles[];
27
+ //# sourceMappingURL=crud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../src/crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAMnE,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0B9C;AAiHD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,EAAE,CAsG3E"}
package/dist/crud.js ADDED
@@ -0,0 +1,234 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Pluralisation helper
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Naive English pluraliser — sufficient for model-name pluralisation.
6
+ *
7
+ * ticket -> tickets
8
+ * company -> companies
9
+ * status -> statuses
10
+ * bus -> buses
11
+ */
12
+ export function pluralize(word) {
13
+ if (word.length === 0)
14
+ return word;
15
+ const lower = word.toLowerCase();
16
+ // Words ending in consonant + y → ies
17
+ if (lower.endsWith("y") &&
18
+ lower.length > 1 &&
19
+ !"aeiou".includes(lower[lower.length - 2])) {
20
+ return word.slice(0, -1) + "ies";
21
+ }
22
+ // Words ending in s, sh, ch, x, z → es
23
+ if (lower.endsWith("s") ||
24
+ lower.endsWith("sh") ||
25
+ lower.endsWith("ch") ||
26
+ lower.endsWith("x") ||
27
+ lower.endsWith("z")) {
28
+ return word + "es";
29
+ }
30
+ return word + "s";
31
+ }
32
+ // ---------------------------------------------------------------------------
33
+ // Field → Zod mapping
34
+ // ---------------------------------------------------------------------------
35
+ /**
36
+ * Convert a FieldDefinition into a Zod expression string.
37
+ */
38
+ function fieldToZod(field) {
39
+ let base;
40
+ // Enum takes precedence when values are supplied.
41
+ if (field.enum && field.enum.length > 0) {
42
+ const values = field.enum.map((v) => `"${v}"`).join(", ");
43
+ base = `z.enum([${values}])`;
44
+ }
45
+ else {
46
+ switch (field.type) {
47
+ case "string":
48
+ case "text":
49
+ case "date":
50
+ case "datetime":
51
+ base = "z.string()";
52
+ break;
53
+ case "integer":
54
+ base = "z.number().int()";
55
+ break;
56
+ case "number":
57
+ base = "z.number()";
58
+ break;
59
+ case "boolean":
60
+ base = "z.boolean()";
61
+ break;
62
+ case "json":
63
+ base = "z.unknown()";
64
+ break;
65
+ default:
66
+ base = "z.unknown()";
67
+ }
68
+ }
69
+ // Constraints (only meaningful for string and number types).
70
+ if (field.min !== undefined) {
71
+ if (field.type === "string" ||
72
+ field.type === "text") {
73
+ base += `.min(${field.min})`;
74
+ }
75
+ else if (field.type === "integer" ||
76
+ field.type === "number") {
77
+ base += `.min(${field.min})`;
78
+ }
79
+ }
80
+ if (field.max !== undefined) {
81
+ if (field.type === "string" ||
82
+ field.type === "text") {
83
+ base += `.max(${field.max})`;
84
+ }
85
+ else if (field.type === "integer" ||
86
+ field.type === "number") {
87
+ base += `.max(${field.max})`;
88
+ }
89
+ }
90
+ // Mark as optional when the field is not required.
91
+ if (!field.required) {
92
+ base += ".optional()";
93
+ }
94
+ return base;
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Schema generation helpers
98
+ // ---------------------------------------------------------------------------
99
+ /**
100
+ * Build a Zod object literal string from a set of fields, optionally
101
+ * excluding certain field names (e.g. auto-generated id).
102
+ */
103
+ function buildZodObject(fields, options) {
104
+ const exclude = options?.exclude ?? new Set();
105
+ const allOptional = options?.allOptional ?? false;
106
+ const lines = [];
107
+ for (const [name, field] of Object.entries(fields)) {
108
+ if (exclude.has(name))
109
+ continue;
110
+ let zodExpr;
111
+ if (allOptional) {
112
+ // Strip existing .optional() to avoid duplicates, then add it.
113
+ zodExpr = fieldToZod({ ...field, required: true });
114
+ zodExpr += ".optional()";
115
+ }
116
+ else {
117
+ zodExpr = fieldToZod(field);
118
+ }
119
+ lines.push(` ${name}: ${zodExpr},`);
120
+ }
121
+ return `z.object({\n${lines.join("\n")}\n})`;
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // Code generation
125
+ // ---------------------------------------------------------------------------
126
+ /**
127
+ * Generate CRUD API route handler files for a given model definition.
128
+ *
129
+ * Returns an array of `{ path, content }` objects where `path` is relative
130
+ * to the app's `app/routes/` directory.
131
+ *
132
+ * Two files are produced:
133
+ * 1. `<plural>/index.api.ts` — GET (list) + POST (create)
134
+ * 2. `<plural>/[id].api.ts` — GET (by id) + PUT (update) + DELETE
135
+ */
136
+ export function generateCrudRoutes(model) {
137
+ const plural = pluralize(model.name.toLowerCase());
138
+ const resourceName = model.name;
139
+ // Determine which fields are auto-generated (id, updatedAt).
140
+ const autoFields = new Set();
141
+ for (const [name, field] of Object.entries(model.fields)) {
142
+ if (field.autoId)
143
+ autoFields.add(name);
144
+ if (field.updatedAt)
145
+ autoFields.add(name);
146
+ }
147
+ // ----- index.api.ts (list + create) -----
148
+ const createSchema = buildZodObject(model.fields, { exclude: autoFields });
149
+ const indexContent = `// Auto-generated CRUD routes for ${resourceName}
150
+ // This file was generated by @zauso-ai/capstan-db generateCrudRoutes().
151
+
152
+ import { defineAPI } from "@zauso-ai/capstan-core";
153
+ import { z } from "zod";
154
+
155
+ export const meta = { resource: "${resourceName}" };
156
+
157
+ const CreateInput = ${createSchema};
158
+
159
+ export const GET = defineAPI({
160
+ capability: "read",
161
+ resource: "${resourceName}",
162
+ description: "List all ${plural}",
163
+ handler: async ({ ctx }) => {
164
+ // TODO: replace with actual database query
165
+ return { data: [], total: 0 };
166
+ },
167
+ });
168
+
169
+ export const POST = defineAPI({
170
+ capability: "write",
171
+ resource: "${resourceName}",
172
+ description: "Create a new ${resourceName}",
173
+ policy: "requireAuth",
174
+ input: CreateInput,
175
+ handler: async ({ input, ctx }) => {
176
+ // TODO: replace with actual database insert
177
+ return { data: { id: "TODO", ...input }, created: true };
178
+ },
179
+ });
180
+ `;
181
+ // ----- [id].api.ts (get by id + update + delete) -----
182
+ const updateSchema = buildZodObject(model.fields, {
183
+ exclude: autoFields,
184
+ allOptional: true,
185
+ });
186
+ const idContent = `// Auto-generated CRUD routes for ${resourceName} (by id)
187
+ // This file was generated by @zauso-ai/capstan-db generateCrudRoutes().
188
+
189
+ import { defineAPI } from "@zauso-ai/capstan-core";
190
+ import { z } from "zod";
191
+
192
+ export const meta = { resource: "${resourceName}" };
193
+
194
+ const UpdateInput = ${updateSchema};
195
+
196
+ export const GET = defineAPI({
197
+ capability: "read",
198
+ resource: "${resourceName}",
199
+ description: "Get a ${resourceName} by id",
200
+ handler: async ({ input, ctx }) => {
201
+ // TODO: replace with actual database lookup
202
+ return { data: null };
203
+ },
204
+ });
205
+
206
+ export const PUT = defineAPI({
207
+ capability: "write",
208
+ resource: "${resourceName}",
209
+ description: "Update a ${resourceName}",
210
+ policy: "requireAuth",
211
+ input: UpdateInput,
212
+ handler: async ({ input, ctx }) => {
213
+ // TODO: replace with actual database update
214
+ return { data: { id: "TODO", ...input }, updated: true };
215
+ },
216
+ });
217
+
218
+ export const DELETE = defineAPI({
219
+ capability: "write",
220
+ resource: "${resourceName}",
221
+ description: "Delete a ${resourceName}",
222
+ policy: "requireAuth",
223
+ handler: async ({ ctx }) => {
224
+ // TODO: replace with actual database delete
225
+ return { deleted: true };
226
+ },
227
+ });
228
+ `;
229
+ return [
230
+ { path: `${plural}/index.api.ts`, content: indexContent },
231
+ { path: `${plural}/[id].api.ts`, content: idContent },
232
+ ];
233
+ }
234
+ //# sourceMappingURL=crud.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud.js","sourceRoot":"","sources":["../src/crud.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { defineModel, field, relation } from "./model.js";
2
+ export { generateDrizzleSchema } from "./schema.js";
3
+ export { createDatabase } from "./client.js";
4
+ export { generateMigration, applyMigration } from "./migrate.js";
5
+ export { generateCrudRoutes, pluralize } from "./crud.js";
6
+ export type { CrudRouteFiles } from "./crud.js";
7
+ export type { ModelDefinition, FieldDefinition, RelationDefinition, IndexDefinition, DatabaseConfig, ScalarType, } from "./types.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EACV,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { defineModel, field, relation } from "./model.js";
2
+ export { generateDrizzleSchema } from "./schema.js";
3
+ export { createDatabase } from "./client.js";
4
+ export { generateMigration, applyMigration } from "./migrate.js";
5
+ export { generateCrudRoutes, pluralize } from "./crud.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { ModelDefinition } from "./types.js";
2
+ /**
3
+ * Generate SQL migration statements that transform `fromModels` into
4
+ * `toModels`. This is a forward-only diff: it creates new tables, adds new
5
+ * columns, and creates indexes. It does NOT handle column drops, renames, or
6
+ * type changes (SQLite has limited ALTER TABLE support).
7
+ *
8
+ * @param fromModels - The previous set of model definitions (can be empty for
9
+ * initial migration).
10
+ * @param toModels - The target set of model definitions.
11
+ * @returns An array of SQL statements to execute.
12
+ */
13
+ export declare function generateMigration(fromModels: ModelDefinition[], toModels: ModelDefinition[]): string[];
14
+ /**
15
+ * Execute an array of SQL migration statements against a database.
16
+ *
17
+ * The statements are run inside a transaction so that either all succeed or
18
+ * none are applied.
19
+ *
20
+ * @param db - A Drizzle `BetterSQLite3Database` instance (or any object whose
21
+ * `$client` property is a better-sqlite3 `Database`).
22
+ * @param sql - The SQL statements to execute.
23
+ */
24
+ export declare function applyMigration(db: {
25
+ $client: {
26
+ exec: (sql: string) => void;
27
+ };
28
+ }, sql: string[]): void;
29
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgD,MAAM,YAAY,CAAC;AAyHhG;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,eAAe,EAAE,EAC7B,QAAQ,EAAE,eAAe,EAAE,GAC1B,MAAM,EAAE,CA0DV;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAcpG"}
@@ -0,0 +1,189 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Helpers
3
+ // ---------------------------------------------------------------------------
4
+ function toSnakeCase(str) {
5
+ return str.replace(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
6
+ }
7
+ function pluralise(name) {
8
+ const lower = name.charAt(0).toLowerCase() + name.slice(1);
9
+ if (lower.endsWith("y") && !/[aeiou]y$/i.test(lower)) {
10
+ return lower.slice(0, -1) + "ies";
11
+ }
12
+ if (lower.endsWith("s") || lower.endsWith("x") || lower.endsWith("sh") || lower.endsWith("ch")) {
13
+ return lower + "es";
14
+ }
15
+ return lower + "s";
16
+ }
17
+ /**
18
+ * Map a Capstan scalar type to a SQLite column type.
19
+ */
20
+ function sqliteType(type) {
21
+ switch (type) {
22
+ case "string":
23
+ case "text":
24
+ case "date":
25
+ case "datetime":
26
+ case "json":
27
+ return "TEXT";
28
+ case "integer":
29
+ case "boolean":
30
+ return "INTEGER";
31
+ case "number":
32
+ return "REAL";
33
+ }
34
+ }
35
+ /**
36
+ * Generate the SQL default expression for a field.
37
+ */
38
+ function sqlDefault(def) {
39
+ if (def.default === undefined)
40
+ return null;
41
+ if (def.default === "now")
42
+ return "DEFAULT (datetime('now'))";
43
+ if (typeof def.default === "string")
44
+ return `DEFAULT '${def.default.replace(/'/g, "''")}'`;
45
+ if (typeof def.default === "boolean")
46
+ return `DEFAULT ${def.default ? 1 : 0}`;
47
+ if (typeof def.default === "number")
48
+ return `DEFAULT ${def.default}`;
49
+ return null;
50
+ }
51
+ /**
52
+ * Build a column definition clause for a CREATE TABLE statement.
53
+ */
54
+ function columnDef(fieldName, def) {
55
+ const colName = toSnakeCase(fieldName);
56
+ const parts = [colName, sqliteType(def.type)];
57
+ if (def.autoId) {
58
+ parts.push("PRIMARY KEY");
59
+ }
60
+ if (def.required && !def.autoId) {
61
+ parts.push("NOT NULL");
62
+ }
63
+ if (def.unique && !def.autoId) {
64
+ parts.push("UNIQUE");
65
+ }
66
+ const defaultExpr = sqlDefault(def);
67
+ if (defaultExpr) {
68
+ parts.push(defaultExpr);
69
+ }
70
+ if (def.references) {
71
+ const refTable = pluralise(def.references);
72
+ parts.push(`REFERENCES ${refTable}(id)`);
73
+ }
74
+ return parts.join(" ");
75
+ }
76
+ function tableInfo(model) {
77
+ return { tableName: pluralise(model.name), model };
78
+ }
79
+ function createTableSQL(model) {
80
+ const tableName = pluralise(model.name);
81
+ const cols = Object.entries(model.fields).map(([name, def]) => ` ${columnDef(name, def)}`);
82
+ return `CREATE TABLE ${tableName} (\n${cols.join(",\n")}\n)`;
83
+ }
84
+ function dropTableSQL(tableName) {
85
+ return `DROP TABLE IF EXISTS ${tableName}`;
86
+ }
87
+ function addColumnSQL(tableName, fieldName, def) {
88
+ return `ALTER TABLE ${tableName} ADD COLUMN ${columnDef(fieldName, def)}`;
89
+ }
90
+ function createIndexSQL(tableName, idx, idxNum) {
91
+ const idxName = `idx_${tableName}_${idx.fields.map(toSnakeCase).join("_")}`;
92
+ const unique = idx.unique ? "UNIQUE " : "";
93
+ const cols = idx.fields.map((f) => {
94
+ const col = toSnakeCase(f);
95
+ return idx.order ? `${col} ${idx.order.toUpperCase()}` : col;
96
+ });
97
+ return `CREATE ${unique}INDEX IF NOT EXISTS ${idxName} ON ${tableName} (${cols.join(", ")})`;
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Public API
101
+ // ---------------------------------------------------------------------------
102
+ /**
103
+ * Generate SQL migration statements that transform `fromModels` into
104
+ * `toModels`. This is a forward-only diff: it creates new tables, adds new
105
+ * columns, and creates indexes. It does NOT handle column drops, renames, or
106
+ * type changes (SQLite has limited ALTER TABLE support).
107
+ *
108
+ * @param fromModels - The previous set of model definitions (can be empty for
109
+ * initial migration).
110
+ * @param toModels - The target set of model definitions.
111
+ * @returns An array of SQL statements to execute.
112
+ */
113
+ export function generateMigration(fromModels, toModels) {
114
+ const statements = [];
115
+ const fromMap = new Map();
116
+ for (const m of fromModels) {
117
+ const info = tableInfo(m);
118
+ fromMap.set(info.tableName, info);
119
+ }
120
+ const toMap = new Map();
121
+ for (const m of toModels) {
122
+ const info = tableInfo(m);
123
+ toMap.set(info.tableName, info);
124
+ }
125
+ // Tables that exist in `from` but not in `to` — drop them
126
+ for (const [tableName] of fromMap) {
127
+ if (!toMap.has(tableName)) {
128
+ statements.push(dropTableSQL(tableName));
129
+ }
130
+ }
131
+ // Tables in `to`
132
+ for (const [tableName, toInfo] of toMap) {
133
+ const fromInfo = fromMap.get(tableName);
134
+ if (!fromInfo) {
135
+ // New table — CREATE TABLE
136
+ statements.push(createTableSQL(toInfo.model));
137
+ // Create indexes for the new table
138
+ for (let i = 0; i < toInfo.model.indexes.length; i++) {
139
+ statements.push(createIndexSQL(tableName, toInfo.model.indexes[i], i));
140
+ }
141
+ }
142
+ else {
143
+ // Existing table — check for new columns
144
+ const existingFields = new Set(Object.keys(fromInfo.model.fields));
145
+ for (const [fieldName, def] of Object.entries(toInfo.model.fields)) {
146
+ if (!existingFields.has(fieldName)) {
147
+ statements.push(addColumnSQL(tableName, fieldName, def));
148
+ }
149
+ }
150
+ // Check for new indexes
151
+ const existingIndexKeys = new Set(fromInfo.model.indexes.map((idx) => idx.fields.join(",") + (idx.unique ? ":u" : "")));
152
+ for (let i = 0; i < toInfo.model.indexes.length; i++) {
153
+ const idx = toInfo.model.indexes[i];
154
+ const key = idx.fields.join(",") + (idx.unique ? ":u" : "");
155
+ if (!existingIndexKeys.has(key)) {
156
+ statements.push(createIndexSQL(tableName, idx, i));
157
+ }
158
+ }
159
+ }
160
+ }
161
+ return statements;
162
+ }
163
+ /**
164
+ * Execute an array of SQL migration statements against a database.
165
+ *
166
+ * The statements are run inside a transaction so that either all succeed or
167
+ * none are applied.
168
+ *
169
+ * @param db - A Drizzle `BetterSQLite3Database` instance (or any object whose
170
+ * `$client` property is a better-sqlite3 `Database`).
171
+ * @param sql - The SQL statements to execute.
172
+ */
173
+ export function applyMigration(db, sql) {
174
+ if (sql.length === 0)
175
+ return;
176
+ const client = db.$client;
177
+ client.exec("BEGIN TRANSACTION");
178
+ try {
179
+ for (const stmt of sql) {
180
+ client.exec(stmt);
181
+ }
182
+ client.exec("COMMIT");
183
+ }
184
+ catch (err) {
185
+ client.exec("ROLLBACK");
186
+ throw err;
187
+ }
188
+ }
189
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { FieldDefinition, RelationDefinition, IndexDefinition, ModelDefinition } from "./types.js";
2
+ type FieldOptions = Omit<FieldDefinition, "type">;
3
+ export declare const field: {
4
+ /**
5
+ * Primary key field — auto-generated UUID, required, unique.
6
+ */
7
+ id(): FieldDefinition;
8
+ /**
9
+ * Short string field (maps to TEXT in SQLite).
10
+ */
11
+ string(opts?: FieldOptions): FieldDefinition;
12
+ /**
13
+ * Long text field (maps to TEXT in SQLite).
14
+ */
15
+ text(opts?: FieldOptions): FieldDefinition;
16
+ /**
17
+ * Integer field (maps to INTEGER in SQLite).
18
+ */
19
+ integer(opts?: FieldOptions): FieldDefinition;
20
+ /**
21
+ * Floating-point number field (maps to REAL in SQLite).
22
+ */
23
+ number(opts?: FieldOptions): FieldDefinition;
24
+ /**
25
+ * Boolean field (maps to INTEGER with 0/1 in SQLite).
26
+ */
27
+ boolean(opts?: FieldOptions): FieldDefinition;
28
+ /**
29
+ * Date-only field (stored as TEXT ISO-8601 in SQLite).
30
+ */
31
+ date(opts?: FieldOptions): FieldDefinition;
32
+ /**
33
+ * Datetime field (stored as TEXT ISO-8601 in SQLite).
34
+ */
35
+ datetime(opts?: FieldOptions): FieldDefinition;
36
+ /**
37
+ * JSON field (stored as TEXT with JSON serialisation in SQLite).
38
+ */
39
+ json<_T = unknown>(opts?: FieldOptions): FieldDefinition;
40
+ /**
41
+ * Enum field — stored as TEXT with a constrained set of values.
42
+ *
43
+ * @example
44
+ * field.enum(["open", "closed"], { default: "open" })
45
+ */
46
+ enum(values: readonly string[], opts?: FieldOptions): FieldDefinition;
47
+ };
48
+ interface RelationOptions {
49
+ foreignKey?: string;
50
+ through?: string;
51
+ }
52
+ export declare const relation: {
53
+ belongsTo(model: string, opts?: RelationOptions): RelationDefinition;
54
+ hasMany(model: string, opts?: RelationOptions): RelationDefinition;
55
+ hasOne(model: string, opts?: RelationOptions): RelationDefinition;
56
+ manyToMany(model: string, opts?: RelationOptions): RelationDefinition;
57
+ };
58
+ export declare function defineModel(name: string, config: {
59
+ fields: Record<string, FieldDefinition>;
60
+ relations?: Record<string, RelationDefinition>;
61
+ indexes?: IndexDefinition[];
62
+ }): ModelDefinition;
63
+ export {};
64
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAc,MAAM,YAAY,CAAC;AAMpH,KAAK,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AAkBlD,eAAO,MAAM,KAAK;IAChB;;OAEG;UACG,eAAe;IAIrB;;OAEG;kBACW,YAAY,GAAG,eAAe;IAI5C;;OAEG;gBACS,YAAY,GAAG,eAAe;IAI1C;;OAEG;mBACY,YAAY,GAAG,eAAe;IAI7C;;OAEG;kBACW,YAAY,GAAG,eAAe;IAI5C;;OAEG;mBACY,YAAY,GAAG,eAAe;IAI7C;;OAEG;gBACS,YAAY,GAAG,eAAe;IAI1C;;OAEG;oBACa,YAAY,GAAG,eAAe;IAI9C;;OAEG;SACE,EAAE,mBAAmB,YAAY,GAAG,eAAe;IAIxD;;;;;OAKG;iBACU,SAAS,MAAM,EAAE,SAAS,YAAY,GAAG,eAAe;CAGtE,CAAC;AAMF,UAAU,eAAe;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,QAAQ;qBACF,MAAM,SAAS,eAAe,GAAG,kBAAkB;mBAIrD,MAAM,SAAS,eAAe,GAAG,kBAAkB;kBAIpD,MAAM,SAAS,eAAe,GAAG,kBAAkB;sBAI/C,MAAM,SAAS,eAAe,GAAG,kBAAkB;CAGtE,CAAC;AAMF,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IACN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B,GACA,eAAe,CAOjB"}
package/dist/model.js ADDED
@@ -0,0 +1,115 @@
1
+ function makeField(type, opts) {
2
+ const def = { type };
3
+ if (opts) {
4
+ if (opts.required !== undefined)
5
+ def.required = opts.required;
6
+ if (opts.unique !== undefined)
7
+ def.unique = opts.unique;
8
+ if (opts.default !== undefined)
9
+ def.default = opts.default;
10
+ if (opts.min !== undefined)
11
+ def.min = opts.min;
12
+ if (opts.max !== undefined)
13
+ def.max = opts.max;
14
+ if (opts.enum !== undefined)
15
+ def.enum = opts.enum;
16
+ if (opts.updatedAt !== undefined)
17
+ def.updatedAt = opts.updatedAt;
18
+ if (opts.autoId !== undefined)
19
+ def.autoId = opts.autoId;
20
+ if (opts.references !== undefined)
21
+ def.references = opts.references;
22
+ }
23
+ return def;
24
+ }
25
+ export const field = {
26
+ /**
27
+ * Primary key field — auto-generated UUID, required, unique.
28
+ */
29
+ id() {
30
+ return { type: "string", autoId: true, required: true, unique: true };
31
+ },
32
+ /**
33
+ * Short string field (maps to TEXT in SQLite).
34
+ */
35
+ string(opts) {
36
+ return makeField("string", opts);
37
+ },
38
+ /**
39
+ * Long text field (maps to TEXT in SQLite).
40
+ */
41
+ text(opts) {
42
+ return makeField("text", opts);
43
+ },
44
+ /**
45
+ * Integer field (maps to INTEGER in SQLite).
46
+ */
47
+ integer(opts) {
48
+ return makeField("integer", opts);
49
+ },
50
+ /**
51
+ * Floating-point number field (maps to REAL in SQLite).
52
+ */
53
+ number(opts) {
54
+ return makeField("number", opts);
55
+ },
56
+ /**
57
+ * Boolean field (maps to INTEGER with 0/1 in SQLite).
58
+ */
59
+ boolean(opts) {
60
+ return makeField("boolean", opts);
61
+ },
62
+ /**
63
+ * Date-only field (stored as TEXT ISO-8601 in SQLite).
64
+ */
65
+ date(opts) {
66
+ return makeField("date", opts);
67
+ },
68
+ /**
69
+ * Datetime field (stored as TEXT ISO-8601 in SQLite).
70
+ */
71
+ datetime(opts) {
72
+ return makeField("datetime", opts);
73
+ },
74
+ /**
75
+ * JSON field (stored as TEXT with JSON serialisation in SQLite).
76
+ */
77
+ json(opts) {
78
+ return makeField("json", opts);
79
+ },
80
+ /**
81
+ * Enum field — stored as TEXT with a constrained set of values.
82
+ *
83
+ * @example
84
+ * field.enum(["open", "closed"], { default: "open" })
85
+ */
86
+ enum(values, opts) {
87
+ return makeField("string", { ...opts, enum: values });
88
+ },
89
+ };
90
+ export const relation = {
91
+ belongsTo(model, opts) {
92
+ return { kind: "belongsTo", model, ...opts };
93
+ },
94
+ hasMany(model, opts) {
95
+ return { kind: "hasMany", model, ...opts };
96
+ },
97
+ hasOne(model, opts) {
98
+ return { kind: "hasOne", model, ...opts };
99
+ },
100
+ manyToMany(model, opts) {
101
+ return { kind: "manyToMany", model, ...opts };
102
+ },
103
+ };
104
+ // ---------------------------------------------------------------------------
105
+ // defineModel
106
+ // ---------------------------------------------------------------------------
107
+ export function defineModel(name, config) {
108
+ return {
109
+ name,
110
+ fields: config.fields,
111
+ relations: config.relations ?? {},
112
+ indexes: config.indexes ?? [],
113
+ };
114
+ }
115
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ModelDefinition } from "./types.js";
2
+ /**
3
+ * Generate a Drizzle ORM schema file (as a TypeScript string) from an array
4
+ * of model definitions.
5
+ *
6
+ * The generated code targets `drizzle-orm/sqlite-core`.
7
+ *
8
+ * @example
9
+ * const src = generateDrizzleSchema([ticketModel, userModel]);
10
+ * fs.writeFileSync("schema.ts", src);
11
+ */
12
+ export declare function generateDrizzleSchema(models: ModelDefinition[]): string;
13
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,YAAY,CAAC;AA2H/E;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CA4CvE"}
package/dist/schema.js ADDED
@@ -0,0 +1,148 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Helpers
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Convert a camelCase name to snake_case for column names.
6
+ */
7
+ function toSnakeCase(str) {
8
+ return str.replace(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
9
+ }
10
+ /**
11
+ * Pluralise a model name for the table name (very simple heuristic).
12
+ * "Ticket" -> "tickets", "Category" -> "categories", "Status" -> "statuses"
13
+ */
14
+ function pluralise(name) {
15
+ const lower = name.charAt(0).toLowerCase() + name.slice(1);
16
+ if (lower.endsWith("y") && !/[aeiou]y$/i.test(lower)) {
17
+ return lower.slice(0, -1) + "ies";
18
+ }
19
+ if (lower.endsWith("s") || lower.endsWith("x") || lower.endsWith("sh") || lower.endsWith("ch")) {
20
+ return lower + "es";
21
+ }
22
+ return lower + "s";
23
+ }
24
+ /**
25
+ * Escape a JavaScript string value for embedding in generated code.
26
+ */
27
+ function jsString(val) {
28
+ if (typeof val === "string") {
29
+ return `"${val.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
30
+ }
31
+ return String(val);
32
+ }
33
+ function columnMapping(fieldType) {
34
+ switch (fieldType) {
35
+ case "string":
36
+ case "text":
37
+ case "date":
38
+ case "datetime":
39
+ return { builder: "text", import: "text" };
40
+ case "integer":
41
+ return { builder: "integer", import: "integer" };
42
+ case "number":
43
+ return { builder: "real", import: "real" };
44
+ case "boolean":
45
+ return { builder: "integer", import: "integer", config: '{ mode: "boolean" }' };
46
+ case "json":
47
+ return { builder: "text", import: "text", config: '{ mode: "json" }' };
48
+ }
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Column expression builder
52
+ // ---------------------------------------------------------------------------
53
+ function buildColumnExpr(fieldName, def) {
54
+ const colName = toSnakeCase(fieldName);
55
+ const mapping = columnMapping(def.type);
56
+ let expr;
57
+ if (mapping.config) {
58
+ expr = `${mapping.builder}("${colName}", ${mapping.config})`;
59
+ }
60
+ else {
61
+ expr = `${mapping.builder}("${colName}")`;
62
+ }
63
+ // Primary key for auto-id fields
64
+ if (def.autoId) {
65
+ expr += ".primaryKey()";
66
+ }
67
+ // NOT NULL for required fields (but not auto-id, which is already implied)
68
+ if (def.required && !def.autoId) {
69
+ expr += ".notNull()";
70
+ }
71
+ // Unique constraint
72
+ if (def.unique && !def.autoId) {
73
+ expr += ".unique()";
74
+ }
75
+ // Default values
76
+ if (def.default !== undefined) {
77
+ if (def.default === "now") {
78
+ // For date/datetime fields, "now" means the current ISO timestamp.
79
+ // We use a SQL expression: (datetime('now')) or store as text.
80
+ expr += `.default(sql\`(datetime('now'))\`)`;
81
+ }
82
+ else if (typeof def.default === "string") {
83
+ expr += `.default(${jsString(def.default)})`;
84
+ }
85
+ else if (typeof def.default === "boolean") {
86
+ expr += `.default(${def.default})`;
87
+ }
88
+ else if (typeof def.default === "number") {
89
+ expr += `.default(${def.default})`;
90
+ }
91
+ else {
92
+ expr += `.default(${jsString(def.default)})`;
93
+ }
94
+ }
95
+ return expr;
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Public API
99
+ // ---------------------------------------------------------------------------
100
+ /**
101
+ * Generate a Drizzle ORM schema file (as a TypeScript string) from an array
102
+ * of model definitions.
103
+ *
104
+ * The generated code targets `drizzle-orm/sqlite-core`.
105
+ *
106
+ * @example
107
+ * const src = generateDrizzleSchema([ticketModel, userModel]);
108
+ * fs.writeFileSync("schema.ts", src);
109
+ */
110
+ export function generateDrizzleSchema(models) {
111
+ // Collect all drizzle imports we need
112
+ const neededImports = new Set(["sqliteTable"]);
113
+ let needsSql = false;
114
+ // First pass: determine imports
115
+ for (const model of models) {
116
+ for (const def of Object.values(model.fields)) {
117
+ const mapping = columnMapping(def.type);
118
+ neededImports.add(mapping.import);
119
+ if (def.default === "now") {
120
+ needsSql = true;
121
+ }
122
+ }
123
+ }
124
+ // Build import line
125
+ const importNames = [...neededImports].sort();
126
+ const lines = [];
127
+ lines.push(`import { ${importNames.join(", ")} } from "drizzle-orm/sqlite-core";`);
128
+ if (needsSql) {
129
+ lines.push(`import { sql } from "drizzle-orm";`);
130
+ }
131
+ // Generate each table
132
+ for (const model of models) {
133
+ const tableName = pluralise(model.name);
134
+ const varName = tableName;
135
+ lines.push("");
136
+ lines.push(`export const ${varName} = sqliteTable("${tableName}", {`);
137
+ const fieldEntries = Object.entries(model.fields);
138
+ for (let i = 0; i < fieldEntries.length; i++) {
139
+ const [fieldName, def] = fieldEntries[i];
140
+ const expr = buildColumnExpr(fieldName, def);
141
+ const comma = i < fieldEntries.length - 1 ? "," : ",";
142
+ lines.push(` ${fieldName}: ${expr}${comma}`);
143
+ }
144
+ lines.push("});");
145
+ }
146
+ return lines.join("\n") + "\n";
147
+ }
148
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export type ScalarType = "string" | "integer" | "number" | "boolean" | "date" | "datetime" | "text" | "json";
2
+ export interface FieldDefinition {
3
+ type: ScalarType;
4
+ required?: boolean;
5
+ unique?: boolean;
6
+ default?: unknown;
7
+ min?: number;
8
+ max?: number;
9
+ enum?: readonly string[];
10
+ /** Auto-set to current time on update */
11
+ updatedAt?: boolean;
12
+ /** Auto-generate UUID */
13
+ autoId?: boolean;
14
+ /** Reference to another model */
15
+ references?: string;
16
+ }
17
+ export type RelationKind = "belongsTo" | "hasMany" | "hasOne" | "manyToMany";
18
+ export interface RelationDefinition {
19
+ kind: RelationKind;
20
+ model: string;
21
+ foreignKey?: string;
22
+ through?: string;
23
+ }
24
+ export interface IndexDefinition {
25
+ fields: string[];
26
+ unique?: boolean;
27
+ order?: "asc" | "desc";
28
+ }
29
+ export interface ModelDefinition {
30
+ name: string;
31
+ fields: Record<string, FieldDefinition>;
32
+ relations: Record<string, RelationDefinition>;
33
+ indexes: IndexDefinition[];
34
+ }
35
+ export interface DatabaseConfig {
36
+ provider: "sqlite" | "postgres";
37
+ url: string;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7G,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,yCAAyC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yBAAyB;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC9C,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;CACb"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@zauso-ai/capstan-db",
3
+ "version": "0.1.2",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "typecheck": "tsc -p tsconfig.json --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "drizzle-orm": "^0.44.0",
19
+ "better-sqlite3": "^11.0.0"
20
+ },
21
+ "description": "Database layer for Capstan — Drizzle ORM, defineModel, auto CRUD generation",
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/barry3406/capstan.git",
28
+ "directory": "packages/db"
29
+ },
30
+ "license": "MIT",
31
+ "author": "barry3406",
32
+ "homepage": "https://github.com/barry3406/capstan",
33
+ "keywords": [
34
+ "capstan",
35
+ "ai-agent",
36
+ "full-stack",
37
+ "framework",
38
+ "mcp",
39
+ "a2a",
40
+ "typescript"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
+ }