browser-extension-manager 1.3.1 → 1.3.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/CHANGELOG.md +9 -6
- package/dist/background.js +5 -5
- package/dist/config/manifest.json +2 -0
- package/dist/defaults/_.env +14 -10
- package/dist/gulp/tasks/package.js +77 -22
- package/dist/gulp/tasks/publish.js +4 -0
- package/dist/gulp/templates/BUILD_INSTRUCTIONS.md +14 -7
- package/dist/lib/affiliatizer.js +1 -1
- package/dist/lib/auth-helpers.js +3 -3
- package/dist/lib/extension.js +0 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,16 +18,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
18
18
|
## [1.3.0] - 2025-12-16
|
|
19
19
|
|
|
20
20
|
### Added
|
|
21
|
-
- Multi-target builds for Chromium and
|
|
21
|
+
- Multi-target builds for Chromium, Firefox, and Opera with automatic manifest adjustments
|
|
22
22
|
- Chromium build uses `background.service_worker`, Firefox build uses `background.scripts`
|
|
23
|
-
-
|
|
23
|
+
- Opera build auto-resolves `__MSG_*__` placeholders in `short_name` (Opera enforces 12-char limit including placeholder text)
|
|
24
|
+
- Browser-specific `TARGETS` config object with `adjustManifest()` functions for each target
|
|
25
|
+
- Opera loading instructions in BUILD_INSTRUCTIONS.md
|
|
24
26
|
|
|
25
27
|
### Changed
|
|
26
|
-
- Packaged output structure changed from `packaged/raw/` to `packaged/{chromium,firefox}/raw/`
|
|
27
|
-
- Extension zip moved from `packaged/extension.zip` to `packaged/{chromium,firefox}/extension.zip`
|
|
28
|
-
- `package.js` now creates separate builds for each browser target
|
|
29
|
-
- `publish.js` uses target-specific paths (chromium zip for Chrome/Edge, firefox raw for Firefox)
|
|
28
|
+
- Packaged output structure changed from `packaged/raw/` to `packaged/{chromium,firefox,opera}/raw/`
|
|
29
|
+
- Extension zip moved from `packaged/extension.zip` to `packaged/{chromium,firefox,opera}/extension.zip`
|
|
30
|
+
- `package.js` now creates separate builds for each browser target using configurable `TARGETS` object
|
|
31
|
+
- `publish.js` uses target-specific paths (chromium zip for Chrome/Edge, firefox raw for Firefox, opera zip for Opera)
|
|
30
32
|
- `audit.js` audits chromium build (code is identical between targets)
|
|
33
|
+
- Manifest compilation now uses 3-step process: apply defaults → target adjustments → cleanup
|
|
31
34
|
|
|
32
35
|
## [1.1.13] - 2025-11-26
|
|
33
36
|
### Added
|
package/dist/background.js
CHANGED
|
@@ -304,7 +304,7 @@ class Manager {
|
|
|
304
304
|
if (user) {
|
|
305
305
|
// User is signed in - get current stored state to preserve token
|
|
306
306
|
const result = await new Promise(resolve =>
|
|
307
|
-
this.extension.storage.get('bxm:authState', resolve)
|
|
307
|
+
this.extension.storage.local.get('bxm:authState', resolve)
|
|
308
308
|
);
|
|
309
309
|
const currentState = result['bxm:authState'] || {};
|
|
310
310
|
|
|
@@ -321,11 +321,11 @@ class Manager {
|
|
|
321
321
|
timestamp: Date.now(),
|
|
322
322
|
};
|
|
323
323
|
|
|
324
|
-
await this.extension.storage.set({ 'bxm:authState': authState });
|
|
324
|
+
await this.extension.storage.local.set({ 'bxm:authState': authState });
|
|
325
325
|
this.logger.log('[AUTH] Auth state synced to storage');
|
|
326
326
|
} else {
|
|
327
327
|
// User is signed out - clear storage
|
|
328
|
-
await this.extension.storage.remove('bxm:authState');
|
|
328
|
+
await this.extension.storage.local.remove('bxm:authState');
|
|
329
329
|
this.logger.log('[AUTH] Auth state cleared from storage');
|
|
330
330
|
}
|
|
331
331
|
}
|
|
@@ -349,11 +349,11 @@ class Manager {
|
|
|
349
349
|
|
|
350
350
|
// Save token to storage (user state will be synced by onAuthStateChanged)
|
|
351
351
|
const result = await new Promise(resolve =>
|
|
352
|
-
this.extension.storage.get('bxm:authState', resolve)
|
|
352
|
+
this.extension.storage.local.get('bxm:authState', resolve)
|
|
353
353
|
);
|
|
354
354
|
const currentState = result['bxm:authState'] || {};
|
|
355
355
|
|
|
356
|
-
await this.extension.storage.set({
|
|
356
|
+
await this.extension.storage.local.set({
|
|
357
357
|
'bxm:authState': {
|
|
358
358
|
...currentState,
|
|
359
359
|
token: token,
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Basic info
|
|
3
3
|
manifest_version: 3,
|
|
4
4
|
name: '__MSG_appName__',
|
|
5
|
+
// NOTE: Opera enforces a 12-char limit on short_name INCLUDING the placeholder text
|
|
6
|
+
// '__MSG_appNameShort__' is 19 chars, so use a static value for Opera compatibility
|
|
5
7
|
short_name: '__MSG_appNameShort__',
|
|
6
8
|
description: '__MSG_appDescription__',
|
|
7
9
|
|
package/dist/defaults/_.env
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# ========== Default Values ==========
|
|
2
|
+
# Github Token
|
|
3
|
+
# Get token at: https://github.com/settings/tokens
|
|
4
|
+
GH_TOKEN=""
|
|
5
|
+
|
|
2
6
|
# Chrome Web Store Publishing Credentials
|
|
3
7
|
# Get credentials at: https://developer.chrome.com/docs/webstore/using_webstore_api/
|
|
4
|
-
CHROME_EXTENSION_ID="
|
|
5
|
-
CHROME_CLIENT_ID="
|
|
6
|
-
CHROME_CLIENT_SECRET="
|
|
7
|
-
CHROME_REFRESH_TOKEN="
|
|
8
|
+
CHROME_EXTENSION_ID=""
|
|
9
|
+
CHROME_CLIENT_ID=""
|
|
10
|
+
CHROME_CLIENT_SECRET=""
|
|
11
|
+
CHROME_REFRESH_TOKEN=""
|
|
8
12
|
|
|
9
13
|
# Firefox Add-ons Publishing Credentials
|
|
10
14
|
# Get credentials at: https://addons.mozilla.org/developers/addon/api/key/
|
|
11
|
-
FIREFOX_EXTENSION_ID="
|
|
12
|
-
FIREFOX_API_KEY="
|
|
13
|
-
FIREFOX_API_SECRET="
|
|
15
|
+
FIREFOX_EXTENSION_ID=""
|
|
16
|
+
FIREFOX_API_KEY=""
|
|
17
|
+
FIREFOX_API_SECRET=""
|
|
14
18
|
|
|
15
19
|
# Microsoft Edge Add-ons Publishing Credentials
|
|
16
20
|
# Get credentials at: https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/api/using-addons-api
|
|
17
|
-
EDGE_PRODUCT_ID="
|
|
18
|
-
EDGE_CLIENT_ID="
|
|
19
|
-
EDGE_API_KEY="
|
|
21
|
+
EDGE_PRODUCT_ID=""
|
|
22
|
+
EDGE_CLIENT_ID=""
|
|
23
|
+
EDGE_API_KEY=""
|
|
20
24
|
|
|
21
25
|
# Opera Add-ons
|
|
22
26
|
# NOTE: Opera does not have a publishing API. Extensions must be submitted manually at:
|
|
@@ -152,8 +152,67 @@ function getPackageVersion(packageName) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
// Build targets
|
|
156
|
-
const TARGETS =
|
|
155
|
+
// Build targets with browser-specific configurations
|
|
156
|
+
const TARGETS = {
|
|
157
|
+
chromium: {
|
|
158
|
+
// Chrome, Edge, Brave, etc. - uses service_worker
|
|
159
|
+
adjustManifest: (manifest) => {
|
|
160
|
+
if (manifest.background) {
|
|
161
|
+
delete manifest.background.scripts;
|
|
162
|
+
}
|
|
163
|
+
return manifest;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
firefox: {
|
|
167
|
+
// Firefox - uses scripts array, no service_worker
|
|
168
|
+
adjustManifest: (manifest) => {
|
|
169
|
+
if (manifest.background) {
|
|
170
|
+
if (manifest.background.service_worker) {
|
|
171
|
+
if (!manifest.background.scripts) {
|
|
172
|
+
manifest.background.scripts = [manifest.background.service_worker];
|
|
173
|
+
}
|
|
174
|
+
delete manifest.background.service_worker;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return manifest;
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
opera: {
|
|
181
|
+
// Opera - like chromium but with stricter requirements
|
|
182
|
+
// Opera enforces 12-char limit on short_name INCLUDING placeholder text
|
|
183
|
+
adjustManifest: (manifest) => {
|
|
184
|
+
// Same as chromium for background
|
|
185
|
+
if (manifest.background) {
|
|
186
|
+
delete manifest.background.scripts;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Opera requires static short_name (12 char limit includes placeholder text)
|
|
190
|
+
// __MSG_appNameShort__ is 19 chars, so we must use actual value
|
|
191
|
+
if (manifest.short_name && manifest.short_name.startsWith('__MSG_')) {
|
|
192
|
+
// Try to get the value from default locale
|
|
193
|
+
const localesDir = path.join('dist', '_locales');
|
|
194
|
+
const defaultLocale = manifest.default_locale || 'en';
|
|
195
|
+
const messagesPath = path.join(localesDir, defaultLocale, 'messages.json');
|
|
196
|
+
|
|
197
|
+
if (jetpack.exists(messagesPath)) {
|
|
198
|
+
try {
|
|
199
|
+
const messages = JSON5.parse(jetpack.read(messagesPath));
|
|
200
|
+
// Extract key from __MSG_keyName__
|
|
201
|
+
const key = manifest.short_name.replace(/^__MSG_/, '').replace(/__$/, '');
|
|
202
|
+
if (messages[key]?.message) {
|
|
203
|
+
manifest.short_name = messages[key].message;
|
|
204
|
+
logger.log(`[opera] Resolved short_name to "${manifest.short_name}"`);
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
logger.warn(`[opera] Could not resolve short_name from locale: ${e.message}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return manifest;
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
};
|
|
157
216
|
|
|
158
217
|
// Recursively remove empty arrays and objects from an object
|
|
159
218
|
function removeEmptyValues(obj) {
|
|
@@ -193,10 +252,12 @@ async function compileManifest(outputDir, target) {
|
|
|
193
252
|
const configPath = path.join(rootPathPackage, 'dist', 'config', 'manifest.json');
|
|
194
253
|
|
|
195
254
|
// Read and parse using JSON5
|
|
196
|
-
|
|
255
|
+
let manifest = JSON5.parse(jetpack.read(manifestPath));
|
|
197
256
|
const defaultConfig = JSON5.parse(jetpack.read(configPath));
|
|
198
257
|
|
|
199
|
-
//
|
|
258
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
259
|
+
// STEP 1: Apply defaults (shared across all targets)
|
|
260
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
200
261
|
getKeys(defaultConfig).forEach(key => {
|
|
201
262
|
const defaultValue = key.split('.').reduce((o, k) => (o || {})[k], defaultConfig);
|
|
202
263
|
const userValue = key.split('.').reduce((o, k) => (o || {})[k], manifest);
|
|
@@ -222,23 +283,17 @@ async function compileManifest(outputDir, target) {
|
|
|
222
283
|
// Add package version to manifest
|
|
223
284
|
manifest.version = project.version;
|
|
224
285
|
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// Firefox uses scripts array only
|
|
232
|
-
if (manifest.background.service_worker) {
|
|
233
|
-
// Ensure scripts array exists (derive from service_worker if needed)
|
|
234
|
-
if (!manifest.background.scripts) {
|
|
235
|
-
manifest.background.scripts = [manifest.background.service_worker];
|
|
236
|
-
}
|
|
237
|
-
delete manifest.background.service_worker;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
286
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
287
|
+
// STEP 2: Apply target-specific adjustments
|
|
288
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
|
+
const targetConfig = TARGETS[target];
|
|
290
|
+
if (targetConfig?.adjustManifest) {
|
|
291
|
+
manifest = targetConfig.adjustManifest(manifest);
|
|
240
292
|
}
|
|
241
293
|
|
|
294
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
295
|
+
// STEP 3: Clean up (shared across all targets)
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
242
297
|
// Remove empty arrays and objects from manifest
|
|
243
298
|
const cleanedManifest = removeEmptyValues(manifest);
|
|
244
299
|
|
|
@@ -278,14 +333,14 @@ async function compileLocales(outputDir) {
|
|
|
278
333
|
}
|
|
279
334
|
}
|
|
280
335
|
|
|
281
|
-
// Package Task for raw - creates
|
|
336
|
+
// Package Task for raw - creates builds for all browser targets
|
|
282
337
|
async function packageRaw() {
|
|
283
338
|
// Log
|
|
284
339
|
logger.log(`Starting raw packaging...`);
|
|
285
340
|
|
|
286
341
|
try {
|
|
287
342
|
// Build for each target
|
|
288
|
-
for (const target of TARGETS) {
|
|
343
|
+
for (const target of Object.keys(TARGETS)) {
|
|
289
344
|
await packageRawForTarget(target);
|
|
290
345
|
}
|
|
291
346
|
|
|
@@ -353,7 +408,7 @@ async function packageZip() {
|
|
|
353
408
|
|
|
354
409
|
try {
|
|
355
410
|
// Create zip for each target
|
|
356
|
-
for (const target of TARGETS) {
|
|
411
|
+
for (const target of Object.keys(TARGETS)) {
|
|
357
412
|
const inputDir = `packaged/${target}/raw`;
|
|
358
413
|
const zipPath = `packaged/${target}/extension.zip`;
|
|
359
414
|
|
|
@@ -19,6 +19,10 @@ const PATHS = {
|
|
|
19
19
|
zip: path.join(process.cwd(), 'packaged', 'firefox', 'extension.zip'),
|
|
20
20
|
raw: path.join(process.cwd(), 'packaged', 'firefox', 'raw'),
|
|
21
21
|
},
|
|
22
|
+
opera: {
|
|
23
|
+
zip: path.join(process.cwd(), 'packaged', 'opera', 'extension.zip'),
|
|
24
|
+
raw: path.join(process.cwd(), 'packaged', 'opera', 'raw'),
|
|
25
|
+
},
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
// Helper to check if a credential is valid (not empty or placeholder)
|
|
@@ -26,17 +26,12 @@ npm install
|
|
|
26
26
|
npm run build
|
|
27
27
|
```
|
|
28
28
|
5. The built extensions will be in `packaged/` directory:
|
|
29
|
-
- **Firefox:** `packaged/firefox/raw/` (unpacked) and `packaged/firefox/extension.zip`
|
|
30
29
|
- **Chrome/Edge:** `packaged/chromium/raw/` (unpacked) and `packaged/chromium/extension.zip`
|
|
30
|
+
- **Firefox:** `packaged/firefox/raw/` (unpacked) and `packaged/firefox/extension.zip`
|
|
31
|
+
- **Opera:** `packaged/opera/raw/` (unpacked) and `packaged/opera/extension.zip`
|
|
31
32
|
|
|
32
33
|
## Loading the Extension
|
|
33
34
|
|
|
34
|
-
### Firefox
|
|
35
|
-
1. Go to `about:debugging`
|
|
36
|
-
2. Click "This Firefox"
|
|
37
|
-
3. Click "Load Temporary Add-on"
|
|
38
|
-
4. Select `packaged/firefox/raw/manifest.json`
|
|
39
|
-
|
|
40
35
|
### Chrome
|
|
41
36
|
1. Go to `chrome://extensions`
|
|
42
37
|
2. Enable "Developer mode"
|
|
@@ -49,6 +44,18 @@ npm run build
|
|
|
49
44
|
3. Click "Load unpacked"
|
|
50
45
|
4. Select the `packaged/chromium/raw/` directory
|
|
51
46
|
|
|
47
|
+
### Firefox
|
|
48
|
+
1. Go to `about:debugging`
|
|
49
|
+
2. Click "This Firefox"
|
|
50
|
+
3. Click "Load Temporary Add-on"
|
|
51
|
+
4. Select `packaged/firefox/raw/manifest.json`
|
|
52
|
+
|
|
53
|
+
### Opera
|
|
54
|
+
1. Go to `opera://extensions`
|
|
55
|
+
2. Enable "Developer mode"
|
|
56
|
+
3. Click "Load unpacked"
|
|
57
|
+
4. Select the `packaged/opera/raw/` directory
|
|
58
|
+
|
|
52
59
|
## Questions
|
|
53
60
|
|
|
54
61
|
If you have any questions about the build process, please contact the developer.
|
package/dist/lib/affiliatizer.js
CHANGED
|
@@ -112,7 +112,7 @@ Affiliatizer.get = function () {
|
|
|
112
112
|
Affiliatizer.initialize = async function (Manager) {
|
|
113
113
|
// Shortcuts
|
|
114
114
|
const { extension, logger } = Manager;
|
|
115
|
-
const
|
|
115
|
+
const storage = extension.storage.local;
|
|
116
116
|
|
|
117
117
|
// Parse the URL
|
|
118
118
|
const url = new URL(window.location.href);
|
package/dist/lib/auth-helpers.js
CHANGED
|
@@ -29,7 +29,7 @@ async function signInWithStoredToken(context, authState) {
|
|
|
29
29
|
// If token is invalid/expired, clear the auth state
|
|
30
30
|
if (error.code === 'auth/invalid-custom-token' || error.code === 'auth/custom-token-expired') {
|
|
31
31
|
logger.log('[AUTH-SYNC] Token expired, clearing auth state');
|
|
32
|
-
context.extension.storage.remove('bxm:authState');
|
|
32
|
+
context.extension.storage.local.remove('bxm:authState');
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -43,7 +43,7 @@ export function setupAuthStorageListener(context) {
|
|
|
43
43
|
const { extension, webManager, logger } = context;
|
|
44
44
|
|
|
45
45
|
// Check existing auth state on load and sign in
|
|
46
|
-
extension.storage.get('bxm:authState', (result) => {
|
|
46
|
+
extension.storage.local.get('bxm:authState', (result) => {
|
|
47
47
|
const authState = result['bxm:authState'];
|
|
48
48
|
|
|
49
49
|
if (authState?.token) {
|
|
@@ -58,7 +58,7 @@ export function setupAuthStorageListener(context) {
|
|
|
58
58
|
if (!state.user) {
|
|
59
59
|
// User signed out - clear storage so all contexts sync
|
|
60
60
|
logger.log('[AUTH-SYNC] WM auth signed out, clearing storage...');
|
|
61
|
-
extension.storage.remove('bxm:authState');
|
|
61
|
+
extension.storage.local.remove('bxm:authState');
|
|
62
62
|
}
|
|
63
63
|
});
|
|
64
64
|
|
package/dist/lib/extension.js
CHANGED
|
@@ -111,15 +111,6 @@ function Extension () {
|
|
|
111
111
|
}
|
|
112
112
|
} catch (e) {}
|
|
113
113
|
|
|
114
|
-
// Fix storage
|
|
115
|
-
if (self.storage) {
|
|
116
|
-
if (self.storage.sync) {
|
|
117
|
-
self.storage = self.storage.sync
|
|
118
|
-
} else if (self.storage.local) {
|
|
119
|
-
self.storage = self.storage.local
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
114
|
// Return the object
|
|
124
115
|
return self;
|
|
125
116
|
}
|