graphile-settings 5.15.1 → 5.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/plugins/meta-schema/graphql-meta-field.js +63 -51
- package/esm/plugins/meta-schema/scope-meta-builders.d.ts +15 -0
- package/esm/plugins/meta-schema/scope-meta-builders.js +40 -0
- package/esm/plugins/meta-schema/table-meta-builder.js +11 -8
- package/esm/plugins/meta-schema/types.d.ts +15 -0
- package/package.json +15 -15
- package/plugins/meta-schema/graphql-meta-field.js +62 -50
- package/plugins/meta-schema/scope-meta-builders.d.ts +15 -0
- package/plugins/meta-schema/scope-meta-builders.js +43 -0
- package/plugins/meta-schema/table-meta-builder.js +7 -4
- package/plugins/meta-schema/types.d.ts +15 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GraphQLBoolean, GraphQLFloat, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString
|
|
1
|
+
import { GraphQLBoolean, GraphQLFloat, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString } from 'graphql';
|
|
2
2
|
function nn(type) {
|
|
3
3
|
return new GraphQLNonNull(type);
|
|
4
4
|
}
|
|
@@ -15,16 +15,16 @@ function createMetaSchemaType() {
|
|
|
15
15
|
isArray: { type: nn(GraphQLBoolean) },
|
|
16
16
|
isNotNull: { type: GraphQLBoolean },
|
|
17
17
|
hasDefault: { type: GraphQLBoolean },
|
|
18
|
-
subtype: { type: GraphQLString }
|
|
19
|
-
})
|
|
18
|
+
subtype: { type: GraphQLString }
|
|
19
|
+
})
|
|
20
20
|
});
|
|
21
21
|
const MetaEnumType = new GraphQLObjectType({
|
|
22
22
|
name: 'MetaEnum',
|
|
23
23
|
description: 'Information about a PostgreSQL enum type',
|
|
24
24
|
fields: () => ({
|
|
25
25
|
name: { type: nn(GraphQLString), description: 'The PostgreSQL enum type name' },
|
|
26
|
-
values: { type: nnList(GraphQLString), description: 'Allowed values for this enum' }
|
|
27
|
-
})
|
|
26
|
+
values: { type: nnList(GraphQLString), description: 'Allowed values for this enum' }
|
|
27
|
+
})
|
|
28
28
|
});
|
|
29
29
|
const MetaFieldType = new GraphQLObjectType({
|
|
30
30
|
name: 'MetaField',
|
|
@@ -37,8 +37,8 @@ function createMetaSchemaType() {
|
|
|
37
37
|
isPrimaryKey: { type: nn(GraphQLBoolean) },
|
|
38
38
|
isForeignKey: { type: nn(GraphQLBoolean) },
|
|
39
39
|
description: { type: GraphQLString },
|
|
40
|
-
enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }
|
|
41
|
-
})
|
|
40
|
+
enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }
|
|
41
|
+
})
|
|
42
42
|
});
|
|
43
43
|
const MetaIndexType = new GraphQLObjectType({
|
|
44
44
|
name: 'MetaIndex',
|
|
@@ -48,31 +48,31 @@ function createMetaSchemaType() {
|
|
|
48
48
|
isUnique: { type: nn(GraphQLBoolean) },
|
|
49
49
|
isPrimary: { type: nn(GraphQLBoolean) },
|
|
50
50
|
columns: { type: nnList(GraphQLString) },
|
|
51
|
-
fields: { type: new GraphQLList(nn(MetaFieldType)) }
|
|
52
|
-
})
|
|
51
|
+
fields: { type: new GraphQLList(nn(MetaFieldType)) }
|
|
52
|
+
})
|
|
53
53
|
});
|
|
54
54
|
const MetaPrimaryKeyConstraintType = new GraphQLObjectType({
|
|
55
55
|
name: 'MetaPrimaryKeyConstraint',
|
|
56
56
|
description: 'Information about a primary key constraint',
|
|
57
57
|
fields: () => ({
|
|
58
58
|
name: { type: nn(GraphQLString) },
|
|
59
|
-
fields: { type: nnList(MetaFieldType) }
|
|
60
|
-
})
|
|
59
|
+
fields: { type: nnList(MetaFieldType) }
|
|
60
|
+
})
|
|
61
61
|
});
|
|
62
62
|
const MetaUniqueConstraintType = new GraphQLObjectType({
|
|
63
63
|
name: 'MetaUniqueConstraint',
|
|
64
64
|
description: 'Information about a unique constraint',
|
|
65
65
|
fields: () => ({
|
|
66
66
|
name: { type: nn(GraphQLString) },
|
|
67
|
-
fields: { type: nnList(MetaFieldType) }
|
|
68
|
-
})
|
|
67
|
+
fields: { type: nnList(MetaFieldType) }
|
|
68
|
+
})
|
|
69
69
|
});
|
|
70
70
|
const MetaRefTableType = new GraphQLObjectType({
|
|
71
71
|
name: 'MetaRefTable',
|
|
72
72
|
description: 'Reference to a related table',
|
|
73
73
|
fields: () => ({
|
|
74
|
-
name: { type: nn(GraphQLString) }
|
|
75
|
-
})
|
|
74
|
+
name: { type: nn(GraphQLString) }
|
|
75
|
+
})
|
|
76
76
|
});
|
|
77
77
|
const MetaForeignKeyConstraintType = new GraphQLObjectType({
|
|
78
78
|
name: 'MetaForeignKeyConstraint',
|
|
@@ -83,8 +83,8 @@ function createMetaSchemaType() {
|
|
|
83
83
|
referencedTable: { type: nn(GraphQLString) },
|
|
84
84
|
referencedFields: { type: nnList(GraphQLString) },
|
|
85
85
|
refFields: { type: new GraphQLList(nn(MetaFieldType)) },
|
|
86
|
-
refTable: { type: MetaRefTableType }
|
|
87
|
-
})
|
|
86
|
+
refTable: { type: MetaRefTableType }
|
|
87
|
+
})
|
|
88
88
|
});
|
|
89
89
|
const MetaConstraintsType = new GraphQLObjectType({
|
|
90
90
|
name: 'MetaConstraints',
|
|
@@ -92,8 +92,8 @@ function createMetaSchemaType() {
|
|
|
92
92
|
fields: () => ({
|
|
93
93
|
primaryKey: { type: MetaPrimaryKeyConstraintType },
|
|
94
94
|
unique: { type: nnList(MetaUniqueConstraintType) },
|
|
95
|
-
foreignKey: { type: nnList(MetaForeignKeyConstraintType) }
|
|
96
|
-
})
|
|
95
|
+
foreignKey: { type: nnList(MetaForeignKeyConstraintType) }
|
|
96
|
+
})
|
|
97
97
|
});
|
|
98
98
|
const MetaInflectionType = new GraphQLObjectType({
|
|
99
99
|
name: 'MetaInflection',
|
|
@@ -110,8 +110,8 @@ function createMetaSchemaType() {
|
|
|
110
110
|
createInputType: { type: nn(GraphQLString) },
|
|
111
111
|
createPayloadType: { type: nn(GraphQLString) },
|
|
112
112
|
updatePayloadType: { type: GraphQLString },
|
|
113
|
-
deletePayloadType: { type: nn(GraphQLString) }
|
|
114
|
-
})
|
|
113
|
+
deletePayloadType: { type: nn(GraphQLString) }
|
|
114
|
+
})
|
|
115
115
|
});
|
|
116
116
|
const MetaQueryType = new GraphQLObjectType({
|
|
117
117
|
name: 'MetaQuery',
|
|
@@ -121,8 +121,8 @@ function createMetaSchemaType() {
|
|
|
121
121
|
one: { type: GraphQLString },
|
|
122
122
|
create: { type: GraphQLString },
|
|
123
123
|
update: { type: GraphQLString },
|
|
124
|
-
delete: { type: GraphQLString }
|
|
125
|
-
})
|
|
124
|
+
delete: { type: GraphQLString }
|
|
125
|
+
})
|
|
126
126
|
});
|
|
127
127
|
const MetaBelongsToRelationType = new GraphQLObjectType({
|
|
128
128
|
name: 'MetaBelongsToRelation',
|
|
@@ -132,8 +132,8 @@ function createMetaSchemaType() {
|
|
|
132
132
|
isUnique: { type: nn(GraphQLBoolean) },
|
|
133
133
|
type: { type: GraphQLString },
|
|
134
134
|
keys: { type: nnList(MetaFieldType) },
|
|
135
|
-
references: { type: nn(MetaRefTableType) }
|
|
136
|
-
})
|
|
135
|
+
references: { type: nn(MetaRefTableType) }
|
|
136
|
+
})
|
|
137
137
|
});
|
|
138
138
|
const MetaHasRelationType = new GraphQLObjectType({
|
|
139
139
|
name: 'MetaHasRelation',
|
|
@@ -143,8 +143,8 @@ function createMetaSchemaType() {
|
|
|
143
143
|
isUnique: { type: nn(GraphQLBoolean) },
|
|
144
144
|
type: { type: GraphQLString },
|
|
145
145
|
keys: { type: nnList(MetaFieldType) },
|
|
146
|
-
referencedBy: { type: nn(MetaRefTableType) }
|
|
147
|
-
})
|
|
146
|
+
referencedBy: { type: nn(MetaRefTableType) }
|
|
147
|
+
})
|
|
148
148
|
});
|
|
149
149
|
const MetaManyToManyRelationType = new GraphQLObjectType({
|
|
150
150
|
name: 'MetaManyToManyRelation',
|
|
@@ -159,8 +159,8 @@ function createMetaSchemaType() {
|
|
|
159
159
|
junctionRightKeyAttributes: { type: nnList(MetaFieldType) },
|
|
160
160
|
leftKeyAttributes: { type: nnList(MetaFieldType) },
|
|
161
161
|
rightKeyAttributes: { type: nnList(MetaFieldType) },
|
|
162
|
-
rightTable: { type: nn(MetaRefTableType) }
|
|
163
|
-
})
|
|
162
|
+
rightTable: { type: nn(MetaRefTableType) }
|
|
163
|
+
})
|
|
164
164
|
});
|
|
165
165
|
const MetaRelationsType = new GraphQLObjectType({
|
|
166
166
|
name: 'MetaRelations',
|
|
@@ -170,16 +170,16 @@ function createMetaSchemaType() {
|
|
|
170
170
|
has: { type: nnList(MetaHasRelationType) },
|
|
171
171
|
hasOne: { type: nnList(MetaHasRelationType) },
|
|
172
172
|
hasMany: { type: nnList(MetaHasRelationType) },
|
|
173
|
-
manyToMany: { type: nnList(MetaManyToManyRelationType) }
|
|
174
|
-
})
|
|
173
|
+
manyToMany: { type: nnList(MetaManyToManyRelationType) }
|
|
174
|
+
})
|
|
175
175
|
});
|
|
176
176
|
const MetaStorageType = new GraphQLObjectType({
|
|
177
177
|
name: 'MetaStorage',
|
|
178
178
|
description: 'Storage metadata for a table',
|
|
179
179
|
fields: () => ({
|
|
180
180
|
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
|
-
})
|
|
181
|
+
isBucketsTable: { type: nn(GraphQLBoolean), description: 'Whether this table is a storage buckets table' }
|
|
182
|
+
})
|
|
183
183
|
});
|
|
184
184
|
const MetaSearchConfigType = new GraphQLObjectType({
|
|
185
185
|
name: 'MetaSearchConfig',
|
|
@@ -190,20 +190,20 @@ function createMetaSchemaType() {
|
|
|
190
190
|
description: 'JSON-encoded per-adapter score weights',
|
|
191
191
|
resolve(source) {
|
|
192
192
|
return source.weights ? JSON.stringify(source.weights) : null;
|
|
193
|
-
}
|
|
193
|
+
}
|
|
194
194
|
},
|
|
195
195
|
boostRecent: { type: nn(GraphQLBoolean), description: 'Whether recency boosting is enabled' },
|
|
196
196
|
boostRecencyField: { type: GraphQLString, description: 'Field used for recency decay' },
|
|
197
|
-
boostRecencyDecay: { type: GraphQLFloat, description: 'Exponential decay factor per day' }
|
|
198
|
-
})
|
|
197
|
+
boostRecencyDecay: { type: GraphQLFloat, description: 'Exponential decay factor per day' }
|
|
198
|
+
})
|
|
199
199
|
});
|
|
200
200
|
const MetaSearchColumnType = new GraphQLObjectType({
|
|
201
201
|
name: 'MetaSearchColumn',
|
|
202
202
|
description: 'A searchable column with its algorithm',
|
|
203
203
|
fields: () => ({
|
|
204
204
|
name: { type: nn(GraphQLString), description: 'Column name (camelCase)' },
|
|
205
|
-
algorithm: { type: nn(GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' }
|
|
206
|
-
})
|
|
205
|
+
algorithm: { type: nn(GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' }
|
|
206
|
+
})
|
|
207
207
|
});
|
|
208
208
|
const MetaSearchType = new GraphQLObjectType({
|
|
209
209
|
name: 'MetaSearch',
|
|
@@ -212,31 +212,42 @@ function createMetaSchemaType() {
|
|
|
212
212
|
algorithms: { type: nnList(GraphQLString), description: 'Active search algorithms on this table' },
|
|
213
213
|
columns: { type: nnList(MetaSearchColumnType), description: 'Searchable columns with their algorithm' },
|
|
214
214
|
hasUnifiedSearch: { type: nn(GraphQLBoolean), description: 'Whether unifiedSearch composite filter is available' },
|
|
215
|
-
config: { type: MetaSearchConfigType, description: 'Per-table search configuration' }
|
|
216
|
-
})
|
|
215
|
+
config: { type: MetaSearchConfigType, description: 'Per-table search configuration' }
|
|
216
|
+
})
|
|
217
217
|
});
|
|
218
218
|
const MetaI18nFieldType = new GraphQLObjectType({
|
|
219
219
|
name: 'MetaI18nField',
|
|
220
220
|
description: 'A translatable field',
|
|
221
221
|
fields: () => ({
|
|
222
222
|
name: { type: nn(GraphQLString), description: 'GraphQL field name' },
|
|
223
|
-
type: { type: nn(GraphQLString), description: 'PostgreSQL column type (text, citext)' }
|
|
224
|
-
})
|
|
223
|
+
type: { type: nn(GraphQLString), description: 'PostgreSQL column type (text, citext)' }
|
|
224
|
+
})
|
|
225
225
|
});
|
|
226
226
|
const MetaI18nType = new GraphQLObjectType({
|
|
227
227
|
name: 'MetaI18n',
|
|
228
228
|
description: 'i18n metadata for a table with @i18n tag',
|
|
229
229
|
fields: () => ({
|
|
230
230
|
translationTable: { type: nn(GraphQLString), description: 'Name of the translation table' },
|
|
231
|
-
translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' }
|
|
232
|
-
})
|
|
231
|
+
translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' }
|
|
232
|
+
})
|
|
233
233
|
});
|
|
234
234
|
const MetaRealtimeType = new GraphQLObjectType({
|
|
235
235
|
name: 'MetaRealtime',
|
|
236
236
|
description: 'Realtime metadata for a table with @realtime tag',
|
|
237
237
|
fields: () => ({
|
|
238
|
-
subscriptionFieldName: { type: nn(GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' }
|
|
239
|
-
})
|
|
238
|
+
subscriptionFieldName: { type: nn(GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' }
|
|
239
|
+
})
|
|
240
|
+
});
|
|
241
|
+
const MetaScopeType = new GraphQLObjectType({
|
|
242
|
+
name: 'MetaScope',
|
|
243
|
+
description: 'Provisioning scope metadata for a table',
|
|
244
|
+
fields: () => ({
|
|
245
|
+
scope: { type: nn(GraphQLString), description: "Provisioning scope: 'platform', 'app', 'database', or an entity scope (e.g. 'org')" },
|
|
246
|
+
tier: { type: nn(GraphQLString), description: "Coarse bucket: 'global', 'database', or 'entity'" },
|
|
247
|
+
keyColumn: { type: GraphQLString, description: 'Inflected scope key column (e.g. databaseId, orgId), null for global tiers' },
|
|
248
|
+
entityTable: { type: GraphQLString, description: 'SQL name of the entity table for entity scopes, else null' },
|
|
249
|
+
source: { type: nn(GraphQLString), description: "Provenance of the scope metadata (always 'smartTag')" }
|
|
250
|
+
})
|
|
240
251
|
});
|
|
241
252
|
const MetaTableType = new GraphQLObjectType({
|
|
242
253
|
name: 'MetaTable',
|
|
@@ -257,14 +268,15 @@ function createMetaSchemaType() {
|
|
|
257
268
|
search: { type: MetaSearchType, description: 'Search metadata (null if no search configured)' },
|
|
258
269
|
i18n: { type: MetaI18nType, description: 'i18n metadata (null if no @i18n tag)' },
|
|
259
270
|
realtime: { type: MetaRealtimeType, description: 'Realtime metadata (null if no @realtime tag)' },
|
|
260
|
-
|
|
271
|
+
scope: { type: MetaScopeType, description: 'Provisioning scope metadata (null if no @scope tag)' }
|
|
272
|
+
})
|
|
261
273
|
});
|
|
262
274
|
return new GraphQLObjectType({
|
|
263
275
|
name: 'MetaSchema',
|
|
264
276
|
description: 'Root meta schema type',
|
|
265
277
|
fields: () => ({
|
|
266
|
-
tables: { type: nnList(MetaTableType) }
|
|
267
|
-
})
|
|
278
|
+
tables: { type: nnList(MetaTableType) }
|
|
279
|
+
})
|
|
268
280
|
});
|
|
269
281
|
}
|
|
270
282
|
export function extendQueryWithMetaField(fields, tablesMeta) {
|
|
@@ -274,10 +286,10 @@ export function extendQueryWithMetaField(fields, tablesMeta) {
|
|
|
274
286
|
description: 'Metadata about the database schema, including tables, fields, indexes, and constraints. Useful for code generation tools.',
|
|
275
287
|
resolve() {
|
|
276
288
|
return { tables: tablesMeta };
|
|
277
|
-
}
|
|
289
|
+
}
|
|
278
290
|
};
|
|
279
291
|
return {
|
|
280
292
|
...fields,
|
|
281
|
-
_meta: metaField
|
|
293
|
+
_meta: metaField
|
|
282
294
|
};
|
|
283
295
|
}
|
|
@@ -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
|
|
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
|
|
4
|
-
import {
|
|
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) {
|
|
@@ -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 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-settings",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.16.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.
|
|
35
|
-
"@constructive-io/graphql-types": "^3.14.
|
|
36
|
-
"@constructive-io/s3-streamer": "^2.29.
|
|
34
|
+
"@constructive-io/graphql-env": "^3.16.2",
|
|
35
|
+
"@constructive-io/graphql-types": "^3.14.2",
|
|
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.
|
|
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.
|
|
52
|
+
"graphile-bulk-mutations": "^0.9.2",
|
|
53
53
|
"graphile-config": "1.0.1",
|
|
54
|
-
"graphile-connection-filter": "^1.16.
|
|
55
|
-
"graphile-i18n": "^1.6.
|
|
56
|
-
"graphile-llm": "^0.19.
|
|
57
|
-
"graphile-ltree": "^1.14.
|
|
58
|
-
"graphile-pg-aggregates": "^1.10.
|
|
59
|
-
"graphile-postgis": "^2.23.
|
|
54
|
+
"graphile-connection-filter": "^1.16.2",
|
|
55
|
+
"graphile-i18n": "^1.6.2",
|
|
56
|
+
"graphile-llm": "^0.19.2",
|
|
57
|
+
"graphile-ltree": "^1.14.2",
|
|
58
|
+
"graphile-pg-aggregates": "^1.10.2",
|
|
59
|
+
"graphile-postgis": "^2.23.2",
|
|
60
60
|
"graphile-presigned-url-plugin": "^0.22.1",
|
|
61
61
|
"graphile-realtime-subscriptions": "^0.10.0",
|
|
62
|
-
"graphile-search": "^1.21.
|
|
62
|
+
"graphile-search": "^1.21.2",
|
|
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.
|
|
80
|
+
"graphile-test": "^4.21.2",
|
|
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": "
|
|
92
|
+
"gitHead": "e53a570a4a70987e82dc074bd73501971fd82828"
|
|
93
93
|
}
|
|
@@ -18,16 +18,16 @@ function createMetaSchemaType() {
|
|
|
18
18
|
isArray: { type: nn(graphql_1.GraphQLBoolean) },
|
|
19
19
|
isNotNull: { type: graphql_1.GraphQLBoolean },
|
|
20
20
|
hasDefault: { type: graphql_1.GraphQLBoolean },
|
|
21
|
-
subtype: { type: graphql_1.GraphQLString }
|
|
22
|
-
})
|
|
21
|
+
subtype: { type: graphql_1.GraphQLString }
|
|
22
|
+
})
|
|
23
23
|
});
|
|
24
24
|
const MetaEnumType = new graphql_1.GraphQLObjectType({
|
|
25
25
|
name: 'MetaEnum',
|
|
26
26
|
description: 'Information about a PostgreSQL enum type',
|
|
27
27
|
fields: () => ({
|
|
28
28
|
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
|
-
})
|
|
29
|
+
values: { type: nnList(graphql_1.GraphQLString), description: 'Allowed values for this enum' }
|
|
30
|
+
})
|
|
31
31
|
});
|
|
32
32
|
const MetaFieldType = new graphql_1.GraphQLObjectType({
|
|
33
33
|
name: 'MetaField',
|
|
@@ -40,8 +40,8 @@ function createMetaSchemaType() {
|
|
|
40
40
|
isPrimaryKey: { type: nn(graphql_1.GraphQLBoolean) },
|
|
41
41
|
isForeignKey: { type: nn(graphql_1.GraphQLBoolean) },
|
|
42
42
|
description: { type: graphql_1.GraphQLString },
|
|
43
|
-
enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }
|
|
44
|
-
})
|
|
43
|
+
enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }
|
|
44
|
+
})
|
|
45
45
|
});
|
|
46
46
|
const MetaIndexType = new graphql_1.GraphQLObjectType({
|
|
47
47
|
name: 'MetaIndex',
|
|
@@ -51,31 +51,31 @@ function createMetaSchemaType() {
|
|
|
51
51
|
isUnique: { type: nn(graphql_1.GraphQLBoolean) },
|
|
52
52
|
isPrimary: { type: nn(graphql_1.GraphQLBoolean) },
|
|
53
53
|
columns: { type: nnList(graphql_1.GraphQLString) },
|
|
54
|
-
fields: { type: new graphql_1.GraphQLList(nn(MetaFieldType)) }
|
|
55
|
-
})
|
|
54
|
+
fields: { type: new graphql_1.GraphQLList(nn(MetaFieldType)) }
|
|
55
|
+
})
|
|
56
56
|
});
|
|
57
57
|
const MetaPrimaryKeyConstraintType = new graphql_1.GraphQLObjectType({
|
|
58
58
|
name: 'MetaPrimaryKeyConstraint',
|
|
59
59
|
description: 'Information about a primary key constraint',
|
|
60
60
|
fields: () => ({
|
|
61
61
|
name: { type: nn(graphql_1.GraphQLString) },
|
|
62
|
-
fields: { type: nnList(MetaFieldType) }
|
|
63
|
-
})
|
|
62
|
+
fields: { type: nnList(MetaFieldType) }
|
|
63
|
+
})
|
|
64
64
|
});
|
|
65
65
|
const MetaUniqueConstraintType = new graphql_1.GraphQLObjectType({
|
|
66
66
|
name: 'MetaUniqueConstraint',
|
|
67
67
|
description: 'Information about a unique constraint',
|
|
68
68
|
fields: () => ({
|
|
69
69
|
name: { type: nn(graphql_1.GraphQLString) },
|
|
70
|
-
fields: { type: nnList(MetaFieldType) }
|
|
71
|
-
})
|
|
70
|
+
fields: { type: nnList(MetaFieldType) }
|
|
71
|
+
})
|
|
72
72
|
});
|
|
73
73
|
const MetaRefTableType = new graphql_1.GraphQLObjectType({
|
|
74
74
|
name: 'MetaRefTable',
|
|
75
75
|
description: 'Reference to a related table',
|
|
76
76
|
fields: () => ({
|
|
77
|
-
name: { type: nn(graphql_1.GraphQLString) }
|
|
78
|
-
})
|
|
77
|
+
name: { type: nn(graphql_1.GraphQLString) }
|
|
78
|
+
})
|
|
79
79
|
});
|
|
80
80
|
const MetaForeignKeyConstraintType = new graphql_1.GraphQLObjectType({
|
|
81
81
|
name: 'MetaForeignKeyConstraint',
|
|
@@ -86,8 +86,8 @@ function createMetaSchemaType() {
|
|
|
86
86
|
referencedTable: { type: nn(graphql_1.GraphQLString) },
|
|
87
87
|
referencedFields: { type: nnList(graphql_1.GraphQLString) },
|
|
88
88
|
refFields: { type: new graphql_1.GraphQLList(nn(MetaFieldType)) },
|
|
89
|
-
refTable: { type: MetaRefTableType }
|
|
90
|
-
})
|
|
89
|
+
refTable: { type: MetaRefTableType }
|
|
90
|
+
})
|
|
91
91
|
});
|
|
92
92
|
const MetaConstraintsType = new graphql_1.GraphQLObjectType({
|
|
93
93
|
name: 'MetaConstraints',
|
|
@@ -95,8 +95,8 @@ function createMetaSchemaType() {
|
|
|
95
95
|
fields: () => ({
|
|
96
96
|
primaryKey: { type: MetaPrimaryKeyConstraintType },
|
|
97
97
|
unique: { type: nnList(MetaUniqueConstraintType) },
|
|
98
|
-
foreignKey: { type: nnList(MetaForeignKeyConstraintType) }
|
|
99
|
-
})
|
|
98
|
+
foreignKey: { type: nnList(MetaForeignKeyConstraintType) }
|
|
99
|
+
})
|
|
100
100
|
});
|
|
101
101
|
const MetaInflectionType = new graphql_1.GraphQLObjectType({
|
|
102
102
|
name: 'MetaInflection',
|
|
@@ -113,8 +113,8 @@ function createMetaSchemaType() {
|
|
|
113
113
|
createInputType: { type: nn(graphql_1.GraphQLString) },
|
|
114
114
|
createPayloadType: { type: nn(graphql_1.GraphQLString) },
|
|
115
115
|
updatePayloadType: { type: graphql_1.GraphQLString },
|
|
116
|
-
deletePayloadType: { type: nn(graphql_1.GraphQLString) }
|
|
117
|
-
})
|
|
116
|
+
deletePayloadType: { type: nn(graphql_1.GraphQLString) }
|
|
117
|
+
})
|
|
118
118
|
});
|
|
119
119
|
const MetaQueryType = new graphql_1.GraphQLObjectType({
|
|
120
120
|
name: 'MetaQuery',
|
|
@@ -124,8 +124,8 @@ function createMetaSchemaType() {
|
|
|
124
124
|
one: { type: graphql_1.GraphQLString },
|
|
125
125
|
create: { type: graphql_1.GraphQLString },
|
|
126
126
|
update: { type: graphql_1.GraphQLString },
|
|
127
|
-
delete: { type: graphql_1.GraphQLString }
|
|
128
|
-
})
|
|
127
|
+
delete: { type: graphql_1.GraphQLString }
|
|
128
|
+
})
|
|
129
129
|
});
|
|
130
130
|
const MetaBelongsToRelationType = new graphql_1.GraphQLObjectType({
|
|
131
131
|
name: 'MetaBelongsToRelation',
|
|
@@ -135,8 +135,8 @@ function createMetaSchemaType() {
|
|
|
135
135
|
isUnique: { type: nn(graphql_1.GraphQLBoolean) },
|
|
136
136
|
type: { type: graphql_1.GraphQLString },
|
|
137
137
|
keys: { type: nnList(MetaFieldType) },
|
|
138
|
-
references: { type: nn(MetaRefTableType) }
|
|
139
|
-
})
|
|
138
|
+
references: { type: nn(MetaRefTableType) }
|
|
139
|
+
})
|
|
140
140
|
});
|
|
141
141
|
const MetaHasRelationType = new graphql_1.GraphQLObjectType({
|
|
142
142
|
name: 'MetaHasRelation',
|
|
@@ -146,8 +146,8 @@ function createMetaSchemaType() {
|
|
|
146
146
|
isUnique: { type: nn(graphql_1.GraphQLBoolean) },
|
|
147
147
|
type: { type: graphql_1.GraphQLString },
|
|
148
148
|
keys: { type: nnList(MetaFieldType) },
|
|
149
|
-
referencedBy: { type: nn(MetaRefTableType) }
|
|
150
|
-
})
|
|
149
|
+
referencedBy: { type: nn(MetaRefTableType) }
|
|
150
|
+
})
|
|
151
151
|
});
|
|
152
152
|
const MetaManyToManyRelationType = new graphql_1.GraphQLObjectType({
|
|
153
153
|
name: 'MetaManyToManyRelation',
|
|
@@ -162,8 +162,8 @@ function createMetaSchemaType() {
|
|
|
162
162
|
junctionRightKeyAttributes: { type: nnList(MetaFieldType) },
|
|
163
163
|
leftKeyAttributes: { type: nnList(MetaFieldType) },
|
|
164
164
|
rightKeyAttributes: { type: nnList(MetaFieldType) },
|
|
165
|
-
rightTable: { type: nn(MetaRefTableType) }
|
|
166
|
-
})
|
|
165
|
+
rightTable: { type: nn(MetaRefTableType) }
|
|
166
|
+
})
|
|
167
167
|
});
|
|
168
168
|
const MetaRelationsType = new graphql_1.GraphQLObjectType({
|
|
169
169
|
name: 'MetaRelations',
|
|
@@ -173,16 +173,16 @@ function createMetaSchemaType() {
|
|
|
173
173
|
has: { type: nnList(MetaHasRelationType) },
|
|
174
174
|
hasOne: { type: nnList(MetaHasRelationType) },
|
|
175
175
|
hasMany: { type: nnList(MetaHasRelationType) },
|
|
176
|
-
manyToMany: { type: nnList(MetaManyToManyRelationType) }
|
|
177
|
-
})
|
|
176
|
+
manyToMany: { type: nnList(MetaManyToManyRelationType) }
|
|
177
|
+
})
|
|
178
178
|
});
|
|
179
179
|
const MetaStorageType = new graphql_1.GraphQLObjectType({
|
|
180
180
|
name: 'MetaStorage',
|
|
181
181
|
description: 'Storage metadata for a table',
|
|
182
182
|
fields: () => ({
|
|
183
183
|
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
|
-
})
|
|
184
|
+
isBucketsTable: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether this table is a storage buckets table' }
|
|
185
|
+
})
|
|
186
186
|
});
|
|
187
187
|
const MetaSearchConfigType = new graphql_1.GraphQLObjectType({
|
|
188
188
|
name: 'MetaSearchConfig',
|
|
@@ -193,20 +193,20 @@ function createMetaSchemaType() {
|
|
|
193
193
|
description: 'JSON-encoded per-adapter score weights',
|
|
194
194
|
resolve(source) {
|
|
195
195
|
return source.weights ? JSON.stringify(source.weights) : null;
|
|
196
|
-
}
|
|
196
|
+
}
|
|
197
197
|
},
|
|
198
198
|
boostRecent: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether recency boosting is enabled' },
|
|
199
199
|
boostRecencyField: { type: graphql_1.GraphQLString, description: 'Field used for recency decay' },
|
|
200
|
-
boostRecencyDecay: { type: graphql_1.GraphQLFloat, description: 'Exponential decay factor per day' }
|
|
201
|
-
})
|
|
200
|
+
boostRecencyDecay: { type: graphql_1.GraphQLFloat, description: 'Exponential decay factor per day' }
|
|
201
|
+
})
|
|
202
202
|
});
|
|
203
203
|
const MetaSearchColumnType = new graphql_1.GraphQLObjectType({
|
|
204
204
|
name: 'MetaSearchColumn',
|
|
205
205
|
description: 'A searchable column with its algorithm',
|
|
206
206
|
fields: () => ({
|
|
207
207
|
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
|
-
})
|
|
208
|
+
algorithm: { type: nn(graphql_1.GraphQLString), description: 'Search algorithm: tsvector, bm25, trgm, or vector' }
|
|
209
|
+
})
|
|
210
210
|
});
|
|
211
211
|
const MetaSearchType = new graphql_1.GraphQLObjectType({
|
|
212
212
|
name: 'MetaSearch',
|
|
@@ -215,31 +215,42 @@ function createMetaSchemaType() {
|
|
|
215
215
|
algorithms: { type: nnList(graphql_1.GraphQLString), description: 'Active search algorithms on this table' },
|
|
216
216
|
columns: { type: nnList(MetaSearchColumnType), description: 'Searchable columns with their algorithm' },
|
|
217
217
|
hasUnifiedSearch: { type: nn(graphql_1.GraphQLBoolean), description: 'Whether unifiedSearch composite filter is available' },
|
|
218
|
-
config: { type: MetaSearchConfigType, description: 'Per-table search configuration' }
|
|
219
|
-
})
|
|
218
|
+
config: { type: MetaSearchConfigType, description: 'Per-table search configuration' }
|
|
219
|
+
})
|
|
220
220
|
});
|
|
221
221
|
const MetaI18nFieldType = new graphql_1.GraphQLObjectType({
|
|
222
222
|
name: 'MetaI18nField',
|
|
223
223
|
description: 'A translatable field',
|
|
224
224
|
fields: () => ({
|
|
225
225
|
name: { type: nn(graphql_1.GraphQLString), description: 'GraphQL field name' },
|
|
226
|
-
type: { type: nn(graphql_1.GraphQLString), description: 'PostgreSQL column type (text, citext)' }
|
|
227
|
-
})
|
|
226
|
+
type: { type: nn(graphql_1.GraphQLString), description: 'PostgreSQL column type (text, citext)' }
|
|
227
|
+
})
|
|
228
228
|
});
|
|
229
229
|
const MetaI18nType = new graphql_1.GraphQLObjectType({
|
|
230
230
|
name: 'MetaI18n',
|
|
231
231
|
description: 'i18n metadata for a table with @i18n tag',
|
|
232
232
|
fields: () => ({
|
|
233
233
|
translationTable: { type: nn(graphql_1.GraphQLString), description: 'Name of the translation table' },
|
|
234
|
-
translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' }
|
|
235
|
-
})
|
|
234
|
+
translatableFields: { type: nnList(MetaI18nFieldType), description: 'Fields that are translatable' }
|
|
235
|
+
})
|
|
236
236
|
});
|
|
237
237
|
const MetaRealtimeType = new graphql_1.GraphQLObjectType({
|
|
238
238
|
name: 'MetaRealtime',
|
|
239
239
|
description: 'Realtime metadata for a table with @realtime tag',
|
|
240
240
|
fields: () => ({
|
|
241
|
-
subscriptionFieldName: { type: nn(graphql_1.GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' }
|
|
242
|
-
})
|
|
241
|
+
subscriptionFieldName: { type: nn(graphql_1.GraphQLString), description: 'The generated subscription field name (e.g. onPostChanged)' }
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
const MetaScopeType = new graphql_1.GraphQLObjectType({
|
|
245
|
+
name: 'MetaScope',
|
|
246
|
+
description: 'Provisioning scope metadata for a table',
|
|
247
|
+
fields: () => ({
|
|
248
|
+
scope: { type: nn(graphql_1.GraphQLString), description: "Provisioning scope: 'platform', 'app', 'database', or an entity scope (e.g. 'org')" },
|
|
249
|
+
tier: { type: nn(graphql_1.GraphQLString), description: "Coarse bucket: 'global', 'database', or 'entity'" },
|
|
250
|
+
keyColumn: { type: graphql_1.GraphQLString, description: 'Inflected scope key column (e.g. databaseId, orgId), null for global tiers' },
|
|
251
|
+
entityTable: { type: graphql_1.GraphQLString, description: 'SQL name of the entity table for entity scopes, else null' },
|
|
252
|
+
source: { type: nn(graphql_1.GraphQLString), description: "Provenance of the scope metadata (always 'smartTag')" }
|
|
253
|
+
})
|
|
243
254
|
});
|
|
244
255
|
const MetaTableType = new graphql_1.GraphQLObjectType({
|
|
245
256
|
name: 'MetaTable',
|
|
@@ -260,14 +271,15 @@ function createMetaSchemaType() {
|
|
|
260
271
|
search: { type: MetaSearchType, description: 'Search metadata (null if no search configured)' },
|
|
261
272
|
i18n: { type: MetaI18nType, description: 'i18n metadata (null if no @i18n tag)' },
|
|
262
273
|
realtime: { type: MetaRealtimeType, description: 'Realtime metadata (null if no @realtime tag)' },
|
|
263
|
-
|
|
274
|
+
scope: { type: MetaScopeType, description: 'Provisioning scope metadata (null if no @scope tag)' }
|
|
275
|
+
})
|
|
264
276
|
});
|
|
265
277
|
return new graphql_1.GraphQLObjectType({
|
|
266
278
|
name: 'MetaSchema',
|
|
267
279
|
description: 'Root meta schema type',
|
|
268
280
|
fields: () => ({
|
|
269
|
-
tables: { type: nnList(MetaTableType) }
|
|
270
|
-
})
|
|
281
|
+
tables: { type: nnList(MetaTableType) }
|
|
282
|
+
})
|
|
271
283
|
});
|
|
272
284
|
}
|
|
273
285
|
function extendQueryWithMetaField(fields, tablesMeta) {
|
|
@@ -277,10 +289,10 @@ function extendQueryWithMetaField(fields, tablesMeta) {
|
|
|
277
289
|
description: 'Metadata about the database schema, including tables, fields, indexes, and constraints. Useful for code generation tools.',
|
|
278
290
|
resolve() {
|
|
279
291
|
return { tables: tablesMeta };
|
|
280
|
-
}
|
|
292
|
+
}
|
|
281
293
|
};
|
|
282
294
|
return {
|
|
283
295
|
...fields,
|
|
284
|
-
_meta: metaField
|
|
296
|
+
_meta: metaField
|
|
285
297
|
};
|
|
286
298
|
}
|
|
@@ -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) {
|
|
@@ -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 */
|