2g 0.4.2 → 0.4.3
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/CHANGELOG.md +7 -0
- package/README.md +3 -3
- package/dist/2g.d.ts +1 -0
- package/dist/2g.js +385 -327
- package/dist/2g.js.map +1 -1
- package/dist/2g.mjs +327 -269
- package/dist/2g.mjs.map +1 -1
- package/dist/chunks/api-chunk.js +8 -4
- package/dist/chunks/api-chunk.js.map +1 -1
- package/dist/chunks/api-chunk.mjs +9 -5
- package/dist/chunks/api-chunk.mjs.map +1 -1
- package/dist/chunks/cli-chunk.js +1 -1
- package/dist/chunks/cli-chunk.js.map +1 -1
- package/dist/chunks/cli-chunk.mjs +7 -7
- package/dist/chunks/cli-chunk.mjs.map +1 -1
- package/dist/chunks/sessionSockets-chunk.js +2 -0
- package/dist/chunks/sessionSockets-chunk.js.map +1 -1
- package/dist/chunks/sessionSockets-chunk.mjs +16 -14
- package/dist/chunks/sessionSockets-chunk.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-chunk.mjs","sources":["../../src/commands/clean/index.ts","../../src/commands/shared.ts","../../src/commands/export/chromeTrace.ts","../../src/commands/export/opentelemetry.ts","../../src/commands/export/index.ts","../../src/commands/ps/index.ts","../../src/commands/record/index.ts","../../src/commands/tap/index.ts","../../src/cli.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\n\nimport { cleanExitedSessionsSync, cleanStaleSessionsSync } from '../../clean';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport function runCleanCli(args: string[] = []) {\n const options = parseCleanOptions(args);\n if (options.json) redirectConsoleToStderr();\n const removed = options.all\n ? cleanExitedSessionsSync()\n : cleanStaleSessionsSync();\n process.stdout.write(\n options.json\n ? `${JSON.stringify({ removed })}\\n`\n : `Removed ${removed} ${options.all ? 'exited' : 'stale'} sessions.\\n`\n );\n}\n\nfunction parseCleanOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n options: {\n all: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n return {\n all: parsed.values.all === true,\n json: parsed.values.json === true,\n };\n}\n\nexport function printCleanHelp() {\n process.stdout.write(\n [\n 'Usage: 2g clean',\n '',\n 'Removes stale exited sessions past the retention window.',\n '',\n 'Options:',\n ' --all, -a Remove all exited sessions',\n ' --json',\n '',\n ].join('\\n')\n );\n}\n","import fs from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Readable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport { detectRotationLoss, type ListedSession } from '../tap';\nimport {\n compileEventFilter,\n matchesTapOptions,\n parseEventLine,\n parseSince,\n parseDuration as parseDurationValue,\n} from '../utils/eventFilter';\nimport type { ParsedEvent } from '../types';\n\nexport const tapArgOptions = {\n since: { type: 'string' },\n filter: { type: 'string', multiple: true },\n spans: { type: 'boolean' },\n debug: { type: 'boolean' },\n tail: { type: 'boolean' },\n} as const;\n\nexport interface SharedTapOptions {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n follow: boolean;\n}\n\n// Warns (via console.warn → stderr, so it never pollutes the JSON/JSONL on\n// stdout, and stays mockable in tests) when segment rotation has already\n// dropped the start of the run from the retained window.\nexport async function warnOnRotationLoss(sessionDir: string, remedy: string) {\n if (!(await detectRotationLoss(sessionDir))) return;\n console.warn(\n `⚠ Earlier events were rotated out of the retained window.\\n` +\n ` For a complete trace, start \\`${remedy}\\` before the workload.`\n );\n}\n\nexport function formatSessionProcessName(session: ListedSession) {\n return session.version\n ? `${session.command} (v${session.version}, PID ${session.pid})`\n : `${session.command} (PID ${session.pid})`;\n}\n\nexport async function* readJsonlFile(file: string) {\n yield* readJsonlStream(fs.createReadStream(file, { encoding: 'utf8' }));\n}\n\nexport async function* readJsonlStdin() {\n yield* readJsonlStream(process.stdin);\n}\n\nexport async function* readJsonlStream(stream: Readable) {\n const rl = createInterface({ input: stream });\n for await (const line of rl) {\n if (!line) continue;\n // Lossless parse; the debug decision belongs to filterEvents\n const event = parseEventLine(line, { debug: true });\n if (event) yield event;\n }\n}\n\nexport async function* filterEvents(\n events: AsyncIterable<ParsedEvent> | Iterable<ParsedEvent>,\n options: Pick<SharedTapOptions, 'since' | 'filter' | 'spans' | 'debug'>\n) {\n const eventFilter = compileEventFilter(options.filter);\n const since = parseSince(options.since);\n for await (const event of events) {\n if (\n matchesTapOptions(\n event,\n {\n filter: options.filter,\n since,\n spans: options.spans,\n debug: options.debug,\n },\n eventFilter\n )\n )\n yield event;\n }\n}\n\nexport function parseSharedTapOptions(values: {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n tail?: boolean;\n}): SharedTapOptions {\n return {\n since: values.since,\n filter: values.filter\n ?.flatMap(value => value.split(','))\n .map(item => item.trim())\n .filter(Boolean),\n spans: values.spans === true,\n debug: values.debug === true,\n follow: values.tail === true,\n };\n}\n\nexport function parseHelp(args: string[]) {\n return (\n parseArgs({\n args,\n allowPositionals: true,\n strict: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n },\n }).values.help === true\n );\n}\n\nexport function parseJsonFlag(args: string[]) {\n return parseArgs({\n args,\n allowPositionals: true,\n options: {\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n }).values;\n}\n\nexport function parseDuration(value?: string) {\n if (value == null || value === '') return undefined;\n const duration = parseDurationValue(value);\n if (duration == null) throw new Error(`Invalid duration: ${value}`);\n return duration;\n}\n","import type { ParsedEvent } from '../../types';\n\nexport interface ConvertToChromeTraceOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface TraceFile {\n traceEvents: TraceEvent[];\n metadata: {\n source: '2g';\n version: string;\n convertedAt: string;\n startTime: string;\n startTimestampMs: number;\n };\n}\n\nexport type TraceEvent = TraceComplete | TraceInstant | TraceMetadata;\n\nexport interface TraceComplete {\n ph: 'X';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n}\n\nexport interface TraceInstant {\n ph: 'i';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n s: 'g' | 'p' | 't';\n args?: Record<string, unknown>;\n}\n\nexport interface TraceMetadata {\n ph: 'M';\n name: 'process_name' | 'thread_name' | 'thread_sort_index';\n pid: number;\n tid?: number;\n args: Record<string, unknown>;\n}\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToChromeTrace(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToChromeTraceOptions = {}\n): Promise<TraceFile> {\n const converter = new TraceConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toTraceFile();\n}\n\ninterface PendingSpan {\n track: string;\n name: string;\n cat: string;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n lane: number;\n}\n\ninterface PendingInstant {\n track: string;\n name: string;\n cat: string;\n ts: number;\n args?: Record<string, unknown>;\n}\n\ntype PendingEvent =\n | ({ kind: 'span' } & PendingSpan)\n | ({ kind: 'instant' } & PendingInstant);\n\nexport class TraceConverter {\n #pending: PendingEvent[] = [];\n #tracks = new Map<string, string>();\n #pid: number;\n #processName: string;\n #version = '0.1.0';\n\n constructor(options: ConvertToChromeTraceOptions = {}) {\n this.#pid = options.pid ?? 1;\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') {\n this.#version = event.version;\n if (this.#processName === '2g') {\n this.#processName = `2g (v${event.version})`;\n }\n }\n return;\n }\n\n const parsed = splitEventName(event._e);\n const track = this.#getTrack(\n parsed.category,\n typeof event._w === 'string' ? event._w : undefined\n );\n const args = extractArgs(event);\n\n if (typeof event._d === 'number') {\n this.#pending.push({\n kind: 'span',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round((event._t - event._d) * 1000),\n dur: Math.round(event._d * 1000),\n args,\n lane: 0,\n });\n } else {\n this.#pending.push({\n kind: 'instant',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round(event._t * 1000),\n args,\n });\n }\n }\n\n toTraceFile(): TraceFile {\n let base = this.#pending.length ? Infinity : 0;\n for (const event of this.#pending) {\n if (event.ts < base) base = event.ts;\n }\n const traceEvents: TraceEvent[] = [\n {\n ph: 'M',\n name: 'process_name',\n pid: this.#pid,\n args: { name: this.#processName },\n },\n ];\n\n const tids = new Map<string, number>();\n let nextTid = 1;\n for (const [track, displayName] of this.#tracks) {\n const spans: PendingSpan[] = [];\n for (const event of this.#pending) {\n if (event.kind === 'span' && event.track === track) spans.push(event);\n }\n const laneCount = Math.max(assignLanes(spans), 1);\n for (let lane = 0; lane < laneCount; lane++) {\n const tid = nextTid++;\n tids.set(`${track}\\0${lane}`, tid);\n traceEvents.push(\n {\n ph: 'M',\n name: 'thread_name',\n pid: this.#pid,\n tid,\n args: {\n name: lane === 0 ? displayName : `${displayName} #${lane + 1}`,\n },\n },\n {\n ph: 'M',\n name: 'thread_sort_index',\n pid: this.#pid,\n tid,\n args: { sort_index: tid },\n }\n );\n }\n }\n\n for (const event of this.#pending) {\n const lane = event.kind === 'span' ? event.lane : 0;\n const tid = tids.get(`${event.track}\\0${lane}`)!;\n if (event.kind === 'span') {\n traceEvents.push({\n ph: 'X',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n dur: event.dur,\n args: event.args,\n });\n } else {\n traceEvents.push({\n ph: 'i',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n s: 't',\n args: event.args,\n });\n }\n }\n\n return {\n traceEvents,\n metadata: {\n source: '2g',\n version: this.#version,\n convertedAt: new Date().toISOString(),\n startTime: new Date(base / 1000).toISOString(),\n startTimestampMs: base / 1000,\n },\n };\n }\n\n #getTrack(category: string, worker?: string) {\n const key = worker ? `${category}:${worker}` : category;\n if (!this.#tracks.has(key)) {\n this.#tracks.set(key, worker ? `${category} ${worker}` : category);\n }\n return key;\n }\n}\n\n// 'X' events on one tid nest by containment; partial overlap is invalid.\nfunction assignLanes(spans: PendingSpan[]) {\n const order = [...spans].sort(\n (a, b) => a.ts - b.ts || b.ts + b.dur - (a.ts + a.dur)\n );\n const lanes: PendingSpan[][] = [];\n for (const span of order) {\n const end = span.ts + span.dur;\n let lane = -1;\n for (let index = 0; index < lanes.length; index++) {\n const stack = lanes[index];\n while (\n stack.length &&\n stack[stack.length - 1].ts + stack[stack.length - 1].dur <= span.ts\n ) {\n stack.pop();\n }\n const top = stack[stack.length - 1];\n if (!top || top.ts + top.dur >= end) {\n stack.push(span);\n lane = index;\n break;\n }\n }\n if (lane < 0) {\n lanes.push([span]);\n lane = lanes.length - 1;\n }\n span.lane = lane;\n }\n return lanes.length;\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return { category: 'uncategorized', name: value };\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n\nfunction extractArgs(event: ParsedEvent) {\n const args: Record<string, unknown> = {};\n for (const key of Object.keys(event)) {\n if (key.startsWith('_')) continue;\n args[key] = event[key];\n }\n return Object.keys(args).length ? args : undefined;\n}\n","import crypto from 'node:crypto';\n\nimport type { ParsedEvent } from '../../types';\n\nexport interface ConvertToOpenTelemetryOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface OpenTelemetryFile {\n resourceSpans: OpenTelemetryResourceSpan[];\n}\n\ninterface OpenTelemetryResourceSpan {\n resource: {\n attributes: OpenTelemetryAttribute[];\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n };\n spans: OpenTelemetrySpan[];\n }>;\n}\n\ninterface OpenTelemetrySpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes?: OpenTelemetryAttribute[];\n events?: OpenTelemetrySpanEvent[];\n}\n\ninterface OpenTelemetrySpanEvent {\n timeUnixNano: string;\n name: string;\n attributes?: OpenTelemetryAttribute[];\n}\n\ninterface OpenTelemetryAttribute {\n key: string;\n value: OpenTelemetryAnyValue;\n}\n\ntype OpenTelemetryAnyValue =\n | { stringValue: string }\n | { intValue: string }\n | { doubleValue: number }\n | { boolValue: boolean }\n | { arrayValue: { values: OpenTelemetryAnyValue[] } }\n | { kvlistValue: { values: OpenTelemetryAttribute[] } };\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToOpenTelemetry(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToOpenTelemetryOptions = {}\n): Promise<OpenTelemetryFile> {\n const converter = new OpenTelemetryConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toFile();\n}\n\nclass OpenTelemetryConverter {\n readonly #traceId = crypto.randomBytes(16).toString('hex');\n readonly #sessionSpanId = createSpanId();\n readonly #spans: OpenTelemetrySpan[] = [];\n readonly #events: OpenTelemetrySpanEvent[] = [];\n #processName: string;\n #version = '0.1.0';\n #startTimeUnixNano: string | undefined;\n #endTimeUnixNano: string | undefined;\n\n constructor(private readonly options: ConvertToOpenTelemetryOptions) {\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n const eventTime = unixNano(event._t);\n this.#startTimeUnixNano =\n this.#startTimeUnixNano == null ||\n BigInt(eventTime) < BigInt(this.#startTimeUnixNano)\n ? eventTime\n : this.#startTimeUnixNano;\n this.#endTimeUnixNano =\n this.#endTimeUnixNano == null ||\n BigInt(eventTime) > BigInt(this.#endTimeUnixNano)\n ? eventTime\n : this.#endTimeUnixNano;\n\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') this.#version = event.version;\n if (this.#processName === '2g' && typeof event.version === 'string') {\n this.#processName = `2g (v${event.version})`;\n }\n return;\n }\n\n if (typeof event._d === 'number') {\n this.#spans.push(this.#createSpan(event));\n } else {\n this.#events.push(this.#createEvent(event));\n }\n }\n\n toFile(): OpenTelemetryFile {\n const startTimeUnixNano = this.#startTimeUnixNano ?? unixNano(Date.now());\n const endTimeUnixNano = this.#endTimeUnixNano ?? startTimeUnixNano;\n return {\n resourceSpans: [\n {\n resource: {\n attributes: attributes({\n 'service.name': this.#processName,\n 'service.version': this.#version,\n 'process.pid': this.options.pid,\n 'telemetry.sdk.name': '2g',\n 'telemetry.sdk.language': 'nodejs',\n }),\n },\n scopeSpans: [\n {\n scope: { name: '2g' },\n spans: [\n {\n traceId: this.#traceId,\n spanId: this.#sessionSpanId,\n name: this.#processName,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: attributes({\n 'event_log.kind': 'session',\n }),\n events: this.#events.length ? this.#events : undefined,\n },\n ...this.#spans,\n ],\n },\n ],\n },\n ],\n };\n }\n\n #createSpan(event: ParsedEvent): OpenTelemetrySpan {\n const parsed = splitEventName(event._e);\n const startTimeUnixNano = unixNano(event._t - event._d!);\n const endTimeUnixNano = unixNano(event._t);\n return {\n traceId: this.#traceId,\n spanId: createSpanId(),\n parentSpanId: this.#sessionSpanId,\n name: parsed ? stripSuffix(parsed.name) : event._e,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: eventAttributes(event, parsed),\n };\n }\n\n #createEvent(event: ParsedEvent): OpenTelemetrySpanEvent {\n const parsed = splitEventName(event._e);\n return {\n timeUnixNano: unixNano(event._t),\n name: parsed ? stripSuffix(parsed.name) : event._e,\n attributes: eventAttributes(event, parsed),\n };\n }\n}\n\nfunction eventAttributes(\n event: ParsedEvent,\n parsed: ReturnType<typeof splitEventName>\n) {\n const values: Record<string, unknown> = {\n 'event.name': event._e,\n };\n if (parsed) {\n values['event.category'] = parsed.category;\n values['event.action'] = parsed.name;\n }\n if (typeof event._w === 'string') values['event.worker.id'] = event._w;\n\n for (const key of Object.keys(event)) {\n if (\n key === '_e' ||\n key === '_t' ||\n key === '_d' ||\n key === '_l' ||\n key === '_w'\n )\n continue;\n values[`event_log.${key}`] = event[key];\n }\n\n return attributes(values);\n}\n\nfunction attributes(values: Record<string, unknown>): OpenTelemetryAttribute[] {\n const result: OpenTelemetryAttribute[] = [];\n for (const [key, value] of Object.entries(values)) {\n const converted = anyValue(value);\n if (converted) result.push({ key, value: converted });\n }\n return result;\n}\n\nfunction anyValue(value: unknown): OpenTelemetryAnyValue | null {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return null;\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value };\n }\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value.flatMap(item => {\n const converted = anyValue(item);\n return converted ? [converted] : [];\n }),\n },\n };\n }\n if (value && typeof value === 'object') {\n return {\n kvlistValue: {\n values: attributes(value as Record<string, unknown>),\n },\n };\n }\n return null;\n}\n\nfunction createSpanId() {\n return crypto.randomBytes(8).toString('hex');\n}\n\nfunction unixNano(timeMs: number) {\n return String(BigInt(Math.round(timeMs * 1_000_000)));\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return null;\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n","import fs from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport type { ParsedEvent } from '../../types';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport {\n filterEvents,\n formatSessionProcessName,\n parseHelp,\n parseDuration,\n parseSharedTapOptions,\n readJsonlFile,\n readJsonlStdin,\n tapArgOptions,\n warnOnRotationLoss,\n} from '../shared';\nimport { convertToChromeTrace } from './chromeTrace';\nimport { convertToOpenTelemetry } from './opentelemetry';\n\ntype ExportFormat = 'chrome-trace' | 'opentelemetry';\nconst DEFAULT_IDLE_TIMEOUT = 30_000;\n\nexport async function runExportCli(args: string[]) {\n if (parseHelp(args)) {\n printExportHelp();\n return;\n }\n const options = parseExportOptions(args);\n if (options.json) redirectConsoleToStderr();\n let events: AsyncIterable<ParsedEvent> | ParsedEvent[];\n let processName: string | undefined;\n let exportPid: number | undefined;\n\n if (options.selector) {\n const session = await resolveSession(options.selector);\n processName = formatSessionProcessName(session);\n exportPid = session.pid;\n await warnOnRotationLoss(session.sessionDir, '2g export --tail');\n events = tap(session.sessionDir, {\n follow: options.follow,\n debug: options.debug,\n timeout: options.timeout,\n idleTimeout: options.follow ? options.idleTimeout : undefined,\n });\n } else if (options.inputFile) {\n events = readJsonlFile(options.inputFile);\n } else {\n events = readJsonlStdin();\n }\n\n const filteredEvents = filterEvents(events, options);\n const file =\n options.format === 'opentelemetry'\n ? await convertToOpenTelemetry(filteredEvents, {\n pid: exportPid,\n processName,\n })\n : await convertToChromeTrace(filteredEvents, {\n pid: exportPid,\n processName,\n });\n const output = JSON.stringify(file, null, 2);\n if (options.output) fs.writeFileSync(options.output, `${output}\\n`);\n else process.stdout.write(`${output}\\n`);\n}\n\nfunction parseExportOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n input: { type: 'string' },\n output: { type: 'string', short: 'o' },\n format: { type: 'string' },\n json: { type: 'boolean' },\n timeout: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n const timeout = parseDuration(parsed.values.timeout);\n\n const format = parsed.values.format ?? 'chrome-trace';\n if (!isExportFormat(format)) throw new Error(`Unsupported format: ${format}`);\n if (parsed.positionals[0] && parsed.values.input)\n throw new Error('Use either a session selector or --input, not both');\n if (timeout != null && !tapOptions.follow)\n throw new Error('Use --timeout only with --tail');\n\n return {\n selector: parsed.positionals[0],\n inputFile: parsed.values.input,\n output: parsed.values.output,\n format,\n since: tapOptions.since,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n follow: tapOptions.follow,\n timeout,\n idleTimeout: DEFAULT_IDLE_TIMEOUT,\n json: parsed.values.json ?? false,\n };\n}\n\nfunction isExportFormat(value: string): value is ExportFormat {\n return value === 'chrome-trace' || value === 'opentelemetry';\n}\n\nfunction printExportHelp() {\n process.stdout.write(\n [\n 'Usage: 2g export [selector]',\n '',\n 'Exports session events to Chrome Trace or OTLP JSON.',\n '(Without --tail it reads only the retained window. Rotated out logs may be lost)',\n '',\n '--tail attaches to an already-running session and follows live from that point.',\n ' Run it after the session is up and leave it attached while the workload runs.',\n ' It will automatically stop on 30s idle.',\n 'To trace a full command instead, use `2g record`.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --input <file> Read JSONL events from a file',\n ' -o, --output <file> Write output to a file',\n ' --format <format> chrome-trace or opentelemetry',\n ' --json Print JSON output',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too. A',\n ' plain \"ms\" payload field is not a span',\n ' --debug Include debug-level events that were captured by a',\n ' command run with LOG_DEBUG. Normal app traces do not',\n ' need this',\n ' --tail Follow live events after replaying history; stops',\n ' automatically on 30s idle',\n ' --timeout <duration> Also stop tailing after this absolute duration',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { listSessions } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport async function runPsCli(args: string[] = []) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n active: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n if (parsed.values.json) redirectConsoleToStderr();\n const sessions = (\n await listSessions({ selector: parsed.positionals[0] })\n ).filter(session => !parsed.values.active || session.alive);\n if (parsed.values.json) {\n process.stdout.write(`${JSON.stringify(sessions, null, 2)}\\n`);\n return;\n }\n\n process.stdout.write('PID\\tSTATUS\\tSTARTED\\tCOMMAND\\tCWD\\n');\n for (const session of sessions) {\n process.stdout.write(\n `${escapeTsv(session.pid)}\\t${escapeTsv(\n session.alive ? 'alive' : 'exited'\n )}\\t${escapeTsv(\n new Date(session.startedAt).toISOString()\n )}\\t${escapeTsv(session.command)}\\t${escapeTsv(session.cwd)}\\n`\n );\n }\n}\n\nfunction escapeTsv(value: unknown) {\n return String(value)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\t/g, '\\\\t')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\n/g, '\\\\n');\n}\n\nexport function printPsHelp() {\n process.stdout.write(\n [\n 'Usage: 2g ps [selector]',\n '',\n 'Lists recorded sessions, newest first; alive means still running.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command',\n ' --active, -a Show only active sessions',\n ' --json Print sessions as JSON',\n '',\n ].join('\\n')\n );\n}\n","import { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { captureEvents } from '../../capture';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport { filterEvents, parseHelp } from '../shared';\nimport { convertToChromeTrace } from '../export/chromeTrace';\nimport { convertToOpenTelemetry } from '../export/opentelemetry';\n\ntype ExportFormat = 'chrome-trace' | 'opentelemetry';\n\n// `record` owns the process it traces: it spawns <command> with event capture\n// wired in, follows its events end-to-end, and writes the trace when it exits.\n// This is the reliable path for a one-off run — there is no window to attach\n// within and nothing to rotate out, so it never returns a partial trace.\nexport async function runRecordCli(args: string[]) {\n if (parseHelp(args)) {\n printRecordHelp();\n return;\n }\n\n // Everything after `--` is the command and its own arguments, verbatim\n const separator = args.indexOf('--');\n const ownArgs = separator === -1 ? args : args.slice(0, separator);\n const command = separator === -1 ? [] : args.slice(separator + 1);\n const options = parseRecordOptions(ownArgs);\n if (!command.length)\n throw new Error(\n 'record needs a command to run: 2g record [options] -- <command> [args...]'\n );\n\n if (options.json) redirectConsoleToStderr();\n\n const capture = captureEvents({\n debug: options.debug,\n filter: options.filter,\n });\n // The trace lands on stdout when there is no -o, so route the child's stdout\n // to stderr to keep it clean; with -o the child can use stdout normally.\n const child = spawn(\n command[0],\n command.slice(1),\n capture.spawnOptions({\n env: process.env,\n stdio: ['inherit', options.output ? 'inherit' : 2, 'inherit'],\n })\n );\n capture.attach(child);\n\n const onSignal = (signal: NodeJS.Signals) => {\n if (signal !== 'SIGINT') {\n try {\n child.kill(signal);\n } catch {}\n }\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n const exited = new Promise<number>((resolve, reject) => {\n child.on('exit', code => resolve(code ?? 0));\n child.on('error', error =>\n reject(new Error(`Failed to run ${command[0]}: ${error.message}`))\n );\n });\n\n const meta = { pid: child.pid, processName: command.join(' ') };\n const filtered = filterEvents(capture, options);\n\n try {\n // Draining the events resolves once the child closes its event pipe (on exit)\n const file =\n options.format === 'opentelemetry'\n ? await convertToOpenTelemetry(filtered, meta)\n : await convertToChromeTrace(filtered, meta);\n const exitCode = await exited;\n\n const output = JSON.stringify(file, null, 2);\n if (options.output) fs.writeFileSync(options.output, `${output}\\n`);\n else process.stdout.write(`${output}\\n`);\n\n if (exitCode) process.exitCode = exitCode;\n } finally {\n process.off('SIGINT', onSignal);\n process.off('SIGTERM', onSignal);\n }\n}\n\nfunction parseRecordOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n filter: { type: 'string', multiple: true },\n spans: { type: 'boolean' },\n debug: { type: 'boolean' },\n output: { type: 'string', short: 'o' },\n format: { type: 'string' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n\n const format = parsed.values.format ?? 'chrome-trace';\n if (!isExportFormat(format)) throw new Error(`Unsupported format: ${format}`);\n\n return {\n output: parsed.values.output,\n format,\n filter: parsed.values.filter\n ?.flatMap(value => value.split(','))\n .map(item => item.trim())\n .filter(Boolean),\n spans: parsed.values.spans === true,\n debug: parsed.values.debug === true,\n json: parsed.values.json ?? false,\n };\n}\n\nfunction isExportFormat(value: string): value is ExportFormat {\n return value === 'chrome-trace' || value === 'opentelemetry';\n}\n\nfunction printRecordHelp() {\n process.stdout.write(\n [\n 'Usage: 2g record [options] -- <command> [args...]',\n '',\n 'Runs <command>, traces the events it emits, and writes the trace on exit.',\n '',\n 'This is the reliable way to capture a one-off run (a build, a script): 2g',\n 'owns the process, so the trace is complete end-to-end with no window to',\n 'attach within and nothing to rotate out. To trace an already-running server',\n 'instead, use `2g export <selector> --tail`.',\n '',\n 'Options:',\n ' -o, --output <file> Write the trace to a file (default: stdout)',\n ' --format <format> chrome-trace or opentelemetry',\n ' --json Print JSON output',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too',\n ' --debug Enable and export debug events; no LOG_DEBUG needed',\n '',\n 'If you are validating instrumentation or looking for debug-level events, run',\n 'the recorded command with LOG_DEBUG and pass --debug, for example:',\n \" LOG_DEBUG='*' 2g record --debug -o trace.json -- <command>\",\n 'Normal app traces do not need this.',\n '',\n 'Example:',\n ' 2g record -o trace.json -- expo export # run & trace a build end-to-end',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport { formatPrettyEvent, shouldColorizeStream } from '../../utils/pretty';\nimport {\n parseHelp,\n parseSharedTapOptions,\n tapArgOptions,\n warnOnRotationLoss,\n} from '../shared';\n\nexport async function runTapCli(args: string[]) {\n if (parseHelp(args)) {\n printTapHelp();\n return;\n }\n const options = parseTapOptions(args);\n if (options.json) redirectConsoleToStderr();\n const session = await resolveSession(options.selector);\n await warnOnRotationLoss(session.sessionDir, '2g tap --tail');\n const colorizePretty = shouldColorizeStream(process.stdout);\n for await (const event of tap(session.sessionDir, options)) {\n if (options.pretty) {\n process.stdout.write(\n `${formatPrettyEvent(event, {\n colorize: colorizePretty,\n stream: process.stdout,\n })}\\n`\n );\n } else {\n process.stdout.write(`${JSON.stringify(event)}\\n`);\n }\n }\n}\n\nfunction parseTapOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n format: { type: 'string' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n if (parsed.values.json && parsed.values.format === 'pretty')\n throw new Error('Use either --json or --format pretty, not both');\n\n return {\n selector: parsed.positionals[0],\n since: tapOptions.since,\n follow: tapOptions.follow,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n json: parsed.values.json ?? false,\n pretty: parsed.values.json ? false : parsed.values.format === 'pretty',\n };\n}\n\nfunction printTapHelp() {\n process.stdout.write(\n [\n 'Usage: 2g tap [selector]',\n '',\n 'Replays retained session history as JSONL, oldest first.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too. A',\n ' plain \"ms\" payload field is not a span',\n ' --debug Include debug events (only recorded when the',\n ' command ran with LOG_DEBUG set)',\n ' --format pretty Print a readable line per event',\n ' --tail Follow live events after replaying history; runs',\n ' until killed (use export with a timeout for a',\n ' bounded follow)',\n '',\n ].join('\\n')\n );\n}\n","#!/usr/bin/env node\nimport { fileURLToPath } from 'node:url';\n\nimport { printCleanHelp, runCleanCli } from './commands/clean';\nimport { runExportCli } from './commands/export';\nimport { printPsHelp, runPsCli } from './commands/ps';\nimport { runRecordCli } from './commands/record';\nimport { runTapCli } from './commands/tap';\nimport { parseHelp } from './commands/shared';\n\nexport async function main(argv = process.argv.slice(2)) {\n const [command, ...args] = argv;\n\n if (\n !command ||\n command === '--help' ||\n command === '-h' ||\n command === 'help'\n ) {\n printHelp();\n return 0;\n }\n\n switch (command) {\n case 'typegen': {\n const { runTypegenCli } = await import('./commands/typegen');\n runTypegenCli(args);\n return 0;\n }\n case 'ps':\n if (parseHelp(args)) {\n printPsHelp();\n return 0;\n }\n await runPsCli(args);\n return 0;\n case 'tap':\n await runTapCli(args);\n return 0;\n case 'clean':\n if (parseHelp(args)) {\n printCleanHelp();\n return 0;\n }\n runCleanCli(args);\n return 0;\n case 'record':\n await runRecordCli(args);\n return 0;\n case 'export':\n await runExportCli(args);\n return 0;\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n\nfunction printHelp() {\n process.stdout.write(\n [\n 'Usage: 2g <command>',\n '',\n 'Reads structured JSONL event logs that instrumented CLIs record during runs',\n 'into the system temp dir, with no setup: run a command, then tap it here.',\n '',\n 'Commands:',\n ' ps [selector] List recorded sessions',\n ' --active, -a (running only) --json',\n '',\n ' tap [selector] Replay session events as JSONL, oldest first',\n ' --since <time> --filter <pattern> --spans --debug --format pretty',\n ' --tail (follow live events after replay; runs until killed)',\n '',\n ' record -- <command> Run a command, trace it end-to-end, write on exit',\n ' -o, --output <file> --format chrome-trace|opentelemetry --json',\n ' --filter <pattern> --spans --debug',\n '',\n ' export [selector] Export a past or live (--tail) session',\n ' --input <jsonl-file> -o, --output <file> --json',\n ' --format chrome-trace|opentelemetry',\n ' --since <time> --filter <pattern> --spans --debug',\n ' --tail --timeout <duration> (absolute; tail also stops after 30s idle)',\n '',\n ' typegen List every event name/payload a project can emit',\n ' -p, --project <tsconfig> -o, --output <file> --format dts|json --json',\n '',\n ' clean Remove stale sessions',\n ' --all, -a (remove all exited sessions) --json',\n '',\n 'Typical use:',\n ' 2g ps -a # what is running now?',\n ' 2g tap \"expo start\" --tail # follow live events',\n ' 2g tap \"expo start\" --since 5m --format pretty # recent history, readable',\n ' 2g tap \"expo start\" --filter metro:bundling # narrow to one area',\n '',\n 'Trace a run to a flame chart:',\n ' 2g record -o trace.json -- expo export # run & trace a build',\n ' 2g export \"expo start\" --tail -o trace.json # attach to a running server',\n ' 2g export \"expo start\" -o trace.json # after the fact (partial)',\n '',\n 'Selectors match sessions by substring of PID, CWD, or command. If several',\n 'match, and exactly one of them is running, it is chosen; otherwise 2g errors',\n 'and lists the candidates.',\n '',\n '--filter takes comma-separated event-name prefixes matched on whole',\n 'segments: \"category\" and \"category:kind\" both match \"category:kind:test\";',\n '* matches anything. Discover names by tapping unfiltered or via typegen.',\n '',\n '--since accepts a duration ago (5m, 90s, 1h30m), Unix time, or ISO date.',\n '--spans keeps only span events (log.span() pairs with a \"_d\" duration in',\n 'ms); without it, point events are included alongside spans.',\n '',\n 'Events are single JSON lines: {\"_e\":\"category:kind\",\"_t\":<epoch ms>,...}.',\n '\"_d\" is a span duration, \"_w\" a worker id; debug events need --debug.',\n '',\n 'Set LOG_EVENTS=<file|fd> on an instrumented command to redirect its events.',\n 'Debug events are only recorded when the command runs with LOG_DEBUG set.',\n '',\n 'Pass LOG_DEBUG=<filter> when debugging; it also mirrors matching events to',\n 'stderr. Reading them back needs --debug.',\n '',\n 'Run 2g <command> --help for per-command help.',\n '',\n ].join('\\n')\n );\n}\n\nif (isCliEntrypoint()) {\n main()\n .then(code => {\n process.exitCode = code;\n })\n .catch(error => {\n process.stderr.write(\n `${error instanceof Error ? error.message : String(error)}\\n`\n );\n process.exitCode = 1;\n });\n}\n\nfunction isCliEntrypoint() {\n try {\n return process.argv[1] === fileURLToPath(import.meta.url);\n } catch {\n return false;\n }\n}\n"],"names":["runCleanCli","args","options","parseCleanOptions","parsed","parseArgs","all","type","short","json","help","values","redirectConsoleToStderr","removed","cleanExitedSessionsSync","cleanStaleSessionsSync","process","stdout","write","JSON","stringify","tapArgOptions","since","filter","multiple","spans","debug","tail","async","warnOnRotationLoss","sessionDir","remedy","detectRotationLoss","console","warn","readJsonlStream","stream","rl","createInterface","input","line","event","parseEventLine","filterEvents","events","eventFilter","compileEventFilter","parseSince","matchesTapOptions","parseSharedTapOptions","flatMap","value","split","map","item","trim","Boolean","follow","parseHelp","allowPositionals","strict","STRIP_SUFFIXES","convertToChromeTrace","converter","TraceConverter","add","toTraceFile","pending","tracks","Map","pid","processName","version","constructor","this","_e","splitEventName","index","indexOf","category","name","slice","track","getTrack","_w","undefined","extractArgs","key","Object","keys","startsWith","length","_d","push","kind","stripSuffix","cat","ts","Math","round","_t","dur","lane","base","Infinity","traceEvents","ph","tids","nextTid","displayName","laneCount","max","assignLanes","tid","set","sort_index","get","s","metadata","source","convertedAt","Date","toISOString","startTime","startTimestampMs","worker","has","order","sort","a","b","lanes","span","end","stack","pop","top","suffix","endsWith","convertToOpenTelemetry","OpenTelemetryConverter","toFile","traceId","crypto","randomBytes","toString","sessionSpanId","createSpanId","startTimeUnixNano","endTimeUnixNano","eventTime","unixNano","BigInt","createSpan","createEvent","now","resourceSpans","resource","attributes","scopeSpans","scope","spanId","parentSpanId","eventAttributes","timeUnixNano","result","entries","converted","anyValue","stringValue","boolValue","Number","isFinite","isInteger","intValue","String","doubleValue","Array","isArray","arrayValue","kvlistValue","timeMs","runExportCli","printExportHelp","join","parseExportOptions","output","format","timeout","tapOptions","parseDuration","duration","parseDurationValue","Error","isExportFormat","positionals","selector","inputFile","idleTimeout","exportPid","session","resolveSession","formatSessionProcessName","command","tap","readJsonlFile","file","fs","createReadStream","encoding","readJsonlStdin","stdin","filteredEvents","writeFileSync","escapeTsv","replace","runRecordCli","printRecordHelp","separator","ownArgs","parseRecordOptions","capture","captureEvents","child","spawn","spawnOptions","env","stdio","attach","onSignal","signal","kill","on","exited","Promise","resolve","reject","code","error","message","meta","filtered","exitCode","off","runTapCli","printTapHelp","parseTapOptions","pretty","colorizePretty","shouldColorizeStream","formatPrettyEvent","colorize","main","argv","printHelp","runTypegenCli","import","printPsHelp","runPsCli","active","sessions","listSessions","alive","startedAt","cwd","printCleanHelp","isCliEntrypoint","fileURLToPath","url","then","catch","stderr"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-chunk.mjs","sources":["../../src/commands/clean/index.ts","../../src/commands/shared.ts","../../src/commands/export/chromeTrace.ts","../../src/commands/export/opentelemetry.ts","../../src/commands/export/index.ts","../../src/commands/ps/index.ts","../../src/commands/record/index.ts","../../src/commands/tap/index.ts","../../src/cli.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\n\nimport { cleanExitedSessionsSync, cleanStaleSessionsSync } from '../../clean';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport function runCleanCli(args: string[] = []) {\n const options = parseCleanOptions(args);\n if (options.json) redirectConsoleToStderr();\n const removed = options.all\n ? cleanExitedSessionsSync()\n : cleanStaleSessionsSync();\n process.stdout.write(\n options.json\n ? `${JSON.stringify({ removed })}\\n`\n : `Removed ${removed} ${options.all ? 'exited' : 'stale'} sessions.\\n`\n );\n}\n\nfunction parseCleanOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n options: {\n all: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n return {\n all: parsed.values.all === true,\n json: parsed.values.json === true,\n };\n}\n\nexport function printCleanHelp() {\n process.stdout.write(\n [\n 'Usage: 2g clean',\n '',\n 'Removes stale exited sessions past the retention window.',\n '',\n 'Options:',\n ' --all, -a Remove all exited sessions',\n ' --json',\n '',\n ].join('\\n')\n );\n}\n","import fs from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Readable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport { detectRotationLoss, type ListedSession } from '../tap';\nimport {\n compileEventFilter,\n matchesTapOptions,\n parseEventLine,\n parseSince,\n parseDuration as parseDurationValue,\n} from '../utils/eventFilter';\nimport type { ParsedEvent } from '../types';\n\nexport const tapArgOptions = {\n since: { type: 'string' },\n filter: { type: 'string', multiple: true },\n spans: { type: 'boolean' },\n debug: { type: 'boolean' },\n tail: { type: 'boolean' },\n} as const;\n\nexport interface SharedTapOptions {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n follow: boolean;\n}\n\n// Warns (via console.warn → stderr, so it never pollutes the JSON/JSONL on\n// stdout, and stays mockable in tests) when segment rotation has already\n// dropped the start of the run from the retained window.\nexport async function warnOnRotationLoss(sessionDir: string, remedy: string) {\n if (!(await detectRotationLoss(sessionDir))) return;\n console.warn(\n `⚠ Earlier events were rotated out of the retained window.\\n` +\n ` For a complete trace, start \\`${remedy}\\` before the workload.`\n );\n}\n\nexport function formatSessionProcessName(session: ListedSession) {\n return session.version\n ? `${session.command} (v${session.version}, PID ${session.pid})`\n : `${session.command} (PID ${session.pid})`;\n}\n\nexport async function* readJsonlFile(file: string) {\n yield* readJsonlStream(fs.createReadStream(file, { encoding: 'utf8' }));\n}\n\nexport async function* readJsonlStdin() {\n yield* readJsonlStream(process.stdin);\n}\n\nexport async function* readJsonlStream(stream: Readable) {\n const rl = createInterface({ input: stream });\n for await (const line of rl) {\n if (!line) continue;\n // Lossless parse; the debug decision belongs to filterEvents\n const event = parseEventLine(line, { debug: true });\n if (event) yield event;\n }\n}\n\nexport async function* filterEvents(\n events: AsyncIterable<ParsedEvent> | Iterable<ParsedEvent>,\n options: Pick<SharedTapOptions, 'since' | 'filter' | 'spans' | 'debug'>\n) {\n const eventFilter = compileEventFilter(options.filter);\n const since = parseSince(options.since);\n for await (const event of events) {\n if (\n matchesTapOptions(\n event,\n {\n filter: options.filter,\n since,\n spans: options.spans,\n debug: options.debug,\n },\n eventFilter\n )\n )\n yield event;\n }\n}\n\nexport function parseSharedTapOptions(values: {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n tail?: boolean;\n}): SharedTapOptions {\n return {\n since: values.since,\n filter: values.filter\n ?.flatMap(value => value.split(','))\n .map(item => item.trim())\n .filter(Boolean),\n spans: values.spans === true,\n debug: values.debug === true,\n follow: values.tail === true,\n };\n}\n\nexport function parseHelp(args: string[]) {\n return (\n parseArgs({\n args,\n allowPositionals: true,\n strict: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n },\n }).values.help === true\n );\n}\n\nexport function parseJsonFlag(args: string[]) {\n return parseArgs({\n args,\n allowPositionals: true,\n options: {\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n }).values;\n}\n\nexport function parseDuration(value?: string) {\n if (value == null || value === '') return undefined;\n const duration = parseDurationValue(value);\n if (duration == null) throw new Error(`Invalid duration: ${value}`);\n return duration;\n}\n","import type { ParsedEvent } from '../../types';\n\nexport interface ConvertToChromeTraceOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface TraceFile {\n traceEvents: TraceEvent[];\n metadata: {\n source: '2g';\n version: string;\n convertedAt: string;\n startTime: string;\n startTimestampMs: number;\n };\n}\n\nexport type TraceEvent = TraceComplete | TraceInstant | TraceMetadata;\n\nexport interface TraceComplete {\n ph: 'X';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n}\n\nexport interface TraceInstant {\n ph: 'i';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n s: 'g' | 'p' | 't';\n args?: Record<string, unknown>;\n}\n\nexport interface TraceMetadata {\n ph: 'M';\n name: 'process_name' | 'thread_name' | 'thread_sort_index';\n pid: number;\n tid?: number;\n args: Record<string, unknown>;\n}\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToChromeTrace(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToChromeTraceOptions = {}\n): Promise<TraceFile> {\n const converter = new TraceConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toTraceFile();\n}\n\ninterface PendingSpan {\n track: string;\n name: string;\n cat: string;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n lane: number;\n}\n\ninterface PendingInstant {\n track: string;\n name: string;\n cat: string;\n ts: number;\n args?: Record<string, unknown>;\n}\n\ntype PendingEvent =\n | ({ kind: 'span' } & PendingSpan)\n | ({ kind: 'instant' } & PendingInstant);\n\nexport class TraceConverter {\n #pending: PendingEvent[] = [];\n #tracks = new Map<string, string>();\n #pid: number;\n #processName: string;\n #version = '0.1.0';\n\n constructor(options: ConvertToChromeTraceOptions = {}) {\n this.#pid = options.pid ?? 1;\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') {\n this.#version = event.version;\n if (this.#processName === '2g') {\n this.#processName = `2g (v${event.version})`;\n }\n }\n return;\n }\n\n const parsed = splitEventName(event._e);\n const track = this.#getTrack(\n parsed.category,\n typeof event._w === 'string' ? event._w : undefined\n );\n const args = extractArgs(event);\n\n if (typeof event._d === 'number') {\n this.#pending.push({\n kind: 'span',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round((event._t - event._d) * 1000),\n dur: Math.round(event._d * 1000),\n args,\n lane: 0,\n });\n } else {\n this.#pending.push({\n kind: 'instant',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round(event._t * 1000),\n args,\n });\n }\n }\n\n toTraceFile(): TraceFile {\n let base = this.#pending.length ? Infinity : 0;\n for (const event of this.#pending) {\n if (event.ts < base) base = event.ts;\n }\n const traceEvents: TraceEvent[] = [\n {\n ph: 'M',\n name: 'process_name',\n pid: this.#pid,\n args: { name: this.#processName },\n },\n ];\n\n const tids = new Map<string, number>();\n let nextTid = 1;\n for (const [track, displayName] of this.#tracks) {\n const spans: PendingSpan[] = [];\n for (const event of this.#pending) {\n if (event.kind === 'span' && event.track === track) spans.push(event);\n }\n const laneCount = Math.max(assignLanes(spans), 1);\n for (let lane = 0; lane < laneCount; lane++) {\n const tid = nextTid++;\n tids.set(`${track}\\0${lane}`, tid);\n traceEvents.push(\n {\n ph: 'M',\n name: 'thread_name',\n pid: this.#pid,\n tid,\n args: {\n name: lane === 0 ? displayName : `${displayName} #${lane + 1}`,\n },\n },\n {\n ph: 'M',\n name: 'thread_sort_index',\n pid: this.#pid,\n tid,\n args: { sort_index: tid },\n }\n );\n }\n }\n\n for (const event of this.#pending) {\n const lane = event.kind === 'span' ? event.lane : 0;\n const tid = tids.get(`${event.track}\\0${lane}`)!;\n if (event.kind === 'span') {\n traceEvents.push({\n ph: 'X',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n dur: event.dur,\n args: event.args,\n });\n } else {\n traceEvents.push({\n ph: 'i',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n s: 't',\n args: event.args,\n });\n }\n }\n\n return {\n traceEvents,\n metadata: {\n source: '2g',\n version: this.#version,\n convertedAt: new Date().toISOString(),\n startTime: new Date(base / 1000).toISOString(),\n startTimestampMs: base / 1000,\n },\n };\n }\n\n #getTrack(category: string, worker?: string) {\n const key = worker ? `${category}:${worker}` : category;\n if (!this.#tracks.has(key)) {\n this.#tracks.set(key, worker ? `${category} ${worker}` : category);\n }\n return key;\n }\n}\n\n// 'X' events on one tid nest by containment; partial overlap is invalid.\nfunction assignLanes(spans: PendingSpan[]) {\n const order = [...spans].sort(\n (a, b) => a.ts - b.ts || b.ts + b.dur - (a.ts + a.dur)\n );\n const lanes: PendingSpan[][] = [];\n for (const span of order) {\n const end = span.ts + span.dur;\n let lane = -1;\n for (let index = 0; index < lanes.length; index++) {\n const stack = lanes[index];\n while (\n stack.length &&\n stack[stack.length - 1].ts + stack[stack.length - 1].dur <= span.ts\n ) {\n stack.pop();\n }\n const top = stack[stack.length - 1];\n if (!top || top.ts + top.dur >= end) {\n stack.push(span);\n lane = index;\n break;\n }\n }\n if (lane < 0) {\n lanes.push([span]);\n lane = lanes.length - 1;\n }\n span.lane = lane;\n }\n return lanes.length;\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return { category: 'uncategorized', name: value };\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n\nfunction extractArgs(event: ParsedEvent) {\n const args: Record<string, unknown> = {};\n for (const key of Object.keys(event)) {\n if (key.startsWith('_')) continue;\n args[key] = event[key];\n }\n return Object.keys(args).length ? args : undefined;\n}\n","import crypto from 'node:crypto';\n\nimport type { ParsedEvent } from '../../types';\n\nexport interface ConvertToOpenTelemetryOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface OpenTelemetryFile {\n resourceSpans: OpenTelemetryResourceSpan[];\n}\n\ninterface OpenTelemetryResourceSpan {\n resource: {\n attributes: OpenTelemetryAttribute[];\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n };\n spans: OpenTelemetrySpan[];\n }>;\n}\n\ninterface OpenTelemetrySpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes?: OpenTelemetryAttribute[];\n events?: OpenTelemetrySpanEvent[];\n}\n\ninterface OpenTelemetrySpanEvent {\n timeUnixNano: string;\n name: string;\n attributes?: OpenTelemetryAttribute[];\n}\n\ninterface OpenTelemetryAttribute {\n key: string;\n value: OpenTelemetryAnyValue;\n}\n\ntype OpenTelemetryAnyValue =\n | { stringValue: string }\n | { intValue: string }\n | { doubleValue: number }\n | { boolValue: boolean }\n | { arrayValue: { values: OpenTelemetryAnyValue[] } }\n | { kvlistValue: { values: OpenTelemetryAttribute[] } };\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToOpenTelemetry(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToOpenTelemetryOptions = {}\n): Promise<OpenTelemetryFile> {\n const converter = new OpenTelemetryConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toFile();\n}\n\nclass OpenTelemetryConverter {\n readonly #traceId = crypto.randomBytes(16).toString('hex');\n readonly #sessionSpanId = createSpanId();\n readonly #spans: OpenTelemetrySpan[] = [];\n readonly #events: OpenTelemetrySpanEvent[] = [];\n #processName: string;\n #version = '0.1.0';\n #startTimeUnixNano: string | undefined;\n #endTimeUnixNano: string | undefined;\n\n constructor(private readonly options: ConvertToOpenTelemetryOptions) {\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n const eventTime = unixNano(event._t);\n this.#startTimeUnixNano =\n this.#startTimeUnixNano == null ||\n BigInt(eventTime) < BigInt(this.#startTimeUnixNano)\n ? eventTime\n : this.#startTimeUnixNano;\n this.#endTimeUnixNano =\n this.#endTimeUnixNano == null ||\n BigInt(eventTime) > BigInt(this.#endTimeUnixNano)\n ? eventTime\n : this.#endTimeUnixNano;\n\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') this.#version = event.version;\n if (this.#processName === '2g' && typeof event.version === 'string') {\n this.#processName = `2g (v${event.version})`;\n }\n return;\n }\n\n if (typeof event._d === 'number') {\n this.#spans.push(this.#createSpan(event));\n } else {\n this.#events.push(this.#createEvent(event));\n }\n }\n\n toFile(): OpenTelemetryFile {\n const startTimeUnixNano = this.#startTimeUnixNano ?? unixNano(Date.now());\n const endTimeUnixNano = this.#endTimeUnixNano ?? startTimeUnixNano;\n return {\n resourceSpans: [\n {\n resource: {\n attributes: attributes({\n 'service.name': this.#processName,\n 'service.version': this.#version,\n 'process.pid': this.options.pid,\n 'telemetry.sdk.name': '2g',\n 'telemetry.sdk.language': 'nodejs',\n }),\n },\n scopeSpans: [\n {\n scope: { name: '2g' },\n spans: [\n {\n traceId: this.#traceId,\n spanId: this.#sessionSpanId,\n name: this.#processName,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: attributes({\n 'event_log.kind': 'session',\n }),\n events: this.#events.length ? this.#events : undefined,\n },\n ...this.#spans,\n ],\n },\n ],\n },\n ],\n };\n }\n\n #createSpan(event: ParsedEvent): OpenTelemetrySpan {\n const parsed = splitEventName(event._e);\n const startTimeUnixNano = unixNano(event._t - event._d!);\n const endTimeUnixNano = unixNano(event._t);\n return {\n traceId: this.#traceId,\n spanId: createSpanId(),\n parentSpanId: this.#sessionSpanId,\n name: parsed ? stripSuffix(parsed.name) : event._e,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: eventAttributes(event, parsed),\n };\n }\n\n #createEvent(event: ParsedEvent): OpenTelemetrySpanEvent {\n const parsed = splitEventName(event._e);\n return {\n timeUnixNano: unixNano(event._t),\n name: parsed ? stripSuffix(parsed.name) : event._e,\n attributes: eventAttributes(event, parsed),\n };\n }\n}\n\nfunction eventAttributes(\n event: ParsedEvent,\n parsed: ReturnType<typeof splitEventName>\n) {\n const values: Record<string, unknown> = {\n 'event.name': event._e,\n };\n if (parsed) {\n values['event.category'] = parsed.category;\n values['event.action'] = parsed.name;\n }\n if (typeof event._w === 'string') values['event.worker.id'] = event._w;\n\n for (const key of Object.keys(event)) {\n if (\n key === '_e' ||\n key === '_t' ||\n key === '_d' ||\n key === '_l' ||\n key === '_w'\n )\n continue;\n values[`event_log.${key}`] = event[key];\n }\n\n return attributes(values);\n}\n\nfunction attributes(values: Record<string, unknown>): OpenTelemetryAttribute[] {\n const result: OpenTelemetryAttribute[] = [];\n for (const [key, value] of Object.entries(values)) {\n const converted = anyValue(value);\n if (converted) result.push({ key, value: converted });\n }\n return result;\n}\n\nfunction anyValue(value: unknown): OpenTelemetryAnyValue | null {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return null;\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value };\n }\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value.flatMap(item => {\n const converted = anyValue(item);\n return converted ? [converted] : [];\n }),\n },\n };\n }\n if (value && typeof value === 'object') {\n return {\n kvlistValue: {\n values: attributes(value as Record<string, unknown>),\n },\n };\n }\n return null;\n}\n\nfunction createSpanId() {\n return crypto.randomBytes(8).toString('hex');\n}\n\nfunction unixNano(timeMs: number) {\n return String(BigInt(Math.round(timeMs * 1_000_000)));\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return null;\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n","import fs from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport type { ParsedEvent } from '../../types';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport {\n filterEvents,\n formatSessionProcessName,\n parseHelp,\n parseDuration,\n parseSharedTapOptions,\n readJsonlFile,\n readJsonlStdin,\n tapArgOptions,\n warnOnRotationLoss,\n} from '../shared';\nimport { convertToChromeTrace } from './chromeTrace';\nimport { convertToOpenTelemetry } from './opentelemetry';\n\ntype ExportFormat = 'chrome-trace' | 'opentelemetry';\nconst DEFAULT_IDLE_TIMEOUT = 30_000;\n\nexport async function runExportCli(args: string[]) {\n if (parseHelp(args)) {\n printExportHelp();\n return;\n }\n const options = parseExportOptions(args);\n if (options.json) redirectConsoleToStderr();\n let events: AsyncIterable<ParsedEvent> | ParsedEvent[];\n let processName: string | undefined;\n let exportPid: number | undefined;\n\n if (options.selector) {\n const session = await resolveSession(options.selector);\n processName = formatSessionProcessName(session);\n exportPid = session.pid;\n await warnOnRotationLoss(session.sessionDir, '2g export --tail');\n events = tap(session.sessionDir, {\n follow: options.follow,\n debug: options.debug,\n timeout: options.timeout,\n idleTimeout: options.follow ? options.idleTimeout : undefined,\n });\n } else if (options.inputFile) {\n events = readJsonlFile(options.inputFile);\n } else {\n events = readJsonlStdin();\n }\n\n const filteredEvents = filterEvents(events, options);\n const file =\n options.format === 'opentelemetry'\n ? await convertToOpenTelemetry(filteredEvents, {\n pid: exportPid,\n processName,\n })\n : await convertToChromeTrace(filteredEvents, {\n pid: exportPid,\n processName,\n });\n const output = JSON.stringify(file, null, 2);\n if (options.output) fs.writeFileSync(options.output, `${output}\\n`);\n else process.stdout.write(`${output}\\n`);\n}\n\nfunction parseExportOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n input: { type: 'string' },\n output: { type: 'string', short: 'o' },\n format: { type: 'string' },\n json: { type: 'boolean' },\n timeout: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n const timeout = parseDuration(parsed.values.timeout);\n\n const format = parsed.values.format ?? 'chrome-trace';\n if (!isExportFormat(format)) throw new Error(`Unsupported format: ${format}`);\n if (parsed.positionals[0] && parsed.values.input)\n throw new Error('Use either a session selector or --input, not both');\n if (timeout != null && !tapOptions.follow)\n throw new Error('Use --timeout only with --tail');\n\n return {\n selector: parsed.positionals[0],\n inputFile: parsed.values.input,\n output: parsed.values.output,\n format,\n since: tapOptions.since,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n follow: tapOptions.follow,\n timeout,\n idleTimeout: DEFAULT_IDLE_TIMEOUT,\n json: parsed.values.json ?? false,\n };\n}\n\nfunction isExportFormat(value: string): value is ExportFormat {\n return value === 'chrome-trace' || value === 'opentelemetry';\n}\n\nfunction printExportHelp() {\n process.stdout.write(\n [\n 'Usage: 2g export [selector]',\n '',\n 'Exports session events to Chrome Trace or OTLP JSON.',\n '(Without --tail it reads only the retained window. Rotated out logs may be lost)',\n '',\n '--tail attaches to an already-running session and follows live from that point.',\n ' Run it after the session is up and leave it attached while the workload runs.',\n ' It will automatically stop on 30s idle.',\n 'To trace a full command instead, use `2g record`.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --input <file> Read JSONL events from a file',\n ' -o, --output <file> Write output to a file',\n ' --format <format> chrome-trace or opentelemetry',\n ' --json Print JSON output',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too. A',\n ' plain \"ms\" payload field is not a span',\n ' --debug Include debug-level events that were captured by a',\n ' command run with LOG_DEBUG. Normal app traces do not',\n ' need this',\n ' --tail Follow live events after replaying history; stops',\n ' automatically on 30s idle',\n ' --timeout <duration> Also stop tailing after this absolute duration',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { listSessions } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport async function runPsCli(args: string[] = []) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n active: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n if (parsed.values.json) redirectConsoleToStderr();\n const sessions = (\n await listSessions({ selector: parsed.positionals[0] })\n ).filter(session => !parsed.values.active || session.alive);\n if (parsed.values.json) {\n process.stdout.write(`${JSON.stringify(sessions, null, 2)}\\n`);\n return;\n }\n\n process.stdout.write('PID\\tSTATUS\\tSTARTED\\tCOMMAND\\tCWD\\n');\n for (const session of sessions) {\n process.stdout.write(\n `${escapeTsv(session.pid)}\\t${escapeTsv(\n session.alive ? 'alive' : 'exited'\n )}\\t${escapeTsv(\n new Date(session.startedAt).toISOString()\n )}\\t${escapeTsv(session.command)}\\t${escapeTsv(session.cwd)}\\n`\n );\n }\n}\n\nfunction escapeTsv(value: unknown) {\n return String(value)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\t/g, '\\\\t')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\n/g, '\\\\n');\n}\n\nexport function printPsHelp() {\n process.stdout.write(\n [\n 'Usage: 2g ps [selector]',\n '',\n 'Lists recorded sessions, newest first; alive means still running.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command',\n ' --active, -a Show only active sessions',\n ' --json Print sessions as JSON',\n '',\n ].join('\\n')\n );\n}\n","import { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { captureEvents } from '../../capture';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport { filterEvents, parseHelp } from '../shared';\nimport { convertToChromeTrace } from '../export/chromeTrace';\nimport { convertToOpenTelemetry } from '../export/opentelemetry';\n\ntype ExportFormat = 'chrome-trace' | 'opentelemetry';\n\n// `record` owns the process it traces: it spawns <command> with event capture\n// wired in, follows its events end-to-end, and writes the trace when it exits.\n// This is the reliable path for a one-off run — there is no window to attach\n// within and nothing to rotate out, so it never returns a partial trace.\nexport async function runRecordCli(args: string[]) {\n if (parseHelp(args)) {\n printRecordHelp();\n return;\n }\n\n // Everything after `--` is the command and its own arguments, verbatim\n const separator = args.indexOf('--');\n const ownArgs = separator === -1 ? args : args.slice(0, separator);\n const command = separator === -1 ? [] : args.slice(separator + 1);\n const options = parseRecordOptions(ownArgs);\n if (!command.length)\n throw new Error(\n 'record needs a command to run: 2g record [options] -- <command> [args...]'\n );\n\n if (options.json) redirectConsoleToStderr();\n\n const capture = captureEvents({\n debug: options.debug,\n filter: options.filter,\n });\n // The trace lands on stdout when there is no -o, so route the child's stdout\n // to stderr to keep it clean; with -o the child can use stdout normally.\n const child = spawn(\n command[0],\n command.slice(1),\n capture.spawnOptions({\n env: process.env,\n stdio: ['inherit', options.output ? 'inherit' : 2, 'inherit'],\n })\n );\n capture.attach(child);\n\n const onSignal = (signal: NodeJS.Signals) => {\n if (signal !== 'SIGINT') {\n try {\n child.kill(signal);\n } catch {}\n }\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n const exited = new Promise<number>((resolve, reject) => {\n child.on('exit', code => resolve(code ?? 0));\n child.on('error', error =>\n reject(new Error(`Failed to run ${command[0]}: ${error.message}`))\n );\n });\n\n const meta = { pid: child.pid, processName: command.join(' ') };\n const filtered = filterEvents(capture, options);\n\n try {\n // Draining the events resolves once the child closes its event pipe (on exit)\n const file =\n options.format === 'opentelemetry'\n ? await convertToOpenTelemetry(filtered, meta)\n : await convertToChromeTrace(filtered, meta);\n const exitCode = await exited;\n\n const output = JSON.stringify(file, null, 2);\n if (options.output) fs.writeFileSync(options.output, `${output}\\n`);\n else process.stdout.write(`${output}\\n`);\n\n if (exitCode) process.exitCode = exitCode;\n } finally {\n process.off('SIGINT', onSignal);\n process.off('SIGTERM', onSignal);\n }\n}\n\nfunction parseRecordOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n filter: { type: 'string', multiple: true },\n spans: { type: 'boolean' },\n debug: { type: 'boolean' },\n output: { type: 'string', short: 'o' },\n format: { type: 'string' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n\n const format = parsed.values.format ?? 'chrome-trace';\n if (!isExportFormat(format)) throw new Error(`Unsupported format: ${format}`);\n\n return {\n output: parsed.values.output,\n format,\n filter: parsed.values.filter\n ?.flatMap(value => value.split(','))\n .map(item => item.trim())\n .filter(Boolean),\n spans: parsed.values.spans === true,\n debug: parsed.values.debug === true,\n json: parsed.values.json ?? false,\n };\n}\n\nfunction isExportFormat(value: string): value is ExportFormat {\n return value === 'chrome-trace' || value === 'opentelemetry';\n}\n\nfunction printRecordHelp() {\n process.stdout.write(\n [\n 'Usage: 2g record [options] -- <command> [args...]',\n '',\n 'Runs <command>, traces the events it emits, and writes the trace on exit.',\n '',\n 'This is the reliable way to capture a one-off run (a build, a script): 2g',\n 'owns the process, so the trace is complete end-to-end with no window to',\n 'attach within and nothing to rotate out. To trace an already-running server',\n 'instead, use `2g export <selector> --tail`.',\n '',\n 'Options:',\n ' -o, --output <file> Write the trace to a file (default: stdout)',\n ' --format <format> chrome-trace or opentelemetry',\n ' --json Print JSON output',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too',\n ' --debug Enable and export debug events; no LOG_DEBUG needed',\n '',\n 'If you are validating instrumentation or looking for debug-level events,',\n 'pass --debug, for example:',\n ' 2g record --debug -o trace.json -- <command>',\n 'Normal app traces do not need this.',\n '',\n 'Example:',\n ' 2g record -o trace.json -- expo export # run & trace a build end-to-end',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport { formatPrettyEvent, shouldColorizeStream } from '../../utils/pretty';\nimport {\n parseHelp,\n parseSharedTapOptions,\n tapArgOptions,\n warnOnRotationLoss,\n} from '../shared';\n\nexport async function runTapCli(args: string[]) {\n if (parseHelp(args)) {\n printTapHelp();\n return;\n }\n const options = parseTapOptions(args);\n if (options.json) redirectConsoleToStderr();\n const session = await resolveSession(options.selector);\n await warnOnRotationLoss(session.sessionDir, '2g tap --tail');\n const colorizePretty = shouldColorizeStream(process.stdout);\n for await (const event of tap(session.sessionDir, options)) {\n if (options.pretty) {\n process.stdout.write(\n `${formatPrettyEvent(event, {\n colorize: colorizePretty,\n stream: process.stdout,\n })}\\n`\n );\n } else {\n process.stdout.write(`${JSON.stringify(event)}\\n`);\n }\n }\n}\n\nfunction parseTapOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n format: { type: 'string' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n if (parsed.values.json && parsed.values.format === 'pretty')\n throw new Error('Use either --json or --format pretty, not both');\n\n return {\n selector: parsed.positionals[0],\n since: tapOptions.since,\n follow: tapOptions.follow,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n json: parsed.values.json ?? false,\n pretty: parsed.values.json ? false : parsed.values.format === 'pretty',\n };\n}\n\nfunction printTapHelp() {\n process.stdout.write(\n [\n 'Usage: 2g tap [selector]',\n '',\n 'Replays retained session history as JSONL, oldest first.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too. A',\n ' plain \"ms\" payload field is not a span',\n ' --debug Include debug events (only recorded when the',\n ' command ran with LOG_DEBUG set)',\n ' --format pretty Print a readable line per event',\n ' --tail Follow live events after replaying history; runs',\n ' until killed (use export with a timeout for a',\n ' bounded follow)',\n '',\n ].join('\\n')\n );\n}\n","#!/usr/bin/env node\nimport { fileURLToPath } from 'node:url';\n\nimport { printCleanHelp, runCleanCli } from './commands/clean';\nimport { runExportCli } from './commands/export';\nimport { printPsHelp, runPsCli } from './commands/ps';\nimport { runRecordCli } from './commands/record';\nimport { runTapCli } from './commands/tap';\nimport { parseHelp } from './commands/shared';\n\nexport async function main(argv = process.argv.slice(2)) {\n const [command, ...args] = argv;\n\n if (\n !command ||\n command === '--help' ||\n command === '-h' ||\n command === 'help'\n ) {\n printHelp();\n return 0;\n }\n\n switch (command) {\n case 'typegen': {\n const { runTypegenCli } = await import('./commands/typegen');\n runTypegenCli(args);\n return 0;\n }\n case 'ps':\n if (parseHelp(args)) {\n printPsHelp();\n return 0;\n }\n await runPsCli(args);\n return 0;\n case 'tap':\n await runTapCli(args);\n return 0;\n case 'clean':\n if (parseHelp(args)) {\n printCleanHelp();\n return 0;\n }\n runCleanCli(args);\n return 0;\n case 'record':\n await runRecordCli(args);\n return 0;\n case 'export':\n await runExportCli(args);\n return 0;\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n\nfunction printHelp() {\n process.stdout.write(\n [\n 'Usage: 2g <command>',\n '',\n 'Reads structured JSONL event logs that instrumented CLIs record during runs',\n 'into the system temp dir, with no setup: run a command, then tap it here.',\n '',\n 'Commands:',\n ' ps [selector] List recorded sessions',\n ' --active, -a (running only) --json',\n '',\n ' tap [selector] Replay session events as JSONL, oldest first',\n ' --since <time> --filter <pattern> --spans --debug --format pretty',\n ' --tail (follow live events after replay; runs until killed)',\n '',\n ' record -- <command> Run a command, trace it end-to-end, write on exit',\n ' -o, --output <file> --format chrome-trace|opentelemetry --json',\n ' --filter <pattern> --spans --debug',\n '',\n ' export [selector] Export a past or live (--tail) session',\n ' --input <jsonl-file> -o, --output <file> --json',\n ' --format chrome-trace|opentelemetry',\n ' --since <time> --filter <pattern> --spans --debug',\n ' --tail --timeout <duration> (absolute; tail also stops after 30s idle)',\n '',\n ' typegen List every event name/payload a project can emit',\n ' -p, --project <tsconfig> -o, --output <file> --format dts|json --json',\n '',\n ' clean Remove stale sessions',\n ' --all, -a (remove all exited sessions) --json',\n '',\n 'Typical use:',\n ' 2g ps -a # what is running now?',\n ' 2g tap \"expo start\" --tail # follow live events',\n ' 2g tap \"expo start\" --since 5m --format pretty # recent history, readable',\n ' 2g tap \"expo start\" --filter metro:bundling # narrow to one area',\n '',\n 'Trace a run to a flame chart:',\n ' 2g record -o trace.json -- expo export # run & trace a build',\n ' 2g export \"expo start\" --tail -o trace.json # attach to a running server',\n ' 2g export \"expo start\" -o trace.json # after the fact (partial)',\n '',\n 'Selectors match sessions by substring of PID, CWD, or command. If several',\n 'match, and exactly one of them is running, it is chosen; otherwise 2g errors',\n 'and lists the candidates.',\n '',\n '--filter takes comma-separated event-name prefixes matched on whole',\n 'segments: \"category\" and \"category:kind\" both match \"category:kind:test\";',\n '* matches anything. Discover names by tapping unfiltered or via typegen.',\n '',\n '--since accepts a duration ago (5m, 90s, 1h30m), Unix time, or ISO date.',\n '--spans keeps only span events (log.span() pairs with a \"_d\" duration in',\n 'ms); without it, point events are included alongside spans.',\n '',\n 'Events are single JSON lines: {\"_e\":\"category:kind\",\"_t\":<epoch ms>,...}.',\n '\"_d\" is a span duration, \"_w\" a worker id; debug events need --debug.',\n '',\n 'Set LOG_EVENTS=<file|fd> on an instrumented command to redirect its events.',\n 'Debug events are only recorded when the command runs with LOG_DEBUG set.',\n '',\n 'Pass LOG_DEBUG=<filter> when debugging; it also mirrors matching events to',\n 'stderr. Reading them back needs --debug.',\n '',\n 'Run 2g <command> --help for per-command help.',\n '',\n ].join('\\n')\n );\n}\n\nif (isCliEntrypoint()) {\n main()\n .then(code => {\n process.exitCode = code;\n })\n .catch(error => {\n process.stderr.write(\n `${error instanceof Error ? error.message : String(error)}\\n`\n );\n process.exitCode = 1;\n });\n}\n\nfunction isCliEntrypoint() {\n try {\n return process.argv[1] === fileURLToPath(import.meta.url);\n } catch {\n return false;\n }\n}\n"],"names":["runCleanCli","args","options","parseCleanOptions","parsed","parseArgs","all","type","short","json","help","values","redirectConsoleToStderr","removed","cleanExitedSessionsSync","cleanStaleSessionsSync","process","stdout","write","JSON","stringify","tapArgOptions","since","filter","multiple","spans","debug","tail","async","warnOnRotationLoss","sessionDir","remedy","detectRotationLoss","console","warn","readJsonlStream","stream","rl","createInterface","input","line","event","parseEventLine","filterEvents","events","eventFilter","compileEventFilter","parseSince","matchesTapOptions","parseSharedTapOptions","flatMap","value","split","map","item","trim","Boolean","follow","parseHelp","allowPositionals","strict","STRIP_SUFFIXES","convertToChromeTrace","converter","TraceConverter","add","toTraceFile","pending","tracks","Map","pid","processName","version","constructor","this","_e","splitEventName","index","indexOf","category","name","slice","track","getTrack","_w","undefined","extractArgs","key","Object","keys","startsWith","length","_d","push","kind","stripSuffix","cat","ts","Math","round","_t","dur","lane","base","Infinity","traceEvents","ph","tids","nextTid","displayName","laneCount","max","assignLanes","tid","set","sort_index","get","s","metadata","source","convertedAt","Date","toISOString","startTime","startTimestampMs","worker","has","order","sort","a","b","lanes","span","end","stack","pop","top","suffix","endsWith","convertToOpenTelemetry","OpenTelemetryConverter","toFile","traceId","crypto","randomBytes","toString","sessionSpanId","createSpanId","startTimeUnixNano","endTimeUnixNano","eventTime","unixNano","BigInt","createSpan","createEvent","now","resourceSpans","resource","attributes","scopeSpans","scope","spanId","parentSpanId","eventAttributes","timeUnixNano","result","entries","converted","anyValue","stringValue","boolValue","Number","isFinite","isInteger","intValue","String","doubleValue","Array","isArray","arrayValue","kvlistValue","timeMs","runExportCli","printExportHelp","join","parseExportOptions","output","format","timeout","tapOptions","parseDuration","duration","parseDurationValue","Error","isExportFormat","positionals","selector","inputFile","idleTimeout","exportPid","session","resolveSession","formatSessionProcessName","command","tap","readJsonlFile","file","fs","createReadStream","encoding","readJsonlStdin","stdin","filteredEvents","writeFileSync","escapeTsv","replace","runRecordCli","printRecordHelp","separator","ownArgs","parseRecordOptions","capture","captureEvents","child","spawn","spawnOptions","env","stdio","attach","onSignal","signal","kill","on","exited","Promise","resolve","reject","code","error","message","meta","filtered","exitCode","off","runTapCli","printTapHelp","parseTapOptions","pretty","colorizePretty","shouldColorizeStream","formatPrettyEvent","colorize","main","argv","printHelp","runTypegenCli","import","printPsHelp","runPsCli","active","sessions","listSessions","alive","startedAt","cwd","printCleanHelp","isCliEntrypoint","fileURLToPath","url","then","catch","stderr"],"mappings":";;;;;;;;;;;;;;;;;;AAKO,SAASA,YAAYC,IAAiB;EAC3C,MAAMC,IAYR,SAASC,kBAAkBF;IACzB,MAAMG,IAASC,EAAU;MACvBJ;MACAC,SAAS;QACPI,KAAK;UAAEC,MAAM;UAAWC,OAAO;;QAC/BC,MAAM;UAAEF,MAAM;;QACdG,MAAM;UAAEH,MAAM;UAAWC,OAAO;;;;IAGpC,OAAO;MACLF,MAA2B,MAAtBF,EAAOO,OAAOL;MACnBG,OAA6B,MAAvBL,EAAOO,OAAOF;;AAExB,GAzBkBN,CAAkBF;EAClC,IAAIC,EAAQO;IAAMG;;EAClB,MAAMC,IAAUX,EAAQI,MACpBQ,MACAC;EACJC,QAAQC,OAAOC,MACbhB,EAAQO,OACJ,GAAGU,KAAKC,UAAU;IAAEP;WACpB,WAAWA,KAAWX,EAAQI,MAAM,WAAW;AAEvD;;ACDO,MAAMe,IAAgB;EAC3BC,OAAO;IAAEf,MAAM;;EACfgB,QAAQ;IAAEhB,MAAM;IAAUiB,WAAU;;EACpCC,OAAO;IAAElB,MAAM;;EACfmB,OAAO;IAAEnB,MAAM;;EACfoB,MAAM;IAAEpB,MAAM;;;;AAcTqB,eAAeC,mBAAmBC,GAAoBC;EAC3D,WAAYC,EAAmBF;IAAc;;EAC7CG,QAAQC,KAEJ,8FAAmCH;AAEzC;;AAgBOH,gBAAgBO,gBAAgBC;EACrC,MAAMC,IAAKC,EAAgB;IAAEC,OAAOH;;EACpC,WAAW,MAAMI,KAAQH,GAAI;IAC3B,KAAKG;MAAM;;IAEX,MAAMC,IAAQC,EAAeF,GAAM;MAAEd,QAAO;;IAC5C,IAAIe;YAAaA;;AACnB;AACF;;AAEOb,gBAAgBe,aACrBC,GACA1C;EAEA,MAAM2C,IAAcC,EAAmB5C,EAAQqB;EAC/C,MAAMD,IAAQyB,EAAW7C,EAAQoB;EACjC,WAAW,MAAMmB,KAASG;IACxB,IACEI,EACEP,GACA;MACElB,QAAQrB,EAAQqB;MAChBD;MACAG,OAAOvB,EAAQuB;MACfC,OAAOxB,EAAQwB;OAEjBmB;YAGIJ;;;AAEZ;;AAEO,SAASQ,sBAAsBtC;EAOpC,OAAO;IACLW,OAAOX,EAAOW;IACdC,QAAQZ,EAAOY,QACX2B,QAAQC,KAASA,EAAMC,MAAM,MAC9BC,IAAIC,KAAQA,EAAKC,QACjBhC,OAAOiC;IACV/B,QAAwB,MAAjBd,EAAOc;IACdC,QAAwB,MAAjBf,EAAOe;IACd+B,SAAwB,MAAhB9C,EAAOgB;;AAEnB;;AAEO,SAAS+B,UAAUzD;EACxB,QAQqB,MAPnBI,EAAU;IACRJ;IACA0D,mBAAkB;IAClBC,SAAQ;IACR1D,SAAS;MACPQ,MAAM;QAAEH,MAAM;QAAWC,OAAO;;;KAEjCG,OAAOD;AAEd;;ACrEA,MAAMmD,IAAiB,EAAC,YAAY,SAAS;;AAEtCjC,eAAekC,qBACpBlB,GACA1C,IAAuC;EAEvC,MAAM6D,IAAY,IAAIC,eAAe9D;EACrC,WAAW,MAAMuC,KAASG;IACxBmB,EAAUE,IAAIxB;;EAEhB,OAAOsB,EAAUG;AACnB;;AAwBO,MAAMF;EACXG,GAA2B;EAC3BC,GAAU,IAAIC;EACdC;EACAC;EACAC,GAAW;EAEXC,WAAAA,CAAYvE,IAAuC;IACjDwE,MAAKJ,IAAOpE,EAAQoE,OAAO;IAC3BI,MAAKH,IAAerE,EAAQqE,eAAe;AAC7C;EAEAN,GAAAA,CAAIxB;IACF,IAAiB,gBAAbA,EAAMkC,IAAoB;MAC5B,IAA6B,mBAAlBlC,EAAM+B,SAAsB;QACrCE,MAAKF,IAAW/B,EAAM+B;QACtB,IAA0B,SAAtBE,MAAKH;UACPG,MAAKH,IAAe,QAAQ9B,EAAM+B;;AAEtC;MACA;AACF;IAEA,MAAMpE,IA8JV,SAASwE,iBAAezB;MACtB,MAAM0B,IAAQ1B,EAAM2B,QAAQ;MAC5B,IAAID,IAAQ;QAAG,OAAO;UAAEE,UAAU;UAAiBC,MAAM7B;;;MACzD,OAAO;QACL4B,UAAU5B,EAAM8B,MAAM,GAAGJ;QACzBG,MAAM7B,EAAM8B,MAAMJ,IAAQ;;AAE9B,KArKmBD,CAAenC,EAAMkC;IACpC,MAAMO,IAAQR,MAAKS,EACjB/E,EAAO2E,UACa,mBAAbtC,EAAM2C,KAAkB3C,EAAM2C,UAAKC;IAE5C,MAAMpF,IAyKV,SAASqF,YAAY7C;MACnB,MAAMxC,IAAgC,CAAA;MACtC,KAAK,MAAMsF,KAAOC,OAAOC,KAAKhD,IAAQ;QACpC,IAAI8C,EAAIG,WAAW;UAAM;;QACzBzF,EAAKsF,KAAO9C,EAAM8C;AACpB;MACA,OAAOC,OAAOC,KAAKxF,GAAM0F,SAAS1F,SAAOoF;AAC3C,KAhLiBC,CAAY7C;IAEzB,IAAwB,mBAAbA,EAAMmD;MACflB,MAAKP,EAAS0B,KAAK;QACjBC,MAAM;QACNZ;QACAF,MAAMe,cAAY3F,EAAO4E;QACzBgB,KAAK5F,EAAO2E;QACZkB,IAAIC,KAAKC,MAA8B,OAAvB1D,EAAM2D,KAAK3D,EAAMmD;QACjCS,KAAKH,KAAKC,MAAiB,MAAX1D,EAAMmD;QACtB3F;QACAqG,MAAM;;;MAGR5B,MAAKP,EAAS0B,KAAK;QACjBC,MAAM;QACNZ;QACAF,MAAMe,cAAY3F,EAAO4E;QACzBgB,KAAK5F,EAAO2E;QACZkB,IAAIC,KAAKC,MAAiB,MAAX1D,EAAM2D;QACrBnG;;;AAGN;EAEAiE,WAAAA;IACE,IAAIqC,IAAO7B,MAAKP,EAASwB,SAASa,QAAW;IAC7C,KAAK,MAAM/D,KAASiC,MAAKP;MACvB,IAAI1B,EAAMwD,KAAKM;QAAMA,IAAO9D,EAAMwD;;;IAEpC,MAAMQ,IAA4B,EAChC;MACEC,IAAI;MACJ1B,MAAM;MACNV,KAAKI,MAAKJ;MACVrE,MAAM;QAAE+E,MAAMN,MAAKH;;;IAIvB,MAAMoC,IAAO,IAAItC;IACjB,IAAIuC,IAAU;IACd,KAAK,OAAO1B,GAAO2B,MAAgBnC,MAAKN,GAAS;MAC/C,MAAM3C,IAAuB;MAC7B,KAAK,MAAMgB,KAASiC,MAAKP;QACvB,IAAmB,WAAf1B,EAAMqD,QAAmBrD,EAAMyC,UAAUA;UAAOzD,EAAMoE,KAAKpD;;;MAEjE,MAAMqE,IAAYZ,KAAKa,IAAIC,YAAYvF,IAAQ;MAC/C,KAAK,IAAI6E,IAAO,GAAGA,IAAOQ,GAAWR,KAAQ;QAC3C,MAAMW,IAAML;QACZD,EAAKO,IAAI,GAAGhC,MAAUoB,KAAQW;QAC9BR,EAAYZ,KACV;UACEa,IAAI;UACJ1B,MAAM;UACNV,KAAKI,MAAKJ;UACV2C;UACAhH,MAAM;YACJ+E,MAAe,MAATsB,IAAaO,IAAc,GAAGA,MAAgBP,IAAO;;WAG/D;UACEI,IAAI;UACJ1B,MAAM;UACNV,KAAKI,MAAKJ;UACV2C;UACAhH,MAAM;YAAEkH,YAAYF;;;AAG1B;AACF;IAEA,KAAK,MAAMxE,KAASiC,MAAKP,GAAU;MAEjC,MAAM8C,IAAMN,EAAKS,IAAI,GAAG3E,EAAMyC,UADF,WAAfzC,EAAMqD,OAAkBrD,EAAM6D,OAAO;MAElD,IAAmB,WAAf7D,EAAMqD;QACRW,EAAYZ,KAAK;UACfa,IAAI;UACJ1B,MAAMvC,EAAMuC;UACZgB,KAAKvD,EAAMuD;UACX1B,KAAKI,MAAKJ;UACV2C;UACAhB,IAAIxD,EAAMwD,KAAKM;UACfF,KAAK5D,EAAM4D;UACXpG,MAAMwC,EAAMxC;;;QAGdwG,EAAYZ,KAAK;UACfa,IAAI;UACJ1B,MAAMvC,EAAMuC;UACZgB,KAAKvD,EAAMuD;UACX1B,KAAKI,MAAKJ;UACV2C;UACAhB,IAAIxD,EAAMwD,KAAKM;UACfc,GAAG;UACHpH,MAAMwC,EAAMxC;;;AAGlB;IAEA,OAAO;MACLwG;MACAa,UAAU;QACRC,QAAQ;QACR/C,SAASE,MAAKF;QACdgD,cAAa,IAAIC,MAAOC;QACxBC,WAAW,IAAIF,KAAKlB,IAAO,KAAMmB;QACjCE,kBAAkBrB,IAAO;;;AAG/B;EAEA,EAAApB,CAAUJ,GAAkB8C;IAC1B,MAAMtC,IAAMsC,IAAS,GAAG9C,KAAY8C,MAAW9C;IAC/C,KAAKL,MAAKN,EAAQ0D,IAAIvC;MACpBb,MAAKN,EAAQ8C,IAAI3B,GAAKsC,IAAS,GAAG9C,KAAY8C,MAAW9C;;IAE3D,OAAOQ;AACT;;;AAIF,SAASyB,YAAYvF;EACnB,MAAMsG,IAAQ,KAAItG,IAAOuG,KACvB,CAACC,GAAGC,MAAMD,EAAEhC,KAAKiC,EAAEjC,MAAMiC,EAAEjC,KAAKiC,EAAE7B,OAAO4B,EAAEhC,KAAKgC,EAAE5B;EAEpD,MAAM8B,IAAyB;EAC/B,KAAK,MAAMC,KAAQL,GAAO;IACxB,MAAMM,IAAMD,EAAKnC,KAAKmC,EAAK/B;IAC3B,IAAIC,KAAO;IACX,KAAK,IAAIzB,IAAQ,GAAGA,IAAQsD,EAAMxC,QAAQd,KAAS;MACjD,MAAMyD,IAAQH,EAAMtD;MACpB,OACEyD,EAAM3C,UACN2C,EAAMA,EAAM3C,SAAS,GAAGM,KAAKqC,EAAMA,EAAM3C,SAAS,GAAGU,OAAO+B,EAAKnC;QAEjEqC,EAAMC;;MAER,MAAMC,IAAMF,EAAMA,EAAM3C,SAAS;MACjC,KAAK6C,KAAOA,EAAIvC,KAAKuC,EAAInC,OAAOgC,GAAK;QACnCC,EAAMzC,KAAKuC;QACX9B,IAAOzB;QACP;AACF;AACF;IACA,IAAIyB,IAAO,GAAG;MACZ6B,EAAMtC,KAAK,EAACuC;MACZ9B,IAAO6B,EAAMxC,SAAS;AACxB;IACAyC,EAAK9B,OAAOA;AACd;EACA,OAAO6B,EAAMxC;AACf;;AAWA,SAASI,cAAYf;EACnB,KAAK,MAAMyD,KAAU5E;IACnB,IAAImB,EAAK0D,SAASD;MAAS,OAAOzD,EAAKC,MAAM,IAAIwD,EAAO9C;;;EAE1D,OAAOX;AACT;;ACjOA,MAAMnB,IAAiB,EAAC,YAAY,SAAS;;AAEtCjC,eAAe+G,uBACpB/F,GACA1C,IAAyC;EAEzC,MAAM6D,IAAY,IAAI6E,uBAAuB1I;EAC7C,WAAW,MAAMuC,KAASG;IACxBmB,EAAUE,IAAIxB;;EAEhB,OAAOsB,EAAU8E;AACnB;;AAEA,MAAMD;EACKE,GAAWC,EAAOC,YAAY,IAAIC,SAAS;EAC3CC,GAAiBC;EACjB1H,GAA8B;EAC9BmB,GAAoC;EAC7C2B;EACAC,GAAW;EACX4E;EACAC;EAEA5E,WAAAA,CAA6BvE;IAAwCwE,KAAxCxE,UAAAA;IAC3BwE,MAAKH,IAAerE,EAAQqE,eAAe;AAC7C;EAEAN,GAAAA,CAAIxB;IACF,MAAM6G,IAAYC,SAAS9G,EAAM2D;IACjC1B,MAAK0E,IACwB,QAA3B1E,MAAK0E,KACLI,OAAOF,KAAaE,OAAO9E,MAAK0E,KAC5BE,IACA5E,MAAK0E;IACX1E,MAAK2E,IACsB,QAAzB3E,MAAK2E,KACLG,OAAOF,KAAaE,OAAO9E,MAAK2E,KAC5BC,IACA5E,MAAK2E;IAEX,IAAiB,gBAAb5G,EAAMkC,IAAoB;MAC5B,IAA6B,mBAAlBlC,EAAM+B;QAAsBE,MAAKF,IAAW/B,EAAM+B;;MAC7D,IAA0B,SAAtBE,MAAKH,KAAkD,mBAAlB9B,EAAM+B;QAC7CE,MAAKH,IAAe,QAAQ9B,EAAM+B;;MAEpC;AACF;IAEA,IAAwB,mBAAb/B,EAAMmD;MACflB,MAAKjD,EAAOoE,KAAKnB,MAAK+E,EAAYhH;;MAElCiC,MAAK9B,EAAQiD,KAAKnB,MAAKgF,EAAajH;;AAExC;EAEAoG,MAAAA;IACE,MAAMO,IAAoB1E,MAAK0E,KAAsBG,SAAS9B,KAAKkC;IACnE,MAAMN,IAAkB3E,MAAK2E,KAAoBD;IACjD,OAAO;MACLQ,eAAe,EACb;QACEC,UAAU;UACRC,YAAYA,WAAW;YACrB,gBAAgBpF,MAAKH;YACrB,mBAAmBG,MAAKF;YACxB,eAAeE,KAAKxE,QAAQoE;YAC5B,sBAAsB;YACtB,0BAA0B;;;QAG9ByF,YAAY,EACV;UACEC,OAAO;YAAEhF,MAAM;;UACfvD,OAAO,EACL;YACEqH,SAASpE,MAAKoE;YACdmB,QAAQvF,MAAKwE;YACblE,MAAMN,MAAKH;YACX6E;YACAC;YACAS,YAAYA,WAAW;cACrB,kBAAkB;;YAEpBlH,QAAQ8B,MAAK9B,EAAQ+C,SAASjB,MAAK9B,SAAUyC;gBAE5CX,MAAKjD;;;;AAOtB;EAEA,EAAAgI,CAAYhH;IACV,MAAMrC,IAASwE,eAAenC,EAAMkC;IACpC,MAAMyE,IAAoBG,SAAS9G,EAAM2D,KAAK3D,EAAMmD;IACpD,MAAMyD,IAAkBE,SAAS9G,EAAM2D;IACvC,OAAO;MACL0C,SAASpE,MAAKoE;MACdmB,QAAQd;MACRe,cAAcxF,MAAKwE;MACnBlE,MAAM5E,IAAS2F,YAAY3F,EAAO4E,QAAQvC,EAAMkC;MAChDyE;MACAC;MACAS,YAAYK,gBAAgB1H,GAAOrC;;AAEvC;EAEA,EAAAsJ,CAAajH;IACX,MAAMrC,IAASwE,eAAenC,EAAMkC;IACpC,OAAO;MACLyF,cAAcb,SAAS9G,EAAM2D;MAC7BpB,MAAM5E,IAAS2F,YAAY3F,EAAO4E,QAAQvC,EAAMkC;MAChDmF,YAAYK,gBAAgB1H,GAAOrC;;AAEvC;;;AAGF,SAAS+J,gBACP1H,GACArC;EAEA,MAAMO,IAAkC;IACtC,cAAc8B,EAAMkC;;EAEtB,IAAIvE,GAAQ;IACVO,EAAO,oBAAoBP,EAAO2E;IAClCpE,EAAO,kBAAkBP,EAAO4E;AAClC;EACA,IAAwB,mBAAbvC,EAAM2C;IAAiBzE,EAAO,qBAAqB8B,EAAM2C;;EAEpE,KAAK,MAAMG,KAAOC,OAAOC,KAAKhD,IAAQ;IACpC,IACU,SAAR8C,KACQ,SAARA,KACQ,SAARA,KACQ,SAARA,KACQ,SAARA;MAEA;;IACF5E,EAAO,aAAa4E,OAAS9C,EAAM8C;AACrC;EAEA,OAAOuE,WAAWnJ;AACpB;;AAEA,SAASmJ,WAAWnJ;EAClB,MAAM0J,IAAmC;EACzC,KAAK,OAAO9E,GAAKpC,MAAUqC,OAAO8E,QAAQ3J,IAAS;IACjD,MAAM4J,IAAYC,SAASrH;IAC3B,IAAIoH;MAAWF,EAAOxE,KAAK;QAAEN;QAAKpC,OAAOoH;;;AAC3C;EACA,OAAOF;AACT;;AAEA,SAASG,SAASrH;EAChB,IAAqB,mBAAVA;IAAoB,OAAO;MAAEsH,aAAatH;;;EACrD,IAAqB,oBAAVA;IAAqB,OAAO;MAAEuH,WAAWvH;;;EACpD,IAAqB,mBAAVA,GAAoB;IAC7B,KAAKwH,OAAOC,SAASzH;MAAQ,OAAO;;IACpC,OAAOwH,OAAOE,UAAU1H,KACpB;MAAE2H,UAAUC,OAAO5H;QACnB;MAAE6H,aAAa7H;;AACrB;EACA,IAAI8H,MAAMC,QAAQ/H;IAChB,OAAO;MACLgI,YAAY;QACVxK,QAAQwC,EAAMD,QAAQI;UACpB,MAAMiH,IAAYC,SAASlH;UAC3B,OAAOiH,IAAY,EAACA,MAAa;;;;;EAKzC,IAAIpH,KAA0B,mBAAVA;IAClB,OAAO;MACLiI,aAAa;QACXzK,QAAQmJ,WAAW3G;;;;EAIzB,OAAO;AACT;;AAEA,SAASgG;EACP,OAAOJ,EAAOC,YAAY,GAAGC,SAAS;AACxC;;AAEA,SAASM,SAAS8B;EAChB,OAAON,OAAOvB,OAAOtD,KAAKC,MAAe,MAATkF;AAClC;;AAEA,SAASzG,eAAezB;EACtB,MAAM0B,IAAQ1B,EAAM2B,QAAQ;EAC5B,IAAID,IAAQ;IAAG,OAAO;;EACtB,OAAO;IACLE,UAAU5B,EAAM8B,MAAM,GAAGJ;IACzBG,MAAM7B,EAAM8B,MAAMJ,IAAQ;;AAE9B;;AAEA,SAASkB,YAAYf;EACnB,KAAK,MAAMyD,KAAU5E;IACnB,IAAImB,EAAK0D,SAASD;MAAS,OAAOzD,EAAKC,MAAM,IAAIwD,EAAO9C;;;EAE1D,OAAOX;AACT;;AC/OOpD,eAAe0J,aAAarL;EACjC,IAAIyD,UAAUzD,IAAO;KAuFvB,SAASsL;MACPvK,QAAQC,OAAOC,MACb,EACE,+BACA,IACA,wDACA,oFACA,IACA,mFACA,mFACA,6CACA,qDACA,IACA,YACA,2EACA,gDACA,4DACA,qDACA,4DACA,gDACA,6EACA,iFACA,0EACA,gFACA,6EACA,qEACA,iFACA,mFACA,wCACA,gFACA,wDACA,6EACA,KACAsK,KAAK;AAEX,KAzHID;IACA;AACF;EACA,MAAMrL,IAuCR,SAASuL,mBAAmBxL;IAC1B,MAAMG,IAASC,EAAU;MACvBJ;MACA0D,mBAAkB;MAClBzD,SAAS;WACJmB;QACHkB,OAAO;UAAEhC,MAAM;;QACfmL,QAAQ;UAAEnL,MAAM;UAAUC,OAAO;;QACjCmL,QAAQ;UAAEpL,MAAM;;QAChBE,MAAM;UAAEF,MAAM;;QACdqL,SAAS;UAAErL,MAAM;;QACjBG,MAAM;UAAEH,MAAM;UAAWC,OAAO;;;;IAGpC,MAAMqL,IAAa5I,sBAAsB7C,EAAOO;IAChD,MAAMiL,IHkDD,SAASE,cAAc3I;MAC5B,IAAa,QAATA,KAA2B,OAAVA;QAAc;;MACnC,MAAM4I,IAAWC,EAAmB7I;MACpC,IAAgB,QAAZ4I;QAAkB,MAAM,IAAIE,MAAM,qBAAqB9I;;MAC3D,OAAO4I;AACT,KGvDkBD,CAAc1L,EAAOO,OAAOiL;IAE5C,MAAMD,IAASvL,EAAOO,OAAOgL,UAAU;IACvC,KAsBF,SAASO,iBAAe/I;MACtB,OAAiB,mBAAVA,KAAsC,oBAAVA;AACrC,KAxBO+I,CAAeP;MAAS,MAAM,IAAIM,MAAM,uBAAuBN;;IACpE,IAAIvL,EAAO+L,YAAY,MAAM/L,EAAOO,OAAO4B;MACzC,MAAM,IAAI0J,MAAM;;IAClB,IAAe,QAAXL,MAAoBC,EAAWpI;MACjC,MAAM,IAAIwI,MAAM;;IAElB,OAAO;MACLG,UAAUhM,EAAO+L,YAAY;MAC7BE,WAAWjM,EAAOO,OAAO4B;MACzBmJ,QAAQtL,EAAOO,OAAO+K;MACtBC;MACArK,OAAOuK,EAAWvK;MAClBC,QAAQsK,EAAWtK;MACnBE,OAAOoK,EAAWpK;MAClBC,OAAOmK,EAAWnK;MAClB+B,QAAQoI,EAAWpI;MACnBmI;MACAU,aAjFyB;MAkFzB7L,MAAML,EAAOO,OAAOF,SAAQ;;AAEhC,GA7EkBgL,CAAmBxL;EACnC,IAAIC,EAAQO;IAAMG;;EAClB,IAAIgC;EACJ,IAAI2B;EACJ,IAAIgI;EAEJ,IAAIrM,EAAQkM,UAAU;IACpB,MAAMI,UAAgBC,EAAevM,EAAQkM;IAC7C7H,IHMG,SAASmI,yBAAyBF;MACvC,OAAOA,EAAQhI,UACX,GAAGgI,EAAQG,aAAaH,EAAQhI,gBAAgBgI,EAAQlI,SACxD,GAAGkI,EAAQG,gBAAgBH,EAAQlI;AACzC,KGVkBoI,CAAyBF;IACvCD,IAAYC,EAAQlI;UACdzC,mBAAmB2K,EAAQ1K,YAAY;IAC7Cc,IAASgK,EAAIJ,EAAQ1K,YAAY;MAC/B2B,QAAQvD,EAAQuD;MAChB/B,OAAOxB,EAAQwB;MACfkK,SAAS1L,EAAQ0L;MACjBU,aAAapM,EAAQuD,SAASvD,EAAQoM,mBAAcjH;;AAExD,SAAO,IAAInF,EAAQmM;IACjBzJ,IHEGhB,gBAAgBiL,cAAcC;aAC5B3K,gBAAgB4K,EAAGC,iBAAiBF,GAAM;QAAEG,UAAU;;AAC/D,KGJaJ,CAAc3M,EAAQmM;;IAE/BzJ,IHIGhB,gBAAgBsL;aACd/K,gBAAgBnB,QAAQmM;AACjC,KGNaD;;EAGX,MAAME,IAAiBzK,aAAaC,GAAQ1C;EAC5C,MAAM4M,IACe,oBAAnB5M,EAAQyL,eACEhD,uBAAuByE,GAAgB;IAC3C9I,KAAKiI;IACLhI;aAEIT,qBAAqBsJ,GAAgB;IACzC9I,KAAKiI;IACLhI;;EAER,MAAMmH,IAASvK,KAAKC,UAAU0L,GAAM,MAAM;EAC1C,IAAI5M,EAAQwL;IAAQqB,EAAGM,cAAcnN,EAAQwL,QAAQ,GAAGA;;IACnD1K,QAAQC,OAAOC,MAAM,GAAGwK;;AAC/B;;AC7BA,SAAS4B,UAAUnK;EACjB,OAAO4H,OAAO5H,GACXoK,QAAQ,OAAO,QACfA,QAAQ,OAAO,OACfA,QAAQ,OAAO,OACfA,QAAQ,OAAO;AACpB;;AC1BO3L,eAAe4L,aAAavN;EACjC,IAAIyD,UAAUzD,IAAO;KA2GvB,SAASwN;MACPzM,QAAQC,OAAOC,MACb,EACE,qDACA,IACA,6EACA,IACA,6EACA,2EACA,+EACA,+CACA,IACA,YACA,0EACA,4DACA,gDACA,iFACA,0EACA,gFACA,0EACA,kFACA,IACA,4EACA,8BACA,kDACA,uCACA,IACA,YACA,+EACA,KACAsK,KAAK;AAEX,KA1IIiC;IACA;AACF;EAGA,MAAMC,IAAYzN,EAAK6E,QAAQ;EAC/B,MAAM6I,WAAUD,IAAmBzN,IAAOA,EAAKgF,MAAM,GAAGyI;EACxD,MAAMf,WAAUe,IAAmB,KAAKzN,EAAKgF,MAAMyI,IAAY;EAC/D,MAAMxN,IA+DR,SAAS0N,mBAAmB3N;IAC1B,MAAMG,IAASC,EAAU;MACvBJ;MACA0D,mBAAkB;MAClBzD,SAAS;QACPqB,QAAQ;UAAEhB,MAAM;UAAUiB,WAAU;;QACpCC,OAAO;UAAElB,MAAM;;QACfmB,OAAO;UAAEnB,MAAM;;QACfmL,QAAQ;UAAEnL,MAAM;UAAUC,OAAO;;QACjCmL,QAAQ;UAAEpL,MAAM;;QAChBE,MAAM;UAAEF,MAAM;;QACdG,MAAM;UAAEH,MAAM;UAAWC,OAAO;;;;IAIpC,MAAMmL,IAASvL,EAAOO,OAAOgL,UAAU;IACvC,KAeF,SAASO,eAAe/I;MACtB,OAAiB,mBAAVA,KAAsC,oBAAVA;AACrC,KAjBO+I,CAAeP;MAAS,MAAM,IAAIM,MAAM,uBAAuBN;;IAEpE,OAAO;MACLD,QAAQtL,EAAOO,OAAO+K;MACtBC;MACApK,QAAQnB,EAAOO,OAAOY,QAClB2B,QAAQC,KAASA,EAAMC,MAAM,MAC9BC,IAAIC,KAAQA,EAAKC,QACjBhC,OAAOiC;MACV/B,QAA+B,MAAxBrB,EAAOO,OAAOc;MACrBC,QAA+B,MAAxBtB,EAAOO,OAAOe;MACrBjB,MAAML,EAAOO,OAAOF,SAAQ;;AAEhC,GA5FkBmN,CAAmBD;EACnC,KAAKhB,EAAQhH;IACX,MAAM,IAAIsG,MACR;;EAGJ,IAAI/L,EAAQO;IAAMG;;EAElB,MAAMiN,IAAUC,EAAc;IAC5BpM,OAAOxB,EAAQwB;IACfH,QAAQrB,EAAQqB;;EAIlB,MAAMwM,IAAQC,EACZrB,EAAQ,IACRA,EAAQ1H,MAAM,IACd4I,EAAQI,aAAa;IACnBC,KAAKlN,QAAQkN;IACbC,OAAO,EAAC,WAAWjO,EAAQwL,SAAS,YAAY,GAAG;;EAGvDmC,EAAQO,OAAOL;EAEf,MAAMM,WAAYC;IAChB,IAAe,aAAXA;MACF;QACEP,EAAMQ,KAAKD;AACb,QAAE,OAAO;;;EAGbtN,QAAQwN,GAAG,UAAUH;EACrBrN,QAAQwN,GAAG,WAAWH;EAEtB,MAAMI,IAAS,IAAIC,QAAgB,CAACC,GAASC;IAC3Cb,EAAMS,GAAG,QAAQK,KAAQF,EAAQE,KAAQ;IACzCd,EAAMS,GAAG,SAASM,KAChBF,EAAO,IAAI3C,MAAM,iBAAiBU,EAAQ,OAAOmC,EAAMC;;EAI3D,MAAMC,IAAO;IAAE1K,KAAKyJ,EAAMzJ;IAAKC,aAAaoI,EAAQnB,KAAK;;EACzD,MAAMyD,IAAWtM,aAAakL,GAAS3N;EAEvC;IAEE,MAAM4M,IACe,oBAAnB5M,EAAQyL,eACEhD,uBAAuBsG,GAAUD,WACjClL,qBAAqBmL,GAAUD;IAC3C,MAAME,UAAiBT;IAEvB,MAAM/C,IAASvK,KAAKC,UAAU0L,GAAM,MAAM;IAC1C,IAAI5M,EAAQwL;MAAQqB,EAAGM,cAAcnN,EAAQwL,QAAQ,GAAGA;;MACnD1K,QAAQC,OAAOC,MAAM,GAAGwK;;IAE7B,IAAIwD;MAAUlO,QAAQkO,WAAWA;;AACnC,IAAC;IACClO,QAAQmO,IAAI,UAAUd;IACtBrN,QAAQmO,IAAI,WAAWd;AACzB;AACF;;AC3EOzM,eAAewN,UAAUnP;EAC9B,IAAIyD,UAAUzD,IAAO;KAkDvB,SAASoP;MACPrO,QAAQC,OAAOC,MACb,EACE,4BACA,IACA,4DACA,IACA,YACA,2EACA,gDACA,6EACA,iFACA,0EACA,gFACA,6EACA,qEACA,2EACA,8DACA,8DACA,+EACA,4EACA,8CACA,KACAsK,KAAK;AAEX,KA1EI6D;IACA;AACF;EACA,MAAMnP,IAmBR,SAASoP,gBAAgBrP;IACvB,MAAMG,IAASC,EAAU;MACvBJ;MACA0D,mBAAkB;MAClBzD,SAAS;WACJmB;QACHsK,QAAQ;UAAEpL,MAAM;;QAChBE,MAAM;UAAEF,MAAM;;QACdG,MAAM;UAAEH,MAAM;UAAWC,OAAO;;;;IAGpC,MAAMqL,IAAa5I,sBAAsB7C,EAAOO;IAChD,IAAIP,EAAOO,OAAOF,QAAiC,aAAzBL,EAAOO,OAAOgL;MACtC,MAAM,IAAIM,MAAM;;IAElB,OAAO;MACLG,UAAUhM,EAAO+L,YAAY;MAC7B7K,OAAOuK,EAAWvK;MAClBmC,QAAQoI,EAAWpI;MACnBlC,QAAQsK,EAAWtK;MACnBE,OAAOoK,EAAWpK;MAClBC,OAAOmK,EAAWnK;MAClBjB,MAAML,EAAOO,OAAOF,SAAQ;MAC5B8O,QAAQnP,EAAOO,OAAOF,QAAO,IAAiC,aAAzBL,EAAOO,OAAOgL;;AAEvD,GA5CkB2D,CAAgBrP;EAChC,IAAIC,EAAQO;IAAMG;;EAClB,MAAM4L,UAAgBC,EAAevM,EAAQkM;QACvCvK,mBAAmB2K,EAAQ1K,YAAY;EAC7C,MAAM0N,IAAiBC,EAAqBzO,QAAQC;EACpD,WAAW,MAAMwB,KAASmK,EAAIJ,EAAQ1K,YAAY5B;IAChD,IAAIA,EAAQqP;MACVvO,QAAQC,OAAOC,MACb,GAAGwO,EAAkBjN,GAAO;QAC1BkN,UAAUH;QACVpN,QAAQpB,QAAQC;;;MAIpBD,QAAQC,OAAOC,MAAM,GAAGC,KAAKC,UAAUqB;;;AAG7C;;ACxBOb,eAAegO,KAAKC,IAAO7O,QAAQ6O,KAAK5K,MAAM;EACnD,OAAO0H,MAAY1M,KAAQ4P;EAE3B,KACGlD,KACW,aAAZA,KACY,SAAZA,KACY,WAAZA,GACA;KAuCJ,SAASmD;MACP9O,QAAQC,OAAOC,MACb,EACE,uBACA,IACA,+EACA,6EACA,IACA,aACA,0DACA,2CACA,IACA,gFACA,6EACA,mEACA,IACA,oFACA,wEACA,4CACA,IACA,0EACA,yDACA,2CACA,4DACA,+EACA,IACA,oFACA,gFACA,IACA,yDACA,sDACA,IACA,gBACA,6EACA,2EACA,iFACA,2EACA,IACA,iCACA,4EACA,mFACA,iFACA,IACA,6EACA,gFACA,6BACA,IACA,uEACA,6EACA,4EACA,IACA,4EACA,4EACA,+DACA,IACA,6EACA,yEACA,IACA,+EACA,4EACA,IACA,8EACA,4CACA,IACA,iDACA,KACAsK,KAAK;AAEX,KA1GIsE;IACA,OAAO;AACT;EAEA,QAAQnD;GACN,KAAK;IAAW;MACd,OAAMoD,eAAEA,WAAwBC,OAAO;MACvCD,EAAc9P;MACd,OAAO;AACT;;GACA,KAAK;IACH,IAAIyD,UAAUzD,IAAO;OHcpB,SAASgQ;QACdjP,QAAQC,OAAOC,MACb,EACE,2BACA,IACA,qEACA,IACA,YACA,+DACA,uDACA,oDACA,KACAsK,KAAK;AAEX,OG3BQyE;MACA,OAAO;AACT;UH5BCrO,eAAesO,SAASjQ,IAAiB;MAC9C,MAAMG,IAASC,EAAU;QACvBJ;QACA0D,mBAAkB;QAClBzD,SAAS;UACPiQ,QAAQ;YAAE5P,MAAM;YAAWC,OAAO;;UAClCC,MAAM;YAAEF,MAAM;;UACdG,MAAM;YAAEH,MAAM;YAAWC,OAAO;;;;MAGpC,IAAIJ,EAAOO,OAAOF;QAAMG;;MACxB,MAAMwP,WACEC,EAAa;QAAEjE,UAAUhM,EAAO+L,YAAY;UAClD5K,OAAOiL,MAAYpM,EAAOO,OAAOwP,UAAU3D,EAAQ8D;MACrD,IAAIlQ,EAAOO,OAAOF,MAAM;QACtBO,QAAQC,OAAOC,MAAM,GAAGC,KAAKC,UAAUgP,GAAU,MAAM;QACvD;AACF;MAEApP,QAAQC,OAAOC,MAAM;MACrB,KAAK,MAAMsL,KAAW4D;QACpBpP,QAAQC,OAAOC,MACb,GAAGoM,UAAUd,EAAQlI,SAASgJ,UAC5Bd,EAAQ8D,QAAQ,UAAU,cACtBhD,UACJ,IAAI7F,KAAK+E,EAAQ+D,WAAW7I,mBACxB4F,UAAUd,EAAQG,aAAaW,UAAUd,EAAQgE;;AAG7D,KGAYN,CAASjQ;IACf,OAAO;;GACT,KAAK;UACGmP,UAAUnP;IAChB,OAAO;;GACT,KAAK;IACH,IAAIyD,UAAUzD,IAAO;ORPpB,SAASwQ;QACdzP,QAAQC,OAAOC,MACb,EACE,mBACA,IACA,4DACA,IACA,YACA,uDACA,YACA,KACAsK,KAAK;AAEX,OQLQiF;MACA,OAAO;AACT;IACAzQ,YAAYC;IACZ,OAAO;;GACT,KAAK;UACGuN,aAAavN;IACnB,OAAO;;GACT,KAAK;UACGqL,aAAarL;IACnB,OAAO;;GACT;IACE,MAAM,IAAIgM,MAAM,oBAAoBU;;AAE1C;;AAwEA,IAaA,SAAS+D;EACP;IACE,OAAO1P,QAAQ6O,KAAK,OAAOc,cAA0BC;AACvD,IAAE;IACA,QAAO;AACT;AACF,CAnBIF;EACFd,OACGiB,KAAKhC;IACJ7N,QAAQkO,WAAWL;KAEpBiC,MAAMhC;IACL9N,QAAQ+P,OAAO7P,MACb,GAAG4N,aAAiB7C,QAAQ6C,EAAMC,UAAUhE,OAAO+D;IAErD9N,QAAQkO,WAAW;;;;"}
|
|
@@ -225,6 +225,8 @@ exports.EVENT_LOG_FORMAT_VERSION = 1;
|
|
|
225
225
|
|
|
226
226
|
exports.EVENT_LOG_STATE_VERSION = 1;
|
|
227
227
|
|
|
228
|
+
exports.INTERNAL_DEBUG_ENV = "__eventLogDebug";
|
|
229
|
+
|
|
228
230
|
exports.INTERNAL_IPC_ENV = "__eventLogIpc";
|
|
229
231
|
|
|
230
232
|
exports.INTERNAL_PROCESS_ORIGIN_ENV = "__eventLogProcessOrigin";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionSockets-chunk.js","sources":["../../src/constants.ts","../../src/clean.ts","../../src/utils/eventFilter.ts","../../src/utils/sessionSockets.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\n\n// The POSIX tmpdir is shared across users; Windows %TEMP% is per-user\nconst uid = process.getuid?.();\nexport const EVENT_LOG_TMP_DIR = path.join(\n os.tmpdir(),\n uid == null ? 'event-log' : `event-log-${uid}`\n);\nexport const EVENT_LOG_FORMAT_VERSION = 1;\nexport const EVENT_LEVEL_DEBUG = 1;\nexport const EVENT_LOG_STATE_VERSION = 1;\nexport const LOG_DEBUG_ENV = 'LOG_DEBUG';\nexport const LOG_EVENTS_ENV = 'LOG_EVENTS';\nexport const INTERNAL_IPC_ENV = '__eventLogIpc';\nexport const INTERNAL_PROCESS_ORIGIN_ENV = '__eventLogProcessOrigin';\n\nexport const SESSION_FILES = {\n meta: 'meta.json',\n liveSocket: 'live.sock',\n ipcSocket: 'ipc.sock',\n} as const;\n\nexport const DEFAULT_SEGMENTS = 3;\nexport const DEFAULT_SEGMENT_SIZE = 512 * 1024;\n\n// Debug recording emits far more events, so retain a larger ring buffer\nexport const DEBUG_SEGMENTS = 6;\nexport const DEBUG_SEGMENT_SIZE = 1024 * 1024;\n\nexport const DEFAULT_RETAIN_MS = 7 * 24 * 60 * 60 * 1000;\nexport const DEFAULT_MAX_SESSIONS = 100;\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n DEFAULT_MAX_SESSIONS,\n DEFAULT_RETAIN_MS,\n EVENT_LOG_TMP_DIR,\n EVENT_LOG_FORMAT_VERSION,\n SESSION_FILES,\n} from './constants';\nimport type { SessionMeta } from './session';\n\ninterface SessionEntry {\n id: string;\n dir: string;\n meta: SessionMeta;\n alive: boolean;\n}\n\nconst SESSION_BASE_DIR_OVERRIDE = Symbol.for('2g/session-base-dir-override');\n\ninterface EventLogGlobal {\n [SESSION_BASE_DIR_OVERRIDE]?: string;\n}\n\nexport function cleanStaleSessionsSync() {\n const now = Date.now();\n const dead: SessionEntry[] = [];\n let removed = 0;\n\n for (const entry of getSessionEntries()) {\n if (entry.alive) continue;\n if (now - entry.meta.startedAt > DEFAULT_RETAIN_MS) {\n fs.rmSync(entry.dir, { recursive: true, force: true });\n removed++;\n } else {\n dead.push(entry);\n }\n }\n\n dead.sort((a, b) => b.meta.startedAt - a.meta.startedAt);\n for (let idx = DEFAULT_MAX_SESSIONS; idx < dead.length; idx++) {\n fs.rmSync(dead[idx].dir, { recursive: true, force: true });\n removed++;\n }\n\n return removed;\n}\n\nexport function cleanExitedSessionsSync() {\n let removed = 0;\n for (const entry of getSessionEntries()) {\n if (!entry.alive) {\n fs.rmSync(entry.dir, { recursive: true, force: true });\n removed++;\n }\n }\n return removed;\n}\n\nexport function readMetaSync(sessionDir: string): SessionMeta | null {\n try {\n const meta = JSON.parse(\n fs.readFileSync(path.join(sessionDir, SESSION_FILES.meta), 'utf8')\n );\n return isCompatibleSessionMeta(meta) ? meta : null;\n } catch {\n return null;\n }\n}\n\nexport function getSessionBaseDir() {\n return (\n (globalThis as EventLogGlobal)[SESSION_BASE_DIR_OVERRIDE] ||\n EVENT_LOG_TMP_DIR\n );\n}\n\nexport function _setSessionBaseDir(dir: string | undefined) {\n const globalScope = globalThis as EventLogGlobal;\n if (dir) globalScope[SESSION_BASE_DIR_OVERRIDE] = dir;\n else delete globalScope[SESSION_BASE_DIR_OVERRIDE];\n}\n\n// Only the newest session per pid can be alive: whatever owns a recycled PID\n// now is not the process behind any older dir claiming it\nexport function newestSessionIds(\n sessions: Array<{ id: string; pid: number; startedAt: number }>\n) {\n const newest = new Map<number, { id: string; startedAt: number }>();\n for (const session of sessions) {\n const current = newest.get(session.pid);\n if (\n !current ||\n session.startedAt > current.startedAt ||\n (session.startedAt === current.startedAt && session.id > current.id)\n ) {\n newest.set(session.pid, session);\n }\n }\n return new Set([...newest.values()].map(session => session.id));\n}\n\nexport function isPidAlive(pid: number) {\n if (!pid || pid === process.pid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getSessionEntries() {\n try {\n const baseDir = getSessionBaseDir();\n const entries = fs\n .readdirSync(baseDir, { withFileTypes: true })\n .filter(entry => entry.isDirectory())\n .map(entry => {\n const dir = path.join(baseDir, entry.name);\n return { id: entry.name, dir, meta: readMetaSync(dir), alive: false };\n })\n .filter((entry): entry is SessionEntry => entry.meta !== null);\n const newest = newestSessionIds(\n entries.map(entry => ({\n id: entry.id,\n pid: entry.meta.pid,\n startedAt: entry.meta.startedAt,\n }))\n );\n return entries.map(entry => ({\n ...entry,\n alive: isPidAlive(entry.meta.pid) && newest.has(entry.id),\n }));\n } catch {\n return [];\n }\n}\n\nfunction isCompatibleSessionMeta(meta: unknown): meta is SessionMeta {\n return (\n !!meta &&\n typeof meta === 'object' &&\n (meta as SessionMeta).formatVersion === EVENT_LOG_FORMAT_VERSION\n );\n}\n","import type { ParsedEvent } from '../types';\n\nconst DURATION_UNITS: Record<string, number> = {\n ms: 1,\n msec: 1,\n msecs: 1,\n millisecond: 1,\n milliseconds: 1,\n s: 1000,\n sec: 1000,\n secs: 1000,\n second: 1000,\n seconds: 1000,\n m: 60_000,\n min: 60_000,\n mins: 60_000,\n minute: 60_000,\n minutes: 60_000,\n h: 3_600_000,\n hr: 3_600_000,\n hrs: 3_600_000,\n hour: 3_600_000,\n hours: 3_600_000,\n};\n\nexport interface EventFilterOptions {\n since?: number | string | Date;\n filter?: string | string[];\n spans?: boolean;\n debug?: boolean;\n}\n\nexport function parseEventLine(\n line: string,\n options: EventFilterOptions = {},\n eventFilter = compileEventFilter(options.filter),\n since = parseSince(options.since)\n): ParsedEvent | null {\n try {\n const event = JSON.parse(line) as ParsedEvent;\n return event &&\n typeof event._e === 'string' &&\n typeof event._t === 'number' &&\n matches(event, options.debug, since, options.spans, eventFilter)\n ? event\n : null;\n } catch {\n return null;\n }\n}\n\nexport function parseSince(value: EventFilterOptions['since']) {\n if (value == null) return undefined;\n if (value instanceof Date) {\n const timestamp = value.getTime();\n if (Number.isFinite(timestamp)) return timestamp;\n throw new Error(`Invalid since: ${value.toString()}`);\n }\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 0)\n throw new Error(`Invalid since: ${value}`);\n return normalizeUnixTimestamp(value);\n }\n\n const input = value.trim();\n if (!input) return undefined;\n if (/^\\d+(?:\\.\\d+)?$/.test(input))\n return normalizeUnixTimestamp(Number(input));\n\n const duration = parseDuration(input);\n if (duration != null) return Date.now() - duration;\n\n const timestamp = Date.parse(input);\n if (Number.isFinite(timestamp)) return timestamp;\n throw new Error(`Invalid since: ${value}`);\n}\n\nexport function parseDuration(value: string) {\n const input = value.trim().toLowerCase();\n if (!input) return undefined;\n\n let total = 0;\n let index = 0;\n const pattern =\n /(\\d+(?:\\.\\d+)?)\\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h)?/gy;\n\n while (index < input.length) {\n pattern.lastIndex = index;\n const match = pattern.exec(input);\n if (!match) return undefined;\n const amount = Number(match[1]);\n if (!Number.isFinite(amount)) return undefined;\n total += amount * DURATION_UNITS[match[2] ?? 'ms'];\n index = pattern.lastIndex;\n while (input[index] === ' ' || input[index] === ',') index++;\n }\n\n return total;\n}\n\nexport function compileEventFilter(filter: string | string[] | undefined) {\n const patterns = normalizeEventFilter(filter);\n if (!patterns.length) return null;\n return new RegExp(`^(?:${patterns.map(eventPatternToRegex).join('|')})$`);\n}\n\nexport function matchesTapOptions(\n event: ParsedEvent,\n options: {\n since?: number;\n spans?: boolean;\n debug?: boolean;\n filter?: EventFilterOptions['filter'];\n },\n eventFilter = compileEventFilter(options.filter)\n) {\n return matches(\n event,\n options.debug,\n options.since,\n options.spans,\n eventFilter\n );\n}\n\nfunction matches(\n event: ParsedEvent,\n debug: boolean | undefined,\n since: number | undefined,\n spans: boolean | undefined,\n eventFilter: RegExp | null\n) {\n if (debug !== true && event._l) return false;\n if (since !== undefined && event._t < since) return false;\n if (spans === true && typeof event._d !== 'number') return false;\n return !eventFilter || eventFilter.test(event._e);\n}\n\nfunction normalizeEventFilter(filter: string | string[] | undefined) {\n const values = Array.isArray(filter) ? filter : filter ? [filter] : [];\n return values\n .flatMap(value => value.split(','))\n .map(value => value.trim())\n .filter(Boolean);\n}\n\nfunction eventPatternToRegex(pattern: string) {\n if (pattern.includes('*')) {\n return pattern.split('*').map(escapeRegex).join('.*');\n } else {\n return `${escapeRegex(pattern)}(?::.*)?`;\n }\n}\n\nfunction escapeRegex(value: string) {\n return value.replace(/[|\\\\{}()[\\]^$+?.]/g, '\\\\$&');\n}\n\nfunction normalizeUnixTimestamp(value: number) {\n return value < 10_000_000_000 ? value * 1000 : value;\n}\n","import fs from 'node:fs';\nimport net from 'node:net';\nimport path from 'node:path';\n\nimport { SESSION_FILES } from '../constants';\n\nexport interface SocketAddress {\n // Relative to the session dir, or a Windows named-pipe name\n name: string;\n path: string;\n}\n\n// Windows net servers cannot listen on filesystem paths\nexport function createSocketAddress(\n sessionDir: string,\n sessionId: string,\n kind: 'live' | 'ipc'\n): SocketAddress {\n const name =\n process.platform === 'win32'\n ? `\\\\\\\\.\\\\pipe\\\\event-log-${sessionId}-${kind}`\n : kind === 'live'\n ? SESSION_FILES.liveSocket\n : SESSION_FILES.ipcSocket;\n return { name, path: resolveSocketPath(sessionDir, name) };\n}\n\n// Meta socket values are relative to the session dir; pipe names are never relative\nexport function resolveSocketPath(sessionDir: string, value: string) {\n return value.startsWith('\\\\\\\\') ? value : path.join(sessionDir, value);\n}\n\n// Pipes are not filesystem entries\nexport function removeSocket(socketPath: string) {\n if (!socketPath.startsWith('\\\\\\\\')) fs.rmSync(socketPath, { force: true });\n}\n\nexport function listenSocket(server: net.Server, socketPath: string) {\n let closed = false;\n let listening = false;\n let shouldClose = false;\n\n // Session sockets must never hold the host process open\n server.unref();\n\n const close = () => {\n shouldClose = true;\n if (listening && !closed) server.close();\n };\n\n server.once('listening', () => {\n listening = true;\n if (shouldClose) close();\n });\n server.once('close', () => {\n closed = true;\n listening = false;\n });\n server.on('error', () => {\n closed = true;\n try {\n server.close(() => {});\n } catch {}\n });\n server.listen(socketPath);\n\n return close;\n}\n"],"names":["uid","process","getuid","EVENT_LOG_TMP_DIR","path","join","os","tmpdir","SESSION_FILES","meta","liveSocket","ipcSocket","SESSION_BASE_DIR_OVERRIDE","Symbol","for","readMetaSync","sessionDir","JSON","parse","fs","readFileSync","isCompatibleSessionMeta","formatVersion","getSessionBaseDir","globalThis","newestSessionIds","sessions","newest","Map","session","current","get","pid","startedAt","id","set","Set","values","map","isPidAlive","kill","getSessionEntries","baseDir","entries","readdirSync","withFileTypes","filter","entry","isDirectory","dir","name","alive","has","DURATION_UNITS","ms","msec","msecs","millisecond","milliseconds","s","sec","secs","second","seconds","m","min","mins","minute","minutes","h","hr","hrs","hour","hours","parseSince","value","Date","timestamp","getTime","Number","isFinite","Error","toString","normalizeUnixTimestamp","input","trim","test","duration","parseDuration","now","toLowerCase","total","index","pattern","length","lastIndex","match","exec","amount","compileEventFilter","patterns","normalizeEventFilter","Array","isArray","flatMap","split","Boolean","RegExp","eventPatternToRegex","matches","event","debug","since","spans","eventFilter","_l","undefined","_t","_d","_e","includes","escapeRegex","replace","resolveSocketPath","startsWith","cleanExitedSessionsSync","removed","rmSync","recursive","force","cleanStaleSessionsSync","dead","push","sort","a","b","idx","createSocketAddress","sessionId","kind","platform","listenSocket","server","socketPath","closed","listening","shouldClose","unref","close","once","on","listen","matchesTapOptions","options","parseEventLine","line","removeSocket"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sessionSockets-chunk.js","sources":["../../src/constants.ts","../../src/clean.ts","../../src/utils/eventFilter.ts","../../src/utils/sessionSockets.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\n\n// The POSIX tmpdir is shared across users; Windows %TEMP% is per-user\nconst uid = process.getuid?.();\nexport const EVENT_LOG_TMP_DIR = path.join(\n os.tmpdir(),\n uid == null ? 'event-log' : `event-log-${uid}`\n);\nexport const EVENT_LOG_FORMAT_VERSION = 1;\nexport const EVENT_LEVEL_DEBUG = 1;\nexport const EVENT_LOG_STATE_VERSION = 1;\nexport const LOG_DEBUG_ENV = 'LOG_DEBUG';\nexport const LOG_EVENTS_ENV = 'LOG_EVENTS';\nexport const INTERNAL_DEBUG_ENV = '__eventLogDebug';\nexport const INTERNAL_IPC_ENV = '__eventLogIpc';\nexport const INTERNAL_PROCESS_ORIGIN_ENV = '__eventLogProcessOrigin';\n\nexport const SESSION_FILES = {\n meta: 'meta.json',\n liveSocket: 'live.sock',\n ipcSocket: 'ipc.sock',\n} as const;\n\nexport const DEFAULT_SEGMENTS = 3;\nexport const DEFAULT_SEGMENT_SIZE = 512 * 1024;\n\n// Debug recording emits far more events, so retain a larger ring buffer\nexport const DEBUG_SEGMENTS = 6;\nexport const DEBUG_SEGMENT_SIZE = 1024 * 1024;\n\nexport const DEFAULT_RETAIN_MS = 7 * 24 * 60 * 60 * 1000;\nexport const DEFAULT_MAX_SESSIONS = 100;\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n DEFAULT_MAX_SESSIONS,\n DEFAULT_RETAIN_MS,\n EVENT_LOG_TMP_DIR,\n EVENT_LOG_FORMAT_VERSION,\n SESSION_FILES,\n} from './constants';\nimport type { SessionMeta } from './session';\n\ninterface SessionEntry {\n id: string;\n dir: string;\n meta: SessionMeta;\n alive: boolean;\n}\n\nconst SESSION_BASE_DIR_OVERRIDE = Symbol.for('2g/session-base-dir-override');\n\ninterface EventLogGlobal {\n [SESSION_BASE_DIR_OVERRIDE]?: string;\n}\n\nexport function cleanStaleSessionsSync() {\n const now = Date.now();\n const dead: SessionEntry[] = [];\n let removed = 0;\n\n for (const entry of getSessionEntries()) {\n if (entry.alive) continue;\n if (now - entry.meta.startedAt > DEFAULT_RETAIN_MS) {\n fs.rmSync(entry.dir, { recursive: true, force: true });\n removed++;\n } else {\n dead.push(entry);\n }\n }\n\n dead.sort((a, b) => b.meta.startedAt - a.meta.startedAt);\n for (let idx = DEFAULT_MAX_SESSIONS; idx < dead.length; idx++) {\n fs.rmSync(dead[idx].dir, { recursive: true, force: true });\n removed++;\n }\n\n return removed;\n}\n\nexport function cleanExitedSessionsSync() {\n let removed = 0;\n for (const entry of getSessionEntries()) {\n if (!entry.alive) {\n fs.rmSync(entry.dir, { recursive: true, force: true });\n removed++;\n }\n }\n return removed;\n}\n\nexport function readMetaSync(sessionDir: string): SessionMeta | null {\n try {\n const meta = JSON.parse(\n fs.readFileSync(path.join(sessionDir, SESSION_FILES.meta), 'utf8')\n );\n return isCompatibleSessionMeta(meta) ? meta : null;\n } catch {\n return null;\n }\n}\n\nexport function getSessionBaseDir() {\n return (\n (globalThis as EventLogGlobal)[SESSION_BASE_DIR_OVERRIDE] ||\n EVENT_LOG_TMP_DIR\n );\n}\n\nexport function _setSessionBaseDir(dir: string | undefined) {\n const globalScope = globalThis as EventLogGlobal;\n if (dir) globalScope[SESSION_BASE_DIR_OVERRIDE] = dir;\n else delete globalScope[SESSION_BASE_DIR_OVERRIDE];\n}\n\n// Only the newest session per pid can be alive: whatever owns a recycled PID\n// now is not the process behind any older dir claiming it\nexport function newestSessionIds(\n sessions: Array<{ id: string; pid: number; startedAt: number }>\n) {\n const newest = new Map<number, { id: string; startedAt: number }>();\n for (const session of sessions) {\n const current = newest.get(session.pid);\n if (\n !current ||\n session.startedAt > current.startedAt ||\n (session.startedAt === current.startedAt && session.id > current.id)\n ) {\n newest.set(session.pid, session);\n }\n }\n return new Set([...newest.values()].map(session => session.id));\n}\n\nexport function isPidAlive(pid: number) {\n if (!pid || pid === process.pid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getSessionEntries() {\n try {\n const baseDir = getSessionBaseDir();\n const entries = fs\n .readdirSync(baseDir, { withFileTypes: true })\n .filter(entry => entry.isDirectory())\n .map(entry => {\n const dir = path.join(baseDir, entry.name);\n return { id: entry.name, dir, meta: readMetaSync(dir), alive: false };\n })\n .filter((entry): entry is SessionEntry => entry.meta !== null);\n const newest = newestSessionIds(\n entries.map(entry => ({\n id: entry.id,\n pid: entry.meta.pid,\n startedAt: entry.meta.startedAt,\n }))\n );\n return entries.map(entry => ({\n ...entry,\n alive: isPidAlive(entry.meta.pid) && newest.has(entry.id),\n }));\n } catch {\n return [];\n }\n}\n\nfunction isCompatibleSessionMeta(meta: unknown): meta is SessionMeta {\n return (\n !!meta &&\n typeof meta === 'object' &&\n (meta as SessionMeta).formatVersion === EVENT_LOG_FORMAT_VERSION\n );\n}\n","import type { ParsedEvent } from '../types';\n\nconst DURATION_UNITS: Record<string, number> = {\n ms: 1,\n msec: 1,\n msecs: 1,\n millisecond: 1,\n milliseconds: 1,\n s: 1000,\n sec: 1000,\n secs: 1000,\n second: 1000,\n seconds: 1000,\n m: 60_000,\n min: 60_000,\n mins: 60_000,\n minute: 60_000,\n minutes: 60_000,\n h: 3_600_000,\n hr: 3_600_000,\n hrs: 3_600_000,\n hour: 3_600_000,\n hours: 3_600_000,\n};\n\nexport interface EventFilterOptions {\n since?: number | string | Date;\n filter?: string | string[];\n spans?: boolean;\n debug?: boolean;\n}\n\nexport function parseEventLine(\n line: string,\n options: EventFilterOptions = {},\n eventFilter = compileEventFilter(options.filter),\n since = parseSince(options.since)\n): ParsedEvent | null {\n try {\n const event = JSON.parse(line) as ParsedEvent;\n return event &&\n typeof event._e === 'string' &&\n typeof event._t === 'number' &&\n matches(event, options.debug, since, options.spans, eventFilter)\n ? event\n : null;\n } catch {\n return null;\n }\n}\n\nexport function parseSince(value: EventFilterOptions['since']) {\n if (value == null) return undefined;\n if (value instanceof Date) {\n const timestamp = value.getTime();\n if (Number.isFinite(timestamp)) return timestamp;\n throw new Error(`Invalid since: ${value.toString()}`);\n }\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 0)\n throw new Error(`Invalid since: ${value}`);\n return normalizeUnixTimestamp(value);\n }\n\n const input = value.trim();\n if (!input) return undefined;\n if (/^\\d+(?:\\.\\d+)?$/.test(input))\n return normalizeUnixTimestamp(Number(input));\n\n const duration = parseDuration(input);\n if (duration != null) return Date.now() - duration;\n\n const timestamp = Date.parse(input);\n if (Number.isFinite(timestamp)) return timestamp;\n throw new Error(`Invalid since: ${value}`);\n}\n\nexport function parseDuration(value: string) {\n const input = value.trim().toLowerCase();\n if (!input) return undefined;\n\n let total = 0;\n let index = 0;\n const pattern =\n /(\\d+(?:\\.\\d+)?)\\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h)?/gy;\n\n while (index < input.length) {\n pattern.lastIndex = index;\n const match = pattern.exec(input);\n if (!match) return undefined;\n const amount = Number(match[1]);\n if (!Number.isFinite(amount)) return undefined;\n total += amount * DURATION_UNITS[match[2] ?? 'ms'];\n index = pattern.lastIndex;\n while (input[index] === ' ' || input[index] === ',') index++;\n }\n\n return total;\n}\n\nexport function compileEventFilter(filter: string | string[] | undefined) {\n const patterns = normalizeEventFilter(filter);\n if (!patterns.length) return null;\n return new RegExp(`^(?:${patterns.map(eventPatternToRegex).join('|')})$`);\n}\n\nexport function matchesTapOptions(\n event: ParsedEvent,\n options: {\n since?: number;\n spans?: boolean;\n debug?: boolean;\n filter?: EventFilterOptions['filter'];\n },\n eventFilter = compileEventFilter(options.filter)\n) {\n return matches(\n event,\n options.debug,\n options.since,\n options.spans,\n eventFilter\n );\n}\n\nfunction matches(\n event: ParsedEvent,\n debug: boolean | undefined,\n since: number | undefined,\n spans: boolean | undefined,\n eventFilter: RegExp | null\n) {\n if (debug !== true && event._l) return false;\n if (since !== undefined && event._t < since) return false;\n if (spans === true && typeof event._d !== 'number') return false;\n return !eventFilter || eventFilter.test(event._e);\n}\n\nfunction normalizeEventFilter(filter: string | string[] | undefined) {\n const values = Array.isArray(filter) ? filter : filter ? [filter] : [];\n return values\n .flatMap(value => value.split(','))\n .map(value => value.trim())\n .filter(Boolean);\n}\n\nfunction eventPatternToRegex(pattern: string) {\n if (pattern.includes('*')) {\n return pattern.split('*').map(escapeRegex).join('.*');\n } else {\n return `${escapeRegex(pattern)}(?::.*)?`;\n }\n}\n\nfunction escapeRegex(value: string) {\n return value.replace(/[|\\\\{}()[\\]^$+?.]/g, '\\\\$&');\n}\n\nfunction normalizeUnixTimestamp(value: number) {\n return value < 10_000_000_000 ? value * 1000 : value;\n}\n","import fs from 'node:fs';\nimport net from 'node:net';\nimport path from 'node:path';\n\nimport { SESSION_FILES } from '../constants';\n\nexport interface SocketAddress {\n // Relative to the session dir, or a Windows named-pipe name\n name: string;\n path: string;\n}\n\n// Windows net servers cannot listen on filesystem paths\nexport function createSocketAddress(\n sessionDir: string,\n sessionId: string,\n kind: 'live' | 'ipc'\n): SocketAddress {\n const name =\n process.platform === 'win32'\n ? `\\\\\\\\.\\\\pipe\\\\event-log-${sessionId}-${kind}`\n : kind === 'live'\n ? SESSION_FILES.liveSocket\n : SESSION_FILES.ipcSocket;\n return { name, path: resolveSocketPath(sessionDir, name) };\n}\n\n// Meta socket values are relative to the session dir; pipe names are never relative\nexport function resolveSocketPath(sessionDir: string, value: string) {\n return value.startsWith('\\\\\\\\') ? value : path.join(sessionDir, value);\n}\n\n// Pipes are not filesystem entries\nexport function removeSocket(socketPath: string) {\n if (!socketPath.startsWith('\\\\\\\\')) fs.rmSync(socketPath, { force: true });\n}\n\nexport function listenSocket(server: net.Server, socketPath: string) {\n let closed = false;\n let listening = false;\n let shouldClose = false;\n\n // Session sockets must never hold the host process open\n server.unref();\n\n const close = () => {\n shouldClose = true;\n if (listening && !closed) server.close();\n };\n\n server.once('listening', () => {\n listening = true;\n if (shouldClose) close();\n });\n server.once('close', () => {\n closed = true;\n listening = false;\n });\n server.on('error', () => {\n closed = true;\n try {\n server.close(() => {});\n } catch {}\n });\n server.listen(socketPath);\n\n return close;\n}\n"],"names":["uid","process","getuid","EVENT_LOG_TMP_DIR","path","join","os","tmpdir","SESSION_FILES","meta","liveSocket","ipcSocket","SESSION_BASE_DIR_OVERRIDE","Symbol","for","readMetaSync","sessionDir","JSON","parse","fs","readFileSync","isCompatibleSessionMeta","formatVersion","getSessionBaseDir","globalThis","newestSessionIds","sessions","newest","Map","session","current","get","pid","startedAt","id","set","Set","values","map","isPidAlive","kill","getSessionEntries","baseDir","entries","readdirSync","withFileTypes","filter","entry","isDirectory","dir","name","alive","has","DURATION_UNITS","ms","msec","msecs","millisecond","milliseconds","s","sec","secs","second","seconds","m","min","mins","minute","minutes","h","hr","hrs","hour","hours","parseSince","value","Date","timestamp","getTime","Number","isFinite","Error","toString","normalizeUnixTimestamp","input","trim","test","duration","parseDuration","now","toLowerCase","total","index","pattern","length","lastIndex","match","exec","amount","compileEventFilter","patterns","normalizeEventFilter","Array","isArray","flatMap","split","Boolean","RegExp","eventPatternToRegex","matches","event","debug","since","spans","eventFilter","_l","undefined","_t","_d","_e","includes","escapeRegex","replace","resolveSocketPath","startsWith","cleanExitedSessionsSync","removed","rmSync","recursive","force","cleanStaleSessionsSync","dead","push","sort","a","b","idx","createSocketAddress","sessionId","kind","platform","listenSocket","server","socketPath","closed","listening","shouldClose","unref","close","once","on","listen","matchesTapOptions","options","parseEventLine","line","removeSocket"],"mappings":"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"}
|
|
@@ -18,25 +18,27 @@ const a = "LOG_DEBUG";
|
|
|
18
18
|
|
|
19
19
|
const l = "LOG_EVENTS";
|
|
20
20
|
|
|
21
|
-
const u = "
|
|
21
|
+
const u = "__eventLogDebug";
|
|
22
22
|
|
|
23
|
-
const m = "
|
|
23
|
+
const m = "__eventLogIpc";
|
|
24
24
|
|
|
25
|
-
const f =
|
|
25
|
+
const f = "__eventLogProcessOrigin";
|
|
26
|
+
|
|
27
|
+
const p = {
|
|
26
28
|
meta: "meta.json",
|
|
27
29
|
liveSocket: "live.sock",
|
|
28
30
|
ipcSocket: "ipc.sock"
|
|
29
31
|
};
|
|
30
32
|
|
|
31
|
-
const
|
|
33
|
+
const d = 3;
|
|
32
34
|
|
|
33
|
-
const
|
|
35
|
+
const S = 524288;
|
|
34
36
|
|
|
35
|
-
const
|
|
37
|
+
const v = 6;
|
|
36
38
|
|
|
37
|
-
const
|
|
39
|
+
const h = 1048576;
|
|
38
40
|
|
|
39
|
-
const
|
|
41
|
+
const g = Symbol.for("2g/session-base-dir-override");
|
|
40
42
|
|
|
41
43
|
function cleanStaleSessionsSync() {
|
|
42
44
|
const t = Date.now();
|
|
@@ -83,7 +85,7 @@ function cleanExitedSessionsSync() {
|
|
|
83
85
|
|
|
84
86
|
function readMetaSync(n) {
|
|
85
87
|
try {
|
|
86
|
-
const s = JSON.parse(e.readFileSync(t.join(n,
|
|
88
|
+
const s = JSON.parse(e.readFileSync(t.join(n, p.meta), "utf8"));
|
|
87
89
|
return function isCompatibleSessionMeta(e) {
|
|
88
90
|
return !!e && "object" == typeof e && 1 === e.formatVersion;
|
|
89
91
|
}(s) ? s : null;
|
|
@@ -93,7 +95,7 @@ function readMetaSync(n) {
|
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
function getSessionBaseDir() {
|
|
96
|
-
return globalThis[
|
|
98
|
+
return globalThis[g] || r;
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
function newestSessionIds(e) {
|
|
@@ -147,7 +149,7 @@ function getSessionEntries() {
|
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
const
|
|
152
|
+
const y = {
|
|
151
153
|
ms: 1,
|
|
152
154
|
msec: 1,
|
|
153
155
|
msecs: 1,
|
|
@@ -232,7 +234,7 @@ function parseDuration(e) {
|
|
|
232
234
|
if (!Number.isFinite(i)) {
|
|
233
235
|
return;
|
|
234
236
|
}
|
|
235
|
-
n += i *
|
|
237
|
+
n += i * y[e[2] ?? "ms"];
|
|
236
238
|
s = r.lastIndex;
|
|
237
239
|
while (" " === t[s] || "," === t[s]) {
|
|
238
240
|
s++;
|
|
@@ -286,7 +288,7 @@ function normalizeUnixTimestamp(e) {
|
|
|
286
288
|
}
|
|
287
289
|
|
|
288
290
|
function createSocketAddress(e, t, n) {
|
|
289
|
-
const s = "win32" === process.platform ? `\\\\.\\pipe\\event-log-${t}-${n}` : "live" === n ?
|
|
291
|
+
const s = "win32" === process.platform ? `\\\\.\\pipe\\event-log-${t}-${n}` : "live" === n ? p.liveSocket : p.ipcSocket;
|
|
290
292
|
return {
|
|
291
293
|
name: s,
|
|
292
294
|
path: resolveSocketPath(e, s)
|
|
@@ -336,5 +338,5 @@ function listenSocket(e, t) {
|
|
|
336
338
|
return close;
|
|
337
339
|
}
|
|
338
340
|
|
|
339
|
-
export {
|
|
341
|
+
export { v as D, c as E, m as I, a as L, p as S, cleanStaleSessionsSync as a, compileEventFilter as b, cleanExitedSessionsSync as c, parseDuration as d, parseEventLine as e, o as f, u as g, l as h, f as i, d as j, h as k, listenSocket as l, matchesTapOptions as m, S as n, createSocketAddress as o, parseSince as p, i as q, removeSocket as r, getSessionBaseDir as s, readMetaSync as t, newestSessionIds as u, isPidAlive as v, resolveSocketPath as w };
|
|
340
342
|
//# sourceMappingURL=sessionSockets-chunk.mjs.map
|