@saasicat/cli 0.27.0 → 1.0.0-rc.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/README.md +4 -2
- package/bin/saasicat.js +145 -0
- package/codemods/v1-imports.map.json +135 -0
- package/codemods/v1-rename.map.json +45 -0
- package/dist/.build-stamp +1 -0
- package/dist/index.cjs +204 -9
- package/dist/index.d.cts +85 -1
- package/dist/index.d.ts +85 -1
- package/dist/index.js +197 -9
- package/package.json +8 -7
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: [`
|
|
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`]
|
|
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
|
|
@@ -43,7 +47,10 @@ import {
|
|
|
43
47
|
findFkPointers,
|
|
44
48
|
hasConstraints,
|
|
45
49
|
assertValidProjectKey,
|
|
50
|
+
buildImportMap,
|
|
46
51
|
migrationCreatedBy,
|
|
52
|
+
rewriteImports,
|
|
53
|
+
rewriteNames,
|
|
47
54
|
reportConstraints,
|
|
48
55
|
patchAppModule,
|
|
49
56
|
patchOptionsFor,
|
|
@@ -523,6 +530,124 @@ async function writeConstraintsIntoMigration(args, migrationsBefore) {
|
|
|
523
530
|
}
|
|
524
531
|
}
|
|
525
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Rewrites `@saasicat/ui-vue` imports to the 1.0 export map.
|
|
535
|
+
*
|
|
536
|
+
* The rules come from the same table the platform's own move ran on, shipped
|
|
537
|
+
* with this package — so what a consumer's imports become cannot disagree with
|
|
538
|
+
* where the files actually went.
|
|
539
|
+
*/
|
|
540
|
+
async function cmdCodemodV1Imports(args) {
|
|
541
|
+
const root = resolve(args.dir ?? '.');
|
|
542
|
+
const dryRun = args['dry-run'] === true;
|
|
543
|
+
|
|
544
|
+
const table = JSON.parse(await readFile(codemodTable('v1-imports.map.json'), 'utf8'));
|
|
545
|
+
const map = buildImportMap(table);
|
|
546
|
+
|
|
547
|
+
const unmapped = new Map();
|
|
548
|
+
let rewritten = 0;
|
|
549
|
+
let touched = 0;
|
|
550
|
+
await walkSources(root, async (full, source) => {
|
|
551
|
+
const result = rewriteImports(source, map);
|
|
552
|
+
for (const [subpath, n] of result.unmapped) {
|
|
553
|
+
unmapped.set(subpath, (unmapped.get(subpath) ?? 0) + n);
|
|
554
|
+
}
|
|
555
|
+
if (result.rewritten === 0) return;
|
|
556
|
+
if (!dryRun) await writeFile(full, result.text);
|
|
557
|
+
rewritten += result.rewritten;
|
|
558
|
+
touched += 1;
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
console.log(
|
|
562
|
+
`${dryRun ? 'Would rewrite' : 'Rewrote'} ${rewritten} import(s) in ${touched} file(s).`,
|
|
563
|
+
);
|
|
564
|
+
if (unmapped.size === 0) return;
|
|
565
|
+
|
|
566
|
+
console.log('');
|
|
567
|
+
console.log('These have no new home — they need a decision, not a rewrite:');
|
|
568
|
+
for (const [subpath, n] of [...unmapped].sort()) {
|
|
569
|
+
console.log(` ${String(n).padStart(3)}× @saasicat/ui-vue/${subpath}`);
|
|
570
|
+
}
|
|
571
|
+
console.log('');
|
|
572
|
+
console.log(' They moved into `features/` or `internal/`, which the 1.0 surface does');
|
|
573
|
+
console.log(' not publish: they were domain or page-private components, and importing');
|
|
574
|
+
console.log(' them tied your app to our internal structure. Copy what you need into');
|
|
575
|
+
console.log(' your own repository.');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Rewrites the names 1.0 changed: identifier stems, registry keys, the one
|
|
580
|
+
* token that had two meanings, and the e2e helper's subpath.
|
|
581
|
+
*
|
|
582
|
+
* Same shape as `v1-imports`, same table discipline: the rules are read from
|
|
583
|
+
* `codemods/v1-rename.map.json`, shipped with this package, so what a
|
|
584
|
+
* consumer's code becomes is what the platform's own rename was checked
|
|
585
|
+
* against.
|
|
586
|
+
*/
|
|
587
|
+
async function cmdCodemodV1Rename(args) {
|
|
588
|
+
const root = resolve(args.dir ?? '.');
|
|
589
|
+
const dryRun = args['dry-run'] === true;
|
|
590
|
+
const table = JSON.parse(await readFile(codemodTable('v1-rename.map.json'), 'utf8'));
|
|
591
|
+
|
|
592
|
+
const ambiguous = new Map();
|
|
593
|
+
let rewritten = 0;
|
|
594
|
+
let touched = 0;
|
|
595
|
+
await walkSources(root, async (full, source) => {
|
|
596
|
+
const result = rewriteNames(source, table);
|
|
597
|
+
for (const name of result.ambiguous) {
|
|
598
|
+
ambiguous.set(name, (ambiguous.get(name) ?? 0) + 1);
|
|
599
|
+
}
|
|
600
|
+
if (result.rewritten === 0) return;
|
|
601
|
+
if (!dryRun) await writeFile(full, result.text);
|
|
602
|
+
rewritten += result.rewritten;
|
|
603
|
+
touched += 1;
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
console.log(
|
|
607
|
+
`${dryRun ? 'Would rewrite' : 'Rewrote'} ${rewritten} name(s) in ${touched} file(s).`,
|
|
608
|
+
);
|
|
609
|
+
if (ambiguous.size === 0) return;
|
|
610
|
+
|
|
611
|
+
console.log('');
|
|
612
|
+
console.log('These need a decision, not a rewrite:');
|
|
613
|
+
for (const [name, n] of [...ambiguous].sort()) {
|
|
614
|
+
console.log(` ${String(n).padStart(3)}× ${name}`);
|
|
615
|
+
}
|
|
616
|
+
console.log('');
|
|
617
|
+
console.log(' FEATURE_UI_REGISTRY_TOKEN meant one registry in `@saasicat/nest/billing`');
|
|
618
|
+
console.log(' and another in `@saasicat/nest/catalog`. Import it from the entry you');
|
|
619
|
+
console.log(' mean — BILLING_FEATURE_UI_REGISTRY_TOKEN or CATALOG_FEATURE_UI_REGISTRY_TOKEN.');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** Where a shipped codemod table lives, resolved through the package itself. */
|
|
623
|
+
function codemodTable(name) {
|
|
624
|
+
return join(dirname(require_.resolve('@saasicat/cli')), '..', 'codemods', name);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Anything a build wrote is skipped, whatever it is called: `dist`, `dist-app`,
|
|
628
|
+
// `dist-dev` — a consumer's declaration output carries the old names too, and
|
|
629
|
+
// rewriting it would only make the next build disagree with it.
|
|
630
|
+
const CODEMOD_SKIP = new Set(['node_modules', '.git', '.output', 'coverage']);
|
|
631
|
+
const isBuildOutput = (name) => name === 'dist' || name.startsWith('dist-');
|
|
632
|
+
const CODEMOD_EXTENSIONS = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|vue|md)$/;
|
|
633
|
+
|
|
634
|
+
/** Every source file under `root` a codemod may touch, with its text. */
|
|
635
|
+
async function walkSources(root, visit) {
|
|
636
|
+
const walk = async (dir) => {
|
|
637
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
638
|
+
if (CODEMOD_SKIP.has(entry.name) || isBuildOutput(entry.name)) continue;
|
|
639
|
+
const full = join(dir, entry.name);
|
|
640
|
+
if (entry.isDirectory()) {
|
|
641
|
+
await walk(full);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (!CODEMOD_EXTENSIONS.test(entry.name)) continue;
|
|
645
|
+
await visit(full, await readFile(full, 'utf8'));
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
await walk(root);
|
|
649
|
+
}
|
|
650
|
+
|
|
526
651
|
/** Reads a repeated flag (`--quota=a --quota=b`) off argv. */
|
|
527
652
|
function repeatedFlag(argv, name) {
|
|
528
653
|
return argv
|
|
@@ -662,6 +787,18 @@ async function patchAppModuleFile(root, plan, args) {
|
|
|
662
787
|
|
|
663
788
|
async function main() {
|
|
664
789
|
const [, , cmd, sub, ...rest] = process.argv;
|
|
790
|
+
if (cmd === 'codemod' && sub === 'v1-imports') {
|
|
791
|
+
return cmdCodemodV1Imports(parseArgs(rest));
|
|
792
|
+
}
|
|
793
|
+
if (cmd === 'codemod' && sub === 'v1-rename') {
|
|
794
|
+
return cmdCodemodV1Rename(parseArgs(rest));
|
|
795
|
+
}
|
|
796
|
+
if (cmd === 'codemod' && sub === 'v1') {
|
|
797
|
+
// Imports first: the rename table keys its per-entry tokens by the
|
|
798
|
+
// specifier they are imported from, which the import rewrite settles.
|
|
799
|
+
await cmdCodemodV1Imports(parseArgs(rest));
|
|
800
|
+
return cmdCodemodV1Rename(parseArgs(rest));
|
|
801
|
+
}
|
|
665
802
|
if (cmd === 'init') {
|
|
666
803
|
return cmdInit(parseArgs([sub, ...rest].filter(Boolean)), process.argv.slice(3));
|
|
667
804
|
}
|
|
@@ -694,6 +831,14 @@ async function main() {
|
|
|
694
831
|
console.log(' every plan must declare one, or the catalogue does not load.');
|
|
695
832
|
console.log(' init --project-key=myapp --quota=notes:Note --quota=seats:Seat');
|
|
696
833
|
console.log('');
|
|
834
|
+
console.log(' codemod v1 [--dir=.] [--dry-run]');
|
|
835
|
+
console.log(' the whole 1.0 migration: v1-imports, then v1-rename');
|
|
836
|
+
console.log(' codemod v1-imports [--dir=.] [--dry-run]');
|
|
837
|
+
console.log(' rewrite @saasicat/ui-vue imports to the 1.0 export map');
|
|
838
|
+
console.log(' codemod v1-rename [--dir=.] [--dry-run]');
|
|
839
|
+
console.log(' rewrite the names 1.0 changed: SaasPlatform*, Symbol.for keys,');
|
|
840
|
+
console.log(' FEATURE_UI_REGISTRY_TOKEN, @saasicat/ui-vue/testing-e2e/*');
|
|
841
|
+
console.log('');
|
|
697
842
|
console.log('Optional --prisma-schema=PATH (default prisma/schema.prisma).');
|
|
698
843
|
console.log('Optional --tenant-model=X --user-model=Y for apply/migrate — enables');
|
|
699
844
|
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,45 @@
|
|
|
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
|
+
"`entryTokens` is keyed by the import specifier, because the old name",
|
|
19
|
+
"meant different registries in different entries. An import of it from",
|
|
20
|
+
"anywhere else is reported, not guessed."
|
|
21
|
+
],
|
|
22
|
+
"identifierStems": {
|
|
23
|
+
"SaasPlatform": "SaaSiCat",
|
|
24
|
+
"Saasicat": "SaaSiCat",
|
|
25
|
+
"SaaSicat": "SaaSiCat"
|
|
26
|
+
},
|
|
27
|
+
"registryKeys": {
|
|
28
|
+
"saas-platform/": "saasicat/nest/",
|
|
29
|
+
"saas-platform-nest/": "saasicat/nest/",
|
|
30
|
+
"saas-platform-cli/": "saasicat/cli/",
|
|
31
|
+
"@saasicat/ui-vue/": "saasicat/ui-vue/",
|
|
32
|
+
"FakeTransactionRunner.tx": "saasicat/nest/FakeTransactionRunner.tx"
|
|
33
|
+
},
|
|
34
|
+
"entryTokens": {
|
|
35
|
+
"@saasicat/nest/billing": {
|
|
36
|
+
"FEATURE_UI_REGISTRY_TOKEN": "BILLING_FEATURE_UI_REGISTRY_TOKEN"
|
|
37
|
+
},
|
|
38
|
+
"@saasicat/nest/catalog": {
|
|
39
|
+
"FEATURE_UI_REGISTRY_TOKEN": "CATALOG_FEATURE_UI_REGISTRY_TOKEN"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"subpaths": {
|
|
43
|
+
"@saasicat/ui-vue/testing-e2e/": "@saasicat/ui-vue/testing/"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
87fdb737df2d686fe3d296add0d87c2a3765371380b486e2ff20fda896b3db65
|
package/dist/index.cjs
CHANGED
|
@@ -64,6 +64,7 @@ __export(index_exports, {
|
|
|
64
64
|
MfaSetupFlow: () => MfaSetupFlow,
|
|
65
65
|
PLATFORM_DOCTOR_CHECK_PROVIDERS: () => PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
66
66
|
PlanCatalogDoctorCheck: () => PlanCatalogDoctorCheck,
|
|
67
|
+
UI_VUE_SPECIFIER: () => UI_VUE_SPECIFIER,
|
|
67
68
|
USER_MANAGEMENT_PORT_TOKEN: () => USER_MANAGEMENT_PORT_TOKEN,
|
|
68
69
|
USER_PORT_TOKEN: () => USER_PORT_TOKEN,
|
|
69
70
|
UserCommands: () => UserCommands,
|
|
@@ -78,6 +79,7 @@ __export(index_exports, {
|
|
|
78
79
|
blankStringLiterals: () => blankStringLiterals,
|
|
79
80
|
blockBodyLines: () => blockBodyLines,
|
|
80
81
|
breaksContract: () => breaksContract,
|
|
82
|
+
buildImportMap: () => buildImportMap,
|
|
81
83
|
checkSchema: () => checkSchema,
|
|
82
84
|
constraintsFor: () => constraintsFor,
|
|
83
85
|
enableFkPointers: () => enableFkPointers,
|
|
@@ -89,10 +91,12 @@ __export(index_exports, {
|
|
|
89
91
|
foreignKeyOf: () => foreignKeyOf,
|
|
90
92
|
hasBackRelation: () => hasBackRelation,
|
|
91
93
|
hasConstraints: () => hasConstraints,
|
|
94
|
+
isNoLongerPublic: () => isNoLongerPublic,
|
|
92
95
|
isOneToOne: () => isOneToOne,
|
|
93
96
|
kebabCase: () => kebabCase,
|
|
94
97
|
migrationCreatedBy: () => migrationCreatedBy,
|
|
95
98
|
minimumQuotasPerPlan: () => minimumQuotasPerPlan,
|
|
99
|
+
namedImports: () => namedImports,
|
|
96
100
|
parseBlockAttributes: () => parseBlockAttributes,
|
|
97
101
|
parseEnumValues: () => parseEnumValues,
|
|
98
102
|
parseFields: () => parseFields,
|
|
@@ -106,20 +110,23 @@ __export(index_exports, {
|
|
|
106
110
|
quotaKeyPattern: () => quotaKeyPattern,
|
|
107
111
|
relationNameOf: () => relationNameOf,
|
|
108
112
|
reportConstraints: () => reportConstraints,
|
|
113
|
+
rewriteImports: () => rewriteImports,
|
|
114
|
+
rewriteNames: () => rewriteNames,
|
|
115
|
+
rewriteSubpath: () => rewriteSubpath,
|
|
109
116
|
stripLineComment: () => stripLineComment,
|
|
110
117
|
structuralOnly: () => structuralOnly,
|
|
111
118
|
tablesAddressedBy: () => tablesAddressedBy
|
|
112
119
|
});
|
|
113
120
|
module.exports = __toCommonJS(index_exports);
|
|
114
121
|
|
|
115
|
-
// src/tokens.ts
|
|
116
|
-
var CLI_CONTEXT_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
117
|
-
var USER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
118
|
-
var USER_MANAGEMENT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
119
|
-
var AUDIT_QUERY_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
120
|
-
var DOCTOR_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
121
|
-
var MANIFEST_ACCESS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
122
|
-
var MANIFEST_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
122
|
+
// src/cli.tokens.ts
|
|
123
|
+
var CLI_CONTEXT_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/Config");
|
|
124
|
+
var USER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/UserPort");
|
|
125
|
+
var USER_MANAGEMENT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/UserManagementPort");
|
|
126
|
+
var AUDIT_QUERY_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/AuditQueryPort");
|
|
127
|
+
var DOCTOR_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/DoctorChecks");
|
|
128
|
+
var MANIFEST_ACCESS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/ManifestAccessPort");
|
|
129
|
+
var MANIFEST_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/ManifestChecks");
|
|
123
130
|
|
|
124
131
|
// src/cli-context.service.ts
|
|
125
132
|
var os = __toESM(require("os"), 1);
|
|
@@ -1827,6 +1834,187 @@ function patchOptionsFor(plan) {
|
|
|
1827
1834
|
}
|
|
1828
1835
|
__name(patchOptionsFor, "patchOptionsFor");
|
|
1829
1836
|
|
|
1837
|
+
// src/codemods/v1-imports.ts
|
|
1838
|
+
var PUBLIC_PREFIXES = [
|
|
1839
|
+
"ui/",
|
|
1840
|
+
"layouts/",
|
|
1841
|
+
"auth/",
|
|
1842
|
+
"pages/"
|
|
1843
|
+
];
|
|
1844
|
+
function buildImportMap(table) {
|
|
1845
|
+
const map = /* @__PURE__ */ new Map();
|
|
1846
|
+
for (const [from, to] of Object.entries(table.moves)) {
|
|
1847
|
+
const onSurface = PUBLIC_PREFIXES.some((prefix) => to.startsWith(prefix));
|
|
1848
|
+
if (!onSurface && !to.startsWith("@")) continue;
|
|
1849
|
+
if (from.startsWith("components/")) {
|
|
1850
|
+
map.set(from, to);
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
if (!from.startsWith("pages-standard/")) continue;
|
|
1854
|
+
const file = from.slice("pages-standard/".length);
|
|
1855
|
+
if (file.includes("/")) continue;
|
|
1856
|
+
map.set(from, to);
|
|
1857
|
+
map.set(`pages/${file}`, to);
|
|
1858
|
+
}
|
|
1859
|
+
for (const [from, to] of Object.entries(table.packages ?? {})) {
|
|
1860
|
+
if (from === "_") continue;
|
|
1861
|
+
map.set(from, to);
|
|
1862
|
+
}
|
|
1863
|
+
for (const [from, to] of Object.entries(table.moveDirectories ?? {})) {
|
|
1864
|
+
map.set(`${from}/`, `${to}/`);
|
|
1865
|
+
}
|
|
1866
|
+
return map;
|
|
1867
|
+
}
|
|
1868
|
+
__name(buildImportMap, "buildImportMap");
|
|
1869
|
+
function wentPrivate(map, subpath) {
|
|
1870
|
+
for (const [prefix, target] of map) {
|
|
1871
|
+
if (prefix.endsWith("/") && target.startsWith("internal/") && subpath.startsWith(prefix)) {
|
|
1872
|
+
return true;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
return false;
|
|
1876
|
+
}
|
|
1877
|
+
__name(wentPrivate, "wentPrivate");
|
|
1878
|
+
function rewriteSubpath(map, subpath) {
|
|
1879
|
+
const direct = map.get(subpath);
|
|
1880
|
+
if (direct !== void 0 && direct !== subpath) return direct;
|
|
1881
|
+
for (const [prefix, target] of map) {
|
|
1882
|
+
if (prefix.endsWith("/") && target.startsWith("@") && subpath.startsWith(prefix)) {
|
|
1883
|
+
return `${target}${subpath.slice(prefix.length)}`;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
if (subpath.startsWith("pages-standard/") && !wentPrivate(map, subpath)) {
|
|
1887
|
+
const file = subpath.slice("pages-standard/".length);
|
|
1888
|
+
if (!file.includes("/")) return `pages/${file}`;
|
|
1889
|
+
}
|
|
1890
|
+
return null;
|
|
1891
|
+
}
|
|
1892
|
+
__name(rewriteSubpath, "rewriteSubpath");
|
|
1893
|
+
function isNoLongerPublic(map, subpath) {
|
|
1894
|
+
if (subpath.startsWith("components/")) return !map.has(subpath);
|
|
1895
|
+
return subpath.startsWith("pages-standard/") && wentPrivate(map, subpath);
|
|
1896
|
+
}
|
|
1897
|
+
__name(isNoLongerPublic, "isNoLongerPublic");
|
|
1898
|
+
var UI_VUE_SPECIFIER = /@saasicat\/ui-vue\/([A-Za-z0-9/_.-]+)/g;
|
|
1899
|
+
function rewriteImports(text, map) {
|
|
1900
|
+
const unmapped = /* @__PURE__ */ new Map();
|
|
1901
|
+
let rewritten = 0;
|
|
1902
|
+
const next = text.replace(UI_VUE_SPECIFIER, (whole, subpath) => {
|
|
1903
|
+
const to = rewriteSubpath(map, subpath);
|
|
1904
|
+
if (to === null) {
|
|
1905
|
+
if (isNoLongerPublic(map, subpath)) {
|
|
1906
|
+
unmapped.set(subpath, (unmapped.get(subpath) ?? 0) + 1);
|
|
1907
|
+
}
|
|
1908
|
+
return whole;
|
|
1909
|
+
}
|
|
1910
|
+
rewritten += 1;
|
|
1911
|
+
return to.startsWith("@") ? to : `@saasicat/ui-vue/${to}`;
|
|
1912
|
+
});
|
|
1913
|
+
return {
|
|
1914
|
+
text: next,
|
|
1915
|
+
rewritten,
|
|
1916
|
+
unmapped
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
__name(rewriteImports, "rewriteImports");
|
|
1920
|
+
|
|
1921
|
+
// src/codemods/v1-rename.ts
|
|
1922
|
+
var escape = /* @__PURE__ */ __name((s) => s.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&"), "escape");
|
|
1923
|
+
var FROM_SPECIFIER = /from\s*(['"])([^'"]+)\1/g;
|
|
1924
|
+
var isSpace = /* @__PURE__ */ __name((ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r", "isSpace");
|
|
1925
|
+
function namedImports(text) {
|
|
1926
|
+
const found = [];
|
|
1927
|
+
for (const match of text.matchAll(FROM_SPECIFIER)) {
|
|
1928
|
+
let i = (match.index ?? 0) - 1;
|
|
1929
|
+
while (i >= 0 && isSpace(text[i])) i -= 1;
|
|
1930
|
+
if (text[i] !== "}") continue;
|
|
1931
|
+
const close = i;
|
|
1932
|
+
const open = text.lastIndexOf("{", close);
|
|
1933
|
+
if (open < 0) continue;
|
|
1934
|
+
i = open - 1;
|
|
1935
|
+
while (i >= 0 && isSpace(text[i])) i -= 1;
|
|
1936
|
+
let head = text.slice(Math.max(0, i - 3), i + 1);
|
|
1937
|
+
if (head === "type") {
|
|
1938
|
+
i -= 4;
|
|
1939
|
+
while (i >= 0 && isSpace(text[i])) i -= 1;
|
|
1940
|
+
head = text.slice(Math.max(0, i - 5), i + 1);
|
|
1941
|
+
} else {
|
|
1942
|
+
head = text.slice(Math.max(0, i - 5), i + 1);
|
|
1943
|
+
}
|
|
1944
|
+
if (head !== "import") continue;
|
|
1945
|
+
const names = text.slice(open + 1, close).split(",").map((raw) => {
|
|
1946
|
+
const words = raw.trim().split(/\s+/);
|
|
1947
|
+
if (words[0] === "type") words.shift();
|
|
1948
|
+
return words[0] ?? "";
|
|
1949
|
+
}).filter((name) => name.length > 0);
|
|
1950
|
+
found.push({
|
|
1951
|
+
names,
|
|
1952
|
+
specifier: match[2]
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
return found;
|
|
1956
|
+
}
|
|
1957
|
+
__name(namedImports, "namedImports");
|
|
1958
|
+
function rewriteNames(text, table) {
|
|
1959
|
+
let next = text;
|
|
1960
|
+
let rewritten = 0;
|
|
1961
|
+
const ambiguous = /* @__PURE__ */ new Set();
|
|
1962
|
+
const perEntry = /* @__PURE__ */ new Map();
|
|
1963
|
+
for (const { names, specifier } of namedImports(text)) {
|
|
1964
|
+
const mapping = table.entryTokens[specifier];
|
|
1965
|
+
for (const name of names) {
|
|
1966
|
+
const knownSomewhere = Object.values(table.entryTokens).some((m) => name in m);
|
|
1967
|
+
if (!knownSomewhere) continue;
|
|
1968
|
+
const already = perEntry.get(name);
|
|
1969
|
+
if (mapping && name in mapping && (already === void 0 || already === mapping[name])) {
|
|
1970
|
+
perEntry.set(name, mapping[name]);
|
|
1971
|
+
} else {
|
|
1972
|
+
if (already !== void 0) perEntry.delete(name);
|
|
1973
|
+
ambiguous.add(`${name} from '${specifier}'`);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
for (const [from, to] of perEntry) {
|
|
1978
|
+
next = next.replace(new RegExp(`\\b${escape(from)}\\b`, "g"), () => {
|
|
1979
|
+
rewritten += 1;
|
|
1980
|
+
return to;
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
for (const [from, to] of Object.entries(table.identifierStems)) {
|
|
1984
|
+
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
1985
|
+
rewritten += 1;
|
|
1986
|
+
return to;
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
for (const [from, to] of Object.entries(table.registryKeys)) {
|
|
1990
|
+
const symbolFor = new RegExp(`(Symbol\\.for\\(\\s*['"\`])${escape(from)}`, "g");
|
|
1991
|
+
next = next.replace(symbolFor, (_, head) => {
|
|
1992
|
+
rewritten += 1;
|
|
1993
|
+
return `${head}${to}`;
|
|
1994
|
+
});
|
|
1995
|
+
if (from.startsWith("@")) continue;
|
|
1996
|
+
const literal = new RegExp(`(['"\`])${escape(from)}`, "g");
|
|
1997
|
+
next = next.replace(literal, (_, quote) => {
|
|
1998
|
+
rewritten += 1;
|
|
1999
|
+
return `${quote}${to}`;
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
for (const [from, to] of Object.entries(table.subpaths)) {
|
|
2003
|
+
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
2004
|
+
rewritten += 1;
|
|
2005
|
+
return to;
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
return {
|
|
2009
|
+
text: next,
|
|
2010
|
+
rewritten,
|
|
2011
|
+
ambiguous: [
|
|
2012
|
+
...ambiguous
|
|
2013
|
+
].sort()
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
__name(rewriteNames, "rewriteNames");
|
|
2017
|
+
|
|
1830
2018
|
// src/init/patch-app-module.ts
|
|
1831
2019
|
var MARKER = "SaaSiCatModule.forRoot";
|
|
1832
2020
|
function patchAppModule(source, options) {
|
|
@@ -1951,7 +2139,7 @@ var LIMIT_FILTER_IMPORTS = [
|
|
|
1951
2139
|
"import { LimitExceededFilter } from '@saasicat/nest/billing';"
|
|
1952
2140
|
].join("\n");
|
|
1953
2141
|
|
|
1954
|
-
// src/module.ts
|
|
2142
|
+
// src/cli-context.module.ts
|
|
1955
2143
|
var import_common8 = require("@nestjs/common");
|
|
1956
2144
|
var import_nest5 = require("@saasicat/nest");
|
|
1957
2145
|
function _ts_decorate8(decorators, target, key, desc) {
|
|
@@ -3037,6 +3225,7 @@ UserCommands = _ts_decorate14([
|
|
|
3037
3225
|
MfaSetupFlow,
|
|
3038
3226
|
PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
3039
3227
|
PlanCatalogDoctorCheck,
|
|
3228
|
+
UI_VUE_SPECIFIER,
|
|
3040
3229
|
USER_MANAGEMENT_PORT_TOKEN,
|
|
3041
3230
|
USER_PORT_TOKEN,
|
|
3042
3231
|
UserCommands,
|
|
@@ -3051,6 +3240,7 @@ UserCommands = _ts_decorate14([
|
|
|
3051
3240
|
blankStringLiterals,
|
|
3052
3241
|
blockBodyLines,
|
|
3053
3242
|
breaksContract,
|
|
3243
|
+
buildImportMap,
|
|
3054
3244
|
checkSchema,
|
|
3055
3245
|
constraintsFor,
|
|
3056
3246
|
enableFkPointers,
|
|
@@ -3062,10 +3252,12 @@ UserCommands = _ts_decorate14([
|
|
|
3062
3252
|
foreignKeyOf,
|
|
3063
3253
|
hasBackRelation,
|
|
3064
3254
|
hasConstraints,
|
|
3255
|
+
isNoLongerPublic,
|
|
3065
3256
|
isOneToOne,
|
|
3066
3257
|
kebabCase,
|
|
3067
3258
|
migrationCreatedBy,
|
|
3068
3259
|
minimumQuotasPerPlan,
|
|
3260
|
+
namedImports,
|
|
3069
3261
|
parseBlockAttributes,
|
|
3070
3262
|
parseEnumValues,
|
|
3071
3263
|
parseFields,
|
|
@@ -3079,6 +3271,9 @@ UserCommands = _ts_decorate14([
|
|
|
3079
3271
|
quotaKeyPattern,
|
|
3080
3272
|
relationNameOf,
|
|
3081
3273
|
reportConstraints,
|
|
3274
|
+
rewriteImports,
|
|
3275
|
+
rewriteNames,
|
|
3276
|
+
rewriteSubpath,
|
|
3082
3277
|
stripLineComment,
|
|
3083
3278
|
structuralOnly,
|
|
3084
3279
|
tablesAddressedBy
|
package/dist/index.d.cts
CHANGED
|
@@ -820,6 +820,90 @@ declare function assertValidProjectKey(projectKey: string): void;
|
|
|
820
820
|
/** The same, for a quota key. `additionalProperties: false` makes it a hard rule. */
|
|
821
821
|
declare function assertValidQuotaKey(quotaKey: string): void;
|
|
822
822
|
|
|
823
|
+
/** One entry of the move table: where a file was, and where it went. */
|
|
824
|
+
interface MoveTable {
|
|
825
|
+
readonly moves: Readonly<Record<string, string>>;
|
|
826
|
+
/**
|
|
827
|
+
* Prefixes that left the package entirely. The value is a full specifier
|
|
828
|
+
* — `@saasicat/ui-vue-tenant/` — and is emitted verbatim.
|
|
829
|
+
*/
|
|
830
|
+
readonly packages?: Readonly<Record<string, string>>;
|
|
831
|
+
/**
|
|
832
|
+
* Directories whose files left the surface as a whole — the page-private
|
|
833
|
+
* parts under `pages-standard/<page>/` that became `internal/<page>/`.
|
|
834
|
+
*/
|
|
835
|
+
readonly moveDirectories?: Readonly<Record<string, string>>;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Old subpath → new subpath, derived from the move table.
|
|
839
|
+
*
|
|
840
|
+
* Both spellings of a page that moved are mapped, because both were reachable:
|
|
841
|
+
* `pages/AdminLayout.vue` and `pages-standard/AdminLayout.vue` named one file.
|
|
842
|
+
*/
|
|
843
|
+
declare function buildImportMap(table: MoveTable): Map<string, string>;
|
|
844
|
+
/**
|
|
845
|
+
* What a subpath becomes, or null when it is already right.
|
|
846
|
+
*
|
|
847
|
+
* A `pages-standard/` path with no entry in the table is a page that did not
|
|
848
|
+
* move: it keeps its name under the surviving alias.
|
|
849
|
+
*/
|
|
850
|
+
declare function rewriteSubpath(map: ReadonlyMap<string, string>, subpath: string): string | null;
|
|
851
|
+
/**
|
|
852
|
+
* Whether a subpath was public before and is not any more.
|
|
853
|
+
*
|
|
854
|
+
* Reported rather than rewritten: it moved into `features/` or `internal/`,
|
|
855
|
+
* which the 1.0 surface does not publish, so there is no destination to point
|
|
856
|
+
* at. Leaving it silently would hand the consumer a build error with no
|
|
857
|
+
* explanation of what happened.
|
|
858
|
+
*/
|
|
859
|
+
declare function isNoLongerPublic(map: ReadonlyMap<string, string>, subpath: string): boolean;
|
|
860
|
+
/** Every `@saasicat/ui-vue/<subpath>` occurrence in a source text. */
|
|
861
|
+
declare const UI_VUE_SPECIFIER: RegExp;
|
|
862
|
+
interface RewriteResult {
|
|
863
|
+
readonly text: string;
|
|
864
|
+
readonly rewritten: number;
|
|
865
|
+
/** Subpaths that lost their export, with how often each appeared. */
|
|
866
|
+
readonly unmapped: ReadonlyMap<string, number>;
|
|
867
|
+
}
|
|
868
|
+
/** Applies the map to one file's text. */
|
|
869
|
+
declare function rewriteImports(text: string, map: ReadonlyMap<string, string>): RewriteResult;
|
|
870
|
+
|
|
871
|
+
/** One entry of the rename table. */
|
|
872
|
+
interface RenameTable {
|
|
873
|
+
/** An identifier stem, matched anywhere in an identifier, and its replacement. */
|
|
874
|
+
readonly identifierStems: Readonly<Record<string, string>>;
|
|
875
|
+
/** A registry-key prefix (or a whole key) inside a string literal, and its replacement. */
|
|
876
|
+
readonly registryKeys: Readonly<Record<string, string>>;
|
|
877
|
+
/** Per import specifier: a name that means something different per entry. */
|
|
878
|
+
readonly entryTokens: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
879
|
+
/** A module specifier prefix and its replacement. */
|
|
880
|
+
readonly subpaths: Readonly<Record<string, string>>;
|
|
881
|
+
}
|
|
882
|
+
interface RenameResult {
|
|
883
|
+
readonly text: string;
|
|
884
|
+
readonly rewritten: number;
|
|
885
|
+
/**
|
|
886
|
+
* Names the table knows only per entry, imported from somewhere the table
|
|
887
|
+
* does not cover. Reported rather than guessed: which registry the
|
|
888
|
+
* consumer meant is not in the text.
|
|
889
|
+
*/
|
|
890
|
+
readonly ambiguous: readonly string[];
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* The specifier and the bound names of every `import { … } from '…'`.
|
|
894
|
+
*
|
|
895
|
+
* Read backwards from each `from`, one character at a time, instead of with
|
|
896
|
+
* one regular expression over the statement: `\{([^}]*)\}\s+from` and its
|
|
897
|
+
* siblings backtrack quadratically on a file full of `import {{`, and the
|
|
898
|
+
* file is a consumer's — whatever they wrote, this must finish.
|
|
899
|
+
*/
|
|
900
|
+
declare function namedImports(text: string): Array<{
|
|
901
|
+
names: string[];
|
|
902
|
+
specifier: string;
|
|
903
|
+
}>;
|
|
904
|
+
/** Applies the table to one file's text. Idempotent: a second run changes nothing. */
|
|
905
|
+
declare function rewriteNames(text: string, table: RenameTable): RenameResult;
|
|
906
|
+
|
|
823
907
|
interface PatchAppModuleOptions {
|
|
824
908
|
/** Import specifier for the persistence bundle, or null when not generated. */
|
|
825
909
|
persistenceImport: string | null;
|
|
@@ -1063,4 +1147,4 @@ declare class UserCommands extends CommandRunner {
|
|
|
1063
1147
|
parsePassword(val: string): string;
|
|
1064
1148
|
}
|
|
1065
1149
|
|
|
1066
|
-
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, CONSTRAINTS_MARKER, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, type ConstraintsOutcome, type ConstraintsReport, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type EnableFkResult, type FieldMismatch, type FieldMismatchReason, type FieldSignature, type FkModelNames, type FkPointer, type InitOptions, type InitPlan, LIMIT_FILTER_IMPORTS, LIMIT_FILTER_PROVIDER, MANIFEST_ACCESS_PORT_TOKEN, MANIFEST_CHECKS_TOKEN, type ManifestCheck, ManifestCheckCommand, type ManifestCheckReport, type ManifestCheckResult, ManifestCliFlow, ManifestCommands, type ManifestDiff, ManifestDumpCommand, ManifestHashCommand, ManifestValidateCommand, MfaSetupFlow, type MfaSetupOptions, type MfaSetupResult, type MissingBlockAttribute, type MissingEnumValue, type MissingField, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, type PatchAppModuleOptions, type PatchOptionsFromPlan, type PatchResult, PlanCatalogDoctorCheck, type PlannedFile, type QuotaProviderFile, type QuotaSpec, type SchemaCheckReport, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, appendConstraints, applyFragmentBlocks, applyTokens, assertModelsExist, assertValidProjectKey, assertValidQuotaKey, blankStringLiterals, blockBodyLines, breaksContract, checkSchema, constraintsFor, enableFkPointers, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, findFkPointers, foreignKeyOf, hasBackRelation, hasConstraints, isOneToOne, kebabCase, migrationCreatedBy, minimumQuotasPerPlan, parseBlockAttributes, parseEnumValues, parseFields, parseQuota, parseSchema, pascalCase, patchAppModule, patchOptionsFor, planInit, projectKeyPattern, quotaKeyPattern, relationNameOf, reportConstraints, stripLineComment, structuralOnly, tablesAddressedBy };
|
|
1150
|
+
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, CONSTRAINTS_MARKER, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, type ConstraintsOutcome, type ConstraintsReport, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type EnableFkResult, type FieldMismatch, type FieldMismatchReason, type FieldSignature, type FkModelNames, type FkPointer, type InitOptions, type InitPlan, LIMIT_FILTER_IMPORTS, LIMIT_FILTER_PROVIDER, MANIFEST_ACCESS_PORT_TOKEN, MANIFEST_CHECKS_TOKEN, type ManifestCheck, ManifestCheckCommand, type ManifestCheckReport, type ManifestCheckResult, ManifestCliFlow, ManifestCommands, type ManifestDiff, ManifestDumpCommand, ManifestHashCommand, ManifestValidateCommand, MfaSetupFlow, type MfaSetupOptions, type MfaSetupResult, type MissingBlockAttribute, type MissingEnumValue, type MissingField, type MoveTable, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, type PatchAppModuleOptions, type PatchOptionsFromPlan, type PatchResult, PlanCatalogDoctorCheck, type PlannedFile, type QuotaProviderFile, type QuotaSpec, type RenameResult, type RenameTable, type RewriteResult, type SchemaCheckReport, UI_VUE_SPECIFIER, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, appendConstraints, applyFragmentBlocks, applyTokens, assertModelsExist, assertValidProjectKey, assertValidQuotaKey, blankStringLiterals, blockBodyLines, breaksContract, buildImportMap, checkSchema, constraintsFor, enableFkPointers, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, findFkPointers, foreignKeyOf, hasBackRelation, hasConstraints, isNoLongerPublic, isOneToOne, kebabCase, migrationCreatedBy, minimumQuotasPerPlan, namedImports, parseBlockAttributes, parseEnumValues, parseFields, parseQuota, parseSchema, pascalCase, patchAppModule, patchOptionsFor, planInit, projectKeyPattern, quotaKeyPattern, relationNameOf, reportConstraints, rewriteImports, rewriteNames, rewriteSubpath, stripLineComment, structuralOnly, tablesAddressedBy };
|
package/dist/index.d.ts
CHANGED
|
@@ -820,6 +820,90 @@ declare function assertValidProjectKey(projectKey: string): void;
|
|
|
820
820
|
/** The same, for a quota key. `additionalProperties: false` makes it a hard rule. */
|
|
821
821
|
declare function assertValidQuotaKey(quotaKey: string): void;
|
|
822
822
|
|
|
823
|
+
/** One entry of the move table: where a file was, and where it went. */
|
|
824
|
+
interface MoveTable {
|
|
825
|
+
readonly moves: Readonly<Record<string, string>>;
|
|
826
|
+
/**
|
|
827
|
+
* Prefixes that left the package entirely. The value is a full specifier
|
|
828
|
+
* — `@saasicat/ui-vue-tenant/` — and is emitted verbatim.
|
|
829
|
+
*/
|
|
830
|
+
readonly packages?: Readonly<Record<string, string>>;
|
|
831
|
+
/**
|
|
832
|
+
* Directories whose files left the surface as a whole — the page-private
|
|
833
|
+
* parts under `pages-standard/<page>/` that became `internal/<page>/`.
|
|
834
|
+
*/
|
|
835
|
+
readonly moveDirectories?: Readonly<Record<string, string>>;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Old subpath → new subpath, derived from the move table.
|
|
839
|
+
*
|
|
840
|
+
* Both spellings of a page that moved are mapped, because both were reachable:
|
|
841
|
+
* `pages/AdminLayout.vue` and `pages-standard/AdminLayout.vue` named one file.
|
|
842
|
+
*/
|
|
843
|
+
declare function buildImportMap(table: MoveTable): Map<string, string>;
|
|
844
|
+
/**
|
|
845
|
+
* What a subpath becomes, or null when it is already right.
|
|
846
|
+
*
|
|
847
|
+
* A `pages-standard/` path with no entry in the table is a page that did not
|
|
848
|
+
* move: it keeps its name under the surviving alias.
|
|
849
|
+
*/
|
|
850
|
+
declare function rewriteSubpath(map: ReadonlyMap<string, string>, subpath: string): string | null;
|
|
851
|
+
/**
|
|
852
|
+
* Whether a subpath was public before and is not any more.
|
|
853
|
+
*
|
|
854
|
+
* Reported rather than rewritten: it moved into `features/` or `internal/`,
|
|
855
|
+
* which the 1.0 surface does not publish, so there is no destination to point
|
|
856
|
+
* at. Leaving it silently would hand the consumer a build error with no
|
|
857
|
+
* explanation of what happened.
|
|
858
|
+
*/
|
|
859
|
+
declare function isNoLongerPublic(map: ReadonlyMap<string, string>, subpath: string): boolean;
|
|
860
|
+
/** Every `@saasicat/ui-vue/<subpath>` occurrence in a source text. */
|
|
861
|
+
declare const UI_VUE_SPECIFIER: RegExp;
|
|
862
|
+
interface RewriteResult {
|
|
863
|
+
readonly text: string;
|
|
864
|
+
readonly rewritten: number;
|
|
865
|
+
/** Subpaths that lost their export, with how often each appeared. */
|
|
866
|
+
readonly unmapped: ReadonlyMap<string, number>;
|
|
867
|
+
}
|
|
868
|
+
/** Applies the map to one file's text. */
|
|
869
|
+
declare function rewriteImports(text: string, map: ReadonlyMap<string, string>): RewriteResult;
|
|
870
|
+
|
|
871
|
+
/** One entry of the rename table. */
|
|
872
|
+
interface RenameTable {
|
|
873
|
+
/** An identifier stem, matched anywhere in an identifier, and its replacement. */
|
|
874
|
+
readonly identifierStems: Readonly<Record<string, string>>;
|
|
875
|
+
/** A registry-key prefix (or a whole key) inside a string literal, and its replacement. */
|
|
876
|
+
readonly registryKeys: Readonly<Record<string, string>>;
|
|
877
|
+
/** Per import specifier: a name that means something different per entry. */
|
|
878
|
+
readonly entryTokens: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
879
|
+
/** A module specifier prefix and its replacement. */
|
|
880
|
+
readonly subpaths: Readonly<Record<string, string>>;
|
|
881
|
+
}
|
|
882
|
+
interface RenameResult {
|
|
883
|
+
readonly text: string;
|
|
884
|
+
readonly rewritten: number;
|
|
885
|
+
/**
|
|
886
|
+
* Names the table knows only per entry, imported from somewhere the table
|
|
887
|
+
* does not cover. Reported rather than guessed: which registry the
|
|
888
|
+
* consumer meant is not in the text.
|
|
889
|
+
*/
|
|
890
|
+
readonly ambiguous: readonly string[];
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* The specifier and the bound names of every `import { … } from '…'`.
|
|
894
|
+
*
|
|
895
|
+
* Read backwards from each `from`, one character at a time, instead of with
|
|
896
|
+
* one regular expression over the statement: `\{([^}]*)\}\s+from` and its
|
|
897
|
+
* siblings backtrack quadratically on a file full of `import {{`, and the
|
|
898
|
+
* file is a consumer's — whatever they wrote, this must finish.
|
|
899
|
+
*/
|
|
900
|
+
declare function namedImports(text: string): Array<{
|
|
901
|
+
names: string[];
|
|
902
|
+
specifier: string;
|
|
903
|
+
}>;
|
|
904
|
+
/** Applies the table to one file's text. Idempotent: a second run changes nothing. */
|
|
905
|
+
declare function rewriteNames(text: string, table: RenameTable): RenameResult;
|
|
906
|
+
|
|
823
907
|
interface PatchAppModuleOptions {
|
|
824
908
|
/** Import specifier for the persistence bundle, or null when not generated. */
|
|
825
909
|
persistenceImport: string | null;
|
|
@@ -1063,4 +1147,4 @@ declare class UserCommands extends CommandRunner {
|
|
|
1063
1147
|
parsePassword(val: string): string;
|
|
1064
1148
|
}
|
|
1065
1149
|
|
|
1066
|
-
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, CONSTRAINTS_MARKER, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, type ConstraintsOutcome, type ConstraintsReport, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type EnableFkResult, type FieldMismatch, type FieldMismatchReason, type FieldSignature, type FkModelNames, type FkPointer, type InitOptions, type InitPlan, LIMIT_FILTER_IMPORTS, LIMIT_FILTER_PROVIDER, MANIFEST_ACCESS_PORT_TOKEN, MANIFEST_CHECKS_TOKEN, type ManifestCheck, ManifestCheckCommand, type ManifestCheckReport, type ManifestCheckResult, ManifestCliFlow, ManifestCommands, type ManifestDiff, ManifestDumpCommand, ManifestHashCommand, ManifestValidateCommand, MfaSetupFlow, type MfaSetupOptions, type MfaSetupResult, type MissingBlockAttribute, type MissingEnumValue, type MissingField, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, type PatchAppModuleOptions, type PatchOptionsFromPlan, type PatchResult, PlanCatalogDoctorCheck, type PlannedFile, type QuotaProviderFile, type QuotaSpec, type SchemaCheckReport, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, appendConstraints, applyFragmentBlocks, applyTokens, assertModelsExist, assertValidProjectKey, assertValidQuotaKey, blankStringLiterals, blockBodyLines, breaksContract, checkSchema, constraintsFor, enableFkPointers, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, findFkPointers, foreignKeyOf, hasBackRelation, hasConstraints, isOneToOne, kebabCase, migrationCreatedBy, minimumQuotasPerPlan, parseBlockAttributes, parseEnumValues, parseFields, parseQuota, parseSchema, pascalCase, patchAppModule, patchOptionsFor, planInit, projectKeyPattern, quotaKeyPattern, relationNameOf, reportConstraints, stripLineComment, structuralOnly, tablesAddressedBy };
|
|
1150
|
+
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, CONSTRAINTS_MARKER, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, type ConstraintsOutcome, type ConstraintsReport, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type EnableFkResult, type FieldMismatch, type FieldMismatchReason, type FieldSignature, type FkModelNames, type FkPointer, type InitOptions, type InitPlan, LIMIT_FILTER_IMPORTS, LIMIT_FILTER_PROVIDER, MANIFEST_ACCESS_PORT_TOKEN, MANIFEST_CHECKS_TOKEN, type ManifestCheck, ManifestCheckCommand, type ManifestCheckReport, type ManifestCheckResult, ManifestCliFlow, ManifestCommands, type ManifestDiff, ManifestDumpCommand, ManifestHashCommand, ManifestValidateCommand, MfaSetupFlow, type MfaSetupOptions, type MfaSetupResult, type MissingBlockAttribute, type MissingEnumValue, type MissingField, type MoveTable, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, type PatchAppModuleOptions, type PatchOptionsFromPlan, type PatchResult, PlanCatalogDoctorCheck, type PlannedFile, type QuotaProviderFile, type QuotaSpec, type RenameResult, type RenameTable, type RewriteResult, type SchemaCheckReport, UI_VUE_SPECIFIER, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, appendConstraints, applyFragmentBlocks, applyTokens, assertModelsExist, assertValidProjectKey, assertValidQuotaKey, blankStringLiterals, blockBodyLines, breaksContract, buildImportMap, checkSchema, constraintsFor, enableFkPointers, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, findFkPointers, foreignKeyOf, hasBackRelation, hasConstraints, isNoLongerPublic, isOneToOne, kebabCase, migrationCreatedBy, minimumQuotasPerPlan, namedImports, parseBlockAttributes, parseEnumValues, parseFields, parseQuota, parseSchema, pascalCase, patchAppModule, patchOptionsFor, planInit, projectKeyPattern, quotaKeyPattern, relationNameOf, reportConstraints, rewriteImports, rewriteNames, rewriteSubpath, stripLineComment, structuralOnly, tablesAddressedBy };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
-
// src/tokens.ts
|
|
5
|
-
var CLI_CONTEXT_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
6
|
-
var USER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
7
|
-
var USER_MANAGEMENT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
8
|
-
var AUDIT_QUERY_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
9
|
-
var DOCTOR_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
10
|
-
var MANIFEST_ACCESS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
11
|
-
var MANIFEST_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("
|
|
4
|
+
// src/cli.tokens.ts
|
|
5
|
+
var CLI_CONTEXT_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/Config");
|
|
6
|
+
var USER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/UserPort");
|
|
7
|
+
var USER_MANAGEMENT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/UserManagementPort");
|
|
8
|
+
var AUDIT_QUERY_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/AuditQueryPort");
|
|
9
|
+
var DOCTOR_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/DoctorChecks");
|
|
10
|
+
var MANIFEST_ACCESS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/ManifestAccessPort");
|
|
11
|
+
var MANIFEST_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/cli/ManifestChecks");
|
|
12
12
|
|
|
13
13
|
// src/cli-context.service.ts
|
|
14
14
|
import * as os from "os";
|
|
@@ -1716,6 +1716,187 @@ function patchOptionsFor(plan) {
|
|
|
1716
1716
|
}
|
|
1717
1717
|
__name(patchOptionsFor, "patchOptionsFor");
|
|
1718
1718
|
|
|
1719
|
+
// src/codemods/v1-imports.ts
|
|
1720
|
+
var PUBLIC_PREFIXES = [
|
|
1721
|
+
"ui/",
|
|
1722
|
+
"layouts/",
|
|
1723
|
+
"auth/",
|
|
1724
|
+
"pages/"
|
|
1725
|
+
];
|
|
1726
|
+
function buildImportMap(table) {
|
|
1727
|
+
const map = /* @__PURE__ */ new Map();
|
|
1728
|
+
for (const [from, to] of Object.entries(table.moves)) {
|
|
1729
|
+
const onSurface = PUBLIC_PREFIXES.some((prefix) => to.startsWith(prefix));
|
|
1730
|
+
if (!onSurface && !to.startsWith("@")) continue;
|
|
1731
|
+
if (from.startsWith("components/")) {
|
|
1732
|
+
map.set(from, to);
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
if (!from.startsWith("pages-standard/")) continue;
|
|
1736
|
+
const file = from.slice("pages-standard/".length);
|
|
1737
|
+
if (file.includes("/")) continue;
|
|
1738
|
+
map.set(from, to);
|
|
1739
|
+
map.set(`pages/${file}`, to);
|
|
1740
|
+
}
|
|
1741
|
+
for (const [from, to] of Object.entries(table.packages ?? {})) {
|
|
1742
|
+
if (from === "_") continue;
|
|
1743
|
+
map.set(from, to);
|
|
1744
|
+
}
|
|
1745
|
+
for (const [from, to] of Object.entries(table.moveDirectories ?? {})) {
|
|
1746
|
+
map.set(`${from}/`, `${to}/`);
|
|
1747
|
+
}
|
|
1748
|
+
return map;
|
|
1749
|
+
}
|
|
1750
|
+
__name(buildImportMap, "buildImportMap");
|
|
1751
|
+
function wentPrivate(map, subpath) {
|
|
1752
|
+
for (const [prefix, target] of map) {
|
|
1753
|
+
if (prefix.endsWith("/") && target.startsWith("internal/") && subpath.startsWith(prefix)) {
|
|
1754
|
+
return true;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
return false;
|
|
1758
|
+
}
|
|
1759
|
+
__name(wentPrivate, "wentPrivate");
|
|
1760
|
+
function rewriteSubpath(map, subpath) {
|
|
1761
|
+
const direct = map.get(subpath);
|
|
1762
|
+
if (direct !== void 0 && direct !== subpath) return direct;
|
|
1763
|
+
for (const [prefix, target] of map) {
|
|
1764
|
+
if (prefix.endsWith("/") && target.startsWith("@") && subpath.startsWith(prefix)) {
|
|
1765
|
+
return `${target}${subpath.slice(prefix.length)}`;
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
if (subpath.startsWith("pages-standard/") && !wentPrivate(map, subpath)) {
|
|
1769
|
+
const file = subpath.slice("pages-standard/".length);
|
|
1770
|
+
if (!file.includes("/")) return `pages/${file}`;
|
|
1771
|
+
}
|
|
1772
|
+
return null;
|
|
1773
|
+
}
|
|
1774
|
+
__name(rewriteSubpath, "rewriteSubpath");
|
|
1775
|
+
function isNoLongerPublic(map, subpath) {
|
|
1776
|
+
if (subpath.startsWith("components/")) return !map.has(subpath);
|
|
1777
|
+
return subpath.startsWith("pages-standard/") && wentPrivate(map, subpath);
|
|
1778
|
+
}
|
|
1779
|
+
__name(isNoLongerPublic, "isNoLongerPublic");
|
|
1780
|
+
var UI_VUE_SPECIFIER = /@saasicat\/ui-vue\/([A-Za-z0-9/_.-]+)/g;
|
|
1781
|
+
function rewriteImports(text, map) {
|
|
1782
|
+
const unmapped = /* @__PURE__ */ new Map();
|
|
1783
|
+
let rewritten = 0;
|
|
1784
|
+
const next = text.replace(UI_VUE_SPECIFIER, (whole, subpath) => {
|
|
1785
|
+
const to = rewriteSubpath(map, subpath);
|
|
1786
|
+
if (to === null) {
|
|
1787
|
+
if (isNoLongerPublic(map, subpath)) {
|
|
1788
|
+
unmapped.set(subpath, (unmapped.get(subpath) ?? 0) + 1);
|
|
1789
|
+
}
|
|
1790
|
+
return whole;
|
|
1791
|
+
}
|
|
1792
|
+
rewritten += 1;
|
|
1793
|
+
return to.startsWith("@") ? to : `@saasicat/ui-vue/${to}`;
|
|
1794
|
+
});
|
|
1795
|
+
return {
|
|
1796
|
+
text: next,
|
|
1797
|
+
rewritten,
|
|
1798
|
+
unmapped
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
__name(rewriteImports, "rewriteImports");
|
|
1802
|
+
|
|
1803
|
+
// src/codemods/v1-rename.ts
|
|
1804
|
+
var escape = /* @__PURE__ */ __name((s) => s.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&"), "escape");
|
|
1805
|
+
var FROM_SPECIFIER = /from\s*(['"])([^'"]+)\1/g;
|
|
1806
|
+
var isSpace = /* @__PURE__ */ __name((ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r", "isSpace");
|
|
1807
|
+
function namedImports(text) {
|
|
1808
|
+
const found = [];
|
|
1809
|
+
for (const match of text.matchAll(FROM_SPECIFIER)) {
|
|
1810
|
+
let i = (match.index ?? 0) - 1;
|
|
1811
|
+
while (i >= 0 && isSpace(text[i])) i -= 1;
|
|
1812
|
+
if (text[i] !== "}") continue;
|
|
1813
|
+
const close = i;
|
|
1814
|
+
const open = text.lastIndexOf("{", close);
|
|
1815
|
+
if (open < 0) continue;
|
|
1816
|
+
i = open - 1;
|
|
1817
|
+
while (i >= 0 && isSpace(text[i])) i -= 1;
|
|
1818
|
+
let head = text.slice(Math.max(0, i - 3), i + 1);
|
|
1819
|
+
if (head === "type") {
|
|
1820
|
+
i -= 4;
|
|
1821
|
+
while (i >= 0 && isSpace(text[i])) i -= 1;
|
|
1822
|
+
head = text.slice(Math.max(0, i - 5), i + 1);
|
|
1823
|
+
} else {
|
|
1824
|
+
head = text.slice(Math.max(0, i - 5), i + 1);
|
|
1825
|
+
}
|
|
1826
|
+
if (head !== "import") continue;
|
|
1827
|
+
const names = text.slice(open + 1, close).split(",").map((raw) => {
|
|
1828
|
+
const words = raw.trim().split(/\s+/);
|
|
1829
|
+
if (words[0] === "type") words.shift();
|
|
1830
|
+
return words[0] ?? "";
|
|
1831
|
+
}).filter((name) => name.length > 0);
|
|
1832
|
+
found.push({
|
|
1833
|
+
names,
|
|
1834
|
+
specifier: match[2]
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
return found;
|
|
1838
|
+
}
|
|
1839
|
+
__name(namedImports, "namedImports");
|
|
1840
|
+
function rewriteNames(text, table) {
|
|
1841
|
+
let next = text;
|
|
1842
|
+
let rewritten = 0;
|
|
1843
|
+
const ambiguous = /* @__PURE__ */ new Set();
|
|
1844
|
+
const perEntry = /* @__PURE__ */ new Map();
|
|
1845
|
+
for (const { names, specifier } of namedImports(text)) {
|
|
1846
|
+
const mapping = table.entryTokens[specifier];
|
|
1847
|
+
for (const name of names) {
|
|
1848
|
+
const knownSomewhere = Object.values(table.entryTokens).some((m) => name in m);
|
|
1849
|
+
if (!knownSomewhere) continue;
|
|
1850
|
+
const already = perEntry.get(name);
|
|
1851
|
+
if (mapping && name in mapping && (already === void 0 || already === mapping[name])) {
|
|
1852
|
+
perEntry.set(name, mapping[name]);
|
|
1853
|
+
} else {
|
|
1854
|
+
if (already !== void 0) perEntry.delete(name);
|
|
1855
|
+
ambiguous.add(`${name} from '${specifier}'`);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
for (const [from, to] of perEntry) {
|
|
1860
|
+
next = next.replace(new RegExp(`\\b${escape(from)}\\b`, "g"), () => {
|
|
1861
|
+
rewritten += 1;
|
|
1862
|
+
return to;
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
for (const [from, to] of Object.entries(table.identifierStems)) {
|
|
1866
|
+
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
1867
|
+
rewritten += 1;
|
|
1868
|
+
return to;
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
for (const [from, to] of Object.entries(table.registryKeys)) {
|
|
1872
|
+
const symbolFor = new RegExp(`(Symbol\\.for\\(\\s*['"\`])${escape(from)}`, "g");
|
|
1873
|
+
next = next.replace(symbolFor, (_, head) => {
|
|
1874
|
+
rewritten += 1;
|
|
1875
|
+
return `${head}${to}`;
|
|
1876
|
+
});
|
|
1877
|
+
if (from.startsWith("@")) continue;
|
|
1878
|
+
const literal = new RegExp(`(['"\`])${escape(from)}`, "g");
|
|
1879
|
+
next = next.replace(literal, (_, quote) => {
|
|
1880
|
+
rewritten += 1;
|
|
1881
|
+
return `${quote}${to}`;
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
for (const [from, to] of Object.entries(table.subpaths)) {
|
|
1885
|
+
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
1886
|
+
rewritten += 1;
|
|
1887
|
+
return to;
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
return {
|
|
1891
|
+
text: next,
|
|
1892
|
+
rewritten,
|
|
1893
|
+
ambiguous: [
|
|
1894
|
+
...ambiguous
|
|
1895
|
+
].sort()
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
__name(rewriteNames, "rewriteNames");
|
|
1899
|
+
|
|
1719
1900
|
// src/init/patch-app-module.ts
|
|
1720
1901
|
var MARKER = "SaaSiCatModule.forRoot";
|
|
1721
1902
|
function patchAppModule(source, options) {
|
|
@@ -1840,7 +2021,7 @@ var LIMIT_FILTER_IMPORTS = [
|
|
|
1840
2021
|
"import { LimitExceededFilter } from '@saasicat/nest/billing';"
|
|
1841
2022
|
].join("\n");
|
|
1842
2023
|
|
|
1843
|
-
// src/module.ts
|
|
2024
|
+
// src/cli-context.module.ts
|
|
1844
2025
|
import { Module } from "@nestjs/common";
|
|
1845
2026
|
import { asProvider } from "@saasicat/nest";
|
|
1846
2027
|
function _ts_decorate8(decorators, target, key, desc) {
|
|
@@ -2925,6 +3106,7 @@ export {
|
|
|
2925
3106
|
MfaSetupFlow,
|
|
2926
3107
|
PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
2927
3108
|
PlanCatalogDoctorCheck,
|
|
3109
|
+
UI_VUE_SPECIFIER,
|
|
2928
3110
|
USER_MANAGEMENT_PORT_TOKEN,
|
|
2929
3111
|
USER_PORT_TOKEN,
|
|
2930
3112
|
UserCommands,
|
|
@@ -2939,6 +3121,7 @@ export {
|
|
|
2939
3121
|
blankStringLiterals,
|
|
2940
3122
|
blockBodyLines,
|
|
2941
3123
|
breaksContract,
|
|
3124
|
+
buildImportMap,
|
|
2942
3125
|
checkSchema,
|
|
2943
3126
|
constraintsFor,
|
|
2944
3127
|
enableFkPointers,
|
|
@@ -2950,10 +3133,12 @@ export {
|
|
|
2950
3133
|
foreignKeyOf,
|
|
2951
3134
|
hasBackRelation,
|
|
2952
3135
|
hasConstraints,
|
|
3136
|
+
isNoLongerPublic,
|
|
2953
3137
|
isOneToOne,
|
|
2954
3138
|
kebabCase,
|
|
2955
3139
|
migrationCreatedBy,
|
|
2956
3140
|
minimumQuotasPerPlan,
|
|
3141
|
+
namedImports,
|
|
2957
3142
|
parseBlockAttributes,
|
|
2958
3143
|
parseEnumValues,
|
|
2959
3144
|
parseFields,
|
|
@@ -2967,6 +3152,9 @@ export {
|
|
|
2967
3152
|
quotaKeyPattern,
|
|
2968
3153
|
relationNameOf,
|
|
2969
3154
|
reportConstraints,
|
|
3155
|
+
rewriteImports,
|
|
3156
|
+
rewriteNames,
|
|
3157
|
+
rewriteSubpath,
|
|
2970
3158
|
stripLineComment,
|
|
2971
3159
|
structuralOnly,
|
|
2972
3160
|
tablesAddressedBy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-rc.0",
|
|
4
4
|
"description": "CLI helpers for SaaS platform consumers. Provides CliContextService (identity, MFA, production confirm, audit tag).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -21,16 +21,17 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"bin",
|
|
24
|
-
"templates"
|
|
24
|
+
"templates",
|
|
25
|
+
"codemods"
|
|
25
26
|
],
|
|
26
27
|
"bin": {
|
|
27
28
|
"saasicat": "./bin/saasicat.js"
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"qrcode-terminal": "^0.12.0",
|
|
31
|
-
"@saasicat/nest": "^0.
|
|
32
|
-
"@saasicat/spec": "^0.
|
|
33
|
-
"@saasicat/types": "^0.
|
|
32
|
+
"@saasicat/nest": "^1.0.0-rc.0",
|
|
33
|
+
"@saasicat/spec": "^1.0.0-rc.0",
|
|
34
|
+
"@saasicat/types": "^1.0.0-rc.0"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
37
|
"@nestjs/common": "^11.0.0",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"repository": {
|
|
50
51
|
"type": "git",
|
|
51
52
|
"url": "https://github.com/uelker70/saasicat.git",
|
|
52
|
-
"directory": "packages/
|
|
53
|
+
"directory": "packages/cli"
|
|
53
54
|
},
|
|
54
55
|
"bugs": {
|
|
55
56
|
"url": "https://github.com/uelker70/saasicat/issues"
|
|
@@ -60,6 +61,6 @@
|
|
|
60
61
|
"scripts": {
|
|
61
62
|
"build": "node ../../scripts/build-and-prune.mjs \"tsup src/index.ts --format esm,cjs --dts --external @saasicat/types --external @saasicat/nest --external @nestjs/common --external nest-commander --external qrcode-terminal\"",
|
|
62
63
|
"pretest": "pnpm run build",
|
|
63
|
-
"test": "node --test tests
|
|
64
|
+
"test": "node --test 'tests/*.test.js'"
|
|
64
65
|
}
|
|
65
66
|
}
|