@webpieces/dev-config 0.2.81 → 0.2.83
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/architecture/executors/validate-code/executor.d.ts +9 -0
- package/architecture/executors/validate-code/executor.js +17 -2
- package/architecture/executors/validate-code/executor.js.map +1 -1
- package/architecture/executors/validate-code/executor.ts +31 -2
- package/architecture/executors/validate-code/schema.json +29 -0
- package/architecture/executors/validate-dtos/executor.d.ts +1 -0
- package/architecture/executors/validate-dtos/executor.js +88 -11
- package/architecture/executors/validate-dtos/executor.js.map +1 -1
- package/architecture/executors/validate-dtos/executor.ts +95 -11
- package/architecture/executors/validate-dtos/schema.json +4 -0
- package/architecture/executors/validate-prisma-converters/executor.d.ts +57 -0
- package/architecture/executors/validate-prisma-converters/executor.js +508 -0
- package/architecture/executors/validate-prisma-converters/executor.js.map +1 -0
- package/architecture/executors/validate-prisma-converters/executor.ts +639 -0
- package/architecture/executors/validate-prisma-converters/schema.json +28 -0
- package/executors.json +5 -0
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ export interface ValidateDtosOptions {
|
|
|
40
40
|
mode?: ValidateDtosMode;
|
|
41
41
|
prismaSchemaPath?: string;
|
|
42
42
|
dtoSourcePaths?: string[];
|
|
43
|
+
ignoreModifiedUntilEpoch?: number;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export interface ExecutorResult {
|
|
@@ -393,29 +394,92 @@ function findViolations(
|
|
|
393
394
|
}
|
|
394
395
|
|
|
395
396
|
/**
|
|
396
|
-
*
|
|
397
|
+
* Compute similarity between two strings using longest common subsequence ratio.
|
|
398
|
+
* Returns a value between 0 and 1, where 1 is an exact match.
|
|
399
|
+
*/
|
|
400
|
+
function similarity(a: string, b: string): number {
|
|
401
|
+
const al = a.toLowerCase();
|
|
402
|
+
const bl = b.toLowerCase();
|
|
403
|
+
if (al === bl) return 1;
|
|
404
|
+
|
|
405
|
+
const m = al.length;
|
|
406
|
+
const n = bl.length;
|
|
407
|
+
const prev = new Array<number>(n + 1).fill(0);
|
|
408
|
+
const curr = new Array<number>(n + 1).fill(0);
|
|
409
|
+
|
|
410
|
+
for (let i = 1; i <= m; i++) {
|
|
411
|
+
for (let j = 1; j <= n; j++) {
|
|
412
|
+
if (al[i - 1] === bl[j - 1]) {
|
|
413
|
+
curr[j] = prev[j - 1] + 1;
|
|
414
|
+
} else {
|
|
415
|
+
curr[j] = Math.max(prev[j], curr[j - 1]);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
for (let j = 0; j <= n; j++) {
|
|
419
|
+
prev[j] = curr[j];
|
|
420
|
+
curr[j] = 0;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const lcsLen = prev[n];
|
|
425
|
+
return (2 * lcsLen) / (m + n);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Find the closest matching Dbo field for a given Dto field name.
|
|
430
|
+
* Returns the best match or null if nothing scores above the threshold.
|
|
431
|
+
*/
|
|
432
|
+
function suggestClosestField(fieldName: string, availableFields: string[]): string | null {
|
|
433
|
+
let bestMatch: string | null = null;
|
|
434
|
+
let bestScore = 0.4; // minimum threshold
|
|
435
|
+
|
|
436
|
+
for (const candidate of availableFields) {
|
|
437
|
+
const score = similarity(fieldName, candidate);
|
|
438
|
+
if (score > bestScore) {
|
|
439
|
+
bestScore = score;
|
|
440
|
+
bestMatch = candidate;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return bestMatch;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Report violations to console with prominent AI warning and rename suggestions.
|
|
397
449
|
*/
|
|
398
450
|
function reportViolations(violations: DtoViolation[]): void {
|
|
399
451
|
console.error('');
|
|
400
|
-
console.error('
|
|
452
|
+
console.error('❌❌❌ DTO FIELD MISMATCH - AI AGENT: READ THIS CAREFULLY ❌❌❌');
|
|
453
|
+
console.error('');
|
|
454
|
+
console.error('🛑 DO NOT attempt find/replace or manual renaming. Instead:');
|
|
455
|
+
console.error(' 1. IDE rename is 10x faster than AI renaming PLUS 100% correct vs. AI\'s 80%');
|
|
456
|
+
console.error(' 2. Based on errors below, suggest renames to the user');
|
|
457
|
+
console.error(' 3. Ask the human to use IDE rename-refactoring (F2 / right-click → Rename Symbol)');
|
|
401
458
|
console.error('');
|
|
402
459
|
console.error('📚 Every non-deprecated field in a Dto must exist in the corresponding Dbo.');
|
|
403
|
-
console.error('
|
|
404
|
-
console.error('
|
|
460
|
+
console.error(' Dbo can have extra fields (e.g., password) — Dto fields cannot be invented.');
|
|
461
|
+
console.error('');
|
|
462
|
+
console.error(' When needing fields from multiple tables (e.g., a join), use a XxxJoinDto that');
|
|
463
|
+
console.error(' contains YYDto and ZZDto fields from the other tables instead of flattening.');
|
|
464
|
+
console.error(' Fields marked @deprecated in the Dto are exempt from this check.');
|
|
405
465
|
console.error('');
|
|
406
466
|
|
|
407
467
|
for (const v of violations) {
|
|
408
468
|
console.error(` ❌ ${v.file}:${v.line}`);
|
|
409
469
|
console.error(` ${v.dtoName}.${v.fieldName} does not exist in ${v.dboName}`);
|
|
410
|
-
|
|
470
|
+
|
|
471
|
+
const suggestion = suggestClosestField(v.fieldName, v.availableFields);
|
|
472
|
+
if (suggestion) {
|
|
473
|
+
console.error(` 💡 Suggested rename: ${v.fieldName} → ${suggestion} (Use IDE rename on ${v.dtoName}.${v.fieldName})`);
|
|
474
|
+
} else {
|
|
475
|
+
const preview = v.availableFields.slice(0, 8).join(', ');
|
|
476
|
+
const ellipsis = v.availableFields.length > 8 ? ', ...' : '';
|
|
477
|
+
console.error(` 💡 No close match found. Available: ${preview}${ellipsis}`);
|
|
478
|
+
}
|
|
411
479
|
}
|
|
412
480
|
console.error('');
|
|
413
481
|
|
|
414
|
-
console.error('
|
|
415
|
-
console.error(' Fields marked @deprecated in the Dto are exempt from this check.');
|
|
416
|
-
console.error('');
|
|
417
|
-
console.error(' When needing fields from multiple tables (e.g., a join), use a XxxJoinDto that');
|
|
418
|
-
console.error(' contains YYDto and ZZDto fields from the other tables instead of flattening.');
|
|
482
|
+
console.error('❌❌❌ AI AGENT: Suggest to the human which are the most likely renames needed and list them ❌❌❌');
|
|
419
483
|
console.error('');
|
|
420
484
|
}
|
|
421
485
|
|
|
@@ -559,12 +623,32 @@ function validateDtoFiles(
|
|
|
559
623
|
return { success: false };
|
|
560
624
|
}
|
|
561
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Resolve mode considering ignoreModifiedUntilEpoch override.
|
|
628
|
+
* When active, downgrades to OFF. When expired, logs a warning.
|
|
629
|
+
*/
|
|
630
|
+
function resolveMode(normalMode: ValidateDtosMode, epoch: number | undefined): ValidateDtosMode {
|
|
631
|
+
if (epoch === undefined || normalMode === 'OFF') {
|
|
632
|
+
return normalMode;
|
|
633
|
+
}
|
|
634
|
+
const nowSeconds = Date.now() / 1000;
|
|
635
|
+
if (nowSeconds < epoch) {
|
|
636
|
+
const expiresDate = new Date(epoch * 1000).toISOString().split('T')[0];
|
|
637
|
+
console.log(`\n⏭️ Skipping validate-dtos (ignoreModifiedUntilEpoch active, expires: ${expiresDate})`);
|
|
638
|
+
console.log('');
|
|
639
|
+
return 'OFF';
|
|
640
|
+
}
|
|
641
|
+
const expiresDate = new Date(epoch * 1000).toISOString().split('T')[0];
|
|
642
|
+
console.log(`\n⚠️ validateDtos.ignoreModifiedUntilEpoch (${epoch}) has expired (${expiresDate}). Remove it from nx.json. Using normal mode: ${normalMode}\n`);
|
|
643
|
+
return normalMode;
|
|
644
|
+
}
|
|
645
|
+
|
|
562
646
|
export default async function runExecutor(
|
|
563
647
|
options: ValidateDtosOptions,
|
|
564
648
|
context: ExecutorContext
|
|
565
649
|
): Promise<ExecutorResult> {
|
|
566
650
|
const workspaceRoot = context.root;
|
|
567
|
-
const mode
|
|
651
|
+
const mode = resolveMode(options.mode ?? 'OFF', options.ignoreModifiedUntilEpoch);
|
|
568
652
|
|
|
569
653
|
if (mode === 'OFF') {
|
|
570
654
|
console.log('\n⏭️ Skipping validate-dtos (mode: OFF)');
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"type": "array",
|
|
19
19
|
"items": { "type": "string" },
|
|
20
20
|
"description": "Array of directories (relative to workspace root) containing Dto files"
|
|
21
|
+
},
|
|
22
|
+
"ignoreModifiedUntilEpoch": {
|
|
23
|
+
"type": "number",
|
|
24
|
+
"description": "Epoch seconds. Until this time, skip DTO validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
21
25
|
}
|
|
22
26
|
},
|
|
23
27
|
"required": []
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Prisma Converters Executor
|
|
3
|
+
*
|
|
4
|
+
* Validates that Prisma converter methods follow a scalable pattern:
|
|
5
|
+
* methods returning XxxDto (where XxxDbo exists in schema.prisma) must
|
|
6
|
+
* accept that exact XxxDbo as the first parameter. This keeps single-table
|
|
7
|
+
* converters clean and forces join converters to compose them.
|
|
8
|
+
*
|
|
9
|
+
* ============================================================================
|
|
10
|
+
* RULES
|
|
11
|
+
* ============================================================================
|
|
12
|
+
*
|
|
13
|
+
* 1. First param must be exact Dbo:
|
|
14
|
+
* If method returns XxxDto and XxxDbo exists in schema.prisma,
|
|
15
|
+
* the first parameter must be of type XxxDbo.
|
|
16
|
+
*
|
|
17
|
+
* 2. Extra params must be booleans:
|
|
18
|
+
* Additional parameters beyond the Dbo are allowed but must be boolean
|
|
19
|
+
* (used for filtering payloads / security info).
|
|
20
|
+
*
|
|
21
|
+
* 3. No async converters:
|
|
22
|
+
* Methods returning Promise<XxxDto> are flagged — converters should be
|
|
23
|
+
* pure data mapping, no async work.
|
|
24
|
+
*
|
|
25
|
+
* 4. No standalone functions:
|
|
26
|
+
* Standalone functions in converter files are flagged — must be class
|
|
27
|
+
* methods so the converter class can be injected (dependency tree tracing).
|
|
28
|
+
*
|
|
29
|
+
* 5. Dto creation outside converters directory:
|
|
30
|
+
* Files outside the configured convertersPath that create `new XxxDto(...)`
|
|
31
|
+
* where XxxDbo exists in schema.prisma are flagged — Dto instances tied to
|
|
32
|
+
* a Dbo must only be created via a converter class.
|
|
33
|
+
*
|
|
34
|
+
* ============================================================================
|
|
35
|
+
* SKIP CONDITIONS
|
|
36
|
+
* ============================================================================
|
|
37
|
+
* - Methods with @deprecated decorator or JSDoc tag
|
|
38
|
+
* - Lines with: // webpieces-disable prisma-converter -- [reason]
|
|
39
|
+
*
|
|
40
|
+
* ============================================================================
|
|
41
|
+
* MODES
|
|
42
|
+
* ============================================================================
|
|
43
|
+
* - OFF: Skip validation entirely
|
|
44
|
+
* - MODIFIED_FILES: Validate converter files that were modified in the diff
|
|
45
|
+
*/
|
|
46
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
47
|
+
export type PrismaConverterMode = 'OFF' | 'MODIFIED_FILES';
|
|
48
|
+
export interface ValidatePrismaConvertersOptions {
|
|
49
|
+
mode?: PrismaConverterMode;
|
|
50
|
+
schemaPath?: string;
|
|
51
|
+
convertersPaths?: string[];
|
|
52
|
+
ignoreModifiedUntilEpoch?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface ExecutorResult {
|
|
55
|
+
success: boolean;
|
|
56
|
+
}
|
|
57
|
+
export default function runExecutor(options: ValidatePrismaConvertersOptions, context: ExecutorContext): Promise<ExecutorResult>;
|