@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.
@@ -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
- * Report violations to console.
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(' DTO fields don\'t match Prisma Dbo models!');
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(' This prevents AI from inventing field names that don\'t match the database schema.');
404
- console.error(' Dbo can have extra fields (e.g., password) - Dto cannot.');
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
- console.error(` Available Dbo fields: ${v.availableFields.join(', ')}`);
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(' Dto fields must be a subset of Dbo fields (matching camelCase field names).');
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: ValidateDtosMode = options.mode ?? 'OFF';
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>;