@databricks/appkit 0.66.1 → 0.68.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.
- package/CLAUDE.md +2 -2
- package/dist/appkit/package.js +1 -1
- package/dist/core/agent/frontmatter.js +23 -0
- package/dist/core/agent/frontmatter.js.map +1 -0
- package/dist/core/agent/load-agents.d.ts.map +1 -1
- package/dist/core/agent/load-agents.js +36 -6
- package/dist/core/agent/load-agents.js.map +1 -1
- package/dist/core/agent/skills/index.js +7 -0
- package/dist/core/agent/skills/load-skills.js +78 -0
- package/dist/core/agent/skills/load-skills.js.map +1 -0
- package/dist/core/agent/skills/parse-skill.js +69 -0
- package/dist/core/agent/skills/parse-skill.js.map +1 -0
- package/dist/core/agent/skills/read-resource.js +31 -0
- package/dist/core/agent/skills/read-resource.js.map +1 -0
- package/dist/core/agent/skills/render.js +33 -0
- package/dist/core/agent/skills/render.js.map +1 -0
- package/dist/core/agent/skills/resolve-catalog.js +78 -0
- package/dist/core/agent/skills/resolve-catalog.js.map +1 -0
- package/dist/core/agent/skills/types.d.ts +50 -0
- package/dist/core/agent/skills/types.d.ts.map +1 -0
- package/dist/core/agent/types.d.ts +47 -0
- package/dist/core/agent/types.d.ts.map +1 -1
- package/dist/core/agent/types.js.map +1 -1
- package/dist/database/contract/index.js +1 -1
- package/dist/database/contract/wire.d.ts.map +1 -1
- package/dist/database/contract/wire.js +5 -1
- package/dist/database/contract/wire.js.map +1 -1
- package/dist/database/errors.js +39 -3
- package/dist/database/errors.js.map +1 -1
- package/dist/database/runtime/data-path.js.map +1 -1
- package/dist/database/runtime/engine/drizzle-data-path.js +1 -1
- package/dist/database/runtime/engine/drizzle-data-path.js.map +1 -1
- package/dist/database/runtime/engine/translate.js +9 -2
- package/dist/database/runtime/engine/translate.js.map +1 -1
- package/dist/database/schema-builder/define-schema.d.ts +5 -1
- package/dist/database/schema-builder/define-schema.d.ts.map +1 -1
- package/dist/database/schema-builder/define-schema.js +4 -0
- package/dist/database/schema-builder/define-schema.js.map +1 -1
- package/dist/database/schema-builder/types.d.ts +7 -2
- package/dist/database/schema-builder/types.d.ts.map +1 -1
- package/dist/database/schema-builder/types.js.map +1 -1
- package/dist/plugins/agents/agents.d.ts +50 -0
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +225 -13
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/agents/manifest.js +40 -21
- package/dist/plugins/agents/schemas.js +2 -1
- package/dist/plugins/agents/schemas.js.map +1 -1
- package/dist/plugins/database/crud/codecs.js +78 -0
- package/dist/plugins/database/crud/codecs.js.map +1 -0
- package/dist/plugins/database/crud/contract.js +151 -0
- package/dist/plugins/database/crud/contract.js.map +1 -0
- package/dist/plugins/database/crud/exposure.js +40 -0
- package/dist/plugins/database/crud/exposure.js.map +1 -0
- package/dist/plugins/database/crud/query.js +244 -0
- package/dist/plugins/database/crud/query.js.map +1 -0
- package/dist/plugins/database/crud/routes.js +124 -0
- package/dist/plugins/database/crud/routes.js.map +1 -0
- package/dist/plugins/database/database.d.ts +6 -0
- package/dist/plugins/database/database.d.ts.map +1 -1
- package/dist/plugins/database/database.js +58 -4
- package/dist/plugins/database/database.js.map +1 -1
- package/dist/plugins/database/defaults.js +28 -1
- package/dist/plugins/database/defaults.js.map +1 -1
- package/dist/plugins/database/entity-client.js +1 -1
- package/dist/plugins/database/entity-types.d.ts +13 -4
- package/dist/plugins/database/entity-types.d.ts.map +1 -1
- package/dist/plugins/database/lifecycle.js +2 -1
- package/dist/plugins/database/lifecycle.js.map +1 -1
- package/dist/plugins/database/types.d.ts +32 -0
- package/dist/plugins/database/types.d.ts.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +35 -35
- package/docs/api/appkit/Function.database.md +3 -3
- package/docs/api/appkit/Function.defineSchema.md +14 -6
- package/docs/api/appkit/Interface.AgentDefinition.md +11 -0
- package/docs/api/appkit/Interface.AgentsPluginConfig.md +35 -0
- package/docs/api/appkit/Interface.RegisteredAgent.md +11 -0
- package/docs/api/appkit/Interface.Schema.md +10 -2
- package/docs/api/appkit/TypeAlias.IDatabaseConfig.md +20 -0
- package/docs/api/appkit/TypeAlias.ResolvedToolEntry.md +46 -0
- package/docs/api/appkit.md +2 -2
- package/docs/plugins/agents.md +68 -1
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
|
@@ -1 +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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"}
|
|
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\" || code === \"57014\"\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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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { filterOperatorsForKind } from "../../schema-builder/types.js";
|
|
2
2
|
import { columnValueSchema } from "../../schema-builder/validators.js";
|
|
3
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";
|
|
4
|
+
import { DEFAULT_LIMIT, IN_CAP, MAX_INCLUDES, MAX_INCLUDE_DEPTH, MAX_INCLUDE_NODES, MAX_WHERE_CONDITIONS, MAX_WHERE_DEPTH, MAX_WHERE_GROUP_ITEMS, isFilterOperator } from "../../contract/wire.js";
|
|
5
5
|
import "../../contract/index.js";
|
|
6
6
|
import { validateLimit } from "../data-path.js";
|
|
7
7
|
import { and, asc, desc, eq, gt, gte, ilike, inArray, isNull, like, lt, lte, ne, or, sql } from "drizzle-orm";
|
|
@@ -141,8 +141,12 @@ function tableByName(schema, name) {
|
|
|
141
141
|
if (!table) throw invalidDatabaseRequest(`Unknown table "${name}"`);
|
|
142
142
|
return table;
|
|
143
143
|
}
|
|
144
|
-
/** Translate
|
|
144
|
+
/** Translate relation edges into Drizzle's relational `with` config. */
|
|
145
145
|
function translateInclude(table, schema, include, access = "public") {
|
|
146
|
+
return translateIncludeTree(table, schema, include, access, 1, { nodes: 0 });
|
|
147
|
+
}
|
|
148
|
+
function translateIncludeTree(table, schema, include, access, depth, budget) {
|
|
149
|
+
if (depth > MAX_INCLUDE_DEPTH) throw invalidDatabaseRequest(`include exceeds the ${MAX_INCLUDE_DEPTH}-edge depth limit`);
|
|
146
150
|
const entries = Object.entries(include);
|
|
147
151
|
if (entries.length > MAX_INCLUDES) throw invalidDatabaseRequest(`include exceeds the ${MAX_INCLUDES}-relation limit`);
|
|
148
152
|
const config = {};
|
|
@@ -150,6 +154,8 @@ function translateInclude(table, schema, include, access = "public") {
|
|
|
150
154
|
const relation = table.$relations.find((candidate) => candidate.name === relationName);
|
|
151
155
|
if (!relation) throw invalidDatabaseRequest(`Unknown relation "${table.$name}.${relationName}"`);
|
|
152
156
|
if (rawOptions === false) continue;
|
|
157
|
+
budget.nodes += 1;
|
|
158
|
+
if (budget.nodes > MAX_INCLUDE_NODES) throw invalidDatabaseRequest(`include exceeds the ${MAX_INCLUDE_NODES}-node limit`);
|
|
153
159
|
const target = tableByName(schema, relation.targetTable);
|
|
154
160
|
if (rawOptions === true) {
|
|
155
161
|
config[relationName] = {
|
|
@@ -166,6 +172,7 @@ function translateInclude(table, schema, include, access = "public") {
|
|
|
166
172
|
if (relation.cardinality !== "toMany") throw invalidDatabaseRequest("Only to-many relations accept a limit");
|
|
167
173
|
relationConfig.limit = validateLimit(options.limit);
|
|
168
174
|
} else if (relation.cardinality === "toMany") relationConfig.limit = DEFAULT_LIMIT;
|
|
175
|
+
if (options.include !== void 0) relationConfig.with = translateIncludeTree(target, schema, options.include, access, depth + 1, budget);
|
|
169
176
|
config[relationName] = relationConfig;
|
|
170
177
|
}
|
|
171
178
|
return config;
|
|
@@ -1 +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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"}
|
|
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_INCLUDE_DEPTH,\n MAX_INCLUDE_NODES,\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 relation edges 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 return translateIncludeTree(table, schema, include, access, 1, { nodes: 0 });\n}\n\nfunction translateIncludeTree(\n table: AppKitTable,\n schema: Schema,\n include: IncludeSpec,\n access: ColumnAccess,\n depth: number,\n budget: { nodes: number },\n): Record<string, unknown> {\n if (depth > MAX_INCLUDE_DEPTH) {\n throw invalidDatabaseRequest(\n `include exceeds the ${MAX_INCLUDE_DEPTH}-edge depth limit`,\n );\n }\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 budget.nodes += 1;\n if (budget.nodes > MAX_INCLUDE_NODES) {\n throw invalidDatabaseRequest(\n `include exceeds the ${MAX_INCLUDE_NODES}-node limit`,\n );\n }\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 if (options.include !== undefined) {\n relationConfig.with = translateIncludeTree(\n target,\n schema,\n options.include,\n access,\n depth + 1,\n budget,\n );\n }\n config[relationName] = relationConfig;\n }\n return config;\n}\n"],"mappings":"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"}
|
|
@@ -6,7 +6,11 @@ interface SchemaBuilderContext {
|
|
|
6
6
|
table<C extends Record<string, ColumnBuilder>>(name: string, columns: C): TableHandle<C>;
|
|
7
7
|
enum(name: string, values: readonly string[]): ColumnBuilder;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Compile one declared schema. The returned type keeps the table names the
|
|
11
|
+
* builder returned, so `crudRoutes` and `hooks` can name only real tables.
|
|
12
|
+
*/
|
|
13
|
+
declare function defineSchema<TTables extends Record<string, AppKitTable>>(builder: (context: SchemaBuilderContext) => TTables, options?: DefineSchemaOptions): Schema<Extract<keyof TTables, string>>;
|
|
10
14
|
//#endregion
|
|
11
15
|
export { defineSchema };
|
|
12
16
|
//# sourceMappingURL=define-schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-schema.d.ts","names":[],"sources":["../../../src/database/schema-builder/define-schema.ts"],"mappings":";;;;UAoCiB,oBAAA;EACf,KAAA,WAAgB,MAAA,SAAe,aAAA,GAC7B,IAAA,UACA,OAAA,EAAS,CAAA,GACR,WAAA,CAAY,CAAA;EACf,IAAA,CAAK,IAAA,UAAc,MAAA,sBAA4B,aAAA;AAAA
|
|
1
|
+
{"version":3,"file":"define-schema.d.ts","names":[],"sources":["../../../src/database/schema-builder/define-schema.ts"],"mappings":";;;;UAoCiB,oBAAA;EACf,KAAA,WAAgB,MAAA,SAAe,aAAA,GAC7B,IAAA,UACA,OAAA,EAAS,CAAA,GACR,WAAA,CAAY,CAAA;EACf,IAAA,CAAK,IAAA,UAAc,MAAA,sBAA4B,aAAA;AAAA;;;;;iBAgTjC,YAAA,iBAA6B,MAAA,SAAe,WAAA,EAAA,CAC1D,OAAA,GAAU,OAAA,EAAS,oBAAA,KAAyB,OAAA,EAC5C,OAAA,GAAU,mBAAA,GACT,MAAA,CAAO,OAAA,OAAc,OAAA"}
|
|
@@ -184,6 +184,10 @@ function publishSchema(raw, candidates, schemaName) {
|
|
|
184
184
|
function assertFinalizedSchema(value) {
|
|
185
185
|
if (value === null || typeof value !== "object" || value[FINALIZED_SCHEMA] !== true) throw new TypeError("Expected a finalized AppKit database schema");
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Compile one declared schema. The returned type keeps the table names the
|
|
189
|
+
* builder returned, so `crudRoutes` and `hooks` can name only real tables.
|
|
190
|
+
*/
|
|
187
191
|
function defineSchema(builder, options) {
|
|
188
192
|
const schemaName = options?.schemaName ?? "public";
|
|
189
193
|
if (!schemaName) throw new SchemaBuildError("Schema name cannot be empty");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-schema.js","names":[],"sources":["../../../src/database/schema-builder/define-schema.ts"],"sourcesContent":["import { ColumnBuilder, enumColumn, validateLiteralDefaults } from \"./columns\";\nimport { buildEngineTables } from \"./engine/tables\";\nimport { resolveForeignKeys } from \"./fk\";\nimport { buildRelations } from \"./relations\";\nimport {\n type AppKitTable,\n type ColumnMeta,\n type ColumnRef,\n type DefineSchemaOptions,\n type MutableColumnMeta,\n type ResolvedRelation,\n type Schema,\n SchemaBuildError,\n type TableHandle,\n} from \"./types\";\nimport { deriveInsertSchema, deriveUpdateSchema } from \"./validators\";\n\ninterface RawTable {\n readonly name: string;\n readonly metas: Record<string, MutableColumnMeta>;\n readonly handle: Record<string, ColumnRef>;\n}\n\ninterface FinalizedTableCandidate {\n readonly columns: Readonly<Record<string, ColumnMeta>>;\n readonly engine: Schema[\"$engine\"][string];\n readonly relations: readonly ResolvedRelation[];\n readonly insertSchema: unknown;\n readonly updateSchema: unknown;\n}\n\ninterface DeclarationState {\n readonly raw: Map<string, RawTable>;\n readonly handleNames: Map<object, string>;\n}\n\nexport interface SchemaBuilderContext {\n table<C extends Record<string, ColumnBuilder>>(\n name: string,\n columns: C,\n ): TableHandle<C>;\n enum(name: string, values: readonly string[]): ColumnBuilder;\n}\n\nconst RESERVED_OBJECT_KEYS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst RESERVED_DATABASE_EXPORT_KEYS = new Set([\"sql\", \"transaction\"]);\n// A global symbol lets typegen recognize schemas loaded through another AppKit instance.\nconst FINALIZED_SCHEMA = Symbol.for(\"@databricks/appkit.database.schema\");\nconst TABLE_METADATA_KEYS = [\n \"$name\",\n \"$schemaName\",\n \"$columns\",\n \"$engine\",\n \"$relations\",\n \"$insertSchema\",\n \"$updateSchema\",\n] as const;\n\nfunction nullRecord<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>;\n}\n\nfunction assertRecord(value: unknown, label: string): asserts value is object {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new SchemaBuildError(`${label} must be an object`);\n }\n}\n\nfunction assertName(value: string, label: string): void {\n if (!value || RESERVED_OBJECT_KEYS.has(value)) {\n throw new SchemaBuildError(`${label} \"${value}\" is reserved`);\n }\n}\n\nfunction assertTableName(value: string): void {\n assertName(value, \"Table name\");\n if (RESERVED_DATABASE_EXPORT_KEYS.has(value)) {\n throw new SchemaBuildError(`Table name \"${value}\" is reserved`);\n }\n}\n\nfunction finalizeColumn(meta: MutableColumnMeta): ColumnMeta {\n if (!meta.engineColumn) {\n throw new SchemaBuildError(\n `Engine column \"${meta.columnName}\" is missing during finalization`,\n );\n }\n const { fkRef: _fkRef, ...published } = meta;\n return Object.freeze({\n ...published,\n enumValues: meta.enumValues\n ? Object.freeze([...meta.enumValues])\n : undefined,\n fk: meta.fk ? Object.freeze({ ...meta.fk }) : undefined,\n engineColumn: meta.engineColumn,\n }) as ColumnMeta;\n}\n\nfunction finalizeRelations(\n relations: readonly ResolvedRelation[],\n): readonly ResolvedRelation[] {\n return Object.freeze(\n relations.map((relation) => Object.freeze({ ...relation })),\n );\n}\n\n/** Clone builders into per-table metadata and create stable column handles. */\nfunction declareTable<C extends Record<string, ColumnBuilder>>(\n state: DeclarationState,\n name: string,\n columns: C,\n): TableHandle<C> {\n assertTableName(name);\n if (state.raw.has(name)) {\n throw new SchemaBuildError(`Duplicate table \"${name}\"`);\n }\n assertRecord(columns, `Columns for table \"${name}\"`);\n\n const metas = nullRecord<MutableColumnMeta>();\n const handle = Object.create(null) as Record<string, ColumnRef>;\n for (const [key, column] of Object.entries(columns)) {\n assertName(key, `Column name on table \"${name}\"`);\n if (key.startsWith(\"$\") || key === \"and\" || key === \"or\") {\n throw new SchemaBuildError(\n `Column \"${name}.${key}\" collides with AppKit runtime metadata`,\n );\n }\n if (!(column instanceof ColumnBuilder)) {\n throw new SchemaBuildError(\n `Column \"${name}.${key}\" was not created by an AppKit column builder`,\n );\n }\n\n const meta = column._cloneMeta();\n meta.name = key;\n meta.columnName = key;\n metas[key] = meta;\n\n const reference: ColumnRef = Object.freeze({\n __isColumnRef: true,\n tableName: name,\n columnName: key,\n });\n Object.defineProperty(handle, key, {\n enumerable: true,\n value: reference,\n });\n }\n\n state.raw.set(name, { name, metas, handle });\n state.handleNames.set(handle, name);\n return handle as unknown as TableHandle<C>;\n}\n\nfunction createBuilderContext(state: DeclarationState): SchemaBuilderContext {\n return {\n table(name, columns) {\n return declareTable(state, name, columns);\n },\n enum(name, values) {\n return enumColumn(name, values);\n },\n };\n}\n\n/** Require every exact table handle once under its declared identity. */\nfunction validateReturnedTables(\n returned: unknown,\n state: DeclarationState,\n): void {\n assertRecord(returned, \"defineSchema() return value\");\n const returnedHandles = new Set<object>();\n for (const [key, value] of Object.entries(returned)) {\n const name =\n value !== null && typeof value === \"object\"\n ? state.handleNames.get(value)\n : undefined;\n if (!name) {\n throw new SchemaBuildError(\n `defineSchema returned a value for \"${key}\" that was not produced by ctx.table()`,\n );\n }\n if (key !== name) {\n throw new SchemaBuildError(\n `defineSchema returned table \"${name}\" under key \"${key}\"; aliases are not supported`,\n );\n }\n if (returnedHandles.has(value)) {\n throw new SchemaBuildError(`Table \"${name}\" was returned more than once`);\n }\n returnedHandles.add(value);\n }\n if (returnedHandles.size !== state.raw.size) {\n const omitted = [...state.raw.values()]\n .filter((table) => !returnedHandles.has(table.handle))\n .map((table) => table.name);\n throw new SchemaBuildError(\n `defineSchema omitted declared table${omitted.length === 1 ? \"\" : \"s\"}: ${omitted.join(\", \")}`,\n );\n }\n}\n\n/** Detect declaration-handle tampering before metadata publication. */\nfunction validateHandles(raw: ReadonlyMap<string, RawTable>): void {\n for (const table of raw.values()) {\n const ownKeys = Reflect.ownKeys(table.handle);\n const hasOnlyDeclaredColumns =\n ownKeys.length === Object.keys(table.metas).length &&\n ownKeys.every(\n (key) => typeof key === \"string\" && Object.hasOwn(table.metas, key),\n );\n const hasMetadataCollision = TABLE_METADATA_KEYS.some((key) =>\n Object.hasOwn(table.handle, key),\n );\n if (\n Object.getPrototypeOf(table.handle) !== null ||\n !Object.isExtensible(table.handle) ||\n !hasOnlyDeclaredColumns ||\n hasMetadataCollision\n ) {\n throw new SchemaBuildError(\n `Table handle \"${table.name}\" was modified during schema declaration`,\n );\n }\n }\n}\n\n/** Reject composite primary keys, which keyed operations cannot represent. */\nfunction validatePrimaryKeys(raw: ReadonlyMap<string, RawTable>): void {\n for (const table of raw.values()) {\n const primaryKeys = Object.values(table.metas).filter(\n (meta) => meta.primaryKey,\n );\n if (primaryKeys.length > 1) {\n throw new SchemaBuildError(\n `Table \"${table.name}\" declares multiple primary-key columns; composite primary keys are not supported`,\n );\n }\n }\n}\n\n/** Reject table names that collide with generated Drizzle relation keys. */\nfunction validateRelationKeys(\n raw: ReadonlyMap<string, RawTable>,\n relations: ReadonlyMap<string, readonly ResolvedRelation[]>,\n): void {\n for (const [name, tableRelations] of relations) {\n if (tableRelations.length > 0 && raw.has(`${name}Relations`)) {\n throw new SchemaBuildError(\n `Table \"${name}Relations\" collides with generated relation metadata for \"${name}\"`,\n );\n }\n }\n}\n\n/** Prepare every engine object and validator without mutating handles. */\nfunction prepareTables(\n raw: ReadonlyMap<string, RawTable>,\n schemaName: string,\n relations: ReadonlyMap<string, readonly ResolvedRelation[]>,\n): Map<string, FinalizedTableCandidate> {\n const built = buildEngineTables(raw.values(), schemaName);\n const candidates = new Map<string, FinalizedTableCandidate>();\n for (const table of raw.values()) {\n const builtTable = built.get(table.name);\n if (!builtTable) {\n throw new SchemaBuildError(\n `Engine table \"${table.name}\" was not constructed`,\n );\n }\n const columns = nullRecord<ColumnMeta>();\n for (const [key, meta] of Object.entries(builtTable.columns)) {\n columns[key] = finalizeColumn(meta);\n }\n Object.freeze(columns);\n const tableRelations = finalizeRelations(relations.get(table.name) ?? []);\n const validatorTable = { $columns: columns } as AppKitTable;\n candidates.set(table.name, {\n columns,\n engine: builtTable.engine,\n relations: tableRelations,\n insertSchema: deriveInsertSchema(validatorTable),\n updateSchema: deriveUpdateSchema(validatorTable),\n });\n }\n return candidates;\n}\n\n/** Atomically publish prepared metadata as the final schema transition. */\nfunction publishSchema(\n raw: ReadonlyMap<string, RawTable>,\n candidates: ReadonlyMap<string, FinalizedTableCandidate>,\n schemaName: string,\n): Schema {\n const publications = [...raw.values()].map((table) => {\n const candidate = candidates.get(table.name);\n if (!candidate) {\n throw new SchemaBuildError(\n `Table \"${table.name}\" was not prepared for finalization`,\n );\n }\n return { table, candidate };\n });\n\n const tables = nullRecord<AppKitTable>();\n const engine = nullRecord<Schema[\"$engine\"][string]>();\n for (const { table, candidate } of publications) {\n Object.defineProperties(table.handle, {\n $name: { value: table.name },\n $schemaName: { value: schemaName },\n $columns: { value: candidate.columns },\n $engine: { value: candidate.engine },\n $relations: { value: candidate.relations },\n $insertSchema: { value: candidate.insertSchema },\n $updateSchema: { value: candidate.updateSchema },\n });\n const finalized = Object.freeze(table.handle) as unknown as AppKitTable;\n tables[table.name] = finalized;\n engine[table.name] = candidate.engine;\n }\n\n const schema = {\n $schemaName: schemaName,\n $tables: Object.freeze(tables),\n $engine: Object.freeze(engine),\n };\n Object.defineProperty(schema, FINALIZED_SCHEMA, { value: true });\n return Object.freeze(schema);\n}\n\n/** @internal Reject values that were not finalized by `defineSchema()`. */\nexport function assertFinalizedSchema(value: unknown): asserts value is Schema {\n if (\n value === null ||\n typeof value !== \"object\" ||\n (value as Record<PropertyKey, unknown>)[FINALIZED_SCHEMA] !== true\n ) {\n throw new TypeError(\"Expected a finalized AppKit database schema\");\n }\n}\n\nexport function defineSchema(\n builder: (context: SchemaBuilderContext) => Record<string, AppKitTable>,\n options?: DefineSchemaOptions,\n): Schema {\n const schemaName = options?.schemaName ?? \"public\";\n if (!schemaName) throw new SchemaBuildError(\"Schema name cannot be empty\");\n\n const state: DeclarationState = {\n raw: new Map(),\n handleNames: new Map(),\n };\n const returned = builder(createBuilderContext(state));\n\n validateReturnedTables(returned, state);\n validateHandles(state.raw);\n validatePrimaryKeys(state.raw);\n resolveForeignKeys(state.raw);\n // FK literals are checked against the inherited target storage, not the placeholder.\n validateLiteralDefaults(state.raw.values());\n\n const relations = buildRelations(state.raw);\n validateRelationKeys(state.raw, relations);\n const candidates = prepareTables(state.raw, schemaName, relations);\n return publishSchema(state.raw, candidates, schemaName);\n}\n"],"mappings":";;;;;;;;AA4CA,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAc,CAAC;AAC/E,MAAM,gCAAgC,IAAI,IAAI,CAAC,OAAO,cAAc,CAAC;AAErE,MAAM,mBAAmB,OAAO,IAAI,qCAAqC;AACzE,MAAM,sBAAsB;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,aAAmC;AAC1C,QAAO,OAAO,OAAO,KAAK;;AAG5B,SAAS,aAAa,OAAgB,OAAwC;AAC5E,KAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CACrE,OAAM,IAAI,iBAAiB,GAAG,MAAM,oBAAoB;;AAI5D,SAAS,WAAW,OAAe,OAAqB;AACtD,KAAI,CAAC,SAAS,qBAAqB,IAAI,MAAM,CAC3C,OAAM,IAAI,iBAAiB,GAAG,MAAM,IAAI,MAAM,eAAe;;AAIjE,SAAS,gBAAgB,OAAqB;AAC5C,YAAW,OAAO,aAAa;AAC/B,KAAI,8BAA8B,IAAI,MAAM,CAC1C,OAAM,IAAI,iBAAiB,eAAe,MAAM,eAAe;;AAInE,SAAS,eAAe,MAAqC;AAC3D,KAAI,CAAC,KAAK,aACR,OAAM,IAAI,iBACR,kBAAkB,KAAK,WAAW,kCACnC;CAEH,MAAM,EAAE,OAAO,QAAQ,GAAG,cAAc;AACxC,QAAO,OAAO,OAAO;EACnB,GAAG;EACH,YAAY,KAAK,aACb,OAAO,OAAO,CAAC,GAAG,KAAK,WAAW,CAAC,GACnC;EACJ,IAAI,KAAK,KAAK,OAAO,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG;EAC9C,cAAc,KAAK;EACpB,CAAC;;AAGJ,SAAS,kBACP,WAC6B;AAC7B,QAAO,OAAO,OACZ,UAAU,KAAK,aAAa,OAAO,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC,CAC5D;;;AAIH,SAAS,aACP,OACA,MACA,SACgB;AAChB,iBAAgB,KAAK;AACrB,KAAI,MAAM,IAAI,IAAI,KAAK,CACrB,OAAM,IAAI,iBAAiB,oBAAoB,KAAK,GAAG;AAEzD,cAAa,SAAS,sBAAsB,KAAK,GAAG;CAEpD,MAAM,QAAQ,YAA+B;CAC7C,MAAM,SAAS,OAAO,OAAO,KAAK;AAClC,MAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,QAAQ,EAAE;AACnD,aAAW,KAAK,yBAAyB,KAAK,GAAG;AACjD,MAAI,IAAI,WAAW,IAAI,IAAI,QAAQ,SAAS,QAAQ,KAClD,OAAM,IAAI,iBACR,WAAW,KAAK,GAAG,IAAI,yCACxB;AAEH,MAAI,EAAE,kBAAkB,eACtB,OAAM,IAAI,iBACR,WAAW,KAAK,GAAG,IAAI,+CACxB;EAGH,MAAM,OAAO,OAAO,YAAY;AAChC,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,QAAM,OAAO;EAEb,MAAM,YAAuB,OAAO,OAAO;GACzC,eAAe;GACf,WAAW;GACX,YAAY;GACb,CAAC;AACF,SAAO,eAAe,QAAQ,KAAK;GACjC,YAAY;GACZ,OAAO;GACR,CAAC;;AAGJ,OAAM,IAAI,IAAI,MAAM;EAAE;EAAM;EAAO;EAAQ,CAAC;AAC5C,OAAM,YAAY,IAAI,QAAQ,KAAK;AACnC,QAAO;;AAGT,SAAS,qBAAqB,OAA+C;AAC3E,QAAO;EACL,MAAM,MAAM,SAAS;AACnB,UAAO,aAAa,OAAO,MAAM,QAAQ;;EAE3C,KAAK,MAAM,QAAQ;AACjB,UAAO,WAAW,MAAM,OAAO;;EAElC;;;AAIH,SAAS,uBACP,UACA,OACM;AACN,cAAa,UAAU,8BAA8B;CACrD,MAAM,kCAAkB,IAAI,KAAa;AACzC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,EAAE;EACnD,MAAM,OACJ,UAAU,QAAQ,OAAO,UAAU,WAC/B,MAAM,YAAY,IAAI,MAAM,GAC5B;AACN,MAAI,CAAC,KACH,OAAM,IAAI,iBACR,sCAAsC,IAAI,wCAC3C;AAEH,MAAI,QAAQ,KACV,OAAM,IAAI,iBACR,gCAAgC,KAAK,eAAe,IAAI,8BACzD;AAEH,MAAI,gBAAgB,IAAI,MAAM,CAC5B,OAAM,IAAI,iBAAiB,UAAU,KAAK,+BAA+B;AAE3E,kBAAgB,IAAI,MAAM;;AAE5B,KAAI,gBAAgB,SAAS,MAAM,IAAI,MAAM;EAC3C,MAAM,UAAU,CAAC,GAAG,MAAM,IAAI,QAAQ,CAAC,CACpC,QAAQ,UAAU,CAAC,gBAAgB,IAAI,MAAM,OAAO,CAAC,CACrD,KAAK,UAAU,MAAM,KAAK;AAC7B,QAAM,IAAI,iBACR,sCAAsC,QAAQ,WAAW,IAAI,KAAK,IAAI,IAAI,QAAQ,KAAK,KAAK,GAC7F;;;;AAKL,SAAS,gBAAgB,KAA0C;AACjE,MAAK,MAAM,SAAS,IAAI,QAAQ,EAAE;EAChC,MAAM,UAAU,QAAQ,QAAQ,MAAM,OAAO;EAC7C,MAAM,yBACJ,QAAQ,WAAW,OAAO,KAAK,MAAM,MAAM,CAAC,UAC5C,QAAQ,OACL,QAAQ,OAAO,QAAQ,YAAY,OAAO,OAAO,MAAM,OAAO,IAAI,CACpE;EACH,MAAM,uBAAuB,oBAAoB,MAAM,QACrD,OAAO,OAAO,MAAM,QAAQ,IAAI,CACjC;AACD,MACE,OAAO,eAAe,MAAM,OAAO,KAAK,QACxC,CAAC,OAAO,aAAa,MAAM,OAAO,IAClC,CAAC,0BACD,qBAEA,OAAM,IAAI,iBACR,iBAAiB,MAAM,KAAK,0CAC7B;;;;AAMP,SAAS,oBAAoB,KAA0C;AACrE,MAAK,MAAM,SAAS,IAAI,QAAQ,CAI9B,KAHoB,OAAO,OAAO,MAAM,MAAM,CAAC,QAC5C,SAAS,KAAK,WAChB,CACe,SAAS,EACvB,OAAM,IAAI,iBACR,UAAU,MAAM,KAAK,mFACtB;;;AAMP,SAAS,qBACP,KACA,WACM;AACN,MAAK,MAAM,CAAC,MAAM,mBAAmB,UACnC,KAAI,eAAe,SAAS,KAAK,IAAI,IAAI,GAAG,KAAK,WAAW,CAC1D,OAAM,IAAI,iBACR,UAAU,KAAK,4DAA4D,KAAK,GACjF;;;AAMP,SAAS,cACP,KACA,YACA,WACsC;CACtC,MAAM,QAAQ,kBAAkB,IAAI,QAAQ,EAAE,WAAW;CACzD,MAAM,6BAAa,IAAI,KAAsC;AAC7D,MAAK,MAAM,SAAS,IAAI,QAAQ,EAAE;EAChC,MAAM,aAAa,MAAM,IAAI,MAAM,KAAK;AACxC,MAAI,CAAC,WACH,OAAM,IAAI,iBACR,iBAAiB,MAAM,KAAK,uBAC7B;EAEH,MAAM,UAAU,YAAwB;AACxC,OAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,WAAW,QAAQ,CAC1D,SAAQ,OAAO,eAAe,KAAK;AAErC,SAAO,OAAO,QAAQ;EACtB,MAAM,iBAAiB,kBAAkB,UAAU,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;EACzE,MAAM,iBAAiB,EAAE,UAAU,SAAS;AAC5C,aAAW,IAAI,MAAM,MAAM;GACzB;GACA,QAAQ,WAAW;GACnB,WAAW;GACX,cAAc,mBAAmB,eAAe;GAChD,cAAc,mBAAmB,eAAe;GACjD,CAAC;;AAEJ,QAAO;;;AAIT,SAAS,cACP,KACA,YACA,YACQ;CACR,MAAM,eAAe,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,KAAK,UAAU;EACpD,MAAM,YAAY,WAAW,IAAI,MAAM,KAAK;AAC5C,MAAI,CAAC,UACH,OAAM,IAAI,iBACR,UAAU,MAAM,KAAK,qCACtB;AAEH,SAAO;GAAE;GAAO;GAAW;GAC3B;CAEF,MAAM,SAAS,YAAyB;CACxC,MAAM,SAAS,YAAuC;AACtD,MAAK,MAAM,EAAE,OAAO,eAAe,cAAc;AAC/C,SAAO,iBAAiB,MAAM,QAAQ;GACpC,OAAO,EAAE,OAAO,MAAM,MAAM;GAC5B,aAAa,EAAE,OAAO,YAAY;GAClC,UAAU,EAAE,OAAO,UAAU,SAAS;GACtC,SAAS,EAAE,OAAO,UAAU,QAAQ;GACpC,YAAY,EAAE,OAAO,UAAU,WAAW;GAC1C,eAAe,EAAE,OAAO,UAAU,cAAc;GAChD,eAAe,EAAE,OAAO,UAAU,cAAc;GACjD,CAAC;EACF,MAAM,YAAY,OAAO,OAAO,MAAM,OAAO;AAC7C,SAAO,MAAM,QAAQ;AACrB,SAAO,MAAM,QAAQ,UAAU;;CAGjC,MAAM,SAAS;EACb,aAAa;EACb,SAAS,OAAO,OAAO,OAAO;EAC9B,SAAS,OAAO,OAAO,OAAO;EAC/B;AACD,QAAO,eAAe,QAAQ,kBAAkB,EAAE,OAAO,MAAM,CAAC;AAChE,QAAO,OAAO,OAAO,OAAO;;;AAI9B,SAAgB,sBAAsB,OAAyC;AAC7E,KACE,UAAU,QACV,OAAO,UAAU,YAChB,MAAuC,sBAAsB,KAE9D,OAAM,IAAI,UAAU,8CAA8C;;AAItE,SAAgB,aACd,SACA,SACQ;CACR,MAAM,aAAa,SAAS,cAAc;AAC1C,KAAI,CAAC,WAAY,OAAM,IAAI,iBAAiB,8BAA8B;CAE1E,MAAM,QAA0B;EAC9B,qBAAK,IAAI,KAAK;EACd,6BAAa,IAAI,KAAK;EACvB;AAGD,wBAFiB,QAAQ,qBAAqB,MAAM,CAAC,EAEpB,MAAM;AACvC,iBAAgB,MAAM,IAAI;AAC1B,qBAAoB,MAAM,IAAI;AAC9B,oBAAmB,MAAM,IAAI;AAE7B,yBAAwB,MAAM,IAAI,QAAQ,CAAC;CAE3C,MAAM,YAAY,eAAe,MAAM,IAAI;AAC3C,sBAAqB,MAAM,KAAK,UAAU;CAC1C,MAAM,aAAa,cAAc,MAAM,KAAK,YAAY,UAAU;AAClE,QAAO,cAAc,MAAM,KAAK,YAAY,WAAW"}
|
|
1
|
+
{"version":3,"file":"define-schema.js","names":[],"sources":["../../../src/database/schema-builder/define-schema.ts"],"sourcesContent":["import { ColumnBuilder, enumColumn, validateLiteralDefaults } from \"./columns\";\nimport { buildEngineTables } from \"./engine/tables\";\nimport { resolveForeignKeys } from \"./fk\";\nimport { buildRelations } from \"./relations\";\nimport {\n type AppKitTable,\n type ColumnMeta,\n type ColumnRef,\n type DefineSchemaOptions,\n type MutableColumnMeta,\n type ResolvedRelation,\n type Schema,\n SchemaBuildError,\n type TableHandle,\n} from \"./types\";\nimport { deriveInsertSchema, deriveUpdateSchema } from \"./validators\";\n\ninterface RawTable {\n readonly name: string;\n readonly metas: Record<string, MutableColumnMeta>;\n readonly handle: Record<string, ColumnRef>;\n}\n\ninterface FinalizedTableCandidate {\n readonly columns: Readonly<Record<string, ColumnMeta>>;\n readonly engine: Schema[\"$engine\"][string];\n readonly relations: readonly ResolvedRelation[];\n readonly insertSchema: unknown;\n readonly updateSchema: unknown;\n}\n\ninterface DeclarationState {\n readonly raw: Map<string, RawTable>;\n readonly handleNames: Map<object, string>;\n}\n\nexport interface SchemaBuilderContext {\n table<C extends Record<string, ColumnBuilder>>(\n name: string,\n columns: C,\n ): TableHandle<C>;\n enum(name: string, values: readonly string[]): ColumnBuilder;\n}\n\nconst RESERVED_OBJECT_KEYS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst RESERVED_DATABASE_EXPORT_KEYS = new Set([\"sql\", \"transaction\"]);\n// A global symbol lets typegen recognize schemas loaded through another AppKit instance.\nconst FINALIZED_SCHEMA = Symbol.for(\"@databricks/appkit.database.schema\");\nconst TABLE_METADATA_KEYS = [\n \"$name\",\n \"$schemaName\",\n \"$columns\",\n \"$engine\",\n \"$relations\",\n \"$insertSchema\",\n \"$updateSchema\",\n] as const;\n\nfunction nullRecord<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>;\n}\n\nfunction assertRecord(value: unknown, label: string): asserts value is object {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new SchemaBuildError(`${label} must be an object`);\n }\n}\n\nfunction assertName(value: string, label: string): void {\n if (!value || RESERVED_OBJECT_KEYS.has(value)) {\n throw new SchemaBuildError(`${label} \"${value}\" is reserved`);\n }\n}\n\nfunction assertTableName(value: string): void {\n assertName(value, \"Table name\");\n if (RESERVED_DATABASE_EXPORT_KEYS.has(value)) {\n throw new SchemaBuildError(`Table name \"${value}\" is reserved`);\n }\n}\n\nfunction finalizeColumn(meta: MutableColumnMeta): ColumnMeta {\n if (!meta.engineColumn) {\n throw new SchemaBuildError(\n `Engine column \"${meta.columnName}\" is missing during finalization`,\n );\n }\n const { fkRef: _fkRef, ...published } = meta;\n return Object.freeze({\n ...published,\n enumValues: meta.enumValues\n ? Object.freeze([...meta.enumValues])\n : undefined,\n fk: meta.fk ? Object.freeze({ ...meta.fk }) : undefined,\n engineColumn: meta.engineColumn,\n }) as ColumnMeta;\n}\n\nfunction finalizeRelations(\n relations: readonly ResolvedRelation[],\n): readonly ResolvedRelation[] {\n return Object.freeze(\n relations.map((relation) => Object.freeze({ ...relation })),\n );\n}\n\n/** Clone builders into per-table metadata and create stable column handles. */\nfunction declareTable<C extends Record<string, ColumnBuilder>>(\n state: DeclarationState,\n name: string,\n columns: C,\n): TableHandle<C> {\n assertTableName(name);\n if (state.raw.has(name)) {\n throw new SchemaBuildError(`Duplicate table \"${name}\"`);\n }\n assertRecord(columns, `Columns for table \"${name}\"`);\n\n const metas = nullRecord<MutableColumnMeta>();\n const handle = Object.create(null) as Record<string, ColumnRef>;\n for (const [key, column] of Object.entries(columns)) {\n assertName(key, `Column name on table \"${name}\"`);\n if (key.startsWith(\"$\") || key === \"and\" || key === \"or\") {\n throw new SchemaBuildError(\n `Column \"${name}.${key}\" collides with AppKit runtime metadata`,\n );\n }\n if (!(column instanceof ColumnBuilder)) {\n throw new SchemaBuildError(\n `Column \"${name}.${key}\" was not created by an AppKit column builder`,\n );\n }\n\n const meta = column._cloneMeta();\n meta.name = key;\n meta.columnName = key;\n metas[key] = meta;\n\n const reference: ColumnRef = Object.freeze({\n __isColumnRef: true,\n tableName: name,\n columnName: key,\n });\n Object.defineProperty(handle, key, {\n enumerable: true,\n value: reference,\n });\n }\n\n state.raw.set(name, { name, metas, handle });\n state.handleNames.set(handle, name);\n return handle as unknown as TableHandle<C>;\n}\n\nfunction createBuilderContext(state: DeclarationState): SchemaBuilderContext {\n return {\n table(name, columns) {\n return declareTable(state, name, columns);\n },\n enum(name, values) {\n return enumColumn(name, values);\n },\n };\n}\n\n/** Require every exact table handle once under its declared identity. */\nfunction validateReturnedTables(\n returned: unknown,\n state: DeclarationState,\n): void {\n assertRecord(returned, \"defineSchema() return value\");\n const returnedHandles = new Set<object>();\n for (const [key, value] of Object.entries(returned)) {\n const name =\n value !== null && typeof value === \"object\"\n ? state.handleNames.get(value)\n : undefined;\n if (!name) {\n throw new SchemaBuildError(\n `defineSchema returned a value for \"${key}\" that was not produced by ctx.table()`,\n );\n }\n if (key !== name) {\n throw new SchemaBuildError(\n `defineSchema returned table \"${name}\" under key \"${key}\"; aliases are not supported`,\n );\n }\n if (returnedHandles.has(value)) {\n throw new SchemaBuildError(`Table \"${name}\" was returned more than once`);\n }\n returnedHandles.add(value);\n }\n if (returnedHandles.size !== state.raw.size) {\n const omitted = [...state.raw.values()]\n .filter((table) => !returnedHandles.has(table.handle))\n .map((table) => table.name);\n throw new SchemaBuildError(\n `defineSchema omitted declared table${omitted.length === 1 ? \"\" : \"s\"}: ${omitted.join(\", \")}`,\n );\n }\n}\n\n/** Detect declaration-handle tampering before metadata publication. */\nfunction validateHandles(raw: ReadonlyMap<string, RawTable>): void {\n for (const table of raw.values()) {\n const ownKeys = Reflect.ownKeys(table.handle);\n const hasOnlyDeclaredColumns =\n ownKeys.length === Object.keys(table.metas).length &&\n ownKeys.every(\n (key) => typeof key === \"string\" && Object.hasOwn(table.metas, key),\n );\n const hasMetadataCollision = TABLE_METADATA_KEYS.some((key) =>\n Object.hasOwn(table.handle, key),\n );\n if (\n Object.getPrototypeOf(table.handle) !== null ||\n !Object.isExtensible(table.handle) ||\n !hasOnlyDeclaredColumns ||\n hasMetadataCollision\n ) {\n throw new SchemaBuildError(\n `Table handle \"${table.name}\" was modified during schema declaration`,\n );\n }\n }\n}\n\n/** Reject composite primary keys, which keyed operations cannot represent. */\nfunction validatePrimaryKeys(raw: ReadonlyMap<string, RawTable>): void {\n for (const table of raw.values()) {\n const primaryKeys = Object.values(table.metas).filter(\n (meta) => meta.primaryKey,\n );\n if (primaryKeys.length > 1) {\n throw new SchemaBuildError(\n `Table \"${table.name}\" declares multiple primary-key columns; composite primary keys are not supported`,\n );\n }\n }\n}\n\n/** Reject table names that collide with generated Drizzle relation keys. */\nfunction validateRelationKeys(\n raw: ReadonlyMap<string, RawTable>,\n relations: ReadonlyMap<string, readonly ResolvedRelation[]>,\n): void {\n for (const [name, tableRelations] of relations) {\n if (tableRelations.length > 0 && raw.has(`${name}Relations`)) {\n throw new SchemaBuildError(\n `Table \"${name}Relations\" collides with generated relation metadata for \"${name}\"`,\n );\n }\n }\n}\n\n/** Prepare every engine object and validator without mutating handles. */\nfunction prepareTables(\n raw: ReadonlyMap<string, RawTable>,\n schemaName: string,\n relations: ReadonlyMap<string, readonly ResolvedRelation[]>,\n): Map<string, FinalizedTableCandidate> {\n const built = buildEngineTables(raw.values(), schemaName);\n const candidates = new Map<string, FinalizedTableCandidate>();\n for (const table of raw.values()) {\n const builtTable = built.get(table.name);\n if (!builtTable) {\n throw new SchemaBuildError(\n `Engine table \"${table.name}\" was not constructed`,\n );\n }\n const columns = nullRecord<ColumnMeta>();\n for (const [key, meta] of Object.entries(builtTable.columns)) {\n columns[key] = finalizeColumn(meta);\n }\n Object.freeze(columns);\n const tableRelations = finalizeRelations(relations.get(table.name) ?? []);\n const validatorTable = { $columns: columns } as AppKitTable;\n candidates.set(table.name, {\n columns,\n engine: builtTable.engine,\n relations: tableRelations,\n insertSchema: deriveInsertSchema(validatorTable),\n updateSchema: deriveUpdateSchema(validatorTable),\n });\n }\n return candidates;\n}\n\n/** Atomically publish prepared metadata as the final schema transition. */\nfunction publishSchema(\n raw: ReadonlyMap<string, RawTable>,\n candidates: ReadonlyMap<string, FinalizedTableCandidate>,\n schemaName: string,\n): Schema {\n const publications = [...raw.values()].map((table) => {\n const candidate = candidates.get(table.name);\n if (!candidate) {\n throw new SchemaBuildError(\n `Table \"${table.name}\" was not prepared for finalization`,\n );\n }\n return { table, candidate };\n });\n\n const tables = nullRecord<AppKitTable>();\n const engine = nullRecord<Schema[\"$engine\"][string]>();\n for (const { table, candidate } of publications) {\n Object.defineProperties(table.handle, {\n $name: { value: table.name },\n $schemaName: { value: schemaName },\n $columns: { value: candidate.columns },\n $engine: { value: candidate.engine },\n $relations: { value: candidate.relations },\n $insertSchema: { value: candidate.insertSchema },\n $updateSchema: { value: candidate.updateSchema },\n });\n const finalized = Object.freeze(table.handle) as unknown as AppKitTable;\n tables[table.name] = finalized;\n engine[table.name] = candidate.engine;\n }\n\n const schema = {\n $schemaName: schemaName,\n $tables: Object.freeze(tables),\n $engine: Object.freeze(engine),\n };\n Object.defineProperty(schema, FINALIZED_SCHEMA, { value: true });\n return Object.freeze(schema);\n}\n\n/** @internal Reject values that were not finalized by `defineSchema()`. */\nexport function assertFinalizedSchema(value: unknown): asserts value is Schema {\n if (\n value === null ||\n typeof value !== \"object\" ||\n (value as Record<PropertyKey, unknown>)[FINALIZED_SCHEMA] !== true\n ) {\n throw new TypeError(\"Expected a finalized AppKit database schema\");\n }\n}\n\n/**\n * Compile one declared schema. The returned type keeps the table names the\n * builder returned, so `crudRoutes` and `hooks` can name only real tables.\n */\nexport function defineSchema<TTables extends Record<string, AppKitTable>>(\n builder: (context: SchemaBuilderContext) => TTables,\n options?: DefineSchemaOptions,\n): Schema<Extract<keyof TTables, string>> {\n const schemaName = options?.schemaName ?? \"public\";\n if (!schemaName) throw new SchemaBuildError(\"Schema name cannot be empty\");\n\n const state: DeclarationState = {\n raw: new Map(),\n handleNames: new Map(),\n };\n const returned = builder(createBuilderContext(state));\n\n validateReturnedTables(returned, state);\n validateHandles(state.raw);\n validatePrimaryKeys(state.raw);\n resolveForeignKeys(state.raw);\n // FK literals are checked against the inherited target storage, not the placeholder.\n validateLiteralDefaults(state.raw.values());\n\n const relations = buildRelations(state.raw);\n validateRelationKeys(state.raw, relations);\n const candidates = prepareTables(state.raw, schemaName, relations);\n return publishSchema(state.raw, candidates, schemaName);\n}\n"],"mappings":"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"}
|
|
@@ -111,9 +111,14 @@ type TableHandle<C extends Record<string, unknown>> = AppKitTable & { readonly [
|
|
|
111
111
|
interface DefineSchemaOptions {
|
|
112
112
|
readonly schemaName?: string;
|
|
113
113
|
}
|
|
114
|
-
|
|
114
|
+
/**
|
|
115
|
+
* One finalized schema. `TTableName` keeps the declared names in the type, so
|
|
116
|
+
* configuration that addresses a table by name is checked against the schema
|
|
117
|
+
* it was written for. Code that accepts any schema uses the default.
|
|
118
|
+
*/
|
|
119
|
+
interface Schema<TTableName extends string = string> {
|
|
115
120
|
readonly $schemaName: string;
|
|
116
|
-
readonly $tables: Readonly<Record<
|
|
121
|
+
readonly $tables: Readonly<Record<TTableName, AppKitTable>>;
|
|
117
122
|
readonly $engine: Readonly<Record<string, EngineTable>>;
|
|
118
123
|
}
|
|
119
124
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/database/schema-builder/types.ts"],"mappings":";cAEc,YAAA;AAAA,cACA,aAAA;;KAEF,WAAA;EAAA,UAA0B,YAAA;AAAA;AAAA,KAC1B,YAAA;EAAA,UAA2B,aAAA;AAAA;;KAG3B,eAAA;;KAiCA,iBAAA;AApCZ;AAAA,KA4CY,cAAA;EAAA,SACG,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;EAAA,SAA0B,MAAA;AAAA;EAAA,SAC1B,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;EAAA,SAA4B,YAAA;AAAA;EAAA,SAC5B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,MAAA;AAAA;EAAA,SAEA,IAAA;AAAA;;KAGH,WAAA;AAAA,UAaK,SAAA;EAAA,SACN,aAAA;EAAA,SACA,SAAA;EAAA,SACA,UAAA;AAAA;AAAA,KAGC,KAAA,GAAQ,SAAA,UAAmB,SAAA;AAAA,UAEtB,kBAAA;EAAA,SACN,WAAA;EAAA,SACA,YAAA;EAAA,SACA,QAAA,GAAW,iBAAA;EAAA,SACX,QAAA,GAAW,iBAAA;AAAA;;UAIL,iBAAA;EACf,IAAA;EACA,UAAA;EACA,IAAA,EAAM,eAAA;EACN,WAAA,EAAa,WAAA;EACb,OAAA;EACA,UAAA;EACA,MAAA;EACA,SAAA;EACA,eAAA;EACA,UAAA;EACA,YAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA,GAAQ,KAAA;EACR,QAAA,GAAW,iBAAA;EACX,QAAA,GAAW,iBAAA;EACX,EAAA,GAAK,kBAAA;EACL,YAAA,GAAe,YAAA;AAAA;;KAIL,UAAA,GAAa,QAAA,CACvB,IAAA,CAAK,iBAAA;EAAA,SACM,UAAA;EAAA,SACA,EAAA,GAAK,QAAA,CAAS,kBAAA;EAAA,SACd,YAAA,EAAc,YAAA;AAAA;AAAA,UAIV,gBAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,YAAA;EAAA,SACA,QAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,KAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,UAAA;EAAA,SAClC,OAAA,EAAS,WAAA;EAAA,SACT,UAAA,WAAqB,gBAAA;EAxC9B;EAAA,SA0CS,aAAA;EAxCT;EAAA,SA0CS,aAAA;AAAA;;KAIC,WAAA,WAAsB,MAAA,qBAA2B,WAAA,0BACtC,CAAA,GAAI,SAAA;AAAA,UAGV,mBAAA;EAAA,SACN,UAAA;AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/database/schema-builder/types.ts"],"mappings":";cAEc,YAAA;AAAA,cACA,aAAA;;KAEF,WAAA;EAAA,UAA0B,YAAA;AAAA;AAAA,KAC1B,YAAA;EAAA,UAA2B,aAAA;AAAA;;KAG3B,eAAA;;KAiCA,iBAAA;AApCZ;AAAA,KA4CY,cAAA;EAAA,SACG,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;EAAA,SAA0B,MAAA;AAAA;EAAA,SAC1B,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;EAAA,SAA4B,YAAA;AAAA;EAAA,SAC5B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,MAAA;AAAA;EAAA,SAEA,IAAA;AAAA;;KAGH,WAAA;AAAA,UAaK,SAAA;EAAA,SACN,aAAA;EAAA,SACA,SAAA;EAAA,SACA,UAAA;AAAA;AAAA,KAGC,KAAA,GAAQ,SAAA,UAAmB,SAAA;AAAA,UAEtB,kBAAA;EAAA,SACN,WAAA;EAAA,SACA,YAAA;EAAA,SACA,QAAA,GAAW,iBAAA;EAAA,SACX,QAAA,GAAW,iBAAA;AAAA;;UAIL,iBAAA;EACf,IAAA;EACA,UAAA;EACA,IAAA,EAAM,eAAA;EACN,WAAA,EAAa,WAAA;EACb,OAAA;EACA,UAAA;EACA,MAAA;EACA,SAAA;EACA,eAAA;EACA,UAAA;EACA,YAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA,GAAQ,KAAA;EACR,QAAA,GAAW,iBAAA;EACX,QAAA,GAAW,iBAAA;EACX,EAAA,GAAK,kBAAA;EACL,YAAA,GAAe,YAAA;AAAA;;KAIL,UAAA,GAAa,QAAA,CACvB,IAAA,CAAK,iBAAA;EAAA,SACM,UAAA;EAAA,SACA,EAAA,GAAK,QAAA,CAAS,kBAAA;EAAA,SACd,YAAA,EAAc,YAAA;AAAA;AAAA,UAIV,gBAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,YAAA;EAAA,SACA,QAAA;AAAA;AAAA,UAGM,WAAA;EAAA,SACN,KAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,UAAA;EAAA,SAClC,OAAA,EAAS,WAAA;EAAA,SACT,UAAA,WAAqB,gBAAA;EAxC9B;EAAA,SA0CS,aAAA;EAxCT;EAAA,SA0CS,aAAA;AAAA;;KAIC,WAAA,WAAsB,MAAA,qBAA2B,WAAA,0BACtC,CAAA,GAAI,SAAA;AAAA,UAGV,mBAAA;EAAA,SACN,UAAA;AAAA;;;;;;UAQM,MAAA;EAAA,SACN,WAAA;EAAA,SACA,OAAA,EAAS,QAAA,CAAS,MAAA,CAAO,UAAA,EAAY,WAAA;EAAA,SACrC,OAAA,EAAS,QAAA,CAAS,MAAA,SAAe,WAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../src/database/schema-builder/types.ts"],"sourcesContent":["import type { FilterOperator } from \"../contract\";\n\ndeclare const ENGINE_TABLE: unique symbol;\ndeclare const ENGINE_COLUMN: unique symbol;\n/** Opaque handles keep Drizzle types behind the schema/runtime boundary. */\nexport type EngineTable = { readonly [ENGINE_TABLE]: true };\nexport type EngineColumn = { readonly [ENGINE_COLUMN]: true };\n\n/** JavaScript value category exposed by a column, independent of its storage. */\nexport type ColumnValueKind =\n | \"string\"\n | \"number\"\n | \"bigint\"\n | \"boolean\"\n | \"date\"\n | \"json\"\n | \"uuid\"\n | \"enum\"\n | \"unknown\";\n\n/** The filter subset shared by runtime translation and later typed surfaces. */\nexport function filterOperatorsForKind(\n kind: ColumnValueKind,\n): readonly FilterOperator[] {\n switch (kind) {\n case \"string\":\n return [\"eq\", \"neq\", \"in\", \"like\", \"ilike\"];\n case \"number\":\n case \"bigint\":\n case \"date\":\n return [\"eq\", \"neq\", \"in\", \"gt\", \"gte\", \"lt\", \"lte\"];\n case \"enum\":\n case \"boolean\":\n case \"uuid\":\n return [\"eq\", \"neq\", \"in\"];\n case \"json\":\n case \"unknown\":\n return [];\n }\n}\n\n/** PostgreSQL action applied when a referenced row changes or is deleted. */\nexport type ReferentialAction =\n | \"cascade\"\n | \"set null\"\n | \"set default\"\n | \"restrict\"\n | \"no action\";\n\n/** DSL declaration kind, including identity shorthand and unresolved FKs. */\nexport type ColumnTypeSpec =\n | { readonly kind: \"id\" }\n | { readonly kind: \"bigid\" }\n | { readonly kind: \"text\" }\n | { readonly kind: \"varchar\"; readonly length: number }\n | { readonly kind: \"integer\" }\n | { readonly kind: \"bigint\" }\n | { readonly kind: \"boolean\" }\n | { readonly kind: \"uuid\" }\n | { readonly kind: \"timestamp\"; readonly withTimezone: boolean }\n | { readonly kind: \"jsonb\" }\n | {\n readonly kind: \"enum\";\n readonly enumName: string;\n readonly values: readonly string[];\n }\n | { readonly kind: \"fk\" };\n\n/** Resolved PostgreSQL storage used to construct the engine column. */\nexport type StorageKind =\n | \"id\"\n | \"bigid\"\n | \"text\"\n | \"varchar\"\n | \"integer\"\n | \"bigint\"\n | \"boolean\"\n | \"uuid\"\n | \"timestamp\"\n | \"jsonb\"\n | \"enum\";\n\nexport interface ColumnRef {\n readonly __isColumnRef: true;\n readonly tableName: string;\n readonly columnName: string;\n}\n\nexport type FkRef = ColumnRef | (() => ColumnRef);\n\nexport interface ResolvedForeignKey {\n readonly targetTable: string;\n readonly targetColumn: string;\n readonly onDelete?: ReferentialAction;\n readonly onUpdate?: ReferentialAction;\n}\n\n/** Mutable declaration state. It is cloned per table and never published. */\nexport interface MutableColumnMeta {\n name: string;\n columnName: string;\n kind: ColumnValueKind;\n storageKind: StorageKind;\n notNull: boolean;\n primaryKey: boolean;\n unique: boolean;\n isPrivate: boolean;\n serverGenerated: boolean;\n hasDefault: boolean;\n defaultValue?: string | number | boolean;\n defaultNow?: boolean;\n defaultRandom?: boolean;\n withTimezone?: boolean;\n varcharLength?: number;\n enumName?: string;\n enumValues?: readonly string[];\n fkRef?: FkRef;\n onDelete?: ReferentialAction;\n onUpdate?: ReferentialAction;\n fk?: ResolvedForeignKey;\n engineColumn?: EngineColumn;\n}\n\n/** Immutable column metadata published by a finalized schema. */\nexport type ColumnMeta = Readonly<\n Omit<MutableColumnMeta, \"enumValues\" | \"fk\" | \"fkRef\" | \"engineColumn\"> & {\n readonly enumValues?: readonly string[];\n readonly fk?: Readonly<ResolvedForeignKey>;\n readonly engineColumn: EngineColumn;\n }\n>;\n\nexport interface ResolvedRelation {\n readonly name: string;\n readonly cardinality: \"toOne\" | \"toMany\";\n readonly localColumn: string;\n readonly targetTable: string;\n readonly targetColumn: string;\n readonly inferred: boolean;\n}\n\nexport interface AppKitTable {\n readonly $name: string;\n readonly $schemaName: string;\n readonly $columns: Readonly<Record<string, ColumnMeta>>;\n readonly $engine: EngineTable;\n readonly $relations: readonly ResolvedRelation[];\n /** @internal insert schema retained from the current-main foundation. */\n readonly $insertSchema: unknown;\n /** @internal update schema retained from the current-main foundation. */\n readonly $updateSchema: unknown;\n}\n\n/** A declaration handle gains finalized table metadata only at publication. */\nexport type TableHandle<C extends Record<string, unknown>> = AppKitTable & {\n readonly [K in keyof C]: ColumnRef;\n};\n\nexport interface DefineSchemaOptions {\n readonly schemaName?: string;\n}\n\nexport interface Schema {\n readonly $schemaName: string;\n readonly $tables: Readonly<Record<
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../src/database/schema-builder/types.ts"],"sourcesContent":["import type { FilterOperator } from \"../contract\";\n\ndeclare const ENGINE_TABLE: unique symbol;\ndeclare const ENGINE_COLUMN: unique symbol;\n/** Opaque handles keep Drizzle types behind the schema/runtime boundary. */\nexport type EngineTable = { readonly [ENGINE_TABLE]: true };\nexport type EngineColumn = { readonly [ENGINE_COLUMN]: true };\n\n/** JavaScript value category exposed by a column, independent of its storage. */\nexport type ColumnValueKind =\n | \"string\"\n | \"number\"\n | \"bigint\"\n | \"boolean\"\n | \"date\"\n | \"json\"\n | \"uuid\"\n | \"enum\"\n | \"unknown\";\n\n/** The filter subset shared by runtime translation and later typed surfaces. */\nexport function filterOperatorsForKind(\n kind: ColumnValueKind,\n): readonly FilterOperator[] {\n switch (kind) {\n case \"string\":\n return [\"eq\", \"neq\", \"in\", \"like\", \"ilike\"];\n case \"number\":\n case \"bigint\":\n case \"date\":\n return [\"eq\", \"neq\", \"in\", \"gt\", \"gte\", \"lt\", \"lte\"];\n case \"enum\":\n case \"boolean\":\n case \"uuid\":\n return [\"eq\", \"neq\", \"in\"];\n case \"json\":\n case \"unknown\":\n return [];\n }\n}\n\n/** PostgreSQL action applied when a referenced row changes or is deleted. */\nexport type ReferentialAction =\n | \"cascade\"\n | \"set null\"\n | \"set default\"\n | \"restrict\"\n | \"no action\";\n\n/** DSL declaration kind, including identity shorthand and unresolved FKs. */\nexport type ColumnTypeSpec =\n | { readonly kind: \"id\" }\n | { readonly kind: \"bigid\" }\n | { readonly kind: \"text\" }\n | { readonly kind: \"varchar\"; readonly length: number }\n | { readonly kind: \"integer\" }\n | { readonly kind: \"bigint\" }\n | { readonly kind: \"boolean\" }\n | { readonly kind: \"uuid\" }\n | { readonly kind: \"timestamp\"; readonly withTimezone: boolean }\n | { readonly kind: \"jsonb\" }\n | {\n readonly kind: \"enum\";\n readonly enumName: string;\n readonly values: readonly string[];\n }\n | { readonly kind: \"fk\" };\n\n/** Resolved PostgreSQL storage used to construct the engine column. */\nexport type StorageKind =\n | \"id\"\n | \"bigid\"\n | \"text\"\n | \"varchar\"\n | \"integer\"\n | \"bigint\"\n | \"boolean\"\n | \"uuid\"\n | \"timestamp\"\n | \"jsonb\"\n | \"enum\";\n\nexport interface ColumnRef {\n readonly __isColumnRef: true;\n readonly tableName: string;\n readonly columnName: string;\n}\n\nexport type FkRef = ColumnRef | (() => ColumnRef);\n\nexport interface ResolvedForeignKey {\n readonly targetTable: string;\n readonly targetColumn: string;\n readonly onDelete?: ReferentialAction;\n readonly onUpdate?: ReferentialAction;\n}\n\n/** Mutable declaration state. It is cloned per table and never published. */\nexport interface MutableColumnMeta {\n name: string;\n columnName: string;\n kind: ColumnValueKind;\n storageKind: StorageKind;\n notNull: boolean;\n primaryKey: boolean;\n unique: boolean;\n isPrivate: boolean;\n serverGenerated: boolean;\n hasDefault: boolean;\n defaultValue?: string | number | boolean;\n defaultNow?: boolean;\n defaultRandom?: boolean;\n withTimezone?: boolean;\n varcharLength?: number;\n enumName?: string;\n enumValues?: readonly string[];\n fkRef?: FkRef;\n onDelete?: ReferentialAction;\n onUpdate?: ReferentialAction;\n fk?: ResolvedForeignKey;\n engineColumn?: EngineColumn;\n}\n\n/** Immutable column metadata published by a finalized schema. */\nexport type ColumnMeta = Readonly<\n Omit<MutableColumnMeta, \"enumValues\" | \"fk\" | \"fkRef\" | \"engineColumn\"> & {\n readonly enumValues?: readonly string[];\n readonly fk?: Readonly<ResolvedForeignKey>;\n readonly engineColumn: EngineColumn;\n }\n>;\n\nexport interface ResolvedRelation {\n readonly name: string;\n readonly cardinality: \"toOne\" | \"toMany\";\n readonly localColumn: string;\n readonly targetTable: string;\n readonly targetColumn: string;\n readonly inferred: boolean;\n}\n\nexport interface AppKitTable {\n readonly $name: string;\n readonly $schemaName: string;\n readonly $columns: Readonly<Record<string, ColumnMeta>>;\n readonly $engine: EngineTable;\n readonly $relations: readonly ResolvedRelation[];\n /** @internal insert schema retained from the current-main foundation. */\n readonly $insertSchema: unknown;\n /** @internal update schema retained from the current-main foundation. */\n readonly $updateSchema: unknown;\n}\n\n/** A declaration handle gains finalized table metadata only at publication. */\nexport type TableHandle<C extends Record<string, unknown>> = AppKitTable & {\n readonly [K in keyof C]: ColumnRef;\n};\n\nexport interface DefineSchemaOptions {\n readonly schemaName?: string;\n}\n\n/**\n * One finalized schema. `TTableName` keeps the declared names in the type, so\n * configuration that addresses a table by name is checked against the schema\n * it was written for. Code that accepts any schema uses the default.\n */\nexport interface Schema<TTableName extends string = string> {\n readonly $schemaName: string;\n readonly $tables: Readonly<Record<TTableName, AppKitTable>>;\n readonly $engine: Readonly<Record<string, EngineTable>>;\n}\n\nexport class SchemaBuildError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SchemaBuildError\";\n }\n}\n"],"mappings":";;AAqBA,SAAgB,uBACd,MAC2B;AAC3B,SAAQ,MAAR;EACE,KAAK,SACH,QAAO;GAAC;GAAM;GAAO;GAAM;GAAQ;GAAQ;EAC7C,KAAK;EACL,KAAK;EACL,KAAK,OACH,QAAO;GAAC;GAAM;GAAO;GAAM;GAAM;GAAO;GAAM;GAAM;EACtD,KAAK;EACL,KAAK;EACL,KAAK,OACH,QAAO;GAAC;GAAM;GAAO;GAAK;EAC5B,KAAK;EACL,KAAK,UACH,QAAO,EAAE;;;AAwIf,IAAa,mBAAb,cAAsC,MAAM;CAC1C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO"}
|
|
@@ -40,6 +40,12 @@ declare class AgentsPlugin extends Plugin implements ToolProvider {
|
|
|
40
40
|
* negative, or `NaN`) can't degrade into immediate auto-denial of every
|
|
41
41
|
* mutating tool call.
|
|
42
42
|
*/
|
|
43
|
+
/**
|
|
44
|
+
* Shared global skill pool (bundle `skills/` + catalog volume), loaded once
|
|
45
|
+
* per registry build. Read by {@link buildRegisteredAgent} to resolve each
|
|
46
|
+
* agent's visible catalog and by the live `register` path.
|
|
47
|
+
*/
|
|
48
|
+
private globalSkills;
|
|
43
49
|
private cachedApprovalPolicy;
|
|
44
50
|
private get resolvedApprovalPolicy();
|
|
45
51
|
/** Effective DoS limits with defaults applied. */
|
|
@@ -108,6 +114,35 @@ declare class AgentsPlugin extends Plugin implements ToolProvider {
|
|
|
108
114
|
*/
|
|
109
115
|
private pluginProviderIndex;
|
|
110
116
|
private buildRegisteredAgent;
|
|
117
|
+
/** Loads the shared global skill pool from `<agentsDir>/skills/`. */
|
|
118
|
+
private loadGlobalSkills;
|
|
119
|
+
/** Configured catalog-skills volume path, or null when none is set. */
|
|
120
|
+
private resolveSkillsVolume;
|
|
121
|
+
/**
|
|
122
|
+
* Workspace client used to read catalog skills. v1 reads as the app service
|
|
123
|
+
* principal; `getWorkspaceClient()` resolves to SP outside a user scope
|
|
124
|
+
* (boot and skill-tool dispatch are both unscoped). This is the single
|
|
125
|
+
* switch point for a future OBO mode.
|
|
126
|
+
*/
|
|
127
|
+
private skillWorkspaceClient;
|
|
128
|
+
/**
|
|
129
|
+
* Discovers catalog skills from the configured UC Volume, read as the
|
|
130
|
+
* service principal at boot (and on `reload()`). Each `<volume>/<name>/`
|
|
131
|
+
* folder with a `SKILL.md` becomes a `source: "volume"` skill. Best-effort:
|
|
132
|
+
* a missing volume, unavailable workspace client, or a malformed individual
|
|
133
|
+
* skill is logged and skipped rather than failing the whole registry build.
|
|
134
|
+
*/
|
|
135
|
+
private loadVolumeSkills;
|
|
136
|
+
/** Lists a volume skill's resource files (one level, excluding SKILL.md). */
|
|
137
|
+
private listVolumeSkillFiles;
|
|
138
|
+
/**
|
|
139
|
+
* Resolves the per-agent skill catalog: loads this agent's private skills
|
|
140
|
+
* (`<agentsDir>/<id>/skills/`, file-origin only), then applies visibility
|
|
141
|
+
* (opt-in via `def.skills` or `autoInheritSkills`) and collision rules
|
|
142
|
+
* against the shared global pool. Returns `undefined` when nothing is
|
|
143
|
+
* visible so the prompt catalog and dispatch can cheaply skip skills.
|
|
144
|
+
*/
|
|
145
|
+
private resolveAgentSkills;
|
|
111
146
|
private resolveAdapter;
|
|
112
147
|
/**
|
|
113
148
|
* Resolves an agent's tool record into a per-agent dispatch index. Connects
|
|
@@ -214,6 +249,21 @@ declare class AgentsPlugin extends Plugin implements ToolProvider {
|
|
|
214
249
|
* sub-agent, so depth limits remain enforced.
|
|
215
250
|
*/
|
|
216
251
|
private dispatchToolCall;
|
|
252
|
+
/**
|
|
253
|
+
* Executes the built-in `load_skill` / `read_skill_file` tools against the
|
|
254
|
+
* agent's resolved skill catalog. `load_skill` returns a skill's body plus a
|
|
255
|
+
* manifest of bundled files; `read_skill_file` returns the contents of one
|
|
256
|
+
* of those files (bundle skills only in v1 — catalog-volume resource reads
|
|
257
|
+
* arrive with the volume source).
|
|
258
|
+
*/
|
|
259
|
+
private dispatchSkillTool;
|
|
260
|
+
/**
|
|
261
|
+
* Renders the prompt addendum for a force-loaded skill (`/skill-name`).
|
|
262
|
+
* Returns null when the agent has no catalog or the name doesn't resolve —
|
|
263
|
+
* an unusable request shouldn't fail the whole turn, so it's logged and the
|
|
264
|
+
* model proceeds with the catalog + `load_skill` still available.
|
|
265
|
+
*/
|
|
266
|
+
private renderForcedSkill;
|
|
217
267
|
/**
|
|
218
268
|
* Runs a sub-agent in response to an `agent-<key>` tool call. Returns the
|
|
219
269
|
* concatenated text output to hand back to the parent adapter as the tool
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","names":[],"sources":["../../../src/plugins/agents/agents.ts"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","names":[],"sources":["../../../src/plugins/agents/agents.ts"],"mappings":";;;;;;;;;;cAoKa,YAAA,SAAqB,MAAA,YAAkB,YAAA;EAAA,OAC3C,QAAA,EADiB,cAAA;EAAA,OAEjB,KAAA,EAAO,WAAA;EAAA,UAEI,MAAA,EAAQ,kBAAA;EAAA,QAElB,MAAA;EAAA,QACA,gBAAA;EAAA,QACA,aAAA;EARG;;;;;;EAAA,QAkBH,gBAAA;EAAA,QACA,SAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EA89BkB;EAAA,QA59BlB,0BAAA;EAoiCQ;EAAA,QAliCR,6BAAA;cAEI,MAAA,EAAQ,kBAAA;EAikE6B;;;;;;;;;;EA5lEC;;;;;EAAA,QA+D1C,YAAA;EAAA,QAEA,oBAAA;EAAA,YAKI,sBAAA,CAAA;EA/DJ;EAAA,YAyFI,cAAA,CAAA;EA9EJ;EAAA,QAiGA,gBAAA;EA/FA;;;;;;EAAA,QAyGA,WAAA;EA9DA;;;;;EAAA,QA+EA,aAAA;EAYF,KAAA,CAAA,GAAK,OAAA;EAAL;;;;;;EAeA,MAAA,CAAA,GAAU,OAAA;EAoLR;;;;;EAAA,QA1JM,kBAAA;EAgSA;;;;;EAAA,QAnLN,mBAAA;EA4UM;;;;EAAA,QA9SN,uBAAA;EAsiBM;;;;EAAA,QAvhBN,0BAAA;EAAA,QAUA,iBAAA;EAwpBA;;;;;;EAAA,QA9oBM,cAAA;EAozBN;EAAA,QAvxBA,mBAAA;EAAA,QAeM,mBAAA;EA+lCA;;;;;;EAAA,QA5iCN,mBAAA;EAAA,QAmBM,oBAAA;EA+jDA;EAAA,QAriDA,gBAAA;EA4jDN;EAAA,QArjDA,mBAAA;EA8kDF;;;;;;EAAA,QAlkDE,oBAAA;EA4kDyC;;;;;;;EAAA,QAjkDnC,gBAAA;;UA0EA,oBAAA;EA6/CiB;;;;;AAyMjC;;EAzMiC,QAt+CjB,kBAAA;EAAA,QA4BA,cAAA;EAmpDG;;;;;EAAA,QAvmDH,cAAA;EAumDG;;;;;;;;;;;EAAA,QAr+CT,eAAA;;;;;;;;;;;;;UA4BA,eAAA;EAAA,QAkBM,gBAAA;EAAA,QA0DA,kBAAA;EAiEd,aAAA,CAAA,GAAiB,mBAAA;EAIX,gBAAA,CAAA,GAAoB,OAAA;;;;;;;UAYlB,iBAAA;EAUR,YAAA,CAAa,MAAA,EAAQ,UAAA;EAkDrB,YAAA,CAAA,GAAgB,MAAA;EAAA,QAeF,WAAA;;;;;;;;;;;;UA2FN,gCAAA;;;;;;;;;;;UAuBM,aAAA;EAAA,QA2FA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;UAqOA,qBAAA;;;;;;;;;;;;UA0KA,gBAAA;;;;;;;;UA8HA,iBAAA;;;;;;;UAkDN,iBAAA;;;;;;;;;;;;;;;UAgCM,WAAA;EAAA,QAiGA,aAAA;EAAA,QA2BA,cAAA;EAAA,QAuCA,kBAAA;EAAA,QASA,gBAAA;EAAA,QAUA,mBAAA;EAAA,QAaN,YAAA;EAAA,QASA,aAAA;EAgBF,QAAA,CAAA,GAAY,OAAA;EAQlB,OAAA,CAAA;6BAE2B,GAAA,EAAO,eAAA,KAAe,OAAA;;2BAG3B,eAAA;;;oCAGS,OAAA,CAAA,MAAA;EAAA;EAAA,QAIjB,iBAAA;AAAA;;;;;;;;;;;;;;;;;;cAqMH,MAAA,EAAM,QAAA,QAAA,YAAA,EAAA,kBAAA"}
|