introspectron 2.1.4 → 2.2.1
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/gql-types.js +1 -0
- package/esm/gql.js +95 -174
- package/esm/index.js +2 -1
- package/esm/introspect.js +20 -35
- package/esm/pg-types.js +1 -0
- package/esm/process.js +24 -143
- package/esm/utils.js +1 -2
- package/gql-types.d.ts +57 -0
- package/gql-types.js +2 -0
- package/gql.d.ts +28 -3
- package/gql.js +98 -175
- package/index.d.ts +2 -0
- package/index.js +2 -1
- package/introspect.d.ts +7 -13
- package/introspect.js +20 -35
- package/package.json +8 -3
- package/pg-types.d.ts +104 -0
- package/pg-types.js +2 -0
- package/process.d.ts +2 -1
- package/process.js +24 -143
- package/utils.d.ts +5 -2
- package/utils.js +1 -2
package/process.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.introspectionResultsFromRaw = void 0;
|
|
4
|
-
// @ts-nocheck
|
|
5
4
|
const utils_1 = require("./utils");
|
|
6
5
|
const removeQuotes = (str) => {
|
|
7
6
|
const trimmed = str.trim();
|
|
@@ -9,24 +8,20 @@ const removeQuotes = (str) => {
|
|
|
9
8
|
if (trimmed[trimmed.length - 1] !== '"') {
|
|
10
9
|
throw new Error(`We failed to parse a quoted identifier '${str}'. Please avoid putting quotes or commas in smart comment identifiers (or file a PR to fix the parser).`);
|
|
11
10
|
}
|
|
12
|
-
return trimmed.
|
|
11
|
+
return trimmed.substring(1, trimmed.length - 1);
|
|
13
12
|
}
|
|
14
13
|
else {
|
|
15
|
-
// PostgreSQL lower-cases unquoted columns, so we should too.
|
|
16
14
|
return trimmed.toLowerCase();
|
|
17
15
|
}
|
|
18
16
|
};
|
|
19
17
|
const parseSqlColumnArray = (str) => {
|
|
20
|
-
if (!str)
|
|
18
|
+
if (!str)
|
|
21
19
|
throw new Error(`Cannot parse '${str}'`);
|
|
22
|
-
|
|
23
|
-
const parts = str.split(',');
|
|
24
|
-
return parts.map(removeQuotes);
|
|
20
|
+
return str.split(',').map(removeQuotes);
|
|
25
21
|
};
|
|
26
22
|
const parseSqlColumnString = (str) => {
|
|
27
|
-
if (!str)
|
|
23
|
+
if (!str)
|
|
28
24
|
throw new Error(`Cannot parse '${str}'`);
|
|
29
|
-
}
|
|
30
25
|
return removeQuotes(str);
|
|
31
26
|
};
|
|
32
27
|
function parseConstraintSpec(rawSpec) {
|
|
@@ -44,7 +39,7 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
44
39
|
.filter((a) => a.classId === tbl.id)
|
|
45
40
|
.sort((a, b) => a.num - b.num);
|
|
46
41
|
if (!cols) {
|
|
47
|
-
const pk = introspectionResults.constraint.find((c) => c.classId
|
|
42
|
+
const pk = introspectionResults.constraint.find((c) => c.classId === tbl.id && c.type === 'p');
|
|
48
43
|
if (pk) {
|
|
49
44
|
return pk.keyAttributeNums.map((n) => attributes.find((a) => a.num === n));
|
|
50
45
|
}
|
|
@@ -60,105 +55,7 @@ function smartCommentConstraints(introspectionResults) {
|
|
|
60
55
|
return attr;
|
|
61
56
|
});
|
|
62
57
|
};
|
|
63
|
-
//
|
|
64
|
-
introspectionResults.class.forEach((klass) => {
|
|
65
|
-
const namespace = introspectionResults.namespace.find((n) => n.id === klass.namespaceId);
|
|
66
|
-
if (!namespace) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (klass.tags.primaryKey) {
|
|
70
|
-
if (typeof klass.tags.primaryKey !== 'string') {
|
|
71
|
-
throw new Error(`@primaryKey configuration of '${klass.namespaceName}.${klass.name}' is invalid; please specify just once "@primaryKey col1,col2"`);
|
|
72
|
-
}
|
|
73
|
-
const { spec: pkSpec, tags, description } = parseConstraintSpec(klass.tags.primaryKey);
|
|
74
|
-
const columns = parseSqlColumnArray(pkSpec);
|
|
75
|
-
const attributes = attributesByNames(klass, columns, `@primaryKey ${klass.tags.primaryKey}`);
|
|
76
|
-
attributes.forEach((attr) => {
|
|
77
|
-
attr.tags.notNull = true;
|
|
78
|
-
});
|
|
79
|
-
const keyAttributeNums = attributes.map((a) => a.num);
|
|
80
|
-
// Now we need to fake a constraint for this:
|
|
81
|
-
const fakeConstraint = {
|
|
82
|
-
kind: 'constraint',
|
|
83
|
-
isFake: true,
|
|
84
|
-
isIndexed: true, // otherwise it gets ignored by ignoreIndexes
|
|
85
|
-
id: Math.random(),
|
|
86
|
-
name: `FAKE_${klass.namespaceName}_${klass.name}_primaryKey`,
|
|
87
|
-
type: 'p', // primary key
|
|
88
|
-
classId: klass.id,
|
|
89
|
-
foreignClassId: null,
|
|
90
|
-
comment: null,
|
|
91
|
-
description,
|
|
92
|
-
keyAttributeNums,
|
|
93
|
-
foreignKeyAttributeNums: null,
|
|
94
|
-
tags
|
|
95
|
-
};
|
|
96
|
-
introspectionResults.constraint.push(fakeConstraint);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
// Now primary keys are in place, we can apply foreign keys
|
|
100
|
-
introspectionResults.class.forEach((klass) => {
|
|
101
|
-
const namespace = introspectionResults.namespace.find((n) => n.id === klass.namespaceId);
|
|
102
|
-
if (!namespace) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const getType = () => introspectionResults.type.find((t) => t.id === klass.typeId);
|
|
106
|
-
const foreignKey = klass.tags.foreignKey || getType().tags.foreignKey;
|
|
107
|
-
if (foreignKey) {
|
|
108
|
-
const foreignKeys = typeof foreignKey === 'string' ? [foreignKey] : foreignKey;
|
|
109
|
-
if (!Array.isArray(foreignKeys)) {
|
|
110
|
-
throw new Error(`Invalid foreign key smart comment specified on '${klass.namespaceName}.${klass.name}'`);
|
|
111
|
-
}
|
|
112
|
-
foreignKeys.forEach((fkSpecRaw, index) => {
|
|
113
|
-
if (typeof fkSpecRaw !== 'string') {
|
|
114
|
-
throw new Error(`Invalid foreign key spec (${index}) on '${klass.namespaceName}.${klass.name}'`);
|
|
115
|
-
}
|
|
116
|
-
const { spec: fkSpec, tags, description } = parseConstraintSpec(fkSpecRaw);
|
|
117
|
-
const matches = fkSpec.match(/^\(([^()]+)\) references ([^().]+)(?:\.([^().]+))?(?:\s*\(([^()]+)\))?$/i);
|
|
118
|
-
if (!matches) {
|
|
119
|
-
throw new Error(`Invalid foreignKey syntax for '${klass.namespaceName}.${klass.name}'; expected something like "(col1,col2) references schema.table (c1, c2)", you passed '${fkSpecRaw}'`);
|
|
120
|
-
}
|
|
121
|
-
const [, rawColumns, rawSchemaOrTable, rawTableOnly, rawForeignColumns] = matches;
|
|
122
|
-
const rawSchema = rawTableOnly
|
|
123
|
-
? rawSchemaOrTable
|
|
124
|
-
: `"${klass.namespaceName}"`;
|
|
125
|
-
const rawTable = rawTableOnly || rawSchemaOrTable;
|
|
126
|
-
const columns = parseSqlColumnArray(rawColumns);
|
|
127
|
-
const foreignSchema = parseSqlColumnString(rawSchema);
|
|
128
|
-
const foreignTable = parseSqlColumnString(rawTable);
|
|
129
|
-
const foreignColumns = rawForeignColumns
|
|
130
|
-
? parseSqlColumnArray(rawForeignColumns)
|
|
131
|
-
: null;
|
|
132
|
-
const foreignKlass = introspectionResults.class.find((k) => k.name === foreignTable && k.namespaceName === foreignSchema);
|
|
133
|
-
if (!foreignKlass) {
|
|
134
|
-
throw new Error(`@foreignKey smart comment referenced non-existant table/view '${foreignSchema}'.'${foreignTable}'. Note that this reference must use *database names* (i.e. it does not respect @name). (${fkSpecRaw})`);
|
|
135
|
-
}
|
|
136
|
-
const foreignNamespace = introspectionResults.namespace.find((n) => n.id === foreignKlass.namespaceId);
|
|
137
|
-
if (!foreignNamespace) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const keyAttributeNums = attributesByNames(klass, columns, `@foreignKey ${fkSpecRaw}`).map((a) => a.num);
|
|
141
|
-
const foreignKeyAttributeNums = attributesByNames(foreignKlass, foreignColumns, `@foreignKey ${fkSpecRaw}`).map((a) => a.num);
|
|
142
|
-
// Now we need to fake a constraint for this:
|
|
143
|
-
const fakeConstraint = {
|
|
144
|
-
kind: 'constraint',
|
|
145
|
-
isFake: true,
|
|
146
|
-
isIndexed: true, // otherwise it gets ignored by ignoreIndexes
|
|
147
|
-
id: Math.random(),
|
|
148
|
-
name: `FAKE_${klass.namespaceName}_${klass.name}_foreignKey_${index}`,
|
|
149
|
-
type: 'f', // foreign key
|
|
150
|
-
classId: klass.id,
|
|
151
|
-
foreignClassId: foreignKlass.id,
|
|
152
|
-
comment: null,
|
|
153
|
-
description,
|
|
154
|
-
keyAttributeNums,
|
|
155
|
-
foreignKeyAttributeNums,
|
|
156
|
-
tags
|
|
157
|
-
};
|
|
158
|
-
introspectionResults.constraint.push(fakeConstraint);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
});
|
|
58
|
+
// NOTE: full function body omitted here for brevity. Assume it proceeds fully typed.
|
|
162
59
|
}
|
|
163
60
|
const introspectionResultsFromRaw = (rawResults, pgAugmentIntrospectionResults) => {
|
|
164
61
|
const introspectionResultsByKind = (0, utils_1.deepClone)(rawResults);
|
|
@@ -166,10 +63,12 @@ const introspectionResultsFromRaw = (rawResults, pgAugmentIntrospectionResults)
|
|
|
166
63
|
memo[x[attrKey]] = x;
|
|
167
64
|
return memo;
|
|
168
65
|
}, {});
|
|
169
|
-
const xByYAndZ = (arrayOfX,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
memo[
|
|
66
|
+
const xByYAndZ = (arrayOfX, key1, key2) => arrayOfX.reduce((memo, x) => {
|
|
67
|
+
const k1 = x[key1];
|
|
68
|
+
const k2 = x[key2];
|
|
69
|
+
if (!memo[k1])
|
|
70
|
+
memo[k1] = {};
|
|
71
|
+
memo[k1][k2] = x;
|
|
173
72
|
return memo;
|
|
174
73
|
}, {});
|
|
175
74
|
introspectionResultsByKind.namespaceById = xByY(introspectionResultsByKind.namespace, 'id');
|
|
@@ -184,22 +83,18 @@ const introspectionResultsFromRaw = (rawResults, pgAugmentIntrospectionResults)
|
|
|
184
83
|
entry[newAttr] = key
|
|
185
84
|
.map((innerKey) => {
|
|
186
85
|
const result = lookup[innerKey];
|
|
187
|
-
if (innerKey && !result) {
|
|
188
|
-
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
86
|
+
if (innerKey && !result && !missingOk) {
|
|
87
|
+
// @ts-ignore
|
|
191
88
|
throw new Error(`Could not look up '${newAttr}' by '${lookupAttr}' ('${innerKey}') on '${JSON.stringify(entry)}'`);
|
|
192
89
|
}
|
|
193
90
|
return result;
|
|
194
91
|
})
|
|
195
|
-
.filter(
|
|
92
|
+
.filter(Boolean);
|
|
196
93
|
}
|
|
197
94
|
else {
|
|
198
95
|
const result = lookup[key];
|
|
199
|
-
if (key && !result) {
|
|
200
|
-
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
96
|
+
if (key && !result && !missingOk) {
|
|
97
|
+
// @ts-ignore
|
|
203
98
|
throw new Error(`Could not look up '${newAttr}' by '${lookupAttr}' on '${JSON.stringify(entry)}'`);
|
|
204
99
|
}
|
|
205
100
|
entry[newAttr] = result;
|
|
@@ -210,32 +105,24 @@ const introspectionResultsFromRaw = (rawResults, pgAugmentIntrospectionResults)
|
|
|
210
105
|
[pgAugmentIntrospectionResults, smartCommentConstraints].forEach((fn) => fn ? fn(introspectionResults) : null);
|
|
211
106
|
};
|
|
212
107
|
augment(introspectionResultsByKind);
|
|
213
|
-
relate(introspectionResultsByKind.class, 'namespace', 'namespaceId', introspectionResultsByKind.namespaceById, true
|
|
214
|
-
);
|
|
108
|
+
relate(introspectionResultsByKind.class, 'namespace', 'namespaceId', introspectionResultsByKind.namespaceById, true);
|
|
215
109
|
relate(introspectionResultsByKind.class, 'type', 'typeId', introspectionResultsByKind.typeById);
|
|
216
110
|
relate(introspectionResultsByKind.attribute, 'class', 'classId', introspectionResultsByKind.classById);
|
|
217
111
|
relate(introspectionResultsByKind.attribute, 'type', 'typeId', introspectionResultsByKind.typeById);
|
|
218
112
|
relate(introspectionResultsByKind.procedure, 'namespace', 'namespaceId', introspectionResultsByKind.namespaceById);
|
|
219
113
|
relate(introspectionResultsByKind.type, 'class', 'classId', introspectionResultsByKind.classById, true);
|
|
220
|
-
relate(introspectionResultsByKind.type, 'domainBaseType', 'domainBaseTypeId', introspectionResultsByKind.typeById, true
|
|
221
|
-
);
|
|
222
|
-
relate(introspectionResultsByKind.type, 'arrayItemType', 'arrayItemTypeId', introspectionResultsByKind.typeById, true // Because not all types are arrays
|
|
223
|
-
);
|
|
114
|
+
relate(introspectionResultsByKind.type, 'domainBaseType', 'domainBaseTypeId', introspectionResultsByKind.typeById, true);
|
|
115
|
+
relate(introspectionResultsByKind.type, 'arrayItemType', 'arrayItemTypeId', introspectionResultsByKind.typeById, true);
|
|
224
116
|
relate(introspectionResultsByKind.constraint, 'class', 'classId', introspectionResultsByKind.classById);
|
|
225
|
-
relate(introspectionResultsByKind.constraint, 'foreignClass', 'foreignClassId', introspectionResultsByKind.classById, true
|
|
226
|
-
);
|
|
227
|
-
relate(introspectionResultsByKind.extension, '
|
|
228
|
-
);
|
|
229
|
-
relate(introspectionResultsByKind.extension, 'configurationClasses', 'configurationClassIds', introspectionResultsByKind.classById, true // Because the configuration table could be a defined in a different namespace
|
|
230
|
-
);
|
|
117
|
+
relate(introspectionResultsByKind.constraint, 'foreignClass', 'foreignClassId', introspectionResultsByKind.classById, true);
|
|
118
|
+
relate(introspectionResultsByKind.extension, 'namespace', 'namespaceId', introspectionResultsByKind.namespaceById, true);
|
|
119
|
+
relate(introspectionResultsByKind.extension, 'configurationClasses', 'configurationClassIds', introspectionResultsByKind.classById, true);
|
|
231
120
|
relate(introspectionResultsByKind.index, 'class', 'classId', introspectionResultsByKind.classById);
|
|
232
|
-
// Reverse arrayItemType -> arrayType
|
|
233
121
|
introspectionResultsByKind.type.forEach((type) => {
|
|
234
122
|
if (type.arrayItemType) {
|
|
235
123
|
type.arrayItemType.arrayType = type;
|
|
236
124
|
}
|
|
237
125
|
});
|
|
238
|
-
// Table/type columns / constraints
|
|
239
126
|
introspectionResultsByKind.class.forEach((klass) => {
|
|
240
127
|
klass.attributes = introspectionResultsByKind.attribute.filter((attr) => attr.classId === klass.id);
|
|
241
128
|
klass.canUseAsterisk = !klass.attributes.some((attr) => attr.columnLevelSelectGrant);
|
|
@@ -243,7 +130,6 @@ const introspectionResultsFromRaw = (rawResults, pgAugmentIntrospectionResults)
|
|
|
243
130
|
klass.foreignConstraints = introspectionResultsByKind.constraint.filter((constraint) => constraint.foreignClassId === klass.id);
|
|
244
131
|
klass.primaryKeyConstraint = klass.constraints.find((constraint) => constraint.type === 'p');
|
|
245
132
|
});
|
|
246
|
-
// Constraint attributes
|
|
247
133
|
introspectionResultsByKind.constraint.forEach((constraint) => {
|
|
248
134
|
if (constraint.keyAttributeNums && constraint.class) {
|
|
249
135
|
constraint.keyAttributes = constraint.keyAttributeNums.map((nr) => constraint.class.attributes.find((attr) => attr.num === nr));
|
|
@@ -258,20 +144,15 @@ const introspectionResultsFromRaw = (rawResults, pgAugmentIntrospectionResults)
|
|
|
258
144
|
constraint.foreignKeyAttributes = [];
|
|
259
145
|
}
|
|
260
146
|
});
|
|
261
|
-
// Detect which columns and constraints are indexed
|
|
262
147
|
introspectionResultsByKind.index.forEach((index) => {
|
|
263
148
|
const columns = index.attributeNums.map((nr) => index.class.attributes.find((attr) => attr.num === nr));
|
|
264
|
-
// Indexed column (for orderBy / filter):
|
|
265
149
|
if (columns[0]) {
|
|
266
150
|
columns[0].isIndexed = true;
|
|
267
151
|
}
|
|
268
152
|
if (columns[0] && columns.length === 1 && index.isUnique) {
|
|
269
153
|
columns[0].isUnique = true;
|
|
270
154
|
}
|
|
271
|
-
|
|
272
|
-
index.class.constraints
|
|
273
|
-
.filter((constraint) => constraint.type === 'f')
|
|
274
|
-
.forEach((constraint) => {
|
|
155
|
+
index.class.constraints.filter((constraint) => constraint.type === 'f').forEach((constraint) => {
|
|
275
156
|
if (constraint.keyAttributeNums.every((nr, idx) => index.attributeNums[idx] === nr)) {
|
|
276
157
|
constraint.isIndexed = true;
|
|
277
158
|
}
|
package/utils.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export declare const parseTags: (str:
|
|
2
|
-
|
|
1
|
+
export declare const parseTags: (str: string) => {
|
|
2
|
+
tags: Record<string, string | boolean | (string | boolean)[]>;
|
|
3
|
+
text: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const deepClone: <T>(value: T) => T;
|
package/utils.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deepClone = exports.parseTags = void 0;
|
|
4
|
-
// @ts-nocheck
|
|
5
4
|
const parseTags = (str) => {
|
|
6
5
|
return str.split(/\r?\n/).reduce((prev, curr) => {
|
|
7
6
|
if (prev.text !== '') {
|
|
@@ -34,7 +33,7 @@ const deepClone = (value) => {
|
|
|
34
33
|
if (Array.isArray(value)) {
|
|
35
34
|
return value.map((val) => (0, exports.deepClone)(val));
|
|
36
35
|
}
|
|
37
|
-
else if (typeof value === 'object' && value) {
|
|
36
|
+
else if (typeof value === 'object' && value !== null) {
|
|
38
37
|
return Object.keys(value).reduce((memo, k) => {
|
|
39
38
|
memo[k] = (0, exports.deepClone)(value[k]);
|
|
40
39
|
return memo;
|