@saasicat/cli 0.27.0 → 1.0.0-rc.1

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/README.md CHANGED
@@ -10,7 +10,7 @@ Cross-cutting helpers for consumer CLIs. Provides:
10
10
  - `ManifestCliFlow` + `DEFAULT_MANIFEST_CHECKS` (12 platform checks) —
11
11
  `<app> manifest dump|validate|hash|diff|check`
12
12
 
13
- Spec: [`saas-platform-spec/cli-conventions.md`](https://github.com/uelker70/saasicat/blob/main/packages/saas-platform-spec/cli-conventions.md).
13
+ Spec: [`cli-conventions.md`][conventions] in `@saasicat/spec`.
14
14
 
15
15
  ## Plugin Architecture
16
16
 
@@ -126,7 +126,7 @@ domain commands on top.
126
126
 
127
127
  ## Exit codes
128
128
 
129
- Per [`cli-conventions.md`](https://github.com/uelker70/saasicat/blob/main/packages/saas-platform-spec/cli-conventions.md) §6:
129
+ Per [`cli-conventions.md`][conventions] §6:
130
130
 
131
131
  | Code | Meaning |
132
132
  | ---- | --------------------------------------- |
@@ -139,3 +139,5 @@ Per [`cli-conventions.md`](https://github.com/uelker70/saasicat/blob/main/packag
139
139
  | 6 | conflict |
140
140
  | 7 | drift (e.g. manifest check: error) |
141
141
  | 99 | internal |
142
+
143
+ [conventions]: https://github.com/uelker70/saasicat/blob/main/packages/spec/cli-conventions.md
package/bin/saasicat.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // `saasicat` — bootstrap CLI for the SaaSiCat framework.
3
+ // naming-history: the codemod help below names the pre-1.0 spellings it rewrites.
3
4
  //
4
5
  // Sub-commands:
5
6
  // schema apply [--prisma-schema=PATH] [--fragments=01,02,03]
@@ -16,6 +17,9 @@
16
17
  // Prisma's DSL cannot express to the migration it just wrote.
17
18
  //
18
19
  // init --project-key=X --quota=key:Model [--quota=…]... [--app-name=X] [--api-base=X]
20
+ // codemod v1-imports [--dir=X] [--dry-run]
21
+ // codemod v1-rename [--dir=X] [--dry-run]
22
+ // codemod v1 [--dir=X] [--dry-run] — both, in that order
19
23
  // [--skip-hasher] [--dry-run] [--dir=.]
20
24
  // Writes the platform wiring — config, persistence, manifest
21
25
  // contribution, admin module, one provider per quota — and adds
@@ -23,7 +27,7 @@
23
27
 
24
28
  import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
25
29
  import { existsSync } from 'node:fs';
26
- import { dirname, join, resolve } from 'node:path';
30
+ import { basename, dirname, join, resolve } from 'node:path';
27
31
  import { createRequire } from 'node:module';
28
32
  import { fileURLToPath } from 'node:url';
29
33
  import { spawn } from 'node:child_process';
@@ -43,7 +47,11 @@ import {
43
47
  findFkPointers,
44
48
  hasConstraints,
45
49
  assertValidProjectKey,
50
+ buildImportMap,
46
51
  migrationCreatedBy,
52
+ rewriteImports,
53
+ rewriteManifest,
54
+ rewriteNames,
47
55
  reportConstraints,
48
56
  patchAppModule,
49
57
  patchOptionsFor,
@@ -523,6 +531,161 @@ async function writeConstraintsIntoMigration(args, migrationsBefore) {
523
531
  }
524
532
  }
525
533
 
534
+ /**
535
+ * Rewrites `@saasicat/ui-vue` imports to the 1.0 export map.
536
+ *
537
+ * The rules come from the same table the platform's own move ran on, shipped
538
+ * with this package — so what a consumer's imports become cannot disagree with
539
+ * where the files actually went.
540
+ */
541
+ async function cmdCodemodV1Imports(args) {
542
+ const root = resolve(args.dir ?? '.');
543
+ const dryRun = args['dry-run'] === true;
544
+
545
+ const table = JSON.parse(await readFile(codemodTable('v1-imports.map.json'), 'utf8'));
546
+ const map = buildImportMap(table);
547
+
548
+ const unmapped = new Map();
549
+ let rewritten = 0;
550
+ let touched = 0;
551
+ await walkSources(root, async (full, source) => {
552
+ if (basename(full) === CODEMOD_MANIFEST) return;
553
+ const result = rewriteImports(source, map);
554
+ for (const [subpath, n] of result.unmapped) {
555
+ unmapped.set(subpath, (unmapped.get(subpath) ?? 0) + n);
556
+ }
557
+ if (result.rewritten === 0) return;
558
+ if (!dryRun) await writeFile(full, result.text);
559
+ rewritten += result.rewritten;
560
+ touched += 1;
561
+ });
562
+
563
+ console.log(
564
+ `${dryRun ? 'Would rewrite' : 'Rewrote'} ${rewritten} import(s) in ${touched} file(s).`,
565
+ );
566
+ if (unmapped.size === 0) return;
567
+
568
+ console.log('');
569
+ console.log('These have no new home — they need a decision, not a rewrite:');
570
+ for (const [subpath, n] of [...unmapped].sort()) {
571
+ console.log(` ${String(n).padStart(3)}× @saasicat/ui-vue/${subpath}`);
572
+ }
573
+ console.log('');
574
+ console.log(' They moved into `features/` or `internal/`, which the 1.0 surface does');
575
+ console.log(' not publish: they were domain or page-private components, and importing');
576
+ console.log(' them tied your app to our internal structure. Copy what you need into');
577
+ console.log(' your own repository.');
578
+ }
579
+
580
+ /**
581
+ * Rewrites the names 1.0 changed: identifier stems, registry keys, the one
582
+ * token that had two meanings, and the e2e helper's subpath.
583
+ *
584
+ * Same shape as `v1-imports`, same table discipline: the rules are read from
585
+ * `codemods/v1-rename.map.json`, shipped with this package, so what a
586
+ * consumer's code becomes is what the platform's own rename was checked
587
+ * against.
588
+ */
589
+ async function cmdCodemodV1Rename(args) {
590
+ const root = resolve(args.dir ?? '.');
591
+ const dryRun = args['dry-run'] === true;
592
+ const table = JSON.parse(await readFile(codemodTable('v1-rename.map.json'), 'utf8'));
593
+
594
+ const ambiguous = new Map();
595
+ let rewritten = 0;
596
+ let touched = 0;
597
+ let manifestsTouched = 0;
598
+ await walkSources(root, async (full, source) => {
599
+ // A manifest takes the package renames in its dependency fields; a
600
+ // source file takes everything. Under pnpm an import a manifest does
601
+ // not declare fails to resolve, so the two travel together.
602
+ const result =
603
+ basename(full) === CODEMOD_MANIFEST
604
+ ? rewriteManifest(source, table, { targetRange: `^${OWN_VERSION}` })
605
+ : rewriteNames(source, table);
606
+ for (const name of result.ambiguous) {
607
+ ambiguous.set(name, (ambiguous.get(name) ?? 0) + 1);
608
+ }
609
+ if (result.rewritten === 0) return;
610
+ if (!dryRun) await writeFile(full, result.text);
611
+ rewritten += result.rewritten;
612
+ touched += 1;
613
+ if (basename(full) === CODEMOD_MANIFEST) manifestsTouched += 1;
614
+ });
615
+
616
+ console.log(
617
+ `${dryRun ? 'Would rewrite' : 'Rewrote'} ${rewritten} name(s) in ${touched} file(s).`,
618
+ );
619
+ if (manifestsTouched > 0) {
620
+ // The lockfile is not rewritten: its shape is the package manager's,
621
+ // and a wrong guess at it is worse than an honest instruction. A CI
622
+ // that installs with a frozen lockfile refuses the migrated checkout
623
+ // until it is regenerated.
624
+ console.log('');
625
+ console.log(
626
+ `${manifestsTouched} package.json ${manifestsTouched === 1 ? 'file' : 'files'} changed — ` +
627
+ 'regenerate the lockfile before committing:',
628
+ );
629
+ console.log(
630
+ ' pnpm install (or npm install / yarn install, whichever owns your lockfile)',
631
+ );
632
+ }
633
+ if (ambiguous.size === 0) return;
634
+
635
+ console.log('');
636
+ console.log('These need a decision, not a rewrite:');
637
+ for (const [name, n] of [...ambiguous].sort()) {
638
+ console.log(` ${String(n).padStart(3)}× ${name}`);
639
+ }
640
+ console.log('');
641
+ console.log(' FEATURE_UI_REGISTRY_TOKEN meant one registry in `@saasicat/nest/billing`');
642
+ console.log(' and another in `@saasicat/nest/catalog`. Import it from the entry you');
643
+ console.log(' mean — BILLING_FEATURE_UI_REGISTRY_TOKEN or CATALOG_FEATURE_UI_REGISTRY_TOKEN.');
644
+ console.log(' A dependency listed "in <field> (<range>)" points at a workspace or a path;');
645
+ console.log(' rename it by hand to @saasicat/core at the location you keep it.');
646
+ }
647
+
648
+ /**
649
+ * The version this CLI was released as — and therefore the line a consumer
650
+ * running its codemod is migrating to. The manifest rewrite sets the renamed
651
+ * dependency to `^<this>`, because the old range (`^0.27.0`) names a line
652
+ * the renamed package was never published on.
653
+ */
654
+ const OWN_VERSION = JSON.parse(
655
+ await readFile(join(dirname(require_.resolve('@saasicat/cli')), '..', 'package.json'), 'utf8'),
656
+ ).version;
657
+
658
+ /** Where a shipped codemod table lives, resolved through the package itself. */
659
+ function codemodTable(name) {
660
+ return join(dirname(require_.resolve('@saasicat/cli')), '..', 'codemods', name);
661
+ }
662
+
663
+ // Anything a build wrote is skipped, whatever it is called: `dist`, `dist-app`,
664
+ // `dist-dev` — a consumer's declaration output carries the old names too, and
665
+ // rewriting it would only make the next build disagree with it.
666
+ const CODEMOD_SKIP = new Set(['node_modules', '.git', '.output', 'coverage']);
667
+ const isBuildOutput = (name) => name === 'dist' || name.startsWith('dist-');
668
+ const CODEMOD_EXTENSIONS = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|vue|md)$/;
669
+ /** Walked for the package renames alone; see `rewriteManifest`. */
670
+ const CODEMOD_MANIFEST = 'package.json';
671
+
672
+ /** Every source file under `root` a codemod may touch, with its text. */
673
+ async function walkSources(root, visit) {
674
+ const walk = async (dir) => {
675
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
676
+ if (CODEMOD_SKIP.has(entry.name) || isBuildOutput(entry.name)) continue;
677
+ const full = join(dir, entry.name);
678
+ if (entry.isDirectory()) {
679
+ await walk(full);
680
+ continue;
681
+ }
682
+ if (!CODEMOD_EXTENSIONS.test(entry.name) && entry.name !== CODEMOD_MANIFEST) continue;
683
+ await visit(full, await readFile(full, 'utf8'));
684
+ }
685
+ };
686
+ await walk(root);
687
+ }
688
+
526
689
  /** Reads a repeated flag (`--quota=a --quota=b`) off argv. */
527
690
  function repeatedFlag(argv, name) {
528
691
  return argv
@@ -662,6 +825,18 @@ async function patchAppModuleFile(root, plan, args) {
662
825
 
663
826
  async function main() {
664
827
  const [, , cmd, sub, ...rest] = process.argv;
828
+ if (cmd === 'codemod' && sub === 'v1-imports') {
829
+ return cmdCodemodV1Imports(parseArgs(rest));
830
+ }
831
+ if (cmd === 'codemod' && sub === 'v1-rename') {
832
+ return cmdCodemodV1Rename(parseArgs(rest));
833
+ }
834
+ if (cmd === 'codemod' && sub === 'v1') {
835
+ // Imports first: the rename table keys its per-entry tokens by the
836
+ // specifier they are imported from, which the import rewrite settles.
837
+ await cmdCodemodV1Imports(parseArgs(rest));
838
+ return cmdCodemodV1Rename(parseArgs(rest));
839
+ }
665
840
  if (cmd === 'init') {
666
841
  return cmdInit(parseArgs([sub, ...rest].filter(Boolean)), process.argv.slice(3));
667
842
  }
@@ -694,6 +869,14 @@ async function main() {
694
869
  console.log(' every plan must declare one, or the catalogue does not load.');
695
870
  console.log(' init --project-key=myapp --quota=notes:Note --quota=seats:Seat');
696
871
  console.log('');
872
+ console.log(' codemod v1 [--dir=.] [--dry-run]');
873
+ console.log(' the whole 1.0 migration: v1-imports, then v1-rename');
874
+ console.log(' codemod v1-imports [--dir=.] [--dry-run]');
875
+ console.log(' rewrite @saasicat/ui-vue imports to the 1.0 export map');
876
+ console.log(' codemod v1-rename [--dir=.] [--dry-run]');
877
+ console.log(' rewrite the names 1.0 changed: SaasPlatform*, Symbol.for keys,');
878
+ console.log(' FEATURE_UI_REGISTRY_TOKEN, @saasicat/ui-vue/testing-e2e/*');
879
+ console.log('');
697
880
  console.log('Optional --prisma-schema=PATH (default prisma/schema.prisma).');
698
881
  console.log('Optional --tenant-model=X --user-model=Y for apply/migrate — enables');
699
882
  console.log(' the foreign keys from the platform tables to your models.');
@@ -0,0 +1,135 @@
1
+ {
2
+ "_comment": [
3
+ "The 4.1 move, as data rather than as commands.",
4
+ "",
5
+ "Two axes decide every destination, and each directory answers exactly one",
6
+ "question (AP3 \u00a73.6): the LAYER a file belongs to (client / vue / quasar /",
7
+ "SFC) and how far it is REUSED \u2014 ui (surface-neutral, many domains) \u2192",
8
+ "features (one domain, many pages) \u2192 internal/<page> (one page) \u2192 pages",
9
+ "(one route).",
10
+ "",
11
+ "Written down because the mapping is the reviewable part. A 210-file move",
12
+ "expressed as `git mv` lines is a diff nobody can check; expressed as a",
13
+ "table, the question is whether each row is right.",
14
+ "",
15
+ "Two destinations differ from the sketch in AP3 \u00a73.6, and the reason is a",
16
+ "measurement rather than a preference. Consumers import `FeatureGate.vue` 19",
17
+ "times and `MfaPromptDialog.vue` 5 times across notesapp, vereinsfux and",
18
+ "autohauspro \u2014 `FeatureGate` is the single most imported component in the",
19
+ "package. AP3 places FeatureGate in `features/entitlement/`, and `features/`",
20
+ "is explicitly not exported; MfaPromptDialog is used by one page, which by",
21
+ "the reuse axis alone would put it in `internal/`. Either placement removes a",
22
+ "component three consumer repositories depend on.",
23
+ "",
24
+ "So both stay on the exported surface. A component a consumer embeds in their",
25
+ "own application is part of the public surface no matter how many of OUR",
26
+ "pages use it \u2014 the reuse axis measures our reuse, and that is not the same",
27
+ "question."
28
+ ],
29
+ "moves": {
30
+ "components/admin-page/AdminPage.vue": "ui/page/AdminPage.vue",
31
+ "components/admin-page/AdminHero.vue": "ui/page/AdminHero.vue",
32
+ "components/admin-page/AdminBody.vue": "ui/page/AdminBody.vue",
33
+ "components/admin-page/AdminSection.vue": "ui/page/AdminSection.vue",
34
+ "components/admin-page/AdminFilters.vue": "ui/page/AdminFilters.vue",
35
+ "components/admin-page/AdminAccordion.vue": "ui/page/AdminAccordion.vue",
36
+ "components/admin-page/AdminTable.vue": "ui/data/AdminTable.vue",
37
+ "components/admin-page/AdminPaginator.vue": "ui/data/AdminPaginator.vue",
38
+ "components/admin-page/AdminStatistics.vue": "ui/data/AdminStatistics.vue",
39
+ "components/admin-page/AdminKpi.vue": "ui/data/AdminKpi.vue",
40
+ "components/KvBlock.vue": "ui/data/KvBlock.vue",
41
+ "components/admin-page/AdminRefreshBtn.vue": "ui/feedback/AdminRefreshBtn.vue",
42
+ "components/wizard-stepper/WizardStepper.vue": "ui/page/WizardStepper.vue",
43
+ "components/plan/PlanCycleToggle.vue": "@saasicat/ui-vue-tenant/plan/PlanCycleToggle.vue",
44
+ "components/plan/PlanGrid.vue": "@saasicat/ui-vue-tenant/plan/PlanGrid.vue",
45
+ "components/plan/PriceSummary.vue": "@saasicat/ui-vue-tenant/plan/PriceSummary.vue",
46
+ "components/plan/PromoCodeInput.vue": "@saasicat/ui-vue-tenant/plan/PromoCodeInput.vue",
47
+ "components/plan/PublicBundleGrid.vue": "@saasicat/ui-vue-tenant/plan/PublicBundleGrid.vue",
48
+ "components/plan-create-dialog/PlanCreateDialog.vue": "features/plan/PlanCreateDialog.vue",
49
+ "components/plan-list/PlanList.vue": "features/plan/PlanList.vue",
50
+ "components/plan-matrix/PlanMatrix.vue": "features/plan/PlanMatrix.vue",
51
+ "components/plan-review/PlanReview.vue": "features/plan/PlanReview.vue",
52
+ "components/plan-detail/PlanDetail.vue": "features/plan/PlanDetail.vue",
53
+ "components/plan-detail/PlanTerminateDialog.vue": "features/plan/PlanTerminateDialog.vue",
54
+ "components/plan-detail/PlanAuditLog.vue": "features/plan/internal/PlanAuditLog.vue",
55
+ "components/plan-detail/PlanDetailKpis.vue": "features/plan/internal/PlanDetailKpis.vue",
56
+ "components/plan-detail/PlanTitleEdit.vue": "features/plan/internal/PlanTitleEdit.vue",
57
+ "components/plan-detail/PlanVersionDiffPanel.vue": "features/plan/internal/PlanVersionDiffPanel.vue",
58
+ "components/plan-detail/PlanVersionsPanel.vue": "features/plan/internal/PlanVersionsPanel.vue",
59
+ "components/plan-detail/types.ts": "features/plan/internal/plan-detail.types.ts",
60
+ "components/plan-version-editor/PlanVersionEditor.vue": "features/plan/PlanVersionEditor.vue",
61
+ "components/plan-version-editor/PlanCatalogPreview.vue": "features/plan/internal/PlanCatalogPreview.vue",
62
+ "components/plan-version-editor/PlanComponentPool.vue": "features/plan/internal/PlanComponentPool.vue",
63
+ "components/plan-version-editor/PlanVersionBasket.vue": "features/plan/internal/PlanVersionBasket.vue",
64
+ "components/plan-version-editor/PlanVersionDiffDialog.vue": "features/plan/internal/PlanVersionDiffDialog.vue",
65
+ "components/plan-version-editor/PlanVersionEditorHeader.vue": "features/plan/internal/PlanVersionEditorHeader.vue",
66
+ "components/plan-version-editor/types.ts": "features/plan/internal/plan-version-editor.types.ts",
67
+ "components/bundle-editor/BundleVersionInlineEditor.vue": "features/bundle/BundleVersionInlineEditor.vue",
68
+ "components/bundle-editor/BundleVersionStrip.vue": "features/bundle/BundleVersionStrip.vue",
69
+ "components/BundleVersionPublishDialog.vue": "features/bundle/BundleVersionPublishDialog.vue",
70
+ "components/bundle-editor/BundleCreatePanel.vue": "features/bundle/internal/BundleCreatePanel.vue",
71
+ "components/bundle-editor/BundleFeaturesEditor.vue": "features/bundle/internal/BundleFeaturesEditor.vue",
72
+ "components/bundle-editor/BundlePlanCompatPicker.vue": "features/bundle/internal/BundlePlanCompatPicker.vue",
73
+ "components/bundle-editor/BundleQuotasEditor.vue": "features/bundle/internal/BundleQuotasEditor.vue",
74
+ "components/bundle-editor/BundleStatusBanner.vue": "features/bundle/internal/BundleStatusBanner.vue",
75
+ "components/bundle-editor/bundle-version-status.ts": "features/bundle/internal/bundle-version-status.ts",
76
+ "components/bundle-editor/catalog-i18n.ts": "features/bundle/internal/catalog-i18n.ts",
77
+ "components/FeatureGate.vue": "ui/entitlement/FeatureGate.vue",
78
+ "components/dialogs/PilotCreateDialog.vue": "internal/dialogs/PilotCreateDialog.vue",
79
+ "components/dialogs/PilotEditDialog.vue": "internal/dialogs/PilotEditDialog.vue",
80
+ "components/dialogs/PromoCodeCreateDialog.vue": "internal/dialogs/PromoCodeCreateDialog.vue",
81
+ "components/dialogs/PromoCodeDialogFields.vue": "internal/dialogs/PromoCodeDialogFields.vue",
82
+ "components/dialogs/PromoCodeEditDialog.vue": "internal/dialogs/PromoCodeEditDialog.vue",
83
+ "components/dialogs/pilot-dialog.css": "internal/dialogs/pilot-dialog.css",
84
+ "components/dialogs/types.ts": "internal/dialogs/types.ts",
85
+ "pages-standard/AdminLayout.vue": "layouts/AdminLayout.vue",
86
+ "pages-standard/SuperAdminLoginPage.vue": "auth/SuperAdminLoginPage.vue",
87
+ "pages-standard/SuperAdminSetupWizard.vue": "auth/SuperAdminSetupWizard.vue",
88
+ "pages-standard/AdminManifestErrorPage.vue": "pages/AdminManifestErrorPage.vue",
89
+ "pages-standard/AuditPage.vue": "pages/AuditPage.vue",
90
+ "pages-standard/BundlesPage.vue": "pages/BundlesPage.vue",
91
+ "pages-standard/DashboardPage.vue": "pages/DashboardPage.vue",
92
+ "pages-standard/DiscoveryPage.vue": "pages/DiscoveryPage.vue",
93
+ "pages-standard/EmailHistoryPage.vue": "pages/EmailHistoryPage.vue",
94
+ "pages-standard/MarketingCatalogPage.vue": "pages/MarketingCatalogPage.vue",
95
+ "pages-standard/PilotsPage.vue": "pages/PilotsPage.vue",
96
+ "pages-standard/PlansPage.vue": "pages/PlansPage.vue",
97
+ "pages-standard/PlatformEmailPage.vue": "pages/PlatformEmailPage.vue",
98
+ "pages-standard/PromoCodeDetailPage.vue": "pages/PromoCodeDetailPage.vue",
99
+ "pages-standard/PromoCodesPage.vue": "pages/PromoCodesPage.vue",
100
+ "pages-standard/SubscriptionsPage.vue": "pages/SubscriptionsPage.vue",
101
+ "pages-standard/TenantDetailPage.vue": "pages/TenantDetailPage.vue",
102
+ "pages-standard/TenantsPage.vue": "pages/TenantsPage.vue",
103
+ "pages-standard/UsersPage.vue": "pages/UsersPage.vue",
104
+ "pages-standard/email-history.types.ts": "internal/email-history/email-history.types.ts",
105
+ "pages-standard/platform-email.types.ts": "internal/platform-email/platform-email.types.ts",
106
+ "components/MfaPromptDialog.vue": "ui/overlay/MfaPromptDialog.vue",
107
+ "components/LocaleSwitcher.vue": "ui/page/LocaleSwitcher.vue",
108
+ "components/ThemeSwitcher.vue": "ui/page/ThemeSwitcher.vue",
109
+ "components/MarketingPromotionsTab.vue": "features/marketing/MarketingPromotionsTab.vue",
110
+ "components/TenantActionConfirmDialog.vue": "features/tenant/TenantActionConfirmDialog.vue",
111
+ "components/VersionDiffPreview.vue": "features/bundle/VersionDiffPreview.vue",
112
+ "pages-standard/sa-theme.css": "ui/theme/sa-theme.css"
113
+ },
114
+ "moveDirectories": {
115
+ "pages-standard/bundles-page": "internal/bundles-page",
116
+ "pages-standard/discovery-page": "internal/discovery-page",
117
+ "pages-standard/marketing-catalog": "internal/marketing-catalog",
118
+ "pages-standard/plan-versions": "internal/plan-versions",
119
+ "pages-standard/plans-page": "internal/plans-page",
120
+ "pages-standard/tenant-detail": "internal/tenant-detail",
121
+ "pages-standard/tenants": "internal/tenants"
122
+ },
123
+ "packages": {
124
+ "_": "Subpaths that left @saasicat/ui-vue for a package of their own. The value is a full specifier, and the codemod emits it verbatim.",
125
+ "pages-tenant/": "@saasicat/ui-vue-tenant/"
126
+ },
127
+ "stays": {
128
+ "client/": "framework-free layer, unchanged",
129
+ "vue/": "composables, unchanged",
130
+ "quasar/": "the one non-SFC place that may import quasar, unchanged",
131
+ "ui/theme/": "already in its destination (AP2)",
132
+ "testing-e2e/ \u2192 testing/": "renamed in phase 5 (5.8); `codemod v1-rename` rewrites the specifier",
133
+ "pages-standard/sa-theme.css \u2192 ui/theme/": "moved so that pages-standard/ can go; renamed to theme.css in 4.2"
134
+ }
135
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "_namingHistory": "The 1.0 rename as data. Every old spelling below is named on purpose: this table is what `saasicat codemod v1-rename` runs on, and `tests/one-spelling.test.js` reads this key as the declaration.",
3
+ "_comment": [
4
+ "Phase 5 (AP1) renamed four things a consumer's code can carry:",
5
+ "identifier stems, registry keys, one token that had two meanings, and",
6
+ "one export subpath. Each section is one of those, and the codemod",
7
+ "applies them in that order.",
8
+ "",
9
+ "`identifierStems` is matched anywhere in an identifier — the stem alone",
10
+ "covers `SaasPlatformModule`, `createSaasPlatformTestModule` and",
11
+ "`CreateSaasPlatformTestModuleOptions` without listing them. The lowercase",
12
+ "`saasicat` (scope, files) is not a stem and is never touched.",
13
+ "",
14
+ "`registryKeys` rewrites the string a `Symbol.for` is called with. A",
15
+ "consumer that spelled a key themselves gets the same symbol the platform",
16
+ "now registers; without this their injection would resolve to nothing.",
17
+ "",
18
+ "`packages` renames a whole package — in specifiers and in package.json.",
19
+ "",
20
+ "`entryTokens` is keyed by the import specifier, because the old name",
21
+ "meant different registries in different entries. An import of it from",
22
+ "anywhere else is reported, not guessed."
23
+ ],
24
+ "identifierStems": {
25
+ "SaasPlatform": "SaaSiCat",
26
+ "Saasicat": "SaaSiCat",
27
+ "SaaSicat": "SaaSiCat"
28
+ },
29
+ "registryKeys": {
30
+ "saas-platform/": "saasicat/nest/",
31
+ "saas-platform-nest/": "saasicat/nest/",
32
+ "saas-platform-cli/": "saasicat/cli/",
33
+ "@saasicat/ui-vue/": "saasicat/ui-vue/",
34
+ "FakeTransactionRunner.tx": "saasicat/nest/FakeTransactionRunner.tx"
35
+ },
36
+ "entryTokens": {
37
+ "@saasicat/nest/billing": {
38
+ "FEATURE_UI_REGISTRY_TOKEN": "BILLING_FEATURE_UI_REGISTRY_TOKEN"
39
+ },
40
+ "@saasicat/nest/catalog": {
41
+ "FEATURE_UI_REGISTRY_TOKEN": "CATALOG_FEATURE_UI_REGISTRY_TOKEN"
42
+ }
43
+ },
44
+ "subpaths": {
45
+ "@saasicat/ui-vue/testing-e2e/": "@saasicat/ui-vue/testing/"
46
+ },
47
+ "packages": {
48
+ "_": "A package that was renamed. Applied to import specifiers AND to the dependency fields of every package.json the walk meets: an import rewritten without its manifest does not resolve under pnpm.",
49
+ "@saasicat/types": "@saasicat/core"
50
+ }
51
+ }
@@ -0,0 +1 @@
1
+ 97f66acb8ca26c7c9470c1991febbb2f68518f1b3cd364af6d3566249550482a