browser-extension-manager 1.3.13 → 1.3.14

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 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
@@ -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,77 @@ 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
- // Check current submission status first
318
- logger.log('[edge] Checking submission status...');
319
- const statusUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions`;
320
- const statusResponse = await fetch(statusUrl, {
321
- method: 'GET',
317
+ // Helper for Edge API requests
318
+ const edgeHeaders = {
319
+ 'Authorization': `ApiKey ${apiKey}`,
320
+ 'X-ClientID': clientId,
321
+ };
322
+
323
+ // Step 1: Try to submit first to check if there's a pending submission
324
+ // This is faster than uploading first, since upload always succeeds
325
+ logger.log('[edge] Checking for pending submissions...');
326
+
327
+ const publishUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions`;
328
+ const checkResponse = await fetch(publishUrl, {
329
+ method: 'POST',
322
330
  headers: {
323
- 'Authorization': `ApiKey ${apiKey}`,
324
- 'X-ClientID': clientId,
331
+ ...edgeHeaders,
332
+ 'Content-Type': 'application/json',
325
333
  },
334
+ body: JSON.stringify({
335
+ notes: `Check for pending submission`,
336
+ }),
326
337
  });
327
338
 
328
- if (statusResponse.ok) {
329
- const statusData = await statusResponse.json();
330
- // Check if there's a pending submission (InReview, PendingPublish, etc.)
331
- const pendingStatuses = ['InReview', 'PendingPublish', 'Submitted', 'InProgress'];
332
- if (statusData && pendingStatuses.includes(statusData.status)) {
333
- throw new Error(`Extension already has a pending submission (status: ${statusData.status}). Wait for it to complete.`);
339
+ const checkData = await checkResponse.json().catch(() => null);
340
+
341
+ // Log the response for debugging
342
+ logger.log(`[edge] Submission check response: ${JSON.stringify(checkData)}`);
343
+
344
+ // Check if there's a pending submission blocking us
345
+ if (checkData && checkData.status === 'Failed') {
346
+ if (checkData.errorCode === 'InProgressSubmission') {
347
+ throw new Error('Extension already has a pending submission in review. Wait for it to complete before publishing again.');
348
+ }
349
+ if (checkData.errorCode === 'UnpublishInProgress') {
350
+ throw new Error('Extension is being unpublished. Wait for unpublish to complete before publishing.');
351
+ }
352
+ // If it failed for another reason (like no draft package), that's expected - continue to upload
353
+ if (checkData.errorCode !== 'NoDraftPackage' && checkData.errorCode !== 'NoPackageToPublish') {
354
+ logger.log(`[edge] Check failed with: ${checkData.errorCode} - ${checkData.message}`);
334
355
  }
335
356
  }
336
357
 
358
+ // Step 2: Upload the package
337
359
  logger.log('[edge] Uploading to Microsoft Edge Add-ons...');
338
360
 
339
- // Read chromium zip file (Edge uses same build as Chrome)
340
361
  const zipBuffer = jetpack.read(PATHS.chromium.zip, 'buffer');
341
-
342
- // Edge API v1.1 endpoint
343
362
  const uploadUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions/draft/package`;
344
363
 
345
- // Upload using fetch
346
- const response = await fetch(uploadUrl, {
364
+ const uploadResponse = await fetch(uploadUrl, {
347
365
  method: 'POST',
348
366
  headers: {
349
- 'Authorization': `ApiKey ${apiKey}`,
350
- 'X-ClientID': clientId,
367
+ ...edgeHeaders,
351
368
  'Content-Type': 'application/zip',
352
369
  },
353
370
  body: zipBuffer,
354
371
  });
355
372
 
356
- if (!response.ok) {
357
- const errorText = await response.text();
358
- throw new Error(`Edge API error: ${response.status} - ${errorText}`);
373
+ if (!uploadResponse.ok) {
374
+ const errorText = await uploadResponse.text();
375
+ throw new Error(`Edge upload error: ${uploadResponse.status} - ${errorText}`);
359
376
  }
360
377
 
378
+ const uploadData = await uploadResponse.json().catch(() => null);
379
+ logger.log(`[edge] Upload response: ${JSON.stringify(uploadData)}`);
380
+
361
381
  logger.log('[edge] Package uploaded, submitting for review...');
362
382
 
363
- // Submit for review
364
- const publishUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions`;
383
+ // Step 3: Submit for review
365
384
  const publishResponse = await fetch(publishUrl, {
366
385
  method: 'POST',
367
386
  headers: {
368
- 'Authorization': `ApiKey ${apiKey}`,
369
- 'X-ClientID': clientId,
387
+ ...edgeHeaders,
370
388
  'Content-Type': 'application/json',
371
389
  },
372
390
  body: JSON.stringify({
@@ -374,9 +392,25 @@ async function publishToEdge() {
374
392
  }),
375
393
  });
376
394
 
395
+ const publishData = await publishResponse.json().catch(() => null);
396
+
397
+ // Log the full response
398
+ logger.log(`[edge] Publish response: ${JSON.stringify(publishData)}`);
399
+
400
+ // Check for HTTP errors
377
401
  if (!publishResponse.ok) {
378
- const errorText = await publishResponse.text();
379
- throw new Error(`Edge publish error: ${publishResponse.status} - ${errorText}`);
402
+ throw new Error(`Edge publish error: ${publishResponse.status} - ${JSON.stringify(publishData)}`);
403
+ }
404
+
405
+ // Check for API-level failures (HTTP 200 but status: "Failed")
406
+ if (publishData && publishData.status === 'Failed') {
407
+ if (publishData.errorCode === 'InProgressSubmission') {
408
+ throw new Error('Extension already has a pending submission in review. Wait for it to complete before publishing again.');
409
+ }
410
+ if (publishData.errorCode === 'UnpublishInProgress') {
411
+ throw new Error('Extension is being unpublished. Wait for unpublish to complete before publishing.');
412
+ }
413
+ throw new Error(`Edge publish failed: ${publishData.message || publishData.errorCode || 'Unknown error'}`);
380
414
  }
381
415
 
382
416
  logger.log('[edge] Upload complete');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-extension-manager",
3
- "version": "1.3.13",
3
+ "version": "1.3.14",
4
4
  "description": "Browser Extension Manager dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {