browser-extension-manager 1.3.13 → 1.3.15
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/CLAUDE.md +26 -1
- package/dist/gulp/tasks/html.js +9 -0
- package/dist/gulp/tasks/publish.js +53 -31
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -209,8 +209,33 @@ When adding a new component type to the framework:
|
|
|
209
209
|
- **distribute** ([gulp/tasks/distribute.js](src/gulp/tasks/distribute.js)) - Copies project files to `dist/`
|
|
210
210
|
- **sass** ([gulp/tasks/sass.js](src/gulp/tasks/sass.js)) - Compiles SCSS with sophisticated load path system
|
|
211
211
|
- **webpack** ([gulp/tasks/webpack.js](src/gulp/tasks/webpack.js)) - Bundles JavaScript with Babel
|
|
212
|
-
- **html** ([gulp/tasks/html.js](src/gulp/tasks/html.js)) - Processes HTML views into templates
|
|
212
|
+
- **html** ([gulp/tasks/html.js](src/gulp/tasks/html.js)) - Processes HTML views into templates (see HTML Templating below)
|
|
213
213
|
- **package** ([gulp/tasks/package.js](src/gulp/tasks/package.js)) - Creates packaged extension
|
|
214
|
+
|
|
215
|
+
### HTML Templating
|
|
216
|
+
|
|
217
|
+
HTML views in `src/views/` are processed through a two-step templating system using `{{ }}` brackets.
|
|
218
|
+
|
|
219
|
+
**Available variables:**
|
|
220
|
+
- `{{ brand.name }}` - Brand name from config
|
|
221
|
+
- `{{ brand.url }}` - Brand URL from config
|
|
222
|
+
- `{{ page.name }}` - Component name (e.g., `popup`, `pages/index`)
|
|
223
|
+
- `{{ page.path }}` - Full view path
|
|
224
|
+
- `{{ page.title }}` - Page title (defaults to brand name)
|
|
225
|
+
- `{{ theme.appearance }}` - Theme appearance (`dark` or `light`)
|
|
226
|
+
- `{{ cacheBust }}` - Cache-busting timestamp
|
|
227
|
+
|
|
228
|
+
**Example usage in views:**
|
|
229
|
+
```html
|
|
230
|
+
<a href="{{ brand.url }}/pricing">Upgrade to Premium</a>
|
|
231
|
+
<p>Welcome to {{ brand.name }}</p>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**How it works:**
|
|
235
|
+
1. Your view file (`src/views/[component]/index.html`) is templated first
|
|
236
|
+
2. The result is injected into [page-template.html](src/config/page-template.html)
|
|
237
|
+
3. The outer template is processed with the same variables
|
|
238
|
+
|
|
214
239
|
- **serve** ([gulp/tasks/serve.js](src/gulp/tasks/serve.js)) - WebSocket server for live reload
|
|
215
240
|
|
|
216
241
|
### Modifying Themes
|
package/dist/gulp/tasks/html.js
CHANGED
|
@@ -105,6 +105,15 @@ function processHtml(templateContent) {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
// Apply template with custom brackets
|
|
108
|
+
// First, template the body content to replace any {{ }} placeholders in the view
|
|
109
|
+
const templatedBody = template(bodyContent, data, {
|
|
110
|
+
brackets: ['{{', '}}'],
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Update data with templated body
|
|
114
|
+
data.content = templatedBody;
|
|
115
|
+
|
|
116
|
+
// Then template the outer page template
|
|
108
117
|
const rendered = template(templateContent, data, {
|
|
109
118
|
brackets: ['{{', '}}'],
|
|
110
119
|
});
|
|
@@ -314,59 +314,57 @@ async function publishToEdge() {
|
|
|
314
314
|
throw new Error('Missing Edge credentials. Set EDGE_PRODUCT_ID, EDGE_CLIENT_ID, EDGE_API_KEY in .env');
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
317
|
+
// Helper for Edge API requests
|
|
318
|
+
const edgeHeaders = {
|
|
319
|
+
'Authorization': `ApiKey ${apiKey}`,
|
|
320
|
+
'X-ClientID': clientId,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// Helper to parse Edge API response (handles empty bodies)
|
|
324
|
+
async function parseEdgeResponse(response, label) {
|
|
325
|
+
const text = await response.text();
|
|
326
|
+
logger.log(`[edge] ${label} - Status: ${response.status}, Body: ${text || '(empty)'}`);
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
328
|
+
if (!text) {
|
|
329
|
+
return { status: response.status, data: null };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
return { status: response.status, data: JSON.parse(text) };
|
|
334
|
+
} catch (e) {
|
|
335
|
+
return { status: response.status, data: text };
|
|
334
336
|
}
|
|
335
337
|
}
|
|
336
338
|
|
|
339
|
+
// Step 1: Upload the package first
|
|
337
340
|
logger.log('[edge] Uploading to Microsoft Edge Add-ons...');
|
|
338
341
|
|
|
339
|
-
// Read chromium zip file (Edge uses same build as Chrome)
|
|
340
342
|
const zipBuffer = jetpack.read(PATHS.chromium.zip, 'buffer');
|
|
341
|
-
|
|
342
|
-
// Edge API v1.1 endpoint
|
|
343
343
|
const uploadUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions/draft/package`;
|
|
344
344
|
|
|
345
|
-
|
|
346
|
-
const response = await fetch(uploadUrl, {
|
|
345
|
+
const uploadResponse = await fetch(uploadUrl, {
|
|
347
346
|
method: 'POST',
|
|
348
347
|
headers: {
|
|
349
|
-
|
|
350
|
-
'X-ClientID': clientId,
|
|
348
|
+
...edgeHeaders,
|
|
351
349
|
'Content-Type': 'application/zip',
|
|
352
350
|
},
|
|
353
351
|
body: zipBuffer,
|
|
354
352
|
});
|
|
355
353
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
354
|
+
const upload = await parseEdgeResponse(uploadResponse, 'Upload response');
|
|
355
|
+
|
|
356
|
+
if (!uploadResponse.ok) {
|
|
357
|
+
throw new Error(`Edge upload error: ${upload.status} - ${JSON.stringify(upload.data)}`);
|
|
359
358
|
}
|
|
360
359
|
|
|
361
360
|
logger.log('[edge] Package uploaded, submitting for review...');
|
|
362
361
|
|
|
363
|
-
// Submit for review
|
|
362
|
+
// Step 2: Submit for review - this is where we'll get InProgressSubmission if there's a pending review
|
|
364
363
|
const publishUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions`;
|
|
365
364
|
const publishResponse = await fetch(publishUrl, {
|
|
366
365
|
method: 'POST',
|
|
367
366
|
headers: {
|
|
368
|
-
|
|
369
|
-
'X-ClientID': clientId,
|
|
367
|
+
...edgeHeaders,
|
|
370
368
|
'Content-Type': 'application/json',
|
|
371
369
|
},
|
|
372
370
|
body: JSON.stringify({
|
|
@@ -374,9 +372,33 @@ async function publishToEdge() {
|
|
|
374
372
|
}),
|
|
375
373
|
});
|
|
376
374
|
|
|
375
|
+
const publish = await parseEdgeResponse(publishResponse, 'Publish response');
|
|
376
|
+
|
|
377
|
+
// Check for HTTP errors (4xx, 5xx)
|
|
377
378
|
if (!publishResponse.ok) {
|
|
378
|
-
|
|
379
|
-
|
|
379
|
+
// Check if it's a 409 Conflict or similar indicating in-progress submission
|
|
380
|
+
if (publish.status === 409) {
|
|
381
|
+
throw new Error('Extension already has a pending submission in review. Wait for it to complete before publishing again.');
|
|
382
|
+
}
|
|
383
|
+
throw new Error(`Edge publish error: ${publish.status} - ${JSON.stringify(publish.data)}`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Check for API-level failures (HTTP 200/202 but status: "Failed" in body)
|
|
387
|
+
if (publish.data && typeof publish.data === 'object') {
|
|
388
|
+
if (publish.data.status === 'Failed') {
|
|
389
|
+
if (publish.data.errorCode === 'InProgressSubmission') {
|
|
390
|
+
throw new Error('Extension already has a pending submission in review. Wait for it to complete before publishing again.');
|
|
391
|
+
}
|
|
392
|
+
if (publish.data.errorCode === 'UnpublishInProgress') {
|
|
393
|
+
throw new Error('Extension is being unpublished. Wait for unpublish to complete before publishing.');
|
|
394
|
+
}
|
|
395
|
+
throw new Error(`Edge publish failed: ${publish.data.message || publish.data.errorCode || 'Unknown error'}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// HTTP 202 Accepted means submission was queued successfully
|
|
400
|
+
if (publish.status === 202) {
|
|
401
|
+
logger.log('[edge] Submission accepted and queued for review');
|
|
380
402
|
}
|
|
381
403
|
|
|
382
404
|
logger.log('[edge] Upload complete');
|