@dereekb/dbx-cli 13.37.0 → 13.39.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/eslint/package.json +8 -8
- package/firebase-api-manifest/main.js +191 -166
- package/firebase-api-manifest/package.json +3 -3
- package/firestore-query-manifest/main.js +2063 -0
- package/firestore-query-manifest/package.json +13 -0
- package/firestore-rules/src/firestore-rules-scan.d.ts +89 -0
- package/firestore-rules/src/index.d.ts +1 -0
- package/generate-firestore-indexes/main.js +2 -2
- package/generate-firestore-indexes/package.json +2 -2
- package/generate-mcp-manifest/main.js +1 -0
- package/generate-mcp-manifest/package.json +3 -3
- package/generate-route-manifest/package.json +2 -2
- package/index.esm.js +12615 -6863
- package/lint-cache/package.json +2 -2
- package/manifest-extract/index.esm.js +4 -1
- package/manifest-extract/package.json +7 -7
- package/manifest-extract/src/lib/types.d.ts +6 -0
- package/model-test/LICENSE +21 -0
- package/model-test/index.d.ts +1 -0
- package/model-test/index.esm.js +5984 -0
- package/model-test/package.json +21 -0
- package/model-test/src/index.d.ts +41 -0
- package/model-test/src/lib/fixture/archetype.d.ts +33 -0
- package/model-test/src/lib/fixture/extract.d.ts +37 -0
- package/model-test/src/lib/fixture/format.json.d.ts +24 -0
- package/model-test/src/lib/fixture/format.markdown.d.ts +23 -0
- package/model-test/src/lib/fixture/forward.d.ts +48 -0
- package/model-test/src/lib/fixture/framework-fixtures.d.ts +70 -0
- package/model-test/src/lib/fixture/inspect.d.ts +22 -0
- package/model-test/src/lib/fixture/scaffold.d.ts +74 -0
- package/model-test/src/lib/fixture/types.d.ts +175 -0
- package/model-test/src/lib/test/discover.d.ts +81 -0
- package/model-test/src/lib/test/extract.d.ts +49 -0
- package/model-test/src/lib/test/format.hotspots.d.ts +23 -0
- package/model-test/src/lib/test/format.json.d.ts +25 -0
- package/model-test/src/lib/test/format.list-app.d.ts +30 -0
- package/model-test/src/lib/test/format.markdown.d.ts +25 -0
- package/model-test/src/lib/test/hotspots.d.ts +98 -0
- package/model-test/src/lib/test/inspect.d.ts +32 -0
- package/model-test/src/lib/test/search.d.ts +34 -0
- package/model-test/src/lib/test/types.d.ts +179 -0
- package/package.json +23 -22
- package/route/package.json +12 -12
- package/src/lib/api/firestore-session.client.d.ts +54 -0
- package/src/lib/api/get-args.helper.d.ts +4 -1
- package/src/lib/api/get-many.command.d.ts +8 -3
- package/src/lib/api/get.command.d.ts +13 -5
- package/src/lib/api/index.d.ts +1 -0
- package/src/lib/config/env.d.ts +106 -0
- package/src/lib/config/firestore-session.cache.d.ts +86 -0
- package/src/lib/config/index.d.ts +1 -0
- package/src/lib/config/paths.d.ts +3 -1
- package/src/lib/context/cli.context.d.ts +87 -3
- package/src/lib/doctor/firestore-session.check.d.ts +113 -0
- package/src/lib/doctor/index.d.ts +1 -0
- package/src/lib/firestore/firestore-get.command.d.ts +28 -0
- package/src/lib/firestore/firestore-queries.command.d.ts +26 -0
- package/src/lib/firestore/firestore-query.command.d.ts +25 -0
- package/src/lib/firestore/firestore.accessor.d.ts +85 -0
- package/src/lib/firestore/firestore.collection.d.ts +44 -0
- package/src/lib/firestore/firestore.error.d.ts +14 -0
- package/src/lib/firestore/firestore.models.d.ts +223 -0
- package/src/lib/firestore/firestore.query-params.d.ts +34 -0
- package/src/lib/firestore/firestore.query.d.ts +79 -0
- package/src/lib/firestore/firestore.read.d.ts +180 -0
- package/src/lib/firestore/firestore.session.d.ts +97 -0
- package/src/lib/firestore/index.d.ts +13 -0
- package/src/lib/firestore/query-info-utils.d.ts +48 -0
- package/src/lib/firestore/query-registry.d.ts +32 -0
- package/src/lib/index.d.ts +3 -0
- package/src/lib/manifest/types.d.ts +117 -0
- package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/model-snapshot-fields-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/pipes-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/ui-components-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/utils-schema.d.ts +2 -2
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-extract.d.ts +1 -1
- package/src/lib/mcp-scan/scan/extract-models/types.d.ts +5 -0
- package/src/lib/mcp-scan/scan/ui-components-extract.d.ts +1 -1
- package/src/lib/middleware/auth.middleware.d.ts +6 -0
- package/src/lib/runner/run.d.ts +28 -1
- package/src/lib/scan-helpers/emit-generated-ts.d.ts +76 -0
- package/src/lib/scan-helpers/exported-from-package.d.ts +29 -0
- package/src/lib/util/index.d.ts +1 -0
- package/src/lib/util/output.d.ts +10 -0
- package/src/lib/util/stdin.d.ts +39 -2
- package/src/lib/util/table.d.ts +40 -0
- package/test/index.esm.js +4 -2
- package/test/package.json +14 -14
- package/test/src/lib/cli-test.d.ts +12 -2
- package/validate/LICENSE +21 -0
- package/validate/index.js +16641 -0
- package/validate/package.json +11 -0
- package/eslint/index.cjs.default.js +0 -1
- package/eslint/index.cjs.js +0 -1112
- package/eslint/index.cjs.mjs +0 -2
- package/index.cjs.js +0 -59813
- package/manifest-extract/index.cjs.default.js +0 -1
- package/manifest-extract/index.cjs.js +0 -1592
- package/manifest-extract/index.cjs.mjs +0 -2
- package/route/index.cjs.default.js +0 -1
- package/route/index.cjs.js +0 -18
- package/route/index.cjs.mjs +0 -2
- package/test/index.cjs.default.js +0 -1
- package/test/index.cjs.js +0 -381
- package/test/index.cjs.mjs +0 -2
package/eslint/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli/eslint",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.39.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/dbx-cli": "13.
|
|
6
|
-
"@dereekb/util": "13.
|
|
6
|
+
"@dereekb/dbx-cli": "13.39.0",
|
|
7
|
+
"@dereekb/util": "13.39.0",
|
|
7
8
|
"@typescript-eslint/utils": "8.59.3",
|
|
8
9
|
"typescript": "5.9.3"
|
|
9
10
|
},
|
|
@@ -14,13 +15,12 @@
|
|
|
14
15
|
"exports": {
|
|
15
16
|
"./package.json": "./package.json",
|
|
16
17
|
".": {
|
|
17
|
-
"module": "./index.esm.js",
|
|
18
18
|
"types": "./index.d.ts",
|
|
19
|
-
"import": "./index.
|
|
20
|
-
"default": "./index.
|
|
19
|
+
"import": "./index.esm.js",
|
|
20
|
+
"default": "./index.esm.js"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"module": "./index.esm.js",
|
|
24
|
-
"main": "./index.
|
|
24
|
+
"main": "./index.esm.js",
|
|
25
25
|
"types": "./index.d.ts"
|
|
26
|
-
}
|
|
26
|
+
}
|
|
@@ -3,15 +3,56 @@ import { createRequire as __createRequire } from 'node:module';
|
|
|
3
3
|
const require = __createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/main.ts
|
|
6
|
-
import { existsSync as
|
|
7
|
-
import {
|
|
6
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
7
|
+
import { isAbsolute as isAbsolute3, relative as relative2, resolve as resolve3 } from "node:path";
|
|
8
|
+
|
|
9
|
+
// packages/util/src/lib/string/sort.ts
|
|
10
|
+
var compareStrings = (a, b) => a.localeCompare(b);
|
|
11
|
+
|
|
12
|
+
// packages/dbx-cli/src/lib/scan-helpers/emit-generated-ts.ts
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { format, resolveConfig } from "prettier";
|
|
16
|
+
// @__NO_SIDE_EFFECTS__
|
|
17
|
+
function renderGroupedImportLines(imports) {
|
|
18
|
+
const importsByPackage = /* @__PURE__ */ new Map();
|
|
19
|
+
for (const { packageName, identifier } of imports) {
|
|
20
|
+
if (!packageName || !identifier) continue;
|
|
21
|
+
const set = importsByPackage.get(packageName) ?? /* @__PURE__ */ new Set();
|
|
22
|
+
set.add(identifier);
|
|
23
|
+
importsByPackage.set(packageName, set);
|
|
24
|
+
}
|
|
25
|
+
return [...importsByPackage.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([pkg, names]) => {
|
|
26
|
+
const sortedNames = [...names].sort(compareStrings).join(", ");
|
|
27
|
+
return `import { ${sortedNames} } from '${pkg}';`;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function formatGeneratedTs(source, outputFile) {
|
|
31
|
+
const config = await resolveConfig(outputFile);
|
|
32
|
+
return format(source, { ...config, filepath: outputFile });
|
|
33
|
+
}
|
|
34
|
+
function writeGeneratedTsFile(input) {
|
|
35
|
+
const { outputFile, contents } = input;
|
|
36
|
+
const outputDir = dirname(outputFile);
|
|
37
|
+
let result;
|
|
38
|
+
if (!existsSync(outputDir)) {
|
|
39
|
+
mkdirSync(outputDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
if (existsSync(outputFile) && readFileSync(outputFile, "utf8") === contents) {
|
|
42
|
+
result = "unchanged";
|
|
43
|
+
} else {
|
|
44
|
+
writeFileSync(outputFile, contents);
|
|
45
|
+
result = "wrote";
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
8
49
|
|
|
9
50
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/parse-functions.ts
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
51
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
11
52
|
import { Node, Project } from "ts-morph";
|
|
12
53
|
function parseFunctionsConfig(functionsTsPath) {
|
|
13
54
|
const project = new Project({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
14
|
-
const text =
|
|
55
|
+
const text = readFileSync2(functionsTsPath, "utf8");
|
|
15
56
|
const sourceFile = project.createSourceFile("functions.ts", text, { overwrite: true });
|
|
16
57
|
const importIdentToModule = collectImportSpecifierMap(sourceFile);
|
|
17
58
|
const result = [];
|
|
@@ -69,8 +110,8 @@ function collectImportSpecifierMap(sourceFile) {
|
|
|
69
110
|
}
|
|
70
111
|
|
|
71
112
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/resolve-package.ts
|
|
72
|
-
import { existsSync, readFileSync as
|
|
73
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
113
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
114
|
+
import { dirname as dirname2, isAbsolute, join, relative, resolve } from "node:path";
|
|
74
115
|
var _pathsCache;
|
|
75
116
|
function loadTsconfigPaths(workspaceRoot) {
|
|
76
117
|
let result;
|
|
@@ -78,7 +119,7 @@ function loadTsconfigPaths(workspaceRoot) {
|
|
|
78
119
|
result = _pathsCache;
|
|
79
120
|
} else {
|
|
80
121
|
const tsconfigPath = join(workspaceRoot, "tsconfig.base.json");
|
|
81
|
-
const raw =
|
|
122
|
+
const raw = readFileSync3(tsconfigPath, "utf8");
|
|
82
123
|
const cleaned = stripJsonComments(raw);
|
|
83
124
|
const tsconfig = JSON.parse(cleaned);
|
|
84
125
|
const paths = tsconfig.compilerOptions?.paths ?? {};
|
|
@@ -97,7 +138,7 @@ function resolveModuleToPackage(input) {
|
|
|
97
138
|
const { workspaceRoot, importingFile, moduleSpecifier } = input;
|
|
98
139
|
let result;
|
|
99
140
|
if (moduleSpecifier.startsWith(".")) {
|
|
100
|
-
const importingDir =
|
|
141
|
+
const importingDir = dirname2(importingFile);
|
|
101
142
|
const candidatePath = resolve(importingDir, moduleSpecifier);
|
|
102
143
|
result = locatePackageForPath(workspaceRoot, candidatePath);
|
|
103
144
|
} else {
|
|
@@ -113,38 +154,38 @@ function resolveModuleToPackage(input) {
|
|
|
113
154
|
}
|
|
114
155
|
function locatePackageInNodeModules(input) {
|
|
115
156
|
const { importingFile, moduleSpecifier } = input;
|
|
116
|
-
let current =
|
|
157
|
+
let current = dirname2(importingFile);
|
|
117
158
|
let result;
|
|
118
|
-
while (!result && current && current !==
|
|
159
|
+
while (!result && current && current !== dirname2(current)) {
|
|
119
160
|
const candidateRoot = join(current, "node_modules", moduleSpecifier);
|
|
120
161
|
const pkgJson = join(candidateRoot, "package.json");
|
|
121
|
-
if (
|
|
162
|
+
if (existsSync2(pkgJson)) {
|
|
122
163
|
try {
|
|
123
|
-
const pkg = JSON.parse(
|
|
164
|
+
const pkg = JSON.parse(readFileSync3(pkgJson, "utf8"));
|
|
124
165
|
if (pkg.name) {
|
|
125
166
|
result = { packageName: pkg.name, packageRoot: candidateRoot };
|
|
126
167
|
}
|
|
127
168
|
} catch {
|
|
128
169
|
}
|
|
129
170
|
}
|
|
130
|
-
if (!result) current =
|
|
171
|
+
if (!result) current = dirname2(current);
|
|
131
172
|
}
|
|
132
173
|
return result;
|
|
133
174
|
}
|
|
134
175
|
function locatePackageForPath(workspaceRoot, startPath) {
|
|
135
176
|
let current = startPath;
|
|
136
177
|
let result;
|
|
137
|
-
while (!result && current && current !== workspaceRoot && current !==
|
|
138
|
-
if (
|
|
178
|
+
while (!result && current && current !== workspaceRoot && current !== dirname2(current)) {
|
|
179
|
+
if (existsSync2(join(current, "package.json"))) {
|
|
139
180
|
try {
|
|
140
|
-
const pkg = JSON.parse(
|
|
181
|
+
const pkg = JSON.parse(readFileSync3(join(current, "package.json"), "utf8"));
|
|
141
182
|
if (pkg.name) {
|
|
142
183
|
result = { packageName: pkg.name, packageRoot: current };
|
|
143
184
|
}
|
|
144
185
|
} catch {
|
|
145
186
|
}
|
|
146
187
|
}
|
|
147
|
-
if (!result) current =
|
|
188
|
+
if (!result) current = dirname2(current);
|
|
148
189
|
}
|
|
149
190
|
return result;
|
|
150
191
|
}
|
|
@@ -156,8 +197,8 @@ function stripJsonComments(text) {
|
|
|
156
197
|
}
|
|
157
198
|
|
|
158
199
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/find-api-files.ts
|
|
159
|
-
import { readdirSync, readFileSync as
|
|
160
|
-
import { join as
|
|
200
|
+
import { readdirSync, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
|
|
201
|
+
import { join as join3 } from "node:path";
|
|
161
202
|
|
|
162
203
|
// packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
|
|
163
204
|
import { Node as Node2, Project as Project2 } from "ts-morph";
|
|
@@ -469,9 +510,6 @@ function readJsDocSummary(node) {
|
|
|
469
510
|
return result;
|
|
470
511
|
}
|
|
471
512
|
|
|
472
|
-
// packages/util/src/lib/string/sort.ts
|
|
473
|
-
var compareStrings = (a, b) => a.localeCompare(b);
|
|
474
|
-
|
|
475
513
|
// packages/dbx-cli/src/lib/scan-helpers/firestore-model-extract-utils.ts
|
|
476
514
|
import { Node as Node3 } from "ts-morph";
|
|
477
515
|
var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
|
|
@@ -548,6 +586,114 @@ function identifierName(node) {
|
|
|
548
586
|
return result;
|
|
549
587
|
}
|
|
550
588
|
|
|
589
|
+
// packages/dbx-cli/src/lib/scan-helpers/exported-from-package.ts
|
|
590
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
591
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join2, resolve as resolve2 } from "node:path";
|
|
592
|
+
function isExportedFromPackage(input) {
|
|
593
|
+
const { packageRoot, identifier } = input;
|
|
594
|
+
const indexPath = locateBarrelEntry(packageRoot);
|
|
595
|
+
return indexPath ? findIdentifierInBarrelChain(indexPath, identifier, /* @__PURE__ */ new Set()) : false;
|
|
596
|
+
}
|
|
597
|
+
function locateBarrelEntry(packageRoot) {
|
|
598
|
+
const candidates = [join2(packageRoot, "src", "index.ts"), join2(packageRoot, "src", "index.d.ts"), join2(packageRoot, "index.d.ts"), join2(packageRoot, "index.ts")];
|
|
599
|
+
return candidates.find((candidate) => existsSync3(candidate));
|
|
600
|
+
}
|
|
601
|
+
var EXPORT_DECL_PATTERNS = [/export\s+(?:declare\s+)?const\s+IDENT\b/, /export\s+(?:declare\s+)?function\s+IDENT\b/, /export\s*\{[^}]*\bIDENT\b[^}]*\}/];
|
|
602
|
+
function findIdentifierInBarrelChain(filePath, identifier, visited) {
|
|
603
|
+
let result = false;
|
|
604
|
+
if (!visited.has(filePath)) {
|
|
605
|
+
visited.add(filePath);
|
|
606
|
+
const text = readFileTextSafe(filePath);
|
|
607
|
+
if (text !== void 0) {
|
|
608
|
+
result = barrelTextDeclaresIdentifier(text, identifier) || reExportChainHasIdentifier({ fromFile: filePath, text, identifier, visited });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
function readFileTextSafe(filePath) {
|
|
614
|
+
let text;
|
|
615
|
+
try {
|
|
616
|
+
text = readFileSync4(filePath, "utf8");
|
|
617
|
+
} catch {
|
|
618
|
+
text = void 0;
|
|
619
|
+
}
|
|
620
|
+
return text;
|
|
621
|
+
}
|
|
622
|
+
function barrelTextDeclaresIdentifier(text, identifier) {
|
|
623
|
+
let result = false;
|
|
624
|
+
for (const pattern of EXPORT_DECL_PATTERNS) {
|
|
625
|
+
const re = new RegExp(pattern.source.replace("IDENT", escapeRegExp(identifier)));
|
|
626
|
+
if (re.test(text)) {
|
|
627
|
+
result = true;
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
function reExportChainHasIdentifier(input) {
|
|
634
|
+
const { fromFile, text, identifier, visited } = input;
|
|
635
|
+
let result = false;
|
|
636
|
+
const dir = dirname3(fromFile);
|
|
637
|
+
for (const reExportTarget of collectReExportTargets(text)) {
|
|
638
|
+
const resolved = resolveReExport(dir, reExportTarget);
|
|
639
|
+
if (resolved && findIdentifierInBarrelChain(resolved, identifier, visited)) {
|
|
640
|
+
result = true;
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return result;
|
|
645
|
+
}
|
|
646
|
+
function collectReExportTargets(text) {
|
|
647
|
+
const out = [];
|
|
648
|
+
const re = /export\s*(?:\*|\{[^}]*\})\s*from\s*['"]([^'"]+)['"]/g;
|
|
649
|
+
let match;
|
|
650
|
+
while ((match = re.exec(text)) !== null) {
|
|
651
|
+
out.push(match[1]);
|
|
652
|
+
}
|
|
653
|
+
return out;
|
|
654
|
+
}
|
|
655
|
+
function resolveReExport(fromDir, target) {
|
|
656
|
+
let result;
|
|
657
|
+
if (target.startsWith(".")) {
|
|
658
|
+
const candidate = isAbsolute2(target) ? target : resolve2(fromDir, target);
|
|
659
|
+
for (const ext of [".ts", ".mts", ".d.ts", "/index.ts", "/index.mts", "/index.d.ts"]) {
|
|
660
|
+
const probe = hasTsModuleExtension(candidate) ? candidate : candidate + ext;
|
|
661
|
+
const resolved = resolveExistingTsPath(probe);
|
|
662
|
+
if (resolved) {
|
|
663
|
+
result = resolved;
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
function hasTsModuleExtension(value) {
|
|
671
|
+
return value.endsWith(".ts") || value.endsWith(".mts");
|
|
672
|
+
}
|
|
673
|
+
function resolveExistingTsPath(probe) {
|
|
674
|
+
let result;
|
|
675
|
+
if (existsSync3(probe)) {
|
|
676
|
+
const stat = statSync(probe);
|
|
677
|
+
if (stat.isFile()) {
|
|
678
|
+
result = probe;
|
|
679
|
+
} else if (stat.isDirectory()) {
|
|
680
|
+
const sourceIndex = join2(probe, "index.ts");
|
|
681
|
+
if (existsSync3(sourceIndex)) {
|
|
682
|
+
result = sourceIndex;
|
|
683
|
+
} else {
|
|
684
|
+
const declarationIndex = join2(probe, "index.d.ts");
|
|
685
|
+
if (existsSync3(declarationIndex)) {
|
|
686
|
+
result = declarationIndex;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return result;
|
|
692
|
+
}
|
|
693
|
+
function escapeRegExp(value) {
|
|
694
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
695
|
+
}
|
|
696
|
+
|
|
551
697
|
// packages/dbx-cli/manifest-extract/src/lib/extract-models.ts
|
|
552
698
|
import { Node as Node4, Project as Project3 } from "ts-morph";
|
|
553
699
|
var READ_LEVEL_VALUES = /* @__PURE__ */ new Set(["system", "owner", "admin-only", "permissions"]);
|
|
@@ -602,6 +748,7 @@ function buildInterface(decl) {
|
|
|
602
748
|
const jsDocs = decl.getJsDocs();
|
|
603
749
|
const hasDbxModelTag = jsDocsHaveTag(jsDocs, "dbxModel");
|
|
604
750
|
const dbxModelRead = readDbxModelReadTag(jsDocs);
|
|
751
|
+
const dbxModelServerOnly = jsDocsHaveTag(jsDocs, "dbxModelServerOnly");
|
|
605
752
|
const mcpToolNameSegment = readMcpToolNameSegmentTag(jsDocs);
|
|
606
753
|
const extendsNames = decl.getExtends().map(resolveExtendsName);
|
|
607
754
|
const props = [];
|
|
@@ -627,6 +774,7 @@ function buildInterface(decl) {
|
|
|
627
774
|
extendsNames,
|
|
628
775
|
props,
|
|
629
776
|
...dbxModelRead === void 0 ? {} : { dbxModelRead },
|
|
777
|
+
...dbxModelServerOnly ? { dbxModelServerOnly: true } : {},
|
|
630
778
|
...mcpToolNameSegment === void 0 ? {} : { mcpToolNameSegment }
|
|
631
779
|
};
|
|
632
780
|
}
|
|
@@ -934,12 +1082,12 @@ function firstParagraph(text) {
|
|
|
934
1082
|
|
|
935
1083
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/find-api-files.ts
|
|
936
1084
|
function findApiFiles(packageRoot) {
|
|
937
|
-
const libRoot =
|
|
1085
|
+
const libRoot = join3(packageRoot, "src", "lib");
|
|
938
1086
|
const out = [];
|
|
939
1087
|
if (safeIsDirectory(libRoot)) {
|
|
940
1088
|
const seenClassNames = /* @__PURE__ */ new Set();
|
|
941
1089
|
for (const file of walkApiFiles(libRoot)) {
|
|
942
|
-
const text =
|
|
1090
|
+
const text = readFileSync5(file, "utf8");
|
|
943
1091
|
const extraction = extractCrudEntries({ name: file, text });
|
|
944
1092
|
if (!extraction.functionsClassName) continue;
|
|
945
1093
|
if (seenClassNames.has(extraction.functionsClassName)) continue;
|
|
@@ -952,8 +1100,8 @@ function findApiFiles(packageRoot) {
|
|
|
952
1100
|
function* walkApiFiles(dir) {
|
|
953
1101
|
for (const entry of readdirSync(dir).sort()) {
|
|
954
1102
|
if (entry === "node_modules" || entry === "dist") continue;
|
|
955
|
-
const p =
|
|
956
|
-
const stat =
|
|
1103
|
+
const p = join3(dir, entry);
|
|
1104
|
+
const stat = statSync2(p);
|
|
957
1105
|
if (stat.isDirectory()) {
|
|
958
1106
|
yield* walkApiFiles(p);
|
|
959
1107
|
} else if (isApiFile(entry)) {
|
|
@@ -968,7 +1116,7 @@ function isApiFile(name) {
|
|
|
968
1116
|
function safeIsDirectory(p) {
|
|
969
1117
|
let result;
|
|
970
1118
|
try {
|
|
971
|
-
result =
|
|
1119
|
+
result = statSync2(p).isDirectory();
|
|
972
1120
|
} catch {
|
|
973
1121
|
result = false;
|
|
974
1122
|
}
|
|
@@ -976,14 +1124,14 @@ function safeIsDirectory(p) {
|
|
|
976
1124
|
}
|
|
977
1125
|
|
|
978
1126
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/find-model-files.ts
|
|
979
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
980
|
-
import { join as
|
|
1127
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
|
|
1128
|
+
import { join as join4 } from "node:path";
|
|
981
1129
|
function findModelFiles(packageRoot) {
|
|
982
|
-
const libRoot =
|
|
1130
|
+
const libRoot = join4(packageRoot, "src", "lib");
|
|
983
1131
|
const out = [];
|
|
984
1132
|
if (safeIsDirectory2(libRoot)) {
|
|
985
1133
|
for (const filePath of walkSourceFiles(libRoot)) {
|
|
986
|
-
const text =
|
|
1134
|
+
const text = readFileSync6(filePath, "utf8");
|
|
987
1135
|
if (!textHasModelMarker(text)) continue;
|
|
988
1136
|
const extraction = extractModelsFromSource({ name: filePath, text });
|
|
989
1137
|
if (extraction.identities.length === 0 && extraction.modelGroups.length === 0 && extraction.converters.length === 0 && extraction.serviceFactories.length === 0) continue;
|
|
@@ -998,8 +1146,8 @@ function textHasModelMarker(text) {
|
|
|
998
1146
|
function* walkSourceFiles(dir) {
|
|
999
1147
|
for (const entry of readdirSync2(dir).sort()) {
|
|
1000
1148
|
if (entry === "node_modules" || entry === "dist") continue;
|
|
1001
|
-
const p =
|
|
1002
|
-
const stat =
|
|
1149
|
+
const p = join4(dir, entry);
|
|
1150
|
+
const stat = statSync3(p);
|
|
1003
1151
|
if (stat.isDirectory()) {
|
|
1004
1152
|
yield* walkSourceFiles(p);
|
|
1005
1153
|
} else if (isCandidateSourceFile(entry)) {
|
|
@@ -1013,7 +1161,7 @@ function isCandidateSourceFile(name) {
|
|
|
1013
1161
|
function safeIsDirectory2(p) {
|
|
1014
1162
|
let result;
|
|
1015
1163
|
try {
|
|
1016
|
-
result =
|
|
1164
|
+
result = statSync3(p).isDirectory();
|
|
1017
1165
|
} catch {
|
|
1018
1166
|
result = false;
|
|
1019
1167
|
}
|
|
@@ -1162,6 +1310,7 @@ function buildManifestEntry(input) {
|
|
|
1162
1310
|
fields,
|
|
1163
1311
|
...iface.mcpToolNameSegment ? { mcpToolNameSegment: iface.mcpToolNameSegment } : {},
|
|
1164
1312
|
...iface.dbxModelRead ? { read: iface.dbxModelRead } : {},
|
|
1313
|
+
...iface.dbxModelServerOnly ? { serverOnly: true } : {},
|
|
1165
1314
|
...serviceFactory ? { serviceFactory } : {}
|
|
1166
1315
|
};
|
|
1167
1316
|
}
|
|
@@ -1298,131 +1447,19 @@ function capitalize(s) {
|
|
|
1298
1447
|
}
|
|
1299
1448
|
|
|
1300
1449
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/bind-validators.ts
|
|
1301
|
-
import { existsSync as existsSync2, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync3 } from "node:fs";
|
|
1302
|
-
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
|
|
1303
1450
|
function deriveValidatorName(paramsTypeName) {
|
|
1304
1451
|
return paramsTypeName ? paramsTypeName.charAt(0).toLowerCase() + paramsTypeName.slice(1) + "Type" : "";
|
|
1305
1452
|
}
|
|
1306
|
-
function isExportedFromPackage(input) {
|
|
1307
|
-
const { packageRoot, identifier } = input;
|
|
1308
|
-
const indexPath = locateBarrelEntry(packageRoot);
|
|
1309
|
-
return indexPath ? findIdentifierInBarrelChain(indexPath, identifier, /* @__PURE__ */ new Set()) : false;
|
|
1310
|
-
}
|
|
1311
|
-
function locateBarrelEntry(packageRoot) {
|
|
1312
|
-
const candidates = [join4(packageRoot, "src", "index.ts"), join4(packageRoot, "src", "index.d.ts"), join4(packageRoot, "index.d.ts"), join4(packageRoot, "index.ts")];
|
|
1313
|
-
return candidates.find((candidate) => existsSync2(candidate));
|
|
1314
|
-
}
|
|
1315
|
-
var EXPORT_DECL_PATTERNS = [/export\s+(?:declare\s+)?const\s+IDENT\b/, /export\s+(?:declare\s+)?function\s+IDENT\b/, /export\s*\{[^}]*\bIDENT\b[^}]*\}/];
|
|
1316
|
-
function findIdentifierInBarrelChain(filePath, identifier, visited) {
|
|
1317
|
-
let result = false;
|
|
1318
|
-
if (!visited.has(filePath)) {
|
|
1319
|
-
visited.add(filePath);
|
|
1320
|
-
const text = readFileTextSafe(filePath);
|
|
1321
|
-
if (text !== void 0) {
|
|
1322
|
-
result = barrelTextDeclaresIdentifier(text, identifier) || reExportChainHasIdentifier({ fromFile: filePath, text, identifier, visited });
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
return result;
|
|
1326
|
-
}
|
|
1327
|
-
function readFileTextSafe(filePath) {
|
|
1328
|
-
let text;
|
|
1329
|
-
try {
|
|
1330
|
-
text = readFileSync5(filePath, "utf8");
|
|
1331
|
-
} catch {
|
|
1332
|
-
text = void 0;
|
|
1333
|
-
}
|
|
1334
|
-
return text;
|
|
1335
|
-
}
|
|
1336
|
-
function barrelTextDeclaresIdentifier(text, identifier) {
|
|
1337
|
-
let result = false;
|
|
1338
|
-
for (const pattern of EXPORT_DECL_PATTERNS) {
|
|
1339
|
-
const re = new RegExp(pattern.source.replace("IDENT", escapeRegExp(identifier)));
|
|
1340
|
-
if (re.test(text)) {
|
|
1341
|
-
result = true;
|
|
1342
|
-
break;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
return result;
|
|
1346
|
-
}
|
|
1347
|
-
function reExportChainHasIdentifier(input) {
|
|
1348
|
-
const { fromFile, text, identifier, visited } = input;
|
|
1349
|
-
let result = false;
|
|
1350
|
-
const dir = dirname2(fromFile);
|
|
1351
|
-
for (const reExportTarget of collectReExportTargets(text)) {
|
|
1352
|
-
const resolved = resolveReExport(dir, reExportTarget);
|
|
1353
|
-
if (resolved && findIdentifierInBarrelChain(resolved, identifier, visited)) {
|
|
1354
|
-
result = true;
|
|
1355
|
-
break;
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
return result;
|
|
1359
|
-
}
|
|
1360
|
-
function collectReExportTargets(text) {
|
|
1361
|
-
const out = [];
|
|
1362
|
-
const re = /export\s*(?:\*|\{[^}]*\})\s*from\s*['"]([^'"]+)['"]/g;
|
|
1363
|
-
let match;
|
|
1364
|
-
while ((match = re.exec(text)) !== null) {
|
|
1365
|
-
out.push(match[1]);
|
|
1366
|
-
}
|
|
1367
|
-
return out;
|
|
1368
|
-
}
|
|
1369
|
-
function resolveReExport(fromDir, target) {
|
|
1370
|
-
let result;
|
|
1371
|
-
if (target.startsWith(".")) {
|
|
1372
|
-
const candidate = isAbsolute2(target) ? target : resolve2(fromDir, target);
|
|
1373
|
-
for (const ext of [".ts", ".mts", ".d.ts", "/index.ts", "/index.mts", "/index.d.ts"]) {
|
|
1374
|
-
const probe = hasTsModuleExtension(candidate) ? candidate : candidate + ext;
|
|
1375
|
-
const resolved = resolveExistingTsPath(probe);
|
|
1376
|
-
if (resolved) {
|
|
1377
|
-
result = resolved;
|
|
1378
|
-
break;
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
return result;
|
|
1383
|
-
}
|
|
1384
|
-
function hasTsModuleExtension(value) {
|
|
1385
|
-
return value.endsWith(".ts") || value.endsWith(".mts");
|
|
1386
|
-
}
|
|
1387
|
-
function resolveExistingTsPath(probe) {
|
|
1388
|
-
let result;
|
|
1389
|
-
if (existsSync2(probe)) {
|
|
1390
|
-
const stat = statSync3(probe);
|
|
1391
|
-
if (stat.isFile()) {
|
|
1392
|
-
result = probe;
|
|
1393
|
-
} else if (stat.isDirectory()) {
|
|
1394
|
-
const sourceIndex = join4(probe, "index.ts");
|
|
1395
|
-
if (existsSync2(sourceIndex)) {
|
|
1396
|
-
result = sourceIndex;
|
|
1397
|
-
} else {
|
|
1398
|
-
const declarationIndex = join4(probe, "index.d.ts");
|
|
1399
|
-
if (existsSync2(declarationIndex)) {
|
|
1400
|
-
result = declarationIndex;
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
return result;
|
|
1406
|
-
}
|
|
1407
|
-
function escapeRegExp(value) {
|
|
1408
|
-
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
1409
|
-
}
|
|
1410
1453
|
|
|
1411
1454
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/emit.ts
|
|
1412
|
-
import { format, resolveConfig } from "prettier";
|
|
1413
1455
|
async function renderManifest(input) {
|
|
1414
1456
|
const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace, enumEntries, enumNamespace, emitConverters = false } = input;
|
|
1415
|
-
const
|
|
1457
|
+
const validatorImports = [];
|
|
1416
1458
|
for (const entry of entries) {
|
|
1417
1459
|
if (!entry.packageName || !entry.validatorName) continue;
|
|
1418
|
-
|
|
1419
|
-
set.add(entry.validatorName);
|
|
1420
|
-
importsByPackage.set(entry.packageName, set);
|
|
1460
|
+
validatorImports.push({ packageName: entry.packageName, identifier: entry.validatorName });
|
|
1421
1461
|
}
|
|
1422
|
-
const importLines =
|
|
1423
|
-
const sortedNames = [...names].sort(compareStrings).join(", ");
|
|
1424
|
-
return `import { ${sortedNames} } from '${pkg}';`;
|
|
1425
|
-
});
|
|
1462
|
+
const importLines = renderGroupedImportLines(validatorImports);
|
|
1426
1463
|
const entryLines = entries.map((e) => renderEntry(e));
|
|
1427
1464
|
const emitModels = Boolean(modelEntries && modelEntries.length > 0 && modelNamespace);
|
|
1428
1465
|
const emitEnums = Boolean(enumEntries && Object.keys(enumEntries).length > 0 && enumNamespace);
|
|
@@ -1449,7 +1486,7 @@ export const ${namespace}: CliApiManifest = [
|
|
|
1449
1486
|
${entryLines.join(",\n")}
|
|
1450
1487
|
];${modelSection}${enumSection}
|
|
1451
1488
|
`;
|
|
1452
|
-
return
|
|
1489
|
+
return formatGeneratedTs(source, outputFile);
|
|
1453
1490
|
}
|
|
1454
1491
|
function renderEntry({ entry, validatorName }) {
|
|
1455
1492
|
const fields = [
|
|
@@ -1480,10 +1517,6 @@ function renderDocFields(fields) {
|
|
|
1480
1517
|
});
|
|
1481
1518
|
return `[${items.join(", ")}]`;
|
|
1482
1519
|
}
|
|
1483
|
-
async function formatWithPrettier(source, outputFile) {
|
|
1484
|
-
const config = await resolveConfig(outputFile);
|
|
1485
|
-
return format(source, { ...config, filepath: outputFile });
|
|
1486
|
-
}
|
|
1487
1520
|
function renderModelEntry(entry, emitConverters) {
|
|
1488
1521
|
const fields = [
|
|
1489
1522
|
`modelType: ${JSON.stringify(entry.modelType)}`,
|
|
@@ -1498,6 +1531,7 @@ function renderModelEntry(entry, emitConverters) {
|
|
|
1498
1531
|
`fields: ${renderModelFields(entry.fields, emitConverters)}`,
|
|
1499
1532
|
entry.mcpToolNameSegment ? `mcpToolNameSegment: ${JSON.stringify(entry.mcpToolNameSegment)}` : void 0,
|
|
1500
1533
|
entry.read ? `read: ${JSON.stringify(entry.read)}` : void 0,
|
|
1534
|
+
entry.serverOnly ? "serverOnly: true" : void 0,
|
|
1501
1535
|
entry.serviceFactory ? `serviceFactory: { exportName: ${JSON.stringify(entry.serviceFactory.exportName)}, sourceFile: ${JSON.stringify(entry.serviceFactory.sourceFile)} }` : void 0
|
|
1502
1536
|
];
|
|
1503
1537
|
return ` { ${fields.filter(Boolean).join(", ")} }`;
|
|
@@ -1556,10 +1590,9 @@ async function main() {
|
|
|
1556
1590
|
}
|
|
1557
1591
|
const functionsConfigPath = resolveWorkspacePath(flags.functionsConfig);
|
|
1558
1592
|
const outputFile = resolveWorkspacePath(flags.output);
|
|
1559
|
-
const outputDir = dirname3(outputFile);
|
|
1560
1593
|
const projectName = flags.project ?? "<cli>";
|
|
1561
1594
|
const namespace = deriveNamespace(flags.project);
|
|
1562
|
-
if (!
|
|
1595
|
+
if (!existsSync4(functionsConfigPath)) {
|
|
1563
1596
|
throw new Error(`functions-config file not found: ${functionsConfigPath}`);
|
|
1564
1597
|
}
|
|
1565
1598
|
const groups = parseFunctionsConfig(functionsConfigPath);
|
|
@@ -1637,14 +1670,9 @@ async function main() {
|
|
|
1637
1670
|
const modelEntries = flags.emitModels ? assembleModels({ extractions: modelSources }) : [];
|
|
1638
1671
|
const filteredModelEntries = flags.only ? modelEntries.filter((m) => flags.only?.has(m.modelType)) : modelEntries;
|
|
1639
1672
|
const enumEntries = flags.emitModels ? collectModelEnums({ extractions: modelSources, models: filteredModelEntries }) : {};
|
|
1640
|
-
ensureOutputDir(outputDir);
|
|
1641
1673
|
const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project), enumEntries, enumNamespace: deriveEnumNamespace(flags.project), emitConverters: flags.emitModelConverters });
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
} else {
|
|
1645
|
-
writeFileSync(outputFile, formatted);
|
|
1646
|
-
console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputFile)}`);
|
|
1647
|
-
}
|
|
1674
|
+
const outcome = writeGeneratedTsFile({ outputFile, contents: formatted });
|
|
1675
|
+
console.log(`[${outcome}] ${relative2(WORKSPACE_ROOT, outputFile)}`);
|
|
1648
1676
|
const groupCount = packageCache.size === 0 ? 0 : new Set(collected.map((c) => c.entry.groupName)).size;
|
|
1649
1677
|
const modelSummary = flags.emitModels ? ` \xB7 ${filteredModelEntries.length} models \xB7 ${Object.keys(enumEntries).length} enums` : "";
|
|
1650
1678
|
console.log(`Summary: ${groupCount} groups \xB7 ${collected.length} entries \xB7 ${collected.length - missingValidators} validators bound \xB7 ${missingValidators} missing \xB7 ${skippedGroups} skipped${modelSummary}`);
|
|
@@ -1664,9 +1692,6 @@ function compareEntries(a, b) {
|
|
|
1664
1692
|
}
|
|
1665
1693
|
return result;
|
|
1666
1694
|
}
|
|
1667
|
-
function ensureOutputDir(outputDir) {
|
|
1668
|
-
if (!existsSync3(outputDir)) mkdirSync(outputDir, { recursive: true });
|
|
1669
|
-
}
|
|
1670
1695
|
function resolveWorkspacePath(value) {
|
|
1671
1696
|
return isAbsolute3(value) ? value : resolve3(WORKSPACE_ROOT, value);
|
|
1672
1697
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli-firebase-api-manifest",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.39.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"ts-morph": "^21.0.0"
|
|
8
8
|
},
|
|
9
9
|
"peerDependencies": {
|
|
10
|
-
"@dereekb/dbx-cli": "13.
|
|
11
|
-
"@dereekb/util": "13.
|
|
10
|
+
"@dereekb/dbx-cli": "13.39.0",
|
|
11
|
+
"@dereekb/util": "13.39.0",
|
|
12
12
|
"prettier": "3.8.3"
|
|
13
13
|
}
|
|
14
14
|
}
|