@neat.is/core 0.3.8 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-LQ3JFBTX.js → chunk-CB2UK4EH.js} +139 -41
- package/dist/chunk-CB2UK4EH.js.map +1 -0
- package/dist/{chunk-V4TU7OKZ.js → chunk-D5PIJFBE.js} +2 -2
- package/dist/{chunk-7TYESDAI.js → chunk-KYRIQIPG.js} +48 -11
- package/dist/chunk-KYRIQIPG.js.map +1 -0
- package/dist/{chunk-CZ3T6TE2.js → chunk-NTQHMXWE.js} +239 -71
- package/dist/chunk-NTQHMXWE.js.map +1 -0
- package/dist/cli.cjs +1209 -176
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +4 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +940 -99
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +418 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +427 -131
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-S3AENOZ6.js → otel-grpc-QTX2YQJZ.js} +3 -3
- package/dist/server.cjs +401 -203
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +5 -4
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-7TYESDAI.js.map +0 -1
- package/dist/chunk-CZ3T6TE2.js.map +0 -1
- package/dist/chunk-LQ3JFBTX.js.map +0 -1
- /package/dist/{chunk-V4TU7OKZ.js.map → chunk-D5PIJFBE.js.map} +0 -0
- /package/dist/{otel-grpc-S3AENOZ6.js.map → otel-grpc-QTX2YQJZ.js.map} +0 -0
package/dist/server.js
CHANGED
|
@@ -13,15 +13,15 @@ import {
|
|
|
13
13
|
pathsForProject,
|
|
14
14
|
startPersistLoop,
|
|
15
15
|
startStalenessLoop
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-NTQHMXWE.js";
|
|
17
17
|
import {
|
|
18
18
|
startOtelGrpcReceiver
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-D5PIJFBE.js";
|
|
20
20
|
import {
|
|
21
21
|
assertBindAuthority,
|
|
22
22
|
buildOtelReceiver,
|
|
23
23
|
readAuthEnv
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-KYRIQIPG.js";
|
|
25
25
|
|
|
26
26
|
// src/server.ts
|
|
27
27
|
import path from "path";
|
|
@@ -76,7 +76,8 @@ async function main() {
|
|
|
76
76
|
const app = await buildApi({
|
|
77
77
|
projects: registry,
|
|
78
78
|
authToken: auth.authToken,
|
|
79
|
-
trustProxy: auth.trustProxy
|
|
79
|
+
trustProxy: auth.trustProxy,
|
|
80
|
+
publicRead: auth.publicRead
|
|
80
81
|
});
|
|
81
82
|
await app.listen({ port, host });
|
|
82
83
|
console.log(`neat-core listening on http://${host}:${port}`);
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import path from 'node:path'\nimport { DEFAULT_PROJECT, getGraph } from './graph.js'\nimport { buildApi } from './api.js'\nimport { extractFromDirectory } from './extract.js'\nimport { loadGraphFromDisk, startPersistLoop } from './persist.js'\nimport { buildOtelReceiver } from './otel.js'\nimport { startOtelGrpcReceiver } from './otel-grpc.js'\nimport { makeSpanHandler, startStalenessLoop } from './ingest.js'\nimport { buildSearchIndex } from './search.js'\nimport { Projects, parseExtraProjects, pathsForProject } from './projects.js'\nimport { assertBindAuthority, readAuthEnv } from './auth.js'\n\nasync function bootProject(\n registry: Projects,\n name: string,\n scanPath: string | undefined,\n baseDir: string,\n): Promise<void> {\n const paths = pathsForProject(name, baseDir)\n const graph = getGraph(name)\n await loadGraphFromDisk(graph, paths.snapshotPath)\n\n if (scanPath) {\n const r = await extractFromDirectory(graph, scanPath)\n console.log(\n `[${name}] extract: ${r.nodesAdded} new nodes, ${r.edgesAdded} new edges (graph total ${graph.order}/${graph.size})`,\n )\n } else {\n console.log(`[${name}] loaded ${graph.order} nodes / ${graph.size} edges from snapshot`)\n }\n\n startPersistLoop(graph, paths.snapshotPath)\n startStalenessLoop(graph, { staleEventsPath: paths.staleEventsPath })\n\n const searchIndex = await buildSearchIndex(graph, {\n cachePath: paths.embeddingsCachePath,\n }).catch((err) => {\n console.warn(\n `[${name}] semantic_search: index build failed (${(err as Error).message}); falling back to inline substring`,\n )\n return undefined\n })\n if (searchIndex) {\n console.log(`[${name}] semantic_search: ${searchIndex.provider} provider`)\n }\n\n registry.set(name, {\n scanPath,\n paths,\n searchIndex,\n })\n}\n\nasync function main(): Promise<void> {\n const baseDirEnv = process.env.NEAT_OUT_DIR\n const legacyOutPath = process.env.NEAT_OUT_PATH\n const baseDir = baseDirEnv\n ? path.resolve(baseDirEnv)\n : legacyOutPath\n ? path.resolve(path.dirname(legacyOutPath))\n : path.resolve('./neat-out')\n\n const defaultScanPath = path.resolve(process.env.NEAT_SCAN_PATH ?? './demo')\n const registry = new Projects()\n\n // Default project always exists. NEAT_SCAN_PATH still wires it to a scan\n // root so existing single-project users see no behaviour change.\n await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir)\n\n // Extra projects come from NEAT_PROJECTS=a,b,c. Their snapshots load from\n // <baseDir>/<name>.json; they have no scan path by default (callers can\n // POST /projects/<name>/graph/scan after wiring NEAT_PROJECT_SCAN_PATH_<NAME>).\n for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {\n const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`\n const projectScan = process.env[envKey]\n await bootProject(registry, name, projectScan ? path.resolve(projectScan) : undefined, baseDir)\n }\n\n const host = process.env.HOST ?? '0.0.0.0'\n const port = Number(process.env.PORT ?? 8080)\n const otelPort = Number(process.env.OTEL_PORT ?? 4318)\n\n // ADR-073 §3 — refuse to bind a public address without a token.\n const auth = readAuthEnv()\n assertBindAuthority(host, auth.authToken)\n\n const app = await buildApi({\n projects: registry,\n authToken: auth.authToken,\n trustProxy: auth.trustProxy,\n })\n await app.listen({ port, host })\n console.log(`neat-core listening on http://${host}:${port}`)\n console.log(` base dir: ${baseDir}`)\n console.log(` projects: ${registry.list().join(', ')}`)\n\n // OTel ingest stays single-project for now: spans always land in the\n // default project's graph + errors log. Multi-project routing for spans\n // is a future concern (would need a header / resource attr).\n const defaultCtx = registry.get(DEFAULT_PROJECT)\n if (defaultCtx) {\n const onSpan = makeSpanHandler({\n graph: defaultCtx.graph,\n errorsPath: defaultCtx.paths.errorsPath,\n })\n const otelApp = await buildOtelReceiver({\n onSpan,\n authToken: auth.otelToken,\n trustProxy: auth.trustProxy,\n })\n await otelApp.listen({ port: otelPort, host })\n console.log(`neat-core OTLP receiver on http://${host}:${otelPort}/v1/traces`)\n\n if (process.env.NEAT_OTLP_GRPC === 'true') {\n const grpcPort = Number(process.env.NEAT_OTLP_GRPC_PORT ?? 4317)\n const grpcReceiver = await startOtelGrpcReceiver({\n onSpan,\n host,\n port: grpcPort,\n authToken: auth.otelToken,\n trustProxy: auth.trustProxy,\n })\n console.log(`neat-core OTLP/gRPC receiver on ${grpcReceiver.address}`)\n }\n }\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import path from 'node:path'\nimport { DEFAULT_PROJECT, getGraph } from './graph.js'\nimport { buildApi } from './api.js'\nimport { extractFromDirectory } from './extract.js'\nimport { loadGraphFromDisk, startPersistLoop } from './persist.js'\nimport { buildOtelReceiver } from './otel.js'\nimport { startOtelGrpcReceiver } from './otel-grpc.js'\nimport { makeSpanHandler, startStalenessLoop } from './ingest.js'\nimport { buildSearchIndex } from './search.js'\nimport { Projects, parseExtraProjects, pathsForProject } from './projects.js'\nimport { assertBindAuthority, readAuthEnv } from './auth.js'\n\nasync function bootProject(\n registry: Projects,\n name: string,\n scanPath: string | undefined,\n baseDir: string,\n): Promise<void> {\n const paths = pathsForProject(name, baseDir)\n const graph = getGraph(name)\n await loadGraphFromDisk(graph, paths.snapshotPath)\n\n if (scanPath) {\n const r = await extractFromDirectory(graph, scanPath)\n console.log(\n `[${name}] extract: ${r.nodesAdded} new nodes, ${r.edgesAdded} new edges (graph total ${graph.order}/${graph.size})`,\n )\n } else {\n console.log(`[${name}] loaded ${graph.order} nodes / ${graph.size} edges from snapshot`)\n }\n\n startPersistLoop(graph, paths.snapshotPath)\n startStalenessLoop(graph, { staleEventsPath: paths.staleEventsPath })\n\n const searchIndex = await buildSearchIndex(graph, {\n cachePath: paths.embeddingsCachePath,\n }).catch((err) => {\n console.warn(\n `[${name}] semantic_search: index build failed (${(err as Error).message}); falling back to inline substring`,\n )\n return undefined\n })\n if (searchIndex) {\n console.log(`[${name}] semantic_search: ${searchIndex.provider} provider`)\n }\n\n registry.set(name, {\n scanPath,\n paths,\n searchIndex,\n })\n}\n\nasync function main(): Promise<void> {\n const baseDirEnv = process.env.NEAT_OUT_DIR\n const legacyOutPath = process.env.NEAT_OUT_PATH\n const baseDir = baseDirEnv\n ? path.resolve(baseDirEnv)\n : legacyOutPath\n ? path.resolve(path.dirname(legacyOutPath))\n : path.resolve('./neat-out')\n\n const defaultScanPath = path.resolve(process.env.NEAT_SCAN_PATH ?? './demo')\n const registry = new Projects()\n\n // Default project always exists. NEAT_SCAN_PATH still wires it to a scan\n // root so existing single-project users see no behaviour change.\n await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir)\n\n // Extra projects come from NEAT_PROJECTS=a,b,c. Their snapshots load from\n // <baseDir>/<name>.json; they have no scan path by default (callers can\n // POST /projects/<name>/graph/scan after wiring NEAT_PROJECT_SCAN_PATH_<NAME>).\n for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {\n const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`\n const projectScan = process.env[envKey]\n await bootProject(registry, name, projectScan ? path.resolve(projectScan) : undefined, baseDir)\n }\n\n const host = process.env.HOST ?? '0.0.0.0'\n const port = Number(process.env.PORT ?? 8080)\n const otelPort = Number(process.env.OTEL_PORT ?? 4318)\n\n // ADR-073 §3 — refuse to bind a public address without a token.\n const auth = readAuthEnv()\n assertBindAuthority(host, auth.authToken)\n\n const app = await buildApi({\n projects: registry,\n authToken: auth.authToken,\n trustProxy: auth.trustProxy,\n publicRead: auth.publicRead,\n })\n await app.listen({ port, host })\n console.log(`neat-core listening on http://${host}:${port}`)\n console.log(` base dir: ${baseDir}`)\n console.log(` projects: ${registry.list().join(', ')}`)\n\n // OTel ingest stays single-project for now: spans always land in the\n // default project's graph + errors log. Multi-project routing for spans\n // is a future concern (would need a header / resource attr).\n const defaultCtx = registry.get(DEFAULT_PROJECT)\n if (defaultCtx) {\n const onSpan = makeSpanHandler({\n graph: defaultCtx.graph,\n errorsPath: defaultCtx.paths.errorsPath,\n })\n const otelApp = await buildOtelReceiver({\n onSpan,\n authToken: auth.otelToken,\n trustProxy: auth.trustProxy,\n })\n await otelApp.listen({ port: otelPort, host })\n console.log(`neat-core OTLP receiver on http://${host}:${otelPort}/v1/traces`)\n\n if (process.env.NEAT_OTLP_GRPC === 'true') {\n const grpcPort = Number(process.env.NEAT_OTLP_GRPC_PORT ?? 4317)\n const grpcReceiver = await startOtelGrpcReceiver({\n onSpan,\n host,\n port: grpcPort,\n authToken: auth.otelToken,\n trustProxy: auth.trustProxy,\n })\n console.log(`neat-core OTLP/gRPC receiver on ${grpcReceiver.address}`)\n }\n }\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neat.is/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "NEAT graph engine: tree-sitter extraction, OTel ingest, REST API",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"homepage": "https://neat.is",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@fastify/cors": "^10.0.0",
|
|
51
51
|
"@grpc/grpc-js": "^1.14.3",
|
|
52
52
|
"@grpc/proto-loader": "^0.8.0",
|
|
53
|
-
"@neat.is/types": "^0.
|
|
53
|
+
"@neat.is/types": "^0.4.2",
|
|
54
54
|
"chokidar": "^4.0.3",
|
|
55
55
|
"fastify": "^5.0.0",
|
|
56
56
|
"graphology": "^0.25.4",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts","../src/otel.ts"],"sourcesContent":["/**\n * Delegated auth at the daemon boundary (ADR-073 §3 + §4).\n *\n * NEAT does not issue, rotate, or distribute the token — that is the deploy\n * platform's job. This module provides the two surfaces the daemon needs:\n *\n * - `assertBindAuthority(host, token)` — fail-loud pre-bind check. When no\n * token is set, the daemon refuses to bind on any non-loopback address.\n * Loopback-only without a token stays unauthenticated (laptop dev path).\n *\n * - `mountBearerAuth(app, opts)` — Fastify `preHandler` that requires\n * `Authorization: Bearer <token>` on every request other than the\n * unauthenticated health/readiness probes. Constant-time comparison.\n *\n * The same shape covers both the REST host and the OTLP receivers; the OTLP\n * side passes a different token (`NEAT_OTEL_TOKEN ?? NEAT_AUTH_TOKEN`) so the\n * two surfaces rotate independently.\n */\n\nimport { timingSafeEqual } from 'node:crypto'\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\n\n// Hosts that count as loopback for the bind-authority gate. `0.0.0.0` is\n// explicitly not loopback — it binds on every interface, including any\n// public one the operator's box happens to have.\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n '127.0.0.1',\n 'localhost',\n '::1',\n '::ffff:127.0.0.1',\n])\n\nexport function isLoopbackHost(host: string | undefined | null): boolean {\n if (!host) return false\n return LOOPBACK_HOSTS.has(host)\n}\n\nexport class BindAuthorityError extends Error {\n constructor(host: string) {\n super(\n `NEAT refuses to bind on a public interface without \\`NEAT_AUTH_TOKEN\\` set (host=\"${host}\"). Set the token or bind to loopback only.`,\n )\n this.name = 'BindAuthorityError'\n }\n}\n\nexport function assertBindAuthority(host: string, token: string | undefined): void {\n if (token && token.length > 0) return\n if (isLoopbackHost(host)) return\n throw new BindAuthorityError(host)\n}\n\nexport interface AuthOptions {\n // Bearer token required on every protected route. Undefined / empty → the\n // middleware is not mounted and the route stays unauthenticated. The\n // loopback-only gate above is what keeps that case from being public.\n token?: string\n // When `true`, trust an upstream reverse proxy and skip the request-side\n // check. The fail-loud bind-authority gate still applies upstream. Wired\n // to `NEAT_AUTH_PROXY=true` in production.\n trustProxy?: boolean\n // Extra paths (or path suffixes) to leave unauthenticated. Used by tests\n // and by ad-hoc callers that mount their own probes.\n extraUnauthenticatedSuffixes?: ReadonlyArray<string>\n}\n\n// Probes that always stay open. Dual-mounted under `/projects/:project/` too,\n// so the check is a suffix match — `/projects/foo/health` is skipped along\n// with the top-level `/health`. ADR-073 §3 names `/healthz` and `/readyz`\n// explicitly; `/health` is the existing endpoint the web shell and CI smoke\n// already lean on, so it keeps the unauthenticated treatment.\nconst DEFAULT_UNAUTH_SUFFIXES: ReadonlyArray<string> = ['/health', '/healthz', '/readyz']\n\nexport function mountBearerAuth(app: FastifyInstance, opts: AuthOptions): void {\n if (!opts.token || opts.token.length === 0) return\n if (opts.trustProxy) return\n\n const expected = Buffer.from(opts.token, 'utf8')\n const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...(opts.extraUnauthenticatedSuffixes ?? [])]\n\n app.addHook('preHandler', (req: FastifyRequest, reply: FastifyReply, done: (err?: Error) => void) => {\n const path = (req.url.split('?')[0] ?? '').replace(/\\/+$/, '')\n for (const suffix of suffixes) {\n if (path === suffix || path.endsWith(suffix)) {\n done()\n return\n }\n }\n\n const header = req.headers.authorization\n if (typeof header !== 'string' || !header.startsWith('Bearer ')) {\n void reply.code(401).send({ error: 'unauthorized' })\n return\n }\n const provided = Buffer.from(header.slice('Bearer '.length).trim(), 'utf8')\n if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {\n void reply.code(401).send({ error: 'unauthorized' })\n return\n }\n done()\n })\n}\n\n// Read both tokens from the environment in one place so server.ts, daemon.ts,\n// and the OTel receivers all agree on precedence (ADR-073 §4).\nexport interface AuthEnv {\n authToken: string | undefined\n otelToken: string | undefined\n trustProxy: boolean\n}\n\nexport function readAuthEnv(env: NodeJS.ProcessEnv = process.env): AuthEnv {\n const t = env.NEAT_AUTH_TOKEN\n const ot = env.NEAT_OTEL_TOKEN\n return {\n authToken: t && t.length > 0 ? t : undefined,\n otelToken: ot && ot.length > 0 ? ot : t && t.length > 0 ? t : undefined,\n trustProxy: env.NEAT_AUTH_PROXY === 'true',\n }\n}\n","import path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport Fastify, { type FastifyInstance } from 'fastify'\nimport protobuf from 'protobufjs'\nimport { mountBearerAuth } from './auth.js'\n\n// OTLP/HTTP receiver. Listens on /v1/traces and decodes the JSON wire format\n// (collector's `otlphttp` exporter with `encoding: json`). Each span is\n// flattened into a ParsedSpan and handed to the configured handler. The\n// handler is the seam #8 wires its edge mapper into; #7 itself stays decoupled\n// from graph mutation.\n\nexport interface ParsedSpan {\n service: string\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n kind?: number\n startTimeUnixNano: string\n endTimeUnixNano: string\n // ISO8601 derived from startTimeUnixNano. Production paths (lastObserved on\n // OBSERVED edges) read this so the recorded time reflects when the span fired,\n // not when the receiver received it. Undefined only when startTimeUnixNano is\n // missing or unparseable — handler falls back to wall-clock in that case.\n // See docs/contracts/otel-ingest.md §lastObserved-from-span-time.\n startTimeIso?: string\n // bigint so the 9-digit-nanos arithmetic doesn't lose precision on long traces.\n durationNanos: bigint\n attributes: Record<string, AttributeValue>\n // Convenience accessors for the attributes #8 cares about.\n dbSystem?: string\n dbName?: string\n // 0 = UNSET, 1 = OK, 2 = ERROR per OTLP. We only care that 2 means error.\n statusCode?: number\n errorMessage?: string\n // Pre-extracted from a span event with name=\"exception\". OTLP SDKs record\n // exceptions this way (richer than status.message). handleSpan reads these\n // first, falling back to status.message and span.name. See\n // docs/contracts/otel-ingest.md §exception-data-from-span-events.\n exception?: {\n type?: string\n message?: string\n stacktrace?: string\n }\n}\n\nexport type AttributeValue =\n | string\n | number\n | boolean\n | bigint\n | string[]\n | number[]\n | boolean[]\n | null\n\nexport type SpanHandler = (span: ParsedSpan) => void | Promise<void>\n\nexport interface BuildOtelReceiverOptions {\n onSpan: SpanHandler\n // Synchronous handler for spans with statusCode === 2. The receiver awaits\n // it before replying, so a write failure can return 500 → OTel SDK retries.\n // Optional — wiring is expected to plumb appendErrorEvent here when error\n // durability matters; ad-hoc receivers leave it undefined.\n // See docs/contracts/otel-ingest.md §Error events.\n onErrorSpanSync?: (span: ParsedSpan) => Promise<void>\n // Fastify body limit. OTLP batches can be large; default is 16 MB.\n bodyLimit?: number\n // ADR-073 §4 — bearer required on `/v1/traces`. Defaults to `NEAT_AUTH_TOKEN`\n // when unset; `NEAT_OTEL_TOKEN` overrides at the call site so the REST and\n // OTLP surfaces rotate on independent schedules.\n authToken?: string\n // Same shape as the REST middleware: skip the request-side check when an\n // upstream reverse proxy already authenticated.\n trustProxy?: boolean\n}\n\ninterface OtlpKeyValue {\n key: string\n value?: OtlpAnyValue\n}\n\ninterface OtlpAnyValue {\n stringValue?: string\n intValue?: string | number\n doubleValue?: number\n boolValue?: boolean\n arrayValue?: { values?: OtlpAnyValue[] }\n // kvlistValue / bytesValue are skipped — neither is on the demo path.\n}\n\ninterface OtlpStatus {\n code?: number\n message?: string\n}\n\ninterface OtlpEvent {\n name?: string\n timeUnixNano?: string\n attributes?: OtlpKeyValue[]\n}\n\ninterface OtlpSpan {\n traceId?: string\n spanId?: string\n parentSpanId?: string\n name?: string\n kind?: number\n startTimeUnixNano?: string\n endTimeUnixNano?: string\n attributes?: OtlpKeyValue[]\n events?: OtlpEvent[]\n status?: OtlpStatus\n}\n\nfunction extractExceptionFromEvents(events: OtlpEvent[] | undefined): ParsedSpan['exception'] {\n if (!events) return undefined\n for (const ev of events) {\n if (ev.name !== 'exception') continue\n const attrs = attrsToRecord(ev.attributes)\n const out: ParsedSpan['exception'] = {}\n const t = attrs['exception.type']\n const m = attrs['exception.message']\n const s = attrs['exception.stacktrace']\n if (typeof t === 'string') out.type = t\n if (typeof m === 'string') out.message = m\n if (typeof s === 'string') out.stacktrace = s\n if (out.type || out.message || out.stacktrace) return out\n }\n return undefined\n}\n\ninterface OtlpScopeSpans {\n spans?: OtlpSpan[]\n}\n\ninterface OtlpResourceSpans {\n resource?: { attributes?: OtlpKeyValue[] }\n scopeSpans?: OtlpScopeSpans[]\n}\n\nexport interface OtlpTracesRequest {\n resourceSpans?: OtlpResourceSpans[]\n}\n\nfunction flattenAttribute(v: OtlpAnyValue | undefined): AttributeValue {\n if (!v) return null\n if (v.stringValue !== undefined) return v.stringValue\n if (v.boolValue !== undefined) return v.boolValue\n if (v.intValue !== undefined) {\n return typeof v.intValue === 'string' ? Number(v.intValue) : v.intValue\n }\n if (v.doubleValue !== undefined) return v.doubleValue\n if (v.arrayValue?.values) {\n return v.arrayValue.values.map((x) => flattenAttribute(x)) as AttributeValue\n }\n return null\n}\n\nfunction attrsToRecord(attrs: OtlpKeyValue[] | undefined): Record<string, AttributeValue> {\n const out: Record<string, AttributeValue> = {}\n if (!attrs) return out\n for (const kv of attrs) {\n if (kv.key) out[kv.key] = flattenAttribute(kv.value)\n }\n return out\n}\n\nfunction durationNanos(start?: string, end?: string): bigint {\n if (!start || !end) return 0n\n try {\n return BigInt(end) - BigInt(start)\n } catch {\n return 0n\n }\n}\n\n// Convert OTLP's startTimeUnixNano (a base-10 string of nanoseconds since the\n// Unix epoch) to ISO8601. Returns undefined when the input is missing, zero,\n// or unparseable, so the caller can fall back to wall-clock without surfacing\n// a fake timestamp on the edge.\nexport function isoFromUnixNano(nanos: string | undefined): string | undefined {\n if (!nanos || nanos === '0') return undefined\n try {\n const ms = Number(BigInt(nanos) / 1_000_000n)\n if (!Number.isFinite(ms)) return undefined\n return new Date(ms).toISOString()\n } catch {\n return undefined\n }\n}\n\nexport function parseOtlpRequest(body: OtlpTracesRequest): ParsedSpan[] {\n const out: ParsedSpan[] = []\n for (const rs of body.resourceSpans ?? []) {\n const resourceAttrs = attrsToRecord(rs.resource?.attributes)\n const service = typeof resourceAttrs['service.name'] === 'string'\n ? (resourceAttrs['service.name'] as string)\n : 'unknown'\n\n for (const ss of rs.scopeSpans ?? []) {\n for (const span of ss.spans ?? []) {\n const attrs = attrsToRecord(span.attributes)\n const parsed: ParsedSpan = {\n service,\n traceId: span.traceId ?? '',\n spanId: span.spanId ?? '',\n parentSpanId: span.parentSpanId || undefined,\n name: span.name ?? '',\n kind: span.kind,\n startTimeUnixNano: span.startTimeUnixNano ?? '0',\n endTimeUnixNano: span.endTimeUnixNano ?? '0',\n startTimeIso: isoFromUnixNano(span.startTimeUnixNano),\n durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),\n attributes: attrs,\n dbSystem: typeof attrs['db.system'] === 'string' ? (attrs['db.system'] as string) : undefined,\n dbName: typeof attrs['db.name'] === 'string' ? (attrs['db.name'] as string) : undefined,\n statusCode: span.status?.code,\n errorMessage: span.status?.message,\n exception: extractExceptionFromEvents(span.events),\n }\n out.push(parsed)\n }\n }\n }\n return out\n}\n\nexport interface OtelReceiver {\n app: FastifyInstance\n // Resolves once every span enqueued so far has been handed to opts.onSpan.\n // Test seam — production code never awaits this.\n flushPending: () => Promise<void>\n}\n\n// Lazy-loaded protobuf decoder for ExportTraceServiceRequest. The bundled\n// .proto tree at packages/core/proto/ is shared with the gRPC receiver\n// (ADR-020). Cached after first load so successive receiver builds reuse it.\nlet exportTraceServiceRequestType: protobuf.Type | null = null\nlet exportTraceServiceResponseType: protobuf.Type | null = null\n\nfunction loadProtoRoot(): protobuf.Root {\n const here = path.dirname(fileURLToPath(import.meta.url))\n const protoRoot = path.resolve(here, '..', 'proto')\n const root = new protobuf.Root()\n root.resolvePath = (_origin, target) => path.resolve(protoRoot, target)\n root.loadSync(\n 'opentelemetry/proto/collector/trace/v1/trace_service.proto',\n { keepCase: true },\n )\n return root\n}\n\nfunction loadProtobufDecoder(): protobuf.Type {\n if (exportTraceServiceRequestType) return exportTraceServiceRequestType\n const root = loadProtoRoot()\n exportTraceServiceRequestType = root.lookupType(\n 'opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest',\n )\n return exportTraceServiceRequestType\n}\n\nfunction loadProtobufResponseEncoder(): protobuf.Type {\n if (exportTraceServiceResponseType) return exportTraceServiceResponseType\n const root = loadProtoRoot()\n exportTraceServiceResponseType = root.lookupType(\n 'opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse',\n )\n return exportTraceServiceResponseType\n}\n\n// Empty-partial-success response, encoded once and cached. Per ADR-033 the\n// receiver always reports \"all accepted\" today — there's no per-span reject\n// path. Caching the bytes avoids re-running the protobuf encoder per request.\nlet cachedProtobufResponseBody: Buffer | null = null\n\nfunction encodeProtobufResponseBody(): Buffer {\n if (cachedProtobufResponseBody) return cachedProtobufResponseBody\n const Type = loadProtobufResponseEncoder()\n // `partial_success` left unset = empty submessage = \"everything accepted\".\n // verify() returns null on success; the empty payload is always valid.\n const msg = Type.create({})\n const encoded = Type.encode(msg).finish()\n cachedProtobufResponseBody = Buffer.from(encoded)\n return cachedProtobufResponseBody\n}\n\nasync function decodeProtobufBody(buf: Buffer): Promise<OtlpTracesRequest> {\n const Type = loadProtobufDecoder()\n // Decode keeps the proto field names verbatim (keepCase: true), matching the\n // GrpcExportRequest shape that reshapeGrpcRequest already understands.\n // Dynamic import sidesteps the circular module dep with otel-grpc.ts.\n const decoded = Type.decode(buf).toJSON() as Record<string, unknown>\n const { reshapeGrpcRequest } = await import('./otel-grpc.js')\n return reshapeGrpcRequest(decoded as never)\n}\n\nexport async function buildOtelReceiver(\n opts: BuildOtelReceiverOptions,\n): Promise<FastifyInstance & { flushPending: () => Promise<void> }> {\n const app = Fastify({\n logger: false,\n bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024,\n })\n\n // ADR-073 §4 — bearer on `/v1/traces`. `/health` stays unauthenticated via\n // the default suffix list (the CI smoke and supervisors lean on it for\n // liveness probes).\n mountBearerAuth(app, { token: opts.authToken, trustProxy: opts.trustProxy })\n\n // Non-blocking ingest (ADR-033). The receiver replies 200 OK as soon as the\n // body is parsed; mutation runs through this queue, drained on the next tick.\n // OTel SDK exporters retry on timeout, so blocking ingest produces observable\n // backpressure on the system being observed — ambient observation requires no\n // observable effect.\n const queue: ParsedSpan[] = []\n let draining = false\n let drainPromise: Promise<void> = Promise.resolve()\n\n const drain = async (): Promise<void> => {\n if (draining) return\n draining = true\n try {\n while (queue.length > 0) {\n const span = queue.shift()!\n try {\n await opts.onSpan(span)\n } catch (err) {\n console.warn(`[neat] otel handler error: ${(err as Error).message}`)\n }\n }\n } finally {\n draining = false\n }\n }\n\n const enqueue = (spans: ParsedSpan[]): void => {\n if (spans.length === 0) return\n for (const s of spans) queue.push(s)\n // Schedule on the next tick so the 200 response is on the wire before any\n // mutation runs. Each call gets its own promise so flushPending() can wait\n // on the latest drain cycle.\n drainPromise = drainPromise.then(() => drain())\n }\n\n // Buffer application/x-protobuf bodies as raw bytes; the route handler\n // decodes them via the bundled .proto tree (ADR-020).\n app.addContentTypeParser(\n 'application/x-protobuf',\n { parseAs: 'buffer', bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024 },\n (_req, body, done) => {\n done(null, body)\n },\n )\n\n app.get('/health', async () => ({ ok: true }))\n\n app.post('/v1/traces', async (req, reply) => {\n // Content-Type dispatch (ADR-033). Both JSON and protobuf decode to the\n // same OtlpTracesRequest shape, then feed parseOtlpRequest unchanged.\n // The response encoding mirrors the request encoding per the OTLP spec —\n // a JSON exporter receives JSON, a protobuf exporter receives protobuf.\n // Mismatched encodings cause client SDKs to log decode errors every batch.\n const ct = (req.headers['content-type'] ?? '').toString().split(';')[0]!.trim().toLowerCase()\n let body: OtlpTracesRequest\n let responseFlavor: 'json' | 'protobuf'\n if (ct === 'application/x-protobuf') {\n responseFlavor = 'protobuf'\n try {\n body = await decodeProtobufBody(req.body as Buffer)\n } catch (err) {\n return reply.code(400).send({\n error: `protobuf decode failed: ${(err as Error).message}`,\n })\n }\n } else if (!ct || ct === 'application/json') {\n responseFlavor = 'json'\n body = (req.body ?? {}) as OtlpTracesRequest\n } else {\n return reply.code(415).send({ error: `unsupported content-type: ${ct}` })\n }\n const spans = parseOtlpRequest(body)\n // Synchronous error-event write before reply (ADR-033 §Error events).\n // Graph mutation stays on the async queue, but the receiver awaits the\n // file write so a write failure surfaces as 500 → OTel SDK retries.\n if (opts.onErrorSpanSync) {\n try {\n for (const span of spans) {\n if (span.statusCode === 2) await opts.onErrorSpanSync(span)\n }\n } catch (err) {\n return reply.code(500).send({\n error: `error-event write failed: ${(err as Error).message}`,\n })\n }\n }\n enqueue(spans)\n // OTLP success response is `{ partialSuccess: {} }` for \"all accepted\".\n // Match the response Content-Type to the request: protobuf in → protobuf\n // out, JSON in → JSON out. Default Fastify JSON reply path stays as the\n // historical shape for JSON callers.\n if (responseFlavor === 'protobuf') {\n const buf = encodeProtobufResponseBody()\n return reply\n .code(200)\n .header('content-type', 'application/x-protobuf')\n .send(buf)\n }\n return reply\n .code(200)\n .header('content-type', 'application/json')\n .send({ partialSuccess: {} })\n })\n\n // Attach flushPending so tests can wait for the queue without exporting a\n // separate handle. The cast goes through `unknown` because Fastify's typing\n // is parameterised over the raw server type and the simple intersection\n // confuses TS's structural narrowing.\n const decorated = app as unknown as FastifyInstance & { flushPending: () => Promise<void> }\n decorated.flushPending = async () => {\n // Settle the current drain chain, then loop until the queue is fully empty\n // (a span enqueued mid-flush would otherwise be missed).\n while (queue.length > 0 || draining) {\n await drainPromise\n }\n }\n return decorated\n}\n\nexport function logSpanHandler(span: ParsedSpan): void {\n const parent = span.parentSpanId ? span.parentSpanId.slice(0, 8) : '<root>'\n const status = span.statusCode === 2 ? 'ERROR' : 'OK'\n const db = span.dbSystem ? ` db=${span.dbSystem}/${span.dbName ?? '?'}` : ''\n console.log(\n `otel: ${span.service} ${span.name} parent=${parent} status=${status}${db}`,\n )\n}\n"],"mappings":"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","names":["path"]}
|