firebase-tools 13.6.0 → 13.7.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 (61) hide show
  1. package/lib/api.js +1 -1
  2. package/lib/apphosting/config.js +31 -0
  3. package/lib/apphosting/githubConnections.js +261 -0
  4. package/lib/{init/features/apphosting → apphosting}/index.js +21 -17
  5. package/lib/{init/features/apphosting → apphosting}/repo.js +9 -9
  6. package/lib/apphosting/secrets/dialogs.js +169 -0
  7. package/lib/apphosting/secrets/index.js +98 -0
  8. package/lib/commands/apphosting-backends-create.js +4 -2
  9. package/lib/commands/apphosting-backends-delete.js +1 -1
  10. package/lib/commands/apphosting-secrets-access.js +24 -0
  11. package/lib/commands/apphosting-secrets-describe.js +29 -0
  12. package/lib/commands/apphosting-secrets-grantaccess.js +45 -0
  13. package/lib/commands/apphosting-secrets-set.js +105 -0
  14. package/lib/commands/functions-secrets-access.js +2 -2
  15. package/lib/commands/functions-secrets-describe.js +14 -0
  16. package/lib/commands/functions-secrets-destroy.js +2 -2
  17. package/lib/commands/functions-secrets-get.js +3 -17
  18. package/lib/commands/functions-secrets-prune.js +2 -1
  19. package/lib/commands/functions-secrets-set.js +2 -2
  20. package/lib/commands/index.js +6 -0
  21. package/lib/deploy/functions/checkIam.js +3 -6
  22. package/lib/deploy/functions/containerCleaner.js +1 -11
  23. package/lib/deploy/functions/params.js +2 -2
  24. package/lib/deploy/functions/prepare.js +12 -3
  25. package/lib/deploy/functions/prompts.js +39 -7
  26. package/lib/deploy/functions/release/fabricator.js +5 -5
  27. package/lib/deploy/functions/release/index.js +17 -2
  28. package/lib/deploy/functions/release/planner.js +11 -3
  29. package/lib/deploy/functions/runtimes/index.js +6 -43
  30. package/lib/deploy/functions/runtimes/node/index.js +3 -2
  31. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +15 -34
  32. package/lib/deploy/functions/runtimes/python/index.js +11 -7
  33. package/lib/deploy/functions/runtimes/supported.js +135 -0
  34. package/lib/deploy/functions/services/index.js +4 -0
  35. package/lib/emulator/controller.js +8 -1
  36. package/lib/emulator/functionsEmulator.js +2 -2
  37. package/lib/emulator/hub.js +5 -0
  38. package/lib/experiments.js +12 -0
  39. package/lib/extensions/emulator/specHelper.js +4 -3
  40. package/lib/frameworks/next/constants.js +2 -1
  41. package/lib/frameworks/next/index.js +22 -12
  42. package/lib/frameworks/next/utils.js +32 -3
  43. package/lib/functional.js +2 -2
  44. package/lib/functions/events/v2.js +7 -1
  45. package/lib/functions/secrets.js +40 -22
  46. package/lib/gcp/apphosting.js +15 -2
  47. package/lib/gcp/cloudbuild.js +7 -3
  48. package/lib/gcp/cloudfunctions.js +5 -5
  49. package/lib/gcp/cloudfunctionsv2.js +3 -3
  50. package/lib/gcp/cloudscheduler.js +2 -2
  51. package/lib/gcp/computeEngine.js +7 -0
  52. package/lib/gcp/devConnect.js +24 -11
  53. package/lib/gcp/iam.js +9 -1
  54. package/lib/gcp/secretManager.js +53 -13
  55. package/lib/gcp/serviceusage.js +21 -5
  56. package/lib/init/features/functions/python.js +4 -3
  57. package/lib/init/features/index.js +1 -1
  58. package/lib/utils.js +6 -6
  59. package/package.json +1 -1
  60. package/schema/firebase-config.json +12 -2
  61. /package/lib/{init/features/apphosting → apphosting}/constants.js +0 -0
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.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");
7
7
  const api_1 = require("../api");
8
+ const ensureApiEnabled = require("../ensureApiEnabled");
9
+ const projectUtils_1 = require("../projectUtils");
8
10
  const SECRET_NAME_REGEX = new RegExp("projects\\/" +
9
11
  "(?<project>(?:\\d+)|(?:[A-Za-z]+[A-Za-z\\d-]*[A-Za-z\\d]?))\\/" +
10
12
  "secrets\\/" +
@@ -15,15 +17,16 @@ exports.secretManagerConsoleUri = secretManagerConsoleUri;
15
17
  const API_VERSION = "v1";
16
18
  const client = new apiv2_1.Client({ urlPrefix: (0, api_1.secretManagerOrigin)(), apiVersion: API_VERSION });
17
19
  async function getSecret(projectId, name) {
18
- var _a;
20
+ var _a, _b;
19
21
  const getRes = await client.get(`projects/${projectId}/secrets/${name}`);
20
22
  const secret = parseSecretResourceName(getRes.body.name);
21
23
  secret.labels = (_a = getRes.body.labels) !== null && _a !== void 0 ? _a : {};
24
+ secret.replication = (_b = getRes.body.replication) !== null && _b !== void 0 ? _b : {};
22
25
  return secret;
23
26
  }
24
27
  exports.getSecret = getSecret;
25
28
  async function listSecrets(projectId, filter) {
26
- var _a;
29
+ var _a, _b;
27
30
  const secrets = [];
28
31
  const path = `projects/${projectId}/secrets`;
29
32
  const baseOpts = filter ? { queryParams: { filter } } : {};
@@ -34,7 +37,7 @@ async function listSecrets(projectId, filter) {
34
37
  : Object.assign(Object.assign({}, baseOpts), { queryParams: Object.assign(Object.assign({}, baseOpts === null || baseOpts === void 0 ? void 0 : baseOpts.queryParams), { pageToken }) });
35
38
  const res = await client.get(path, opts);
36
39
  for (const s of res.body.secrets || []) {
37
- secrets.push(Object.assign(Object.assign({}, parseSecretResourceName(s.name)), { labels: (_a = s.labels) !== null && _a !== void 0 ? _a : {} }));
40
+ secrets.push(Object.assign(Object.assign({}, parseSecretResourceName(s.name)), { labels: (_a = s.labels) !== null && _a !== void 0 ? _a : {}, replication: (_b = s.replication) !== null && _b !== void 0 ? _b : {} }));
38
41
  }
39
42
  if (!res.body.nextPageToken) {
40
43
  break;
@@ -69,7 +72,7 @@ async function listSecretVersions(projectId, name, filter) {
69
72
  : Object.assign(Object.assign({}, baseOpts), { queryParams: Object.assign(Object.assign({}, baseOpts === null || baseOpts === void 0 ? void 0 : baseOpts.queryParams), { pageToken }) });
70
73
  const res = await client.get(path, opts);
71
74
  for (const s of res.body.versions || []) {
72
- secrets.push(Object.assign(Object.assign({}, parseSecretVersionResourceName(s.name)), { state: s.state }));
75
+ secrets.push(Object.assign(Object.assign({}, parseSecretVersionResourceName(s.name)), { state: s.state, createTime: s.createTime }));
73
76
  }
74
77
  if (!res.body.nextPageToken) {
75
78
  break;
@@ -81,7 +84,7 @@ async function listSecretVersions(projectId, name, filter) {
81
84
  exports.listSecretVersions = listSecretVersions;
82
85
  async function getSecretVersion(projectId, name, version) {
83
86
  const getRes = await client.get(`projects/${projectId}/secrets/${name}/versions/${version}`);
84
- return Object.assign(Object.assign({}, parseSecretVersionResourceName(getRes.body.name)), { state: getRes.body.state });
87
+ return Object.assign(Object.assign({}, parseSecretVersionResourceName(getRes.body.name)), { state: getRes.body.state, createTime: getRes.body.createTime });
85
88
  }
86
89
  exports.getSecretVersion = getSecretVersion;
87
90
  async function accessSecretVersion(projectId, name, version) {
@@ -118,6 +121,8 @@ function parseSecretResourceName(resourceName) {
118
121
  return {
119
122
  projectId: match.groups.project,
120
123
  name: match.groups.secret,
124
+ labels: {},
125
+ replication: {},
121
126
  };
122
127
  }
123
128
  exports.parseSecretResourceName = parseSecretResourceName;
@@ -130,8 +135,11 @@ function parseSecretVersionResourceName(resourceName) {
130
135
  secret: {
131
136
  projectId: match.groups.project,
132
137
  name: match.groups.secret,
138
+ labels: {},
139
+ replication: {},
133
140
  },
134
141
  versionId: match.groups.version,
142
+ createTime: "",
135
143
  };
136
144
  }
137
145
  exports.parseSecretVersionResourceName = parseSecretVersionResourceName;
@@ -139,21 +147,35 @@ function toSecretVersionResourceName(secretVersion) {
139
147
  return `projects/${secretVersion.secret.projectId}/secrets/${secretVersion.secret.name}/versions/${secretVersion.versionId}`;
140
148
  }
141
149
  exports.toSecretVersionResourceName = toSecretVersionResourceName;
142
- async function createSecret(projectId, name, labels) {
150
+ async function createSecret(projectId, name, labels, location) {
151
+ let replication;
152
+ if (location) {
153
+ replication = {
154
+ userManaged: {
155
+ replicas: [
156
+ {
157
+ location,
158
+ },
159
+ ],
160
+ },
161
+ };
162
+ }
163
+ else {
164
+ replication = { automatic: {} };
165
+ }
143
166
  const createRes = await client.post(`projects/${projectId}/secrets`, {
144
167
  name,
145
- replication: {
146
- automatic: {},
147
- },
168
+ replication,
148
169
  labels,
149
170
  }, { queryParams: { secretId: name } });
150
- return Object.assign(Object.assign({}, parseSecretResourceName(createRes.body.name)), { labels });
171
+ return Object.assign(Object.assign({}, parseSecretResourceName(createRes.body.name)), { labels,
172
+ replication });
151
173
  }
152
174
  exports.createSecret = createSecret;
153
175
  async function patchSecret(projectId, name, labels) {
154
176
  const fullName = `projects/${projectId}/secrets/${name}`;
155
177
  const res = await client.patch(fullName, { name: fullName, labels }, { queryParams: { updateMask: "labels" } });
156
- return parseSecretResourceName(res.body.name);
178
+ return Object.assign(Object.assign({}, parseSecretResourceName(res.body.name)), { labels: res.body.labels, replication: res.body.replication });
157
179
  }
158
180
  exports.patchSecret = patchSecret;
159
181
  async function deleteSecret(projectId, name) {
@@ -167,7 +189,7 @@ async function addVersion(projectId, name, payloadData) {
167
189
  data: Buffer.from(payloadData).toString("base64"),
168
190
  },
169
191
  });
170
- return Object.assign(Object.assign({}, parseSecretVersionResourceName(res.body.name)), { state: res.body.state });
192
+ return Object.assign(Object.assign({}, parseSecretVersionResourceName(res.body.name)), { state: res.body.state, createTime: "" });
171
193
  }
172
194
  exports.addVersion = addVersion;
173
195
  async function getIamPolicy(secret) {
@@ -205,3 +227,21 @@ async function ensureServiceAgentRole(secret, serviceAccountEmails, role) {
205
227
  (0, utils_1.logLabeledSuccess)("secretmanager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmails.join(", ")}`);
206
228
  }
207
229
  exports.ensureServiceAgentRole = ensureServiceAgentRole;
230
+ exports.FIREBASE_MANAGED = "firebase-managed";
231
+ function isFunctionsManaged(secret) {
232
+ return (secret.labels[exports.FIREBASE_MANAGED] === "true" || secret.labels[exports.FIREBASE_MANAGED] === "functions");
233
+ }
234
+ exports.isFunctionsManaged = isFunctionsManaged;
235
+ function isAppHostingManaged(secret) {
236
+ return secret.labels[exports.FIREBASE_MANAGED] === "apphosting";
237
+ }
238
+ exports.isAppHostingManaged = isAppHostingManaged;
239
+ function ensureApi(options) {
240
+ const projectId = (0, projectUtils_1.needProjectId)(options);
241
+ return ensureApiEnabled.ensure(projectId, (0, api_1.secretManagerOrigin)(), "secretmanager", true);
242
+ }
243
+ exports.ensureApi = ensureApi;
244
+ function labels(product = "functions") {
245
+ return { [exports.FIREBASE_MANAGED]: product };
246
+ }
247
+ exports.labels = labels;
@@ -1,19 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateServiceIdentity = void 0;
3
+ exports.generateServiceIdentityAndPoll = exports.generateServiceIdentity = exports.apiClient = void 0;
4
4
  const colorette_1 = require("colorette");
5
5
  const api_1 = require("../api");
6
6
  const apiv2_1 = require("../apiv2");
7
7
  const error_1 = require("../error");
8
8
  const utils = require("../utils");
9
- const apiClient = new apiv2_1.Client({
10
- urlPrefix: (0, api_1.serviceUsageOrigin)(),
11
- apiVersion: "v1beta1",
9
+ const poller = require("../operation-poller");
10
+ const API_VERSION = "v1beta1";
11
+ const SERVICE_USAGE_ORIGIN = (0, api_1.serviceUsageOrigin)();
12
+ exports.apiClient = new apiv2_1.Client({
13
+ urlPrefix: SERVICE_USAGE_ORIGIN,
14
+ apiVersion: API_VERSION,
12
15
  });
16
+ const serviceUsagePollerOptions = {
17
+ apiOrigin: SERVICE_USAGE_ORIGIN,
18
+ apiVersion: API_VERSION,
19
+ };
13
20
  async function generateServiceIdentity(projectNumber, service, prefix) {
14
21
  utils.logLabeledBullet(prefix, `generating the service identity for ${(0, colorette_1.bold)(service)}...`);
15
22
  try {
16
- return await apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`);
23
+ const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`);
24
+ return res.body;
17
25
  }
18
26
  catch (err) {
19
27
  throw new error_1.FirebaseError(`Error generating the service identity for ${service}.`, {
@@ -22,3 +30,11 @@ async function generateServiceIdentity(projectNumber, service, prefix) {
22
30
  }
23
31
  }
24
32
  exports.generateServiceIdentity = generateServiceIdentity;
33
+ async function generateServiceIdentityAndPoll(projectNumber, service, prefix) {
34
+ const op = await generateServiceIdentity(projectNumber, service, prefix);
35
+ if (op.done) {
36
+ return;
37
+ }
38
+ await poller.pollOperation(Object.assign(Object.assign({}, serviceUsagePollerOptions), { operationResourceName: op.name }));
39
+ }
40
+ exports.generateServiceIdentityAndPoll = generateServiceIdentityAndPoll;
@@ -7,6 +7,7 @@ const path = require("path");
7
7
  const python_1 = require("../../../deploy/functions/runtimes/python");
8
8
  const python_2 = require("../../../functions/python");
9
9
  const prompt_1 = require("../../../prompt");
10
+ const supported_1 = require("../../../deploy/functions/runtimes/supported");
10
11
  const TEMPLATE_ROOT = path.resolve(__dirname, "../../../../templates/init/functions/python");
11
12
  const MAIN_TEMPLATE = fs.readFileSync(path.join(TEMPLATE_ROOT, "main.py"), "utf8");
12
13
  const REQUIREMENTS_TEMPLATE = fs.readFileSync(path.join(TEMPLATE_ROOT, "requirements.txt"), "utf8");
@@ -15,9 +16,9 @@ async function setup(setup, config) {
15
16
  await config.askWriteProjectFile(`${setup.functions.source}/requirements.txt`, REQUIREMENTS_TEMPLATE);
16
17
  await config.askWriteProjectFile(`${setup.functions.source}/.gitignore`, GITIGNORE_TEMPLATE);
17
18
  await config.askWriteProjectFile(`${setup.functions.source}/main.py`, MAIN_TEMPLATE);
18
- config.set("functions.runtime", python_1.LATEST_VERSION);
19
+ config.set("functions.runtime", (0, supported_1.latest)("python"));
19
20
  config.set("functions.ignore", ["venv", "__pycache__"]);
20
- const venvProcess = spawn((0, python_1.getPythonBinary)(python_1.LATEST_VERSION), ["-m", "venv", "venv"], {
21
+ const venvProcess = spawn((0, python_1.getPythonBinary)((0, supported_1.latest)("python")), ["-m", "venv", "venv"], {
21
22
  shell: true,
22
23
  cwd: config.path(setup.functions.source),
23
24
  stdio: ["pipe", "pipe", "pipe", "pipe"],
@@ -38,7 +39,7 @@ async function setup(setup, config) {
38
39
  upgradeProcess.on("exit", resolve);
39
40
  upgradeProcess.on("error", reject);
40
41
  });
41
- const installProcess = (0, python_2.runWithVirtualEnv)([(0, python_1.getPythonBinary)(python_1.LATEST_VERSION), "-m", "pip", "install", "-r", "requirements.txt"], config.path(setup.functions.source), {}, { stdio: ["inherit", "inherit", "inherit"] });
42
+ const installProcess = (0, python_2.runWithVirtualEnv)([(0, python_1.getPythonBinary)((0, supported_1.latest)("python")), "-m", "pip", "install", "-r", "requirements.txt"], config.path(setup.functions.source), {}, { stdio: ["inherit", "inherit", "inherit"] });
42
43
  await new Promise((resolve, reject) => {
43
44
  installProcess.on("exit", resolve);
44
45
  installProcess.on("error", reject);
@@ -23,5 +23,5 @@ var remoteconfig_1 = require("./remoteconfig");
23
23
  Object.defineProperty(exports, "remoteconfig", { enumerable: true, get: function () { return remoteconfig_1.doSetup; } });
24
24
  var github_1 = require("./hosting/github");
25
25
  Object.defineProperty(exports, "hostingGithub", { enumerable: true, get: function () { return github_1.initGitHub; } });
26
- var apphosting_1 = require("./apphosting");
26
+ var apphosting_1 = require("../../apphosting");
27
27
  Object.defineProperty(exports, "apphosting", { enumerable: true, get: function () { return apphosting_1.doSetup; } });
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getHostnameFromUrl = 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.isVSCodeExtension = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = 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.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
3
+ exports.getHostnameFromUrl = 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.isVSCodeExtension = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = 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.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = exports.IS_WINDOWS = void 0;
4
4
  const fs = require("node:fs");
5
5
  const path = require("node:path");
6
6
  const _ = require("lodash");
@@ -19,10 +19,10 @@ const portfinder_1 = require("portfinder");
19
19
  const configstore_1 = require("./configstore");
20
20
  const error_1 = require("./error");
21
21
  const logger_1 = require("./logger");
22
- const IS_WINDOWS = process.platform === "win32";
23
- const SUCCESS_CHAR = IS_WINDOWS ? "+" : "✔";
24
- const WARNING_CHAR = IS_WINDOWS ? "!" : "⚠";
25
- const ERROR_CHAR = IS_WINDOWS ? "!!" : "⬢";
22
+ exports.IS_WINDOWS = process.platform === "win32";
23
+ const SUCCESS_CHAR = exports.IS_WINDOWS ? "+" : "✔";
24
+ const WARNING_CHAR = exports.IS_WINDOWS ? "!" : "⚠";
25
+ const ERROR_CHAR = exports.IS_WINDOWS ? "!!" : "⬢";
26
26
  const THIRTY_DAYS_IN_MILLISECONDS = 30 * 24 * 60 * 60 * 1000;
27
27
  exports.envOverrides = [];
28
28
  function consoleUrl(project, path) {
@@ -148,7 +148,7 @@ function allSettled(promises) {
148
148
  }
149
149
  exports.allSettled = allSettled;
150
150
  function explainStdin() {
151
- if (IS_WINDOWS) {
151
+ if (exports.IS_WINDOWS) {
152
152
  throw new error_1.FirebaseError("STDIN input is not available on Windows.", {
153
153
  exit: 1,
154
154
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.6.0",
3
+ "version": "13.7.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -622,7 +622,12 @@
622
622
  "nodejs14",
623
623
  "nodejs16",
624
624
  "nodejs18",
625
- "nodejs20"
625
+ "nodejs20",
626
+ "nodejs6",
627
+ "nodejs8",
628
+ "python310",
629
+ "python311",
630
+ "python312"
626
631
  ],
627
632
  "type": "string"
628
633
  },
@@ -678,7 +683,12 @@
678
683
  "nodejs14",
679
684
  "nodejs16",
680
685
  "nodejs18",
681
- "nodejs20"
686
+ "nodejs20",
687
+ "nodejs6",
688
+ "nodejs8",
689
+ "python310",
690
+ "python311",
691
+ "python312"
682
692
  ],
683
693
  "type": "string"
684
694
  },