@databricks/appkit 0.65.0 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CLAUDE.md +18 -0
  2. package/dist/appkit/package.js +1 -1
  3. package/dist/beta.d.ts +9 -1
  4. package/dist/beta.js +6 -1
  5. package/dist/cli/commands/generate-types.js +12 -3
  6. package/dist/cli/commands/generate-types.js.map +1 -1
  7. package/dist/database/contract/index.js +3 -0
  8. package/dist/database/contract/registry.d.ts +26 -0
  9. package/dist/database/contract/registry.d.ts.map +1 -0
  10. package/dist/database/contract/wire.d.ts +8 -0
  11. package/dist/database/contract/wire.d.ts.map +1 -0
  12. package/dist/database/contract/wire.js +37 -0
  13. package/dist/database/contract/wire.js.map +1 -0
  14. package/dist/database/errors.js +71 -0
  15. package/dist/database/errors.js.map +1 -0
  16. package/dist/database/runtime/data-path.js +39 -0
  17. package/dist/database/runtime/data-path.js.map +1 -0
  18. package/dist/database/runtime/engine/drizzle-data-path.js +194 -0
  19. package/dist/database/runtime/engine/drizzle-data-path.js.map +1 -0
  20. package/dist/database/runtime/engine/translate.js +176 -0
  21. package/dist/database/runtime/engine/translate.js.map +1 -0
  22. package/dist/database/schema-builder/columns.d.ts +39 -0
  23. package/dist/database/schema-builder/columns.d.ts.map +1 -0
  24. package/dist/database/schema-builder/columns.js +162 -0
  25. package/dist/database/schema-builder/columns.js.map +1 -0
  26. package/dist/database/schema-builder/define-schema.d.ts +12 -0
  27. package/dist/database/schema-builder/define-schema.d.ts.map +1 -0
  28. package/dist/database/schema-builder/define-schema.js +207 -0
  29. package/dist/database/schema-builder/define-schema.js.map +1 -0
  30. package/dist/database/schema-builder/engine/relations.js +37 -0
  31. package/dist/database/schema-builder/engine/relations.js.map +1 -0
  32. package/dist/database/schema-builder/engine/tables.js +124 -0
  33. package/dist/database/schema-builder/engine/tables.js.map +1 -0
  34. package/dist/database/schema-builder/fk.d.ts +9 -0
  35. package/dist/database/schema-builder/fk.d.ts.map +1 -0
  36. package/dist/database/schema-builder/fk.js +71 -0
  37. package/dist/database/schema-builder/fk.js.map +1 -0
  38. package/dist/database/schema-builder/index.js +6 -0
  39. package/dist/database/schema-builder/relations.js +51 -0
  40. package/dist/database/schema-builder/relations.js.map +1 -0
  41. package/dist/database/schema-builder/types.d.ts +121 -0
  42. package/dist/database/schema-builder/types.d.ts.map +1 -0
  43. package/dist/database/schema-builder/types.js +43 -0
  44. package/dist/database/schema-builder/types.js.map +1 -0
  45. package/dist/database/schema-builder/validators.js +53 -0
  46. package/dist/database/schema-builder/validators.js.map +1 -0
  47. package/dist/index.d.ts +2 -1
  48. package/dist/plugins/beta-exports.generated.d.ts +3 -1
  49. package/dist/plugins/beta-exports.generated.js +2 -0
  50. package/dist/plugins/database/database.d.ts +37 -0
  51. package/dist/plugins/database/database.d.ts.map +1 -0
  52. package/dist/plugins/database/database.js +68 -0
  53. package/dist/plugins/database/database.js.map +1 -0
  54. package/dist/plugins/database/defaults.js +12 -0
  55. package/dist/plugins/database/defaults.js.map +1 -0
  56. package/dist/plugins/database/entity-client.js +148 -0
  57. package/dist/plugins/database/entity-client.js.map +1 -0
  58. package/dist/plugins/database/entity-types.d.ts +76 -0
  59. package/dist/plugins/database/entity-types.d.ts.map +1 -0
  60. package/dist/plugins/database/index.d.ts +3 -0
  61. package/dist/plugins/database/index.js +3 -0
  62. package/dist/plugins/database/lifecycle.js +107 -0
  63. package/dist/plugins/database/lifecycle.js.map +1 -0
  64. package/dist/plugins/database/manifest.js +84 -0
  65. package/dist/plugins/database/manifest.js.map +1 -0
  66. package/dist/plugins/database/types.d.ts +10 -0
  67. package/dist/plugins/database/types.d.ts.map +1 -0
  68. package/dist/type-generator/database/generate.d.ts +15 -0
  69. package/dist/type-generator/database/generate.d.ts.map +1 -0
  70. package/dist/type-generator/database/generate.js +104 -0
  71. package/dist/type-generator/database/generate.js.map +1 -0
  72. package/dist/type-generator/database/index.js +3 -0
  73. package/dist/type-generator/database/walk-schema.js +65 -0
  74. package/dist/type-generator/database/walk-schema.js.map +1 -0
  75. package/dist/type-generator/index.d.ts +148 -0
  76. package/dist/type-generator/index.d.ts.map +1 -0
  77. package/dist/type-generator/index.js +3 -1
  78. package/dist/type-generator/index.js.map +1 -1
  79. package/dist/type-generator/mv-registry/types.d.ts +99 -0
  80. package/dist/type-generator/mv-registry/types.d.ts.map +1 -0
  81. package/dist/type-generator/preflight.d.ts +16 -0
  82. package/dist/type-generator/preflight.d.ts.map +1 -0
  83. package/dist/type-generator/serving/generator.d.ts +20 -0
  84. package/dist/type-generator/serving/generator.d.ts.map +1 -0
  85. package/dist/type-generator/types.d.ts +72 -0
  86. package/dist/type-generator/types.d.ts.map +1 -0
  87. package/dist/type-generator/vite-plugin.d.ts +1 -0
  88. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  89. package/dist/type-generator/vite-plugin.js +41 -8
  90. package/dist/type-generator/vite-plugin.js.map +1 -1
  91. package/dist/type-generator/warehouse-status.d.ts +1 -0
  92. package/docs/api/appkit/Function.bigid.md +10 -0
  93. package/docs/api/appkit/Function.bigint.md +10 -0
  94. package/docs/api/appkit/Function.boolean.md +10 -0
  95. package/docs/api/appkit/Function.database.md +56 -0
  96. package/docs/api/appkit/Function.defineSchema.md +17 -0
  97. package/docs/api/appkit/Function.enumColumn.md +17 -0
  98. package/docs/api/appkit/Function.fk.md +18 -0
  99. package/docs/api/appkit/Function.id.md +10 -0
  100. package/docs/api/appkit/Function.integer.md +10 -0
  101. package/docs/api/appkit/Function.jsonb.md +10 -0
  102. package/docs/api/appkit/Function.text.md +10 -0
  103. package/docs/api/appkit/Function.timestamp.md +19 -0
  104. package/docs/api/appkit/Function.uuid.md +10 -0
  105. package/docs/api/appkit/Function.varchar.md +16 -0
  106. package/docs/api/appkit/Interface.DatabaseRegistry.md +3 -0
  107. package/docs/api/appkit/Interface.Schema.md +28 -0
  108. package/docs/api/appkit/TypeAlias.DatabaseExports.md +35 -0
  109. package/docs/api/appkit/TypeAlias.IDatabaseConfig.md +25 -0
  110. package/docs/api/appkit.md +18 -0
  111. package/llms.txt +18 -0
  112. package/package.json +4 -3
  113. package/sbom.cdx.json +1 -1
@@ -0,0 +1,194 @@
1
+ import { createLogger } from "../../../logging/logger.js";
2
+ import { columnValueSchema } from "../../schema-builder/validators.js";
3
+ import { DatabasePluginError, invalidDatabaseRequest } from "../../errors.js";
4
+ import { buildEngineRelations } from "../../schema-builder/engine/relations.js";
5
+ import { conflictTargetMeta, limitOrDefault, primaryKeyMeta, validateOffset } from "../data-path.js";
6
+ import { columnOf, defaultColumns, publicColumnNames, returningColumns, selectToColumns, translateInclude, translateOrder, translateWhere } from "./translate.js";
7
+ import { and, eq, isSQLWrapper, sql } from "drizzle-orm";
8
+ import { drizzle } from "drizzle-orm/node-postgres";
9
+
10
+ //#region src/database/runtime/engine/drizzle-data-path.ts
11
+ const logger = createLogger("database");
12
+ /** Bind finalized AppKit metadata to a relational Drizzle database. */
13
+ function createDrizzleDb(pool, schema) {
14
+ return drizzle(pool, { schema: Object.assign(Object.create(null), schema.$engine, buildEngineRelations(schema.$tables)) });
15
+ }
16
+ /** Reject same-name or forged tables by requiring finalized object identity. */
17
+ function assertRegisteredTable(schema, table) {
18
+ if (schema.$tables[table.$name] !== table) throw invalidDatabaseRequest(`Table "${table.$name}" is not registered`);
19
+ }
20
+ function relationalQueryBuilder(db, schema, table) {
21
+ assertRegisteredTable(schema, table);
22
+ const query = db.query[table.$name];
23
+ if (!query) throw invalidDatabaseRequest(`Table "${table.$name}" is not registered`);
24
+ return query;
25
+ }
26
+ function selectedColumns(table, select, access = "public") {
27
+ return select === void 0 ? defaultColumns(table) : selectToColumns(table, select, access);
28
+ }
29
+ /** Apply the finalized write schema again at the persistence boundary. */
30
+ function mutationValues(table, values, kind) {
31
+ if (values === null || typeof values !== "object" || Array.isArray(values)) throw invalidDatabaseRequest("Database mutation values must be an object");
32
+ for (const [key, value] of Object.entries(values)) {
33
+ if (!Object.hasOwn(table.$columns, key)) throw invalidDatabaseRequest(`Unknown column "${table.$name}.${key}"`);
34
+ if (isSQLWrapper(value)) throw invalidDatabaseRequest("Database mutation values cannot contain SQL");
35
+ }
36
+ const result = (kind === "insert" ? table.$insertSchema : table.$updateSchema).safeParse(values);
37
+ if (!result.success) throw invalidDatabaseRequest(`Invalid database ${kind} values`);
38
+ return result.data;
39
+ }
40
+ /** Never change row identity or the conflict key during the update half. */
41
+ function upsertUpdateValues(table, parameters, conflictTarget) {
42
+ const updates = {};
43
+ for (const [key, value] of Object.entries(parameters)) {
44
+ const column = table.$columns[key];
45
+ if (column.primaryKey || column.serverGenerated || column.columnName === conflictTarget) continue;
46
+ updates[key] = value;
47
+ }
48
+ if (Object.keys(updates).length > 0) return updates;
49
+ return { [conflictTarget]: columnOf(table, conflictTarget) };
50
+ }
51
+ const MAX_CAUSE_DEPTH = 5;
52
+ function sqlStateOf(error) {
53
+ let current = error;
54
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth++) {
55
+ if (!current || typeof current !== "object") return void 0;
56
+ try {
57
+ const candidate = Reflect.get(current, "code");
58
+ if (typeof candidate === "string" && /^[0-9A-Z]{5}$/.test(candidate)) return candidate;
59
+ current = Reflect.get(current, "cause");
60
+ } catch {
61
+ return;
62
+ }
63
+ }
64
+ }
65
+ /** Classify SQLSTATE without retaining the driver error or its properties. */
66
+ function classifyDriverError(error) {
67
+ const code = sqlStateOf(error);
68
+ const category = code === "40001" || code === "40P01" ? "TRANSIENT" : code === "42501" ? "FORBIDDEN" : code?.startsWith("23") ? "CONFLICT" : "INTERNAL";
69
+ logger.error("Database driver error classified as %s (SQLSTATE %s)", category, code ?? "unknown");
70
+ return new DatabasePluginError(category, "runtime");
71
+ }
72
+ async function runDatabaseOperation(operation) {
73
+ try {
74
+ return await operation();
75
+ } catch (error) {
76
+ if (error instanceof DatabasePluginError) throw error;
77
+ throw classifyDriverError(error);
78
+ }
79
+ }
80
+ /** Apply the adapter's column capability before rows reach cardinality checks. */
81
+ function mutationRows(table, rows, access) {
82
+ if (access === "trusted") return rows;
83
+ const names = publicColumnNames(table);
84
+ return rows.map((row) => {
85
+ const projected = {};
86
+ for (const name of names) if (Object.hasOwn(row, name)) projected[name] = row[name];
87
+ return projected;
88
+ });
89
+ }
90
+ /** Resolve and validate the sole key once before a keyed operation executes. */
91
+ function validatedPrimaryKey(table, id) {
92
+ const meta = primaryKeyMeta(table);
93
+ const result = columnValueSchema(meta).safeParse(id);
94
+ if (!result.success) throw invalidDatabaseRequest(`Invalid primary-key value for "${table.$name}.${meta.columnName}"`);
95
+ return {
96
+ meta,
97
+ value: result.data
98
+ };
99
+ }
100
+ function expectExactlyOne(rows) {
101
+ if (rows.length !== 1) throw new DatabasePluginError("INTERNAL", "runtime");
102
+ return rows[0];
103
+ }
104
+ function expectZeroOrOne(rows) {
105
+ if (rows.length > 1) throw new DatabasePluginError("INTERNAL", "runtime");
106
+ return rows[0] ?? null;
107
+ }
108
+ /** Adapt a Drizzle database to AppKit's internal execution port. */
109
+ function createDrizzleDataPath(db, schema, options = {}) {
110
+ const columnAccess = options.columnAccess ?? "public";
111
+ const pgTable = (table) => {
112
+ assertRegisteredTable(schema, table);
113
+ return table.$engine;
114
+ };
115
+ const whereSql = (table, where) => where === void 0 ? void 0 : translateWhere(table, where, columnAccess);
116
+ return {
117
+ async select(table, spec) {
118
+ return runDatabaseOperation(() => relationalQueryBuilder(db, schema, table).findMany({
119
+ where: whereSql(table, spec.where),
120
+ orderBy: spec.order === void 0 ? void 0 : translateOrder(table, spec.order, columnAccess),
121
+ columns: selectedColumns(table, spec.select, columnAccess),
122
+ with: spec.include === void 0 ? void 0 : translateInclude(table, schema, spec.include, columnAccess),
123
+ limit: limitOrDefault(spec.limit),
124
+ offset: spec.offset === void 0 ? void 0 : validateOffset(spec.offset)
125
+ }));
126
+ },
127
+ async findOne(table, id, spec) {
128
+ const { meta: primaryKey, value: validatedId } = validatedPrimaryKey(table, id);
129
+ const primaryKeyPredicate = eq(columnOf(table, primaryKey.columnName), validatedId);
130
+ const additionalPredicate = spec?.where === void 0 ? void 0 : translateWhere(table, spec.where, columnAccess);
131
+ return await runDatabaseOperation(() => relationalQueryBuilder(db, schema, table).findFirst({
132
+ where: additionalPredicate === void 0 ? primaryKeyPredicate : and(primaryKeyPredicate, additionalPredicate),
133
+ columns: selectedColumns(table, spec?.select, columnAccess),
134
+ with: spec?.include === void 0 ? void 0 : translateInclude(table, schema, spec.include, columnAccess)
135
+ })) ?? null;
136
+ },
137
+ async count(table, where) {
138
+ return runDatabaseOperation(() => db.$count(pgTable(table), whereSql(table, where)));
139
+ },
140
+ async insert(table, values) {
141
+ const engineTable = pgTable(table);
142
+ const parameters = mutationValues(table, values, "insert");
143
+ return expectExactlyOne(mutationRows(table, await runDatabaseOperation(() => db.insert(engineTable).values(parameters).returning(returningColumns(table, columnAccess))), columnAccess));
144
+ },
145
+ async update(table, id, values) {
146
+ const engineTable = pgTable(table);
147
+ const parameters = mutationValues(table, values, "update");
148
+ const { meta: primaryKey, value: validatedId } = validatedPrimaryKey(table, id);
149
+ return expectZeroOrOne(mutationRows(table, await runDatabaseOperation(() => db.update(engineTable).set(parameters).where(eq(columnOf(table, primaryKey.columnName), validatedId)).returning(returningColumns(table, columnAccess))), columnAccess));
150
+ },
151
+ async upsert(table, values, onConflict) {
152
+ const engineTable = pgTable(table);
153
+ const parameters = mutationValues(table, values, "insert");
154
+ const target = conflictTargetMeta(table, onConflict);
155
+ const updates = upsertUpdateValues(table, parameters, target.columnName);
156
+ return expectExactlyOne(mutationRows(table, await runDatabaseOperation(() => db.insert(engineTable).values(parameters).onConflictDoUpdate({
157
+ target: columnOf(table, target.columnName),
158
+ set: updates
159
+ }).returning(returningColumns(table, columnAccess))), columnAccess));
160
+ },
161
+ async delete(table, id) {
162
+ const { meta: primaryKey, value: validatedId } = validatedPrimaryKey(table, id);
163
+ return expectZeroOrOne(await runDatabaseOperation(() => db.delete(pgTable(table)).where(eq(columnOf(table, primaryKey.columnName), validatedId)).returning({ id: columnOf(table, primaryKey.columnName) }))) !== null;
164
+ },
165
+ async raw(strings, ...values) {
166
+ if (values.some((value) => isSQLWrapper(value))) throw invalidDatabaseRequest("Tagged SQL interpolations must be parameter values");
167
+ const result = await runDatabaseOperation(() => db.execute(sql(strings, ...values.map((value) => sql.param(value)))));
168
+ return result.rows ?? result;
169
+ },
170
+ async transaction(callback) {
171
+ let callbackFailed = false;
172
+ let callbackError;
173
+ try {
174
+ return await db.transaction(async (transaction) => {
175
+ try {
176
+ return await callback(createDrizzleDataPath(transaction, schema, { columnAccess }));
177
+ } catch (error) {
178
+ callbackFailed = true;
179
+ callbackError = error;
180
+ throw error;
181
+ }
182
+ });
183
+ } catch (error) {
184
+ if (callbackFailed && (error === callbackError || callbackError instanceof DatabasePluginError)) throw callbackError;
185
+ if (error instanceof DatabasePluginError) throw error;
186
+ throw classifyDriverError(error);
187
+ }
188
+ }
189
+ };
190
+ }
191
+
192
+ //#endregion
193
+ export { createDrizzleDataPath, createDrizzleDb };
194
+ //# sourceMappingURL=drizzle-data-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-data-path.js","names":[],"sources":["../../../../src/database/runtime/engine/drizzle-data-path.ts"],"sourcesContent":["import { and, eq, isSQLWrapper, type SQL, sql } from \"drizzle-orm\";\nimport { drizzle, type NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { PgTable } from \"drizzle-orm/pg-core\";\nimport type { Pool } from \"pg\";\n\nimport { createLogger } from \"../../../logging/logger\";\nimport {\n type DatabaseErrorCategory,\n DatabasePluginError,\n invalidDatabaseRequest,\n} from \"../../errors\";\nimport type { AppKitTable, ColumnMeta, Schema } from \"../../schema-builder\";\nimport { buildEngineRelations } from \"../../schema-builder/engine/relations\";\nimport { columnValueSchema } from \"../../schema-builder/validators\";\nimport {\n conflictTargetMeta,\n type DataPath,\n type IdValue,\n limitOrDefault,\n primaryKeyMeta,\n type Row,\n validateOffset,\n type WhereClause,\n} from \"../data-path\";\nimport {\n columnOf,\n type ColumnAccess,\n defaultColumns,\n publicColumnNames,\n returningColumns,\n selectToColumns,\n translateInclude,\n translateOrder,\n translateWhere,\n} from \"./translate\";\n\nconst logger = createLogger(\"database\");\n\n/** Concrete Drizzle seam shared by the adapter and its focused tests. */\nexport type DrizzleDb = NodePgDatabase<Record<string, never>>;\n\nexport interface DrizzleDataPathOptions {\n /** Explicit capability for trusted server code that must read private data. */\n readonly columnAccess?: ColumnAccess;\n}\n\n/** Bind finalized AppKit metadata to a relational Drizzle database. */\nexport function createDrizzleDb(pool: Pool, schema: Schema): DrizzleDb {\n const completeSchema: Record<string, unknown> = Object.assign(\n Object.create(null),\n schema.$engine,\n buildEngineRelations(schema.$tables),\n );\n return drizzle(pool, { schema: completeSchema }) as unknown as DrizzleDb;\n}\n\ninterface RelationalQueryBuilder {\n findMany(config: Record<string, unknown>): Promise<Row[]>;\n findFirst(config: Record<string, unknown>): Promise<Row | undefined>;\n}\n\n/** Reject same-name or forged tables by requiring finalized object identity. */\nfunction assertRegisteredTable(schema: Schema, table: AppKitTable): void {\n if (schema.$tables[table.$name] !== table) {\n throw invalidDatabaseRequest(`Table \"${table.$name}\" is not registered`);\n }\n}\n\nfunction relationalQueryBuilder(\n db: DrizzleDb,\n schema: Schema,\n table: AppKitTable,\n): RelationalQueryBuilder {\n assertRegisteredTable(schema, table);\n const query = (db.query as unknown as Record<string, RelationalQueryBuilder>)[\n table.$name\n ];\n if (!query) {\n throw invalidDatabaseRequest(`Table \"${table.$name}\" is not registered`);\n }\n return query;\n}\n\nfunction selectedColumns(\n table: AppKitTable,\n select?: readonly string[],\n access: ColumnAccess = \"public\",\n): Record<string, true> {\n return select === undefined\n ? defaultColumns(table)\n : selectToColumns(table, select, access);\n}\n\ntype MutationKind = \"insert\" | \"update\";\n\ninterface MutationSchema {\n safeParse(\n value: unknown,\n ):\n | { readonly success: true; readonly data: unknown }\n | { readonly success: false };\n}\n\n/** Apply the finalized write schema again at the persistence boundary. */\nfunction mutationValues(\n table: AppKitTable,\n values: Row,\n kind: MutationKind,\n): Row {\n if (values === null || typeof values !== \"object\" || Array.isArray(values)) {\n throw invalidDatabaseRequest(\"Database mutation values must be an object\");\n }\n for (const [key, value] of Object.entries(values)) {\n if (!Object.hasOwn(table.$columns, key)) {\n throw invalidDatabaseRequest(`Unknown column \"${table.$name}.${key}\"`);\n }\n if (isSQLWrapper(value)) {\n throw invalidDatabaseRequest(\n \"Database mutation values cannot contain SQL\",\n );\n }\n }\n const schema = (\n kind === \"insert\" ? table.$insertSchema : table.$updateSchema\n ) as MutationSchema;\n const result = schema.safeParse(values);\n if (!result.success) {\n throw invalidDatabaseRequest(`Invalid database ${kind} values`);\n }\n return result.data as Row;\n}\n\n/** Never change row identity or the conflict key during the update half. */\nfunction upsertUpdateValues(\n table: AppKitTable,\n parameters: Row,\n conflictTarget: string,\n): Row {\n const updates: Row = {};\n for (const [key, value] of Object.entries(parameters)) {\n const column = table.$columns[key];\n if (\n column.primaryKey ||\n column.serverGenerated ||\n column.columnName === conflictTarget\n ) {\n continue;\n }\n updates[key] = value;\n }\n if (Object.keys(updates).length > 0) return updates;\n\n // A safe self-assignment preserves the exactly-one-row upsert contract.\n return { [conflictTarget]: columnOf(table, conflictTarget) };\n}\n\n// Drizzle wraps driver failures in DrizzleQueryError, so the SQLSTATE sits on a\n// nested `cause` rather than the thrown error. Walk a bounded chain to find it.\nconst MAX_CAUSE_DEPTH = 5;\n\nfunction sqlStateOf(error: unknown): string | undefined {\n let current = error;\n for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth++) {\n if (!current || typeof current !== \"object\") return undefined;\n try {\n const candidate = Reflect.get(current, \"code\");\n // SQLSTATE is always a five-character alphanumeric class code.\n if (typeof candidate === \"string\" && /^[0-9A-Z]{5}$/.test(candidate)) {\n return candidate;\n }\n current = Reflect.get(current, \"cause\");\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\n/** Classify SQLSTATE without retaining the driver error or its properties. */\nfunction classifyDriverError(error: unknown): DatabasePluginError {\n const code = sqlStateOf(error);\n const category: DatabaseErrorCategory =\n code === \"40001\" || code === \"40P01\"\n ? \"TRANSIENT\"\n : code === \"42501\"\n ? \"FORBIDDEN\"\n : code?.startsWith(\"23\")\n ? \"CONFLICT\"\n : \"INTERNAL\";\n logger.error(\n \"Database driver error classified as %s (SQLSTATE %s)\",\n category,\n code ?? \"unknown\",\n );\n return new DatabasePluginError(category, \"runtime\");\n}\n\nasync function runDatabaseOperation<T>(\n operation: () => Promise<T>,\n): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof DatabasePluginError) throw error;\n throw classifyDriverError(error);\n }\n}\n\n/** Apply the adapter's column capability before rows reach cardinality checks. */\nfunction mutationRows(\n table: AppKitTable,\n rows: Row[],\n access: ColumnAccess,\n): Row[] {\n if (access === \"trusted\") return rows;\n const names = publicColumnNames(table);\n return rows.map((row) => {\n const projected: Row = {};\n for (const name of names) {\n if (Object.hasOwn(row, name)) projected[name] = row[name];\n }\n return projected;\n });\n}\n\n/** Resolve and validate the sole key once before a keyed operation executes. */\nfunction validatedPrimaryKey(\n table: AppKitTable,\n id: unknown,\n): { readonly meta: ColumnMeta; readonly value: IdValue } {\n const meta = primaryKeyMeta(table);\n const result = columnValueSchema(meta).safeParse(id);\n if (!result.success) {\n throw invalidDatabaseRequest(\n `Invalid primary-key value for \"${table.$name}.${meta.columnName}\"`,\n );\n }\n return { meta, value: result.data as IdValue };\n}\n\n// Enforce the single-row DataPath contract before results reach callers.\nfunction expectExactlyOne(rows: Row[]): Row {\n if (rows.length !== 1) {\n throw new DatabasePluginError(\"INTERNAL\", \"runtime\");\n }\n return rows[0];\n}\n\nfunction expectZeroOrOne(rows: Row[]): Row | null {\n if (rows.length > 1) {\n throw new DatabasePluginError(\"INTERNAL\", \"runtime\");\n }\n return rows[0] ?? null;\n}\n\n/** Adapt a Drizzle database to AppKit's internal execution port. */\nexport function createDrizzleDataPath(\n db: DrizzleDb,\n schema: Schema,\n options: DrizzleDataPathOptions = {},\n): DataPath {\n const columnAccess = options.columnAccess ?? \"public\";\n const pgTable = (table: AppKitTable): PgTable => {\n assertRegisteredTable(schema, table);\n return table.$engine as unknown as PgTable;\n };\n const whereSql = (\n table: AppKitTable,\n where?: WhereClause,\n ): SQL | undefined =>\n where === undefined\n ? undefined\n : translateWhere(table, where, columnAccess);\n\n return {\n async select(table, spec) {\n return runDatabaseOperation(() =>\n relationalQueryBuilder(db, schema, table).findMany({\n where: whereSql(table, spec.where),\n orderBy:\n spec.order === undefined\n ? undefined\n : translateOrder(table, spec.order, columnAccess),\n columns: selectedColumns(table, spec.select, columnAccess),\n with:\n spec.include === undefined\n ? undefined\n : translateInclude(table, schema, spec.include, columnAccess),\n limit: limitOrDefault(spec.limit),\n offset:\n spec.offset === undefined ? undefined : validateOffset(spec.offset),\n }),\n );\n },\n\n async findOne(table, id, spec) {\n const { meta: primaryKey, value: validatedId } = validatedPrimaryKey(\n table,\n id,\n );\n const primaryKeyPredicate = eq(\n columnOf(table, primaryKey.columnName),\n validatedId,\n );\n const additionalPredicate =\n spec?.where === undefined\n ? undefined\n : translateWhere(table, spec.where, columnAccess);\n const row = await runDatabaseOperation(() =>\n relationalQueryBuilder(db, schema, table).findFirst({\n where:\n additionalPredicate === undefined\n ? primaryKeyPredicate\n : and(primaryKeyPredicate, additionalPredicate),\n columns: selectedColumns(table, spec?.select, columnAccess),\n with:\n spec?.include === undefined\n ? undefined\n : translateInclude(table, schema, spec.include, columnAccess),\n }),\n );\n return row ?? null;\n },\n\n async count(table, where) {\n return runDatabaseOperation(() =>\n db.$count(pgTable(table), whereSql(table, where)),\n );\n },\n\n async insert(table, values) {\n const engineTable = pgTable(table);\n const parameters = mutationValues(table, values, \"insert\");\n const rows = await runDatabaseOperation(() =>\n db\n .insert(engineTable)\n .values(parameters)\n .returning(returningColumns(table, columnAccess)),\n );\n return expectExactlyOne(mutationRows(table, rows as Row[], columnAccess));\n },\n\n async update(table, id, values) {\n const engineTable = pgTable(table);\n const parameters = mutationValues(table, values, \"update\");\n const { meta: primaryKey, value: validatedId } = validatedPrimaryKey(\n table,\n id,\n );\n const rows = await runDatabaseOperation(() =>\n db\n .update(engineTable)\n .set(parameters)\n .where(eq(columnOf(table, primaryKey.columnName), validatedId))\n .returning(returningColumns(table, columnAccess)),\n );\n return expectZeroOrOne(mutationRows(table, rows as Row[], columnAccess));\n },\n\n async upsert(table, values, onConflict) {\n const engineTable = pgTable(table);\n const parameters = mutationValues(table, values, \"insert\");\n const target = conflictTargetMeta(table, onConflict);\n const updates = upsertUpdateValues(table, parameters, target.columnName);\n const rows = await runDatabaseOperation(() =>\n db\n .insert(engineTable)\n .values(parameters)\n .onConflictDoUpdate({\n target: columnOf(table, target.columnName),\n set: updates,\n })\n .returning(returningColumns(table, columnAccess)),\n );\n return expectExactlyOne(mutationRows(table, rows as Row[], columnAccess));\n },\n\n async delete(table, id) {\n const { meta: primaryKey, value: validatedId } = validatedPrimaryKey(\n table,\n id,\n );\n const rows = await runDatabaseOperation(() =>\n db\n .delete(pgTable(table))\n .where(eq(columnOf(table, primaryKey.columnName), validatedId))\n .returning({ id: columnOf(table, primaryKey.columnName) }),\n );\n return expectZeroOrOne(rows as Row[]) !== null;\n },\n\n async raw<T = Row>(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): Promise<T[]> {\n // SQL wrappers carry structure; tagged interpolations may carry values only.\n if (values.some((value) => isSQLWrapper(value))) {\n throw invalidDatabaseRequest(\n \"Tagged SQL interpolations must be parameter values\",\n );\n }\n const result = await runDatabaseOperation(() =>\n db.execute(sql(strings, ...values.map((value) => sql.param(value)))),\n );\n return ((result as { rows?: unknown }).rows ?? result) as T[];\n },\n\n async transaction<T>(callback: (tx: DataPath) => Promise<T>): Promise<T> {\n let callbackFailed = false;\n let callbackError: unknown;\n try {\n return await db.transaction(async (transaction) => {\n try {\n return await callback(\n createDrizzleDataPath(\n transaction as unknown as DrizzleDb,\n schema,\n { columnAccess },\n ),\n );\n } catch (error) {\n callbackFailed = true;\n callbackError = error;\n throw error;\n }\n });\n } catch (error) {\n // Callback errors are application-owned; sanitize only tx lifecycle errors.\n if (\n callbackFailed &&\n (error === callbackError ||\n callbackError instanceof DatabasePluginError)\n ) {\n throw callbackError;\n }\n if (error instanceof DatabasePluginError) throw error;\n throw classifyDriverError(error);\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,176 @@
1
+ import { filterOperatorsForKind } from "../../schema-builder/types.js";
2
+ import { columnValueSchema } from "../../schema-builder/validators.js";
3
+ import { invalidDatabaseRequest } from "../../errors.js";
4
+ import { DEFAULT_LIMIT, IN_CAP, MAX_INCLUDES, MAX_WHERE_CONDITIONS, MAX_WHERE_DEPTH, MAX_WHERE_GROUP_ITEMS, isFilterOperator } from "../../contract/wire.js";
5
+ import "../../contract/index.js";
6
+ import { validateLimit } from "../data-path.js";
7
+ import { and, asc, desc, eq, gt, gte, ilike, inArray, isNull, like, lt, lte, ne, or, sql } from "drizzle-orm";
8
+
9
+ //#region src/database/runtime/engine/translate.ts
10
+ function columnMetaOf(table, key) {
11
+ const column = table.$columns[key];
12
+ if (!column) throw invalidDatabaseRequest(`Unknown column "${table.$name}.${key}"`);
13
+ return column;
14
+ }
15
+ function readableColumnMeta(table, key, access) {
16
+ const column = columnMetaOf(table, key);
17
+ if (column.isPrivate && access !== "trusted") throw invalidDatabaseRequest(`Column "${table.$name}.${key}" is not readable`);
18
+ return column;
19
+ }
20
+ /** Resolve SQL identifiers only through columns finalized by the schema builder. */
21
+ function columnOf(table, key) {
22
+ return columnMetaOf(table, key).engineColumn;
23
+ }
24
+ /** Column names that default reads and mutation returns may expose. */
25
+ function publicColumnNames(table) {
26
+ return Object.values(table.$columns).filter((column) => !column.isPrivate).map((column) => column.columnName);
27
+ }
28
+ /** Default reads select all finalized columns except private application data. */
29
+ function defaultColumns(table) {
30
+ const columns = {};
31
+ for (const name of publicColumnNames(table)) columns[name] = true;
32
+ return columns;
33
+ }
34
+ /** Drizzle `.returning()` fields allowed by the adapter's read capability. */
35
+ function returningColumns(table, access = "public") {
36
+ const columns = {};
37
+ const names = access === "trusted" ? Object.values(table.$columns).map((column) => column.columnName) : publicColumnNames(table);
38
+ for (const name of names) columns[name] = columnOf(table, name);
39
+ return columns;
40
+ }
41
+ function supportsOperator(meta, operator) {
42
+ if (operator === "is") return !meta.notNull && meta.kind !== "json" && meta.kind !== "unknown";
43
+ return filterOperatorsForKind(meta.kind).includes(operator);
44
+ }
45
+ function assertColumnValue(table, meta, operator, value) {
46
+ if (!columnValueSchema(meta).safeParse(value).success) throw invalidDatabaseRequest(`Invalid ${operator} operand for "${table.$name}.${meta.columnName}"`);
47
+ }
48
+ function inList(table, meta, column, value) {
49
+ if (!Array.isArray(value)) throw invalidDatabaseRequest("The \"in\" operator requires an array");
50
+ if (value.length > IN_CAP) throw invalidDatabaseRequest(`in list exceeds the ${IN_CAP}-value limit`);
51
+ for (const item of value) {
52
+ if (item === null) throw invalidDatabaseRequest("The \"in\" operator does not accept null");
53
+ assertColumnValue(table, meta, "in", item);
54
+ }
55
+ return value.length === 0 ? sql.raw("false") : inArray(column, value);
56
+ }
57
+ function translateOperator(table, meta, column, operator, value) {
58
+ if (!supportsOperator(meta, operator)) throw invalidDatabaseRequest(`Operator "${operator}" is not supported for "${table.$name}.${meta.columnName}"`);
59
+ if (operator === "is") {
60
+ if (value !== null) throw invalidDatabaseRequest("The \"is\" operator accepts only null");
61
+ return isNull(column);
62
+ }
63
+ if (operator === "in") return inList(table, meta, column, value);
64
+ assertColumnValue(table, meta, operator, value);
65
+ switch (operator) {
66
+ case "eq": return eq(column, value);
67
+ case "neq": return ne(column, value);
68
+ case "gt": return gt(column, value);
69
+ case "gte": return gte(column, value);
70
+ case "lt": return lt(column, value);
71
+ case "lte": return lte(column, value);
72
+ case "like": return like(column, value);
73
+ case "ilike": return ilike(column, value);
74
+ default: throw invalidDatabaseRequest(`Unsupported filter operator "${operator}"`);
75
+ }
76
+ }
77
+ function chargeWhereCondition(budget) {
78
+ budget.conditions += 1;
79
+ if (budget.conditions > MAX_WHERE_CONDITIONS) throw invalidDatabaseRequest(`where exceeds the ${MAX_WHERE_CONDITIONS}-condition limit`);
80
+ }
81
+ function translateWhereNode(table, clause, access, depth, budget) {
82
+ if (depth > MAX_WHERE_DEPTH) throw invalidDatabaseRequest(`where exceeds the ${MAX_WHERE_DEPTH}-level depth limit`);
83
+ if (clause === null || typeof clause !== "object" || Array.isArray(clause)) throw invalidDatabaseRequest("where must be an object");
84
+ const conditions = [];
85
+ for (const [key, value] of Object.entries(clause)) {
86
+ if (key === "and" || key === "or") {
87
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_WHERE_GROUP_ITEMS) {
88
+ if (Array.isArray(value) && value.length > MAX_WHERE_GROUP_ITEMS) throw invalidDatabaseRequest(`${key} exceeds the ${MAX_WHERE_GROUP_ITEMS}-predicate limit`);
89
+ throw invalidDatabaseRequest(`${key} requires a non-empty predicate array`);
90
+ }
91
+ const groups = value.map((group) => {
92
+ const translated = translateWhereNode(table, group, access, depth + 1, budget);
93
+ if (!translated) throw invalidDatabaseRequest(`${key} predicates cannot be empty`);
94
+ return translated;
95
+ });
96
+ const combined = key === "and" ? and(...groups) : or(...groups);
97
+ if (combined) conditions.push(combined);
98
+ continue;
99
+ }
100
+ const meta = readableColumnMeta(table, key, access);
101
+ const column = meta.engineColumn;
102
+ if (Array.isArray(value)) {
103
+ chargeWhereCondition(budget);
104
+ conditions.push(translateOperator(table, meta, column, "in", value));
105
+ } else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
106
+ const operators = Object.entries(value);
107
+ if (operators.length === 0) throw invalidDatabaseRequest(`Filter for "${table.$name}.${key}" cannot be empty`);
108
+ for (const [operator, operand] of operators) {
109
+ chargeWhereCondition(budget);
110
+ if (!isFilterOperator(operator)) throw invalidDatabaseRequest(`Unknown filter operator "${operator}"`);
111
+ conditions.push(translateOperator(table, meta, column, operator, operand));
112
+ }
113
+ } else {
114
+ chargeWhereCondition(budget);
115
+ conditions.push(translateOperator(table, meta, column, "eq", value));
116
+ }
117
+ }
118
+ return conditions.length > 0 ? and(...conditions) : void 0;
119
+ }
120
+ /** Translate one bounded direct-column predicate tree. */
121
+ function translateWhere(table, clause, access = "public") {
122
+ return translateWhereNode(table, clause, access, 1, { conditions: 0 });
123
+ }
124
+ function translateOrder(table, order, access = "public") {
125
+ return Object.entries(order).map(([key, direction]) => {
126
+ if (direction !== "asc" && direction !== "desc") throw invalidDatabaseRequest(`Unknown order direction "${direction}"`);
127
+ const column = readableColumnMeta(table, key, access).engineColumn;
128
+ return direction === "desc" ? desc(column) : asc(column);
129
+ });
130
+ }
131
+ function selectToColumns(table, select, access = "public") {
132
+ const columns = {};
133
+ for (const key of select) {
134
+ readableColumnMeta(table, key, access);
135
+ columns[key] = true;
136
+ }
137
+ return columns;
138
+ }
139
+ function tableByName(schema, name) {
140
+ const table = schema.$tables[name];
141
+ if (!table) throw invalidDatabaseRequest(`Unknown table "${name}"`);
142
+ return table;
143
+ }
144
+ /** Translate one relation edge into Drizzle's relational `with` config. */
145
+ function translateInclude(table, schema, include, access = "public") {
146
+ const entries = Object.entries(include);
147
+ if (entries.length > MAX_INCLUDES) throw invalidDatabaseRequest(`include exceeds the ${MAX_INCLUDES}-relation limit`);
148
+ const config = {};
149
+ for (const [relationName, rawOptions] of entries) {
150
+ const relation = table.$relations.find((candidate) => candidate.name === relationName);
151
+ if (!relation) throw invalidDatabaseRequest(`Unknown relation "${table.$name}.${relationName}"`);
152
+ if (rawOptions === false) continue;
153
+ const target = tableByName(schema, relation.targetTable);
154
+ if (rawOptions === true) {
155
+ config[relationName] = {
156
+ columns: defaultColumns(target),
157
+ ...relation.cardinality === "toMany" ? { limit: DEFAULT_LIMIT } : {}
158
+ };
159
+ continue;
160
+ }
161
+ const options = rawOptions;
162
+ const relationConfig = { columns: options.select === void 0 ? defaultColumns(target) : selectToColumns(target, options.select, access) };
163
+ if (options.where !== void 0) relationConfig.where = translateWhere(target, options.where, access);
164
+ if (options.order !== void 0) relationConfig.orderBy = translateOrder(target, options.order, access);
165
+ if (options.limit !== void 0) {
166
+ if (relation.cardinality !== "toMany") throw invalidDatabaseRequest("Only to-many relations accept a limit");
167
+ relationConfig.limit = validateLimit(options.limit);
168
+ } else if (relation.cardinality === "toMany") relationConfig.limit = DEFAULT_LIMIT;
169
+ config[relationName] = relationConfig;
170
+ }
171
+ return config;
172
+ }
173
+
174
+ //#endregion
175
+ export { columnOf, defaultColumns, publicColumnNames, returningColumns, selectToColumns, translateInclude, translateOrder, translateWhere };
176
+ //# sourceMappingURL=translate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.js","names":[],"sources":["../../../../src/database/runtime/engine/translate.ts"],"sourcesContent":["import {\n and,\n asc,\n desc,\n eq,\n gt,\n gte,\n ilike,\n inArray,\n isNull,\n like,\n lt,\n lte,\n ne,\n or,\n type SQL,\n sql,\n} from \"drizzle-orm\";\nimport type { AnyPgColumn } from \"drizzle-orm/pg-core\";\n\nimport {\n DEFAULT_LIMIT,\n type FilterOperator,\n IN_CAP,\n isFilterOperator,\n MAX_INCLUDES,\n MAX_WHERE_CONDITIONS,\n MAX_WHERE_DEPTH,\n MAX_WHERE_GROUP_ITEMS,\n} from \"../../contract\";\nimport { invalidDatabaseRequest } from \"../../errors\";\nimport type { AppKitTable, ColumnMeta, Schema } from \"../../schema-builder\";\nimport { filterOperatorsForKind } from \"../../schema-builder/types\";\nimport { columnValueSchema } from \"../../schema-builder/validators\";\nimport {\n type FilterOps,\n type IncludeOptions,\n type IncludeSpec,\n type OrderSpec,\n validateLimit,\n type WhereClause,\n} from \"../data-path\";\n\n/** Read policy selected by the trusted adapter constructor, never by query input. */\nexport type ColumnAccess = \"public\" | \"trusted\";\n\nfunction columnMetaOf(table: AppKitTable, key: string): ColumnMeta {\n const column = table.$columns[key];\n if (!column) {\n throw invalidDatabaseRequest(`Unknown column \"${table.$name}.${key}\"`);\n }\n return column;\n}\n\nfunction readableColumnMeta(\n table: AppKitTable,\n key: string,\n access: ColumnAccess,\n): ColumnMeta {\n const column = columnMetaOf(table, key);\n if (column.isPrivate && access !== \"trusted\") {\n throw invalidDatabaseRequest(\n `Column \"${table.$name}.${key}\" is not readable`,\n );\n }\n return column;\n}\n\n/** Resolve SQL identifiers only through columns finalized by the schema builder. */\nexport function columnOf(table: AppKitTable, key: string): AnyPgColumn {\n return columnMetaOf(table, key).engineColumn as unknown as AnyPgColumn;\n}\n\n/** Column names that default reads and mutation returns may expose. */\nexport function publicColumnNames(table: AppKitTable): string[] {\n return Object.values(table.$columns)\n .filter((column) => !column.isPrivate)\n .map((column) => column.columnName);\n}\n\n/** Default reads select all finalized columns except private application data. */\nexport function defaultColumns(table: AppKitTable): Record<string, true> {\n const columns: Record<string, true> = {};\n for (const name of publicColumnNames(table)) columns[name] = true;\n return columns;\n}\n\n/** Drizzle `.returning()` fields allowed by the adapter's read capability. */\nexport function returningColumns(\n table: AppKitTable,\n access: ColumnAccess = \"public\",\n): Record<string, AnyPgColumn> {\n const columns: Record<string, AnyPgColumn> = {};\n const names =\n access === \"trusted\"\n ? Object.values(table.$columns).map((column) => column.columnName)\n : publicColumnNames(table);\n for (const name of names) {\n columns[name] = columnOf(table, name);\n }\n return columns;\n}\n\nfunction supportsOperator(meta: ColumnMeta, operator: FilterOperator): boolean {\n if (operator === \"is\") {\n return !meta.notNull && meta.kind !== \"json\" && meta.kind !== \"unknown\";\n }\n return filterOperatorsForKind(meta.kind).includes(operator);\n}\n\nfunction assertColumnValue(\n table: AppKitTable,\n meta: ColumnMeta,\n operator: FilterOperator,\n value: unknown,\n): void {\n if (!columnValueSchema(meta).safeParse(value).success) {\n throw invalidDatabaseRequest(\n `Invalid ${operator} operand for \"${table.$name}.${meta.columnName}\"`,\n );\n }\n}\n\nfunction inList(\n table: AppKitTable,\n meta: ColumnMeta,\n column: AnyPgColumn,\n value: unknown,\n): SQL {\n if (!Array.isArray(value)) {\n throw invalidDatabaseRequest('The \"in\" operator requires an array');\n }\n if (value.length > IN_CAP) {\n throw invalidDatabaseRequest(`in list exceeds the ${IN_CAP}-value limit`);\n }\n for (const item of value) {\n if (item === null) {\n throw invalidDatabaseRequest('The \"in\" operator does not accept null');\n }\n assertColumnValue(table, meta, \"in\", item);\n }\n return value.length === 0 ? sql.raw(\"false\") : inArray(column, value);\n}\n\nfunction translateOperator(\n table: AppKitTable,\n meta: ColumnMeta,\n column: AnyPgColumn,\n operator: FilterOperator,\n value: unknown,\n): SQL {\n if (!supportsOperator(meta, operator)) {\n throw invalidDatabaseRequest(\n `Operator \"${operator}\" is not supported for \"${table.$name}.${meta.columnName}\"`,\n );\n }\n if (operator === \"is\") {\n if (value !== null) {\n throw invalidDatabaseRequest('The \"is\" operator accepts only null');\n }\n return isNull(column);\n }\n if (operator === \"in\") return inList(table, meta, column, value);\n\n assertColumnValue(table, meta, operator, value);\n switch (operator) {\n case \"eq\":\n return eq(column, value);\n case \"neq\":\n return ne(column, value);\n case \"gt\":\n return gt(column, value);\n case \"gte\":\n return gte(column, value);\n case \"lt\":\n return lt(column, value);\n case \"lte\":\n return lte(column, value);\n case \"like\":\n return like(column, value as string);\n case \"ilike\":\n return ilike(column, value as string);\n default:\n throw invalidDatabaseRequest(`Unsupported filter operator \"${operator}\"`);\n }\n}\n\ninterface WhereBudget {\n conditions: number;\n}\n\nfunction chargeWhereCondition(budget: WhereBudget): void {\n budget.conditions += 1;\n if (budget.conditions > MAX_WHERE_CONDITIONS) {\n throw invalidDatabaseRequest(\n `where exceeds the ${MAX_WHERE_CONDITIONS}-condition limit`,\n );\n }\n}\n\nfunction translateWhereNode(\n table: AppKitTable,\n clause: WhereClause,\n access: ColumnAccess,\n depth: number,\n budget: WhereBudget,\n): SQL | undefined {\n if (depth > MAX_WHERE_DEPTH) {\n throw invalidDatabaseRequest(\n `where exceeds the ${MAX_WHERE_DEPTH}-level depth limit`,\n );\n }\n if (clause === null || typeof clause !== \"object\" || Array.isArray(clause)) {\n throw invalidDatabaseRequest(\"where must be an object\");\n }\n const conditions: SQL[] = [];\n for (const [key, value] of Object.entries(clause)) {\n if (key === \"and\" || key === \"or\") {\n if (\n !Array.isArray(value) ||\n value.length === 0 ||\n value.length > MAX_WHERE_GROUP_ITEMS\n ) {\n if (Array.isArray(value) && value.length > MAX_WHERE_GROUP_ITEMS) {\n throw invalidDatabaseRequest(\n `${key} exceeds the ${MAX_WHERE_GROUP_ITEMS}-predicate limit`,\n );\n }\n throw invalidDatabaseRequest(\n `${key} requires a non-empty predicate array`,\n );\n }\n const groups = value.map((group) => {\n const translated = translateWhereNode(\n table,\n group as WhereClause,\n access,\n depth + 1,\n budget,\n );\n if (!translated) {\n throw invalidDatabaseRequest(`${key} predicates cannot be empty`);\n }\n return translated;\n });\n const combined = key === \"and\" ? and(...groups) : or(...groups);\n if (combined) {\n conditions.push(combined);\n }\n continue;\n }\n\n const meta = readableColumnMeta(table, key, access);\n const column = meta.engineColumn as unknown as AnyPgColumn;\n if (Array.isArray(value)) {\n chargeWhereCondition(budget);\n conditions.push(translateOperator(table, meta, column, \"in\", value));\n } else if (\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value)\n ) {\n const operators = Object.entries(value as FilterOps);\n if (operators.length === 0) {\n throw invalidDatabaseRequest(\n `Filter for \"${table.$name}.${key}\" cannot be empty`,\n );\n }\n for (const [operator, operand] of operators) {\n chargeWhereCondition(budget);\n if (!isFilterOperator(operator)) {\n throw invalidDatabaseRequest(`Unknown filter operator \"${operator}\"`);\n }\n conditions.push(\n translateOperator(table, meta, column, operator, operand),\n );\n }\n } else {\n chargeWhereCondition(budget);\n conditions.push(translateOperator(table, meta, column, \"eq\", value));\n }\n }\n return conditions.length > 0 ? and(...conditions) : undefined;\n}\n\n/** Translate one bounded direct-column predicate tree. */\nexport function translateWhere(\n table: AppKitTable,\n clause: WhereClause,\n access: ColumnAccess = \"public\",\n): SQL | undefined {\n return translateWhereNode(table, clause, access, 1, { conditions: 0 });\n}\n\nexport function translateOrder(\n table: AppKitTable,\n order: OrderSpec,\n access: ColumnAccess = \"public\",\n): SQL[] {\n return Object.entries(order).map(([key, direction]) => {\n if (direction !== \"asc\" && direction !== \"desc\") {\n throw invalidDatabaseRequest(`Unknown order direction \"${direction}\"`);\n }\n const column = readableColumnMeta(table, key, access)\n .engineColumn as unknown as AnyPgColumn;\n return direction === \"desc\" ? desc(column) : asc(column);\n });\n}\n\nexport function selectToColumns(\n table: AppKitTable,\n select: readonly string[],\n access: ColumnAccess = \"public\",\n): Record<string, true> {\n const columns: Record<string, true> = {};\n for (const key of select) {\n readableColumnMeta(table, key, access);\n columns[key] = true;\n }\n return columns;\n}\n\nfunction tableByName(schema: Schema, name: string): AppKitTable {\n const table = schema.$tables[name];\n if (!table) throw invalidDatabaseRequest(`Unknown table \"${name}\"`);\n return table;\n}\n\n/** Translate one relation edge into Drizzle's relational `with` config. */\nexport function translateInclude(\n table: AppKitTable,\n schema: Schema,\n include: IncludeSpec,\n access: ColumnAccess = \"public\",\n): Record<string, unknown> {\n const entries = Object.entries(include);\n if (entries.length > MAX_INCLUDES) {\n throw invalidDatabaseRequest(\n `include exceeds the ${MAX_INCLUDES}-relation limit`,\n );\n }\n\n const config: Record<string, unknown> = {};\n for (const [relationName, rawOptions] of entries) {\n const relation = table.$relations.find(\n (candidate) => candidate.name === relationName,\n );\n if (!relation) {\n throw invalidDatabaseRequest(\n `Unknown relation \"${table.$name}.${relationName}\"`,\n );\n }\n if (rawOptions === false) continue;\n\n const target = tableByName(schema, relation.targetTable);\n if (rawOptions === true) {\n config[relationName] = {\n columns: defaultColumns(target),\n ...(relation.cardinality === \"toMany\" ? { limit: DEFAULT_LIMIT } : {}),\n };\n continue;\n }\n\n const options = rawOptions as IncludeOptions;\n const relationConfig: Record<string, unknown> = {\n columns:\n options.select === undefined\n ? defaultColumns(target)\n : selectToColumns(target, options.select, access),\n };\n if (options.where !== undefined) {\n relationConfig.where = translateWhere(target, options.where, access);\n }\n if (options.order !== undefined) {\n relationConfig.orderBy = translateOrder(target, options.order, access);\n }\n if (options.limit !== undefined) {\n if (relation.cardinality !== \"toMany\") {\n throw invalidDatabaseRequest(\"Only to-many relations accept a limit\");\n }\n relationConfig.limit = validateLimit(options.limit);\n } else if (relation.cardinality === \"toMany\") {\n relationConfig.limit = DEFAULT_LIMIT;\n }\n config[relationName] = relationConfig;\n }\n return config;\n}\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { ColumnTypeSpec, ColumnValueKind, MutableColumnMeta, ReferentialAction } from "./types.js";
2
+
3
+ //#region src/database/schema-builder/columns.d.ts
4
+ /** Mutable DSL builder; table() clones its metadata before finalization. */
5
+ declare class ColumnBuilder {
6
+ /** @internal */
7
+ readonly _meta: MutableColumnMeta;
8
+ private readonly declarationKind;
9
+ constructor(spec: ColumnTypeSpec, kind: ColumnValueKind);
10
+ /** @internal Clone declaration state so builder reuse cannot mutate a table. */
11
+ _cloneMeta(): MutableColumnMeta;
12
+ notNull(): this;
13
+ primaryKey(): this;
14
+ unique(): this;
15
+ private(): this;
16
+ default(value: string | number | boolean): this;
17
+ defaultNow(): this;
18
+ defaultRandom(): this;
19
+ onDelete(action: ReferentialAction): this;
20
+ onUpdate(action: ReferentialAction): this;
21
+ private requireNoDefault;
22
+ private requireFk;
23
+ }
24
+ declare const id: () => ColumnBuilder;
25
+ declare const bigid: () => ColumnBuilder;
26
+ declare const text: () => ColumnBuilder;
27
+ declare const varchar: (length?: number) => ColumnBuilder;
28
+ declare const integer: () => ColumnBuilder;
29
+ declare const bigint: () => ColumnBuilder;
30
+ declare const boolean: () => ColumnBuilder;
31
+ declare const uuid: () => ColumnBuilder;
32
+ declare const timestamp: (opts?: {
33
+ withTimezone?: boolean;
34
+ }) => ColumnBuilder;
35
+ declare const jsonb: () => ColumnBuilder;
36
+ declare function enumColumn(name: string, values: readonly string[]): ColumnBuilder;
37
+ //#endregion
38
+ export { ColumnBuilder, bigid, bigint, boolean, enumColumn, id, integer, jsonb, text, timestamp, uuid, varchar };
39
+ //# sourceMappingURL=columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columns.d.ts","names":[],"sources":["../../../src/database/schema-builder/columns.ts"],"mappings":";;;;cA4Fa,aAAA;EA+BG;EAAA,SA9BY,KAAA,EAAO,iBAAA;EAAA,iBAChB,eAAA;cAEL,IAAA,EAAM,cAAA,EAAgB,IAAA,EAAM,eAAA;EAgGN;EArElC,UAAA,CAAA,GAAc,iBAAA;EAUd,OAAA,CAAA;EAKA,UAAA,CAAA;EAMA,MAAA,CAAA;EAKA,OAAA,CAAA;EAKA,OAAA,CAAQ,KAAA;EAOR,UAAA,CAAA;EAYA,aAAA,CAAA;EAYA,QAAA,CAAS,MAAA,EAAQ,iBAAA;EAOjB,QAAA,CAAS,MAAA,EAAQ,iBAAA;EAAA,QAOT,gBAAA;EAAA,QAMA,SAAA;AAAA;AAAA,cASG,EAAA,QAAE,aAAA;AAAA,cACF,KAAA,QAAK,aAAA;AAAA,cACL,IAAA,QAAI,aAAA;AAAA,cACJ,OAAA,GAAW,MAAA,cAAY,aAAA;AAAA,cAIvB,OAAA,QAAO,aAAA;AAAA,cACP,MAAA,QAAM,aAAA;AAAA,cACN,OAAA,QAAO,aAAA;AAAA,cACP,IAAA,QAAI,aAAA;AAAA,cACJ,SAAA,GAAa,IAAA;EAAS,YAAA;AAAA,MAAwB,aAAA;AAAA,cAI9C,KAAA,QAAK,aAAA;AAAA,iBAEF,UAAA,CAAW,IAAA,UAAc,MAAA,sBAAyB,aAAA"}