firebase-tools 11.29.1 → 12.0.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.
- package/lib/api.js +4 -2
- package/lib/commands/database-import.js +2 -2
- package/lib/commands/ext-configure.js +2 -1
- package/lib/commands/ext-dev-deprecate.js +24 -20
- package/lib/commands/ext-dev-list.js +12 -11
- package/lib/commands/ext-dev-publish.js +13 -47
- package/lib/commands/ext-dev-register.js +8 -5
- package/lib/commands/ext-dev-undeprecate.js +4 -4
- package/lib/commands/ext-dev-upload.js +88 -0
- package/lib/commands/ext-dev-usage.js +3 -3
- package/lib/commands/ext-install.js +5 -10
- package/lib/commands/ext-uninstall.js +0 -1
- package/lib/commands/ext-update.js +4 -10
- package/lib/commands/hosting-channel-deploy.js +3 -0
- package/lib/commands/index.js +9 -19
- package/lib/database/import.js +113 -18
- package/lib/deploy/extensions/planner.js +13 -7
- package/lib/deploy/extensions/prepare.js +16 -32
- package/lib/deploy/functions/ensure.js +7 -1
- package/lib/deploy/functions/release/fabricator.js +2 -0
- package/lib/deploy/functions/runtimes/discovery/index.js +1 -1
- package/lib/deploy/functions/runtimes/index.js +11 -3
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
- package/lib/deploy/functions/runtimes/python/index.js +41 -13
- package/lib/deploy/hosting/convertConfig.js +8 -4
- package/lib/deploy/hosting/prepare.js +64 -6
- package/lib/deploy/index.js +24 -8
- package/lib/emulator/adminSdkConfig.js +8 -0
- package/lib/emulator/controller.js +7 -9
- package/lib/emulator/download.js +3 -12
- package/lib/emulator/downloadableEmulators.js +5 -5
- package/lib/emulator/functionsEmulator.js +57 -7
- package/lib/emulator/functionsEmulatorRuntime.js +4 -1
- package/lib/emulator/functionsEmulatorShared.js +1 -0
- package/lib/emulator/functionsRuntimeWorker.js +12 -4
- package/lib/emulator/storage/rules/config.js +17 -7
- package/lib/experiments.js +22 -8
- package/lib/extensions/extensionsApi.js +24 -151
- package/lib/extensions/extensionsHelper.js +283 -146
- package/lib/extensions/manifest.js +1 -8
- package/lib/extensions/publisherApi.js +215 -0
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/resolveSource.js +1 -18
- package/lib/extensions/tos.js +78 -0
- package/lib/extensions/warnings.js +21 -41
- package/lib/frameworks/angular/index.js +74 -192
- package/lib/frameworks/angular/interfaces.js +2 -0
- package/lib/frameworks/angular/utils.js +274 -0
- package/lib/frameworks/astro/index.js +3 -4
- package/lib/frameworks/constants.js +45 -0
- package/lib/frameworks/express/index.js +3 -2
- package/lib/frameworks/flutter/index.js +39 -0
- package/lib/frameworks/flutter/utils.js +11 -0
- package/lib/frameworks/index.js +104 -145
- package/lib/frameworks/interfaces.js +2 -0
- package/lib/frameworks/next/constants.js +2 -1
- package/lib/frameworks/next/index.js +197 -114
- package/lib/frameworks/next/utils.js +97 -15
- package/lib/frameworks/nuxt/index.js +4 -5
- package/lib/frameworks/nuxt/utils.js +2 -2
- package/lib/frameworks/nuxt2/index.js +5 -5
- package/lib/frameworks/utils.js +108 -1
- package/lib/frameworks/vite/index.js +5 -6
- package/lib/functions/ensureTargeted.js +4 -4
- package/lib/functions/python.js +12 -5
- package/lib/gcp/resourceManager.js +1 -0
- package/lib/hosting/api.js +32 -1
- package/lib/hosting/config.js +4 -8
- package/lib/init/features/functions/index.js +4 -7
- package/lib/init/features/hosting/github.js +7 -2
- package/lib/init/features/hosting/index.js +3 -2
- package/lib/serve/index.js +2 -1
- package/lib/unzip.js +126 -0
- package/package.json +2 -3
- package/schema/firebase-config.json +1 -1
- package/templates/extensions/POSTINSTALL.md +2 -2
- package/templates/extensions/PREINSTALL.md +1 -1
- package/templates/extensions/extension.yaml +10 -6
- package/templates/extensions/javascript/WELCOME.md +1 -1
- package/templates/extensions/typescript/WELCOME.md +1 -1
- package/templates/extensions/typescript/index.ts +1 -1
- package/templates/init/functions/javascript/index.js +16 -6
- package/templates/init/functions/javascript/package.lint.json +4 -4
- package/templates/init/functions/javascript/package.nolint.json +4 -4
- package/templates/init/functions/python/requirements.txt +1 -1
- package/templates/init/functions/typescript/index.ts +16 -6
- package/templates/init/functions/typescript/package.lint.json +4 -4
- package/templates/init/functions/typescript/package.nolint.json +4 -4
- package/lib/commands/ext-dev-emulators-exec.js +0 -27
- package/lib/commands/ext-dev-emulators-start.js +0 -24
- package/lib/commands/ext-dev-extension-delete.js +0 -45
- package/lib/commands/ext-dev-unpublish.js +0 -49
- package/lib/commands/ext-sources-create.js +0 -24
- package/lib/extensions/askUserForConsent.js +0 -33
- package/npm-shrinkwrap.json +0 -12649
|
@@ -6,7 +6,6 @@ const promises_1 = require("fs/promises");
|
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const semver_1 = require("semver");
|
|
8
8
|
const cross_spawn_1 = require("cross-spawn");
|
|
9
|
-
const __1 = require("..");
|
|
10
9
|
const utils_1 = require("../utils");
|
|
11
10
|
const utils_2 = require("./utils");
|
|
12
11
|
exports.name = "Nuxt";
|
|
@@ -28,7 +27,7 @@ async function discover(dir) {
|
|
|
28
27
|
}
|
|
29
28
|
exports.discover = discover;
|
|
30
29
|
async function build(root) {
|
|
31
|
-
const { buildNuxt } = await (0,
|
|
30
|
+
const { buildNuxt } = await (0, utils_1.relativeRequire)(root, "@nuxt/kit");
|
|
32
31
|
const nuxtApp = await getNuxt3App(root);
|
|
33
32
|
await (0, utils_1.warnIfCustomBuildScript)(root, exports.name, DEFAULT_BUILD_SCRIPT);
|
|
34
33
|
await buildNuxt(nuxtApp);
|
|
@@ -36,7 +35,7 @@ async function build(root) {
|
|
|
36
35
|
}
|
|
37
36
|
exports.build = build;
|
|
38
37
|
async function getNuxt3App(cwd) {
|
|
39
|
-
const { loadNuxt } = await (0,
|
|
38
|
+
const { loadNuxt } = await (0, utils_1.relativeRequire)(cwd, "@nuxt/kit");
|
|
40
39
|
return await loadNuxt({
|
|
41
40
|
cwd,
|
|
42
41
|
overrides: {
|
|
@@ -60,7 +59,7 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir) {
|
|
|
60
59
|
exports.ɵcodegenFunctionsDirectory = ɵcodegenFunctionsDirectory;
|
|
61
60
|
async function getDevModeHandle(cwd) {
|
|
62
61
|
const host = new Promise((resolve) => {
|
|
63
|
-
const cli = (0,
|
|
62
|
+
const cli = (0, utils_1.getNodeModuleBin)("nuxt", cwd);
|
|
64
63
|
const serve = (0, cross_spawn_1.spawn)(cli, ["dev"], { cwd: cwd });
|
|
65
64
|
serve.stdout.on("data", (data) => {
|
|
66
65
|
process.stdout.write(data);
|
|
@@ -76,7 +75,7 @@ async function getDevModeHandle(cwd) {
|
|
|
76
75
|
}
|
|
77
76
|
exports.getDevModeHandle = getDevModeHandle;
|
|
78
77
|
async function getConfig(dir) {
|
|
79
|
-
const { loadNuxtConfig } = await (0,
|
|
78
|
+
const { loadNuxtConfig } = await (0, utils_1.relativeRequire)(dir, "@nuxt/kit");
|
|
80
79
|
return await loadNuxtConfig(dir);
|
|
81
80
|
}
|
|
82
81
|
exports.getConfig = getConfig;
|
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.nuxtConfigFilesExist = exports.getNuxtVersion = void 0;
|
|
4
4
|
const fs_extra_1 = require("fs-extra");
|
|
5
5
|
const path_1 = require("path");
|
|
6
|
-
const
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
7
|
function getNuxtVersion(cwd) {
|
|
8
8
|
var _a;
|
|
9
|
-
return (_a = (0,
|
|
9
|
+
return (_a = (0, utils_1.findDependency)("nuxt", {
|
|
10
10
|
cwd,
|
|
11
11
|
depth: 0,
|
|
12
12
|
omitDev: false,
|
|
@@ -5,16 +5,16 @@ const fs_extra_1 = require("fs-extra");
|
|
|
5
5
|
const promises_1 = require("fs/promises");
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const semver_1 = require("semver");
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
const utils_2 = require("../nuxt/utils");
|
|
10
10
|
exports.name = "Nuxt";
|
|
11
11
|
exports.support = "experimental";
|
|
12
12
|
exports.type = 2;
|
|
13
13
|
async function discover(dir) {
|
|
14
14
|
if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
|
|
15
15
|
return;
|
|
16
|
-
const nuxtVersion = (0,
|
|
17
|
-
const anyConfigFileExists = await (0,
|
|
16
|
+
const nuxtVersion = (0, utils_2.getNuxtVersion)(dir);
|
|
17
|
+
const anyConfigFileExists = await (0, utils_2.nuxtConfigFilesExist)(dir);
|
|
18
18
|
if (!anyConfigFileExists && !nuxtVersion)
|
|
19
19
|
return;
|
|
20
20
|
if (nuxtVersion && (0, semver_1.lt)(nuxtVersion, "3.0.0-0"))
|
|
@@ -22,7 +22,7 @@ async function discover(dir) {
|
|
|
22
22
|
}
|
|
23
23
|
exports.discover = discover;
|
|
24
24
|
async function getNuxtApp(cwd) {
|
|
25
|
-
return await (0,
|
|
25
|
+
return await (0, utils_1.relativeRequire)(cwd, "nuxt/dist/nuxt.js");
|
|
26
26
|
}
|
|
27
27
|
async function build(root) {
|
|
28
28
|
const nuxt = await getNuxtApp(root);
|
package/lib/frameworks/utils.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.simpleProxy = exports.warnIfCustomBuildScript = exports.readJSON = exports.isUrl = void 0;
|
|
3
|
+
exports.validateLocales = exports.frameworksCallToAction = exports.conjoinOptions = exports.relativeRequire = exports.findDependency = exports.getNodeModuleBin = exports.getNpmRoot = exports.simpleProxy = exports.warnIfCustomBuildScript = exports.readJSON = exports.isUrl = void 0;
|
|
4
4
|
const fs_extra_1 = require("fs-extra");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
const promises_1 = require("fs/promises");
|
|
7
7
|
const http_1 = require("http");
|
|
8
|
+
const cross_spawn_1 = require("cross-spawn");
|
|
9
|
+
const clc = require("colorette");
|
|
8
10
|
const logger_1 = require("../logger");
|
|
11
|
+
const error_1 = require("../error");
|
|
12
|
+
const fsutils_1 = require("../fsutils");
|
|
13
|
+
const url_1 = require("url");
|
|
14
|
+
const constants_1 = require("./constants");
|
|
15
|
+
const { dynamicImport } = require(true && "../dynamicImport");
|
|
16
|
+
const NPM_ROOT_TIMEOUT_MILLIES = 2000;
|
|
9
17
|
function isUrl(url) {
|
|
10
18
|
return /^https?:\/\//.test(url);
|
|
11
19
|
}
|
|
@@ -67,3 +75,102 @@ function simpleProxy(hostOrRequestHandler) {
|
|
|
67
75
|
};
|
|
68
76
|
}
|
|
69
77
|
exports.simpleProxy = simpleProxy;
|
|
78
|
+
function scanDependencyTree(searchingFor, dependencies = {}) {
|
|
79
|
+
for (const [name, dependency] of Object.entries(dependencies)) {
|
|
80
|
+
if (name === searchingFor)
|
|
81
|
+
return dependency;
|
|
82
|
+
const result = scanDependencyTree(searchingFor, dependency.dependencies);
|
|
83
|
+
if (result)
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
function getNpmRoot(cwd) {
|
|
89
|
+
var _a;
|
|
90
|
+
return (_a = (0, cross_spawn_1.sync)("npm", ["root"], { cwd, timeout: NPM_ROOT_TIMEOUT_MILLIES })
|
|
91
|
+
.stdout) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
92
|
+
}
|
|
93
|
+
exports.getNpmRoot = getNpmRoot;
|
|
94
|
+
function getNodeModuleBin(name, cwd) {
|
|
95
|
+
const cantFindExecutable = new error_1.FirebaseError(`Could not find the ${name} executable.`);
|
|
96
|
+
const npmRoot = getNpmRoot(cwd);
|
|
97
|
+
if (!npmRoot) {
|
|
98
|
+
throw cantFindExecutable;
|
|
99
|
+
}
|
|
100
|
+
const path = (0, path_1.join)(npmRoot, ".bin", name);
|
|
101
|
+
if (!(0, fsutils_1.fileExistsSync)(path)) {
|
|
102
|
+
throw cantFindExecutable;
|
|
103
|
+
}
|
|
104
|
+
return path;
|
|
105
|
+
}
|
|
106
|
+
exports.getNodeModuleBin = getNodeModuleBin;
|
|
107
|
+
const DEFAULT_FIND_DEP_OPTIONS = {
|
|
108
|
+
cwd: process.cwd(),
|
|
109
|
+
omitDev: true,
|
|
110
|
+
};
|
|
111
|
+
function findDependency(name, options = {}) {
|
|
112
|
+
const { cwd: dir, depth, omitDev } = Object.assign(Object.assign({}, DEFAULT_FIND_DEP_OPTIONS), options);
|
|
113
|
+
const cwd = getNpmRoot(dir);
|
|
114
|
+
if (!cwd)
|
|
115
|
+
return;
|
|
116
|
+
const env = Object.assign({}, process.env);
|
|
117
|
+
delete env.NODE_ENV;
|
|
118
|
+
const result = (0, cross_spawn_1.sync)("npm", [
|
|
119
|
+
"list",
|
|
120
|
+
name,
|
|
121
|
+
"--json=true",
|
|
122
|
+
...(omitDev ? ["--omit", "dev"] : []),
|
|
123
|
+
...(depth === undefined ? [] : ["--depth", depth.toString(10)]),
|
|
124
|
+
], { cwd, env, timeout: constants_1.NPM_COMMAND_TIMEOUT_MILLIES });
|
|
125
|
+
if (!result.stdout)
|
|
126
|
+
return;
|
|
127
|
+
const json = JSON.parse(result.stdout.toString());
|
|
128
|
+
return scanDependencyTree(name, json.dependencies);
|
|
129
|
+
}
|
|
130
|
+
exports.findDependency = findDependency;
|
|
131
|
+
function relativeRequire(dir, mod) {
|
|
132
|
+
try {
|
|
133
|
+
const path = require.resolve(mod, { paths: [dir] });
|
|
134
|
+
if ((0, path_1.extname)(path) === ".mjs") {
|
|
135
|
+
return dynamicImport((0, url_1.pathToFileURL)(path).toString());
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return require(path);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
const path = (0, path_1.relative)(process.cwd(), dir);
|
|
143
|
+
console.error(`Could not load dependency ${mod} in ${path.startsWith("..") ? path : `./${path}`}, have you run \`npm install\`?`);
|
|
144
|
+
throw e;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.relativeRequire = relativeRequire;
|
|
148
|
+
function conjoinOptions(opts, conjunction = "and", separator = ",") {
|
|
149
|
+
if (!opts.length)
|
|
150
|
+
return;
|
|
151
|
+
if (opts.length === 1)
|
|
152
|
+
return opts[0].toString();
|
|
153
|
+
if (opts.length === 2)
|
|
154
|
+
return `${opts[0].toString()} ${conjunction} ${opts[1].toString()}`;
|
|
155
|
+
const lastElement = opts.slice(-1)[0].toString();
|
|
156
|
+
const allButLast = opts.slice(0, -1).map((it) => it.toString());
|
|
157
|
+
return `${allButLast.join(`${separator} `)}${separator} ${conjunction} ${lastElement}`;
|
|
158
|
+
}
|
|
159
|
+
exports.conjoinOptions = conjoinOptions;
|
|
160
|
+
function frameworksCallToAction(message, docsUrl = constants_1.DEFAULT_DOCS_URL, prefix = "") {
|
|
161
|
+
return `${prefix}${message}
|
|
162
|
+
|
|
163
|
+
${prefix}${clc.bold("Documentation:")} ${docsUrl}
|
|
164
|
+
${prefix}${clc.bold("File a bug:")} ${constants_1.FILE_BUG_URL}
|
|
165
|
+
${prefix}${clc.bold("Submit a feature request:")} ${constants_1.FEATURE_REQUEST_URL}
|
|
166
|
+
|
|
167
|
+
${prefix}We'd love to learn from you. Express your interest in helping us shape the future of Firebase Hosting: ${constants_1.MAILING_LIST_URL}`;
|
|
168
|
+
}
|
|
169
|
+
exports.frameworksCallToAction = frameworksCallToAction;
|
|
170
|
+
function validateLocales(locales = []) {
|
|
171
|
+
const invalidLocales = locales.filter((locale) => !constants_1.VALID_LOCALE_FORMATS.some((format) => locale.match(format)));
|
|
172
|
+
if (invalidLocales.length) {
|
|
173
|
+
throw new error_1.FirebaseError(`Invalid i18n locales (${invalidLocales.join(", ")}) for Firebase. See our docs for more information https://firebase.google.com/docs/hosting/i18n-rewrites#country-and-language-codes`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.validateLocales = validateLocales;
|
|
@@ -6,7 +6,6 @@ const cross_spawn_1 = require("cross-spawn");
|
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const fs_extra_1 = require("fs-extra");
|
|
8
8
|
const path_1 = require("path");
|
|
9
|
-
const __1 = require("..");
|
|
10
9
|
const prompt_1 = require("../../prompt");
|
|
11
10
|
const utils_1 = require("../utils");
|
|
12
11
|
exports.name = "Vite";
|
|
@@ -39,14 +38,14 @@ exports.vitePluginDiscover = vitePluginDiscover;
|
|
|
39
38
|
async function discover(dir, plugin, npmDependency) {
|
|
40
39
|
if (!(0, fs_1.existsSync)((0, path_1.join)(dir, "package.json")))
|
|
41
40
|
return;
|
|
42
|
-
const additionalDep = npmDependency && (0,
|
|
41
|
+
const additionalDep = npmDependency && (0, utils_1.findDependency)(npmDependency, { cwd: dir, depth: 0, omitDev: false });
|
|
43
42
|
const depth = plugin ? undefined : 0;
|
|
44
43
|
const configFilesExist = await Promise.all([
|
|
45
44
|
(0, fs_extra_1.pathExists)((0, path_1.join)(dir, "vite.config.js")),
|
|
46
45
|
(0, fs_extra_1.pathExists)((0, path_1.join)(dir, "vite.config.ts")),
|
|
47
46
|
]);
|
|
48
47
|
const anyConfigFileExists = configFilesExist.some((it) => it);
|
|
49
|
-
if (!anyConfigFileExists && !(0,
|
|
48
|
+
if (!anyConfigFileExists && !(0, utils_1.findDependency)("vite", { cwd: dir, depth, omitDev: false }))
|
|
50
49
|
return;
|
|
51
50
|
if (npmDependency && !additionalDep)
|
|
52
51
|
return;
|
|
@@ -57,7 +56,7 @@ async function discover(dir, plugin, npmDependency) {
|
|
|
57
56
|
}
|
|
58
57
|
exports.discover = discover;
|
|
59
58
|
async function build(root) {
|
|
60
|
-
const { build } = (0,
|
|
59
|
+
const { build } = (0, utils_1.relativeRequire)(root, "vite");
|
|
61
60
|
await (0, utils_1.warnIfCustomBuildScript)(root, exports.name, exports.DEFAULT_BUILD_SCRIPT);
|
|
62
61
|
const cwd = process.cwd();
|
|
63
62
|
process.chdir(root);
|
|
@@ -73,7 +72,7 @@ async function ɵcodegenPublicDirectory(root, dest) {
|
|
|
73
72
|
exports.ɵcodegenPublicDirectory = ɵcodegenPublicDirectory;
|
|
74
73
|
async function getDevModeHandle(dir) {
|
|
75
74
|
const host = new Promise((resolve) => {
|
|
76
|
-
const cli = (0,
|
|
75
|
+
const cli = (0, utils_1.getNodeModuleBin)("vite", dir);
|
|
77
76
|
const serve = (0, cross_spawn_1.spawn)(cli, [], { cwd: dir });
|
|
78
77
|
serve.stdout.on("data", (data) => {
|
|
79
78
|
process.stdout.write(data);
|
|
@@ -89,7 +88,7 @@ async function getDevModeHandle(dir) {
|
|
|
89
88
|
}
|
|
90
89
|
exports.getDevModeHandle = getDevModeHandle;
|
|
91
90
|
async function getConfig(root) {
|
|
92
|
-
const { resolveConfig } = (0,
|
|
91
|
+
const { resolveConfig } = (0, utils_1.relativeRequire)(root, "vite");
|
|
93
92
|
const cwd = process.cwd();
|
|
94
93
|
process.chdir(root);
|
|
95
94
|
const config = await resolveConfig({ root }, "build", "production");
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ensureTargeted = void 0;
|
|
4
|
-
function ensureTargeted(only,
|
|
4
|
+
function ensureTargeted(only, codebaseOrFunction, functionId) {
|
|
5
5
|
const parts = only.split(",");
|
|
6
6
|
if (parts.includes("functions")) {
|
|
7
7
|
return only;
|
|
8
8
|
}
|
|
9
|
-
let newTarget = `functions:${
|
|
9
|
+
let newTarget = `functions:${codebaseOrFunction}`;
|
|
10
10
|
if (parts.includes(newTarget)) {
|
|
11
11
|
return only;
|
|
12
12
|
}
|
|
13
|
-
if (
|
|
14
|
-
newTarget = `${newTarget}:${
|
|
13
|
+
if (functionId) {
|
|
14
|
+
newTarget = `${newTarget}:${functionId}`;
|
|
15
15
|
if (parts.includes(newTarget)) {
|
|
16
16
|
return only;
|
|
17
17
|
}
|
package/lib/functions/python.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runWithVirtualEnv = void 0;
|
|
3
|
+
exports.runWithVirtualEnv = exports.virtualEnvCmd = exports.DEFAULT_VENV_DIR = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const spawn = require("cross-spawn");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
|
-
|
|
8
|
-
function
|
|
7
|
+
exports.DEFAULT_VENV_DIR = "venv";
|
|
8
|
+
function virtualEnvCmd(cwd, venvDir) {
|
|
9
9
|
const activateScriptPath = process.platform === "win32" ? ["Scripts", "activate.bat"] : ["bin", "activate"];
|
|
10
10
|
const venvActivate = path.join(cwd, venvDir, ...activateScriptPath);
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
return {
|
|
12
|
+
command: process.platform === "win32" ? venvActivate : ".",
|
|
13
|
+
args: [process.platform === "win32" ? "" : venvActivate],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
exports.virtualEnvCmd = virtualEnvCmd;
|
|
17
|
+
function runWithVirtualEnv(commandAndArgs, cwd, envs, spawnOpts = {}, venvDir = exports.DEFAULT_VENV_DIR) {
|
|
18
|
+
const { command, args } = virtualEnvCmd(cwd, venvDir);
|
|
19
|
+
args.push("&&", ...commandAndArgs);
|
|
13
20
|
logger_1.logger.debug(`Running command with virtualenv: command=${command}, args=${JSON.stringify(args)}`);
|
|
14
21
|
return spawn(command, args, Object.assign(Object.assign({ shell: true, cwd, stdio: ["pipe", "pipe", "pipe", "pipe"] }, spawnOpts), { env: envs }));
|
|
15
22
|
}
|
|
@@ -10,6 +10,7 @@ const apiClient = new apiv2_1.Client({ urlPrefix: api_1.resourceManagerOrigin, a
|
|
|
10
10
|
exports.firebaseRoles = {
|
|
11
11
|
apiKeysViewer: "roles/serviceusage.apiKeysViewer",
|
|
12
12
|
authAdmin: "roles/firebaseauth.admin",
|
|
13
|
+
functionsDeveloper: "roles/cloudfunctions.developer",
|
|
13
14
|
hostingAdmin: "roles/firebasehosting.admin",
|
|
14
15
|
runViewer: "roles/run.viewer",
|
|
15
16
|
};
|
package/lib/hosting/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cleanAuthState = exports.getCleanDomains = exports.removeAuthDomain = exports.addAuthDomains = exports.deleteSite = exports.updateSite = exports.createSite = exports.getSite = exports.listSites = exports.createRelease = exports.cloneVersion = exports.listVersions = exports.updateVersion = exports.createVersion = exports.deleteChannel = exports.updateChannelTtl = exports.createChannel = exports.listChannels = exports.getChannel = exports.normalizeName = void 0;
|
|
3
|
+
exports.getAllSiteDomains = exports.getSiteDomains = exports.cleanAuthState = exports.getCleanDomains = exports.removeAuthDomain = exports.addAuthDomains = exports.deleteSite = exports.updateSite = exports.createSite = exports.getSite = exports.listSites = exports.createRelease = exports.cloneVersion = exports.listVersions = exports.updateVersion = exports.createVersion = exports.deleteChannel = exports.updateChannelTtl = exports.createChannel = exports.listChannels = exports.getChannel = exports.normalizeName = void 0;
|
|
4
4
|
const error_1 = require("../error");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
6
|
const apiv2_1 = require("../apiv2");
|
|
@@ -246,3 +246,34 @@ async function cleanAuthState(project, sites) {
|
|
|
246
246
|
return siteDomainMap;
|
|
247
247
|
}
|
|
248
248
|
exports.cleanAuthState = cleanAuthState;
|
|
249
|
+
async function getSiteDomains(project, site) {
|
|
250
|
+
var _a;
|
|
251
|
+
try {
|
|
252
|
+
const res = await apiClient.get(`/projects/${project}/sites/${site}/domains`);
|
|
253
|
+
return (_a = res.body.domains) !== null && _a !== void 0 ? _a : [];
|
|
254
|
+
}
|
|
255
|
+
catch (e) {
|
|
256
|
+
if (e instanceof error_1.FirebaseError && e.status === 404) {
|
|
257
|
+
throw new error_1.FirebaseError(`could not find site "${site}" for project "${project}"`, {
|
|
258
|
+
original: e,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
throw e;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
exports.getSiteDomains = getSiteDomains;
|
|
265
|
+
async function getAllSiteDomains(projectId, siteId) {
|
|
266
|
+
const [hostingDomains, defaultDomain] = await Promise.all([
|
|
267
|
+
getSiteDomains(projectId, siteId),
|
|
268
|
+
getSite(projectId, siteId),
|
|
269
|
+
]);
|
|
270
|
+
const defaultDomainWithoutHttp = defaultDomain.defaultUrl.replace(/^https?:\/\//, "");
|
|
271
|
+
const allSiteDomains = new Set([
|
|
272
|
+
...hostingDomains.map(({ domainName }) => domainName),
|
|
273
|
+
defaultDomainWithoutHttp,
|
|
274
|
+
`${siteId}.web.app`,
|
|
275
|
+
`${siteId}.firebaseapp.com`,
|
|
276
|
+
]);
|
|
277
|
+
return Array.from(allSiteDomains);
|
|
278
|
+
}
|
|
279
|
+
exports.getAllSiteDomains = getAllSiteDomains;
|
package/lib/hosting/config.js
CHANGED
|
@@ -8,7 +8,6 @@ const functional_1 = require("../functional");
|
|
|
8
8
|
const fsutils_1 = require("../fsutils");
|
|
9
9
|
const projectPath_1 = require("../projectPath");
|
|
10
10
|
const path = require("node:path");
|
|
11
|
-
const experiments = require("../experiments");
|
|
12
11
|
const logger_1 = require("../logger");
|
|
13
12
|
function matchingConfigs(configs, targets, assertMatches) {
|
|
14
13
|
const matches = [];
|
|
@@ -95,15 +94,12 @@ function validateOne(config, options) {
|
|
|
95
94
|
if (config.source && config.public) {
|
|
96
95
|
throw new error_1.FirebaseError('Can only specify "source" or "public" in a Hosting config, not both');
|
|
97
96
|
}
|
|
98
|
-
const root =
|
|
99
|
-
? config.source || config.public
|
|
100
|
-
: config.public;
|
|
101
|
-
const orSource = experiments.isEnabled("webframeworks") ? ' or "source"' : "";
|
|
97
|
+
const root = config.source || config.public;
|
|
102
98
|
if (!root && hasAnyStaticRewrites) {
|
|
103
|
-
throw new error_1.FirebaseError(`Must supply a "public"
|
|
99
|
+
throw new error_1.FirebaseError(`Must supply a "public" or "source" directory when using "destination" rewrites.`);
|
|
104
100
|
}
|
|
105
101
|
if (!root && !hasAnyDynamicRewrites && !hasAnyRedirects) {
|
|
106
|
-
throw new error_1.FirebaseError(`Must supply a "public"
|
|
102
|
+
throw new error_1.FirebaseError(`Must supply a "public" or "source" directory or at least one rewrite or redirect in each "hosting" config.`);
|
|
107
103
|
}
|
|
108
104
|
if (root && !(0, fsutils_1.dirExistsSync)((0, projectPath_1.resolveProjectPath)(options, root))) {
|
|
109
105
|
logger_1.logger.debug(`Specified "${config.source ? "source" : "public"}" directory "${root}" does not exist; Deploy to Hosting site "${config.site || config.target || ""}" may fail or be empty.`);
|
|
@@ -115,7 +111,7 @@ function validateOne(config, options) {
|
|
|
115
111
|
}
|
|
116
112
|
if (config.i18n) {
|
|
117
113
|
if (!root) {
|
|
118
|
-
throw new error_1.FirebaseError(`Must supply a "public"
|
|
114
|
+
throw new error_1.FirebaseError(`Must supply a "public" or "source" directory when using "i18n" configuration.`);
|
|
119
115
|
}
|
|
120
116
|
if (!config.i18n.root) {
|
|
121
117
|
throw new error_1.FirebaseError('Must supply a "root" in "i18n" config.');
|
|
@@ -8,7 +8,6 @@ const requirePermissions_1 = require("../../../requirePermissions");
|
|
|
8
8
|
const ensureApiEnabled_1 = require("../../../ensureApiEnabled");
|
|
9
9
|
const projectConfig_1 = require("../../../functions/projectConfig");
|
|
10
10
|
const error_1 = require("../../../error");
|
|
11
|
-
const experiments_1 = require("../../../experiments");
|
|
12
11
|
const MAX_ATTEMPTS = 5;
|
|
13
12
|
async function doSetup(setup, config, options) {
|
|
14
13
|
var _a, _b;
|
|
@@ -140,12 +139,10 @@ async function languageSetup(setup, config) {
|
|
|
140
139
|
value: "typescript",
|
|
141
140
|
},
|
|
142
141
|
];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
});
|
|
148
|
-
}
|
|
142
|
+
choices.push({
|
|
143
|
+
name: "Python",
|
|
144
|
+
value: "python",
|
|
145
|
+
});
|
|
149
146
|
const language = await (0, prompt_1.promptOnce)({
|
|
150
147
|
type: "list",
|
|
151
148
|
message: "What language would you like to use to write Cloud Functions?",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initGitHub = void 0;
|
|
3
|
+
exports.isRunningInGithubAction = exports.initGitHub = void 0;
|
|
4
4
|
const colorette_1 = require("colorette");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const yaml = require("js-yaml");
|
|
@@ -355,7 +355,7 @@ async function createServiceAccountAndKeyWithRetry(options, repo, accountId) {
|
|
|
355
355
|
}
|
|
356
356
|
async function createServiceAccountAndKey(options, repo, accountId) {
|
|
357
357
|
try {
|
|
358
|
-
await (0, iam_1.createServiceAccount)(options.projectId, accountId, `A service account with permission to deploy to Firebase Hosting for the GitHub repository ${repo}`, `GitHub Actions (${repo})`);
|
|
358
|
+
await (0, iam_1.createServiceAccount)(options.projectId, accountId, `A service account with permission to deploy to Firebase Hosting and Cloud Functions for the GitHub repository ${repo}`, `GitHub Actions (${repo})`);
|
|
359
359
|
}
|
|
360
360
|
catch (e) {
|
|
361
361
|
if (!e.message.includes("409")) {
|
|
@@ -367,6 +367,7 @@ async function createServiceAccountAndKey(options, repo, accountId) {
|
|
|
367
367
|
resourceManager_1.firebaseRoles.apiKeysViewer,
|
|
368
368
|
resourceManager_1.firebaseRoles.hostingAdmin,
|
|
369
369
|
resourceManager_1.firebaseRoles.runViewer,
|
|
370
|
+
resourceManager_1.firebaseRoles.functionsDeveloper,
|
|
370
371
|
];
|
|
371
372
|
await (0, resourceManager_1.addServiceAccountToRoles)(options.projectId, accountId, requiredRoles);
|
|
372
373
|
const serviceAccountKey = await (0, iam_1.createServiceAccountKey)(options.projectId, accountId);
|
|
@@ -381,3 +382,7 @@ async function encryptServiceAccountJSON(serviceAccountJSON, key) {
|
|
|
381
382
|
const encryptedBytes = libsodium.crypto_box_seal(messageBytes, keyBytes);
|
|
382
383
|
return Buffer.from(encryptedBytes).toString("base64");
|
|
383
384
|
}
|
|
385
|
+
function isRunningInGithubAction() {
|
|
386
|
+
return process.env.GITHUB_ACTION_REPOSITORY === HOSTING_GITHUB_ACTION_NAME.split("@")[0];
|
|
387
|
+
}
|
|
388
|
+
exports.isRunningInGithubAction = isRunningInGithubAction;
|
|
@@ -9,6 +9,7 @@ const github_1 = require("./github");
|
|
|
9
9
|
const prompt_1 = require("../../../prompt");
|
|
10
10
|
const logger_1 = require("../../../logger");
|
|
11
11
|
const frameworks_1 = require("../../../frameworks");
|
|
12
|
+
const constants_1 = require("../../../frameworks/constants");
|
|
12
13
|
const experiments = require("../../../experiments");
|
|
13
14
|
const path_1 = require("path");
|
|
14
15
|
const INDEX_TEMPLATE = fs.readFileSync(__dirname + "/../../../../templates/init/hosting/index.html", "utf8");
|
|
@@ -90,8 +91,8 @@ async function doSetup(setup, config) {
|
|
|
90
91
|
name: "region",
|
|
91
92
|
type: "list",
|
|
92
93
|
message: "In which region would you like to host server-side content, if applicable?",
|
|
93
|
-
default:
|
|
94
|
-
choices:
|
|
94
|
+
default: constants_1.DEFAULT_REGION,
|
|
95
|
+
choices: constants_1.ALLOWED_SSR_REGIONS,
|
|
95
96
|
}, setup.hosting);
|
|
96
97
|
setup.config.hosting = {
|
|
97
98
|
source: setup.hosting.source,
|
package/lib/serve/index.js
CHANGED
|
@@ -14,7 +14,8 @@ const TARGETS = {
|
|
|
14
14
|
functions: new FunctionsServer(),
|
|
15
15
|
};
|
|
16
16
|
async function serve(options) {
|
|
17
|
-
|
|
17
|
+
options.targets || (options.targets = []);
|
|
18
|
+
const targetNames = options.targets;
|
|
18
19
|
options.port = parseInt(options.port, 10);
|
|
19
20
|
if (targetNames.includes("hosting") && config.extract(options).some((it) => it.source)) {
|
|
20
21
|
experiments.assertEnabled("webframeworks", "emulate a web framework");
|
package/lib/unzip.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createUnzipTransform = exports.unzip = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const zlib = require("zlib");
|
|
7
|
+
const stream_1 = require("stream");
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const error_1 = require("./error");
|
|
10
|
+
const stream_2 = require("stream");
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
const pipelineAsync = (0, util_1.promisify)(stream_2.pipeline);
|
|
13
|
+
const readUInt32LE = (buf, offset) => {
|
|
14
|
+
return ((buf[offset] | (buf[offset + 1] << 8) | (buf[offset + 2] << 16) | (buf[offset + 3] << 24)) >>> 0);
|
|
15
|
+
};
|
|
16
|
+
const findNextDataDescriptor = (data, offset) => {
|
|
17
|
+
const dataDescriptorSignature = 0x08074b50;
|
|
18
|
+
let position = offset;
|
|
19
|
+
while (position < data.length) {
|
|
20
|
+
const potentialDescriptor = data.slice(position, position + 16);
|
|
21
|
+
if (readUInt32LE(potentialDescriptor, 0) === dataDescriptorSignature) {
|
|
22
|
+
logger_1.logger.debug(`[unzip] found data descriptor signature @ ${position}`);
|
|
23
|
+
const compressedSize = readUInt32LE(potentialDescriptor, 8);
|
|
24
|
+
const uncompressedSize = readUInt32LE(potentialDescriptor, 12);
|
|
25
|
+
return [compressedSize, uncompressedSize];
|
|
26
|
+
}
|
|
27
|
+
position++;
|
|
28
|
+
}
|
|
29
|
+
throw new error_1.FirebaseError("Unable to find compressed and uncompressed size of file in ZIP archive.");
|
|
30
|
+
};
|
|
31
|
+
const extractEntriesFromBuffer = async (data, outputDir) => {
|
|
32
|
+
let position = 0;
|
|
33
|
+
logger_1.logger.debug(`Data is ${data.length}`);
|
|
34
|
+
while (position < data.length) {
|
|
35
|
+
const entryHeader = data.slice(position, position + 30);
|
|
36
|
+
const entry = {};
|
|
37
|
+
if (readUInt32LE(entryHeader, 0) !== 0x04034b50) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
entry.generalPurposeBitFlag = entryHeader.readUint16LE(6);
|
|
41
|
+
entry.compressedSize = readUInt32LE(entryHeader, 18);
|
|
42
|
+
entry.uncompressedSize = readUInt32LE(entryHeader, 22);
|
|
43
|
+
entry.fileNameLength = entryHeader.readUInt16LE(26);
|
|
44
|
+
entry.extraLength = entryHeader.readUInt16LE(28);
|
|
45
|
+
entry.fileName = data.toString("utf-8", position + 30, position + 30 + entry.fileNameLength);
|
|
46
|
+
entry.headerSize = 30 + entry.fileNameLength + entry.extraLength;
|
|
47
|
+
let dataDescriptorSize = 0;
|
|
48
|
+
if (entry.generalPurposeBitFlag === 8 &&
|
|
49
|
+
entry.compressedSize === 0 &&
|
|
50
|
+
entry.uncompressedSize === 0) {
|
|
51
|
+
const [compressedSize, uncompressedSize] = findNextDataDescriptor(data, position);
|
|
52
|
+
entry.compressedSize = compressedSize;
|
|
53
|
+
entry.uncompressedSize = uncompressedSize;
|
|
54
|
+
dataDescriptorSize = 16;
|
|
55
|
+
}
|
|
56
|
+
entry.compressedData = data.slice(position + entry.headerSize, position + entry.headerSize + entry.compressedSize);
|
|
57
|
+
logger_1.logger.debug(`[unzip] Entry: ${entry.fileName} (compressed_size=${entry.compressedSize} bytes, uncompressed_size=${entry.uncompressedSize} bytes)`);
|
|
58
|
+
entry.fileName = entry.fileName.replace(/\//g, path.sep);
|
|
59
|
+
const outputFilePath = path.normalize(path.join(outputDir, entry.fileName));
|
|
60
|
+
logger_1.logger.debug(`[unzip] Processing entry: ${entry.fileName}`);
|
|
61
|
+
if (entry.fileName.endsWith(path.sep)) {
|
|
62
|
+
logger_1.logger.debug(`[unzip] mkdir: ${outputFilePath}`);
|
|
63
|
+
await fs.promises.mkdir(outputFilePath, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const parentDir = outputFilePath.substring(0, outputFilePath.lastIndexOf(path.sep));
|
|
67
|
+
logger_1.logger.debug(`[unzip] else mkdir: ${parentDir}`);
|
|
68
|
+
await fs.promises.mkdir(parentDir, { recursive: true });
|
|
69
|
+
const compressionMethod = entryHeader.readUInt16LE(8);
|
|
70
|
+
if (compressionMethod === 0) {
|
|
71
|
+
logger_1.logger.debug(`[unzip] Writing file: ${outputFilePath}`);
|
|
72
|
+
await fs.promises.writeFile(outputFilePath, entry.compressedData);
|
|
73
|
+
}
|
|
74
|
+
else if (compressionMethod === 8) {
|
|
75
|
+
logger_1.logger.debug(`[unzip] deflating: ${outputFilePath}`);
|
|
76
|
+
await pipelineAsync(stream_1.Readable.from(entry.compressedData), zlib.createInflateRaw(), fs.createWriteStream(outputFilePath));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw new error_1.FirebaseError(`Unsupported compression method: ${compressionMethod}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
position += entry.headerSize + entry.compressedSize + dataDescriptorSize;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const unzip = async (inputPath, outputDir) => {
|
|
86
|
+
const data = await fs.promises.readFile(inputPath);
|
|
87
|
+
await extractEntriesFromBuffer(data, outputDir);
|
|
88
|
+
};
|
|
89
|
+
exports.unzip = unzip;
|
|
90
|
+
class UnzipTransform extends stream_1.Transform {
|
|
91
|
+
constructor(outputDir) {
|
|
92
|
+
super();
|
|
93
|
+
this.outputDir = outputDir;
|
|
94
|
+
this.chunks = [];
|
|
95
|
+
}
|
|
96
|
+
_transform(chunk, _, callback) {
|
|
97
|
+
this.chunks.push(chunk);
|
|
98
|
+
callback();
|
|
99
|
+
}
|
|
100
|
+
async _flush(callback) {
|
|
101
|
+
var _a, _b;
|
|
102
|
+
try {
|
|
103
|
+
await extractEntriesFromBuffer(Buffer.concat(this.chunks), this.outputDir);
|
|
104
|
+
callback();
|
|
105
|
+
(_a = this._resolve) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const firebaseError = new error_1.FirebaseError("Unable to unzip the target", {
|
|
109
|
+
children: [error],
|
|
110
|
+
original: error instanceof Error ? error : undefined,
|
|
111
|
+
});
|
|
112
|
+
callback(firebaseError);
|
|
113
|
+
(_b = this._reject) === null || _b === void 0 ? void 0 : _b.call(this, firebaseError);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async promise() {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
this._resolve = resolve;
|
|
119
|
+
this._reject = reject;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const createUnzipTransform = (outputDir) => {
|
|
124
|
+
return new UnzipTransform(outputDir);
|
|
125
|
+
};
|
|
126
|
+
exports.createUnzipTransform = createUnzipTransform;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
],
|
|
30
30
|
"preferGlobal": true,
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": "
|
|
32
|
+
"node": ">=16.13.0 || >=18.0.0"
|
|
33
33
|
},
|
|
34
34
|
"author": "Firebase (https://firebase.google.com/)",
|
|
35
35
|
"license": "MIT",
|
|
@@ -113,7 +113,6 @@
|
|
|
113
113
|
"tmp": "^0.2.1",
|
|
114
114
|
"triple-beam": "^1.3.0",
|
|
115
115
|
"universal-analytics": "^0.5.3",
|
|
116
|
-
"unzipper": "^0.10.10",
|
|
117
116
|
"update-notifier-cjs": "^5.1.6",
|
|
118
117
|
"uuid": "^8.3.2",
|
|
119
118
|
"winston": "^3.0.0",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"type": "string"
|
|
105
105
|
},
|
|
106
106
|
"timeoutSeconds": {
|
|
107
|
-
"description": "Timeout for the function in
|
|
107
|
+
"description": "Timeout for the function in seconds, possible values are 0 to 540.\nHTTPS functions can specify a higher timeout.",
|
|
108
108
|
"type": "number"
|
|
109
109
|
},
|
|
110
110
|
"vpcConnector": {
|