@saasicat/cli 0.12.0 → 0.13.0
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 +13 -0
- package/dist/index.cjs +49 -10
- package/dist/index.d.cts +30 -4
- package/dist/index.d.ts +30 -4
- package/dist/index.js +46 -9
- package/package.json +4 -4
package/bin/saasicat.js
CHANGED
|
@@ -53,6 +53,19 @@ function resolveFragmentsDir() {
|
|
|
53
53
|
async function selectFragmentFiles(dir, filter) {
|
|
54
54
|
const files = (await readdir(dir)).filter((f) => f.endsWith('.prisma')).sort();
|
|
55
55
|
if (!filter) return files;
|
|
56
|
+
|
|
57
|
+
// A selector that matches nothing is a typo, and silently dropping it would
|
|
58
|
+
// leave part of the requested schema surface unchecked while the command
|
|
59
|
+
// still exits 0 — the worst outcome for something used as a CI gate.
|
|
60
|
+
const available = new Set(files.map((f) => f.split('-')[0]));
|
|
61
|
+
const unknown = filter.filter((prefix) => !available.has(prefix));
|
|
62
|
+
if (unknown.length > 0) {
|
|
63
|
+
console.error(
|
|
64
|
+
`✗ Unbekannte Fragmente: ${unknown.join(', ')}. ` +
|
|
65
|
+
`Verfügbar: ${[...available].join(', ')}`,
|
|
66
|
+
);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
56
69
|
return files.filter((f) => filter.includes(f.split('-')[0]));
|
|
57
70
|
}
|
|
58
71
|
|
package/dist/index.cjs
CHANGED
|
@@ -67,6 +67,7 @@ __export(index_exports, {
|
|
|
67
67
|
UserPortDoctorCheck: () => UserPortDoctorCheck,
|
|
68
68
|
WhoAmIFlow: () => WhoAmIFlow,
|
|
69
69
|
applyFragmentBlocks: () => applyFragmentBlocks,
|
|
70
|
+
blankStringLiterals: () => blankStringLiterals,
|
|
70
71
|
blockBodyLines: () => blockBodyLines,
|
|
71
72
|
breaksContract: () => breaksContract,
|
|
72
73
|
checkSchema: () => checkSchema,
|
|
@@ -78,7 +79,8 @@ __export(index_exports, {
|
|
|
78
79
|
parseEnumValues: () => parseEnumValues,
|
|
79
80
|
parseFields: () => parseFields,
|
|
80
81
|
parseSchema: () => parseSchema,
|
|
81
|
-
stripLineComment: () => stripLineComment
|
|
82
|
+
stripLineComment: () => stripLineComment,
|
|
83
|
+
structuralOnly: () => structuralOnly
|
|
82
84
|
});
|
|
83
85
|
module.exports = __toCommonJS(index_exports);
|
|
84
86
|
|
|
@@ -1111,7 +1113,7 @@ var PLATFORM_DOCTOR_CHECK_PROVIDERS = [
|
|
|
1111
1113
|
|
|
1112
1114
|
// src/prisma-blocks.ts
|
|
1113
1115
|
function stripLineComment(line) {
|
|
1114
|
-
const commentStart = line.indexOf("//");
|
|
1116
|
+
const commentStart = blankStringLiterals(line).indexOf("//");
|
|
1115
1117
|
return commentStart === -1 ? line : line.slice(0, commentStart);
|
|
1116
1118
|
}
|
|
1117
1119
|
__name(stripLineComment, "stripLineComment");
|
|
@@ -1119,11 +1121,45 @@ function declarationPattern(keyword) {
|
|
|
1119
1121
|
return new RegExp(`^\\s*${keyword}\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\{`);
|
|
1120
1122
|
}
|
|
1121
1123
|
__name(declarationPattern, "declarationPattern");
|
|
1124
|
+
function blankStringLiterals(line) {
|
|
1125
|
+
let out = "";
|
|
1126
|
+
let inString = false;
|
|
1127
|
+
let escaped = false;
|
|
1128
|
+
for (const char of line) {
|
|
1129
|
+
if (!inString) {
|
|
1130
|
+
out += char;
|
|
1131
|
+
if (char === '"') inString = true;
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
if (escaped) {
|
|
1135
|
+
out += " ";
|
|
1136
|
+
escaped = false;
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (char === "\\") {
|
|
1140
|
+
out += " ";
|
|
1141
|
+
escaped = true;
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (char === '"') {
|
|
1145
|
+
out += '"';
|
|
1146
|
+
inString = false;
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
out += " ";
|
|
1150
|
+
}
|
|
1151
|
+
return out;
|
|
1152
|
+
}
|
|
1153
|
+
__name(blankStringLiterals, "blankStringLiterals");
|
|
1154
|
+
function structuralOnly(line) {
|
|
1155
|
+
return stripLineComment(blankStringLiterals(line));
|
|
1156
|
+
}
|
|
1157
|
+
__name(structuralOnly, "structuralOnly");
|
|
1122
1158
|
function extractBlockNames(schema, keyword) {
|
|
1123
1159
|
const pattern = declarationPattern(keyword);
|
|
1124
1160
|
const names = [];
|
|
1125
1161
|
for (const line of schema.split("\n")) {
|
|
1126
|
-
const match =
|
|
1162
|
+
const match = structuralOnly(line).match(pattern);
|
|
1127
1163
|
if (match) names.push(match[1]);
|
|
1128
1164
|
}
|
|
1129
1165
|
return names;
|
|
@@ -1134,7 +1170,7 @@ function extractBlocks(schema, keyword) {
|
|
|
1134
1170
|
const blocks = /* @__PURE__ */ new Map();
|
|
1135
1171
|
let current = null;
|
|
1136
1172
|
for (const rawLine of schema.split("\n")) {
|
|
1137
|
-
const stripped =
|
|
1173
|
+
const stripped = structuralOnly(rawLine);
|
|
1138
1174
|
const openCount = (stripped.match(/\{/g) ?? []).length;
|
|
1139
1175
|
const closeCount = (stripped.match(/\}/g) ?? []).length;
|
|
1140
1176
|
if (!current) {
|
|
@@ -1163,7 +1199,7 @@ function blockBodyLines(block) {
|
|
|
1163
1199
|
const bodyStart = block.indexOf("{");
|
|
1164
1200
|
const bodyEnd = block.lastIndexOf("}");
|
|
1165
1201
|
if (bodyStart === -1 || bodyEnd <= bodyStart) return [];
|
|
1166
|
-
return block.slice(bodyStart + 1, bodyEnd).split("\n").map((line) =>
|
|
1202
|
+
return block.slice(bodyStart + 1, bodyEnd).split("\n").map((line) => structuralOnly(line).trim()).filter((line) => line.length > 0 && !line.startsWith("@@"));
|
|
1167
1203
|
}
|
|
1168
1204
|
__name(blockBodyLines, "blockBodyLines");
|
|
1169
1205
|
|
|
@@ -1248,17 +1284,18 @@ function parseEnumValues(block) {
|
|
|
1248
1284
|
}
|
|
1249
1285
|
__name(parseEnumValues, "parseEnumValues");
|
|
1250
1286
|
function attributeFieldLists(block, attribute) {
|
|
1251
|
-
const pattern = new RegExp(`@@${attribute}\\(([
|
|
1287
|
+
const pattern = new RegExp(`@@${attribute}\\(\\s*(\\[[^\\]]*\\])`, "g");
|
|
1252
1288
|
return new Set([
|
|
1253
1289
|
...block.matchAll(pattern)
|
|
1254
1290
|
].map((match) => match[1].replace(/\s+/g, "")));
|
|
1255
1291
|
}
|
|
1256
1292
|
__name(attributeFieldLists, "attributeFieldLists");
|
|
1257
1293
|
function parseBlockAttributes(name, block) {
|
|
1294
|
+
const active = block.split("\n").map(stripLineComment).join("\n");
|
|
1258
1295
|
return {
|
|
1259
|
-
indexes: attributeFieldLists(
|
|
1260
|
-
uniques: attributeFieldLists(
|
|
1261
|
-
map:
|
|
1296
|
+
indexes: attributeFieldLists(active, "index"),
|
|
1297
|
+
uniques: attributeFieldLists(active, "unique"),
|
|
1298
|
+
map: active.match(/@@map\("([^"]+)"\)/)?.[1] ?? name
|
|
1262
1299
|
};
|
|
1263
1300
|
}
|
|
1264
1301
|
__name(parseBlockAttributes, "parseBlockAttributes");
|
|
@@ -2485,6 +2522,7 @@ UserCommands = _ts_decorate14([
|
|
|
2485
2522
|
UserPortDoctorCheck,
|
|
2486
2523
|
WhoAmIFlow,
|
|
2487
2524
|
applyFragmentBlocks,
|
|
2525
|
+
blankStringLiterals,
|
|
2488
2526
|
blockBodyLines,
|
|
2489
2527
|
breaksContract,
|
|
2490
2528
|
checkSchema,
|
|
@@ -2496,5 +2534,6 @@ UserCommands = _ts_decorate14([
|
|
|
2496
2534
|
parseEnumValues,
|
|
2497
2535
|
parseFields,
|
|
2498
2536
|
parseSchema,
|
|
2499
|
-
stripLineComment
|
|
2537
|
+
stripLineComment,
|
|
2538
|
+
structuralOnly
|
|
2500
2539
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -327,11 +327,32 @@ declare class AdminManifestDoctorCheck implements DoctorCheck {
|
|
|
327
327
|
declare const PLATFORM_DOCTOR_CHECK_PROVIDERS: Array<Type<DoctorCheck>>;
|
|
328
328
|
|
|
329
329
|
/**
|
|
330
|
-
* Cuts off a trailing `//` comment
|
|
331
|
-
*
|
|
332
|
-
*
|
|
330
|
+
* Cuts off a trailing `//` comment, ignoring `//` inside string literals — a
|
|
331
|
+
* `@default("http://x")` must survive intact. The position is found on a
|
|
332
|
+
* string-blanked copy, but the original line is what gets cut, so quoted
|
|
333
|
+
* content is preserved.
|
|
334
|
+
*
|
|
335
|
+
* Deliberately index-based instead of `replace(/\/\/.*$/, '')`: the regex
|
|
336
|
+
* backtracks quadratically on lines with many single slashes.
|
|
333
337
|
*/
|
|
334
338
|
declare function stripLineComment(line: string): string;
|
|
339
|
+
/**
|
|
340
|
+
* Blanks the contents of double-quoted strings, keeping the quotes and the
|
|
341
|
+
* line length so column positions stay put. Brace counting must not see
|
|
342
|
+
* `@default("}")` as the end of a model — it would close the block early and
|
|
343
|
+
* every field below it would look missing.
|
|
344
|
+
*
|
|
345
|
+
* A linear scan rather than `"(?:[^"\\]|\\.)*"`: that pattern backtracks
|
|
346
|
+
* quadratically on many repeated `\"` (CodeQL `js/polynomial-redos`), and this
|
|
347
|
+
* runs over every line of a consumer schema.
|
|
348
|
+
*/
|
|
349
|
+
declare function blankStringLiterals(line: string): string;
|
|
350
|
+
/**
|
|
351
|
+
* Comment- and string-safe view of a line, for structural scanning. Strings are
|
|
352
|
+
* blanked first: `@default("http://x")` contains `//`, and stripping comments
|
|
353
|
+
* before that would cut the line in half.
|
|
354
|
+
*/
|
|
355
|
+
declare function structuralOnly(line: string): string;
|
|
335
356
|
/** Returns the names of all top-level `<keyword> X { … }` blocks. */
|
|
336
357
|
declare function extractBlockNames(schema: string, keyword: string): string[];
|
|
337
358
|
/**
|
|
@@ -454,6 +475,11 @@ declare function parseEnumValues(block: string): string[];
|
|
|
454
475
|
* Parses the block-level attributes of a `model`. Comparing these is what
|
|
455
476
|
* catches a missing index or unique constraint — differences the field-level
|
|
456
477
|
* comparison is blind to.
|
|
478
|
+
*
|
|
479
|
+
* The block is reduced to its structural content first. A consumer who
|
|
480
|
+
* comments out `// @@unique([tenantId])` has removed the constraint, and
|
|
481
|
+
* reading the raw text would count it as present — passing the very check that
|
|
482
|
+
* exists to catch its absence.
|
|
457
483
|
*/
|
|
458
484
|
declare function parseBlockAttributes(name: string, block: string): BlockAttributes;
|
|
459
485
|
/** Parses every `model` and `enum` declaration of a Prisma schema. */
|
|
@@ -661,4 +687,4 @@ declare class UserCommands extends CommandRunner {
|
|
|
661
687
|
parsePassword(val: string): string;
|
|
662
688
|
}
|
|
663
689
|
|
|
664
|
-
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type FieldMismatch, type FieldMismatchReason, type FieldSignature, 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, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, PlanCatalogDoctorCheck, type SchemaCheckReport, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, applyFragmentBlocks, blockBodyLines, breaksContract, checkSchema, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, parseBlockAttributes, parseEnumValues, parseFields, parseSchema, stripLineComment };
|
|
690
|
+
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type FieldMismatch, type FieldMismatchReason, type FieldSignature, 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, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, PlanCatalogDoctorCheck, type SchemaCheckReport, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, applyFragmentBlocks, blankStringLiterals, blockBodyLines, breaksContract, checkSchema, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, parseBlockAttributes, parseEnumValues, parseFields, parseSchema, stripLineComment, structuralOnly };
|
package/dist/index.d.ts
CHANGED
|
@@ -327,11 +327,32 @@ declare class AdminManifestDoctorCheck implements DoctorCheck {
|
|
|
327
327
|
declare const PLATFORM_DOCTOR_CHECK_PROVIDERS: Array<Type<DoctorCheck>>;
|
|
328
328
|
|
|
329
329
|
/**
|
|
330
|
-
* Cuts off a trailing `//` comment
|
|
331
|
-
*
|
|
332
|
-
*
|
|
330
|
+
* Cuts off a trailing `//` comment, ignoring `//` inside string literals — a
|
|
331
|
+
* `@default("http://x")` must survive intact. The position is found on a
|
|
332
|
+
* string-blanked copy, but the original line is what gets cut, so quoted
|
|
333
|
+
* content is preserved.
|
|
334
|
+
*
|
|
335
|
+
* Deliberately index-based instead of `replace(/\/\/.*$/, '')`: the regex
|
|
336
|
+
* backtracks quadratically on lines with many single slashes.
|
|
333
337
|
*/
|
|
334
338
|
declare function stripLineComment(line: string): string;
|
|
339
|
+
/**
|
|
340
|
+
* Blanks the contents of double-quoted strings, keeping the quotes and the
|
|
341
|
+
* line length so column positions stay put. Brace counting must not see
|
|
342
|
+
* `@default("}")` as the end of a model — it would close the block early and
|
|
343
|
+
* every field below it would look missing.
|
|
344
|
+
*
|
|
345
|
+
* A linear scan rather than `"(?:[^"\\]|\\.)*"`: that pattern backtracks
|
|
346
|
+
* quadratically on many repeated `\"` (CodeQL `js/polynomial-redos`), and this
|
|
347
|
+
* runs over every line of a consumer schema.
|
|
348
|
+
*/
|
|
349
|
+
declare function blankStringLiterals(line: string): string;
|
|
350
|
+
/**
|
|
351
|
+
* Comment- and string-safe view of a line, for structural scanning. Strings are
|
|
352
|
+
* blanked first: `@default("http://x")` contains `//`, and stripping comments
|
|
353
|
+
* before that would cut the line in half.
|
|
354
|
+
*/
|
|
355
|
+
declare function structuralOnly(line: string): string;
|
|
335
356
|
/** Returns the names of all top-level `<keyword> X { … }` blocks. */
|
|
336
357
|
declare function extractBlockNames(schema: string, keyword: string): string[];
|
|
337
358
|
/**
|
|
@@ -454,6 +475,11 @@ declare function parseEnumValues(block: string): string[];
|
|
|
454
475
|
* Parses the block-level attributes of a `model`. Comparing these is what
|
|
455
476
|
* catches a missing index or unique constraint — differences the field-level
|
|
456
477
|
* comparison is blind to.
|
|
478
|
+
*
|
|
479
|
+
* The block is reduced to its structural content first. A consumer who
|
|
480
|
+
* comments out `// @@unique([tenantId])` has removed the constraint, and
|
|
481
|
+
* reading the raw text would count it as present — passing the very check that
|
|
482
|
+
* exists to catch its absence.
|
|
457
483
|
*/
|
|
458
484
|
declare function parseBlockAttributes(name: string, block: string): BlockAttributes;
|
|
459
485
|
/** Parses every `model` and `enum` declaration of a Prisma schema. */
|
|
@@ -661,4 +687,4 @@ declare class UserCommands extends CommandRunner {
|
|
|
661
687
|
parsePassword(val: string): string;
|
|
662
688
|
}
|
|
663
689
|
|
|
664
|
-
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type FieldMismatch, type FieldMismatchReason, type FieldSignature, 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, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, PlanCatalogDoctorCheck, type SchemaCheckReport, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, applyFragmentBlocks, blockBodyLines, breaksContract, checkSchema, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, parseBlockAttributes, parseEnumValues, parseFields, parseSchema, stripLineComment };
|
|
690
|
+
export { AUDIT_QUERY_PORT_TOKEN, AdminCommands, AdminManifestDoctorCheck, AdminMfaSetupCommand, AdminWhoamiCommand, type ApplyResult, AuditCommands, AuditTailCommand, AuditTailFlow, type AuditTailOptions, type BlockAttributeKind, type BlockAttributes, CLI_CONTEXT_CONFIG_TOKEN, type CheckSeverity, type CliContextConfig, CliContextModule, type CliContextModuleOptions, CliContextService, CliError, type CliIdentity, DEFAULT_MANIFEST_CHECKS, DOCTOR_CHECKS_TOKEN, DiscoveryCommands, DiscoveryScanCommand, DiscoverySnapshotDoctorCheck, type DoctorCheck, type DoctorCheckResult, DoctorCommands, DoctorFlow, type DoctorReport, type FieldMismatch, type FieldMismatchReason, type FieldSignature, 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, PLATFORM_DOCTOR_CHECK_PROVIDERS, type ParsedSchema, PlanCatalogDoctorCheck, type SchemaCheckReport, USER_MANAGEMENT_PORT_TOKEN, USER_PORT_TOKEN, UserCommands, UserPortDoctorCheck, WhoAmIFlow, type WhoAmIResult, applyFragmentBlocks, blankStringLiterals, blockBodyLines, breaksContract, checkSchema, extractBlockNames, extractBlocks, extractModelBlocks, extractModelNames, parseBlockAttributes, parseEnumValues, parseFields, parseSchema, stripLineComment, structuralOnly };
|
package/dist/index.js
CHANGED
|
@@ -1030,7 +1030,7 @@ var PLATFORM_DOCTOR_CHECK_PROVIDERS = [
|
|
|
1030
1030
|
|
|
1031
1031
|
// src/prisma-blocks.ts
|
|
1032
1032
|
function stripLineComment(line) {
|
|
1033
|
-
const commentStart = line.indexOf("//");
|
|
1033
|
+
const commentStart = blankStringLiterals(line).indexOf("//");
|
|
1034
1034
|
return commentStart === -1 ? line : line.slice(0, commentStart);
|
|
1035
1035
|
}
|
|
1036
1036
|
__name(stripLineComment, "stripLineComment");
|
|
@@ -1038,11 +1038,45 @@ function declarationPattern(keyword) {
|
|
|
1038
1038
|
return new RegExp(`^\\s*${keyword}\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\{`);
|
|
1039
1039
|
}
|
|
1040
1040
|
__name(declarationPattern, "declarationPattern");
|
|
1041
|
+
function blankStringLiterals(line) {
|
|
1042
|
+
let out = "";
|
|
1043
|
+
let inString = false;
|
|
1044
|
+
let escaped = false;
|
|
1045
|
+
for (const char of line) {
|
|
1046
|
+
if (!inString) {
|
|
1047
|
+
out += char;
|
|
1048
|
+
if (char === '"') inString = true;
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
if (escaped) {
|
|
1052
|
+
out += " ";
|
|
1053
|
+
escaped = false;
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
if (char === "\\") {
|
|
1057
|
+
out += " ";
|
|
1058
|
+
escaped = true;
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
if (char === '"') {
|
|
1062
|
+
out += '"';
|
|
1063
|
+
inString = false;
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
out += " ";
|
|
1067
|
+
}
|
|
1068
|
+
return out;
|
|
1069
|
+
}
|
|
1070
|
+
__name(blankStringLiterals, "blankStringLiterals");
|
|
1071
|
+
function structuralOnly(line) {
|
|
1072
|
+
return stripLineComment(blankStringLiterals(line));
|
|
1073
|
+
}
|
|
1074
|
+
__name(structuralOnly, "structuralOnly");
|
|
1041
1075
|
function extractBlockNames(schema, keyword) {
|
|
1042
1076
|
const pattern = declarationPattern(keyword);
|
|
1043
1077
|
const names = [];
|
|
1044
1078
|
for (const line of schema.split("\n")) {
|
|
1045
|
-
const match =
|
|
1079
|
+
const match = structuralOnly(line).match(pattern);
|
|
1046
1080
|
if (match) names.push(match[1]);
|
|
1047
1081
|
}
|
|
1048
1082
|
return names;
|
|
@@ -1053,7 +1087,7 @@ function extractBlocks(schema, keyword) {
|
|
|
1053
1087
|
const blocks = /* @__PURE__ */ new Map();
|
|
1054
1088
|
let current = null;
|
|
1055
1089
|
for (const rawLine of schema.split("\n")) {
|
|
1056
|
-
const stripped =
|
|
1090
|
+
const stripped = structuralOnly(rawLine);
|
|
1057
1091
|
const openCount = (stripped.match(/\{/g) ?? []).length;
|
|
1058
1092
|
const closeCount = (stripped.match(/\}/g) ?? []).length;
|
|
1059
1093
|
if (!current) {
|
|
@@ -1082,7 +1116,7 @@ function blockBodyLines(block) {
|
|
|
1082
1116
|
const bodyStart = block.indexOf("{");
|
|
1083
1117
|
const bodyEnd = block.lastIndexOf("}");
|
|
1084
1118
|
if (bodyStart === -1 || bodyEnd <= bodyStart) return [];
|
|
1085
|
-
return block.slice(bodyStart + 1, bodyEnd).split("\n").map((line) =>
|
|
1119
|
+
return block.slice(bodyStart + 1, bodyEnd).split("\n").map((line) => structuralOnly(line).trim()).filter((line) => line.length > 0 && !line.startsWith("@@"));
|
|
1086
1120
|
}
|
|
1087
1121
|
__name(blockBodyLines, "blockBodyLines");
|
|
1088
1122
|
|
|
@@ -1167,17 +1201,18 @@ function parseEnumValues(block) {
|
|
|
1167
1201
|
}
|
|
1168
1202
|
__name(parseEnumValues, "parseEnumValues");
|
|
1169
1203
|
function attributeFieldLists(block, attribute) {
|
|
1170
|
-
const pattern = new RegExp(`@@${attribute}\\(([
|
|
1204
|
+
const pattern = new RegExp(`@@${attribute}\\(\\s*(\\[[^\\]]*\\])`, "g");
|
|
1171
1205
|
return new Set([
|
|
1172
1206
|
...block.matchAll(pattern)
|
|
1173
1207
|
].map((match) => match[1].replace(/\s+/g, "")));
|
|
1174
1208
|
}
|
|
1175
1209
|
__name(attributeFieldLists, "attributeFieldLists");
|
|
1176
1210
|
function parseBlockAttributes(name, block) {
|
|
1211
|
+
const active = block.split("\n").map(stripLineComment).join("\n");
|
|
1177
1212
|
return {
|
|
1178
|
-
indexes: attributeFieldLists(
|
|
1179
|
-
uniques: attributeFieldLists(
|
|
1180
|
-
map:
|
|
1213
|
+
indexes: attributeFieldLists(active, "index"),
|
|
1214
|
+
uniques: attributeFieldLists(active, "unique"),
|
|
1215
|
+
map: active.match(/@@map\("([^"]+)"\)/)?.[1] ?? name
|
|
1181
1216
|
};
|
|
1182
1217
|
}
|
|
1183
1218
|
__name(parseBlockAttributes, "parseBlockAttributes");
|
|
@@ -2403,6 +2438,7 @@ export {
|
|
|
2403
2438
|
UserPortDoctorCheck,
|
|
2404
2439
|
WhoAmIFlow,
|
|
2405
2440
|
applyFragmentBlocks,
|
|
2441
|
+
blankStringLiterals,
|
|
2406
2442
|
blockBodyLines,
|
|
2407
2443
|
breaksContract,
|
|
2408
2444
|
checkSchema,
|
|
@@ -2414,5 +2450,6 @@ export {
|
|
|
2414
2450
|
parseEnumValues,
|
|
2415
2451
|
parseFields,
|
|
2416
2452
|
parseSchema,
|
|
2417
|
-
stripLineComment
|
|
2453
|
+
stripLineComment,
|
|
2454
|
+
structuralOnly
|
|
2418
2455
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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.
|
|
31
|
-
"@saasicat/spec": "^0.
|
|
32
|
-
"@saasicat/types": "^0.
|
|
30
|
+
"@saasicat/nest": "^0.13.0",
|
|
31
|
+
"@saasicat/spec": "^0.13.0",
|
|
32
|
+
"@saasicat/types": "^0.13.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@nestjs/common": "^11.0.0",
|