trickle-observe 0.2.83 → 0.2.84

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.
@@ -47,6 +47,13 @@ export declare function tricklePlugin(options?: TricklePluginOptions): {
47
47
  * Returns -1 if not found.
48
48
  */
49
49
  export declare function findFunctionBodyBrace(source: string, afterOpenParen: number): number;
50
+ /**
51
+ * Find class body ranges in source code. Handles both:
52
+ * class Foo { ... }
53
+ * var Foo = class { ... }
54
+ * Returns an array of [start, end] positions (inclusive of braces).
55
+ */
56
+ export declare function findClassBodyRanges(source: string): Array<[number, number]>;
50
57
  /**
51
58
  * Find variable reassignments (not declarations) and return insertions for tracing.
52
59
  * Handles: x = newValue; x += 1; x ||= fallback; etc.
@@ -24,6 +24,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.tricklePlugin = tricklePlugin;
26
26
  exports.findFunctionBodyBrace = findFunctionBodyBrace;
27
+ exports.findClassBodyRanges = findClassBodyRanges;
27
28
  exports.findReassignments = findReassignments;
28
29
  exports.findCatchVars = findCatchVars;
29
30
  exports.findForLoopVars = findForLoopVars;
@@ -504,6 +505,73 @@ function extractDestructuredNames(pattern) {
504
505
  }
505
506
  return names;
506
507
  }
508
+ /**
509
+ * Find class body ranges in source code. Handles both:
510
+ * class Foo { ... }
511
+ * var Foo = class { ... }
512
+ * Returns an array of [start, end] positions (inclusive of braces).
513
+ */
514
+ function findClassBodyRanges(source) {
515
+ const ranges = [];
516
+ // Match both class declarations and class expressions
517
+ const classRegex = /\bclass\s*(?:[a-zA-Z_$][a-zA-Z0-9_$]*)?\s*(?:extends\s+[a-zA-Z_$.[\]]+\s*)?\{/g;
518
+ let m;
519
+ while ((m = classRegex.exec(source)) !== null) {
520
+ const openBrace = source.indexOf('{', m.index + 5); // skip past 'class'
521
+ if (openBrace === -1)
522
+ continue;
523
+ // Find matching close brace
524
+ let depth = 1;
525
+ let pos = openBrace + 1;
526
+ while (pos < source.length && depth > 0) {
527
+ const ch = source[pos];
528
+ if (ch === '{')
529
+ depth++;
530
+ else if (ch === '}') {
531
+ depth--;
532
+ if (depth === 0)
533
+ break;
534
+ }
535
+ else if (ch === '"' || ch === "'" || ch === '`') {
536
+ const q = ch;
537
+ pos++;
538
+ while (pos < source.length) {
539
+ if (source[pos] === '\\')
540
+ pos++;
541
+ else if (source[pos] === q)
542
+ break;
543
+ else if (q === '`' && source[pos] === '$' && source[pos + 1] === '{') {
544
+ pos += 2;
545
+ let td = 1;
546
+ while (pos < source.length && td > 0) {
547
+ if (source[pos] === '{')
548
+ td++;
549
+ else if (source[pos] === '}')
550
+ td--;
551
+ pos++;
552
+ }
553
+ continue;
554
+ }
555
+ pos++;
556
+ }
557
+ }
558
+ else if (ch === '/' && source[pos + 1] === '/') {
559
+ while (pos < source.length && source[pos] !== '\n')
560
+ pos++;
561
+ }
562
+ else if (ch === '/' && source[pos + 1] === '*') {
563
+ pos += 2;
564
+ while (pos < source.length - 1 && !(source[pos] === '*' && source[pos + 1] === '/'))
565
+ pos++;
566
+ pos++;
567
+ }
568
+ pos++;
569
+ }
570
+ if (depth === 0)
571
+ ranges.push([openBrace, pos]);
572
+ }
573
+ return ranges;
574
+ }
507
575
  /**
508
576
  * Find variable reassignments (not declarations) and return insertions for tracing.
509
577
  * Handles: x = newValue; x += 1; x ||= fallback; etc.
@@ -513,6 +581,8 @@ function extractDestructuredNames(pattern) {
513
581
  */
514
582
  function findReassignments(source) {
515
583
  const results = [];
584
+ // Pre-compute class body ranges to skip class field declarations
585
+ const classRanges = findClassBodyRanges(source);
516
586
  // Match: <identifier> <assignOp>= <value> at the start of a line
517
587
  // Compound operators: +=, -=, *=, /=, %=, **=, &&=, ||=, ??=, <<=, >>=, >>>=, &=, |=, ^=
518
588
  // Plain: = (but not ==, ===, =>, !=)
@@ -548,6 +618,10 @@ function findReassignments(source) {
548
618
  const linePrefix = source.slice(lineStart, match.index + match[1].length).trim();
549
619
  if (/^(export\s+)?(const|let|var)\s/.test(source.slice(lineStart).trimStart()))
550
620
  continue;
621
+ // Skip class field declarations (e.g., `tasks = []` inside a class body)
622
+ // Inserting trace calls inside class bodies causes SyntaxError
623
+ if (classRanges.some(([start, end]) => match.index > start && match.index < end))
624
+ continue;
551
625
  // Skip if this looks like a property in an object literal (preceded by a key: pattern on same line)
552
626
  // or if it's a label (label: ...)
553
627
  const beforeOnLine = source.slice(lineStart, match.index).trim();
@@ -492,6 +492,73 @@ function extractDestructuredNames(pattern) {
492
492
  }
493
493
  return names;
494
494
  }
495
+ /**
496
+ * Find class body ranges in source code. Handles both:
497
+ * class Foo { ... }
498
+ * var Foo = class { ... }
499
+ * Returns an array of [start, end] positions (inclusive of braces).
500
+ */
501
+ export function findClassBodyRanges(source) {
502
+ const ranges = [];
503
+ // Match both class declarations and class expressions
504
+ const classRegex = /\bclass\s*(?:[a-zA-Z_$][a-zA-Z0-9_$]*)?\s*(?:extends\s+[a-zA-Z_$.[\]]+\s*)?\{/g;
505
+ let m;
506
+ while ((m = classRegex.exec(source)) !== null) {
507
+ const openBrace = source.indexOf('{', m.index + 5); // skip past 'class'
508
+ if (openBrace === -1)
509
+ continue;
510
+ // Find matching close brace
511
+ let depth = 1;
512
+ let pos = openBrace + 1;
513
+ while (pos < source.length && depth > 0) {
514
+ const ch = source[pos];
515
+ if (ch === '{')
516
+ depth++;
517
+ else if (ch === '}') {
518
+ depth--;
519
+ if (depth === 0)
520
+ break;
521
+ }
522
+ else if (ch === '"' || ch === "'" || ch === '`') {
523
+ const q = ch;
524
+ pos++;
525
+ while (pos < source.length) {
526
+ if (source[pos] === '\\')
527
+ pos++;
528
+ else if (source[pos] === q)
529
+ break;
530
+ else if (q === '`' && source[pos] === '$' && source[pos + 1] === '{') {
531
+ pos += 2;
532
+ let td = 1;
533
+ while (pos < source.length && td > 0) {
534
+ if (source[pos] === '{')
535
+ td++;
536
+ else if (source[pos] === '}')
537
+ td--;
538
+ pos++;
539
+ }
540
+ continue;
541
+ }
542
+ pos++;
543
+ }
544
+ }
545
+ else if (ch === '/' && source[pos + 1] === '/') {
546
+ while (pos < source.length && source[pos] !== '\n')
547
+ pos++;
548
+ }
549
+ else if (ch === '/' && source[pos + 1] === '*') {
550
+ pos += 2;
551
+ while (pos < source.length - 1 && !(source[pos] === '*' && source[pos + 1] === '/'))
552
+ pos++;
553
+ pos++;
554
+ }
555
+ pos++;
556
+ }
557
+ if (depth === 0)
558
+ ranges.push([openBrace, pos]);
559
+ }
560
+ return ranges;
561
+ }
495
562
  /**
496
563
  * Find variable reassignments (not declarations) and return insertions for tracing.
497
564
  * Handles: x = newValue; x += 1; x ||= fallback; etc.
@@ -501,6 +568,8 @@ function extractDestructuredNames(pattern) {
501
568
  */
502
569
  export function findReassignments(source) {
503
570
  const results = [];
571
+ // Pre-compute class body ranges to skip class field declarations
572
+ const classRanges = findClassBodyRanges(source);
504
573
  // Match: <identifier> <assignOp>= <value> at the start of a line
505
574
  // Compound operators: +=, -=, *=, /=, %=, **=, &&=, ||=, ??=, <<=, >>=, >>>=, &=, |=, ^=
506
575
  // Plain: = (but not ==, ===, =>, !=)
@@ -536,6 +605,10 @@ export function findReassignments(source) {
536
605
  const linePrefix = source.slice(lineStart, match.index + match[1].length).trim();
537
606
  if (/^(export\s+)?(const|let|var)\s/.test(source.slice(lineStart).trimStart()))
538
607
  continue;
608
+ // Skip class field declarations (e.g., `tasks = []` inside a class body)
609
+ // Inserting trace calls inside class bodies causes SyntaxError
610
+ if (classRanges.some(([start, end]) => match.index > start && match.index < end))
611
+ continue;
539
612
  // Skip if this looks like a property in an object literal (preceded by a key: pattern on same line)
540
613
  // or if it's a label (label: ...)
541
614
  const beforeOnLine = source.slice(lineStart, match.index).trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trickle-observe",
3
- "version": "0.2.83",
3
+ "version": "0.2.84",
4
4
  "description": "Runtime type observability for JavaScript applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -472,6 +472,57 @@ function extractDestructuredNames(pattern: string): string[] {
472
472
  return names;
473
473
  }
474
474
 
475
+ /**
476
+ * Find class body ranges in source code. Handles both:
477
+ * class Foo { ... }
478
+ * var Foo = class { ... }
479
+ * Returns an array of [start, end] positions (inclusive of braces).
480
+ */
481
+ export function findClassBodyRanges(source: string): Array<[number, number]> {
482
+ const ranges: Array<[number, number]> = [];
483
+ // Match both class declarations and class expressions
484
+ const classRegex = /\bclass\s*(?:[a-zA-Z_$][a-zA-Z0-9_$]*)?\s*(?:extends\s+[a-zA-Z_$.[\]]+\s*)?\{/g;
485
+ let m;
486
+ while ((m = classRegex.exec(source)) !== null) {
487
+ const openBrace = source.indexOf('{', m.index + 5); // skip past 'class'
488
+ if (openBrace === -1) continue;
489
+ // Find matching close brace
490
+ let depth = 1;
491
+ let pos = openBrace + 1;
492
+ while (pos < source.length && depth > 0) {
493
+ const ch = source[pos];
494
+ if (ch === '{') depth++;
495
+ else if (ch === '}') { depth--; if (depth === 0) break; }
496
+ else if (ch === '"' || ch === "'" || ch === '`') {
497
+ const q = ch; pos++;
498
+ while (pos < source.length) {
499
+ if (source[pos] === '\\') pos++;
500
+ else if (source[pos] === q) break;
501
+ else if (q === '`' && source[pos] === '$' && source[pos + 1] === '{') {
502
+ pos += 2; let td = 1;
503
+ while (pos < source.length && td > 0) {
504
+ if (source[pos] === '{') td++;
505
+ else if (source[pos] === '}') td--;
506
+ pos++;
507
+ }
508
+ continue;
509
+ }
510
+ pos++;
511
+ }
512
+ } else if (ch === '/' && source[pos + 1] === '/') {
513
+ while (pos < source.length && source[pos] !== '\n') pos++;
514
+ } else if (ch === '/' && source[pos + 1] === '*') {
515
+ pos += 2;
516
+ while (pos < source.length - 1 && !(source[pos] === '*' && source[pos + 1] === '/')) pos++;
517
+ pos++;
518
+ }
519
+ pos++;
520
+ }
521
+ if (depth === 0) ranges.push([openBrace, pos]);
522
+ }
523
+ return ranges;
524
+ }
525
+
475
526
  /**
476
527
  * Find variable reassignments (not declarations) and return insertions for tracing.
477
528
  * Handles: x = newValue; x += 1; x ||= fallback; etc.
@@ -481,6 +532,8 @@ function extractDestructuredNames(pattern: string): string[] {
481
532
  */
482
533
  export function findReassignments(source: string): Array<{ lineEnd: number; varName: string; lineNo: number }> {
483
534
  const results: Array<{ lineEnd: number; varName: string; lineNo: number }> = [];
535
+ // Pre-compute class body ranges to skip class field declarations
536
+ const classRanges = findClassBodyRanges(source);
484
537
 
485
538
  // Match: <identifier> <assignOp>= <value> at the start of a line
486
539
  // Compound operators: +=, -=, *=, /=, %=, **=, &&=, ||=, ??=, <<=, >>=, >>>=, &=, |=, ^=
@@ -513,6 +566,10 @@ export function findReassignments(source: string): Array<{ lineEnd: number; varN
513
566
  const linePrefix = source.slice(lineStart, match.index + match[1].length).trim();
514
567
  if (/^(export\s+)?(const|let|var)\s/.test(source.slice(lineStart).trimStart())) continue;
515
568
 
569
+ // Skip class field declarations (e.g., `tasks = []` inside a class body)
570
+ // Inserting trace calls inside class bodies causes SyntaxError
571
+ if (classRanges.some(([start, end]) => match!.index > start && match!.index < end)) continue;
572
+
516
573
  // Skip if this looks like a property in an object literal (preceded by a key: pattern on same line)
517
574
  // or if it's a label (label: ...)
518
575
  const beforeOnLine = source.slice(lineStart, match.index).trim();