trickle-observe 0.2.84 → 0.2.85
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/dist/trace-var.js +9 -1
- package/dist/vite-plugin.js +1 -1
- package/dist-esm/vite-plugin.js +1 -1
- package/package.json +1 -1
- package/src/trace-var.ts +9 -1
- package/src/vite-plugin.ts +5 -0
package/dist/trace-var.js
CHANGED
|
@@ -59,6 +59,9 @@ let varsFilePath = '';
|
|
|
59
59
|
let debugMode = false;
|
|
60
60
|
/** Cache: "file:line:varName" → { fingerprint, timestamp } for value-aware dedup */
|
|
61
61
|
const varCache = new Map();
|
|
62
|
+
/** Per-line sample count to avoid loop variable spam */
|
|
63
|
+
const sampleCount = new Map();
|
|
64
|
+
const MAX_SAMPLES_PER_LINE = 5;
|
|
62
65
|
/** Batch buffer for writing — avoids one fs.appendFileSync per variable */
|
|
63
66
|
let varBuffer = [];
|
|
64
67
|
let flushTimer = null;
|
|
@@ -105,8 +108,12 @@ function traceVar(value, varName, line, moduleName, filePath) {
|
|
|
105
108
|
// Create a stable hash for dedup
|
|
106
109
|
const dummyArgs = { kind: 'tuple', elements: [] };
|
|
107
110
|
const typeHash = (0, type_hash_1.hashType)(dummyArgs, type);
|
|
108
|
-
//
|
|
111
|
+
// Per-line sample count limit: stop after N samples to avoid loop spam
|
|
109
112
|
const cacheKey = `${filePath}:${line}:${varName}`;
|
|
113
|
+
const cnt = sampleCount.get(cacheKey) || 0;
|
|
114
|
+
if (cnt >= MAX_SAMPLES_PER_LINE)
|
|
115
|
+
return;
|
|
116
|
+
// Value-aware dedup: re-send if value changed or 10s elapsed
|
|
110
117
|
const t = typeof value;
|
|
111
118
|
const fp = (t === 'string' || t === 'number' || t === 'boolean' || value === null || value === undefined)
|
|
112
119
|
? String(value).substring(0, 60)
|
|
@@ -116,6 +123,7 @@ function traceVar(value, varName, line, moduleName, filePath) {
|
|
|
116
123
|
if (prev && prev.fp === fp && (now - prev.ts) < 10000)
|
|
117
124
|
return;
|
|
118
125
|
varCache.set(cacheKey, { fp, ts: now });
|
|
126
|
+
sampleCount.set(cacheKey, cnt + 1);
|
|
119
127
|
const sample = sanitizeVarSample(value);
|
|
120
128
|
const observation = {
|
|
121
129
|
kind: 'variable',
|
package/dist/vite-plugin.js
CHANGED
|
@@ -1604,7 +1604,7 @@ ingestUrl) {
|
|
|
1604
1604
|
}
|
|
1605
1605
|
// Add variable tracing if needed — inlined to avoid import resolution issues in Vite SSR.
|
|
1606
1606
|
if (varInsertions.length > 0 || destructInsertions.length > 0 || reassignInsertions.length > 0 || forLoopInsertions.length > 0 || catchInsertions.length > 0 || funcParamInsertions.length > 0 || jsxExprInsertions.length > 0) {
|
|
1607
|
-
prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
|
|
1607
|
+
prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` const _sampleCount = new Map();`, ` const _MAX_SAMPLES = 5;`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const cnt = _sampleCount.get(ck) || 0;`, ` if (cnt >= _MAX_SAMPLES) return;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` _sampleCount.set(ck, cnt + 1);`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
|
|
1608
1608
|
}
|
|
1609
1609
|
// Add React component render tracker if needed
|
|
1610
1610
|
if (bodyInsertions.length > 0 || conciseBodyInsertions.length > 0) {
|
package/dist-esm/vite-plugin.js
CHANGED
|
@@ -1591,7 +1591,7 @@ ingestUrl) {
|
|
|
1591
1591
|
}
|
|
1592
1592
|
// Add variable tracing if needed — inlined to avoid import resolution issues in Vite SSR.
|
|
1593
1593
|
if (varInsertions.length > 0 || destructInsertions.length > 0 || reassignInsertions.length > 0 || forLoopInsertions.length > 0 || catchInsertions.length > 0 || funcParamInsertions.length > 0 || jsxExprInsertions.length > 0) {
|
|
1594
|
-
prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
|
|
1594
|
+
prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` const _sampleCount = new Map();`, ` const _MAX_SAMPLES = 5;`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const cnt = _sampleCount.get(ck) || 0;`, ` if (cnt >= _MAX_SAMPLES) return;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` _sampleCount.set(ck, cnt + 1);`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
|
|
1595
1595
|
}
|
|
1596
1596
|
// Add React component render tracker if needed
|
|
1597
1597
|
if (bodyInsertions.length > 0 || conciseBodyInsertions.length > 0) {
|
package/package.json
CHANGED
package/src/trace-var.ts
CHANGED
|
@@ -26,6 +26,9 @@ let debugMode = false;
|
|
|
26
26
|
|
|
27
27
|
/** Cache: "file:line:varName" → { fingerprint, timestamp } for value-aware dedup */
|
|
28
28
|
const varCache = new Map<string, { fp: string; ts: number }>();
|
|
29
|
+
/** Per-line sample count to avoid loop variable spam */
|
|
30
|
+
const sampleCount = new Map<string, number>();
|
|
31
|
+
const MAX_SAMPLES_PER_LINE = 5;
|
|
29
32
|
|
|
30
33
|
/** Batch buffer for writing — avoids one fs.appendFileSync per variable */
|
|
31
34
|
let varBuffer: string[] = [];
|
|
@@ -92,8 +95,12 @@ export function traceVar(
|
|
|
92
95
|
const dummyArgs: TypeNode = { kind: 'tuple', elements: [] };
|
|
93
96
|
const typeHash = hashType(dummyArgs, type);
|
|
94
97
|
|
|
95
|
-
//
|
|
98
|
+
// Per-line sample count limit: stop after N samples to avoid loop spam
|
|
96
99
|
const cacheKey = `${filePath}:${line}:${varName}`;
|
|
100
|
+
const cnt = sampleCount.get(cacheKey) || 0;
|
|
101
|
+
if (cnt >= MAX_SAMPLES_PER_LINE) return;
|
|
102
|
+
|
|
103
|
+
// Value-aware dedup: re-send if value changed or 10s elapsed
|
|
97
104
|
const t = typeof value;
|
|
98
105
|
const fp = (t === 'string' || t === 'number' || t === 'boolean' || value === null || value === undefined)
|
|
99
106
|
? String(value).substring(0, 60)
|
|
@@ -102,6 +109,7 @@ export function traceVar(
|
|
|
102
109
|
const prev = varCache.get(cacheKey);
|
|
103
110
|
if (prev && prev.fp === fp && (now - prev.ts) < 10000) return;
|
|
104
111
|
varCache.set(cacheKey, { fp, ts: now });
|
|
112
|
+
sampleCount.set(cacheKey, cnt + 1);
|
|
105
113
|
|
|
106
114
|
const sample = sanitizeVarSample(value);
|
|
107
115
|
|
package/src/vite-plugin.ts
CHANGED
|
@@ -1624,6 +1624,8 @@ export function transformEsmSource(
|
|
|
1624
1624
|
prefixLines.push(
|
|
1625
1625
|
`if (!globalThis.__trickle_var_tracer) {`,
|
|
1626
1626
|
` const _cache = new Map();`,
|
|
1627
|
+
` const _sampleCount = new Map();`,
|
|
1628
|
+
` const _MAX_SAMPLES = 5;`,
|
|
1627
1629
|
` function _inferType(v, d) {`,
|
|
1628
1630
|
` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`,
|
|
1629
1631
|
` if (v === null) return { kind: 'primitive', name: 'null' };`,
|
|
@@ -1660,10 +1662,13 @@ export function transformEsmSource(
|
|
|
1660
1662
|
` const sample = _sanitize(v, 2);`,
|
|
1661
1663
|
` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`,
|
|
1662
1664
|
` const ck = file + ':' + l + ':' + n;`,
|
|
1665
|
+
` const cnt = _sampleCount.get(ck) || 0;`,
|
|
1666
|
+
` if (cnt >= _MAX_SAMPLES) return;`,
|
|
1663
1667
|
` const prev = _cache.get(ck);`,
|
|
1664
1668
|
` const now = Date.now();`,
|
|
1665
1669
|
` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`,
|
|
1666
1670
|
` _cache.set(ck, { sv: sv, ts: now });`,
|
|
1671
|
+
` _sampleCount.set(ck, cnt + 1);`,
|
|
1667
1672
|
` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`,
|
|
1668
1673
|
` } catch(e) {}`,
|
|
1669
1674
|
` };`,
|