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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-settings",
3
- "version": "5.15.1",
3
+ "version": "5.17.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "graphile settings",
6
6
  "main": "index.js",
@@ -31,16 +31,16 @@
31
31
  "dependencies": {
32
32
  "@aws-sdk/client-s3": "^3.1052.0",
33
33
  "@constructive-io/bucket-provisioner": "^0.14.0",
34
- "@constructive-io/graphql-env": "^3.16.1",
35
- "@constructive-io/graphql-types": "^3.14.1",
36
- "@constructive-io/s3-streamer": "^2.29.1",
34
+ "@constructive-io/graphql-env": "^3.16.3",
35
+ "@constructive-io/graphql-types": "^3.15.0",
36
+ "@constructive-io/s3-streamer": "^2.29.2",
37
37
  "@constructive-io/s3-utils": "^2.20.0",
38
38
  "@constructive-io/upload-names": "^2.19.0",
39
39
  "@dataplan/json": "1.0.0",
40
40
  "@dataplan/pg": "1.0.3",
41
41
  "@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
42
42
  "@pgpmjs/logger": "^2.14.0",
43
- "@pgpmjs/types": "^2.35.0",
43
+ "@pgpmjs/types": "^2.35.1",
44
44
  "@pgsql/quotes": "^17.1.0",
45
45
  "cors": "^2.8.6",
46
46
  "express": "^5.2.1",
@@ -49,17 +49,17 @@
49
49
  "graphile-bucket-provisioner-plugin": "0.14.0",
50
50
  "graphile-build": "5.0.2",
51
51
  "graphile-build-pg": "5.0.2",
52
- "graphile-bulk-mutations": "^0.9.1",
52
+ "graphile-bulk-mutations": "^0.9.3",
53
53
  "graphile-config": "1.0.1",
54
- "graphile-connection-filter": "^1.16.1",
55
- "graphile-i18n": "^1.6.1",
56
- "graphile-llm": "^0.19.1",
57
- "graphile-ltree": "^1.14.1",
58
- "graphile-pg-aggregates": "^1.10.1",
59
- "graphile-postgis": "^2.23.1",
54
+ "graphile-connection-filter": "^1.16.3",
55
+ "graphile-i18n": "^1.6.3",
56
+ "graphile-llm": "^0.19.3",
57
+ "graphile-ltree": "^1.14.3",
58
+ "graphile-pg-aggregates": "^1.10.3",
59
+ "graphile-postgis": "^2.23.3",
60
60
  "graphile-presigned-url-plugin": "^0.22.1",
61
61
  "graphile-realtime-subscriptions": "^0.10.0",
62
- "graphile-search": "^1.21.1",
62
+ "graphile-search": "^1.21.3",
63
63
  "graphile-upload-plugin": "^2.14.0",
64
64
  "graphile-utils": "5.0.1",
65
65
  "graphql": "16.13.0",
@@ -77,7 +77,7 @@
77
77
  "@types/express": "^5.0.6",
78
78
  "@types/pg": "^8.20.0",
79
79
  "@types/request-ip": "^0.0.41",
80
- "graphile-test": "^4.21.1",
80
+ "graphile-test": "^4.21.3",
81
81
  "makage": "^0.3.0",
82
82
  "nodemon": "^3.1.14",
83
83
  "ts-node": "^10.9.2"
@@ -89,5 +89,5 @@
89
89
  "constructive",
90
90
  "graphql"
91
91
  ],
92
- "gitHead": "dd2accb683fe20c7278a0d58fd36a04a3b07ac63"
92
+ "gitHead": "b0843e23070cf4e28a119b06e4ebd1828be1ebde"
93
93
  }
@@ -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,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildScalarEncoding = buildScalarEncoding;
4
+ /**
5
+ * Scalar encoding contracts — the one piece of field metadata standard GraphQL
6
+ * introspection can't provide.
7
+ *
8
+ * Introspection tells a client a field's scalar *name* (`GeoJSON`, `LTree`,
9
+ * `Interval`, `BigInt`, `vector`, …) but not how to serialize/parse it or its
10
+ * structural parameters (geometry subtype/SRID, vector dimensions, ltree
11
+ * dot-path semantics). Without that, generated clients treat these as opaque
12
+ * strings / `any` and mis-encode inputs or fail to parse outputs.
13
+ *
14
+ * `kind` is derived deterministically from the *underlying* codec (array and
15
+ * domain wrappers unwrapped first, first match wins) so exactly one kind is
16
+ * ever assigned and nothing can clobber it. Structural extras (subtype, SRID,
17
+ * dimensions) are read verbatim from attribute extensions — never guessed, and
18
+ * `null` when absent.
19
+ */
20
+ /** pg type name → encoding kind. Plain scalars are intentionally absent → null. */
21
+ const KIND_BY_PG_TYPE = {
22
+ int8: 'bigint',
23
+ bigint: 'bigint',
24
+ numeric: 'bigint',
25
+ timestamptz: 'datetime',
26
+ timestamp: 'datetime',
27
+ date: 'date',
28
+ time: 'time',
29
+ timetz: 'time',
30
+ interval: 'interval',
31
+ uuid: 'uuid',
32
+ geometry: 'geojson',
33
+ geography: 'geojson',
34
+ point: 'point',
35
+ inet: 'inet',
36
+ cidr: 'inet',
37
+ ltree: 'ltree',
38
+ vector: 'vector',
39
+ bytea: 'bytea'
40
+ };
41
+ /** Unwrap array-of and domain-of wrappers to the underlying scalar/record codec. */
42
+ function underlyingCodec(codec) {
43
+ let current = codec ?? null;
44
+ const seen = new Set();
45
+ while (current && !seen.has(current) && (current.arrayOfCodec || current.domainOfCodec)) {
46
+ seen.add(current);
47
+ current = current.arrayOfCodec || current.domainOfCodec || null;
48
+ }
49
+ return current;
50
+ }
51
+ /** True when a codec is a composite/record type (has named attributes). */
52
+ function isCompositeCodec(codec) {
53
+ return !!codec?.attributes && Object.keys(codec.attributes).length > 0;
54
+ }
55
+ function buildScalarEncoding(attr) {
56
+ const codec = underlyingCodec(attr?.codec);
57
+ if (!codec)
58
+ return null;
59
+ const ext = attr?.extensions ?? undefined;
60
+ const kind = KIND_BY_PG_TYPE[codec.name];
61
+ if (!kind) {
62
+ // Composite/record types are the only non-mapped scalars worth describing;
63
+ // everything else (text, int, bool, json, enum, …) is fully covered by gqlType.
64
+ if (isCompositeCodec(codec)) {
65
+ return { kind: 'composite', fields: null };
66
+ }
67
+ return null;
68
+ }
69
+ switch (kind) {
70
+ case 'geojson':
71
+ return {
72
+ kind,
73
+ geometrySubtype: typeof ext?.geometrySubtype === 'string' ? ext.geometrySubtype : null,
74
+ srid: typeof ext?.geometrySrid === 'number' ? ext.geometrySrid : null
75
+ };
76
+ case 'vector':
77
+ return {
78
+ kind,
79
+ elementType: 'float',
80
+ dimensions: typeof ext?.vectorDimensions === 'number' ? ext.vectorDimensions : null
81
+ };
82
+ case 'ltree':
83
+ return { kind, dotPath: true };
84
+ default:
85
+ return { kind };
86
+ }
87
+ }
@@ -9,6 +9,21 @@ function nnList(type) {
9
9
  return nn(new graphql_1.GraphQLList(nn(type)));
10
10
  }
11
11
  function createMetaSchemaType() {
12
+ const MetaScalarEncodingType = new graphql_1.GraphQLObjectType({
13
+ name: 'MetaScalarEncoding',
14
+ 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.',
15
+ fields: () => ({
16
+ kind: {
17
+ type: nn(graphql_1.GraphQLString),
18
+ description: "Machine kind: bigint, datetime, date, time, interval, uuid, geojson, point, inet, ltree, vector, bytea, or composite."
19
+ },
20
+ elementType: { type: graphql_1.GraphQLString, description: "For 'vector': element scalar (e.g. 'float')." },
21
+ dimensions: { type: graphql_1.GraphQLInt, description: "For 'vector': declared length, else null." },
22
+ geometrySubtype: { type: graphql_1.GraphQLString, description: "For 'geojson': Point/LineString/Polygon/…, else null." },
23
+ srid: { type: graphql_1.GraphQLInt, description: "For 'geojson': spatial reference id, else null." },
24
+ dotPath: { type: graphql_1.GraphQLBoolean, description: "For 'ltree': values are dot-separated path strings." }
25
+ })
26
+ });
12
27
  const MetaTypeType = new graphql_1.GraphQLObjectType({
13
28
  name: 'MetaType',
14
29
  description: 'Information about a PostgreSQL type',
@@ -19,15 +34,19 @@ function createMetaSchemaType() {
19
34
  isNotNull: { type: graphql_1.GraphQLBoolean },
20
35
  hasDefault: { type: graphql_1.GraphQLBoolean },
21
36
  subtype: { type: graphql_1.GraphQLString },
22
- }),
37
+ encoding: {
38
+ type: MetaScalarEncodingType,
39
+ description: 'Scalar serialization contract (null for plain scalars)'
40
+ }
41
+ })
23
42
  });
24
43
  const MetaEnumType = new graphql_1.GraphQLObjectType({
25
44
  name: 'MetaEnum',
26
45
  description: 'Information about a PostgreSQL enum type',
27
46
  fields: () => ({
28
47
  name: { type: nn(graphql_1.GraphQLString), description: 'The PostgreSQL enum type name' },
29
- values: { type: nnList(graphql_1.GraphQLString), description: 'Allowed values for this enum' },
30
- }),
48
+ values: { type: nnList(graphql_1.GraphQLString), description: 'Allowed values for this enum' }
49
+ })
31
50
  });
32
51
  const MetaFieldType = new graphql_1.GraphQLObjectType({
33
52
  name: 'MetaField',
@@ -40,8 +59,8 @@ function createMetaSchemaType() {
40
59
  isPrimaryKey: { type: nn(graphql_1.GraphQLBoolean) },
41
60
  isForeignKey: { type: nn(graphql_1.GraphQLBoolean) },
42
61
  description: { type: graphql_1.GraphQLString },
43
- enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' },
44
- }),
62
+ enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }
63
+ })
45
64
  });
46
65
  const MetaIndexType = new graphql_1.GraphQLObjectType({
47
66
  name: 'MetaIndex',
@@ -51,31 +70,31 @@ function createMetaSchemaType() {
51
70
  isUnique: { type: nn(graphql_1.GraphQLBoolean) },
52
71
  isPrimary: { type: nn(graphql_1.GraphQLBoolean) },
53
72
  columns: { type: nnList(graphql_1.GraphQLString) },
54
- fields: { type: new graphql_1.GraphQLList(nn(MetaFieldType)) },
55
- }),
73
+ fields: { type: new graphql_1.GraphQLList(nn(MetaFieldType)) }
74
+ })
56
75
  });
57
76
  const MetaPrimaryKeyConstraintType = new graphql_1.GraphQLObjectType({
58
77
  name: 'MetaPrimaryKeyConstraint',
59
78
  description: 'Information about a primary key constraint',
60
79
  fields: () => ({
61
80
  name: { type: nn(graphql_1.GraphQLString) },
62
- fields: { type: nnList(MetaFieldType) },
63
- }),
81
+ fields: { type: nnList(MetaFieldType) }
82
+ })
64
83
  });
65
84
  const MetaUniqueConstraintType = new graphql_1.GraphQLObjectType({
66
85
  name: 'MetaUniqueConstraint',
67
86
  description: 'Information about a unique constraint',
68
87
  fields: () => ({
69
88
  name: { type: nn(graphql_1.GraphQLString) },
70
- fields: { type: nnList(MetaFieldType) },
71
- }),
89
+ fields: { type: nnList(MetaFieldType) }
90
+ })
72
91
  });
73
92
  const MetaRefTableType = new graphql_1.GraphQLObjectType({
74
93
  name: 'MetaRefTable',
75
94
  description: 'Reference to a related table',
76
95
  fields: () => ({
77
- name: { type: nn(graphql_1.GraphQLString) },
78
- }),
96
+ name: { type: nn(graphql_1.GraphQLString) }
97
+ })
79
98
  });
80
99
  const MetaForeignKeyConstraintType = new graphql_1.GraphQLObjectType({
81
100
  name: 'MetaForeignKeyConstraint',
@@ -86,8 +105,8 @@ function createMetaSchemaType() {
86
105
  referencedTable: { type: nn(graphql_1.GraphQLString) },
87
106
  referencedFields: { type: nnList(graphql_1.GraphQLString) },
88
107
  refFields: { type: new graphql_1.GraphQLList(nn(MetaFieldType)) },
89
- refTable: { type: MetaRefTableType },
90
- }),
108
+ refTable: { type: MetaRefTableType }
109
+ })
91
110
  });
92
111
  const MetaConstraintsType = new graphql_1.GraphQLObjectType({
93
112
  name: 'MetaConstraints',
@@ -95,8 +114,8 @@ function createMetaSchemaType() {
95
114
  fields: () => ({
96
115
  primaryKey: { type: MetaPrimaryKeyConstraintType },
97
116
  unique: { type: nnList(MetaUniqueConstraintType) },
98
- foreignKey: { type: nnList(MetaForeignKeyConstraintType) },
99
- }),
117
+ foreignKey: { type: nnList(MetaForeignKeyConstraintType) }
118
+ })
100
119
  });
101
120
  const MetaInflectionType = new graphql_1.GraphQLObjectType({
102
121
  name: 'MetaInflection',
@@ -113,8 +132,8 @@ function createMetaSchemaType() {
113
132
  createInputType: { type: nn(graphql_1.GraphQLString) },
114
133
  createPayloadType: { type: nn(graphql_1.GraphQLString) },
115
134
  updatePayloadType: { type: graphql_1.GraphQLString },
116
- deletePayloadType: { type: nn(graphql_1.GraphQLString) },
117
- }),
135
+ deletePayloadType: { type: nn(graphql_1.GraphQLString) }
136
+ })
118
137
  });
119
138
  const MetaQueryType = new graphql_1.GraphQLObjectType({
120
139
  name: 'MetaQuery',
@@ -124,8 +143,8 @@ function createMetaSchemaType() {
124
143
  one: { type: graphql_1.GraphQLString },
125
144
  create: { type: graphql_1.GraphQLString },
126
145
  update: { type: graphql_1.GraphQLString },
127
- delete: { type: graphql_1.GraphQLString },
128
- }),
146
+ delete: { type: graphql_1.GraphQLString }
147
+ })
129
148
  });
130
149
  const MetaBelongsToRelationType = new graphql_1.GraphQLObjectType({
131
150
  name: 'MetaBelongsToRelation',
@@ -135,8 +154,8 @@ function createMetaSchemaType() {
135
154
  isUnique: { type: nn(graphql_1.GraphQLBoolean) },
136
155
  type: { type: graphql_1.GraphQLString },
137
156
  keys: { type: nnList(MetaFieldType) },
138
- references: { type: nn(MetaRefTableType) },
139
- }),
157
+ references: { type: nn(MetaRefTableType) }
158
+ })
140
159
  });
141
160
  const MetaHasRelationType = new graphql_1.GraphQLObjectType({
142
161
  name: 'MetaHasRelation',
@@ -146,8 +165,8 @@ function createMetaSchemaType() {
146
165
  isUnique: { type: nn(graphql_1.GraphQLBoolean) },
147
166
  type: { type: graphql_1.GraphQLString },
148
167
  keys: { type: nnList(MetaFieldType) },
149
- referencedBy: { type: nn(MetaRefTableType) },
150
- }),
168
+ referencedBy: { type: nn(MetaRefTableType) }
169
+ })
151
170
  });
152
171
  const MetaManyToManyRelationType = new graphql_1.GraphQLObjectType({
153
172
  name: 'MetaManyToManyRelation',
@@ -162,8 +181,8 @@ function createMetaSchemaType() {
162
181
  junctionRightKeyAttributes: { type: nnList(MetaFieldType) },
163
182
  leftKeyAttributes: { type: nnList(MetaFieldType) },
164
183
  rightKeyAttributes: { type: nnList(MetaFieldType) },
165
- rightTable: { type: nn(MetaRefTableType) },
166
- }),
184
+ rightTable: { type: nn(MetaRefTableType) }
185
+ })
167
186
  });
168
187
  const MetaRelationsType = new graphql_1.GraphQLObjectType({
169
188
  name: 'MetaRelations',
@@ -173,16 +192,16 @@ function createMetaSchemaType() {
173
192
  has: { type: nnList(MetaHasRelationType) },
174
193
  hasOne: { type: nnList(MetaHasRelationType) },
175
194
  hasMany: { type: nnList(MetaHasRelationType) },
176
- manyToMany: { type: nnList(MetaManyToManyRelationType) },
177
- }),
195
+ manyToMany: { type: nnList(MetaManyToManyRelationType) }
196
+ })
178
197
  });
179
198
  const MetaStorageType = new graphql_1.GraphQLObjectType({
180
199
  name: 'MetaStorage',
181
200
  description: 'Storage metadata for a table',
182
201
  fields: () => ({
183
202
  isFilesTable: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether this table is a storage files table' },
184
- isBucketsTable: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether this table is a storage buckets table' },
185
- }),
203
+ isBucketsTable: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether this table is a storage buckets table' }
204
+ })
186
205
  });
187
206
  const MetaSearchConfigType = new graphql_1.GraphQLObjectType({
188
207
  name: 'MetaSearchConfig',
@@ -193,20 +212,20 @@ function createMetaSchemaType() {
193
212
  description: 'JSON-encoded per-adapter score weights',
194
213
  resolve(source) {
195
214
  return source.weights ? JSON.stringify(source.weights) : null;
196
- },
215
+ }
197
216
  },
198
217
  boostRecent: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether recency boosting is enabled' },
199
218
  boostRecencyField: { type: graphql_1.GraphQLString, description: 'Field used for recency decay' },
200
- boostRecencyDecay: { type: graphql_1.GraphQLFloat, description: 'Exponential decay factor per day' },
201
- }),
219
+ boostRecencyDecay: { type: graphql_1.GraphQLFloat, description: 'Exponential decay factor per day' }
220
+ })
202
221
  });
203
222
  const MetaSearchColumnType = new graphql_1.GraphQLObjectType({
204
223
  name: 'MetaSearchColumn',
205
224
  description: 'A searchable column with its algorithm',
206
225
  fields: () => ({
207
226
  name: { type: nn(graphql_1.GraphQLString), description: 'Column name (camelCase)' },
208
- algorithm: { type: nn(graphql_1.GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' },
209
- }),
227
+ algorithm: { type: nn(graphql_1.GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' }
228
+ })
210
229
  });
211
230
  const MetaSearchType = new graphql_1.GraphQLObjectType({
212
231
  name: 'MetaSearch',
@@ -215,31 +234,42 @@ function createMetaSchemaType() {
215
234
  algorithms: { type: nnList(graphql_1.GraphQLString), description: 'Active search algorithms on this table' },
216
235
  columns: { type: nnList(MetaSearchColumnType), description: 'Searchable columns with their algorithm' },
217
236
  hasUnifiedSearch: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether unifiedSearch composite filter is available' },
218
- config: { type: MetaSearchConfigType, description: 'Per-table search configuration' },
219
- }),
237
+ config: { type: MetaSearchConfigType, description: 'Per-table search configuration' }
238
+ })
220
239
  });
221
240
  const MetaI18nFieldType = new graphql_1.GraphQLObjectType({
222
241
  name: 'MetaI18nField',
223
242
  description: 'A translatable field',
224
243
  fields: () => ({
225
244
  name: { type: nn(graphql_1.GraphQLString), description: 'GraphQL field name' },
226
- type: { type: nn(graphql_1.GraphQLString), description: 'PostgreSQL column type (text, citext)' },
227
- }),
245
+ type: { type: nn(graphql_1.GraphQLString), description: 'PostgreSQL column type (text, citext)' }
246
+ })
228
247
  });
229
248
  const MetaI18nType = new graphql_1.GraphQLObjectType({
230
249
  name: 'MetaI18n',
231
250
  description: 'i18n metadata for a table with @i18n tag',
232
251
  fields: () => ({
233
252
  translationTable: { type: nn(graphql_1.GraphQLString), description: 'Name of the translation table' },
234
- translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' },
235
- }),
253
+ translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' }
254
+ })
236
255
  });
237
256
  const MetaRealtimeType = new graphql_1.GraphQLObjectType({
238
257
  name: 'MetaRealtime',
239
258
  description: 'Realtime metadata for a table with @realtime tag',
240
259
  fields: () => ({
241
- subscriptionFieldName: { type: nn(graphql_1.GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' },
242
- }),
260
+ subscriptionFieldName: { type: nn(graphql_1.GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' }
261
+ })
262
+ });
263
+ const MetaScopeType = new graphql_1.GraphQLObjectType({
264
+ name: 'MetaScope',
265
+ description: 'Provisioning scope metadata for a table',
266
+ fields: () => ({
267
+ scope: { type: nn(graphql_1.GraphQLString), description: "Provisioning scope: 'platform', 'app', 'database', or an entity scope (e.g. 'org')" },
268
+ tier: { type: nn(graphql_1.GraphQLString), description: "Coarse bucket: 'global', 'database', or 'entity'" },
269
+ keyColumn: { type: graphql_1.GraphQLString, description: 'Inflected scope key column (e.g. databaseId, orgId), null for global tiers' },
270
+ entityTable: { type: graphql_1.GraphQLString, description: 'SQL name of the entity table for entity scopes, else null' },
271
+ source: { type: nn(graphql_1.GraphQLString), description: "Provenance of the scope metadata (always 'smartTag')" }
272
+ })
243
273
  });
244
274
  const MetaTableType = new graphql_1.GraphQLObjectType({
245
275
  name: 'MetaTable',
@@ -260,14 +290,15 @@ function createMetaSchemaType() {
260
290
  search: { type: MetaSearchType, description: 'Search metadata (null if no search configured)' },
261
291
  i18n: { type: MetaI18nType, description: 'i18n metadata (null if no @i18n tag)' },
262
292
  realtime: { type: MetaRealtimeType, description: 'Realtime metadata (null if no @realtime tag)' },
263
- }),
293
+ scope: { type: MetaScopeType, description: 'Provisioning scope metadata (null if no @scope tag)' }
294
+ })
264
295
  });
265
296
  return new graphql_1.GraphQLObjectType({
266
297
  name: 'MetaSchema',
267
298
  description: 'Root meta schema type',
268
299
  fields: () => ({
269
- tables: { type: nnList(MetaTableType) },
270
- }),
300
+ tables: { type: nnList(MetaTableType) }
301
+ })
271
302
  });
272
303
  }
273
304
  function extendQueryWithMetaField(fields, tablesMeta) {
@@ -277,10 +308,10 @@ function extendQueryWithMetaField(fields, tablesMeta) {
277
308
  description: 'Metadata about the database schema, including tables, fields, indexes, and constraints. Useful for code generation tools.',
278
309
  resolve() {
279
310
  return { tables: tablesMeta };
280
- },
311
+ }
281
312
  };
282
313
  return {
283
314
  ...fields,
284
- _meta: metaField,
315
+ _meta: metaField
285
316
  };
286
317
  }
@@ -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,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildScopeMeta = buildScopeMeta;
4
+ const VALID_TIERS = new Set(['global', 'database', 'entity']);
5
+ function readString(value) {
6
+ return typeof value === 'string' && value.length > 0 ? value : null;
7
+ }
8
+ /**
9
+ * Detect provisioning scope metadata for a table.
10
+ *
11
+ * The `@scope` smart tag emitted by the constructive-db generators is the sole
12
+ * source of truth. `apply_scope_fields` knows the exact scope, tier, key column,
13
+ * and entity table, and emits them via
14
+ * `metaschema.append_table_smart_tags(id, { scope, scopeTier, scopeKey, scopeEntityTable })`.
15
+ * PostGraphile surfaces these as `codec.extensions.tags.*`.
16
+ *
17
+ * There is NO inference from column names — a table without the tag returns
18
+ * `null` (the same convention as every other nullable metadata block). This
19
+ * avoids guessing key columns, whose names are chosen freely by each generator.
20
+ */
21
+ function buildScopeMeta(codec, inflectAttr) {
22
+ const tags = codec.extensions?.tags;
23
+ const scope = readString(tags?.scope);
24
+ if (!scope)
25
+ return null;
26
+ const rawTier = readString(tags.scopeTier);
27
+ const tier = rawTier && VALID_TIERS.has(rawTier)
28
+ ? rawTier
29
+ : null;
30
+ if (!tier) {
31
+ throw new Error(`@scope tag on codec "${codec.name}" is missing a valid scopeTier ` +
32
+ `(expected one of global|database|entity, got ${JSON.stringify(rawTier)})`);
33
+ }
34
+ const keyColumn = readString(tags.scopeKey);
35
+ const entityTable = readString(tags.scopeEntityTable);
36
+ return {
37
+ scope,
38
+ tier,
39
+ keyColumn: keyColumn ? inflectAttr(keyColumn, codec) : null,
40
+ entityTable,
41
+ source: 'smartTag'
42
+ };
43
+ }
@@ -4,10 +4,11 @@ exports.collectTablesMeta = collectTablesMeta;
4
4
  const constraint_meta_builders_1 = require("./constraint-meta-builders");
5
5
  const name_meta_builders_1 = require("./name-meta-builders");
6
6
  const relation_meta_builders_1 = require("./relation-meta-builders");
7
+ const scope_meta_builders_1 = require("./scope-meta-builders");
7
8
  const storage_search_meta_builders_1 = require("./storage-search-meta-builders");
8
- const type_mappings_1 = require("./type-mappings");
9
9
  const table_meta_context_1 = require("./table-meta-context");
10
10
  const table_resource_utils_1 = require("./table-resource-utils");
11
+ const type_mappings_1 = require("./type-mappings");
11
12
  function buildTableMeta(resource, schemaName, context) {
12
13
  const codec = resource.codec;
13
14
  const attributes = codec.attributes;
@@ -30,7 +31,7 @@ function buildTableMeta(resource, schemaName, context) {
30
31
  }
31
32
  const fields = Object.entries(attributes).map(([attrName, attr]) => (0, type_mappings_1.buildFieldMeta)(context.inflectAttr(attrName, codec), attr, context.build, {
32
33
  isPrimaryKey: pkAttrNames.has(attrName),
33
- isForeignKey: fkAttrNames.has(attrName),
34
+ isForeignKey: fkAttrNames.has(attrName)
34
35
  }));
35
36
  const indexes = (0, constraint_meta_builders_1.buildIndexes)(codec, attributes, uniques, context);
36
37
  const primaryKey = (0, constraint_meta_builders_1.buildPrimaryKey)(codec, attributes, uniques, context);
@@ -39,7 +40,7 @@ function buildTableMeta(resource, schemaName, context) {
39
40
  const constraints = {
40
41
  primaryKey,
41
42
  unique: uniqueConstraints,
42
- foreignKey: foreignKeyConstraints,
43
+ foreignKey: foreignKeyConstraints
43
44
  };
44
45
  const belongsTo = (0, relation_meta_builders_1.buildBelongsToRelations)(codec, attributes, uniques, relations, context);
45
46
  const { hasOne, hasMany } = (0, relation_meta_builders_1.buildReverseRelations)(codec, attributes, relations, context);
@@ -49,13 +50,14 @@ function buildTableMeta(resource, schemaName, context) {
49
50
  has: [...hasOne, ...hasMany],
50
51
  hasOne,
51
52
  hasMany,
52
- manyToMany,
53
+ manyToMany
53
54
  };
54
55
  const tableType = (0, name_meta_builders_1.resolveTableType)(context.build, codec);
55
56
  const storage = (0, storage_search_meta_builders_1.buildStorageMeta)(codec);
56
57
  const search = (0, storage_search_meta_builders_1.buildSearchMeta)(codec, context.build, context.inflectAttr);
57
58
  const i18n = (0, storage_search_meta_builders_1.buildI18nMeta)(codec, context.build, context.inflectAttr);
58
59
  const realtime = (0, storage_search_meta_builders_1.buildRealtimeMeta)(codec, context.build);
60
+ const scope = (0, scope_meta_builders_1.buildScopeMeta)(codec, context.inflectAttr);
59
61
  return {
60
62
  name: tableType,
61
63
  schemaName,
@@ -72,6 +74,7 @@ function buildTableMeta(resource, schemaName, context) {
72
74
  search,
73
75
  i18n,
74
76
  realtime,
77
+ scope
75
78
  };
76
79
  }
77
80
  function collectTablesMeta(build) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.pgTypeToGqlType = pgTypeToGqlType;
4
4
  exports.buildFieldMeta = buildFieldMeta;
5
+ const encoding_meta_builders_1 = require("./encoding-meta-builders");
5
6
  const PG_TO_GQL_TYPE = {
6
7
  text: 'String',
7
8
  varchar: 'String',
@@ -91,6 +92,7 @@ function buildFieldMeta(name, attr, build, options) {
91
92
  isNotNull,
92
93
  hasDefault,
93
94
  subtype,
95
+ encoding: (0, encoding_meta_builders_1.buildScalarEncoding)(attr),
94
96
  },
95
97
  isNotNull,
96
98
  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;