firebase-tools 13.15.0 → 13.15.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.
Files changed (36) hide show
  1. package/lib/commands/deploy.js +3 -1
  2. package/lib/dataconnect/fileUtils.js +52 -11
  3. package/lib/dataconnect/provisionCloudSql.js +5 -1
  4. package/lib/deploy/extensions/planner.js +46 -1
  5. package/lib/deploy/extensions/prepare.js +99 -23
  6. package/lib/deploy/functions/build.js +5 -5
  7. package/lib/deploy/functions/deploy.js +12 -12
  8. package/lib/deploy/functions/params.js +5 -3
  9. package/lib/deploy/functions/prepare.js +16 -1
  10. package/lib/deploy/functions/release/index.js +4 -0
  11. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  12. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +46 -0
  13. package/lib/emulator/downloadableEmulators.js +9 -9
  14. package/lib/emulator/functionsEmulator.js +8 -1
  15. package/lib/extensions/askUserForEventsConfig.js +18 -8
  16. package/lib/extensions/askUserForParam.js +2 -1
  17. package/lib/extensions/displayExtensionInfo.js +5 -5
  18. package/lib/extensions/extensionsApi.js +1 -1
  19. package/lib/extensions/extensionsHelper.js +42 -3
  20. package/lib/extensions/localHelper.js +1 -1
  21. package/lib/extensions/refs.js +26 -11
  22. package/lib/extensions/runtimes/common.js +75 -0
  23. package/lib/extensions/types.js +56 -1
  24. package/lib/frameworks/constants.js +1 -1
  25. package/lib/frameworks/next/constants.js +1 -1
  26. package/lib/frameworks/next/index.js +26 -14
  27. package/lib/frameworks/next/utils.js +45 -1
  28. package/lib/init/features/dataconnect/index.js +10 -7
  29. package/lib/init/features/dataconnect/sdk.js +67 -51
  30. package/lib/prompt.js +22 -1
  31. package/package.json +1 -1
  32. package/templates/init/dataconnect/connector.yaml +5 -3
  33. package/templates/init/dataconnect/dataconnect.yaml +5 -5
  34. package/templates/init/dataconnect/mutations.gql +44 -29
  35. package/templates/init/dataconnect/queries.gql +66 -38
  36. package/templates/init/dataconnect/schema.gql +38 -21
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.askForEventArcLocation = exports.askShouldCollectEventsConfig = exports.askForAllowedEventTypes = exports.askForEventsConfig = exports.checkAllowedEventTypesResponse = void 0;
3
+ exports.askForEventArcLocation = exports.EXTENSIONS_DEFAULT_EVENT_ARC_REGION = exports.ALLOWED_EVENT_ARC_REGIONS = exports.askShouldCollectEventsConfig = exports.askForAllowedEventTypes = exports.getEventArcChannel = exports.askForEventsConfig = exports.checkAllowedEventTypesResponse = void 0;
4
4
  const prompt_1 = require("../prompt");
5
5
  const extensionsApi = require("../extensions/extensionsApi");
6
6
  const utils = require("../utils");
@@ -23,7 +23,7 @@ function checkAllowedEventTypesResponse(response, validEvents) {
23
23
  exports.checkAllowedEventTypesResponse = checkAllowedEventTypesResponse;
24
24
  async function askForEventsConfig(events, projectId, instanceId) {
25
25
  var _a, _b;
26
- logger_1.logger.info(`\n${clc.bold("Enable Events")}: ${(0, marked_1.marked)("If you enable events, you can write custom event handlers ([https://firebase.google.com/docs/extensions/install-extensions#eventarc](https://firebase.google.com/docs/extensions/install-extensions#eventarc)) that respond to these events.\n\nYou can always enable or disable events later. Events will be emitted via Eventarc. Fees apply ([https://cloud.google.com/eventarc/pricing](https://cloud.google.com/eventarc/pricing)).")}`);
26
+ logger_1.logger.info(`\n${clc.bold("Enable Events")}: ${await (0, marked_1.marked)("If you enable events, you can write custom event handlers ([https://firebase.google.com/docs/extensions/install-extensions#eventarc](https://firebase.google.com/docs/extensions/install-extensions#eventarc)) that respond to these events.\n\nYou can always enable or disable events later. Events will be emitted via Eventarc. Fees apply ([https://cloud.google.com/eventarc/pricing](https://cloud.google.com/eventarc/pricing)).")}`);
27
27
  if (!(await askShouldCollectEventsConfig())) {
28
28
  return undefined;
29
29
  }
@@ -38,11 +38,15 @@ async function askForEventsConfig(events, projectId, instanceId) {
38
38
  const preselectedTypes = (_a = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.allowedEventTypes) !== null && _a !== void 0 ? _a : [];
39
39
  const oldLocation = (_b = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.eventarcChannel) === null || _b === void 0 ? void 0 : _b.split("/")[3];
40
40
  const location = await askForEventArcLocation(oldLocation);
41
- const channel = `projects/${projectId}/locations/${location}/channels/firebase`;
41
+ const channel = getEventArcChannel(projectId, location);
42
42
  const allowedEventTypes = await askForAllowedEventTypes(events, preselectedTypes);
43
43
  return { channel, allowedEventTypes };
44
44
  }
45
45
  exports.askForEventsConfig = askForEventsConfig;
46
+ function getEventArcChannel(projectId, location) {
47
+ return `projects/${projectId}/locations/${location}/channels/firebase`;
48
+ }
49
+ exports.getEventArcChannel = getEventArcChannel;
46
50
  async function askForAllowedEventTypes(eventDescriptors, preselectedTypes) {
47
51
  let valid = false;
48
52
  let response = [];
@@ -75,21 +79,27 @@ async function askShouldCollectEventsConfig() {
75
79
  });
76
80
  }
77
81
  exports.askShouldCollectEventsConfig = askShouldCollectEventsConfig;
82
+ exports.ALLOWED_EVENT_ARC_REGIONS = [
83
+ "us-central1",
84
+ "us-west1",
85
+ "europe-west4",
86
+ "asia-northeast1",
87
+ ];
88
+ exports.EXTENSIONS_DEFAULT_EVENT_ARC_REGION = "us-central1";
78
89
  async function askForEventArcLocation(preselectedLocation) {
79
90
  let valid = false;
80
- const allowedRegions = ["us-central1", "us-west1", "europe-west4", "asia-northeast1"];
81
91
  let location = "";
82
92
  while (!valid) {
83
93
  location = await (0, prompt_1.promptOnce)({
84
94
  name: "input",
85
95
  type: "list",
86
- default: preselectedLocation !== null && preselectedLocation !== void 0 ? preselectedLocation : "us-central1",
96
+ default: preselectedLocation !== null && preselectedLocation !== void 0 ? preselectedLocation : exports.EXTENSIONS_DEFAULT_EVENT_ARC_REGION,
87
97
  message: "Which location would you like the Eventarc channel to live in? We recommend using the default option. A channel location that differs from the extension's Cloud Functions location can incur egress cost.",
88
- choices: allowedRegions.map((e) => ({ checked: false, value: e })),
98
+ choices: exports.ALLOWED_EVENT_ARC_REGIONS.map((e) => ({ checked: false, value: e })),
89
99
  });
90
- valid = allowedRegions.includes(location);
100
+ valid = exports.ALLOWED_EVENT_ARC_REGIONS.includes(location);
91
101
  if (!valid) {
92
- utils.logWarning(`Unexpected EventArc region '${location}' was specified. Allowed regions: ${allowedRegions.join(", ")}`);
102
+ utils.logWarning(`Unexpected EventArc region '${location}' was specified. Allowed regions: ${exports.ALLOWED_EVENT_ARC_REGIONS.join(", ")}`);
93
103
  }
94
104
  }
95
105
  return location;
@@ -255,7 +255,7 @@ async function promptReconfigureSecret(projectId, instanceId, paramSpec) {
255
255
  ],
256
256
  });
257
257
  switch (action) {
258
- case SecretUpdateAction.SET_NEW:
258
+ case SecretUpdateAction.SET_NEW: {
259
259
  let secret;
260
260
  let secretName;
261
261
  if (paramSpec.default) {
@@ -288,6 +288,7 @@ async function promptReconfigureSecret(projectId, instanceId, paramSpec) {
288
288
  else {
289
289
  return "";
290
290
  }
291
+ }
291
292
  case SecretUpdateAction.LEAVE:
292
293
  default:
293
294
  return paramSpec.default || "";
@@ -50,7 +50,7 @@ async function displayExtensionVersionInfo(args) {
50
50
  if (extensionVersion.buildSourceUri) {
51
51
  const buildSourceUri = new URL(extensionVersion.buildSourceUri);
52
52
  buildSourceUri.pathname = path.join(buildSourceUri.pathname, (_c = extensionVersion.extensionRoot) !== null && _c !== void 0 ? _c : "");
53
- lines.push(`${clc.bold("Source in GitHub:")} ${buildSourceUri}`);
53
+ lines.push(`${clc.bold("Source in GitHub:")} ${buildSourceUri.toString()}`);
54
54
  }
55
55
  else {
56
56
  lines.push(`${clc.bold("Source download URI:")} ${(_d = extensionVersion.sourceDownloadUri) !== null && _d !== void 0 ? _d : "-"}`);
@@ -105,24 +105,24 @@ function displayResources(spec) {
105
105
  break;
106
106
  default:
107
107
  }
108
- return ` - ${clc.blue(`${resource.name} (${type})`)}${resource.description ? `: ${resource.description}` : ""}`;
108
+ return ` - ${clc.blueBright(`${resource.name} (${type})`)}${resource.description ? `: ${resource.description}` : ""}`;
109
109
  });
110
110
  lines.push(...new Set((_a = spec.lifecycleEvents) === null || _a === void 0 ? void 0 : _a.map((event) => {
111
- return ` - ${clc.blue(`${event.taskQueueTriggerFunction} (Cloud Task queue)`)}`;
111
+ return ` - ${clc.blueBright(`${event.taskQueueTriggerFunction} (Cloud Task queue)`)}`;
112
112
  })));
113
113
  lines.push(...spec.params
114
114
  .filter((param) => {
115
115
  return param.type === "SECRET";
116
116
  })
117
117
  .map((param) => {
118
- return ` - ${clc.blue(`${param.param} (Cloud Secret Manager secret)`)}`;
118
+ return ` - ${clc.blueBright(`${param.param} (Cloud Secret Manager secret)`)}`;
119
119
  }));
120
120
  return clc.bold("Resources created:\n") + (lines.length ? lines.join("\n") : " - None");
121
121
  }
122
122
  exports.displayResources = displayResources;
123
123
  async function retrieveRoleInfo(role) {
124
124
  const res = await iam.getRole(role);
125
- return ` - ${clc.yellow(res.title)}${res.description ? `: ${res.description}` : ""}`;
125
+ return ` - ${clc.yellow(res.title || res.name)}${res.description ? `: ${res.description}` : ""}`;
126
126
  }
127
127
  exports.retrieveRoleInfo = retrieveRoleInfo;
128
128
  async function displayRoles(roles) {
@@ -356,6 +356,6 @@ function refNotFoundError(ref) {
356
356
  return new error_1.FirebaseError(`The extension reference '${clc.bold(ref.version ? refs.toExtensionVersionRef(ref) : refs.toExtensionRef(ref))}' doesn't exist. This could happen for two reasons:\n` +
357
357
  ` -The publisher ID '${clc.bold(ref.publisherId)}' doesn't exist or could be misspelled\n` +
358
358
  ` -The name of the ${ref.version ? "extension version" : "extension"} '${clc.bold(ref.version ? `${ref.extensionId}@${ref.version}` : ref.extensionId)}' doesn't exist or could be misspelled\n\n` +
359
- `Please correct the extension reference and try again. If you meant to install an extension from a local source, please provide a relative path prefixed with '${clc.bold("./")}', '${clc.bold("../")}', or '${clc.bold("~/")}'.}`, { status: 404 });
359
+ `Please correct the extension reference and try again. If you meant to reference an extension from a local source, please provide a relative path prefixed with '${clc.bold("./")}', '${clc.bold("../")}', or '${clc.bold("~/")}'.}`, { status: 404 });
360
360
  }
361
361
  exports.refNotFoundError = refNotFoundError;
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
3
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
4
+ var m = o[Symbol.asyncIterator], i;
5
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
6
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
7
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
8
+ };
2
9
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
10
+ exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteSecretParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
4
11
  const clc = require("colorette");
5
12
  const ora = require("ora");
6
13
  const semver = require("semver");
@@ -126,6 +133,38 @@ function substituteParams(original, params) {
126
133
  return JSON.parse(s);
127
134
  }
128
135
  exports.substituteParams = substituteParams;
136
+ async function substituteSecretParams(projectNumber, params) {
137
+ var _a, e_1, _b, _c;
138
+ const newParams = {};
139
+ try {
140
+ for (var _d = true, _e = __asyncValues(Object.entries(params)), _f; _f = await _e.next(), _a = _f.done, !_a;) {
141
+ _c = _f.value;
142
+ _d = false;
143
+ try {
144
+ const [key, value] = _c;
145
+ if (typeof value !== "string") {
146
+ newParams[key] =
147
+ `projects/${projectNumber}/secrets/${value.name}/versions/latest`;
148
+ }
149
+ else {
150
+ newParams[key] = value;
151
+ }
152
+ }
153
+ finally {
154
+ _d = true;
155
+ }
156
+ }
157
+ }
158
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
159
+ finally {
160
+ try {
161
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
162
+ }
163
+ finally { if (e_1) throw e_1.error; }
164
+ }
165
+ return newParams;
166
+ }
167
+ exports.substituteSecretParams = substituteSecretParams;
129
168
  function populateDefaultParams(paramVars, paramSpecs) {
130
169
  const newParams = paramVars;
131
170
  for (const param of paramSpecs) {
@@ -472,7 +511,7 @@ function displayExtensionHeader(extensionRef, extension, extensionRoot) {
472
511
  }
473
512
  else {
474
513
  logger_1.logger.info(`\n${clc.bold("Extension:")} ${extensionRef}\n` +
475
- `${clc.bold("State:")} ${clc.bold(clc.blue("New"))}\n`);
514
+ `${clc.bold("State:")} ${clc.bold(clc.blueBright("New"))}\n`);
476
515
  }
477
516
  }
478
517
  async function fetchExtensionSource(repoUri, sourceRef, extensionRoot) {
@@ -794,7 +833,7 @@ async function instanceIdExists(projectId, instanceId) {
794
833
  if (err.status === 404) {
795
834
  return false;
796
835
  }
797
- const msg = `Unexpected error when checking if instance ID exists: ${err}`;
836
+ const msg = `Unexpected error when checking if instance ID exists: ${err.message}`;
798
837
  throw new error_1.FirebaseError(msg, {
799
838
  original: err,
800
839
  });
@@ -38,7 +38,7 @@ function readFile(pathToFile) {
38
38
  }
39
39
  catch (err) {
40
40
  if (err.code === "ENOENT") {
41
- throw new error_1.FirebaseError(`Could not find "${pathToFile}""`, { original: err });
41
+ throw new error_1.FirebaseError(`Could not find "${pathToFile}"`, { original: err });
42
42
  }
43
43
  throw new error_1.FirebaseError(`Failed to read file at "${pathToFile}"`, { original: err });
44
44
  }
@@ -7,13 +7,16 @@ const refRegex = new RegExp(/^([^/@\n]+)\/{1}([^/@\n]+)(@{1}([^\n]+)|)$/);
7
7
  function parse(refOrName) {
8
8
  const ret = parseRef(refOrName) || parseName(refOrName);
9
9
  if (!ret || !ret.publisherId || !ret.extensionId) {
10
- throw new error_1.FirebaseError(`Unable to parse ${refOrName} as an extension ref`);
10
+ throw new error_1.FirebaseError(`Unable to parse ${refOrName} as an extension ref.\n` +
11
+ "Expected format is either publisherId/extensionId@version or " +
12
+ "publishers/publisherId/extensions/extensionId/versions/version. If you " +
13
+ "are referring to a local extension directory, please ensure the directory exists.");
11
14
  }
12
15
  if (ret.version &&
13
16
  !semver.valid(ret.version) &&
14
17
  !semver.validRange(ret.version) &&
15
18
  !["latest", "latest-approved"].includes(ret.version)) {
16
- throw new error_1.FirebaseError(`Extension reference ${ret} contains an invalid version ${ret.version}.`);
19
+ throw new error_1.FirebaseError(`Extension reference ${JSON.stringify(ret, null, 2)} contains an invalid version ${ret.version}.`);
17
20
  }
18
21
  return ret;
19
22
  }
@@ -21,19 +24,31 @@ exports.parse = parse;
21
24
  function parseRef(ref) {
22
25
  const parts = refRegex.exec(ref);
23
26
  if (parts && (parts.length === 5 || parts.length === 7)) {
24
- const publisherId = parts[1];
25
- const extensionId = parts[2];
26
- const version = parts[4];
27
- return { publisherId, extensionId, version };
27
+ return {
28
+ publisherId: parts[1],
29
+ extensionId: parts[2],
30
+ version: parts[4],
31
+ };
28
32
  }
29
33
  }
30
34
  function parseName(name) {
31
35
  const parts = name.split("/");
32
- return {
33
- publisherId: parts[1],
34
- extensionId: parts[3],
35
- version: parts[5],
36
- };
36
+ if (parts[0] !== "publishers" || parts[2] !== "extensions") {
37
+ return;
38
+ }
39
+ if (parts.length === 4) {
40
+ return {
41
+ publisherId: parts[1],
42
+ extensionId: parts[3],
43
+ };
44
+ }
45
+ if (parts.length === 6 && parts[4] === "versions") {
46
+ return {
47
+ publisherId: parts[1],
48
+ extensionId: parts[3],
49
+ version: parts[5],
50
+ };
51
+ }
37
52
  }
38
53
  function toExtensionRef(ref) {
39
54
  return `${ref.publisherId}/${ref.extensionId}`;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractExtensionsFromBuilds = exports.extractAllDynamicExtensions = void 0;
4
+ const projectConfig_1 = require("../../functions/projectConfig");
5
+ const prepare_1 = require("../../deploy/functions/prepare");
6
+ const functionsConfig_1 = require("../../functionsConfig");
7
+ const functionsDeployHelper_1 = require("../../deploy/functions/functionsDeployHelper");
8
+ const logger_1 = require("../../logger");
9
+ const savedLoggerSilent = logger_1.logger.silent;
10
+ function silenceLogging() {
11
+ logger_1.logger.silent = true;
12
+ }
13
+ function resumeLogging() {
14
+ logger_1.logger.silent = savedLoggerSilent;
15
+ }
16
+ async function extractAllDynamicExtensions(options) {
17
+ const firebaseConfig = await (0, functionsConfig_1.getFirebaseConfig)(options);
18
+ const runtimeConfig = { firebase: firebaseConfig };
19
+ const functionsConfig = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
20
+ let functionsBuilds = {};
21
+ const codebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig);
22
+ silenceLogging();
23
+ for (const codebase of codebases) {
24
+ try {
25
+ const filters = [{ codebase: `${codebase}` }];
26
+ const builds = await (0, prepare_1.loadCodebases)(functionsConfig, options, firebaseConfig, runtimeConfig, filters);
27
+ functionsBuilds = Object.assign(Object.assign({}, functionsBuilds), builds);
28
+ }
29
+ catch (err) {
30
+ }
31
+ }
32
+ resumeLogging();
33
+ return extractExtensionsFromBuilds(functionsBuilds);
34
+ }
35
+ exports.extractAllDynamicExtensions = extractAllDynamicExtensions;
36
+ function extractExtensionsFromBuilds(builds, filters) {
37
+ const extRecords = {};
38
+ for (const [codebase, build] of Object.entries(builds)) {
39
+ if (build.extensions) {
40
+ for (const [id, ext] of Object.entries(build.extensions)) {
41
+ if (extensionMatchesAnyFilter(codebase, id, filters)) {
42
+ extRecords[id] = ext;
43
+ }
44
+ }
45
+ }
46
+ }
47
+ return extRecords;
48
+ }
49
+ exports.extractExtensionsFromBuilds = extractExtensionsFromBuilds;
50
+ function extensionMatchesAnyFilter(codebase, extensionId, filters) {
51
+ if (!filters) {
52
+ return true;
53
+ }
54
+ return filters.some((f) => extensionMatchesFilter(codebase, extensionId, f));
55
+ }
56
+ function extensionMatchesFilter(codebase, extensionId, filter) {
57
+ if (codebase && filter.codebase) {
58
+ if (codebase !== filter.codebase) {
59
+ return false;
60
+ }
61
+ }
62
+ if (!filter.idChunks) {
63
+ return true;
64
+ }
65
+ const idChunks = extensionId.split("-");
66
+ if (idChunks.length < filter.idChunks.length) {
67
+ return false;
68
+ }
69
+ for (let i = 0; i < filter.idChunks.length; i++) {
70
+ if (idChunks[i] !== filter.idChunks[i]) {
71
+ return false;
72
+ }
73
+ }
74
+ return true;
75
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.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";
@@ -21,5 +21,60 @@ var ParamType;
21
21
  ParamType["STRING"] = "STRING";
22
22
  ParamType["SELECT"] = "SELECT";
23
23
  ParamType["MULTISELECT"] = "MULTISELECT";
24
+ ParamType["SELECT_RESOURCE"] = "SELECT_RESOURCE";
24
25
  ParamType["SECRET"] = "SECRET";
25
26
  })(ParamType = exports.ParamType || (exports.ParamType = {}));
27
+ function isObject(value) {
28
+ return typeof value === "object" && value !== null;
29
+ }
30
+ const isParam = (param) => {
31
+ return (isObject(param) && typeof param["param"] === "string" && typeof param["label"] === "string");
32
+ };
33
+ exports.isParam = isParam;
34
+ const isResource = (res) => {
35
+ return isObject(res) && typeof res["name"] === "string";
36
+ };
37
+ exports.isResource = isResource;
38
+ const isExtensionSpec = (spec) => {
39
+ if (!isObject(spec) || typeof spec.name !== "string" || typeof spec.version !== "string") {
40
+ return false;
41
+ }
42
+ let validResources = true;
43
+ if (spec.resources && Array.isArray(spec.resources)) {
44
+ for (const res of spec.resources) {
45
+ validResources = validResources && (0, exports.isResource)(res);
46
+ if (!validResources) {
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ else {
52
+ return false;
53
+ }
54
+ let validParams = true;
55
+ if (spec.params && Array.isArray(spec.params)) {
56
+ for (const param of spec.params) {
57
+ validParams = validParams && (0, exports.isParam)(param);
58
+ if (!validParams) {
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ else {
64
+ return false;
65
+ }
66
+ let validSysParams = true;
67
+ if (spec.systemParams && Array.isArray(spec.systemParams)) {
68
+ for (const param of spec.systemParams) {
69
+ validSysParams = validSysParams && (0, exports.isParam)(param);
70
+ if (!validSysParams) {
71
+ break;
72
+ }
73
+ }
74
+ }
75
+ else {
76
+ return false;
77
+ }
78
+ return true;
79
+ };
80
+ exports.isExtensionSpec = isExtensionSpec;
@@ -20,7 +20,7 @@ exports.FIREBASE_FRAMEWORKS_VERSION = (experiments.isEnabled("internaltesting")
20
20
  DEFAULT_FIREBASE_FRAMEWORKS_VERSION;
21
21
  exports.FIREBASE_FUNCTIONS_VERSION = "^4.5.0";
22
22
  exports.FIREBASE_ADMIN_VERSION = "^11.11.1";
23
- exports.SHARP_VERSION = "^0.32.1";
23
+ exports.SHARP_VERSION = "^0.32 || ^0.33";
24
24
  exports.NODE_VERSION = parseInt(process.versions.node, 10);
25
25
  exports.VALID_ENGINES = { node: [16, 18, 20] };
26
26
  exports.VALID_LOCALE_FORMATS = [/^ALL_[a-z]+$/, /^[a-z]+_ALL$/, /^[a-z]+(_[a-z]+)?$/];
@@ -11,7 +11,7 @@ exports.ROUTES_MANIFEST = "routes-manifest.json";
11
11
  exports.APP_PATHS_MANIFEST = "app-paths-manifest.json";
12
12
  exports.SERVER_REFERENCE_MANIFEST = "server-reference-manifest.json";
13
13
  exports.CONFIG_FILES = ["next.config.js", "next.config.mjs"];
14
- exports.ESBUILD_VERSION = "0.19.2";
14
+ exports.ESBUILD_VERSION = "^0.19.2";
15
15
  const WEBPACK_LAYERS_NAMES = {
16
16
  shared: "shared",
17
17
  reactServerComponents: "rsc",
@@ -29,7 +29,6 @@ exports.name = "Next.js";
29
29
  exports.support = "preview";
30
30
  exports.type = 2;
31
31
  exports.docsUrl = "https://firebase.google.com/docs/hosting/frameworks/nextjs";
32
- const BUNDLE_NEXT_CONFIG_TIMEOUT = 60000;
33
32
  const DEFAULT_NUMBER_OF_REASONS_TO_LIST = 5;
34
33
  function getReactVersion(cwd) {
35
34
  var _a;
@@ -338,6 +337,19 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
338
337
  const configFile = await (0, utils_2.whichNextConfigFile)(sourceDir);
339
338
  if (configFile) {
340
339
  try {
340
+ let esbuildPath = (0, utils_2.findEsbuildPath)();
341
+ if (!esbuildPath || !(0, fs_extra_1.pathExistsSync)(esbuildPath)) {
342
+ console.warn("esbuild not found, installing...");
343
+ (0, utils_2.installEsbuild)(constants_2.ESBUILD_VERSION);
344
+ esbuildPath = (0, utils_2.findEsbuildPath)();
345
+ if (!esbuildPath || !(0, fs_extra_1.pathExistsSync)(esbuildPath)) {
346
+ throw new error_1.FirebaseError("Failed to locate esbuild after installation.");
347
+ }
348
+ }
349
+ const esbuild = require(esbuildPath);
350
+ if (!esbuild) {
351
+ throw new error_1.FirebaseError(`Failed to load esbuild from path: ${esbuildPath}`);
352
+ }
341
353
  const productionDeps = await new Promise((resolve) => {
342
354
  const dependencies = [];
343
355
  const npmLs = (0, cross_spawn_1.spawn)("npm", ["ls", "--omit=dev", "--all", "--json=true"], {
@@ -359,21 +371,21 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
359
371
  resolve([...new Set(dependencies)]);
360
372
  });
361
373
  });
362
- const esbuildArgs = productionDeps
363
- .map((it) => `--external:${it}`)
364
- .concat("--bundle", "--platform=node", `--target=node${constants_1.NODE_VERSION}`, "--log-level=error");
374
+ const esbuildArgs = {
375
+ entryPoints: [(0, path_1.join)(sourceDir, configFile)],
376
+ outfile: (0, path_1.join)(destDir, configFile),
377
+ bundle: true,
378
+ platform: "node",
379
+ target: `node${constants_1.NODE_VERSION}`,
380
+ logLevel: "error",
381
+ external: productionDeps,
382
+ };
365
383
  if (configFile === "next.config.mjs") {
366
- esbuildArgs.push(...[`--outfile=${(0, path_1.join)(destDir, configFile)}`, "--format=esm"]);
384
+ esbuildArgs.format = "esm";
367
385
  }
368
- else {
369
- esbuildArgs.push(`--outfile=${(0, path_1.join)(destDir, configFile)}`);
370
- }
371
- const bundle = (0, cross_spawn_1.sync)("npx", ["--yes", `esbuild@${constants_2.ESBUILD_VERSION}`, configFile, ...esbuildArgs], {
372
- cwd: sourceDir,
373
- timeout: BUNDLE_NEXT_CONFIG_TIMEOUT,
374
- });
375
- if (bundle.status !== 0) {
376
- throw new error_1.FirebaseError(bundle.stderr.toString());
386
+ const bundle = await esbuild.build(esbuildArgs);
387
+ if (bundle.errors && bundle.errors.length > 0) {
388
+ throw new error_1.FirebaseError(bundle.errors.toString());
377
389
  }
378
390
  }
379
391
  catch (e) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
3
+ exports.installEsbuild = exports.getGlobalEsbuildVersion = exports.findEsbuildPath = exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const fs_extra_1 = require("fs-extra");
6
6
  const path_1 = require("path");
@@ -11,6 +11,8 @@ const utils_1 = require("../utils");
11
11
  const constants_1 = require("./constants");
12
12
  const fsutils_1 = require("../../fsutils");
13
13
  const utils_2 = require("../../utils");
14
+ const child_process_1 = require("child_process");
15
+ const error_1 = require("../../error");
14
16
  exports.I18N_SOURCE = /\/:nextInternalLocale(\([^\)]+\))?/;
15
17
  function cleanEscapedChars(path) {
16
18
  return path.replace(/\\([(){}:+?*])/g, (a, b) => b);
@@ -241,3 +243,45 @@ async function whichNextConfigFile(dir) {
241
243
  return null;
242
244
  }
243
245
  exports.whichNextConfigFile = whichNextConfigFile;
246
+ function findEsbuildPath() {
247
+ try {
248
+ const esbuildBinPath = (0, child_process_1.execSync)("npx which esbuild", { encoding: "utf8" }).trim();
249
+ const globalVersion = getGlobalEsbuildVersion();
250
+ if (globalVersion && !(0, semver_1.satisfies)(globalVersion, constants_1.ESBUILD_VERSION)) {
251
+ console.warn(`Warning: Global esbuild version (${globalVersion}) does not match the required version (${constants_1.ESBUILD_VERSION}).`);
252
+ }
253
+ return (0, path_1.resolve)((0, path_1.dirname)(esbuildBinPath), "../esbuild");
254
+ }
255
+ catch (error) {
256
+ console.error(`Failed to find esbuild with npx which: ${error}`);
257
+ return null;
258
+ }
259
+ }
260
+ exports.findEsbuildPath = findEsbuildPath;
261
+ function getGlobalEsbuildVersion() {
262
+ try {
263
+ const versionOutput = (0, child_process_1.execSync)("esbuild --version", { encoding: "utf8" }).trim();
264
+ const versionMatch = versionOutput.match(/(\d+\.\d+\.\d+)/);
265
+ return versionMatch ? versionMatch[0] : null;
266
+ }
267
+ catch (error) {
268
+ console.error(`Failed to get global esbuild version: ${error}`);
269
+ return null;
270
+ }
271
+ }
272
+ exports.getGlobalEsbuildVersion = getGlobalEsbuildVersion;
273
+ function installEsbuild(version) {
274
+ const installCommand = `npm install esbuild@${version} --no-save`;
275
+ try {
276
+ (0, child_process_1.execSync)(installCommand, { stdio: "inherit" });
277
+ }
278
+ catch (error) {
279
+ if (error instanceof error_1.FirebaseError) {
280
+ throw error;
281
+ }
282
+ else {
283
+ throw new error_1.FirebaseError(`Failed to install esbuild: ${error}`, { original: error });
284
+ }
285
+ }
286
+ }
287
+ exports.installEsbuild = installEsbuild;
@@ -21,6 +21,7 @@ const QUERIES_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/que
21
21
  const MUTATIONS_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/mutations.gql");
22
22
  const defaultConnector = {
23
23
  id: "default",
24
+ path: "./connector",
24
25
  files: [
25
26
  {
26
27
  path: "queries.gql",
@@ -92,7 +93,7 @@ exports.actuate = actuate;
92
93
  async function writeFiles(config, info) {
93
94
  const dir = config.get("dataconnect.source") || "dataconnect";
94
95
  console.log(dir);
95
- const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorIds: info.connectors.map((c) => `"./${c.id}"`).join(", ") }));
96
+ const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorDirs: info.connectors.map((c) => c.path) }));
96
97
  config.set("dataconnect", { source: dir });
97
98
  await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
98
99
  if (info.schemaGql.length) {
@@ -111,9 +112,9 @@ async function writeFiles(config, info) {
111
112
  async function writeConnectorFiles(config, connectorInfo) {
112
113
  const subbedConnectorYaml = subConnectorYamlValues({ connectorId: connectorInfo.id });
113
114
  const dir = config.get("dataconnect.source") || "dataconnect";
114
- await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.id, "connector.yaml"), subbedConnectorYaml);
115
+ await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, "connector.yaml"), subbedConnectorYaml);
115
116
  for (const f of connectorInfo.files) {
116
- await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.id, f.path), f.content);
117
+ await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, f.path), f.content);
117
118
  }
118
119
  }
119
120
  function subDataconnectYamlValues(replacementValues) {
@@ -121,12 +122,12 @@ function subDataconnectYamlValues(replacementValues) {
121
122
  serviceId: "__serviceId__",
122
123
  cloudSqlDatabase: "__cloudSqlDatabase__",
123
124
  cloudSqlInstanceId: "__cloudSqlInstanceId__",
124
- connectorIds: "__connectorIds__",
125
+ connectorDirs: "__connectorDirs__",
125
126
  locationId: "__location__",
126
127
  };
127
128
  let replaced = DATACONNECT_YAML_TEMPLATE;
128
129
  for (const [k, v] of Object.entries(replacementValues)) {
129
- replaced = replaced.replace(replacements[k], v);
130
+ replaced = replaced.replace(replacements[k], JSON.stringify(v));
130
131
  }
131
132
  return replaced;
132
133
  }
@@ -136,7 +137,7 @@ function subConnectorYamlValues(replacementValues) {
136
137
  };
137
138
  let replaced = CONNECTOR_YAML_TEMPLATE;
138
139
  for (const [k, v] of Object.entries(replacementValues)) {
139
- replaced = replaced.replace(replacements[k], v);
140
+ replaced = replaced.replace(replacements[k], JSON.stringify(v));
140
141
  }
141
142
  return replaced;
142
143
  }
@@ -181,8 +182,10 @@ async function promptForService(setup, info) {
181
182
  const connectors = await (0, client_1.listConnectors)(choice.service.name);
182
183
  if (connectors.length) {
183
184
  info.connectors = connectors.map((c) => {
185
+ const id = c.name.split("/").pop();
184
186
  return {
185
- id: c.name.split("/").pop(),
187
+ id,
188
+ path: `./${id}`,
186
189
  files: c.source.files || [],
187
190
  };
188
191
  });