@stacknext/regression 0.0.0-stage → 0.6.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AACxD,eAAO,MAAM,yBAAyB,uBAAuB,CAAA;AAC7D,eAAO,MAAM,2BAA2B,yBAAyB,CAAA;AACjE,eAAO,MAAM,mCAAmC,IAAI,CAAA;AACpD,eAAO,MAAM,mCAAmC,UAAU,CAAA;AAE1D,eAAO,MAAM,YAAY,0EAMf,CAAA;AACV,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAErD,eAAO,MAAM,eAAe,qEAMlB,CAAA;AACV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3D,eAAO,MAAM,wBAAwB,0CAA2C,CAAA;AAChF,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5E,eAAO,MAAM,oBAAoB,qCAAsC,CAAA;AACvE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,eAAO,MAAM,mBAAmB,iEAKtB,CAAA;AACV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,qBAAqB,4CAA6C,CAAA;AAC/E,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,eAAO,MAAM,uBAAuB,mEAM1B,CAAA;AACV,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3E,eAAO,MAAM,qBAAqB,QAAkC,CAAA;AACpE,eAAO,MAAM,mBAAmB,QAAuB,CAAA;AACvD,eAAO,MAAM,iBAAiB,QAAuB,CAAA;AAErD,eAAO,MAAM,0BAA0B;;;;;;;;CAQ7B,CAAA;AAEV,eAAO,MAAM,sBAAsB,krBAOzB,CAAA"}
@@ -0,0 +1,56 @@
1
+ export const STACKNEXT_RUN_FORMAT = 'stacknext-test-run';
2
+ export const STACKNEXT_BASELINE_FORMAT = 'stacknext-baseline';
3
+ export const STACKNEXT_COMPARISON_FORMAT = 'stacknext-comparison';
4
+ export const STACKNEXT_REGRESSION_FORMAT_VERSION = 1;
5
+ export const STACKNEXT_REGRESSION_ENGINE_VERSION = '0.6.0';
6
+ export const RUN_STATUSES = [
7
+ 'running',
8
+ 'completed',
9
+ 'failed',
10
+ 'interrupted',
11
+ 'incomplete',
12
+ ];
13
+ export const SAMPLE_STATUSES = [
14
+ 'passed',
15
+ 'failed',
16
+ 'timedOut',
17
+ 'skipped',
18
+ 'interrupted',
19
+ ];
20
+ export const SAMPLE_EXPECTED_STATUSES = ['passed', 'failed', 'skipped'];
21
+ export const CORRELATION_STATUSES = ['verified', 'incomplete'];
22
+ export const REGRESSION_STATUSES = [
23
+ 'improved',
24
+ 'regressed',
25
+ 'unchanged',
26
+ 'inconclusive',
27
+ ];
28
+ export const REGRESSION_SEVERITIES = ['high', 'medium', 'low', 'none'];
29
+ export const REGRESSION_METRIC_KINDS = [
30
+ 'duration',
31
+ 'count',
32
+ 'test-status',
33
+ 'error',
34
+ 'finding',
35
+ ];
36
+ export const REGRESSION_ID_PATTERN = /^(run|base|cmp)-[0-9a-f]{16}$/;
37
+ export const SCENARIO_ID_PATTERN = /^scn-[0-9a-f]{16}$/;
38
+ export const SAMPLE_ID_PATTERN = /^smp-[0-9a-f]{16}$/;
39
+ export const DEFAULT_COMPARISON_OPTIONS = {
40
+ minSamples: 3,
41
+ durationRegressionPct: 20,
42
+ durationRegressionMs: 25,
43
+ durationImprovementPct: 20,
44
+ durationImprovementMs: 25,
45
+ countRegressionMin: 1,
46
+ ignoreEnvironment: false,
47
+ };
48
+ export const REGRESSION_LIMITATIONS = [
49
+ 'Comparisons use only telemetry correlated to Playwright scenario samples in the captured run.',
50
+ 'Durations are wall-clock measurements from the local run and include machine and dev-server noise.',
51
+ 'Medians reduce noise but are not a statistical significance test; StackNext makes no significance claim.',
52
+ 'Null database durations are unmeasured and are never converted to zero.',
53
+ 'A baseline or candidate with missing samples, incomplete correlation or a changed trace shape is inconclusive by default.',
54
+ 'Environment differences such as Node.js, Next.js, OS, runtime, database, instrumentation or verbose tracing make a comparison inconclusive unless explicitly overridden.',
55
+ ];
56
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,CAAA;AACxD,MAAM,CAAC,MAAM,yBAAyB,GAAG,oBAAoB,CAAA;AAC7D,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAA;AACjE,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,CAAA;AACpD,MAAM,CAAC,MAAM,mCAAmC,GAAG,OAAO,CAAA;AAE1D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,SAAS;IACT,WAAW;IACX,QAAQ;IACR,aAAa;IACb,YAAY;CACJ,CAAA;AAGV,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,SAAS;IACT,aAAa;CACL,CAAA;AAGV,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAA;AAGhF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,YAAY,CAAU,CAAA;AAGvE,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,UAAU;IACV,WAAW;IACX,WAAW;IACX,cAAc;CACN,CAAA;AAGV,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAU,CAAA;AAG/E,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,UAAU;IACV,OAAO;IACP,aAAa;IACb,OAAO;IACP,SAAS;CACD,CAAA;AAGV,MAAM,CAAC,MAAM,qBAAqB,GAAG,+BAA+B,CAAA;AACpE,MAAM,CAAC,MAAM,mBAAmB,GAAG,oBAAoB,CAAA;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,oBAAoB,CAAA;AAErD,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,UAAU,EAAE,CAAC;IACb,qBAAqB,EAAE,EAAE;IACzB,oBAAoB,EAAE,EAAE;IACxB,sBAAsB,EAAE,EAAE;IAC1B,qBAAqB,EAAE,EAAE;IACzB,kBAAkB,EAAE,CAAC;IACrB,iBAAiB,EAAE,KAAK;CAChB,CAAA;AAEV,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,+FAA+F;IAC/F,oGAAoG;IACpG,0GAA0G;IAC1G,yEAAyE;IACzE,2HAA2H;IAC3H,0KAA0K;CAClK,CAAA"}
@@ -0,0 +1,23 @@
1
+ import { type ConnectionInfo, type NextOtelVerboseStatus, type StackNextConfig } from '@stacknext/core';
2
+ import type { EnvironmentDifference, EnvironmentSnapshot } from './schema.js';
3
+ export interface BuildEnvironmentInput {
4
+ config: StackNextConfig;
5
+ connections?: ConnectionInfo[];
6
+ verbose?: NextOtelVerboseStatus;
7
+ nodeVersion?: string;
8
+ platform?: string;
9
+ arch?: string;
10
+ observedDatabases?: string[];
11
+ playwrightVersion?: string;
12
+ }
13
+ /**
14
+ * Captures only environment facts that can change runtime measurements. No
15
+ * hostnames, usernames, tokens or machine identifiers are stored.
16
+ */
17
+ export declare function buildEnvironmentSnapshot(input: BuildEnvironmentInput): EnvironmentSnapshot;
18
+ /**
19
+ * Compares the environment recorded with each side. Any difference defaults to
20
+ * an inconclusive comparison unless the caller explicitly overrides it.
21
+ */
22
+ export declare function compareEnvironments(baseline: EnvironmentSnapshot, candidate: EnvironmentSnapshot): EnvironmentDifference[];
23
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAE7E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,eAAe,CAAA;IACvB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;IAC9B,OAAO,CAAC,EAAE,qBAAqB,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAuBD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,qBAAqB,GAC3B,mBAAmB,CAwBrB;AAwBD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,mBAAmB,GAC7B,qBAAqB,EAAE,CAczB"}
@@ -0,0 +1,86 @@
1
+ import { STACKNEXT_SDK_VERSION, } from '@stacknext/core';
2
+ function latestConnection(connections) {
3
+ if (!connections || connections.length === 0)
4
+ return null;
5
+ return [...connections].sort((a, b) => b.lastSeen - a.lastSeen)[0] ?? null;
6
+ }
7
+ function databaseNames(config, observed) {
8
+ const names = new Set();
9
+ if (config.database.drizzle)
10
+ names.add('drizzle');
11
+ if (config.database.prisma)
12
+ names.add('prisma');
13
+ for (const name of observed ?? []) {
14
+ const normalized = name.trim().toLowerCase();
15
+ if (normalized !== '')
16
+ names.add(normalized);
17
+ }
18
+ return [...names].sort();
19
+ }
20
+ /**
21
+ * Captures only environment facts that can change runtime measurements. No
22
+ * hostnames, usernames, tokens or machine identifiers are stored.
23
+ */
24
+ export function buildEnvironmentSnapshot(input) {
25
+ const connection = latestConnection(input.connections);
26
+ const verbose = input.verbose ?? {
27
+ enabled: input.config.instrumentation.verbose,
28
+ source: input.config.instrumentation.verbose ? 'config' : 'disabled',
29
+ };
30
+ return {
31
+ nodeVersion: connection?.runtimeVersion ?? input.nodeVersion ?? process.version,
32
+ nextVersion: connection?.nextVersion ?? null,
33
+ platform: input.platform ?? process.platform,
34
+ arch: input.arch ?? process.arch,
35
+ runtime: 'nodejs',
36
+ databases: databaseNames(input.config, input.observedDatabases),
37
+ instrumentation: {
38
+ verbose: verbose.enabled,
39
+ verboseSource: verbose.source,
40
+ captureFetch: input.config.instrumentation.captureFetch,
41
+ captureErrors: input.config.instrumentation.captureErrors,
42
+ },
43
+ sdkVersion: connection?.sdkVersion ?? STACKNEXT_SDK_VERSION,
44
+ ...(input.playwrightVersion
45
+ ? { playwrightVersion: input.playwrightVersion.slice(0, 100) }
46
+ : {}),
47
+ };
48
+ }
49
+ function valueOrNone(value) {
50
+ return value && value !== '' ? value : 'none';
51
+ }
52
+ function environmentValues(environment) {
53
+ return [
54
+ ['nodeVersion', environment.nodeVersion],
55
+ ['nextVersion', valueOrNone(environment.nextVersion)],
56
+ ['platform', environment.platform],
57
+ ['arch', environment.arch],
58
+ ['runtime', environment.runtime],
59
+ ['databases', environment.databases.join(',') || 'none'],
60
+ ['instrumentation.verbose', environment.instrumentation.verbose ? 'enabled' : 'disabled'],
61
+ ['instrumentation.captureFetch', environment.instrumentation.captureFetch ? 'enabled' : 'disabled'],
62
+ ['instrumentation.captureErrors', environment.instrumentation.captureErrors ? 'enabled' : 'disabled'],
63
+ ['sdkVersion', environment.sdkVersion],
64
+ ['playwrightVersion', valueOrNone(environment.playwrightVersion)],
65
+ ];
66
+ }
67
+ /**
68
+ * Compares the environment recorded with each side. Any difference defaults to
69
+ * an inconclusive comparison unless the caller explicitly overrides it.
70
+ */
71
+ export function compareEnvironments(baseline, candidate) {
72
+ const candidateValues = new Map(environmentValues(candidate));
73
+ const differences = [];
74
+ for (const [field, baselineValue] of environmentValues(baseline)) {
75
+ const candidateValue = candidateValues.get(field) ?? 'none';
76
+ if (baselineValue !== candidateValue) {
77
+ differences.push({
78
+ field,
79
+ baseline: baselineValue.slice(0, 500),
80
+ candidate: candidateValue.slice(0, 500),
81
+ });
82
+ }
83
+ }
84
+ return differences.sort((a, b) => (a.field < b.field ? -1 : a.field > b.field ? 1 : 0));
85
+ }
86
+ //# sourceMappingURL=environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { type StackNextSpan, type TraceDetail } from '@stacknext/core';
2
+ import { diagnosticSpanCategory } from '@stacknext/diagnostics';
3
+ import type { ScenarioIdentity } from './schema.js';
4
+ /** Removes dynamic path segments so trace and span identities survive test data changes. */
5
+ export declare function normalizePathLike(value: string): string;
6
+ export declare function normalizeSpanName(value: string): string;
7
+ /**
8
+ * Scenario ids are stable hashes. An explicit `stacknext` Playwright annotation
9
+ * wins; Playwright's stable test id is the next choice; file/title/project is
10
+ * the final fallback. The raw annotation value is never used as a path or id.
11
+ */
12
+ export declare function scenarioIdentity(input: {
13
+ title: string;
14
+ file: string;
15
+ project: string;
16
+ tags?: string[];
17
+ testId?: string;
18
+ annotation?: string;
19
+ rootDir?: string;
20
+ }): ScenarioIdentity;
21
+ export declare function normalizeScenarioFile(file: string, rootDir?: string): string;
22
+ export declare function traceIdentity(detail: Pick<TraceDetail, 'request' | 'spans'>): {
23
+ key: string;
24
+ method: string | null;
25
+ route: string | null;
26
+ rootName: string;
27
+ };
28
+ export declare function spanIdentity(span: StackNextSpan): {
29
+ key: string;
30
+ name: string;
31
+ category: ReturnType<typeof diagnosticSpanCategory>;
32
+ };
33
+ /** Redacts local account paths and URL query values from stored command arguments. */
34
+ export declare function sanitizeCommandArg(value: string): string;
35
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,aAAa,EAElB,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,sBAAsB,EAKvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAiBnD,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASvD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,GAAG,gBAAgB,CAwBnB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAY5E;AAoBD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,OAAO,CAAC,GAC7C;IACD,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAaA;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG;IACjD,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAA;CACpD,CAgBA;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOxD"}
@@ -0,0 +1,121 @@
1
+ import { createHash } from 'node:crypto';
2
+ import path from 'node:path';
3
+ import { redactText, stripUrlQueryInText, } from '@stacknext/core';
4
+ import { diagnosticSpanCategory, safeRoute, safeSpanName, spanHttpEndpoint, spanHttpMethod, } from '@stacknext/diagnostics';
5
+ const ID_SEGMENT_PATTERNS = [
6
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
7
+ /^[0-9a-f]{16,}$/i,
8
+ /^[a-zA-Z0-9_-]{32,}$/,
9
+ /^\d+$/,
10
+ ];
11
+ function shortHash(value) {
12
+ return createHash('sha256').update(value).digest('hex').slice(0, 16);
13
+ }
14
+ function safeText(value, maxLength) {
15
+ return stripUrlQueryInText(redactText(value, maxLength));
16
+ }
17
+ /** Removes dynamic path segments so trace and span identities survive test data changes. */
18
+ export function normalizePathLike(value) {
19
+ const withoutQuery = safeText(value, 1_000);
20
+ const segments = withoutQuery.split('/');
21
+ return segments
22
+ .map((segment, index) => {
23
+ if (index === 0)
24
+ return segment;
25
+ return ID_SEGMENT_PATTERNS.some((pattern) => pattern.test(segment)) ? ':id' : segment;
26
+ })
27
+ .join('/');
28
+ }
29
+ export function normalizeSpanName(value) {
30
+ return normalizePathLike(value).replace(/\s+/g, ' ').trim().slice(0, 300);
31
+ }
32
+ /**
33
+ * Scenario ids are stable hashes. An explicit `stacknext` Playwright annotation
34
+ * wins; Playwright's stable test id is the next choice; file/title/project is
35
+ * the final fallback. The raw annotation value is never used as a path or id.
36
+ */
37
+ export function scenarioIdentity(input) {
38
+ const file = normalizeScenarioFile(input.file, input.rootDir);
39
+ const title = safeText(input.title, 500);
40
+ const project = safeText(input.project || 'default', 200);
41
+ const annotation = input.annotation?.trim();
42
+ const testId = input.testId?.trim();
43
+ const source = annotation
44
+ ? 'annotation'
45
+ : testId
46
+ ? 'test-id'
47
+ : 'title';
48
+ const basis = annotation
49
+ ? `annotation\u0000${annotation}`
50
+ : testId
51
+ ? `test-id\u0000${testId}`
52
+ : `title\u0000${file}\u0000${title}\u0000${project}`;
53
+ return {
54
+ id: `scn-${shortHash(basis)}`,
55
+ source,
56
+ title,
57
+ file,
58
+ project,
59
+ tags: [...new Set((input.tags ?? []).map((tag) => safeText(tag, 100)))].slice(0, 20),
60
+ };
61
+ }
62
+ export function normalizeScenarioFile(file, rootDir) {
63
+ let value = file.replace(/\\/g, '/');
64
+ if (path.isAbsolute(value)) {
65
+ if (rootDir) {
66
+ const relative = path.relative(rootDir, value).replace(/\\/g, '/');
67
+ value = relative.startsWith('..') ? path.basename(value) : relative;
68
+ }
69
+ else {
70
+ value = path.basename(value);
71
+ }
72
+ }
73
+ value = value.replace(/^\.\//, '');
74
+ return value === '' ? 'unknown.spec.ts' : value.slice(0, 1_000);
75
+ }
76
+ function compareText(left, right) {
77
+ return left < right ? -1 : left > right ? 1 : 0;
78
+ }
79
+ function traceRoot(detail) {
80
+ const spanIds = new Set(detail.spans.map((span) => span.spanId));
81
+ const roots = detail.spans.filter((span) => !span.parentSpanId || !spanIds.has(span.parentSpanId));
82
+ const candidates = roots.length > 0 ? roots : detail.spans;
83
+ return [...candidates].sort((left, right) => left.startTime - right.startTime || compareText(left.spanId, right.spanId))[0];
84
+ }
85
+ export function traceIdentity(detail) {
86
+ const root = traceRoot(detail);
87
+ const request = detail.request;
88
+ const optionRoute = safeRoute(request?.route ?? root?.route);
89
+ const rootName = safeSpanName(root?.name ?? request?.name ?? 'trace');
90
+ const route = optionRoute ?? (rootName ? safeRoute(rootName) : null);
91
+ const method = request?.method ?? root?.method ?? null;
92
+ return {
93
+ key: [method ?? '', route ?? '', normalizeSpanName(rootName)].join('|').slice(0, 2_000),
94
+ method,
95
+ route,
96
+ rootName,
97
+ };
98
+ }
99
+ export function spanIdentity(span) {
100
+ const category = diagnosticSpanCategory(span);
101
+ const name = safeSpanName(span.name);
102
+ const method = category === 'http' ? spanHttpMethod(span) ?? '' : '';
103
+ const endpoint = category === 'http' ? spanHttpEndpoint(span) ?? normalizeSpanName(name) : normalizeSpanName(name);
104
+ const key = [
105
+ category,
106
+ span.kind,
107
+ span.nextSpanType ?? '',
108
+ method,
109
+ endpoint,
110
+ ]
111
+ .join('|')
112
+ .slice(0, 2_000);
113
+ return { key, name, category };
114
+ }
115
+ /** Redacts local account paths and URL query values from stored command arguments. */
116
+ export function sanitizeCommandArg(value) {
117
+ let output = stripUrlQueryInText(redactText(value, 500));
118
+ output = output.replace(/^(?:[A-Za-z]:\\Users\\[^\\]+|\/home\/[^/]+|\/Users\/[^/]+)/, '~');
119
+ return output;
120
+ }
121
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export * from './constants.js';
2
+ export * from './schema.js';
3
+ export * from './identity.js';
4
+ export * from './capture.js';
5
+ export * from './environment.js';
6
+ export * from './stats.js';
7
+ export * from './storage.js';
8
+ export * from './compare.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export * from './constants.js';
2
+ export * from './schema.js';
3
+ export * from './identity.js';
4
+ export * from './capture.js';
5
+ export * from './environment.js';
6
+ export * from './stats.js';
7
+ export * from './storage.js';
8
+ export * from './compare.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA"}