firebase-tools 11.29.0 → 11.30.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 (52) hide show
  1. package/lib/commands/database-import.js +2 -2
  2. package/lib/commands/hosting-channel-deploy.js +3 -0
  3. package/lib/database/import.js +113 -18
  4. package/lib/deploy/extensions/secrets.js +2 -2
  5. package/lib/deploy/functions/ensure.js +7 -1
  6. package/lib/deploy/functions/release/fabricator.js +2 -0
  7. package/lib/deploy/functions/runtimes/discovery/index.js +1 -1
  8. package/lib/deploy/functions/runtimes/index.js +11 -3
  9. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  10. package/lib/deploy/functions/runtimes/python/index.js +41 -13
  11. package/lib/deploy/hosting/convertConfig.js +8 -4
  12. package/lib/deploy/hosting/prepare.js +115 -2
  13. package/lib/deploy/index.js +24 -8
  14. package/lib/emulator/adminSdkConfig.js +8 -0
  15. package/lib/emulator/controller.js +7 -9
  16. package/lib/emulator/download.js +3 -12
  17. package/lib/emulator/downloadableEmulators.js +5 -5
  18. package/lib/emulator/functionsEmulator.js +57 -7
  19. package/lib/emulator/functionsEmulatorRuntime.js +4 -1
  20. package/lib/emulator/functionsEmulatorShared.js +1 -0
  21. package/lib/emulator/functionsRuntimeWorker.js +12 -4
  22. package/lib/experiments.js +20 -7
  23. package/lib/extensions/extensionsHelper.js +2 -2
  24. package/lib/frameworks/angular/index.js +13 -13
  25. package/lib/frameworks/astro/index.js +3 -4
  26. package/lib/frameworks/constants.js +42 -0
  27. package/lib/frameworks/express/index.js +3 -2
  28. package/lib/frameworks/flutter/index.js +39 -0
  29. package/lib/frameworks/flutter/utils.js +11 -0
  30. package/lib/frameworks/index.js +58 -131
  31. package/lib/frameworks/interfaces.js +2 -0
  32. package/lib/frameworks/next/constants.js +2 -1
  33. package/lib/frameworks/next/index.js +124 -87
  34. package/lib/frameworks/next/utils.js +71 -6
  35. package/lib/frameworks/nuxt/index.js +4 -5
  36. package/lib/frameworks/nuxt/utils.js +2 -2
  37. package/lib/frameworks/nuxt2/index.js +5 -5
  38. package/lib/frameworks/utils.js +101 -1
  39. package/lib/frameworks/vite/index.js +5 -6
  40. package/lib/functions/ensureTargeted.js +4 -4
  41. package/lib/functions/python.js +12 -5
  42. package/lib/gcp/resourceManager.js +1 -0
  43. package/lib/hosting/config.js +4 -8
  44. package/lib/init/features/functions/index.js +4 -7
  45. package/lib/init/features/hosting/github.js +7 -2
  46. package/lib/init/features/hosting/index.js +3 -2
  47. package/lib/serve/index.js +2 -1
  48. package/lib/unzip.js +126 -0
  49. package/npm-shrinkwrap.json +6 -287
  50. package/package.json +1 -2
  51. package/schema/firebase-config.json +1 -1
  52. package/templates/init/functions/python/requirements.txt +1 -1
@@ -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.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 child_process_1 = require("child_process");
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,95 @@ 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, child_process_1.spawnSync)("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, child_process_1.spawnSync)("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;
@@ -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, __1.findDependency)(npmDependency, { cwd: dir, depth: 0, omitDev: false });
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, __1.findDependency)("vite", { cwd: dir, depth, omitDev: false }))
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, __1.relativeRequire)(root, "vite");
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, __1.getNodeModuleBin)("vite", dir);
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, __1.relativeRequire)(root, "vite");
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, codebase, id) {
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:${codebase}`;
9
+ let newTarget = `functions:${codebaseOrFunction}`;
10
10
  if (parts.includes(newTarget)) {
11
11
  return only;
12
12
  }
13
- if (id) {
14
- newTarget = `${newTarget}:${id}`;
13
+ if (functionId) {
14
+ newTarget = `${newTarget}:${functionId}`;
15
15
  if (parts.includes(newTarget)) {
16
16
  return only;
17
17
  }
@@ -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
- const DEFAULT_VENV_DIR = "venv";
8
- function runWithVirtualEnv(commandAndArgs, cwd, envs, spawnOpts = {}, venvDir = DEFAULT_VENV_DIR) {
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
- const command = process.platform === "win32" ? venvActivate : "source";
12
- const args = [process.platform === "win32" ? "" : venvActivate, "&&", ...commandAndArgs];
11
+ return {
12
+ command: process.platform === "win32" ? venvActivate : "source",
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
  };
@@ -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 = experiments.isEnabled("webframeworks")
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"${orSource} directory when using "destination" rewrites.`);
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"${orSource} directory or at least one rewrite or redirect in each "hosting" config.`);
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"${orSource} directory when using "i18n" configuration.`);
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
- if ((0, experiments_1.isEnabled)("pythonfunctions")) {
144
- choices.push({
145
- name: "Python",
146
- value: "python",
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: frameworks_1.DEFAULT_REGION,
94
- choices: frameworks_1.ALLOWED_SSR_REGIONS,
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,
@@ -14,7 +14,8 @@ const TARGETS = {
14
14
  functions: new FunctionsServer(),
15
15
  };
16
16
  async function serve(options) {
17
- const targetNames = options.targets || [];
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;