extension-from-store 0.2.0 → 0.2.2
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 +9 -1
- package/bin.cjs +66 -59
- package/bin.js +65 -59
- package/dist/588.js +211 -0
- package/dist/browser.cjs +21 -29
- package/dist/browser.js +14 -226
- package/dist/core.cjs +29 -36
- package/dist/core.d.ts +5 -5
- package/dist/core.js +1 -222
- package/dist/index.cjs +19 -26
- package/dist/index.js +42 -257
- package/dist/resolve.d.ts +2 -2
- package/package.json +27 -11
package/dist/index.js
CHANGED
|
@@ -1,91 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
if (key in obj) Object.defineProperty(obj, key, {
|
|
12
|
-
value: value,
|
|
13
|
-
enumerable: true,
|
|
14
|
-
configurable: true,
|
|
15
|
-
writable: true
|
|
16
|
-
});
|
|
17
|
-
else obj[key] = value;
|
|
18
|
-
return obj;
|
|
19
|
-
}
|
|
20
|
-
class errors_extensionFromStoreError extends Error {
|
|
21
|
-
constructor(code, message, cause){
|
|
22
|
-
super(message), _define_property(this, "code", void 0), _define_property(this, "cause", void 0);
|
|
23
|
-
this.code = code;
|
|
24
|
-
this.cause = cause;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function readUInt32LE(bytes, offset) {
|
|
28
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset + offset, Uint32Array.BYTES_PER_ELEMENT);
|
|
29
|
-
return view.getUint32(0, true);
|
|
30
|
-
}
|
|
31
|
-
function readMagic(bytes) {
|
|
32
|
-
let out = '';
|
|
33
|
-
const slice = bytes.subarray(0, 4);
|
|
34
|
-
for(let index = 0; index < slice.length; index += 1)out += String.fromCharCode(slice[index]);
|
|
35
|
-
return out;
|
|
36
|
-
}
|
|
37
|
-
function stripCrxHeader(buffer) {
|
|
38
|
-
if (buffer.length < 16) throw new errors_extensionFromStoreError('ExtractionFailed', 'CRX file too small');
|
|
39
|
-
const magic = readMagic(buffer);
|
|
40
|
-
if ('Cr24' !== magic) throw new errors_extensionFromStoreError('ExtractionFailed', 'Invalid CRX header');
|
|
41
|
-
const version = readUInt32LE(buffer, 4);
|
|
42
|
-
if (2 === version) {
|
|
43
|
-
const publicKeyLength = readUInt32LE(buffer, 8);
|
|
44
|
-
const signatureLength = readUInt32LE(buffer, 12);
|
|
45
|
-
const headerSize = 16 + publicKeyLength + signatureLength;
|
|
46
|
-
return buffer.subarray(headerSize);
|
|
47
|
-
}
|
|
48
|
-
if (3 === version) {
|
|
49
|
-
const headerSize = readUInt32LE(buffer, 8);
|
|
50
|
-
return buffer.subarray(12 + headerSize);
|
|
51
|
-
}
|
|
52
|
-
throw new errors_extensionFromStoreError('ExtractionFailed', `Unsupported CRX version ${version}`);
|
|
53
|
-
}
|
|
1
|
+
import promises from "node:fs/promises";
|
|
2
|
+
import node_os from "node:os";
|
|
3
|
+
import node_path from "node:path";
|
|
4
|
+
import extract_zip from "extract-zip";
|
|
5
|
+
import node_fs from "node:fs";
|
|
6
|
+
import node_http from "node:http";
|
|
7
|
+
import node_https from "node:https";
|
|
8
|
+
import { pipeline } from "node:stream/promises";
|
|
9
|
+
import { URL } from "node:url";
|
|
10
|
+
import { createLogger, stripCrxHeader, extensionFromStoreError as errors_extensionFromStoreError, resolveDownload, sanitizeSegment, validateInput, parseManifestInfo } from "./588.js";
|
|
54
11
|
async function extractCrx(crxPath, extractDir, workDir) {
|
|
55
|
-
const crxBuffer = await
|
|
12
|
+
const crxBuffer = await promises.readFile(crxPath);
|
|
56
13
|
const zipBuffer = Buffer.from(stripCrxHeader(crxBuffer));
|
|
57
|
-
const zipPath =
|
|
58
|
-
await
|
|
59
|
-
await (
|
|
14
|
+
const zipPath = node_path.join(workDir, 'payload.zip');
|
|
15
|
+
await promises.writeFile(zipPath, zipBuffer);
|
|
16
|
+
await extract_zip(zipPath, {
|
|
60
17
|
dir: extractDir
|
|
61
18
|
});
|
|
62
|
-
await
|
|
19
|
+
await promises.unlink(zipPath).catch(()=>void 0);
|
|
63
20
|
}
|
|
64
21
|
async function extractZipArchive(zipPath, extractDir) {
|
|
65
|
-
await (
|
|
22
|
+
await extract_zip(zipPath, {
|
|
66
23
|
dir: extractDir
|
|
67
24
|
});
|
|
68
25
|
}
|
|
69
|
-
function createLogger(logger) {
|
|
70
|
-
return {
|
|
71
|
-
info: (message)=>{
|
|
72
|
-
var _logger_onInfo;
|
|
73
|
-
return null == logger ? void 0 : null == (_logger_onInfo = logger.onInfo) ? void 0 : _logger_onInfo.call(logger, message);
|
|
74
|
-
},
|
|
75
|
-
warn: (message)=>{
|
|
76
|
-
var _logger_onWarn;
|
|
77
|
-
return null == logger ? void 0 : null == (_logger_onWarn = logger.onWarn) ? void 0 : _logger_onWarn.call(logger, message);
|
|
78
|
-
},
|
|
79
|
-
error: (message, error)=>{
|
|
80
|
-
var _logger_onError;
|
|
81
|
-
return null == logger ? void 0 : null == (_logger_onError = logger.onError) ? void 0 : _logger_onError.call(logger, message, error);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
26
|
const DEFAULT_USER_AGENT = 'extension-from-store';
|
|
86
27
|
function request(url, options) {
|
|
87
|
-
const target = new
|
|
88
|
-
const client = 'http:' === target.protocol ?
|
|
28
|
+
const target = new URL(url);
|
|
29
|
+
const client = 'http:' === target.protocol ? node_http : node_https;
|
|
89
30
|
const headers = {
|
|
90
31
|
'user-agent': options.userAgent || DEFAULT_USER_AGENT
|
|
91
32
|
};
|
|
@@ -110,7 +51,7 @@ function request(url, options) {
|
|
|
110
51
|
});
|
|
111
52
|
}
|
|
112
53
|
function resolveRedirectUrl(base, location) {
|
|
113
|
-
return new
|
|
54
|
+
return new URL(location, base).toString();
|
|
114
55
|
}
|
|
115
56
|
async function downloadToFile(url, filePath, options, maxRedirects = 5) {
|
|
116
57
|
const log = createLogger(options.logger);
|
|
@@ -123,10 +64,10 @@ async function downloadToFile(url, filePath, options, maxRedirects = 5) {
|
|
|
123
64
|
}
|
|
124
65
|
if (statusCode < 200 || statusCode >= 300) {
|
|
125
66
|
if (404 === statusCode) throw new errors_extensionFromStoreError('NotFound', `Extension not found at ${url}`);
|
|
126
|
-
if (401 === statusCode || 403 === statusCode) throw new errors_extensionFromStoreError('NotPublic',
|
|
67
|
+
if (401 === statusCode || 403 === statusCode) throw new errors_extensionFromStoreError('NotPublic', 'Extension is not publicly downloadable');
|
|
127
68
|
throw new errors_extensionFromStoreError('DownloadFailed', `Failed to download ${url} (HTTP ${statusCode})`);
|
|
128
69
|
}
|
|
129
|
-
await
|
|
70
|
+
await pipeline(stream, node_fs.createWriteStream(filePath));
|
|
130
71
|
}
|
|
131
72
|
async function requestJson(url, options, maxRedirects = 5) {
|
|
132
73
|
const { statusCode, headers, stream } = await request(url, options);
|
|
@@ -148,29 +89,10 @@ async function requestJson(url, options, maxRedirects = 5) {
|
|
|
148
89
|
throw new errors_extensionFromStoreError('StoreIncompatibility', `Invalid JSON response from ${url}`, error);
|
|
149
90
|
}
|
|
150
91
|
}
|
|
151
|
-
function parseManifestInfo(raw) {
|
|
152
|
-
let parsed;
|
|
153
|
-
try {
|
|
154
|
-
parsed = JSON.parse(raw);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
throw new errors_extensionFromStoreError('ExtractionFailed', 'manifest.json is not valid JSON', error);
|
|
157
|
-
}
|
|
158
|
-
if (!parsed || 'object' != typeof parsed || Array.isArray(parsed)) throw new errors_extensionFromStoreError('ExtractionFailed', 'manifest.json must contain an object');
|
|
159
|
-
const manifest = parsed;
|
|
160
|
-
const manifestVersion = manifest.manifest_version;
|
|
161
|
-
const extensionVersion = manifest.version;
|
|
162
|
-
if (2 !== manifestVersion && 3 !== manifestVersion) throw new errors_extensionFromStoreError('ExtractionFailed', 'manifest_version must be 2 or 3');
|
|
163
|
-
if (!extensionVersion || 'string' != typeof extensionVersion) throw new errors_extensionFromStoreError('ExtractionFailed', 'manifest.json is missing a version');
|
|
164
|
-
return {
|
|
165
|
-
manifest,
|
|
166
|
-
manifestVersion,
|
|
167
|
-
extensionVersion
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
92
|
async function readManifestInfo(manifestPath) {
|
|
171
93
|
let raw = '';
|
|
172
94
|
try {
|
|
173
|
-
raw = await
|
|
95
|
+
raw = await promises.readFile(manifestPath, 'utf8');
|
|
174
96
|
} catch (error) {
|
|
175
97
|
throw new errors_extensionFromStoreError('ExtractionFailed', 'manifest.json was not found after extraction', error);
|
|
176
98
|
}
|
|
@@ -187,155 +109,17 @@ function getNodeChromePlatformInfo() {
|
|
|
187
109
|
arch: getNodeArch()
|
|
188
110
|
};
|
|
189
111
|
}
|
|
190
|
-
function normalizeChromePlatformInfo(platform) {
|
|
191
|
-
return {
|
|
192
|
-
os: platform.os,
|
|
193
|
-
arch: platform.arch,
|
|
194
|
-
naclArch: platform.naclArch || platform.arch
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
const DEFAULT_CHROME_PLATFORM = {
|
|
198
|
-
os: 'linux',
|
|
199
|
-
arch: 'x64'
|
|
200
|
-
};
|
|
201
|
-
function getChromeDownloadUrl(id, platformInfo = DEFAULT_CHROME_PLATFORM) {
|
|
202
|
-
const encoded = encodeURIComponent(id);
|
|
203
|
-
const platform = normalizeChromePlatformInfo(platformInfo);
|
|
204
|
-
const productId = 'chromiumcrx';
|
|
205
|
-
const productChannel = 'unknown';
|
|
206
|
-
const productVersion = '9999.0.9999.0';
|
|
207
|
-
return [
|
|
208
|
-
'https://clients2.google.com/service/update2/crx',
|
|
209
|
-
'?response=redirect',
|
|
210
|
-
`&os=${platform.os}`,
|
|
211
|
-
`&arch=${platform.arch}`,
|
|
212
|
-
`&os_arch=${platform.arch}`,
|
|
213
|
-
`&nacl_arch=${platform.naclArch}`,
|
|
214
|
-
`&prod=${productId}`,
|
|
215
|
-
`&prodchannel=${productChannel}`,
|
|
216
|
-
`&prodversion=${productVersion}`,
|
|
217
|
-
'&acceptformat=crx2,crx3',
|
|
218
|
-
`&x=id%3D${encoded}%26uc`
|
|
219
|
-
].join('');
|
|
220
|
-
}
|
|
221
|
-
function getEdgeDownloadUrl(id) {
|
|
222
|
-
const encoded = encodeURIComponent(id);
|
|
223
|
-
return [
|
|
224
|
-
'https://edge.microsoft.com/extensionwebstorebase/v1/crx',
|
|
225
|
-
'?response=redirect',
|
|
226
|
-
'&prodversion=109.0.0.0',
|
|
227
|
-
`&x=id%3D${encoded}%26installsource%3Dondemand%26uc`
|
|
228
|
-
].join('');
|
|
229
|
-
}
|
|
230
|
-
async function resolveFirefoxDownload(idOrSlug, versionHint, options) {
|
|
231
|
-
var _addon_current_version_file, _addon_current_version, _addon_current_version1;
|
|
232
|
-
const baseUrl = `https://addons.mozilla.org/api/v5/addons/addon/${encodeURIComponent(idOrSlug)}/`;
|
|
233
|
-
const addon = await options.requestJson(baseUrl, options);
|
|
234
|
-
const slugOrId = addon.slug || idOrSlug;
|
|
235
|
-
if (versionHint) {
|
|
236
|
-
var _version_file;
|
|
237
|
-
const versionUrl = `${baseUrl}versions/${encodeURIComponent(versionHint)}/`;
|
|
238
|
-
const version = await options.requestJson(versionUrl, options);
|
|
239
|
-
const downloadUrl = null == (_version_file = version.file) ? void 0 : _version_file.url;
|
|
240
|
-
if (!downloadUrl) throw new errors_extensionFromStoreError('NotPublic', `Version ${versionHint} is not publicly downloadable`);
|
|
241
|
-
return {
|
|
242
|
-
downloadUrl,
|
|
243
|
-
version: version.version || versionHint,
|
|
244
|
-
slugOrId
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
const downloadUrl = null == (_addon_current_version = addon.current_version) ? void 0 : null == (_addon_current_version_file = _addon_current_version.file) ? void 0 : _addon_current_version_file.url;
|
|
248
|
-
const version = null == (_addon_current_version1 = addon.current_version) ? void 0 : _addon_current_version1.version;
|
|
249
|
-
if (!downloadUrl || !version) throw new errors_extensionFromStoreError('NotPublic', 'Extension is not publicly downloadable');
|
|
250
|
-
return {
|
|
251
|
-
downloadUrl,
|
|
252
|
-
version,
|
|
253
|
-
slugOrId
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
const chromePattern = /^https?:\/\/(?:chrome\.google\.com\/webstore|chromewebstore\.google\.com)\/.+?\/([a-p]{32})(?=[\/#?]|$)/i;
|
|
257
|
-
const chromeDownloadPattern = /^https?:\/\/clients2\.google\.com\/service\/update2\/crx\b.*?%3D([a-p]{32})%26uc/i;
|
|
258
|
-
const edgePattern = /^https?:\/\/microsoftedge\.microsoft\.com\/addons\/.+?\/([a-z]{32})(?=[\/#?]|$)/i;
|
|
259
|
-
const edgeDownloadPattern = /^https?:\/\/edge\.microsoft\.com\/extensionwebstorebase\/v1\/crx\b.*?%3D([a-z]{32})%26/i;
|
|
260
|
-
const firefoxPattern = /^https?:\/\/((?:reviewers\.)?(?:addons\.mozilla\.org|addons(?:-dev)?\.allizom\.org))\/.*?(?:addon|review)\/([^/<>"'?#]+)/i;
|
|
261
|
-
const firefoxDownloadPattern = /^https?:\/\/(addons\.mozilla\.org|addons(?:-dev)?\.allizom\.org)\/[^?#]*\/downloads\/latest\/([^/?#]+)/i;
|
|
262
|
-
function detectStoreFromUrl(url) {
|
|
263
|
-
if (chromePattern.test(url) || chromeDownloadPattern.test(url)) return 'chrome';
|
|
264
|
-
if (edgePattern.test(url) || edgeDownloadPattern.test(url)) return 'edge';
|
|
265
|
-
if (firefoxPattern.test(url) || firefoxDownloadPattern.test(url)) return 'firefox';
|
|
266
|
-
return null;
|
|
267
|
-
}
|
|
268
|
-
function extractChromeIdFromUrl(url) {
|
|
269
|
-
const match = chromePattern.exec(url) || chromeDownloadPattern.exec(url);
|
|
270
|
-
return match ? match[1] : null;
|
|
271
|
-
}
|
|
272
|
-
function extractEdgeIdFromUrl(url) {
|
|
273
|
-
const match = edgePattern.exec(url) || edgeDownloadPattern.exec(url);
|
|
274
|
-
return match ? match[1] : null;
|
|
275
|
-
}
|
|
276
|
-
function extractFirefoxSlugFromUrl(url) {
|
|
277
|
-
const match = firefoxPattern.exec(url) || firefoxDownloadPattern.exec(url);
|
|
278
|
-
return match ? match[2] : null;
|
|
279
|
-
}
|
|
280
|
-
function validateInput(url) {
|
|
281
|
-
if (!url || 'string' != typeof url) throw new errors_extensionFromStoreError('InvalidInput', 'URL is required');
|
|
282
|
-
}
|
|
283
|
-
function sanitizeSegment(value, label) {
|
|
284
|
-
const sanitized = value.replace(/[\\/]/g, '-').trim();
|
|
285
|
-
if (!sanitized) throw new errors_extensionFromStoreError('InvalidInput', `${label} is not a valid path segment`);
|
|
286
|
-
return sanitized;
|
|
287
|
-
}
|
|
288
|
-
async function resolveDownload(url, options) {
|
|
289
|
-
const store = detectStoreFromUrl(url);
|
|
290
|
-
if (!store) throw new errors_extensionFromStoreError('UnsupportedStore', 'URL does not match a supported store');
|
|
291
|
-
if ('chrome' === store) {
|
|
292
|
-
const downloadId = extractChromeIdFromUrl(url);
|
|
293
|
-
if (!downloadId) throw new errors_extensionFromStoreError('NotFound', 'Chrome extension id not found in URL');
|
|
294
|
-
return {
|
|
295
|
-
store,
|
|
296
|
-
downloadUrl: getChromeDownloadUrl(downloadId, options.platform),
|
|
297
|
-
archiveType: 'crx',
|
|
298
|
-
downloadId,
|
|
299
|
-
slugOrId: downloadId
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
if ('edge' === store) {
|
|
303
|
-
const downloadId = extractEdgeIdFromUrl(url);
|
|
304
|
-
if (!downloadId) throw new errors_extensionFromStoreError('NotFound', 'Edge extension id not found in URL');
|
|
305
|
-
return {
|
|
306
|
-
store,
|
|
307
|
-
downloadUrl: getEdgeDownloadUrl(downloadId),
|
|
308
|
-
archiveType: 'crx',
|
|
309
|
-
downloadId,
|
|
310
|
-
slugOrId: downloadId
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
const slug = extractFirefoxSlugFromUrl(url);
|
|
314
|
-
if (!slug) throw new errors_extensionFromStoreError('NotFound', 'Firefox extension slug not found in URL');
|
|
315
|
-
const firefox = await resolveFirefoxDownload(slug, options.version, {
|
|
316
|
-
userAgent: options.userAgent,
|
|
317
|
-
logger: options.logger,
|
|
318
|
-
requestJson: options.requestJson
|
|
319
|
-
});
|
|
320
|
-
return {
|
|
321
|
-
store,
|
|
322
|
-
downloadUrl: firefox.downloadUrl,
|
|
323
|
-
archiveType: 'xpi',
|
|
324
|
-
versionHint: firefox.version,
|
|
325
|
-
slugOrId: firefox.slugOrId
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
112
|
function defaultOutputDir() {
|
|
329
|
-
return
|
|
113
|
+
return node_path.resolve(process.cwd(), 'extensions');
|
|
330
114
|
}
|
|
331
115
|
async function ensureDirExists(dir) {
|
|
332
|
-
await
|
|
116
|
+
await promises.mkdir(dir, {
|
|
333
117
|
recursive: true
|
|
334
118
|
});
|
|
335
119
|
}
|
|
336
120
|
async function pathExists(target) {
|
|
337
121
|
try {
|
|
338
|
-
await
|
|
122
|
+
await promises.access(target);
|
|
339
123
|
return true;
|
|
340
124
|
} catch {
|
|
341
125
|
return false;
|
|
@@ -343,12 +127,12 @@ async function pathExists(target) {
|
|
|
343
127
|
}
|
|
344
128
|
async function moveDir(source, destination) {
|
|
345
129
|
try {
|
|
346
|
-
await
|
|
130
|
+
await promises.rename(source, destination);
|
|
347
131
|
} catch {
|
|
348
|
-
await
|
|
132
|
+
await promises.cp(source, destination, {
|
|
349
133
|
recursive: true
|
|
350
134
|
});
|
|
351
|
-
await
|
|
135
|
+
await promises.rm(source, {
|
|
352
136
|
recursive: true,
|
|
353
137
|
force: true
|
|
354
138
|
});
|
|
@@ -360,7 +144,7 @@ function errorWithCode(code, message, cause) {
|
|
|
360
144
|
async function fetchExtensionFromStore(url, options = {}) {
|
|
361
145
|
validateInput(url);
|
|
362
146
|
const log = createLogger(options.logger);
|
|
363
|
-
const outDir = options.outDir ?
|
|
147
|
+
const outDir = options.outDir ? node_path.resolve(options.outDir) : defaultOutputDir();
|
|
364
148
|
await ensureDirExists(outDir);
|
|
365
149
|
const resolved = await resolveDownload(url, {
|
|
366
150
|
version: options.version,
|
|
@@ -370,9 +154,9 @@ async function fetchExtensionFromStore(url, options = {}) {
|
|
|
370
154
|
requestJson: requestJson
|
|
371
155
|
});
|
|
372
156
|
if (options.version && !url.includes('addons.mozilla.org')) log.warn('Version hints are best-effort and only supported on Firefox at the moment.');
|
|
373
|
-
const workDir = await
|
|
374
|
-
const archivePath =
|
|
375
|
-
const extractDir =
|
|
157
|
+
const workDir = await promises.mkdtemp(node_path.join(node_os.tmpdir(), 'extension-from-store-'));
|
|
158
|
+
const archivePath = node_path.join(workDir, `archive.${resolved.archiveType}`);
|
|
159
|
+
const extractDir = node_path.join(workDir, 'extracted');
|
|
376
160
|
try {
|
|
377
161
|
log.info(`Downloading from ${resolved.downloadUrl}`);
|
|
378
162
|
await downloadToFile(resolved.downloadUrl, archivePath, {
|
|
@@ -383,40 +167,41 @@ async function fetchExtensionFromStore(url, options = {}) {
|
|
|
383
167
|
const versionSuffix = resolved.versionHint ? `@${sanitizeSegment(resolved.versionHint, 'Extension version')}` : '';
|
|
384
168
|
if (true !== options.extract) {
|
|
385
169
|
const archiveName = `${safeId}${versionSuffix}.${resolved.archiveType}`;
|
|
386
|
-
const finalArchivePath =
|
|
170
|
+
const finalArchivePath = node_path.join(outDir, archiveName);
|
|
387
171
|
if (await pathExists(finalArchivePath)) throw errorWithCode('FilesystemConflict', `Target file already exists: ${finalArchivePath}`);
|
|
388
|
-
await
|
|
172
|
+
await promises.rename(archivePath, finalArchivePath);
|
|
389
173
|
return;
|
|
390
174
|
}
|
|
391
|
-
await
|
|
175
|
+
await promises.mkdir(extractDir, {
|
|
392
176
|
recursive: true
|
|
393
177
|
});
|
|
394
178
|
if ('crx' === resolved.archiveType) await extractCrx(archivePath, extractDir, workDir);
|
|
395
179
|
else await extractZipArchive(archivePath, extractDir);
|
|
396
|
-
const manifestPath =
|
|
180
|
+
const manifestPath = node_path.join(extractDir, 'manifest.json');
|
|
397
181
|
const manifestInfo = await readManifestInfo(manifestPath);
|
|
398
182
|
const resolvedVersion = resolved.versionHint || manifestInfo.extensionVersion;
|
|
399
183
|
const safeVersion = sanitizeSegment(resolvedVersion, 'Extension version');
|
|
400
184
|
const folderName = `${safeId}@${safeVersion}`;
|
|
401
|
-
const finalDir =
|
|
185
|
+
const finalDir = node_path.join(outDir, folderName);
|
|
402
186
|
if (await pathExists(finalDir)) throw errorWithCode('FilesystemConflict', `Target folder already exists: ${finalDir}`);
|
|
403
187
|
await moveDir(extractDir, finalDir);
|
|
404
|
-
const metaPath =
|
|
188
|
+
const metaPath = node_path.join(finalDir, 'extension.meta.json');
|
|
405
189
|
const meta = {
|
|
406
190
|
store: resolved.store,
|
|
407
191
|
identifier: resolved.slugOrId,
|
|
408
192
|
version: resolvedVersion,
|
|
409
193
|
manifestVersion: manifestInfo.manifestVersion
|
|
410
194
|
};
|
|
411
|
-
await
|
|
195
|
+
await promises.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}\n`, 'utf8');
|
|
412
196
|
} catch (error) {
|
|
413
197
|
if (error instanceof errors_extensionFromStoreError) throw error;
|
|
414
198
|
throw errorWithCode('ExtractionFailed', 'Failed to extract extension', error);
|
|
415
199
|
} finally{
|
|
416
|
-
await
|
|
200
|
+
await promises.rm(workDir, {
|
|
417
201
|
recursive: true,
|
|
418
202
|
force: true
|
|
419
203
|
}).catch(()=>void 0);
|
|
420
204
|
}
|
|
421
205
|
}
|
|
422
|
-
export {
|
|
206
|
+
export { extensionFromStoreError } from "./588.js";
|
|
207
|
+
export { fetchExtensionFromStore };
|
package/dist/resolve.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Logger } from './logger';
|
|
2
|
-
import type { ChromePlatformInfo } from './platform';
|
|
3
1
|
import { type JsonRequester } from './stores/firefox';
|
|
2
|
+
import type { ChromePlatformInfo } from './platform';
|
|
3
|
+
import type { Logger } from './logger';
|
|
4
4
|
export type ResolvedDownload = {
|
|
5
5
|
store: 'chrome' | 'edge' | 'firefox';
|
|
6
6
|
downloadUrl: string;
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"engineStrict": false,
|
|
11
11
|
"name": "extension-from-store",
|
|
12
|
-
"version": "0.2.
|
|
12
|
+
"version": "0.2.2",
|
|
13
13
|
"description": "Download public browser extensions from official stores",
|
|
14
14
|
"homepage": "https://www.npmjs.com/package/extension-from-store",
|
|
15
15
|
"type": "module",
|
|
@@ -43,14 +43,13 @@
|
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "rslib build",
|
|
45
45
|
"typecheck": "tsc --noEmit",
|
|
46
|
-
"check": "pnpm dlx @biomejs/biome@1.9.4 check --write",
|
|
47
46
|
"dev": "rslib build --watch",
|
|
48
|
-
"format": "pnpm dlx @biomejs/biome@1.9.4 format --write",
|
|
49
47
|
"pretest": "npm run build",
|
|
50
48
|
"test": "vitest run",
|
|
51
49
|
"test:live": "RUN_LIVE_FETCH=1 vitest run tests/live-fetch.test.ts",
|
|
52
50
|
"prepublishOnly": "npm run build",
|
|
53
|
-
"
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"lint:fix": "eslint . --fix"
|
|
54
53
|
},
|
|
55
54
|
"publishConfig": {
|
|
56
55
|
"provenance": true
|
|
@@ -81,14 +80,31 @@
|
|
|
81
80
|
],
|
|
82
81
|
"dependencies": {
|
|
83
82
|
"extract-zip": "^2.0.1",
|
|
84
|
-
"fflate": "^0.8.
|
|
83
|
+
"fflate": "^0.8.3"
|
|
84
|
+
},
|
|
85
|
+
"pnpm": {
|
|
86
|
+
"overrides": {
|
|
87
|
+
"vite": ">=7.3.5",
|
|
88
|
+
"vitest": ">=3.2.6",
|
|
89
|
+
"rollup": ">=4.59.0",
|
|
90
|
+
"minimatch": ">=9.0.7",
|
|
91
|
+
"picomatch": ">=4.0.4",
|
|
92
|
+
"postcss": ">=8.5.10",
|
|
93
|
+
"js-yaml": ">=4.2.0",
|
|
94
|
+
"brace-expansion": ">=2.0.2",
|
|
95
|
+
"tmp": ">=0.2.6",
|
|
96
|
+
"esbuild": ">=0.25.0"
|
|
97
|
+
}
|
|
85
98
|
},
|
|
86
99
|
"devDependencies": {
|
|
87
|
-
"@
|
|
88
|
-
"@
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
100
|
+
"@rslib/core": "^0.23.0",
|
|
101
|
+
"@types/node": "^25.9.3",
|
|
102
|
+
"eslint": "^10.5.0",
|
|
103
|
+
"eslint-config-auditor": "^2.3.1",
|
|
104
|
+
"typescript": "^6.0.3",
|
|
105
|
+
"vitest": "^4.1.9"
|
|
92
106
|
},
|
|
93
|
-
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1"
|
|
107
|
+
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1",
|
|
108
|
+
"bugs": "https://github.com/cezaraugusto/extension-from-store/issues",
|
|
109
|
+
"sideEffects": false
|
|
94
110
|
}
|