@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 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
- const result = rewriteNames(source, table);
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
- 87fdb737df2d686fe3d296add0d87c2a3765371380b486e2ff20fda896b3db65
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/types';
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/types';
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.0",
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.0",
33
- "@saasicat/spec": "^1.0.0-rc.0",
34
- "@saasicat/types": "^1.0.0-rc.0"
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/types --external @saasicat/nest --external @nestjs/common --external nest-commander --external qrcode-terminal\"",
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 { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';
2
2
  import { Injectable } from '@nestjs/common';
3
- import type { PasswordHasher } from '@saasicat/types';
3
+ import type { PasswordHasher } from '@saasicat/core';
4
4
 
5
5
  const KEY_LENGTH = 64;
6
6
 
@@ -1,4 +1,4 @@
1
- import type { ManifestContribution } from '@saasicat/types';
1
+ import type { ManifestContribution } from '@saasicat/core';
2
2
 
3
3
  /**
4
4
  * What this app adds to the SuperAdmin UI.
@@ -1,4 +1,4 @@
1
- import type { FeatureUiRegistry } from '@saasicat/types';
1
+ import type { FeatureUiRegistry } from '@saasicat/core';
2
2
 
3
3
  /**
4
4
  * Labels and icons for the feature and quota keys discovery finds in your code.
@@ -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/types';
3
+ import type { QuotaProvider } from '@saasicat/core';
4
4
 
5
5
  import { PrismaService } from '../prisma/prisma.service';
6
6