browser-extension-manager 1.3.12 → 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,39 +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
+ // 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',
330
+ headers: {
331
+ ...edgeHeaders,
332
+ 'Content-Type': 'application/json',
333
+ },
334
+ body: JSON.stringify({
335
+ notes: `Check for pending submission`,
336
+ }),
337
+ });
338
+
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}`);
355
+ }
356
+ }
357
+
358
+ // Step 2: Upload the package
317
359
  logger.log('[edge] Uploading to Microsoft Edge Add-ons...');
318
360
 
319
- // Read chromium zip file (Edge uses same build as Chrome)
320
361
  const zipBuffer = jetpack.read(PATHS.chromium.zip, 'buffer');
321
-
322
- // Edge API v1.1 endpoint
323
362
  const uploadUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions/draft/package`;
324
363
 
325
- // Upload using fetch
326
- const response = await fetch(uploadUrl, {
364
+ const uploadResponse = await fetch(uploadUrl, {
327
365
  method: 'POST',
328
366
  headers: {
329
- 'Authorization': `ApiKey ${apiKey}`,
330
- 'X-ClientID': clientId,
367
+ ...edgeHeaders,
331
368
  'Content-Type': 'application/zip',
332
369
  },
333
370
  body: zipBuffer,
334
371
  });
335
372
 
336
- if (!response.ok) {
337
- const errorText = await response.text();
338
- 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}`);
339
376
  }
340
377
 
378
+ const uploadData = await uploadResponse.json().catch(() => null);
379
+ logger.log(`[edge] Upload response: ${JSON.stringify(uploadData)}`);
380
+
341
381
  logger.log('[edge] Package uploaded, submitting for review...');
342
382
 
343
- // Submit for review
344
- const publishUrl = `https://api.addons.microsoftedge.microsoft.com/v1/products/${productId}/submissions`;
383
+ // Step 3: Submit for review
345
384
  const publishResponse = await fetch(publishUrl, {
346
385
  method: 'POST',
347
386
  headers: {
348
- 'Authorization': `ApiKey ${apiKey}`,
349
- 'X-ClientID': clientId,
387
+ ...edgeHeaders,
350
388
  'Content-Type': 'application/json',
351
389
  },
352
390
  body: JSON.stringify({
@@ -354,9 +392,25 @@ async function publishToEdge() {
354
392
  }),
355
393
  });
356
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
357
401
  if (!publishResponse.ok) {
358
- const errorText = await publishResponse.text();
359
- 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'}`);
360
414
  }
361
415
 
362
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.12",
3
+ "version": "1.3.14",
4
4
  "description": "Browser Extension Manager dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {