node-type-registry 0.12.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blueprint-types.generated.d.ts +38 -3
- package/codegen/generate-types.js +26 -4
- package/esm/blueprint-types.generated.d.ts +38 -3
- package/esm/codegen/generate-types.js +26 -4
- package/esm/relation/index.d.ts +1 -0
- package/esm/relation/index.js +1 -0
- package/esm/relation/relation-spatial.d.ts +2 -0
- package/esm/relation/relation-spatial.js +68 -0
- package/package.json +2 -2
- package/relation/index.d.ts +1 -0
- package/relation/index.js +3 -1
- package/relation/relation-spatial.d.ts +2 -0
- package/relation/relation-spatial.js +71 -0
|
@@ -401,6 +401,16 @@ export interface RelationManyToManyParams {
|
|
|
401
401
|
[key: string]: unknown;
|
|
402
402
|
};
|
|
403
403
|
}
|
|
404
|
+
/** Declares a spatial predicate between two existing geometry/geography columns. Inserts a metaschema_public.spatial_relation row; the sync_spatial_relation_tags trigger then projects a @spatialRelation smart tag onto the owner column so graphile-postgis' PostgisSpatialRelationsPlugin can expose it as a cross-table filter in GraphQL. Metadata-only: both source_field and target_field must already exist on their tables. Idempotent on (source_table_id, name). One direction per tag — author two RelationSpatial entries if symmetry is desired. */
|
|
405
|
+
export interface RelationSpatialParams {
|
|
406
|
+
source_table_id: string;
|
|
407
|
+
source_field_id: string;
|
|
408
|
+
target_table_id: string;
|
|
409
|
+
target_field_id: string;
|
|
410
|
+
name: string;
|
|
411
|
+
operator: "st_contains" | "st_within" | "st_intersects" | "st_covers" | "st_coveredby" | "st_overlaps" | "st_touches" | "st_dwithin";
|
|
412
|
+
param_name?: string;
|
|
413
|
+
}
|
|
404
414
|
/** Simple column selection from a single source table. Projects all or specific fields. */
|
|
405
415
|
export interface ViewTableProjectionParams {
|
|
406
416
|
source_table_id: string;
|
|
@@ -559,6 +569,21 @@ export interface BlueprintTableUniqueConstraint {
|
|
|
559
569
|
/** Optional schema name override. */
|
|
560
570
|
schema_name?: string;
|
|
561
571
|
}
|
|
572
|
+
/** Override object for the entity table created by a BlueprintMembershipType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely. */
|
|
573
|
+
export interface BlueprintEntityTableProvision {
|
|
574
|
+
/** Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true. */
|
|
575
|
+
use_rls?: boolean;
|
|
576
|
+
/** Node objects applied to the entity table for field creation (e.g., DataTimestamps, DataPeoplestamps). Forwarded to secure_table_provision as-is. */
|
|
577
|
+
nodes?: BlueprintNode[];
|
|
578
|
+
/** Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is. */
|
|
579
|
+
fields?: BlueprintField[];
|
|
580
|
+
/** Privilege grants for the entity table as [verb, columns] tuples (e.g. [["select","*"],["insert","*"]]). Forwarded to secure_table_provision as-is. */
|
|
581
|
+
grant_privileges?: unknown[];
|
|
582
|
+
/** Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"]. */
|
|
583
|
+
grant_roles?: string[];
|
|
584
|
+
/** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
|
|
585
|
+
policies?: BlueprintPolicy[];
|
|
586
|
+
}
|
|
562
587
|
/** A membership type entry for Phase 0 of construct_blueprint(). Provisions a full entity type with its own entity table, membership modules, and security policies via entity_type_provision. */
|
|
563
588
|
export interface BlueprintMembershipType {
|
|
564
589
|
/** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
|
|
@@ -571,7 +596,7 @@ export interface BlueprintMembershipType {
|
|
|
571
596
|
parent_entity?: string;
|
|
572
597
|
/** Custom table name for the entity table. Defaults to name-derived convention. */
|
|
573
598
|
table_name?: string;
|
|
574
|
-
/** Whether
|
|
599
|
+
/** Whether parent-entity members can see child entities via the default parent_member SELECT policy. Gates one of the five default policies. No-op when table_provision is supplied. Defaults to true. */
|
|
575
600
|
is_visible?: boolean;
|
|
576
601
|
/** Whether to provision a limits module for this entity type. Defaults to false. */
|
|
577
602
|
has_limits?: boolean;
|
|
@@ -579,8 +604,10 @@ export interface BlueprintMembershipType {
|
|
|
579
604
|
has_profiles?: boolean;
|
|
580
605
|
/** Whether to provision a levels module for this entity type. Defaults to false. */
|
|
581
606
|
has_levels?: boolean;
|
|
582
|
-
/**
|
|
607
|
+
/** Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false. */
|
|
583
608
|
skip_entity_policies?: boolean;
|
|
609
|
+
/** Override for the entity table. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, its policies[] replaces the five default entity-table policies; is_visible becomes a no-op. When NULL (default), the five default policies are applied (gated by is_visible). */
|
|
610
|
+
table_provision?: BlueprintEntityTableProvision;
|
|
584
611
|
}
|
|
585
612
|
/** String shorthand -- just the node type name. */
|
|
586
613
|
export type BlueprintNodeShorthand = "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "AuthzNotReadOnly" | "AuthzPeerOwnership" | "AuthzRelatedPeerOwnership" | "DataId" | "DataDirectOwner" | "DataEntityMembership" | "DataOwnershipInEntity" | "DataTimestamps" | "DataPeoplestamps" | "DataPublishable" | "DataSoftDelete" | "SearchVector" | "SearchFullText" | "SearchBm25" | "SearchUnified" | "SearchSpatial" | "SearchSpatialAggregate" | "DataJobTrigger" | "DataTags" | "DataStatusField" | "DataJsonb" | "SearchTrgm" | "DataSlug" | "DataInflection" | "DataOwnedFields" | "DataInheritFromParent" | "DataForceCurrentUser" | "DataImmutableFields" | "DataCompositeField" | "TableUserProfiles" | "TableOrganizationSettings" | "TableUserSettings";
|
|
@@ -748,7 +775,15 @@ export type BlueprintRelation = {
|
|
|
748
775
|
target_table: string;
|
|
749
776
|
source_schema_name?: string;
|
|
750
777
|
target_schema_name?: string;
|
|
751
|
-
} & Partial<RelationManyToManyParams
|
|
778
|
+
} & Partial<RelationManyToManyParams> | {
|
|
779
|
+
$type: "RelationSpatial";
|
|
780
|
+
source_table: string;
|
|
781
|
+
target_table: string;
|
|
782
|
+
source_schema_name?: string;
|
|
783
|
+
target_schema_name?: string;
|
|
784
|
+
/** Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag. */ source_field: string;
|
|
785
|
+
/** Name of the geometry/geography column on target_table that the predicate is evaluated against. */ target_field: string;
|
|
786
|
+
} & Partial<RelationSpatialParams>;
|
|
752
787
|
/** A table definition within a blueprint. */
|
|
753
788
|
export interface BlueprintTable {
|
|
754
789
|
/** The PostgreSQL table name to create. */
|
|
@@ -352,13 +352,23 @@ function buildNodeTypes(dataNodes) {
|
|
|
352
352
|
// ---------------------------------------------------------------------------
|
|
353
353
|
function buildRelationTypes(relationNodes) {
|
|
354
354
|
const relationMembers = relationNodes.map((nt) => {
|
|
355
|
-
const
|
|
355
|
+
const baseMembers = [
|
|
356
356
|
requiredProp('$type', strLit(nt.name)),
|
|
357
357
|
requiredProp('source_table', t.tsStringKeyword()),
|
|
358
358
|
requiredProp('target_table', t.tsStringKeyword()),
|
|
359
359
|
optionalProp('source_schema_name', t.tsStringKeyword()),
|
|
360
360
|
optionalProp('target_schema_name', t.tsStringKeyword()),
|
|
361
|
-
]
|
|
361
|
+
];
|
|
362
|
+
// RelationSpatial is the only relation type that references *existing*
|
|
363
|
+
// columns rather than creating FK/junction fields. Its blueprint JSON
|
|
364
|
+
// therefore needs source_field / target_field (column *names* resolved
|
|
365
|
+
// server-side by resolve_blueprint_field), which have no ID-space
|
|
366
|
+
// equivalent in parameter_schema. Surface them here so blueprint authors
|
|
367
|
+
// get autocomplete without a cast.
|
|
368
|
+
if (nt.name === 'RelationSpatial') {
|
|
369
|
+
baseMembers.push(addJSDoc(requiredProp('source_field', t.tsStringKeyword()), 'Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag.'), addJSDoc(requiredProp('target_field', t.tsStringKeyword()), 'Name of the geometry/geography column on target_table that the predicate is evaluated against.'));
|
|
370
|
+
}
|
|
371
|
+
const baseType = t.tsTypeLiteral(baseMembers);
|
|
362
372
|
return t.tsIntersectionType([
|
|
363
373
|
baseType,
|
|
364
374
|
partialOf(t.tsTypeReference(t.identifier(`${nt.name}Params`))),
|
|
@@ -384,6 +394,16 @@ function buildBlueprintTableUniqueConstraint() {
|
|
|
384
394
|
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.'),
|
|
385
395
|
]), 'A unique constraint nested inside a table definition (table_name not required).');
|
|
386
396
|
}
|
|
397
|
+
function buildBlueprintEntityTableProvision() {
|
|
398
|
+
return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
|
|
399
|
+
addJSDoc(optionalProp('use_rls', t.tsBooleanKeyword()), 'Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true.'),
|
|
400
|
+
addJSDoc(optionalProp('nodes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintNode')))), 'Node objects applied to the entity table for field creation (e.g., DataTimestamps, DataPeoplestamps). Forwarded to secure_table_provision as-is.'),
|
|
401
|
+
addJSDoc(optionalProp('fields', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintField')))), 'Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is.'),
|
|
402
|
+
addJSDoc(optionalProp('grant_privileges', t.tsArrayType(t.tsUnknownKeyword())), 'Privilege grants for the entity table as [verb, columns] tuples (e.g. [["select","*"],["insert","*"]]). Forwarded to secure_table_provision as-is.'),
|
|
403
|
+
addJSDoc(optionalProp('grant_roles', t.tsArrayType(t.tsStringKeyword())), 'Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"].'),
|
|
404
|
+
addJSDoc(optionalProp('policies', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintPolicy')))), 'RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op).'),
|
|
405
|
+
]), 'Override object for the entity table created by a BlueprintMembershipType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely.');
|
|
406
|
+
}
|
|
387
407
|
function buildBlueprintMembershipType() {
|
|
388
408
|
return addJSDoc(exportInterface('BlueprintMembershipType', [
|
|
389
409
|
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
|
|
@@ -391,11 +411,12 @@ function buildBlueprintMembershipType() {
|
|
|
391
411
|
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
|
|
392
412
|
addJSDoc(optionalProp('parent_entity', t.tsStringKeyword()), 'Parent entity type name. Defaults to "org".'),
|
|
393
413
|
addJSDoc(optionalProp('table_name', t.tsStringKeyword()), 'Custom table name for the entity table. Defaults to name-derived convention.'),
|
|
394
|
-
addJSDoc(optionalProp('is_visible', t.tsBooleanKeyword()), 'Whether
|
|
414
|
+
addJSDoc(optionalProp('is_visible', t.tsBooleanKeyword()), 'Whether parent-entity members can see child entities via the default parent_member SELECT policy. Gates one of the five default policies. No-op when table_provision is supplied. Defaults to true.'),
|
|
395
415
|
addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
|
|
396
416
|
addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
|
|
397
417
|
addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
|
|
398
|
-
addJSDoc(optionalProp('skip_entity_policies', t.tsBooleanKeyword()), '
|
|
418
|
+
addJSDoc(optionalProp('skip_entity_policies', t.tsBooleanKeyword()), 'Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false.'),
|
|
419
|
+
addJSDoc(optionalProp('table_provision', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))), 'Override for the entity table. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, its policies[] replaces the five default entity-table policies; is_visible becomes a no-op. When NULL (default), the five default policies are applied (gated by is_visible).'),
|
|
399
420
|
]), 'A membership type entry for Phase 0 of construct_blueprint(). Provisions a full entity type with its own entity table, membership modules, and security policies via entity_type_provision.');
|
|
400
421
|
}
|
|
401
422
|
function buildBlueprintTable() {
|
|
@@ -474,6 +495,7 @@ function buildProgram(meta) {
|
|
|
474
495
|
statements.push(buildBlueprintTableIndex());
|
|
475
496
|
statements.push(buildBlueprintUniqueConstraint());
|
|
476
497
|
statements.push(buildBlueprintTableUniqueConstraint());
|
|
498
|
+
statements.push(buildBlueprintEntityTableProvision());
|
|
477
499
|
statements.push(buildBlueprintMembershipType());
|
|
478
500
|
// -- Node types discriminated union --
|
|
479
501
|
statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
|
|
@@ -401,6 +401,16 @@ export interface RelationManyToManyParams {
|
|
|
401
401
|
[key: string]: unknown;
|
|
402
402
|
};
|
|
403
403
|
}
|
|
404
|
+
/** Declares a spatial predicate between two existing geometry/geography columns. Inserts a metaschema_public.spatial_relation row; the sync_spatial_relation_tags trigger then projects a @spatialRelation smart tag onto the owner column so graphile-postgis' PostgisSpatialRelationsPlugin can expose it as a cross-table filter in GraphQL. Metadata-only: both source_field and target_field must already exist on their tables. Idempotent on (source_table_id, name). One direction per tag — author two RelationSpatial entries if symmetry is desired. */
|
|
405
|
+
export interface RelationSpatialParams {
|
|
406
|
+
source_table_id: string;
|
|
407
|
+
source_field_id: string;
|
|
408
|
+
target_table_id: string;
|
|
409
|
+
target_field_id: string;
|
|
410
|
+
name: string;
|
|
411
|
+
operator: "st_contains" | "st_within" | "st_intersects" | "st_covers" | "st_coveredby" | "st_overlaps" | "st_touches" | "st_dwithin";
|
|
412
|
+
param_name?: string;
|
|
413
|
+
}
|
|
404
414
|
/** Simple column selection from a single source table. Projects all or specific fields. */
|
|
405
415
|
export interface ViewTableProjectionParams {
|
|
406
416
|
source_table_id: string;
|
|
@@ -559,6 +569,21 @@ export interface BlueprintTableUniqueConstraint {
|
|
|
559
569
|
/** Optional schema name override. */
|
|
560
570
|
schema_name?: string;
|
|
561
571
|
}
|
|
572
|
+
/** Override object for the entity table created by a BlueprintMembershipType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely. */
|
|
573
|
+
export interface BlueprintEntityTableProvision {
|
|
574
|
+
/** Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true. */
|
|
575
|
+
use_rls?: boolean;
|
|
576
|
+
/** Node objects applied to the entity table for field creation (e.g., DataTimestamps, DataPeoplestamps). Forwarded to secure_table_provision as-is. */
|
|
577
|
+
nodes?: BlueprintNode[];
|
|
578
|
+
/** Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is. */
|
|
579
|
+
fields?: BlueprintField[];
|
|
580
|
+
/** Privilege grants for the entity table as [verb, columns] tuples (e.g. [["select","*"],["insert","*"]]). Forwarded to secure_table_provision as-is. */
|
|
581
|
+
grant_privileges?: unknown[];
|
|
582
|
+
/** Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"]. */
|
|
583
|
+
grant_roles?: string[];
|
|
584
|
+
/** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
|
|
585
|
+
policies?: BlueprintPolicy[];
|
|
586
|
+
}
|
|
562
587
|
/** A membership type entry for Phase 0 of construct_blueprint(). Provisions a full entity type with its own entity table, membership modules, and security policies via entity_type_provision. */
|
|
563
588
|
export interface BlueprintMembershipType {
|
|
564
589
|
/** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
|
|
@@ -571,7 +596,7 @@ export interface BlueprintMembershipType {
|
|
|
571
596
|
parent_entity?: string;
|
|
572
597
|
/** Custom table name for the entity table. Defaults to name-derived convention. */
|
|
573
598
|
table_name?: string;
|
|
574
|
-
/** Whether
|
|
599
|
+
/** Whether parent-entity members can see child entities via the default parent_member SELECT policy. Gates one of the five default policies. No-op when table_provision is supplied. Defaults to true. */
|
|
575
600
|
is_visible?: boolean;
|
|
576
601
|
/** Whether to provision a limits module for this entity type. Defaults to false. */
|
|
577
602
|
has_limits?: boolean;
|
|
@@ -579,8 +604,10 @@ export interface BlueprintMembershipType {
|
|
|
579
604
|
has_profiles?: boolean;
|
|
580
605
|
/** Whether to provision a levels module for this entity type. Defaults to false. */
|
|
581
606
|
has_levels?: boolean;
|
|
582
|
-
/**
|
|
607
|
+
/** Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false. */
|
|
583
608
|
skip_entity_policies?: boolean;
|
|
609
|
+
/** Override for the entity table. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, its policies[] replaces the five default entity-table policies; is_visible becomes a no-op. When NULL (default), the five default policies are applied (gated by is_visible). */
|
|
610
|
+
table_provision?: BlueprintEntityTableProvision;
|
|
584
611
|
}
|
|
585
612
|
/** String shorthand -- just the node type name. */
|
|
586
613
|
export type BlueprintNodeShorthand = "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "AuthzNotReadOnly" | "AuthzPeerOwnership" | "AuthzRelatedPeerOwnership" | "DataId" | "DataDirectOwner" | "DataEntityMembership" | "DataOwnershipInEntity" | "DataTimestamps" | "DataPeoplestamps" | "DataPublishable" | "DataSoftDelete" | "SearchVector" | "SearchFullText" | "SearchBm25" | "SearchUnified" | "SearchSpatial" | "SearchSpatialAggregate" | "DataJobTrigger" | "DataTags" | "DataStatusField" | "DataJsonb" | "SearchTrgm" | "DataSlug" | "DataInflection" | "DataOwnedFields" | "DataInheritFromParent" | "DataForceCurrentUser" | "DataImmutableFields" | "DataCompositeField" | "TableUserProfiles" | "TableOrganizationSettings" | "TableUserSettings";
|
|
@@ -748,7 +775,15 @@ export type BlueprintRelation = {
|
|
|
748
775
|
target_table: string;
|
|
749
776
|
source_schema_name?: string;
|
|
750
777
|
target_schema_name?: string;
|
|
751
|
-
} & Partial<RelationManyToManyParams
|
|
778
|
+
} & Partial<RelationManyToManyParams> | {
|
|
779
|
+
$type: "RelationSpatial";
|
|
780
|
+
source_table: string;
|
|
781
|
+
target_table: string;
|
|
782
|
+
source_schema_name?: string;
|
|
783
|
+
target_schema_name?: string;
|
|
784
|
+
/** Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag. */ source_field: string;
|
|
785
|
+
/** Name of the geometry/geography column on target_table that the predicate is evaluated against. */ target_field: string;
|
|
786
|
+
} & Partial<RelationSpatialParams>;
|
|
752
787
|
/** A table definition within a blueprint. */
|
|
753
788
|
export interface BlueprintTable {
|
|
754
789
|
/** The PostgreSQL table name to create. */
|
|
@@ -317,13 +317,23 @@ function buildNodeTypes(dataNodes) {
|
|
|
317
317
|
// ---------------------------------------------------------------------------
|
|
318
318
|
function buildRelationTypes(relationNodes) {
|
|
319
319
|
const relationMembers = relationNodes.map((nt) => {
|
|
320
|
-
const
|
|
320
|
+
const baseMembers = [
|
|
321
321
|
requiredProp('$type', strLit(nt.name)),
|
|
322
322
|
requiredProp('source_table', t.tsStringKeyword()),
|
|
323
323
|
requiredProp('target_table', t.tsStringKeyword()),
|
|
324
324
|
optionalProp('source_schema_name', t.tsStringKeyword()),
|
|
325
325
|
optionalProp('target_schema_name', t.tsStringKeyword()),
|
|
326
|
-
]
|
|
326
|
+
];
|
|
327
|
+
// RelationSpatial is the only relation type that references *existing*
|
|
328
|
+
// columns rather than creating FK/junction fields. Its blueprint JSON
|
|
329
|
+
// therefore needs source_field / target_field (column *names* resolved
|
|
330
|
+
// server-side by resolve_blueprint_field), which have no ID-space
|
|
331
|
+
// equivalent in parameter_schema. Surface them here so blueprint authors
|
|
332
|
+
// get autocomplete without a cast.
|
|
333
|
+
if (nt.name === 'RelationSpatial') {
|
|
334
|
+
baseMembers.push(addJSDoc(requiredProp('source_field', t.tsStringKeyword()), 'Name of the geometry/geography column on source_table that carries the @spatialRelation smart tag.'), addJSDoc(requiredProp('target_field', t.tsStringKeyword()), 'Name of the geometry/geography column on target_table that the predicate is evaluated against.'));
|
|
335
|
+
}
|
|
336
|
+
const baseType = t.tsTypeLiteral(baseMembers);
|
|
327
337
|
return t.tsIntersectionType([
|
|
328
338
|
baseType,
|
|
329
339
|
partialOf(t.tsTypeReference(t.identifier(`${nt.name}Params`))),
|
|
@@ -349,6 +359,16 @@ function buildBlueprintTableUniqueConstraint() {
|
|
|
349
359
|
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.'),
|
|
350
360
|
]), 'A unique constraint nested inside a table definition (table_name not required).');
|
|
351
361
|
}
|
|
362
|
+
function buildBlueprintEntityTableProvision() {
|
|
363
|
+
return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
|
|
364
|
+
addJSDoc(optionalProp('use_rls', t.tsBooleanKeyword()), 'Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true.'),
|
|
365
|
+
addJSDoc(optionalProp('nodes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintNode')))), 'Node objects applied to the entity table for field creation (e.g., DataTimestamps, DataPeoplestamps). Forwarded to secure_table_provision as-is.'),
|
|
366
|
+
addJSDoc(optionalProp('fields', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintField')))), 'Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is.'),
|
|
367
|
+
addJSDoc(optionalProp('grant_privileges', t.tsArrayType(t.tsUnknownKeyword())), 'Privilege grants for the entity table as [verb, columns] tuples (e.g. [["select","*"],["insert","*"]]). Forwarded to secure_table_provision as-is.'),
|
|
368
|
+
addJSDoc(optionalProp('grant_roles', t.tsArrayType(t.tsStringKeyword())), 'Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"].'),
|
|
369
|
+
addJSDoc(optionalProp('policies', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintPolicy')))), 'RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op).'),
|
|
370
|
+
]), 'Override object for the entity table created by a BlueprintMembershipType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely.');
|
|
371
|
+
}
|
|
352
372
|
function buildBlueprintMembershipType() {
|
|
353
373
|
return addJSDoc(exportInterface('BlueprintMembershipType', [
|
|
354
374
|
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
|
|
@@ -356,11 +376,12 @@ function buildBlueprintMembershipType() {
|
|
|
356
376
|
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
|
|
357
377
|
addJSDoc(optionalProp('parent_entity', t.tsStringKeyword()), 'Parent entity type name. Defaults to "org".'),
|
|
358
378
|
addJSDoc(optionalProp('table_name', t.tsStringKeyword()), 'Custom table name for the entity table. Defaults to name-derived convention.'),
|
|
359
|
-
addJSDoc(optionalProp('is_visible', t.tsBooleanKeyword()), 'Whether
|
|
379
|
+
addJSDoc(optionalProp('is_visible', t.tsBooleanKeyword()), 'Whether parent-entity members can see child entities via the default parent_member SELECT policy. Gates one of the five default policies. No-op when table_provision is supplied. Defaults to true.'),
|
|
360
380
|
addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
|
|
361
381
|
addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
|
|
362
382
|
addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
|
|
363
|
-
addJSDoc(optionalProp('skip_entity_policies', t.tsBooleanKeyword()), '
|
|
383
|
+
addJSDoc(optionalProp('skip_entity_policies', t.tsBooleanKeyword()), 'Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false.'),
|
|
384
|
+
addJSDoc(optionalProp('table_provision', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))), 'Override for the entity table. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, its policies[] replaces the five default entity-table policies; is_visible becomes a no-op. When NULL (default), the five default policies are applied (gated by is_visible).'),
|
|
364
385
|
]), 'A membership type entry for Phase 0 of construct_blueprint(). Provisions a full entity type with its own entity table, membership modules, and security policies via entity_type_provision.');
|
|
365
386
|
}
|
|
366
387
|
function buildBlueprintTable() {
|
|
@@ -439,6 +460,7 @@ function buildProgram(meta) {
|
|
|
439
460
|
statements.push(buildBlueprintTableIndex());
|
|
440
461
|
statements.push(buildBlueprintUniqueConstraint());
|
|
441
462
|
statements.push(buildBlueprintTableUniqueConstraint());
|
|
463
|
+
statements.push(buildBlueprintEntityTableProvision());
|
|
442
464
|
statements.push(buildBlueprintMembershipType());
|
|
443
465
|
// -- Node types discriminated union --
|
|
444
466
|
statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
|
package/esm/relation/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { RelationBelongsTo } from './relation-belongs-to';
|
|
|
2
2
|
export { RelationHasOne } from './relation-has-one';
|
|
3
3
|
export { RelationHasMany } from './relation-has-many';
|
|
4
4
|
export { RelationManyToMany } from './relation-many-to-many';
|
|
5
|
+
export { RelationSpatial } from './relation-spatial';
|
package/esm/relation/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { RelationBelongsTo } from './relation-belongs-to';
|
|
|
2
2
|
export { RelationHasOne } from './relation-has-one';
|
|
3
3
|
export { RelationHasMany } from './relation-has-many';
|
|
4
4
|
export { RelationManyToMany } from './relation-many-to-many';
|
|
5
|
+
export { RelationSpatial } from './relation-spatial';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const RelationSpatial = {
|
|
2
|
+
"name": "RelationSpatial",
|
|
3
|
+
"slug": "relation_spatial",
|
|
4
|
+
"category": "relation",
|
|
5
|
+
"display_name": "Spatial Relation",
|
|
6
|
+
"description": "Declares a spatial predicate between two existing geometry/geography columns. Inserts a metaschema_public.spatial_relation row; the sync_spatial_relation_tags trigger then projects a @spatialRelation smart tag onto the owner column so graphile-postgis' PostgisSpatialRelationsPlugin can expose it as a cross-table filter in GraphQL. Metadata-only: both source_field and target_field must already exist on their tables. Idempotent on (source_table_id, name). One direction per tag — author two RelationSpatial entries if symmetry is desired.",
|
|
7
|
+
"parameter_schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"source_table_id": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"format": "uuid",
|
|
13
|
+
"description": "Table that owns the relation (the @spatialRelation tag is emitted on the owner column of this table)"
|
|
14
|
+
},
|
|
15
|
+
"source_field_id": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"format": "uuid",
|
|
18
|
+
"description": "Geometry/geography column on source_table that carries the @spatialRelation smart tag"
|
|
19
|
+
},
|
|
20
|
+
"target_table_id": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"format": "uuid",
|
|
23
|
+
"description": "Table being referenced by the spatial predicate"
|
|
24
|
+
},
|
|
25
|
+
"target_field_id": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"format": "uuid",
|
|
28
|
+
"description": "Geometry/geography column on target_table that the predicate is evaluated against"
|
|
29
|
+
},
|
|
30
|
+
"name": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Relation name (stable, snake_case). Becomes the generated filter field name in GraphQL (e.g. nearby_clinic). Unique per (source_table_id, name) — idempotency key."
|
|
33
|
+
},
|
|
34
|
+
"operator": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": [
|
|
37
|
+
"st_contains",
|
|
38
|
+
"st_within",
|
|
39
|
+
"st_intersects",
|
|
40
|
+
"st_covers",
|
|
41
|
+
"st_coveredby",
|
|
42
|
+
"st_overlaps",
|
|
43
|
+
"st_touches",
|
|
44
|
+
"st_dwithin"
|
|
45
|
+
],
|
|
46
|
+
"description": "PostGIS spatial predicate. One of the 8 whitelisted operators. st_dwithin requires param_name."
|
|
47
|
+
},
|
|
48
|
+
"param_name": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "Parameter name for parametric operators (currently only st_dwithin, which needs a distance argument). Must be NULL for all other operators. Enforced by table CHECK."
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"required": [
|
|
54
|
+
"source_table_id",
|
|
55
|
+
"source_field_id",
|
|
56
|
+
"target_table_id",
|
|
57
|
+
"target_field_id",
|
|
58
|
+
"name",
|
|
59
|
+
"operator"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"tags": [
|
|
63
|
+
"relation",
|
|
64
|
+
"spatial",
|
|
65
|
+
"postgis",
|
|
66
|
+
"schema"
|
|
67
|
+
]
|
|
68
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-type-registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Node type definitions for the Constructive blueprint system. Single source of truth for all Authz*, Data*, Relation*, and View* node types.",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"main": "index.js",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"registry",
|
|
48
48
|
"graphile"
|
|
49
49
|
],
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "5094bbe92916be47234fe80c583c6957f51226e1"
|
|
51
51
|
}
|
package/relation/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { RelationBelongsTo } from './relation-belongs-to';
|
|
|
2
2
|
export { RelationHasOne } from './relation-has-one';
|
|
3
3
|
export { RelationHasMany } from './relation-has-many';
|
|
4
4
|
export { RelationManyToMany } from './relation-many-to-many';
|
|
5
|
+
export { RelationSpatial } from './relation-spatial';
|
package/relation/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RelationManyToMany = exports.RelationHasMany = exports.RelationHasOne = exports.RelationBelongsTo = void 0;
|
|
3
|
+
exports.RelationSpatial = exports.RelationManyToMany = exports.RelationHasMany = exports.RelationHasOne = exports.RelationBelongsTo = void 0;
|
|
4
4
|
var relation_belongs_to_1 = require("./relation-belongs-to");
|
|
5
5
|
Object.defineProperty(exports, "RelationBelongsTo", { enumerable: true, get: function () { return relation_belongs_to_1.RelationBelongsTo; } });
|
|
6
6
|
var relation_has_one_1 = require("./relation-has-one");
|
|
@@ -9,3 +9,5 @@ var relation_has_many_1 = require("./relation-has-many");
|
|
|
9
9
|
Object.defineProperty(exports, "RelationHasMany", { enumerable: true, get: function () { return relation_has_many_1.RelationHasMany; } });
|
|
10
10
|
var relation_many_to_many_1 = require("./relation-many-to-many");
|
|
11
11
|
Object.defineProperty(exports, "RelationManyToMany", { enumerable: true, get: function () { return relation_many_to_many_1.RelationManyToMany; } });
|
|
12
|
+
var relation_spatial_1 = require("./relation-spatial");
|
|
13
|
+
Object.defineProperty(exports, "RelationSpatial", { enumerable: true, get: function () { return relation_spatial_1.RelationSpatial; } });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RelationSpatial = void 0;
|
|
4
|
+
exports.RelationSpatial = {
|
|
5
|
+
"name": "RelationSpatial",
|
|
6
|
+
"slug": "relation_spatial",
|
|
7
|
+
"category": "relation",
|
|
8
|
+
"display_name": "Spatial Relation",
|
|
9
|
+
"description": "Declares a spatial predicate between two existing geometry/geography columns. Inserts a metaschema_public.spatial_relation row; the sync_spatial_relation_tags trigger then projects a @spatialRelation smart tag onto the owner column so graphile-postgis' PostgisSpatialRelationsPlugin can expose it as a cross-table filter in GraphQL. Metadata-only: both source_field and target_field must already exist on their tables. Idempotent on (source_table_id, name). One direction per tag — author two RelationSpatial entries if symmetry is desired.",
|
|
10
|
+
"parameter_schema": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"source_table_id": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"format": "uuid",
|
|
16
|
+
"description": "Table that owns the relation (the @spatialRelation tag is emitted on the owner column of this table)"
|
|
17
|
+
},
|
|
18
|
+
"source_field_id": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"format": "uuid",
|
|
21
|
+
"description": "Geometry/geography column on source_table that carries the @spatialRelation smart tag"
|
|
22
|
+
},
|
|
23
|
+
"target_table_id": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"format": "uuid",
|
|
26
|
+
"description": "Table being referenced by the spatial predicate"
|
|
27
|
+
},
|
|
28
|
+
"target_field_id": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"format": "uuid",
|
|
31
|
+
"description": "Geometry/geography column on target_table that the predicate is evaluated against"
|
|
32
|
+
},
|
|
33
|
+
"name": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Relation name (stable, snake_case). Becomes the generated filter field name in GraphQL (e.g. nearby_clinic). Unique per (source_table_id, name) — idempotency key."
|
|
36
|
+
},
|
|
37
|
+
"operator": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"enum": [
|
|
40
|
+
"st_contains",
|
|
41
|
+
"st_within",
|
|
42
|
+
"st_intersects",
|
|
43
|
+
"st_covers",
|
|
44
|
+
"st_coveredby",
|
|
45
|
+
"st_overlaps",
|
|
46
|
+
"st_touches",
|
|
47
|
+
"st_dwithin"
|
|
48
|
+
],
|
|
49
|
+
"description": "PostGIS spatial predicate. One of the 8 whitelisted operators. st_dwithin requires param_name."
|
|
50
|
+
},
|
|
51
|
+
"param_name": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Parameter name for parametric operators (currently only st_dwithin, which needs a distance argument). Must be NULL for all other operators. Enforced by table CHECK."
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"required": [
|
|
57
|
+
"source_table_id",
|
|
58
|
+
"source_field_id",
|
|
59
|
+
"target_table_id",
|
|
60
|
+
"target_field_id",
|
|
61
|
+
"name",
|
|
62
|
+
"operator"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"tags": [
|
|
66
|
+
"relation",
|
|
67
|
+
"spatial",
|
|
68
|
+
"postgis",
|
|
69
|
+
"schema"
|
|
70
|
+
]
|
|
71
|
+
};
|