@saasicat/cli 0.21.0 → 0.22.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.
Files changed (2) hide show
  1. package/bin/saasicat.js +48 -48
  2. package/package.json +4 -4
package/bin/saasicat.js CHANGED
@@ -10,8 +10,6 @@
10
10
  // schema check [--prisma-schema=PATH] [--fragments=01,02,03]
11
11
  // Reports what your schema is missing relative to the canonical
12
12
  // fragments. Read-only; exits 1 on drift so CI can gate on it.
13
- //
14
- // Spec: handoff/superadmin/QUICKSTART_SIMPLIFICATIONS.md §P5.
15
13
 
16
14
  import { readFile, writeFile, readdir } from 'node:fs/promises';
17
15
  import { existsSync } from 'node:fs';
@@ -43,8 +41,8 @@ function resolveFragmentsDir() {
43
41
  const candidate = join(root, 'prisma-fragments');
44
42
  if (!existsSync(candidate)) {
45
43
  throw new Error(
46
- `prisma-fragments/-Verzeichnis nicht gefunden unter ${candidate}. ` +
47
- 'Stelle sicher, dass @saasicat/spec installiert ist.',
44
+ `prisma-fragments/ directory not found at ${candidate}. ` +
45
+ 'Make sure @saasicat/spec is installed.',
48
46
  );
49
47
  }
50
48
  return candidate;
@@ -61,8 +59,8 @@ async function selectFragmentFiles(dir, filter) {
61
59
  const unknown = filter.filter((prefix) => !available.has(prefix));
62
60
  if (unknown.length > 0) {
63
61
  console.error(
64
- `✗ Unbekannte Fragmente: ${unknown.join(', ')}. ` +
65
- `Verfügbar: ${[...available].join(', ')}`,
62
+ `✗ Unknown fragments: ${unknown.join(', ')}. ` +
63
+ `Available: ${[...available].join(', ')}`,
66
64
  );
67
65
  process.exit(1);
68
66
  }
@@ -91,7 +89,7 @@ function parseFragmentFilter(raw) {
91
89
  async function readSchemaOrExit(args) {
92
90
  const schemaPath = resolve(args['prisma-schema'] ?? 'prisma/schema.prisma');
93
91
  if (!existsSync(schemaPath)) {
94
- console.error(`✗ schema.prisma nicht gefunden: ${schemaPath}`);
92
+ console.error(`✗ schema.prisma not found: ${schemaPath}`);
95
93
  process.exit(1);
96
94
  }
97
95
  return { schemaPath, schema: await readFile(schemaPath, 'utf8') };
@@ -103,9 +101,7 @@ async function cmdSchemaApply(args) {
103
101
  const fragmentsDir = resolveFragmentsDir();
104
102
  const filter = parseFragmentFilter(args.fragments);
105
103
  if (!filter && !args.all) {
106
- console.error(
107
- '✗ Entweder --fragments=01,02,03 oder --all übergeben. ' + 'Verfügbare Fragmente:',
108
- );
104
+ console.error('✗ Pass either --fragments=01,02,03 or --all. Available fragments:');
109
105
  const files = (await readdir(fragmentsDir)).filter((f) => f.endsWith('.prisma')).sort();
110
106
  for (const f of files) console.error(` ${f}`);
111
107
  process.exit(1);
@@ -113,7 +109,7 @@ async function cmdSchemaApply(args) {
113
109
 
114
110
  const { files, blocks } = await loadFragments(fragmentsDir, filter);
115
111
  if (blocks.size === 0) {
116
- console.error('✗ Keine Models in den gewählten Fragmenten gefunden.');
112
+ console.error('✗ No models found in the selected fragments.');
117
113
  process.exit(1);
118
114
  }
119
115
 
@@ -122,14 +118,14 @@ async function cmdSchemaApply(args) {
122
118
  });
123
119
 
124
120
  if (result.added.length === 0) {
125
- console.log(`→ Nichts zu tun. Bereits vorhandene Models: ${result.skipped.join(', ')}`);
121
+ console.log(`→ Nothing to do. Models already present: ${result.skipped.join(', ')}`);
126
122
  return;
127
123
  }
128
124
 
129
125
  if (args['dry-run']) {
130
- console.log(`(--dry-run) Würde anfügen: ${result.added.join(', ')}`);
126
+ console.log(`(--dry-run) Would append: ${result.added.join(', ')}`);
131
127
  if (result.skipped.length) {
132
- console.log(`(--dry-run) Übersprungen (vorhanden): ${result.skipped.join(', ')}`);
128
+ console.log(`(--dry-run) Skipped (already present): ${result.skipped.join(', ')}`);
133
129
  }
134
130
  console.log('');
135
131
  console.log(result.schema.slice(schema.length));
@@ -137,25 +133,25 @@ async function cmdSchemaApply(args) {
137
133
  }
138
134
 
139
135
  await writeFile(schemaPath, result.schema, 'utf8');
140
- console.log(`✓ ${result.added.length} Model(s) angefügt: ${result.added.join(', ')}`);
136
+ console.log(`✓ Appended ${result.added.length} model(s): ${result.added.join(', ')}`);
141
137
  if (result.skipped.length) {
142
- console.log(`→ Übersprungen (vorhanden): ${result.skipped.join(', ')}`);
138
+ console.log(`→ Skipped (already present): ${result.skipped.join(', ')}`);
143
139
  }
144
140
  console.log('');
145
- console.log('Nächste Schritte:');
146
- console.log(' 1. schema.prisma reviewen (insbesondere FK-Pointer zu User/Tenant)');
147
- console.log(' 2. pnpm prisma migrate dev --name add_saas_platform');
141
+ console.log('Next steps:');
142
+ console.log(' 1. Review schema.prisma especially the FK pointers to User/Tenant');
143
+ console.log(' 2. pnpm prisma migrate dev --name add_saasicat');
148
144
  }
149
145
 
150
146
  const MISMATCH_LABELS = {
151
- type: 'Typ',
152
- optionality: 'Optionalität',
153
- list: 'Liste',
147
+ type: 'type',
148
+ optionality: 'optionality',
149
+ list: 'list',
154
150
  };
155
151
 
156
152
  function printCheckReport(report) {
157
153
  if (report.missingFields.length > 0) {
158
- console.log(`✗ Fehlende Felder (${report.missingFields.length}):`);
154
+ console.log(`✗ Missing fields (${report.missingFields.length}):`);
159
155
  for (const { model, field, type } of report.missingFields) {
160
156
  console.log(` ${`${model}.${field}`.padEnd(44)} ${type}`);
161
157
  }
@@ -163,7 +159,7 @@ function printCheckReport(report) {
163
159
  }
164
160
 
165
161
  if (report.missingEnumValues.length > 0) {
166
- console.log(`✗ Fehlende Enum-Werte (${report.missingEnumValues.length}):`);
162
+ console.log(`✗ Missing enum values (${report.missingEnumValues.length}):`);
167
163
  for (const entry of report.missingEnumValues) {
168
164
  console.log(` ${entry.enum}.${entry.value}`);
169
165
  }
@@ -171,11 +167,11 @@ function printCheckReport(report) {
171
167
  }
172
168
 
173
169
  if (report.fieldMismatches.length > 0) {
174
- console.log(`✗ Abweichende Felder (${report.fieldMismatches.length}):`);
170
+ console.log(`✗ Diverging fields (${report.fieldMismatches.length}):`);
175
171
  for (const { model, field, reason, expected, actual } of report.fieldMismatches) {
176
172
  const location = `${model}.${field}`.padEnd(44);
177
173
  console.log(
178
- ` ${location} [${MISMATCH_LABELS[reason]}] erwartet ${expected}, vorhanden ${actual}`,
174
+ ` ${location} [${MISMATCH_LABELS[reason]}] expected ${expected}, found ${actual}`,
179
175
  );
180
176
  }
181
177
  console.log('');
@@ -183,9 +179,9 @@ function printCheckReport(report) {
183
179
 
184
180
  const breaking = report.missingBlockAttributes.filter((a) => a.kind !== 'index');
185
181
  if (breaking.length > 0) {
186
- console.log(`✗ Fehlende Constraints (${breaking.length}):`);
182
+ console.log(`✗ Missing constraints (${breaking.length}):`);
187
183
  for (const { model, kind, expected, actual } of breaking) {
188
- const suffix = kind === 'map' ? ` — vorhanden: @@map("${actual}")` : '';
184
+ const suffix = kind === 'map' ? ` — found: @@map("${actual}")` : '';
189
185
  console.log(` ${model.padEnd(28)} ${expected}${suffix}`);
190
186
  }
191
187
  console.log('');
@@ -193,18 +189,18 @@ function printCheckReport(report) {
193
189
 
194
190
  const missingIndexes = report.missingBlockAttributes.filter((a) => a.kind === 'index');
195
191
  if (missingIndexes.length > 0) {
196
- console.log(`→ Fehlende Indizes (${missingIndexes.length}):`);
192
+ console.log(`→ Missing indexes (${missingIndexes.length}):`);
197
193
  for (const { model, expected } of missingIndexes) {
198
194
  console.log(` ${model.padEnd(28)} ${expected}`);
199
195
  }
200
- console.log(' Kein Fehlerkostet Query-Zeit, bricht aber nichts.');
196
+ console.log(' Not an error costs query time, but breaks nothing.');
201
197
  console.log('');
202
198
  }
203
199
 
204
200
  const absent = [...report.absentModels, ...report.absentEnums];
205
201
  if (absent.length > 0) {
206
- console.log(`→ Nicht übernommen (${absent.length}): ${absent.join(', ')}`);
207
- console.log(' Kein Fehlerdiese Fragmente nutzt die App nicht.');
202
+ console.log(`→ Not adopted (${absent.length}): ${absent.join(', ')}`);
203
+ console.log(' Not an error the app does not use these fragments.');
208
204
  console.log('');
209
205
  }
210
206
  }
@@ -216,7 +212,7 @@ async function cmdSchemaCheck(args) {
216
212
  const files = await selectFragmentFiles(fragmentsDir, filter);
217
213
 
218
214
  if (files.length === 0) {
219
- console.error('✗ Keine Fragmente ausgewählt.');
215
+ console.error('✗ No fragments selected.');
220
216
  process.exit(1);
221
217
  }
222
218
 
@@ -225,7 +221,7 @@ async function cmdSchemaCheck(args) {
225
221
  fragments.push(await readFile(join(fragmentsDir, file), 'utf8'));
226
222
  }
227
223
 
228
- console.log(`→ Prüfe ${schemaPath} gegen ${files.length} Fragment(e) aus @saasicat/spec`);
224
+ console.log(`→ Checking ${schemaPath} against ${files.length} fragment(s) from @saasicat/spec`);
229
225
  console.log('');
230
226
 
231
227
  const report = checkSchema(fragments.join('\n'), schema);
@@ -233,13 +229,13 @@ async function cmdSchemaCheck(args) {
233
229
 
234
230
  const checked = `${report.checkedModelCount} Model(s), ${report.checkedEnumCount} Enum(s)`;
235
231
  if (report.ok) {
236
- console.log(`✓ Kein Drift. ${checked} geprüft.`);
232
+ console.log(`✓ No drift. Checked ${checked}.`);
237
233
  return;
238
234
  }
239
235
 
240
- console.log(`✗ Drift gefunden (${checked} geprüft).`);
241
- console.log(' Fehlende Felder und Enum-Werte ergänzen, dann migrieren.');
242
- console.log(' Abweichende Felder prüfen: Plattform-Code liest sie mit dem Spec-Typ.');
236
+ console.log(`✗ Drift found (checked ${checked}).`);
237
+ console.log(' Add the missing fields and enum values, then migrate.');
238
+ console.log(' Review diverging fields: platform code reads them with the spec type.');
243
239
  process.exit(1);
244
240
  }
245
241
 
@@ -256,22 +252,24 @@ function runChild(cmd, args, opts = {}) {
256
252
 
257
253
  async function cmdSchemaMigrate(args) {
258
254
  if (!args.name) {
259
- console.error('✗ --name=<migration_name> ist Pflicht.');
255
+ console.error('✗ --name=<migration_name> is required.');
260
256
  process.exit(1);
261
257
  }
262
258
 
263
259
  console.log(
264
- `→ Schritt 1/2: saasicat schema apply ${args['fragments'] ? `--fragments=${args['fragments']}` : '--all'}`,
260
+ `→ Step 1/2: saasicat schema apply ${args['fragments'] ? `--fragments=${args['fragments']}` : '--all'}`,
265
261
  );
266
262
  await cmdSchemaApply({
267
263
  ...args,
268
264
  all: args['fragments'] ? undefined : true,
269
265
  });
270
266
 
271
- console.log(`→ Schritt 2/2: pnpm prisma migrate dev --name ${args.name}`);
267
+ console.log(
268
+ `→ Step 2/2: ${args['package-manager'] ?? 'pnpm'} prisma migrate dev --name ${args.name}`,
269
+ );
272
270
  const pmRunner = args['package-manager'] ?? 'pnpm';
273
271
  await runChild(pmRunner, ['prisma', 'migrate', 'dev', '--name', args.name]);
274
- console.log('✓ schema migrate erfolgreich.');
272
+ console.log('✓ schema migrate succeeded.');
275
273
  }
276
274
 
277
275
  async function main() {
@@ -289,18 +287,20 @@ async function main() {
289
287
  console.log('Usage: saasicat <command> [...args]');
290
288
  console.log('');
291
289
  console.log('Commands:');
292
- console.log(' schema apply --all alle Plattform-Models einfügen');
293
- console.log(' schema apply --fragments=01,02 nur diese Fragmente einfügen');
294
- console.log(' schema apply --dry-run nur Diff ausgeben');
295
- console.log(' schema check Drift gegen @saasicat/spec melden');
296
- console.log(' schema check --fragments=01,02 nur diese Fragmente prüfen');
290
+ console.log(' schema apply --all insert all platform models');
291
+ console.log(' schema apply --fragments=01,02 insert only these fragments');
292
+ console.log(' schema apply --dry-run print the diff only');
293
+ console.log(
294
+ ' schema check report drift against @saasicat/spec',
295
+ );
296
+ console.log(' schema check --fragments=01,02 check only these fragments');
297
297
  console.log(' schema migrate --name=<name> apply --all + prisma migrate dev');
298
298
  console.log('');
299
299
  console.log('Optional --prisma-schema=PATH (default prisma/schema.prisma).');
300
300
  console.log('Optional --package-manager=pnpm|npm|yarn (default pnpm).');
301
301
  return;
302
302
  }
303
- console.error(`Unbekannter Befehl: ${cmd} ${sub ?? ''}`);
303
+ console.error(`Unknown command: ${cmd} ${sub ?? ''}`);
304
304
  process.exit(1);
305
305
  }
306
306
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasicat/cli",
3
- "version": "0.21.0",
3
+ "version": "0.22.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",
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "qrcode-terminal": "^0.12.0",
30
- "@saasicat/nest": "^0.21.0",
31
- "@saasicat/spec": "^0.21.0",
32
- "@saasicat/types": "^0.21.0"
30
+ "@saasicat/nest": "^0.22.1",
31
+ "@saasicat/spec": "^0.22.1",
32
+ "@saasicat/types": "^0.22.1"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@nestjs/common": "^11.0.0",