@nextcommerce/campaigns-os 1.33.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/AGENTS.md +204 -0
- package/CHANGELOG.md +5002 -0
- package/CONTEXT.md +685 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +368 -0
- package/agents/claude/CLAUDE.md +32 -0
- package/agents/codex/AGENTS.md +27 -0
- package/agents/copilot/copilot-instructions.md +14 -0
- package/agents/cursor/campaigns-os.mdc +13 -0
- package/bin/campaigns-os.mjs +38 -0
- package/campaign-spec/README.md +138 -0
- package/campaign-spec/dist/analytics-vocabulary.d.ts +47 -0
- package/campaign-spec/dist/analytics-vocabulary.js +74 -0
- package/campaign-spec/dist/index.d.ts +40 -0
- package/campaign-spec/dist/index.js +77 -0
- package/campaign-spec/dist/normalize.d.ts +22 -0
- package/campaign-spec/dist/normalize.js +41 -0
- package/campaign-spec/dist/routing.d.ts +190 -0
- package/campaign-spec/dist/routing.js +263 -0
- package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +42 -0
- package/campaign-spec/dist/rules/analytics-contract-shape.js +303 -0
- package/campaign-spec/dist/rules/assembly-hints-shape.d.ts +42 -0
- package/campaign-spec/dist/rules/assembly-hints-shape.js +191 -0
- package/campaign-spec/dist/rules/campaign-metadata.d.ts +14 -0
- package/campaign-spec/dist/rules/campaign-metadata.js +40 -0
- package/campaign-spec/dist/rules/checkout-has-success-url.d.ts +31 -0
- package/campaign-spec/dist/rules/checkout-has-success-url.js +66 -0
- package/campaign-spec/dist/rules/cycle-detection.d.ts +12 -0
- package/campaign-spec/dist/rules/cycle-detection.js +141 -0
- package/campaign-spec/dist/rules/design-source-shape.d.ts +29 -0
- package/campaign-spec/dist/rules/design-source-shape.js +142 -0
- package/campaign-spec/dist/rules/downsell-without-upsell.d.ts +11 -0
- package/campaign-spec/dist/rules/downsell-without-upsell.js +40 -0
- package/campaign-spec/dist/rules/exit-intent-validation.d.ts +23 -0
- package/campaign-spec/dist/rules/exit-intent-validation.js +147 -0
- package/campaign-spec/dist/rules/funnel-count.d.ts +9 -0
- package/campaign-spec/dist/rules/funnel-count.js +37 -0
- package/campaign-spec/dist/rules/funnel-hypothesis-length.d.ts +22 -0
- package/campaign-spec/dist/rules/funnel-hypothesis-length.js +60 -0
- package/campaign-spec/dist/rules/funnel-identity.d.ts +15 -0
- package/campaign-spec/dist/rules/funnel-identity.js +57 -0
- package/campaign-spec/dist/rules/funnel-weight-sum.d.ts +20 -0
- package/campaign-spec/dist/rules/funnel-weight-sum.js +66 -0
- package/campaign-spec/dist/rules/index.d.ts +69 -0
- package/campaign-spec/dist/rules/index.js +131 -0
- package/campaign-spec/dist/rules/offer-ref-integrity.d.ts +13 -0
- package/campaign-spec/dist/rules/offer-ref-integrity.js +59 -0
- package/campaign-spec/dist/rules/package-pricing-sanity.d.ts +12 -0
- package/campaign-spec/dist/rules/package-pricing-sanity.js +42 -0
- package/campaign-spec/dist/rules/page-count.d.ts +11 -0
- package/campaign-spec/dist/rules/page-count.js +31 -0
- package/campaign-spec/dist/rules/page-id-uniqueness.d.ts +14 -0
- package/campaign-spec/dist/rules/page-id-uniqueness.js +47 -0
- package/campaign-spec/dist/rules/promo-code-input-validation.d.ts +8 -0
- package/campaign-spec/dist/rules/promo-code-input-validation.js +126 -0
- package/campaign-spec/dist/rules/promo-codes-shape.d.ts +30 -0
- package/campaign-spec/dist/rules/promo-codes-shape.js +187 -0
- package/campaign-spec/dist/rules/route-field-ignored-for-page-type.d.ts +33 -0
- package/campaign-spec/dist/rules/route-field-ignored-for-page-type.js +81 -0
- package/campaign-spec/dist/rules/route-target-resolves.d.ts +32 -0
- package/campaign-spec/dist/rules/route-target-resolves.js +112 -0
- package/campaign-spec/dist/rules/schema-version.d.ts +21 -0
- package/campaign-spec/dist/rules/schema-version.js +53 -0
- package/campaign-spec/dist/rules/sdk-version.d.ts +26 -0
- package/campaign-spec/dist/rules/sdk-version.js +96 -0
- package/campaign-spec/dist/rules/shipping-countries-shape.d.ts +10 -0
- package/campaign-spec/dist/rules/shipping-countries-shape.js +30 -0
- package/campaign-spec/dist/rules/shipping-methods-present.d.ts +10 -0
- package/campaign-spec/dist/rules/shipping-methods-present.js +26 -0
- package/campaign-spec/dist/rules/store-profile-shape.d.ts +30 -0
- package/campaign-spec/dist/rules/store-profile-shape.js +127 -0
- package/campaign-spec/dist/rules/thank-you-requirement.d.ts +16 -0
- package/campaign-spec/dist/rules/thank-you-requirement.js +50 -0
- package/campaign-spec/dist/rules/unknown-top-level-fields.d.ts +28 -0
- package/campaign-spec/dist/rules/unknown-top-level-fields.js +114 -0
- package/campaign-spec/dist/rules/upsell-has-packages.d.ts +9 -0
- package/campaign-spec/dist/rules/upsell-has-packages.js +35 -0
- package/campaign-spec/dist/rules/upsell-routing-complete.d.ts +10 -0
- package/campaign-spec/dist/rules/upsell-routing-complete.js +45 -0
- package/campaign-spec/dist/rules/upsell-without-checkout.d.ts +11 -0
- package/campaign-spec/dist/rules/upsell-without-checkout.js +44 -0
- package/campaign-spec/dist/rules/variant-labels-shape.d.ts +28 -0
- package/campaign-spec/dist/rules/variant-labels-shape.js +86 -0
- package/campaign-spec/dist/sdk-version-parse.d.ts +43 -0
- package/campaign-spec/dist/sdk-version-parse.js +62 -0
- package/campaign-spec/dist/types.d.ts +674 -0
- package/campaign-spec/dist/types.js +40 -0
- package/campaign-spec/package.json +12 -0
- package/compatibility.json +25 -0
- package/contracts/agent-relevant-change-policy.v1.json +111 -0
- package/contracts/brand-theme-source-defaults.figma-sections-export.v0.json +32 -0
- package/contracts/brand-theme-target-tokens.next-core.v0.json +65 -0
- package/contracts/campaign-cart-checkout-field-contract.v0.json +45 -0
- package/contracts/campaign-cart-sdk-support-policy.v0.json +11 -0
- package/contracts/commerce-surface-catalog.json +2452 -0
- package/contracts/fixtures/orientation/canonicalization/v1.json +34 -0
- package/contracts/fixtures/orientation/envelope/current.json +95 -0
- package/contracts/fixtures/orientation/envelope/freshness_unknown.json +97 -0
- package/contracts/fixtures/orientation/envelope/legacy_baseline.json +95 -0
- package/contracts/fixtures/orientation/envelope/orientation_available.json +136 -0
- package/contracts/fixtures/orientation/envelope/recovered_interrupted_update.json +136 -0
- package/contracts/fixtures/orientation/envelope/refused.json +100 -0
- package/contracts/fixtures/orientation/envelope/restart_required.json +137 -0
- package/contracts/fixtures/orientation/envelope/updated.json +135 -0
- package/contracts/fixtures/orientation/hostile-target/README.md +61 -0
- package/contracts/fixtures/orientation/hostile-target/manifest.json +82 -0
- package/contracts/fixtures/orientation/hostile-target/repo/CHANGELOG.md +18 -0
- package/contracts/fixtures/orientation/hostile-target/repo/bin/intended.mjs +12 -0
- package/contracts/fixtures/orientation/hostile-target/repo/bin/tripwire.mjs +15 -0
- package/contracts/fixtures/orientation/hostile-target/repo/contracts/release-ledger.json +48 -0
- package/contracts/fixtures/orientation/hostile-target/repo/contracts/supported-surface.json +17 -0
- package/contracts/fixtures/orientation/hostile-target/repo/docs/example-contract.md +13 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-checkout +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-merge +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/pre-commit +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/package.json +15 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/tripwire.mjs +9 -0
- package/contracts/fixtures/orientation/hostile-target/repo/package.json +20 -0
- package/contracts/fixtures/orientation/hostile-target/repo/schemas/example.v0.schema.json +15 -0
- package/contracts/fixtures/orientation/release-gate/cases.json +1073 -0
- package/contracts/fixtures/runtime-recipe/accept/current.json +299 -0
- package/contracts/fixtures/runtime-recipe/accept/minimal.json +294 -0
- package/contracts/fixtures/runtime-recipe/dist-states.json +51 -0
- package/contracts/fixtures/runtime-recipe/manifest.json +85 -0
- package/contracts/fixtures/runtime-recipe/reject/advisory-enforcement.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/allowlist-without-hosts.json +297 -0
- package/contracts/fixtures/runtime-recipe/reject/committed-output-claim.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/engines-disagreement-warns.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/lifecycle-scripts-enabled.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/missing-required-field.json +251 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-kind.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-network-policy.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-output-check.json +310 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-revision.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-step-id.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unperformable-check-skipped.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unpinned-lockfile.json +299 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/assembly-report.json +180 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/build-context.json +115 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/doctor-output.json +29 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/qa-verdict.json +27 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/campaign-runtime.build.json +141 -0
- package/contracts/migration-sidecar-bundle.v0.json +149 -0
- package/contracts/orientation-limits.v1.json +41 -0
- package/contracts/orientation-reason-codes.v1.json +196 -0
- package/contracts/private-template-sources.json +8 -0
- package/contracts/release-ledger.json +4598 -0
- package/contracts/reserved-skill-names.json +13 -0
- package/contracts/runtime-recipe.campaigns-os-node-v1.json +299 -0
- package/contracts/supported-surface.json +180 -0
- package/contracts/template-brand-contract.apollo-mv-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.apollo.v0.json +27 -0
- package/contracts/template-brand-contract.demeter.v0.json +27 -0
- package/contracts/template-brand-contract.olympus-mv-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.olympus-mv-two-step.v0.json +28 -0
- package/contracts/template-brand-contract.olympus.v0.json +27 -0
- package/contracts/template-brand-contract.shared-commerce.v0.json +190 -0
- package/contracts/template-brand-contract.shop-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.shop-three-step.v0.json +29 -0
- package/contracts/template-slot-manifest.apollo-mv-single-step.v0.json +14 -0
- package/contracts/template-slot-manifest.apollo.v0.json +12 -0
- package/contracts/template-slot-manifest.demeter.v0.json +30 -0
- package/contracts/template-slot-manifest.olympus-mv-single-step.v0.json +14 -0
- package/contracts/template-slot-manifest.olympus-mv-two-step.v0.json +15 -0
- package/contracts/template-slot-manifest.olympus.v0.json +12 -0
- package/contracts/template-slot-manifest.shared-content-core.v0.json +4254 -0
- package/contracts/template-slot-manifest.shop-single-step.v0.json +47 -0
- package/contracts/template-slot-manifest.shop-three-step.v0.json +33 -0
- package/docs/brand-theme-bridge.md +159 -0
- package/docs/build-packet.md +1300 -0
- package/docs/campaign-build-brief.md +145 -0
- package/docs/campaign-standardization-report.md +329 -0
- package/docs/campaigns-os-build-flow.md +117 -0
- package/docs/design-source-package.md +784 -0
- package/docs/legacy-migration.md +58 -0
- package/docs/migration-sidecar-bundle.md +139 -0
- package/docs/orientation-contract-reference.md +1220 -0
- package/docs/polish-evidence.md +502 -0
- package/docs/qa-and-test-orders.md +1691 -0
- package/docs/release-ledger-authoring-guide.md +274 -0
- package/docs/runtime-readiness.md +211 -0
- package/docs/supported-surface.md +83 -0
- package/docs/versioning.md +55 -0
- package/docs/workflow-findings-sidecar.md +588 -0
- package/package.json +135 -0
- package/prompts/first-build.md +27 -0
- package/prompts/friction-log.md +30 -0
- package/schemas/campaign-build-brief.v1.schema.json +149 -0
- package/schemas/campaign-design-source-package.v0.schema.json +697 -0
- package/schemas/campaign-runtime-assembly-report.v0.schema.json +390 -0
- package/schemas/campaign-runtime-build-context.v0.schema.json +339 -0
- package/schemas/campaign-runtime-build-packet.v0.schema.json +452 -0
- package/schemas/campaign-spec.v4.schema.json +582 -0
- package/schemas/campaigns-os-doctor-output.v0.schema.json +43 -0
- package/schemas/campaigns-os-legacy-migration-inventory.v0.schema.json +112 -0
- package/schemas/campaigns-os-legacy-provisioning-plan.v0.schema.json +38 -0
- package/schemas/campaigns-os-legacy-provisioning-receipt.v0.schema.json +57 -0
- package/schemas/campaigns-os-qa-verdict-sidecar.v0.schema.json +150 -0
- package/schemas/campaigns-os-qa-verdict.v0.schema.json +438 -0
- package/schemas/campaigns-os-release-ledger.v1.schema.json +162 -0
- package/schemas/campaigns-os-run-record.v0.schema.json +343 -0
- package/schemas/campaigns-os-runtime-recipe.v1.schema.json +313 -0
- package/schemas/campaigns-os-sidecar-bundle-conformance.v0.schema.json +78 -0
- package/schemas/campaigns-os-tooling-orientation.v1.schema.json +381 -0
- package/schemas/campaigns-os-workflow-finding.v0.schema.json +134 -0
- package/schemas/source-html-manifest.v0.schema.json +252 -0
- package/skills/next-campaigns-build/SKILL.md +73 -0
- package/skills/next-campaigns-os/SKILL.md +101 -0
- package/skills/next-campaigns-os/references/session-intake.md +160 -0
- package/skills/next-campaigns-os-setup/SKILL.md +22 -0
- package/skills/next-campaigns-polish/SKILL.md +145 -0
- package/skills/next-campaigns-qa/SKILL.md +92 -0
- package/skills.json +56 -0
- package/skills.sh +64 -0
- package/src/adapter-decision-contract.mjs +333 -0
- package/src/brand-theme.mjs +1151 -0
- package/src/browser-launch.mjs +79 -0
- package/src/build-brief.mjs +781 -0
- package/src/built-site-scope.mjs +312 -0
- package/src/campaign-ecosystem.mjs +734 -0
- package/src/campaign-identity.mjs +405 -0
- package/src/campaign-workspace.mjs +127 -0
- package/src/checkpoint-waiver.mjs +302 -0
- package/src/cli.mjs +13442 -0
- package/src/commercial-journey.mjs +1119 -0
- package/src/commercial-parity.mjs +965 -0
- package/src/consent.mjs +347 -0
- package/src/content-residue.mjs +322 -0
- package/src/deadline.mjs +81 -0
- package/src/design-source-package.mjs +2604 -0
- package/src/deviation.mjs +107 -0
- package/src/doctor-check-registry.mjs +49 -0
- package/src/doctor-sidecar.mjs +106 -0
- package/src/finding-cause.mjs +557 -0
- package/src/findings.mjs +326 -0
- package/src/fs-identity.mjs +68 -0
- package/src/gate-actions.mjs +105 -0
- package/src/html-scan.mjs +53 -0
- package/src/install-mode.mjs +272 -0
- package/src/legacy-migration.d.ts +128 -0
- package/src/legacy-migration.mjs +510 -0
- package/src/lifecycle.mjs +338 -0
- package/src/local-proof.mjs +401 -0
- package/src/map-pin-writeback.mjs +210 -0
- package/src/orchestration-stage-contract.mjs +81 -0
- package/src/package-install-fixture.mjs +33 -0
- package/src/page-kit-build-summary.mjs +175 -0
- package/src/page-kit-campaign-config.mjs +57 -0
- package/src/page-kit-sdk-version.mjs +392 -0
- package/src/page-kit-store-profile.mjs +369 -0
- package/src/page-kit-sync.mjs +162 -0
- package/src/polish-browser.mjs +867 -0
- package/src/polish-capture.mjs +1094 -0
- package/src/polish-deadline.mjs +51 -0
- package/src/polish-gate.mjs +739 -0
- package/src/polish-node.mjs +639 -0
- package/src/polish-page-load.mjs +1100 -0
- package/src/private-template-source.mjs +237 -0
- package/src/proof-policy.mjs +82 -0
- package/src/qa-analytics-correctness.mjs +307 -0
- package/src/qa-analytics-errors.mjs +38 -0
- package/src/qa-analytics-parity.mjs +699 -0
- package/src/qa-binding-evidence.mjs +140 -0
- package/src/qa-browser.mjs +6608 -0
- package/src/qa-cart-entry.mjs +406 -0
- package/src/qa-commercial-parity.mjs +641 -0
- package/src/qa-node.mjs +3620 -0
- package/src/qa-order-bump.mjs +381 -0
- package/src/qa-parity-capture.mjs +428 -0
- package/src/qa-parity-fixture.mjs +359 -0
- package/src/qa-publish.mjs +362 -0
- package/src/qa-purchase-data-layer.mjs +263 -0
- package/src/qa-route-probe.mjs +272 -0
- package/src/qa-sidecar.mjs +188 -0
- package/src/qa-test-order-topology.mjs +207 -0
- package/src/qa-url-privacy.mjs +13 -0
- package/src/qa-verdict-discovery.mjs +192 -0
- package/src/qa-verdict-publish.mjs +105 -0
- package/src/qa-verdict.mjs +287 -0
- package/src/remit.mjs +388 -0
- package/src/repo-scan.mjs +83 -0
- package/src/route-identity.mjs +133 -0
- package/src/run-record-closeout.mjs +229 -0
- package/src/run-record.mjs +839 -0
- package/src/run-session.mjs +226 -0
- package/src/runtime-state-ignore.mjs +113 -0
- package/src/sdk-attribute-index.mjs +212 -0
- package/src/sdk-markup.mjs +358 -0
- package/src/sdk-meta-tags.mjs +52 -0
- package/src/shell-token.mjs +7 -0
- package/src/sidecar-bundle.mjs +399 -0
- package/src/source-asset-crawl.mjs +469 -0
- package/src/source-html-intake.mjs +627 -0
- package/src/source-html-manifest.mjs +276 -0
- package/src/source-prep.mjs +284 -0
- package/src/spec-derive-store.mjs +431 -0
- package/src/spec-derive.mjs +514 -0
- package/src/spec-fetch.mjs +66 -0
- package/src/spec-hash.mjs +48 -0
- package/src/spec-identity.mjs +27 -0
- package/src/stage-ledger.mjs +509 -0
- package/src/standardization-report.mjs +1297 -0
- package/src/template-brand-contract.mjs +473 -0
- package/src/template-freshness.mjs +196 -0
- package/src/template-reference.mjs +81 -0
- package/src/template-slot-manifest.mjs +150 -0
- package/src/text-safety.mjs +62 -0
- package/src/theme-gate.mjs +185 -0
- package/src/upsell-selector-scope.mjs +299 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type surface for the CampaignSpec contract layer.
|
|
3
|
+
*
|
|
4
|
+
* The CampaignSpec is the central authoring contract of this repo (see
|
|
5
|
+
* ../CONTEXT.md). v4.3 is the authoring shape; v4.2 funnels[] is the canonical
|
|
6
|
+
* internal shape that rules operate on (after normalize()).
|
|
7
|
+
*
|
|
8
|
+
* Types here are intentionally permissive on optional fields — the rule
|
|
9
|
+
* registry catches missing/malformed fields, rather than the type system
|
|
10
|
+
* refusing to compile against in-progress Map Builder drafts.
|
|
11
|
+
*/
|
|
12
|
+
export type Severity = 'error' | 'warning';
|
|
13
|
+
export type Tag = 'fast' | 'structure' | 'references' | 'meta-tags' | 'tracking' | 'spec-only' | 'requires-complete-spec';
|
|
14
|
+
/**
|
|
15
|
+
* A rule's structured output. One shape across every consumer (Map Builder
|
|
16
|
+
* field-level UI, QA verdict, CLI output, compiler exceptions).
|
|
17
|
+
*
|
|
18
|
+
* `path` is a JSON Pointer locator into the normalized spec, e.g.
|
|
19
|
+
* "/funnels/0/pages/2/route". Enables field-level UI without rule-specific
|
|
20
|
+
* wiring.
|
|
21
|
+
*
|
|
22
|
+
* `data` carries rule-specific structured detail (e.g. CycleDetection
|
|
23
|
+
* includes the offending cycle's page IDs).
|
|
24
|
+
*/
|
|
25
|
+
export interface Violation {
|
|
26
|
+
ruleId: string;
|
|
27
|
+
severity: Severity;
|
|
28
|
+
message: string;
|
|
29
|
+
path: string;
|
|
30
|
+
data?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The unit of composition for spec validation.
|
|
34
|
+
*
|
|
35
|
+
* Pure: takes only a normalized CampaignSpec, returns Violations. No context
|
|
36
|
+
* bag, no live data dependency. Mode flags become tag filters at the call
|
|
37
|
+
* site; rule parameters bind at registration time.
|
|
38
|
+
*
|
|
39
|
+
* `severity` is the rule's default. Individual violations can override
|
|
40
|
+
* (e.g. CycleDetection emits warnings for self-loops, errors for multi-page
|
|
41
|
+
* cycles).
|
|
42
|
+
*/
|
|
43
|
+
export interface Rule {
|
|
44
|
+
id: string;
|
|
45
|
+
severity: Severity;
|
|
46
|
+
tags: Tag[];
|
|
47
|
+
check(spec: CampaignSpec): Violation[];
|
|
48
|
+
}
|
|
49
|
+
/** A `Rule[]`. Compose with plain array operations. */
|
|
50
|
+
export type RuleSet = Rule[];
|
|
51
|
+
export type PageType = 'presell' | 'landing'
|
|
52
|
+
/**
|
|
53
|
+
* The bundle-selection step of a two-step family: a template-owned commerce
|
|
54
|
+
* page where the shopper picks a package before checkout. Distinct from
|
|
55
|
+
* 'landing' — a landing page is design-source-owned and carries no SDK cart
|
|
56
|
+
* selection, so commerce gates (brand-theme load order, logo and
|
|
57
|
+
* computed-style residue, bump pricing) apply to 'select' and not to
|
|
58
|
+
* 'landing'. Routes forward like a landing page, through `next_page`: it is
|
|
59
|
+
* the first step of a two-step checkout flow, and it neither takes payment
|
|
60
|
+
* nor presents an accept/decline offer, so a `success_url` or `on_accept`
|
|
61
|
+
* here is ignored for routing (campaigns-os#234).
|
|
62
|
+
*
|
|
63
|
+
* Not to be confused with the page ID "select", which several family
|
|
64
|
+
* contracts use for this page and which is a separate namespace.
|
|
65
|
+
*/
|
|
66
|
+
| 'select' | 'checkout' | 'upsell' | 'downsell' | 'thankyou';
|
|
67
|
+
/**
|
|
68
|
+
* Offer condition/benefit blocks, as exported by the Map Builder. Both share
|
|
69
|
+
* the { type, value, description } shape (e.g. condition
|
|
70
|
+
* `{ type: 'count', value: 2 }`, benefit
|
|
71
|
+
* `{ type: 'package_percentage', value: '20.00' }`).
|
|
72
|
+
*/
|
|
73
|
+
export interface OfferCondition {
|
|
74
|
+
type?: string;
|
|
75
|
+
value?: number | string | null;
|
|
76
|
+
description?: string | null;
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
export interface OfferBenefit {
|
|
80
|
+
type?: string;
|
|
81
|
+
value?: number | string | null;
|
|
82
|
+
description?: string | null;
|
|
83
|
+
[key: string]: unknown;
|
|
84
|
+
}
|
|
85
|
+
export interface PageOffer {
|
|
86
|
+
ref_id: string | number;
|
|
87
|
+
code?: string | null;
|
|
88
|
+
name?: string | null;
|
|
89
|
+
type?: string;
|
|
90
|
+
condition?: OfferCondition;
|
|
91
|
+
benefit?: OfferBenefit;
|
|
92
|
+
[key: string]: unknown;
|
|
93
|
+
}
|
|
94
|
+
export interface PagePackage {
|
|
95
|
+
ref_id?: string | number;
|
|
96
|
+
/**
|
|
97
|
+
* Package quantity. The real export key is `qty` (206/211 real package
|
|
98
|
+
* entries) — NOT `quantity`, which never appears in exports.
|
|
99
|
+
*/
|
|
100
|
+
qty?: number;
|
|
101
|
+
name?: string | null;
|
|
102
|
+
price?: number | string | null;
|
|
103
|
+
price_retail?: number | string | null;
|
|
104
|
+
product_name?: string | null;
|
|
105
|
+
product_sku?: string | null;
|
|
106
|
+
product_variant_name?: string | null;
|
|
107
|
+
product_purchase_availability?: string | null;
|
|
108
|
+
product_inventory_availability?: string | null;
|
|
109
|
+
image?: string | null;
|
|
110
|
+
/**
|
|
111
|
+
* Boolean role trio. There is no `role` field in real exports — a
|
|
112
|
+
* package's role is expressed by is_upsell / is_order_bump /
|
|
113
|
+
* default_selected flags (absent means a main package).
|
|
114
|
+
*/
|
|
115
|
+
is_upsell?: boolean;
|
|
116
|
+
is_order_bump?: boolean;
|
|
117
|
+
default_selected?: boolean;
|
|
118
|
+
variant_attributes?: Array<{
|
|
119
|
+
code?: string;
|
|
120
|
+
name?: string;
|
|
121
|
+
value?: string;
|
|
122
|
+
[key: string]: unknown;
|
|
123
|
+
}>;
|
|
124
|
+
is_recurring?: boolean;
|
|
125
|
+
price_recurring?: number | string | null;
|
|
126
|
+
interval?: string | null;
|
|
127
|
+
interval_count?: number | null;
|
|
128
|
+
[key: string]: unknown;
|
|
129
|
+
}
|
|
130
|
+
export interface ExitIntent {
|
|
131
|
+
enabled?: boolean;
|
|
132
|
+
offer_ref_id?: string | number;
|
|
133
|
+
offer_code?: string;
|
|
134
|
+
[key: string]: unknown;
|
|
135
|
+
}
|
|
136
|
+
export interface PromoCodeInput {
|
|
137
|
+
enabled?: boolean;
|
|
138
|
+
mode?: 'mapped_offer' | string;
|
|
139
|
+
offer_ref_id?: string | number;
|
|
140
|
+
offer_code?: string;
|
|
141
|
+
[key: string]: unknown;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Pointer to the design artifact that supplies the prepared HTML for this
|
|
145
|
+
* page. Read by figma-sections-export (and future design-tool exporters) to
|
|
146
|
+
* locate the source frames; read by campaigns-os doctor to decide whether a
|
|
147
|
+
* missing source-html manifest is "designer hasn't exported yet" (blocker
|
|
148
|
+
* with run-export-first guidance) vs. plain `collect-inputs`.
|
|
149
|
+
*
|
|
150
|
+
* Today `figma` is the only supported `type`; the field is structured this
|
|
151
|
+
* way so future tools (Penpot, Sketch, hand-authored HTML, AI-generated)
|
|
152
|
+
* slot in without a schema break.
|
|
153
|
+
*
|
|
154
|
+
* `file_url` is the design-tool file URL (canonical identity of the file).
|
|
155
|
+
* `breakpoints` carry per-viewport pointers — for Figma, these are selection
|
|
156
|
+
* URLs that already encode node IDs, which is what designers copy via
|
|
157
|
+
* "Copy link to selection". Empty/missing breakpoints are valid during
|
|
158
|
+
* draft authoring; rules surface incompleteness as warnings, not errors.
|
|
159
|
+
*/
|
|
160
|
+
export interface DesignSourceBreakpoints {
|
|
161
|
+
desktop?: string;
|
|
162
|
+
tablet?: string;
|
|
163
|
+
mobile?: string;
|
|
164
|
+
[key: string]: unknown;
|
|
165
|
+
}
|
|
166
|
+
export interface DesignSource {
|
|
167
|
+
type: 'figma' | string;
|
|
168
|
+
file_url: string;
|
|
169
|
+
breakpoints?: DesignSourceBreakpoints;
|
|
170
|
+
notes?: string;
|
|
171
|
+
[key: string]: unknown;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Per-page hint declaring which UI variant the build should render this
|
|
175
|
+
* page as. Today only upsell pages have meaningful variants: the
|
|
176
|
+
* olympus-mv-single-step family ships with `mv` (multi-quantity tier
|
|
177
|
+
* pills), `bundle_tier_pills`, `bundle_tier_cards`, and a `single`
|
|
178
|
+
* fallback, but the spec previously carried no way to declare which one
|
|
179
|
+
* each OTO should use, forcing per-page decisions at build time.
|
|
180
|
+
*
|
|
181
|
+
* Like preferred_template_family, this is a HINT — the build agent
|
|
182
|
+
* uses it as the default when no per-page override is given. CLI args
|
|
183
|
+
* and operator overrides win.
|
|
184
|
+
*
|
|
185
|
+
* Open-string at the type level so future template patterns slot in
|
|
186
|
+
* without a schema break; validation rule narrows to the known set.
|
|
187
|
+
*/
|
|
188
|
+
export type UpsellTemplatePattern = 'mv' | 'bundle_tier_pills' | 'bundle_tier_cards' | 'single' | (string & {});
|
|
189
|
+
/**
|
|
190
|
+
* Per-page MV upsell tier range. Pairs with `upsell_template_pattern: 'mv'`
|
|
191
|
+
* (or any tier-based variant) to declare the inclusive `{min, max}`
|
|
192
|
+
* quantity-tier range the page should render.
|
|
193
|
+
*
|
|
194
|
+
* Slice 4b context: the olympus-mv-single-step family ships with a fixed
|
|
195
|
+
* pill ladder but the spec previously carried no way to declare which
|
|
196
|
+
* subset each OTO should render.
|
|
197
|
+
* Author-time: "Upsell 1 ranges 1-5, Upsell 2 ranges 2-4". Build agent
|
|
198
|
+
* reads the range to scope the pill set; the source HTML's static
|
|
199
|
+
* markup is overridden when the hint disagrees.
|
|
200
|
+
*
|
|
201
|
+
* HINT semantics match the other authoring-time fields: validation
|
|
202
|
+
* warns when shape is malformed or `min > max`, but never blocks a
|
|
203
|
+
* build. Hand-authored or operator-supplied tier counts at build time
|
|
204
|
+
* still win.
|
|
205
|
+
*/
|
|
206
|
+
export interface UpsellMvTiers {
|
|
207
|
+
min: number;
|
|
208
|
+
max: number;
|
|
209
|
+
[key: string]: unknown;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Per-page MV upsell variant column labels. Used by template families
|
|
213
|
+
* that render multi-attribute variant tables (olympus-mv-single-step
|
|
214
|
+
* tier-cards) where columns map to product attributes like size,
|
|
215
|
+
* color, or flavor. The starter HTML often assumes two columns —
|
|
216
|
+
* single-attribute products (size-only, color-only) end up with an
|
|
217
|
+
* empty second column.
|
|
218
|
+
*
|
|
219
|
+
* Slice 4e context: declare `{primary: "Size"}` and the build drops
|
|
220
|
+
* the second column; declare `{primary: "Size", secondary: "Color"}`
|
|
221
|
+
* and both columns render with the spec-declared labels. HINT
|
|
222
|
+
* semantics: warning-severity validation, never blocks a build,
|
|
223
|
+
* CLI/operator overrides at build time win.
|
|
224
|
+
*/
|
|
225
|
+
export interface VariantLabels {
|
|
226
|
+
primary: string;
|
|
227
|
+
secondary?: string;
|
|
228
|
+
[key: string]: unknown;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Per-funnel promo-code roster. Replaces the hardcoded `sales` array
|
|
232
|
+
* in the starter templates' promo-banner.js / promo-timer.js so each
|
|
233
|
+
* merchant carries their own seasonal calendar in the spec rather
|
|
234
|
+
* than inheriting demo defaults.
|
|
235
|
+
*
|
|
236
|
+
* Slice 4c context: every campaign ships with the demo SUMMER26 /
|
|
237
|
+
* BF26 / etc. codes burned into promo-banner.js source. The
|
|
238
|
+
* build-side replacement step (next-campaigns-build skill addendum)
|
|
239
|
+
* reads `funnels[].promo_codes` and regenerates the sales array in
|
|
240
|
+
* the assembled JS.
|
|
241
|
+
*
|
|
242
|
+
* Per-funnel scope: A/B funnels can run different rosters. `id` and
|
|
243
|
+
* `code` are required identity; visual presentation fields are
|
|
244
|
+
* optional and mirror the existing template shape so the build can
|
|
245
|
+
* do a clean array replace. `starts_at` / `ends_at` are ISO date
|
|
246
|
+
* strings — missing means "active whenever selected." Array order
|
|
247
|
+
* decides priority (first matching date range wins).
|
|
248
|
+
*/
|
|
249
|
+
export interface PromoCode {
|
|
250
|
+
id: string;
|
|
251
|
+
code: string;
|
|
252
|
+
starts_at?: string;
|
|
253
|
+
ends_at?: string;
|
|
254
|
+
title?: string;
|
|
255
|
+
emoji?: string;
|
|
256
|
+
offer1?: string;
|
|
257
|
+
offer2?: string;
|
|
258
|
+
top_bar_bg?: string;
|
|
259
|
+
highlight_color?: string;
|
|
260
|
+
banner_text?: string;
|
|
261
|
+
banner_text_sec?: string;
|
|
262
|
+
limited_time?: string;
|
|
263
|
+
[key: string]: unknown;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* SDK-layer page type projection. `thankyou` is the canonical terminal
|
|
267
|
+
* page.type; `receipt` is its SDK projection and lives HERE (and in
|
|
268
|
+
* meta_tags["next-page-type"]), never in page.type. Observed values across
|
|
269
|
+
* the real corpus: product, checkout, upsell, receipt.
|
|
270
|
+
*/
|
|
271
|
+
export type SdkPageType = 'product' | 'checkout' | 'upsell' | 'receipt';
|
|
272
|
+
/**
|
|
273
|
+
* Per-page SDK hints. Real export subkeys are `sdk_page_type` + `meta_tags`
|
|
274
|
+
* ONLY. `frontmatter` and `template_family` are template-handoff extensions
|
|
275
|
+
* carried by the contracts/fixtures/campaign-specs agent-contract fixtures
|
|
276
|
+
* (and blessed as optional extensions by schemas/campaign-spec.v4.schema.json);
|
|
277
|
+
* they are not Map Builder export fields.
|
|
278
|
+
*/
|
|
279
|
+
export interface SdkHints {
|
|
280
|
+
sdk_page_type?: SdkPageType;
|
|
281
|
+
meta_tags?: Record<string, string>;
|
|
282
|
+
frontmatter?: Record<string, unknown>;
|
|
283
|
+
template_family?: string;
|
|
284
|
+
[key: string]: unknown;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* DERIVED routing projection (present on 135/195 real pages). The declared
|
|
288
|
+
* routing fields (next_page / on_accept / on_decline / success_url) hold
|
|
289
|
+
* PAGE IDS in every real occurrence; this object is where the corresponding
|
|
290
|
+
* route paths (`*_route`, trailing-slash) and filenames (`*_filename`,
|
|
291
|
+
* route path or legacy .html) live.
|
|
292
|
+
*/
|
|
293
|
+
export interface ResolvedRouting {
|
|
294
|
+
accept?: string;
|
|
295
|
+
decline?: string;
|
|
296
|
+
success?: string;
|
|
297
|
+
next_page?: string;
|
|
298
|
+
accept_route?: string;
|
|
299
|
+
decline_route?: string;
|
|
300
|
+
success_route?: string;
|
|
301
|
+
next_page_route?: string;
|
|
302
|
+
accept_filename?: string;
|
|
303
|
+
decline_filename?: string;
|
|
304
|
+
success_filename?: string;
|
|
305
|
+
next_page_filename?: string;
|
|
306
|
+
[key: string]: unknown;
|
|
307
|
+
}
|
|
308
|
+
/** Per-page design pointers (93/195 real pages). */
|
|
309
|
+
export interface DesignHooks {
|
|
310
|
+
figma_frame_url?: string | null;
|
|
311
|
+
component_slots?: unknown;
|
|
312
|
+
[key: string]: unknown;
|
|
313
|
+
}
|
|
314
|
+
export interface Page {
|
|
315
|
+
id: string;
|
|
316
|
+
type: PageType;
|
|
317
|
+
label?: string;
|
|
318
|
+
order?: number;
|
|
319
|
+
/** True on funnel entry pages (178/195 real pages carry the flag). */
|
|
320
|
+
is_entry?: boolean;
|
|
321
|
+
/**
|
|
322
|
+
* Authored route field: usually a trailing-slash route path
|
|
323
|
+
* ("checkout/"), occasionally empty, a bare token, or a legacy .html
|
|
324
|
+
* filename. This — not the declared routing fields — is where authored
|
|
325
|
+
* route paths live.
|
|
326
|
+
*/
|
|
327
|
+
page_url?: string;
|
|
328
|
+
next_page?: string;
|
|
329
|
+
success_url?: string;
|
|
330
|
+
on_accept?: string;
|
|
331
|
+
on_decline?: string;
|
|
332
|
+
resolved_routing?: ResolvedRouting;
|
|
333
|
+
packages?: PagePackage[];
|
|
334
|
+
offers?: PageOffer[];
|
|
335
|
+
exit_intent?: ExitIntent;
|
|
336
|
+
promo_code_input?: PromoCodeInput;
|
|
337
|
+
sdk_hints?: SdkHints;
|
|
338
|
+
design_hooks?: DesignHooks;
|
|
339
|
+
design_source?: DesignSource;
|
|
340
|
+
/**
|
|
341
|
+
* Per-page UI variant hint. Meaningful only on upsell-type pages
|
|
342
|
+
* today; on non-upsell pages the validation rule warns.
|
|
343
|
+
*/
|
|
344
|
+
upsell_template_pattern?: UpsellTemplatePattern;
|
|
345
|
+
/**
|
|
346
|
+
* Per-page MV upsell tier range. Pairs with the `mv` UI pattern (or any
|
|
347
|
+
* tier-based variant) to declare the inclusive `{min, max}` quantity-tier
|
|
348
|
+
* range the build should render. Meaningful on upsell pages; validation
|
|
349
|
+
* warns when set elsewhere or when shape is malformed.
|
|
350
|
+
*/
|
|
351
|
+
upsell_mv_tiers?: UpsellMvTiers;
|
|
352
|
+
/**
|
|
353
|
+
* Per-page MV upsell variant column labels (Slice 4e). Meaningful
|
|
354
|
+
* only on upsell pages today; validation warns when set elsewhere
|
|
355
|
+
* or when primary is missing/empty.
|
|
356
|
+
*/
|
|
357
|
+
variant_labels?: VariantLabels;
|
|
358
|
+
[key: string]: unknown;
|
|
359
|
+
}
|
|
360
|
+
export interface Funnel {
|
|
361
|
+
id: string;
|
|
362
|
+
name?: string;
|
|
363
|
+
hypothesis?: string;
|
|
364
|
+
weight?: number;
|
|
365
|
+
pages?: Page[];
|
|
366
|
+
/**
|
|
367
|
+
* Per-funnel promo-code roster (Slice 4c). The build-side
|
|
368
|
+
* replacement step regenerates promo-banner.js / promo-timer.js
|
|
369
|
+
* sales arrays from this list when present.
|
|
370
|
+
*/
|
|
371
|
+
promo_codes?: PromoCode[];
|
|
372
|
+
[key: string]: unknown;
|
|
373
|
+
}
|
|
374
|
+
/** A package nested under a root offer (package_id identity). */
|
|
375
|
+
export interface OfferPackage {
|
|
376
|
+
package_id?: number | string;
|
|
377
|
+
package_name?: string | null;
|
|
378
|
+
package_image?: string | null;
|
|
379
|
+
product_name?: string | null;
|
|
380
|
+
product_variant_name?: string | null;
|
|
381
|
+
unit_price?: number | string | null;
|
|
382
|
+
unit_price_before_discount?: number | string | null;
|
|
383
|
+
package_price?: number | string | null;
|
|
384
|
+
package_price_before_discount?: number | string | null;
|
|
385
|
+
package_unit_qty?: number | null;
|
|
386
|
+
[key: string]: unknown;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Root offer catalog entry. `ref_id` is the SOLE offer identity — all 236
|
|
390
|
+
* real offer occurrences use ref_id (+ code, often null, + name). A
|
|
391
|
+
* `package_ref_id` key never appears in real exports and is not part of
|
|
392
|
+
* the contract; offer→package links are the nested `packages[]` with
|
|
393
|
+
* package_id identity.
|
|
394
|
+
*/
|
|
395
|
+
export interface Offer {
|
|
396
|
+
ref_id: string | number;
|
|
397
|
+
code?: string | null;
|
|
398
|
+
name?: string | null;
|
|
399
|
+
type?: string;
|
|
400
|
+
condition?: OfferCondition;
|
|
401
|
+
benefit?: OfferBenefit;
|
|
402
|
+
packages?: OfferPackage[];
|
|
403
|
+
/** Offer-scoped shipping method pricing. */
|
|
404
|
+
shipping_methods?: unknown[];
|
|
405
|
+
[key: string]: unknown;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Optional hint declaring which starter template family the campaign was
|
|
409
|
+
* authored against. Doctrine: template family is a build-time decision,
|
|
410
|
+
* not a spec-time decision; this field is a HINT that the build agent
|
|
411
|
+
* uses as the default when no `--template-family` CLI override is given.
|
|
412
|
+
*
|
|
413
|
+
* Pre-Slice 4a, this field already existed as an undocumented convention
|
|
414
|
+
* read by campaigns-os/src/cli.mjs preferredTemplateFamily(); Slice 4a
|
|
415
|
+
* blesses it in the schema, adds Map Builder UI to author it, and adds
|
|
416
|
+
* a validation rule that warns if the value isn't a recognized family.
|
|
417
|
+
*
|
|
418
|
+
* Operators / agents always retain veto power via `--template-family`;
|
|
419
|
+
* the hint never silently locks the build.
|
|
420
|
+
*
|
|
421
|
+
* Known families track the certified Campaigns OS commerce-surface catalog.
|
|
422
|
+
* The AssemblyHintsShape test pins this list to the vendored catalog so a
|
|
423
|
+
* newly certified family cannot start producing a false unknown-family
|
|
424
|
+
* warning.
|
|
425
|
+
*/
|
|
426
|
+
export declare const KNOWN_TEMPLATE_FAMILY_HINTS: readonly ["apollo", "apollo-mv-single-step", "demeter", "olympus", "olympus-mv-single-step", "olympus-mv-two-step", "shop-single-step", "shop-three-step"];
|
|
427
|
+
export type TemplateFamilyHint = typeof KNOWN_TEMPLATE_FAMILY_HINTS[number] | (string & {});
|
|
428
|
+
/**
|
|
429
|
+
* One entry of the object-shaped available_shipping_countries variant
|
|
430
|
+
* (`{ code, label }` pairs — 5/33 real specs).
|
|
431
|
+
*/
|
|
432
|
+
export interface ShippingCountry {
|
|
433
|
+
code?: string;
|
|
434
|
+
label?: string;
|
|
435
|
+
[key: string]: unknown;
|
|
436
|
+
}
|
|
437
|
+
export interface Campaign {
|
|
438
|
+
ref_id?: number | string;
|
|
439
|
+
/** Campaign slug-style identifier (28/33 real specs), distinct from ref_id. */
|
|
440
|
+
id?: string;
|
|
441
|
+
name?: string;
|
|
442
|
+
slug?: string;
|
|
443
|
+
currency?: string;
|
|
444
|
+
language?: string;
|
|
445
|
+
/** Public-by-design, domain-allowlisted Campaigns API key. */
|
|
446
|
+
campaigns_api_key?: string | null;
|
|
447
|
+
available_payment_methods?: unknown[];
|
|
448
|
+
available_express_payment_methods?: unknown[];
|
|
449
|
+
available_currencies?: string[];
|
|
450
|
+
/**
|
|
451
|
+
* Public route root the campaign is served under. `'/'` declares a
|
|
452
|
+
* ROOT-SERVED campaign: the whole funnel lives at site-root paths
|
|
453
|
+
* (`/checkout-v2`, `/receipt`) with no slug prefix — e.g. a single-campaign
|
|
454
|
+
* site whose deploy publishes the funnel at the domain root. When absent,
|
|
455
|
+
* consumers default to `'/<slug>/'`. `slug` stays required identity either
|
|
456
|
+
* way; route_root only changes how public routes and SDK routing metas are
|
|
457
|
+
* composed and validated (campaigns-os doctor honors it).
|
|
458
|
+
*/
|
|
459
|
+
route_root?: string;
|
|
460
|
+
payment_env_key?: string;
|
|
461
|
+
/**
|
|
462
|
+
* Arrives in THREE incompatible shapes in real exports: bare string
|
|
463
|
+
* "all", an array of country-code strings (incl. []), and an array of
|
|
464
|
+
* { code, label } objects. All three are accepted; normalization to one
|
|
465
|
+
* canonical shape is deliberately deferred.
|
|
466
|
+
*/
|
|
467
|
+
available_shipping_countries?: 'all' | string[] | ShippingCountry[];
|
|
468
|
+
tracking?: Record<string, unknown>;
|
|
469
|
+
preferred_template_family?: TemplateFamilyHint;
|
|
470
|
+
store_url?: string | null;
|
|
471
|
+
store_name?: string | null;
|
|
472
|
+
store_contact?: string | null;
|
|
473
|
+
store_terms?: string | null;
|
|
474
|
+
store_shipping?: string | null;
|
|
475
|
+
store_privacy?: string | null;
|
|
476
|
+
store_returns?: string | null;
|
|
477
|
+
store_phone?: string | null;
|
|
478
|
+
/**
|
|
479
|
+
* Domain allowlist for the SDK / Campaigns API key (Slice 4f). The
|
|
480
|
+
* Campaigns API treats domain allowlisting as the access boundary for
|
|
481
|
+
* public-by-design keys; carrying the allowlist in the spec lets the
|
|
482
|
+
* build packet bind config.js to the same surface. Empty/missing
|
|
483
|
+
* value is a warning, not an error — pre-launch specs frequently
|
|
484
|
+
* lack a final domain.
|
|
485
|
+
*/
|
|
486
|
+
allowed_domains?: string[];
|
|
487
|
+
/**
|
|
488
|
+
* `tel:`-prefixed phone URI for "Call us" CTAs (Slice 4f). Distinct
|
|
489
|
+
* from store_phone (the human-readable display string); store_phone_tel
|
|
490
|
+
* goes into <a href="tel:..."> attributes. Validation warns when the
|
|
491
|
+
* value is present but doesn't start with `tel:`.
|
|
492
|
+
*/
|
|
493
|
+
store_phone_tel?: string;
|
|
494
|
+
[key: string]: unknown;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Analytics & attribution contract (Slice 4g) — what a campaign's analytics,
|
|
498
|
+
* tag-management, and querystring-param tracking are SUPPOSED to be, so doctor
|
|
499
|
+
* + QA can validate them instead of discovering gaps in QA (cf. the Chamelo
|
|
500
|
+
* Shield `?reviews=n`-has-no-handler finding and the Walla Sound Redtrack/
|
|
501
|
+
* campaign.js sub1-6 param conflict).
|
|
502
|
+
*
|
|
503
|
+
* Modeled on real production-funnel usage, NOT the idealized "SDK fires the
|
|
504
|
+
* canonical dl_* set" view. The field reality the block must express:
|
|
505
|
+
* - Events fire from three sources: SDK auto, SDK-blocked-then-manual
|
|
506
|
+
* (`blockedEvents` suppresses the SDK event, a side script re-fires it via
|
|
507
|
+
* raw fbq/gtag to control timing), and fully out-of-band pixels loaded via
|
|
508
|
+
* GTM (Everflow / TriplePixel / Northbeam / RudderStack) that never touch
|
|
509
|
+
* the dataLayer. So outbound pixel fires — not dataLayer events — are the
|
|
510
|
+
* QA source of truth.
|
|
511
|
+
* - A custom provider may carry an endpoint + transform (cookie injection).
|
|
512
|
+
* - Querystring params split into two classes: CONTENT (param.* → visibility
|
|
513
|
+
* via data-next-hide) and TRACKING (utm params, gclid, fbclid, subN, click-ids that
|
|
514
|
+
* must be preserved across funnel steps and not collide with ad trackers).
|
|
515
|
+
*
|
|
516
|
+
* The whole block is OPTIONAL — absent analytics means "use SDK defaults",
|
|
517
|
+
* exactly as today. When present it becomes the source of truth for the
|
|
518
|
+
* AnalyticsContractShape rule + downstream doctor/QA.
|
|
519
|
+
*/
|
|
520
|
+
export type AnalyticsMode = 'auto' | 'manual' | 'disabled';
|
|
521
|
+
export interface AnalyticsProvider {
|
|
522
|
+
enabled?: boolean;
|
|
523
|
+
/** GTM container id (kind: gtm). */
|
|
524
|
+
containerId?: string;
|
|
525
|
+
/** Meta/Facebook pixel id (kind: facebook). */
|
|
526
|
+
pixelId?: string;
|
|
527
|
+
/** Custom-provider HTTP endpoint (kind: custom). */
|
|
528
|
+
endpoint?: string;
|
|
529
|
+
/** Custom-provider transform hint, e.g. a cookie name to inject (`cf_click_id`). */
|
|
530
|
+
transform?: string;
|
|
531
|
+
/** Events the SDK must NOT fire for this provider (a side script fires them). */
|
|
532
|
+
blockedEvents?: string[];
|
|
533
|
+
[key: string]: unknown;
|
|
534
|
+
}
|
|
535
|
+
/** A pixel/tag fired OUTSIDE the SDK (via GTM or a raw snippet) — QA must still
|
|
536
|
+
* expect it on a live run even though it never appears in the dataLayer. */
|
|
537
|
+
export interface OutOfBandPixel {
|
|
538
|
+
vendor: string;
|
|
539
|
+
loaded_via?: 'gtm' | 'script' | (string & {});
|
|
540
|
+
id?: string;
|
|
541
|
+
}
|
|
542
|
+
/** An event the SDK is configured NOT to fire (`blockedEvents`), declared with
|
|
543
|
+
* where + when a side script fires it instead. A `purchase`/`Purchase` manual
|
|
544
|
+
* event SHOULD name the page it lives on — the first-upsell placement footgun
|
|
545
|
+
* (async purchase beacons get lost in the checkout→upsell redirect). */
|
|
546
|
+
export interface ManualEvent {
|
|
547
|
+
event: string;
|
|
548
|
+
page?: string;
|
|
549
|
+
trigger?: string;
|
|
550
|
+
}
|
|
551
|
+
/** A content param that drives visibility via `data-next-hide="param.X=='n'"`. */
|
|
552
|
+
export interface ContentParam {
|
|
553
|
+
name: string;
|
|
554
|
+
hides?: string;
|
|
555
|
+
pages?: string[];
|
|
556
|
+
}
|
|
557
|
+
export interface TrackingParams {
|
|
558
|
+
/** Params captured + preserved across funnel steps (utm_*, gclid, fbclid, sub1..5). */
|
|
559
|
+
preserve?: string[];
|
|
560
|
+
/** Funnel step page-types/ids the params must survive across. */
|
|
561
|
+
across?: string[];
|
|
562
|
+
/** The affiliate click id: inbound querystring param → SDK attribution field. */
|
|
563
|
+
click_id?: {
|
|
564
|
+
inbound?: string;
|
|
565
|
+
maps_to?: string;
|
|
566
|
+
};
|
|
567
|
+
/** External ad trackers sharing the URL (Redtrack/Clickflare) — collision watch. */
|
|
568
|
+
external_trackers?: string[];
|
|
569
|
+
}
|
|
570
|
+
export interface AnalyticsParams {
|
|
571
|
+
content?: ContentParam[];
|
|
572
|
+
tracking?: TrackingParams;
|
|
573
|
+
}
|
|
574
|
+
export interface UtmTransfer {
|
|
575
|
+
enabled?: boolean;
|
|
576
|
+
applyToExternalLinks?: boolean;
|
|
577
|
+
paramsToCopy?: string[];
|
|
578
|
+
excludedDomains?: string[];
|
|
579
|
+
}
|
|
580
|
+
export interface AnalyticsContract {
|
|
581
|
+
mode?: AnalyticsMode;
|
|
582
|
+
/** Keyed by provider kind: gtm | facebook | rudderstack | custom | … */
|
|
583
|
+
providers?: Record<string, AnalyticsProvider>;
|
|
584
|
+
out_of_band_pixels?: OutOfBandPixel[];
|
|
585
|
+
manual_events?: ManualEvent[];
|
|
586
|
+
utmTransfer?: UtmTransfer;
|
|
587
|
+
params?: AnalyticsParams;
|
|
588
|
+
[key: string]: unknown;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Saved Map Builder identity block (26/33 real specs). `source` values
|
|
592
|
+
* observed: campaign-map-builder, hand-authored-simulation,
|
|
593
|
+
* local-experimental.
|
|
594
|
+
*/
|
|
595
|
+
export interface SpecIdentity {
|
|
596
|
+
map_id?: string;
|
|
597
|
+
source?: string;
|
|
598
|
+
id?: string;
|
|
599
|
+
map_url?: string;
|
|
600
|
+
edit_url?: string;
|
|
601
|
+
spec_url?: string;
|
|
602
|
+
spec_hash?: string;
|
|
603
|
+
saved_at?: string;
|
|
604
|
+
public_route_slug?: string;
|
|
605
|
+
variant_slug?: string;
|
|
606
|
+
template_family?: string;
|
|
607
|
+
derived_from?: string;
|
|
608
|
+
authority?: string;
|
|
609
|
+
[key: string]: unknown;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* funnel_pages[] mirror entry: a Page plus the _funnel_id/_funnel_name
|
|
613
|
+
* annotations linking it back to its funnel. The mirror is a flattened
|
|
614
|
+
* LEGACY projection of funnels[].pages[] (declared "legacy" by
|
|
615
|
+
* _provenance); funnels[] is authoritative.
|
|
616
|
+
*/
|
|
617
|
+
export interface FunnelPageMirrorEntry extends Page {
|
|
618
|
+
_funnel_id?: string;
|
|
619
|
+
_funnel_name?: string;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Field-ownership declaration (27/33 real specs): which JSON paths are
|
|
623
|
+
* ops-authored vs api-owned vs derived vs legacy mirrors.
|
|
624
|
+
*/
|
|
625
|
+
export interface SpecProvenance {
|
|
626
|
+
ops?: string[];
|
|
627
|
+
api?: string[];
|
|
628
|
+
derived?: string[];
|
|
629
|
+
legacy?: string[];
|
|
630
|
+
[key: string]: unknown;
|
|
631
|
+
}
|
|
632
|
+
export interface CampaignSpec {
|
|
633
|
+
schema_version?: string;
|
|
634
|
+
builder_version?: string;
|
|
635
|
+
generated_at?: string;
|
|
636
|
+
spec_identity?: SpecIdentity;
|
|
637
|
+
campaign?: Campaign;
|
|
638
|
+
funnels: Funnel[];
|
|
639
|
+
/** Flattened legacy mirror of funnels[].pages[]; funnels[] is authoritative. */
|
|
640
|
+
funnel_pages?: FunnelPageMirrorEntry[];
|
|
641
|
+
offers?: Offer[];
|
|
642
|
+
shipping_methods?: unknown[];
|
|
643
|
+
/**
|
|
644
|
+
* CANONICAL home of the SDK pin for the 4.x lineage (33/33 real specs
|
|
645
|
+
* declare global_config.sdk_version).
|
|
646
|
+
*/
|
|
647
|
+
global_config?: {
|
|
648
|
+
sdk_version?: string;
|
|
649
|
+
[key: string]: unknown;
|
|
650
|
+
};
|
|
651
|
+
/** Accepted ALIAS location for sdk_version (local drafts only). */
|
|
652
|
+
runtime?: {
|
|
653
|
+
sdk_version?: string;
|
|
654
|
+
[key: string]: unknown;
|
|
655
|
+
};
|
|
656
|
+
build_scope?: {
|
|
657
|
+
mode?: 'partial' | 'full';
|
|
658
|
+
[key: string]: unknown;
|
|
659
|
+
};
|
|
660
|
+
/** Analytics & attribution contract (optional). See AnalyticsContract. */
|
|
661
|
+
analytics?: AnalyticsContract;
|
|
662
|
+
_provenance?: SpecProvenance;
|
|
663
|
+
/** Flat mirrors of the saved-map identity (7/33 real specs). */
|
|
664
|
+
slug?: string;
|
|
665
|
+
map_id?: string;
|
|
666
|
+
saved_at?: string;
|
|
667
|
+
[key: string]: unknown;
|
|
668
|
+
}
|
|
669
|
+
export interface Fixture {
|
|
670
|
+
spec: CampaignSpec;
|
|
671
|
+
expected: {
|
|
672
|
+
violations: Violation[];
|
|
673
|
+
};
|
|
674
|
+
}
|