@autonoma-ai/sdk-drizzle 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @autonoma-ai/sdk-drizzle
2
+
3
+ Drizzle ORM adapter for the Autonoma SDK. Introspects your Drizzle schema, creates records in FK order, and tears down scoped test data.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @autonoma-ai/sdk @autonoma-ai/sdk-drizzle
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { drizzleAdapter } from '@autonoma-ai/sdk-drizzle'
15
+ import { db } from '~/db'
16
+ import * as schema from '~/db/schema'
17
+
18
+ const adapter = drizzleAdapter(db, schema, { scopeField: 'organizationId' })
19
+ ```
20
+
21
+ Pass the adapter to your server handler:
22
+
23
+ ```typescript
24
+ // app/api/autonoma/route.ts
25
+ import { createHandler } from '@autonoma-ai/server-web'
26
+ import { drizzleAdapter } from '@autonoma-ai/sdk-drizzle'
27
+ import { db } from '~/db'
28
+ import * as schema from '~/db/schema'
29
+
30
+ export const POST = createHandler({
31
+ adapter: drizzleAdapter(db, schema, { scopeField: 'organizationId' }),
32
+ sharedSecret: process.env.AUTONOMA_SHARED_SECRET!,
33
+ signingSecret: process.env.AUTONOMA_SIGNING_SECRET!,
34
+ auth: async (user) => {
35
+ const session = await createSession(user.id as string)
36
+ return { token: session.token }
37
+ },
38
+ })
39
+ ```
40
+
41
+ ## Scope field
42
+
43
+ The scope field is the FK that most models use to reference a root tenant entity — usually `organizationId`, `orgId`, `tenantId`, or `workspaceId`.
44
+
45
+ During `up`: child records inherit the scope value automatically via nesting.
46
+ During `down`: the adapter deletes everything where `scopeField = <root entity ID>`.
47
+
48
+ ## Peer dependencies
49
+
50
+ Requires `drizzle-orm >= 0.30.0`.
51
+
52
+ ## Documentation
53
+
54
+ Full docs: [docs/](../../docs/) — see [setup guide](../../docs/setup.txt) and [validation](../../docs/validation.txt).
package/dist/index.js CHANGED
@@ -145,6 +145,12 @@ function drizzleAdapter(db, schema, config) {
145
145
  const schemaInfo = this.getSchema();
146
146
  const { eq } = await import("drizzle-orm");
147
147
  return teardown(db, tableMap, schemaInfo, scopeValue, eq);
148
+ },
149
+ async updateEntity(model, id, fields) {
150
+ const table = tableMap.get(model);
151
+ if (!table) throw new Error(`Model "${model}" not found in Drizzle schema`);
152
+ const { eq } = await import("drizzle-orm");
153
+ await db.update(table).set(fields).where(eq(table.id, id));
148
154
  }
149
155
  };
150
156
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/introspect.ts","../src/create.ts","../src/teardown.ts","../src/index.ts"],"sourcesContent":["import type { SchemaInfo, SchemaRelation, ModelInfo, FieldInfo, FKEdge } from '@autonoma-ai/sdk'\n\nexport interface DrizzleAdapterConfig {\n scopeField: string\n}\n\n// DrizzleTable is accessed dynamically — Symbol.for('drizzle:Name') can't be in an interface\ntype DrizzleTable = Record<string | symbol, unknown> & {\n _?: { name: string; columns: Record<string, DrizzleColumn> }\n}\n\ninterface DrizzleColumn {\n name: string\n dataType: string\n notNull: boolean\n primary: boolean\n hasDefault: boolean\n}\n\ninterface DrizzleRelation {\n fieldName: string\n referencedTableName: string\n columns?: Array<{ name: string }>\n foreignColumns?: Array<{ name: string }>\n isNullable?: boolean\n}\n\n/**\n * Introspect a Drizzle schema to extract schema metadata.\n */\nexport function introspectDrizzle(\n schema: Record<string, unknown>,\n config: DrizzleAdapterConfig,\n): SchemaInfo {\n const models: ModelInfo[] = []\n const edges: FKEdge[] = []\n const tables = new Map<string, DrizzleTable>()\n\n // Find all table objects in the schema export\n for (const [key, value] of Object.entries(schema)) {\n if (isTable(value)) {\n tables.set(key, value as DrizzleTable)\n }\n }\n\n for (const [key, table] of tables) {\n const tableName = getTableName(table, key)\n const columns = getTableColumns(table)\n const fields: FieldInfo[] = []\n\n for (const [colName, col] of Object.entries(columns)) {\n fields.push({\n name: colName,\n type: col.dataType,\n isRequired: col.notNull,\n isId: col.primary,\n hasDefault: col.hasDefault,\n })\n }\n\n models.push({ name: tableName, fields })\n }\n\n // Extract relations from the schema\n for (const [key, value] of Object.entries(schema)) {\n if (isRelations(value)) {\n const rels = extractRelations(value)\n for (const rel of rels) {\n if (rel.columns?.length && rel.foreignColumns?.length) {\n edges.push({\n from: getRelationSourceTable(key, tables),\n to: rel.referencedTableName,\n localField: rel.columns[0]!.name,\n foreignField: rel.foreignColumns[0]!.name,\n nullable: !rel.isNullable,\n })\n }\n }\n }\n }\n\n return { models, edges, relations: [], scopeField: config.scopeField }\n}\n\nfunction isTable(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n const v = value as Record<string | symbol, unknown>\n // Drizzle tables have a Symbol.for('drizzle:Name') or a _ property with columns\n return (\n Symbol.for('drizzle:Name') in v ||\n (v._ != null && typeof v._ === 'object' && 'columns' in (v._ as object))\n )\n}\n\nfunction isRelations(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n // Relations objects have a specific structure\n const v = value as Record<string, unknown>\n return v.dbName != null || v.table != null\n}\n\nfunction getTableName(table: DrizzleTable, fallback: string): string {\n const sym = Symbol.for('drizzle:Name')\n if (sym in table) return (table as any)[sym] as string\n if (table._?.name) return table._.name\n return fallback\n}\n\nfunction getTableColumns(table: DrizzleTable): Record<string, DrizzleColumn> {\n if (table._?.columns) return table._.columns\n // Fallback: inspect own properties that look like columns\n const cols: Record<string, DrizzleColumn> = {}\n for (const [key, val] of Object.entries(table)) {\n if (val && typeof val === 'object' && 'dataType' in val) {\n cols[key] = val as DrizzleColumn\n }\n }\n return cols\n}\n\nfunction extractRelations(_value: unknown): DrizzleRelation[] {\n // Drizzle relations are complex — for the prototype, return empty\n // Real implementation would traverse the relations() call tree\n return []\n}\n\nfunction getRelationSourceTable(\n key: string,\n tables: Map<string, DrizzleTable>,\n): string {\n // Convention: relations are named like `usersRelations` → `users`\n const match = key.match(/^(.+?)Relations$/)\n if (match) {\n const tableName = match[1]!\n if (tables.has(tableName)) return getTableName(tables.get(tableName)!, tableName)\n }\n return key\n}\n","import type { ResolvedEntitySpec, CreateContext } from '@autonoma-ai/sdk'\n\ntype DrizzleDB = Record<string, any> & {\n insert(table: any): { values(data: any): { returning(): Promise<any[]> } }\n}\n\n/**\n * Create entities using Drizzle ORM.\n */\nexport async function createEntities(\n db: DrizzleDB,\n tableMap: Map<string, unknown>,\n spec: Record<string, ResolvedEntitySpec>,\n _context: CreateContext,\n): Promise<Record<string, Record<string, unknown>[]>> {\n const results: Record<string, Record<string, unknown>[]> = {}\n\n for (const [model, entitySpec] of Object.entries(spec)) {\n const table = tableMap.get(model)\n if (!table) {\n throw new Error(`Drizzle table '${model}' not found in schema.`)\n }\n\n const created: Record<string, unknown>[] = []\n for (const fields of entitySpec.fields) {\n const [record] = await db.insert(table).values(fields).returning()\n if (record) created.push(record)\n }\n results[model] = created\n }\n\n return results\n}\n","import type { SchemaInfo } from '@autonoma-ai/sdk'\nimport { topoSort, findDeferrableEdge } from '@autonoma-ai/sdk'\n\ntype DrizzleDB = Record<string, any> & {\n delete(table: any): { where(condition: any): Promise<void> }\n update(table: any): { set(data: any): { where(condition: any): Promise<void> } }\n}\n\n/**\n * Tear down all data scoped to a value using Drizzle ORM.\n */\nexport async function teardown(\n db: DrizzleDB,\n tableMap: Map<string, unknown>,\n schema: SchemaInfo,\n scopeValue: string,\n eq: (col: any, val: any) => any,\n): Promise<void> {\n const modelNames = schema.models.map((m) => m.name)\n const { sorted, cycles } = topoSort(modelNames, schema.edges)\n\n // Break cycles by nullifying deferrable FKs\n for (const cycle of cycles) {\n const edge = findDeferrableEdge(cycle, schema.edges)\n if (edge) {\n const table = tableMap.get(edge.from)\n if (table) {\n await db\n .update(table)\n .set({ [edge.localField]: null })\n .where(eq((table as any)[schema.scopeField], scopeValue))\n }\n }\n }\n\n // Delete cycle nodes\n for (const cycle of cycles) {\n for (const model of cycle) {\n await deleteModel(db, tableMap, model, schema.scopeField, scopeValue, eq)\n }\n }\n\n // Delete in reverse topo order\n const reversed = [...sorted].reverse()\n for (const model of reversed) {\n await deleteModel(db, tableMap, model, schema.scopeField, scopeValue, eq)\n }\n}\n\nasync function deleteModel(\n db: DrizzleDB,\n tableMap: Map<string, unknown>,\n model: string,\n scopeField: string,\n scopeValue: string,\n eq: (col: any, val: any) => any,\n): Promise<void> {\n const table = tableMap.get(model)\n if (!table) return\n\n await db.delete(table).where(eq((table as any)[scopeField], scopeValue))\n}\n","import type { OrmAdapter, SchemaInfo, ResolvedEntitySpec, CreateContext } from '@autonoma-ai/sdk'\nimport { introspectDrizzle, type DrizzleAdapterConfig } from './introspect'\nimport { createEntities } from './create'\nimport { teardown } from './teardown'\n\nexport type { DrizzleAdapterConfig }\n\n/**\n * Create a Drizzle ORM adapter for the Autonoma SDK.\n *\n * @example\n * ```ts\n * import { drizzleAdapter } from '@autonoma-ai/sdk-drizzle'\n * import { db } from '~/db'\n * import * as schema from '~/db/schema'\n *\n * const adapter = drizzleAdapter(db, schema, { scopeField: 'organizationId' })\n * ```\n */\nexport function drizzleAdapter(\n db: any,\n schema: Record<string, unknown>,\n config: DrizzleAdapterConfig,\n): OrmAdapter {\n let cachedSchema: SchemaInfo | null = null\n const tableMap = buildTableMap(schema)\n\n return {\n getSchema() {\n if (!cachedSchema) {\n cachedSchema = introspectDrizzle(schema, config)\n }\n return cachedSchema\n },\n\n async createEntities(\n spec: Record<string, ResolvedEntitySpec>,\n context: CreateContext,\n ) {\n return createEntities(db, tableMap, spec, context)\n },\n\n async teardown(scopeValue: string) {\n const schemaInfo = this.getSchema()\n // Import eq from drizzle-orm dynamically\n const { eq } = await import('drizzle-orm')\n return teardown(db, tableMap, schemaInfo, scopeValue, eq)\n },\n }\n}\n\nfunction buildTableMap(schema: Record<string, unknown>): Map<string, unknown> {\n const map = new Map<string, unknown>()\n for (const [key, value] of Object.entries(schema)) {\n if (isTable(value)) {\n // Use the Drizzle table name or the export key\n const name = getTableName(value, key)\n map.set(name, value)\n }\n }\n return map\n}\n\nfunction isTable(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n const v = value as Record<string | symbol, unknown>\n return (\n Symbol.for('drizzle:Name') in v ||\n (v._ != null && typeof v._ === 'object' && 'columns' in (v._ as object))\n )\n}\n\nfunction getTableName(table: unknown, fallback: string): string {\n const sym = Symbol.for('drizzle:Name')\n if (table && typeof table === 'object' && sym in table) {\n return (table as any)[sym] as string\n }\n const t = table as any\n if (t._?.name) return t._.name\n return fallback\n}\n"],"mappings":"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","names":["isTable","getTableName"]}
1
+ {"version":3,"sources":["../src/introspect.ts","../src/create.ts","../src/teardown.ts","../src/index.ts"],"sourcesContent":["import type { SchemaInfo, SchemaRelation, ModelInfo, FieldInfo, FKEdge } from '@autonoma-ai/sdk'\n\nexport interface DrizzleAdapterConfig {\n scopeField: string\n}\n\n// DrizzleTable is accessed dynamically — Symbol.for('drizzle:Name') can't be in an interface\ntype DrizzleTable = Record<string | symbol, unknown> & {\n _?: { name: string; columns: Record<string, DrizzleColumn> }\n}\n\ninterface DrizzleColumn {\n name: string\n dataType: string\n notNull: boolean\n primary: boolean\n hasDefault: boolean\n}\n\ninterface DrizzleRelation {\n fieldName: string\n referencedTableName: string\n columns?: Array<{ name: string }>\n foreignColumns?: Array<{ name: string }>\n isNullable?: boolean\n}\n\n/**\n * Introspect a Drizzle schema to extract schema metadata.\n */\nexport function introspectDrizzle(\n schema: Record<string, unknown>,\n config: DrizzleAdapterConfig,\n): SchemaInfo {\n const models: ModelInfo[] = []\n const edges: FKEdge[] = []\n const tables = new Map<string, DrizzleTable>()\n\n // Find all table objects in the schema export\n for (const [key, value] of Object.entries(schema)) {\n if (isTable(value)) {\n tables.set(key, value as DrizzleTable)\n }\n }\n\n for (const [key, table] of tables) {\n const tableName = getTableName(table, key)\n const columns = getTableColumns(table)\n const fields: FieldInfo[] = []\n\n for (const [colName, col] of Object.entries(columns)) {\n fields.push({\n name: colName,\n type: col.dataType,\n isRequired: col.notNull,\n isId: col.primary,\n hasDefault: col.hasDefault,\n })\n }\n\n models.push({ name: tableName, fields })\n }\n\n // Extract relations from the schema\n for (const [key, value] of Object.entries(schema)) {\n if (isRelations(value)) {\n const rels = extractRelations(value)\n for (const rel of rels) {\n if (rel.columns?.length && rel.foreignColumns?.length) {\n edges.push({\n from: getRelationSourceTable(key, tables),\n to: rel.referencedTableName,\n localField: rel.columns[0]!.name,\n foreignField: rel.foreignColumns[0]!.name,\n nullable: !rel.isNullable,\n })\n }\n }\n }\n }\n\n return { models, edges, relations: [], scopeField: config.scopeField }\n}\n\nfunction isTable(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n const v = value as Record<string | symbol, unknown>\n // Drizzle tables have a Symbol.for('drizzle:Name') or a _ property with columns\n return (\n Symbol.for('drizzle:Name') in v ||\n (v._ != null && typeof v._ === 'object' && 'columns' in (v._ as object))\n )\n}\n\nfunction isRelations(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n // Relations objects have a specific structure\n const v = value as Record<string, unknown>\n return v.dbName != null || v.table != null\n}\n\nfunction getTableName(table: DrizzleTable, fallback: string): string {\n const sym = Symbol.for('drizzle:Name')\n if (sym in table) return (table as any)[sym] as string\n if (table._?.name) return table._.name\n return fallback\n}\n\nfunction getTableColumns(table: DrizzleTable): Record<string, DrizzleColumn> {\n if (table._?.columns) return table._.columns\n // Fallback: inspect own properties that look like columns\n const cols: Record<string, DrizzleColumn> = {}\n for (const [key, val] of Object.entries(table)) {\n if (val && typeof val === 'object' && 'dataType' in val) {\n cols[key] = val as DrizzleColumn\n }\n }\n return cols\n}\n\nfunction extractRelations(_value: unknown): DrizzleRelation[] {\n // Drizzle relations are complex — for the prototype, return empty\n // Real implementation would traverse the relations() call tree\n return []\n}\n\nfunction getRelationSourceTable(\n key: string,\n tables: Map<string, DrizzleTable>,\n): string {\n // Convention: relations are named like `usersRelations` → `users`\n const match = key.match(/^(.+?)Relations$/)\n if (match) {\n const tableName = match[1]!\n if (tables.has(tableName)) return getTableName(tables.get(tableName)!, tableName)\n }\n return key\n}\n","import type { ResolvedEntitySpec, CreateContext } from '@autonoma-ai/sdk'\n\ntype DrizzleDB = Record<string, any> & {\n insert(table: any): { values(data: any): { returning(): Promise<any[]> } }\n}\n\n/**\n * Create entities using Drizzle ORM.\n */\nexport async function createEntities(\n db: DrizzleDB,\n tableMap: Map<string, unknown>,\n spec: Record<string, ResolvedEntitySpec>,\n _context: CreateContext,\n): Promise<Record<string, Record<string, unknown>[]>> {\n const results: Record<string, Record<string, unknown>[]> = {}\n\n for (const [model, entitySpec] of Object.entries(spec)) {\n const table = tableMap.get(model)\n if (!table) {\n throw new Error(`Drizzle table '${model}' not found in schema.`)\n }\n\n const created: Record<string, unknown>[] = []\n for (const fields of entitySpec.fields) {\n const [record] = await db.insert(table).values(fields).returning()\n if (record) created.push(record)\n }\n results[model] = created\n }\n\n return results\n}\n","import type { SchemaInfo } from '@autonoma-ai/sdk'\nimport { topoSort, findDeferrableEdge } from '@autonoma-ai/sdk'\n\ntype DrizzleDB = Record<string, any> & {\n delete(table: any): { where(condition: any): Promise<void> }\n update(table: any): { set(data: any): { where(condition: any): Promise<void> } }\n}\n\n/**\n * Tear down all data scoped to a value using Drizzle ORM.\n */\nexport async function teardown(\n db: DrizzleDB,\n tableMap: Map<string, unknown>,\n schema: SchemaInfo,\n scopeValue: string,\n eq: (col: any, val: any) => any,\n): Promise<void> {\n const modelNames = schema.models.map((m) => m.name)\n const { sorted, cycles } = topoSort(modelNames, schema.edges)\n\n // Break cycles by nullifying deferrable FKs\n for (const cycle of cycles) {\n const edge = findDeferrableEdge(cycle, schema.edges)\n if (edge) {\n const table = tableMap.get(edge.from)\n if (table) {\n await db\n .update(table)\n .set({ [edge.localField]: null })\n .where(eq((table as any)[schema.scopeField], scopeValue))\n }\n }\n }\n\n // Delete cycle nodes\n for (const cycle of cycles) {\n for (const model of cycle) {\n await deleteModel(db, tableMap, model, schema.scopeField, scopeValue, eq)\n }\n }\n\n // Delete in reverse topo order\n const reversed = [...sorted].reverse()\n for (const model of reversed) {\n await deleteModel(db, tableMap, model, schema.scopeField, scopeValue, eq)\n }\n}\n\nasync function deleteModel(\n db: DrizzleDB,\n tableMap: Map<string, unknown>,\n model: string,\n scopeField: string,\n scopeValue: string,\n eq: (col: any, val: any) => any,\n): Promise<void> {\n const table = tableMap.get(model)\n if (!table) return\n\n await db.delete(table).where(eq((table as any)[scopeField], scopeValue))\n}\n","import type { OrmAdapter, SchemaInfo, ResolvedEntitySpec, CreateContext } from '@autonoma-ai/sdk'\nimport { introspectDrizzle, type DrizzleAdapterConfig } from './introspect'\nimport { createEntities } from './create'\nimport { teardown } from './teardown'\n\nexport type { DrizzleAdapterConfig }\n\n/**\n * Create a Drizzle ORM adapter for the Autonoma SDK.\n *\n * @example\n * ```ts\n * import { drizzleAdapter } from '@autonoma-ai/sdk-drizzle'\n * import { db } from '~/db'\n * import * as schema from '~/db/schema'\n *\n * const adapter = drizzleAdapter(db, schema, { scopeField: 'organizationId' })\n * ```\n */\nexport function drizzleAdapter(\n db: any,\n schema: Record<string, unknown>,\n config: DrizzleAdapterConfig,\n): OrmAdapter {\n let cachedSchema: SchemaInfo | null = null\n const tableMap = buildTableMap(schema)\n\n return {\n getSchema() {\n if (!cachedSchema) {\n cachedSchema = introspectDrizzle(schema, config)\n }\n return cachedSchema\n },\n\n async createEntities(\n spec: Record<string, ResolvedEntitySpec>,\n context: CreateContext,\n ) {\n return createEntities(db, tableMap, spec, context)\n },\n\n async teardown(scopeValue: string) {\n const schemaInfo = this.getSchema()\n // Import eq from drizzle-orm dynamically\n const { eq } = await import('drizzle-orm')\n return teardown(db, tableMap, schemaInfo, scopeValue, eq)\n },\n\n async updateEntity(model: string, id: string, fields: Record<string, unknown>) {\n const table = tableMap.get(model)\n if (!table) throw new Error(`Model \"${model}\" not found in Drizzle schema`)\n const { eq } = await import('drizzle-orm')\n await db.update(table).set(fields).where(eq((table as any).id, id))\n },\n }\n}\n\nfunction buildTableMap(schema: Record<string, unknown>): Map<string, unknown> {\n const map = new Map<string, unknown>()\n for (const [key, value] of Object.entries(schema)) {\n if (isTable(value)) {\n // Use the Drizzle table name or the export key\n const name = getTableName(value, key)\n map.set(name, value)\n }\n }\n return map\n}\n\nfunction isTable(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n const v = value as Record<string | symbol, unknown>\n return (\n Symbol.for('drizzle:Name') in v ||\n (v._ != null && typeof v._ === 'object' && 'columns' in (v._ as object))\n )\n}\n\nfunction getTableName(table: unknown, fallback: string): string {\n const sym = Symbol.for('drizzle:Name')\n if (table && typeof table === 'object' && sym in table) {\n return (table as any)[sym] as string\n }\n const t = table as any\n if (t._?.name) return t._.name\n return fallback\n}\n"],"mappings":"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","names":["isTable","getTableName"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autonoma-ai/sdk-drizzle",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Drizzle adapter for Autonoma SDK",
5
5
  "type": "module",
6
6
  "exports": {
@@ -16,7 +16,7 @@
16
16
  "access": "public"
17
17
  },
18
18
  "dependencies": {
19
- "@autonoma-ai/sdk": "0.1.0"
19
+ "@autonoma-ai/sdk": "0.1.2"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "drizzle-orm": ">=0.30.0"