firestore-mcp-readonly 0.1.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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +37 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +4 -0
- package/dist/db.js +49 -0
- package/dist/db.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +62 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/limiter.d.ts +12 -0
- package/dist/limiter.js +30 -0
- package/dist/limiter.js.map +1 -0
- package/dist/serializer.d.ts +18 -0
- package/dist/serializer.js +39 -0
- package/dist/serializer.js.map +1 -0
- package/dist/tools/collection-group-query.d.ts +2 -0
- package/dist/tools/collection-group-query.js +80 -0
- package/dist/tools/collection-group-query.js.map +1 -0
- package/dist/tools/count-documents.d.ts +2 -0
- package/dist/tools/count-documents.js +54 -0
- package/dist/tools/count-documents.js.map +1 -0
- package/dist/tools/dump-database.d.ts +3 -0
- package/dist/tools/dump-database.js +89 -0
- package/dist/tools/dump-database.js.map +1 -0
- package/dist/tools/dump-node.d.ts +3 -0
- package/dist/tools/dump-node.js +107 -0
- package/dist/tools/dump-node.js.map +1 -0
- package/dist/tools/get-collection.d.ts +3 -0
- package/dist/tools/get-collection.js +53 -0
- package/dist/tools/get-collection.js.map +1 -0
- package/dist/tools/get-document.d.ts +2 -0
- package/dist/tools/get-document.js +52 -0
- package/dist/tools/get-document.js.map +1 -0
- package/dist/tools/get-server-info.d.ts +3 -0
- package/dist/tools/get-server-info.js +31 -0
- package/dist/tools/get-server-info.js.map +1 -0
- package/dist/tools/list-indexes.d.ts +3 -0
- package/dist/tools/list-indexes.js +92 -0
- package/dist/tools/list-indexes.js.map +1 -0
- package/dist/tools/list-subcollections.d.ts +2 -0
- package/dist/tools/list-subcollections.js +46 -0
- package/dist/tools/list-subcollections.js.map +1 -0
- package/dist/tools/query-collection.d.ts +2 -0
- package/dist/tools/query-collection.js +108 -0
- package/dist/tools/query-collection.js.map +1 -0
- package/dist/tools/read-collection-ordered.d.ts +3 -0
- package/dist/tools/read-collection-ordered.js +135 -0
- package/dist/tools/read-collection-ordered.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDb } from '../db.js';
|
|
3
|
+
import { formatFirestoreError } from '../errors.js';
|
|
4
|
+
const whereClause = z.object({
|
|
5
|
+
field: z.string().describe('Field name to filter on'),
|
|
6
|
+
op: z.enum(['==', '!=', '<', '<=', '>', '>=', 'array-contains', 'array-contains-any', 'in', 'not-in'])
|
|
7
|
+
.describe('Firestore comparison operator'),
|
|
8
|
+
value: z.union([
|
|
9
|
+
z.string(),
|
|
10
|
+
z.number(),
|
|
11
|
+
z.boolean(),
|
|
12
|
+
z.null(),
|
|
13
|
+
z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])),
|
|
14
|
+
]).describe('Value to compare against'),
|
|
15
|
+
});
|
|
16
|
+
export function registerCountDocuments(server) {
|
|
17
|
+
server.tool('firestore_count_documents', 'Count documents in a collection or matching a query without fetching the actual document data. Efficient aggregate operation.', {
|
|
18
|
+
collectionPath: z.string().describe('Collection path, e.g. "users" or "tenants/t1/projects"'),
|
|
19
|
+
where: z.array(whereClause).optional()
|
|
20
|
+
.describe('Optional where filter clauses — all are ANDed together'),
|
|
21
|
+
}, async ({ collectionPath, where: whereClauses }) => {
|
|
22
|
+
try {
|
|
23
|
+
const db = getDb();
|
|
24
|
+
let query = db.collection(collectionPath);
|
|
25
|
+
if (whereClauses) {
|
|
26
|
+
for (const clause of whereClauses) {
|
|
27
|
+
query = query.where(clause.field, clause.op, clause.value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const snapshot = await query.count().get();
|
|
31
|
+
const count = snapshot.data().count;
|
|
32
|
+
return {
|
|
33
|
+
content: [{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify({
|
|
36
|
+
collectionPath,
|
|
37
|
+
count,
|
|
38
|
+
filters: whereClauses?.length ?? 0,
|
|
39
|
+
}, null, 2),
|
|
40
|
+
}],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
content: [{
|
|
46
|
+
type: 'text',
|
|
47
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_count_documents'), null, 2),
|
|
48
|
+
}],
|
|
49
|
+
isError: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=count-documents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-documents.js","sourceRoot":"","sources":["../../src/tools/count-documents.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IACrD,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;SACnG,QAAQ,CAAC,+BAA+B,CAAC;IAC5C,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC;QACb,CAAC,CAAC,MAAM,EAAE;QACV,CAAC,CAAC,MAAM,EAAE;QACV,CAAC,CAAC,OAAO,EAAE;QACX,CAAC,CAAC,IAAI,EAAE;QACR,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;KAClE,CAAC,CAAC,QAAQ,CAAC,0BAA0B,CAAC;CACxC,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,IAAI,CACT,2BAA2B,EAC3B,+HAA+H,EAC/H;QACE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;QAC7F,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE;aACnC,QAAQ,CAAC,wDAAwD,CAAC;KACtE,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE;QAChD,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;YACnB,IAAI,KAAK,GAAwB,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAE/D,IAAI,YAAY,EAAE,CAAC;gBACjB,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;oBAClC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC7D,CAAC;YACH,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAEpC,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,cAAc;4BACd,KAAK;4BACL,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;yBACnC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,2BAA2B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxF,CAAC;gBACF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDb } from '../db.js';
|
|
3
|
+
import { serializeDocument } from '../serializer.js';
|
|
4
|
+
import { DumpLimiter, LimitExceededError } from '../limiter.js';
|
|
5
|
+
import { formatFirestoreError } from '../errors.js';
|
|
6
|
+
async function dumpCollectionRecursive(collectionPath, depth, limiter, maxDocsPerCollection) {
|
|
7
|
+
limiter.checkDepth(depth);
|
|
8
|
+
const db = getDb();
|
|
9
|
+
const snapshot = await db.collection(collectionPath).limit(maxDocsPerCollection).get();
|
|
10
|
+
limiter.addDocs(snapshot.docs.length);
|
|
11
|
+
const result = {
|
|
12
|
+
_type: 'collection',
|
|
13
|
+
_path: collectionPath,
|
|
14
|
+
_documentCount: snapshot.docs.length,
|
|
15
|
+
documents: {},
|
|
16
|
+
};
|
|
17
|
+
const documents = result.documents;
|
|
18
|
+
for (const doc of snapshot.docs) {
|
|
19
|
+
const serialized = serializeDocument(doc.id, doc.ref.path, doc.data());
|
|
20
|
+
const subcollections = await doc.ref.listCollections();
|
|
21
|
+
if (subcollections.length > 0) {
|
|
22
|
+
const subs = {};
|
|
23
|
+
for (const sub of subcollections) {
|
|
24
|
+
subs[sub.id] = await dumpCollectionRecursive(sub.path, depth + 1, limiter, maxDocsPerCollection);
|
|
25
|
+
}
|
|
26
|
+
serialized._subcollections = subs;
|
|
27
|
+
}
|
|
28
|
+
documents[doc.id] = serialized;
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
export function registerDumpDatabase(server, config) {
|
|
33
|
+
server.tool('firestore_dump_database', 'Recursively dump the entire Firestore database. Lists all root-level collections and traverses each one depth-first. Returns a nested tree of all documents and subcollections. Use with caution on large databases — configure limits via env vars or parameters.', {
|
|
34
|
+
maxDepth: z.number().int().min(1).max(15).optional()
|
|
35
|
+
.describe('Override max recursion depth (default from server config)'),
|
|
36
|
+
maxDocs: z.number().int().min(1).max(10000).optional()
|
|
37
|
+
.describe('Override max total documents to read (default from server config)'),
|
|
38
|
+
excludeCollections: z.array(z.string()).optional()
|
|
39
|
+
.describe('Root collection IDs to skip, e.g. ["_metadata", "_temp"]'),
|
|
40
|
+
}, async ({ maxDepth, maxDocs, excludeCollections }) => {
|
|
41
|
+
const limiter = new DumpLimiter(maxDocs ?? config.limits.maxTotalDumpDocs, maxDepth ?? config.limits.maxRecursionDepth);
|
|
42
|
+
try {
|
|
43
|
+
const db = getDb();
|
|
44
|
+
const rootCollections = await db.listCollections();
|
|
45
|
+
const excludeSet = new Set(excludeCollections ?? []);
|
|
46
|
+
const filtered = rootCollections.filter(col => !excludeSet.has(col.id));
|
|
47
|
+
const rootNames = filtered.map(col => col.id);
|
|
48
|
+
const data = {};
|
|
49
|
+
for (const col of filtered) {
|
|
50
|
+
data[col.id] = await dumpCollectionRecursive(col.path, 0, limiter, config.limits.maxDocsPerCollection);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
content: [{
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: JSON.stringify({
|
|
56
|
+
rootCollections: rootNames,
|
|
57
|
+
excludedCollections: excludeCollections ?? [],
|
|
58
|
+
totalDocsRead: limiter.docsProcessed,
|
|
59
|
+
limitReached: false,
|
|
60
|
+
data,
|
|
61
|
+
}, null, 2),
|
|
62
|
+
}],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof LimitExceededError) {
|
|
67
|
+
return {
|
|
68
|
+
content: [{
|
|
69
|
+
type: 'text',
|
|
70
|
+
text: JSON.stringify({
|
|
71
|
+
totalDocsRead: limiter.docsProcessed,
|
|
72
|
+
limitReached: true,
|
|
73
|
+
limitReason: error.message,
|
|
74
|
+
note: 'Partial data returned. Use excludeCollections or lower maxDocs/maxDepth to narrow scope.',
|
|
75
|
+
}, null, 2),
|
|
76
|
+
}],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
content: [{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_dump_database'), null, 2),
|
|
83
|
+
}],
|
|
84
|
+
isError: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=dump-database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dump-database.js","sourceRoot":"","sources":["../../src/tools/dump-database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDb } from '../db.js';
|
|
3
|
+
import { serializeDocument } from '../serializer.js';
|
|
4
|
+
import { DumpLimiter, LimitExceededError } from '../limiter.js';
|
|
5
|
+
import { formatFirestoreError } from '../errors.js';
|
|
6
|
+
async function dumpCollection(collectionPath, depth, limiter, maxDocsPerCollection) {
|
|
7
|
+
limiter.checkDepth(depth);
|
|
8
|
+
const db = getDb();
|
|
9
|
+
const snapshot = await db.collection(collectionPath).limit(maxDocsPerCollection).get();
|
|
10
|
+
limiter.addDocs(snapshot.docs.length);
|
|
11
|
+
const result = {
|
|
12
|
+
_type: 'collection',
|
|
13
|
+
_path: collectionPath,
|
|
14
|
+
_documentCount: snapshot.docs.length,
|
|
15
|
+
documents: {},
|
|
16
|
+
};
|
|
17
|
+
const documents = result.documents;
|
|
18
|
+
for (const doc of snapshot.docs) {
|
|
19
|
+
const serialized = serializeDocument(doc.id, doc.ref.path, doc.data());
|
|
20
|
+
const subcollections = await doc.ref.listCollections();
|
|
21
|
+
if (subcollections.length > 0) {
|
|
22
|
+
const subs = {};
|
|
23
|
+
for (const sub of subcollections) {
|
|
24
|
+
subs[sub.id] = await dumpCollection(sub.path, depth + 1, limiter, maxDocsPerCollection);
|
|
25
|
+
}
|
|
26
|
+
serialized._subcollections = subs;
|
|
27
|
+
}
|
|
28
|
+
documents[doc.id] = serialized;
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
async function dumpDocument(documentPath, depth, limiter, maxDocsPerCollection) {
|
|
33
|
+
const db = getDb();
|
|
34
|
+
const docRef = db.doc(documentPath);
|
|
35
|
+
const snapshot = await docRef.get();
|
|
36
|
+
if (!snapshot.exists) {
|
|
37
|
+
return { _type: 'document', _path: documentPath, exists: false };
|
|
38
|
+
}
|
|
39
|
+
limiter.addDocs(1);
|
|
40
|
+
const serialized = serializeDocument(snapshot.id, snapshot.ref.path, snapshot.data());
|
|
41
|
+
const subcollections = await docRef.listCollections();
|
|
42
|
+
if (subcollections.length > 0) {
|
|
43
|
+
const subs = {};
|
|
44
|
+
for (const sub of subcollections) {
|
|
45
|
+
subs[sub.id] = await dumpCollection(sub.path, depth + 1, limiter, maxDocsPerCollection);
|
|
46
|
+
}
|
|
47
|
+
serialized._subcollections = subs;
|
|
48
|
+
}
|
|
49
|
+
return serialized;
|
|
50
|
+
}
|
|
51
|
+
export function registerDumpNode(server, config) {
|
|
52
|
+
server.tool('firestore_dump_node', 'Recursively dump all documents and subcollections under a given Firestore path. Works with both document paths (even segments) and collection paths (odd segments). Returns a nested tree structure. Respects configurable depth and document count limits to prevent excessive reads.', {
|
|
53
|
+
path: z.string().describe('Starting path — document (e.g. "users/alice") or collection (e.g. "users")'),
|
|
54
|
+
maxDepth: z.number().int().min(1).max(20).optional()
|
|
55
|
+
.describe('Override max recursion depth (default from server config)'),
|
|
56
|
+
maxDocs: z.number().int().min(1).max(10000).optional()
|
|
57
|
+
.describe('Override max total documents to read (default from server config)'),
|
|
58
|
+
}, async ({ path, maxDepth, maxDocs }) => {
|
|
59
|
+
const limiter = new DumpLimiter(maxDocs ?? config.limits.maxTotalDumpDocs, maxDepth ?? config.limits.maxRecursionDepth);
|
|
60
|
+
try {
|
|
61
|
+
const segments = path.split('/').filter(Boolean);
|
|
62
|
+
const isDocument = segments.length % 2 === 0;
|
|
63
|
+
let data;
|
|
64
|
+
if (isDocument) {
|
|
65
|
+
data = await dumpDocument(path, 0, limiter, config.limits.maxDocsPerCollection);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
data = await dumpCollection(path, 0, limiter, config.limits.maxDocsPerCollection);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
content: [{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: JSON.stringify({
|
|
74
|
+
rootPath: path,
|
|
75
|
+
totalDocsRead: limiter.docsProcessed,
|
|
76
|
+
limitReached: false,
|
|
77
|
+
data,
|
|
78
|
+
}, null, 2),
|
|
79
|
+
}],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (error instanceof LimitExceededError) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: JSON.stringify({
|
|
88
|
+
rootPath: path,
|
|
89
|
+
totalDocsRead: limiter.docsProcessed,
|
|
90
|
+
limitReached: true,
|
|
91
|
+
limitReason: error.message,
|
|
92
|
+
note: 'Partial data returned. Use a narrower path or increase limits.',
|
|
93
|
+
}, null, 2),
|
|
94
|
+
}],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
content: [{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_dump_node'), null, 2),
|
|
101
|
+
}],
|
|
102
|
+
isError: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=dump-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dump-node.js","sourceRoot":"","sources":["../../src/tools/dump-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDb } from '../db.js';
|
|
3
|
+
import { serializeDocument } from '../serializer.js';
|
|
4
|
+
import { formatFirestoreError } from '../errors.js';
|
|
5
|
+
export function registerGetCollection(server, config) {
|
|
6
|
+
server.tool('firestore_get_collection', 'Get all documents from a Firestore collection by its full path. Returns documents with optional limit.', {
|
|
7
|
+
path: z.string().describe('Full collection path, e.g. "users" or "tenants/t1/projects"'),
|
|
8
|
+
limit: z.number().int().min(1).max(1000).optional()
|
|
9
|
+
.describe('Max documents to return. Defaults to server max (configurable, default 500). Hard cap: 1000.'),
|
|
10
|
+
}, async ({ path, limit }) => {
|
|
11
|
+
try {
|
|
12
|
+
const segments = path.split('/').filter(Boolean);
|
|
13
|
+
if (segments.length === 0 || segments.length % 2 !== 1) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: JSON.stringify({
|
|
18
|
+
error: `Invalid collection path "${path}": must have an odd number of segments. Got ${segments.length} segment(s).`,
|
|
19
|
+
}),
|
|
20
|
+
}],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const db = getDb();
|
|
25
|
+
const effectiveLimit = Math.min(limit ?? config.limits.maxDocsPerCollection, 1000);
|
|
26
|
+
const snapshot = await db.collection(path).limit(effectiveLimit + 1).get();
|
|
27
|
+
const truncated = snapshot.docs.length > effectiveLimit;
|
|
28
|
+
const docs = snapshot.docs.slice(0, effectiveLimit);
|
|
29
|
+
const documents = docs.map(doc => serializeDocument(doc.id, doc.ref.path, doc.data()));
|
|
30
|
+
return {
|
|
31
|
+
content: [{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: JSON.stringify({
|
|
34
|
+
collectionPath: path,
|
|
35
|
+
count: documents.length,
|
|
36
|
+
truncated,
|
|
37
|
+
documents,
|
|
38
|
+
}, null, 2),
|
|
39
|
+
}],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
content: [{
|
|
45
|
+
type: 'text',
|
|
46
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_get_collection'), null, 2),
|
|
47
|
+
}],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=get-collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-collection.js","sourceRoot":"","sources":["../../src/tools/get-collection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,MAAuB;IAC9E,MAAM,CAAC,IAAI,CACT,0BAA0B,EAC1B,wGAAwG,EACxG;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6DAA6D,CAAC;QACxF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;aAChD,QAAQ,CAAC,8FAA8F,CAAC;KAC5G,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,KAAK,EAAE,4BAA4B,IAAI,+CAA+C,QAAQ,CAAC,MAAM,cAAc;6BACpH,CAAC;yBACH,CAAC;oBACF,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;YACnB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;YACnF,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YAE3E,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;YACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;YAEpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAC/B,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CACpD,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,cAAc,EAAE,IAAI;4BACpB,KAAK,EAAE,SAAS,CAAC,MAAM;4BACvB,SAAS;4BACT,SAAS;yBACV,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,0BAA0B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACvF,CAAC;gBACF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDb } from '../db.js';
|
|
3
|
+
import { serializeDocument } from '../serializer.js';
|
|
4
|
+
import { formatFirestoreError } from '../errors.js';
|
|
5
|
+
export function registerGetDocument(server) {
|
|
6
|
+
server.tool('firestore_get_document', 'Get a single Firestore document by its full path. Path must have an even number of segments (collection/doc pairs), e.g. "users/alice" or "tenants/t1/projects/p1".', {
|
|
7
|
+
path: z.string().describe('Full document path, e.g. "users/alice" or "tenants/t1/projects/p1/docs/d1"'),
|
|
8
|
+
}, async ({ path }) => {
|
|
9
|
+
try {
|
|
10
|
+
const segments = path.split('/').filter(Boolean);
|
|
11
|
+
if (segments.length === 0 || segments.length % 2 !== 0) {
|
|
12
|
+
return {
|
|
13
|
+
content: [{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: JSON.stringify({
|
|
16
|
+
error: `Invalid document path "${path}": must have an even number of segments (collection/doc pairs). Got ${segments.length} segment(s).`,
|
|
17
|
+
}),
|
|
18
|
+
}],
|
|
19
|
+
isError: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const db = getDb();
|
|
23
|
+
const docRef = db.doc(path);
|
|
24
|
+
const snapshot = await docRef.get();
|
|
25
|
+
if (!snapshot.exists) {
|
|
26
|
+
return {
|
|
27
|
+
content: [{
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: JSON.stringify({ exists: false, path }),
|
|
30
|
+
}],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const result = serializeDocument(snapshot.id, snapshot.ref.path, snapshot.data());
|
|
34
|
+
return {
|
|
35
|
+
content: [{
|
|
36
|
+
type: 'text',
|
|
37
|
+
text: JSON.stringify(result, null, 2),
|
|
38
|
+
}],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return {
|
|
43
|
+
content: [{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_get_document'), null, 2),
|
|
46
|
+
}],
|
|
47
|
+
isError: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=get-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-document.js","sourceRoot":"","sources":["../../src/tools/get-document.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,qKAAqK,EACrK;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4EAA4E,CAAC;KACxG,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,KAAK,EAAE,0BAA0B,IAAI,uEAAuE,QAAQ,CAAC,MAAM,cAAc;6BAC1I,CAAC;yBACH,CAAC;oBACF,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC5B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;YAEpC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACrB,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;yBAC9C,CAAC;iBACH,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAG,CAAC,CAAC;YAEnF,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACtC,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,wBAAwB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACrF,CAAC;gBACF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getDb } from '../db.js';
|
|
2
|
+
import { formatFirestoreError } from '../errors.js';
|
|
3
|
+
export function registerGetServerInfo(server, config) {
|
|
4
|
+
server.tool('firestore_get_server_info', 'Get the MCP server configuration summary: project ID, database ID, and active safety limits. Useful for verifying you are connected to the correct Firestore database.', {}, async () => {
|
|
5
|
+
try {
|
|
6
|
+
const db = getDb();
|
|
7
|
+
const rootCollections = await db.listCollections();
|
|
8
|
+
return {
|
|
9
|
+
content: [{
|
|
10
|
+
type: 'text',
|
|
11
|
+
text: JSON.stringify({
|
|
12
|
+
projectId: config.projectId,
|
|
13
|
+
databaseId: config.databaseId,
|
|
14
|
+
limits: config.limits,
|
|
15
|
+
rootCollections: rootCollections.map(col => col.id),
|
|
16
|
+
}, null, 2),
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return {
|
|
22
|
+
content: [{
|
|
23
|
+
type: 'text',
|
|
24
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_get_server_info'), null, 2),
|
|
25
|
+
}],
|
|
26
|
+
isError: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=get-server-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-server-info.js","sourceRoot":"","sources":["../../src/tools/get-server-info.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,MAAuB;IAC9E,MAAM,CAAC,IAAI,CACT,2BAA2B,EAC3B,wKAAwK,EACxK,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;YACnB,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,eAAe,EAAE,CAAC;YAEnD,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,SAAS,EAAE,MAAM,CAAC,SAAS;4BAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;4BAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,eAAe,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;yBACpD,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,2BAA2B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxF,CAAC;gBACF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { getApps } from 'firebase-admin/app';
|
|
2
|
+
import { formatFirestoreError } from '../errors.js';
|
|
3
|
+
export function registerListIndexes(server, config) {
|
|
4
|
+
server.tool('firestore_list_indexes', 'List all composite indexes defined for the Firestore database. Useful for understanding what queries are efficiently supported and diagnosing "missing index" errors.', {}, async () => {
|
|
5
|
+
try {
|
|
6
|
+
const app = getApps()[0];
|
|
7
|
+
const credential = app.options.credential;
|
|
8
|
+
if (!credential) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{
|
|
11
|
+
type: 'text',
|
|
12
|
+
text: JSON.stringify({ error: 'No credential available to list indexes' }),
|
|
13
|
+
}],
|
|
14
|
+
isError: true,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const token = await credential.getAccessToken();
|
|
18
|
+
const dbPath = config.databaseId === '(default)' ? '(default)' : config.databaseId;
|
|
19
|
+
const url = `https://firestore.googleapis.com/v1/projects/${config.projectId}/databases/${dbPath}/collectionGroups/-/indexes`;
|
|
20
|
+
const response = await fetch(url, {
|
|
21
|
+
headers: { Authorization: `Bearer ${token.access_token}` },
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
const body = await response.text();
|
|
25
|
+
let parsedError = {};
|
|
26
|
+
try {
|
|
27
|
+
parsedError = JSON.parse(body);
|
|
28
|
+
}
|
|
29
|
+
catch { /* raw text fallback */ }
|
|
30
|
+
const errorMessage = typeof parsedError.error === 'object' && parsedError.error !== null
|
|
31
|
+
? parsedError.error.message || `HTTP ${response.status} ${response.statusText}`
|
|
32
|
+
: `HTTP ${response.status} ${response.statusText}`;
|
|
33
|
+
const httpStatus = response.status;
|
|
34
|
+
let suggestion = 'Check the error details for more information.';
|
|
35
|
+
if (httpStatus === 403) {
|
|
36
|
+
suggestion = 'The service account lacks permission to list indexes. Ensure it has the Cloud Datastore Index Admin role (roles/datastore.indexAdmin) in the GCP IAM console.';
|
|
37
|
+
}
|
|
38
|
+
else if (httpStatus === 404) {
|
|
39
|
+
suggestion = 'The project or database was not found. Verify FIRESTORE_PROJECT_ID and FIRESTORE_DATABASE_ID.';
|
|
40
|
+
}
|
|
41
|
+
else if (httpStatus === 401) {
|
|
42
|
+
suggestion = 'Authentication failed. The service account credentials may be invalid or expired. Check FIRESTORE_SERVICE_ACCOUNT_KEY_PATH.';
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
content: [{
|
|
46
|
+
type: 'text',
|
|
47
|
+
text: JSON.stringify({
|
|
48
|
+
error: errorMessage,
|
|
49
|
+
code: `HTTP_${httpStatus}`,
|
|
50
|
+
tool: 'firestore_list_indexes',
|
|
51
|
+
suggestion,
|
|
52
|
+
details: body,
|
|
53
|
+
}, null, 2),
|
|
54
|
+
}],
|
|
55
|
+
isError: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const data = await response.json();
|
|
59
|
+
const indexes = (data.indexes ?? []).map(idx => ({
|
|
60
|
+
name: idx.name.split('/').pop(),
|
|
61
|
+
queryScope: idx.queryScope,
|
|
62
|
+
fields: idx.fields.map(f => ({
|
|
63
|
+
fieldPath: f.fieldPath,
|
|
64
|
+
...(f.order ? { order: f.order } : {}),
|
|
65
|
+
...(f.arrayConfig ? { arrayConfig: f.arrayConfig } : {}),
|
|
66
|
+
})),
|
|
67
|
+
state: idx.state,
|
|
68
|
+
}));
|
|
69
|
+
return {
|
|
70
|
+
content: [{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: JSON.stringify({
|
|
73
|
+
projectId: config.projectId,
|
|
74
|
+
databaseId: config.databaseId,
|
|
75
|
+
count: indexes.length,
|
|
76
|
+
indexes,
|
|
77
|
+
}, null, 2),
|
|
78
|
+
}],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return {
|
|
83
|
+
content: [{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_list_indexes'), null, 2),
|
|
86
|
+
}],
|
|
87
|
+
isError: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=list-indexes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-indexes.js","sourceRoot":"","sources":["../../src/tools/list-indexes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDb } from '../db.js';
|
|
3
|
+
import { formatFirestoreError } from '../errors.js';
|
|
4
|
+
export function registerListSubcollections(server) {
|
|
5
|
+
server.tool('firestore_list_subcollections', 'List all subcollection names directly under a document. This is an Admin SDK exclusive feature — client SDKs cannot enumerate subcollections.', {
|
|
6
|
+
documentPath: z.string().describe('Full document path, e.g. "users/alice" or "tenants/t1/projects/p1"'),
|
|
7
|
+
}, async ({ documentPath }) => {
|
|
8
|
+
try {
|
|
9
|
+
const segments = documentPath.split('/').filter(Boolean);
|
|
10
|
+
if (segments.length === 0 || segments.length % 2 !== 0) {
|
|
11
|
+
return {
|
|
12
|
+
content: [{
|
|
13
|
+
type: 'text',
|
|
14
|
+
text: JSON.stringify({
|
|
15
|
+
error: `Invalid document path "${documentPath}": must have an even number of segments.`,
|
|
16
|
+
}),
|
|
17
|
+
}],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const db = getDb();
|
|
22
|
+
const collections = await db.doc(documentPath).listCollections();
|
|
23
|
+
const subcollections = collections.map(col => col.id);
|
|
24
|
+
return {
|
|
25
|
+
content: [{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: JSON.stringify({
|
|
28
|
+
documentPath,
|
|
29
|
+
count: subcollections.length,
|
|
30
|
+
subcollections,
|
|
31
|
+
}, null, 2),
|
|
32
|
+
}],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{
|
|
38
|
+
type: 'text',
|
|
39
|
+
text: JSON.stringify(formatFirestoreError(error, 'firestore_list_subcollections'), null, 2),
|
|
40
|
+
}],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=list-subcollections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-subcollections.js","sourceRoot":"","sources":["../../src/tools/list-subcollections.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,IAAI,CACT,+BAA+B,EAC/B,+IAA+I,EAC/I;QACE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oEAAoE,CAAC;KACxG,EACD,KAAK,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,KAAK,EAAE,0BAA0B,YAAY,0CAA0C;6BACxF,CAAC;yBACH,CAAC;oBACF,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;YACnB,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,eAAe,EAAE,CAAC;YACjE,MAAM,cAAc,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAEtD,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,YAAY;4BACZ,KAAK,EAAE,cAAc,CAAC,MAAM;4BAC5B,cAAc;yBACf,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,+BAA+B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBAC5F,CAAC;gBACF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|