node-type-registry 0.42.0 → 0.43.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.
Files changed (57) hide show
  1. package/authz/authz-app-membership.js +8 -8
  2. package/authz/index.d.ts +2 -2
  3. package/authz/index.js +5 -5
  4. package/blueprint-types.generated.d.ts +99 -10
  5. package/codegen/generate-types.js +56 -2
  6. package/data/check-greater-than.js +6 -6
  7. package/data/check-less-than.js +6 -6
  8. package/data/check-not-equal.js +4 -4
  9. package/data/check-one-of.js +5 -5
  10. package/data/data-jsonb.js +4 -3
  11. package/data/data-status-field.js +4 -3
  12. package/data/data-tags.js +4 -3
  13. package/esm/authz/authz-app-membership.js +8 -8
  14. package/esm/authz/index.d.ts +2 -2
  15. package/esm/authz/index.js +2 -2
  16. package/esm/blueprint-types.generated.d.ts +99 -10
  17. package/esm/codegen/generate-types.js +56 -2
  18. package/esm/data/check-greater-than.js +6 -6
  19. package/esm/data/check-less-than.js +6 -6
  20. package/esm/data/check-not-equal.js +4 -4
  21. package/esm/data/check-one-of.js +5 -5
  22. package/esm/data/data-jsonb.js +4 -3
  23. package/esm/data/data-status-field.js +4 -3
  24. package/esm/data/data-tags.js +4 -3
  25. package/esm/event/referral.js +21 -2
  26. package/esm/event/tracker.js +20 -1
  27. package/esm/index.d.ts +1 -1
  28. package/esm/limit/enforce-aggregate.js +32 -8
  29. package/esm/limit/enforce-counter.js +33 -10
  30. package/esm/limit/enforce-feature.js +26 -8
  31. package/esm/limit/enforce-rate.js +28 -9
  32. package/esm/limit/track-usage.js +28 -9
  33. package/esm/limit/warning-aggregate.js +29 -5
  34. package/esm/limit/warning-counter.js +30 -7
  35. package/esm/limit/warning-rate.js +31 -7
  36. package/esm/module-presets/full.js +1 -1
  37. package/esm/process/chunks.js +1 -1
  38. package/esm/process/file-embedding.js +1 -1
  39. package/esm/types.d.ts +92 -0
  40. package/esm/types.js +1 -0
  41. package/event/referral.js +21 -2
  42. package/event/tracker.js +20 -1
  43. package/index.d.ts +1 -1
  44. package/limit/enforce-aggregate.js +32 -8
  45. package/limit/enforce-counter.js +33 -10
  46. package/limit/enforce-feature.js +26 -8
  47. package/limit/enforce-rate.js +28 -9
  48. package/limit/track-usage.js +28 -9
  49. package/limit/warning-aggregate.js +29 -5
  50. package/limit/warning-counter.js +30 -7
  51. package/limit/warning-rate.js +31 -7
  52. package/module-presets/full.js +1 -1
  53. package/package.json +2 -2
  54. package/process/chunks.js +1 -1
  55. package/process/file-embedding.js +1 -1
  56. package/types.d.ts +92 -0
  57. package/types.js +1 -0
@@ -20,6 +20,40 @@ export interface TriggerCondition {
20
20
  /** Negated condition. */
21
21
  NOT?: TriggerCondition;
22
22
  }
23
+ /** Structured representation of a PostgreSQL data type. Stored as JSONB in metaschema_public.field.type. */
24
+ export interface FieldType {
25
+ /** Type name. Must be a valid SQL identifier. */
26
+ name: string;
27
+ /** Schema qualifier. */
28
+ schema?: string;
29
+ /** Type arguments (e.g., [10, 2] for numeric(10,2), ["Point", 4326] for geometry). */
30
+ args?: (string | number | boolean)[];
31
+ /** Number of array dimensions. 1 = text[], 2 = text[][]. */
32
+ array_dimensions?: number;
33
+ /** Interval field range. 1-2 elements: ["day"] or ["day", "second"]. */
34
+ range?: string[];
35
+ }
36
+ /** Structured representation of a PostgreSQL default value expression. Stored as JSONB in metaschema_public.field.default_value. */
37
+ export interface FieldDefault {
38
+ /** Literal value (string, number, boolean, null, array, or object). */
39
+ value?: string | number | boolean | null | unknown[] | Record<string, unknown>;
40
+ /** Function name. Must be a valid SQL identifier. */
41
+ function?: string;
42
+ /** Schema qualifier for function. */
43
+ schema?: string;
44
+ /** Function arguments (recursive). */
45
+ args?: (string | number | boolean | null | FieldDefault)[];
46
+ /** Output type cast. */
47
+ cast?: FieldType;
48
+ /** Binary operator (e.g., "+", "-", "||"). */
49
+ operator?: string;
50
+ /** Left operand for operator expression. */
51
+ left?: FieldDefault;
52
+ /** Right operand for operator expression. */
53
+ right?: FieldDefault;
54
+ /** SQL keyword (e.g., "CURRENT_TIMESTAMP", "CURRENT_USER"). */
55
+ sql_keyword?: string;
56
+ }
23
57
  /** Adds a CHECK constraint that validates a column value is greater than a threshold (single-column: column > value) or that one column is greater than another (cross-column: columns[0] > columns[1]). Compiled via AST helpers. */
24
58
  export interface CheckGreaterThanParams {
25
59
  column?: string;
@@ -95,7 +129,7 @@ export interface DataInheritFromParentParams {
95
129
  /** Adds a JSONB column with optional GIN index for containment queries (@>, ?, ?|, ?&). Standard pattern for semi-structured metadata. */
96
130
  export interface DataJsonbParams {
97
131
  field_name?: string;
98
- default_value?: string;
132
+ default_value?: FieldDefault;
99
133
  is_required?: boolean;
100
134
  create_index?: boolean;
101
135
  }
@@ -154,7 +188,7 @@ export interface DataSoftDeleteParams {
154
188
  /** Adds a status column with B-tree index for efficient equality filtering and sorting. Optionally constrains values via CHECK constraint when allowed_values is provided. */
155
189
  export interface DataStatusFieldParams {
156
190
  field_name?: string;
157
- type?: string;
191
+ type?: FieldType;
158
192
  default_value?: string;
159
193
  is_required?: boolean;
160
194
  allowed_values?: string[];
@@ -162,7 +196,7 @@ export interface DataStatusFieldParams {
162
196
  /** Adds a citext[] tags column with GIN index for efficient array containment queries (@>, &&). Standard tagging pattern for categorization and filtering. */
163
197
  export interface DataTagsParams {
164
198
  field_name?: string;
165
- default_value?: string;
199
+ default_value?: FieldDefault;
166
200
  is_required?: boolean;
167
201
  }
168
202
  /** Adds automatic timestamp tracking with created_at and updated_at columns. */
@@ -183,6 +217,11 @@ export interface EventReferralParams {
183
217
  events?: ('INSERT' | 'UPDATE' | 'DELETE')[];
184
218
  actor_field?: string;
185
219
  entity_field?: string;
220
+ entity_lookup?: {
221
+ obj_table: string;
222
+ obj_schema?: string;
223
+ obj_field: string;
224
+ };
186
225
  max_depth?: number;
187
226
  auto_register_type?: boolean;
188
227
  condition_field?: string;
@@ -198,6 +237,11 @@ export interface EventTrackerParams {
198
237
  toggle?: boolean;
199
238
  actor_field?: string;
200
239
  entity_field?: string;
240
+ entity_lookup?: {
241
+ obj_table: string;
242
+ obj_schema?: string;
243
+ obj_field: string;
244
+ };
201
245
  auto_register_type?: boolean;
202
246
  condition_field?: string;
203
247
  condition_value?: string;
@@ -207,26 +251,48 @@ export interface EventTrackerParams {
207
251
  /** Declaratively attaches aggregate limit-tracking triggers to a table. On INSERT the named limit is incremented per entity; on DELETE it is decremented. Uses org_limit_aggregates_inc/dec for per-entity (org-level) aggregate limits rather than per-user limits. Requires a provisioned limits_module for the target database. */
208
252
  export interface LimitEnforceAggregateParams {
209
253
  limit_name: string;
254
+ scope?: string;
210
255
  entity_field?: string;
256
+ entity_lookup?: {
257
+ obj_table: string;
258
+ obj_schema?: string;
259
+ obj_field: string;
260
+ };
211
261
  events?: ('INSERT' | 'DELETE' | 'UPDATE')[];
212
262
  }
213
263
  /** Declaratively attaches limit-tracking triggers to a table. On INSERT the named limit is incremented; on DELETE it is decremented. Requires a provisioned limits_module for the target scope. */
214
264
  export interface LimitEnforceCounterParams {
215
265
  limit_name: string;
216
- scope?: 'app' | 'org';
266
+ scope?: string;
217
267
  actor_field?: string;
268
+ entity_field?: string;
269
+ entity_lookup?: {
270
+ obj_table: string;
271
+ obj_schema?: string;
272
+ obj_field: string;
273
+ };
218
274
  events?: ('INSERT' | 'DELETE' | 'UPDATE')[];
219
275
  }
220
276
  /** Gates a table behind a feature flag backed by the cap tables. Attaches a BEFORE INSERT trigger that checks whether the named feature cap value is > 0. Features are modeled as caps with max=0 (disabled) or max=1 (enabled) in limit_caps / limit_caps_defaults tables. Resolution: COALESCE(per-entity cap, scope default, 0). */
221
277
  export interface LimitEnforceFeatureParams {
222
278
  feature_name: string;
223
- scope?: 'app' | 'org';
279
+ scope?: string;
224
280
  entity_field?: string;
281
+ entity_lookup?: {
282
+ obj_table: string;
283
+ obj_schema?: string;
284
+ obj_field: string;
285
+ };
225
286
  }
226
287
  /** Attaches a BEFORE trigger that calls check_rate_limit() to enforce sliding-window rate limits before allowing mutations. The function checks all three scopes (entity, actor-in-entity, actor) in a single call; which scopes are actually enforced is controlled by what rows exist in rate_window_limits (plan-based config). Requires a provisioned meter_rate_limits_module and billing_module for the target database. */
227
288
  export interface LimitEnforceRateParams {
228
289
  meter_slug: string;
229
290
  entity_field?: string;
291
+ entity_lookup?: {
292
+ obj_table: string;
293
+ obj_schema?: string;
294
+ obj_field: string;
295
+ };
230
296
  actor_field?: string;
231
297
  events?: ('INSERT' | 'UPDATE')[];
232
298
  }
@@ -234,24 +300,47 @@ export interface LimitEnforceRateParams {
234
300
  export interface LimitTrackUsageParams {
235
301
  meter_slug: string;
236
302
  entity_field?: string;
303
+ entity_lookup?: {
304
+ obj_table: string;
305
+ obj_schema?: string;
306
+ obj_field: string;
307
+ };
237
308
  quantity?: number;
238
309
  events?: ('INSERT' | 'DELETE' | 'UPDATE')[];
239
310
  }
240
311
  /** Attaches an AFTER INSERT trigger that checks if the entity's aggregate usage has crossed any warning threshold configured in the limit_warnings table. If a threshold is reached for the first time, enqueues a background job (e.g. email notification). Uses limit_warning_state for one-time dedup per warning/actor/entity triple. Requires a provisioned limits_module with limit_warnings and aggregate limits enabled. */
241
312
  export interface LimitWarningAggregateParams {
242
313
  limit_name: string;
314
+ scope?: string;
243
315
  entity_field?: string;
316
+ entity_lookup?: {
317
+ obj_table: string;
318
+ obj_schema?: string;
319
+ obj_field: string;
320
+ };
244
321
  }
245
322
  /** Attaches an AFTER INSERT trigger that checks if the actor's current usage has crossed any warning threshold configured in the limit_warnings table. If a threshold is reached for the first time, enqueues a background job (e.g. email notification). Uses limit_warning_state for one-time dedup per warning/actor pair. Requires a provisioned limits_module with limit_warnings enabled. */
246
323
  export interface LimitWarningCounterParams {
247
324
  limit_name: string;
248
- scope?: 'app' | 'org';
325
+ scope?: string;
249
326
  actor_field?: string;
327
+ entity_field?: string;
328
+ entity_lookup?: {
329
+ obj_table: string;
330
+ obj_schema?: string;
331
+ obj_field: string;
332
+ };
250
333
  }
251
334
  /** Attaches an AFTER INSERT trigger that checks if the actor's current request count in the active sliding window has crossed any warning threshold configured in the limit_warnings table. If a threshold is reached for the first time, enqueues a background job (e.g. email notification). Uses limit_warning_state for one-time dedup per warning/actor pair. Requires both a limits_module with limit_warnings enabled and a rate_limit_meters_module. */
252
335
  export interface LimitWarningRateParams {
253
336
  meter_slug: string;
337
+ scope?: string;
254
338
  entity_field?: string;
339
+ entity_lookup?: {
340
+ obj_table: string;
341
+ obj_schema?: string;
342
+ obj_field: string;
343
+ };
255
344
  actor_field?: string;
256
345
  }
257
346
  /** Creates a BM25 index on an existing text column using pg_textsearch. Enables statistical relevance ranking with configurable k1 and b parameters. The BM25 index is auto-detected by graphile-search. */
@@ -804,12 +893,12 @@ export interface ViewTableProjectionParams {
804
893
  export interface BlueprintField {
805
894
  /** The column name. */
806
895
  name: string;
807
- /** The PostgreSQL type (e.g., "text", "integer", "boolean", "uuid"). */
808
- type: string;
896
+ /** PostgreSQL type as a FieldType object (e.g., { name: "text" }) or legacy string. */
897
+ type: FieldType | string;
809
898
  /** Whether the column has a NOT NULL constraint. */
810
899
  is_required?: boolean;
811
- /** SQL default value expression (e.g., "true", "now()"). */
812
- default_value?: string;
900
+ /** Default value as a FieldDefault object (e.g., { function: "now" }) or legacy string. */
901
+ default_value?: FieldDefault | string;
813
902
  /** Comment/description for this field. */
814
903
  description?: string;
815
904
  }
@@ -137,6 +137,56 @@ function buildTriggerConditionInterface() {
137
137
  ]), 'Recursive condition type for compound trigger WHEN clauses. Leaf conditions specify {field, op, value?, row?, ref?}. Combinators nest via AND, OR, NOT.');
138
138
  }
139
139
  // ---------------------------------------------------------------------------
140
+ // FieldType — structured PostgreSQL type representation.
141
+ // ---------------------------------------------------------------------------
142
+ function buildFieldTypeInterface() {
143
+ const argType = t.tsUnionType([
144
+ t.tsStringKeyword(),
145
+ t.tsNumberKeyword(),
146
+ t.tsBooleanKeyword()
147
+ ]);
148
+ return addJSDoc(exportInterface('FieldType', [
149
+ addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Type name. Must be a valid SQL identifier.'),
150
+ addJSDoc(optionalProp('schema', t.tsStringKeyword()), 'Schema qualifier.'),
151
+ addJSDoc(optionalProp('args', t.tsArrayType(argType)), 'Type arguments (e.g., [10, 2] for numeric(10,2), ["Point", 4326] for geometry).'),
152
+ addJSDoc(optionalProp('array_dimensions', t.tsNumberKeyword()), 'Number of array dimensions. 1 = text[], 2 = text[][].'),
153
+ addJSDoc(optionalProp('range', t.tsArrayType(t.tsStringKeyword())), 'Interval field range. 1-2 elements: ["day"] or ["day", "second"].')
154
+ ]), 'Structured representation of a PostgreSQL data type. Stored as JSONB in metaschema_public.field.type.');
155
+ }
156
+ // ---------------------------------------------------------------------------
157
+ // FieldDefault — structured PostgreSQL default value expression.
158
+ // ---------------------------------------------------------------------------
159
+ function buildFieldDefaultInterface() {
160
+ const defaultRef = t.tsTypeReference(t.identifier('FieldDefault'));
161
+ const fieldTypeRef = t.tsTypeReference(t.identifier('FieldType'));
162
+ const valueType = t.tsUnionType([
163
+ t.tsStringKeyword(),
164
+ t.tsNumberKeyword(),
165
+ t.tsBooleanKeyword(),
166
+ t.tsNullKeyword(),
167
+ t.tsArrayType(t.tsUnknownKeyword()),
168
+ recordType(t.tsStringKeyword(), t.tsUnknownKeyword())
169
+ ]);
170
+ const argType = t.tsUnionType([
171
+ t.tsStringKeyword(),
172
+ t.tsNumberKeyword(),
173
+ t.tsBooleanKeyword(),
174
+ t.tsNullKeyword(),
175
+ defaultRef
176
+ ]);
177
+ return addJSDoc(exportInterface('FieldDefault', [
178
+ addJSDoc(optionalProp('value', valueType), 'Literal value (string, number, boolean, null, array, or object).'),
179
+ addJSDoc(optionalProp('function', t.tsStringKeyword()), 'Function name. Must be a valid SQL identifier.'),
180
+ addJSDoc(optionalProp('schema', t.tsStringKeyword()), 'Schema qualifier for function.'),
181
+ addJSDoc(optionalProp('args', t.tsArrayType(argType)), 'Function arguments (recursive).'),
182
+ addJSDoc(optionalProp('cast', fieldTypeRef), 'Output type cast.'),
183
+ addJSDoc(optionalProp('operator', t.tsStringKeyword()), 'Binary operator (e.g., "+", "-", "||").'),
184
+ addJSDoc(optionalProp('left', defaultRef), 'Left operand for operator expression.'),
185
+ addJSDoc(optionalProp('right', defaultRef), 'Right operand for operator expression.'),
186
+ addJSDoc(optionalProp('sql_keyword', t.tsStringKeyword()), 'SQL keyword (e.g., "CURRENT_TIMESTAMP", "CURRENT_USER").')
187
+ ]), 'Structured representation of a PostgreSQL default value expression. Stored as JSONB in metaschema_public.field.default_value.');
188
+ }
189
+ // ---------------------------------------------------------------------------
140
190
  // x-codegen-type post-processing — replaces properties that have an
141
191
  // 'x-codegen-type' marker in their JSON Schema with a hand-written TS type
142
192
  // reference. This lets node type definitions delegate complex types
@@ -302,11 +352,13 @@ function buildBlueprintField(meta) {
302
352
  return deriveInterfaceFromTable(table, 'BlueprintField', 'A custom field (column) to add to a blueprint table. Derived from _meta.');
303
353
  }
304
354
  // Static fallback
355
+ const fieldTypeRef = t.tsTypeReference(t.identifier('FieldType'));
356
+ const fieldDefaultRef = t.tsTypeReference(t.identifier('FieldDefault'));
305
357
  return addJSDoc(exportInterface('BlueprintField', [
306
358
  addJSDoc(requiredProp('name', t.tsStringKeyword()), 'The column name.'),
307
- addJSDoc(requiredProp('type', t.tsStringKeyword()), 'The PostgreSQL type (e.g., "text", "integer", "boolean", "uuid").'),
359
+ addJSDoc(requiredProp('type', t.tsUnionType([fieldTypeRef, t.tsStringKeyword()])), 'PostgreSQL type as a FieldType object (e.g., { name: "text" }) or legacy string.'),
308
360
  addJSDoc(optionalProp('is_required', t.tsBooleanKeyword()), 'Whether the column has a NOT NULL constraint.'),
309
- addJSDoc(optionalProp('default_value', t.tsStringKeyword()), 'SQL default value expression (e.g., "true", "now()").'),
361
+ addJSDoc(optionalProp('default_value', t.tsUnionType([fieldDefaultRef, t.tsStringKeyword()])), 'Default value as a FieldDefault object (e.g., { function: "now" }) or legacy string.'),
310
362
  addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Comment/description for this field.')
311
363
  ]), 'A custom field (column) to add to a blueprint table.');
312
364
  }
@@ -728,6 +780,8 @@ function buildProgram(meta) {
728
780
  // -- Shared recursive types (emitted before parameter interfaces) --
729
781
  statements.push(sectionComment('Shared recursive types'));
730
782
  statements.push(buildTriggerConditionInterface());
783
+ statements.push(buildFieldTypeInterface());
784
+ statements.push(buildFieldDefaultInterface());
731
785
  // -- Parameter interfaces grouped by category --
732
786
  const categoryOrder = ['billing', 'check', 'data', 'event', 'limit', 'limit_enforce', 'limit_track', 'limit_warning', 'search', 'job', 'process', 'authz', 'relation', 'view'];
733
787
  for (const cat of categoryOrder) {
@@ -10,21 +10,21 @@ export const CheckGreaterThan = {
10
10
  column: {
11
11
  type: 'string',
12
12
  format: 'column-ref',
13
- description: 'Single column to compare against value (mutually exclusive with columns)',
13
+ description: 'Single column to compare against value (mutually exclusive with columns)'
14
14
  },
15
15
  value: {
16
16
  type: 'number',
17
17
  description: 'Threshold value for single-column comparison (column > value)',
18
- default: 0,
18
+ default: 0
19
19
  },
20
20
  columns: {
21
21
  type: 'array',
22
22
  items: { type: 'string', format: 'column-ref' },
23
23
  description: 'Two columns for cross-column comparison (columns[0] > columns[1])',
24
24
  minItems: 2,
25
- maxItems: 2,
26
- },
27
- },
25
+ maxItems: 2
26
+ }
27
+ }
28
28
  },
29
- tags: ['check', 'constraint', 'validation', 'comparison'],
29
+ tags: ['check', 'constraint', 'validation', 'comparison']
30
30
  };
@@ -10,20 +10,20 @@ export const CheckLessThan = {
10
10
  column: {
11
11
  type: 'string',
12
12
  format: 'column-ref',
13
- description: 'Single column to compare against value (mutually exclusive with columns)',
13
+ description: 'Single column to compare against value (mutually exclusive with columns)'
14
14
  },
15
15
  value: {
16
16
  type: 'number',
17
- description: 'Threshold value for single-column comparison (column < value)',
17
+ description: 'Threshold value for single-column comparison (column < value)'
18
18
  },
19
19
  columns: {
20
20
  type: 'array',
21
21
  items: { type: 'string', format: 'column-ref' },
22
22
  description: 'Two columns for cross-column comparison (columns[0] < columns[1])',
23
23
  minItems: 2,
24
- maxItems: 2,
25
- },
26
- },
24
+ maxItems: 2
25
+ }
26
+ }
27
27
  },
28
- tags: ['check', 'constraint', 'validation', 'comparison'],
28
+ tags: ['check', 'constraint', 'validation', 'comparison']
29
29
  };
@@ -12,10 +12,10 @@ export const CheckNotEqual = {
12
12
  items: { type: 'string', format: 'column-ref' },
13
13
  description: 'Two columns that must not be equal',
14
14
  minItems: 2,
15
- maxItems: 2,
16
- },
15
+ maxItems: 2
16
+ }
17
17
  },
18
- required: ['columns'],
18
+ required: ['columns']
19
19
  },
20
- tags: ['check', 'constraint', 'validation', 'inequality'],
20
+ tags: ['check', 'constraint', 'validation', 'inequality']
21
21
  };
@@ -10,15 +10,15 @@ export const CheckOneOf = {
10
10
  column: {
11
11
  type: 'string',
12
12
  format: 'column-ref',
13
- description: 'Column to validate against the allowed values',
13
+ description: 'Column to validate against the allowed values'
14
14
  },
15
15
  values: {
16
16
  type: 'array',
17
17
  items: { type: 'string' },
18
- description: 'Array of allowed values for the column',
19
- },
18
+ description: 'Array of allowed values for the column'
19
+ }
20
20
  },
21
- required: ['column', 'values'],
21
+ required: ['column', 'values']
22
22
  },
23
- tags: ['check', 'constraint', 'validation', 'enum'],
23
+ tags: ['check', 'constraint', 'validation', 'enum']
24
24
  };
@@ -14,9 +14,10 @@ export const DataJsonb = {
14
14
  default: 'metadata'
15
15
  },
16
16
  default_value: {
17
- type: 'string',
18
- description: 'Default value expression',
19
- default: "'{}'::jsonb"
17
+ type: 'object',
18
+ description: 'Default value as a FieldDefault object',
19
+ 'x-codegen-type': 'FieldDefault',
20
+ default: { value: {}, cast: { name: 'jsonb' } }
20
21
  },
21
22
  is_required: {
22
23
  type: 'boolean',
@@ -14,9 +14,10 @@ export const DataStatusField = {
14
14
  default: 'status'
15
15
  },
16
16
  type: {
17
- type: 'string',
18
- description: 'Column type (text or citext)',
19
- default: 'text'
17
+ type: 'object',
18
+ description: 'Column type as a FieldType object',
19
+ 'x-codegen-type': 'FieldType',
20
+ default: { name: 'text' }
20
21
  },
21
22
  default_value: {
22
23
  type: 'string',
@@ -14,9 +14,10 @@ export const DataTags = {
14
14
  default: 'tags'
15
15
  },
16
16
  default_value: {
17
- type: 'string',
18
- description: 'Default value expression for the tags column',
19
- default: 'ARRAY[]::citext[]'
17
+ type: 'object',
18
+ description: 'Default value as a FieldDefault object',
19
+ 'x-codegen-type': 'FieldDefault',
20
+ default: { value: [], cast: { name: 'citext', array_dimensions: 1 } }
20
21
  },
21
22
  is_required: {
22
23
  type: 'boolean',
@@ -39,7 +39,26 @@ export const EventReferral = {
39
39
  entity_field: {
40
40
  type: 'string',
41
41
  format: 'column-ref',
42
- description: 'Column containing the entity ID (org/group) for entity-scoped referral events. Omit for user-only events.'
42
+ description: 'Column containing the entity ID (org/group) for entity-scoped referral events. For FK lookups (e.g., channel_id → channels.entity_id), combine with entity_lookup. Omit for user-only events.'
43
+ },
44
+ entity_lookup: {
45
+ type: 'object',
46
+ description: 'FK lookup configuration for resolving entity_id through a related table. Used when entity_field is a FK (e.g., channel_id) rather than a direct entity_id. The generator validates all fields against metaschema within the same database_id.',
47
+ properties: {
48
+ obj_table: {
49
+ type: 'string',
50
+ description: 'Name of the related table to look up entity_id from (e.g., "channels"). Required.'
51
+ },
52
+ obj_schema: {
53
+ type: 'string',
54
+ description: 'Schema of the related table (user-facing name, e.g., "public"). Optional — if omitted, resolved by table name within the same database_id (raises error if ambiguous).'
55
+ },
56
+ obj_field: {
57
+ type: 'string',
58
+ description: 'Column on the related table that holds the entity_id (e.g., "entity_id"). Required.'
59
+ }
60
+ },
61
+ required: ['obj_table', 'obj_field']
43
62
  },
44
63
  max_depth: {
45
64
  type: 'integer',
@@ -49,7 +68,7 @@ export const EventReferral = {
49
68
  'be combined with entity_field.',
50
69
  default: 1,
51
70
  minimum: 1,
52
- maximum: 10,
71
+ maximum: 10
53
72
  },
54
73
  auto_register_type: {
55
74
  type: 'boolean',
@@ -49,7 +49,26 @@ export const EventTracker = {
49
49
  entity_field: {
50
50
  type: 'string',
51
51
  format: 'column-ref',
52
- description: 'Column containing the entity ID (org/group) for entity-scoped events. Omit for user-only events.'
52
+ description: 'Column containing the entity ID (org/group) for entity-scoped events. For FK lookups (e.g., channel_id → channels.entity_id), combine with entity_lookup. Omit for user-only events.'
53
+ },
54
+ entity_lookup: {
55
+ type: 'object',
56
+ description: 'FK lookup configuration for resolving entity_id through a related table. Used when entity_field is a FK (e.g., channel_id) rather than a direct entity_id. The generator validates all fields against metaschema within the same database_id.',
57
+ properties: {
58
+ obj_table: {
59
+ type: 'string',
60
+ description: 'Name of the related table to look up entity_id from (e.g., "channels"). Required.'
61
+ },
62
+ obj_schema: {
63
+ type: 'string',
64
+ description: 'Schema of the related table (user-facing name, e.g., "public"). Optional — if omitted, resolved by table name within the same database_id (raises error if ambiguous).'
65
+ },
66
+ obj_field: {
67
+ type: 'string',
68
+ description: 'Column on the related table that holds the entity_id (e.g., "entity_id"). Required.'
69
+ }
70
+ },
71
+ required: ['obj_table', 'obj_field']
53
72
  },
54
73
  auto_register_type: {
55
74
  type: 'boolean',
package/esm/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export * from './limit';
8
8
  export * from './module-presets';
9
9
  export * from './process';
10
10
  export * from './relation';
11
- export type { JSONSchema, NodeTypeDefinition } from './types';
11
+ export type { FieldDefault, FieldDefaultArg, FieldType, JSONSchema, NodeTypeDefinition } from './types';
12
12
  export * from './view';
13
13
  import type { NodeTypeDefinition } from './types';
14
14
  export declare const allNodeTypes: NodeTypeDefinition[];
@@ -9,25 +9,49 @@ export const LimitEnforceAggregate = {
9
9
  properties: {
10
10
  limit_name: {
11
11
  type: 'string',
12
- description: 'Name of the aggregate limit to track (must match a default_limits entry, e.g. "databases", "members")',
12
+ description: 'Name of the aggregate limit to track (must match a default_limits entry, e.g. "databases", "members")'
13
+ },
14
+ scope: {
15
+ type: 'string',
16
+ description: 'Membership type prefix that determines which limits_module row to use. Resolved dynamically via memberships_module — supports any provisioned type (e.g. "org", "data_room", "channel", "team").',
17
+ default: 'org'
13
18
  },
14
19
  entity_field: {
15
20
  type: 'string',
16
21
  format: 'column-ref',
17
- description: 'Column on the target table that holds the entity id for aggregate limit lookup',
18
- default: 'entity_id',
22
+ description: 'Column on the target table that holds (or references) the entity id for aggregate limit lookup. For direct entity_id columns, just set this field. For FK lookups (e.g., channel_id → channels.entity_id), combine with entity_lookup.',
23
+ default: 'entity_id'
24
+ },
25
+ entity_lookup: {
26
+ type: 'object',
27
+ description: 'FK lookup configuration for resolving entity_id through a related table. Used when entity_field is a FK (e.g., channel_id) rather than a direct entity_id. The generator validates all fields against metaschema within the same database_id.',
28
+ properties: {
29
+ obj_table: {
30
+ type: 'string',
31
+ description: 'Name of the related table to look up entity_id from (e.g., "channels"). Required.'
32
+ },
33
+ obj_schema: {
34
+ type: 'string',
35
+ description: 'Schema of the related table (user-facing name, e.g., "public"). Optional — if omitted, resolved by table name within the same database_id (raises error if ambiguous).'
36
+ },
37
+ obj_field: {
38
+ type: 'string',
39
+ description: 'Column on the related table that holds the entity_id (e.g., "entity_id"). Required.'
40
+ }
41
+ },
42
+ required: ['obj_table', 'obj_field']
19
43
  },
20
44
  events: {
21
45
  type: 'array',
22
46
  items: {
23
47
  type: 'string',
24
- enum: ['INSERT', 'DELETE', 'UPDATE'],
48
+ enum: ['INSERT', 'DELETE', 'UPDATE']
25
49
  },
26
50
  description: 'Which DML events to attach triggers for',
27
- default: ['INSERT', 'DELETE'],
28
- },
51
+ default: ['INSERT', 'DELETE']
52
+ }
29
53
  },
30
- required: ['limit_name'],
54
+ required: ['limit_name']
31
55
  },
32
- tags: ['limits', 'triggers', 'aggregates', 'enforce'],
56
+ tags: ['limits', 'triggers', 'aggregates', 'enforce']
33
57
  };
@@ -9,31 +9,54 @@ export const LimitEnforceCounter = {
9
9
  properties: {
10
10
  limit_name: {
11
11
  type: 'string',
12
- description: 'Name of the limit to track (must match a default_limits entry, e.g. "projects", "members")',
12
+ description: 'Name of the limit to track (must match a default_limits entry, e.g. "projects", "members")'
13
13
  },
14
14
  scope: {
15
15
  type: 'string',
16
- enum: ['app', 'org'],
17
- description: 'Limit scope: "app" (membership_type=1, user-level) or "org" (membership_type=2, entity-level)',
18
- default: 'app',
16
+ description: 'Membership type prefix that determines which limits_module row to use. Resolved dynamically via memberships_module — supports any provisioned type (e.g. "app", "org", "data_room", "channel", "team").',
17
+ default: 'app'
19
18
  },
20
19
  actor_field: {
21
20
  type: 'string',
22
21
  format: 'column-ref',
23
22
  description: 'Column on the target table that holds the actor or entity id used for limit lookup',
24
- default: 'owner_id',
23
+ default: 'owner_id'
24
+ },
25
+ entity_field: {
26
+ type: 'string',
27
+ format: 'column-ref',
28
+ description: 'Column on the target table that holds (or references) the entity id for entity context resolution. For direct entity_id columns, just set this field. For FK lookups (e.g., channel_id → channels.entity_id), combine with entity_lookup.'
29
+ },
30
+ entity_lookup: {
31
+ type: 'object',
32
+ description: 'FK lookup configuration for resolving entity_id through a related table. Used when entity_field is a FK (e.g., channel_id) rather than a direct entity_id. The generator validates all fields against metaschema within the same database_id.',
33
+ properties: {
34
+ obj_table: {
35
+ type: 'string',
36
+ description: 'Name of the related table to look up entity_id from (e.g., "channels"). Required.'
37
+ },
38
+ obj_schema: {
39
+ type: 'string',
40
+ description: 'Schema of the related table (user-facing name, e.g., "public"). Optional — if omitted, resolved by table name within the same database_id (raises error if ambiguous).'
41
+ },
42
+ obj_field: {
43
+ type: 'string',
44
+ description: 'Column on the related table that holds the entity_id (e.g., "entity_id"). Required.'
45
+ }
46
+ },
47
+ required: ['obj_table', 'obj_field']
25
48
  },
26
49
  events: {
27
50
  type: 'array',
28
51
  items: {
29
52
  type: 'string',
30
- enum: ['INSERT', 'DELETE', 'UPDATE'],
53
+ enum: ['INSERT', 'DELETE', 'UPDATE']
31
54
  },
32
55
  description: 'Which DML events to attach triggers for',
33
- default: ['INSERT', 'DELETE'],
34
- },
56
+ default: ['INSERT', 'DELETE']
57
+ }
35
58
  },
36
- required: ['limit_name'],
59
+ required: ['limit_name']
37
60
  },
38
- tags: ['limits', 'triggers', 'enforce'],
61
+ tags: ['limits', 'triggers', 'enforce']
39
62
  };