node-type-registry 0.11.0 → 0.14.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/README.md +0 -8
- package/authz/authz-not-read-only.d.ts +2 -0
- package/authz/authz-not-read-only.js +34 -0
- package/authz/index.d.ts +1 -0
- package/authz/index.js +3 -1
- package/blueprint-types.generated.d.ts +52 -6
- package/codegen/generate-types.js +14 -2
- package/esm/authz/authz-not-read-only.d.ts +2 -0
- package/esm/authz/authz-not-read-only.js +31 -0
- package/esm/authz/index.d.ts +1 -0
- package/esm/authz/index.js +1 -0
- package/esm/blueprint-types.generated.d.ts +52 -6
- package/esm/codegen/generate-types.js +14 -2
- 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 -3
- 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
- package/codegen/generate-seed.d.ts +0 -32
- package/codegen/generate-seed.js +0 -252
- package/esm/codegen/generate-seed.d.ts +0 -32
- package/esm/codegen/generate-seed.js +0 -250
package/README.md
CHANGED
|
@@ -83,14 +83,6 @@ cd graphql/node-type-registry && pnpm generate:types
|
|
|
83
83
|
|
|
84
84
|
This produces `src/blueprint-types.generated.ts` from the TS node type source of truth.
|
|
85
85
|
|
|
86
|
-
## Codegen: SQL seed
|
|
87
|
-
|
|
88
|
-
Generate SQL seed scripts for `node_type_registry` table:
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
cd graphql/node-type-registry && pnpm generate:seed --pgpm ../../constructive-db/packages/metaschema
|
|
92
|
-
```
|
|
93
|
-
|
|
94
86
|
---
|
|
95
87
|
|
|
96
88
|
## Education and Tutorials
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthzNotReadOnly = void 0;
|
|
4
|
+
exports.AuthzNotReadOnly = {
|
|
5
|
+
"name": "AuthzNotReadOnly",
|
|
6
|
+
"slug": "authz_not_read_only",
|
|
7
|
+
"category": "authz",
|
|
8
|
+
"display_name": "Not Read-Only",
|
|
9
|
+
"description": "Restrictive policy that blocks read-only members from mutations. Checks actor_id + is_read_only IS NOT TRUE on the SPRT. Designed to run as a restrictive counterpart after a permissive AuthzEntityMembership policy has already verified membership.",
|
|
10
|
+
"parameter_schema": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"entity_field": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Column name referencing the entity (e.g., entity_id, org_id)"
|
|
16
|
+
},
|
|
17
|
+
"membership_type": {
|
|
18
|
+
"type": [
|
|
19
|
+
"integer",
|
|
20
|
+
"string"
|
|
21
|
+
],
|
|
22
|
+
"description": "Scope: 2=org, 3+=dynamic entity types. Must be >= 2 (entity-scoped)."
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": [
|
|
26
|
+
"entity_field"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"tags": [
|
|
30
|
+
"membership",
|
|
31
|
+
"authz",
|
|
32
|
+
"restrictive"
|
|
33
|
+
]
|
|
34
|
+
};
|
package/authz/index.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export { AuthzRelatedMemberList } from './authz-related-member-list';
|
|
|
11
11
|
export { AuthzAllowAll } from './authz-allow-all';
|
|
12
12
|
export { AuthzDenyAll } from './authz-deny-all';
|
|
13
13
|
export { AuthzComposite } from './authz-composite';
|
|
14
|
+
export { AuthzNotReadOnly } from './authz-not-read-only';
|
|
14
15
|
export { AuthzPeerOwnership } from './authz-peer-ownership';
|
|
15
16
|
export { AuthzRelatedPeerOwnership } from './authz-related-peer-ownership';
|
package/authz/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AuthzRelatedPeerOwnership = exports.AuthzPeerOwnership = exports.AuthzComposite = exports.AuthzDenyAll = exports.AuthzAllowAll = exports.AuthzRelatedMemberList = exports.AuthzMemberList = exports.AuthzPublishable = exports.AuthzTemporal = exports.AuthzOrgHierarchy = exports.AuthzRelatedEntityMembership = exports.AuthzEntityMembership = exports.AuthzMembership = exports.AuthzDirectOwnerAny = exports.AuthzDirectOwner = void 0;
|
|
3
|
+
exports.AuthzRelatedPeerOwnership = exports.AuthzPeerOwnership = exports.AuthzNotReadOnly = exports.AuthzComposite = exports.AuthzDenyAll = exports.AuthzAllowAll = exports.AuthzRelatedMemberList = exports.AuthzMemberList = exports.AuthzPublishable = exports.AuthzTemporal = exports.AuthzOrgHierarchy = exports.AuthzRelatedEntityMembership = exports.AuthzEntityMembership = exports.AuthzMembership = exports.AuthzDirectOwnerAny = exports.AuthzDirectOwner = void 0;
|
|
4
4
|
var authz_direct_owner_1 = require("./authz-direct-owner");
|
|
5
5
|
Object.defineProperty(exports, "AuthzDirectOwner", { enumerable: true, get: function () { return authz_direct_owner_1.AuthzDirectOwner; } });
|
|
6
6
|
var authz_direct_owner_any_1 = require("./authz-direct-owner-any");
|
|
@@ -27,6 +27,8 @@ var authz_deny_all_1 = require("./authz-deny-all");
|
|
|
27
27
|
Object.defineProperty(exports, "AuthzDenyAll", { enumerable: true, get: function () { return authz_deny_all_1.AuthzDenyAll; } });
|
|
28
28
|
var authz_composite_1 = require("./authz-composite");
|
|
29
29
|
Object.defineProperty(exports, "AuthzComposite", { enumerable: true, get: function () { return authz_composite_1.AuthzComposite; } });
|
|
30
|
+
var authz_not_read_only_1 = require("./authz-not-read-only");
|
|
31
|
+
Object.defineProperty(exports, "AuthzNotReadOnly", { enumerable: true, get: function () { return authz_not_read_only_1.AuthzNotReadOnly; } });
|
|
30
32
|
var authz_peer_ownership_1 = require("./authz-peer-ownership");
|
|
31
33
|
Object.defineProperty(exports, "AuthzPeerOwnership", { enumerable: true, get: function () { return authz_peer_ownership_1.AuthzPeerOwnership; } });
|
|
32
34
|
var authz_related_peer_ownership_1 = require("./authz-related-peer-ownership");
|
|
@@ -247,7 +247,8 @@ export interface AuthzDirectOwnerAnyParams {
|
|
|
247
247
|
}
|
|
248
248
|
/** Membership check that verifies the user has membership (optionally with specific permission) without binding to any entity from the row. Uses EXISTS subquery against SPRT table. */
|
|
249
249
|
export interface AuthzMembershipParams {
|
|
250
|
-
membership_type
|
|
250
|
+
membership_type?: number | string;
|
|
251
|
+
entity_type?: string;
|
|
251
252
|
permission?: string;
|
|
252
253
|
permissions?: string[];
|
|
253
254
|
is_admin?: boolean;
|
|
@@ -257,6 +258,7 @@ export interface AuthzMembershipParams {
|
|
|
257
258
|
export interface AuthzEntityMembershipParams {
|
|
258
259
|
entity_field: string;
|
|
259
260
|
membership_type?: number | string;
|
|
261
|
+
entity_type?: string;
|
|
260
262
|
permission?: string;
|
|
261
263
|
permissions?: string[];
|
|
262
264
|
is_admin?: boolean;
|
|
@@ -266,6 +268,7 @@ export interface AuthzEntityMembershipParams {
|
|
|
266
268
|
export interface AuthzRelatedEntityMembershipParams {
|
|
267
269
|
entity_field: string;
|
|
268
270
|
membership_type?: number | string;
|
|
271
|
+
entity_type?: string;
|
|
269
272
|
obj_table_id?: string;
|
|
270
273
|
obj_schema?: string;
|
|
271
274
|
obj_table?: string;
|
|
@@ -321,10 +324,16 @@ export interface AuthzCompositeParams {
|
|
|
321
324
|
}[];
|
|
322
325
|
};
|
|
323
326
|
}
|
|
327
|
+
/** Restrictive policy that blocks read-only members from mutations. Checks actor_id + is_read_only IS NOT TRUE on the SPRT. Designed to run as a restrictive counterpart after a permissive AuthzEntityMembership policy has already verified membership. */
|
|
328
|
+
export interface AuthzNotReadOnlyParams {
|
|
329
|
+
entity_field: string;
|
|
330
|
+
membership_type?: number | string;
|
|
331
|
+
}
|
|
324
332
|
/** Peer visibility through shared entity membership. Authorizes access to user-owned rows when the owner and current user are both members of the same entity. Self-joins the SPRT table to find peers. */
|
|
325
333
|
export interface AuthzPeerOwnershipParams {
|
|
326
334
|
owner_field: string;
|
|
327
335
|
membership_type?: number | string;
|
|
336
|
+
entity_type?: string;
|
|
328
337
|
permission?: string;
|
|
329
338
|
permissions?: string[];
|
|
330
339
|
is_admin?: boolean;
|
|
@@ -334,6 +343,7 @@ export interface AuthzPeerOwnershipParams {
|
|
|
334
343
|
export interface AuthzRelatedPeerOwnershipParams {
|
|
335
344
|
entity_field: string;
|
|
336
345
|
membership_type?: number | string;
|
|
346
|
+
entity_type?: string;
|
|
337
347
|
obj_table_id?: string;
|
|
338
348
|
obj_schema?: string;
|
|
339
349
|
obj_table?: string;
|
|
@@ -391,6 +401,16 @@ export interface RelationManyToManyParams {
|
|
|
391
401
|
[key: string]: unknown;
|
|
392
402
|
};
|
|
393
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
|
+
}
|
|
394
414
|
/** Simple column selection from a single source table. Projects all or specific fields. */
|
|
395
415
|
export interface ViewTableProjectionParams {
|
|
396
416
|
source_table_id: string;
|
|
@@ -452,7 +472,7 @@ export interface BlueprintField {
|
|
|
452
472
|
/** An RLS policy entry for a blueprint table. Uses $type to match the blueprint JSON convention. */
|
|
453
473
|
export interface BlueprintPolicy {
|
|
454
474
|
/** Authz* policy type name (e.g., "AuthzDirectOwner", "AuthzAllowAll"). */
|
|
455
|
-
$type: "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "AuthzPeerOwnership" | "AuthzRelatedPeerOwnership";
|
|
475
|
+
$type: "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "AuthzNotReadOnly" | "AuthzPeerOwnership" | "AuthzRelatedPeerOwnership";
|
|
456
476
|
/** Privileges this policy applies to (e.g., ["select"], ["insert", "update", "delete"]). */
|
|
457
477
|
privileges?: string[];
|
|
458
478
|
/** Whether this policy is permissive (true) or restrictive (false). Defaults to true. */
|
|
@@ -549,6 +569,21 @@ export interface BlueprintTableUniqueConstraint {
|
|
|
549
569
|
/** Optional schema name override. */
|
|
550
570
|
schema_name?: string;
|
|
551
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
|
+
}
|
|
552
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. */
|
|
553
588
|
export interface BlueprintMembershipType {
|
|
554
589
|
/** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
|
|
@@ -561,7 +596,7 @@ export interface BlueprintMembershipType {
|
|
|
561
596
|
parent_entity?: string;
|
|
562
597
|
/** Custom table name for the entity table. Defaults to name-derived convention. */
|
|
563
598
|
table_name?: string;
|
|
564
|
-
/** 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. */
|
|
565
600
|
is_visible?: boolean;
|
|
566
601
|
/** Whether to provision a limits module for this entity type. Defaults to false. */
|
|
567
602
|
has_limits?: boolean;
|
|
@@ -569,11 +604,13 @@ export interface BlueprintMembershipType {
|
|
|
569
604
|
has_profiles?: boolean;
|
|
570
605
|
/** Whether to provision a levels module for this entity type. Defaults to false. */
|
|
571
606
|
has_levels?: boolean;
|
|
572
|
-
/**
|
|
607
|
+
/** Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false. */
|
|
573
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;
|
|
574
611
|
}
|
|
575
612
|
/** String shorthand -- just the node type name. */
|
|
576
|
-
export type BlueprintNodeShorthand = "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "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";
|
|
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";
|
|
577
614
|
/** Object form -- { $type, data } with typed parameters. */
|
|
578
615
|
export type BlueprintNodeObject = {
|
|
579
616
|
$type: "AuthzDirectOwner";
|
|
@@ -614,6 +651,9 @@ export type BlueprintNodeObject = {
|
|
|
614
651
|
} | {
|
|
615
652
|
$type: "AuthzComposite";
|
|
616
653
|
data: AuthzCompositeParams;
|
|
654
|
+
} | {
|
|
655
|
+
$type: "AuthzNotReadOnly";
|
|
656
|
+
data: AuthzNotReadOnlyParams;
|
|
617
657
|
} | {
|
|
618
658
|
$type: "AuthzPeerOwnership";
|
|
619
659
|
data: AuthzPeerOwnershipParams;
|
|
@@ -735,7 +775,13 @@ export type BlueprintRelation = {
|
|
|
735
775
|
target_table: string;
|
|
736
776
|
source_schema_name?: string;
|
|
737
777
|
target_schema_name?: string;
|
|
738
|
-
} & 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
|
+
} & Partial<RelationSpatialParams>;
|
|
739
785
|
/** A table definition within a blueprint. */
|
|
740
786
|
export interface BlueprintTable {
|
|
741
787
|
/** The PostgreSQL table name to create. */
|
|
@@ -384,6 +384,16 @@ function buildBlueprintTableUniqueConstraint() {
|
|
|
384
384
|
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.'),
|
|
385
385
|
]), 'A unique constraint nested inside a table definition (table_name not required).');
|
|
386
386
|
}
|
|
387
|
+
function buildBlueprintEntityTableProvision() {
|
|
388
|
+
return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
|
|
389
|
+
addJSDoc(optionalProp('use_rls', t.tsBooleanKeyword()), 'Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true.'),
|
|
390
|
+
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.'),
|
|
391
|
+
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.'),
|
|
392
|
+
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.'),
|
|
393
|
+
addJSDoc(optionalProp('grant_roles', t.tsArrayType(t.tsStringKeyword())), 'Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"].'),
|
|
394
|
+
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).'),
|
|
395
|
+
]), '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.');
|
|
396
|
+
}
|
|
387
397
|
function buildBlueprintMembershipType() {
|
|
388
398
|
return addJSDoc(exportInterface('BlueprintMembershipType', [
|
|
389
399
|
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
|
|
@@ -391,11 +401,12 @@ function buildBlueprintMembershipType() {
|
|
|
391
401
|
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
|
|
392
402
|
addJSDoc(optionalProp('parent_entity', t.tsStringKeyword()), 'Parent entity type name. Defaults to "org".'),
|
|
393
403
|
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
|
|
404
|
+
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
405
|
addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
|
|
396
406
|
addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
|
|
397
407
|
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()), '
|
|
408
|
+
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.'),
|
|
409
|
+
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
410
|
]), '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
411
|
}
|
|
401
412
|
function buildBlueprintTable() {
|
|
@@ -474,6 +485,7 @@ function buildProgram(meta) {
|
|
|
474
485
|
statements.push(buildBlueprintTableIndex());
|
|
475
486
|
statements.push(buildBlueprintUniqueConstraint());
|
|
476
487
|
statements.push(buildBlueprintTableUniqueConstraint());
|
|
488
|
+
statements.push(buildBlueprintEntityTableProvision());
|
|
477
489
|
statements.push(buildBlueprintMembershipType());
|
|
478
490
|
// -- Node types discriminated union --
|
|
479
491
|
statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const AuthzNotReadOnly = {
|
|
2
|
+
"name": "AuthzNotReadOnly",
|
|
3
|
+
"slug": "authz_not_read_only",
|
|
4
|
+
"category": "authz",
|
|
5
|
+
"display_name": "Not Read-Only",
|
|
6
|
+
"description": "Restrictive policy that blocks read-only members from mutations. Checks actor_id + is_read_only IS NOT TRUE on the SPRT. Designed to run as a restrictive counterpart after a permissive AuthzEntityMembership policy has already verified membership.",
|
|
7
|
+
"parameter_schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"entity_field": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Column name referencing the entity (e.g., entity_id, org_id)"
|
|
13
|
+
},
|
|
14
|
+
"membership_type": {
|
|
15
|
+
"type": [
|
|
16
|
+
"integer",
|
|
17
|
+
"string"
|
|
18
|
+
],
|
|
19
|
+
"description": "Scope: 2=org, 3+=dynamic entity types. Must be >= 2 (entity-scoped)."
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"entity_field"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"tags": [
|
|
27
|
+
"membership",
|
|
28
|
+
"authz",
|
|
29
|
+
"restrictive"
|
|
30
|
+
]
|
|
31
|
+
};
|
package/esm/authz/index.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export { AuthzRelatedMemberList } from './authz-related-member-list';
|
|
|
11
11
|
export { AuthzAllowAll } from './authz-allow-all';
|
|
12
12
|
export { AuthzDenyAll } from './authz-deny-all';
|
|
13
13
|
export { AuthzComposite } from './authz-composite';
|
|
14
|
+
export { AuthzNotReadOnly } from './authz-not-read-only';
|
|
14
15
|
export { AuthzPeerOwnership } from './authz-peer-ownership';
|
|
15
16
|
export { AuthzRelatedPeerOwnership } from './authz-related-peer-ownership';
|
package/esm/authz/index.js
CHANGED
|
@@ -11,5 +11,6 @@ export { AuthzRelatedMemberList } from './authz-related-member-list';
|
|
|
11
11
|
export { AuthzAllowAll } from './authz-allow-all';
|
|
12
12
|
export { AuthzDenyAll } from './authz-deny-all';
|
|
13
13
|
export { AuthzComposite } from './authz-composite';
|
|
14
|
+
export { AuthzNotReadOnly } from './authz-not-read-only';
|
|
14
15
|
export { AuthzPeerOwnership } from './authz-peer-ownership';
|
|
15
16
|
export { AuthzRelatedPeerOwnership } from './authz-related-peer-ownership';
|
|
@@ -247,7 +247,8 @@ export interface AuthzDirectOwnerAnyParams {
|
|
|
247
247
|
}
|
|
248
248
|
/** Membership check that verifies the user has membership (optionally with specific permission) without binding to any entity from the row. Uses EXISTS subquery against SPRT table. */
|
|
249
249
|
export interface AuthzMembershipParams {
|
|
250
|
-
membership_type
|
|
250
|
+
membership_type?: number | string;
|
|
251
|
+
entity_type?: string;
|
|
251
252
|
permission?: string;
|
|
252
253
|
permissions?: string[];
|
|
253
254
|
is_admin?: boolean;
|
|
@@ -257,6 +258,7 @@ export interface AuthzMembershipParams {
|
|
|
257
258
|
export interface AuthzEntityMembershipParams {
|
|
258
259
|
entity_field: string;
|
|
259
260
|
membership_type?: number | string;
|
|
261
|
+
entity_type?: string;
|
|
260
262
|
permission?: string;
|
|
261
263
|
permissions?: string[];
|
|
262
264
|
is_admin?: boolean;
|
|
@@ -266,6 +268,7 @@ export interface AuthzEntityMembershipParams {
|
|
|
266
268
|
export interface AuthzRelatedEntityMembershipParams {
|
|
267
269
|
entity_field: string;
|
|
268
270
|
membership_type?: number | string;
|
|
271
|
+
entity_type?: string;
|
|
269
272
|
obj_table_id?: string;
|
|
270
273
|
obj_schema?: string;
|
|
271
274
|
obj_table?: string;
|
|
@@ -321,10 +324,16 @@ export interface AuthzCompositeParams {
|
|
|
321
324
|
}[];
|
|
322
325
|
};
|
|
323
326
|
}
|
|
327
|
+
/** Restrictive policy that blocks read-only members from mutations. Checks actor_id + is_read_only IS NOT TRUE on the SPRT. Designed to run as a restrictive counterpart after a permissive AuthzEntityMembership policy has already verified membership. */
|
|
328
|
+
export interface AuthzNotReadOnlyParams {
|
|
329
|
+
entity_field: string;
|
|
330
|
+
membership_type?: number | string;
|
|
331
|
+
}
|
|
324
332
|
/** Peer visibility through shared entity membership. Authorizes access to user-owned rows when the owner and current user are both members of the same entity. Self-joins the SPRT table to find peers. */
|
|
325
333
|
export interface AuthzPeerOwnershipParams {
|
|
326
334
|
owner_field: string;
|
|
327
335
|
membership_type?: number | string;
|
|
336
|
+
entity_type?: string;
|
|
328
337
|
permission?: string;
|
|
329
338
|
permissions?: string[];
|
|
330
339
|
is_admin?: boolean;
|
|
@@ -334,6 +343,7 @@ export interface AuthzPeerOwnershipParams {
|
|
|
334
343
|
export interface AuthzRelatedPeerOwnershipParams {
|
|
335
344
|
entity_field: string;
|
|
336
345
|
membership_type?: number | string;
|
|
346
|
+
entity_type?: string;
|
|
337
347
|
obj_table_id?: string;
|
|
338
348
|
obj_schema?: string;
|
|
339
349
|
obj_table?: string;
|
|
@@ -391,6 +401,16 @@ export interface RelationManyToManyParams {
|
|
|
391
401
|
[key: string]: unknown;
|
|
392
402
|
};
|
|
393
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
|
+
}
|
|
394
414
|
/** Simple column selection from a single source table. Projects all or specific fields. */
|
|
395
415
|
export interface ViewTableProjectionParams {
|
|
396
416
|
source_table_id: string;
|
|
@@ -452,7 +472,7 @@ export interface BlueprintField {
|
|
|
452
472
|
/** An RLS policy entry for a blueprint table. Uses $type to match the blueprint JSON convention. */
|
|
453
473
|
export interface BlueprintPolicy {
|
|
454
474
|
/** Authz* policy type name (e.g., "AuthzDirectOwner", "AuthzAllowAll"). */
|
|
455
|
-
$type: "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "AuthzPeerOwnership" | "AuthzRelatedPeerOwnership";
|
|
475
|
+
$type: "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "AuthzNotReadOnly" | "AuthzPeerOwnership" | "AuthzRelatedPeerOwnership";
|
|
456
476
|
/** Privileges this policy applies to (e.g., ["select"], ["insert", "update", "delete"]). */
|
|
457
477
|
privileges?: string[];
|
|
458
478
|
/** Whether this policy is permissive (true) or restrictive (false). Defaults to true. */
|
|
@@ -549,6 +569,21 @@ export interface BlueprintTableUniqueConstraint {
|
|
|
549
569
|
/** Optional schema name override. */
|
|
550
570
|
schema_name?: string;
|
|
551
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
|
+
}
|
|
552
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. */
|
|
553
588
|
export interface BlueprintMembershipType {
|
|
554
589
|
/** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
|
|
@@ -561,7 +596,7 @@ export interface BlueprintMembershipType {
|
|
|
561
596
|
parent_entity?: string;
|
|
562
597
|
/** Custom table name for the entity table. Defaults to name-derived convention. */
|
|
563
598
|
table_name?: string;
|
|
564
|
-
/** 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. */
|
|
565
600
|
is_visible?: boolean;
|
|
566
601
|
/** Whether to provision a limits module for this entity type. Defaults to false. */
|
|
567
602
|
has_limits?: boolean;
|
|
@@ -569,11 +604,13 @@ export interface BlueprintMembershipType {
|
|
|
569
604
|
has_profiles?: boolean;
|
|
570
605
|
/** Whether to provision a levels module for this entity type. Defaults to false. */
|
|
571
606
|
has_levels?: boolean;
|
|
572
|
-
/**
|
|
607
|
+
/** Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false. */
|
|
573
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;
|
|
574
611
|
}
|
|
575
612
|
/** String shorthand -- just the node type name. */
|
|
576
|
-
export type BlueprintNodeShorthand = "AuthzDirectOwner" | "AuthzDirectOwnerAny" | "AuthzMembership" | "AuthzEntityMembership" | "AuthzRelatedEntityMembership" | "AuthzOrgHierarchy" | "AuthzTemporal" | "AuthzPublishable" | "AuthzMemberList" | "AuthzRelatedMemberList" | "AuthzAllowAll" | "AuthzDenyAll" | "AuthzComposite" | "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";
|
|
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";
|
|
577
614
|
/** Object form -- { $type, data } with typed parameters. */
|
|
578
615
|
export type BlueprintNodeObject = {
|
|
579
616
|
$type: "AuthzDirectOwner";
|
|
@@ -614,6 +651,9 @@ export type BlueprintNodeObject = {
|
|
|
614
651
|
} | {
|
|
615
652
|
$type: "AuthzComposite";
|
|
616
653
|
data: AuthzCompositeParams;
|
|
654
|
+
} | {
|
|
655
|
+
$type: "AuthzNotReadOnly";
|
|
656
|
+
data: AuthzNotReadOnlyParams;
|
|
617
657
|
} | {
|
|
618
658
|
$type: "AuthzPeerOwnership";
|
|
619
659
|
data: AuthzPeerOwnershipParams;
|
|
@@ -735,7 +775,13 @@ export type BlueprintRelation = {
|
|
|
735
775
|
target_table: string;
|
|
736
776
|
source_schema_name?: string;
|
|
737
777
|
target_schema_name?: string;
|
|
738
|
-
} & 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
|
+
} & Partial<RelationSpatialParams>;
|
|
739
785
|
/** A table definition within a blueprint. */
|
|
740
786
|
export interface BlueprintTable {
|
|
741
787
|
/** The PostgreSQL table name to create. */
|
|
@@ -349,6 +349,16 @@ function buildBlueprintTableUniqueConstraint() {
|
|
|
349
349
|
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.'),
|
|
350
350
|
]), 'A unique constraint nested inside a table definition (table_name not required).');
|
|
351
351
|
}
|
|
352
|
+
function buildBlueprintEntityTableProvision() {
|
|
353
|
+
return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
|
|
354
|
+
addJSDoc(optionalProp('use_rls', t.tsBooleanKeyword()), 'Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true.'),
|
|
355
|
+
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.'),
|
|
356
|
+
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.'),
|
|
357
|
+
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.'),
|
|
358
|
+
addJSDoc(optionalProp('grant_roles', t.tsArrayType(t.tsStringKeyword())), 'Database roles to grant privileges to. Forwarded to secure_table_provision as-is. Defaults to ["authenticated"].'),
|
|
359
|
+
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).'),
|
|
360
|
+
]), '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.');
|
|
361
|
+
}
|
|
352
362
|
function buildBlueprintMembershipType() {
|
|
353
363
|
return addJSDoc(exportInterface('BlueprintMembershipType', [
|
|
354
364
|
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
|
|
@@ -356,11 +366,12 @@ function buildBlueprintMembershipType() {
|
|
|
356
366
|
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
|
|
357
367
|
addJSDoc(optionalProp('parent_entity', t.tsStringKeyword()), 'Parent entity type name. Defaults to "org".'),
|
|
358
368
|
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
|
|
369
|
+
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
370
|
addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
|
|
361
371
|
addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
|
|
362
372
|
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()), '
|
|
373
|
+
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.'),
|
|
374
|
+
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
375
|
]), '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
376
|
}
|
|
366
377
|
function buildBlueprintTable() {
|
|
@@ -439,6 +450,7 @@ function buildProgram(meta) {
|
|
|
439
450
|
statements.push(buildBlueprintTableIndex());
|
|
440
451
|
statements.push(buildBlueprintUniqueConstraint());
|
|
441
452
|
statements.push(buildBlueprintTableUniqueConstraint());
|
|
453
|
+
statements.push(buildBlueprintEntityTableProvision());
|
|
442
454
|
statements.push(buildBlueprintMembershipType());
|
|
443
455
|
// -- Node types discriminated union --
|
|
444
456
|
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
|
+
};
|