@saasicat/cli 1.0.0-rc.1 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/saasicat.js CHANGED
@@ -43,10 +43,12 @@ import {
43
43
  applyTokens,
44
44
  constraintsFor,
45
45
  checkSchema,
46
- extractModelBlocks,
46
+ extractFragmentBlocks,
47
47
  findFkPointers,
48
48
  hasConstraints,
49
49
  assertValidProjectKey,
50
+ judgeModuleResolution,
51
+ readEffectiveModuleResolution,
50
52
  buildImportMap,
51
53
  migrationCreatedBy,
52
54
  rewriteImports,
@@ -133,14 +135,15 @@ async function selectFragmentFiles(dir, filter) {
133
135
 
134
136
  async function loadFragments(dir, filter) {
135
137
  const selected = await selectFragmentFiles(dir, filter);
136
- const blocks = new Map();
138
+ const blocks = { enums: new Map(), models: new Map() };
137
139
  for (const file of selected) {
138
140
  const content = await readFile(join(dir, file), 'utf8');
139
- const fileBlocks = extractModelBlocks(content);
140
- for (const [name, body] of fileBlocks) {
141
- if (!blocks.has(name)) {
142
- blocks.set(name, body);
143
- }
141
+ const fileBlocks = extractFragmentBlocks(content);
142
+ for (const [name, body] of fileBlocks.enums) {
143
+ if (!blocks.enums.has(name)) blocks.enums.set(name, body);
144
+ }
145
+ for (const [name, body] of fileBlocks.models) {
146
+ if (!blocks.models.has(name)) blocks.models.set(name, body);
144
147
  }
145
148
  }
146
149
  return { files: selected, blocks };
@@ -172,7 +175,7 @@ async function cmdSchemaApply(args) {
172
175
  }
173
176
 
174
177
  const { files, blocks } = await loadFragments(fragmentsDir, filter);
175
- if (blocks.size === 0) {
178
+ if (blocks.models.size === 0) {
176
179
  console.error('✗ No models found in the selected fragments.');
177
180
  process.exit(1);
178
181
  }
@@ -186,13 +189,16 @@ async function cmdSchemaApply(args) {
186
189
  // one where the manual step is most likely to have been forgotten.
187
190
  const fk = resolveFkPointers(result.schema, args);
188
191
 
189
- if (result.added.length === 0 && fk.enabled.length === 0) {
192
+ if (result.added.length === 0 && result.addedEnums.length === 0 && fk.enabled.length === 0) {
190
193
  console.log(`→ Nothing to do. Models already present: ${result.skipped.join(', ')}`);
191
194
  reportFkPointers(fk, args);
192
195
  return;
193
196
  }
194
197
 
195
198
  if (args['dry-run']) {
199
+ if (result.addedEnums.length) {
200
+ console.log(`(--dry-run) Would append enums: ${result.addedEnums.join(', ')}`);
201
+ }
196
202
  if (result.added.length) {
197
203
  console.log(`(--dry-run) Would append: ${result.added.join(', ')}`);
198
204
  }
@@ -210,12 +216,18 @@ async function cmdSchemaApply(args) {
210
216
  for (const { line } of fk.enabled) {
211
217
  console.log(` ${line + 1}: ${fk.schema.split('\n')[line]?.trim() ?? ''}`);
212
218
  }
213
- if (fk.enabled.length > 0 && result.added.length > 0) console.log('');
214
- if (result.added.length > 0) console.log(result.schema.slice(schema.length));
219
+ const appended = result.added.length > 0 || result.addedEnums.length > 0;
220
+ if (fk.enabled.length > 0 && appended) console.log('');
221
+ if (appended) console.log(result.schema.slice(schema.length));
215
222
  return;
216
223
  }
217
224
 
218
225
  await writeFile(schemaPath, fk.schema, 'utf8');
226
+ if (result.addedEnums.length) {
227
+ console.log(
228
+ `✓ Appended ${result.addedEnums.length} enum(s): ${result.addedEnums.join(', ')}`,
229
+ );
230
+ }
219
231
  console.log(`✓ Appended ${result.added.length} model(s): ${result.added.join(', ')}`);
220
232
  if (result.skipped.length) {
221
233
  console.log(`→ Skipped (already present): ${result.skipped.join(', ')}`);
@@ -711,6 +723,23 @@ async function cmdInit(args, argv) {
711
723
  }
712
724
 
713
725
  const root = resolve(args.dir ?? '.');
726
+
727
+ // Before anything is written: the files below import subpath exports,
728
+ // and under `moduleResolution: node` none of them resolves. Found by
729
+ // running the quickstart against an app that predates `nodenext`.
730
+ if (existsSync(join(root, 'tsconfig.json'))) {
731
+ const ts = loadTypeScript(root);
732
+ if (ts === null) {
733
+ console.log('! tsconfig.json not checked: no TypeScript resolvable from this project.');
734
+ } else {
735
+ const verdict = judgeModuleResolution(readEffectiveModuleResolution(root, ts));
736
+ if (!verdict.ok) {
737
+ console.error(`✗ ${verdict.reason}`);
738
+ process.exit(1);
739
+ }
740
+ }
741
+ }
742
+
714
743
  const plan = planInit({
715
744
  projectKey: args['project-key'],
716
745
  appName: args['app-name'],
@@ -772,12 +801,33 @@ async function cmdInit(args, argv) {
772
801
  console.log(' — the generated app does NOT compile until you do. An empty');
773
802
  console.log(' array means "deliberately auth-free" to the platform, and');
774
803
  console.log(' would publish GET /admin/discovery to anyone who asks.');
804
+ console.log(' 2. Name the modules in `imports: [YourPrismaModule, YourAuthModule]`');
805
+ console.log(' — the one exporting PrismaService and the one your guard needs.');
806
+ console.log(' The platform module resolves its providers from that list;');
807
+ console.log(' without it the first boot stops at "Nest can\'t resolve');
808
+ console.log(' dependencies of … (PrismaService)".');
775
809
  if (plan.quotaProviders.length > 0) {
776
- console.log(' 2. Check each quota provider counts the right thing');
777
- console.log(' 3. saasicat schema migrate --name=add_saasicat');
810
+ console.log(' 3. Check each quota provider counts the right thing');
811
+ console.log(' 4. saasicat schema migrate --name=add_saasicat');
778
812
  } else {
779
- console.log(' 2. saasicat schema migrate --name=add_saasicat');
813
+ console.log(' 3. saasicat schema migrate --name=add_saasicat');
814
+ }
815
+ }
816
+
817
+ /**
818
+ * The TypeScript that will compile the project: the consumer's own, resolved
819
+ * from the project root, so the tsconfig is read the way the build reads it.
820
+ * Falls back to the one this CLI was installed with, then to null.
821
+ */
822
+ function loadTypeScript(root) {
823
+ for (const from of [join(root, 'package.json'), import.meta.url]) {
824
+ try {
825
+ return createRequire(from)('typescript');
826
+ } catch {
827
+ // Not resolvable from here — try the next origin.
828
+ }
780
829
  }
830
+ return null;
781
831
  }
782
832
 
783
833
  /** Adds the platform to `src/app.module.ts`, or says exactly what to paste. */
package/dist/.build-stamp CHANGED
@@ -1 +1 @@
1
- 97f66acb8ca26c7c9470c1991febbb2f68518f1b3cd364af6d3566249550482a
1
+ d16106f714ce74c061cc0211a2be4ebe01a1cb80b8e4b5da45ff296c861cff71
package/dist/index.cjs CHANGED
@@ -85,6 +85,9 @@ __export(index_exports, {
85
85
  enableFkPointers: () => enableFkPointers,
86
86
  extractBlockNames: () => extractBlockNames,
87
87
  extractBlocks: () => extractBlocks,
88
+ extractEnumBlocks: () => extractEnumBlocks,
89
+ extractEnumNames: () => extractEnumNames,
90
+ extractFragmentBlocks: () => extractFragmentBlocks,
88
91
  extractModelBlocks: () => extractModelBlocks,
89
92
  extractModelNames: () => extractModelNames,
90
93
  findFkPointers: () => findFkPointers,
@@ -93,6 +96,7 @@ __export(index_exports, {
93
96
  hasConstraints: () => hasConstraints,
94
97
  isNoLongerPublic: () => isNoLongerPublic,
95
98
  isOneToOne: () => isOneToOne,
99
+ judgeModuleResolution: () => judgeModuleResolution,
96
100
  kebabCase: () => kebabCase,
97
101
  migrationCreatedBy: () => migrationCreatedBy,
98
102
  minimumQuotasPerPlan: () => minimumQuotasPerPlan,
@@ -108,6 +112,7 @@ __export(index_exports, {
108
112
  planInit: () => planInit,
109
113
  projectKeyPattern: () => projectKeyPattern,
110
114
  quotaKeyPattern: () => quotaKeyPattern,
115
+ readEffectiveModuleResolution: () => readEffectiveModuleResolution,
111
116
  relationNameOf: () => relationNameOf,
112
117
  reportConstraints: () => reportConstraints,
113
118
  rewriteImports: () => rewriteImports,
@@ -1248,13 +1253,43 @@ function extractModelBlocks(fragment) {
1248
1253
  return extractBlocks(fragment, "model");
1249
1254
  }
1250
1255
  __name(extractModelBlocks, "extractModelBlocks");
1256
+ function extractEnumBlocks(fragment) {
1257
+ return extractBlocks(fragment, "enum");
1258
+ }
1259
+ __name(extractEnumBlocks, "extractEnumBlocks");
1260
+ function extractEnumNames(schema2) {
1261
+ return extractBlockNames(schema2, "enum");
1262
+ }
1263
+ __name(extractEnumNames, "extractEnumNames");
1264
+ function extractFragmentBlocks(fragment) {
1265
+ return {
1266
+ enums: extractEnumBlocks(fragment),
1267
+ models: extractModelBlocks(fragment)
1268
+ };
1269
+ }
1270
+ __name(extractFragmentBlocks, "extractFragmentBlocks");
1251
1271
  function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
1252
- const existing = new Set(extractModelNames(schema2));
1272
+ const blocks = fragmentBlocks instanceof Map ? {
1273
+ enums: /* @__PURE__ */ new Map(),
1274
+ models: fragmentBlocks
1275
+ } : fragmentBlocks;
1276
+ const existingModels = new Set(extractModelNames(schema2));
1277
+ const existingEnums = new Set(extractEnumNames(schema2));
1253
1278
  const added = [];
1254
1279
  const skipped = [];
1280
+ const addedEnums = [];
1281
+ const skippedEnums = [];
1255
1282
  const additions = [];
1256
- for (const [name, block] of fragmentBlocks) {
1257
- if (existing.has(name)) {
1283
+ for (const [name, block] of blocks.enums) {
1284
+ if (existingEnums.has(name)) {
1285
+ skippedEnums.push(name);
1286
+ } else {
1287
+ addedEnums.push(name);
1288
+ additions.push(block);
1289
+ }
1290
+ }
1291
+ for (const [name, block] of blocks.models) {
1292
+ if (existingModels.has(name)) {
1258
1293
  skipped.push(name);
1259
1294
  } else {
1260
1295
  added.push(name);
@@ -1265,6 +1300,8 @@ function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
1265
1300
  return {
1266
1301
  added,
1267
1302
  skipped,
1303
+ addedEnums,
1304
+ skippedEnums,
1268
1305
  schema: schema2
1269
1306
  };
1270
1307
  }
@@ -1281,6 +1318,8 @@ function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
1281
1318
  return {
1282
1319
  added,
1283
1320
  skipped,
1321
+ addedEnums,
1322
+ skippedEnums,
1284
1323
  schema: trimmedSchema + header + additions.join("\n\n") + "\n"
1285
1324
  };
1286
1325
  }
@@ -1835,6 +1874,38 @@ function patchOptionsFor(plan) {
1835
1874
  }
1836
1875
  __name(patchOptionsFor, "patchOptionsFor");
1837
1876
 
1877
+ // src/init/module-resolution.ts
1878
+ var import_node_path = require("path");
1879
+ var RESOLVES_SUBPATHS = /* @__PURE__ */ new Set([
1880
+ "node16",
1881
+ "nodenext",
1882
+ "bundler"
1883
+ ]);
1884
+ function judgeModuleResolution(value) {
1885
+ if (value === null || RESOLVES_SUBPATHS.has(value)) {
1886
+ return {
1887
+ ok: true,
1888
+ value
1889
+ };
1890
+ }
1891
+ return {
1892
+ ok: false,
1893
+ value,
1894
+ reason: `tsconfig.json resolves modules with "moduleResolution": "${value}"` + (value === "node10" ? ' (what TypeScript calls the "node" setting)' : "") + '. The files init writes import subpath exports (`@saasicat/nest/platform`, `/billing`, `/discovery`), which TypeScript resolves only under "node16", "nodenext" or "bundler". Set one of those first \u2014 `nest new` has used "nodenext" since NestJS 10.'
1895
+ };
1896
+ }
1897
+ __name(judgeModuleResolution, "judgeModuleResolution");
1898
+ function readEffectiveModuleResolution(root, ts) {
1899
+ const configPath = (0, import_node_path.join)(root, "tsconfig.json");
1900
+ const read = ts.readConfigFile(configPath, ts.sys.readFile);
1901
+ if (read.error || read.config === void 0) return null;
1902
+ const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, root);
1903
+ const kind = parsed.options.moduleResolution;
1904
+ if (kind === void 0) return null;
1905
+ return ts.ModuleResolutionKind[kind].toLowerCase();
1906
+ }
1907
+ __name(readEffectiveModuleResolution, "readEffectiveModuleResolution");
1908
+
1838
1909
  // src/codemods/v1-imports.ts
1839
1910
  var PUBLIC_PREFIXES = [
1840
1911
  "ui/",
@@ -2118,7 +2189,7 @@ ${block}`;
2118
2189
  return {
2119
2190
  source,
2120
2191
  status: "declined",
2121
- reason: "no `@Module({ imports: [ ... ] })` was found in this file, so there is nowhere to add the platform without guessing at the structure",
2192
+ reason: "this file has no `@Module({ ... })` with an `imports:` key on a line of its own \u2014 the shape `nest new` writes \u2014 so there is nowhere to add the platform without guessing at the structure",
2122
2193
  manualBlock
2123
2194
  };
2124
2195
  }
@@ -2206,6 +2277,13 @@ function renderForRootBlock(options) {
2206
2277
  " // your whole capability inventory \u2014 and the manifest routes to",
2207
2278
  " // anyone who asks. Import your guard and put it in.",
2208
2279
  " controller: { guards: [YourAuthGuard] },",
2280
+ " // The modules whose providers the platform injects: the one",
2281
+ " // exporting PrismaService, and the one your guard depends on.",
2282
+ " // `prismaPersistence({ client: PrismaService })` is resolved",
2283
+ " // inside the platform module, which sees only what is listed",
2284
+ " // here or declared @Global. Same rule as the guard: this does",
2285
+ " // not compile until you name them.",
2286
+ " imports: [YourPrismaModule, YourAuthModule],",
2209
2287
  options.persistenceImport ? " persistence," : " // persistence: prismaPersistence({ client: PrismaService }),",
2210
2288
  " catalog: { featureUiRegistry: " + options.registry.constName + " },",
2211
2289
  " adminResources: true,",
@@ -2852,7 +2930,7 @@ DoctorCommands = _ts_decorate12([
2852
2930
 
2853
2931
  // src/discovery.command.ts
2854
2932
  var import_node_fs = require("fs");
2855
- var import_node_path = require("path");
2933
+ var import_node_path2 = require("path");
2856
2934
  var import_common13 = require("@nestjs/common");
2857
2935
  var import_nest_commander5 = require("nest-commander");
2858
2936
  var import_nest6 = require("@saasicat/nest");
@@ -2890,8 +2968,8 @@ var DiscoveryScanCommand = class extends import_nest_commander5.CommandRunner {
2890
2968
  try {
2891
2969
  const snapshot = this.scanner.rebuildSnapshot();
2892
2970
  if (flags.out) {
2893
- const outPath = (0, import_node_path.resolve)(flags.out);
2894
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(outPath), {
2971
+ const outPath = (0, import_node_path2.resolve)(flags.out);
2972
+ (0, import_node_fs.mkdirSync)((0, import_node_path2.dirname)(outPath), {
2895
2973
  recursive: true
2896
2974
  });
2897
2975
  (0, import_node_fs.writeFileSync)(outPath, JSON.stringify(snapshot, null, 2) + "\n", "utf-8");
@@ -2900,7 +2978,7 @@ var DiscoveryScanCommand = class extends import_nest_commander5.CommandRunner {
2900
2978
  process.stdout.write(`Discovery-Scan (${snapshot.app.key} v${snapshot.app.version}): ${snapshot.capabilities.length} Capabilities \xB7 ${snapshot.features.length} Features \xB7 ${snapshot.quotas.length} Quotas \xB7 hash ${snapshot.hash.slice(0, 19)}\u2026
2901
2979
  `);
2902
2980
  if (target) {
2903
- process.stdout.write(`Snapshot persisted: ${(0, import_node_path.resolve)(target)}
2981
+ process.stdout.write(`Snapshot persisted: ${(0, import_node_path2.resolve)(target)}
2904
2982
  `);
2905
2983
  } else {
2906
2984
  process.stderr.write("WARNING: the snapshot was not persisted \u2014 neither snapshotPath (DiscoveryModule.forRoot) configured nor --out given. The seed gate will find no snapshot this way.\n");
@@ -3329,6 +3407,9 @@ UserCommands = _ts_decorate14([
3329
3407
  enableFkPointers,
3330
3408
  extractBlockNames,
3331
3409
  extractBlocks,
3410
+ extractEnumBlocks,
3411
+ extractEnumNames,
3412
+ extractFragmentBlocks,
3332
3413
  extractModelBlocks,
3333
3414
  extractModelNames,
3334
3415
  findFkPointers,
@@ -3337,6 +3418,7 @@ UserCommands = _ts_decorate14([
3337
3418
  hasConstraints,
3338
3419
  isNoLongerPublic,
3339
3420
  isOneToOne,
3421
+ judgeModuleResolution,
3340
3422
  kebabCase,
3341
3423
  migrationCreatedBy,
3342
3424
  minimumQuotasPerPlan,
@@ -3352,6 +3434,7 @@ UserCommands = _ts_decorate14([
3352
3434
  planInit,
3353
3435
  projectKeyPattern,
3354
3436
  quotaKeyPattern,
3437
+ readEffectiveModuleResolution,
3355
3438
  relationNameOf,
3356
3439
  reportConstraints,
3357
3440
  rewriteImports,
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { MfaService, AdminAuditService, AdminManifestService, DiscoverySnapshot, ProviderSpec, DiscoveryScanner } from '@saasicat/nest';
2
2
  import { UserPort, PlatformUserDto, AuditQueryPort, AuditEntry, AdminManifest, ManifestAccessPort, PlanCatalog, UserManagementPort } from '@saasicat/core';
3
3
  import { Type, DynamicModule } from '@nestjs/common';
4
+ import * as TypeScript from 'typescript';
4
5
  import { CommandRunner } from 'nest-commander';
5
6
 
6
7
  declare const CLI_CONTEXT_CONFIG_TOKEN: unique symbol;
@@ -381,19 +382,41 @@ declare function extractModelNames(schema: string): string[];
381
382
  * `name -> complete block text incl. opening/closing braces`.
382
383
  */
383
384
  declare function extractModelBlocks(fragment: string): Map<string, string>;
385
+ /** Returns all `enum X { ... }` blocks from a fragment, keyed by name. */
386
+ declare function extractEnumBlocks(fragment: string): Map<string, string>;
387
+ /** Names of all top-level `enum X { ... }` blocks in the schema. */
388
+ declare function extractEnumNames(schema: string): string[];
389
+ /**
390
+ * What a fragment contributes: its enums and its models. Both travel
391
+ * together, because a model's `BillingCycle` field is a validation error in
392
+ * a schema that has the model and not the enum — which is what `apply` used
393
+ * to produce for every consumer whose schema did not already carry the enums
394
+ * by hand, and what the example app masked by carrying them.
395
+ */
396
+ interface FragmentBlocks {
397
+ enums: Map<string, string>;
398
+ models: Map<string, string>;
399
+ }
400
+ declare function extractFragmentBlocks(fragment: string): FragmentBlocks;
384
401
  interface ApplyResult {
385
- /** Number of models that were added. */
402
+ /** Models that were added. */
386
403
  added: string[];
387
- /** Number of models that were already present (no write). */
404
+ /** Models that were already present (no write). */
388
405
  skipped: string[];
389
- /** Resulting schema text. When `added.length === 0` identical to the input. */
406
+ /** Enums that were added, before the models that use them. */
407
+ addedEnums: string[];
408
+ /** Enums that were already present (no write). */
409
+ skippedEnums: string[];
410
+ /** Resulting schema text. When nothing was added, identical to the input. */
390
411
  schema: string;
391
412
  }
392
413
  /**
393
- * Appends missing models from `fragmentBlocks` to the end of `schema`. Existing
394
- * models (same name) stay unchanged and are listed in `skipped`.
414
+ * Appends missing enums and models from `fragmentBlocks` to the end of
415
+ * `schema`. Existing blocks (same name) stay unchanged and are listed as
416
+ * skipped. A plain map of models is accepted for the callers that only ever
417
+ * had models; the enums are then `[]`, which is what they were before.
395
418
  */
396
- declare function applyFragmentBlocks(schema: string, fragmentBlocks: Map<string, string>, options?: {
419
+ declare function applyFragmentBlocks(schema: string, fragmentBlocks: FragmentBlocks | Map<string, string>, options?: {
397
420
  fragmentLabel?: string;
398
421
  }): ApplyResult;
399
422
 
@@ -804,6 +827,33 @@ interface PatchOptionsFromPlan {
804
827
  };
805
828
  }
806
829
 
830
+ interface ModuleResolutionVerdict {
831
+ readonly ok: boolean;
832
+ /** The effective setting, lower-cased as TypeScript names it, or null when unset. */
833
+ readonly value: string | null;
834
+ readonly reason?: string;
835
+ }
836
+ /**
837
+ * Judges an effective `moduleResolution`, as `readEffectiveModuleResolution`
838
+ * reports it.
839
+ *
840
+ * Unset is `ok`: TypeScript then derives it from `module`, and a `module`
841
+ * that implies the old resolution is a setup this cannot see from here —
842
+ * the next build says so, with TypeScript's own message.
843
+ */
844
+ declare function judgeModuleResolution(value: string | null): ModuleResolutionVerdict;
845
+ /**
846
+ * The `moduleResolution` a project's `tsconfig.json` resolves to, after
847
+ * `extends`, lower-cased as TypeScript names the kind (`node10`, `node16`,
848
+ * `nodenext`, `bundler`, `classic`) — or null when there is no config, the
849
+ * config cannot be parsed, or the option is not set anywhere in the chain.
850
+ *
851
+ * `ts` is whichever TypeScript will compile the project: the consumer's own
852
+ * when it can be resolved from the project root, so the reading matches the
853
+ * build that follows.
854
+ */
855
+ declare function readEffectiveModuleResolution(root: string, ts: typeof TypeScript): string | null;
856
+
807
857
  /** The pattern `plan-catalog.schema.json` puts on `projectKey`. */
808
858
  declare function projectKeyPattern(): RegExp;
809
859
  /** The pattern it puts on the keys inside a plan's `quotas` object. */
@@ -1174,4 +1224,4 @@ declare class UserCommands extends CommandRunner {
1174
1224
  parsePassword(val: string): string;
1175
1225
  }
1176
1226
 
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 };
1227
+ 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 FragmentBlocks, 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 ModuleResolutionVerdict, 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, extractEnumBlocks, extractEnumNames, extractFragmentBlocks, extractModelBlocks, extractModelNames, findFkPointers, foreignKeyOf, hasBackRelation, hasConstraints, isNoLongerPublic, isOneToOne, judgeModuleResolution, kebabCase, migrationCreatedBy, minimumQuotasPerPlan, namedImports, parseBlockAttributes, parseEnumValues, parseFields, parseQuota, parseSchema, pascalCase, patchAppModule, patchOptionsFor, planInit, projectKeyPattern, quotaKeyPattern, readEffectiveModuleResolution, relationNameOf, reportConstraints, rewriteImports, rewriteManifest, rewriteNames, rewriteSubpath, stripLineComment, structuralOnly, tablesAddressedBy };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { MfaService, AdminAuditService, AdminManifestService, DiscoverySnapshot, ProviderSpec, DiscoveryScanner } from '@saasicat/nest';
2
2
  import { UserPort, PlatformUserDto, AuditQueryPort, AuditEntry, AdminManifest, ManifestAccessPort, PlanCatalog, UserManagementPort } from '@saasicat/core';
3
3
  import { Type, DynamicModule } from '@nestjs/common';
4
+ import * as TypeScript from 'typescript';
4
5
  import { CommandRunner } from 'nest-commander';
5
6
 
6
7
  declare const CLI_CONTEXT_CONFIG_TOKEN: unique symbol;
@@ -381,19 +382,41 @@ declare function extractModelNames(schema: string): string[];
381
382
  * `name -> complete block text incl. opening/closing braces`.
382
383
  */
383
384
  declare function extractModelBlocks(fragment: string): Map<string, string>;
385
+ /** Returns all `enum X { ... }` blocks from a fragment, keyed by name. */
386
+ declare function extractEnumBlocks(fragment: string): Map<string, string>;
387
+ /** Names of all top-level `enum X { ... }` blocks in the schema. */
388
+ declare function extractEnumNames(schema: string): string[];
389
+ /**
390
+ * What a fragment contributes: its enums and its models. Both travel
391
+ * together, because a model's `BillingCycle` field is a validation error in
392
+ * a schema that has the model and not the enum — which is what `apply` used
393
+ * to produce for every consumer whose schema did not already carry the enums
394
+ * by hand, and what the example app masked by carrying them.
395
+ */
396
+ interface FragmentBlocks {
397
+ enums: Map<string, string>;
398
+ models: Map<string, string>;
399
+ }
400
+ declare function extractFragmentBlocks(fragment: string): FragmentBlocks;
384
401
  interface ApplyResult {
385
- /** Number of models that were added. */
402
+ /** Models that were added. */
386
403
  added: string[];
387
- /** Number of models that were already present (no write). */
404
+ /** Models that were already present (no write). */
388
405
  skipped: string[];
389
- /** Resulting schema text. When `added.length === 0` identical to the input. */
406
+ /** Enums that were added, before the models that use them. */
407
+ addedEnums: string[];
408
+ /** Enums that were already present (no write). */
409
+ skippedEnums: string[];
410
+ /** Resulting schema text. When nothing was added, identical to the input. */
390
411
  schema: string;
391
412
  }
392
413
  /**
393
- * Appends missing models from `fragmentBlocks` to the end of `schema`. Existing
394
- * models (same name) stay unchanged and are listed in `skipped`.
414
+ * Appends missing enums and models from `fragmentBlocks` to the end of
415
+ * `schema`. Existing blocks (same name) stay unchanged and are listed as
416
+ * skipped. A plain map of models is accepted for the callers that only ever
417
+ * had models; the enums are then `[]`, which is what they were before.
395
418
  */
396
- declare function applyFragmentBlocks(schema: string, fragmentBlocks: Map<string, string>, options?: {
419
+ declare function applyFragmentBlocks(schema: string, fragmentBlocks: FragmentBlocks | Map<string, string>, options?: {
397
420
  fragmentLabel?: string;
398
421
  }): ApplyResult;
399
422
 
@@ -804,6 +827,33 @@ interface PatchOptionsFromPlan {
804
827
  };
805
828
  }
806
829
 
830
+ interface ModuleResolutionVerdict {
831
+ readonly ok: boolean;
832
+ /** The effective setting, lower-cased as TypeScript names it, or null when unset. */
833
+ readonly value: string | null;
834
+ readonly reason?: string;
835
+ }
836
+ /**
837
+ * Judges an effective `moduleResolution`, as `readEffectiveModuleResolution`
838
+ * reports it.
839
+ *
840
+ * Unset is `ok`: TypeScript then derives it from `module`, and a `module`
841
+ * that implies the old resolution is a setup this cannot see from here —
842
+ * the next build says so, with TypeScript's own message.
843
+ */
844
+ declare function judgeModuleResolution(value: string | null): ModuleResolutionVerdict;
845
+ /**
846
+ * The `moduleResolution` a project's `tsconfig.json` resolves to, after
847
+ * `extends`, lower-cased as TypeScript names the kind (`node10`, `node16`,
848
+ * `nodenext`, `bundler`, `classic`) — or null when there is no config, the
849
+ * config cannot be parsed, or the option is not set anywhere in the chain.
850
+ *
851
+ * `ts` is whichever TypeScript will compile the project: the consumer's own
852
+ * when it can be resolved from the project root, so the reading matches the
853
+ * build that follows.
854
+ */
855
+ declare function readEffectiveModuleResolution(root: string, ts: typeof TypeScript): string | null;
856
+
807
857
  /** The pattern `plan-catalog.schema.json` puts on `projectKey`. */
808
858
  declare function projectKeyPattern(): RegExp;
809
859
  /** The pattern it puts on the keys inside a plan's `quotas` object. */
@@ -1174,4 +1224,4 @@ declare class UserCommands extends CommandRunner {
1174
1224
  parsePassword(val: string): string;
1175
1225
  }
1176
1226
 
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 };
1227
+ 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 FragmentBlocks, 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 ModuleResolutionVerdict, 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, extractEnumBlocks, extractEnumNames, extractFragmentBlocks, extractModelBlocks, extractModelNames, findFkPointers, foreignKeyOf, hasBackRelation, hasConstraints, isNoLongerPublic, isOneToOne, judgeModuleResolution, kebabCase, migrationCreatedBy, minimumQuotasPerPlan, namedImports, parseBlockAttributes, parseEnumValues, parseFields, parseQuota, parseSchema, pascalCase, patchAppModule, patchOptionsFor, planInit, projectKeyPattern, quotaKeyPattern, readEffectiveModuleResolution, relationNameOf, reportConstraints, rewriteImports, rewriteManifest, rewriteNames, rewriteSubpath, stripLineComment, structuralOnly, tablesAddressedBy };
package/dist/index.js CHANGED
@@ -1129,13 +1129,43 @@ function extractModelBlocks(fragment) {
1129
1129
  return extractBlocks(fragment, "model");
1130
1130
  }
1131
1131
  __name(extractModelBlocks, "extractModelBlocks");
1132
+ function extractEnumBlocks(fragment) {
1133
+ return extractBlocks(fragment, "enum");
1134
+ }
1135
+ __name(extractEnumBlocks, "extractEnumBlocks");
1136
+ function extractEnumNames(schema2) {
1137
+ return extractBlockNames(schema2, "enum");
1138
+ }
1139
+ __name(extractEnumNames, "extractEnumNames");
1140
+ function extractFragmentBlocks(fragment) {
1141
+ return {
1142
+ enums: extractEnumBlocks(fragment),
1143
+ models: extractModelBlocks(fragment)
1144
+ };
1145
+ }
1146
+ __name(extractFragmentBlocks, "extractFragmentBlocks");
1132
1147
  function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
1133
- const existing = new Set(extractModelNames(schema2));
1148
+ const blocks = fragmentBlocks instanceof Map ? {
1149
+ enums: /* @__PURE__ */ new Map(),
1150
+ models: fragmentBlocks
1151
+ } : fragmentBlocks;
1152
+ const existingModels = new Set(extractModelNames(schema2));
1153
+ const existingEnums = new Set(extractEnumNames(schema2));
1134
1154
  const added = [];
1135
1155
  const skipped = [];
1156
+ const addedEnums = [];
1157
+ const skippedEnums = [];
1136
1158
  const additions = [];
1137
- for (const [name, block] of fragmentBlocks) {
1138
- if (existing.has(name)) {
1159
+ for (const [name, block] of blocks.enums) {
1160
+ if (existingEnums.has(name)) {
1161
+ skippedEnums.push(name);
1162
+ } else {
1163
+ addedEnums.push(name);
1164
+ additions.push(block);
1165
+ }
1166
+ }
1167
+ for (const [name, block] of blocks.models) {
1168
+ if (existingModels.has(name)) {
1139
1169
  skipped.push(name);
1140
1170
  } else {
1141
1171
  added.push(name);
@@ -1146,6 +1176,8 @@ function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
1146
1176
  return {
1147
1177
  added,
1148
1178
  skipped,
1179
+ addedEnums,
1180
+ skippedEnums,
1149
1181
  schema: schema2
1150
1182
  };
1151
1183
  }
@@ -1162,6 +1194,8 @@ function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
1162
1194
  return {
1163
1195
  added,
1164
1196
  skipped,
1197
+ addedEnums,
1198
+ skippedEnums,
1165
1199
  schema: trimmedSchema + header + additions.join("\n\n") + "\n"
1166
1200
  };
1167
1201
  }
@@ -1716,6 +1750,38 @@ function patchOptionsFor(plan) {
1716
1750
  }
1717
1751
  __name(patchOptionsFor, "patchOptionsFor");
1718
1752
 
1753
+ // src/init/module-resolution.ts
1754
+ import { join } from "path";
1755
+ var RESOLVES_SUBPATHS = /* @__PURE__ */ new Set([
1756
+ "node16",
1757
+ "nodenext",
1758
+ "bundler"
1759
+ ]);
1760
+ function judgeModuleResolution(value) {
1761
+ if (value === null || RESOLVES_SUBPATHS.has(value)) {
1762
+ return {
1763
+ ok: true,
1764
+ value
1765
+ };
1766
+ }
1767
+ return {
1768
+ ok: false,
1769
+ value,
1770
+ reason: `tsconfig.json resolves modules with "moduleResolution": "${value}"` + (value === "node10" ? ' (what TypeScript calls the "node" setting)' : "") + '. The files init writes import subpath exports (`@saasicat/nest/platform`, `/billing`, `/discovery`), which TypeScript resolves only under "node16", "nodenext" or "bundler". Set one of those first \u2014 `nest new` has used "nodenext" since NestJS 10.'
1771
+ };
1772
+ }
1773
+ __name(judgeModuleResolution, "judgeModuleResolution");
1774
+ function readEffectiveModuleResolution(root, ts) {
1775
+ const configPath = join(root, "tsconfig.json");
1776
+ const read = ts.readConfigFile(configPath, ts.sys.readFile);
1777
+ if (read.error || read.config === void 0) return null;
1778
+ const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, root);
1779
+ const kind = parsed.options.moduleResolution;
1780
+ if (kind === void 0) return null;
1781
+ return ts.ModuleResolutionKind[kind].toLowerCase();
1782
+ }
1783
+ __name(readEffectiveModuleResolution, "readEffectiveModuleResolution");
1784
+
1719
1785
  // src/codemods/v1-imports.ts
1720
1786
  var PUBLIC_PREFIXES = [
1721
1787
  "ui/",
@@ -1999,7 +2065,7 @@ ${block}`;
1999
2065
  return {
2000
2066
  source,
2001
2067
  status: "declined",
2002
- reason: "no `@Module({ imports: [ ... ] })` was found in this file, so there is nowhere to add the platform without guessing at the structure",
2068
+ reason: "this file has no `@Module({ ... })` with an `imports:` key on a line of its own \u2014 the shape `nest new` writes \u2014 so there is nowhere to add the platform without guessing at the structure",
2003
2069
  manualBlock
2004
2070
  };
2005
2071
  }
@@ -2087,6 +2153,13 @@ function renderForRootBlock(options) {
2087
2153
  " // your whole capability inventory \u2014 and the manifest routes to",
2088
2154
  " // anyone who asks. Import your guard and put it in.",
2089
2155
  " controller: { guards: [YourAuthGuard] },",
2156
+ " // The modules whose providers the platform injects: the one",
2157
+ " // exporting PrismaService, and the one your guard depends on.",
2158
+ " // `prismaPersistence({ client: PrismaService })` is resolved",
2159
+ " // inside the platform module, which sees only what is listed",
2160
+ " // here or declared @Global. Same rule as the guard: this does",
2161
+ " // not compile until you name them.",
2162
+ " imports: [YourPrismaModule, YourAuthModule],",
2090
2163
  options.persistenceImport ? " persistence," : " // persistence: prismaPersistence({ client: PrismaService }),",
2091
2164
  " catalog: { featureUiRegistry: " + options.registry.constName + " },",
2092
2165
  " adminResources: true,",
@@ -3209,6 +3282,9 @@ export {
3209
3282
  enableFkPointers,
3210
3283
  extractBlockNames,
3211
3284
  extractBlocks,
3285
+ extractEnumBlocks,
3286
+ extractEnumNames,
3287
+ extractFragmentBlocks,
3212
3288
  extractModelBlocks,
3213
3289
  extractModelNames,
3214
3290
  findFkPointers,
@@ -3217,6 +3293,7 @@ export {
3217
3293
  hasConstraints,
3218
3294
  isNoLongerPublic,
3219
3295
  isOneToOne,
3296
+ judgeModuleResolution,
3220
3297
  kebabCase,
3221
3298
  migrationCreatedBy,
3222
3299
  minimumQuotasPerPlan,
@@ -3232,6 +3309,7 @@ export {
3232
3309
  planInit,
3233
3310
  projectKeyPattern,
3234
3311
  quotaKeyPattern,
3312
+ readEffectiveModuleResolution,
3235
3313
  relationNameOf,
3236
3314
  reportConstraints,
3237
3315
  rewriteImports,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasicat/cli",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.0-rc.2",
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",
@@ -16,7 +16,8 @@
16
16
  "types": "./dist/index.d.cts",
17
17
  "default": "./dist/index.cjs"
18
18
  }
19
- }
19
+ },
20
+ "./package.json": "./package.json"
20
21
  },
21
22
  "files": [
22
23
  "dist",
@@ -29,9 +30,9 @@
29
30
  },
30
31
  "dependencies": {
31
32
  "qrcode-terminal": "^0.12.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"
33
+ "@saasicat/nest": "^1.0.0-rc.2",
34
+ "@saasicat/spec": "^1.0.0-rc.2",
35
+ "@saasicat/core": "^1.0.0-rc.2"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "@nestjs/common": "^11.0.0",