{"version":3,"file":"pg.cjs","names":[],"sources":["../../../../src/adapters/drizzle/schema/pg.ts"],"sourcesContent":["import { sql } from 'drizzle-orm'\nimport {\n  check,\n  foreignKey,\n  index,\n  integer,\n  jsonb,\n  pgEnum,\n  pgTable,\n  primaryKey,\n  text,\n  timestamp,\n  unique,\n} from 'drizzle-orm/pg-core'\nimport type { AccessControl, IamPrimitives } from '../../../core/types'\n\n/**\n * PostgreSQL schema for the duck-iam IamDrizzle adapter.\n *\n * Run `drizzle-kit generate` against this file to emit migrations.\n *\n * ### JSON storage\n * With the adapter's default `json: 'native'` mode, `rules`, `targets`,\n * `permissions`, `inherits`, `metadata`, and `data` hold real `jsonb` - so the\n * GIN indexes below work and payloads stay queryable. Columns are typed with\n * `$type<>()` for end-to-end safety. (Run the adapter in `json: 'string'` mode\n * only for text-column backends like SQLite.)\n *\n * ### Soft delete\n * No `deletedAt` columns: the adapter's `listRoles`/`listPolicies` do not\n * filter on deletion, so a soft-deleted role would keep granting access. Use\n * hard deletes (`deleteRole`/`deletePolicy`) to revoke.\n *\n * ### Audit\n * `created_by` / `updated_by` capture the actor behind a change. The adapter\n * has no actor context, so it leaves them NULL; set them from triggers or\n * direct admin writes.\n *\n * ### Constraint naming\n * pk_ primary key, fk_ foreign key, uq_ unique, idx_ index, ch_ check.\n * All declared in the table's (t) => [...] block.\n */\n\n/**\n * Postgres enum mirroring {@link AccessControl.CombiningAlgorithm}. The\n * `satisfies` clause turns any drift between this list and the engine union\n * into a compile error.\n */\nexport const combineAlgorithm = pgEnum('access_combine_algorithm', [\n  'deny-overrides',\n  'allow-overrides',\n  'first-match',\n  'highest-priority',\n] as const satisfies readonly AccessControl.CombiningAlgorithm[])\n\n/**\n * Stored ABAC policies. `rules` and `targets` carry the policy payload as\n * `jsonb`; `algorithm` is constrained to the engine's combining algorithms.\n */\nexport const iamPolicies = pgTable(\n  'access_policies',\n  {\n    id: text('id').notNull(),\n    name: text('name').notNull(),\n    description: text('description'),\n    version: integer('version').notNull().default(1),\n    algorithm: combineAlgorithm('algorithm').notNull().default('deny-overrides'),\n    rules: jsonb('rules').$type<AccessControl.IRule[]>().notNull(),\n    targets: jsonb('targets').$type<NonNullable<AccessControl.IPolicy['targets']>>(),\n    createdBy: text('created_by'),\n    updatedBy: text('updated_by'),\n    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n    updatedAt: timestamp('updated_at', { withTimezone: true })\n      .notNull()\n      .defaultNow()\n      .$onUpdate(() => new Date()),\n  },\n  (t) => [\n    primaryKey({ name: 'pk_access_policies', columns: [t.id] }),\n    unique('uq_access_policies_name').on(t.name),\n    // Containment search over rules, e.g. `rules @> '[{\"actions\":[\"read\"]}]'`.\n    index('idx_access_policies_rules_gin').using('gin', t.rules),\n    check('ch_access_policies_name_not_blank', sql`length(trim(${t.name})) > 0`),\n    check('ch_access_policies_version_positive', sql`${t.version} >= 1`),\n  ],\n)\n\n/**\n * Stored RBAC roles. `permissions` and `metadata` are `jsonb`; `inherits` is a\n * `jsonb` array of parent role IDs.\n */\nexport const iamRoles = pgTable(\n  'access_roles',\n  {\n    id: text('id').notNull(),\n    name: text('name').notNull(),\n    description: text('description'),\n    permissions: jsonb('permissions').$type<AccessControl.IPermission[]>().notNull(),\n    inherits: jsonb('inherits').$type<string[]>().notNull().default(sql`'[]'::jsonb`),\n    scope: text('scope'),\n    metadata: jsonb('metadata').$type<IamPrimitives.Attributes>(),\n    createdBy: text('created_by'),\n    updatedBy: text('updated_by'),\n    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n    updatedAt: timestamp('updated_at', { withTimezone: true })\n      .notNull()\n      .defaultNow()\n      .$onUpdate(() => new Date()),\n  },\n  (t) => [\n    primaryKey({ name: 'pk_access_roles', columns: [t.id] }),\n    // Role name is unique per scope; NULL (global) scopes collapse so two\n    // global roles cannot share a name.\n    unique('uq_access_roles_name_scope').on(t.name, t.scope).nullsNotDistinct(),\n    // Scoped roles only - global roles (NULL scope) are excluded to keep it small.\n    index('idx_access_roles_scope').on(t.scope).where(sql`${t.scope} IS NOT NULL`),\n    // Containment search over permissions, e.g. `permissions @> '[{\"resource\":\"post\"}]'`.\n    index('idx_access_roles_permissions_gin').using('gin', t.permissions),\n    check('ch_access_roles_name_not_blank', sql`length(trim(${t.name})) > 0`),\n    check('ch_access_roles_scope_not_blank', sql`${t.scope} IS NULL OR length(trim(${t.scope})) > 0`),\n  ],\n)\n\n/**\n * Subject-to-role assignments. A `NULL` scope is a global (unscoped)\n * assignment; a non-null scope binds the role to that tenant/scope. Unique on\n * `(subject_id, role_id, scope)` with NULL scopes collapsed, so `assignRole`'s\n * `onConflictDoNothing` is idempotent for global grants too.\n */\nexport const iamAssignments = pgTable(\n  'access_assignments',\n  {\n    id: text('id').$defaultFn(() => crypto.randomUUID()),\n    subjectId: text('subject_id').notNull(),\n    roleId: text('role_id').notNull(),\n    scope: text('scope'),\n    createdBy: text('created_by'),\n    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n  },\n  (t) => [\n    primaryKey({ name: 'pk_access_assignments', columns: [t.id] }),\n    foreignKey({\n      name: 'fk_access_assignments_role',\n      columns: [t.roleId],\n      foreignColumns: [iamRoles.id],\n    }).onDelete('cascade'),\n    unique('uq_access_assignments_subject_role_scope').on(t.subjectId, t.roleId, t.scope).nullsNotDistinct(),\n    index('idx_access_assignments_subject').on(t.subjectId),\n    index('idx_access_assignments_role').on(t.roleId),\n    // Scoped assignments only - speeds tenant-scoped lookups.\n    index('idx_access_assignments_subject_scope').on(t.subjectId, t.scope).where(sql`${t.scope} IS NOT NULL`),\n    check('ch_access_assignments_subject_not_blank', sql`length(trim(${t.subjectId})) > 0`),\n    check('ch_access_assignments_scope_not_blank', sql`${t.scope} IS NULL OR length(trim(${t.scope})) > 0`),\n  ],\n)\n\n/**\n * Per-subject attribute bags, one row per subject. `data` holds the\n * attribute map (`jsonb`) consumed by the ABAC condition engine.\n */\nexport const iamSubjectAttrs = pgTable(\n  'access_subject_attrs',\n  {\n    subjectId: text('subject_id').notNull(),\n    data: jsonb('data').$type<IamPrimitives.Attributes>().notNull(),\n    updatedBy: text('updated_by'),\n    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n    updatedAt: timestamp('updated_at', { withTimezone: true })\n      .notNull()\n      .defaultNow()\n      .$onUpdate(() => new Date()),\n  },\n  (t) => [\n    primaryKey({ name: 'pk_access_subject_attrs', columns: [t.subjectId] }),\n    check('ch_access_subject_attrs_subject_not_blank', sql`length(trim(${t.subjectId})) > 0`),\n  ],\n)\n"],"mappings":"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"}