@dereekb/dbx-cli 13.11.0 → 13.11.2
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/firebase-api-manifest/main.js +711 -0
- package/firebase-api-manifest/package.json +10 -0
- package/index.cjs.js +242 -54
- package/index.esm.js +240 -55
- package/manifest-extract/LICENSE +21 -0
- package/manifest-extract/index.cjs.default.js +1 -0
- package/manifest-extract/index.cjs.js +459 -0
- package/manifest-extract/index.cjs.mjs +2 -0
- package/manifest-extract/index.d.ts +1 -0
- package/manifest-extract/index.esm.js +457 -0
- package/manifest-extract/package.json +20 -0
- package/manifest-extract/src/index.d.ts +2 -0
- package/manifest-extract/src/lib/extract-crud.d.ts +36 -0
- package/manifest-extract/src/lib/types.d.ts +86 -0
- package/package.json +22 -10
- package/src/lib/config/env.d.ts +1 -1
- package/src/lib/manifest/build-manifest-commands.d.ts +58 -3
- package/src/lib/manifest/types.d.ts +20 -0
- package/src/lib/runner/run.d.ts +3 -0
- package/firebase-api-manifest/src/generate-api-manifest/bind-validators.d.ts +0 -44
- package/firebase-api-manifest/src/generate-api-manifest/emit.d.ts +0 -29
- package/firebase-api-manifest/src/generate-api-manifest/extract-crud.d.ts +0 -25
- package/firebase-api-manifest/src/generate-api-manifest/find-api-files.d.ts +0 -16
- package/firebase-api-manifest/src/generate-api-manifest/main.d.ts +0 -46
- package/firebase-api-manifest/src/generate-api-manifest/parse-functions.d.ts +0 -18
- package/firebase-api-manifest/src/generate-api-manifest/resolve-package.d.ts +0 -59
- package/firebase-api-manifest/src/generate-api-manifest/types.d.ts +0 -44
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { Project, Node } from 'ts-morph';
|
|
2
|
+
|
|
3
|
+
// 'query' is accepted today even though `<Group>ModelCrudFunctionsConfig` in @dereekb/firebase does not yet permit `query:` keys (deferred follow-up). Once query support lands upstream, every query entry flows through here with no change.
|
|
4
|
+
var SUPPORTED_VERBS = new Set([
|
|
5
|
+
'create',
|
|
6
|
+
'read',
|
|
7
|
+
'update',
|
|
8
|
+
'delete',
|
|
9
|
+
'query'
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Walks a `<model>.api.ts` source and returns one {@link CrudEntry} per
|
|
13
|
+
* callable leaf (CRUD or standalone). Best-effort: malformed configs return
|
|
14
|
+
* fewer entries rather than throwing.
|
|
15
|
+
*
|
|
16
|
+
* @param source - in-memory source name + text pair to extract
|
|
17
|
+
* @returns the CRUD extraction with group name, model keys, entries, and
|
|
18
|
+
* `*Functions` class name (when present).
|
|
19
|
+
*/ // eslint-disable-next-line sonarjs/cognitive-complexity
|
|
20
|
+
function extractCrudEntries(source) {
|
|
21
|
+
var project = new Project({
|
|
22
|
+
useInMemoryFileSystem: true,
|
|
23
|
+
skipAddingFilesFromTsConfig: true
|
|
24
|
+
});
|
|
25
|
+
var sourceFile = project.createSourceFile(source.name, source.text, {
|
|
26
|
+
overwrite: true
|
|
27
|
+
});
|
|
28
|
+
var entries = [];
|
|
29
|
+
var modelKeys = [];
|
|
30
|
+
var crudConfigType = findTypeAliasByEnding(sourceFile, 'ModelCrudFunctionsConfig');
|
|
31
|
+
var groupName = inferGroupName(sourceFile);
|
|
32
|
+
var functionsClassName = findFunctionsClassName(sourceFile);
|
|
33
|
+
var typeDocsCache = new Map();
|
|
34
|
+
var resolveTypeDocs = function resolveTypeDocs(typeName) {
|
|
35
|
+
if (!typeName) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
if (typeDocsCache.has(typeName)) {
|
|
39
|
+
return typeDocsCache.get(typeName);
|
|
40
|
+
}
|
|
41
|
+
var docs = readTypeDocs(sourceFile, typeName);
|
|
42
|
+
if (docs) {
|
|
43
|
+
typeDocsCache.set(typeName, docs);
|
|
44
|
+
}
|
|
45
|
+
return docs;
|
|
46
|
+
};
|
|
47
|
+
if (crudConfigType) {
|
|
48
|
+
var literal = crudConfigType.getTypeNode();
|
|
49
|
+
if (literal && Node.isTypeLiteral(literal)) {
|
|
50
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
51
|
+
try {
|
|
52
|
+
for(var _iterator = literal.getMembers()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
53
|
+
var member = _step.value;
|
|
54
|
+
if (!Node.isPropertySignature(member)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
var modelName = member.getName();
|
|
58
|
+
modelKeys.push(modelName);
|
|
59
|
+
var valueNode = member.getTypeNode();
|
|
60
|
+
if (!valueNode) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (isNullLiteralType(valueNode)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (Node.isTypeLiteral(valueNode)) {
|
|
67
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
68
|
+
try {
|
|
69
|
+
for(var _iterator1 = valueNode.getMembers()[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
70
|
+
var verbMember = _step1.value;
|
|
71
|
+
if (!Node.isPropertySignature(verbMember)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
var verbName = verbMember.getName();
|
|
75
|
+
if (!SUPPORTED_VERBS.has(verbName)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
var verb = verbName;
|
|
79
|
+
var verbValueNode = verbMember.getTypeNode();
|
|
80
|
+
if (!verbValueNode) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
collectVerbEntries({
|
|
84
|
+
modelName: modelName,
|
|
85
|
+
verb: verb,
|
|
86
|
+
valueNode: verbValueNode,
|
|
87
|
+
entries: entries,
|
|
88
|
+
fallbackDescription: readJsDocSummary(verbMember),
|
|
89
|
+
resolveTypeDocs: resolveTypeDocs
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
_didIteratorError1 = true;
|
|
94
|
+
_iteratorError1 = err;
|
|
95
|
+
} finally{
|
|
96
|
+
try {
|
|
97
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
98
|
+
_iterator1.return();
|
|
99
|
+
}
|
|
100
|
+
} finally{
|
|
101
|
+
if (_didIteratorError1) {
|
|
102
|
+
throw _iteratorError1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
_didIteratorError = true;
|
|
110
|
+
_iteratorError = err;
|
|
111
|
+
} finally{
|
|
112
|
+
try {
|
|
113
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
114
|
+
_iterator.return();
|
|
115
|
+
}
|
|
116
|
+
} finally{
|
|
117
|
+
if (_didIteratorError) {
|
|
118
|
+
throw _iteratorError;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
var functionTypeMap = findTypeAliasByEnding(sourceFile, 'FunctionTypeMap');
|
|
125
|
+
if (functionTypeMap) {
|
|
126
|
+
var literal1 = functionTypeMap.getTypeNode();
|
|
127
|
+
if (literal1 && Node.isTypeLiteral(literal1)) {
|
|
128
|
+
var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
|
|
129
|
+
try {
|
|
130
|
+
for(var _iterator2 = literal1.getMembers()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
|
|
131
|
+
var member1 = _step2.value;
|
|
132
|
+
if (!Node.isPropertySignature(member1)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
var key = member1.getName();
|
|
136
|
+
var valueNode1 = member1.getTypeNode();
|
|
137
|
+
var tuple = valueNode1 ? readTupleParamsResult(valueNode1) : undefined;
|
|
138
|
+
var paramsDocs = resolveTypeDocs(tuple === null || tuple === void 0 ? void 0 : tuple.params);
|
|
139
|
+
var resultDocs = resolveTypeDocs(tuple === null || tuple === void 0 ? void 0 : tuple.result);
|
|
140
|
+
entries.push({
|
|
141
|
+
model: key,
|
|
142
|
+
verb: 'standalone',
|
|
143
|
+
specifier: undefined,
|
|
144
|
+
paramsTypeName: tuple === null || tuple === void 0 ? void 0 : tuple.params,
|
|
145
|
+
resultTypeName: tuple === null || tuple === void 0 ? void 0 : tuple.result,
|
|
146
|
+
line: member1.getStartLineNumber(),
|
|
147
|
+
description: readJsDocSummary(member1),
|
|
148
|
+
paramsTypeDescription: paramsDocs === null || paramsDocs === void 0 ? void 0 : paramsDocs.typeDescription,
|
|
149
|
+
paramsFields: paramsDocs === null || paramsDocs === void 0 ? void 0 : paramsDocs.fields,
|
|
150
|
+
resultTypeDescription: resultDocs === null || resultDocs === void 0 ? void 0 : resultDocs.typeDescription,
|
|
151
|
+
resultFields: resultDocs === null || resultDocs === void 0 ? void 0 : resultDocs.fields
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch (err) {
|
|
155
|
+
_didIteratorError2 = true;
|
|
156
|
+
_iteratorError2 = err;
|
|
157
|
+
} finally{
|
|
158
|
+
try {
|
|
159
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
|
160
|
+
_iterator2.return();
|
|
161
|
+
}
|
|
162
|
+
} finally{
|
|
163
|
+
if (_didIteratorError2) {
|
|
164
|
+
throw _iteratorError2;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
groupName: groupName,
|
|
172
|
+
modelKeys: modelKeys,
|
|
173
|
+
entries: entries,
|
|
174
|
+
functionsClassName: functionsClassName
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function findTypeAliasByEnding(sourceFile, ending) {
|
|
178
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
179
|
+
try {
|
|
180
|
+
for(var _iterator = sourceFile.getTypeAliases()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
181
|
+
var alias = _step.value;
|
|
182
|
+
if (alias.getName().endsWith(ending) && alias.getTypeNode()) {
|
|
183
|
+
return alias;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} catch (err) {
|
|
187
|
+
_didIteratorError = true;
|
|
188
|
+
_iteratorError = err;
|
|
189
|
+
} finally{
|
|
190
|
+
try {
|
|
191
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
192
|
+
_iterator.return();
|
|
193
|
+
}
|
|
194
|
+
} finally{
|
|
195
|
+
if (_didIteratorError) {
|
|
196
|
+
throw _iteratorError;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
function findFunctionsClassName(sourceFile) {
|
|
203
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
204
|
+
try {
|
|
205
|
+
for(var _iterator = sourceFile.getClasses()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
206
|
+
var cls = _step.value;
|
|
207
|
+
if (!cls.isAbstract()) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
var name = cls.getName();
|
|
211
|
+
if (name === null || name === void 0 ? void 0 : name.endsWith('Functions')) {
|
|
212
|
+
return name;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} catch (err) {
|
|
216
|
+
_didIteratorError = true;
|
|
217
|
+
_iteratorError = err;
|
|
218
|
+
} finally{
|
|
219
|
+
try {
|
|
220
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
221
|
+
_iterator.return();
|
|
222
|
+
}
|
|
223
|
+
} finally{
|
|
224
|
+
if (_didIteratorError) {
|
|
225
|
+
throw _iteratorError;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
function inferGroupName(sourceFile) {
|
|
232
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
233
|
+
try {
|
|
234
|
+
for(var _iterator = sourceFile.getTypeAliases()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
235
|
+
var alias = _step.value;
|
|
236
|
+
var name = alias.getName();
|
|
237
|
+
if (name.endsWith('ModelCrudFunctionsConfig')) {
|
|
238
|
+
var stem = name.slice(0, -'ModelCrudFunctionsConfig'.length);
|
|
239
|
+
if (stem.length > 0) {
|
|
240
|
+
return stem;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
_didIteratorError = true;
|
|
246
|
+
_iteratorError = err;
|
|
247
|
+
} finally{
|
|
248
|
+
try {
|
|
249
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
250
|
+
_iterator.return();
|
|
251
|
+
}
|
|
252
|
+
} finally{
|
|
253
|
+
if (_didIteratorError) {
|
|
254
|
+
throw _iteratorError;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
259
|
+
try {
|
|
260
|
+
for(var _iterator1 = sourceFile.getTypeAliases()[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
261
|
+
var alias1 = _step1.value;
|
|
262
|
+
var name1 = alias1.getName();
|
|
263
|
+
if (name1.endsWith('FunctionTypeMap')) {
|
|
264
|
+
var stem1 = name1.slice(0, -'FunctionTypeMap'.length);
|
|
265
|
+
if (stem1.length > 0) {
|
|
266
|
+
return stem1;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (err) {
|
|
271
|
+
_didIteratorError1 = true;
|
|
272
|
+
_iteratorError1 = err;
|
|
273
|
+
} finally{
|
|
274
|
+
try {
|
|
275
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
276
|
+
_iterator1.return();
|
|
277
|
+
}
|
|
278
|
+
} finally{
|
|
279
|
+
if (_didIteratorError1) {
|
|
280
|
+
throw _iteratorError1;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
function isNullLiteralType(node) {
|
|
287
|
+
if (Node.isLiteralTypeNode(node)) {
|
|
288
|
+
var literal = node.getLiteral();
|
|
289
|
+
if (Node.isNullLiteral(literal)) {
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
function collectVerbEntries(input) {
|
|
296
|
+
var _readTupleParamsResult;
|
|
297
|
+
var modelName = input.modelName, verb = input.verb, valueNode = input.valueNode, entries = input.entries, fallbackDescription = input.fallbackDescription, resolveTypeDocs = input.resolveTypeDocs;
|
|
298
|
+
if (Node.isTypeLiteral(valueNode)) {
|
|
299
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
300
|
+
try {
|
|
301
|
+
for(var _iterator = valueNode.getMembers()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
302
|
+
var specMember = _step.value;
|
|
303
|
+
var _readTupleParamsResult1;
|
|
304
|
+
if (!Node.isPropertySignature(specMember)) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
var specifier = specMember.getName();
|
|
308
|
+
var leafNode = specMember.getTypeNode();
|
|
309
|
+
var leaf = leafNode ? (_readTupleParamsResult1 = readTupleParamsResult(leafNode)) !== null && _readTupleParamsResult1 !== void 0 ? _readTupleParamsResult1 : readBareParams(leafNode) : undefined;
|
|
310
|
+
var paramsDocs = resolveTypeDocs(leaf === null || leaf === void 0 ? void 0 : leaf.params);
|
|
311
|
+
var resultDocs = resolveTypeDocs(leaf === null || leaf === void 0 ? void 0 : leaf.result);
|
|
312
|
+
entries.push({
|
|
313
|
+
model: modelName,
|
|
314
|
+
verb: verb,
|
|
315
|
+
specifier: specifier,
|
|
316
|
+
paramsTypeName: leaf === null || leaf === void 0 ? void 0 : leaf.params,
|
|
317
|
+
resultTypeName: leaf === null || leaf === void 0 ? void 0 : leaf.result,
|
|
318
|
+
line: specMember.getStartLineNumber(),
|
|
319
|
+
description: readJsDocSummary(specMember),
|
|
320
|
+
paramsTypeDescription: paramsDocs === null || paramsDocs === void 0 ? void 0 : paramsDocs.typeDescription,
|
|
321
|
+
paramsFields: paramsDocs === null || paramsDocs === void 0 ? void 0 : paramsDocs.fields,
|
|
322
|
+
resultTypeDescription: resultDocs === null || resultDocs === void 0 ? void 0 : resultDocs.typeDescription,
|
|
323
|
+
resultFields: resultDocs === null || resultDocs === void 0 ? void 0 : resultDocs.fields
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
} catch (err) {
|
|
327
|
+
_didIteratorError = true;
|
|
328
|
+
_iteratorError = err;
|
|
329
|
+
} finally{
|
|
330
|
+
try {
|
|
331
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
332
|
+
_iterator.return();
|
|
333
|
+
}
|
|
334
|
+
} finally{
|
|
335
|
+
if (_didIteratorError) {
|
|
336
|
+
throw _iteratorError;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
var leaf1 = (_readTupleParamsResult = readTupleParamsResult(valueNode)) !== null && _readTupleParamsResult !== void 0 ? _readTupleParamsResult : readBareParams(valueNode);
|
|
343
|
+
var paramsDocs1 = resolveTypeDocs(leaf1 === null || leaf1 === void 0 ? void 0 : leaf1.params);
|
|
344
|
+
var resultDocs1 = resolveTypeDocs(leaf1 === null || leaf1 === void 0 ? void 0 : leaf1.result);
|
|
345
|
+
entries.push({
|
|
346
|
+
model: modelName,
|
|
347
|
+
verb: verb,
|
|
348
|
+
specifier: undefined,
|
|
349
|
+
paramsTypeName: leaf1 === null || leaf1 === void 0 ? void 0 : leaf1.params,
|
|
350
|
+
resultTypeName: leaf1 === null || leaf1 === void 0 ? void 0 : leaf1.result,
|
|
351
|
+
line: valueNode.getStartLineNumber(),
|
|
352
|
+
description: fallbackDescription,
|
|
353
|
+
paramsTypeDescription: paramsDocs1 === null || paramsDocs1 === void 0 ? void 0 : paramsDocs1.typeDescription,
|
|
354
|
+
paramsFields: paramsDocs1 === null || paramsDocs1 === void 0 ? void 0 : paramsDocs1.fields,
|
|
355
|
+
resultTypeDescription: resultDocs1 === null || resultDocs1 === void 0 ? void 0 : resultDocs1.typeDescription,
|
|
356
|
+
resultFields: resultDocs1 === null || resultDocs1 === void 0 ? void 0 : resultDocs1.fields
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function readTupleParamsResult(node) {
|
|
360
|
+
if (!Node.isTupleTypeNode(node)) {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
var elements = node.getElements();
|
|
364
|
+
if (elements.length === 0) {
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
var params = elements[0] ? typeNodeName(elements[0]) : undefined;
|
|
368
|
+
var result = elements[1] ? typeNodeName(elements[1]) : undefined;
|
|
369
|
+
return {
|
|
370
|
+
params: params,
|
|
371
|
+
result: result
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function readBareParams(node) {
|
|
375
|
+
var params = typeNodeName(node);
|
|
376
|
+
if (!params) {
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
params: params,
|
|
381
|
+
result: undefined
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function typeNodeName(node) {
|
|
385
|
+
if (Node.isTypeReference(node)) {
|
|
386
|
+
return node.getTypeName().getText();
|
|
387
|
+
}
|
|
388
|
+
// Fall back to the raw text for primitive / inline types like `boolean`.
|
|
389
|
+
var text = node.getText().trim();
|
|
390
|
+
return text.length > 0 ? text : undefined;
|
|
391
|
+
}
|
|
392
|
+
function readTypeDocs(sourceFile, typeName) {
|
|
393
|
+
var interfaceDecl = sourceFile.getInterface(typeName);
|
|
394
|
+
if (interfaceDecl) {
|
|
395
|
+
var typeDescription = readJsDocSummary(interfaceDecl);
|
|
396
|
+
var fields = [];
|
|
397
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
398
|
+
try {
|
|
399
|
+
for(var _iterator = interfaceDecl.getProperties()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
400
|
+
var property = _step.value;
|
|
401
|
+
var _ref;
|
|
402
|
+
var fieldName = property.getName();
|
|
403
|
+
var description = readJsDocSummary(property);
|
|
404
|
+
var typeNode = property.getTypeNode();
|
|
405
|
+
var typeText = (_ref = typeNode === null || typeNode === void 0 ? void 0 : typeNode.getText().trim()) !== null && _ref !== void 0 ? _ref : '';
|
|
406
|
+
var field = description ? {
|
|
407
|
+
name: fieldName,
|
|
408
|
+
typeText: typeText,
|
|
409
|
+
description: description
|
|
410
|
+
} : {
|
|
411
|
+
name: fieldName,
|
|
412
|
+
typeText: typeText
|
|
413
|
+
};
|
|
414
|
+
fields.push(field);
|
|
415
|
+
}
|
|
416
|
+
} catch (err) {
|
|
417
|
+
_didIteratorError = true;
|
|
418
|
+
_iteratorError = err;
|
|
419
|
+
} finally{
|
|
420
|
+
try {
|
|
421
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
422
|
+
_iterator.return();
|
|
423
|
+
}
|
|
424
|
+
} finally{
|
|
425
|
+
if (_didIteratorError) {
|
|
426
|
+
throw _iteratorError;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (!typeDescription && fields.length === 0) {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
typeDescription: typeDescription,
|
|
435
|
+
fields: fields.length > 0 ? fields : undefined
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
var typeAlias = sourceFile.getTypeAlias(typeName);
|
|
439
|
+
if (typeAlias) {
|
|
440
|
+
var typeDescription1 = readJsDocSummary(typeAlias);
|
|
441
|
+
return typeDescription1 ? {
|
|
442
|
+
typeDescription: typeDescription1
|
|
443
|
+
} : undefined;
|
|
444
|
+
}
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
function readJsDocSummary(node) {
|
|
448
|
+
var docs = node.getJsDocs();
|
|
449
|
+
if (docs.length === 0) {
|
|
450
|
+
return undefined;
|
|
451
|
+
}
|
|
452
|
+
var last = docs[docs.length - 1];
|
|
453
|
+
var description = last.getDescription().trim();
|
|
454
|
+
return description.length > 0 ? description : undefined;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export { extractCrudEntries };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dereekb/dbx-cli/manifest-extract",
|
|
3
|
+
"version": "13.11.2",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"ts-morph": "^21.0.0"
|
|
7
|
+
},
|
|
8
|
+
"exports": {
|
|
9
|
+
"./package.json": "./package.json",
|
|
10
|
+
".": {
|
|
11
|
+
"module": "./index.esm.js",
|
|
12
|
+
"types": "./index.d.ts",
|
|
13
|
+
"import": "./index.cjs.mjs",
|
|
14
|
+
"default": "./index.cjs.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"module": "./index.esm.js",
|
|
18
|
+
"main": "./index.cjs.js",
|
|
19
|
+
"types": "./index.d.ts"
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRUD-entry walker for `<model>.api.ts` files.
|
|
3
|
+
*
|
|
4
|
+
* Re-parses the source with ts-morph and walks the `<Group>ModelCrudFunctionsConfig`
|
|
5
|
+
* type literal recursively to enumerate every callable leaf, keyed by
|
|
6
|
+
* (model, verb, specifier). Also enumerates `<Group>FunctionTypeMap` keys as
|
|
7
|
+
* `standalone` entries. The walker returns a flat list of {@link CrudEntry}
|
|
8
|
+
* records suitable for tabular rendering.
|
|
9
|
+
*
|
|
10
|
+
* Distinct from the lighter `summarizeCrudConfigType()` in
|
|
11
|
+
* `dbx-components-mcp`'s `model-validate-api/extract.ts`: that helper only
|
|
12
|
+
* collects top-level keys and bare-leaf params names; this walker preserves
|
|
13
|
+
* the full verb→specifier tree, tuple/result information, and JSDoc on both
|
|
14
|
+
* params and result interfaces.
|
|
15
|
+
*
|
|
16
|
+
* Canonical source for both the MCP server's `dbx_model_api_*` tools and the
|
|
17
|
+
* `dbx-cli-firebase-api-manifest` build CLI.
|
|
18
|
+
*/
|
|
19
|
+
import type { CrudExtraction } from './types';
|
|
20
|
+
/**
|
|
21
|
+
* Inputs for {@link extractCrudEntries}.
|
|
22
|
+
*/
|
|
23
|
+
export interface ExtractCrudInput {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly text: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Walks a `<model>.api.ts` source and returns one {@link CrudEntry} per
|
|
29
|
+
* callable leaf (CRUD or standalone). Best-effort: malformed configs return
|
|
30
|
+
* fewer entries rather than throwing.
|
|
31
|
+
*
|
|
32
|
+
* @param source - in-memory source name + text pair to extract
|
|
33
|
+
* @returns the CRUD extraction with group name, model keys, entries, and
|
|
34
|
+
* `*Functions` class name (when present).
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractCrudEntries(source: ExtractCrudInput): CrudExtraction;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the `<model>.api.ts` CRUD walker.
|
|
3
|
+
*
|
|
4
|
+
* A CRUD entry is one callable site in a `<model>.api.ts` file —
|
|
5
|
+
* either a leaf in `<Group>ModelCrudFunctionsConfig` (verb + optional
|
|
6
|
+
* specifier) or a key in `<Group>FunctionTypeMap` (standalone).
|
|
7
|
+
*
|
|
8
|
+
* Consumed both by `dbx-components-mcp`'s `dbx_model_api_*` tools and by the
|
|
9
|
+
* `dbx-cli-firebase-api-manifest` build CLI. Re-exported under
|
|
10
|
+
* `@dereekb/dbx-cli/manifest-extract`.
|
|
11
|
+
*/
|
|
12
|
+
export type CrudVerb = 'create' | 'read' | 'update' | 'delete' | 'query' | 'standalone';
|
|
13
|
+
export interface CrudEntryDocField {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly typeText: string;
|
|
16
|
+
readonly description?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CrudEntry {
|
|
19
|
+
/**
|
|
20
|
+
* Top-level model key from `<Group>ModelCrudFunctionsConfig` (e.g. `profile`,
|
|
21
|
+
* `guestbookEntry`). For standalone entries this is the firebase function
|
|
22
|
+
* key itself.
|
|
23
|
+
*/
|
|
24
|
+
readonly model: string;
|
|
25
|
+
readonly verb: CrudVerb;
|
|
26
|
+
/**
|
|
27
|
+
* Specifier sub-key (e.g. `username`, `_`, `subscribeToNotifications`).
|
|
28
|
+
* `undefined` when the verb has no nested specifier object (the value is
|
|
29
|
+
* a bare params reference, e.g. `create: CreateGuestbookParams`), or when
|
|
30
|
+
* the entry is `standalone`.
|
|
31
|
+
*/
|
|
32
|
+
readonly specifier: string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Bare params type name resolved at the leaf (e.g. `SetProfileUsernameParams`).
|
|
35
|
+
* `undefined` when the leaf could not be resolved to a type reference.
|
|
36
|
+
*/
|
|
37
|
+
readonly paramsTypeName: string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Result type name when the leaf is a `[Params, Result]` tuple, otherwise
|
|
40
|
+
* `undefined` (implies `void`).
|
|
41
|
+
*/
|
|
42
|
+
readonly resultTypeName: string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Source line of the leaf property in the type literal.
|
|
45
|
+
*/
|
|
46
|
+
readonly line: number;
|
|
47
|
+
/**
|
|
48
|
+
* JSDoc summary on the property signature in `<Group>ModelCrudFunctionsConfig` (or the key
|
|
49
|
+
* in `<Group>FunctionTypeMap`).
|
|
50
|
+
*/
|
|
51
|
+
readonly description?: string;
|
|
52
|
+
/**
|
|
53
|
+
* JSDoc summary on the params interface itself (e.g. on `ResetProfilePasswordParams`).
|
|
54
|
+
*/
|
|
55
|
+
readonly paramsTypeDescription?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Per-field JSDocs read from the params interface's properties.
|
|
58
|
+
*/
|
|
59
|
+
readonly paramsFields?: readonly CrudEntryDocField[];
|
|
60
|
+
/**
|
|
61
|
+
* JSDoc summary on the result interface itself (e.g. on `DownloadProfileArchiveResult`).
|
|
62
|
+
*/
|
|
63
|
+
readonly resultTypeDescription?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Per-field JSDocs read from the result interface's properties.
|
|
66
|
+
*/
|
|
67
|
+
readonly resultFields?: readonly CrudEntryDocField[];
|
|
68
|
+
}
|
|
69
|
+
export interface CrudExtraction {
|
|
70
|
+
/**
|
|
71
|
+
* Inferred group pascal name (e.g. `Profile`, `Guestbook`).
|
|
72
|
+
*/
|
|
73
|
+
readonly groupName: string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Top-level model keys declared in `<Group>ModelCrudFunctionsConfig`,
|
|
76
|
+
* including null-valued entries (e.g. `profilePrivate: null`).
|
|
77
|
+
*/
|
|
78
|
+
readonly modelKeys: readonly string[];
|
|
79
|
+
readonly entries: readonly CrudEntry[];
|
|
80
|
+
/**
|
|
81
|
+
* Name of the abstract `*Functions` class declared in the source, when present.
|
|
82
|
+
* Used by the manifest build CLI to bind `<APP>_FIREBASE_FUNCTIONS_CONFIG`
|
|
83
|
+
* class identifiers to source files.
|
|
84
|
+
*/
|
|
85
|
+
readonly functionsClassName?: string;
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli",
|
|
3
|
-
"version": "13.11.
|
|
3
|
+
"version": "13.11.2",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"@dereekb/nestjs": "13.11.0",
|
|
8
|
-
"@dereekb/util": "13.11.0",
|
|
9
|
-
"arktype": "^2.2.0",
|
|
10
|
-
"yargs": "^18.0.0"
|
|
11
|
-
},
|
|
12
|
-
"devDependencies": {
|
|
13
|
-
"@types/yargs": "^17.0.35"
|
|
5
|
+
"bin": {
|
|
6
|
+
"dbx-cli-generate-firebase-api-manifest": "firebase-api-manifest/main.js"
|
|
14
7
|
},
|
|
15
8
|
"exports": {
|
|
9
|
+
"./firebase-api-manifest": {
|
|
10
|
+
"default": "./firebase-api-manifest/main.js"
|
|
11
|
+
},
|
|
12
|
+
"./manifest-extract": {
|
|
13
|
+
"module": "./manifest-extract/index.esm.js",
|
|
14
|
+
"types": "./manifest-extract/index.d.ts",
|
|
15
|
+
"import": "./manifest-extract/index.cjs.mjs",
|
|
16
|
+
"default": "./manifest-extract/index.cjs.js"
|
|
17
|
+
},
|
|
16
18
|
"./package.json": "./package.json",
|
|
17
19
|
".": {
|
|
18
20
|
"module": "./index.esm.js",
|
|
@@ -21,6 +23,16 @@
|
|
|
21
23
|
"default": "./index.cjs.js"
|
|
22
24
|
}
|
|
23
25
|
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@dereekb/firebase": "13.11.2",
|
|
28
|
+
"@dereekb/nestjs": "13.11.2",
|
|
29
|
+
"@dereekb/util": "13.11.2",
|
|
30
|
+
"arktype": "^2.2.0",
|
|
31
|
+
"yargs": "^18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/yargs": "^17.0.35"
|
|
35
|
+
},
|
|
24
36
|
"module": "./index.esm.js",
|
|
25
37
|
"main": "./index.cjs.js",
|
|
26
38
|
"types": "./index.d.ts"
|
package/src/lib/config/env.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const DEFAULT_CLI_OIDC_SCOPES = "openid profile email";
|
|
|
7
7
|
* The `model.*` write scopes filtered out by {@link filterReadOnlyModelScopes}.
|
|
8
8
|
*
|
|
9
9
|
* Mirrors the write half of the dbx-components callModel CRUD scope set
|
|
10
|
-
* (`CALL_MODEL_OIDC_SCOPES` in `@dereekb/firebase
|
|
10
|
+
* (`CALL_MODEL_OIDC_SCOPES` in `@dereekb/firebase`) — duplicated here so the
|
|
11
11
|
* CLI doesn't take a server-side dependency just to know the names.
|
|
12
12
|
*/
|
|
13
13
|
export declare const MODEL_WRITE_OIDC_SCOPES: readonly ["model.create", "model.update", "model.delete"];
|