@parsrun/entity 0.1.39 → 0.2.1
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/chunk-5UDSZ7DZ.js +132 -0
- package/dist/chunk-5UDSZ7DZ.js.map +1 -0
- package/dist/chunk-I357VWZF.js +137 -0
- package/dist/chunk-I357VWZF.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +25 -3
- package/dist/index.js.map +1 -1
- package/dist/pg.d.ts +1 -1
- package/dist/pg.js +1 -135
- package/dist/pg.js.map +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/sqlite.js +1 -130
- package/dist/sqlite.js.map +1 -1
- package/dist/{types-keJMe1IE.d.ts → types-Zt7TOEtI.d.ts} +18 -0
- package/package.json +2 -2
- package/src/define.test.ts +82 -0
- package/src/define.ts +30 -1
- package/src/types.ts +20 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { text, sqliteTable, uniqueIndex, index, integer, real } from 'drizzle-orm/sqlite-core';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
|
|
4
|
+
// src/sqlite.ts
|
|
5
|
+
function fieldToColumn(name, field) {
|
|
6
|
+
const def = typeof field === "string" ? { type: field } : field;
|
|
7
|
+
const columnName = toSnakeCase(name);
|
|
8
|
+
let column;
|
|
9
|
+
const fieldType = def.type.split(" ")[0] ?? "string";
|
|
10
|
+
switch (fieldType) {
|
|
11
|
+
case "string.uuid":
|
|
12
|
+
case "string.email":
|
|
13
|
+
case "string.url":
|
|
14
|
+
case "string":
|
|
15
|
+
column = text(columnName);
|
|
16
|
+
break;
|
|
17
|
+
case "number.integer":
|
|
18
|
+
column = integer(columnName);
|
|
19
|
+
break;
|
|
20
|
+
case "number":
|
|
21
|
+
column = real(columnName);
|
|
22
|
+
break;
|
|
23
|
+
case "boolean":
|
|
24
|
+
column = integer(columnName, { mode: "boolean" });
|
|
25
|
+
break;
|
|
26
|
+
case "Date":
|
|
27
|
+
column = text(columnName);
|
|
28
|
+
break;
|
|
29
|
+
case "json":
|
|
30
|
+
column = text(columnName, { mode: "json" });
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
column = text(columnName);
|
|
34
|
+
}
|
|
35
|
+
if (!def.optional && def.default === void 0) {
|
|
36
|
+
column = column.notNull();
|
|
37
|
+
}
|
|
38
|
+
if (def.default !== void 0) {
|
|
39
|
+
if (typeof def.default === "object") {
|
|
40
|
+
column = column.default(JSON.stringify(def.default));
|
|
41
|
+
} else {
|
|
42
|
+
column = column.default(def.default);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return column;
|
|
46
|
+
}
|
|
47
|
+
function toSnakeCase(str) {
|
|
48
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
49
|
+
}
|
|
50
|
+
function toSqliteTable(entity, tableRefs) {
|
|
51
|
+
const def = entity.definition;
|
|
52
|
+
const columns = {};
|
|
53
|
+
columns["id"] = text("id").primaryKey().$defaultFn(() => crypto.randomUUID());
|
|
54
|
+
if (def.tenant) {
|
|
55
|
+
const tenantsTable = tableRefs?.["tenants"];
|
|
56
|
+
if (tenantsTable) {
|
|
57
|
+
columns["tenantId"] = text("tenant_id").notNull().references(() => tenantsTable.id, { onDelete: "cascade" });
|
|
58
|
+
} else {
|
|
59
|
+
columns["tenantId"] = text("tenant_id").notNull();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const [name, field] of Object.entries(def.fields)) {
|
|
63
|
+
const fieldDef = typeof field === "string" ? { } : field;
|
|
64
|
+
if (fieldDef.db?.references && tableRefs) {
|
|
65
|
+
const refTable = tableRefs[fieldDef.db.references.entity];
|
|
66
|
+
if (refTable) {
|
|
67
|
+
const refField = fieldDef.db.references.field ?? "id";
|
|
68
|
+
const onDelete = fieldDef.db.references.onDelete ?? "restrict";
|
|
69
|
+
let col = text(toSnakeCase(name)).references(() => refTable[refField], { onDelete });
|
|
70
|
+
if (!fieldDef.optional) {
|
|
71
|
+
col = col.notNull();
|
|
72
|
+
}
|
|
73
|
+
columns[name] = col;
|
|
74
|
+
} else {
|
|
75
|
+
columns[name] = fieldToColumn(name, field);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
columns[name] = fieldToColumn(name, field);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (def.timestamps) {
|
|
82
|
+
columns["insertedAt"] = text("inserted_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString());
|
|
83
|
+
columns["updatedAt"] = text("updated_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString()).$onUpdate(() => (/* @__PURE__ */ new Date()).toISOString());
|
|
84
|
+
}
|
|
85
|
+
if (def.softDelete) {
|
|
86
|
+
columns["deletedAt"] = text("deleted_at");
|
|
87
|
+
}
|
|
88
|
+
return sqliteTable(def.name, columns, (t) => {
|
|
89
|
+
const indexes = {};
|
|
90
|
+
if (def.indexes) {
|
|
91
|
+
for (const idx of def.indexes) {
|
|
92
|
+
const idxName = idx.name ?? `${def.name}_${idx.fields.join("_")}_idx`;
|
|
93
|
+
const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean);
|
|
94
|
+
if (idxColumns.length === 0) continue;
|
|
95
|
+
if (idx.unique) {
|
|
96
|
+
if (idx.where) {
|
|
97
|
+
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
98
|
+
} else {
|
|
99
|
+
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
if (idx.where) {
|
|
103
|
+
indexes[idxName] = index(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
104
|
+
} else {
|
|
105
|
+
indexes[idxName] = index(idxName).on(...idxColumns);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (def.tenant && t.tenantId) {
|
|
111
|
+
const tenantIdxName = `${def.name}_tenant_id_idx`;
|
|
112
|
+
if (!indexes[tenantIdxName]) {
|
|
113
|
+
indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return indexes;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function createSqliteSchema(entities) {
|
|
120
|
+
const tables = {};
|
|
121
|
+
for (const [key, entity] of Object.entries(entities)) {
|
|
122
|
+
tables[key] = toSqliteTable(entity);
|
|
123
|
+
}
|
|
124
|
+
for (const [key, entity] of Object.entries(entities)) {
|
|
125
|
+
tables[key] = toSqliteTable(entity, tables);
|
|
126
|
+
}
|
|
127
|
+
return tables;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { createSqliteSchema, toSqliteTable };
|
|
131
|
+
//# sourceMappingURL=chunk-5UDSZ7DZ.js.map
|
|
132
|
+
//# sourceMappingURL=chunk-5UDSZ7DZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sqlite.ts"],"names":[],"mappings":"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","file":"chunk-5UDSZ7DZ.js","sourcesContent":["/**\n * SQLite Drizzle table generation from entity definitions\n * Compatible with Cloudflare D1\n */\nimport {\n sqliteTable,\n text,\n integer,\n real,\n index,\n uniqueIndex,\n} from 'drizzle-orm/sqlite-core'\nimport { sql } from 'drizzle-orm'\nimport type { Entity, Field, FieldDefinition } from './types.js'\n\n/**\n * Convert field definition to Drizzle SQLite column builder\n */\nfunction fieldToColumn(name: string, field: Field) {\n const def: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n const columnName = toSnakeCase(name)\n let column: any\n\n // Determine column type based on field type\n const fieldType = def.type.split(' ')[0] ?? 'string'\n\n switch (fieldType) {\n case 'string.uuid':\n case 'string.email':\n case 'string.url':\n case 'string':\n column = text(columnName)\n break\n\n case 'number.integer':\n column = integer(columnName)\n break\n\n case 'number':\n column = real(columnName)\n break\n\n case 'boolean':\n column = integer(columnName, { mode: 'boolean' })\n break\n\n case 'Date':\n column = text(columnName)\n break\n\n case 'json':\n column = text(columnName, { mode: 'json' })\n break\n\n default:\n column = text(columnName)\n }\n\n // Apply modifiers\n if (!def.optional && def.default === undefined) {\n column = column.notNull()\n }\n\n if (def.default !== undefined) {\n if (typeof def.default === 'object') {\n column = column.default(JSON.stringify(def.default))\n } else {\n column = column.default(def.default)\n }\n }\n\n return column\n}\n\n/**\n * Convert camelCase to snake_case\n */\nfunction toSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)\n}\n\n/**\n * Generate a Drizzle sqliteTable from an entity definition\n */\nexport function toSqliteTable<E extends Entity<string, Record<string, Field>, unknown>>(\n entity: E,\n tableRefs?: Record<string, any>\n): any {\n const def = entity.definition\n const columns: Record<string, any> = {}\n\n // Add id column\n columns['id'] = text('id')\n .primaryKey()\n .$defaultFn(() => crypto.randomUUID())\n\n // Add tenantId if tenant-scoped\n if (def.tenant) {\n const tenantsTable = tableRefs?.['tenants']\n if (tenantsTable) {\n columns['tenantId'] = text('tenant_id')\n .notNull()\n .references(() => tenantsTable.id, { onDelete: 'cascade' })\n } else {\n columns['tenantId'] = text('tenant_id').notNull()\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(def.fields)) {\n const fieldDef: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n // Handle references\n if (fieldDef.db?.references && tableRefs) {\n const refTable = tableRefs[fieldDef.db.references.entity]\n if (refTable) {\n const refField = fieldDef.db.references.field ?? 'id'\n const onDelete = fieldDef.db.references.onDelete ?? 'restrict'\n\n let col = text(toSnakeCase(name)).references(() => refTable[refField], { onDelete })\n if (!fieldDef.optional) {\n col = col.notNull()\n }\n columns[name] = col\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n }\n\n // Add timestamp fields\n if (def.timestamps) {\n columns['insertedAt'] = text('inserted_at')\n .notNull()\n .$defaultFn(() => new Date().toISOString())\n\n columns['updatedAt'] = text('updated_at')\n .notNull()\n .$defaultFn(() => new Date().toISOString())\n .$onUpdate(() => new Date().toISOString())\n }\n\n // Add soft delete field\n if (def.softDelete) {\n columns['deletedAt'] = text('deleted_at')\n }\n\n // Create table with indexes\n return sqliteTable(def.name, columns, (t: any) => {\n const indexes: Record<string, any> = {}\n\n // Add indexes from definition\n if (def.indexes) {\n for (const idx of def.indexes) {\n const idxName = idx.name ?? `${def.name}_${idx.fields.join('_')}_idx`\n const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean)\n\n if (idxColumns.length === 0) continue\n\n if (idx.unique) {\n if (idx.where) {\n indexes[idxName] = uniqueIndex(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = uniqueIndex(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n } else {\n if (idx.where) {\n indexes[idxName] = index(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = index(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n }\n }\n }\n\n // Add default tenant index if tenant-scoped\n if (def.tenant && t.tenantId) {\n const tenantIdxName = `${def.name}_tenant_id_idx`\n if (!indexes[tenantIdxName]) {\n indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId)\n }\n }\n\n return indexes\n })\n}\n\n/**\n * Create multiple SQLite tables with resolved references\n */\nexport function createSqliteSchema<\n T extends Record<string, Entity<string, Record<string, Field>, unknown>>,\n>(entities: T): { [K in keyof T]: any } {\n const tables: Record<string, any> = {}\n\n // First pass: create tables without references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toSqliteTable(entity)\n }\n\n // Second pass: recreate tables with resolved references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toSqliteTable(entity, tables)\n }\n\n return tables as { [K in keyof T]: any }\n}\n\nexport { toSqliteTable as toTable }\n"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { uuid, timestamp, pgTable, uniqueIndex, index, text, json, boolean, numeric, integer } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
|
|
4
|
+
// src/pg.ts
|
|
5
|
+
function fieldToColumn(name, field) {
|
|
6
|
+
const def = typeof field === "string" ? { type: field } : field;
|
|
7
|
+
const columnName = def.db?.column ?? toSnakeCase(name);
|
|
8
|
+
let column;
|
|
9
|
+
const fieldType = def.type.split(" ")[0] ?? "string";
|
|
10
|
+
switch (fieldType) {
|
|
11
|
+
case "string.uuid":
|
|
12
|
+
column = uuid(columnName);
|
|
13
|
+
break;
|
|
14
|
+
case "string.email":
|
|
15
|
+
case "string.url":
|
|
16
|
+
case "string":
|
|
17
|
+
column = text(columnName);
|
|
18
|
+
break;
|
|
19
|
+
case "number.integer":
|
|
20
|
+
column = integer(columnName);
|
|
21
|
+
break;
|
|
22
|
+
case "number":
|
|
23
|
+
if (def.db?.precision) {
|
|
24
|
+
column = numeric(columnName, {
|
|
25
|
+
precision: def.db.precision,
|
|
26
|
+
scale: def.db.scale ?? 2
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
column = numeric(columnName);
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
case "boolean":
|
|
33
|
+
column = boolean(columnName);
|
|
34
|
+
break;
|
|
35
|
+
case "Date":
|
|
36
|
+
column = timestamp(columnName, { withTimezone: true });
|
|
37
|
+
break;
|
|
38
|
+
case "json":
|
|
39
|
+
column = json(columnName);
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
column = text(columnName);
|
|
43
|
+
}
|
|
44
|
+
if (!def.optional && def.default === void 0) {
|
|
45
|
+
column = column.notNull();
|
|
46
|
+
}
|
|
47
|
+
if (def.default !== void 0) {
|
|
48
|
+
column = column.default(def.default);
|
|
49
|
+
}
|
|
50
|
+
return column;
|
|
51
|
+
}
|
|
52
|
+
function toSnakeCase(str) {
|
|
53
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
54
|
+
}
|
|
55
|
+
function toPgTable(entity, tableRefs) {
|
|
56
|
+
const def = entity.definition;
|
|
57
|
+
const columns = {};
|
|
58
|
+
columns["id"] = uuid("id").primaryKey().defaultRandom();
|
|
59
|
+
if (def.tenant) {
|
|
60
|
+
const tenantsTable = tableRefs?.["tenants"];
|
|
61
|
+
if (tenantsTable) {
|
|
62
|
+
columns["tenantId"] = uuid("tenant_id").notNull().references(() => tenantsTable.id, { onDelete: "cascade" });
|
|
63
|
+
} else {
|
|
64
|
+
columns["tenantId"] = uuid("tenant_id").notNull();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const [name, field] of Object.entries(def.fields)) {
|
|
68
|
+
const fieldDef = typeof field === "string" ? { } : field;
|
|
69
|
+
if (fieldDef.db?.references && tableRefs) {
|
|
70
|
+
const refTable = tableRefs[fieldDef.db.references.entity];
|
|
71
|
+
if (refTable) {
|
|
72
|
+
const refField = fieldDef.db.references.field ?? "id";
|
|
73
|
+
const onDelete = fieldDef.db.references.onDelete ?? "restrict";
|
|
74
|
+
let col = uuid(toSnakeCase(name)).references(() => refTable[refField], { onDelete });
|
|
75
|
+
if (!fieldDef.optional) {
|
|
76
|
+
col = col.notNull();
|
|
77
|
+
}
|
|
78
|
+
columns[name] = col;
|
|
79
|
+
} else {
|
|
80
|
+
columns[name] = fieldToColumn(name, field);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
columns[name] = fieldToColumn(name, field);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (def.timestamps) {
|
|
87
|
+
columns["insertedAt"] = timestamp("inserted_at", { withTimezone: true }).notNull().defaultNow();
|
|
88
|
+
columns["updatedAt"] = timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => /* @__PURE__ */ new Date());
|
|
89
|
+
}
|
|
90
|
+
if (def.softDelete) {
|
|
91
|
+
columns["deletedAt"] = timestamp("deleted_at", { withTimezone: true });
|
|
92
|
+
}
|
|
93
|
+
return pgTable(def.name, columns, (t) => {
|
|
94
|
+
const indexes = {};
|
|
95
|
+
if (def.indexes) {
|
|
96
|
+
for (const idx of def.indexes) {
|
|
97
|
+
const idxName = idx.name ?? `${def.name}_${idx.fields.join("_")}_idx`;
|
|
98
|
+
const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean);
|
|
99
|
+
if (idxColumns.length === 0) continue;
|
|
100
|
+
if (idx.unique) {
|
|
101
|
+
if (idx.where) {
|
|
102
|
+
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
103
|
+
} else {
|
|
104
|
+
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
if (idx.where) {
|
|
108
|
+
indexes[idxName] = index(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
109
|
+
} else {
|
|
110
|
+
indexes[idxName] = index(idxName).on(...idxColumns);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (def.tenant && t.tenantId) {
|
|
116
|
+
const tenantIdxName = `${def.name}_tenant_id_idx`;
|
|
117
|
+
if (!indexes[tenantIdxName]) {
|
|
118
|
+
indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return indexes;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function createPgSchema(entities) {
|
|
125
|
+
const tables = {};
|
|
126
|
+
for (const [key, entity] of Object.entries(entities)) {
|
|
127
|
+
tables[key] = toPgTable(entity);
|
|
128
|
+
}
|
|
129
|
+
for (const [key, entity] of Object.entries(entities)) {
|
|
130
|
+
tables[key] = toPgTable(entity, tables);
|
|
131
|
+
}
|
|
132
|
+
return tables;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { createPgSchema, toPgTable };
|
|
136
|
+
//# sourceMappingURL=chunk-I357VWZF.js.map
|
|
137
|
+
//# sourceMappingURL=chunk-I357VWZF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pg.ts"],"names":[],"mappings":"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","file":"chunk-I357VWZF.js","sourcesContent":["/**\n * PostgreSQL Drizzle table generation from entity definitions\n */\nimport {\n pgTable,\n uuid,\n text,\n integer,\n boolean,\n timestamp,\n numeric,\n json,\n index,\n uniqueIndex,\n} from 'drizzle-orm/pg-core'\nimport { sql } from 'drizzle-orm'\nimport type { Entity, Field, FieldDefinition } from './types.js'\n\n/**\n * Convert field definition to Drizzle column builder\n */\nfunction fieldToColumn(name: string, field: Field) {\n const def: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n const columnName = def.db?.column ?? toSnakeCase(name)\n let column: any\n\n // Determine column type based on field type\n const fieldType = def.type.split(' ')[0] ?? 'string'\n\n switch (fieldType) {\n case 'string.uuid':\n column = uuid(columnName)\n break\n\n case 'string.email':\n case 'string.url':\n case 'string':\n column = text(columnName)\n break\n\n case 'number.integer':\n column = integer(columnName)\n break\n\n case 'number':\n if (def.db?.precision) {\n column = numeric(columnName, {\n precision: def.db.precision,\n scale: def.db.scale ?? 2,\n })\n } else {\n column = numeric(columnName)\n }\n break\n\n case 'boolean':\n column = boolean(columnName)\n break\n\n case 'Date':\n column = timestamp(columnName, { withTimezone: true })\n break\n\n case 'json':\n column = json(columnName)\n break\n\n default:\n // Union types like \"'draft' | 'active'\" become text\n column = text(columnName)\n }\n\n // Apply modifiers\n if (!def.optional && def.default === undefined) {\n column = column.notNull()\n }\n\n if (def.default !== undefined) {\n column = column.default(def.default)\n }\n\n return column\n}\n\n/**\n * Convert camelCase to snake_case\n */\nfunction toSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)\n}\n\n/**\n * Generate a Drizzle pgTable from an entity definition\n */\nexport function toPgTable<E extends Entity<string, Record<string, Field>, unknown>>(\n entity: E,\n tableRefs?: Record<string, any>\n): any {\n const def = entity.definition\n const columns: Record<string, any> = {}\n\n // Add id column\n columns['id'] = uuid('id').primaryKey().defaultRandom()\n\n // Add tenantId if tenant-scoped\n if (def.tenant) {\n const tenantsTable = tableRefs?.['tenants']\n if (tenantsTable) {\n columns['tenantId'] = uuid('tenant_id')\n .notNull()\n .references(() => tenantsTable.id, { onDelete: 'cascade' })\n } else {\n columns['tenantId'] = uuid('tenant_id').notNull()\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(def.fields)) {\n const fieldDef: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n // Handle references\n if (fieldDef.db?.references && tableRefs) {\n const refTable = tableRefs[fieldDef.db.references.entity]\n if (refTable) {\n const refField = fieldDef.db.references.field ?? 'id'\n const onDelete = fieldDef.db.references.onDelete ?? 'restrict'\n\n let col = uuid(toSnakeCase(name)).references(() => refTable[refField], { onDelete })\n if (!fieldDef.optional) {\n col = col.notNull()\n }\n columns[name] = col\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n }\n\n // Add timestamp fields\n if (def.timestamps) {\n columns['insertedAt'] = timestamp('inserted_at', { withTimezone: true })\n .notNull()\n .defaultNow()\n\n columns['updatedAt'] = timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow()\n .$onUpdate(() => new Date())\n }\n\n // Add soft delete field\n if (def.softDelete) {\n columns['deletedAt'] = timestamp('deleted_at', { withTimezone: true })\n }\n\n // Create table with indexes\n return pgTable(def.name, columns, (t: any) => {\n const indexes: Record<string, any> = {}\n\n // Add indexes from definition\n if (def.indexes) {\n for (const idx of def.indexes) {\n const idxName = idx.name ?? `${def.name}_${idx.fields.join('_')}_idx`\n const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean)\n\n if (idxColumns.length === 0) continue\n\n if (idx.unique) {\n if (idx.where) {\n indexes[idxName] = uniqueIndex(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = uniqueIndex(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n } else {\n if (idx.where) {\n indexes[idxName] = index(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = index(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n }\n }\n }\n\n // Add default tenant index if tenant-scoped\n if (def.tenant && t.tenantId) {\n const tenantIdxName = `${def.name}_tenant_id_idx`\n if (!indexes[tenantIdxName]) {\n indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId)\n }\n }\n\n return indexes\n })\n}\n\n/**\n * Create multiple tables with resolved references\n */\nexport function createPgSchema<\n T extends Record<string, Entity<string, Record<string, Field>, unknown>>,\n>(entities: T): { [K in keyof T]: any } {\n const tables: Record<string, any> = {}\n\n // First pass: create tables without references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toPgTable(entity)\n }\n\n // Second pass: recreate tables with resolved references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toPgTable(entity, tables)\n }\n\n return tables as { [K in keyof T]: any }\n}\n\nexport { toPgTable as toTable }\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as Field, E as EntityDefinition, a as Entity, b as FieldDefinition } from './types-
|
|
2
|
-
export { D as DrizzleOptions, d as EntitySchemas, c as FieldType, I as IndexDefinition, f as InferCreateInput, e as InferEntity, g as InferUpdateInput, S as SimpleFieldDefinition } from './types-
|
|
1
|
+
import { F as Field, E as EntityDefinition, a as Entity, b as FieldDefinition } from './types-Zt7TOEtI.js';
|
|
2
|
+
export { D as DrizzleOptions, d as EntitySchemas, c as FieldType, I as IndexDefinition, f as InferCreateInput, e as InferEntity, g as InferUpdateInput, S as SimpleFieldDefinition } from './types-Zt7TOEtI.js';
|
|
3
3
|
export { type } from 'arktype';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { toPgTable } from './chunk-I357VWZF.js';
|
|
2
|
+
import { toSqliteTable } from './chunk-5UDSZ7DZ.js';
|
|
1
3
|
import { type } from 'arktype';
|
|
2
4
|
export { type } from 'arktype';
|
|
3
5
|
|
|
4
|
-
// src/define.ts
|
|
5
6
|
function fieldToArkType(field) {
|
|
6
7
|
const def = typeof field === "string" ? { type: field } : field;
|
|
7
8
|
let typeStr = def.type;
|
|
@@ -124,7 +125,11 @@ function defineEntity(definition) {
|
|
|
124
125
|
requiredFields.push(name);
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
|
-
|
|
128
|
+
let cachedPgTable = null;
|
|
129
|
+
let cachedSqliteTable = null;
|
|
130
|
+
let lastPgRefs;
|
|
131
|
+
let lastSqliteRefs;
|
|
132
|
+
const entity = {
|
|
128
133
|
name: definition.name,
|
|
129
134
|
definition,
|
|
130
135
|
schema,
|
|
@@ -134,8 +139,25 @@ function defineEntity(definition) {
|
|
|
134
139
|
infer: {},
|
|
135
140
|
autoFields,
|
|
136
141
|
requiredFields,
|
|
137
|
-
optionalFields
|
|
142
|
+
optionalFields,
|
|
143
|
+
pgTable: (tableRefs) => {
|
|
144
|
+
if (cachedPgTable && tableRefs === lastPgRefs) {
|
|
145
|
+
return cachedPgTable;
|
|
146
|
+
}
|
|
147
|
+
cachedPgTable = toPgTable(entity, tableRefs);
|
|
148
|
+
lastPgRefs = tableRefs;
|
|
149
|
+
return cachedPgTable;
|
|
150
|
+
},
|
|
151
|
+
sqliteTable: (tableRefs) => {
|
|
152
|
+
if (cachedSqliteTable && tableRefs === lastSqliteRefs) {
|
|
153
|
+
return cachedSqliteTable;
|
|
154
|
+
}
|
|
155
|
+
cachedSqliteTable = toSqliteTable(entity, tableRefs);
|
|
156
|
+
lastSqliteRefs = tableRefs;
|
|
157
|
+
return cachedSqliteTable;
|
|
158
|
+
}
|
|
138
159
|
};
|
|
160
|
+
return entity;
|
|
139
161
|
}
|
|
140
162
|
function ref(entity, options) {
|
|
141
163
|
const entityName = typeof entity === "string" ? entity : entity.name;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/define.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { type, type Type } from 'arktype'\nimport type {\n EntityDefinition,\n Field,\n FieldDefinition,\n Entity,\n} from './types.js'\n\n/**\n * Convert a field definition to an ArkType type string\n */\nfunction fieldToArkType(field: Field): string {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n let typeStr = def.type\n\n // Handle json type - ArkType doesn't have 'json', use 'object' instead\n if (typeStr === 'json') {\n typeStr = 'object'\n }\n\n // Handle min/max constraints\n if (def.min !== undefined || def.max !== undefined) {\n if (typeStr === 'string' || typeStr.startsWith('string')) {\n if (def.min !== undefined && def.max !== undefined) {\n typeStr = `string >= ${def.min} <= ${def.max}`\n } else if (def.min !== undefined) {\n typeStr = `string >= ${def.min}`\n } else if (def.max !== undefined) {\n typeStr = `string <= ${def.max}`\n }\n } else if (typeStr === 'number' || typeStr.startsWith('number')) {\n const base = typeStr.includes('.integer') ? 'number.integer' : 'number'\n if (def.min !== undefined && def.max !== undefined) {\n typeStr = `${base} >= ${def.min} <= ${def.max}`\n } else if (def.min !== undefined) {\n typeStr = `${base} >= ${def.min}`\n } else if (def.max !== undefined) {\n typeStr = `${base} <= ${def.max}`\n }\n }\n }\n\n // Handle optional fields\n if (def.optional) {\n // For optional fields, allow undefined or the type\n typeStr = `${typeStr} | undefined`\n }\n\n return typeStr\n}\n\n/**\n * Build the full schema object for ArkType\n */\nfunction buildSchemaObject(\n definition: EntityDefinition<Record<string, Field>>,\n mode: 'full' | 'create' | 'update' | 'query'\n): Record<string, string> {\n const schema: Record<string, string> = {}\n const idType = definition.idType ?? 'string.uuid'\n\n // Add id field for full schema\n if (mode === 'full') {\n schema['id'] = idType\n }\n\n // Add tenantId if tenant-scoped\n if (definition.tenant) {\n if (mode === 'full' || mode === 'create') {\n schema['tenantId'] = idType\n }\n if (mode === 'query') {\n schema['tenantId?'] = idType\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(definition.fields)) {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n if (mode === 'update' || mode === 'query') {\n // All fields optional for update/query\n schema[`${name}?`] = fieldToArkType(field)\n } else if (mode === 'create') {\n // Skip fields with defaults or optional fields\n if (def.default !== undefined || def.optional) {\n schema[`${name}?`] = fieldToArkType(field)\n } else {\n schema[name] = fieldToArkType(field)\n }\n } else {\n // Full schema\n if (def.optional) {\n schema[`${name}?`] = fieldToArkType(field)\n } else {\n schema[name] = fieldToArkType(field)\n }\n }\n }\n\n // Add timestamp fields\n if (definition.timestamps) {\n if (mode === 'full') {\n schema['insertedAt'] = 'Date'\n schema['updatedAt'] = 'Date'\n }\n if (mode === 'query') {\n schema['insertedAt?'] = 'Date'\n schema['updatedAt?'] = 'Date'\n schema['insertedAfter?'] = 'Date'\n schema['insertedBefore?'] = 'Date'\n }\n }\n\n // Add soft delete field\n if (definition.softDelete) {\n if (mode === 'full') {\n schema['deletedAt?'] = 'Date'\n }\n if (mode === 'query') {\n schema['includeDeleted?'] = 'boolean'\n }\n }\n\n // Add pagination for query\n if (mode === 'query') {\n schema['limit?'] = 'number.integer > 0'\n schema['offset?'] = 'number.integer >= 0'\n schema['cursor?'] = 'string'\n schema['orderBy?'] = 'string'\n schema['orderDirection?'] = \"'asc' | 'desc'\"\n schema['search?'] = 'string'\n }\n\n return schema\n}\n\n/**\n * Define an entity with single-source schema generation\n *\n * @example\n * ```typescript\n * const Product = defineEntity({\n * name: 'products',\n * tenant: true,\n * timestamps: true,\n * softDelete: true,\n * fields: {\n * name: 'string >= 1',\n * price: { type: 'number', min: 0 },\n * status: \"'draft' | 'active' | 'archived'\",\n * },\n * indexes: [\n * { fields: ['tenantId', 'status'] },\n * ],\n * })\n *\n * // Use schemas\n * const validated = Product.createSchema(input)\n * const products = await db.select().from(Product.table)\n * ```\n */\nexport function defineEntity<\n TName extends string,\n TFields extends Record<string, Field>,\n>(\n definition: EntityDefinition<TFields> & { name: TName }\n): Entity<TName, TFields, Record<string, unknown>> {\n // Build schema objects\n const fullSchemaObj = buildSchemaObject(definition, 'full')\n const createSchemaObj = buildSchemaObject(definition, 'create')\n const updateSchemaObj = buildSchemaObject(definition, 'update')\n const querySchemaObj = buildSchemaObject(definition, 'query')\n\n // Create ArkType schemas\n const schema = type(fullSchemaObj as Record<string, string>)\n const createSchema = type(createSchemaObj as Record<string, string>)\n const updateSchema = type(updateSchemaObj as Record<string, string>)\n const querySchema = type(querySchemaObj as Record<string, string>)\n\n // Determine auto and required fields\n const autoFields: string[] = ['id']\n if (definition.timestamps) {\n autoFields.push('insertedAt', 'updatedAt')\n }\n if (definition.softDelete) {\n autoFields.push('deletedAt')\n }\n\n const requiredFields: string[] = []\n const optionalFields: string[] = []\n\n if (definition.tenant) {\n requiredFields.push('tenantId')\n }\n\n for (const [name, field] of Object.entries(definition.fields)) {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n if (def.optional || def.default !== undefined) {\n optionalFields.push(name)\n } else {\n requiredFields.push(name)\n }\n }\n\n return {\n name: definition.name as TName,\n definition,\n schema: schema as Type<Record<string, unknown>>,\n createSchema: createSchema as Type<Record<string, unknown>>,\n updateSchema: updateSchema as Type<Record<string, unknown>>,\n querySchema: querySchema as Type<Record<string, unknown>>,\n infer: {} as Record<string, unknown>,\n autoFields,\n requiredFields,\n optionalFields,\n }\n}\n\n/**\n * Create a reference field to another entity\n */\nexport function ref(\n entity: string | { name: string },\n options?: {\n field?: string\n onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action'\n optional?: boolean\n /** ID type for this reference (default: 'string.uuid') */\n idType?: string\n }\n): FieldDefinition {\n const entityName = typeof entity === 'string' ? entity : entity.name\n const result: FieldDefinition = {\n type: (options?.idType ?? 'string.uuid') as FieldDefinition['type'],\n db: {\n references: {\n entity: entityName,\n field: options?.field ?? 'id',\n onDelete: options?.onDelete ?? 'restrict',\n },\n },\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n\n/**\n * Create an enum field from a list of values\n */\nexport function enumField<T extends string>(\n values: readonly T[],\n options?: { default?: T; optional?: boolean }\n): FieldDefinition {\n const typeStr = values.map(v => `'${v}'`).join(' | ')\n const result: FieldDefinition = {\n type: typeStr as `'${string}'`,\n }\n if (options?.default !== undefined) {\n result.default = options.default\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n\n/**\n * Create a JSON field\n */\nexport function jsonField<T = Record<string, unknown>>(\n options?: { optional?: boolean; default?: T }\n): FieldDefinition {\n const result: FieldDefinition = {\n type: 'json',\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n if (options?.default !== undefined) {\n result.default = options.default\n }\n return result\n}\n\n/**\n * Create a decimal field with precision\n */\nexport function decimal(\n precision: number,\n scale: number,\n options?: { min?: number; max?: number; optional?: boolean }\n): FieldDefinition {\n const result: FieldDefinition = {\n type: 'number',\n db: {\n precision,\n scale,\n },\n }\n if (options?.min !== undefined) {\n result.min = options.min\n }\n if (options?.max !== undefined) {\n result.max = options.max\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/define.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { type, type Type } from 'arktype'\nimport type {\n EntityDefinition,\n Field,\n FieldDefinition,\n Entity,\n DrizzleTable,\n} from './types.js'\nimport { toPgTable } from './pg.js'\nimport { toSqliteTable } from './sqlite.js'\n\n/**\n * Convert a field definition to an ArkType type string\n */\nfunction fieldToArkType(field: Field): string {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n let typeStr = def.type\n\n // Handle json type - ArkType doesn't have 'json', use 'object' instead\n if (typeStr === 'json') {\n typeStr = 'object'\n }\n\n // Handle min/max constraints\n if (def.min !== undefined || def.max !== undefined) {\n if (typeStr === 'string' || typeStr.startsWith('string')) {\n if (def.min !== undefined && def.max !== undefined) {\n typeStr = `string >= ${def.min} <= ${def.max}`\n } else if (def.min !== undefined) {\n typeStr = `string >= ${def.min}`\n } else if (def.max !== undefined) {\n typeStr = `string <= ${def.max}`\n }\n } else if (typeStr === 'number' || typeStr.startsWith('number')) {\n const base = typeStr.includes('.integer') ? 'number.integer' : 'number'\n if (def.min !== undefined && def.max !== undefined) {\n typeStr = `${base} >= ${def.min} <= ${def.max}`\n } else if (def.min !== undefined) {\n typeStr = `${base} >= ${def.min}`\n } else if (def.max !== undefined) {\n typeStr = `${base} <= ${def.max}`\n }\n }\n }\n\n // Handle optional fields\n if (def.optional) {\n // For optional fields, allow undefined or the type\n typeStr = `${typeStr} | undefined`\n }\n\n return typeStr\n}\n\n/**\n * Build the full schema object for ArkType\n */\nfunction buildSchemaObject(\n definition: EntityDefinition<Record<string, Field>>,\n mode: 'full' | 'create' | 'update' | 'query'\n): Record<string, string> {\n const schema: Record<string, string> = {}\n const idType = definition.idType ?? 'string.uuid'\n\n // Add id field for full schema\n if (mode === 'full') {\n schema['id'] = idType\n }\n\n // Add tenantId if tenant-scoped\n if (definition.tenant) {\n if (mode === 'full' || mode === 'create') {\n schema['tenantId'] = idType\n }\n if (mode === 'query') {\n schema['tenantId?'] = idType\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(definition.fields)) {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n if (mode === 'update' || mode === 'query') {\n // All fields optional for update/query\n schema[`${name}?`] = fieldToArkType(field)\n } else if (mode === 'create') {\n // Skip fields with defaults or optional fields\n if (def.default !== undefined || def.optional) {\n schema[`${name}?`] = fieldToArkType(field)\n } else {\n schema[name] = fieldToArkType(field)\n }\n } else {\n // Full schema\n if (def.optional) {\n schema[`${name}?`] = fieldToArkType(field)\n } else {\n schema[name] = fieldToArkType(field)\n }\n }\n }\n\n // Add timestamp fields\n if (definition.timestamps) {\n if (mode === 'full') {\n schema['insertedAt'] = 'Date'\n schema['updatedAt'] = 'Date'\n }\n if (mode === 'query') {\n schema['insertedAt?'] = 'Date'\n schema['updatedAt?'] = 'Date'\n schema['insertedAfter?'] = 'Date'\n schema['insertedBefore?'] = 'Date'\n }\n }\n\n // Add soft delete field\n if (definition.softDelete) {\n if (mode === 'full') {\n schema['deletedAt?'] = 'Date'\n }\n if (mode === 'query') {\n schema['includeDeleted?'] = 'boolean'\n }\n }\n\n // Add pagination for query\n if (mode === 'query') {\n schema['limit?'] = 'number.integer > 0'\n schema['offset?'] = 'number.integer >= 0'\n schema['cursor?'] = 'string'\n schema['orderBy?'] = 'string'\n schema['orderDirection?'] = \"'asc' | 'desc'\"\n schema['search?'] = 'string'\n }\n\n return schema\n}\n\n/**\n * Define an entity with single-source schema generation\n *\n * @example\n * ```typescript\n * const Product = defineEntity({\n * name: 'products',\n * tenant: true,\n * timestamps: true,\n * softDelete: true,\n * fields: {\n * name: 'string >= 1',\n * price: { type: 'number', min: 0 },\n * status: \"'draft' | 'active' | 'archived'\",\n * },\n * indexes: [\n * { fields: ['tenantId', 'status'] },\n * ],\n * })\n *\n * // Use schemas\n * const validated = Product.createSchema(input)\n * const products = await db.select().from(Product.table)\n * ```\n */\nexport function defineEntity<\n TName extends string,\n TFields extends Record<string, Field>,\n>(\n definition: EntityDefinition<TFields> & { name: TName }\n): Entity<TName, TFields, Record<string, unknown>> {\n // Build schema objects\n const fullSchemaObj = buildSchemaObject(definition, 'full')\n const createSchemaObj = buildSchemaObject(definition, 'create')\n const updateSchemaObj = buildSchemaObject(definition, 'update')\n const querySchemaObj = buildSchemaObject(definition, 'query')\n\n // Create ArkType schemas\n const schema = type(fullSchemaObj as Record<string, string>)\n const createSchema = type(createSchemaObj as Record<string, string>)\n const updateSchema = type(updateSchemaObj as Record<string, string>)\n const querySchema = type(querySchemaObj as Record<string, string>)\n\n // Determine auto and required fields\n const autoFields: string[] = ['id']\n if (definition.timestamps) {\n autoFields.push('insertedAt', 'updatedAt')\n }\n if (definition.softDelete) {\n autoFields.push('deletedAt')\n }\n\n const requiredFields: string[] = []\n const optionalFields: string[] = []\n\n if (definition.tenant) {\n requiredFields.push('tenantId')\n }\n\n for (const [name, field] of Object.entries(definition.fields)) {\n const def: FieldDefinition = typeof field === 'string'\n ? { type: field }\n : field\n\n if (def.optional || def.default !== undefined) {\n optionalFields.push(name)\n } else {\n requiredFields.push(name)\n }\n }\n\n // Cache for generated tables\n let cachedPgTable: DrizzleTable | null = null\n let cachedSqliteTable: DrizzleTable | null = null\n let lastPgRefs: Record<string, DrizzleTable> | undefined\n let lastSqliteRefs: Record<string, DrizzleTable> | undefined\n\n const entity: Entity<TName, TFields, Record<string, unknown>> = {\n name: definition.name as TName,\n definition,\n schema: schema as Type<Record<string, unknown>>,\n createSchema: createSchema as Type<Record<string, unknown>>,\n updateSchema: updateSchema as Type<Record<string, unknown>>,\n querySchema: querySchema as Type<Record<string, unknown>>,\n infer: {} as Record<string, unknown>,\n autoFields,\n requiredFields,\n optionalFields,\n pgTable: (tableRefs?: Record<string, DrizzleTable>) => {\n // Return cached if refs haven't changed\n if (cachedPgTable && tableRefs === lastPgRefs) {\n return cachedPgTable\n }\n cachedPgTable = toPgTable(entity, tableRefs)\n lastPgRefs = tableRefs\n return cachedPgTable\n },\n sqliteTable: (tableRefs?: Record<string, DrizzleTable>) => {\n // Return cached if refs haven't changed\n if (cachedSqliteTable && tableRefs === lastSqliteRefs) {\n return cachedSqliteTable\n }\n cachedSqliteTable = toSqliteTable(entity, tableRefs)\n lastSqliteRefs = tableRefs\n return cachedSqliteTable\n },\n }\n\n return entity\n}\n\n/**\n * Create a reference field to another entity\n */\nexport function ref(\n entity: string | { name: string },\n options?: {\n field?: string\n onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action'\n optional?: boolean\n /** ID type for this reference (default: 'string.uuid') */\n idType?: string\n }\n): FieldDefinition {\n const entityName = typeof entity === 'string' ? entity : entity.name\n const result: FieldDefinition = {\n type: (options?.idType ?? 'string.uuid') as FieldDefinition['type'],\n db: {\n references: {\n entity: entityName,\n field: options?.field ?? 'id',\n onDelete: options?.onDelete ?? 'restrict',\n },\n },\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n\n/**\n * Create an enum field from a list of values\n */\nexport function enumField<T extends string>(\n values: readonly T[],\n options?: { default?: T; optional?: boolean }\n): FieldDefinition {\n const typeStr = values.map(v => `'${v}'`).join(' | ')\n const result: FieldDefinition = {\n type: typeStr as `'${string}'`,\n }\n if (options?.default !== undefined) {\n result.default = options.default\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n\n/**\n * Create a JSON field\n */\nexport function jsonField<T = Record<string, unknown>>(\n options?: { optional?: boolean; default?: T }\n): FieldDefinition {\n const result: FieldDefinition = {\n type: 'json',\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n if (options?.default !== undefined) {\n result.default = options.default\n }\n return result\n}\n\n/**\n * Create a decimal field with precision\n */\nexport function decimal(\n precision: number,\n scale: number,\n options?: { min?: number; max?: number; optional?: boolean }\n): FieldDefinition {\n const result: FieldDefinition = {\n type: 'number',\n db: {\n precision,\n scale,\n },\n }\n if (options?.min !== undefined) {\n result.min = options.min\n }\n if (options?.max !== undefined) {\n result.max = options.max\n }\n if (options?.optional !== undefined) {\n result.optional = options.optional\n }\n return result\n}\n"]}
|
package/dist/pg.d.ts
CHANGED
package/dist/pg.js
CHANGED
|
@@ -1,137 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import { sql } from 'drizzle-orm';
|
|
3
|
-
|
|
4
|
-
// src/pg.ts
|
|
5
|
-
function fieldToColumn(name, field) {
|
|
6
|
-
const def = typeof field === "string" ? { type: field } : field;
|
|
7
|
-
const columnName = def.db?.column ?? toSnakeCase(name);
|
|
8
|
-
let column;
|
|
9
|
-
const fieldType = def.type.split(" ")[0] ?? "string";
|
|
10
|
-
switch (fieldType) {
|
|
11
|
-
case "string.uuid":
|
|
12
|
-
column = uuid(columnName);
|
|
13
|
-
break;
|
|
14
|
-
case "string.email":
|
|
15
|
-
case "string.url":
|
|
16
|
-
case "string":
|
|
17
|
-
column = text(columnName);
|
|
18
|
-
break;
|
|
19
|
-
case "number.integer":
|
|
20
|
-
column = integer(columnName);
|
|
21
|
-
break;
|
|
22
|
-
case "number":
|
|
23
|
-
if (def.db?.precision) {
|
|
24
|
-
column = numeric(columnName, {
|
|
25
|
-
precision: def.db.precision,
|
|
26
|
-
scale: def.db.scale ?? 2
|
|
27
|
-
});
|
|
28
|
-
} else {
|
|
29
|
-
column = numeric(columnName);
|
|
30
|
-
}
|
|
31
|
-
break;
|
|
32
|
-
case "boolean":
|
|
33
|
-
column = boolean(columnName);
|
|
34
|
-
break;
|
|
35
|
-
case "Date":
|
|
36
|
-
column = timestamp(columnName, { withTimezone: true });
|
|
37
|
-
break;
|
|
38
|
-
case "json":
|
|
39
|
-
column = json(columnName);
|
|
40
|
-
break;
|
|
41
|
-
default:
|
|
42
|
-
column = text(columnName);
|
|
43
|
-
}
|
|
44
|
-
if (!def.optional && def.default === void 0) {
|
|
45
|
-
column = column.notNull();
|
|
46
|
-
}
|
|
47
|
-
if (def.default !== void 0) {
|
|
48
|
-
column = column.default(def.default);
|
|
49
|
-
}
|
|
50
|
-
return column;
|
|
51
|
-
}
|
|
52
|
-
function toSnakeCase(str) {
|
|
53
|
-
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
54
|
-
}
|
|
55
|
-
function toPgTable(entity, tableRefs) {
|
|
56
|
-
const def = entity.definition;
|
|
57
|
-
const columns = {};
|
|
58
|
-
columns["id"] = uuid("id").primaryKey().defaultRandom();
|
|
59
|
-
if (def.tenant) {
|
|
60
|
-
const tenantsTable = tableRefs?.["tenants"];
|
|
61
|
-
if (tenantsTable) {
|
|
62
|
-
columns["tenantId"] = uuid("tenant_id").notNull().references(() => tenantsTable.id, { onDelete: "cascade" });
|
|
63
|
-
} else {
|
|
64
|
-
columns["tenantId"] = uuid("tenant_id").notNull();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
for (const [name, field] of Object.entries(def.fields)) {
|
|
68
|
-
const fieldDef = typeof field === "string" ? { } : field;
|
|
69
|
-
if (fieldDef.db?.references && tableRefs) {
|
|
70
|
-
const refTable = tableRefs[fieldDef.db.references.entity];
|
|
71
|
-
if (refTable) {
|
|
72
|
-
const refField = fieldDef.db.references.field ?? "id";
|
|
73
|
-
const onDelete = fieldDef.db.references.onDelete ?? "restrict";
|
|
74
|
-
let col = uuid(toSnakeCase(name)).references(() => refTable[refField], { onDelete });
|
|
75
|
-
if (!fieldDef.optional) {
|
|
76
|
-
col = col.notNull();
|
|
77
|
-
}
|
|
78
|
-
columns[name] = col;
|
|
79
|
-
} else {
|
|
80
|
-
columns[name] = fieldToColumn(name, field);
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
columns[name] = fieldToColumn(name, field);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (def.timestamps) {
|
|
87
|
-
columns["insertedAt"] = timestamp("inserted_at", { withTimezone: true }).notNull().defaultNow();
|
|
88
|
-
columns["updatedAt"] = timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => /* @__PURE__ */ new Date());
|
|
89
|
-
}
|
|
90
|
-
if (def.softDelete) {
|
|
91
|
-
columns["deletedAt"] = timestamp("deleted_at", { withTimezone: true });
|
|
92
|
-
}
|
|
93
|
-
return pgTable(def.name, columns, (t) => {
|
|
94
|
-
const indexes = {};
|
|
95
|
-
if (def.indexes) {
|
|
96
|
-
for (const idx of def.indexes) {
|
|
97
|
-
const idxName = idx.name ?? `${def.name}_${idx.fields.join("_")}_idx`;
|
|
98
|
-
const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean);
|
|
99
|
-
if (idxColumns.length === 0) continue;
|
|
100
|
-
if (idx.unique) {
|
|
101
|
-
if (idx.where) {
|
|
102
|
-
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
103
|
-
} else {
|
|
104
|
-
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns);
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
if (idx.where) {
|
|
108
|
-
indexes[idxName] = index(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
109
|
-
} else {
|
|
110
|
-
indexes[idxName] = index(idxName).on(...idxColumns);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (def.tenant && t.tenantId) {
|
|
116
|
-
const tenantIdxName = `${def.name}_tenant_id_idx`;
|
|
117
|
-
if (!indexes[tenantIdxName]) {
|
|
118
|
-
indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return indexes;
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
function createPgSchema(entities) {
|
|
125
|
-
const tables = {};
|
|
126
|
-
for (const [key, entity] of Object.entries(entities)) {
|
|
127
|
-
tables[key] = toPgTable(entity);
|
|
128
|
-
}
|
|
129
|
-
for (const [key, entity] of Object.entries(entities)) {
|
|
130
|
-
tables[key] = toPgTable(entity, tables);
|
|
131
|
-
}
|
|
132
|
-
return tables;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export { createPgSchema, toPgTable, toPgTable as toTable };
|
|
1
|
+
export { createPgSchema, toPgTable, toPgTable as toTable } from './chunk-I357VWZF.js';
|
|
136
2
|
//# sourceMappingURL=pg.js.map
|
|
137
3
|
//# sourceMappingURL=pg.js.map
|
package/dist/pg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pg.ts"],"names":[],"mappings":"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","file":"pg.js","sourcesContent":["/**\n * PostgreSQL Drizzle table generation from entity definitions\n */\nimport {\n pgTable,\n uuid,\n text,\n integer,\n boolean,\n timestamp,\n numeric,\n json,\n index,\n uniqueIndex,\n} from 'drizzle-orm/pg-core'\nimport { sql } from 'drizzle-orm'\nimport type { Entity, Field, FieldDefinition } from './types.js'\n\n/**\n * Convert field definition to Drizzle column builder\n */\nfunction fieldToColumn(name: string, field: Field) {\n const def: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n const columnName = def.db?.column ?? toSnakeCase(name)\n let column: any\n\n // Determine column type based on field type\n const fieldType = def.type.split(' ')[0] ?? 'string'\n\n switch (fieldType) {\n case 'string.uuid':\n column = uuid(columnName)\n break\n\n case 'string.email':\n case 'string.url':\n case 'string':\n column = text(columnName)\n break\n\n case 'number.integer':\n column = integer(columnName)\n break\n\n case 'number':\n if (def.db?.precision) {\n column = numeric(columnName, {\n precision: def.db.precision,\n scale: def.db.scale ?? 2,\n })\n } else {\n column = numeric(columnName)\n }\n break\n\n case 'boolean':\n column = boolean(columnName)\n break\n\n case 'Date':\n column = timestamp(columnName, { withTimezone: true })\n break\n\n case 'json':\n column = json(columnName)\n break\n\n default:\n // Union types like \"'draft' | 'active'\" become text\n column = text(columnName)\n }\n\n // Apply modifiers\n if (!def.optional && def.default === undefined) {\n column = column.notNull()\n }\n\n if (def.default !== undefined) {\n column = column.default(def.default)\n }\n\n return column\n}\n\n/**\n * Convert camelCase to snake_case\n */\nfunction toSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)\n}\n\n/**\n * Generate a Drizzle pgTable from an entity definition\n */\nexport function toPgTable<E extends Entity<string, Record<string, Field>, unknown>>(\n entity: E,\n tableRefs?: Record<string, any>\n): any {\n const def = entity.definition\n const columns: Record<string, any> = {}\n\n // Add id column\n columns['id'] = uuid('id').primaryKey().defaultRandom()\n\n // Add tenantId if tenant-scoped\n if (def.tenant) {\n const tenantsTable = tableRefs?.['tenants']\n if (tenantsTable) {\n columns['tenantId'] = uuid('tenant_id')\n .notNull()\n .references(() => tenantsTable.id, { onDelete: 'cascade' })\n } else {\n columns['tenantId'] = uuid('tenant_id').notNull()\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(def.fields)) {\n const fieldDef: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n // Handle references\n if (fieldDef.db?.references && tableRefs) {\n const refTable = tableRefs[fieldDef.db.references.entity]\n if (refTable) {\n const refField = fieldDef.db.references.field ?? 'id'\n const onDelete = fieldDef.db.references.onDelete ?? 'restrict'\n\n let col = uuid(toSnakeCase(name)).references(() => refTable[refField], { onDelete })\n if (!fieldDef.optional) {\n col = col.notNull()\n }\n columns[name] = col\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n }\n\n // Add timestamp fields\n if (def.timestamps) {\n columns['insertedAt'] = timestamp('inserted_at', { withTimezone: true })\n .notNull()\n .defaultNow()\n\n columns['updatedAt'] = timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow()\n .$onUpdate(() => new Date())\n }\n\n // Add soft delete field\n if (def.softDelete) {\n columns['deletedAt'] = timestamp('deleted_at', { withTimezone: true })\n }\n\n // Create table with indexes\n return pgTable(def.name, columns, (t: any) => {\n const indexes: Record<string, any> = {}\n\n // Add indexes from definition\n if (def.indexes) {\n for (const idx of def.indexes) {\n const idxName = idx.name ?? `${def.name}_${idx.fields.join('_')}_idx`\n const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean)\n\n if (idxColumns.length === 0) continue\n\n if (idx.unique) {\n if (idx.where) {\n indexes[idxName] = uniqueIndex(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = uniqueIndex(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n } else {\n if (idx.where) {\n indexes[idxName] = index(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = index(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n }\n }\n }\n\n // Add default tenant index if tenant-scoped\n if (def.tenant && t.tenantId) {\n const tenantIdxName = `${def.name}_tenant_id_idx`\n if (!indexes[tenantIdxName]) {\n indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId)\n }\n }\n\n return indexes\n })\n}\n\n/**\n * Create multiple tables with resolved references\n */\nexport function createPgSchema<\n T extends Record<string, Entity<string, Record<string, Field>, unknown>>,\n>(entities: T): { [K in keyof T]: any } {\n const tables: Record<string, any> = {}\n\n // First pass: create tables without references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toPgTable(entity)\n }\n\n // Second pass: recreate tables with resolved references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toPgTable(entity, tables)\n }\n\n return tables as { [K in keyof T]: any }\n}\n\nexport { toPgTable as toTable }\n"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"pg.js"}
|
package/dist/sqlite.d.ts
CHANGED
package/dist/sqlite.js
CHANGED
|
@@ -1,132 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import { sql } from 'drizzle-orm';
|
|
3
|
-
|
|
4
|
-
// src/sqlite.ts
|
|
5
|
-
function fieldToColumn(name, field) {
|
|
6
|
-
const def = typeof field === "string" ? { type: field } : field;
|
|
7
|
-
const columnName = toSnakeCase(name);
|
|
8
|
-
let column;
|
|
9
|
-
const fieldType = def.type.split(" ")[0] ?? "string";
|
|
10
|
-
switch (fieldType) {
|
|
11
|
-
case "string.uuid":
|
|
12
|
-
case "string.email":
|
|
13
|
-
case "string.url":
|
|
14
|
-
case "string":
|
|
15
|
-
column = text(columnName);
|
|
16
|
-
break;
|
|
17
|
-
case "number.integer":
|
|
18
|
-
column = integer(columnName);
|
|
19
|
-
break;
|
|
20
|
-
case "number":
|
|
21
|
-
column = real(columnName);
|
|
22
|
-
break;
|
|
23
|
-
case "boolean":
|
|
24
|
-
column = integer(columnName, { mode: "boolean" });
|
|
25
|
-
break;
|
|
26
|
-
case "Date":
|
|
27
|
-
column = text(columnName);
|
|
28
|
-
break;
|
|
29
|
-
case "json":
|
|
30
|
-
column = text(columnName, { mode: "json" });
|
|
31
|
-
break;
|
|
32
|
-
default:
|
|
33
|
-
column = text(columnName);
|
|
34
|
-
}
|
|
35
|
-
if (!def.optional && def.default === void 0) {
|
|
36
|
-
column = column.notNull();
|
|
37
|
-
}
|
|
38
|
-
if (def.default !== void 0) {
|
|
39
|
-
if (typeof def.default === "object") {
|
|
40
|
-
column = column.default(JSON.stringify(def.default));
|
|
41
|
-
} else {
|
|
42
|
-
column = column.default(def.default);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return column;
|
|
46
|
-
}
|
|
47
|
-
function toSnakeCase(str) {
|
|
48
|
-
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
49
|
-
}
|
|
50
|
-
function toSqliteTable(entity, tableRefs) {
|
|
51
|
-
const def = entity.definition;
|
|
52
|
-
const columns = {};
|
|
53
|
-
columns["id"] = text("id").primaryKey().$defaultFn(() => crypto.randomUUID());
|
|
54
|
-
if (def.tenant) {
|
|
55
|
-
const tenantsTable = tableRefs?.["tenants"];
|
|
56
|
-
if (tenantsTable) {
|
|
57
|
-
columns["tenantId"] = text("tenant_id").notNull().references(() => tenantsTable.id, { onDelete: "cascade" });
|
|
58
|
-
} else {
|
|
59
|
-
columns["tenantId"] = text("tenant_id").notNull();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
for (const [name, field] of Object.entries(def.fields)) {
|
|
63
|
-
const fieldDef = typeof field === "string" ? { } : field;
|
|
64
|
-
if (fieldDef.db?.references && tableRefs) {
|
|
65
|
-
const refTable = tableRefs[fieldDef.db.references.entity];
|
|
66
|
-
if (refTable) {
|
|
67
|
-
const refField = fieldDef.db.references.field ?? "id";
|
|
68
|
-
const onDelete = fieldDef.db.references.onDelete ?? "restrict";
|
|
69
|
-
let col = text(toSnakeCase(name)).references(() => refTable[refField], { onDelete });
|
|
70
|
-
if (!fieldDef.optional) {
|
|
71
|
-
col = col.notNull();
|
|
72
|
-
}
|
|
73
|
-
columns[name] = col;
|
|
74
|
-
} else {
|
|
75
|
-
columns[name] = fieldToColumn(name, field);
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
columns[name] = fieldToColumn(name, field);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (def.timestamps) {
|
|
82
|
-
columns["insertedAt"] = text("inserted_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString());
|
|
83
|
-
columns["updatedAt"] = text("updated_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString()).$onUpdate(() => (/* @__PURE__ */ new Date()).toISOString());
|
|
84
|
-
}
|
|
85
|
-
if (def.softDelete) {
|
|
86
|
-
columns["deletedAt"] = text("deleted_at");
|
|
87
|
-
}
|
|
88
|
-
return sqliteTable(def.name, columns, (t) => {
|
|
89
|
-
const indexes = {};
|
|
90
|
-
if (def.indexes) {
|
|
91
|
-
for (const idx of def.indexes) {
|
|
92
|
-
const idxName = idx.name ?? `${def.name}_${idx.fields.join("_")}_idx`;
|
|
93
|
-
const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean);
|
|
94
|
-
if (idxColumns.length === 0) continue;
|
|
95
|
-
if (idx.unique) {
|
|
96
|
-
if (idx.where) {
|
|
97
|
-
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
98
|
-
} else {
|
|
99
|
-
indexes[idxName] = uniqueIndex(idxName).on(...idxColumns);
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
if (idx.where) {
|
|
103
|
-
indexes[idxName] = index(idxName).on(...idxColumns).where(sql.raw(idx.where));
|
|
104
|
-
} else {
|
|
105
|
-
indexes[idxName] = index(idxName).on(...idxColumns);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (def.tenant && t.tenantId) {
|
|
111
|
-
const tenantIdxName = `${def.name}_tenant_id_idx`;
|
|
112
|
-
if (!indexes[tenantIdxName]) {
|
|
113
|
-
indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return indexes;
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
function createSqliteSchema(entities) {
|
|
120
|
-
const tables = {};
|
|
121
|
-
for (const [key, entity] of Object.entries(entities)) {
|
|
122
|
-
tables[key] = toSqliteTable(entity);
|
|
123
|
-
}
|
|
124
|
-
for (const [key, entity] of Object.entries(entities)) {
|
|
125
|
-
tables[key] = toSqliteTable(entity, tables);
|
|
126
|
-
}
|
|
127
|
-
return tables;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export { createSqliteSchema, toSqliteTable, toSqliteTable as toTable };
|
|
1
|
+
export { createSqliteSchema, toSqliteTable, toSqliteTable as toTable } from './chunk-5UDSZ7DZ.js';
|
|
131
2
|
//# sourceMappingURL=sqlite.js.map
|
|
132
3
|
//# sourceMappingURL=sqlite.js.map
|
package/dist/sqlite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sqlite.ts"],"names":[],"mappings":"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","file":"sqlite.js","sourcesContent":["/**\n * SQLite Drizzle table generation from entity definitions\n * Compatible with Cloudflare D1\n */\nimport {\n sqliteTable,\n text,\n integer,\n real,\n index,\n uniqueIndex,\n} from 'drizzle-orm/sqlite-core'\nimport { sql } from 'drizzle-orm'\nimport type { Entity, Field, FieldDefinition } from './types.js'\n\n/**\n * Convert field definition to Drizzle SQLite column builder\n */\nfunction fieldToColumn(name: string, field: Field) {\n const def: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n const columnName = toSnakeCase(name)\n let column: any\n\n // Determine column type based on field type\n const fieldType = def.type.split(' ')[0] ?? 'string'\n\n switch (fieldType) {\n case 'string.uuid':\n case 'string.email':\n case 'string.url':\n case 'string':\n column = text(columnName)\n break\n\n case 'number.integer':\n column = integer(columnName)\n break\n\n case 'number':\n column = real(columnName)\n break\n\n case 'boolean':\n column = integer(columnName, { mode: 'boolean' })\n break\n\n case 'Date':\n column = text(columnName)\n break\n\n case 'json':\n column = text(columnName, { mode: 'json' })\n break\n\n default:\n column = text(columnName)\n }\n\n // Apply modifiers\n if (!def.optional && def.default === undefined) {\n column = column.notNull()\n }\n\n if (def.default !== undefined) {\n if (typeof def.default === 'object') {\n column = column.default(JSON.stringify(def.default))\n } else {\n column = column.default(def.default)\n }\n }\n\n return column\n}\n\n/**\n * Convert camelCase to snake_case\n */\nfunction toSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)\n}\n\n/**\n * Generate a Drizzle sqliteTable from an entity definition\n */\nexport function toSqliteTable<E extends Entity<string, Record<string, Field>, unknown>>(\n entity: E,\n tableRefs?: Record<string, any>\n): any {\n const def = entity.definition\n const columns: Record<string, any> = {}\n\n // Add id column\n columns['id'] = text('id')\n .primaryKey()\n .$defaultFn(() => crypto.randomUUID())\n\n // Add tenantId if tenant-scoped\n if (def.tenant) {\n const tenantsTable = tableRefs?.['tenants']\n if (tenantsTable) {\n columns['tenantId'] = text('tenant_id')\n .notNull()\n .references(() => tenantsTable.id, { onDelete: 'cascade' })\n } else {\n columns['tenantId'] = text('tenant_id').notNull()\n }\n }\n\n // Add user-defined fields\n for (const [name, field] of Object.entries(def.fields)) {\n const fieldDef: FieldDefinition = typeof field === 'string' ? { type: field } : field\n\n // Handle references\n if (fieldDef.db?.references && tableRefs) {\n const refTable = tableRefs[fieldDef.db.references.entity]\n if (refTable) {\n const refField = fieldDef.db.references.field ?? 'id'\n const onDelete = fieldDef.db.references.onDelete ?? 'restrict'\n\n let col = text(toSnakeCase(name)).references(() => refTable[refField], { onDelete })\n if (!fieldDef.optional) {\n col = col.notNull()\n }\n columns[name] = col\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n } else {\n columns[name] = fieldToColumn(name, field)\n }\n }\n\n // Add timestamp fields\n if (def.timestamps) {\n columns['insertedAt'] = text('inserted_at')\n .notNull()\n .$defaultFn(() => new Date().toISOString())\n\n columns['updatedAt'] = text('updated_at')\n .notNull()\n .$defaultFn(() => new Date().toISOString())\n .$onUpdate(() => new Date().toISOString())\n }\n\n // Add soft delete field\n if (def.softDelete) {\n columns['deletedAt'] = text('deleted_at')\n }\n\n // Create table with indexes\n return sqliteTable(def.name, columns, (t: any) => {\n const indexes: Record<string, any> = {}\n\n // Add indexes from definition\n if (def.indexes) {\n for (const idx of def.indexes) {\n const idxName = idx.name ?? `${def.name}_${idx.fields.join('_')}_idx`\n const idxColumns = idx.fields.map((f) => t[f]).filter(Boolean)\n\n if (idxColumns.length === 0) continue\n\n if (idx.unique) {\n if (idx.where) {\n indexes[idxName] = uniqueIndex(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = uniqueIndex(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n } else {\n if (idx.where) {\n indexes[idxName] = index(idxName)\n .on(...(idxColumns as [any, ...any[]]))\n .where(sql.raw(idx.where))\n } else {\n indexes[idxName] = index(idxName).on(...(idxColumns as [any, ...any[]]))\n }\n }\n }\n }\n\n // Add default tenant index if tenant-scoped\n if (def.tenant && t.tenantId) {\n const tenantIdxName = `${def.name}_tenant_id_idx`\n if (!indexes[tenantIdxName]) {\n indexes[tenantIdxName] = index(tenantIdxName).on(t.tenantId)\n }\n }\n\n return indexes\n })\n}\n\n/**\n * Create multiple SQLite tables with resolved references\n */\nexport function createSqliteSchema<\n T extends Record<string, Entity<string, Record<string, Field>, unknown>>,\n>(entities: T): { [K in keyof T]: any } {\n const tables: Record<string, any> = {}\n\n // First pass: create tables without references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toSqliteTable(entity)\n }\n\n // Second pass: recreate tables with resolved references\n for (const [key, entity] of Object.entries(entities)) {\n tables[key] = toSqliteTable(entity, tables)\n }\n\n return tables as { [K in keyof T]: any }\n}\n\nexport { toSqliteTable as toTable }\n"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"sqlite.js"}
|
|
@@ -101,6 +101,14 @@ interface EntitySchemas<T> {
|
|
|
101
101
|
nextCursor?: string;
|
|
102
102
|
}>;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Drizzle table type placeholder (actual type comes from drizzle-orm)
|
|
106
|
+
*/
|
|
107
|
+
type DrizzleTable = any;
|
|
108
|
+
/**
|
|
109
|
+
* Table generator function type
|
|
110
|
+
*/
|
|
111
|
+
type TableGenerator = (tableRefs?: Record<string, DrizzleTable>) => DrizzleTable;
|
|
104
112
|
/**
|
|
105
113
|
* The complete entity object returned by defineEntity
|
|
106
114
|
*/
|
|
@@ -125,6 +133,16 @@ interface Entity<TName extends string, TFields extends Record<string, Field>, TT
|
|
|
125
133
|
requiredFields: string[];
|
|
126
134
|
/** Field names that are optional */
|
|
127
135
|
optionalFields: string[];
|
|
136
|
+
/**
|
|
137
|
+
* Generate PostgreSQL Drizzle table
|
|
138
|
+
* @param tableRefs Optional map of table references for foreign keys
|
|
139
|
+
*/
|
|
140
|
+
pgTable: TableGenerator;
|
|
141
|
+
/**
|
|
142
|
+
* Generate SQLite Drizzle table
|
|
143
|
+
* @param tableRefs Optional map of table references for foreign keys
|
|
144
|
+
*/
|
|
145
|
+
sqliteTable: TableGenerator;
|
|
128
146
|
}
|
|
129
147
|
/**
|
|
130
148
|
* Options for Drizzle table generation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parsrun/entity",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Single-source entity definitions for Pars - generates ArkType schemas and Drizzle tables",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"arktype": "^2.0.0",
|
|
29
|
-
"@parsrun/core": "0.1
|
|
29
|
+
"@parsrun/core": "0.2.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"drizzle-orm": "^0.44.0",
|
package/src/define.test.ts
CHANGED
|
@@ -110,3 +110,85 @@ describe('helper functions', () => {
|
|
|
110
110
|
expect(field.min).toBe(0)
|
|
111
111
|
})
|
|
112
112
|
})
|
|
113
|
+
|
|
114
|
+
describe('table generation', () => {
|
|
115
|
+
it('should generate pgTable', () => {
|
|
116
|
+
const Product = defineEntity({
|
|
117
|
+
name: 'products',
|
|
118
|
+
tenant: true,
|
|
119
|
+
timestamps: true,
|
|
120
|
+
fields: {
|
|
121
|
+
name: 'string',
|
|
122
|
+
price: 'number',
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const table = Product.pgTable()
|
|
127
|
+
|
|
128
|
+
expect(table).toBeDefined()
|
|
129
|
+
// Drizzle tables have columns as properties
|
|
130
|
+
expect(table.id).toBeDefined()
|
|
131
|
+
expect(table.name).toBeDefined()
|
|
132
|
+
expect(table.price).toBeDefined()
|
|
133
|
+
expect(table.tenantId).toBeDefined()
|
|
134
|
+
expect(table.insertedAt).toBeDefined()
|
|
135
|
+
expect(table.updatedAt).toBeDefined()
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('should generate sqliteTable', () => {
|
|
139
|
+
const Product = defineEntity({
|
|
140
|
+
name: 'products',
|
|
141
|
+
tenant: true,
|
|
142
|
+
timestamps: true,
|
|
143
|
+
fields: {
|
|
144
|
+
name: 'string',
|
|
145
|
+
price: 'number',
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const table = Product.sqliteTable()
|
|
150
|
+
|
|
151
|
+
expect(table).toBeDefined()
|
|
152
|
+
// Drizzle tables have columns as properties
|
|
153
|
+
expect(table.id).toBeDefined()
|
|
154
|
+
expect(table.name).toBeDefined()
|
|
155
|
+
expect(table.price).toBeDefined()
|
|
156
|
+
expect(table.tenantId).toBeDefined()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should cache generated tables', () => {
|
|
160
|
+
const Product = defineEntity({
|
|
161
|
+
name: 'products',
|
|
162
|
+
fields: {
|
|
163
|
+
name: 'string',
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const table1 = Product.pgTable()
|
|
168
|
+
const table2 = Product.pgTable()
|
|
169
|
+
|
|
170
|
+
expect(table1).toBe(table2)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should regenerate table when refs change', () => {
|
|
174
|
+
const Category = defineEntity({
|
|
175
|
+
name: 'categories',
|
|
176
|
+
fields: {
|
|
177
|
+
name: 'string',
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const Product = defineEntity({
|
|
182
|
+
name: 'products',
|
|
183
|
+
fields: {
|
|
184
|
+
name: 'string',
|
|
185
|
+
categoryId: ref('categories'),
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const table1 = Product.pgTable()
|
|
190
|
+
const table2 = Product.pgTable({ categories: Category.pgTable() })
|
|
191
|
+
|
|
192
|
+
expect(table1).not.toBe(table2)
|
|
193
|
+
})
|
|
194
|
+
})
|
package/src/define.ts
CHANGED
|
@@ -4,7 +4,10 @@ import type {
|
|
|
4
4
|
Field,
|
|
5
5
|
FieldDefinition,
|
|
6
6
|
Entity,
|
|
7
|
+
DrizzleTable,
|
|
7
8
|
} from './types.js'
|
|
9
|
+
import { toPgTable } from './pg.js'
|
|
10
|
+
import { toSqliteTable } from './sqlite.js'
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Convert a field definition to an ArkType type string
|
|
@@ -211,7 +214,13 @@ export function defineEntity<
|
|
|
211
214
|
}
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
// Cache for generated tables
|
|
218
|
+
let cachedPgTable: DrizzleTable | null = null
|
|
219
|
+
let cachedSqliteTable: DrizzleTable | null = null
|
|
220
|
+
let lastPgRefs: Record<string, DrizzleTable> | undefined
|
|
221
|
+
let lastSqliteRefs: Record<string, DrizzleTable> | undefined
|
|
222
|
+
|
|
223
|
+
const entity: Entity<TName, TFields, Record<string, unknown>> = {
|
|
215
224
|
name: definition.name as TName,
|
|
216
225
|
definition,
|
|
217
226
|
schema: schema as Type<Record<string, unknown>>,
|
|
@@ -222,7 +231,27 @@ export function defineEntity<
|
|
|
222
231
|
autoFields,
|
|
223
232
|
requiredFields,
|
|
224
233
|
optionalFields,
|
|
234
|
+
pgTable: (tableRefs?: Record<string, DrizzleTable>) => {
|
|
235
|
+
// Return cached if refs haven't changed
|
|
236
|
+
if (cachedPgTable && tableRefs === lastPgRefs) {
|
|
237
|
+
return cachedPgTable
|
|
238
|
+
}
|
|
239
|
+
cachedPgTable = toPgTable(entity, tableRefs)
|
|
240
|
+
lastPgRefs = tableRefs
|
|
241
|
+
return cachedPgTable
|
|
242
|
+
},
|
|
243
|
+
sqliteTable: (tableRefs?: Record<string, DrizzleTable>) => {
|
|
244
|
+
// Return cached if refs haven't changed
|
|
245
|
+
if (cachedSqliteTable && tableRefs === lastSqliteRefs) {
|
|
246
|
+
return cachedSqliteTable
|
|
247
|
+
}
|
|
248
|
+
cachedSqliteTable = toSqliteTable(entity, tableRefs)
|
|
249
|
+
lastSqliteRefs = tableRefs
|
|
250
|
+
return cachedSqliteTable
|
|
251
|
+
},
|
|
225
252
|
}
|
|
253
|
+
|
|
254
|
+
return entity
|
|
226
255
|
}
|
|
227
256
|
|
|
228
257
|
/**
|
package/src/types.ts
CHANGED
|
@@ -116,6 +116,16 @@ export interface EntitySchemas<T> {
|
|
|
116
116
|
listSchema: Type<{ items: T[]; total: number; nextCursor?: string }>
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Drizzle table type placeholder (actual type comes from drizzle-orm)
|
|
121
|
+
*/
|
|
122
|
+
export type DrizzleTable = any
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Table generator function type
|
|
126
|
+
*/
|
|
127
|
+
export type TableGenerator = (tableRefs?: Record<string, DrizzleTable>) => DrizzleTable
|
|
128
|
+
|
|
119
129
|
/**
|
|
120
130
|
* The complete entity object returned by defineEntity
|
|
121
131
|
*/
|
|
@@ -144,6 +154,16 @@ export interface Entity<
|
|
|
144
154
|
requiredFields: string[]
|
|
145
155
|
/** Field names that are optional */
|
|
146
156
|
optionalFields: string[]
|
|
157
|
+
/**
|
|
158
|
+
* Generate PostgreSQL Drizzle table
|
|
159
|
+
* @param tableRefs Optional map of table references for foreign keys
|
|
160
|
+
*/
|
|
161
|
+
pgTable: TableGenerator
|
|
162
|
+
/**
|
|
163
|
+
* Generate SQLite Drizzle table
|
|
164
|
+
* @param tableRefs Optional map of table references for foreign keys
|
|
165
|
+
*/
|
|
166
|
+
sqliteTable: TableGenerator
|
|
147
167
|
}
|
|
148
168
|
|
|
149
169
|
/**
|