graphile-settings 5.15.1 → 5.17.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.
@@ -0,0 +1,2 @@
1
+ import type { PgAttribute, ScalarEncodingMeta } from './types';
2
+ export declare function buildScalarEncoding(attr: PgAttribute | null | undefined): ScalarEncodingMeta | null;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Scalar encoding contracts — the one piece of field metadata standard GraphQL
3
+ * introspection can't provide.
4
+ *
5
+ * Introspection tells a client a field's scalar *name* (`GeoJSON`, `LTree`,
6
+ * `Interval`, `BigInt`, `vector`, …) but not how to serialize/parse it or its
7
+ * structural parameters (geometry subtype/SRID, vector dimensions, ltree
8
+ * dot-path semantics). Without that, generated clients treat these as opaque
9
+ * strings / `any` and mis-encode inputs or fail to parse outputs.
10
+ *
11
+ * `kind` is derived deterministically from the *underlying* codec (array and
12
+ * domain wrappers unwrapped first, first match wins) so exactly one kind is
13
+ * ever assigned and nothing can clobber it. Structural extras (subtype, SRID,
14
+ * dimensions) are read verbatim from attribute extensions — never guessed, and
15
+ * `null` when absent.
16
+ */
17
+ /** pg type name → encoding kind. Plain scalars are intentionally absent → null. */
18
+ const KIND_BY_PG_TYPE = {
19
+ int8: 'bigint',
20
+ bigint: 'bigint',
21
+ numeric: 'bigint',
22
+ timestamptz: 'datetime',
23
+ timestamp: 'datetime',
24
+ date: 'date',
25
+ time: 'time',
26
+ timetz: 'time',
27
+ interval: 'interval',
28
+ uuid: 'uuid',
29
+ geometry: 'geojson',
30
+ geography: 'geojson',
31
+ point: 'point',
32
+ inet: 'inet',
33
+ cidr: 'inet',
34
+ ltree: 'ltree',
35
+ vector: 'vector',
36
+ bytea: 'bytea'
37
+ };
38
+ /** Unwrap array-of and domain-of wrappers to the underlying scalar/record codec. */
39
+ function underlyingCodec(codec) {
40
+ let current = codec ?? null;
41
+ const seen = new Set();
42
+ while (current && !seen.has(current) && (current.arrayOfCodec || current.domainOfCodec)) {
43
+ seen.add(current);
44
+ current = current.arrayOfCodec || current.domainOfCodec || null;
45
+ }
46
+ return current;
47
+ }
48
+ /** True when a codec is a composite/record type (has named attributes). */
49
+ function isCompositeCodec(codec) {
50
+ return !!codec?.attributes && Object.keys(codec.attributes).length > 0;
51
+ }
52
+ export function buildScalarEncoding(attr) {
53
+ const codec = underlyingCodec(attr?.codec);
54
+ if (!codec)
55
+ return null;
56
+ const ext = attr?.extensions ?? undefined;
57
+ const kind = KIND_BY_PG_TYPE[codec.name];
58
+ if (!kind) {
59
+ // Composite/record types are the only non-mapped scalars worth describing;
60
+ // everything else (text, int, bool, json, enum, …) is fully covered by gqlType.
61
+ if (isCompositeCodec(codec)) {
62
+ return { kind: 'composite', fields: null };
63
+ }
64
+ return null;
65
+ }
66
+ switch (kind) {
67
+ case 'geojson':
68
+ return {
69
+ kind,
70
+ geometrySubtype: typeof ext?.geometrySubtype === 'string' ? ext.geometrySubtype : null,
71
+ srid: typeof ext?.geometrySrid === 'number' ? ext.geometrySrid : null
72
+ };
73
+ case 'vector':
74
+ return {
75
+ kind,
76
+ elementType: 'float',
77
+ dimensions: typeof ext?.vectorDimensions === 'number' ? ext.vectorDimensions : null
78
+ };
79
+ case 'ltree':
80
+ return { kind, dotPath: true };
81
+ default:
82
+ return { kind };
83
+ }
84
+ }
@@ -1,4 +1,4 @@
1
- import { GraphQLBoolean, GraphQLFloat, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString, } from 'graphql';
1
+ import { GraphQLBoolean, GraphQLFloat, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString } from 'graphql';
2
2
  function nn(type) {
3
3
  return new GraphQLNonNull(type);
4
4
  }
@@ -6,6 +6,21 @@ function nnList(type) {
6
6
  return nn(new GraphQLList(nn(type)));
7
7
  }
8
8
  function createMetaSchemaType() {
9
+ const MetaScalarEncodingType = new GraphQLObjectType({
10
+ name: 'MetaScalarEncoding',
11
+ description: 'How a client must serialize/parse a scalar — the one field-type detail standard GraphQL introspection cannot describe. Null for plain scalars whose wire format is obvious from gqlType.',
12
+ fields: () => ({
13
+ kind: {
14
+ type: nn(GraphQLString),
15
+ description: "Machine kind: bigint, datetime, date, time, interval, uuid, geojson, point, inet, ltree, vector, bytea, or composite."
16
+ },
17
+ elementType: { type: GraphQLString, description: "For 'vector': element scalar (e.g. 'float')." },
18
+ dimensions: { type: GraphQLInt, description: "For 'vector': declared length, else null." },
19
+ geometrySubtype: { type: GraphQLString, description: "For 'geojson': Point/LineString/Polygon/…, else null." },
20
+ srid: { type: GraphQLInt, description: "For 'geojson': spatial reference id, else null." },
21
+ dotPath: { type: GraphQLBoolean, description: "For 'ltree': values are dot-separated path strings." }
22
+ })
23
+ });
9
24
  const MetaTypeType = new GraphQLObjectType({
10
25
  name: 'MetaType',
11
26
  description: 'Information about a PostgreSQL type',
@@ -16,15 +31,19 @@ function createMetaSchemaType() {
16
31
  isNotNull: { type: GraphQLBoolean },
17
32
  hasDefault: { type: GraphQLBoolean },
18
33
  subtype: { type: GraphQLString },
19
- }),
34
+ encoding: {
35
+ type: MetaScalarEncodingType,
36
+ description: 'Scalar serialization contract (null for plain scalars)'
37
+ }
38
+ })
20
39
  });
21
40
  const MetaEnumType = new GraphQLObjectType({
22
41
  name: 'MetaEnum',
23
42
  description: 'Information about a PostgreSQL enum type',
24
43
  fields: () => ({
25
44
  name: { type: nn(GraphQLString), description: 'The PostgreSQL enum type name' },
26
- values: { type: nnList(GraphQLString), description: 'Allowed values for this enum' },
27
- }),
45
+ values: { type: nnList(GraphQLString), description: 'Allowed values for this enum' }
46
+ })
28
47
  });
29
48
  const MetaFieldType = new GraphQLObjectType({
30
49
  name: 'MetaField',
@@ -37,8 +56,8 @@ function createMetaSchemaType() {
37
56
  isPrimaryKey: { type: nn(GraphQLBoolean) },
38
57
  isForeignKey: { type: nn(GraphQLBoolean) },
39
58
  description: { type: GraphQLString },
40
- enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' },
41
- }),
59
+ enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }
60
+ })
42
61
  });
43
62
  const MetaIndexType = new GraphQLObjectType({
44
63
  name: 'MetaIndex',
@@ -48,31 +67,31 @@ function createMetaSchemaType() {
48
67
  isUnique: { type: nn(GraphQLBoolean) },
49
68
  isPrimary: { type: nn(GraphQLBoolean) },
50
69
  columns: { type: nnList(GraphQLString) },
51
- fields: { type: new GraphQLList(nn(MetaFieldType)) },
52
- }),
70
+ fields: { type: new GraphQLList(nn(MetaFieldType)) }
71
+ })
53
72
  });
54
73
  const MetaPrimaryKeyConstraintType = new GraphQLObjectType({
55
74
  name: 'MetaPrimaryKeyConstraint',
56
75
  description: 'Information about a primary key constraint',
57
76
  fields: () => ({
58
77
  name: { type: nn(GraphQLString) },
59
- fields: { type: nnList(MetaFieldType) },
60
- }),
78
+ fields: { type: nnList(MetaFieldType) }
79
+ })
61
80
  });
62
81
  const MetaUniqueConstraintType = new GraphQLObjectType({
63
82
  name: 'MetaUniqueConstraint',
64
83
  description: 'Information about a unique constraint',
65
84
  fields: () => ({
66
85
  name: { type: nn(GraphQLString) },
67
- fields: { type: nnList(MetaFieldType) },
68
- }),
86
+ fields: { type: nnList(MetaFieldType) }
87
+ })
69
88
  });
70
89
  const MetaRefTableType = new GraphQLObjectType({
71
90
  name: 'MetaRefTable',
72
91
  description: 'Reference to a related table',
73
92
  fields: () => ({
74
- name: { type: nn(GraphQLString) },
75
- }),
93
+ name: { type: nn(GraphQLString) }
94
+ })
76
95
  });
77
96
  const MetaForeignKeyConstraintType = new GraphQLObjectType({
78
97
  name: 'MetaForeignKeyConstraint',
@@ -83,8 +102,8 @@ function createMetaSchemaType() {
83
102
  referencedTable: { type: nn(GraphQLString) },
84
103
  referencedFields: { type: nnList(GraphQLString) },
85
104
  refFields: { type: new GraphQLList(nn(MetaFieldType)) },
86
- refTable: { type: MetaRefTableType },
87
- }),
105
+ refTable: { type: MetaRefTableType }
106
+ })
88
107
  });
89
108
  const MetaConstraintsType = new GraphQLObjectType({
90
109
  name: 'MetaConstraints',
@@ -92,8 +111,8 @@ function createMetaSchemaType() {
92
111
  fields: () => ({
93
112
  primaryKey: { type: MetaPrimaryKeyConstraintType },
94
113
  unique: { type: nnList(MetaUniqueConstraintType) },
95
- foreignKey: { type: nnList(MetaForeignKeyConstraintType) },
96
- }),
114
+ foreignKey: { type: nnList(MetaForeignKeyConstraintType) }
115
+ })
97
116
  });
98
117
  const MetaInflectionType = new GraphQLObjectType({
99
118
  name: 'MetaInflection',
@@ -110,8 +129,8 @@ function createMetaSchemaType() {
110
129
  createInputType: { type: nn(GraphQLString) },
111
130
  createPayloadType: { type: nn(GraphQLString) },
112
131
  updatePayloadType: { type: GraphQLString },
113
- deletePayloadType: { type: nn(GraphQLString) },
114
- }),
132
+ deletePayloadType: { type: nn(GraphQLString) }
133
+ })
115
134
  });
116
135
  const MetaQueryType = new GraphQLObjectType({
117
136
  name: 'MetaQuery',
@@ -121,8 +140,8 @@ function createMetaSchemaType() {
121
140
  one: { type: GraphQLString },
122
141
  create: { type: GraphQLString },
123
142
  update: { type: GraphQLString },
124
- delete: { type: GraphQLString },
125
- }),
143
+ delete: { type: GraphQLString }
144
+ })
126
145
  });
127
146
  const MetaBelongsToRelationType = new GraphQLObjectType({
128
147
  name: 'MetaBelongsToRelation',
@@ -132,8 +151,8 @@ function createMetaSchemaType() {
132
151
  isUnique: { type: nn(GraphQLBoolean) },
133
152
  type: { type: GraphQLString },
134
153
  keys: { type: nnList(MetaFieldType) },
135
- references: { type: nn(MetaRefTableType) },
136
- }),
154
+ references: { type: nn(MetaRefTableType) }
155
+ })
137
156
  });
138
157
  const MetaHasRelationType = new GraphQLObjectType({
139
158
  name: 'MetaHasRelation',
@@ -143,8 +162,8 @@ function createMetaSchemaType() {
143
162
  isUnique: { type: nn(GraphQLBoolean) },
144
163
  type: { type: GraphQLString },
145
164
  keys: { type: nnList(MetaFieldType) },
146
- referencedBy: { type: nn(MetaRefTableType) },
147
- }),
165
+ referencedBy: { type: nn(MetaRefTableType) }
166
+ })
148
167
  });
149
168
  const MetaManyToManyRelationType = new GraphQLObjectType({
150
169
  name: 'MetaManyToManyRelation',
@@ -159,8 +178,8 @@ function createMetaSchemaType() {
159
178
  junctionRightKeyAttributes: { type: nnList(MetaFieldType) },
160
179
  leftKeyAttributes: { type: nnList(MetaFieldType) },
161
180
  rightKeyAttributes: { type: nnList(MetaFieldType) },
162
- rightTable: { type: nn(MetaRefTableType) },
163
- }),
181
+ rightTable: { type: nn(MetaRefTableType) }
182
+ })
164
183
  });
165
184
  const MetaRelationsType = new GraphQLObjectType({
166
185
  name: 'MetaRelations',
@@ -170,16 +189,16 @@ function createMetaSchemaType() {
170
189
  has: { type: nnList(MetaHasRelationType) },
171
190
  hasOne: { type: nnList(MetaHasRelationType) },
172
191
  hasMany: { type: nnList(MetaHasRelationType) },
173
- manyToMany: { type: nnList(MetaManyToManyRelationType) },
174
- }),
192
+ manyToMany: { type: nnList(MetaManyToManyRelationType) }
193
+ })
175
194
  });
176
195
  const MetaStorageType = new GraphQLObjectType({
177
196
  name: 'MetaStorage',
178
197
  description: 'Storage metadata for a table',
179
198
  fields: () => ({
180
199
  isFilesTable: { type: nn(GraphQLBoolean), description: 'Whether this table is a storage files table' },
181
- isBucketsTable: { type: nn(GraphQLBoolean), description: 'Whether this table is a storage buckets table' },
182
- }),
200
+ isBucketsTable: { type: nn(GraphQLBoolean), description: 'Whether this table is a storage buckets table' }
201
+ })
183
202
  });
184
203
  const MetaSearchConfigType = new GraphQLObjectType({
185
204
  name: 'MetaSearchConfig',
@@ -190,20 +209,20 @@ function createMetaSchemaType() {
190
209
  description: 'JSON-encoded per-adapter score weights',
191
210
  resolve(source) {
192
211
  return source.weights ? JSON.stringify(source.weights) : null;
193
- },
212
+ }
194
213
  },
195
214
  boostRecent: { type: nn(GraphQLBoolean), description: 'Whether recency boosting is enabled' },
196
215
  boostRecencyField: { type: GraphQLString, description: 'Field used for recency decay' },
197
- boostRecencyDecay: { type: GraphQLFloat, description: 'Exponential decay factor per day' },
198
- }),
216
+ boostRecencyDecay: { type: GraphQLFloat, description: 'Exponential decay factor per day' }
217
+ })
199
218
  });
200
219
  const MetaSearchColumnType = new GraphQLObjectType({
201
220
  name: 'MetaSearchColumn',
202
221
  description: 'A searchable column with its algorithm',
203
222
  fields: () => ({
204
223
  name: { type: nn(GraphQLString), description: 'Column name (camelCase)' },
205
- algorithm: { type: nn(GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' },
206
- }),
224
+ algorithm: { type: nn(GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' }
225
+ })
207
226
  });
208
227
  const MetaSearchType = new GraphQLObjectType({
209
228
  name: 'MetaSearch',
@@ -212,31 +231,42 @@ function createMetaSchemaType() {
212
231
  algorithms: { type: nnList(GraphQLString), description: 'Active search algorithms on this table' },
213
232
  columns: { type: nnList(MetaSearchColumnType), description: 'Searchable columns with their algorithm' },
214
233
  hasUnifiedSearch: { type: nn(GraphQLBoolean), description: 'Whether unifiedSearch composite filter is available' },
215
- config: { type: MetaSearchConfigType, description: 'Per-table search configuration' },
216
- }),
234
+ config: { type: MetaSearchConfigType, description: 'Per-table search configuration' }
235
+ })
217
236
  });
218
237
  const MetaI18nFieldType = new GraphQLObjectType({
219
238
  name: 'MetaI18nField',
220
239
  description: 'A translatable field',
221
240
  fields: () => ({
222
241
  name: { type: nn(GraphQLString), description: 'GraphQL field name' },
223
- type: { type: nn(GraphQLString), description: 'PostgreSQL column type (text, citext)' },
224
- }),
242
+ type: { type: nn(GraphQLString), description: 'PostgreSQL column type (text, citext)' }
243
+ })
225
244
  });
226
245
  const MetaI18nType = new GraphQLObjectType({
227
246
  name: 'MetaI18n',
228
247
  description: 'i18n metadata for a table with @i18n tag',
229
248
  fields: () => ({
230
249
  translationTable: { type: nn(GraphQLString), description: 'Name of the translation table' },
231
- translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' },
232
- }),
250
+ translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' }
251
+ })
233
252
  });
234
253
  const MetaRealtimeType = new GraphQLObjectType({
235
254
  name: 'MetaRealtime',
236
255
  description: 'Realtime metadata for a table with @realtime tag',
237
256
  fields: () => ({
238
- subscriptionFieldName: { type: nn(GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' },
239
- }),
257
+ subscriptionFieldName: { type: nn(GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' }
258
+ })
259
+ });
260
+ const MetaScopeType = new GraphQLObjectType({
261
+ name: 'MetaScope',
262
+ description: 'Provisioning scope metadata for a table',
263
+ fields: () => ({
264
+ scope: { type: nn(GraphQLString), description: "Provisioning scope: 'platform', 'app', 'database', or an entity scope (e.g. 'org')" },
265
+ tier: { type: nn(GraphQLString), description: "Coarse bucket: 'global', 'database', or 'entity'" },
266
+ keyColumn: { type: GraphQLString, description: 'Inflected scope key column (e.g. databaseId, orgId), null for global tiers' },
267
+ entityTable: { type: GraphQLString, description: 'SQL name of the entity table for entity scopes, else null' },
268
+ source: { type: nn(GraphQLString), description: "Provenance of the scope metadata (always 'smartTag')" }
269
+ })
240
270
  });
241
271
  const MetaTableType = new GraphQLObjectType({
242
272
  name: 'MetaTable',
@@ -257,14 +287,15 @@ function createMetaSchemaType() {
257
287
  search: { type: MetaSearchType, description: 'Search metadata (null if no search configured)' },
258
288
  i18n: { type: MetaI18nType, description: 'i18n metadata (null if no @i18n tag)' },
259
289
  realtime: { type: MetaRealtimeType, description: 'Realtime metadata (null if no @realtime tag)' },
260
- }),
290
+ scope: { type: MetaScopeType, description: 'Provisioning scope metadata (null if no @scope tag)' }
291
+ })
261
292
  });
262
293
  return new GraphQLObjectType({
263
294
  name: 'MetaSchema',
264
295
  description: 'Root meta schema type',
265
296
  fields: () => ({
266
- tables: { type: nnList(MetaTableType) },
267
- }),
297
+ tables: { type: nnList(MetaTableType) }
298
+ })
268
299
  });
269
300
  }
270
301
  export function extendQueryWithMetaField(fields, tablesMeta) {
@@ -274,10 +305,10 @@ export function extendQueryWithMetaField(fields, tablesMeta) {
274
305
  description: 'Metadata about the database schema, including tables, fields, indexes, and constraints. Useful for code generation tools.',
275
306
  resolve() {
276
307
  return { tables: tablesMeta };
277
- },
308
+ }
278
309
  };
279
310
  return {
280
311
  ...fields,
281
- _meta: metaField,
312
+ _meta: metaField
282
313
  };
283
314
  }
@@ -0,0 +1,15 @@
1
+ import type { PgCodec, ScopeMeta } from './types';
2
+ /**
3
+ * Detect provisioning scope metadata for a table.
4
+ *
5
+ * The `@scope` smart tag emitted by the constructive-db generators is the sole
6
+ * source of truth. `apply_scope_fields` knows the exact scope, tier, key column,
7
+ * and entity table, and emits them via
8
+ * `metaschema.append_table_smart_tags(id, { scope, scopeTier, scopeKey, scopeEntityTable })`.
9
+ * PostGraphile surfaces these as `codec.extensions.tags.*`.
10
+ *
11
+ * There is NO inference from column names — a table without the tag returns
12
+ * `null` (the same convention as every other nullable metadata block). This
13
+ * avoids guessing key columns, whose names are chosen freely by each generator.
14
+ */
15
+ export declare function buildScopeMeta(codec: PgCodec, inflectAttr: (attrName: string, codec: PgCodec) => string): ScopeMeta | null;
@@ -0,0 +1,40 @@
1
+ const VALID_TIERS = new Set(['global', 'database', 'entity']);
2
+ function readString(value) {
3
+ return typeof value === 'string' && value.length > 0 ? value : null;
4
+ }
5
+ /**
6
+ * Detect provisioning scope metadata for a table.
7
+ *
8
+ * The `@scope` smart tag emitted by the constructive-db generators is the sole
9
+ * source of truth. `apply_scope_fields` knows the exact scope, tier, key column,
10
+ * and entity table, and emits them via
11
+ * `metaschema.append_table_smart_tags(id, { scope, scopeTier, scopeKey, scopeEntityTable })`.
12
+ * PostGraphile surfaces these as `codec.extensions.tags.*`.
13
+ *
14
+ * There is NO inference from column names — a table without the tag returns
15
+ * `null` (the same convention as every other nullable metadata block). This
16
+ * avoids guessing key columns, whose names are chosen freely by each generator.
17
+ */
18
+ export function buildScopeMeta(codec, inflectAttr) {
19
+ const tags = codec.extensions?.tags;
20
+ const scope = readString(tags?.scope);
21
+ if (!scope)
22
+ return null;
23
+ const rawTier = readString(tags.scopeTier);
24
+ const tier = rawTier && VALID_TIERS.has(rawTier)
25
+ ? rawTier
26
+ : null;
27
+ if (!tier) {
28
+ throw new Error(`@scope tag on codec "${codec.name}" is missing a valid scopeTier ` +
29
+ `(expected one of global|database|entity, got ${JSON.stringify(rawTier)})`);
30
+ }
31
+ const keyColumn = readString(tags.scopeKey);
32
+ const entityTable = readString(tags.scopeEntityTable);
33
+ return {
34
+ scope,
35
+ tier,
36
+ keyColumn: keyColumn ? inflectAttr(keyColumn, codec) : null,
37
+ entityTable,
38
+ source: 'smartTag'
39
+ };
40
+ }
@@ -1,10 +1,11 @@
1
- import { buildForeignKeyConstraints, buildIndexes, buildPrimaryKey, buildUniqueConstraints, } from './constraint-meta-builders';
1
+ import { buildForeignKeyConstraints, buildIndexes, buildPrimaryKey, buildUniqueConstraints } from './constraint-meta-builders';
2
2
  import { buildInflectionMeta, buildQueryMeta, resolveTableType } from './name-meta-builders';
3
- import { buildBelongsToRelations, buildManyToManyRelations, buildReverseRelations, } from './relation-meta-builders';
4
- import { buildStorageMeta, buildSearchMeta, buildI18nMeta, buildRealtimeMeta } from './storage-search-meta-builders';
3
+ import { buildBelongsToRelations, buildManyToManyRelations, buildReverseRelations } from './relation-meta-builders';
4
+ import { buildScopeMeta } from './scope-meta-builders';
5
+ import { buildI18nMeta, buildRealtimeMeta, buildSearchMeta, buildStorageMeta } from './storage-search-meta-builders';
6
+ import { createBuildContext } from './table-meta-context';
7
+ import { getConfiguredSchemas, getRelations, getSchemaName, getUniques, isTableResource } from './table-resource-utils';
5
8
  import { buildFieldMeta } from './type-mappings';
6
- import { createBuildContext, } from './table-meta-context';
7
- import { getConfiguredSchemas, getRelations, getSchemaName, getUniques, isTableResource, } from './table-resource-utils';
8
9
  function buildTableMeta(resource, schemaName, context) {
9
10
  const codec = resource.codec;
10
11
  const attributes = codec.attributes;
@@ -27,7 +28,7 @@ function buildTableMeta(resource, schemaName, context) {
27
28
  }
28
29
  const fields = Object.entries(attributes).map(([attrName, attr]) => buildFieldMeta(context.inflectAttr(attrName, codec), attr, context.build, {
29
30
  isPrimaryKey: pkAttrNames.has(attrName),
30
- isForeignKey: fkAttrNames.has(attrName),
31
+ isForeignKey: fkAttrNames.has(attrName)
31
32
  }));
32
33
  const indexes = buildIndexes(codec, attributes, uniques, context);
33
34
  const primaryKey = buildPrimaryKey(codec, attributes, uniques, context);
@@ -36,7 +37,7 @@ function buildTableMeta(resource, schemaName, context) {
36
37
  const constraints = {
37
38
  primaryKey,
38
39
  unique: uniqueConstraints,
39
- foreignKey: foreignKeyConstraints,
40
+ foreignKey: foreignKeyConstraints
40
41
  };
41
42
  const belongsTo = buildBelongsToRelations(codec, attributes, uniques, relations, context);
42
43
  const { hasOne, hasMany } = buildReverseRelations(codec, attributes, relations, context);
@@ -46,13 +47,14 @@ function buildTableMeta(resource, schemaName, context) {
46
47
  has: [...hasOne, ...hasMany],
47
48
  hasOne,
48
49
  hasMany,
49
- manyToMany,
50
+ manyToMany
50
51
  };
51
52
  const tableType = resolveTableType(context.build, codec);
52
53
  const storage = buildStorageMeta(codec);
53
54
  const search = buildSearchMeta(codec, context.build, context.inflectAttr);
54
55
  const i18n = buildI18nMeta(codec, context.build, context.inflectAttr);
55
56
  const realtime = buildRealtimeMeta(codec, context.build);
57
+ const scope = buildScopeMeta(codec, context.inflectAttr);
56
58
  return {
57
59
  name: tableType,
58
60
  schemaName,
@@ -69,6 +71,7 @@ function buildTableMeta(resource, schemaName, context) {
69
71
  search,
70
72
  i18n,
71
73
  realtime,
74
+ scope
72
75
  };
73
76
  }
74
77
  export function collectTablesMeta(build) {
@@ -1,3 +1,4 @@
1
+ import { buildScalarEncoding } from './encoding-meta-builders';
1
2
  const PG_TO_GQL_TYPE = {
2
3
  text: 'String',
3
4
  varchar: 'String',
@@ -87,6 +88,7 @@ export function buildFieldMeta(name, attr, build, options) {
87
88
  isNotNull,
88
89
  hasDefault,
89
90
  subtype,
91
+ encoding: buildScalarEncoding(attr),
90
92
  },
91
93
  isNotNull,
92
94
  hasDefault,
@@ -14,6 +14,21 @@ export interface TableMeta {
14
14
  search: SearchMeta | null;
15
15
  i18n: I18nMeta | null;
16
16
  realtime: RealtimeMeta | null;
17
+ scope: ScopeMeta | null;
18
+ }
19
+ /** Coarse provisioning tier a table's scope belongs to. */
20
+ export type ScopeTier = 'global' | 'database' | 'entity';
21
+ export interface ScopeMeta {
22
+ /** The provisioning scope, e.g. 'platform', 'app', 'database', 'org', 'user' */
23
+ scope: string;
24
+ /** Coarse bucket: global (platform/app), database, or entity */
25
+ tier: ScopeTier;
26
+ /** Inflected scope key column (e.g. 'databaseId', 'orgId'), or null for global tiers */
27
+ keyColumn: string | null;
28
+ /** SQL name of the entity table for entity scopes, else null */
29
+ entityTable: string | null;
30
+ /** Provenance of the scope metadata. Always the @scope smart tag. */
31
+ source: 'smartTag';
17
32
  }
18
33
  export interface StorageMeta {
19
34
  /** Whether this table is tagged as a storage files table */
@@ -86,6 +101,33 @@ export interface TypeMeta {
86
101
  isNotNull?: boolean;
87
102
  hasDefault?: boolean;
88
103
  subtype?: string | null;
104
+ /**
105
+ * How the client must serialize/parse this scalar. `null` for plain scalars
106
+ * (String/Int/Float/Boolean/JSON/enum) whose wire format is obvious from
107
+ * `gqlType` — populated only for scalars introspection can't fully describe.
108
+ */
109
+ encoding?: ScalarEncodingMeta | null;
110
+ }
111
+ /** The machine kind a scalar marshals as on the client. */
112
+ export type ScalarEncodingKind = 'bigint' | 'datetime' | 'date' | 'time' | 'interval' | 'uuid' | 'geojson' | 'point' | 'inet' | 'ltree' | 'vector' | 'bytea' | 'composite';
113
+ export interface ScalarEncodingMeta {
114
+ /** Deterministic kind derived from the underlying (unwrapped) codec. */
115
+ kind: ScalarEncodingKind;
116
+ /** For 'vector': element scalar (e.g. 'float'), else null. */
117
+ elementType?: string | null;
118
+ /** For 'vector': declared length from typmod, else null. */
119
+ dimensions?: number | null;
120
+ /** For 'geojson': geometry subtype (Point/LineString/Polygon/…), else null. */
121
+ geometrySubtype?: string | null;
122
+ /** For 'geojson': spatial reference id, else null. */
123
+ srid?: number | null;
124
+ /** For 'ltree': values are dot-separated path strings ('a.b.c'). */
125
+ dotPath?: boolean;
126
+ /** For 'composite': nested attribute encodings. */
127
+ fields?: {
128
+ name: string;
129
+ type: TypeMeta;
130
+ }[] | null;
89
131
  }
90
132
  export interface IndexMeta {
91
133
  name: string;
@@ -183,6 +225,7 @@ export interface PgCodec {
183
225
  name: string;
184
226
  attributes?: Record<string, PgAttribute>;
185
227
  arrayOfCodec?: PgCodec | null;
228
+ domainOfCodec?: PgCodec | null;
186
229
  isAnonymous?: boolean;
187
230
  extensions?: {
188
231
  pg?: {
@@ -192,6 +235,8 @@ export interface PgCodec {
192
235
  }
193
236
  export interface PgAttributeExtensions extends Record<string, unknown> {
194
237
  geometrySubtype?: string | null;
238
+ geometrySrid?: number | null;
239
+ vectorDimensions?: number | null;
195
240
  }
196
241
  export interface PgAttribute {
197
242
  codec?: PgCodec | null;
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import type { GraphileConfig } from 'graphile-config';
8
8
  import { cachedTablesMeta } from './meta-schema/cache';
9
+ import { buildScalarEncoding } from './meta-schema/encoding-meta-builders';
9
10
  import { MetaSchemaPlugin } from './meta-schema/plugin';
10
11
  import { buildFieldMeta, pgTypeToGqlType } from './meta-schema/type-mappings';
11
12
  export { MetaSchemaPlugin };
@@ -16,4 +17,6 @@ export { pgTypeToGqlType as _pgTypeToGqlType };
16
17
  export { buildFieldMeta as _buildFieldMeta };
17
18
  /** @internal Exported for testing only */
18
19
  export { cachedTablesMeta as _cachedTablesMeta };
20
+ /** @internal Exported for testing only */
21
+ export { buildScalarEncoding as _buildScalarEncoding };
19
22
  export default MetaSchemaPlugin;
@@ -5,6 +5,7 @@
5
5
  * constraints, indexes, and relations for code generation tooling.
6
6
  */
7
7
  import { cachedTablesMeta } from './meta-schema/cache';
8
+ import { buildScalarEncoding } from './meta-schema/encoding-meta-builders';
8
9
  import { MetaSchemaPlugin } from './meta-schema/plugin';
9
10
  import { buildFieldMeta, pgTypeToGqlType } from './meta-schema/type-mappings';
10
11
  export { MetaSchemaPlugin };
@@ -17,4 +18,6 @@ export { pgTypeToGqlType as _pgTypeToGqlType };
17
18
  export { buildFieldMeta as _buildFieldMeta };
18
19
  /** @internal Exported for testing only */
19
20
  export { cachedTablesMeta as _cachedTablesMeta };
21
+ /** @internal Exported for testing only */
22
+ export { buildScalarEncoding as _buildScalarEncoding };
20
23
  export default MetaSchemaPlugin;