@happyvertical/smrt-core 0.47.0 → 0.47.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/AGENTS.md +2 -0
- package/agents/generators.md +19 -5
- package/agents/registry-snapshot.md +28 -0
- package/agents/schema-paths.md +12 -0
- package/agents/system-diagnostics.md +43 -0
- package/dist/browser.js +4 -2
- package/dist/change-feed.d.ts +8 -0
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +1 -1
- package/dist/change-feed.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/migrations/differ.d.ts +78 -0
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +191 -1
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/generator.js +2 -1
- package/dist/migrations/generator.js.map +1 -1
- package/dist/postgres-permissions.d.ts +3 -0
- package/dist/postgres-permissions.d.ts.map +1 -1
- package/dist/postgres-permissions.js +49 -6
- package/dist/postgres-permissions.js.map +1 -1
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +4 -3
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +3 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/schema-builder.js +1 -1
- package/dist/registry.js +1 -1
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +22 -1
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/foreign-key-ddl.d.ts +18 -0
- package/dist/schema/foreign-key-ddl.d.ts.map +1 -1
- package/dist/schema/foreign-key-ddl.js +15 -2
- package/dist/schema/foreign-key-ddl.js.map +1 -1
- package/dist/schema/foreign-key-orphan-report.d.ts +57 -0
- package/dist/schema/foreign-key-orphan-report.d.ts.map +1 -0
- package/dist/schema/foreign-key-orphan-report.js +142 -0
- package/dist/schema/foreign-key-orphan-report.js.map +1 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -1
- package/dist/schema/live-parity.d.ts +8 -1
- package/dist/schema/live-parity.d.ts.map +1 -1
- package/dist/schema/live-parity.js +98 -2
- package/dist/schema/live-parity.js.map +1 -1
- package/dist/schema/types.d.ts +11 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema.js +2 -1
- package/dist/smrt-knowledge.json +22 -10
- package/dist/system/diagnostics.d.ts +299 -0
- package/dist/system/diagnostics.d.ts.map +1 -0
- package/dist/system/diagnostics.js +530 -0
- package/dist/system/diagnostics.js.map +1 -0
- package/dist/system/index.d.ts +2 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +3 -1
- package/dist/system/registry-snapshot.d.ts +112 -0
- package/dist/system/registry-snapshot.d.ts.map +1 -0
- package/dist/system/registry-snapshot.js +171 -0
- package/dist/system/registry-snapshot.js.map +1 -0
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +10 -2
- package/dist/testing/database.js.map +1 -1
- package/dist/utils/scanner-module.d.ts +16 -0
- package/dist/utils/scanner-module.d.ts.map +1 -1
- package/dist/vite-plugin/index.d.ts +34 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +85 -5
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts +6 -0
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +9 -4
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/dist/vite-plugin.js +2 -2
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-permissions.js","names":[],"sources":["../src/postgres-permissions.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION,\n CREATE_POSTGRES_CHANGE_FEED_DRAIN_FUNCTION,\n POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY,\n POSTGRES_CHANGE_FEED_DRAIN_BATCH,\n POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY,\n} from './system/schema.js';\n\n/** PostgreSQL ACLs only; this is not application authorization or RLS. */\nexport interface PostgresPermissionContract {\n schema: string;\n /** Defaults affect every future object created by migrationOwner in schema. */\n schemaExclusive: true;\n migrationOwner: string;\n runtimeRole: string;\n managedTables: string[];\n /** Exact zero-argument trigger functions bound only to managed tables. */\n managedTriggerFunctions?: string[];\n monitor?: { role: string; tables: Record<string, string[]> };\n}\n\nexport interface PostgresPermissionDiagnostic {\n code: string;\n severity: 'missing' | 'excessive' | 'unsupported';\n role?: string;\n resource: string;\n message: string;\n}\n\nexport interface PostgresPermissionPlan {\n contract: PostgresPermissionContract;\n diagnostics: PostgresPermissionDiagnostic[];\n statements: string[];\n canApply: boolean;\n fingerprint: string;\n limitations: string[];\n}\n\ntype NormalizedPostgresPermissionContract = PostgresPermissionContract & {\n managedTriggerFunctions: string[];\n};\n\ntype Executor = Pick<DatabaseInterface, 'query'>;\ninterface Row {\n [key: string]: unknown;\n oid: string;\n name: string;\n schema: string;\n identity: string;\n argument_types: string;\n argument_count: string;\n argument_defaults: string | null;\n return_type: string;\n result: string;\n cost: string;\n rows: string;\n support: string;\n language: string;\n security_definer: boolean;\n config: unknown;\n source: string;\n volatility: string;\n parallel: string;\n leakproof: boolean;\n strict: boolean;\n function_oid: string;\n table_name: string;\n enabled: string;\n internal: boolean;\n owner: string;\n acl: Acl[];\n kind: string;\n rolname: string;\n member: string;\n parent: string;\n relation: string;\n version: string;\n executor: string;\n superuser: boolean;\n rls: boolean;\n}\ntype Acl = {\n grantee: string;\n privilege: string;\n grantable: boolean;\n grantor?: string;\n};\nconst bookkeeping = new Set([\n '_smrt_migrations',\n '_smrt_schema_migrations',\n '_smrt_backfills',\n]);\nconst identifier = (value: string) => `\"${value.replaceAll('\"', '\"\"')}\"`;\nconst literal = (value: string) =>\n `E'${value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"''\")}'`;\nconst qualified = (schema: string, name: string) =>\n `${identifier(schema)}.${identifier(name)}`;\n\nconst normalizeFunctionSource = (value: string) =>\n value.replaceAll('\\r\\n', '\\n').trim();\nconst normalizeArgumentTypes = (value: string) =>\n value.replaceAll(/\\s*,\\s*/g, ',');\nconst normalizeFunctionResult = (value: string) =>\n value\n .replaceAll(/\\s+/g, ' ')\n .replaceAll(/\\s*,\\s*/g, ',')\n .replaceAll(/\\(\\s*/g, '(')\n .replaceAll(/\\s*\\)/g, ')')\n .trim()\n .toLowerCase();\n\nfunction sourceFromFunctionDdl(ddl: string): string {\n const match = ddl.match(\n /\\bAS\\s+\\$([A-Za-z_][A-Za-z0-9_]*)\\$\\n([\\s\\S]*?)\\n\\$\\1\\$;/,\n );\n if (!match)\n throw new Error('Framework function DDL has no dollar-quoted body.');\n return normalizeFunctionSource(match[2]);\n}\n\nfunction resultFromFunctionDdl(ddl: string): string {\n const match = ddl.match(/\\bRETURNS\\s+([\\s\\S]*?)\\nLANGUAGE\\s+/);\n if (!match) throw new Error('Framework function DDL has no return result.');\n return normalizeFunctionResult(match[1]);\n}\n\ntype FrameworkRoutine = {\n name: string;\n argumentTypes: string;\n argumentDefaults: string | null;\n result: string;\n source: string;\n};\n\nfunction frameworkRoutine(identity: string, ddl: string): FrameworkRoutine {\n const match = identity.match(/^([^()]+)\\((.*)\\)$/);\n if (!match)\n throw new Error(`Invalid framework function identity: ${identity}`);\n return {\n name: match[1],\n argumentTypes: normalizeArgumentTypes(match[2]),\n argumentDefaults:\n match[1] === '_smrt_drain_changes'\n ? String(POSTGRES_CHANGE_FEED_DRAIN_BATCH)\n : null,\n result: resultFromFunctionDdl(ddl),\n source: sourceFromFunctionDdl(ddl),\n };\n}\n\nconst frameworkRoutines = [\n frameworkRoutine(\n POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY,\n CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION,\n ),\n frameworkRoutine(\n POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY,\n CREATE_POSTGRES_CHANGE_FEED_DRAIN_FUNCTION,\n ),\n];\n\nfunction object(\n value: unknown,\n keys: string[],\n label: string,\n): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} must be an object.`);\n }\n for (const key of Object.keys(value)) {\n if (!keys.includes(key))\n throw new Error(`Unknown ${label} property: ${key}.`);\n }\n return value as Record<string, unknown>;\n}\nfunction name(value: unknown, label: string): string {\n if (\n typeof value !== 'string' ||\n !value.length ||\n value.includes('\\0') ||\n Buffer.byteLength(value) > 63\n ) {\n throw new Error(\n `${label} must be a nonempty PostgreSQL identifier (at most 63 bytes).`,\n );\n }\n return value;\n}\n/** Validate configuration before opening a connection; unknown options fail closed. */\nexport function validatePostgresPermissionContract(\n value: unknown,\n): NormalizedPostgresPermissionContract {\n const input = object(\n value,\n [\n 'schema',\n 'schemaExclusive',\n 'migrationOwner',\n 'runtimeRole',\n 'managedTables',\n 'managedTriggerFunctions',\n 'monitor',\n ],\n 'postgresPermissions',\n );\n if (input.schemaExclusive !== true)\n throw new Error(\n 'postgresPermissions.schemaExclusive must explicitly be true.',\n );\n const schema = name(input.schema, 'schema');\n if (schema === 'information_schema' || schema.startsWith('pg_'))\n throw new Error('A dedicated application schema is required.');\n const migrationOwner = name(input.migrationOwner, 'migrationOwner');\n const runtimeRole = name(input.runtimeRole, 'runtimeRole');\n if (!Array.isArray(input.managedTables))\n throw new Error('managedTables must be an array.');\n const managedTables = [\n ...new Set(\n input.managedTables.map((table) => name(table, 'managedTables entry')),\n ),\n ].sort();\n if (\n input.managedTriggerFunctions !== undefined &&\n !Array.isArray(input.managedTriggerFunctions)\n )\n throw new Error('managedTriggerFunctions must be an array.');\n const managedTriggerFunctions = [\n ...new Set(\n (input.managedTriggerFunctions ?? []).map((routine) =>\n name(routine, 'managedTriggerFunctions entry'),\n ),\n ),\n ].sort();\n let monitor: PostgresPermissionContract['monitor'];\n if (input.monitor !== undefined) {\n const entry = object(input.monitor, ['role', 'tables'], 'monitor');\n const role = name(entry.role, 'monitor.role');\n if (\n !entry.tables ||\n typeof entry.tables !== 'object' ||\n Array.isArray(entry.tables)\n )\n throw new Error('monitor.tables must map tables to column arrays.');\n const tables: Record<string, string[]> = Object.create(null);\n for (const [table, columns] of Object.entries(entry.tables).sort(\n ([a], [b]) => a.localeCompare(b),\n )) {\n name(table, 'monitor table');\n if (!Array.isArray(columns) || !columns.length)\n throw new Error(\n `monitor table ${table} requires a nonempty column array.`,\n );\n tables[table] = [\n ...new Set(columns.map((column) => name(column, 'monitor column'))),\n ].sort();\n }\n monitor = { role, tables };\n }\n const roles = [\n migrationOwner,\n runtimeRole,\n ...(monitor ? [monitor.role] : []),\n ];\n if (\n new Set(roles).size !== roles.length ||\n roles.some(\n (role) => role.toUpperCase() === 'PUBLIC' || role.startsWith('pg_'),\n )\n )\n throw new Error(\n 'Migration, runtime and monitor roles must be distinct non-system roles.',\n );\n return {\n schema,\n schemaExclusive: true,\n migrationOwner,\n runtimeRole,\n managedTables,\n managedTriggerFunctions,\n ...(monitor ? { monitor } : {}),\n };\n}\n\nconst acl = (expression: string) =>\n `COALESCE((SELECT json_agg(json_build_object('grantee', CASE WHEN a.grantee = 0 THEN 'PUBLIC' ELSE pg_get_userbyid(a.grantee) END, 'grantor', pg_get_userbyid(a.grantor), 'privilege', a.privilege_type, 'grantable', a.is_grantable) ORDER BY a.grantee, a.privilege_type, a.grantor) FROM aclexplode(${expression}) a), '[]'::json)`;\n\nasync function snapshot(\n db: Executor,\n contract: NormalizedPostgresPermissionContract,\n): Promise<Record<string, Row[]>> {\n const roleNames = [\n contract.migrationOwner,\n contract.runtimeRole,\n ...(contract.monitor ? [contract.monitor.role] : []),\n ]\n .map(literal)\n .join(',');\n const restrictedRoleNames = [\n contract.runtimeRole,\n ...(contract.monitor ? [contract.monitor.role] : []),\n ]\n .map(literal)\n .join(',');\n // information_schema.sql runs after initial ACL recording. Trust only its\n // explicitly PUBLIC-readable stock relations, never internal metadata views.\n // Normal-operation OIDs start at 16384:\n // https://www.postgresql.org/docs/16/system-catalog-initial-data.html\n // Source: postgres/postgres REL_14_STABLE..REL_18_STABLE,\n // GRANT SELECT [ON TABLE] ... TO PUBLIC in:\n // https://github.com/postgres/postgres/blob/REL_14_STABLE/src/backend/catalog/information_schema.sql\n // https://github.com/postgres/postgres/blob/REL_18_STABLE/src/backend/catalog/information_schema.sql\n const informationSchemaPublicSelect =\n \"'administrable_role_authorizations','applicable_roles','attributes','character_sets','check_constraint_routine_usage','check_constraints','collation_character_set_applicability','collations','column_column_usage','column_domain_usage','column_options','column_privileges','column_udt_usage','columns','constraint_column_usage','constraint_table_usage','data_type_privileges','domain_constraints','domain_udt_usage','domains','element_types','enabled_roles','foreign_data_wrapper_options','foreign_data_wrappers','foreign_server_options','foreign_servers','foreign_table_options','foreign_tables','information_schema_catalog_name','key_column_usage','parameters','referential_constraints','role_column_grants','role_routine_grants','role_table_grants','role_udt_grants','role_usage_grants','routine_column_usage','routine_privileges','routine_routine_usage','routine_sequence_usage','routine_table_usage','routines','schemata','sequences','sql_features','sql_implementation_info','sql_sizing','table_constraints','table_privileges','tables','triggered_update_columns','triggers','udt_privileges','usage_privileges','user_defined_types','user_mapping_options','user_mappings','view_column_usage','view_routine_usage','view_table_usage','views'\";\n // pg_init_privs records non-default initial ACLs. Missing entries use the\n // PostgreSQL object-kind default; extension-provided ACLs are not stock trust.\n // https://www.postgresql.org/docs/current/catalog-pg-init-privs.html\n const initialAcl = (fallback: string) =>\n `CASE WHEN i.privtype='i' THEN i.initprivs WHEN i.privtype='e' THEN NULL::aclitem[] ELSE ${fallback} END`;\n // Only a type referenced by another type's typarray is its generated array.\n // Nonzero typelem also describes non-array types (point); domains over arrays\n // remain independent privilege targets and must not be filtered out.\n const queries: Record<string, string> = {\n server: `SELECT current_setting('server_version_num') AS version, current_database() AS database, current_user AS executor, (SELECT rolsuper FROM pg_roles WHERE rolname=current_user) AS superuser`,\n roles: `SELECT oid::text, rolname, rolsuper, rolcreaterole, rolcreatedb, rolreplication, rolbypassrls FROM pg_roles WHERE rolname IN (${roleNames}) ORDER BY rolname`,\n memberships: `SELECT pg_get_userbyid(member) AS member, pg_get_userbyid(roleid) AS parent FROM pg_auth_members WHERE member IN (SELECT oid FROM pg_roles WHERE rolname IN (${roleNames})) OR roleid IN (SELECT oid FROM pg_roles WHERE rolname IN (${roleNames})) ORDER BY member, roleid`,\n database: `SELECT datname AS name, pg_get_userbyid(datdba) AS owner, ${acl(\"COALESCE(datacl, acldefault('d',datdba))\")} AS acl FROM pg_database WHERE datname=current_database()`,\n schemas: `SELECT nspname AS name, pg_get_userbyid(nspowner) AS owner, ${acl(\"COALESCE(nspacl, acldefault('n',nspowner))\")} AS acl FROM pg_namespace WHERE left(nspname,3) <> 'pg_' AND nspname <> 'information_schema' ORDER BY nspname`,\n relations: `SELECT c.oid::text, n.nspname AS schema, c.relname AS name, c.relkind AS kind, pg_get_userbyid(c.relowner) AS owner, c.relrowsecurity AS rls, ${acl(\"COALESCE(c.relacl, acldefault(CASE WHEN c.relkind='S' THEN 's'::\\\"char\\\" ELSE 'r'::\\\"char\\\" END,c.relowner))\")} AS acl FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' AND c.relkind IN ('r','p','v','m','f','S') ORDER BY n.nspname,c.relname`,\n columns: `SELECT c.oid::text AS relation, a.attname AS name, ${acl('a.attacl')} AS acl FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' AND c.relkind IN ('r','p','v','m','f') AND a.attnum>0 AND NOT a.attisdropped ORDER BY c.oid,a.attnum`,\n routines: `SELECT n.nspname AS schema, p.oid::text, p.proname AS name, format('%I.%I(%s)', n.nspname, p.proname, oidvectortypes(p.proargtypes)) AS identity, oidvectortypes(p.proargtypes) AS argument_types, pg_get_expr(p.proargdefaults,0) AS argument_defaults, p.prokind AS kind, p.prorettype::regtype::text AS return_type, pg_get_function_result(p.oid) AS result, p.procost::text AS cost, p.prorows::text AS rows, p.prosupport::regproc::text AS support, p.pronargs::text AS argument_count, l.lanname AS language, p.prosecdef AS security_definer, p.provolatile::text AS volatility, p.proparallel::text AS parallel, p.proleakproof AS leakproof, p.proisstrict AS strict, COALESCE(to_json(p.proconfig),'[]'::json) AS config, p.prosrc AS source, CASE WHEN p.prokind='f' THEN pg_get_functiondef(p.oid) END AS definition, pg_get_userbyid(p.proowner) AS owner, ${acl(\"COALESCE(p.proacl, acldefault('f',p.proowner))\")} AS acl FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace JOIN pg_language l ON l.oid=p.prolang WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' ORDER BY n.nspname,p.oid`,\n triggers: `SELECT t.oid::text, n.nspname AS schema, c.relname AS table_name, t.tgname AS name, t.tgfoid::text AS function_oid, t.tgenabled AS enabled, t.tgisinternal AS internal, t.tgtype::text AS type, encode(t.tgargs,'hex') AS arguments, pg_get_triggerdef(t.oid, true) AS definition FROM pg_trigger t JOIN pg_class c ON c.oid=t.tgrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' ORDER BY n.nspname,c.oid,t.oid`,\n types: `SELECT n.nspname AS schema, t.typname AS name, pg_get_userbyid(t.typowner) AS owner, ${acl(\"COALESCE(t.typacl, acldefault('T',t.typowner))\")} AS acl FROM pg_type t JOIN pg_namespace n ON n.oid=t.typnamespace LEFT JOIN pg_class c ON c.oid=t.typrelid WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' AND NOT EXISTS (SELECT 1 FROM pg_type element WHERE element.typarray=t.oid) AND (t.typrelid=0 OR c.relkind='c') ORDER BY n.nspname,t.typname`,\n systemSchemas: `SELECT nspname AS name, pg_get_userbyid(nspowner) AS owner, ${acl(\"COALESCE(nspacl, acldefault('n',nspowner))\")} AS acl FROM pg_namespace WHERE left(nspname,3)='pg_' OR nspname='information_schema' ORDER BY nspname`,\n systemPrivileges: `WITH resources AS (\n SELECT n.nspname AS schema, c.relname AS name, c.oid::text AS oid, CASE WHEN c.relkind='S' THEN 'sequence' ELSE 'relation' END AS kind, pg_get_userbyid(c.relowner) AS owner,\n COALESCE(c.relacl,acldefault(CASE WHEN c.relkind='S' THEN 's'::\"char\" ELSE 'r'::\"char\" END,c.relowner)) AS current_acl,\n CASE WHEN i.objoid IS NULL AND n.nspname='information_schema' AND c.oid<16384 AND c.relname IN (${informationSchemaPublicSelect}) THEN ARRAY[makeaclitem(0,c.relowner,'SELECT',false)] ELSE ${initialAcl(\"acldefault(CASE WHEN c.relkind='S' THEN 's'::\\\"char\\\" ELSE 'r'::\\\"char\\\" END,c.relowner)\")} END AS initial_acl\n FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace\n LEFT JOIN pg_init_privs i ON i.objoid=c.oid AND i.classoid='pg_class'::regclass AND i.objsubid=0\n WHERE (left(n.nspname,3)='pg_' OR n.nspname='information_schema') AND c.relkind IN ('r','p','v','m','f','S')\n UNION ALL\n SELECT n.nspname, c.relname || '.' || a.attname, c.oid::text || ':' || a.attnum::text, 'column', pg_get_userbyid(c.relowner),\n a.attacl, ${initialAcl('NULL::aclitem[]')}\n FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace\n LEFT JOIN pg_init_privs i ON i.objoid=c.oid AND i.classoid='pg_class'::regclass AND i.objsubid=a.attnum\n WHERE (left(n.nspname,3)='pg_' OR n.nspname='information_schema') AND a.attnum>0 AND NOT a.attisdropped\n UNION ALL\n SELECT n.nspname, p.proname, p.oid::text, 'routine', pg_get_userbyid(p.proowner), COALESCE(p.proacl,acldefault('f',p.proowner)), ${initialAcl(\"CASE WHEN p.oid<16384 THEN acldefault('f',p.proowner) ELSE NULL::aclitem[] END\")}\n FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace\n LEFT JOIN pg_init_privs i ON i.objoid=p.oid AND i.classoid='pg_proc'::regclass AND i.objsubid=0\n WHERE left(n.nspname,3)='pg_' OR n.nspname='information_schema'\n UNION ALL\n SELECT n.nspname, t.typname, t.oid::text, 'type', pg_get_userbyid(t.typowner), COALESCE(t.typacl,acldefault('T',t.typowner)), ${initialAcl(\"CASE WHEN t.oid<16384 THEN acldefault('T',t.typowner) ELSE NULL::aclitem[] END\")}\n FROM pg_type t JOIN pg_namespace n ON n.oid=t.typnamespace LEFT JOIN pg_class c ON c.oid=t.typrelid\n LEFT JOIN pg_init_privs i ON i.objoid=t.oid AND i.classoid='pg_type'::regclass AND i.objsubid=0\n WHERE (left(n.nspname,3)='pg_' OR n.nspname='information_schema') AND NOT EXISTS (SELECT 1 FROM pg_type element WHERE element.typarray=t.oid) AND (t.typrelid=0 OR c.relkind='c')\n ) SELECT r.schema,r.name,r.oid,r.kind,r.owner,COALESCE(d.acl,'[]'::json) AS acl FROM resources r\n CROSS JOIN LATERAL (\n SELECT json_agg(json_build_object('grantee', CASE WHEN a.grantee=0 THEN 'PUBLIC' ELSE pg_get_userbyid(a.grantee) END,\n 'grantor',pg_get_userbyid(a.grantor),'privilege',a.privilege_type,'grantable',a.is_grantable)\n ORDER BY a.grantee,a.privilege_type,a.grantor) AS acl\n FROM aclexplode(r.current_acl) a\n WHERE a.grantee IN (SELECT oid FROM pg_roles WHERE rolname IN (${restrictedRoleNames}))\n OR (a.grantee=0 AND NOT EXISTS (SELECT 1 FROM aclexplode(r.initial_acl) b WHERE b.grantee=0 AND b.privilege_type=a.privilege_type AND (b.is_grantable OR NOT a.is_grantable)))\n ) d WHERE d.acl IS NOT NULL OR (r.kind<>'column' AND r.owner IN (${restrictedRoleNames})) ORDER BY r.schema,r.kind,r.name,r.oid`,\n largeObjects: `SELECT oid::text, pg_get_userbyid(lomowner) AS owner, ${acl(\"COALESCE(lomacl, acldefault('L',lomowner))\")} AS acl FROM pg_largeobject_metadata ORDER BY oid`,\n largeObjectSettings: `SELECT setting,reset_val,source FROM pg_settings WHERE name='lo_compat_privileges'`,\n largeObjectRoleSettings: `SELECT s.setdatabase::text AS database, CASE WHEN s.setrole=0 THEN 'ALL ROLES' ELSE pg_get_userbyid(s.setrole) END AS role, split_part(config.value,'=',2) AS setting FROM pg_db_role_setting s CROSS JOIN LATERAL unnest(s.setconfig) config(value) WHERE s.setdatabase IN (0,(SELECT oid FROM pg_database WHERE datname=current_database())) AND (s.setrole=0 OR s.setrole IN (SELECT oid FROM pg_roles WHERE rolname IN (${restrictedRoleNames}))) AND split_part(config.value,'=',1)='lo_compat_privileges' ORDER BY s.setdatabase,s.setrole,config.value`,\n parameters: `SELECT parname AS name, ${acl('paracl')} AS acl FROM pg_parameter_acl ORDER BY parname`,\n defaults: `SELECT pg_get_userbyid(d.defaclrole) AS owner, COALESCE(n.nspname,'') AS schema, d.defaclobjtype AS kind, ${acl('d.defaclacl')} AS acl FROM pg_default_acl d LEFT JOIN pg_namespace n ON n.oid=d.defaclnamespace ORDER BY d.defaclrole,d.defaclnamespace,d.defaclobjtype`,\n };\n const result: Record<string, Row[]> = {};\n // A transaction executor must never have concurrent statements dispatched to it.\n for (const [key, query] of Object.entries(queries)) {\n // Parameter ACLs were introduced in PostgreSQL 15.\n if (key === 'parameters' && Number(result.server[0].version) < 150000) {\n result[key] = [];\n continue;\n }\n result[key] = (await db.query(query)).rows as Row[];\n }\n return result;\n}\n\nasync function plan(\n db: Executor,\n input: unknown,\n): Promise<PostgresPermissionPlan> {\n const contract = validatePostgresPermissionContract(input);\n const state = await snapshot(db, contract);\n const diagnostics: PostgresPermissionDiagnostic[] = [];\n const statements: string[] = [];\n const roles = [\n contract.runtimeRole,\n ...(contract.monitor ? [contract.monitor.role] : []),\n ];\n const add = (\n severity: PostgresPermissionDiagnostic['severity'],\n code: string,\n resource: string,\n message: string,\n role?: string,\n ) =>\n diagnostics.push({\n severity,\n code,\n resource,\n message,\n ...(role ? { role } : {}),\n });\n const unsupported = (\n code: string,\n resource: string,\n message: string,\n role?: string,\n ) => add('unsupported', code, resource, message, role);\n if (Number(state.server[0].version) < 140000)\n unsupported(\n 'server-version',\n 'database',\n 'PostgreSQL 14 or later is required.',\n );\n for (const role of [contract.migrationOwner, ...roles]) {\n const row = state.roles.find((entry) => entry.rolname === role);\n if (!row) {\n unsupported(\n 'missing-role',\n role,\n 'Infrastructure must create this role.',\n role,\n );\n continue;\n }\n if (\n roles.includes(role) &&\n [\n 'rolsuper',\n 'rolcreaterole',\n 'rolcreatedb',\n 'rolreplication',\n 'rolbypassrls',\n ].some((key) => row[key])\n )\n unsupported(\n 'privileged-role',\n role,\n 'Remove SUPERUSER, CREATEROLE, CREATEDB, REPLICATION and BYPASSRLS authority using infrastructure role management.',\n role,\n );\n }\n for (const membership of state.memberships)\n unsupported(\n 'role-membership',\n membership.member,\n `Role ${membership.member} is a member of ${membership.parent}; inbound and outbound membership, inherited privileges, and SET ROLE authority require infrastructure review. Memberships are never modified.`,\n membership.member,\n );\n const schema = state.schemas.find((entry) => entry.name === contract.schema);\n if (!schema)\n unsupported(\n 'missing-schema',\n contract.schema,\n 'Create the dedicated schema as the migration owner before reconciliation.',\n );\n else if (schema.owner !== contract.migrationOwner)\n unsupported(\n 'schema-owner',\n contract.schema,\n `Schema must be owned by ${contract.migrationOwner}; ownership is never changed.`,\n );\n const database = state.database[0];\n if (roles.includes(database.owner))\n unsupported(\n 'database-owner',\n database.name,\n 'Runtime and monitor must not own the database.',\n database.owner,\n );\n\n const relevant = (entries: Acl[], role: string) =>\n entries.filter(\n (entry) => entry.grantee === role || entry.grantee === 'PUBLIC',\n );\n function reconcile(\n kind: string,\n resource: string,\n entries: Acl[],\n role: string,\n desired: string[],\n suffix = '',\n ) {\n for (const entry of entries)\n if (entry.grantor === role && entry.grantee !== role)\n unsupported(\n 'dependent-grant',\n resource,\n `Configured role granted ${entry.privilege} to ${entry.grantee}; grant chains require infrastructure review to preserve other roles.`,\n role,\n );\n for (const entry of entries) {\n if (\n entry.grantee === 'PUBLIC' &&\n !(\n kind === 'DATABASE' &&\n entry.privilege === 'CONNECT' &&\n !entry.grantable\n )\n ) {\n unsupported(\n 'public-privilege',\n resource,\n `PUBLIC ${entry.privilege} is outside the explicit role contract; infrastructure must review it without changing unrelated access.`,\n role,\n );\n }\n }\n const effective = relevant(entries, role);\n const excess = effective.filter(\n (entry) => !desired.includes(entry.privilege) || entry.grantable,\n );\n const missing = desired.filter(\n (privilege) =>\n !entries.some(\n (entry) => entry.grantee === role && entry.privilege === privilege,\n ),\n );\n for (const entry of excess) {\n add(\n 'excessive',\n 'excessive-privilege',\n resource,\n `${entry.privilege}${entry.grantable ? ' WITH GRANT OPTION' : ''} is not declared (source: ${entry.grantee}).`,\n role,\n );\n if (entry.grantee === 'PUBLIC')\n unsupported(\n 'public-privilege',\n resource,\n `PUBLIC ${entry.privilege} requires infrastructure review; revoking it would affect other roles.`,\n role,\n );\n }\n for (const privilege of missing)\n add(\n 'missing',\n 'missing-privilege',\n resource,\n `${privilege} is required.`,\n role,\n );\n if (!excess.length && !missing.length) return;\n statements.push(\n `REVOKE ALL PRIVILEGES${suffix} ON ${kind} ${resource} FROM ${identifier(role)}`,\n );\n if (desired.length)\n statements.push(\n `GRANT ${desired.join(', ')}${suffix} ON ${kind} ${resource} TO ${identifier(role)}`,\n );\n }\n function reconcileRoutine(\n resource: string,\n entries: Acl[],\n desiredByRole: ReadonlyMap<string, readonly string[]>,\n ) {\n const grantee = (role: string) =>\n role === 'PUBLIC' ? 'PUBLIC' : identifier(role);\n for (const entry of entries)\n if (\n roles.includes(entry.grantor ?? '') &&\n entry.grantee !== entry.grantor\n )\n unsupported(\n 'dependent-routine-grant',\n resource,\n `Configured role granted ${entry.privilege} to ${entry.grantee}; grant chains require infrastructure review to preserve other roles.`,\n entry.grantor,\n );\n const managedGrantees = new Set([\n contract.migrationOwner,\n ...roles,\n 'PUBLIC',\n ]);\n for (const entry of entries)\n if (!managedGrantees.has(entry.grantee))\n unsupported(\n 'outside-routine-grant',\n resource,\n `${entry.grantee} has ${entry.privilege}; direct routine access outside the declared roles requires infrastructure review.`,\n entry.grantee,\n );\n for (const role of [...roles, 'PUBLIC']) {\n const desired = desiredByRole.get(role) ?? [];\n const effective = entries.filter((entry) => entry.grantee === role);\n const excess = effective.filter(\n (entry) => !desired.includes(entry.privilege) || entry.grantable,\n );\n const missing = desired.filter(\n (privilege) =>\n !entries.some(\n (entry) => entry.grantee === role && entry.privilege === privilege,\n ),\n );\n for (const entry of excess)\n add(\n 'excessive',\n 'excessive-routine-privilege',\n resource,\n `${entry.privilege}${entry.grantable ? ' WITH GRANT OPTION' : ''} is not declared.`,\n role,\n );\n for (const privilege of missing)\n add(\n 'missing',\n 'missing-routine-privilege',\n resource,\n `${privilege} is required.`,\n role,\n );\n if (!excess.length && !missing.length) continue;\n statements.push(\n `REVOKE ALL PRIVILEGES ON FUNCTION ${resource} FROM ${grantee(role)}`,\n );\n if (desired.length)\n statements.push(\n `GRANT ${desired.join(', ')} ON FUNCTION ${resource} TO ${grantee(role)}`,\n );\n }\n }\n for (const role of roles) {\n reconcile('DATABASE', identifier(database.name), database.acl, role, [\n 'CONNECT',\n ]);\n for (const entry of state.schemas) {\n if (entry.owner === role)\n unsupported(\n 'resource-owner',\n entry.name,\n 'Configured role owns a schema and has DDL authority.',\n role,\n );\n if (entry.name === contract.schema)\n reconcile('SCHEMA', identifier(entry.name), entry.acl, role, ['USAGE']);\n else if (\n relevant(entry.acl, role).some((grant) => grant.privilege === 'CREATE')\n )\n unsupported(\n 'outside-schema-create',\n entry.name,\n 'Effective CREATE outside the dedicated schema requires infrastructure repair.',\n role,\n );\n }\n }\n for (const schema of state.systemSchemas) {\n for (const role of roles) {\n if (schema.owner === role) {\n unsupported(\n 'system-schema-owner',\n schema.name,\n 'Configured roles must not own system schemas; infrastructure must restore separate ownership. System schema ownership is never modified.',\n role,\n );\n }\n for (const grant of relevant(schema.acl, role)) {\n if (grant.privilege === 'CREATE') {\n unsupported(\n 'system-schema-authority',\n schema.name,\n `Effective CREATE on a system schema (source: ${grant.grantee}) gives DDL authority and requires infrastructure repair. System schema ACLs are never modified.`,\n role,\n );\n }\n }\n }\n }\n for (const resource of state.systemPrivileges) {\n if (resource.kind !== 'column' && roles.includes(resource.owner)) {\n unsupported(\n 'system-object-owner',\n qualified(resource.schema, resource.name),\n `Configured role owns a system ${resource.kind}; ownership retains implicit alteration and grant authority even when its ordinary ACLs are revoked. Infrastructure must restore ownership; SMRT never changes it.`,\n resource.owner,\n );\n }\n for (const grant of resource.acl) {\n unsupported(\n 'system-privilege-delta',\n qualified(resource.schema, resource.name),\n `${grant.grantee} has exceptional ${grant.privilege}${grant.grantable ? ' WITH GRANT OPTION' : ''} on a system ${resource.kind}; review infrastructure grants against PostgreSQL initial ACLs. System ACLs are never modified.`,\n grant.grantee === 'PUBLIC' ? undefined : grant.grantee,\n );\n }\n }\n for (const resource of state.largeObjects) {\n if (roles.includes(resource.owner)) {\n unsupported(\n 'large-object-owner',\n `large object ${resource.oid}`,\n 'Configured role owns a large object outside the declared table/column contract. Infrastructure must review ownership; SMRT never changes it.',\n resource.owner,\n );\n }\n for (const grant of resource.acl) {\n if (roles.includes(grant.grantee) || grant.grantee === 'PUBLIC') {\n unsupported(\n 'large-object-privilege',\n `large object ${resource.oid}`,\n `${grant.grantee} has ${grant.privilege}${grant.grantable ? ' WITH GRANT OPTION' : ''} outside the declared table/column contract. Infrastructure must review this ACL; SMRT neither reads payloads nor changes large-object permissions.`,\n grant.grantee === 'PUBLIC' ? undefined : grant.grantee,\n );\n }\n }\n }\n const explicitlyOff = (value: unknown) =>\n ['off', 'false', 'no', '0'].includes(String(value).toLowerCase());\n if (state.largeObjectSettings.length !== 1) {\n unsupported(\n 'large-object-compatibility',\n 'lo_compat_privileges',\n 'The large-object ACL compatibility setting could not be established; qualification requires an observable disabled setting.',\n );\n }\n for (const settings of state.largeObjectSettings) {\n if (\n !explicitlyOff(settings.setting) ||\n !explicitlyOff(settings.reset_val) ||\n !['default', 'configuration file', 'command line', 'database'].includes(\n String(settings.source),\n )\n ) {\n unsupported(\n 'large-object-compatibility',\n 'lo_compat_privileges',\n 'Large-object ACL checks must be enabled for every configured role. An enabled or operator-specific lo_compat_privileges override cannot establish that baseline; review deployment settings and reconnect without an operator-specific override.',\n );\n }\n }\n for (const settings of state.largeObjectRoleSettings) {\n if (!explicitlyOff(settings.setting)) {\n unsupported(\n 'large-object-compatibility',\n 'lo_compat_privileges',\n `Role/database defaults enable or do not establish large-object ACL checks (${String(settings.role)}, database OID ${String(settings.database)}). Infrastructure must disable compatibility mode before qualification.`,\n settings.role === 'ALL ROLES' ? undefined : String(settings.role),\n );\n }\n }\n for (const parameter of state.parameters) {\n for (const role of roles) {\n for (const grant of relevant(parameter.acl, role)) {\n unsupported(\n 'parameter-privilege',\n parameter.name,\n `Effective ${grant.privilege} on a PostgreSQL configuration parameter (source: ${grant.grantee}) requires infrastructure review; parameter ACLs are never modified.`,\n role,\n );\n }\n }\n }\n const tables = state.relations.filter(\n (entry) => entry.schema === contract.schema && entry.kind !== 'S',\n );\n for (const table of bookkeeping) {\n if (!tables.some((entry) => entry.name === table)) {\n unsupported(\n 'missing-bookkeeping-table',\n qualified(contract.schema, table),\n 'Bootstrap all framework bookkeeping tables as the migration owner before permission setup. Stop runtime and monitoring access during migrations or restores, then reconcile before reactivation.',\n );\n }\n }\n const declared = new Set(contract.managedTables);\n // Framework feature tables are owned by SMRT even when absent from manifests.\n for (const table of tables)\n if (table.name.startsWith('_smrt_')) declared.add(table.name);\n for (const table of declared)\n if (!tables.some((entry) => entry.name === table))\n unsupported(\n 'missing-table',\n qualified(contract.schema, table),\n 'Run supported schema migrations before reconciling permissions.',\n );\n for (const [table, columns] of Object.entries(\n contract.monitor?.tables ?? {},\n )) {\n const relation = tables.find((entry) => entry.name === table);\n if (!declared.has(table) || !relation)\n unsupported(\n 'monitor-table',\n table,\n 'Monitor tables must exist and belong to managedTables or framework tables.',\n );\n for (const column of columns)\n if (\n !relation ||\n !state.columns.some(\n (entry) => entry.relation === relation.oid && entry.name === column,\n )\n )\n unsupported(\n 'monitor-column',\n `${table}.${column}`,\n 'The declared monitoring column does not exist.',\n );\n }\n for (const relation of state.relations) {\n const resource = qualified(relation.schema, relation.name);\n const columns = state.columns.filter(\n (entry) => entry.relation === relation.oid,\n );\n if (relation.schema !== contract.schema) {\n for (const role of roles)\n if (\n relation.owner === role ||\n relevant(relation.acl, role).length ||\n columns.some((column) => relevant(column.acl, role).length)\n )\n unsupported(\n 'outside-resource-access',\n resource,\n 'Effective access to an unrelated relation requires infrastructure review; unrelated ACLs are preserved.',\n role,\n );\n continue;\n }\n if (relation.owner !== contract.migrationOwner)\n unsupported(\n 'resource-owner',\n resource,\n `Managed resources must be owned by ${contract.migrationOwner}; ownership is never changed.`,\n );\n if (relation.kind !== 'S' && !declared.has(relation.name))\n unsupported(\n 'undeclared-table',\n resource,\n 'The dedicated schema contains an undeclared table; defaults cannot safely cover a mixed schema.',\n );\n if (!['r', 'p', 'S'].includes(relation.kind))\n unsupported(\n 'unsupported-relation',\n resource,\n `Relation kind ${relation.kind} requires a separate security review.`,\n );\n if (relation.rls)\n unsupported(\n 'row-level-security',\n resource,\n 'RLS policies require a separate application authorization review; ACL verification cannot establish allowed row operations.',\n );\n for (const role of roles) {\n const isRuntime = role === contract.runtimeRole;\n const desired = isRuntime\n ? relation.kind === 'S'\n ? ['USAGE']\n : bookkeeping.has(relation.name)\n ? ['SELECT']\n : ['SELECT', 'INSERT', 'UPDATE', 'DELETE']\n : [];\n reconcile(\n relation.kind === 'S' ? 'SEQUENCE' : 'TABLE',\n resource,\n relation.acl,\n role,\n desired,\n );\n for (const column of columns) {\n const wanted =\n !isRuntime &&\n (contract.monitor?.tables[relation.name] ?? []).includes(column.name)\n ? ['SELECT']\n : [];\n reconcile(\n 'TABLE',\n resource,\n column.acl,\n role,\n wanted,\n ` (${identifier(column.name)})`,\n );\n }\n }\n }\n const triggerRoutineNames = new Set(contract.managedTriggerFunctions);\n const acceptedTriggerRoutineNames = new Set<string>();\n const routineConfigIsEmpty = (value: unknown) =>\n (Array.isArray(value) && value.length === 0) || value === '[]';\n for (const resource of state.routines) {\n const resourceName = qualified(resource.schema, resource.name);\n const framework = frameworkRoutines.find(\n (expected) =>\n resource.schema === contract.schema &&\n resource.name === expected.name &&\n normalizeArgumentTypes(resource.argument_types) ===\n expected.argumentTypes,\n );\n if (framework) {\n const valid =\n resource.owner === contract.migrationOwner &&\n resource.kind === 'f' &&\n resource.language === 'plpgsql' &&\n resource.security_definer === false &&\n resource.argument_defaults === framework.argumentDefaults &&\n normalizeFunctionResult(resource.result) === framework.result &&\n resource.cost === '100' &&\n resource.rows === '1000' &&\n resource.support === '-' &&\n resource.volatility === 'v' &&\n resource.parallel === 'u' &&\n resource.leakproof === false &&\n resource.strict === false &&\n routineConfigIsEmpty(resource.config) &&\n normalizeFunctionSource(String(resource.source)) === framework.source;\n if (!valid)\n unsupported(\n 'framework-routine-definition',\n resourceName,\n 'Framework routine must retain its canonical identity, owner, invoker security, defaults, execution properties, settings and generated body.',\n );\n else\n reconcileRoutine(\n resource.identity,\n resource.acl,\n new Map([[contract.runtimeRole, ['EXECUTE']]]),\n );\n continue;\n }\n const bindings = state.triggers.filter(\n (trigger) => trigger.function_oid === resource.oid,\n );\n const triggerRoutine =\n resource.schema === contract.schema &&\n triggerRoutineNames.has(resource.name) &&\n resource.kind === 'f' &&\n resource.return_type === 'trigger' &&\n resource.argument_types === '' &&\n resource.argument_count === '0' &&\n resource.owner === contract.migrationOwner &&\n resource.language === 'plpgsql' &&\n resource.security_definer === false &&\n routineConfigIsEmpty(resource.config) &&\n bindings.length > 0 &&\n bindings.every(\n (trigger) =>\n trigger.schema === contract.schema &&\n trigger.internal === false &&\n (trigger.enabled === 'O' || trigger.enabled === 'A') &&\n declared.has(trigger.table_name),\n );\n if (triggerRoutine) {\n acceptedTriggerRoutineNames.add(resource.name);\n reconcileRoutine(resource.identity, resource.acl, new Map());\n continue;\n }\n for (const role of roles)\n if (\n resource.schema === contract.schema ||\n resource.owner === role ||\n relevant(resource.acl, role).length\n )\n unsupported(\n 'unsupported-routines',\n resourceName,\n triggerRoutineNames.has(resource.name)\n ? 'Declared trigger routines must be migration-owner invoker PL/pgSQL functions returning trigger with no declared arguments, no function settings, and enabled non-internal bindings only to declared managed tables.'\n : 'User-defined routines are outside the supported table/sequence and declared trigger contract; review their effective privileges explicitly.',\n role,\n );\n }\n for (const name of triggerRoutineNames)\n if (!acceptedTriggerRoutineNames.has(name))\n unsupported(\n 'missing-managed-trigger-function',\n qualified(contract.schema, name),\n 'Run the migration that creates this exact managed trigger function and its enabled binding before reconciling permissions.',\n );\n for (const resource of state.types)\n for (const role of roles)\n if (\n resource.schema === contract.schema ||\n resource.owner === role ||\n relevant(resource.acl, role).length\n )\n unsupported(\n 'unsupported-types',\n qualified(resource.schema, resource.name),\n 'User-defined types are outside the supported table/sequence contract; review their effective privileges explicitly.',\n role,\n );\n // Global grants combine with schema grants: a schema REVOKE cannot cancel them.\n for (const defaults of state.defaults) {\n const resource = `DEFAULT ${defaults.kind} (${defaults.schema || 'global'})`;\n if (\n defaults.owner !== contract.migrationOwner &&\n defaults.acl.some((entry: Acl) => roles.includes(entry.grantee))\n )\n unsupported(\n 'other-creator-access',\n resource,\n `Creator ${defaults.owner} grants configured roles future access outside this contract.`,\n );\n if (\n defaults.schema !== contract.schema &&\n defaults.schema !== '' &&\n defaults.acl.some(\n (entry: Acl) =>\n entry.grantee === 'PUBLIC' || roles.includes(entry.grantee),\n )\n )\n unsupported(\n 'outside-default-privilege',\n resource,\n 'Defaults outside the managed schema grant future access and require infrastructure review.',\n );\n if (\n defaults.schema === '' &&\n defaults.acl.some(\n (entry: Acl) =>\n roles.includes(entry.grantee) ||\n (entry.grantee === 'PUBLIC' &&\n ['r', 'S', 'n'].includes(defaults.kind)),\n )\n )\n unsupported(\n 'global-default-privilege',\n resource,\n 'Global creator defaults grant configured roles or PUBLIC access and cannot be repaired within this schema.',\n );\n if (\n defaults.schema === contract.schema &&\n defaults.owner !== contract.migrationOwner\n )\n unsupported(\n 'other-creator-default',\n resource,\n `Defaults for creator ${defaults.owner} require separate ownership review.`,\n );\n if (\n defaults.schema === contract.schema &&\n !['r', 'S'].includes(defaults.kind) &&\n defaults.acl.some(\n (entry: Acl) =>\n roles.includes(entry.grantee) || entry.grantee === 'PUBLIC',\n )\n )\n unsupported(\n 'unsupported-default',\n resource,\n 'Only table and sequence schema defaults are supported.',\n );\n }\n for (const [kind, sqlKind, runtime] of [\n ['r', 'TABLES', ['SELECT', 'INSERT', 'UPDATE', 'DELETE']],\n ['S', 'SEQUENCES', ['USAGE']],\n ] as const) {\n const entry = state.defaults.find(\n (item) =>\n item.schema === contract.schema &&\n item.owner === contract.migrationOwner &&\n item.kind === kind,\n );\n if (entry?.acl.some((grant) => grant.grantee === 'PUBLIC')) {\n unsupported(\n 'public-default',\n `${contract.schema} ${sqlKind}`,\n 'PUBLIC creator defaults are outside the explicit role contract and require infrastructure review.',\n );\n }\n for (const role of roles) {\n const desired: readonly string[] =\n role === contract.runtimeRole ? runtime : [];\n const effective = relevant(entry?.acl ?? [], role);\n const extra = effective.filter(\n (grant) => !desired.includes(grant.privilege) || grant.grantable,\n );\n const missing = desired.filter(\n (privilege) =>\n !(entry?.acl ?? []).some(\n (grant) => grant.grantee === role && grant.privilege === privilege,\n ),\n );\n if (!extra.length && !missing.length) continue;\n for (const grant of extra) {\n add(\n 'excessive',\n 'default-excessive',\n `${contract.schema} ${sqlKind}`,\n `${grant.privilege} default from ${grant.grantee} is not declared.`,\n role,\n );\n if (grant.grantee === 'PUBLIC')\n unsupported(\n 'public-default',\n contract.schema,\n 'PUBLIC defaults affect other roles and require infrastructure repair.',\n );\n }\n for (const privilege of missing)\n add(\n 'missing',\n 'default-missing',\n `${contract.schema} ${sqlKind}`,\n `Creator ${contract.migrationOwner} must default ${privilege}.`,\n role,\n );\n const prefix = `ALTER DEFAULT PRIVILEGES FOR ROLE ${identifier(contract.migrationOwner)} IN SCHEMA ${identifier(contract.schema)}`;\n statements.push(\n `${prefix} REVOKE ALL PRIVILEGES ON ${sqlKind} FROM ${identifier(role)}`,\n );\n if (desired.length)\n statements.push(\n `${prefix} GRANT ${desired.join(', ')} ON ${sqlKind} TO ${identifier(role)}`,\n );\n }\n }\n const executor = state.server[0];\n if (\n statements.length &&\n !executor.superuser &&\n executor.executor !== contract.migrationOwner\n )\n unsupported(\n 'execution-authority',\n 'database',\n 'Apply requires the migration owner or a superuser; plan and apply must use the same actor.',\n );\n if (\n statements.some((sql) => sql.includes(' ON DATABASE ')) &&\n !executor.superuser &&\n database.owner !== executor.executor\n )\n unsupported(\n 'database-authority',\n database.name,\n 'Database ACL remediation requires its owner or a superuser.',\n );\n return {\n contract,\n diagnostics,\n limitations: [\n 'Only PostgreSQL table, column, sequence, schema and database ACLs are qualified; application authorization and RLS are separate. Large-object ownership/access is unsupported, and lo_compat_privileges must remain off for deployed roles.',\n 'Future user-defined routines and types are unsupported. PostgreSQL implicit global defaults grant PUBLIC EXECUTE/USAGE; rerun diagnostics after every migration before activating runtime roles.',\n 'Pause migrations and external ACL/role writers during planning and apply. The advisory lock coordinates SMRT permission writers only.',\n 'All framework bookkeeping tables must exist before setup. Stop runtime and monitor access throughout migrations, restores and repair: recreated bookkeeping tables temporarily receive creator CRUD defaults and require explicit reconciliation before either role is reactivated.',\n ],\n statements: [...new Set(statements)],\n canApply: !diagnostics.some((entry) => entry.severity === 'unsupported'),\n fingerprint: createHash('sha256')\n .update(JSON.stringify({ contract, state }))\n .digest('hex'),\n };\n}\n\n/** Read-only catalog inspection. No application records or credentials are read. */\nexport async function planPostgresPermissions(\n db: Executor,\n contract: PostgresPermissionContract,\n): Promise<PostgresPermissionPlan> {\n return plan(db, contract);\n}\n\n/** Apply the reviewed contract atomically, rejecting stale catalog snapshots. */\nexport async function applyPostgresPermissions(\n db: DatabaseInterface,\n contract: PostgresPermissionContract,\n options: { expectedFingerprint: string },\n): Promise<PostgresPermissionPlan> {\n const normalizedContract = validatePostgresPermissionContract(contract);\n if (!options || !/^[a-f0-9]{64}$/.test(options.expectedFingerprint))\n throw new Error(\n 'An expectedFingerprint from a reviewed permission plan is required.',\n );\n if (!db.transaction)\n throw new Error(\n 'PostgreSQL permissions require a transaction-capable database.',\n );\n return db.transaction(async (tx) => {\n await tx.query(\"SET LOCAL lock_timeout = '5s'\");\n await tx.query(\"SET LOCAL statement_timeout = '30s'\");\n // Cooperating permission writers serialize; operators must quiesce migrations.\n await tx.query(\n `SELECT pg_advisory_xact_lock(hashtext(${literal(`smrt-permissions:${normalizedContract.schema}`)}))`,\n );\n const before = await plan(tx, normalizedContract);\n if (before.fingerprint !== options.expectedFingerprint)\n throw new Error(\n 'Permission plan is stale; generate and review a new plan.',\n );\n if (!before.canApply)\n throw new Error(\n 'Permission plan has unsupported privilege sources or missing prerequisites; resolve its diagnostics first.',\n );\n const executor = (\n await tx.query(\n 'SELECT current_user AS name, rolsuper FROM pg_roles WHERE rolname=current_user',\n )\n ).rows[0];\n if (\n !executor.rolsuper &&\n executor.name !== normalizedContract.migrationOwner\n )\n throw new Error(\n 'Apply must execute as the migration owner or a PostgreSQL superuser.',\n );\n for (const statement of before.statements) await tx.query(statement);\n const after = await plan(tx, normalizedContract);\n if (!after.canApply || after.diagnostics.length)\n throw new Error(\n 'Permission verification failed; all permission changes have been rolled back.',\n );\n return after;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"postgres-permissions.js","names":[],"sources":["../src/postgres-permissions.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION,\n CREATE_POSTGRES_CHANGE_FEED_DRAIN_FUNCTION,\n POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY,\n POSTGRES_CHANGE_FEED_DRAIN_BATCH,\n POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY,\n} from './system/schema.js';\n\n/** PostgreSQL ACLs only; this is not application authorization or RLS. */\nexport interface PostgresPermissionContract {\n schema: string;\n /** Defaults affect every future object created by migrationOwner in schema. */\n schemaExclusive: true;\n migrationOwner: string;\n runtimeRole: string;\n managedTables: string[];\n /** Existing operator-owned tables retained outside the runtime data surface. */\n retainedTables?: string[];\n /** Exact zero-argument trigger functions bound only to managed tables. */\n managedTriggerFunctions?: string[];\n monitor?: { role: string; tables: Record<string, string[]> };\n}\n\nexport interface PostgresPermissionDiagnostic {\n code: string;\n severity: 'missing' | 'excessive' | 'unsupported';\n role?: string;\n resource: string;\n message: string;\n}\n\nexport interface PostgresPermissionPlan {\n contract: PostgresPermissionContract;\n diagnostics: PostgresPermissionDiagnostic[];\n statements: string[];\n canApply: boolean;\n fingerprint: string;\n limitations: string[];\n}\n\ntype NormalizedPostgresPermissionContract = PostgresPermissionContract & {\n managedTriggerFunctions: string[];\n retainedTables: string[];\n};\n\ntype Executor = Pick<DatabaseInterface, 'query'>;\ninterface Row {\n [key: string]: unknown;\n oid: string;\n name: string;\n schema: string;\n identity: string;\n argument_types: string;\n argument_count: string;\n argument_defaults: string | null;\n return_type: string;\n result: string;\n cost: string;\n rows: string;\n support: string;\n language: string;\n security_definer: boolean;\n config: unknown;\n source: string;\n volatility: string;\n parallel: string;\n leakproof: boolean;\n strict: boolean;\n function_oid: string;\n table_name: string;\n enabled: string;\n internal: boolean;\n owner: string;\n acl: Acl[];\n kind: string;\n rolname: string;\n member: string;\n parent: string;\n relation: string;\n parent_table: string | null;\n child_schema: string;\n child_table: string;\n parent_schema: string;\n delete_action: string;\n update_action: string;\n rule_name: string;\n referenced_schema: string;\n referenced_table: string;\n version: string;\n executor: string;\n superuser: boolean;\n rls: boolean;\n}\ntype Acl = {\n grantee: string;\n privilege: string;\n grantable: boolean;\n grantor?: string;\n};\nconst bookkeeping = new Set([\n '_smrt_migrations',\n '_smrt_schema_migrations',\n '_smrt_backfills',\n]);\nconst identifier = (value: string) => `\"${value.replaceAll('\"', '\"\"')}\"`;\nconst literal = (value: string) =>\n `E'${value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"''\")}'`;\nconst qualified = (schema: string, name: string) =>\n `${identifier(schema)}.${identifier(name)}`;\n\nconst normalizeFunctionSource = (value: string) =>\n value.replaceAll('\\r\\n', '\\n').trim();\nconst normalizeArgumentTypes = (value: string) =>\n value.replaceAll(/\\s*,\\s*/g, ',');\nconst normalizeFunctionResult = (value: string) =>\n value\n .replaceAll(/\\s+/g, ' ')\n .replaceAll(/\\s*,\\s*/g, ',')\n .replaceAll(/\\(\\s*/g, '(')\n .replaceAll(/\\s*\\)/g, ')')\n .trim()\n .toLowerCase();\n\nfunction sourceFromFunctionDdl(ddl: string): string {\n const match = ddl.match(\n /\\bAS\\s+\\$([A-Za-z_][A-Za-z0-9_]*)\\$\\n([\\s\\S]*?)\\n\\$\\1\\$;/,\n );\n if (!match)\n throw new Error('Framework function DDL has no dollar-quoted body.');\n return normalizeFunctionSource(match[2]);\n}\n\nfunction resultFromFunctionDdl(ddl: string): string {\n const match = ddl.match(/\\bRETURNS\\s+([\\s\\S]*?)\\nLANGUAGE\\s+/);\n if (!match) throw new Error('Framework function DDL has no return result.');\n return normalizeFunctionResult(match[1]);\n}\n\ntype FrameworkRoutine = {\n name: string;\n argumentTypes: string;\n argumentDefaults: string | null;\n result: string;\n source: string;\n};\n\nfunction frameworkRoutine(identity: string, ddl: string): FrameworkRoutine {\n const match = identity.match(/^([^()]+)\\((.*)\\)$/);\n if (!match)\n throw new Error(`Invalid framework function identity: ${identity}`);\n return {\n name: match[1],\n argumentTypes: normalizeArgumentTypes(match[2]),\n argumentDefaults:\n match[1] === '_smrt_drain_changes'\n ? String(POSTGRES_CHANGE_FEED_DRAIN_BATCH)\n : null,\n result: resultFromFunctionDdl(ddl),\n source: sourceFromFunctionDdl(ddl),\n };\n}\n\nconst frameworkRoutines = [\n frameworkRoutine(\n POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY,\n CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION,\n ),\n frameworkRoutine(\n POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY,\n CREATE_POSTGRES_CHANGE_FEED_DRAIN_FUNCTION,\n ),\n];\n\nfunction object(\n value: unknown,\n keys: string[],\n label: string,\n): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} must be an object.`);\n }\n for (const key of Object.keys(value)) {\n if (!keys.includes(key))\n throw new Error(`Unknown ${label} property: ${key}.`);\n }\n return value as Record<string, unknown>;\n}\nfunction name(value: unknown, label: string): string {\n if (\n typeof value !== 'string' ||\n !value.length ||\n value.includes('\\0') ||\n Buffer.byteLength(value) > 63\n ) {\n throw new Error(\n `${label} must be a nonempty PostgreSQL identifier (at most 63 bytes).`,\n );\n }\n return value;\n}\n/** Validate configuration before opening a connection; unknown options fail closed. */\nexport function validatePostgresPermissionContract(\n value: unknown,\n): NormalizedPostgresPermissionContract {\n const input = object(\n value,\n [\n 'schema',\n 'schemaExclusive',\n 'migrationOwner',\n 'runtimeRole',\n 'managedTables',\n 'retainedTables',\n 'managedTriggerFunctions',\n 'monitor',\n ],\n 'postgresPermissions',\n );\n if (input.schemaExclusive !== true)\n throw new Error(\n 'postgresPermissions.schemaExclusive must explicitly be true.',\n );\n const schema = name(input.schema, 'schema');\n if (schema === 'information_schema' || schema.startsWith('pg_'))\n throw new Error('A dedicated application schema is required.');\n const migrationOwner = name(input.migrationOwner, 'migrationOwner');\n const runtimeRole = name(input.runtimeRole, 'runtimeRole');\n if (!Array.isArray(input.managedTables))\n throw new Error('managedTables must be an array.');\n const managedTables = [\n ...new Set(\n input.managedTables.map((table) => name(table, 'managedTables entry')),\n ),\n ].sort();\n if (\n input.retainedTables !== undefined &&\n !Array.isArray(input.retainedTables)\n )\n throw new Error('retainedTables must be an array.');\n const retainedTables = [\n ...new Set(\n (input.retainedTables ?? []).map((table) =>\n name(table, 'retainedTables entry'),\n ),\n ),\n ].sort();\n const overlap = retainedTables.find((table) => managedTables.includes(table));\n if (overlap)\n throw new Error(`Table ${overlap} cannot be both managed and retained.`);\n if (\n input.managedTriggerFunctions !== undefined &&\n !Array.isArray(input.managedTriggerFunctions)\n )\n throw new Error('managedTriggerFunctions must be an array.');\n const managedTriggerFunctions = [\n ...new Set(\n (input.managedTriggerFunctions ?? []).map((routine) =>\n name(routine, 'managedTriggerFunctions entry'),\n ),\n ),\n ].sort();\n let monitor: PostgresPermissionContract['monitor'];\n if (input.monitor !== undefined) {\n const entry = object(input.monitor, ['role', 'tables'], 'monitor');\n const role = name(entry.role, 'monitor.role');\n if (\n !entry.tables ||\n typeof entry.tables !== 'object' ||\n Array.isArray(entry.tables)\n )\n throw new Error('monitor.tables must map tables to column arrays.');\n const tables: Record<string, string[]> = Object.create(null);\n for (const [table, columns] of Object.entries(entry.tables).sort(\n ([a], [b]) => a.localeCompare(b),\n )) {\n name(table, 'monitor table');\n if (!Array.isArray(columns) || !columns.length)\n throw new Error(\n `monitor table ${table} requires a nonempty column array.`,\n );\n tables[table] = [\n ...new Set(columns.map((column) => name(column, 'monitor column'))),\n ].sort();\n }\n monitor = { role, tables };\n }\n const roles = [\n migrationOwner,\n runtimeRole,\n ...(monitor ? [monitor.role] : []),\n ];\n if (\n new Set(roles).size !== roles.length ||\n roles.some(\n (role) => role.toUpperCase() === 'PUBLIC' || role.startsWith('pg_'),\n )\n )\n throw new Error(\n 'Migration, runtime and monitor roles must be distinct non-system roles.',\n );\n return {\n schema,\n schemaExclusive: true,\n migrationOwner,\n runtimeRole,\n managedTables,\n retainedTables,\n managedTriggerFunctions,\n ...(monitor ? { monitor } : {}),\n };\n}\n\nconst acl = (expression: string) =>\n `COALESCE((SELECT json_agg(json_build_object('grantee', CASE WHEN a.grantee = 0 THEN 'PUBLIC' ELSE pg_get_userbyid(a.grantee) END, 'grantor', pg_get_userbyid(a.grantor), 'privilege', a.privilege_type, 'grantable', a.is_grantable) ORDER BY a.grantee, a.privilege_type, a.grantor) FROM aclexplode(${expression}) a), '[]'::json)`;\n\nasync function snapshot(\n db: Executor,\n contract: NormalizedPostgresPermissionContract,\n): Promise<Record<string, Row[]>> {\n const roleNames = [\n contract.migrationOwner,\n contract.runtimeRole,\n ...(contract.monitor ? [contract.monitor.role] : []),\n ]\n .map(literal)\n .join(',');\n const restrictedRoleNames = [\n contract.runtimeRole,\n ...(contract.monitor ? [contract.monitor.role] : []),\n ]\n .map(literal)\n .join(',');\n // information_schema.sql runs after initial ACL recording. Trust only its\n // explicitly PUBLIC-readable stock relations, never internal metadata views.\n // Normal-operation OIDs start at 16384:\n // https://www.postgresql.org/docs/16/system-catalog-initial-data.html\n // Source: postgres/postgres REL_14_STABLE..REL_18_STABLE,\n // GRANT SELECT [ON TABLE] ... TO PUBLIC in:\n // https://github.com/postgres/postgres/blob/REL_14_STABLE/src/backend/catalog/information_schema.sql\n // https://github.com/postgres/postgres/blob/REL_18_STABLE/src/backend/catalog/information_schema.sql\n const informationSchemaPublicSelect =\n \"'administrable_role_authorizations','applicable_roles','attributes','character_sets','check_constraint_routine_usage','check_constraints','collation_character_set_applicability','collations','column_column_usage','column_domain_usage','column_options','column_privileges','column_udt_usage','columns','constraint_column_usage','constraint_table_usage','data_type_privileges','domain_constraints','domain_udt_usage','domains','element_types','enabled_roles','foreign_data_wrapper_options','foreign_data_wrappers','foreign_server_options','foreign_servers','foreign_table_options','foreign_tables','information_schema_catalog_name','key_column_usage','parameters','referential_constraints','role_column_grants','role_routine_grants','role_table_grants','role_udt_grants','role_usage_grants','routine_column_usage','routine_privileges','routine_routine_usage','routine_sequence_usage','routine_table_usage','routines','schemata','sequences','sql_features','sql_implementation_info','sql_sizing','table_constraints','table_privileges','tables','triggered_update_columns','triggers','udt_privileges','usage_privileges','user_defined_types','user_mapping_options','user_mappings','view_column_usage','view_routine_usage','view_table_usage','views'\";\n // pg_init_privs records non-default initial ACLs. Missing entries use the\n // PostgreSQL object-kind default; extension-provided ACLs are not stock trust.\n // https://www.postgresql.org/docs/current/catalog-pg-init-privs.html\n const initialAcl = (fallback: string) =>\n `CASE WHEN i.privtype='i' THEN i.initprivs WHEN i.privtype='e' THEN NULL::aclitem[] ELSE ${fallback} END`;\n // Only a type referenced by another type's typarray is its generated array.\n // Nonzero typelem also describes non-array types (point); domains over arrays\n // remain independent privilege targets and must not be filtered out.\n const queries: Record<string, string> = {\n server: `SELECT current_setting('server_version_num') AS version, current_database() AS database, current_user AS executor, (SELECT rolsuper FROM pg_roles WHERE rolname=current_user) AS superuser`,\n roles: `SELECT oid::text, rolname, rolsuper, rolcreaterole, rolcreatedb, rolreplication, rolbypassrls FROM pg_roles WHERE rolname IN (${roleNames}) ORDER BY rolname`,\n memberships: `SELECT pg_get_userbyid(member) AS member, pg_get_userbyid(roleid) AS parent FROM pg_auth_members WHERE member IN (SELECT oid FROM pg_roles WHERE rolname IN (${roleNames})) OR roleid IN (SELECT oid FROM pg_roles WHERE rolname IN (${roleNames})) ORDER BY member, roleid`,\n database: `SELECT datname AS name, pg_get_userbyid(datdba) AS owner, ${acl(\"COALESCE(datacl, acldefault('d',datdba))\")} AS acl FROM pg_database WHERE datname=current_database()`,\n schemas: `SELECT nspname AS name, pg_get_userbyid(nspowner) AS owner, ${acl(\"COALESCE(nspacl, acldefault('n',nspowner))\")} AS acl FROM pg_namespace WHERE left(nspname,3) <> 'pg_' AND nspname <> 'information_schema' ORDER BY nspname`,\n relations: `SELECT c.oid::text, n.nspname AS schema, c.relname AS name, c.relkind AS kind, pg_get_userbyid(c.relowner) AS owner, c.relrowsecurity AS rls, (SELECT parent.relname FROM pg_depend d JOIN pg_class parent ON parent.oid=d.refobjid JOIN pg_namespace parent_schema ON parent_schema.oid=parent.relnamespace WHERE d.classid='pg_class'::regclass AND d.objid=c.oid AND d.refclassid='pg_class'::regclass AND d.deptype IN ('a','i') AND parent_schema.oid=n.oid LIMIT 1) AS parent_table, ${acl(\"COALESCE(c.relacl, acldefault(CASE WHEN c.relkind='S' THEN 's'::\\\"char\\\" ELSE 'r'::\\\"char\\\" END,c.relowner))\")} AS acl FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' AND c.relkind IN ('r','p','v','m','f','S') ORDER BY n.nspname,c.relname`,\n inherits: `SELECT child_schema.nspname AS child_schema, child.relname AS child_table, parent_schema.nspname AS parent_schema, parent.relname AS parent_table FROM pg_inherits i JOIN pg_class child ON child.oid=i.inhrelid JOIN pg_namespace child_schema ON child_schema.oid=child.relnamespace JOIN pg_class parent ON parent.oid=i.inhparent JOIN pg_namespace parent_schema ON parent_schema.oid=parent.relnamespace ORDER BY child_schema.nspname,child.relname,parent_schema.nspname,parent.relname`,\n foreignKeys: `SELECT child_schema.nspname AS child_schema, child.relname AS child_table, parent_schema.nspname AS parent_schema, parent.relname AS parent_table, con.confdeltype::text AS delete_action, con.confupdtype::text AS update_action FROM pg_constraint con JOIN pg_class child ON child.oid=con.conrelid JOIN pg_namespace child_schema ON child_schema.oid=child.relnamespace JOIN pg_class parent ON parent.oid=con.confrelid JOIN pg_namespace parent_schema ON parent_schema.oid=parent.relnamespace WHERE con.contype='f' ORDER BY child_schema.nspname,child.relname,parent_schema.nspname,parent.relname,con.conname`,\n rewriteRules: `WITH RECURSIVE rule_dependencies AS (SELECT r.oid AS rule_oid, r.ev_class AS source_oid, r.rulename AS rule_name, d.refobjid AS referenced_oid FROM pg_rewrite r JOIN pg_depend d ON d.classid='pg_rewrite'::regclass AND d.objid=r.oid AND d.refclassid='pg_class'::regclass WHERE r.rulename <> '_RETURN' UNION SELECT dependencies.rule_oid, dependencies.source_oid, dependencies.rule_name, d.refobjid FROM rule_dependencies dependencies JOIN pg_rewrite view_rule ON view_rule.ev_class=dependencies.referenced_oid AND view_rule.rulename='_RETURN' JOIN pg_depend d ON d.classid='pg_rewrite'::regclass AND d.objid=view_rule.oid AND d.refclassid='pg_class'::regclass) SELECT n.nspname AS schema, c.relname AS table_name, dependencies.rule_name, referenced_schema.nspname AS referenced_schema, referenced.relname AS referenced_table FROM rule_dependencies dependencies JOIN pg_class c ON c.oid=dependencies.source_oid JOIN pg_namespace n ON n.oid=c.relnamespace JOIN pg_class referenced ON referenced.oid=dependencies.referenced_oid JOIN pg_namespace referenced_schema ON referenced_schema.oid=referenced.relnamespace ORDER BY n.nspname,c.relname,dependencies.rule_name,referenced_schema.nspname,referenced.relname`,\n columns: `SELECT c.oid::text AS relation, a.attname AS name, ${acl('a.attacl')} AS acl FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' AND c.relkind IN ('r','p','v','m','f') AND a.attnum>0 AND NOT a.attisdropped ORDER BY c.oid,a.attnum`,\n routines: `SELECT n.nspname AS schema, p.oid::text, p.proname AS name, format('%I.%I(%s)', n.nspname, p.proname, oidvectortypes(p.proargtypes)) AS identity, oidvectortypes(p.proargtypes) AS argument_types, pg_get_expr(p.proargdefaults,0) AS argument_defaults, p.prokind AS kind, p.prorettype::regtype::text AS return_type, pg_get_function_result(p.oid) AS result, p.procost::text AS cost, p.prorows::text AS rows, p.prosupport::regproc::text AS support, p.pronargs::text AS argument_count, l.lanname AS language, p.prosecdef AS security_definer, p.provolatile::text AS volatility, p.proparallel::text AS parallel, p.proleakproof AS leakproof, p.proisstrict AS strict, COALESCE(to_json(p.proconfig),'[]'::json) AS config, p.prosrc AS source, CASE WHEN p.prokind='f' THEN pg_get_functiondef(p.oid) END AS definition, pg_get_userbyid(p.proowner) AS owner, ${acl(\"COALESCE(p.proacl, acldefault('f',p.proowner))\")} AS acl FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace JOIN pg_language l ON l.oid=p.prolang WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' ORDER BY n.nspname,p.oid`,\n triggers: `SELECT t.oid::text, n.nspname AS schema, c.relname AS table_name, t.tgname AS name, t.tgfoid::text AS function_oid, t.tgenabled AS enabled, t.tgisinternal AS internal, t.tgtype::text AS type, encode(t.tgargs,'hex') AS arguments, pg_get_triggerdef(t.oid, true) AS definition FROM pg_trigger t JOIN pg_class c ON c.oid=t.tgrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' ORDER BY n.nspname,c.oid,t.oid`,\n types: `SELECT n.nspname AS schema, t.typname AS name, pg_get_userbyid(t.typowner) AS owner, ${acl(\"COALESCE(t.typacl, acldefault('T',t.typowner))\")} AS acl FROM pg_type t JOIN pg_namespace n ON n.oid=t.typnamespace LEFT JOIN pg_class c ON c.oid=t.typrelid WHERE left(n.nspname,3) <> 'pg_' AND n.nspname <> 'information_schema' AND NOT EXISTS (SELECT 1 FROM pg_type element WHERE element.typarray=t.oid) AND (t.typrelid=0 OR c.relkind='c') ORDER BY n.nspname,t.typname`,\n systemSchemas: `SELECT nspname AS name, pg_get_userbyid(nspowner) AS owner, ${acl(\"COALESCE(nspacl, acldefault('n',nspowner))\")} AS acl FROM pg_namespace WHERE left(nspname,3)='pg_' OR nspname='information_schema' ORDER BY nspname`,\n systemPrivileges: `WITH resources AS (\n SELECT n.nspname AS schema, c.relname AS name, c.oid::text AS oid, CASE WHEN c.relkind='S' THEN 'sequence' ELSE 'relation' END AS kind, pg_get_userbyid(c.relowner) AS owner,\n COALESCE(c.relacl,acldefault(CASE WHEN c.relkind='S' THEN 's'::\"char\" ELSE 'r'::\"char\" END,c.relowner)) AS current_acl,\n CASE WHEN i.objoid IS NULL AND n.nspname='information_schema' AND c.oid<16384 AND c.relname IN (${informationSchemaPublicSelect}) THEN ARRAY[makeaclitem(0,c.relowner,'SELECT',false)] ELSE ${initialAcl(\"acldefault(CASE WHEN c.relkind='S' THEN 's'::\\\"char\\\" ELSE 'r'::\\\"char\\\" END,c.relowner)\")} END AS initial_acl\n FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace\n LEFT JOIN pg_init_privs i ON i.objoid=c.oid AND i.classoid='pg_class'::regclass AND i.objsubid=0\n WHERE (left(n.nspname,3)='pg_' OR n.nspname='information_schema') AND c.relkind IN ('r','p','v','m','f','S')\n UNION ALL\n SELECT n.nspname, c.relname || '.' || a.attname, c.oid::text || ':' || a.attnum::text, 'column', pg_get_userbyid(c.relowner),\n a.attacl, ${initialAcl('NULL::aclitem[]')}\n FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace\n LEFT JOIN pg_init_privs i ON i.objoid=c.oid AND i.classoid='pg_class'::regclass AND i.objsubid=a.attnum\n WHERE (left(n.nspname,3)='pg_' OR n.nspname='information_schema') AND a.attnum>0 AND NOT a.attisdropped\n UNION ALL\n SELECT n.nspname, p.proname, p.oid::text, 'routine', pg_get_userbyid(p.proowner), COALESCE(p.proacl,acldefault('f',p.proowner)), ${initialAcl(\"CASE WHEN p.oid<16384 THEN acldefault('f',p.proowner) ELSE NULL::aclitem[] END\")}\n FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace\n LEFT JOIN pg_init_privs i ON i.objoid=p.oid AND i.classoid='pg_proc'::regclass AND i.objsubid=0\n WHERE left(n.nspname,3)='pg_' OR n.nspname='information_schema'\n UNION ALL\n SELECT n.nspname, t.typname, t.oid::text, 'type', pg_get_userbyid(t.typowner), COALESCE(t.typacl,acldefault('T',t.typowner)), ${initialAcl(\"CASE WHEN t.oid<16384 THEN acldefault('T',t.typowner) ELSE NULL::aclitem[] END\")}\n FROM pg_type t JOIN pg_namespace n ON n.oid=t.typnamespace LEFT JOIN pg_class c ON c.oid=t.typrelid\n LEFT JOIN pg_init_privs i ON i.objoid=t.oid AND i.classoid='pg_type'::regclass AND i.objsubid=0\n WHERE (left(n.nspname,3)='pg_' OR n.nspname='information_schema') AND NOT EXISTS (SELECT 1 FROM pg_type element WHERE element.typarray=t.oid) AND (t.typrelid=0 OR c.relkind='c')\n ) SELECT r.schema,r.name,r.oid,r.kind,r.owner,COALESCE(d.acl,'[]'::json) AS acl FROM resources r\n CROSS JOIN LATERAL (\n SELECT json_agg(json_build_object('grantee', CASE WHEN a.grantee=0 THEN 'PUBLIC' ELSE pg_get_userbyid(a.grantee) END,\n 'grantor',pg_get_userbyid(a.grantor),'privilege',a.privilege_type,'grantable',a.is_grantable)\n ORDER BY a.grantee,a.privilege_type,a.grantor) AS acl\n FROM aclexplode(r.current_acl) a\n WHERE a.grantee IN (SELECT oid FROM pg_roles WHERE rolname IN (${restrictedRoleNames}))\n OR (a.grantee=0 AND NOT EXISTS (SELECT 1 FROM aclexplode(r.initial_acl) b WHERE b.grantee=0 AND b.privilege_type=a.privilege_type AND (b.is_grantable OR NOT a.is_grantable)))\n ) d WHERE d.acl IS NOT NULL OR (r.kind<>'column' AND r.owner IN (${restrictedRoleNames})) ORDER BY r.schema,r.kind,r.name,r.oid`,\n largeObjects: `SELECT oid::text, pg_get_userbyid(lomowner) AS owner, ${acl(\"COALESCE(lomacl, acldefault('L',lomowner))\")} AS acl FROM pg_largeobject_metadata ORDER BY oid`,\n largeObjectSettings: `SELECT setting,reset_val,source FROM pg_settings WHERE name='lo_compat_privileges'`,\n largeObjectRoleSettings: `SELECT s.setdatabase::text AS database, CASE WHEN s.setrole=0 THEN 'ALL ROLES' ELSE pg_get_userbyid(s.setrole) END AS role, split_part(config.value,'=',2) AS setting FROM pg_db_role_setting s CROSS JOIN LATERAL unnest(s.setconfig) config(value) WHERE s.setdatabase IN (0,(SELECT oid FROM pg_database WHERE datname=current_database())) AND (s.setrole=0 OR s.setrole IN (SELECT oid FROM pg_roles WHERE rolname IN (${restrictedRoleNames}))) AND split_part(config.value,'=',1)='lo_compat_privileges' ORDER BY s.setdatabase,s.setrole,config.value`,\n parameters: `SELECT parname AS name, ${acl('paracl')} AS acl FROM pg_parameter_acl ORDER BY parname`,\n defaults: `SELECT pg_get_userbyid(d.defaclrole) AS owner, COALESCE(n.nspname,'') AS schema, d.defaclobjtype AS kind, ${acl('d.defaclacl')} AS acl FROM pg_default_acl d LEFT JOIN pg_namespace n ON n.oid=d.defaclnamespace ORDER BY d.defaclrole,d.defaclnamespace,d.defaclobjtype`,\n };\n const result: Record<string, Row[]> = {};\n // A transaction executor must never have concurrent statements dispatched to it.\n for (const [key, query] of Object.entries(queries)) {\n // Parameter ACLs were introduced in PostgreSQL 15.\n if (key === 'parameters' && Number(result.server[0].version) < 150000) {\n result[key] = [];\n continue;\n }\n result[key] = (await db.query(query)).rows as Row[];\n }\n return result;\n}\n\nasync function plan(\n db: Executor,\n input: unknown,\n): Promise<PostgresPermissionPlan> {\n const contract = validatePostgresPermissionContract(input);\n const state = await snapshot(db, contract);\n const diagnostics: PostgresPermissionDiagnostic[] = [];\n const statements: string[] = [];\n const roles = [\n contract.runtimeRole,\n ...(contract.monitor ? [contract.monitor.role] : []),\n ];\n const add = (\n severity: PostgresPermissionDiagnostic['severity'],\n code: string,\n resource: string,\n message: string,\n role?: string,\n ) =>\n diagnostics.push({\n severity,\n code,\n resource,\n message,\n ...(role ? { role } : {}),\n });\n const unsupported = (\n code: string,\n resource: string,\n message: string,\n role?: string,\n ) => add('unsupported', code, resource, message, role);\n if (Number(state.server[0].version) < 140000)\n unsupported(\n 'server-version',\n 'database',\n 'PostgreSQL 14 or later is required.',\n );\n for (const role of [contract.migrationOwner, ...roles]) {\n const row = state.roles.find((entry) => entry.rolname === role);\n if (!row) {\n unsupported(\n 'missing-role',\n role,\n 'Infrastructure must create this role.',\n role,\n );\n continue;\n }\n if (\n roles.includes(role) &&\n [\n 'rolsuper',\n 'rolcreaterole',\n 'rolcreatedb',\n 'rolreplication',\n 'rolbypassrls',\n ].some((key) => row[key])\n )\n unsupported(\n 'privileged-role',\n role,\n 'Remove SUPERUSER, CREATEROLE, CREATEDB, REPLICATION and BYPASSRLS authority using infrastructure role management.',\n role,\n );\n }\n for (const membership of state.memberships)\n unsupported(\n 'role-membership',\n membership.member,\n `Role ${membership.member} is a member of ${membership.parent}; inbound and outbound membership, inherited privileges, and SET ROLE authority require infrastructure review. Memberships are never modified.`,\n membership.member,\n );\n const schema = state.schemas.find((entry) => entry.name === contract.schema);\n if (!schema)\n unsupported(\n 'missing-schema',\n contract.schema,\n 'Create the dedicated schema as the migration owner before reconciliation.',\n );\n else if (schema.owner !== contract.migrationOwner)\n unsupported(\n 'schema-owner',\n contract.schema,\n `Schema must be owned by ${contract.migrationOwner}; ownership is never changed.`,\n );\n const database = state.database[0];\n if (roles.includes(database.owner))\n unsupported(\n 'database-owner',\n database.name,\n 'Runtime and monitor must not own the database.',\n database.owner,\n );\n\n const relevant = (entries: Acl[], role: string) =>\n entries.filter(\n (entry) => entry.grantee === role || entry.grantee === 'PUBLIC',\n );\n function reconcile(\n kind: string,\n resource: string,\n entries: Acl[],\n role: string,\n desired: string[],\n suffix = '',\n ) {\n for (const entry of entries)\n if (entry.grantor === role && entry.grantee !== role)\n unsupported(\n 'dependent-grant',\n resource,\n `Configured role granted ${entry.privilege} to ${entry.grantee}; grant chains require infrastructure review to preserve other roles.`,\n role,\n );\n for (const entry of entries) {\n if (\n entry.grantee === 'PUBLIC' &&\n !(\n kind === 'DATABASE' &&\n entry.privilege === 'CONNECT' &&\n !entry.grantable\n )\n ) {\n unsupported(\n 'public-privilege',\n resource,\n `PUBLIC ${entry.privilege} is outside the explicit role contract; infrastructure must review it without changing unrelated access.`,\n role,\n );\n }\n }\n const effective = relevant(entries, role);\n const excess = effective.filter(\n (entry) => !desired.includes(entry.privilege) || entry.grantable,\n );\n const missing = desired.filter(\n (privilege) =>\n !entries.some(\n (entry) => entry.grantee === role && entry.privilege === privilege,\n ),\n );\n for (const entry of excess) {\n add(\n 'excessive',\n 'excessive-privilege',\n resource,\n `${entry.privilege}${entry.grantable ? ' WITH GRANT OPTION' : ''} is not declared (source: ${entry.grantee}).`,\n role,\n );\n if (entry.grantee === 'PUBLIC')\n unsupported(\n 'public-privilege',\n resource,\n `PUBLIC ${entry.privilege} requires infrastructure review; revoking it would affect other roles.`,\n role,\n );\n }\n for (const privilege of missing)\n add(\n 'missing',\n 'missing-privilege',\n resource,\n `${privilege} is required.`,\n role,\n );\n if (!excess.length && !missing.length) return;\n statements.push(\n `REVOKE ALL PRIVILEGES${suffix} ON ${kind} ${resource} FROM ${identifier(role)}`,\n );\n if (desired.length)\n statements.push(\n `GRANT ${desired.join(', ')}${suffix} ON ${kind} ${resource} TO ${identifier(role)}`,\n );\n }\n function reconcileRoutine(\n resource: string,\n entries: Acl[],\n desiredByRole: ReadonlyMap<string, readonly string[]>,\n ) {\n const grantee = (role: string) =>\n role === 'PUBLIC' ? 'PUBLIC' : identifier(role);\n for (const entry of entries)\n if (\n roles.includes(entry.grantor ?? '') &&\n entry.grantee !== entry.grantor\n )\n unsupported(\n 'dependent-routine-grant',\n resource,\n `Configured role granted ${entry.privilege} to ${entry.grantee}; grant chains require infrastructure review to preserve other roles.`,\n entry.grantor,\n );\n const managedGrantees = new Set([\n contract.migrationOwner,\n ...roles,\n 'PUBLIC',\n ]);\n for (const entry of entries)\n if (!managedGrantees.has(entry.grantee))\n unsupported(\n 'outside-routine-grant',\n resource,\n `${entry.grantee} has ${entry.privilege}; direct routine access outside the declared roles requires infrastructure review.`,\n entry.grantee,\n );\n for (const role of [...roles, 'PUBLIC']) {\n const desired = desiredByRole.get(role) ?? [];\n const effective = entries.filter((entry) => entry.grantee === role);\n const excess = effective.filter(\n (entry) => !desired.includes(entry.privilege) || entry.grantable,\n );\n const missing = desired.filter(\n (privilege) =>\n !entries.some(\n (entry) => entry.grantee === role && entry.privilege === privilege,\n ),\n );\n for (const entry of excess)\n add(\n 'excessive',\n 'excessive-routine-privilege',\n resource,\n `${entry.privilege}${entry.grantable ? ' WITH GRANT OPTION' : ''} is not declared.`,\n role,\n );\n for (const privilege of missing)\n add(\n 'missing',\n 'missing-routine-privilege',\n resource,\n `${privilege} is required.`,\n role,\n );\n if (!excess.length && !missing.length) continue;\n statements.push(\n `REVOKE ALL PRIVILEGES ON FUNCTION ${resource} FROM ${grantee(role)}`,\n );\n if (desired.length)\n statements.push(\n `GRANT ${desired.join(', ')} ON FUNCTION ${resource} TO ${grantee(role)}`,\n );\n }\n }\n for (const role of roles) {\n reconcile('DATABASE', identifier(database.name), database.acl, role, [\n 'CONNECT',\n ]);\n for (const entry of state.schemas) {\n if (entry.owner === role)\n unsupported(\n 'resource-owner',\n entry.name,\n 'Configured role owns a schema and has DDL authority.',\n role,\n );\n if (entry.name === contract.schema)\n reconcile('SCHEMA', identifier(entry.name), entry.acl, role, ['USAGE']);\n else if (\n relevant(entry.acl, role).some((grant) => grant.privilege === 'CREATE')\n )\n unsupported(\n 'outside-schema-create',\n entry.name,\n 'Effective CREATE outside the dedicated schema requires infrastructure repair.',\n role,\n );\n }\n }\n for (const schema of state.systemSchemas) {\n for (const role of roles) {\n if (schema.owner === role) {\n unsupported(\n 'system-schema-owner',\n schema.name,\n 'Configured roles must not own system schemas; infrastructure must restore separate ownership. System schema ownership is never modified.',\n role,\n );\n }\n for (const grant of relevant(schema.acl, role)) {\n if (grant.privilege === 'CREATE') {\n unsupported(\n 'system-schema-authority',\n schema.name,\n `Effective CREATE on a system schema (source: ${grant.grantee}) gives DDL authority and requires infrastructure repair. System schema ACLs are never modified.`,\n role,\n );\n }\n }\n }\n }\n for (const resource of state.systemPrivileges) {\n if (resource.kind !== 'column' && roles.includes(resource.owner)) {\n unsupported(\n 'system-object-owner',\n qualified(resource.schema, resource.name),\n `Configured role owns a system ${resource.kind}; ownership retains implicit alteration and grant authority even when its ordinary ACLs are revoked. Infrastructure must restore ownership; SMRT never changes it.`,\n resource.owner,\n );\n }\n for (const grant of resource.acl) {\n unsupported(\n 'system-privilege-delta',\n qualified(resource.schema, resource.name),\n `${grant.grantee} has exceptional ${grant.privilege}${grant.grantable ? ' WITH GRANT OPTION' : ''} on a system ${resource.kind}; review infrastructure grants against PostgreSQL initial ACLs. System ACLs are never modified.`,\n grant.grantee === 'PUBLIC' ? undefined : grant.grantee,\n );\n }\n }\n for (const resource of state.largeObjects) {\n if (roles.includes(resource.owner)) {\n unsupported(\n 'large-object-owner',\n `large object ${resource.oid}`,\n 'Configured role owns a large object outside the declared table/column contract. Infrastructure must review ownership; SMRT never changes it.',\n resource.owner,\n );\n }\n for (const grant of resource.acl) {\n if (roles.includes(grant.grantee) || grant.grantee === 'PUBLIC') {\n unsupported(\n 'large-object-privilege',\n `large object ${resource.oid}`,\n `${grant.grantee} has ${grant.privilege}${grant.grantable ? ' WITH GRANT OPTION' : ''} outside the declared table/column contract. Infrastructure must review this ACL; SMRT neither reads payloads nor changes large-object permissions.`,\n grant.grantee === 'PUBLIC' ? undefined : grant.grantee,\n );\n }\n }\n }\n const explicitlyOff = (value: unknown) =>\n ['off', 'false', 'no', '0'].includes(String(value).toLowerCase());\n if (state.largeObjectSettings.length !== 1) {\n unsupported(\n 'large-object-compatibility',\n 'lo_compat_privileges',\n 'The large-object ACL compatibility setting could not be established; qualification requires an observable disabled setting.',\n );\n }\n for (const settings of state.largeObjectSettings) {\n if (\n !explicitlyOff(settings.setting) ||\n !explicitlyOff(settings.reset_val) ||\n !['default', 'configuration file', 'command line', 'database'].includes(\n String(settings.source),\n )\n ) {\n unsupported(\n 'large-object-compatibility',\n 'lo_compat_privileges',\n 'Large-object ACL checks must be enabled for every configured role. An enabled or operator-specific lo_compat_privileges override cannot establish that baseline; review deployment settings and reconnect without an operator-specific override.',\n );\n }\n }\n for (const settings of state.largeObjectRoleSettings) {\n if (!explicitlyOff(settings.setting)) {\n unsupported(\n 'large-object-compatibility',\n 'lo_compat_privileges',\n `Role/database defaults enable or do not establish large-object ACL checks (${String(settings.role)}, database OID ${String(settings.database)}). Infrastructure must disable compatibility mode before qualification.`,\n settings.role === 'ALL ROLES' ? undefined : String(settings.role),\n );\n }\n }\n for (const parameter of state.parameters) {\n for (const role of roles) {\n for (const grant of relevant(parameter.acl, role)) {\n unsupported(\n 'parameter-privilege',\n parameter.name,\n `Effective ${grant.privilege} on a PostgreSQL configuration parameter (source: ${grant.grantee}) requires infrastructure review; parameter ACLs are never modified.`,\n role,\n );\n }\n }\n }\n const tables = state.relations.filter(\n (entry) => entry.schema === contract.schema && entry.kind !== 'S',\n );\n for (const table of bookkeeping) {\n if (!tables.some((entry) => entry.name === table)) {\n unsupported(\n 'missing-bookkeeping-table',\n qualified(contract.schema, table),\n 'Bootstrap all framework bookkeeping tables as the migration owner before permission setup. Stop runtime and monitoring access during migrations or restores, then reconcile before reactivation.',\n );\n }\n }\n const declared = new Set(contract.managedTables);\n const retained = new Set(contract.retainedTables);\n // Framework feature tables are owned by SMRT even when absent from manifests.\n for (const table of tables)\n if (table.name.startsWith('_smrt_')) declared.add(table.name);\n for (const table of retained)\n if (declared.has(table))\n unsupported(\n 'retained-managed-table',\n qualified(contract.schema, table),\n 'Retained tables must be outside the managed and framework table surfaces.',\n );\n for (const table of declared)\n if (!tables.some((entry) => entry.name === table))\n unsupported(\n 'missing-table',\n qualified(contract.schema, table),\n 'Run supported schema migrations before reconciling permissions.',\n );\n for (const table of retained)\n if (!tables.some((entry) => entry.name === table))\n unsupported(\n 'missing-retained-table',\n qualified(contract.schema, table),\n 'Retained tables must already exist before permission reconciliation.',\n );\n for (const relation of state.inherits) {\n const managedParent =\n relation.parent_schema === contract.schema &&\n declared.has(relation.parent_table ?? '');\n const childRetained =\n relation.child_schema === contract.schema &&\n retained.has(relation.child_table);\n const parentRetained =\n relation.parent_schema === contract.schema &&\n retained.has(relation.parent_table ?? '');\n if (managedParent && relation.child_schema !== contract.schema)\n unsupported(\n 'managed-inheritance',\n qualified(relation.child_schema, relation.child_table),\n 'Managed tables may not have external inheritance or partition children: parent table privileges can invoke external triggers outside the retained-table isolation boundary.',\n );\n else if (childRetained !== parentRetained)\n unsupported(\n 'retained-inheritance',\n qualified(relation.child_schema, relation.child_table),\n 'Retained tables may inherit only from retained tables in the dedicated schema; an accessible parent can otherwise expose retained rows.',\n );\n }\n for (const relation of state.foreignKeys) {\n const managedParent =\n relation.parent_schema === contract.schema &&\n declared.has(relation.parent_table ?? '');\n const childRetained =\n relation.child_schema === contract.schema &&\n retained.has(relation.child_table);\n const parentRetained =\n relation.parent_schema === contract.schema &&\n retained.has(relation.parent_table ?? '');\n if (managedParent && relation.child_schema !== contract.schema) {\n unsupported(\n 'managed-foreign-key',\n `${qualified(relation.child_schema, relation.child_table)} -> ${qualified(relation.parent_schema, relation.parent_table ?? '')}`,\n 'Managed tables may not have external foreign-key children: referential actions can invoke external triggers outside the retained-table isolation boundary.',\n );\n continue;\n }\n if (childRetained === parentRetained) continue;\n unsupported(\n 'retained-foreign-key',\n `${qualified(relation.child_schema, relation.child_table)} -> ${qualified(relation.parent_schema, relation.parent_table ?? '')}`,\n 'Retained tables may reference only retained tables; referential actions from an accessible table can otherwise mutate retained rows.',\n );\n }\n for (const rule of state.rewriteRules) {\n const managedSource =\n rule.schema === contract.schema && declared.has(rule.table_name);\n const retainedSource =\n rule.schema === contract.schema && retained.has(rule.table_name);\n const managedTarget =\n rule.referenced_schema === contract.schema &&\n declared.has(rule.referenced_table);\n if (retainedSource && managedTarget)\n unsupported(\n 'user-rewrite-rule',\n `${qualified(rule.schema, rule.table_name)} (${rule.rule_name})`,\n 'Retained tables may not reference managed tables through rewrite rules.',\n );\n else if (managedSource && rule.referenced_schema !== contract.schema)\n unsupported(\n 'user-rewrite-rule',\n `${qualified(rule.schema, rule.table_name)} (${rule.rule_name})`,\n 'Managed rewrite rules may not reference external relations: rule-owner table privileges can invoke external triggers outside the retained-table isolation boundary.',\n );\n else if (\n rule.referenced_schema === contract.schema &&\n retained.has(rule.referenced_table)\n )\n unsupported(\n 'user-rewrite-rule',\n `${qualified(rule.schema, rule.table_name)} (${rule.rule_name})`,\n 'User-defined rewrite rules are unsupported because rule-owner privileges can write retained tables indirectly.',\n );\n }\n for (const [table, columns] of Object.entries(\n contract.monitor?.tables ?? {},\n )) {\n const relation = tables.find((entry) => entry.name === table);\n if (!declared.has(table) || retained.has(table) || !relation)\n unsupported(\n 'monitor-table',\n table,\n 'Monitor tables must exist and belong to the managed or framework table surface, never retained tables.',\n );\n for (const column of columns)\n if (\n !relation ||\n !state.columns.some(\n (entry) => entry.relation === relation.oid && entry.name === column,\n )\n )\n unsupported(\n 'monitor-column',\n `${table}.${column}`,\n 'The declared monitoring column does not exist.',\n );\n }\n for (const relation of state.relations) {\n const resource = qualified(relation.schema, relation.name);\n const columns = state.columns.filter(\n (entry) => entry.relation === relation.oid,\n );\n if (relation.schema !== contract.schema) {\n for (const role of roles)\n if (\n relation.owner === role ||\n relevant(relation.acl, role).length ||\n columns.some((column) => relevant(column.acl, role).length)\n )\n unsupported(\n 'outside-resource-access',\n resource,\n 'Effective access to an unrelated relation requires infrastructure review; unrelated ACLs are preserved.',\n role,\n );\n continue;\n }\n if (relation.owner !== contract.migrationOwner)\n unsupported(\n 'resource-owner',\n resource,\n `Managed resources must be owned by ${contract.migrationOwner}; ownership is never changed.`,\n );\n if (\n relation.kind !== 'S' &&\n !declared.has(relation.name) &&\n !retained.has(relation.name)\n )\n unsupported(\n 'undeclared-table',\n resource,\n 'The dedicated schema contains an undeclared table; defaults cannot safely cover a mixed schema.',\n );\n if (\n relation.kind === 'S' &&\n (!relation.parent_table ||\n (!declared.has(relation.parent_table) &&\n !retained.has(relation.parent_table)))\n )\n unsupported(\n 'undeclared-sequence',\n resource,\n 'Sequences must belong to a declared managed or retained table before permission reconciliation.',\n );\n if (!['r', 'p', 'S'].includes(relation.kind))\n unsupported(\n 'unsupported-relation',\n resource,\n `Relation kind ${relation.kind} requires a separate security review.`,\n );\n if (relation.rls)\n unsupported(\n 'row-level-security',\n resource,\n 'RLS policies require a separate application authorization review; ACL verification cannot establish allowed row operations.',\n );\n for (const role of roles) {\n const isRuntime = role === contract.runtimeRole;\n const desired = isRuntime\n ? relation.kind === 'S'\n ? retained.has(relation.parent_table ?? '')\n ? []\n : ['USAGE']\n : retained.has(relation.name)\n ? []\n : bookkeeping.has(relation.name)\n ? ['SELECT']\n : ['SELECT', 'INSERT', 'UPDATE', 'DELETE']\n : [];\n reconcile(\n relation.kind === 'S' ? 'SEQUENCE' : 'TABLE',\n resource,\n relation.acl,\n role,\n desired,\n );\n for (const column of columns) {\n const wanted =\n !isRuntime &&\n !retained.has(relation.name) &&\n (contract.monitor?.tables[relation.name] ?? []).includes(column.name)\n ? ['SELECT']\n : [];\n reconcile(\n 'TABLE',\n resource,\n column.acl,\n role,\n wanted,\n ` (${identifier(column.name)})`,\n );\n }\n }\n }\n const triggerRoutineNames = new Set(contract.managedTriggerFunctions);\n const acceptedTriggerRoutineNames = new Set<string>();\n const acceptedTriggerRoutineOids = new Set<string>();\n const routineConfigIsEmpty = (value: unknown) =>\n (Array.isArray(value) && value.length === 0) || value === '[]';\n for (const resource of state.routines) {\n const resourceName = qualified(resource.schema, resource.name);\n const framework = frameworkRoutines.find(\n (expected) =>\n resource.schema === contract.schema &&\n resource.name === expected.name &&\n normalizeArgumentTypes(resource.argument_types) ===\n expected.argumentTypes,\n );\n if (framework) {\n const valid =\n resource.owner === contract.migrationOwner &&\n resource.kind === 'f' &&\n resource.language === 'plpgsql' &&\n resource.security_definer === false &&\n resource.argument_defaults === framework.argumentDefaults &&\n normalizeFunctionResult(resource.result) === framework.result &&\n resource.cost === '100' &&\n resource.rows === '1000' &&\n resource.support === '-' &&\n resource.volatility === 'v' &&\n resource.parallel === 'u' &&\n resource.leakproof === false &&\n resource.strict === false &&\n routineConfigIsEmpty(resource.config) &&\n normalizeFunctionSource(String(resource.source)) === framework.source;\n if (!valid)\n unsupported(\n 'framework-routine-definition',\n resourceName,\n 'Framework routine must retain its canonical identity, owner, invoker security, defaults, execution properties, settings and generated body.',\n );\n else\n reconcileRoutine(\n resource.identity,\n resource.acl,\n new Map([[contract.runtimeRole, ['EXECUTE']]]),\n );\n continue;\n }\n const bindings = state.triggers.filter(\n (trigger) => trigger.function_oid === resource.oid,\n );\n const triggerRoutine =\n resource.schema === contract.schema &&\n triggerRoutineNames.has(resource.name) &&\n resource.kind === 'f' &&\n resource.return_type === 'trigger' &&\n resource.argument_types === '' &&\n resource.argument_count === '0' &&\n resource.owner === contract.migrationOwner &&\n resource.language === 'plpgsql' &&\n resource.security_definer === false &&\n routineConfigIsEmpty(resource.config) &&\n bindings.length > 0 &&\n bindings.every(\n (trigger) =>\n trigger.schema === contract.schema &&\n trigger.internal === false &&\n (trigger.enabled === 'O' || trigger.enabled === 'A') &&\n declared.has(trigger.table_name),\n );\n if (triggerRoutine) {\n acceptedTriggerRoutineNames.add(resource.name);\n acceptedTriggerRoutineOids.add(resource.oid);\n reconcileRoutine(resource.identity, resource.acl, new Map());\n continue;\n }\n for (const role of roles)\n if (\n resource.schema === contract.schema ||\n resource.owner === role ||\n relevant(resource.acl, role).length\n )\n unsupported(\n 'unsupported-routines',\n resourceName,\n triggerRoutineNames.has(resource.name)\n ? 'Declared trigger routines must be migration-owner invoker PL/pgSQL functions returning trigger with no declared arguments, no function settings, and enabled non-internal bindings only to declared managed tables.'\n : 'User-defined routines are outside the supported table/sequence and declared trigger contract; review their effective privileges explicitly.',\n role,\n );\n }\n for (const name of triggerRoutineNames)\n if (!acceptedTriggerRoutineNames.has(name))\n unsupported(\n 'missing-managed-trigger-function',\n qualified(contract.schema, name),\n 'Run the migration that creates this exact managed trigger function and its enabled binding before reconciling permissions.',\n );\n for (const trigger of state.triggers)\n if (\n trigger.schema === contract.schema &&\n declared.has(trigger.table_name) &&\n trigger.internal === false &&\n (trigger.enabled === 'O' || trigger.enabled === 'A') &&\n !acceptedTriggerRoutineOids.has(trigger.function_oid)\n )\n unsupported(\n 'unsupported-managed-trigger',\n `${qualified(trigger.schema, trigger.table_name)} (${trigger.name})`,\n 'Enabled managed-table triggers must use an exact declared invoker trigger routine; undeclared or SECURITY DEFINER trigger functions can bypass retained-table isolation.',\n );\n for (const resource of state.types)\n for (const role of roles)\n if (\n resource.schema === contract.schema ||\n resource.owner === role ||\n relevant(resource.acl, role).length\n )\n unsupported(\n 'unsupported-types',\n qualified(resource.schema, resource.name),\n 'User-defined types are outside the supported table/sequence contract; review their effective privileges explicitly.',\n role,\n );\n // Global grants combine with schema grants: a schema REVOKE cannot cancel them.\n for (const defaults of state.defaults) {\n const resource = `DEFAULT ${defaults.kind} (${defaults.schema || 'global'})`;\n if (\n defaults.owner !== contract.migrationOwner &&\n defaults.acl.some((entry: Acl) => roles.includes(entry.grantee))\n )\n unsupported(\n 'other-creator-access',\n resource,\n `Creator ${defaults.owner} grants configured roles future access outside this contract.`,\n );\n if (\n defaults.schema !== contract.schema &&\n defaults.schema !== '' &&\n defaults.acl.some(\n (entry: Acl) =>\n entry.grantee === 'PUBLIC' || roles.includes(entry.grantee),\n )\n )\n unsupported(\n 'outside-default-privilege',\n resource,\n 'Defaults outside the managed schema grant future access and require infrastructure review.',\n );\n if (\n defaults.schema === '' &&\n defaults.acl.some(\n (entry: Acl) =>\n roles.includes(entry.grantee) ||\n (entry.grantee === 'PUBLIC' &&\n ['r', 'S', 'n'].includes(defaults.kind)),\n )\n )\n unsupported(\n 'global-default-privilege',\n resource,\n 'Global creator defaults grant configured roles or PUBLIC access and cannot be repaired within this schema.',\n );\n if (\n defaults.schema === contract.schema &&\n defaults.owner !== contract.migrationOwner\n )\n unsupported(\n 'other-creator-default',\n resource,\n `Defaults for creator ${defaults.owner} require separate ownership review.`,\n );\n if (\n defaults.schema === contract.schema &&\n !['r', 'S'].includes(defaults.kind) &&\n defaults.acl.some(\n (entry: Acl) =>\n roles.includes(entry.grantee) || entry.grantee === 'PUBLIC',\n )\n )\n unsupported(\n 'unsupported-default',\n resource,\n 'Only table and sequence schema defaults are supported.',\n );\n }\n for (const [kind, sqlKind, runtime] of [\n ['r', 'TABLES', ['SELECT', 'INSERT', 'UPDATE', 'DELETE']],\n ['S', 'SEQUENCES', ['USAGE']],\n ] as const) {\n const entry = state.defaults.find(\n (item) =>\n item.schema === contract.schema &&\n item.owner === contract.migrationOwner &&\n item.kind === kind,\n );\n if (entry?.acl.some((grant) => grant.grantee === 'PUBLIC')) {\n unsupported(\n 'public-default',\n `${contract.schema} ${sqlKind}`,\n 'PUBLIC creator defaults are outside the explicit role contract and require infrastructure review.',\n );\n }\n for (const role of roles) {\n const desired: readonly string[] =\n role === contract.runtimeRole ? runtime : [];\n const effective = relevant(entry?.acl ?? [], role);\n const extra = effective.filter(\n (grant) => !desired.includes(grant.privilege) || grant.grantable,\n );\n const missing = desired.filter(\n (privilege) =>\n !(entry?.acl ?? []).some(\n (grant) => grant.grantee === role && grant.privilege === privilege,\n ),\n );\n if (!extra.length && !missing.length) continue;\n for (const grant of extra) {\n add(\n 'excessive',\n 'default-excessive',\n `${contract.schema} ${sqlKind}`,\n `${grant.privilege} default from ${grant.grantee} is not declared.`,\n role,\n );\n if (grant.grantee === 'PUBLIC')\n unsupported(\n 'public-default',\n contract.schema,\n 'PUBLIC defaults affect other roles and require infrastructure repair.',\n );\n }\n for (const privilege of missing)\n add(\n 'missing',\n 'default-missing',\n `${contract.schema} ${sqlKind}`,\n `Creator ${contract.migrationOwner} must default ${privilege}.`,\n role,\n );\n const prefix = `ALTER DEFAULT PRIVILEGES FOR ROLE ${identifier(contract.migrationOwner)} IN SCHEMA ${identifier(contract.schema)}`;\n statements.push(\n `${prefix} REVOKE ALL PRIVILEGES ON ${sqlKind} FROM ${identifier(role)}`,\n );\n if (desired.length)\n statements.push(\n `${prefix} GRANT ${desired.join(', ')} ON ${sqlKind} TO ${identifier(role)}`,\n );\n }\n }\n const executor = state.server[0];\n if (\n statements.length &&\n !executor.superuser &&\n executor.executor !== contract.migrationOwner\n )\n unsupported(\n 'execution-authority',\n 'database',\n 'Apply requires the migration owner or a superuser; plan and apply must use the same actor.',\n );\n if (\n statements.some((sql) => sql.includes(' ON DATABASE ')) &&\n !executor.superuser &&\n database.owner !== executor.executor\n )\n unsupported(\n 'database-authority',\n database.name,\n 'Database ACL remediation requires its owner or a superuser.',\n );\n return {\n contract,\n diagnostics,\n limitations: [\n 'Only PostgreSQL table, column, sequence, schema and database ACLs are qualified; application authorization and RLS are separate. Large-object ownership/access is unsupported, and lo_compat_privileges must remain off for deployed roles.',\n 'Future user-defined routines and types are unsupported. PostgreSQL implicit global defaults grant PUBLIC EXECUTE/USAGE; rerun diagnostics after every migration before activating runtime roles.',\n 'Pause migrations and external ACL/role writers during planning and apply. The advisory lock coordinates SMRT permission writers only.',\n 'All framework bookkeeping tables must exist before setup. Stop runtime and monitor access throughout migrations, restores and repair: recreated bookkeeping tables temporarily receive creator CRUD defaults and require explicit reconciliation before either role is reactivated.',\n 'Retained tables are operator-owned data outside the runtime and monitor surfaces. Creator defaults initially grant runtime access to every new table and sequence, so create or restore every retained table first, then apply its reviewed plan and verify access is revoked before reactivating restricted roles.',\n ],\n statements: [...new Set(statements)],\n canApply: !diagnostics.some((entry) => entry.severity === 'unsupported'),\n fingerprint: createHash('sha256')\n .update(JSON.stringify({ contract, state }))\n .digest('hex'),\n };\n}\n\n/** Read-only catalog inspection. No application records or credentials are read. */\nexport async function planPostgresPermissions(\n db: Executor,\n contract: PostgresPermissionContract,\n): Promise<PostgresPermissionPlan> {\n return plan(db, contract);\n}\n\n/** Apply the reviewed contract atomically, rejecting stale catalog snapshots. */\nexport async function applyPostgresPermissions(\n db: DatabaseInterface,\n contract: PostgresPermissionContract,\n options: { expectedFingerprint: string },\n): Promise<PostgresPermissionPlan> {\n const normalizedContract = validatePostgresPermissionContract(contract);\n if (!options || !/^[a-f0-9]{64}$/.test(options.expectedFingerprint))\n throw new Error(\n 'An expectedFingerprint from a reviewed permission plan is required.',\n );\n if (!db.transaction)\n throw new Error(\n 'PostgreSQL permissions require a transaction-capable database.',\n );\n return db.transaction(async (tx) => {\n await tx.query(\"SET LOCAL lock_timeout = '5s'\");\n await tx.query(\"SET LOCAL statement_timeout = '30s'\");\n // Cooperating permission writers serialize; operators must quiesce migrations.\n await tx.query(\n `SELECT pg_advisory_xact_lock(hashtext(${literal(`smrt-permissions:${normalizedContract.schema}`)}))`,\n );\n const before = await plan(tx, normalizedContract);\n if (before.fingerprint !== options.expectedFingerprint)\n throw new Error(\n 'Permission plan is stale; generate and review a new plan.',\n );\n if (!before.canApply)\n throw new Error(\n 'Permission plan has unsupported privilege sources or missing prerequisites; resolve its diagnostics first.',\n );\n const executor = (\n await tx.query(\n 'SELECT current_user AS name, rolsuper FROM pg_roles WHERE rolname=current_user',\n )\n ).rows[0];\n if (\n !executor.rolsuper &&\n executor.name !== normalizedContract.migrationOwner\n )\n throw new Error(\n 'Apply must execute as the migration owner or a PostgreSQL superuser.',\n );\n for (const statement of before.statements) await tx.query(statement);\n const after = await plan(tx, normalizedContract);\n if (!after.canApply || after.diagnostics.length)\n throw new Error(\n 'Permission verification failed; all permission changes have been rolled back.',\n );\n return after;\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prebuild/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prebuild/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAW5D,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACvC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sCAAsC;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkCf;AA4aD;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
package/dist/prebuild/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { selectWebCollectionEntries } from "../vite-plugin/web-collections.js";
|
|
1
|
+
import { compareText, selectWebCollectionEntries } from "../vite-plugin/web-collections.js";
|
|
2
2
|
import { renderApiClientCrudType, renderApiClientCustomMethodParameters, selectApiClientEntries } from "../vite-plugin/api-client-entries.js";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
@@ -25,7 +25,8 @@ async function generateDeclarations(options) {
|
|
|
25
25
|
*/
|
|
26
26
|
async function generateObjectTypeDeclarations(manifest, outDir) {
|
|
27
27
|
const interfaces = [];
|
|
28
|
-
|
|
28
|
+
const sortedObjectEntries = Object.entries(manifest.objects).sort(([leftKey, left], [rightKey, right]) => compareText(left.qualifiedName || leftKey, right.qualifiedName || rightKey) || compareText(leftKey, rightKey));
|
|
29
|
+
for (const [_objectName, objectMeta] of sortedObjectEntries) {
|
|
29
30
|
const fields = objectMeta.fields || {};
|
|
30
31
|
const propertyLines = [];
|
|
31
32
|
propertyLines.push(" id?: string;");
|
|
@@ -191,7 +192,7 @@ declare module '@smrt/mcp' {
|
|
|
191
192
|
* Auto-generated types module declaration
|
|
192
193
|
*/
|
|
193
194
|
declare module '@smrt/types' {
|
|
194
|
-
${Object.
|
|
195
|
+
${Object.entries(manifest.objects).sort(([leftKey, left], [rightKey, right]) => compareText(left.qualifiedName || leftKey, right.qualifiedName || rightKey) || compareText(leftKey, rightKey)).map(([, obj]) => ` export type ${obj.className}Data = import('./smrt-objects').${obj.className}Data;`).join("\n")}
|
|
195
196
|
|
|
196
197
|
export interface Request {
|
|
197
198
|
params: Record<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/prebuild/index.ts"],"sourcesContent":["/**\n * Pre-build utilities for generating TypeScript declarations\n * Solves virtual module resolution by creating physical .d.ts files\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { SmartObjectManifest } from '../scanner/types';\nimport {\n renderApiClientCrudType,\n renderApiClientCustomMethodParameters,\n selectApiClientEntries,\n} from '../vite-plugin/api-client-entries.js';\nimport { selectWebCollectionEntries } from '../vite-plugin/web-collections.js';\n\nexport interface PrebuildOptions {\n /** Path to manifest file or manifest object */\n manifest: string | SmartObjectManifest;\n /** Output directory for generated types */\n outDir: string;\n /** Include virtual module declarations */\n includeVirtualModules?: boolean;\n /** Include object type definitions */\n includeObjectTypes?: boolean;\n /** Project root path for resolving relative paths */\n projectRoot?: string;\n}\n\n/**\n * Generate TypeScript declaration files from SMRT manifest\n */\nexport async function generateDeclarations(\n options: PrebuildOptions,\n): Promise<void> {\n const {\n manifest: manifestInput,\n outDir,\n includeVirtualModules = true,\n includeObjectTypes = true,\n projectRoot = process.cwd(),\n } = options;\n\n // Load manifest\n const manifest: SmartObjectManifest =\n typeof manifestInput === 'string'\n ? JSON.parse(fs.readFileSync(manifestInput, 'utf-8'))\n : manifestInput;\n\n // Ensure output directory exists\n const fullOutDir = path.isAbsolute(outDir)\n ? outDir\n : path.join(projectRoot, outDir);\n fs.mkdirSync(fullOutDir, { recursive: true });\n\n console.log(`[smrt:prebuild] Generating declarations to ${fullOutDir}`);\n\n if (includeObjectTypes) {\n await generateObjectTypeDeclarations(manifest, fullOutDir);\n }\n\n if (includeVirtualModules) {\n await generateVirtualModuleDeclarations(manifest, fullOutDir);\n }\n\n console.log(\n `[smrt:prebuild] Generated declarations for ${Object.keys(manifest.objects).length} SMRT objects`,\n );\n}\n\n/**\n * Generate TypeScript interfaces for SMRT objects\n */\nasync function generateObjectTypeDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n const interfaces: string[] = [];\n\n // Generate interfaces for each discovered SMRT object\n for (const [_objectName, objectMeta] of Object.entries(manifest.objects)) {\n const fields = objectMeta.fields || {};\n const propertyLines: string[] = [];\n\n // Add standard SmrtObject properties\n propertyLines.push(' id?: string;');\n propertyLines.push(' created_at?: string;');\n propertyLines.push(' updated_at?: string;');\n\n // Add object-specific properties\n for (const [fieldName, fieldDef] of Object.entries(fields)) {\n const type = mapFieldTypeToTypeScript(fieldDef.type);\n const optional = !fieldDef.required ? '?' : '';\n propertyLines.push(` ${fieldName}${optional}: ${type};`);\n }\n\n const interfaceDef = `export interface ${objectMeta.className}Data {\n${propertyLines.join('\\n')}\n}`;\n interfaces.push(interfaceDef);\n }\n\n // Write object types file\n const objectTypesContent = `/**\n * Auto-generated TypeScript interfaces for SMRT objects\n * Generated at build time from @smrt() decorated classes\n *\n * DO NOT EDIT THIS FILE MANUALLY\n */\n\n${interfaces.join('\\n\\n')}\n`;\n\n fs.writeFileSync(path.join(outDir, 'smrt-objects.d.ts'), objectTypesContent);\n}\n\n/**\n * Generate virtual module declarations\n */\nasync function generateVirtualModuleDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n // Generate manifest module declaration\n const manifestDeclaration = `/**\n * Auto-generated manifest module declaration\n */\ndeclare module '@smrt/manifest' {\n export interface SmrtObjectField {\n type: string;\n required?: boolean;\n default?: any;\n }\n\n export interface SmrtObjectMethod {\n name: string;\n parameters: Array<{\n name: string;\n type: string;\n optional?: boolean;\n default?: any;\n }>;\n returnType: string;\n async: boolean;\n isStatic: boolean;\n isPublic: boolean;\n }\n\n export interface SmrtObjectDefinition {\n name: string;\n className: string;\n collection: string;\n filePath: string;\n fields: Record<string, SmrtObjectField>;\n methods: Record<string, SmrtObjectMethod>;\n decoratorConfig: any;\n extends?: string;\n }\n\n export interface SmrtManifest {\n version: string;\n timestamp: number;\n objects: Record<string, SmrtObjectDefinition>;\n }\n\n export const manifest: SmrtManifest;\n export default manifest;\n}`;\n\n // Generate client module declaration\n const apiClientInterface = selectApiClientEntries(manifest)\n .map(({ clientKey, dataInterfaceName, crudMethods, customMethods }) => {\n const overriddenMethods = customMethods.map(({ name }) => name);\n const crudType = renderApiClientCrudType(\n dataInterfaceName,\n crudMethods,\n overriddenMethods,\n );\n const customSignatures = customMethods\n .map(\n (method) =>\n ` ${method.name}(${renderApiClientCustomMethodParameters(\n method,\n mapFieldTypeToTypeScript,\n )}): Promise<any>;`,\n )\n .join('\\n');\n\n if (customSignatures) {\n const intersection = crudType ? `${crudType} & ` : '';\n return ` ${JSON.stringify(clientKey)}: ${intersection}{\\n${customSignatures}\\n };`;\n }\n\n return ` ${JSON.stringify(clientKey)}: ${crudType ?? 'Record<string, never>'};`;\n })\n .join('\\n');\n\n // Wire-shape policy (#1797): the server returns BARE JSON — a bare array for\n // list/search, a bare object for get/create/update — with snake_case field\n // names (created_at, updated_at). These physical declarations share client\n // entry selection and method-signature rendering with the Vite declaration.\n // Fetchers reject on non-2xx with a SmrtClientError (#1796).\n const clientDeclaration = `/**\n * Auto-generated API client module declaration\n */\ndeclare module '@smrt/client' {\n /** Structured custom-action failure nested under an API error body's \\`error\\` key. */\n export interface SmrtClientFailure {\n ok: false;\n code: string;\n message: string;\n status?: number;\n }\n\n /** Shape of a JSON error body carried by a rejected request (SmrtClientError.body). */\n export interface ApiError {\n error?: string | SmrtClientFailure;\n message?: string;\n }\n\n /** Typed error thrown by every fetcher on a non-2xx response (#1796). */\n export interface SmrtClientError extends Error {\n name: 'SmrtClientError';\n status: number;\n /** Machine-readable code from a structured custom-action failure, when present. */\n code?: string;\n body?: ApiError | string;\n }\n\n export interface CrudOperations<T = any> {\n list(params?: Record<string, any>): Promise<T[]>;\n get(id: string): Promise<T>;\n create(data: Partial<T>): Promise<T>;\n update(id: string, data: Partial<T>): Promise<T>;\n delete(id: string): Promise<boolean>;\n search(query: string): Promise<T[]>;\n }\n\n export interface ApiClient {\n${apiClientInterface}\n }\n\n export function createClient(basePath?: string): ApiClient;\n export default createClient;\n}`;\n\n // Generate routes module declaration\n const routesDeclaration = `/**\n * Auto-generated routes module declaration\n */\ndeclare module '@smrt/routes' {\n export interface RouteApp {\n get(path: string, handler: (req: any, res: any) => void): void;\n post(path: string, handler: (req: any, res: any) => void): void;\n put(path: string, handler: (req: any, res: any) => void): void;\n delete(path: string, handler: (req: any, res: any) => void): void;\n }\n\n export function setupRoutes(app: RouteApp): void;\n export default setupRoutes;\n}`;\n\n // Generate MCP module declaration\n const mcpDeclaration = `/**\n * Auto-generated MCP module declaration\n */\ndeclare module '@smrt/mcp' {\n export interface McpTool {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n };\n }\n\n export const tools: McpTool[];\n export function createMCPServer(): {\n name: string;\n version: string;\n tools: McpTool[]\n };\n export default createMCPServer;\n}`;\n\n // Generate types module declaration with object imports\n const objectImports = Object.values(manifest.objects)\n .map(\n (obj) =>\n ` export type ${obj.className}Data = import('./smrt-objects').${obj.className}Data;`,\n )\n .join('\\n');\n\n const typesDeclaration = `/**\n * Auto-generated types module declaration\n */\ndeclare module '@smrt/types' {\n${objectImports}\n\n export interface Request {\n params: Record<string, string>;\n query: Record<string, any>;\n json(): Promise<any>;\n }\n\n export interface Response {\n json(data: any, init?: { status?: number }): Response;\n status(code: number): Response;\n }\n}`;\n\n // Generate web collection-definition module declaration (#1761). Selection\n // shares selectWebCollectionEntries with the vite-plugin runtime module and\n // its virt-web d.ts, so this `tsc`-only consumer declaration cannot drift\n // from the emitted values.\n const webCollectionEntries = selectWebCollectionEntries(manifest)\n .map(\n ({ collection, obj }) =>\n ` ${JSON.stringify(collection)}: SmrtWebCollectionDefinition<import('./smrt-objects').${obj.className}Data>;`,\n )\n .join('\\n');\n\n const webDeclaration = `/**\n * Auto-generated web collection-definition module declaration (#1761)\n */\ndeclare module '@smrt/web' {\n export type SmrtWebFieldType =\n | 'text'\n | 'decimal'\n | 'boolean'\n | 'integer'\n | 'datetime'\n | 'json'\n | 'foreignKey'\n | 'crossPackageRef';\n\n /** Static \\`@field({ ui })\\` hints (#2046) — the field-policy rail seed. */\n export interface SmrtWebFieldUIHints {\n basic?: boolean;\n group?: string;\n order?: number;\n locked?: boolean;\n }\n\n export interface SmrtWebFieldDefinition {\n type: SmrtWebFieldType;\n required?: boolean;\n nullable?: boolean;\n default?: unknown;\n /** Developer-authored \\`@field({ description })\\` (#2046) — end-user help seed. */\n description?: string;\n /** Static \\`@field({ ui })\\` hints (#2046). */\n ui?: SmrtWebFieldUIHints;\n }\n\n export type SmrtWebRelationshipKind =\n | 'foreignKey'\n | 'crossPackageRef'\n | 'oneToMany'\n | 'manyToMany';\n\n /**\n * A manifest-derived edge to a sibling REST collection. Mutating this\n * collection invalidates the caches of the collections named by these edges\n * (#1761 relationship-derived invalidation).\n */\n export interface SmrtWebRelationship {\n field: string;\n kind: SmrtWebRelationshipKind;\n relatedCollection: string;\n }\n\n export interface WebToolRouteDescriptor {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n scope: 'item' | 'collection';\n path: string[];\n parameterAliases?: Record<string, string>;\n optionsBag?: boolean;\n }\n\n /** A WebMCP/MCP tool descriptor for one collection action (#1812). */\n export interface WebToolDescriptor {\n action: string;\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n readOnly: boolean;\n effect: 'read' | 'write' | 'destructive';\n idempotent: boolean;\n openWorld: boolean;\n route?: WebToolRouteDescriptor;\n }\n\n /** Canonical data-only definition for one generated browser tool. */\n export interface WebMcpToolDefinition extends WebToolDescriptor {\n collection: string;\n objectRef: string;\n className: string;\n endpoint: string;\n idField: string;\n idType: 'uuid' | 'text';\n route: WebToolRouteDescriptor;\n relationships: SmrtWebRelationship[];\n }\n\n export interface SmrtWebCollectionDefinition<TData = Record<string, unknown>> {\n name: string;\n /** Canonical qualified model identity (\\`@package/name:ClassName\\`) for policy APIs. */\n objectRef: string;\n className: string;\n endpoint: string;\n idField: string;\n actions: string[];\n fields: Record<string, SmrtWebFieldDefinition>;\n /** Manifest-derived relationship edges to sibling REST collections. */\n relationships: SmrtWebRelationship[];\n /** WebMCP/MCP tool descriptors for the exposed actions (#1812). */\n toolDescriptors: WebToolDescriptor[];\n /** Phantom row-type carrier for inference — never present at runtime. */\n _row?: TData;\n }\n\n export interface SmrtWebCollectionDefinitions {\n${webCollectionEntries}\n }\n\n export const collectionDefinitions: SmrtWebCollectionDefinitions;\n /** Every API-backed WebMCP tool, independent of list materialization. */\n export const webMcpToolDefinitions: readonly WebMcpToolDefinition[];\n export function getCollectionDefinition<\n K extends keyof SmrtWebCollectionDefinitions,\n >(name: K): SmrtWebCollectionDefinitions[K];\n /**\n * Build-time web-collection shape digest (#1764). A deterministic,\n * replica-stable hash of the emitted collection definitions; a change means\n * old persisted client rows may mis-hydrate. Consumers fold it into the\n * durable persistence namespace and the version-awareness updateAvailable\n * contract signal.\n */\n export const manifestHash: string;\n export default collectionDefinitions;\n}`;\n\n // Write all virtual module declarations\n fs.writeFileSync(\n path.join(outDir, 'smrt-manifest.d.ts'),\n manifestDeclaration,\n );\n fs.writeFileSync(path.join(outDir, 'smrt-client.d.ts'), clientDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-routes.d.ts'), routesDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-mcp.d.ts'), mcpDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-types.d.ts'), typesDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-web.d.ts'), webDeclaration);\n}\n\n/**\n * Map SMRT field types to TypeScript types\n */\nfunction mapFieldTypeToTypeScript(smrtType: string): string {\n switch (smrtType) {\n case 'text':\n return 'string';\n case 'decimal':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'string | Date';\n case 'json':\n return 'any';\n case 'foreignKey':\n return 'string';\n default:\n return 'any';\n }\n}\n\n/**\n * CLI command for generating declarations\n */\nexport async function generateDeclarationsFromCLI(\n args: string[],\n): Promise<void> {\n const manifestPath = args[0];\n const outDir = args[1] || 'src/types/generated';\n\n if (!manifestPath) {\n console.error('Usage: generate-declarations <manifest-path> [output-dir]');\n process.exit(1);\n }\n\n if (!fs.existsSync(manifestPath)) {\n console.error(`Manifest file not found: ${manifestPath}`);\n process.exit(1);\n }\n\n await generateDeclarations({\n manifest: manifestPath,\n outDir,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/prebuild/index.ts"],"sourcesContent":["/**\n * Pre-build utilities for generating TypeScript declarations\n * Solves virtual module resolution by creating physical .d.ts files\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { SmartObjectManifest } from '../scanner/types';\nimport {\n renderApiClientCrudType,\n renderApiClientCustomMethodParameters,\n selectApiClientEntries,\n} from '../vite-plugin/api-client-entries.js';\nimport {\n compareText,\n selectWebCollectionEntries,\n} from '../vite-plugin/web-collections.js';\n\nexport interface PrebuildOptions {\n /** Path to manifest file or manifest object */\n manifest: string | SmartObjectManifest;\n /** Output directory for generated types */\n outDir: string;\n /** Include virtual module declarations */\n includeVirtualModules?: boolean;\n /** Include object type definitions */\n includeObjectTypes?: boolean;\n /** Project root path for resolving relative paths */\n projectRoot?: string;\n}\n\n/**\n * Generate TypeScript declaration files from SMRT manifest\n */\nexport async function generateDeclarations(\n options: PrebuildOptions,\n): Promise<void> {\n const {\n manifest: manifestInput,\n outDir,\n includeVirtualModules = true,\n includeObjectTypes = true,\n projectRoot = process.cwd(),\n } = options;\n\n // Load manifest\n const manifest: SmartObjectManifest =\n typeof manifestInput === 'string'\n ? JSON.parse(fs.readFileSync(manifestInput, 'utf-8'))\n : manifestInput;\n\n // Ensure output directory exists\n const fullOutDir = path.isAbsolute(outDir)\n ? outDir\n : path.join(projectRoot, outDir);\n fs.mkdirSync(fullOutDir, { recursive: true });\n\n console.log(`[smrt:prebuild] Generating declarations to ${fullOutDir}`);\n\n if (includeObjectTypes) {\n await generateObjectTypeDeclarations(manifest, fullOutDir);\n }\n\n if (includeVirtualModules) {\n await generateVirtualModuleDeclarations(manifest, fullOutDir);\n }\n\n console.log(\n `[smrt:prebuild] Generated declarations for ${Object.keys(manifest.objects).length} SMRT objects`,\n );\n}\n\n/**\n * Generate TypeScript interfaces for SMRT objects\n */\nasync function generateObjectTypeDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n const interfaces: string[] = [];\n\n // Generate interfaces for each discovered SMRT object. Manifest key order\n // follows scan/discovery order, which is not stable across runs (#2749),\n // so sort deterministically before emitting.\n const sortedObjectEntries = Object.entries(manifest.objects).sort(\n ([leftKey, left], [rightKey, right]) =>\n compareText(\n left.qualifiedName || leftKey,\n right.qualifiedName || rightKey,\n ) || compareText(leftKey, rightKey),\n );\n for (const [_objectName, objectMeta] of sortedObjectEntries) {\n const fields = objectMeta.fields || {};\n const propertyLines: string[] = [];\n\n // Add standard SmrtObject properties\n propertyLines.push(' id?: string;');\n propertyLines.push(' created_at?: string;');\n propertyLines.push(' updated_at?: string;');\n\n // Add object-specific properties\n for (const [fieldName, fieldDef] of Object.entries(fields)) {\n const type = mapFieldTypeToTypeScript(fieldDef.type);\n const optional = !fieldDef.required ? '?' : '';\n propertyLines.push(` ${fieldName}${optional}: ${type};`);\n }\n\n const interfaceDef = `export interface ${objectMeta.className}Data {\n${propertyLines.join('\\n')}\n}`;\n interfaces.push(interfaceDef);\n }\n\n // Write object types file\n const objectTypesContent = `/**\n * Auto-generated TypeScript interfaces for SMRT objects\n * Generated at build time from @smrt() decorated classes\n *\n * DO NOT EDIT THIS FILE MANUALLY\n */\n\n${interfaces.join('\\n\\n')}\n`;\n\n fs.writeFileSync(path.join(outDir, 'smrt-objects.d.ts'), objectTypesContent);\n}\n\n/**\n * Generate virtual module declarations\n */\nasync function generateVirtualModuleDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n // Generate manifest module declaration\n const manifestDeclaration = `/**\n * Auto-generated manifest module declaration\n */\ndeclare module '@smrt/manifest' {\n export interface SmrtObjectField {\n type: string;\n required?: boolean;\n default?: any;\n }\n\n export interface SmrtObjectMethod {\n name: string;\n parameters: Array<{\n name: string;\n type: string;\n optional?: boolean;\n default?: any;\n }>;\n returnType: string;\n async: boolean;\n isStatic: boolean;\n isPublic: boolean;\n }\n\n export interface SmrtObjectDefinition {\n name: string;\n className: string;\n collection: string;\n filePath: string;\n fields: Record<string, SmrtObjectField>;\n methods: Record<string, SmrtObjectMethod>;\n decoratorConfig: any;\n extends?: string;\n }\n\n export interface SmrtManifest {\n version: string;\n timestamp: number;\n objects: Record<string, SmrtObjectDefinition>;\n }\n\n export const manifest: SmrtManifest;\n export default manifest;\n}`;\n\n // Generate client module declaration\n const apiClientInterface = selectApiClientEntries(manifest)\n .map(({ clientKey, dataInterfaceName, crudMethods, customMethods }) => {\n const overriddenMethods = customMethods.map(({ name }) => name);\n const crudType = renderApiClientCrudType(\n dataInterfaceName,\n crudMethods,\n overriddenMethods,\n );\n const customSignatures = customMethods\n .map(\n (method) =>\n ` ${method.name}(${renderApiClientCustomMethodParameters(\n method,\n mapFieldTypeToTypeScript,\n )}): Promise<any>;`,\n )\n .join('\\n');\n\n if (customSignatures) {\n const intersection = crudType ? `${crudType} & ` : '';\n return ` ${JSON.stringify(clientKey)}: ${intersection}{\\n${customSignatures}\\n };`;\n }\n\n return ` ${JSON.stringify(clientKey)}: ${crudType ?? 'Record<string, never>'};`;\n })\n .join('\\n');\n\n // Wire-shape policy (#1797): the server returns BARE JSON — a bare array for\n // list/search, a bare object for get/create/update — with snake_case field\n // names (created_at, updated_at). These physical declarations share client\n // entry selection and method-signature rendering with the Vite declaration.\n // Fetchers reject on non-2xx with a SmrtClientError (#1796).\n const clientDeclaration = `/**\n * Auto-generated API client module declaration\n */\ndeclare module '@smrt/client' {\n /** Structured custom-action failure nested under an API error body's \\`error\\` key. */\n export interface SmrtClientFailure {\n ok: false;\n code: string;\n message: string;\n status?: number;\n }\n\n /** Shape of a JSON error body carried by a rejected request (SmrtClientError.body). */\n export interface ApiError {\n error?: string | SmrtClientFailure;\n message?: string;\n }\n\n /** Typed error thrown by every fetcher on a non-2xx response (#1796). */\n export interface SmrtClientError extends Error {\n name: 'SmrtClientError';\n status: number;\n /** Machine-readable code from a structured custom-action failure, when present. */\n code?: string;\n body?: ApiError | string;\n }\n\n export interface CrudOperations<T = any> {\n list(params?: Record<string, any>): Promise<T[]>;\n get(id: string): Promise<T>;\n create(data: Partial<T>): Promise<T>;\n update(id: string, data: Partial<T>): Promise<T>;\n delete(id: string): Promise<boolean>;\n search(query: string): Promise<T[]>;\n }\n\n export interface ApiClient {\n${apiClientInterface}\n }\n\n export function createClient(basePath?: string): ApiClient;\n export default createClient;\n}`;\n\n // Generate routes module declaration\n const routesDeclaration = `/**\n * Auto-generated routes module declaration\n */\ndeclare module '@smrt/routes' {\n export interface RouteApp {\n get(path: string, handler: (req: any, res: any) => void): void;\n post(path: string, handler: (req: any, res: any) => void): void;\n put(path: string, handler: (req: any, res: any) => void): void;\n delete(path: string, handler: (req: any, res: any) => void): void;\n }\n\n export function setupRoutes(app: RouteApp): void;\n export default setupRoutes;\n}`;\n\n // Generate MCP module declaration\n const mcpDeclaration = `/**\n * Auto-generated MCP module declaration\n */\ndeclare module '@smrt/mcp' {\n export interface McpTool {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n };\n }\n\n export const tools: McpTool[];\n export function createMCPServer(): {\n name: string;\n version: string;\n tools: McpTool[]\n };\n export default createMCPServer;\n}`;\n\n // Generate types module declaration with object imports. Sorted for the\n // same reason as generateObjectTypeDeclarations (#2749).\n const objectImports = Object.entries(manifest.objects)\n .sort(\n ([leftKey, left], [rightKey, right]) =>\n compareText(\n left.qualifiedName || leftKey,\n right.qualifiedName || rightKey,\n ) || compareText(leftKey, rightKey),\n )\n .map(\n ([, obj]) =>\n ` export type ${obj.className}Data = import('./smrt-objects').${obj.className}Data;`,\n )\n .join('\\n');\n\n const typesDeclaration = `/**\n * Auto-generated types module declaration\n */\ndeclare module '@smrt/types' {\n${objectImports}\n\n export interface Request {\n params: Record<string, string>;\n query: Record<string, any>;\n json(): Promise<any>;\n }\n\n export interface Response {\n json(data: any, init?: { status?: number }): Response;\n status(code: number): Response;\n }\n}`;\n\n // Generate web collection-definition module declaration (#1761). Selection\n // shares selectWebCollectionEntries with the vite-plugin runtime module and\n // its virt-web d.ts, so this `tsc`-only consumer declaration cannot drift\n // from the emitted values.\n const webCollectionEntries = selectWebCollectionEntries(manifest)\n .map(\n ({ collection, obj }) =>\n ` ${JSON.stringify(collection)}: SmrtWebCollectionDefinition<import('./smrt-objects').${obj.className}Data>;`,\n )\n .join('\\n');\n\n const webDeclaration = `/**\n * Auto-generated web collection-definition module declaration (#1761)\n */\ndeclare module '@smrt/web' {\n export type SmrtWebFieldType =\n | 'text'\n | 'decimal'\n | 'boolean'\n | 'integer'\n | 'datetime'\n | 'json'\n | 'foreignKey'\n | 'crossPackageRef';\n\n /** Static \\`@field({ ui })\\` hints (#2046) — the field-policy rail seed. */\n export interface SmrtWebFieldUIHints {\n basic?: boolean;\n group?: string;\n order?: number;\n locked?: boolean;\n }\n\n export interface SmrtWebFieldDefinition {\n type: SmrtWebFieldType;\n required?: boolean;\n nullable?: boolean;\n default?: unknown;\n /** Developer-authored \\`@field({ description })\\` (#2046) — end-user help seed. */\n description?: string;\n /** Static \\`@field({ ui })\\` hints (#2046). */\n ui?: SmrtWebFieldUIHints;\n }\n\n export type SmrtWebRelationshipKind =\n | 'foreignKey'\n | 'crossPackageRef'\n | 'oneToMany'\n | 'manyToMany';\n\n /**\n * A manifest-derived edge to a sibling REST collection. Mutating this\n * collection invalidates the caches of the collections named by these edges\n * (#1761 relationship-derived invalidation).\n */\n export interface SmrtWebRelationship {\n field: string;\n kind: SmrtWebRelationshipKind;\n relatedCollection: string;\n }\n\n export interface WebToolRouteDescriptor {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n scope: 'item' | 'collection';\n path: string[];\n parameterAliases?: Record<string, string>;\n optionsBag?: boolean;\n }\n\n /** A WebMCP/MCP tool descriptor for one collection action (#1812). */\n export interface WebToolDescriptor {\n action: string;\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n readOnly: boolean;\n effect: 'read' | 'write' | 'destructive';\n idempotent: boolean;\n openWorld: boolean;\n route?: WebToolRouteDescriptor;\n }\n\n /** Canonical data-only definition for one generated browser tool. */\n export interface WebMcpToolDefinition extends WebToolDescriptor {\n collection: string;\n objectRef: string;\n className: string;\n endpoint: string;\n idField: string;\n idType: 'uuid' | 'text';\n route: WebToolRouteDescriptor;\n relationships: SmrtWebRelationship[];\n }\n\n export interface SmrtWebCollectionDefinition<TData = Record<string, unknown>> {\n name: string;\n /** Canonical qualified model identity (\\`@package/name:ClassName\\`) for policy APIs. */\n objectRef: string;\n className: string;\n endpoint: string;\n idField: string;\n actions: string[];\n fields: Record<string, SmrtWebFieldDefinition>;\n /** Manifest-derived relationship edges to sibling REST collections. */\n relationships: SmrtWebRelationship[];\n /** WebMCP/MCP tool descriptors for the exposed actions (#1812). */\n toolDescriptors: WebToolDescriptor[];\n /** Phantom row-type carrier for inference — never present at runtime. */\n _row?: TData;\n }\n\n export interface SmrtWebCollectionDefinitions {\n${webCollectionEntries}\n }\n\n export const collectionDefinitions: SmrtWebCollectionDefinitions;\n /** Every API-backed WebMCP tool, independent of list materialization. */\n export const webMcpToolDefinitions: readonly WebMcpToolDefinition[];\n export function getCollectionDefinition<\n K extends keyof SmrtWebCollectionDefinitions,\n >(name: K): SmrtWebCollectionDefinitions[K];\n /**\n * Build-time web-collection shape digest (#1764). A deterministic,\n * replica-stable hash of the emitted collection definitions; a change means\n * old persisted client rows may mis-hydrate. Consumers fold it into the\n * durable persistence namespace and the version-awareness updateAvailable\n * contract signal.\n */\n export const manifestHash: string;\n export default collectionDefinitions;\n}`;\n\n // Write all virtual module declarations\n fs.writeFileSync(\n path.join(outDir, 'smrt-manifest.d.ts'),\n manifestDeclaration,\n );\n fs.writeFileSync(path.join(outDir, 'smrt-client.d.ts'), clientDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-routes.d.ts'), routesDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-mcp.d.ts'), mcpDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-types.d.ts'), typesDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-web.d.ts'), webDeclaration);\n}\n\n/**\n * Map SMRT field types to TypeScript types\n */\nfunction mapFieldTypeToTypeScript(smrtType: string): string {\n switch (smrtType) {\n case 'text':\n return 'string';\n case 'decimal':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'string | Date';\n case 'json':\n return 'any';\n case 'foreignKey':\n return 'string';\n default:\n return 'any';\n }\n}\n\n/**\n * CLI command for generating declarations\n */\nexport async function generateDeclarationsFromCLI(\n args: string[],\n): Promise<void> {\n const manifestPath = args[0];\n const outDir = args[1] || 'src/types/generated';\n\n if (!manifestPath) {\n console.error('Usage: generate-declarations <manifest-path> [output-dir]');\n process.exit(1);\n }\n\n if (!fs.existsSync(manifestPath)) {\n console.error(`Manifest file not found: ${manifestPath}`);\n process.exit(1);\n }\n\n await generateDeclarations({\n manifest: manifestPath,\n outDir,\n });\n}\n"],"mappings":";;;;;;;;;;;;AAkCA,eAAsB,qBACpB,SACe;CACf,MAAM,EACJ,UAAU,eACV,QACA,wBAAwB,MACxB,qBAAqB,MACrB,cAAc,QAAQ,IAAI,MACxB;CAGJ,MAAM,WACJ,OAAO,kBAAkB,WACrB,KAAK,MAAM,GAAG,aAAa,eAAe,OAAO,CAAC,IAClD;CAGN,MAAM,aAAa,KAAK,WAAW,MAAM,IACrC,SACA,KAAK,KAAK,aAAa,MAAM;CACjC,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAE5C,QAAQ,IAAI,8CAA8C,YAAY;CAEtE,IAAI,oBACF,MAAM,+BAA+B,UAAU,UAAU;CAG3D,IAAI,uBACF,MAAM,kCAAkC,UAAU,UAAU;CAG9D,QAAQ,IACN,8CAA8C,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,OAAO,cACrF;AACF;;;;AAKA,eAAe,+BACb,UACA,QACe;CACf,MAAM,aAAuB,CAAC;CAK9B,MAAM,sBAAsB,OAAO,QAAQ,SAAS,OAAO,CAAC,CAAC,MAC1D,CAAC,SAAS,OAAO,CAAC,UAAU,WAC3B,YACE,KAAK,iBAAiB,SACtB,MAAM,iBAAiB,QACzB,KAAK,YAAY,SAAS,QAAQ,CACtC;CACA,KAAK,MAAM,CAAC,aAAa,eAAe,qBAAqB;EAC3D,MAAM,SAAS,WAAW,UAAU,CAAC;EACrC,MAAM,gBAA0B,CAAC;EAGjC,cAAc,KAAK,gBAAgB;EACnC,cAAc,KAAK,wBAAwB;EAC3C,cAAc,KAAK,wBAAwB;EAG3C,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,MAAM,GAAG;GAC1D,MAAM,OAAO,yBAAyB,SAAS,IAAI;GACnD,MAAM,WAAW,CAAC,SAAS,WAAW,MAAM;GAC5C,cAAc,KAAK,KAAK,YAAY,SAAS,IAAI,KAAK,EAAE;EAC1D;EAEA,MAAM,eAAe,oBAAoB,WAAW,UAAU;EAChE,cAAc,KAAK,IAAI,EAAE;;EAEvB,WAAW,KAAK,YAAY;CAC9B;CAGA,MAAM,qBAAqB;;;;;;;EAO3B,WAAW,KAAK,MAAM,EAAE;;CAGxB,GAAG,cAAc,KAAK,KAAK,QAAQ,mBAAmB,GAAG,kBAAkB;AAC7E;;;;AAKA,eAAe,kCACb,UACA,QACe;CAEf,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8E5B,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAhCC,uBAAuB,QAAQ,CAAC,CACxD,KAAK,EAAE,WAAW,mBAAmB,aAAa,oBAAoB;EAErE,MAAM,WAAW,wBACf,mBACA,aAHwB,cAAc,KAAK,EAAE,WAAW,IAIxD,CACF;EACA,MAAM,mBAAmB,cACtB,KACE,WACC,SAAS,OAAO,KAAK,GAAG,sCACtB,QACA,wBACF,EAAE,iBACN,CAAC,CACA,KAAK,IAAI;EAEZ,IAAI,kBAAkB;GACpB,MAAM,eAAe,WAAW,GAAG,SAAS,OAAO;GACnD,OAAO,OAAO,KAAK,UAAU,SAAS,EAAE,IAAI,aAAa,KAAK,iBAAiB;EACjF;EAEA,OAAO,OAAO,KAAK,UAAU,SAAS,EAAE,IAAI,YAAY,wBAAwB;CAClF,CAAC,CAAC,CACD,KAAK,IA4CR,EAAmB;;;;;;CAQnB,MAAM,oBAAoB;;;;;;;;;;;;;;CAgB1B,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;CAuCvB,MAAM,mBAAmB;;;;EAdH,OAAO,QAAQ,SAAS,OAAO,CAAC,CACnD,MACE,CAAC,SAAS,OAAO,CAAC,UAAU,WAC3B,YACE,KAAK,iBAAiB,SACtB,MAAM,iBAAiB,QACzB,KAAK,YAAY,SAAS,QAAQ,CACtC,CAAC,CACA,KACE,GAAG,SACF,iBAAiB,IAAI,UAAU,kCAAkC,IAAI,UAAU,MACnF,CAAC,CACA,KAAK,IAMR,EAAc;;;;;;;;;;;;;CAyBd,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAPM,2BAA2B,QAAQ,CAAC,CAC9D,KACE,EAAE,YAAY,UACb,OAAO,KAAK,UAAU,UAAU,EAAE,yDAAyD,IAAI,UAAU,OAC7G,CAAC,CACA,KAAK,IAuGR,EAAqB;;;;;;;;;;;;;;;;;;;CAqBrB,GAAG,cACD,KAAK,KAAK,QAAQ,oBAAoB,GACtC,mBACF;CACA,GAAG,cAAc,KAAK,KAAK,QAAQ,kBAAkB,GAAG,iBAAiB;CACzE,GAAG,cAAc,KAAK,KAAK,QAAQ,kBAAkB,GAAG,iBAAiB;CACzE,GAAG,cAAc,KAAK,KAAK,QAAQ,eAAe,GAAG,cAAc;CACnE,GAAG,cAAc,KAAK,KAAK,QAAQ,iBAAiB,GAAG,gBAAgB;CACvE,GAAG,cAAc,KAAK,KAAK,QAAQ,eAAe,GAAG,cAAc;AACrE;;;;AAKA,SAAS,yBAAyB,UAA0B;CAC1D,QAAQ,UAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK;EACL,KAAK,WACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,YACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;AAKA,eAAsB,4BACpB,MACe;CACf,MAAM,eAAe,KAAK;CAC1B,MAAM,SAAS,KAAK,MAAM;CAE1B,IAAI,CAAC,cAAc;EACjB,QAAQ,MAAM,2DAA2D;EACzE,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,CAAC,GAAG,WAAW,YAAY,GAAG;EAChC,QAAQ,MAAM,4BAA4B,cAAc;EACxD,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,qBAAqB;EACzB,UAAU;EACV;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-registration.d.ts","sourceRoot":"","sources":["../../src/registry/class-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAKV,qBAAqB,EAGtB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"class-registration.d.ts","sourceRoot":"","sources":["../../src/registry/class-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAKV,qBAAqB,EAGtB,MAAM,qBAAqB,CAAC;AAsC7B,OAAO,KAAK,EACV,eAAe,EAEf,iBAAiB,EAElB,MAAM,YAAY,CAAC;AA0dpB,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,UAAU,EACvB,MAAM,GAAE,iBAAsB,GAC7B,IAAI,CAqwBN;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAMlB,qBAAqB,EAAE,WAAW,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,GAC3E,IAAI,CAeN;AA6LD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EACzB,gBAAgB,CAAC,EAAE;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACA,IAAI,CA2CN;AA0ID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,qBAAqB,EAChC,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CA4QN"}
|
|
@@ -9,6 +9,7 @@ import { discoverManifestSync, discoverSTISiblingsSync, getPackageName, lookupIn
|
|
|
9
9
|
import { tableNameFromClass } from "../utils.js";
|
|
10
10
|
import { SmrtObject } from "../object.js";
|
|
11
11
|
import { decideCollisionPolicy } from "./collision-policy.js";
|
|
12
|
+
import { isFrameworkBaseClass } from "./framework-base-classes.js";
|
|
12
13
|
import { createFieldFromManifest, mergeManifestField } from "./manifest-field-merge.js";
|
|
13
14
|
import { compileValidators } from "./validator.js";
|
|
14
15
|
//#region src/registry/class-registration.ts
|
|
@@ -482,7 +483,8 @@ function register(ctor, config = {}) {
|
|
|
482
483
|
if (inheritanceChain.length > 1) {
|
|
483
484
|
const parentName = inheritanceChain[inheritanceChain.length - 2];
|
|
484
485
|
const parentEntry = findClass(parentName);
|
|
485
|
-
|
|
486
|
+
const parentIsFrameworkBase = parentEntry !== void 0 && isFrameworkBaseClass(parentEntry.name, parentEntry.packageName);
|
|
487
|
+
if (parentEntry && !parentIsFrameworkBase) {
|
|
486
488
|
const parentStrategy = parentEntry.config?.tableStrategy || "default";
|
|
487
489
|
const childStrategy = config.tableStrategy;
|
|
488
490
|
if (childStrategy !== void 0 && parentStrategy !== childStrategy) throw ConfigurationError.incompatibleStrategy(name, childStrategy, parentName, parentStrategy);
|