@decocms/blocks-cli 7.5.1 → 7.5.2

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 (35) hide show
  1. package/package.json +2 -2
  2. package/scripts/generate-invoke.ts +10 -0
  3. package/scripts/migrate/analyzers/loader-inventory.ts +1 -1
  4. package/scripts/migrate/phase-analyze.ts +3 -3
  5. package/scripts/migrate/phase-cleanup.ts +29 -23
  6. package/scripts/migrate/phase-report.ts +7 -7
  7. package/scripts/migrate/phase-scaffold.ts +4 -4
  8. package/scripts/migrate/phase-transform.ts +1 -1
  9. package/scripts/migrate/phase-verify.ts +6 -5
  10. package/scripts/migrate/post-cleanup/rules.ts +55 -44
  11. package/scripts/migrate/post-cleanup/runner.test.ts +58 -34
  12. package/scripts/migrate/templates/cache-config.ts +4 -4
  13. package/scripts/migrate/templates/commerce-loaders.ts +10 -10
  14. package/scripts/migrate/templates/cursor-rules.test.ts +6 -0
  15. package/scripts/migrate/templates/cursor-rules.ts +12 -11
  16. package/scripts/migrate/templates/hooks.test.ts +7 -7
  17. package/scripts/migrate/templates/hooks.ts +10 -10
  18. package/scripts/migrate/templates/lib-utils.test.ts +2 -2
  19. package/scripts/migrate/templates/lib-utils.ts +4 -4
  20. package/scripts/migrate/templates/no-legacy-packages.test.ts +147 -0
  21. package/scripts/migrate/templates/package-json.ts +24 -10
  22. package/scripts/migrate/templates/routes.ts +8 -10
  23. package/scripts/migrate/templates/sdk-gen.ts +1 -1
  24. package/scripts/migrate/templates/section-loaders.ts +7 -7
  25. package/scripts/migrate/templates/server-entry.ts +37 -37
  26. package/scripts/migrate/templates/setup.ts +6 -6
  27. package/scripts/migrate/templates/types-gen.ts +2 -2
  28. package/scripts/migrate/templates/ui-components.ts +2 -2
  29. package/scripts/migrate/templates/vite-config.ts +12 -3
  30. package/scripts/migrate/transforms/fresh-apis.ts +5 -5
  31. package/scripts/migrate/transforms/imports.ts +49 -43
  32. package/scripts/migrate/transforms/section-conventions.ts +1 -1
  33. package/scripts/migrate/types.ts +1 -1
  34. package/scripts/migrate-post-cleanup.ts +8 -8
  35. package/scripts/migrate.ts +11 -11
@@ -467,23 +467,30 @@ function findAttachedLeadingComments(content: string, openIdx: number): number {
467
467
  *
468
468
  * 1. Legacy inline proxy (pre-Wave 15-A migration template) — defines
469
469
  * `createNestedInvokeProxy` plus `invoke` and `Runtime` constants.
470
- * The whole 40-50 LOC body duplicates `@decocms/start/sdk`'s `invoke`.
470
+ * The whole 40-50 LOC body duplicates `@decocms/blocks/sdk/invoke`'s
471
+ * `invoke`.
471
472
  *
472
473
  * 2. Simple re-export shim — the file only re-exports `invoke` /
473
474
  * `createNestedInvokeProxy` (no inline proxy body, but also not yet
474
- * pointing at `@decocms/start/sdk`).
475
+ * pointing at `@decocms/blocks/sdk/invoke`).
475
476
  *
476
- * Both should be replaced with `import { invoke } from "@decocms/start/sdk"`
477
- * at every callsite, and the file deleted. The Wave 15-A migration template
477
+ * Both should be replaced with `import { invoke } from "@decocms/blocks/sdk/invoke"`
478
+ * at every callsite, and the file deleted. The current migration template
478
479
  * scaffolds a thin re-export form that's also acceptable (re-exports
479
- * `invoke` from `@decocms/start/sdk` and rebuilds `Runtime = { invoke }`);
480
- * we explicitly skip it via the "imports invoke from @decocms/start/sdk
480
+ * `invoke` from `@decocms/blocks/sdk/invoke` and rebuilds `Runtime = { invoke }`);
481
+ * we explicitly skip it via the "imports invoke from @decocms/blocks/sdk
481
482
  * AND no inline proxy" check below.
483
+ *
484
+ * Sites migrated before the 7.x package split may still import from the
485
+ * retired `@decocms/start/sdk` — that package no longer exists for 7.x
486
+ * consumers, so this is intentionally NOT treated as "already fixed"; it
487
+ * falls through to the flag-and-rewrite path below like any other stale
488
+ * shim.
482
489
  */
483
490
  const INLINE_PROXY_RE =
484
491
  /(?:function|const)\s+createNestedInvokeProxy\b|new\s+Proxy\s*\(\s*Object\.assign\s*\(\s*async\s*\(\s*props/;
485
492
  const FRAMEWORK_INVOKE_IMPORT_RE =
486
- /import\s+\{[^}]*\binvoke\b[^}]*\}\s+from\s+['"]@decocms\/start(?:\/sdk)?['"]/;
493
+ /import\s+\{[^}]*\binvoke\b[^}]*\}\s+from\s+['"]@decocms\/blocks\/sdk(?:\/invoke)?['"]/;
487
494
 
488
495
  const ALLOWED_RUNTIME_EXPORTS = new Set(["invoke", "createNestedInvokeProxy", "Runtime"]);
489
496
 
@@ -501,7 +508,7 @@ const ruleDeadRuntimeShim: Rule = {
501
508
  const onlyKnownInvokeExports =
502
509
  exports.length > 0 && exports.every((e) => ALLOWED_RUNTIME_EXPORTS.has(e));
503
510
 
504
- // Wave 15-A canonical template: re-exports invoke from @decocms/start/sdk
511
+ // Current canonical template: re-exports invoke from @decocms/blocks/sdk/invoke
505
512
  // and exposes `Runtime = { invoke }` for legacy callers. No inline proxy
506
513
  // body. This is the desired shape — skip.
507
514
  if (reExportsFromFramework && !hasInlineProxy) return [];
@@ -523,11 +530,11 @@ const ruleDeadRuntimeShim: Rule = {
523
530
  severity: "info",
524
531
  file: "src/runtime.ts",
525
532
  message: safeToAutoFix
526
- ? `${flavor} [${exportSummary}] — replace with @decocms/start/sdk`
533
+ ? `${flavor} [${exportSummary}] — replace with @decocms/blocks/sdk/invoke`
527
534
  : `${flavor} [${exportSummary}] — manual review: file mixes the runtime proxy with site-specific exports`,
528
535
  fix: safeToAutoFix
529
- ? 'rg -l "from \\"~/runtime\\"" src/ | xargs sed -i \'\' \'s|from "~/runtime"|from "@decocms/start/sdk"|g\' && rm src/runtime.ts'
530
- : "Move the inline `createNestedInvokeProxy` body to call @decocms/start/sdk's `invoke`; relocate site-specific helpers to a dedicated module before deleting src/runtime.ts",
536
+ ? 'rg -l "from \\"~/runtime\\"" src/ | xargs sed -i \'\' \'s|from "~/runtime"|from "@decocms/blocks/sdk/invoke"|g\' && rm src/runtime.ts'
537
+ : "Move the inline `createNestedInvokeProxy` body to call @decocms/blocks/sdk/invoke's `invoke`; relocate site-specific helpers to a dedicated module before deleting src/runtime.ts",
531
538
  meta: {
532
539
  hasInlineProxy,
533
540
  exports,
@@ -542,13 +549,13 @@ const ruleDeadRuntimeShim: Rule = {
542
549
  // a runtime.ts that mixes the proxy with site-specific helpers.
543
550
  const safe = findings.every((f) => f.meta?.safeToAutoFix !== false);
544
551
  if (!safe) return [];
545
- const updated = rewriteImportSpec(ctx, writer, "~/runtime", "@decocms/start/sdk");
552
+ const updated = rewriteImportSpec(ctx, writer, "~/runtime", "@decocms/blocks/sdk/invoke");
546
553
  writer.deleteFile(`${ctx.siteDir}/src/runtime.ts`);
547
554
  return [
548
555
  {
549
556
  file: "src/runtime.ts",
550
557
  kind: "rewrite-imports+delete",
551
- detail: `rewrote ${updated.length} import(s) → @decocms/start/sdk and deleted src/runtime.ts`,
558
+ detail: `rewrote ${updated.length} import(s) → @decocms/blocks/sdk/invoke and deleted src/runtime.ts`,
552
559
  },
553
560
  ];
554
561
  },
@@ -573,7 +580,7 @@ const ruleSiteLocalGlobals: Rule = {
573
580
  /(?:export\s+)?(?:function|const)\s+(?:withSiteGlobals|cmsRouteWithGlobals)\b/.test(
574
581
  content,
575
582
  );
576
- const reExportsFromFramework = /from\s+['"]@decocms\/start\/routes['"]/.test(content);
583
+ const reExportsFromFramework = /from\s+['"]@decocms\/tanstack['"]/.test(content);
577
584
  if (!definesWrapper || reExportsFromFramework) continue;
578
585
  const rel = abs.slice(siteDir.length + 1);
579
586
  const lineCount = content.split("\n").length;
@@ -581,8 +588,8 @@ const ruleSiteLocalGlobals: Rule = {
581
588
  rule: "site-local-with-globals",
582
589
  severity: "warning",
583
590
  file: rel,
584
- message: `Local wrapper (~${lineCount} LOC) — framework now exports withSiteGlobals from @decocms/start/routes`,
585
- fix: "delete the local wrapper and import { withSiteGlobals } from '@decocms/start/routes'",
591
+ message: `Local wrapper (~${lineCount} LOC) — framework now exports withSiteGlobals from @decocms/tanstack`,
592
+ fix: "delete the local wrapper and import { withSiteGlobals } from '@decocms/tanstack'",
586
593
  meta: { lineCount },
587
594
  });
588
595
  }
@@ -623,7 +630,7 @@ export const STUB_FIX_HINTS: Record<string, FixHint> = {
623
630
  // src/lib/vtex-transform
624
631
  toProduct: {
625
632
  kind: "swap",
626
- canonical: "@decocms/apps/vtex/utils/transform",
633
+ canonical: "@decocms/apps-vtex/utils/transform",
627
634
  note:
628
635
  "canonical signature is `toProduct(product, sku, level, options)`; " +
629
636
  "1-arg call sites need to expand args first — see skill § 5",
@@ -633,12 +640,12 @@ export const STUB_FIX_HINTS: Record<string, FixHint> = {
633
640
  kind: "refactor",
634
641
  note:
635
642
  "read cookies via `request.headers.get('cookie')` then call " +
636
- "`buildSegmentFromCookies()` from '@decocms/apps/vtex/utils/segment'. " +
643
+ "`buildSegmentFromCookies()` from '@decocms/apps-vtex/utils/segment'. " +
637
644
  "The bag-based lookup mechanism does not exist on TanStack Start.",
638
645
  },
639
646
  withSegmentCookie: {
640
647
  kind: "swap",
641
- canonical: "@decocms/apps/vtex/utils/segment",
648
+ canonical: "@decocms/apps-vtex/utils/segment",
642
649
  note:
643
650
  "canonical signature is `withSegmentCookie(segment, headers?)`; " +
644
651
  "if you currently pass only headers, also pass a segment object",
@@ -686,12 +693,12 @@ export function buildVtexShimFixMessage(stubsBySim: Map<string, string[]>): stri
686
693
  const parts: string[] = [...known];
687
694
  if (unknown.length > 0) {
688
695
  parts.push(
689
- `${unknown.join(", ")} → repoint to '@decocms/apps/vtex/...' or 'apps/commerce/utils/...'`,
696
+ `${unknown.join(", ")} → repoint to '@decocms/apps-vtex/...' or 'apps/commerce/utils/...'`,
690
697
  );
691
698
  }
692
699
  return parts.length > 0
693
700
  ? parts.join(" | ")
694
- : "Repoint imports to '@decocms/apps/vtex/...' or 'apps/commerce/utils/...'";
701
+ : "Repoint imports to '@decocms/apps-vtex/...' or 'apps/commerce/utils/...'";
695
702
  }
696
703
 
697
704
  /**
@@ -885,8 +892,8 @@ const ruleLocalWidgetsTypes: Rule = {
885
892
  rule: "local-widgets-types",
886
893
  severity: "info",
887
894
  file: "src/types/widgets.ts",
888
- message: `Local file shadows @decocms/start/types/widgets (used in ${importCount} place(s))`,
889
- fix: 'rewrite imports to "@decocms/start/types/widgets" and rm src/types/widgets.ts',
895
+ message: `Local file shadows @decocms/blocks/types/widgets (used in ${importCount} place(s))`,
896
+ fix: 'rewrite imports to "@decocms/blocks/types/widgets" and rm src/types/widgets.ts',
890
897
  meta: { importCount },
891
898
  },
892
899
  ];
@@ -897,14 +904,14 @@ const ruleLocalWidgetsTypes: Rule = {
897
904
  ctx,
898
905
  writer,
899
906
  "~/types/widgets",
900
- "@decocms/start/types/widgets",
907
+ "@decocms/blocks/types/widgets",
901
908
  );
902
909
  writer.deleteFile(`${ctx.siteDir}/src/types/widgets.ts`);
903
910
  return [
904
911
  {
905
912
  file: "src/types/widgets.ts",
906
913
  kind: "rewrite-imports+delete",
907
- detail: `rewrote ${updated.length} import(s) → @decocms/start/types/widgets and deleted src/types/widgets.ts`,
914
+ detail: `rewrote ${updated.length} import(s) → @decocms/blocks/types/widgets and deleted src/types/widgets.ts`,
908
915
  },
909
916
  ];
910
917
  },
@@ -949,7 +956,7 @@ const ruleFrameworkTodos: Rule = {
949
956
 
950
957
  /**
951
958
  * Registry of files we expect sites to NOT carry locally because the
952
- * canonical implementation already lives in `@decocms/start` (or a
959
+ * canonical implementation already lives in `@decocms/blocks` (or a
953
960
  * sibling apps package).
954
961
  *
955
962
  * Two flavours:
@@ -1000,7 +1007,7 @@ interface FrameworkDuplicate {
1000
1007
  /**
1001
1008
  * Add an entry here when:
1002
1009
  * - 1+ migrated sites carry their own copy of code that already
1003
- * exists in `@decocms/start` (or a sibling apps package), AND
1010
+ * exists in `@decocms/blocks` (or a sibling apps package), AND
1004
1011
  * - the canonical version is at least feature-equivalent.
1005
1012
  *
1006
1013
  * Per D4 in the migration tooling policy, the framework promotion
@@ -1011,18 +1018,18 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
1011
1018
  {
1012
1019
  id: "clx",
1013
1020
  sitePath: "src/sdk/clx.ts",
1014
- canonicalImport: "@decocms/start/sdk/clx",
1021
+ canonicalImport: "@decocms/blocks/sdk/clx",
1015
1022
  contentSignature: [
1016
1023
  /export\s+const\s+clx\s*=/,
1017
1024
  /args\.filter\(Boolean\)\.join/,
1018
1025
  ],
1019
1026
  safeToAutoFix: true,
1020
- description: "src/sdk/clx.ts duplicates @decocms/start/sdk/clx",
1027
+ description: "src/sdk/clx.ts duplicates @decocms/blocks/sdk/clx",
1021
1028
  },
1022
1029
  {
1023
1030
  id: "use-send-event",
1024
1031
  sitePath: "src/sdk/useSendEvent.ts",
1025
- canonicalImport: "@decocms/start/sdk/analytics",
1032
+ canonicalImport: "@decocms/blocks/sdk/analytics",
1026
1033
  contentSignature: [
1027
1034
  /export\s+(?:const|function)\s+useSendEvent/,
1028
1035
  /data-event/,
@@ -1034,12 +1041,12 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
1034
1041
  "Replacing 1:1 weakens type-safety. Either widen the framework export (preferred), or " +
1035
1042
  "rewrite call sites to drop the generic. Manual review required.",
1036
1043
  description:
1037
- "src/sdk/useSendEvent.ts overlaps with @decocms/start/sdk/analytics → useSendEvent",
1044
+ "src/sdk/useSendEvent.ts overlaps with @decocms/blocks/sdk/analytics → useSendEvent",
1038
1045
  },
1039
1046
  {
1040
1047
  id: "location-matcher",
1041
1048
  sitePath: "src/matchers/location.ts",
1042
- canonicalImport: "@decocms/start/matchers/builtins",
1049
+ canonicalImport: "@decocms/blocks/matchers/builtins",
1043
1050
  contentSignature: [
1044
1051
  /registerMatcher\(\s*['"]website\/matchers\/location\.ts['"]/,
1045
1052
  /__cf_geo/,
@@ -1051,12 +1058,12 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
1051
1058
  "verify country-name lookup parity (resolveCountryCode vs site's inline table) and " +
1052
1059
  "swap setup.ts's customMatchers entry to call registerBuiltinMatchers().",
1053
1060
  description:
1054
- "src/matchers/location.ts overlaps with @decocms/start/matchers/builtins → registerBuiltinMatchers()",
1061
+ "src/matchers/location.ts overlaps with @decocms/blocks/matchers/builtins → registerBuiltinMatchers()",
1055
1062
  },
1056
1063
  {
1057
1064
  id: "url-relative",
1058
1065
  sitePath: "src/sdk/url.ts",
1059
- canonicalImport: "@decocms/apps/commerce/sdk/url",
1066
+ canonicalImport: "@decocms/apps-commerce/sdk/url",
1060
1067
  // Fingerprint: site fork carries a positional `removeIdSku?: boolean`
1061
1068
  // flag + hardcoded VTEX-specific keys (`idsku`, `skuId`). Canonical
1062
1069
  // apps export uses an options object — `{ stripSearchParams: string[] }`
@@ -1068,7 +1075,7 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
1068
1075
  ],
1069
1076
  safeToAutoFix: false,
1070
1077
  reason:
1071
- "rewrite imports to '@decocms/apps/commerce/sdk/url'. " +
1078
+ "rewrite imports to '@decocms/apps-commerce/sdk/url'. " +
1072
1079
  "Each call site using the boolean form `relative(url, true)` becomes " +
1073
1080
  "`relative(url, { stripSearchParams: [\"idsku\", \"skuId\"] })`. " +
1074
1081
  "1-arg calls are unchanged. Then delete src/sdk/url.ts. " +
@@ -1076,12 +1083,12 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
1076
1083
  "transformation (positional bool → options object), not pure import " +
1077
1084
  "rewrite.",
1078
1085
  description:
1079
- "src/sdk/url.ts overlaps with @decocms/apps/commerce/sdk/url → relative() (extended in @decocms/apps@1.9+)",
1086
+ "src/sdk/url.ts overlaps with @decocms/apps-commerce/sdk/url → relative()",
1080
1087
  },
1081
1088
  {
1082
1089
  id: "use-suggestions",
1083
1090
  sitePath: "src/sdk/useSuggestions.ts",
1084
- canonicalImport: "@decocms/start/sdk/useSuggestions",
1091
+ canonicalImport: "@decocms/blocks/sdk/useSuggestions",
1085
1092
  // Fingerprint: hand-rolled hook with the module-level signal +
1086
1093
  // serial-queue + latestQuery cancel pattern. Both casaevideo and
1087
1094
  // baggagio independently invented this exact shape. Sites that
@@ -1102,7 +1109,7 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
1102
1109
  "the per-site type parameter and onError wiring need site-specific " +
1103
1110
  "decisions. See references/platform-hooks-factories.md § useSuggestions.",
1104
1111
  description:
1105
- "src/sdk/useSuggestions.ts duplicates @decocms/start/sdk/useSuggestions → createUseSuggestions() (added in @decocms/start@2.25+)",
1112
+ "src/sdk/useSuggestions.ts duplicates @decocms/blocks/sdk/useSuggestions → createUseSuggestions()",
1106
1113
  },
1107
1114
  ];
1108
1115
 
@@ -1175,7 +1182,7 @@ function stripExt(path: string): string {
1175
1182
 
1176
1183
  /**
1177
1184
  * Per D2 in the migration tooling policy, every `hx-*` attribute is
1178
- * rewritten on migration; nothing in `@decocms/start` ships an htmx
1185
+ * rewritten on migration; nothing in `@decocms/blocks` ships an htmx
1179
1186
  * runtime. This rule is the verification gate: a migrated site is
1180
1187
  * "rewrite-complete" when there are zero `hx-*` attributes left in
1181
1188
  * `src/`.
@@ -1427,7 +1434,7 @@ function satisfiesRange(version: string, range: string): boolean | null {
1427
1434
  * package name. Bun's lockfile format embeds the version inside the
1428
1435
  * package descriptor array, e.g.:
1429
1436
  *
1430
- * "@decocms/start": ["@decocms/start@2.1.1", ...]
1437
+ * "@decocms/blocks": ["@decocms/blocks@7.5.1", ...]
1431
1438
  *
1432
1439
  * We scan all such occurrences (a single package may appear multiple
1433
1440
  * times if the dep tree pulled different versions).
@@ -1583,7 +1590,7 @@ const rulePackageManagerMissing: Rule = {
1583
1590
  /* ------------------------------------------------------------------ */
1584
1591
 
1585
1592
  /**
1586
- * Every VTEX storefront on @decocms/start needs a reverse proxy for
1593
+ * Every VTEX storefront on @decocms/tanstack needs a reverse proxy for
1587
1594
  * `/checkout/*`, `/account/*`, `/api/*`, `/files/*`, etc. — those paths
1588
1595
  * must hit the VTEX origin (not TanStack Start) so the user lands on
1589
1596
  * the real checkout UI carrying their VTEX session cookies.
@@ -1614,11 +1621,14 @@ const ruleVtexProxyHandlerMissing: Rule = {
1614
1621
  title: "VTEX worker-entry missing the checkout/API proxy handler",
1615
1622
  run({ siteDir, fs }: RuleContext): Finding[] {
1616
1623
  // Only run when the site is actually VTEX. The cheapest signal is
1617
- // any import from `@decocms/apps/vtex/...` in src/ — every VTEX
1624
+ // any import from `@decocms/apps-vtex/...` in src/ — every VTEX
1618
1625
  // site has at least one (commerceLoaders, hooks, types, etc.).
1626
+ // Also matches the pre-7.x `@decocms/apps/vtex/...` monolith path,
1627
+ // since sites migrated before the package split still carry it.
1628
+ const VTEX_IMPORT_RE = /@decocms\/apps-vtex|@decocms\/apps\/vtex/;
1619
1629
  const srcFiles = fs.glob(siteDir, "src/**/*.{ts,tsx}", SRC_GLOB_EXCLUDES);
1620
1630
  const isVtex = srcFiles.some((abs) =>
1621
- fs.readText(abs).includes("@decocms/apps/vtex"),
1631
+ VTEX_IMPORT_RE.test(fs.readText(abs)),
1622
1632
  );
1623
1633
  if (!isVtex) return [];
1624
1634
 
@@ -1642,6 +1652,7 @@ const ruleVtexProxyHandlerMissing: Rule = {
1642
1652
  // either is a strong signal the proxy block exists; absence of
1643
1653
  // both means it was dropped.
1644
1654
  const hasProxyImport =
1655
+ /from\s+["']@decocms\/apps-vtex\/utils\/proxy["']/.test(content) ||
1645
1656
  /from\s+["']@decocms\/apps\/vtex\/utils\/proxy["']/.test(content);
1646
1657
  // Match both long form (`proxyHandler: async (...) => ...`) and
1647
1658
  // object-shorthand wiring (`{ proxyHandler }`, `{ proxyHandler, admin }`).
@@ -1659,7 +1670,7 @@ const ruleVtexProxyHandlerMissing: Rule = {
1659
1670
  file: "src/worker-entry.ts",
1660
1671
  message: hasProxyImport
1661
1672
  ? "imports proxy helpers but no `proxyHandler:` is wired into createDecoWorkerEntry — /checkout requests will fall through to TanStack and render the SPA shell"
1662
- : "no `@decocms/apps/vtex/utils/proxy` import — VTEX /checkout, /account, /api won't be proxied to the origin",
1673
+ : "no `@decocms/apps-vtex/utils/proxy` import — VTEX /checkout, /account, /api won't be proxied to the origin",
1663
1674
  fix: "Add `proxyHandler` to createDecoWorkerEntry; see scripts/migrate/templates/server-entry.ts (generateVtexWorkerEntry) for the canonical block",
1664
1675
  },
1665
1676
  ];
@@ -242,10 +242,10 @@ describe("rule: dead-runtime-shim", () => {
242
242
 
243
243
  it("does NOT flag the Wave 15-A canonical re-export shape", () => {
244
244
  // The migration template now scaffolds a thin re-export from
245
- // @decocms/start/sdk plus a Runtime alias. No inline proxy body.
245
+ // @decocms/blocks/sdk/invoke plus a Runtime alias. No inline proxy body.
246
246
  const fs = makeFs({
247
247
  "/site/src/runtime.ts":
248
- 'import { invoke } from "@decocms/start/sdk";\nexport { invoke };\nexport const Runtime = { invoke };\n',
248
+ 'import { invoke } from "@decocms/blocks/sdk/invoke";\nexport { invoke };\nexport const Runtime = { invoke };\n',
249
249
  });
250
250
  const report = runAudit(SITE, fs);
251
251
  const r = report.rules.find((r) => r.rule === "dead-runtime-shim")!;
@@ -330,7 +330,7 @@ describe("rule: site-local-with-globals", () => {
330
330
  it("does not flag a re-export from the framework", () => {
331
331
  const fs = makeFs({
332
332
  "/site/src/server/routes/withSiteGlobals.ts":
333
- 'export { withSiteGlobals } from "@decocms/start/routes";\n',
333
+ 'export { withSiteGlobals } from "@decocms/tanstack";\n',
334
334
  });
335
335
  const report = runAudit(SITE, fs);
336
336
  const r = report.rules.find((r) => r.rule === "site-local-with-globals")!;
@@ -492,12 +492,12 @@ describe("rule: vtex-shim-regression — per-symbol fix hints", () => {
492
492
  const r = report.rules.find((r) => r.rule === "vtex-shim-regression")!;
493
493
  expect(r.findings).toHaveLength(1);
494
494
  const f = r.findings[0];
495
- expect(f.fix).toContain("toProduct → @decocms/apps/vtex/utils/transform");
495
+ expect(f.fix).toContain("toProduct → @decocms/apps-vtex/utils/transform");
496
496
  expect(f.fix).toContain("1:1 import swap");
497
497
  expect(f.meta?.fixHints).toEqual({
498
498
  toProduct: {
499
499
  kind: "swap",
500
- canonical: "@decocms/apps/vtex/utils/transform",
500
+ canonical: "@decocms/apps-vtex/utils/transform",
501
501
  note: expect.stringContaining("canonical signature"),
502
502
  },
503
503
  });
@@ -537,7 +537,7 @@ describe("rule: vtex-shim-regression — per-symbol fix hints", () => {
537
537
  const r = report.rules.find((r) => r.rule === "vtex-shim-regression")!;
538
538
  const f = r.findings[0];
539
539
  expect(f.fix).toContain("getSegmentFromBag → call-site refactor");
540
- expect(f.fix).toContain("toProduct → @decocms/apps/vtex/utils/transform");
540
+ expect(f.fix).toContain("toProduct → @decocms/apps-vtex/utils/transform");
541
541
  // Joined with " | " for visual separation.
542
542
  expect(f.fix).toContain(" | ");
543
543
  expect(Object.keys(f.meta?.fixHints as object)).toEqual(
@@ -555,7 +555,7 @@ describe("rule: vtex-shim-regression — per-symbol fix hints", () => {
555
555
  const report = runAudit(SITE, fs);
556
556
  const r = report.rules.find((r) => r.rule === "vtex-shim-regression")!;
557
557
  const f = r.findings[0];
558
- expect(f.fix).toContain("unknownStub → repoint to '@decocms/apps/vtex/...");
558
+ expect(f.fix).toContain("unknownStub → repoint to '@decocms/apps-vtex/...");
559
559
  // No fixHints in meta when no symbols match the table.
560
560
  expect(f.meta?.fixHints).toBeUndefined();
561
561
  });
@@ -573,7 +573,7 @@ describe("rule: vtex-shim-regression — per-symbol fix hints", () => {
573
573
  const report = runAudit(SITE, fs);
574
574
  const r = report.rules.find((r) => r.rule === "vtex-shim-regression")!;
575
575
  const f = r.findings[0];
576
- expect(f.fix).toContain("toProduct → @decocms/apps/vtex/utils/transform");
576
+ expect(f.fix).toContain("toProduct → @decocms/apps-vtex/utils/transform");
577
577
  expect(f.fix).toContain("unknownStub → repoint");
578
578
  // Only the known symbol shows up in fixHints.
579
579
  expect(f.meta?.fixHints).toEqual({
@@ -665,6 +665,30 @@ export default createDecoWorkerEntry(serverEntry, {
665
665
  expect(r.findings).toEqual([]);
666
666
  });
667
667
 
668
+ it("recognizes the current @decocms/apps-vtex package name (post-7.x split)", () => {
669
+ // Sites migrated with the current scaffolder emit `@decocms/apps-vtex`,
670
+ // not the retired `@decocms/apps/vtex` monolith path. Both the isVtex
671
+ // signal and the proxy-import detection must recognize the new form.
672
+ const currentWorkerEntry = `
673
+ import { createDecoWorkerEntry } from "@decocms/tanstack";
674
+ import { shouldProxyToVtex, createVtexCheckoutProxy } from "@decocms/apps-vtex/utils/proxy";
675
+ const proxy = createVtexCheckoutProxy({ account: "x", checkoutOrigin: "x.vtexcommercestable.com.br" });
676
+ export default createDecoWorkerEntry(serverEntry, {
677
+ proxyHandler: async (req, url) => {
678
+ if (!shouldProxyToVtex(url.pathname)) return null;
679
+ return proxy(req, url);
680
+ },
681
+ });
682
+ `;
683
+ const fs = makeFs({
684
+ "/site/src/commerceLoaders.ts": 'import {} from "@decocms/apps-vtex/mod";',
685
+ "/site/src/worker-entry.ts": currentWorkerEntry,
686
+ });
687
+ const report = runAudit(SITE, fs);
688
+ const r = report.rules.find((r) => r.rule === "vtex-proxy-handler-missing")!;
689
+ expect(r.findings).toEqual([]);
690
+ });
691
+
668
692
  it("flags VTEX site missing src/worker-entry.ts entirely", () => {
669
693
  const fs = makeFs({
670
694
  "/site/src/commerceLoaders.ts": "import {} from \"@decocms/apps/vtex/mod\";",
@@ -687,7 +711,7 @@ export default createDecoWorkerEntry(serverEntry, { admin: {} });
687
711
  const report = runAudit(SITE, fs);
688
712
  const r = report.rules.find((r) => r.rule === "vtex-proxy-handler-missing")!;
689
713
  expect(r.findings).toHaveLength(1);
690
- expect(r.findings[0].message).toContain("no `@decocms/apps/vtex/utils/proxy` import");
714
+ expect(r.findings[0].message).toContain("no `@decocms/apps-vtex/utils/proxy` import");
691
715
  });
692
716
 
693
717
  it("does not flag VTEX site using object-shorthand proxyHandler wiring", () => {
@@ -821,8 +845,8 @@ describe("runAudit — fix mode", () => {
821
845
  expect(r.fixes).toHaveLength(1);
822
846
  expect(r.fixes![0].detail).toMatch(/rewrote 2 import/);
823
847
  expect("/site/src/runtime.ts" in store).toBe(false);
824
- expect(store["/site/src/sections/A.tsx"]).toContain('"@decocms/start/sdk"');
825
- expect(store["/site/src/sections/B.tsx"]).toContain("'@decocms/start/sdk'");
848
+ expect(store["/site/src/sections/A.tsx"]).toContain('"@decocms/blocks/sdk/invoke"');
849
+ expect(store["/site/src/sections/B.tsx"]).toContain("'@decocms/blocks/sdk/invoke'");
826
850
  expect(store["/site/src/sections/C.tsx"]).toContain('"~/something-else"');
827
851
  expect(log.filter((e) => e.kind === "delete")).toHaveLength(1);
828
852
  expect(log.filter((e) => e.kind === "write")).toHaveLength(2);
@@ -841,8 +865,8 @@ describe("runAudit — fix mode", () => {
841
865
  expect(r.fixes).toHaveLength(1);
842
866
  expect(r.fixes![0].detail).toMatch(/rewrote 2 import/);
843
867
  expect("/site/src/types/widgets.ts" in store).toBe(false);
844
- expect(store["/site/src/sections/A.tsx"]).toContain('"@decocms/start/types/widgets"');
845
- expect(store["/site/src/sections/B.tsx"]).toContain("'@decocms/start/types/widgets'");
868
+ expect(store["/site/src/sections/A.tsx"]).toContain('"@decocms/blocks/types/widgets"');
869
+ expect(store["/site/src/sections/B.tsx"]).toContain("'@decocms/blocks/types/widgets'");
846
870
  });
847
871
 
848
872
  it("fix mode is a no-op for rules without applyFix (e.g. framework-todos)", () => {
@@ -865,7 +889,7 @@ describe("runAudit — fix mode", () => {
865
889
  'import type { ImageWidget } from "~/types/widgets";\nimport thing from "~/types/widgets-extra";\n',
866
890
  });
867
891
  runAudit(SITE, fs, { writer });
868
- expect(store["/site/src/sections/A.tsx"]).toContain('"@decocms/start/types/widgets"');
892
+ expect(store["/site/src/sections/A.tsx"]).toContain('"@decocms/blocks/types/widgets"');
869
893
  expect(store["/site/src/sections/A.tsx"]).toContain('"~/types/widgets-extra"');
870
894
  });
871
895
  });
@@ -887,10 +911,10 @@ describe("runAudit — fix mode — vtex-shim-regression swap cases", () => {
887
911
  expect(r.fixes).toHaveLength(1);
888
912
  expect(r.fixes![0].file).toBe("src/loaders/search.ts");
889
913
  expect(r.fixes![0].kind).toBe("rewrite-imports");
890
- expect(r.fixes![0].detail).toContain("@decocms/apps/vtex/utils/transform");
914
+ expect(r.fixes![0].detail).toContain("@decocms/apps-vtex/utils/transform");
891
915
  expect(r.fixes![0].detail).toContain("toProduct");
892
916
  expect(store["/site/src/loaders/search.ts"]).toContain(
893
- '"@decocms/apps/vtex/utils/transform"',
917
+ '"@decocms/apps-vtex/utils/transform"',
894
918
  );
895
919
  expect(store["/site/src/loaders/search.ts"]).not.toContain('"~/lib/vtex-transform"');
896
920
  });
@@ -907,9 +931,9 @@ describe("runAudit — fix mode — vtex-shim-regression swap cases", () => {
907
931
  const report = runAudit(SITE, fs, { writer });
908
932
  const r = report.rules.find((r) => r.rule === "vtex-shim-regression")!;
909
933
  expect(r.fixes).toHaveLength(1);
910
- expect(r.fixes![0].detail).toContain("@decocms/apps/vtex/utils/segment");
934
+ expect(r.fixes![0].detail).toContain("@decocms/apps-vtex/utils/segment");
911
935
  expect(store["/site/src/loaders/x.ts"]).toContain(
912
- '"@decocms/apps/vtex/utils/segment"',
936
+ '"@decocms/apps-vtex/utils/segment"',
913
937
  );
914
938
  });
915
939
 
@@ -964,10 +988,10 @@ describe("runAudit — fix mode — vtex-shim-regression swap cases", () => {
964
988
  const r = report.rules.find((r) => r.rule === "vtex-shim-regression")!;
965
989
  expect(r.fixes!.length).toBe(2);
966
990
  expect(store["/site/src/loaders/A.ts"]).toContain(
967
- '"@decocms/apps/vtex/utils/transform"',
991
+ '"@decocms/apps-vtex/utils/transform"',
968
992
  );
969
993
  expect(store["/site/src/loaders/B.ts"]).toContain(
970
- "'@decocms/apps/vtex/utils/transform'",
994
+ "'@decocms/apps-vtex/utils/transform'",
971
995
  );
972
996
  });
973
997
 
@@ -983,7 +1007,7 @@ describe("runAudit — fix mode — vtex-shim-regression swap cases", () => {
983
1007
  });
984
1008
  runAudit(SITE, fs, { writer });
985
1009
  expect(store["/site/src/loaders/x.ts"]).toContain(
986
- 'import { toProduct as toP } from "@decocms/apps/vtex/utils/transform"',
1010
+ 'import { toProduct as toP } from "@decocms/apps-vtex/utils/transform"',
987
1011
  );
988
1012
  });
989
1013
  });
@@ -1296,7 +1320,7 @@ describe("rule: local-framework-duplicate", () => {
1296
1320
  expect(r.findings[0].message).toContain("pure dup");
1297
1321
  expect(r.findings[0].meta?.id).toBe("clx");
1298
1322
  expect(r.findings[0].meta?.safeToAutoFix).toBe(true);
1299
- expect(r.findings[0].meta?.canonicalImport).toBe("@decocms/start/sdk/clx");
1323
+ expect(r.findings[0].meta?.canonicalImport).toBe("@decocms/blocks/sdk/clx");
1300
1324
  });
1301
1325
 
1302
1326
  it("flags src/sdk/clx.ts when site adds a clsx alias (signature still matches)", () => {
@@ -1328,7 +1352,7 @@ describe("rule: local-framework-duplicate", () => {
1328
1352
  it("flags src/sdk/useSendEvent.ts as warn-only (typing regression risk)", () => {
1329
1353
  const fs = makeFs({
1330
1354
  "/site/src/sdk/useSendEvent.ts":
1331
- 'import { AnalyticsEvent } from "@decocms/apps/commerce/types";\n' +
1355
+ 'import { AnalyticsEvent } from "@decocms/apps-commerce/types";\n' +
1332
1356
  "export const useSendEvent = <E extends AnalyticsEvent>(\n" +
1333
1357
  " { event, on }: { event: E; on: 'click' | 'view' | 'change' },\n" +
1334
1358
  ") => ({\n" +
@@ -1367,7 +1391,7 @@ describe("rule: local-framework-duplicate", () => {
1367
1391
  expect(r.findings[0].meta?.id).toBe("url-relative");
1368
1392
  expect(r.findings[0].meta?.safeToAutoFix).toBe(false);
1369
1393
  expect(r.findings[0].meta?.canonicalImport).toBe(
1370
- "@decocms/apps/commerce/sdk/url",
1394
+ "@decocms/apps-commerce/sdk/url",
1371
1395
  );
1372
1396
  expect(r.findings[0].fix).toContain("stripSearchParams");
1373
1397
  });
@@ -1393,7 +1417,7 @@ describe("rule: local-framework-duplicate", () => {
1393
1417
  expect(r.findings[0].meta?.id).toBe("use-suggestions");
1394
1418
  expect(r.findings[0].meta?.safeToAutoFix).toBe(false);
1395
1419
  expect(r.findings[0].meta?.canonicalImport).toBe(
1396
- "@decocms/start/sdk/useSuggestions",
1420
+ "@decocms/blocks/sdk/useSuggestions",
1397
1421
  );
1398
1422
  expect(r.findings[0].fix).toContain("createUseSuggestions");
1399
1423
  });
@@ -1404,7 +1428,7 @@ describe("rule: local-framework-duplicate", () => {
1404
1428
  // strings. The rule's signature must NOT fire on the shim.
1405
1429
  const fs = makeFs({
1406
1430
  "/site/src/sdk/useSuggestions.ts":
1407
- 'import { createUseSuggestions } from "@decocms/start/sdk/useSuggestions";\n' +
1431
+ 'import { createUseSuggestions } from "@decocms/blocks/sdk/useSuggestions";\n' +
1408
1432
  'import * as Sentry from "@sentry/react";\n' +
1409
1433
  "export const { useSuggestions } = createUseSuggestions({\n" +
1410
1434
  " onError: (err) => Sentry.captureException(err),\n" +
@@ -1437,7 +1461,7 @@ describe("rule: local-framework-duplicate", () => {
1437
1461
  it("flags src/matchers/location.ts as warn-only (behaviour-superset opportunity)", () => {
1438
1462
  const fs = makeFs({
1439
1463
  "/site/src/matchers/location.ts":
1440
- 'import { registerMatcher } from "@decocms/start/cms";\n' +
1464
+ 'import { registerMatcher } from "@decocms/blocks/cms";\n' +
1441
1465
  "export function registerLocationMatcher(): void {\n" +
1442
1466
  ' registerMatcher("website/matchers/location.ts", (rule, ctx) => {\n' +
1443
1467
  " const cookies = ctx.cookies ?? {};\n" +
@@ -1457,7 +1481,7 @@ describe("rule: local-framework-duplicate", () => {
1457
1481
  it("emits zero findings on a clean tree (no duplicates present)", () => {
1458
1482
  const fs = makeFs({
1459
1483
  "/site/src/sections/Hello.tsx":
1460
- 'import { clx } from "@decocms/start/sdk/clx";\nexport default () => <div className={clx("a")}>x</div>;\n',
1484
+ 'import { clx } from "@decocms/blocks/sdk/clx";\nexport default () => <div className={clx("a")}>x</div>;\n',
1461
1485
  });
1462
1486
  const report = runAudit(SITE, fs);
1463
1487
  const r = report.rules.find((r) => r.rule === "local-framework-duplicate")!;
@@ -1485,7 +1509,7 @@ describe("rule: local-framework-duplicate", () => {
1485
1509
  "/site/src/components/B.tsx":
1486
1510
  'import { clx } from "~/sdk/clx";\nimport React from "react";\nexport default () => clx("y");\n',
1487
1511
  "/site/src/components/Unrelated.tsx":
1488
- 'import { clx } from "@decocms/start/sdk/clx";\nexport default () => clx("z");\n',
1512
+ 'import { clx } from "@decocms/blocks/sdk/clx";\nexport default () => clx("z");\n',
1489
1513
  });
1490
1514
  const report = runAudit(SITE, fs, { writer });
1491
1515
  const r = report.rules.find((r) => r.rule === "local-framework-duplicate")!;
@@ -1497,14 +1521,14 @@ describe("rule: local-framework-duplicate", () => {
1497
1521
  expect(store["/site/src/sdk/clx.ts"]).toBeUndefined();
1498
1522
  // Importers rewritten
1499
1523
  expect(store["/site/src/components/A.tsx"]).toContain(
1500
- 'from "@decocms/start/sdk/clx"',
1524
+ 'from "@decocms/blocks/sdk/clx"',
1501
1525
  );
1502
1526
  expect(store["/site/src/components/B.tsx"]).toContain(
1503
- 'from "@decocms/start/sdk/clx"',
1527
+ 'from "@decocms/blocks/sdk/clx"',
1504
1528
  );
1505
1529
  // Already-canonical import untouched
1506
1530
  expect(store["/site/src/components/Unrelated.tsx"]).toContain(
1507
- 'from "@decocms/start/sdk/clx"',
1531
+ 'from "@decocms/blocks/sdk/clx"',
1508
1532
  );
1509
1533
  expect(store["/site/src/components/Unrelated.tsx"]).not.toMatch(/~\/sdk\/clx/);
1510
1534
  });
@@ -1512,12 +1536,12 @@ describe("rule: local-framework-duplicate", () => {
1512
1536
  it("auto-fix is a no-op for warn-only entries (does NOT delete partial-overlap files)", () => {
1513
1537
  const { fs, writer, store } = makeMutableFs({
1514
1538
  "/site/src/sdk/useSendEvent.ts":
1515
- 'import { AnalyticsEvent } from "@decocms/apps/commerce/types";\n' +
1539
+ 'import { AnalyticsEvent } from "@decocms/apps-commerce/types";\n' +
1516
1540
  "export const useSendEvent = <E extends AnalyticsEvent>() => ({\n" +
1517
1541
  ' "data-event": encodeURIComponent("x"),\n' +
1518
1542
  "});\n",
1519
1543
  "/site/src/matchers/location.ts":
1520
- 'import { registerMatcher } from "@decocms/start/cms";\n' +
1544
+ 'import { registerMatcher } from "@decocms/blocks/cms";\n' +
1521
1545
  'registerMatcher("website/matchers/location.ts", () => Boolean(__cf_geo_country));\n',
1522
1546
  });
1523
1547
  const report = runAudit(SITE, fs, { writer });
@@ -4,18 +4,18 @@ export function generateCacheConfig(ctx: MigrationContext): string {
4
4
  if (ctx.platform !== "vtex") {
5
5
  return `/**
6
6
  * Cache configuration — customize edge cache profiles per route type.
7
- * See @decocms/start/sdk/cacheHeaders for available profiles.
7
+ * See @decocms/blocks/sdk/cacheHeaders for available profiles.
8
8
  */
9
- // import { setCacheProfile } from "@decocms/start/sdk/cacheHeaders";
9
+ // import { setCacheProfile } from "@decocms/blocks/sdk/cacheHeaders";
10
10
  // setCacheProfile("product", { sMaxAge: 300, staleWhileRevalidate: 600 });
11
11
  `;
12
12
  }
13
13
 
14
14
  return `/**
15
15
  * Cache configuration for VTEX storefront.
16
- * Overrides default cache profiles from @decocms/start/sdk/cacheHeaders.
16
+ * Overrides default cache profiles from @decocms/blocks/sdk/cacheHeaders.
17
17
  */
18
- // import { setCacheProfile } from "@decocms/start/sdk/cacheHeaders";
18
+ // import { setCacheProfile } from "@decocms/blocks/sdk/cacheHeaders";
19
19
 
20
20
  // Uncomment and adjust as needed:
21
21
  // setCacheProfile("product", { sMaxAge: 300, staleWhileRevalidate: 600 });