@saasicat/cli 1.0.0-rc.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/bin/saasicat.js +41 -3
- package/codemods/v1-rename.map.json +6 -0
- package/dist/.build-stamp +1 -1
- package/dist/index.cjs +84 -0
- package/dist/index.d.cts +29 -2
- package/dist/index.d.ts +29 -2
- package/dist/index.js +83 -0
- package/package.json +5 -5
- package/templates/init/src/auth/password.hasher.ts.tpl +1 -1
- package/templates/init/src/saas/admin-manifest.contribution.ts.tpl +1 -1
- package/templates/init/src/saas/feature-ui-registry.ts.tpl +1 -1
- package/templates/init/src/saas/quota.provider.ts.tpl +1 -1
package/bin/saasicat.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
|
|
29
29
|
import { existsSync } from 'node:fs';
|
|
30
|
-
import { dirname, join, resolve } from 'node:path';
|
|
30
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
31
31
|
import { createRequire } from 'node:module';
|
|
32
32
|
import { fileURLToPath } from 'node:url';
|
|
33
33
|
import { spawn } from 'node:child_process';
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
buildImportMap,
|
|
51
51
|
migrationCreatedBy,
|
|
52
52
|
rewriteImports,
|
|
53
|
+
rewriteManifest,
|
|
53
54
|
rewriteNames,
|
|
54
55
|
reportConstraints,
|
|
55
56
|
patchAppModule,
|
|
@@ -548,6 +549,7 @@ async function cmdCodemodV1Imports(args) {
|
|
|
548
549
|
let rewritten = 0;
|
|
549
550
|
let touched = 0;
|
|
550
551
|
await walkSources(root, async (full, source) => {
|
|
552
|
+
if (basename(full) === CODEMOD_MANIFEST) return;
|
|
551
553
|
const result = rewriteImports(source, map);
|
|
552
554
|
for (const [subpath, n] of result.unmapped) {
|
|
553
555
|
unmapped.set(subpath, (unmapped.get(subpath) ?? 0) + n);
|
|
@@ -592,8 +594,15 @@ async function cmdCodemodV1Rename(args) {
|
|
|
592
594
|
const ambiguous = new Map();
|
|
593
595
|
let rewritten = 0;
|
|
594
596
|
let touched = 0;
|
|
597
|
+
let manifestsTouched = 0;
|
|
595
598
|
await walkSources(root, async (full, source) => {
|
|
596
|
-
|
|
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);
|
|
597
606
|
for (const name of result.ambiguous) {
|
|
598
607
|
ambiguous.set(name, (ambiguous.get(name) ?? 0) + 1);
|
|
599
608
|
}
|
|
@@ -601,11 +610,26 @@ async function cmdCodemodV1Rename(args) {
|
|
|
601
610
|
if (!dryRun) await writeFile(full, result.text);
|
|
602
611
|
rewritten += result.rewritten;
|
|
603
612
|
touched += 1;
|
|
613
|
+
if (basename(full) === CODEMOD_MANIFEST) manifestsTouched += 1;
|
|
604
614
|
});
|
|
605
615
|
|
|
606
616
|
console.log(
|
|
607
617
|
`${dryRun ? 'Would rewrite' : 'Rewrote'} ${rewritten} name(s) in ${touched} file(s).`,
|
|
608
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
|
+
}
|
|
609
633
|
if (ambiguous.size === 0) return;
|
|
610
634
|
|
|
611
635
|
console.log('');
|
|
@@ -617,8 +641,20 @@ async function cmdCodemodV1Rename(args) {
|
|
|
617
641
|
console.log(' FEATURE_UI_REGISTRY_TOKEN meant one registry in `@saasicat/nest/billing`');
|
|
618
642
|
console.log(' and another in `@saasicat/nest/catalog`. Import it from the entry you');
|
|
619
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.');
|
|
620
646
|
}
|
|
621
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
|
+
|
|
622
658
|
/** Where a shipped codemod table lives, resolved through the package itself. */
|
|
623
659
|
function codemodTable(name) {
|
|
624
660
|
return join(dirname(require_.resolve('@saasicat/cli')), '..', 'codemods', name);
|
|
@@ -630,6 +666,8 @@ function codemodTable(name) {
|
|
|
630
666
|
const CODEMOD_SKIP = new Set(['node_modules', '.git', '.output', 'coverage']);
|
|
631
667
|
const isBuildOutput = (name) => name === 'dist' || name.startsWith('dist-');
|
|
632
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';
|
|
633
671
|
|
|
634
672
|
/** Every source file under `root` a codemod may touch, with its text. */
|
|
635
673
|
async function walkSources(root, visit) {
|
|
@@ -641,7 +679,7 @@ async function walkSources(root, visit) {
|
|
|
641
679
|
await walk(full);
|
|
642
680
|
continue;
|
|
643
681
|
}
|
|
644
|
-
if (!CODEMOD_EXTENSIONS.test(entry.name)) continue;
|
|
682
|
+
if (!CODEMOD_EXTENSIONS.test(entry.name) && entry.name !== CODEMOD_MANIFEST) continue;
|
|
645
683
|
await visit(full, await readFile(full, 'utf8'));
|
|
646
684
|
}
|
|
647
685
|
};
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"consumer that spelled a key themselves gets the same symbol the platform",
|
|
16
16
|
"now registers; without this their injection would resolve to nothing.",
|
|
17
17
|
"",
|
|
18
|
+
"`packages` renames a whole package — in specifiers and in package.json.",
|
|
19
|
+
"",
|
|
18
20
|
"`entryTokens` is keyed by the import specifier, because the old name",
|
|
19
21
|
"meant different registries in different entries. An import of it from",
|
|
20
22
|
"anywhere else is reported, not guessed."
|
|
@@ -41,5 +43,9 @@
|
|
|
41
43
|
},
|
|
42
44
|
"subpaths": {
|
|
43
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"
|
|
44
50
|
}
|
|
45
51
|
}
|
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
97f66acb8ca26c7c9470c1991febbb2f68518f1b3cd364af6d3566249550482a
|
package/dist/index.cjs
CHANGED
|
@@ -111,6 +111,7 @@ __export(index_exports, {
|
|
|
111
111
|
relationNameOf: () => relationNameOf,
|
|
112
112
|
reportConstraints: () => reportConstraints,
|
|
113
113
|
rewriteImports: () => rewriteImports,
|
|
114
|
+
rewriteManifest: () => rewriteManifest,
|
|
114
115
|
rewriteNames: () => rewriteNames,
|
|
115
116
|
rewriteSubpath: () => rewriteSubpath,
|
|
116
117
|
stripLineComment: () => stripLineComment,
|
|
@@ -1999,6 +2000,13 @@ function rewriteNames(text, table) {
|
|
|
1999
2000
|
return `${quote}${to}`;
|
|
2000
2001
|
});
|
|
2001
2002
|
}
|
|
2003
|
+
for (const [from, to] of Object.entries(table.packages ?? {})) {
|
|
2004
|
+
if (from === "_") continue;
|
|
2005
|
+
next = next.replace(new RegExp(`${escape(from)}(?=['"\`/])`, "g"), () => {
|
|
2006
|
+
rewritten += 1;
|
|
2007
|
+
return to;
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2002
2010
|
for (const [from, to] of Object.entries(table.subpaths)) {
|
|
2003
2011
|
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
2004
2012
|
rewritten += 1;
|
|
@@ -2014,6 +2022,81 @@ function rewriteNames(text, table) {
|
|
|
2014
2022
|
};
|
|
2015
2023
|
}
|
|
2016
2024
|
__name(rewriteNames, "rewriteNames");
|
|
2025
|
+
var DEPENDENCY_FIELDS = [
|
|
2026
|
+
"dependencies",
|
|
2027
|
+
"devDependencies",
|
|
2028
|
+
"peerDependencies",
|
|
2029
|
+
"optionalDependencies"
|
|
2030
|
+
];
|
|
2031
|
+
var UNTRANSLATABLE_RANGE = /^(workspace:|file:|link:|npm:|git\+|https?:)/;
|
|
2032
|
+
function rewriteManifest(text, table, options) {
|
|
2033
|
+
const renames = Object.entries(table.packages ?? {}).filter(([from]) => from !== "_");
|
|
2034
|
+
const ambiguous = [];
|
|
2035
|
+
let manifest;
|
|
2036
|
+
try {
|
|
2037
|
+
manifest = JSON.parse(text);
|
|
2038
|
+
} catch {
|
|
2039
|
+
return {
|
|
2040
|
+
text,
|
|
2041
|
+
rewritten: 0,
|
|
2042
|
+
ambiguous
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
let rewritten = 0;
|
|
2046
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
2047
|
+
const deps = manifest[field];
|
|
2048
|
+
if (!deps || typeof deps !== "object") continue;
|
|
2049
|
+
const entries = Object.entries(deps);
|
|
2050
|
+
const renamed = entries.map(([name, range]) => {
|
|
2051
|
+
const to = renames.find(([from]) => from === name)?.[1];
|
|
2052
|
+
if (!to) return [
|
|
2053
|
+
name,
|
|
2054
|
+
range
|
|
2055
|
+
];
|
|
2056
|
+
if (UNTRANSLATABLE_RANGE.test(range)) {
|
|
2057
|
+
ambiguous.push(`${name} in ${field} (${range})`);
|
|
2058
|
+
return [
|
|
2059
|
+
name,
|
|
2060
|
+
range
|
|
2061
|
+
];
|
|
2062
|
+
}
|
|
2063
|
+
rewritten += 1;
|
|
2064
|
+
return [
|
|
2065
|
+
to,
|
|
2066
|
+
options.targetRange
|
|
2067
|
+
];
|
|
2068
|
+
});
|
|
2069
|
+
manifest[field] = Object.fromEntries(renamed);
|
|
2070
|
+
}
|
|
2071
|
+
const meta = manifest.peerDependenciesMeta;
|
|
2072
|
+
if (meta && typeof meta === "object") {
|
|
2073
|
+
manifest.peerDependenciesMeta = Object.fromEntries(Object.entries(meta).map(([name, flags]) => {
|
|
2074
|
+
const to = renames.find(([from]) => from === name)?.[1];
|
|
2075
|
+
if (!to) return [
|
|
2076
|
+
name,
|
|
2077
|
+
flags
|
|
2078
|
+
];
|
|
2079
|
+
rewritten += 1;
|
|
2080
|
+
return [
|
|
2081
|
+
to,
|
|
2082
|
+
flags
|
|
2083
|
+
];
|
|
2084
|
+
}));
|
|
2085
|
+
}
|
|
2086
|
+
if (rewritten === 0) return {
|
|
2087
|
+
text,
|
|
2088
|
+
rewritten: 0,
|
|
2089
|
+
ambiguous
|
|
2090
|
+
};
|
|
2091
|
+
const indent = /^[ \t]+/m.exec(text)?.[0] ?? " ";
|
|
2092
|
+
const trailing = text.endsWith("\n") ? "\n" : "";
|
|
2093
|
+
return {
|
|
2094
|
+
text: JSON.stringify(manifest, null, indent) + trailing,
|
|
2095
|
+
rewritten,
|
|
2096
|
+
ambiguous
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
__name(rewriteManifest, "rewriteManifest");
|
|
2017
2100
|
|
|
2018
2101
|
// src/init/patch-app-module.ts
|
|
2019
2102
|
var MARKER = "SaaSiCatModule.forRoot";
|
|
@@ -3272,6 +3355,7 @@ UserCommands = _ts_decorate14([
|
|
|
3272
3355
|
relationNameOf,
|
|
3273
3356
|
reportConstraints,
|
|
3274
3357
|
rewriteImports,
|
|
3358
|
+
rewriteManifest,
|
|
3275
3359
|
rewriteNames,
|
|
3276
3360
|
rewriteSubpath,
|
|
3277
3361
|
stripLineComment,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MfaService, AdminAuditService, AdminManifestService, DiscoverySnapshot, ProviderSpec, DiscoveryScanner } from '@saasicat/nest';
|
|
2
|
-
import { UserPort, PlatformUserDto, AuditQueryPort, AuditEntry, AdminManifest, ManifestAccessPort, PlanCatalog, UserManagementPort } from '@saasicat/
|
|
2
|
+
import { UserPort, PlatformUserDto, AuditQueryPort, AuditEntry, AdminManifest, ManifestAccessPort, PlanCatalog, UserManagementPort } from '@saasicat/core';
|
|
3
3
|
import { Type, DynamicModule } from '@nestjs/common';
|
|
4
4
|
import { CommandRunner } from 'nest-commander';
|
|
5
5
|
|
|
@@ -878,6 +878,13 @@ interface RenameTable {
|
|
|
878
878
|
readonly entryTokens: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
879
879
|
/** A module specifier prefix and its replacement. */
|
|
880
880
|
readonly subpaths: Readonly<Record<string, string>>;
|
|
881
|
+
/**
|
|
882
|
+
* A package that was renamed. Rewritten in specifiers by `rewriteNames`
|
|
883
|
+
* and in `package.json` dependency fields by `rewriteManifest` — both,
|
|
884
|
+
* because an import a manifest does not declare fails to resolve under
|
|
885
|
+
* pnpm's isolated `node_modules`.
|
|
886
|
+
*/
|
|
887
|
+
readonly packages?: Readonly<Record<string, string>>;
|
|
881
888
|
}
|
|
882
889
|
interface RenameResult {
|
|
883
890
|
readonly text: string;
|
|
@@ -903,6 +910,26 @@ declare function namedImports(text: string): Array<{
|
|
|
903
910
|
}>;
|
|
904
911
|
/** Applies the table to one file's text. Idempotent: a second run changes nothing. */
|
|
905
912
|
declare function rewriteNames(text: string, table: RenameTable): RenameResult;
|
|
913
|
+
/**
|
|
914
|
+
* Applies the package renames to a `package.json` text.
|
|
915
|
+
*
|
|
916
|
+
* Parsed and re-serialised rather than string-replaced, so a rename lands in a
|
|
917
|
+
* dependency field and nowhere else — not in `name`, not in a description.
|
|
918
|
+
* The file's indentation is kept; a consumer's formatter must not see a diff
|
|
919
|
+
* it did not cause. Returns the text unchanged when nothing applied.
|
|
920
|
+
*/
|
|
921
|
+
interface ManifestRewriteOptions {
|
|
922
|
+
/**
|
|
923
|
+
* The range the renamed dependency gets — `^<the version this CLI was
|
|
924
|
+
* released as>`. The old range cannot be carried over: a 0.x consumer
|
|
925
|
+
* declares `"@saasicat/types": "^0.27.0"`, and `@saasicat/core` has no
|
|
926
|
+
* 0.27 — the rename starts on the 1.0 line. The caller passes the CLI's
|
|
927
|
+
* own version because that IS the line the consumer is migrating to;
|
|
928
|
+
* the codemod ships with it.
|
|
929
|
+
*/
|
|
930
|
+
readonly targetRange: string;
|
|
931
|
+
}
|
|
932
|
+
declare function rewriteManifest(text: string, table: RenameTable, options: ManifestRewriteOptions): RenameResult;
|
|
906
933
|
|
|
907
934
|
interface PatchAppModuleOptions {
|
|
908
935
|
/** Import specifier for the persistence bundle, or null when not generated. */
|
|
@@ -1147,4 +1174,4 @@ declare class UserCommands extends CommandRunner {
|
|
|
1147
1174
|
parsePassword(val: string): string;
|
|
1148
1175
|
}
|
|
1149
1176
|
|
|
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 };
|
|
1177
|
+
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, type ManifestRewriteOptions, 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, rewriteManifest, rewriteNames, rewriteSubpath, stripLineComment, structuralOnly, tablesAddressedBy };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MfaService, AdminAuditService, AdminManifestService, DiscoverySnapshot, ProviderSpec, DiscoveryScanner } from '@saasicat/nest';
|
|
2
|
-
import { UserPort, PlatformUserDto, AuditQueryPort, AuditEntry, AdminManifest, ManifestAccessPort, PlanCatalog, UserManagementPort } from '@saasicat/
|
|
2
|
+
import { UserPort, PlatformUserDto, AuditQueryPort, AuditEntry, AdminManifest, ManifestAccessPort, PlanCatalog, UserManagementPort } from '@saasicat/core';
|
|
3
3
|
import { Type, DynamicModule } from '@nestjs/common';
|
|
4
4
|
import { CommandRunner } from 'nest-commander';
|
|
5
5
|
|
|
@@ -878,6 +878,13 @@ interface RenameTable {
|
|
|
878
878
|
readonly entryTokens: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
879
879
|
/** A module specifier prefix and its replacement. */
|
|
880
880
|
readonly subpaths: Readonly<Record<string, string>>;
|
|
881
|
+
/**
|
|
882
|
+
* A package that was renamed. Rewritten in specifiers by `rewriteNames`
|
|
883
|
+
* and in `package.json` dependency fields by `rewriteManifest` — both,
|
|
884
|
+
* because an import a manifest does not declare fails to resolve under
|
|
885
|
+
* pnpm's isolated `node_modules`.
|
|
886
|
+
*/
|
|
887
|
+
readonly packages?: Readonly<Record<string, string>>;
|
|
881
888
|
}
|
|
882
889
|
interface RenameResult {
|
|
883
890
|
readonly text: string;
|
|
@@ -903,6 +910,26 @@ declare function namedImports(text: string): Array<{
|
|
|
903
910
|
}>;
|
|
904
911
|
/** Applies the table to one file's text. Idempotent: a second run changes nothing. */
|
|
905
912
|
declare function rewriteNames(text: string, table: RenameTable): RenameResult;
|
|
913
|
+
/**
|
|
914
|
+
* Applies the package renames to a `package.json` text.
|
|
915
|
+
*
|
|
916
|
+
* Parsed and re-serialised rather than string-replaced, so a rename lands in a
|
|
917
|
+
* dependency field and nowhere else — not in `name`, not in a description.
|
|
918
|
+
* The file's indentation is kept; a consumer's formatter must not see a diff
|
|
919
|
+
* it did not cause. Returns the text unchanged when nothing applied.
|
|
920
|
+
*/
|
|
921
|
+
interface ManifestRewriteOptions {
|
|
922
|
+
/**
|
|
923
|
+
* The range the renamed dependency gets — `^<the version this CLI was
|
|
924
|
+
* released as>`. The old range cannot be carried over: a 0.x consumer
|
|
925
|
+
* declares `"@saasicat/types": "^0.27.0"`, and `@saasicat/core` has no
|
|
926
|
+
* 0.27 — the rename starts on the 1.0 line. The caller passes the CLI's
|
|
927
|
+
* own version because that IS the line the consumer is migrating to;
|
|
928
|
+
* the codemod ships with it.
|
|
929
|
+
*/
|
|
930
|
+
readonly targetRange: string;
|
|
931
|
+
}
|
|
932
|
+
declare function rewriteManifest(text: string, table: RenameTable, options: ManifestRewriteOptions): RenameResult;
|
|
906
933
|
|
|
907
934
|
interface PatchAppModuleOptions {
|
|
908
935
|
/** Import specifier for the persistence bundle, or null when not generated. */
|
|
@@ -1147,4 +1174,4 @@ declare class UserCommands extends CommandRunner {
|
|
|
1147
1174
|
parsePassword(val: string): string;
|
|
1148
1175
|
}
|
|
1149
1176
|
|
|
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 };
|
|
1177
|
+
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, type ManifestRewriteOptions, 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, rewriteManifest, rewriteNames, rewriteSubpath, stripLineComment, structuralOnly, tablesAddressedBy };
|
package/dist/index.js
CHANGED
|
@@ -1881,6 +1881,13 @@ function rewriteNames(text, table) {
|
|
|
1881
1881
|
return `${quote}${to}`;
|
|
1882
1882
|
});
|
|
1883
1883
|
}
|
|
1884
|
+
for (const [from, to] of Object.entries(table.packages ?? {})) {
|
|
1885
|
+
if (from === "_") continue;
|
|
1886
|
+
next = next.replace(new RegExp(`${escape(from)}(?=['"\`/])`, "g"), () => {
|
|
1887
|
+
rewritten += 1;
|
|
1888
|
+
return to;
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1884
1891
|
for (const [from, to] of Object.entries(table.subpaths)) {
|
|
1885
1892
|
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
1886
1893
|
rewritten += 1;
|
|
@@ -1896,6 +1903,81 @@ function rewriteNames(text, table) {
|
|
|
1896
1903
|
};
|
|
1897
1904
|
}
|
|
1898
1905
|
__name(rewriteNames, "rewriteNames");
|
|
1906
|
+
var DEPENDENCY_FIELDS = [
|
|
1907
|
+
"dependencies",
|
|
1908
|
+
"devDependencies",
|
|
1909
|
+
"peerDependencies",
|
|
1910
|
+
"optionalDependencies"
|
|
1911
|
+
];
|
|
1912
|
+
var UNTRANSLATABLE_RANGE = /^(workspace:|file:|link:|npm:|git\+|https?:)/;
|
|
1913
|
+
function rewriteManifest(text, table, options) {
|
|
1914
|
+
const renames = Object.entries(table.packages ?? {}).filter(([from]) => from !== "_");
|
|
1915
|
+
const ambiguous = [];
|
|
1916
|
+
let manifest;
|
|
1917
|
+
try {
|
|
1918
|
+
manifest = JSON.parse(text);
|
|
1919
|
+
} catch {
|
|
1920
|
+
return {
|
|
1921
|
+
text,
|
|
1922
|
+
rewritten: 0,
|
|
1923
|
+
ambiguous
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
let rewritten = 0;
|
|
1927
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
1928
|
+
const deps = manifest[field];
|
|
1929
|
+
if (!deps || typeof deps !== "object") continue;
|
|
1930
|
+
const entries = Object.entries(deps);
|
|
1931
|
+
const renamed = entries.map(([name, range]) => {
|
|
1932
|
+
const to = renames.find(([from]) => from === name)?.[1];
|
|
1933
|
+
if (!to) return [
|
|
1934
|
+
name,
|
|
1935
|
+
range
|
|
1936
|
+
];
|
|
1937
|
+
if (UNTRANSLATABLE_RANGE.test(range)) {
|
|
1938
|
+
ambiguous.push(`${name} in ${field} (${range})`);
|
|
1939
|
+
return [
|
|
1940
|
+
name,
|
|
1941
|
+
range
|
|
1942
|
+
];
|
|
1943
|
+
}
|
|
1944
|
+
rewritten += 1;
|
|
1945
|
+
return [
|
|
1946
|
+
to,
|
|
1947
|
+
options.targetRange
|
|
1948
|
+
];
|
|
1949
|
+
});
|
|
1950
|
+
manifest[field] = Object.fromEntries(renamed);
|
|
1951
|
+
}
|
|
1952
|
+
const meta = manifest.peerDependenciesMeta;
|
|
1953
|
+
if (meta && typeof meta === "object") {
|
|
1954
|
+
manifest.peerDependenciesMeta = Object.fromEntries(Object.entries(meta).map(([name, flags]) => {
|
|
1955
|
+
const to = renames.find(([from]) => from === name)?.[1];
|
|
1956
|
+
if (!to) return [
|
|
1957
|
+
name,
|
|
1958
|
+
flags
|
|
1959
|
+
];
|
|
1960
|
+
rewritten += 1;
|
|
1961
|
+
return [
|
|
1962
|
+
to,
|
|
1963
|
+
flags
|
|
1964
|
+
];
|
|
1965
|
+
}));
|
|
1966
|
+
}
|
|
1967
|
+
if (rewritten === 0) return {
|
|
1968
|
+
text,
|
|
1969
|
+
rewritten: 0,
|
|
1970
|
+
ambiguous
|
|
1971
|
+
};
|
|
1972
|
+
const indent = /^[ \t]+/m.exec(text)?.[0] ?? " ";
|
|
1973
|
+
const trailing = text.endsWith("\n") ? "\n" : "";
|
|
1974
|
+
return {
|
|
1975
|
+
text: JSON.stringify(manifest, null, indent) + trailing,
|
|
1976
|
+
rewritten,
|
|
1977
|
+
ambiguous
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
__name(rewriteManifest, "rewriteManifest");
|
|
1899
1981
|
|
|
1900
1982
|
// src/init/patch-app-module.ts
|
|
1901
1983
|
var MARKER = "SaaSiCatModule.forRoot";
|
|
@@ -3153,6 +3235,7 @@ export {
|
|
|
3153
3235
|
relationNameOf,
|
|
3154
3236
|
reportConstraints,
|
|
3155
3237
|
rewriteImports,
|
|
3238
|
+
rewriteManifest,
|
|
3156
3239
|
rewriteNames,
|
|
3157
3240
|
rewriteSubpath,
|
|
3158
3241
|
stripLineComment,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/cli",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
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",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"qrcode-terminal": "^0.12.0",
|
|
32
|
-
"@saasicat/nest": "^1.0.0-rc.
|
|
33
|
-
"@saasicat/spec": "^1.0.0-rc.
|
|
34
|
-
"@saasicat/
|
|
32
|
+
"@saasicat/nest": "^1.0.0-rc.1",
|
|
33
|
+
"@saasicat/spec": "^1.0.0-rc.1",
|
|
34
|
+
"@saasicat/core": "^1.0.0-rc.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@nestjs/common": "^11.0.0",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"access": "public"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
|
-
"build": "node ../../scripts/build-and-prune.mjs \"tsup src/index.ts --format esm,cjs --dts --external @saasicat/
|
|
62
|
+
"build": "node ../../scripts/build-and-prune.mjs \"tsup src/index.ts --format esm,cjs --dts --external @saasicat/core --external @saasicat/nest --external @nestjs/common --external nest-commander --external qrcode-terminal\"",
|
|
63
63
|
"pretest": "pnpm run build",
|
|
64
64
|
"test": "node --test 'tests/*.test.js'"
|
|
65
65
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
2
|
import { DefinesQuota } from '@saasicat/nest/discovery';
|
|
3
|
-
import type { QuotaProvider } from '@saasicat/
|
|
3
|
+
import type { QuotaProvider } from '@saasicat/core';
|
|
4
4
|
|
|
5
5
|
import { PrismaService } from '../prisma/prisma.service';
|
|
6
6
|
|