@ubox-tools/deploy-xperience 1.1.19 → 1.1.21

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.
Files changed (2) hide show
  1. package/deploy.js +52 -48
  2. package/package.json +1 -1
package/deploy.js CHANGED
@@ -241,10 +241,7 @@ function generateProxy(appName) {
241
241
  // 1. Parameter token substitution (JS only — parameters live in JS files)
242
242
  if (isJS) {
243
243
  for (const key of Object.keys(params)) {
244
- const re = new RegExp(
245
- `((?:const|let|var)\\s+${key}\\s*=\\s*)(?:"[^"]*"|'[^']*'|\`[^\`]*\`|-?\\d+\\.?\\d*(?:[eE][+-]?\\d+)?|true|false|null|undefined)`,
246
- 'g'
247
- );
244
+ const re = new RegExp(`((?:const|let|var)\\s+${key}\\s*=\\s*)(?:"[^"]*"|'[^']*')`, 'g');
248
245
  content = content.replace(re, `$1"{parameter:${key}}"`);
249
246
  }
250
247
  }
@@ -419,17 +416,33 @@ async function uploadFile(page, absPath) {
419
416
  await client.detach();
420
417
  }
421
418
 
422
- /** Inject text content into the active Monaco/CodeMirror/Ace editor. */
423
- async function injectToEditor(page, content) {
424
- // Try Monaco API first (no clipboard needed)
425
- const monacoOk = await page.evaluate(text => {
426
- if (window.monaco?.editor) {
427
- const models = window.monaco.editor.getModels();
428
- if (models?.length > 0) { models[0].setValue(text); return true; }
419
+ /** Inject text content into the active Monaco/CodeMirror/Ace editor.
420
+ * @param {string} filename Source filename (e.g. "index.html") — used to pick
421
+ * the right Monaco model by URI when multiple models are
422
+ * pre-loaded (e.g. after visiting the Parameters tab). */
423
+ async function injectToEditor(page, content, filename) {
424
+ // Try Monaco API
425
+ const monacoResult = await page.evaluate((text, fn) => {
426
+ if (!window.monaco?.editor) return null;
427
+ const models = window.monaco.editor.getModels();
428
+ if (!models?.length) return null;
429
+
430
+ // Best: match model by filename in its URI — avoids the models[0] trap when
431
+ // multiple models are pre-loaded (happens after visiting the Parameters tab).
432
+ if (fn) {
433
+ const m = models.find(m => m.uri?.toString().includes(fn));
434
+ if (m) { m.setValue(text); return 'uri'; }
429
435
  }
430
- return false;
431
- }, content);
432
- if (monacoOk) { console.log(' [editor] Monaco'); return; }
436
+
437
+ // Safe fallback: single model is always the active one (lazy-load case).
438
+ if (models.length === 1) { models[0].setValue(text); return 'single'; }
439
+
440
+ // Multiple models, no URI match — fall through to keyboard injection.
441
+ return `multi:${models.length}:${models.map(m => m.uri?.toString()).join('|')}`;
442
+ }, content, filename ?? null);
443
+
444
+ if (monacoResult === 'uri' || monacoResult === 'single') return;
445
+ if (monacoResult) console.log(` [editor] Monaco ${monacoResult} → keyboard fallback`);
433
446
 
434
447
  // Try CodeMirror API
435
448
  const cmOk = await page.evaluate(text => {
@@ -437,19 +450,25 @@ async function injectToEditor(page, content) {
437
450
  if (cm?.CodeMirror) { cm.CodeMirror.setValue(text); return true; }
438
451
  return false;
439
452
  }, content);
440
- if (cmOk) { console.log(' [editor] CodeMirror'); return; }
453
+ if (cmOk) return;
441
454
 
442
- // Fallback: clipboard paste into Ace / generic textarea
443
- console.log(' [editor] clipboard');
455
+ // Keyboard injection: click the visible editor, Ctrl+A, Ctrl+V.
456
+ // Targets whatever editor is currently focused (active tab) — model-agnostic.
444
457
  writeClipboard(content);
445
- const ed = await page.$('.ace_editor, .cm-editor, .monaco-editor, textarea');
446
- if (!ed) throw new Error('No code editor found on page');
447
- const edRect = await page.evaluate(el => {
448
- el.scrollIntoView({ block: 'nearest' });
449
- const r = el.getBoundingClientRect();
450
- return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) };
451
- }, ed);
452
- await page.mouse.click(edRect.x, edRect.y);
458
+ const edPos = await page.evaluate(() => {
459
+ for (const sel of ['.monaco-editor', '.ace_editor', '.cm-editor', 'textarea']) {
460
+ const el = Array.from(document.querySelectorAll(sel))
461
+ .find(e => { const r = e.getBoundingClientRect(); return r.width > 10 && r.height > 10; });
462
+ if (el) {
463
+ el.scrollIntoView({ block: 'nearest' });
464
+ const r = el.getBoundingClientRect();
465
+ return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) };
466
+ }
467
+ }
468
+ return null;
469
+ });
470
+ if (!edPos) throw new Error('No code editor found on page');
471
+ await page.mouse.click(edPos.x, edPos.y);
453
472
  await sleep(200);
454
473
  await page.keyboard.down('Control');
455
474
  await page.keyboard.press('a');
@@ -609,6 +628,9 @@ async function configureParameters(page, params) {
609
628
 
610
629
  console.log(` [params] Creating ${toCreate.length} new parameter(s) (${existingKeys.length} already exist)...`);
611
630
 
631
+ // Accept any confirm() dialogs that Save triggers
632
+ page.on('dialog', async d => { await d.accept(); });
633
+
612
634
  await clickByText(page, 'Edit', 'button');
613
635
  await sleep(1000);
614
636
 
@@ -662,20 +684,11 @@ async function configureParameters(page, params) {
662
684
 
663
685
  async function injectSource(page, proxy) {
664
686
  console.log(' [source] Injecting source files...');
665
- console.log(` [source] URL: ${page.url()}`);
666
687
  await clickByText(page, 'Source');
667
688
  await sleep(1500);
668
689
 
669
690
  await clickByText(page, 'Edit source');
670
691
  await sleep(3000);
671
- // Wait for the editor (Monaco or CodeMirror) to fully initialize.
672
- // On first-ever source editor visit in a session, Monaco may still be loading
673
- // after 3 s — this ensures injection happens only once it's ready.
674
- await page.waitForFunction(() => {
675
- return !!(window.monaco?.editor?.getModels) ||
676
- !!(document.querySelector('.CodeMirror')?.CodeMirror);
677
- }, { timeout: 15000 }).catch(() => null);
678
- console.log(` [source] URL after Edit source: ${page.url()}`);
679
692
 
680
693
  // Tab label → filename
681
694
  const tabs = [
@@ -692,15 +705,12 @@ async function injectSource(page, proxy) {
692
705
  console.log(` ${filename} → ${tabLabel}`);
693
706
  await clickByText(page, tabLabel);
694
707
  await sleep(1000);
695
- await injectToEditor(page, proxy[filename]);
708
+ await injectToEditor(page, proxy[filename], filename);
696
709
  await sleep(500);
697
710
  }
698
711
 
699
- const navPromise = page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 }).catch(() => null);
700
712
  await clickByText(page, 'Save and back to app');
701
- const navResult = await navPromise;
702
- await sleep(1000);
703
- console.log(` [source] URL after save: ${page.url()} (navigated: ${navResult ? 'yes' : 'no'})`);
713
+ await sleep(3000);
704
714
  }
705
715
 
706
716
  async function deployApp(page, appName, projectName, { noAssets = false, noSource = false } = {}) {
@@ -729,7 +739,7 @@ async function deployApp(page, appName, projectName, { noAssets = false, noSourc
729
739
  // Extract app ID from current URL
730
740
  const appId = page.url().match(/#\/application(?:-source)?\/(\d+)/)?.[1] || '?';
731
741
  console.log(` [app] Done — ID: ${appId}`);
732
- return { appId, params };
742
+ return appId;
733
743
  }
734
744
 
735
745
  // ─── Phase 3: Ubox Deployment ─────────────────────────────────────────────────
@@ -1143,12 +1153,6 @@ async function main() {
1143
1153
  const state = {};
1144
1154
 
1145
1155
  try {
1146
- // Always accept any confirm() dialogs the studio may show during deployment
1147
- page.on('dialog', async d => {
1148
- console.log(` [dialog] type=${d.type()} msg="${d.message()}" → accepted`);
1149
- await d.accept();
1150
- });
1151
-
1152
1156
  // Phase 1 — Login
1153
1157
  await login(page, email, password);
1154
1158
 
@@ -1156,9 +1160,9 @@ async function main() {
1156
1160
  const appParamsMap = {};
1157
1161
  for (const appName of apps) {
1158
1162
  state[appName] = {};
1159
- const { appId, params } = await deployApp(page, appName, projectName, { noAssets, noSource });
1160
- state[appName].appId = appId;
1163
+ const { params } = generateProxy(appName);
1161
1164
  appParamsMap[appName] = params;
1165
+ state[appName].appId = await deployApp(page, appName, projectName, { noAssets, noSource });
1162
1166
  }
1163
1167
 
1164
1168
  // Phase 3 — Deploy Uboxes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubox-tools/deploy-xperience",
3
- "version": "1.1.19",
3
+ "version": "1.1.21",
4
4
  "description": "Deploy a Ubox experience to studio.ubox.world",
5
5
  "bin": { "deploy-xperience": "./deploy.js" },
6
6
  "dependencies": { "puppeteer": "*" },