@nextcommerce/campaigns-os 1.60.0 → 1.61.0
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/CHANGELOG.md +126 -0
- package/README.md +5 -5
- package/agents/claude/CLAUDE.md +15 -0
- package/agents/codex/AGENTS.md +15 -0
- package/agents/copilot/copilot-instructions.md +15 -0
- package/agents/cursor/campaigns-os.mdc +15 -0
- package/compatibility.json +1 -1
- package/contracts/brand-theme-target-tokens.next-core.v0.json +95 -25
- package/contracts/campaign-cart-sdk-support-policy.v0.json +2 -2
- package/contracts/commerce-surface-catalog.json +27 -26
- package/contracts/release-ledger.json +768 -0
- package/contracts/supported-surface.json +4 -4
- package/contracts/template-brand-contract.shared-commerce.v0.json +1 -1
- package/docs/access-model.md +20 -0
- package/docs/brand-theme-bridge.md +62 -3
- package/docs/build-packet.md +28 -1
- package/docs/campaign-build-brief.md +16 -0
- package/docs/campaigns-os-build-flow.md +61 -2
- package/docs/campaignspec-authoring-examples.md +116 -0
- package/docs/design-source-package.md +46 -2
- package/docs/entry-points.md +188 -0
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/polish-evidence.md +40 -2
- package/docs/qa-and-test-orders.md +50 -2
- package/docs/quickstart.md +438 -0
- package/docs/readback.md +119 -6
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +3 -1
- package/docs/skills-revision.md +10 -10
- package/docs/source-adapters.md +213 -0
- package/package.json +10 -3
- package/schemas/campaign-design-source-package.v0.schema.json +8 -0
- package/schemas/campaigns-os-readback.v2.schema.json +177 -1
- package/schemas/source-html-manifest.v0.schema.json +3 -0
- package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +3 -3
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +4 -4
- package/skills/next-campaigns-os/SKILL.md +4 -3
- package/skills/next-campaigns-os-setup/SKILL.md +3 -3
- package/skills/next-campaigns-polish/SKILL.md +6 -9
- package/skills/next-campaigns-qa/SKILL.md +3 -3
- package/skills/next-campaigns-sdk-bump/SKILL.md +117 -24
- package/skills.json +11 -11
- package/src/brand-theme.mjs +413 -8
- package/src/brief-summary.mjs +6 -2
- package/src/browser-launch.mjs +2 -1
- package/src/build-brief.mjs +9 -2
- package/src/built-site-scope.mjs +2 -2
- package/src/cli.mjs +52 -13
- package/src/design-file-reference.mjs +93 -0
- package/src/design-source-package.mjs +55 -1
- package/src/doctor/checks.mjs +8 -74
- package/src/doctor/ci.mjs +112 -0
- package/src/doctor/inspect.mjs +18 -0
- package/src/doctor/source-provenance.mjs +74 -0
- package/src/evidence-debt.mjs +119 -0
- package/src/parity/compare.mjs +316 -0
- package/src/parity/design-access.mjs +54 -0
- package/src/parity/element-parity-record.mjs +569 -0
- package/src/parity/probe.mjs +205 -0
- package/src/parity/snapshot.mjs +110 -0
- package/src/png-header.mjs +87 -0
- package/src/polish-browser.mjs +186 -9
- package/src/polish-captures-record.mjs +52 -0
- package/src/polish-deadline.mjs +14 -0
- package/src/polish-gate.mjs +25 -6
- package/src/polish-node.mjs +81 -7
- package/src/polish-visual-fixtures.mjs +74 -0
- package/src/polish-visual.mjs +606 -0
- package/src/qa-analytics-correctness.mjs +14 -2
- package/src/qa-analytics-parity.mjs +94 -5
- package/src/qa-node.mjs +39 -6
- package/src/qc-accept.mjs +148 -6
- package/src/qc-check-registry.mjs +37 -1
- package/src/qc-results.mjs +404 -16
- package/src/readback.mjs +57 -13
- package/src/sdk-attribute-index.mjs +11 -11
- package/src/sdk-markup.mjs +77 -2
- package/src/sdk-storage-compatibility.mjs +94 -4
- package/src/source-html-manifest.mjs +7 -0
- package/src/source-html-server.mjs +129 -0
- package/src/stage-record.mjs +45 -7
- package/src/theme-gate.mjs +2 -2
- package/src/visual-diff.mjs +296 -0
- package/src/visual-reference.mjs +126 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Synthetic test data only. No browser, source HTML capture or merchant material.
|
|
2
|
+
import { deflateSync } from "node:zlib";
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { crc32, sha256Hex, boundedDimensions, VISUAL_MAX_REFERENCE_BYTES } from "./png-header.mjs";
|
|
7
|
+
import { buildCapturesRecord, planVisualCapture, writePolishPng, containedWritePath, VISUAL_VIEWPORTS, VISUAL_MASKS } from "./polish-visual.mjs";
|
|
8
|
+
|
|
9
|
+
export function pngChunk(type, data = Buffer.alloc(0)) {
|
|
10
|
+
if (!Buffer.isBuffer(data) || data.length > VISUAL_MAX_REFERENCE_BYTES - 12) throw new RangeError("too_large");
|
|
11
|
+
const chunk = Buffer.alloc(12 + data.length);
|
|
12
|
+
chunk.writeUInt32BE(data.length); chunk.write(type, 4, 4, "ascii"); data.copy(chunk, 8);
|
|
13
|
+
chunk.writeUInt32BE(crc32(chunk.subarray(4, chunk.length - 4)), chunk.length - 4);
|
|
14
|
+
return chunk;
|
|
15
|
+
}
|
|
16
|
+
export function createRgba(width, height, color = [255, 255, 255, 255]) {
|
|
17
|
+
boundedDimensions(width, height);
|
|
18
|
+
const data = Buffer.alloc(width * height * 4);
|
|
19
|
+
for (let i = 0; i < data.length; i += 4) data.set(color, i);
|
|
20
|
+
return { width, height, data };
|
|
21
|
+
}
|
|
22
|
+
export function createRgbaPng(width, height, color = [255, 255, 255, 255], { raw = null, idat = null, interlace = 0 } = {}) {
|
|
23
|
+
// Explicit IDAT permits deliberately oversized headers without allocating pixels.
|
|
24
|
+
if (!idat) boundedDimensions(width, height);
|
|
25
|
+
else if (![width, height].every((n) => Number.isSafeInteger(n) && n > 0 && n <= 0x7fffffff)) throw new RangeError("image_too_large");
|
|
26
|
+
const ihdr = Buffer.alloc(13);
|
|
27
|
+
ihdr.writeUInt32BE(width); ihdr.writeUInt32BE(height, 4); ihdr[8] = 8; ihdr[9] = 6; ihdr[12] = interlace;
|
|
28
|
+
if (!idat) {
|
|
29
|
+
if (!raw) {
|
|
30
|
+
raw = Buffer.alloc(height * (1 + width * 4));
|
|
31
|
+
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) raw.set(color, y * (1 + width * 4) + 1 + x * 4);
|
|
32
|
+
}
|
|
33
|
+
idat = deflateSync(raw);
|
|
34
|
+
}
|
|
35
|
+
return Buffer.concat([Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), pngChunk("IHDR", ihdr), pngChunk("IDAT", idat), pngChunk("IEND")]);
|
|
36
|
+
}
|
|
37
|
+
export const syntheticHash = (bytes) => `sha256:${sha256Hex(bytes)}`;
|
|
38
|
+
export const syntheticReport = () => ({ stages: { assembly: { build_fingerprint: `sha256:${"a".repeat(64)}` } } });
|
|
39
|
+
export function syntheticCapturesRecord(report, { routes = [{ page_id: "page", requested_route: "/page/" }], viewports = VISUAL_VIEWPORTS,
|
|
40
|
+
targetRepo = mkdtempSync(resolve(tmpdir(), "visual-record-")) } = {}) {
|
|
41
|
+
const plan = planVisualCapture({ route_scope: "all", routes, viewports });
|
|
42
|
+
if (!plan) return null;
|
|
43
|
+
const cells = plan.cells.map((cell) => {
|
|
44
|
+
const png = writePolishPng(targetRepo, "captures", createRgbaPng(cell.width, cell.height));
|
|
45
|
+
const shot = { status: "captured", png, full_height_css: cell.height,
|
|
46
|
+
crop: { x: 0, y: 0, width: cell.width, height: cell.height }, truncated: false };
|
|
47
|
+
return { ...cell, source_path_rel: null, source_sha256: null, status: "rendered",
|
|
48
|
+
networkidle: { status: "settled", duration_ms: 0 },
|
|
49
|
+
readiness: { fonts: { status: "settled", failed_faces: 0, waited_ms: 0 }, await: { status: "not_present", remaining_count: 0, waited_ms: 0 } },
|
|
50
|
+
external: cell.side === "source_html" ? { blocked_count: 0, blocked_websocket_count: 0, blocked_origins: [] } : null,
|
|
51
|
+
sections: { status: "no_section_keys", bands: [], duplicated_keys: [], capped: false },
|
|
52
|
+
actual: structuredClone(shot), stabilized: { ...structuredClone(shot), treatment: {
|
|
53
|
+
animations: "disabled", caret: "hide", mask_color: "#ff00ff", mask_selectors: VISUAL_MASKS, masks: [], mask_cap_reached: false } },
|
|
54
|
+
captured_at: "2026-01-01T00:00:00.000Z" };
|
|
55
|
+
});
|
|
56
|
+
return buildCapturesRecord({ buildFingerprint: report.stages.assembly.build_fingerprint, slug: "sample", routes, cells,
|
|
57
|
+
environment: { browser: "chromium/synthetic", device_scale_factor: 1 },
|
|
58
|
+
dependencies: { decoder: { status: "available", version: null }, diff: { status: "available", version: null } },
|
|
59
|
+
measuredAt: "2026-01-01T00:00:00.000Z" });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function syntheticVisualFixture() {
|
|
63
|
+
const targetRepo = mkdtempSync(resolve(tmpdir(), "visual-core-"));
|
|
64
|
+
const sourceRoot = containedWritePath(targetRepo, "source");
|
|
65
|
+
const htmlPath = containedWritePath(targetRepo, "source/page.html");
|
|
66
|
+
mkdirSync(sourceRoot);
|
|
67
|
+
const html = Buffer.from("<!doctype html><p>synthetic source</p>");
|
|
68
|
+
writeFileSync(htmlPath, html);
|
|
69
|
+
const linkedSource = { kind: "html_file", identity: { sha256: syntheticHash(html), sha256_recorded: syntheticHash(html) }, provenance: { class: "declared_source", reasons: [] } };
|
|
70
|
+
return { targetRepo, sourceRoot, linkedSource, report: syntheticReport(),
|
|
71
|
+
recorded: { captured_at: "2026-01-01T00:00:00.000Z", source_ref_id: "html", scale_factor: 1 },
|
|
72
|
+
write(name, bytes) { const path = containedWritePath(sourceRoot, name); mkdirSync(resolve(path, ".."), { recursive: true }); writeFileSync(path, bytes); return path; },
|
|
73
|
+
cleanup() { rmSync(targetRepo, { recursive: true, force: true }); } };
|
|
74
|
+
}
|