w3wallets 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -17,13 +17,11 @@ Only the `Backpack` wallet is supported at this point.
17
17
 
18
18
  #### 1. Download Backpack
19
19
 
20
- Currently, you need to download the extension manually. You can use [Chrome extension source viewer](https://chromewebstore.google.com/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin).
21
-
22
- Put the unzipped files to the root of your Playwright project `wallets/backpack`.
20
+ ```sh
21
+ npx w3wallets backpack
22
+ ```
23
23
 
24
- <!-- ```sh
25
- npx w3wallets
26
- ``` -->
24
+ The unzipped files should be stored in the `wallets/backpack` directory. Add them to `.gitignore`.
27
25
 
28
26
  #### 2. Wrap your fixture `withWallets`
29
27
 
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as playwright_test from 'playwright/test';
2
2
  import { Page, test, BrowserContext } from '@playwright/test';
3
3
 
4
- type BackPackNetwork = "Eclipse";
4
+ type BackPackNetwork = "Eclipse" | "Ethereum";
5
5
 
6
6
  declare class Backpack {
7
7
  private page;
@@ -18,7 +18,7 @@ declare class Backpack {
18
18
  }
19
19
 
20
20
  type Config = {
21
- backpack: boolean;
21
+ backpack?: boolean;
22
22
  };
23
23
  declare function withWallets(test: typeof test, config: Config): playwright_test.TestType<playwright_test.PlaywrightTestArgs & playwright_test.PlaywrightTestOptions & {
24
24
  context: BrowserContext;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as playwright_test from 'playwright/test';
2
2
  import { Page, test, BrowserContext } from '@playwright/test';
3
3
 
4
- type BackPackNetwork = "Eclipse";
4
+ type BackPackNetwork = "Eclipse" | "Ethereum";
5
5
 
6
6
  declare class Backpack {
7
7
  private page;
@@ -18,7 +18,7 @@ declare class Backpack {
18
18
  }
19
19
 
20
20
  type Config = {
21
- backpack: boolean;
21
+ backpack?: boolean;
22
22
  };
23
23
  declare function withWallets(test: typeof test, config: Config): playwright_test.TestType<playwright_test.PlaywrightTestArgs & playwright_test.PlaywrightTestOptions & {
24
24
  context: BrowserContext;
package/dist/index.js CHANGED
@@ -91,35 +91,42 @@ var Backpack = class {
91
91
 
92
92
  // src/withWallets.ts
93
93
  function withWallets(test, config) {
94
- const backpack = import_path.default.join(process.cwd(), "wallets", "backpack");
94
+ const backpackPath = import_path.default.join(process.cwd(), "wallets", "backpack");
95
+ const metamaskPath = import_path.default.join(process.cwd(), "wallets", "metamask");
95
96
  return test.extend({
96
97
  backpack: async ({ context, extensionId }, use) => {
97
98
  const page = context.pages()[0];
98
99
  if (!page) throw Error("No pages in context");
99
- const backpack2 = new Backpack(page, extensionId);
100
+ const backpack = new Backpack(page, extensionId);
100
101
  await page.goto(
101
102
  `chrome-extension://${extensionId}/options.html?onboarding=true`
102
103
  );
103
- await use(backpack2);
104
+ await use(backpack);
104
105
  },
106
+ // Browser context fixture
105
107
  context: async ({}, use, testInfo) => {
106
108
  const userDataDir = import_path.default.join(
107
109
  process.cwd(),
108
110
  ".w3wallets",
109
111
  testInfo.testId
110
112
  );
111
- if (import_fs.default.existsSync(userDataDir))
113
+ if (import_fs.default.existsSync(userDataDir)) {
112
114
  import_fs.default.rmSync(userDataDir, { recursive: true });
113
- const backpackDownloaded = import_fs.default.existsSync(
114
- import_path.default.join(backpack, "manifest.json")
115
- );
116
- if (!backpackDownloaded)
117
- throw Error("Cannot find Backpack. download it `npx w3wallets`");
115
+ }
116
+ const extensionPaths = [];
117
+ if (config.backpack) {
118
+ if (!import_fs.default.existsSync(import_path.default.join(backpackPath, "manifest.json"))) {
119
+ throw Error(
120
+ "Cannot find Backpack. Please download it via `npx w3wallets`"
121
+ );
122
+ }
123
+ extensionPaths.push(backpackPath);
124
+ }
118
125
  const context = await import_test3.chromium.launchPersistentContext(userDataDir, {
119
126
  headless: false,
120
127
  args: [
121
- `--disable-extensions-except=${backpack}`,
122
- `--load-extension=${backpack}`
128
+ `--disable-extensions-except=${extensionPaths.join(",")}`,
129
+ `--load-extension=${extensionPaths.join(",")}`
123
130
  ]
124
131
  });
125
132
  await use(context);
@@ -127,7 +134,9 @@ function withWallets(test, config) {
127
134
  },
128
135
  extensionId: async ({ context }, use) => {
129
136
  let [background] = context.serviceWorkers();
130
- if (!background) background = await context.waitForEvent("serviceworker");
137
+ if (!background) {
138
+ background = await context.waitForEvent("serviceworker");
139
+ }
131
140
  const extensionId = background.url().split("/")[2];
132
141
  if (!extensionId) throw Error("No extension id");
133
142
  await use(extensionId);
package/dist/index.mjs CHANGED
@@ -55,35 +55,42 @@ var Backpack = class {
55
55
 
56
56
  // src/withWallets.ts
57
57
  function withWallets(test, config) {
58
- const backpack = path.join(process.cwd(), "wallets", "backpack");
58
+ const backpackPath = path.join(process.cwd(), "wallets", "backpack");
59
+ const metamaskPath = path.join(process.cwd(), "wallets", "metamask");
59
60
  return test.extend({
60
61
  backpack: async ({ context, extensionId }, use) => {
61
62
  const page = context.pages()[0];
62
63
  if (!page) throw Error("No pages in context");
63
- const backpack2 = new Backpack(page, extensionId);
64
+ const backpack = new Backpack(page, extensionId);
64
65
  await page.goto(
65
66
  `chrome-extension://${extensionId}/options.html?onboarding=true`
66
67
  );
67
- await use(backpack2);
68
+ await use(backpack);
68
69
  },
70
+ // Browser context fixture
69
71
  context: async ({}, use, testInfo) => {
70
72
  const userDataDir = path.join(
71
73
  process.cwd(),
72
74
  ".w3wallets",
73
75
  testInfo.testId
74
76
  );
75
- if (fs.existsSync(userDataDir))
77
+ if (fs.existsSync(userDataDir)) {
76
78
  fs.rmSync(userDataDir, { recursive: true });
77
- const backpackDownloaded = fs.existsSync(
78
- path.join(backpack, "manifest.json")
79
- );
80
- if (!backpackDownloaded)
81
- throw Error("Cannot find Backpack. download it `npx w3wallets`");
79
+ }
80
+ const extensionPaths = [];
81
+ if (config.backpack) {
82
+ if (!fs.existsSync(path.join(backpackPath, "manifest.json"))) {
83
+ throw Error(
84
+ "Cannot find Backpack. Please download it via `npx w3wallets`"
85
+ );
86
+ }
87
+ extensionPaths.push(backpackPath);
88
+ }
82
89
  const context = await chromium.launchPersistentContext(userDataDir, {
83
90
  headless: false,
84
91
  args: [
85
- `--disable-extensions-except=${backpack}`,
86
- `--load-extension=${backpack}`
92
+ `--disable-extensions-except=${extensionPaths.join(",")}`,
93
+ `--load-extension=${extensionPaths.join(",")}`
87
94
  ]
88
95
  });
89
96
  await use(context);
@@ -91,7 +98,9 @@ function withWallets(test, config) {
91
98
  },
92
99
  extensionId: async ({ context }, use) => {
93
100
  let [background] = context.serviceWorkers();
94
- if (!background) background = await context.waitForEvent("serviceworker");
101
+ if (!background) {
102
+ background = await context.waitForEvent("serviceworker");
103
+ }
95
104
  const extensionId = background.url().split("/")[2];
96
105
  if (!extensionId) throw Error("No extension id");
97
106
  await use(extensionId);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "w3wallets",
3
3
  "description": "browser wallets for playwright",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "homepage": "https://github.com/Maksandre/w3wallets",
@@ -33,8 +33,8 @@
33
33
  "local-release": "changeset version && changeset publish"
34
34
  },
35
35
  "dependencies": {
36
- "adm-zip": "^0.5.16",
37
- "follow-redirects": "^1.15.9"
36
+ "follow-redirects": "^1.15.9",
37
+ "unzip-crx": "^0.2.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@arethetypeswrong/cli": "^0.17.2",
@@ -49,4 +49,4 @@
49
49
  "peerDependencies": {
50
50
  "@playwright/test": "^1.49.1"
51
51
  }
52
- }
52
+ }
@@ -1,79 +1,306 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /**
4
+ *
5
+ * Downloads and extracts Chrome extensions by alias ("backpack" and "metamask")
6
+ *
7
+ * Usage:
8
+ * npx w3wallets backpack
9
+ * npx w3wallets metamask
10
+ * npx w3wallets backpack metamask
11
+ */
12
+
3
13
  const fs = require("fs");
14
+ const https = require("https");
4
15
  const path = require("path");
5
- const { https } = require("follow-redirects");
6
- const AdmZip = require("adm-zip");
7
-
8
- const args = process.argv.slice(2);
9
-
10
- // Default configurations
11
- const DEFAULT_WALLET = "backpack";
12
- const DEFAULT_DOWNLOAD_LINK =
13
- "https://github.com/coral-xyz/backpack/releases/download/0.10.1-latest-4/build-beta-4.zip";
14
-
15
- // Parse arguments
16
- const walletArg = args.find((arg) => arg.startsWith("--wallet="));
17
- const downloadArg = args.find((arg) => arg.startsWith("--version="));
18
-
19
- const wallet = walletArg ? walletArg.split("=")[1] : DEFAULT_WALLET;
20
- const downloadLink = downloadArg
21
- ? downloadArg.split("=")[1]
22
- : DEFAULT_DOWNLOAD_LINK;
23
- const outputDir = path.resolve(`extensions/${wallet}`);
24
- const zipPath = path.resolve(outputDir, `${wallet}.zip`);
25
-
26
- console.log(`Fetching ${wallet}...`);
27
-
28
- // Ensure the output directory exists
29
- fs.mkdirSync(outputDir, { recursive: true });
30
-
31
- // Download the zip file
32
- https
33
- .get(downloadLink, (response) => {
34
- if (response.statusCode !== 200) {
35
- console.error(
36
- `Failed to download file. Status Code: ${response.statusCode}`,
37
- );
38
- return;
16
+ const url = require("url");
17
+ const zlib = require("zlib");
18
+
19
+ // ---------------------------------------------------------------------
20
+ // 1. Known aliases -> extension IDs
21
+ // ---------------------------------------------------------------------
22
+ const ALIASES = {
23
+ backpack: "aflkmfhebedbjioipglgcbcmnbpgliof",
24
+ metamask: "nkbihfbeogaeaoehlefnkodbefgpgknn",
25
+ };
26
+
27
+ // ---------------------------------------------------------------------
28
+ // 2. Read aliases from CLI
29
+ // ---------------------------------------------------------------------
30
+ const inputAliases = process.argv.slice(2);
31
+
32
+ if (!inputAliases.length) {
33
+ console.error("Usage: npx w3wallets <aliases...>");
34
+ console.error("Available aliases:", Object.keys(ALIASES).join(", "));
35
+ process.exit(1);
36
+ }
37
+
38
+ for (const alias of inputAliases) {
39
+ if (!ALIASES[alias]) {
40
+ console.error(
41
+ `Unknown alias "${alias}". Must be one of: ${Object.keys(ALIASES).join(", ")}`,
42
+ );
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ // ---------------------------------------------------------------------
48
+ // 3. Main: download and extract each requested alias
49
+ // ---------------------------------------------------------------------
50
+ (async function main() {
51
+ for (const alias of inputAliases) {
52
+ const extensionId = ALIASES[alias];
53
+ console.log(`\n=== Processing alias: "${alias}" (ID: ${extensionId}) ===`);
54
+
55
+ try {
56
+ // 1) Download CRX
57
+ const crxBuffer = await downloadCrx(extensionId);
58
+ console.log(`Got CRX data for "${alias}"! ${crxBuffer.length} bytes`);
59
+
60
+ // 2) Save raw CRX to disk
61
+ const outDir = path.join("wallets", alias);
62
+ fs.mkdirSync(outDir, { recursive: true });
63
+
64
+ const debugPath = path.join(outDir, `debug-${alias}.crx`);
65
+ fs.writeFileSync(debugPath, crxBuffer);
66
+ console.log(`Saved ${debugPath}`);
67
+
68
+ // 3) Extract CRX into "wallets/<alias>"
69
+ extractCrxToFolder(crxBuffer, outDir);
70
+ console.log(`Extraction complete! See folder: ${outDir}`);
71
+ } catch (err) {
72
+ console.error(`Failed to process "${alias}":`, err.message);
73
+ process.exit(1);
74
+ }
75
+ }
76
+ })();
77
+
78
+ // ---------------------------------------------------------------------
79
+ // downloadCrx: Build CRX URL and fetch it
80
+ // ---------------------------------------------------------------------
81
+ async function downloadCrx(extensionId) {
82
+ const downloadUrl =
83
+ "https://clients2.google.com/service/update2/crx" +
84
+ "?response=redirect" +
85
+ "&prod=chrome" +
86
+ "&prodversion=9999" +
87
+ "&acceptformat=crx2,crx3" +
88
+ `&x=id%3D${extensionId}%26uc`;
89
+
90
+ console.log("Requesting:", downloadUrl);
91
+
92
+ const crxBuffer = await fetchUrl(downloadUrl);
93
+ return crxBuffer;
94
+ }
95
+
96
+ // ---------------------------------------------------------------------
97
+ // fetchUrl: minimal GET + redirect handling
98
+ // ---------------------------------------------------------------------
99
+ function fetchUrl(
100
+ targetUrl,
101
+ options = {},
102
+ redirectCount = 0,
103
+ maxRedirects = 10,
104
+ ) {
105
+ return new Promise((resolve, reject) => {
106
+ if (redirectCount > maxRedirects) {
107
+ return reject(new Error("Too many redirects"));
39
108
  }
40
109
 
41
- const fileStream = fs.createWriteStream(zipPath);
42
- response.pipe(fileStream);
43
-
44
- fileStream.on("finish", () => {
45
- fileStream.close();
46
- console.log(`Downloaded to ${zipPath}`);
47
-
48
- // Unzip the file
49
- console.log("Unzipping...");
50
- const zip = new AdmZip(zipPath);
51
- zip.extractAllTo(outputDir, true);
52
- console.log(`Extracted to ${outputDir}`);
53
-
54
- fs.unlinkSync(zipPath);
55
-
56
- // Check if the result is a single directory with manifest.json inside
57
- const files = fs.readdirSync(outputDir);
58
- if (files.length === 1) {
59
- const singleDirPath = path.join(outputDir, files[0]);
60
- if (
61
- fs.lstatSync(singleDirPath).isDirectory() &&
62
- fs.existsSync(path.join(singleDirPath, "manifest.json"))
63
- ) {
64
- // Move all files from the directory to the outputDir
65
- const nestedFiles = fs.readdirSync(singleDirPath);
66
- nestedFiles.forEach((file) => {
67
- const srcPath = path.join(singleDirPath, file);
68
- const destPath = path.join(outputDir, file);
69
- fs.renameSync(srcPath, destPath);
70
- });
71
- } else {
72
- throw Error("Cannot find the manifest.json file");
73
- }
110
+ const req = https.get(targetUrl, options, (res) => {
111
+ const { statusCode, headers } = res;
112
+
113
+ // Follow redirects
114
+ if ([301, 302, 303, 307, 308].includes(statusCode) && headers.location) {
115
+ const newUrl = url.resolve(targetUrl, headers.location);
116
+ res.resume(); // discard body
117
+ return resolve(
118
+ fetchUrl(newUrl, options, redirectCount + 1, maxRedirects),
119
+ );
120
+ }
121
+
122
+ if (statusCode !== 200) {
123
+ res.resume();
124
+ return reject(
125
+ new Error(`Request failed with status code ${statusCode}`),
126
+ );
74
127
  }
128
+
129
+ const dataChunks = [];
130
+ res.on("data", (chunk) => dataChunks.push(chunk));
131
+ res.on("end", () => resolve(Buffer.concat(dataChunks)));
75
132
  });
76
- })
77
- .on("error", (err) => {
78
- console.error(`Error downloading the file: ${err.message}`);
133
+
134
+ req.on("error", (err) => reject(err));
79
135
  });
136
+ }
137
+
138
+ // ---------------------------------------------------------------------
139
+ // extractCrxToFolder
140
+ // 1) Checks "Cr24" magic
141
+ // 2) Reads version (2 or 3/4) to find the ZIP start
142
+ // 3) Uses parseZipCentralDirectory() to extract files properly
143
+ // ---------------------------------------------------------------------
144
+ function extractCrxToFolder(crxBuffer, outFolder) {
145
+ if (crxBuffer.toString("utf8", 0, 4) !== "Cr24") {
146
+ throw new Error("Not a valid CRX file (missing Cr24 magic).");
147
+ }
148
+
149
+ const version = crxBuffer.readUInt32LE(4);
150
+ let zipStartOffset = 0;
151
+ if (version === 2) {
152
+ const pkLen = crxBuffer.readUInt32LE(8);
153
+ const sigLen = crxBuffer.readUInt32LE(12);
154
+ zipStartOffset = 16 + pkLen + sigLen;
155
+ } else if (version === 3 || version === 4) {
156
+ const headerSize = crxBuffer.readUInt32LE(8);
157
+ zipStartOffset = 12 + headerSize;
158
+ } else {
159
+ throw new Error(
160
+ `Unsupported CRX version (${version}). Only v2, v3, or v4 are supported.`,
161
+ );
162
+ }
163
+
164
+ if (zipStartOffset >= crxBuffer.length) {
165
+ throw new Error("Malformed CRX: header size exceeds file length.");
166
+ }
167
+
168
+ const zipBuffer = crxBuffer.slice(zipStartOffset);
169
+
170
+ // Parse that ZIP via the central directory approach
171
+ parseZipCentralDirectory(zipBuffer, outFolder);
172
+ }
173
+
174
+ // ---------------------------------------------------------------------
175
+ // parseZipCentralDirectory(buffer, outFolder)
176
+ // 1) Finds End of Central Directory (EOCD) record (0x06054b50).
177
+ // 2) Reads central directory for file metadata
178
+ // 3) For each file, decompress into outFolder
179
+ // ---------------------------------------------------------------------
180
+ function parseZipCentralDirectory(zipBuffer, outFolder) {
181
+ const eocdSig = 0x06054b50;
182
+ let eocdPos = -1;
183
+ const minPos = Math.max(0, zipBuffer.length - 65557);
184
+ for (let i = zipBuffer.length - 4; i >= minPos; i--) {
185
+ if (zipBuffer.readUInt32LE(i) === eocdSig) {
186
+ eocdPos = i;
187
+ break;
188
+ }
189
+ }
190
+ if (eocdPos < 0) {
191
+ throw new Error("Could not find End of Central Directory (EOCD) in ZIP.");
192
+ }
193
+
194
+ const totalCD = zipBuffer.readUInt16LE(eocdPos + 10);
195
+ const cdSize = zipBuffer.readUInt32LE(eocdPos + 12);
196
+ const cdOffset = zipBuffer.readUInt32LE(eocdPos + 16);
197
+
198
+ if (cdOffset + cdSize > zipBuffer.length) {
199
+ throw new Error("Central directory offset/size out of range.");
200
+ }
201
+
202
+ let ptr = cdOffset;
203
+ const files = [];
204
+ for (let i = 0; i < totalCD; i++) {
205
+ const sig = zipBuffer.readUInt32LE(ptr);
206
+ if (sig !== 0x02014b50) {
207
+ throw new Error(`Central directory signature mismatch at ${ptr}`);
208
+ }
209
+ ptr += 4;
210
+
211
+ /* const verMade = */ zipBuffer.readUInt16LE(ptr);
212
+ ptr += 2;
213
+ const verNeed = zipBuffer.readUInt16LE(ptr);
214
+ ptr += 2;
215
+ const flags = zipBuffer.readUInt16LE(ptr);
216
+ ptr += 2;
217
+ const method = zipBuffer.readUInt16LE(ptr);
218
+ ptr += 2;
219
+ /* const modTime = */ zipBuffer.readUInt16LE(ptr);
220
+ ptr += 2;
221
+ /* const modDate = */ zipBuffer.readUInt16LE(ptr);
222
+ ptr += 2;
223
+ const crc32 = zipBuffer.readUInt32LE(ptr);
224
+ ptr += 4;
225
+ const compSize = zipBuffer.readUInt32LE(ptr);
226
+ ptr += 4;
227
+ const unCompSize = zipBuffer.readUInt32LE(ptr);
228
+ ptr += 4;
229
+ const fLen = zipBuffer.readUInt16LE(ptr);
230
+ ptr += 2;
231
+ const xLen = zipBuffer.readUInt16LE(ptr);
232
+ ptr += 2;
233
+ const cLen = zipBuffer.readUInt16LE(ptr);
234
+ ptr += 2;
235
+ /* const diskNo = */ zipBuffer.readUInt16LE(ptr);
236
+ ptr += 2;
237
+ /* const intAttr = */ zipBuffer.readUInt16LE(ptr);
238
+ ptr += 2;
239
+ /* const extAttr = */ zipBuffer.readUInt32LE(ptr);
240
+ ptr += 4;
241
+ const localHeaderOffset = zipBuffer.readUInt32LE(ptr);
242
+ ptr += 4;
243
+
244
+ const filename = zipBuffer.toString("utf8", ptr, ptr + fLen);
245
+ ptr += fLen + xLen + cLen; // skip the extra + comment
246
+
247
+ files.push({
248
+ filename,
249
+ method,
250
+ compSize,
251
+ unCompSize,
252
+ flags,
253
+ localHeaderOffset,
254
+ crc32,
255
+ verNeed,
256
+ });
257
+ }
258
+
259
+ fs.mkdirSync(outFolder, { recursive: true });
260
+
261
+ for (const file of files) {
262
+ const { filename, method, compSize, localHeaderOffset } = file;
263
+
264
+ if (filename.endsWith("/")) {
265
+ fs.mkdirSync(path.join(outFolder, filename), { recursive: true });
266
+ continue;
267
+ }
268
+
269
+ let lhPtr = localHeaderOffset;
270
+ const localSig = zipBuffer.readUInt32LE(lhPtr);
271
+ if (localSig !== 0x04034b50) {
272
+ throw new Error(`Local file header mismatch at ${lhPtr} for ${filename}`);
273
+ }
274
+ lhPtr += 4;
275
+
276
+ lhPtr += 2; // version needed
277
+ lhPtr += 2; // flags
278
+ lhPtr += 2; // method
279
+ lhPtr += 2; // mod time
280
+ lhPtr += 2; // mod date
281
+ lhPtr += 4; // crc32
282
+ lhPtr += 4; // comp size
283
+ lhPtr += 4; // uncomp size
284
+ const lhFNameLen = zipBuffer.readUInt16LE(lhPtr);
285
+ lhPtr += 2;
286
+ const lhXLen = zipBuffer.readUInt16LE(lhPtr);
287
+ lhPtr += 2;
288
+
289
+ lhPtr += lhFNameLen + lhXLen;
290
+ const fileData = zipBuffer.slice(lhPtr, lhPtr + compSize);
291
+
292
+ const outPath = path.join(outFolder, filename);
293
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
294
+
295
+ if (method === 0) {
296
+ fs.writeFileSync(outPath, fileData);
297
+ } else if (method === 8) {
298
+ const unzipped = zlib.inflateRawSync(fileData);
299
+ fs.writeFileSync(outPath, unzipped);
300
+ } else {
301
+ throw new Error(
302
+ `Unsupported compression method (${method}) for file ${filename}`,
303
+ );
304
+ }
305
+ }
306
+ }