@resolveio/server-lib 22.3.117 → 22.3.118

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resolveio/server-lib",
3
- "version": "22.3.117",
3
+ "version": "22.3.118",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "package": "./build_package.sh",
@@ -2563,6 +2563,135 @@ function buildResolveIORunnerQaWorkflowProbeScript() {
2563
2563
  ' }',
2564
2564
  ' throw new Error(`Billing Dashboard loaded but no actionable seeded billing rows became visible before QA handoff. This is a runner data-seeding blocker, not a feature failure. Page summary: ${JSON.stringify(summary).slice(0, 1000)}`);',
2565
2565
  '}',
2566
+ 'function matrixRows(matrix) { return Array.isArray(matrix && matrix.rows) ? matrix.rows : []; }',
2567
+ 'function activeRowText() {',
2568
+ ' const matrix = readJson(matrixPath) || {};',
2569
+ ' const row = matrixRows(matrix).find((candidate) => !/^(pass|passed)$/i.test(String(candidate && candidate.status || ""))) || matrixRows(matrix)[0] || {};',
2570
+ ' return [row.workflow, row.route, row.assertion, row.required_proof].map((value) => String(value || "")).join("\\n");',
2571
+ '}',
2572
+ 'function readSeedAssetContext() {',
2573
+ ' const seed = readJson(path.join(artifactDir, "qa-live-data-seed-result.json"));',
2574
+ ' const context = seed && seed.selected && seed.selected.qa_asset_context;',
2575
+ ' if (!context || !Array.isArray(context.asset_ids) || !context.asset_ids.length) return null;',
2576
+ ' return context;',
2577
+ '}',
2578
+ 'function mongoRequire() {',
2579
+ ' const candidates = [',
2580
+ ' path.join(projectRoot, "server", "node_modules", "mongodb"),',
2581
+ ' path.join(projectRoot, "node_modules", "mongodb"),',
2582
+ ' path.join(process.cwd(), "server", "node_modules", "mongodb"),',
2583
+ ' path.join(process.cwd(), "node_modules", "mongodb"),',
2584
+ ' "mongodb"',
2585
+ ' ];',
2586
+ ' for (const candidate of candidates) { try { return require(candidate); } catch (error) {} }',
2587
+ ' throw new Error("Unable to require mongodb from project/server node_modules or global resolution");',
2588
+ '}',
2589
+ 'function localMongoUrl() {',
2590
+ ' return process.env.MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || `mongodb://127.0.0.1:${process.env.RESOLVEIO_SUPPORT_QA_MONGO_PORT || "3001"}/resolveio?directConnection=true`;',
2591
+ '}',
2592
+ 'function assetDisplayNumber(asset) { return String(asset && (asset.unit_number || asset.asset_number || asset.number || asset.name || asset._id) || ""); }',
2593
+ 'function uniqueStrings(values) { return Array.from(new Set((values || []).map((value) => String(value || "").trim()).filter(Boolean))); }',
2594
+ 'function staleLocationTerms(text) {',
2595
+ ' const terms = [];',
2596
+ ' const value = String(text || "");',
2597
+ ' const staleMatch = /stale\\s+([^.;\\n]+)/i.exec(value);',
2598
+ ' if (staleMatch) {',
2599
+ ' for (const part of staleMatch[1].split(/,?\\s+and\\s+|\\s+or\\s+|;/i)) {',
2600
+ ' const clean = part.replace(/\\b(absent|are|is|old|name|names)\\b/ig, " ").replace(/\\s+/g, " ").trim();',
2601
+ ' if (/\\b[A-Z][A-Za-z]+(?:ton|port|city|ville|field)?\\b.*\\b[A-Z]{2}\\b/.test(clean)) terms.push(clean);',
2602
+ ' }',
2603
+ ' }',
2604
+ ' for (const fallback of ["Jourdanton, TX", "Schreveport, LA", "Shreveport, LA"]) {',
2605
+ ' if (new RegExp(fallback.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), "i").test(value)) terms.push(fallback);',
2606
+ ' }',
2607
+ ' return uniqueStrings(terms);',
2608
+ '}',
2609
+ 'async function fetchAssetLocationProofData(context) {',
2610
+ ' const { MongoClient, ObjectId } = mongoRequire();',
2611
+ ' const client = new MongoClient(localMongoUrl());',
2612
+ ' await client.connect();',
2613
+ ' try {',
2614
+ ' const db = client.db();',
2615
+ ' const assetIds = uniqueStrings(context.asset_ids);',
2616
+ ' const assetNumbers = uniqueStrings(context.asset_numbers);',
2617
+ ' const objectIds = assetIds.filter((id) => /^[a-f0-9]{24}$/i.test(id)).map((id) => new ObjectId(id));',
2618
+ ' const query = { $or: [] };',
2619
+ ' if (objectIds.length) query.$or.push({ _id: { $in: objectIds } });',
2620
+ ' if (assetNumbers.length) query.$or.push({ unit_number: { $in: assetNumbers } }, { asset_number: { $in: assetNumbers } }, { number: { $in: assetNumbers } });',
2621
+ ' const assets = query.$or.length ? await db.collection("assets").find(query).toArray() : [];',
2622
+ ' const yardIds = uniqueStrings([...(context.yard_ids || []), ...assets.map((asset) => asset && asset.current_location && asset.current_location.id), ...assets.map((asset) => asset && asset.id_default_yard), ...assets.flatMap((asset) => Array.isArray(asset && asset.locations) ? asset.locations.map((location) => location && location.id) : [])]);',
2623
+ ' const yardObjectIds = yardIds.filter((id) => /^[a-f0-9]{24}$/i.test(id)).map((id) => new ObjectId(id));',
2624
+ ' const yards = yardObjectIds.length ? await db.collection("yards").find({ _id: { $in: yardObjectIds } }).toArray() : [];',
2625
+ ' const yardById = new Map(yards.map((yard) => [String(yard._id), yard]));',
2626
+ ' const rows = assets.map((asset) => {',
2627
+ ' const currentId = asset && asset.current_location && asset.current_location.id ? String(asset.current_location.id) : "";',
2628
+ ' const currentYard = currentId ? yardById.get(currentId) : null;',
2629
+ ' const canonicalName = currentYard && currentYard.name ? String(currentYard.name) : "";',
2630
+ ' const currentName = asset && asset.current_location && asset.current_location.name ? String(asset.current_location.name) : "";',
2631
+ ' return { id: String(asset._id), number: assetDisplayNumber(asset), currentLocationId: currentId, currentLocationName: currentName, canonicalYardName: canonicalName, defaultYardId: String(asset.id_default_yard || ""), defaultYardName: String(asset.default_yard || ""), pass: Boolean(canonicalName && currentName === canonicalName) };',
2632
+ ' });',
2633
+ ' return { assetIds, assetNumbers, rows, yards: yards.map((yard) => ({ id: String(yard._id), name: yard.name })) };',
2634
+ ' } finally {',
2635
+ ' await client.close().catch(() => undefined);',
2636
+ ' }',
2637
+ '}',
2638
+ 'async function gotoAndSummarize(page, route, screenshotName) {',
2639
+ ' await page.goto(new URL(route, clientUrl).href, { waitUntil: "domcontentloaded", timeout: 60000 });',
2640
+ ' await page.waitForSelector("body", { timeout: 30000 });',
2641
+ ' await delay(Number(process.env.RESOLVEIO_RUNNER_QA_SCREENSHOT_SETTLE_MS || process.env.RESOLVEIO_SUPPORT_QA_SCREENSHOT_SETTLE_MS || 1000));',
2642
+ ' await page.keyboard.press("Escape").catch(() => undefined);',
2643
+ ' await delay(750);',
2644
+ ' const summary = await pageSummary(page);',
2645
+ ' const screenshot = path.join(artifactDir, screenshotName);',
2646
+ ' await page.screenshot({ path: screenshot, type: "jpeg", quality: 82, fullPage: false });',
2647
+ ' return { route, screenshot, summary };',
2648
+ '}',
2649
+ 'async function runAssetLocationRowProof(page, routeSummary) {',
2650
+ ' const rowText = activeRowText();',
2651
+ ' const context = readSeedAssetContext();',
2652
+ ' if (!context || !/asset|unit|yard|location|current\\s+location/i.test(rowText)) return null;',
2653
+ ' const proofPath = path.join(artifactDir, "qa-asset-location-proof.json");',
2654
+ ' const staleTerms = staleLocationTerms(rowText);',
2655
+ ' const data = await fetchAssetLocationProofData(context);',
2656
+ ' const expectedNumbers = uniqueStrings(context.asset_numbers).filter((value) => /^\\d+[A-Za-z-]*$/.test(value));',
2657
+ ' const missingNumbers = expectedNumbers.filter((number) => !data.rows.some((row) => row.number === number));',
2658
+ ' const mismatchedRows = data.rows.filter((row) => !row.pass);',
2659
+ ' const listText = String((routeSummary && routeSummary.bodyTextSnippet) || "");',
2660
+ ' const listMissing = expectedNumbers.filter((number) => !new RegExp(`\\\\b${number.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}\\\\b`).test(listText));',
2661
+ ' const staleSeen = staleTerms.filter((term) => new RegExp(term.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), "i").test(listText) || data.rows.some((row) => new RegExp(term.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), "i").test([row.currentLocationName, row.defaultYardName].join(" "))));',
2662
+ ' const artifacts = [];',
2663
+ ' if (routeSummary) artifacts.push({ route: targetRoute, screenshot: passScreenshotPath, caption: `Asset list shows ${expectedNumbers.join(", ")} with canonical yard names and no stale yard labels.` });',
2664
+ ' for (const row of data.rows) {',
2665
+ ' const detail = await gotoAndSummarize(page, `/asset/detail/${row.id}`, `asset-location-detail-${row.number || row.id}.jpg`);',
2666
+ ' const edit = await gotoAndSummarize(page, `/asset/edit/${row.id}`, `asset-location-edit-${row.number || row.id}.jpg`);',
2667
+ ' artifacts.push({ route: detail.route, screenshot: detail.screenshot, caption: `Asset ${row.number || row.id} detail shows canonical yard ${row.canonicalYardName || row.currentLocationName}.`, bodyTextSnippet: detail.summary.bodyTextSnippet });',
2668
+ ' artifacts.push({ route: edit.route, screenshot: edit.screenshot, caption: `Asset ${row.number || row.id} edit screen opens with canonical yard ${row.canonicalYardName || row.currentLocationName} selected/visible.`, bodyTextSnippet: edit.summary.bodyTextSnippet });',
2669
+ ' }',
2670
+ ' const pass = !missingNumbers.length && !mismatchedRows.length && !listMissing.length && !staleSeen.length && data.rows.length >= Math.max(expectedNumbers.length, 1);',
2671
+ ' const payload = { status: pass ? "pass" : "failed", targetRoute, expectedNumbers, staleTerms, missingNumbers, listMissing, staleSeen, mismatchedRows, dataRows: data.rows, artifacts, updated_at: new Date().toISOString() };',
2672
+ ' writeJson(proofPath, payload);',
2673
+ ' const matrix = readJson(matrixPath) || { status: "started", rows: [] };',
2674
+ ' const rows = matrixRows(matrix);',
2675
+ ' const rowIndex = rows.findIndex((candidate) => !/^(pass|passed)$/i.test(String(candidate && candidate.status || "")));',
2676
+ ' const activeIndex = rowIndex >= 0 ? rowIndex : 0;',
2677
+ ' if (rows[activeIndex]) {',
2678
+ ' rows[activeIndex].status = pass ? "pass" : "failed";',
2679
+ ' rows[activeIndex].result = pass ? "pass" : "failed";',
2680
+ ' rows[activeIndex].screenshot = artifacts[0] && artifacts[0].screenshot || passScreenshotPath;',
2681
+ ' rows[activeIndex].screenshots = artifacts.map((artifact) => artifact.screenshot).filter(Boolean);',
2682
+ ' rows[activeIndex].caption = pass ? `Verified exact assets ${expectedNumbers.join(", ")} use canonical yard names ${uniqueStrings(data.rows.map((row) => row.canonicalYardName)).join(", ")} on list/detail/edit screens; stale names ${staleTerms.join(", ") || "from the ticket"} are absent.` : `Asset location QA failed: missing=${missingNumbers.join(",") || "none"} listMissing=${listMissing.join(",") || "none"} staleSeen=${staleSeen.join(",") || "none"} mismatched=${mismatchedRows.map((row) => row.number).join(",") || "none"}.`;',
2683
+ ' rows[activeIndex].artifact = proofPath;',
2684
+ ' rows[activeIndex].artifacts = artifacts;',
2685
+ ' rows[activeIndex].persisted_assertion = `Local QA Mongo joined ${data.rows.length} asset(s) to yards; ${data.rows.map((row) => `${row.number}:${row.currentLocationName}->${row.canonicalYardName}`).join("; ")}`;',
2686
+ ' if (!pass) rows[activeIndex].blocker = rows[activeIndex].caption;',
2687
+ ' }',
2688
+ ' matrix.rows = rows;',
2689
+ ' matrix.status = pass ? "pass" : "failed";',
2690
+ ' matrix.asset_location_proof = proofPath;',
2691
+ ' matrix.updated_at = new Date().toISOString();',
2692
+ ' writeJson(matrixPath, matrix);',
2693
+ ' return payload;',
2694
+ '}',
2566
2695
  'function updateMatrix(status, screenshotPath, caption, assertion) {',
2567
2696
  ' const matrix = readJson(matrixPath) || { status: "started", rows: [] };',
2568
2697
  ' matrix.workflow_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: new Date().toISOString() };',
@@ -2598,7 +2727,15 @@ function buildResolveIORunnerQaWorkflowProbeScript() {
2598
2727
  ' const caption = `Workflow route ready: ${targetRoute} loaded in authenticated local QA with live seeded data available.`;',
2599
2728
  ' await page.screenshot({ path: passScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
2600
2729
  ' updateMatrix("pass", passScreenshotPath, caption, "Authenticated customer workflow route loaded; deeper row-specific UI/data proof still required.");',
2601
- ' const result = { status: "pass", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption, page: summary, matrix: matrixPath };',
2730
+ ' const specializedProof = await runAssetLocationRowProof(page, summary);',
2731
+ ' if (specializedProof && specializedProof.status !== "pass") {',
2732
+ ' const result = { status: "failed", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption: "Asset location row proof failed; see qa-asset-location-proof.json.", page: summary, matrix: matrixPath, specializedProof: path.join(artifactDir, "qa-asset-location-proof.json") };',
2733
+ ' writeJson(resultPath, result);',
2734
+ ' console.error(JSON.stringify(result, null, 2));',
2735
+ ' process.exitCode = 1;',
2736
+ ' return;',
2737
+ ' }',
2738
+ ' const result = { status: "pass", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption: specializedProof ? "Asset location list/detail/edit QA passed with exact seeded assets and captions." : caption, page: summary, matrix: matrixPath, specializedProof: specializedProof ? path.join(artifactDir, "qa-asset-location-proof.json") : "" };',
2602
2739
  ' writeJson(resultPath, result);',
2603
2740
  ' console.log(JSON.stringify(result, null, 2));',
2604
2741
  ' } catch (error) {',