From 066ff6d09085c0dd562de8e3e8adf60d18442404 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Thu, 26 Mar 2026 05:48:16 +0000 Subject: [PATCH] feat(node-type-registry): add DataInheritFromParent, DataForceCurrentUser, DataImmutableFields Syncs the TS package with the 3 new Data* node types added to constructive-db seed.sql: DataInheritFromParent (copies fields from parent via FK), DataForceCurrentUser (forces current_user_id on insert), DataImmutableFields (prevents field changes after insert). --- .../src/data/data-force-current-user.ts | 24 +++++++++++ .../src/data/data-immutable-fields.ts | 29 +++++++++++++ .../src/data/data-inherit-from-parent.ts | 42 +++++++++++++++++++ graphile/node-type-registry/src/data/index.ts | 3 ++ 4 files changed, 98 insertions(+) create mode 100644 graphile/node-type-registry/src/data/data-force-current-user.ts create mode 100644 graphile/node-type-registry/src/data/data-immutable-fields.ts create mode 100644 graphile/node-type-registry/src/data/data-inherit-from-parent.ts diff --git a/graphile/node-type-registry/src/data/data-force-current-user.ts b/graphile/node-type-registry/src/data/data-force-current-user.ts new file mode 100644 index 0000000000..68f3e3aaea --- /dev/null +++ b/graphile/node-type-registry/src/data/data-force-current-user.ts @@ -0,0 +1,24 @@ +import type { NodeTypeDefinition } from '../types'; + +export const DataForceCurrentUser: NodeTypeDefinition = { + "name": "DataForceCurrentUser", + "slug": "data_force_current_user", + "category": "data", + "display_name": "Force Current User", + "description": "BEFORE INSERT trigger that forces a field to the value of jwt_public.current_user_id(). Prevents clients from spoofing the actor/uploader identity. The field value is always overwritten regardless of what the client provides.", + "parameter_schema": { + "type": "object", + "properties": { + "field_name": { + "type": "string", + "description": "Name of the field to force to current_user_id()", + "default": "actor_id" + } + } + }, + "tags": [ + "trigger", + "security", + "schema" + ] +}; diff --git a/graphile/node-type-registry/src/data/data-immutable-fields.ts b/graphile/node-type-registry/src/data/data-immutable-fields.ts new file mode 100644 index 0000000000..497f867190 --- /dev/null +++ b/graphile/node-type-registry/src/data/data-immutable-fields.ts @@ -0,0 +1,29 @@ +import type { NodeTypeDefinition } from '../types'; + +export const DataImmutableFields: NodeTypeDefinition = { + "name": "DataImmutableFields", + "slug": "data_immutable_fields", + "category": "data", + "display_name": "Immutable Fields", + "description": "BEFORE UPDATE trigger that prevents changes to a list of specified fields after INSERT. Raises an exception if any of the listed fields have changed. Unlike FieldImmutable (single-field), this handles multiple fields in a single trigger for efficiency.", + "parameter_schema": { + "type": "object", + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Field names that cannot be modified after INSERT (e.g. [\"key\", \"bucket_id\", \"owner_id\"])" + } + }, + "required": [ + "fields" + ] + }, + "tags": [ + "trigger", + "constraint", + "schema" + ] +}; diff --git a/graphile/node-type-registry/src/data/data-inherit-from-parent.ts b/graphile/node-type-registry/src/data/data-inherit-from-parent.ts new file mode 100644 index 0000000000..3b850eafd9 --- /dev/null +++ b/graphile/node-type-registry/src/data/data-inherit-from-parent.ts @@ -0,0 +1,42 @@ +import type { NodeTypeDefinition } from '../types'; + +export const DataInheritFromParent: NodeTypeDefinition = { + "name": "DataInheritFromParent", + "slug": "data_inherit_from_parent", + "category": "data", + "display_name": "Inherit From Parent", + "description": "BEFORE INSERT trigger that copies specified fields from a parent table via a foreign key. The parent row is looked up through RLS (SECURITY INVOKER), so the insert fails if the caller cannot see the parent. Used by the storage module to inherit owner_id and is_public from buckets to files.", + "parameter_schema": { + "type": "object", + "properties": { + "parent_fk_field": { + "type": "string", + "description": "Name of the FK field on this table that references the parent (e.g. bucket_id)" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Field names to copy from the parent row (e.g. [\"owner_id\", \"is_public\"])" + }, + "parent_table": { + "type": "string", + "description": "Parent table name (optional fallback if FK not yet registered in metaschema)" + }, + "parent_schema": { + "type": "string", + "description": "Parent table schema (optional, defaults to same schema as child table)" + } + }, + "required": [ + "parent_fk_field", + "fields" + ] + }, + "tags": [ + "trigger", + "inheritance", + "schema" + ] +}; diff --git a/graphile/node-type-registry/src/data/index.ts b/graphile/node-type-registry/src/data/index.ts index 509da138eb..62fbe91c3e 100644 --- a/graphile/node-type-registry/src/data/index.ts +++ b/graphile/node-type-registry/src/data/index.ts @@ -20,6 +20,9 @@ export { DataTrgm } from './data-trgm'; export { DataSlug } from './data-slug'; export { DataInflection } from './data-inflection'; export { DataOwnedFields } from './data-owned-fields'; +export { DataInheritFromParent } from './data-inherit-from-parent'; +export { DataForceCurrentUser } from './data-force-current-user'; +export { DataImmutableFields } from './data-immutable-fields'; export { TableUserProfiles } from './table-user-profiles'; export { TableOrganizationSettings } from './table-organization-settings'; export { TableUserSettings } from './table-user-settings';