@saasicat/cli 1.0.0-rc.19 → 1.0.0-rc.20
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 +25 -2
- package/dist/.build-stamp +1 -1
- package/dist/index.cjs +16 -13
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +16 -13
- package/package.json +4 -4
package/bin/saasicat.js
CHANGED
|
@@ -320,7 +320,16 @@ function printCheckReport(report) {
|
|
|
320
320
|
console.log('');
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
|
|
323
|
+
// An enum a reported field names is not a fragment the app went without: the
|
|
324
|
+
// field above it is the drift, and copying the enum is what fixes both.
|
|
325
|
+
// Listing it as "not an error" in the same run would print the contradiction
|
|
326
|
+
// this check exists to avoid — which is exactly what it used to do for a
|
|
327
|
+
// relation pointing at an unadopted model.
|
|
328
|
+
const named = new Set(report.missingFields.map((field) => field.type));
|
|
329
|
+
const absent = [
|
|
330
|
+
...report.absentModels,
|
|
331
|
+
...report.absentEnums.filter((name) => !named.has(name)),
|
|
332
|
+
];
|
|
324
333
|
if (absent.length > 0) {
|
|
325
334
|
console.log(`→ Not adopted (${absent.length}): ${absent.join(', ')}`);
|
|
326
335
|
console.log(' Not an error — the app does not use these fragments.');
|
|
@@ -347,7 +356,21 @@ async function cmdSchemaCheck(args) {
|
|
|
347
356
|
console.log(`→ Checking ${schemaPath} against ${files.length} fragment(s) from @saasicat/spec`);
|
|
348
357
|
console.log('');
|
|
349
358
|
|
|
350
|
-
|
|
359
|
+
// Every model the shipped fragments declare, not only the selected ones: a
|
|
360
|
+
// relation from a selected fragment can point at a model in one that was
|
|
361
|
+
// left out, and without this the narrowed run would report that field as
|
|
362
|
+
// missing — the very contradiction the full run stopped producing. Without
|
|
363
|
+
// a filter the files in hand are already all of them.
|
|
364
|
+
const allFragments = filter
|
|
365
|
+
? await Promise.all(
|
|
366
|
+
(await selectFragmentFiles(fragmentsDir, null)).map((file) =>
|
|
367
|
+
readFile(join(fragmentsDir, file), 'utf8'),
|
|
368
|
+
),
|
|
369
|
+
)
|
|
370
|
+
: fragments;
|
|
371
|
+
const knownModels = new Set(extractModelNames(allFragments.join('\n')));
|
|
372
|
+
|
|
373
|
+
const report = checkSchema(fragments.join('\n'), schema, knownModels);
|
|
351
374
|
printCheckReport(report);
|
|
352
375
|
|
|
353
376
|
const checked = `${report.checkedModelCount} Model(s), ${report.checkedEnumCount} Enum(s)`;
|
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
89cdc61db4ad8f0d8bc18287c0d1fe76e1a7e9410f0797a525db4c9b1bbdc3ab
|
package/dist/index.cjs
CHANGED
|
@@ -1593,10 +1593,11 @@ function isDocumentedTypeSubstitution(spec, app, appEnums) {
|
|
|
1593
1593
|
return spec.type === "String" && appEnums.has(app.type);
|
|
1594
1594
|
}
|
|
1595
1595
|
__name(isDocumentedTypeSubstitution, "isDocumentedTypeSubstitution");
|
|
1596
|
-
function compareFields(model, specFields, appFields, appEnums, missingFields, fieldMismatches) {
|
|
1596
|
+
function compareFields(model, specFields, appFields, appEnums, notAdopted, missingFields, fieldMismatches) {
|
|
1597
1597
|
for (const [name, spec] of specFields) {
|
|
1598
1598
|
const app = appFields.get(name);
|
|
1599
1599
|
if (!app) {
|
|
1600
|
+
if (notAdopted.has(spec.type)) continue;
|
|
1600
1601
|
missingFields.push({
|
|
1601
1602
|
model,
|
|
1602
1603
|
field: name,
|
|
@@ -1675,20 +1676,26 @@ function cascadesFromTenant(model, block) {
|
|
|
1675
1676
|
return found;
|
|
1676
1677
|
}
|
|
1677
1678
|
__name(cascadesFromTenant, "cascadesFromTenant");
|
|
1678
|
-
function checkSchema(specSchema, appSchema) {
|
|
1679
|
+
function checkSchema(specSchema, appSchema, knownModels) {
|
|
1679
1680
|
const spec = parseSchema(specSchema);
|
|
1680
1681
|
const app = parseSchema(appSchema);
|
|
1681
|
-
const absentModels = [];
|
|
1682
1682
|
const missingFields = [];
|
|
1683
1683
|
const fieldMismatches = [];
|
|
1684
1684
|
const missingBlockAttributes = [];
|
|
1685
|
+
const absentModels = [
|
|
1686
|
+
...spec.models.keys()
|
|
1687
|
+
].filter((name) => !app.models.has(name));
|
|
1688
|
+
const absentEnums = [
|
|
1689
|
+
...spec.enums.keys()
|
|
1690
|
+
].filter((name) => !app.enums.has(name));
|
|
1691
|
+
const shipped = knownModels ?? new Set(spec.models.keys());
|
|
1692
|
+
const notAdopted = new Set([
|
|
1693
|
+
...shipped
|
|
1694
|
+
].filter((name) => !app.models.has(name)));
|
|
1685
1695
|
for (const [model, specFields] of spec.models) {
|
|
1686
1696
|
const appFields = app.models.get(model);
|
|
1687
|
-
if (!appFields)
|
|
1688
|
-
|
|
1689
|
-
continue;
|
|
1690
|
-
}
|
|
1691
|
-
compareFields(model, specFields, appFields, app.enums, missingFields, fieldMismatches);
|
|
1697
|
+
if (!appFields) continue;
|
|
1698
|
+
compareFields(model, specFields, appFields, app.enums, notAdopted, missingFields, fieldMismatches);
|
|
1692
1699
|
const specAttrs = spec.modelAttributes.get(model);
|
|
1693
1700
|
const appAttrs = app.modelAttributes.get(model);
|
|
1694
1701
|
if (specAttrs && appAttrs) {
|
|
@@ -1700,14 +1707,10 @@ function checkSchema(specSchema, appSchema) {
|
|
|
1700
1707
|
const block = appBlocks.get(model);
|
|
1701
1708
|
return block ? cascadesFromTenant(model, block) : [];
|
|
1702
1709
|
});
|
|
1703
|
-
const absentEnums = [];
|
|
1704
1710
|
const missingEnumValues = [];
|
|
1705
1711
|
for (const [name, specValues] of spec.enums) {
|
|
1706
1712
|
const appValues = app.enums.get(name);
|
|
1707
|
-
if (!appValues)
|
|
1708
|
-
absentEnums.push(name);
|
|
1709
|
-
continue;
|
|
1710
|
-
}
|
|
1713
|
+
if (!appValues) continue;
|
|
1711
1714
|
for (const value of specValues) {
|
|
1712
1715
|
if (!appValues.includes(value)) {
|
|
1713
1716
|
missingEnumValues.push({
|
package/dist/index.d.cts
CHANGED
|
@@ -562,8 +562,12 @@ declare function modelsKeptPastTheTenant(specSchema: string): string[];
|
|
|
562
562
|
/**
|
|
563
563
|
* Compares a consumer schema against the canonical fragments. `specSchema` is
|
|
564
564
|
* the concatenation of the fragments the check should cover.
|
|
565
|
+
*
|
|
566
|
+
* @param knownModels Every model the shipped fragments declare, where the spec
|
|
567
|
+
* passed in is a narrowed selection of them (`schema check --fragments=…`).
|
|
568
|
+
* Omitted, the spec is taken to be the whole of it.
|
|
565
569
|
*/
|
|
566
|
-
declare function checkSchema(specSchema: string, appSchema: string): SchemaCheckReport;
|
|
570
|
+
declare function checkSchema(specSchema: string, appSchema: string, knownModels?: ReadonlySet<string>): SchemaCheckReport;
|
|
567
571
|
|
|
568
572
|
/** Marks the block this tool appends, and lets it recognise its own work. */
|
|
569
573
|
declare const CONSTRAINTS_MARKER = "-- saasicat:constraints";
|
package/dist/index.d.ts
CHANGED
|
@@ -562,8 +562,12 @@ declare function modelsKeptPastTheTenant(specSchema: string): string[];
|
|
|
562
562
|
/**
|
|
563
563
|
* Compares a consumer schema against the canonical fragments. `specSchema` is
|
|
564
564
|
* the concatenation of the fragments the check should cover.
|
|
565
|
+
*
|
|
566
|
+
* @param knownModels Every model the shipped fragments declare, where the spec
|
|
567
|
+
* passed in is a narrowed selection of them (`schema check --fragments=…`).
|
|
568
|
+
* Omitted, the spec is taken to be the whole of it.
|
|
565
569
|
*/
|
|
566
|
-
declare function checkSchema(specSchema: string, appSchema: string): SchemaCheckReport;
|
|
570
|
+
declare function checkSchema(specSchema: string, appSchema: string, knownModels?: ReadonlySet<string>): SchemaCheckReport;
|
|
567
571
|
|
|
568
572
|
/** Marks the block this tool appends, and lets it recognise its own work. */
|
|
569
573
|
declare const CONSTRAINTS_MARKER = "-- saasicat:constraints";
|
package/dist/index.js
CHANGED
|
@@ -1457,10 +1457,11 @@ function isDocumentedTypeSubstitution(spec, app, appEnums) {
|
|
|
1457
1457
|
return spec.type === "String" && appEnums.has(app.type);
|
|
1458
1458
|
}
|
|
1459
1459
|
__name(isDocumentedTypeSubstitution, "isDocumentedTypeSubstitution");
|
|
1460
|
-
function compareFields(model, specFields, appFields, appEnums, missingFields, fieldMismatches) {
|
|
1460
|
+
function compareFields(model, specFields, appFields, appEnums, notAdopted, missingFields, fieldMismatches) {
|
|
1461
1461
|
for (const [name, spec] of specFields) {
|
|
1462
1462
|
const app = appFields.get(name);
|
|
1463
1463
|
if (!app) {
|
|
1464
|
+
if (notAdopted.has(spec.type)) continue;
|
|
1464
1465
|
missingFields.push({
|
|
1465
1466
|
model,
|
|
1466
1467
|
field: name,
|
|
@@ -1539,20 +1540,26 @@ function cascadesFromTenant(model, block) {
|
|
|
1539
1540
|
return found;
|
|
1540
1541
|
}
|
|
1541
1542
|
__name(cascadesFromTenant, "cascadesFromTenant");
|
|
1542
|
-
function checkSchema(specSchema, appSchema) {
|
|
1543
|
+
function checkSchema(specSchema, appSchema, knownModels) {
|
|
1543
1544
|
const spec = parseSchema(specSchema);
|
|
1544
1545
|
const app = parseSchema(appSchema);
|
|
1545
|
-
const absentModels = [];
|
|
1546
1546
|
const missingFields = [];
|
|
1547
1547
|
const fieldMismatches = [];
|
|
1548
1548
|
const missingBlockAttributes = [];
|
|
1549
|
+
const absentModels = [
|
|
1550
|
+
...spec.models.keys()
|
|
1551
|
+
].filter((name) => !app.models.has(name));
|
|
1552
|
+
const absentEnums = [
|
|
1553
|
+
...spec.enums.keys()
|
|
1554
|
+
].filter((name) => !app.enums.has(name));
|
|
1555
|
+
const shipped = knownModels ?? new Set(spec.models.keys());
|
|
1556
|
+
const notAdopted = new Set([
|
|
1557
|
+
...shipped
|
|
1558
|
+
].filter((name) => !app.models.has(name)));
|
|
1549
1559
|
for (const [model, specFields] of spec.models) {
|
|
1550
1560
|
const appFields = app.models.get(model);
|
|
1551
|
-
if (!appFields)
|
|
1552
|
-
|
|
1553
|
-
continue;
|
|
1554
|
-
}
|
|
1555
|
-
compareFields(model, specFields, appFields, app.enums, missingFields, fieldMismatches);
|
|
1561
|
+
if (!appFields) continue;
|
|
1562
|
+
compareFields(model, specFields, appFields, app.enums, notAdopted, missingFields, fieldMismatches);
|
|
1556
1563
|
const specAttrs = spec.modelAttributes.get(model);
|
|
1557
1564
|
const appAttrs = app.modelAttributes.get(model);
|
|
1558
1565
|
if (specAttrs && appAttrs) {
|
|
@@ -1564,14 +1571,10 @@ function checkSchema(specSchema, appSchema) {
|
|
|
1564
1571
|
const block = appBlocks.get(model);
|
|
1565
1572
|
return block ? cascadesFromTenant(model, block) : [];
|
|
1566
1573
|
});
|
|
1567
|
-
const absentEnums = [];
|
|
1568
1574
|
const missingEnumValues = [];
|
|
1569
1575
|
for (const [name, specValues] of spec.enums) {
|
|
1570
1576
|
const appValues = app.enums.get(name);
|
|
1571
|
-
if (!appValues)
|
|
1572
|
-
absentEnums.push(name);
|
|
1573
|
-
continue;
|
|
1574
|
-
}
|
|
1577
|
+
if (!appValues) continue;
|
|
1575
1578
|
for (const value of specValues) {
|
|
1576
1579
|
if (!appValues.includes(value)) {
|
|
1577
1580
|
missingEnumValues.push({
|
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.20",
|
|
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",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"qrcode-terminal": "^0.12.0",
|
|
33
|
-
"@saasicat/nest": "^1.0.0-rc.
|
|
34
|
-
"@saasicat/spec": "^1.0.0-rc.
|
|
35
|
-
"@saasicat/core": "^1.0.0-rc.
|
|
33
|
+
"@saasicat/nest": "^1.0.0-rc.20",
|
|
34
|
+
"@saasicat/spec": "^1.0.0-rc.20",
|
|
35
|
+
"@saasicat/core": "^1.0.0-rc.20"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@nestjs/common": "^11.0.0",
|