firebase-tools 14.12.0 → 14.12.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/lib/commands/firestore-databases-create.js +11 -0
- package/lib/crashlytics/buildToolsJarHelper.js +1 -2
- package/lib/deploy/dataconnect/prepare.js +2 -2
- package/lib/deploy/dataconnect/release.js +2 -2
- package/lib/deploy/firestore/deploy.js +10 -0
- package/lib/deploy/functions/prepareFunctionsUpload.js +3 -1
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/firestore/api-sort.js +96 -3
- package/lib/firestore/api-types.js +14 -1
- package/lib/firestore/api.js +68 -1
- package/lib/firestore/validator.js +1 -1
- package/lib/functions/deprecationWarnings.js +4 -4
- package/lib/gcp/cloudsql/connect.js +1 -1
- package/lib/init/features/dataconnect/sdk.js +1 -2
- package/lib/mcp/index.js +75 -2
- package/lib/mcp/prompt.js +10 -0
- package/lib/mcp/prompts/core/deploy.js +58 -0
- package/lib/mcp/prompts/core/index.js +5 -0
- package/lib/mcp/prompts/index.js +45 -0
- package/lib/mcp/tools/core/get_sdk_config.js +10 -0
- package/lib/mcp/tools/database/get_data.js +49 -0
- package/lib/mcp/tools/database/get_rules.js +39 -0
- package/lib/mcp/tools/database/index.js +8 -0
- package/lib/mcp/tools/database/set_data.js +57 -0
- package/lib/mcp/tools/database/set_rules.js +41 -0
- package/lib/mcp/tools/database/validate_rules.js +41 -0
- package/lib/mcp/tools/index.js +4 -1
- package/lib/mcp/tools/rules/get_rules.js +1 -1
- package/lib/mcp/types.js +2 -0
- package/lib/mcp/util.js +2 -0
- package/lib/rtdb.js +10 -6
- package/lib/utils.js +24 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +3 -0
- package/templates/init/firestore/firestore.indexes.json +26 -1
|
@@ -14,6 +14,7 @@ const error_1 = require("../error");
|
|
|
14
14
|
exports.command = new command_1.Command("firestore:databases:create <database>")
|
|
15
15
|
.description("create a database in your Firebase project")
|
|
16
16
|
.option("--location <locationId>", "region to create database, for example 'nam5'. Run 'firebase firestore:locations' to get a list of eligible locations (required)")
|
|
17
|
+
.option("--edition <edition>", "the edition of the database to create, for example 'standard' or 'enterprise'. If not provided, 'standard' is used as a default.")
|
|
17
18
|
.option("--delete-protection <deleteProtectionState>", "whether or not to prevent deletion of database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'")
|
|
18
19
|
.option("--point-in-time-recovery <enablement>", "whether to enable the PITR feature on this database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'")
|
|
19
20
|
.option("-k, --kms-key-name <kmsKeyName>", "the resource ID of a Cloud KMS key. If set, the database created will be a " +
|
|
@@ -29,6 +30,15 @@ exports.command = new command_1.Command("firestore:databases:create <database>")
|
|
|
29
30
|
throw new error_1.FirebaseError(`Missing required flag --location. ${helpCommandText}`);
|
|
30
31
|
}
|
|
31
32
|
const type = types.DatabaseType.FIRESTORE_NATIVE;
|
|
33
|
+
let databaseEdition = types.DatabaseEdition.STANDARD;
|
|
34
|
+
if (options.edition) {
|
|
35
|
+
const edition = options.edition.toUpperCase();
|
|
36
|
+
if (edition !== types.DatabaseEdition.STANDARD &&
|
|
37
|
+
edition !== types.DatabaseEdition.ENTERPRISE) {
|
|
38
|
+
throw new error_1.FirebaseError(`Invalid value for flag --edition. ${helpCommandText}`);
|
|
39
|
+
}
|
|
40
|
+
databaseEdition = edition;
|
|
41
|
+
}
|
|
32
42
|
if (options.deleteProtection &&
|
|
33
43
|
options.deleteProtection !== types.DatabaseDeleteProtectionStateOption.ENABLED &&
|
|
34
44
|
options.deleteProtection !== types.DatabaseDeleteProtectionStateOption.DISABLED) {
|
|
@@ -56,6 +66,7 @@ exports.command = new command_1.Command("firestore:databases:create <database>")
|
|
|
56
66
|
databaseId: database,
|
|
57
67
|
locationId: options.location,
|
|
58
68
|
type,
|
|
69
|
+
databaseEdition,
|
|
59
70
|
deleteProtectionState,
|
|
60
71
|
pointInTimeRecoveryEnablement,
|
|
61
72
|
cmekConfig,
|
|
@@ -8,7 +8,6 @@ const spawn = require("cross-spawn");
|
|
|
8
8
|
const downloadUtils = require("../downloadUtils");
|
|
9
9
|
const error_1 = require("../error");
|
|
10
10
|
const logger_1 = require("../logger");
|
|
11
|
-
const node_fs_1 = require("node:fs");
|
|
12
11
|
const utils = require("../utils");
|
|
13
12
|
const JAR_CACHE_DIR = process.env.FIREBASE_CRASHLYTICS_BUILDTOOLS_PATH ||
|
|
14
13
|
path.join(os.homedir(), ".cache", "firebase", "crashlytics", "buildtools");
|
|
@@ -26,7 +25,7 @@ async function fetchBuildtoolsJar() {
|
|
|
26
25
|
}
|
|
27
26
|
if (fs.existsSync(JAR_CACHE_DIR)) {
|
|
28
27
|
logger_1.logger.debug(`Deleting Jar cache at ${JAR_CACHE_DIR} because the CLI was run with a newer Jar version`);
|
|
29
|
-
|
|
28
|
+
fs.rmSync(JAR_CACHE_DIR, { recursive: true, force: true });
|
|
30
29
|
}
|
|
31
30
|
utils.logBullet("Downloading crashlytics-buildtools.jar to " + jarPath);
|
|
32
31
|
utils.logBullet("For open source licenses used by this command, look in the META-INF directory in the buildtools.jar file");
|
|
@@ -19,7 +19,7 @@ const types_1 = require("../../dataconnect/types");
|
|
|
19
19
|
const schemaMigration_1 = require("../../dataconnect/schemaMigration");
|
|
20
20
|
const freeTrial_1 = require("../../dataconnect/freeTrial");
|
|
21
21
|
async function default_1(context, options) {
|
|
22
|
-
var _a;
|
|
22
|
+
var _a, _b, _c;
|
|
23
23
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
24
24
|
if (!(await (0, cloudbilling_1.checkBillingEnabled)(projectId))) {
|
|
25
25
|
throw new error_1.FirebaseError((0, freeTrial_1.upgradeInstructions)(projectId));
|
|
@@ -53,7 +53,7 @@ async function default_1(context, options) {
|
|
|
53
53
|
utils.logLabeledBullet("dataconnect", `Successfully prepared schema and connectors`);
|
|
54
54
|
if (options.dryRun) {
|
|
55
55
|
for (const si of serviceInfos) {
|
|
56
|
-
await (0, schemaMigration_1.diffSchema)(options, si.schema, (_a = si.dataConnectYaml.schema.datasource.postgresql) === null ||
|
|
56
|
+
await (0, schemaMigration_1.diffSchema)(options, si.schema, (_c = (_b = (_a = si.dataConnectYaml.schema) === null || _a === void 0 ? void 0 : _a.datasource) === null || _b === void 0 ? void 0 : _b.postgresql) === null || _c === void 0 ? void 0 : _c.schemaValidation);
|
|
57
57
|
}
|
|
58
58
|
utils.logLabeledBullet("dataconnect", "Checking for CloudSQL resources...");
|
|
59
59
|
await Promise.all(serviceInfos
|
|
@@ -17,10 +17,10 @@ async function default_1(context, options) {
|
|
|
17
17
|
}));
|
|
18
18
|
})
|
|
19
19
|
.map((s) => {
|
|
20
|
-
var _a;
|
|
20
|
+
var _a, _b, _c, _d;
|
|
21
21
|
return ({
|
|
22
22
|
schema: s.schema,
|
|
23
|
-
validationMode: (_a = s.dataConnectYaml.schema.datasource.postgresql) === null ||
|
|
23
|
+
validationMode: (_d = (_c = (_b = (_a = s.dataConnectYaml) === null || _a === void 0 ? void 0 : _a.schema) === null || _b === void 0 ? void 0 : _b.datasource) === null || _c === void 0 ? void 0 : _c.postgresql) === null || _d === void 0 ? void 0 : _d.schemaValidation,
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
26
|
if (wantSchemas.length) {
|
|
@@ -22,6 +22,15 @@ async function createDatabase(context, options) {
|
|
|
22
22
|
if (!firestoreCfg.database) {
|
|
23
23
|
firestoreCfg.database = "(default)";
|
|
24
24
|
}
|
|
25
|
+
let edition = types.DatabaseEdition.STANDARD;
|
|
26
|
+
if (firestoreCfg.edition) {
|
|
27
|
+
const upperEdition = firestoreCfg.edition.toUpperCase();
|
|
28
|
+
if (upperEdition !== types.DatabaseEdition.STANDARD &&
|
|
29
|
+
upperEdition !== types.DatabaseEdition.ENTERPRISE) {
|
|
30
|
+
throw new error_1.FirebaseError(`Invalid edition specified for database in firebase.json: ${firestoreCfg.edition}`);
|
|
31
|
+
}
|
|
32
|
+
edition = upperEdition;
|
|
33
|
+
}
|
|
25
34
|
const api = new api_1.FirestoreApi();
|
|
26
35
|
try {
|
|
27
36
|
await api.getDatabase(options.projectId, firestoreCfg.database);
|
|
@@ -34,6 +43,7 @@ async function createDatabase(context, options) {
|
|
|
34
43
|
databaseId: firestoreCfg.database,
|
|
35
44
|
locationId: firestoreCfg.location || "nam5",
|
|
36
45
|
type: types.DatabaseType.FIRESTORE_NATIVE,
|
|
46
|
+
databaseEdition: edition,
|
|
37
47
|
deleteProtectionState: types.DatabaseDeleteProtectionState.DISABLED,
|
|
38
48
|
pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement.DISABLED,
|
|
39
49
|
};
|
|
@@ -74,7 +74,9 @@ async function packageSource(sourceDir, config, runtimeConfig) {
|
|
|
74
74
|
name: CONFIG_DEST_FILE,
|
|
75
75
|
mode: 420,
|
|
76
76
|
});
|
|
77
|
-
(
|
|
77
|
+
if (Object.keys(runtimeConfig).some((k) => k !== "firebase")) {
|
|
78
|
+
(0, deprecationWarnings_1.logFunctionsConfigDeprecationWarning)();
|
|
79
|
+
}
|
|
78
80
|
}
|
|
79
81
|
await pipeAsync(archive, fileStream);
|
|
80
82
|
}
|
|
@@ -54,28 +54,28 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.11.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.11.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.
|
|
57
|
+
"version": "2.11.1",
|
|
58
|
+
"expectedSize": 29352800,
|
|
59
|
+
"expectedChecksum": "52d86a5546bbb9e2fcd67faa90b9f07e",
|
|
60
|
+
"expectedChecksumSHA256": "217b66589c32d4378201100fa968e69f7c94537044b892be1aaa14d7f6ce6b12",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.11.1",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.1"
|
|
63
63
|
},
|
|
64
64
|
"win32": {
|
|
65
|
-
"version": "2.11.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.11.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.
|
|
65
|
+
"version": "2.11.1",
|
|
66
|
+
"expectedSize": 29841920,
|
|
67
|
+
"expectedChecksum": "704cee75ad2d384cf28ac1683c8d1179",
|
|
68
|
+
"expectedChecksumSHA256": "fadb5a1d0f03160c133389df9d452cc620cfdface041971a5356ac5a69e937ff",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.11.1",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.1.exe"
|
|
71
71
|
},
|
|
72
72
|
"linux": {
|
|
73
|
-
"version": "2.11.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.11.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.
|
|
73
|
+
"version": "2.11.1",
|
|
74
|
+
"expectedSize": 29282488,
|
|
75
|
+
"expectedChecksum": "435656eab760033228bf7b7e77d56dde",
|
|
76
|
+
"expectedChecksumSHA256": "0abb33c0bba0ed8ef1394cc1cab5a333ec81b3dddf48aa625abe8ca28f38840e",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.11.1",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.1"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -8,6 +8,19 @@ const QUERY_SCOPE_SEQUENCE = [
|
|
|
8
8
|
API.QueryScope.COLLECTION,
|
|
9
9
|
undefined,
|
|
10
10
|
];
|
|
11
|
+
const API_SCOPE_SEQUENCE = [
|
|
12
|
+
API.ApiScope.ANY_API,
|
|
13
|
+
API.ApiScope.DATASTORE_MODE_API,
|
|
14
|
+
API.ApiScope.MONGODB_COMPATIBLE_API,
|
|
15
|
+
undefined,
|
|
16
|
+
];
|
|
17
|
+
const DENSITY_SEQUENCE = [
|
|
18
|
+
API.Density.DENSITY_UNSPECIFIED,
|
|
19
|
+
API.Density.SPARSE_ALL,
|
|
20
|
+
API.Density.SPARSE_ANY,
|
|
21
|
+
API.Density.DENSE,
|
|
22
|
+
undefined,
|
|
23
|
+
];
|
|
11
24
|
const ORDER_SEQUENCE = [API.Order.ASCENDING, API.Order.DESCENDING, undefined];
|
|
12
25
|
const ARRAY_CONFIG_SEQUENCE = [API.ArrayConfig.CONTAINS, undefined];
|
|
13
26
|
function compareSpecIndex(a, b) {
|
|
@@ -17,7 +30,23 @@ function compareSpecIndex(a, b) {
|
|
|
17
30
|
if (a.queryScope !== b.queryScope) {
|
|
18
31
|
return compareQueryScope(a.queryScope, b.queryScope);
|
|
19
32
|
}
|
|
20
|
-
|
|
33
|
+
let cmp = compareArrays(a.fields, b.fields, compareIndexField);
|
|
34
|
+
if (cmp !== 0) {
|
|
35
|
+
return cmp;
|
|
36
|
+
}
|
|
37
|
+
cmp = compareApiScope(a.apiScope, b.apiScope);
|
|
38
|
+
if (cmp !== 0) {
|
|
39
|
+
return cmp;
|
|
40
|
+
}
|
|
41
|
+
cmp = compareDensity(a.density, b.density);
|
|
42
|
+
if (cmp !== 0) {
|
|
43
|
+
return cmp;
|
|
44
|
+
}
|
|
45
|
+
cmp = compareBoolean(a.multikey, b.multikey);
|
|
46
|
+
if (cmp !== 0) {
|
|
47
|
+
return cmp;
|
|
48
|
+
}
|
|
49
|
+
return compareBoolean(a.unique, b.unique);
|
|
21
50
|
}
|
|
22
51
|
exports.compareSpecIndex = compareSpecIndex;
|
|
23
52
|
function compareApiIndex(a, b) {
|
|
@@ -31,7 +60,23 @@ function compareApiIndex(a, b) {
|
|
|
31
60
|
if (a.queryScope !== b.queryScope) {
|
|
32
61
|
return compareQueryScope(a.queryScope, b.queryScope);
|
|
33
62
|
}
|
|
34
|
-
|
|
63
|
+
let cmp = compareArrays(a.fields, b.fields, compareIndexField);
|
|
64
|
+
if (cmp !== 0) {
|
|
65
|
+
return cmp;
|
|
66
|
+
}
|
|
67
|
+
cmp = compareApiScope(a.apiScope, b.apiScope);
|
|
68
|
+
if (cmp !== 0) {
|
|
69
|
+
return cmp;
|
|
70
|
+
}
|
|
71
|
+
cmp = compareDensity(a.density, b.density);
|
|
72
|
+
if (cmp !== 0) {
|
|
73
|
+
return cmp;
|
|
74
|
+
}
|
|
75
|
+
cmp = compareBoolean(a.multikey, b.multikey);
|
|
76
|
+
if (cmp !== 0) {
|
|
77
|
+
return cmp;
|
|
78
|
+
}
|
|
79
|
+
return compareBoolean(a.unique, b.unique);
|
|
35
80
|
}
|
|
36
81
|
exports.compareApiIndex = compareApiIndex;
|
|
37
82
|
function compareApiDatabase(a, b) {
|
|
@@ -115,14 +160,62 @@ function compareFieldIndex(a, b) {
|
|
|
115
160
|
if (a.arrayConfig !== b.arrayConfig) {
|
|
116
161
|
return compareArrayConfig(a.arrayConfig, b.arrayConfig);
|
|
117
162
|
}
|
|
118
|
-
|
|
163
|
+
let cmp = compareApiScope(a.apiScope, b.apiScope);
|
|
164
|
+
if (cmp !== 0) {
|
|
165
|
+
return cmp;
|
|
166
|
+
}
|
|
167
|
+
cmp = compareDensity(a.density, b.density);
|
|
168
|
+
if (cmp !== 0) {
|
|
169
|
+
return cmp;
|
|
170
|
+
}
|
|
171
|
+
cmp = compareBoolean(a.multikey, b.multikey);
|
|
172
|
+
if (cmp !== 0) {
|
|
173
|
+
return cmp;
|
|
174
|
+
}
|
|
175
|
+
return compareBoolean(a.unique, b.unique);
|
|
119
176
|
}
|
|
120
177
|
function compareQueryScope(a, b) {
|
|
121
178
|
return QUERY_SCOPE_SEQUENCE.indexOf(a) - QUERY_SCOPE_SEQUENCE.indexOf(b);
|
|
122
179
|
}
|
|
180
|
+
function compareApiScope(a, b) {
|
|
181
|
+
if (a === b) {
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
if (a === undefined) {
|
|
185
|
+
return -1;
|
|
186
|
+
}
|
|
187
|
+
if (b === undefined) {
|
|
188
|
+
return 1;
|
|
189
|
+
}
|
|
190
|
+
return API_SCOPE_SEQUENCE.indexOf(a) - API_SCOPE_SEQUENCE.indexOf(b);
|
|
191
|
+
}
|
|
192
|
+
function compareDensity(a, b) {
|
|
193
|
+
if (a === b) {
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
if (a === undefined) {
|
|
197
|
+
return -1;
|
|
198
|
+
}
|
|
199
|
+
if (b === undefined) {
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
return DENSITY_SEQUENCE.indexOf(a) - DENSITY_SEQUENCE.indexOf(b);
|
|
203
|
+
}
|
|
123
204
|
function compareOrder(a, b) {
|
|
124
205
|
return ORDER_SEQUENCE.indexOf(a) - ORDER_SEQUENCE.indexOf(b);
|
|
125
206
|
}
|
|
207
|
+
function compareBoolean(a, b) {
|
|
208
|
+
if (a === b) {
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
if (a === undefined) {
|
|
212
|
+
return -1;
|
|
213
|
+
}
|
|
214
|
+
if (b === undefined) {
|
|
215
|
+
return 1;
|
|
216
|
+
}
|
|
217
|
+
return Number(a) - Number(b);
|
|
218
|
+
}
|
|
126
219
|
function compareArrayConfig(a, b) {
|
|
127
220
|
return ARRAY_CONFIG_SEQUENCE.indexOf(a) - ARRAY_CONFIG_SEQUENCE.indexOf(b);
|
|
128
221
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RecurrenceType = exports.DatabaseEdition = exports.PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablementOption = exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
|
|
3
|
+
exports.RecurrenceType = exports.DatabaseEdition = exports.PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablementOption = exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.Density = exports.ApiScope = exports.QueryScope = exports.Mode = void 0;
|
|
4
4
|
var Mode;
|
|
5
5
|
(function (Mode) {
|
|
6
6
|
Mode["ASCENDING"] = "ASCENDING";
|
|
@@ -12,6 +12,19 @@ var QueryScope;
|
|
|
12
12
|
QueryScope["COLLECTION"] = "COLLECTION";
|
|
13
13
|
QueryScope["COLLECTION_GROUP"] = "COLLECTION_GROUP";
|
|
14
14
|
})(QueryScope = exports.QueryScope || (exports.QueryScope = {}));
|
|
15
|
+
var ApiScope;
|
|
16
|
+
(function (ApiScope) {
|
|
17
|
+
ApiScope["ANY_API"] = "ANY_API";
|
|
18
|
+
ApiScope["DATASTORE_MODE_API"] = "DATASTORE_MODE_API";
|
|
19
|
+
ApiScope["MONGODB_COMPATIBLE_API"] = "MONGODB_COMPATIBLE_API";
|
|
20
|
+
})(ApiScope = exports.ApiScope || (exports.ApiScope = {}));
|
|
21
|
+
var Density;
|
|
22
|
+
(function (Density) {
|
|
23
|
+
Density["DENSITY_UNSPECIFIED"] = "DENSITY_UNSPECIFIED";
|
|
24
|
+
Density["SPARSE_ALL"] = "SPARSE_ALL";
|
|
25
|
+
Density["SPARSE_ANY"] = "SPARSE_ANY";
|
|
26
|
+
Density["DENSE"] = "DENSE";
|
|
27
|
+
})(Density = exports.Density || (exports.Density = {}));
|
|
15
28
|
var Order;
|
|
16
29
|
(function (Order) {
|
|
17
30
|
Order["ASCENDING"] = "ASCENDING";
|
package/lib/firestore/api.js
CHANGED
|
@@ -160,6 +160,10 @@ class FirestoreApi {
|
|
|
160
160
|
collectionGroup: util.parseIndexName(index.name).collectionGroupId,
|
|
161
161
|
queryScope: index.queryScope,
|
|
162
162
|
fields: index.fields,
|
|
163
|
+
apiScope: index.apiScope,
|
|
164
|
+
density: index.density,
|
|
165
|
+
multikey: index.multikey,
|
|
166
|
+
unique: index.unique,
|
|
163
167
|
};
|
|
164
168
|
});
|
|
165
169
|
if (!fields) {
|
|
@@ -179,6 +183,10 @@ class FirestoreApi {
|
|
|
179
183
|
order: firstField.order,
|
|
180
184
|
arrayConfig: firstField.arrayConfig,
|
|
181
185
|
queryScope: index.queryScope,
|
|
186
|
+
apiScope: index.apiScope,
|
|
187
|
+
density: index.density,
|
|
188
|
+
multikey: index.multikey,
|
|
189
|
+
unique: index.unique,
|
|
182
190
|
};
|
|
183
191
|
}),
|
|
184
192
|
};
|
|
@@ -205,6 +213,18 @@ class FirestoreApi {
|
|
|
205
213
|
validator.assertHas(index, "collectionGroup");
|
|
206
214
|
validator.assertHas(index, "queryScope");
|
|
207
215
|
validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
|
|
216
|
+
if (index.apiScope) {
|
|
217
|
+
validator.assertEnum(index, "apiScope", Object.keys(types.ApiScope));
|
|
218
|
+
}
|
|
219
|
+
if (index.density) {
|
|
220
|
+
validator.assertEnum(index, "density", Object.keys(types.Density));
|
|
221
|
+
}
|
|
222
|
+
if (index.multikey) {
|
|
223
|
+
validator.assertType("multikey", index.multikey, "boolean");
|
|
224
|
+
}
|
|
225
|
+
if (index.unique) {
|
|
226
|
+
validator.assertType("unique", index.unique, "boolean");
|
|
227
|
+
}
|
|
208
228
|
validator.assertHas(index, "fields");
|
|
209
229
|
index.fields.forEach((field) => {
|
|
210
230
|
validator.assertHas(field, "fieldPath");
|
|
@@ -239,6 +259,18 @@ class FirestoreApi {
|
|
|
239
259
|
if (index.queryScope) {
|
|
240
260
|
validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
|
|
241
261
|
}
|
|
262
|
+
if (index.apiScope) {
|
|
263
|
+
validator.assertEnum(index, "apiScope", Object.keys(types.ApiScope));
|
|
264
|
+
}
|
|
265
|
+
if (index.density) {
|
|
266
|
+
validator.assertEnum(index, "density", Object.keys(types.Density));
|
|
267
|
+
}
|
|
268
|
+
if (index.multikey) {
|
|
269
|
+
validator.assertType("multikey", index.multikey, "boolean");
|
|
270
|
+
}
|
|
271
|
+
if (index.unique) {
|
|
272
|
+
validator.assertType("unique", index.unique, "boolean");
|
|
273
|
+
}
|
|
242
274
|
});
|
|
243
275
|
}
|
|
244
276
|
async patchField(project, spec, databaseId = "(default)") {
|
|
@@ -246,6 +278,10 @@ class FirestoreApi {
|
|
|
246
278
|
const indexes = spec.indexes.map((index) => {
|
|
247
279
|
return {
|
|
248
280
|
queryScope: index.queryScope,
|
|
281
|
+
apiScope: index.apiScope,
|
|
282
|
+
density: index.density,
|
|
283
|
+
multikey: index.multikey,
|
|
284
|
+
unique: index.unique,
|
|
249
285
|
fields: [
|
|
250
286
|
{
|
|
251
287
|
fieldPath: spec.fieldPath,
|
|
@@ -282,6 +318,10 @@ class FirestoreApi {
|
|
|
282
318
|
return this.apiClient.post(url, {
|
|
283
319
|
fields: index.fields,
|
|
284
320
|
queryScope: index.queryScope,
|
|
321
|
+
apiScope: index.apiScope,
|
|
322
|
+
density: index.density,
|
|
323
|
+
multikey: index.multikey,
|
|
324
|
+
unique: index.unique,
|
|
285
325
|
});
|
|
286
326
|
}
|
|
287
327
|
deleteIndex(index) {
|
|
@@ -296,6 +336,18 @@ class FirestoreApi {
|
|
|
296
336
|
if (index.queryScope !== spec.queryScope) {
|
|
297
337
|
return false;
|
|
298
338
|
}
|
|
339
|
+
if (index.apiScope !== spec.apiScope) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
if (index.density !== spec.density) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
if (index.multikey !== spec.multikey) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
if (index.unique !== spec.unique) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
299
351
|
if (index.fields.length !== spec.fields.length) {
|
|
300
352
|
return false;
|
|
301
353
|
}
|
|
@@ -312,6 +364,9 @@ class FirestoreApi {
|
|
|
312
364
|
if (iField.arrayConfig !== sField.arrayConfig) {
|
|
313
365
|
return false;
|
|
314
366
|
}
|
|
367
|
+
if (iField.vectorConfig !== sField.vectorConfig) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
315
370
|
i++;
|
|
316
371
|
}
|
|
317
372
|
return true;
|
|
@@ -368,8 +423,19 @@ class FirestoreApi {
|
|
|
368
423
|
const i = {
|
|
369
424
|
collectionGroup: index.collectionGroup || index.collectionId,
|
|
370
425
|
queryScope: index.queryScope || types.QueryScope.COLLECTION,
|
|
371
|
-
fields: [],
|
|
372
426
|
};
|
|
427
|
+
if (index.apiScope) {
|
|
428
|
+
i.apiScope = index.apiScope;
|
|
429
|
+
}
|
|
430
|
+
if (index.density) {
|
|
431
|
+
i.density = index.density;
|
|
432
|
+
}
|
|
433
|
+
if (index.multikey !== undefined) {
|
|
434
|
+
i.multikey = index.multikey;
|
|
435
|
+
}
|
|
436
|
+
if (index.unique !== undefined) {
|
|
437
|
+
i.unique = index.unique;
|
|
438
|
+
}
|
|
373
439
|
if (index.fields) {
|
|
374
440
|
i.fields = index.fields.map((field) => {
|
|
375
441
|
const f = {
|
|
@@ -429,6 +495,7 @@ class FirestoreApi {
|
|
|
429
495
|
const payload = {
|
|
430
496
|
locationId: req.locationId,
|
|
431
497
|
type: req.type,
|
|
498
|
+
databaseEdition: req.databaseEdition,
|
|
432
499
|
deleteProtectionState: req.deleteProtectionState,
|
|
433
500
|
pointInTimeRecoveryEnablement: req.pointInTimeRecoveryEnablement,
|
|
434
501
|
cmekConfig: req.cmekConfig,
|
|
@@ -26,7 +26,7 @@ exports.assertHasOneOf = assertHasOneOf;
|
|
|
26
26
|
function assertEnum(obj, prop, valid) {
|
|
27
27
|
const objString = clc.cyan(JSON.stringify(obj));
|
|
28
28
|
if (valid.indexOf(obj[prop]) < 0) {
|
|
29
|
-
throw new error_1.FirebaseError(`Field "${prop}" must be one of
|
|
29
|
+
throw new error_1.FirebaseError(`Field "${prop}" must be one of ${valid.join(", ")}: ${objString}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
exports.assertEnum = assertEnum;
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.logFunctionsConfigDeprecationWarning = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
|
-
const FUNCTIONS_CONFIG_DEPRECATION_MESSAGE = `DEPRECATION NOTICE: Action required to deploy after
|
|
5
|
+
const FUNCTIONS_CONFIG_DEPRECATION_MESSAGE = `DEPRECATION NOTICE: Action required to deploy after March 2026
|
|
6
6
|
|
|
7
7
|
functions.config() API is deprecated.
|
|
8
|
-
Cloud Runtime Configuration API, the Google Cloud service used to store function configuration data, will be shut down
|
|
8
|
+
Cloud Runtime Configuration API, the Google Cloud service used to store function configuration data, will be shut down in March 2026. As a result, you must migrate away from using functions.config() to continue deploying your functions after March 2026.
|
|
9
9
|
|
|
10
10
|
What this means for you:
|
|
11
11
|
|
|
12
|
-
- The Firebase CLI commands for managing this configuration (functions:config:set, get, unset, clone, and export) are deprecated. These commands no longer work after
|
|
13
|
-
- firebase deploy command will fail for functions that use the legacy functions.config() API after
|
|
12
|
+
- The Firebase CLI commands for managing this configuration (functions:config:set, get, unset, clone, and export) are deprecated. These commands will no longer work after March 2026.
|
|
13
|
+
- firebase deploy command will fail for functions that use the legacy functions.config() API after March 2026.
|
|
14
14
|
|
|
15
15
|
Existing deployments will continue to work with their current configuration.
|
|
16
16
|
|
|
@@ -103,7 +103,7 @@ exports.executeSqlCmdsAsIamUser = executeSqlCmdsAsIamUser;
|
|
|
103
103
|
async function executeSqlCmdsAsSuperUser(options, instanceId, databaseId, cmds, silent = false, transaction = false) {
|
|
104
104
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
105
105
|
const superuser = "firebasesuperuser";
|
|
106
|
-
const temporaryPassword = utils.
|
|
106
|
+
const temporaryPassword = utils.generatePassword(20);
|
|
107
107
|
await cloudSqlAdminClient.createUser(projectId, instanceId, "BUILT_IN", superuser, temporaryPassword);
|
|
108
108
|
return await execute([`SET ROLE = '${superuser}'`, ...cmds], {
|
|
109
109
|
projectId,
|
|
@@ -75,8 +75,7 @@ async function askQuestions(setup, config) {
|
|
|
75
75
|
const unusedFrameworks = fileUtils_1.SUPPORTED_FRAMEWORKS.filter((framework) => { var _a; return !((_a = newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk[framework]); });
|
|
76
76
|
if (unusedFrameworks.length > 0) {
|
|
77
77
|
const additionalFrameworks = await (0, prompt_1.checkbox)({
|
|
78
|
-
message: "Which frameworks would you like to generate SDKs for? "
|
|
79
|
-
"Press Space to select features, then Enter to confirm your choices.",
|
|
78
|
+
message: "Which frameworks would you like to generate SDKs for in addition to the TypeScript SDK? Press Enter to skip.\n",
|
|
80
79
|
choices: fileUtils_1.SUPPORTED_FRAMEWORKS.map((frameworkStr) => {
|
|
81
80
|
var _a, _b;
|
|
82
81
|
return ({
|
package/lib/mcp/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
|
7
7
|
const util_1 = require("./util");
|
|
8
8
|
const types_1 = require("./types");
|
|
9
9
|
const index_1 = require("./tools/index");
|
|
10
|
+
const index_2 = require("./prompts/index");
|
|
10
11
|
const configstore_1 = require("../configstore");
|
|
11
12
|
const command_1 = require("../command");
|
|
12
13
|
const requireAuth_1 = require("../requireAuth");
|
|
@@ -22,7 +23,7 @@ const api = require("../api");
|
|
|
22
23
|
const logging_transport_1 = require("./logging-transport");
|
|
23
24
|
const env_1 = require("../env");
|
|
24
25
|
const timeout_1 = require("../timeout");
|
|
25
|
-
const SERVER_VERSION = "0.
|
|
26
|
+
const SERVER_VERSION = "0.3.0";
|
|
26
27
|
const cmd = new command_1.Command("experimental:mcp");
|
|
27
28
|
const orderedLogLevels = [
|
|
28
29
|
"debug",
|
|
@@ -56,9 +57,15 @@ class FirebaseMcpServer {
|
|
|
56
57
|
this.activeFeatures = options.activeFeatures;
|
|
57
58
|
this.startupRoot = options.projectRoot || process.env.PROJECT_ROOT;
|
|
58
59
|
this.server = new index_js_1.Server({ name: "firebase", version: SERVER_VERSION });
|
|
59
|
-
this.server.registerCapabilities({
|
|
60
|
+
this.server.registerCapabilities({
|
|
61
|
+
tools: { listChanged: true },
|
|
62
|
+
logging: {},
|
|
63
|
+
prompts: { listChanged: true },
|
|
64
|
+
});
|
|
60
65
|
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, this.mcpListTools.bind(this));
|
|
61
66
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, this.mcpCallTool.bind(this));
|
|
67
|
+
this.server.setRequestHandler(types_js_1.ListPromptsRequestSchema, this.mcpListPrompts.bind(this));
|
|
68
|
+
this.server.setRequestHandler(types_js_1.GetPromptRequestSchema, this.mcpGetPrompt.bind(this));
|
|
62
69
|
this.server.oninitialized = async () => {
|
|
63
70
|
var _a, _b;
|
|
64
71
|
const clientInfo = this.server.getClientVersion();
|
|
@@ -159,11 +166,19 @@ class FirebaseMcpServer {
|
|
|
159
166
|
getTool(name) {
|
|
160
167
|
return this.availableTools.find((t) => t.mcp.name === name) || null;
|
|
161
168
|
}
|
|
169
|
+
get availablePrompts() {
|
|
170
|
+
var _a;
|
|
171
|
+
return (0, index_2.availablePrompts)(((_a = this.activeFeatures) === null || _a === void 0 ? void 0 : _a.length) ? this.activeFeatures : this.detectedFeatures);
|
|
172
|
+
}
|
|
173
|
+
getPrompt(name) {
|
|
174
|
+
return this.availablePrompts.find((p) => p.mcp.name === name) || null;
|
|
175
|
+
}
|
|
162
176
|
setProjectRoot(newRoot) {
|
|
163
177
|
this.updateStoredClientConfig({ projectRoot: newRoot });
|
|
164
178
|
this.cachedProjectRoot = newRoot || undefined;
|
|
165
179
|
this.detectedFeatures = undefined;
|
|
166
180
|
void this.server.sendToolListChanged();
|
|
181
|
+
void this.server.sendPromptListChanged();
|
|
167
182
|
}
|
|
168
183
|
async resolveOptions() {
|
|
169
184
|
const options = { cwd: this.cachedProjectRoot, isMCP: true };
|
|
@@ -258,6 +273,64 @@ class FirebaseMcpServer {
|
|
|
258
273
|
return (0, util_1.mcpError)(err);
|
|
259
274
|
}
|
|
260
275
|
}
|
|
276
|
+
async mcpListPrompts() {
|
|
277
|
+
await Promise.all([this.detectActiveFeatures(), this.detectProjectRoot()]);
|
|
278
|
+
const hasActiveProject = !!(await this.getProjectId());
|
|
279
|
+
await this.trackGA4("mcp_list_prompts");
|
|
280
|
+
const skipAutoAuthForStudio = (0, env_1.isFirebaseStudio)();
|
|
281
|
+
return {
|
|
282
|
+
prompts: this.availablePrompts.map((p) => ({
|
|
283
|
+
name: p.mcp.name,
|
|
284
|
+
description: p.mcp.description,
|
|
285
|
+
annotations: p.mcp.annotations,
|
|
286
|
+
arguments: p.mcp.arguments,
|
|
287
|
+
})),
|
|
288
|
+
_meta: {
|
|
289
|
+
projectRoot: this.cachedProjectRoot,
|
|
290
|
+
projectDetected: hasActiveProject,
|
|
291
|
+
authenticatedUser: await this.getAuthenticatedUser(skipAutoAuthForStudio),
|
|
292
|
+
activeFeatures: this.activeFeatures,
|
|
293
|
+
detectedFeatures: this.detectedFeatures,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async mcpGetPrompt(req) {
|
|
298
|
+
await this.detectProjectRoot();
|
|
299
|
+
const promptName = req.params.name;
|
|
300
|
+
const promptArgs = req.params.arguments || {};
|
|
301
|
+
const prompt = this.getPrompt(promptName);
|
|
302
|
+
if (!prompt) {
|
|
303
|
+
throw new Error(`Prompt '${promptName}' could not be found.`);
|
|
304
|
+
}
|
|
305
|
+
let projectId = await this.getProjectId();
|
|
306
|
+
projectId = projectId || "";
|
|
307
|
+
const skipAutoAuthForStudio = (0, env_1.isFirebaseStudio)();
|
|
308
|
+
const accountEmail = await this.getAuthenticatedUser(skipAutoAuthForStudio);
|
|
309
|
+
const options = { projectDir: this.cachedProjectRoot, cwd: this.cachedProjectRoot };
|
|
310
|
+
const promptsCtx = {
|
|
311
|
+
projectId: projectId,
|
|
312
|
+
host: this,
|
|
313
|
+
config: config_1.Config.load(options, true) || new config_1.Config({}, options),
|
|
314
|
+
rc: (0, rc_1.loadRC)(options),
|
|
315
|
+
accountEmail,
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
const messages = await prompt.fn(promptArgs, promptsCtx);
|
|
319
|
+
await this.trackGA4("mcp_get_prompt", {
|
|
320
|
+
tool_name: promptName,
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
messages,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
await this.trackGA4("mcp_get_prompt", {
|
|
328
|
+
tool_name: promptName,
|
|
329
|
+
error: 1,
|
|
330
|
+
});
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
261
334
|
async start() {
|
|
262
335
|
const transport = process.env.FIREBASE_MCP_DEBUG_LOG
|
|
263
336
|
? new logging_transport_1.LoggingStdioServerTransport(process.env.FIREBASE_MCP_DEBUG_LOG)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy = void 0;
|
|
4
|
+
const prompt_1 = require("../../prompt");
|
|
5
|
+
exports.deploy = (0, prompt_1.prompt)({
|
|
6
|
+
name: "deploy",
|
|
7
|
+
omitPrefix: true,
|
|
8
|
+
description: "Use this command to deploy resources to Firebase.",
|
|
9
|
+
arguments: [
|
|
10
|
+
{
|
|
11
|
+
name: "prompt",
|
|
12
|
+
description: "any specific instructions you wish to provide about deploying",
|
|
13
|
+
required: false,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
annotations: {
|
|
17
|
+
title: "Deploy to Firebase",
|
|
18
|
+
},
|
|
19
|
+
}, async ({ prompt }, { config, projectId, accountEmail }) => {
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
role: "user",
|
|
23
|
+
content: {
|
|
24
|
+
type: "text",
|
|
25
|
+
text: `
|
|
26
|
+
Your goal is to deploy resources from the current project to Firebase.
|
|
27
|
+
|
|
28
|
+
Active user: ${accountEmail || "<NONE>"}
|
|
29
|
+
Active project: ${projectId || "<NONE>"}
|
|
30
|
+
|
|
31
|
+
Contents of \`firebase.json\` config file:
|
|
32
|
+
|
|
33
|
+
\`\`\`json
|
|
34
|
+
${config.readProjectFile("firebase.json", { fallback: "<FILE DOES NOT EXIST>" })}
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
## User Instructions
|
|
38
|
+
|
|
39
|
+
${prompt || "<the user didn't supply specific instructions>"}
|
|
40
|
+
|
|
41
|
+
## Steps
|
|
42
|
+
|
|
43
|
+
Follow the steps below taking note of any user instructions provided above.
|
|
44
|
+
|
|
45
|
+
1. If there is no active user, prompt the user to run \`firebase login\` in an interactive terminal before continuing.
|
|
46
|
+
2. If there is no \`firebase.json\` file and the current workspace is a static web application, manually create a \`firebase.json\` with \`"hosting"\` configuration based on the current directory's web app configuration. Add a \`{"hosting": {"predeploy": "<build_script>"}}\` config to build before deploying.
|
|
47
|
+
3. If there is no active project, ask the user if they want to use an existing project or create a new one.
|
|
48
|
+
3a. If create a new one, use the \`firebase_create_project\` tool.
|
|
49
|
+
3b. If they want to use an existing one, ask them for a project id (the \`firebase_list_projects\` tool may be helpful).
|
|
50
|
+
4. Only after making sure Firebase has been initialized, run the \`firebase deploy\` shell command to perform the deploy. This may take a few minutes.
|
|
51
|
+
5. If the deploy has errors, attempt to fix them and ask the user clarifying questions as needed.
|
|
52
|
+
6. If the deploy needs \`--force\` to run successfully, ALWAYS prompt the user before running \`firebase deploy --force\`.
|
|
53
|
+
7. If only one specific feature is failing, use command \`firebase deploy --only <feature>\` as you debug.
|
|
54
|
+
`.trim(),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.availablePrompts = void 0;
|
|
4
|
+
const core_1 = require("./core");
|
|
5
|
+
const prompts = {
|
|
6
|
+
core: core_1.corePrompts,
|
|
7
|
+
firestore: [],
|
|
8
|
+
storage: [],
|
|
9
|
+
dataconnect: [],
|
|
10
|
+
auth: [],
|
|
11
|
+
messaging: [],
|
|
12
|
+
remoteconfig: [],
|
|
13
|
+
crashlytics: [],
|
|
14
|
+
apphosting: [],
|
|
15
|
+
database: [],
|
|
16
|
+
};
|
|
17
|
+
function namespacePrompts(promptsToNamespace, feature) {
|
|
18
|
+
return promptsToNamespace.map((p) => {
|
|
19
|
+
const newPrompt = Object.assign({}, p);
|
|
20
|
+
newPrompt.mcp = Object.assign({}, p.mcp);
|
|
21
|
+
if (newPrompt.mcp.omitPrefix) {
|
|
22
|
+
}
|
|
23
|
+
else if (feature === "core") {
|
|
24
|
+
newPrompt.mcp.name = `firebase:${p.mcp.name}`;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
newPrompt.mcp.name = `firebase:${feature}:${p.mcp.name}`;
|
|
28
|
+
}
|
|
29
|
+
newPrompt.mcp._meta = Object.assign(Object.assign({}, p.mcp._meta), { feature });
|
|
30
|
+
return newPrompt;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function availablePrompts(features) {
|
|
34
|
+
const allPrompts = namespacePrompts(prompts["core"], "core");
|
|
35
|
+
if (!features) {
|
|
36
|
+
features = Object.keys(prompts).filter((f) => f !== "core");
|
|
37
|
+
}
|
|
38
|
+
for (const feature of features) {
|
|
39
|
+
if (prompts[feature] && feature !== "core") {
|
|
40
|
+
allPrompts.push(...namespacePrompts(prompts[feature], feature));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return allPrompts;
|
|
44
|
+
}
|
|
45
|
+
exports.availablePrompts = availablePrompts;
|
|
@@ -37,5 +37,15 @@ exports.get_sdk_config = (0, tool_1.tool)({
|
|
|
37
37
|
if (!appId)
|
|
38
38
|
return (0, util_1.mcpError)(`Could not find an app for platform '${inputPlatform}' in project '${projectId}'`);
|
|
39
39
|
const sdkConfig = await (0, apps_1.getAppConfig)(appId, platform);
|
|
40
|
+
if ("configFilename" in sdkConfig) {
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "text",
|
|
45
|
+
text: `SDK config content for \`${sdkConfig.configFilename}\`:\n\n\`\`\`\n${Buffer.from(sdkConfig.configFileContents, "base64").toString("utf-8")}\n\`\`\``,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
40
50
|
return (0, util_1.toContent)(sdkConfig, { format: "json" });
|
|
41
51
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_data = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const url = require("node:url");
|
|
8
|
+
const apiv2_1 = require("../../../apiv2");
|
|
9
|
+
const consumers_1 = require("node:stream/consumers");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
exports.get_data = (0, tool_1.tool)({
|
|
12
|
+
name: "get_data",
|
|
13
|
+
description: "Returns RTDB data from the specified location",
|
|
14
|
+
inputSchema: zod_1.z.object({
|
|
15
|
+
databaseUrl: zod_1.z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebasedatabase.app. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
19
|
+
path: zod_1.z.string().describe("The path to the data to read. (ex: /my/cool/path)"),
|
|
20
|
+
}),
|
|
21
|
+
annotations: {
|
|
22
|
+
title: "Get Realtime Database data",
|
|
23
|
+
readOnlyHint: true,
|
|
24
|
+
},
|
|
25
|
+
_meta: {
|
|
26
|
+
requiresAuth: false,
|
|
27
|
+
requiresProject: false,
|
|
28
|
+
},
|
|
29
|
+
}, async ({ path: getPath, databaseUrl }, { projectId, host }) => {
|
|
30
|
+
if (!getPath.startsWith("/")) {
|
|
31
|
+
return (0, util_1.mcpError)(`paths must start with '/' (you passed ''${getPath}')`);
|
|
32
|
+
}
|
|
33
|
+
const dbUrl = new url.URL(databaseUrl
|
|
34
|
+
? `${databaseUrl}/${getPath}.json`
|
|
35
|
+
: node_path_1.default.join(`https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`, `${getPath}.json`));
|
|
36
|
+
const client = new apiv2_1.Client({
|
|
37
|
+
urlPrefix: dbUrl.origin,
|
|
38
|
+
auth: true,
|
|
39
|
+
});
|
|
40
|
+
host.logger.debug(`sending read request to path '${getPath}' for url '${dbUrl.toString()}'`);
|
|
41
|
+
const res = await client.request({
|
|
42
|
+
method: "GET",
|
|
43
|
+
path: dbUrl.pathname,
|
|
44
|
+
responseType: "stream",
|
|
45
|
+
resolveOnHTTPError: true,
|
|
46
|
+
});
|
|
47
|
+
const content = await (0, consumers_1.text)(res.body);
|
|
48
|
+
return (0, util_1.toContent)(content);
|
|
49
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_rules = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const apiv2_1 = require("../../../apiv2");
|
|
6
|
+
const tool_1 = require("../../tool");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
exports.get_rules = (0, tool_1.tool)({
|
|
9
|
+
name: "get_rules",
|
|
10
|
+
description: "Get an RTDB database's rules",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
databaseUrl: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
16
|
+
}),
|
|
17
|
+
annotations: {
|
|
18
|
+
title: "Get Realtime Database rules",
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
},
|
|
21
|
+
_meta: {
|
|
22
|
+
requiresAuth: false,
|
|
23
|
+
requiresProject: false,
|
|
24
|
+
},
|
|
25
|
+
}, async ({ databaseUrl }, { projectId }) => {
|
|
26
|
+
const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
|
|
27
|
+
const client = new apiv2_1.Client({ urlPrefix: dbUrl });
|
|
28
|
+
const response = await client.request({
|
|
29
|
+
method: "GET",
|
|
30
|
+
path: "/.settings/rules.json",
|
|
31
|
+
responseType: "stream",
|
|
32
|
+
resolveOnHTTPError: true,
|
|
33
|
+
});
|
|
34
|
+
if (response.status !== 200) {
|
|
35
|
+
return (0, util_1.mcpError)(`Failed to fetch current rules. Code: ${response.status}`);
|
|
36
|
+
}
|
|
37
|
+
const rules = await response.response.text();
|
|
38
|
+
return (0, util_1.toContent)(rules);
|
|
39
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.realtimeDatabaseTools = void 0;
|
|
4
|
+
const get_rules_1 = require("./get_rules");
|
|
5
|
+
const get_data_1 = require("./get_data");
|
|
6
|
+
const set_data_1 = require("./set_data");
|
|
7
|
+
const validate_rules_1 = require("./validate_rules");
|
|
8
|
+
exports.realtimeDatabaseTools = [get_data_1.get_data, set_data_1.set_data, get_rules_1.get_rules, validate_rules_1.validate_rules];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.set_data = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const url = require("node:url");
|
|
8
|
+
const utils_1 = require("../../../utils");
|
|
9
|
+
const apiv2_1 = require("../../../apiv2");
|
|
10
|
+
const error_1 = require("../../../error");
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
exports.set_data = (0, tool_1.tool)({
|
|
13
|
+
name: "set_data",
|
|
14
|
+
description: "Writes RTDB data to the specified location",
|
|
15
|
+
inputSchema: zod_1.z.object({
|
|
16
|
+
databaseUrl: zod_1.z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.us-central1.firebasedatabase.app. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
20
|
+
path: zod_1.z.string().describe("The path to the data to read. (ex: /my/cool/path)"),
|
|
21
|
+
data: zod_1.z.string().describe('The JSON to write. (ex: {"alphabet": ["a", "b", "c"]})'),
|
|
22
|
+
}),
|
|
23
|
+
annotations: {
|
|
24
|
+
title: "Set Realtime Database data",
|
|
25
|
+
readOnlyHint: false,
|
|
26
|
+
idempotentHint: true,
|
|
27
|
+
},
|
|
28
|
+
_meta: {
|
|
29
|
+
requiresAuth: false,
|
|
30
|
+
requiresProject: false,
|
|
31
|
+
},
|
|
32
|
+
}, async ({ path: setPath, databaseUrl, data }, { projectId, host }) => {
|
|
33
|
+
if (!setPath.startsWith("/")) {
|
|
34
|
+
return (0, util_1.mcpError)(`paths must start with '/' (you passed ''${setPath}')`);
|
|
35
|
+
}
|
|
36
|
+
const dbUrl = new url.URL(databaseUrl
|
|
37
|
+
? `${databaseUrl}/${setPath}.json`
|
|
38
|
+
: node_path_1.default.join(`https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`, `${setPath}.json`));
|
|
39
|
+
const client = new apiv2_1.Client({
|
|
40
|
+
urlPrefix: dbUrl.origin,
|
|
41
|
+
auth: true,
|
|
42
|
+
});
|
|
43
|
+
const inStream = (0, utils_1.stringToStream)(data);
|
|
44
|
+
host.logger.debug(`sending write request to path '${setPath}' for url '${dbUrl.toString()}'`);
|
|
45
|
+
try {
|
|
46
|
+
await client.request({
|
|
47
|
+
method: "PUT",
|
|
48
|
+
path: dbUrl.pathname,
|
|
49
|
+
body: inStream,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
host.logger.debug((0, error_1.getErrMsg)(err));
|
|
54
|
+
return (0, util_1.mcpError)(`Unexpected error while setting data: ${(0, error_1.getErrMsg)(err)}`);
|
|
55
|
+
}
|
|
56
|
+
return (0, util_1.toContent)("write successful!");
|
|
57
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validate_rules = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const apiv2_1 = require("../../../apiv2");
|
|
6
|
+
const tool_1 = require("../../tool");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
const rtdb_1 = require("../../../rtdb");
|
|
9
|
+
const error_1 = require("../../../error");
|
|
10
|
+
exports.validate_rules = (0, tool_1.tool)({
|
|
11
|
+
name: "validate_rules",
|
|
12
|
+
description: "Validates an RTDB database's rules",
|
|
13
|
+
inputSchema: zod_1.z.object({
|
|
14
|
+
databaseUrl: zod_1.z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
18
|
+
rules: zod_1.z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('The rules object, as a string (ex: {".read": false, ".write": false})'),
|
|
21
|
+
}),
|
|
22
|
+
annotations: {
|
|
23
|
+
title: "Validate Realtime Database rules",
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
},
|
|
26
|
+
_meta: {
|
|
27
|
+
requiresAuth: true,
|
|
28
|
+
requiresProject: false,
|
|
29
|
+
},
|
|
30
|
+
}, async ({ databaseUrl, rules }, { projectId, host }) => {
|
|
31
|
+
const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
|
|
32
|
+
const client = new apiv2_1.Client({ urlPrefix: dbUrl });
|
|
33
|
+
try {
|
|
34
|
+
await (0, rtdb_1.updateRulesWithClient)(client, rules, { dryRun: true });
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
host.logger.debug(`failed to update rules at url ${dbUrl}`);
|
|
38
|
+
return (0, util_1.mcpError)((0, error_1.getErrMsg)(e));
|
|
39
|
+
}
|
|
40
|
+
return (0, util_1.toContent)("the inputted rules are valid!");
|
|
41
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validate_rules = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const apiv2_1 = require("../../../apiv2");
|
|
6
|
+
const tool_1 = require("../../tool");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
const rtdb_1 = require("../../../rtdb");
|
|
9
|
+
const error_1 = require("../../../error");
|
|
10
|
+
exports.validate_rules = (0, tool_1.tool)({
|
|
11
|
+
name: "validate_rules",
|
|
12
|
+
description: "Validates an RTDB database's rules",
|
|
13
|
+
inputSchema: zod_1.z.object({
|
|
14
|
+
databaseUrl: zod_1.z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
18
|
+
rules: zod_1.z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('The rules object, as a string (ex: {"rules": {".read": false, ".write": false}})'),
|
|
21
|
+
}),
|
|
22
|
+
annotations: {
|
|
23
|
+
title: "Validate Realtime Database rules",
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
},
|
|
26
|
+
_meta: {
|
|
27
|
+
requiresAuth: true,
|
|
28
|
+
requiresProject: false,
|
|
29
|
+
},
|
|
30
|
+
}, async ({ databaseUrl, rules }, { projectId, host }) => {
|
|
31
|
+
const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
|
|
32
|
+
const client = new apiv2_1.Client({ urlPrefix: dbUrl });
|
|
33
|
+
try {
|
|
34
|
+
await (0, rtdb_1.updateRulesWithClient)(client, rules, { dryRun: true });
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
host.logger.debug(`failed to validate rules at url ${dbUrl}`);
|
|
38
|
+
return (0, util_1.mcpError)((0, error_1.getErrMsg)(e));
|
|
39
|
+
}
|
|
40
|
+
return (0, util_1.toContent)("the inputted rules are valid!");
|
|
41
|
+
});
|
package/lib/mcp/tools/index.js
CHANGED
|
@@ -10,8 +10,9 @@ const index_6 = require("./messaging/index");
|
|
|
10
10
|
const index_7 = require("./remoteconfig/index");
|
|
11
11
|
const index_8 = require("./crashlytics/index");
|
|
12
12
|
const index_9 = require("./apphosting/index");
|
|
13
|
+
const index_10 = require("./database/index");
|
|
13
14
|
function availableTools(activeFeatures) {
|
|
14
|
-
const toolDefs =
|
|
15
|
+
const toolDefs = [];
|
|
15
16
|
if (!(activeFeatures === null || activeFeatures === void 0 ? void 0 : activeFeatures.length)) {
|
|
16
17
|
activeFeatures = Object.keys(tools);
|
|
17
18
|
}
|
|
@@ -22,6 +23,7 @@ function availableTools(activeFeatures) {
|
|
|
22
23
|
}
|
|
23
24
|
exports.availableTools = availableTools;
|
|
24
25
|
const tools = {
|
|
26
|
+
core: addFeaturePrefix("firebase", index_4.coreTools),
|
|
25
27
|
firestore: addFeaturePrefix("firestore", index_3.firestoreTools),
|
|
26
28
|
auth: addFeaturePrefix("auth", index_1.authTools),
|
|
27
29
|
dataconnect: addFeaturePrefix("dataconnect", index_2.dataconnectTools),
|
|
@@ -30,6 +32,7 @@ const tools = {
|
|
|
30
32
|
remoteconfig: addFeaturePrefix("remoteconfig", index_7.remoteConfigTools),
|
|
31
33
|
crashlytics: addFeaturePrefix("crashlytics", index_8.crashlyticsTools),
|
|
32
34
|
apphosting: addFeaturePrefix("apphosting", index_9.appHostingTools),
|
|
35
|
+
database: addFeaturePrefix("database", index_10.realtimeDatabaseTools),
|
|
33
36
|
};
|
|
34
37
|
function addFeaturePrefix(feature, tools) {
|
|
35
38
|
return tools.map((tool) => (Object.assign(Object.assign({}, tool), { mcp: Object.assign(Object.assign({}, tool.mcp), { name: `${feature}_${tool.mcp.name}`, _meta: Object.assign(Object.assign({}, tool.mcp._meta), { feature }) }) })));
|
|
@@ -21,7 +21,7 @@ function getRulesTool(productName, releaseName) {
|
|
|
21
21
|
}, async (_, { projectId }) => {
|
|
22
22
|
const rulesetName = await (0, rules_1.getLatestRulesetName)(projectId, releaseName);
|
|
23
23
|
if (!rulesetName)
|
|
24
|
-
return (0, util_1.mcpError)(`No active
|
|
24
|
+
return (0, util_1.mcpError)(`No active ${productName} rules were found in project '${projectId}'`);
|
|
25
25
|
const rules = await (0, rules_1.getRulesetContent)(rulesetName);
|
|
26
26
|
return (0, util_1.toContent)(rules[0].content);
|
|
27
27
|
});
|
package/lib/mcp/types.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SERVER_FEATURES = void 0;
|
|
4
4
|
exports.SERVER_FEATURES = [
|
|
5
|
+
"core",
|
|
5
6
|
"firestore",
|
|
6
7
|
"storage",
|
|
7
8
|
"dataconnect",
|
|
@@ -10,4 +11,5 @@ exports.SERVER_FEATURES = [
|
|
|
10
11
|
"remoteconfig",
|
|
11
12
|
"crashlytics",
|
|
12
13
|
"apphosting",
|
|
14
|
+
"database",
|
|
13
15
|
];
|
package/lib/mcp/util.js
CHANGED
|
@@ -56,6 +56,7 @@ function commandExistsSync(command) {
|
|
|
56
56
|
}
|
|
57
57
|
exports.commandExistsSync = commandExistsSync;
|
|
58
58
|
const SERVER_FEATURE_APIS = {
|
|
59
|
+
core: "",
|
|
59
60
|
firestore: (0, api_1.firestoreOrigin)(),
|
|
60
61
|
storage: (0, api_1.storageOrigin)(),
|
|
61
62
|
dataconnect: (0, api_1.dataconnectOrigin)(),
|
|
@@ -64,6 +65,7 @@ const SERVER_FEATURE_APIS = {
|
|
|
64
65
|
remoteconfig: (0, api_1.remoteConfigApiOrigin)(),
|
|
65
66
|
crashlytics: (0, api_1.crashlyticsApiOrigin)(),
|
|
66
67
|
apphosting: (0, api_1.apphostingOrigin)(),
|
|
68
|
+
database: (0, api_1.realtimeOrigin)(),
|
|
67
69
|
};
|
|
68
70
|
async function checkFeatureActive(feature, projectId, options) {
|
|
69
71
|
var _a;
|
package/lib/rtdb.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.updateRules = void 0;
|
|
3
|
+
exports.updateRulesWithClient = exports.updateRules = void 0;
|
|
4
4
|
const apiv2_1 = require("./apiv2");
|
|
5
5
|
const database_1 = require("./management/database");
|
|
6
6
|
const error_1 = require("./error");
|
|
7
7
|
const api_1 = require("./database/api");
|
|
8
8
|
const utils = require("./utils");
|
|
9
9
|
async function updateRules(projectId, instance, src, options = {}) {
|
|
10
|
-
const queryParams = {};
|
|
11
|
-
if (options.dryRun) {
|
|
12
|
-
queryParams.dryRun = "true";
|
|
13
|
-
}
|
|
14
10
|
const downstreamOptions = { instance: instance, project: projectId };
|
|
15
11
|
await (0, database_1.populateInstanceDetails)(downstreamOptions);
|
|
16
12
|
if (!downstreamOptions.instanceDetails) {
|
|
@@ -18,6 +14,14 @@ async function updateRules(projectId, instance, src, options = {}) {
|
|
|
18
14
|
}
|
|
19
15
|
const origin = utils.getDatabaseUrl((0, api_1.realtimeOriginOrCustomUrl)(downstreamOptions.instanceDetails.databaseUrl), instance, "");
|
|
20
16
|
const client = new apiv2_1.Client({ urlPrefix: origin });
|
|
17
|
+
return updateRulesWithClient(client, options);
|
|
18
|
+
}
|
|
19
|
+
exports.updateRules = updateRules;
|
|
20
|
+
async function updateRulesWithClient(client, src, options = {}) {
|
|
21
|
+
const queryParams = {};
|
|
22
|
+
if (options.dryRun) {
|
|
23
|
+
queryParams.dryRun = "true";
|
|
24
|
+
}
|
|
21
25
|
const response = await client.request({
|
|
22
26
|
method: "PUT",
|
|
23
27
|
path: ".settings/rules.json",
|
|
@@ -32,4 +36,4 @@ async function updateRules(projectId, instance, src, options = {}) {
|
|
|
32
36
|
throw new error_1.FirebaseError("Unexpected error while deploying database rules.", { exit: 2 });
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
|
-
exports.
|
|
39
|
+
exports.updateRulesWithClient = updateRulesWithClient;
|
package/lib/utils.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.openInBrowserPopup = exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.sleep = exports.promiseWithSpinner = exports.tryParse = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarningToStderr = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.setVSCodeEnvVars = exports.getInheritedOption = exports.consoleUrl = exports.vscodeEnvVars = exports.envOverrides = exports.IS_WINDOWS = void 0;
|
|
4
|
-
exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = void 0;
|
|
4
|
+
exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generatePassword = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = void 0;
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const tty = require("tty");
|
|
7
7
|
const path = require("node:path");
|
|
8
8
|
const yaml = require("yaml");
|
|
9
|
+
const crypto = require("node:crypto");
|
|
9
10
|
const _ = require("lodash");
|
|
10
11
|
const url = require("url");
|
|
11
12
|
const http = require("http");
|
|
@@ -546,6 +547,28 @@ function generateId(n = 6) {
|
|
|
546
547
|
return id;
|
|
547
548
|
}
|
|
548
549
|
exports.generateId = generateId;
|
|
550
|
+
function generatePassword(n = 20) {
|
|
551
|
+
const lower = "abcdefghijklmnopqrstuvwxyz";
|
|
552
|
+
const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
553
|
+
const numbers = "0123456789";
|
|
554
|
+
const special = "!@#$%^&*()_+~`|}{[]:;?><,./-=";
|
|
555
|
+
const all = lower + upper + numbers + special;
|
|
556
|
+
let pw = "";
|
|
557
|
+
pw += lower[crypto.randomInt(lower.length)];
|
|
558
|
+
pw += upper[crypto.randomInt(upper.length)];
|
|
559
|
+
pw += numbers[crypto.randomInt(numbers.length)];
|
|
560
|
+
pw += special[crypto.randomInt(special.length)];
|
|
561
|
+
for (let i = 4; i < n; i++) {
|
|
562
|
+
pw += all[crypto.randomInt(all.length)];
|
|
563
|
+
}
|
|
564
|
+
const pwArray = pw.split("");
|
|
565
|
+
for (let i = pwArray.length - 1; i > 0; i--) {
|
|
566
|
+
const j = crypto.randomInt(i);
|
|
567
|
+
[pwArray[i], pwArray[j]] = [pwArray[j], pwArray[i]];
|
|
568
|
+
}
|
|
569
|
+
return pwArray.join("");
|
|
570
|
+
}
|
|
571
|
+
exports.generatePassword = generatePassword;
|
|
549
572
|
function readSecretValue(prompt, dataFile) {
|
|
550
573
|
if ((!dataFile || dataFile === "-") && tty.isatty(0)) {
|
|
551
574
|
return (0, prompt_1.password)({ message: prompt });
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
// Example:
|
|
2
|
+
// Example (Standard Edition):
|
|
3
3
|
//
|
|
4
4
|
// "indexes": [
|
|
5
5
|
// {
|
|
@@ -21,6 +21,31 @@
|
|
|
21
21
|
// },
|
|
22
22
|
// ]
|
|
23
23
|
// ]
|
|
24
|
+
//
|
|
25
|
+
// Example (Enterprise Edition):
|
|
26
|
+
//
|
|
27
|
+
// "indexes": [
|
|
28
|
+
// {
|
|
29
|
+
// "collectionGroup": "reviews",
|
|
30
|
+
// "queryScope": "COLLECTION_GROUP",
|
|
31
|
+
// "apiScope": "MONGODB_COMPATIBLE_API",
|
|
32
|
+
// "density": "DENSE",
|
|
33
|
+
// "multikey": false,
|
|
34
|
+
// "fields": [
|
|
35
|
+
// { "fieldPath": "baz", "mode": "ASCENDING" }
|
|
36
|
+
// ]
|
|
37
|
+
// },
|
|
38
|
+
// {
|
|
39
|
+
// "collectionGroup": "items",
|
|
40
|
+
// "queryScope": "COLLECTION_GROUP",
|
|
41
|
+
// "apiScope": "MONGODB_COMPATIBLE_API",
|
|
42
|
+
// "density": "SPARSE_ANY",
|
|
43
|
+
// "multikey": true,
|
|
44
|
+
// "fields": [
|
|
45
|
+
// { "fieldPath": "baz", "mode": "ASCENDING" }
|
|
46
|
+
// ]
|
|
47
|
+
// },
|
|
48
|
+
// ]
|
|
24
49
|
"indexes": [],
|
|
25
50
|
"fieldOverrides": []
|
|
26
51
|
}
|