firebase-tools 13.17.0 → 13.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +10 -9
  2. package/lib/commands/dataconnect-services-list.js +4 -3
  3. package/lib/commands/dataconnect-sql-grant.js +37 -0
  4. package/lib/commands/dataconnect-sql-migrate.js +2 -1
  5. package/lib/commands/deploy.js +2 -0
  6. package/lib/commands/ext-info.js +3 -1
  7. package/lib/commands/ext-sdk-install.js +88 -0
  8. package/lib/commands/ext.js +1 -0
  9. package/lib/commands/index.js +3 -0
  10. package/lib/dataconnect/client.js +1 -1
  11. package/lib/dataconnect/dataplaneClient.js +1 -1
  12. package/lib/dataconnect/ensureApis.js +6 -1
  13. package/lib/dataconnect/load.js +3 -1
  14. package/lib/dataconnect/provisionCloudSql.js +34 -21
  15. package/lib/dataconnect/schemaMigration.js +126 -73
  16. package/lib/deploy/dataconnect/deploy.js +16 -13
  17. package/lib/deploy/dataconnect/prepare.js +36 -0
  18. package/lib/deploy/dataconnect/release.js +9 -2
  19. package/lib/deploy/extensions/deploymentSummary.js +3 -2
  20. package/lib/deploy/extensions/planner.js +32 -3
  21. package/lib/deploy/extensions/prepare.js +36 -64
  22. package/lib/deploy/extensions/release.js +11 -10
  23. package/lib/deploy/extensions/tasks.js +32 -21
  24. package/lib/deploy/firestore/prepare.js +10 -0
  25. package/lib/deploy/firestore/release.js +3 -6
  26. package/lib/deploy/functions/checkIam.js +7 -2
  27. package/lib/deploy/functions/ensure.js +10 -2
  28. package/lib/deploy/functions/prepare.js +10 -2
  29. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  30. package/lib/deploy/index.js +9 -5
  31. package/lib/emulator/downloadableEmulators.js +9 -9
  32. package/lib/extensions/extensionsApi.js +3 -2
  33. package/lib/extensions/extensionsHelper.js +3 -3
  34. package/lib/extensions/localHelper.js +31 -0
  35. package/lib/extensions/runtimes/common.js +186 -38
  36. package/lib/extensions/runtimes/node.js +399 -0
  37. package/lib/extensions/types.js +10 -14
  38. package/lib/extensions/warnings.js +5 -2
  39. package/lib/firestore/checkDatabaseType.js +10 -3
  40. package/lib/frameworks/angular/index.js +15 -3
  41. package/lib/frameworks/next/utils.js +1 -1
  42. package/lib/gcp/cloudsql/permissions.js +6 -1
  43. package/lib/gcp/secretManager.js +12 -5
  44. package/lib/init/features/dataconnect/index.js +66 -53
  45. package/lib/init/features/firestore/index.js +20 -1
  46. package/lib/init/features/firestore/indexes.js +4 -4
  47. package/package.json +1 -1
  48. package/schema/connector-yaml.json +43 -17
  49. package/templates/init/dataconnect/connector.yaml +0 -1
  50. package/templates/init/dataconnect/dataconnect-fdccompatiblemode.yaml +12 -0
  51. package/templates/init/dataconnect/dataconnect.yaml +1 -1
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isExtensionSpec = exports.isResource = exports.isParam = exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
3
+ exports.isExtensionSpec = exports.isResource = exports.isParam = exports.isObject = exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
4
4
  var RegistryLaunchStage;
5
5
  (function (RegistryLaunchStage) {
6
6
  RegistryLaunchStage["EXPERIMENTAL"] = "EXPERIMENTAL";
@@ -14,6 +14,7 @@ var Visibility;
14
14
  Visibility["UNLISTED"] = "unlisted";
15
15
  Visibility["PUBLIC"] = "public";
16
16
  })(Visibility = exports.Visibility || (exports.Visibility = {}));
17
+ const lifecycleStages = ["STAGE_UNSPECIFIED", "ON_INSTALL", "ON_UPDATE", "ON_CONFIGURE"];
17
18
  exports.FUNCTIONS_RESOURCE_TYPE = "firebaseextensions.v1beta.function";
18
19
  exports.FUNCTIONS_V2_RESOURCE_TYPE = "firebaseextensions.v1beta.v2function";
19
20
  var ParamType;
@@ -27,6 +28,7 @@ var ParamType;
27
28
  function isObject(value) {
28
29
  return typeof value === "object" && value !== null;
29
30
  }
31
+ exports.isObject = isObject;
30
32
  const isParam = (param) => {
31
33
  return (isObject(param) && typeof param["param"] === "string" && typeof param["label"] === "string");
32
34
  };
@@ -39,41 +41,35 @@ const isExtensionSpec = (spec) => {
39
41
  if (!isObject(spec) || typeof spec.name !== "string" || typeof spec.version !== "string") {
40
42
  return false;
41
43
  }
42
- let validResources = true;
43
44
  if (spec.resources && Array.isArray(spec.resources)) {
44
45
  for (const res of spec.resources) {
45
- validResources = validResources && (0, exports.isResource)(res);
46
- if (!validResources) {
47
- break;
46
+ if (!(0, exports.isResource)(res)) {
47
+ return false;
48
48
  }
49
49
  }
50
50
  }
51
51
  else {
52
52
  return false;
53
53
  }
54
- let validParams = true;
55
54
  if (spec.params && Array.isArray(spec.params)) {
56
55
  for (const param of spec.params) {
57
- validParams = validParams && (0, exports.isParam)(param);
58
- if (!validParams) {
59
- break;
56
+ if (!(0, exports.isParam)(param)) {
57
+ return false;
60
58
  }
61
59
  }
62
60
  }
63
61
  else {
64
62
  return false;
65
63
  }
66
- let validSysParams = true;
67
64
  if (spec.systemParams && Array.isArray(spec.systemParams)) {
68
65
  for (const param of spec.systemParams) {
69
- validSysParams = validSysParams && (0, exports.isParam)(param);
70
- if (!validSysParams) {
71
- break;
66
+ if (!(0, exports.isParam)(param)) {
67
+ return false;
72
68
  }
73
69
  }
74
70
  }
75
71
  else {
76
- return false;
72
+ return !spec.systemParams;
77
73
  }
78
74
  return true;
79
75
  };
@@ -31,9 +31,12 @@ async function displayWarningsForDeploy(instancesToCreate) {
31
31
  return unpublishedExtensions.length > 0;
32
32
  }
33
33
  exports.displayWarningsForDeploy = displayWarningsForDeploy;
34
- function outOfBandChangesWarning(instanceIds) {
34
+ function outOfBandChangesWarning(instanceIds, isDynamic) {
35
+ const extra = isDynamic
36
+ ? ""
37
+ : " To avoid this, run `firebase ext:export` to sync these changes to your local extensions manifest.";
35
38
  logger_1.logger.warn("The following instances may have been changed in the Firebase console or by another machine since the last deploy from this machine.\n\t" +
36
39
  clc.bold(instanceIds.join("\n\t")) +
37
- "\nIf you proceed with this deployment, those changes will be overwritten. To avoid this, run `firebase ext:export` to sync these changes to your local extensions manifest.");
40
+ `\nIf you proceed with this deployment, those changes will be overwritten.${extra}`);
38
41
  }
39
42
  exports.outOfBandChangesWarning = outOfBandChangesWarning;
@@ -4,14 +4,21 @@ exports.checkDatabaseType = void 0;
4
4
  const api_1 = require("../api");
5
5
  const apiv2_1 = require("../apiv2");
6
6
  const logger_1 = require("../logger");
7
- async function checkDatabaseType(projectId) {
7
+ const error_1 = require("../error");
8
+ async function checkDatabaseType(projectId, databaseId = "(default)") {
8
9
  try {
9
10
  const client = new apiv2_1.Client({ urlPrefix: (0, api_1.firestoreOrigin)(), apiVersion: "v1" });
10
- const resp = await client.get(`/projects/${projectId}/databases/(default)`);
11
+ const resp = await client.get(`/projects/${projectId}/databases/${databaseId}`);
11
12
  return resp.body.type;
12
13
  }
13
14
  catch (err) {
14
- logger_1.logger.debug("error getting database type", err);
15
+ logger_1.logger.debug("error getting database type: ", err);
16
+ if (err instanceof error_1.FirebaseError) {
17
+ if (err.status === 404) {
18
+ logger_1.logger.info(`${databaseId} does not exist in project ${projectId}.`);
19
+ return "DATABASE_DOES_NOT_EXIST";
20
+ }
21
+ }
15
22
  return undefined;
16
23
  }
17
24
  }
@@ -15,7 +15,7 @@ exports.support = "preview";
15
15
  exports.type = 3;
16
16
  exports.docsUrl = "https://firebase.google.com/docs/hosting/frameworks/angular";
17
17
  const DEFAULT_BUILD_SCRIPT = ["ng build"];
18
- exports.supportedRange = "14 - 17";
18
+ exports.supportedRange = "16 - 18";
19
19
  async function discover(dir) {
20
20
  if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
21
21
  return;
@@ -181,9 +181,21 @@ exports.handle = function(req,res) {
181
181
  };\n`;
182
182
  }
183
183
  else if (serverOutputPath) {
184
- bootstrapScript = `const app = ${(serverEntry === null || serverEntry === void 0 ? void 0 : serverEntry.endsWith(".mjs"))
184
+ bootstrapScript = `
185
+ const app = new Promise((resolve) => {
186
+ setTimeout(() => {
187
+ const port = process.env.PORT;
188
+ const socket = 'express.sock';
189
+ process.env.PORT = socket;
190
+
191
+ ${(serverEntry === null || serverEntry === void 0 ? void 0 : serverEntry.endsWith(".mjs"))
185
192
  ? `import(\`./${serverOutputPath}/${serverEntry}\`)`
186
- : `Promise.resolve(require('./${serverOutputPath}/${serverEntry}'))`}.then(server => server.app());
193
+ : `Promise.resolve(require('./${serverOutputPath}/${serverEntry}'))`}.then(({ app }) => {
194
+ process.env.PORT = port;
195
+ resolve(app());
196
+ });
197
+ }, 0);
198
+ });
187
199
  exports.handle = (req,res) => app.then(it => it(req,res));\n`;
188
200
  }
189
201
  else {
@@ -265,7 +265,7 @@ exports.findEsbuildPath = findEsbuildPath;
265
265
  function getGlobalEsbuildVersion(binPath) {
266
266
  var _a;
267
267
  try {
268
- const versionOutput = (_a = (0, child_process_1.execSync)(`${binPath} --version`, { encoding: "utf8" })) === null || _a === void 0 ? void 0 : _a.trim();
268
+ const versionOutput = (_a = (0, child_process_1.execSync)(`"${binPath}" --version`, { encoding: "utf8" })) === null || _a === void 0 ? void 0 : _a.trim();
269
269
  if (!versionOutput) {
270
270
  return null;
271
271
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupSQLPermissions = exports.iamUserIsCSQLAdmin = exports.checkSQLRoleIsGranted = exports.firebasewriter = exports.firebasereader = exports.firebaseowner = void 0;
3
+ exports.setupSQLPermissions = exports.iamUserIsCSQLAdmin = exports.checkSQLRoleIsGranted = exports.fdcSqlRoleMap = exports.firebasewriter = exports.firebasereader = exports.firebaseowner = void 0;
4
4
  const projectUtils_1 = require("../../projectUtils");
5
5
  const connect_1 = require("./connect");
6
6
  const iam_1 = require("../iam");
@@ -19,6 +19,11 @@ function firebasewriter(databaseId) {
19
19
  return `firebasewriter_${databaseId}_public`;
20
20
  }
21
21
  exports.firebasewriter = firebasewriter;
22
+ exports.fdcSqlRoleMap = {
23
+ owner: firebaseowner,
24
+ writer: firebasewriter,
25
+ reader: firebasereader,
26
+ };
22
27
  async function checkSQLRoleIsGranted(options, instanceId, databaseId, grantedRole, granteeRole) {
23
28
  const checkCmd = `
24
29
  DO $$
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.labels = exports.ensureApi = exports.isAppHostingManaged = exports.isFunctionsManaged = exports.FIREBASE_MANAGED = exports.ensureServiceAgentRole = exports.setIamPolicy = exports.getIamPolicy = exports.addVersion = exports.deleteSecret = exports.patchSecret = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.destroySecretVersion = exports.accessSecretVersion = exports.getSecretVersion = exports.listSecretVersions = exports.getSecretMetadata = exports.listSecrets = exports.getSecret = exports.secretManagerConsoleUri = void 0;
3
+ exports.labels = exports.ensureApi = exports.isAppHostingManaged = exports.isFunctionsManaged = exports.FIREBASE_MANAGED = exports.checkServiceAgentRole = exports.ensureServiceAgentRole = exports.setIamPolicy = exports.getIamPolicy = exports.addVersion = exports.deleteSecret = exports.patchSecret = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.destroySecretVersion = exports.accessSecretVersion = exports.getSecretVersion = exports.listSecretVersions = exports.getSecretMetadata = exports.listSecrets = exports.getSecret = exports.secretManagerConsoleUri = void 0;
4
4
  const utils_1 = require("../utils");
5
5
  const error_1 = require("../error");
6
6
  const apiv2_1 = require("../apiv2");
@@ -207,6 +207,14 @@ async function setIamPolicy(secret, bindings) {
207
207
  }
208
208
  exports.setIamPolicy = setIamPolicy;
209
209
  async function ensureServiceAgentRole(secret, serviceAccountEmails, role) {
210
+ const bindings = await checkServiceAgentRole(secret, serviceAccountEmails, role);
211
+ if (bindings.length) {
212
+ await module.exports.setIamPolicy(secret, bindings);
213
+ }
214
+ (0, utils_1.logLabeledSuccess)("secretmanager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmails.join(", ")}`);
215
+ }
216
+ exports.ensureServiceAgentRole = ensureServiceAgentRole;
217
+ async function checkServiceAgentRole(secret, serviceAccountEmails, role) {
210
218
  const policy = await module.exports.getIamPolicy(secret);
211
219
  const bindings = policy.bindings || [];
212
220
  let binding = bindings.find((b) => b.role === role);
@@ -222,11 +230,10 @@ async function ensureServiceAgentRole(secret, serviceAccountEmails, role) {
222
230
  }
223
231
  }
224
232
  if (shouldShortCircuit)
225
- return;
226
- await module.exports.setIamPolicy(secret, bindings);
227
- (0, utils_1.logLabeledSuccess)("secretmanager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmails.join(", ")}`);
233
+ return [];
234
+ return bindings;
228
235
  }
229
- exports.ensureServiceAgentRole = ensureServiceAgentRole;
236
+ exports.checkServiceAgentRole = checkServiceAgentRole;
230
237
  exports.FIREBASE_MANAGED = "firebase-managed";
231
238
  function isFunctionsManaged(secret) {
232
239
  return (secret.labels[exports.FIREBASE_MANAGED] === "true" || secret.labels[exports.FIREBASE_MANAGED] === "functions");
@@ -8,13 +8,16 @@ const provisionCloudSql_1 = require("../../../dataconnect/provisionCloudSql");
8
8
  const freeTrial_1 = require("../../../dataconnect/freeTrial");
9
9
  const cloudsql = require("../../../gcp/cloudsql/cloudsqladmin");
10
10
  const ensureApis_1 = require("../../../dataconnect/ensureApis");
11
+ const experiments = require("../../../experiments");
11
12
  const client_1 = require("../../../dataconnect/client");
12
13
  const emulators_1 = require("../emulators");
13
14
  const names_1 = require("../../../dataconnect/names");
14
15
  const logger_1 = require("../../../logger");
15
16
  const templates_1 = require("../../../templates");
16
17
  const utils_1 = require("../../../utils");
18
+ const cloudbilling_1 = require("../../../gcp/cloudbilling");
17
19
  const DATACONNECT_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/dataconnect.yaml");
20
+ const DATACONNECT_YAML_COMPAT_EXPERIMENT_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/dataconnect-fdccompatiblemode.yaml");
18
21
  const CONNECTOR_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/connector.yaml");
19
22
  const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/schema.gql");
20
23
  const QUERIES_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/queries.gql");
@@ -53,7 +56,8 @@ async function askQuestions(setup, config) {
53
56
  schemaGql: [],
54
57
  shouldProvisionCSQL: false,
55
58
  };
56
- info = await promptForService(setup, info);
59
+ const isBillingEnabled = setup.projectId ? await (0, cloudbilling_1.checkBillingEnabled)(setup.projectId) : false;
60
+ info = await promptForService(setup, info, isBillingEnabled);
57
61
  if (info.cloudSqlInstanceId === "") {
58
62
  info = await promptForCloudSQLInstance(setup, info);
59
63
  }
@@ -70,8 +74,9 @@ async function askQuestions(setup, config) {
70
74
  setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
71
75
  info.shouldProvisionCSQL = !!(setup.projectId &&
72
76
  (info.isNewInstance || info.isNewDatabase) &&
77
+ isBillingEnabled &&
73
78
  (await (0, prompt_1.confirm)({
74
- message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
79
+ message: `Would you like to provision your Cloud SQL instance and database now?${info.isNewInstance ? " This will take several minutes." : ""}.`,
75
80
  default: true,
76
81
  })));
77
82
  return info;
@@ -125,7 +130,9 @@ function subDataconnectYamlValues(replacementValues) {
125
130
  connectorDirs: "__connectorDirs__",
126
131
  locationId: "__location__",
127
132
  };
128
- let replaced = DATACONNECT_YAML_TEMPLATE;
133
+ let replaced = experiments.isEnabled("fdccompatiblemode")
134
+ ? DATACONNECT_YAML_COMPAT_EXPERIMENT_TEMPLATE
135
+ : DATACONNECT_YAML_TEMPLATE;
129
136
  for (const [k, v] of Object.entries(replacementValues)) {
130
137
  replaced = replaced.replace(replacements[k], JSON.stringify(v));
131
138
  }
@@ -141,67 +148,73 @@ function subConnectorYamlValues(replacementValues) {
141
148
  }
142
149
  return replaced;
143
150
  }
144
- async function promptForService(setup, info) {
145
- var _a, _b, _c, _d;
151
+ async function promptForService(setup, info, isBillingEnabled) {
152
+ var _a, _b, _c;
146
153
  if (setup.projectId) {
147
- await (0, ensureApis_1.ensureApis)(setup.projectId);
148
- const existingServices = await (0, client_1.listAllServices)(setup.projectId);
149
- const existingServicesAndSchemas = await Promise.all(existingServices.map(async (s) => {
150
- return {
151
- service: s,
152
- schema: await (0, client_1.getSchema)(s.name),
153
- };
154
- }));
155
- if (existingServicesAndSchemas.length) {
156
- const choices = existingServicesAndSchemas.map((s) => {
157
- const serviceName = (0, names_1.parseServiceName)(s.service.name);
154
+ if (isBillingEnabled) {
155
+ await (0, ensureApis_1.ensureApis)(setup.projectId);
156
+ const existingServices = await (0, client_1.listAllServices)(setup.projectId);
157
+ const existingServicesAndSchemas = await Promise.all(existingServices.map(async (s) => {
158
158
  return {
159
- name: `${serviceName.location}/${serviceName.serviceId}`,
160
- value: s,
159
+ service: s,
160
+ schema: await (0, client_1.getSchema)(s.name),
161
161
  };
162
- });
163
- choices.push({ name: "Create a new service", value: undefined });
164
- const choice = await (0, prompt_1.promptOnce)({
165
- message: "Your project already has existing services. Which would you like to set up local files for?",
166
- type: "list",
167
- choices,
168
- });
169
- if (choice) {
170
- const serviceName = (0, names_1.parseServiceName)(choice.service.name);
171
- info.serviceId = serviceName.serviceId;
172
- info.locationId = serviceName.location;
173
- if (choice.schema) {
174
- if ((_a = choice.schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) {
175
- const instanceName = (0, names_1.parseCloudSQLInstanceName)((_b = choice.schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance);
176
- info.cloudSqlInstanceId = instanceName.instanceId;
177
- }
178
- if (choice.schema.source.files) {
179
- info.schemaGql = choice.schema.source.files;
180
- }
181
- info.cloudSqlDatabase = (_d = (_c = choice.schema.primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
182
- const connectors = await (0, client_1.listConnectors)(choice.service.name, [
183
- "connectors.name",
184
- "connectors.source.files",
185
- ]);
186
- if (connectors.length) {
187
- info.connectors = connectors.map((c) => {
188
- const id = c.name.split("/").pop();
189
- return {
190
- id,
191
- path: connectors.length === 1 ? "./connector" : `./${id}`,
192
- files: c.source.files || [],
193
- };
194
- });
162
+ }));
163
+ if (existingServicesAndSchemas.length) {
164
+ const choices = existingServicesAndSchemas.map((s) => {
165
+ const serviceName = (0, names_1.parseServiceName)(s.service.name);
166
+ return {
167
+ name: `${serviceName.location}/${serviceName.serviceId}`,
168
+ value: s,
169
+ };
170
+ });
171
+ choices.push({ name: "Create a new service", value: undefined });
172
+ const choice = await (0, prompt_1.promptOnce)({
173
+ message: "Your project already has existing services. Which would you like to set up local files for?",
174
+ type: "list",
175
+ choices,
176
+ });
177
+ if (choice) {
178
+ const serviceName = (0, names_1.parseServiceName)(choice.service.name);
179
+ info.serviceId = serviceName.serviceId;
180
+ info.locationId = serviceName.location;
181
+ if (choice.schema) {
182
+ const primaryDatasource = choice.schema.datasources.find((d) => d.postgresql);
183
+ if ((_a = primaryDatasource === null || primaryDatasource === void 0 ? void 0 : primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) {
184
+ const instanceName = (0, names_1.parseCloudSQLInstanceName)(primaryDatasource.postgresql.cloudSql.instance);
185
+ info.cloudSqlInstanceId = instanceName.instanceId;
186
+ }
187
+ if (choice.schema.source.files) {
188
+ info.schemaGql = choice.schema.source.files;
189
+ }
190
+ info.cloudSqlDatabase = (_c = (_b = primaryDatasource === null || primaryDatasource === void 0 ? void 0 : primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.database) !== null && _c !== void 0 ? _c : "";
191
+ const connectors = await (0, client_1.listConnectors)(choice.service.name, [
192
+ "connectors.name",
193
+ "connectors.source.files",
194
+ ]);
195
+ if (connectors.length) {
196
+ info.connectors = connectors.map((c) => {
197
+ const id = c.name.split("/").pop();
198
+ return {
199
+ id,
200
+ path: connectors.length === 1 ? "./connector" : `./${id}`,
201
+ files: c.source.files || [],
202
+ };
203
+ });
204
+ }
195
205
  }
196
206
  }
197
207
  }
198
208
  }
209
+ else {
210
+ await (0, ensureApis_1.ensureSparkApis)(setup.projectId);
211
+ }
199
212
  }
200
213
  if (info.serviceId === "") {
201
214
  info.serviceId = await (0, prompt_1.promptOnce)({
202
215
  message: "What ID would you like to use for this service?",
203
216
  type: "input",
204
- default: "my-service",
217
+ default: "app",
205
218
  });
206
219
  }
207
220
  return info;
@@ -233,7 +246,7 @@ async function promptForCloudSQLInstance(setup, info) {
233
246
  info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
234
247
  message: `What ID would you like to use for your new CloudSQL instance?`,
235
248
  type: "input",
236
- default: `fdc-sql`,
249
+ default: `${info.serviceId || "app"}-fdc`,
237
250
  });
238
251
  }
239
252
  if (info.locationId === "") {
@@ -9,13 +9,14 @@ const rules = require("./rules");
9
9
  const indexes = require("./indexes");
10
10
  const error_1 = require("../../../error");
11
11
  const clc = require("colorette");
12
+ const prompt_1 = require("../../../prompt");
12
13
  async function checkProjectSetup(setup, config, options) {
13
14
  const firestoreUnusedError = new error_1.FirebaseError(`It looks like you haven't used Cloud Firestore in this project before. Go to ${clc.bold(clc.underline(`https://console.firebase.google.com/project/${setup.projectId}/firestore`))} to create your Cloud Firestore database.`, { exit: 1 });
14
15
  const isFirestoreEnabled = await apiEnabled.check(setup.projectId, "firestore.googleapis.com", "", true);
15
16
  if (!isFirestoreEnabled) {
16
17
  throw firestoreUnusedError;
17
18
  }
18
- const dbType = await (0, checkDatabaseType_1.checkDatabaseType)(setup.projectId);
19
+ const dbType = await getDatabaseType(setup);
19
20
  logger_1.logger.debug(`database_type: ${dbType}`);
20
21
  if (!dbType) {
21
22
  throw firestoreUnusedError;
@@ -25,6 +26,24 @@ async function checkProjectSetup(setup, config, options) {
25
26
  }
26
27
  await (0, requirePermissions_1.requirePermissions)(Object.assign(Object.assign({}, options), { project: setup.projectId }));
27
28
  }
29
+ async function getDatabaseType(setup) {
30
+ const dbType = await (0, checkDatabaseType_1.checkDatabaseType)(setup.projectId, setup.databaseId);
31
+ logger_1.logger.debug(`database_type: ${dbType}`);
32
+ if (dbType === "DATABASE_DOES_NOT_EXIST") {
33
+ setup.databaseId = await selectDatabaseByPrompting();
34
+ return await getDatabaseType(setup);
35
+ }
36
+ else {
37
+ return dbType;
38
+ }
39
+ }
40
+ async function selectDatabaseByPrompting() {
41
+ const database = await (0, prompt_1.promptOnce)({
42
+ type: "input",
43
+ message: "Please input the name of the Native Firestore database you would like to use:",
44
+ });
45
+ return database;
46
+ }
28
47
  async function doSetup(setup, config, options) {
29
48
  if (setup.projectId) {
30
49
  await checkProjectSetup(setup, config, options);
@@ -47,15 +47,15 @@ function initIndexes(setup, config) {
47
47
  if (!setup.projectId) {
48
48
  return config.writeProjectFile(setup.config.firestore.indexes, INDEXES_TEMPLATE);
49
49
  }
50
- return getIndexesFromConsole(setup.projectId).then((contents) => {
50
+ return getIndexesFromConsole(setup.projectId, setup.databaseId).then((contents) => {
51
51
  return config.writeProjectFile(setup.config.firestore.indexes, contents);
52
52
  });
53
53
  });
54
54
  }
55
55
  exports.initIndexes = initIndexes;
56
- function getIndexesFromConsole(projectId) {
57
- const indexesPromise = indexes.listIndexes(projectId);
58
- const fieldOverridesPromise = indexes.listFieldOverrides(projectId);
56
+ function getIndexesFromConsole(projectId, databaseId) {
57
+ const indexesPromise = indexes.listIndexes(projectId, databaseId);
58
+ const fieldOverridesPromise = indexes.listFieldOverrides(projectId, databaseId);
59
59
  return Promise.all([indexesPromise, fieldOverridesPromise])
60
60
  .then((res) => {
61
61
  return indexes.makeIndexSpec(res[0], res[1]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.17.0",
3
+ "version": "13.19.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -43,6 +43,16 @@
43
43
  "description": "Path to the directory where generated files should be written to."
44
44
  }
45
45
  }
46
+ },
47
+ "llmTools": {
48
+ "additionalProperties": true,
49
+ "type": "object",
50
+ "properties": {
51
+ "outputFile": {
52
+ "type": "string",
53
+ "description": "Path where the JSON LLM tool definitions file should be generated."
54
+ }
55
+ }
46
56
  }
47
57
  },
48
58
  "properties": {
@@ -50,35 +60,51 @@
50
60
  "type": "string",
51
61
  "description": "The ID of the Firebase Data Connect connector."
52
62
  },
53
- "authMode": {
54
- "type": "string",
55
- "description": "The authentication strategy to use for this connector"
56
-
57
- },
58
63
  "generate": {
59
64
  "type": "object",
60
65
  "additionalProperties": false,
61
66
  "properties": {
62
67
  "javascriptSdk": {
63
- "type": "array",
64
- "items": {
65
- "$ref": "#/definitions/javascriptSdk"
66
- },
68
+ "oneOf": [
69
+ { "$ref": "#/definitions/javascriptSdk" },
70
+ {
71
+ "type": "array",
72
+ "items": {
73
+ "$ref": "#/definitions/javascriptSdk"
74
+ }
75
+ }
76
+ ],
67
77
  "description": "Configuration for a generated Javascript SDK"
68
78
  },
69
79
  "kotlinSdk": {
70
- "type": "array",
71
- "items": {
72
- "$ref": "#/definitions/kotlinSdk"
73
- },
80
+ "oneOf": [
81
+ { "$ref": "#/definitions/kotlinSdk" },
82
+ {
83
+ "type": "array",
84
+ "items": {
85
+ "$ref": "#/definitions/kotlinSdk"
86
+ }
87
+ }
88
+ ],
74
89
  "description": "Configuration for a generated Kotlin SDK"
75
90
  },
76
91
  "swiftSdk": {
77
- "type": "array",
78
- "items": {
79
- "$ref": "#/definitions/swiftSdk"
80
- },
92
+ "oneOf": [
93
+ { "$ref": "#/definitions/swiftSdk" },
94
+ {
95
+ "type": "array",
96
+ "items": {
97
+ "$ref": "#/definitions/swiftSdk"
98
+ }
99
+ }
100
+ ],
81
101
  "description": "Configuration for a generated Swift SDK"
102
+ },
103
+ "llmTools": {
104
+ "oneOf": [
105
+ { "$ref": "#/definitions/llmTools" },
106
+ { "type": "array", "items": { "$ref": "#/definitions/llmTools" } }
107
+ ]
82
108
  }
83
109
  }
84
110
  }
@@ -1,5 +1,4 @@
1
1
  connectorId: __connectorId__
2
- authMode: "PUBLIC"
3
2
  ## ## Here's an example of how to add generated SDKs.
4
3
  ## ## You'll need to replace the outputDirs with ones pointing to where you want the generated code in your app.
5
4
  # generate:
@@ -0,0 +1,12 @@
1
+ specVersion: "v1beta"
2
+ serviceId: __serviceId__
3
+ location: __location__
4
+ schema:
5
+ source: "./schema"
6
+ datasource:
7
+ postgresql:
8
+ database: __cloudSqlDatabase__
9
+ cloudSql:
10
+ instanceId: __cloudSqlInstanceId__
11
+ # schemaValidation: "COMPATIBLE"
12
+ connectorDirs: __connectorDirs__
@@ -1,4 +1,4 @@
1
- specVersion: "v1alpha"
1
+ specVersion: "v1beta"
2
2
  serviceId: __serviceId__
3
3
  location: __location__
4
4
  schema: