@odla-ai/kg 0.2.4 → 0.3.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/temporal/prng.ts","../../src/temporal/adjacency.ts","../../src/temporal/events.ts","../../src/temporal/series.ts","../../src/temporal/hotspots.ts","../../src/temporal/snapshot.ts","../../src/temporal/communities.ts","../../src/temporal/clusters.ts","../../src/temporal/layout.ts","../../src/temporal/report.ts","../../src/temporal/narrate.ts","../../src/temporal/steer.ts","../../src/temporal/flows.ts","../../src/temporal/spatial.ts","../../src/temporal/lenses.ts","../../src/temporal/bridges.ts"],"sourcesContent":["// Deterministic randomness for temporal analytics. Communities and layout\n// derive ALL randomness from node identity + an explicit seed, so results are\n// pure functions of their inputs — never of Map iteration or insertion order.\n\n/**\n * FNV-1a 32-bit hash of a string. Used to derive a per-identity PRNG seed\n * (`mulberry32(hash32(id) ^ seed)`), which makes node placement independent\n * of the order nodes arrive in.\n */\nexport function hash32(s: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n\n/**\n * mulberry32: a tiny, fast, seedable PRNG returning floats in [0, 1).\n * The same seed always yields the same sequence — replayable layouts and\n * community assignments depend on this.\n */\nexport function mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Fisher–Yates shuffle into a NEW array, driven by the supplied random source\n * (pass a seeded `mulberry32` for a deterministic permutation).\n */\nexport function shuffle<T>(items: readonly T[], rand: () => number): T[] {\n const out = items.slice();\n for (let i = out.length - 1; i > 0; i--) {\n const j = Math.floor(rand() * (i + 1));\n const tmp = out[i] as T; // i, j always in bounds\n out[i] = out[j] as T;\n out[j] = tmp;\n }\n return out;\n}\n","// Indexed, read-only view of a GraphFragment for path walking. Everything the\n// temporal toolkit does starts from this — it never touches odla-db.\nimport type { GraphFragment, GraphNode } from \"../types\";\n\n/** A node reference: the `{type, key}` pair used by fragment edges. */\nexport interface NodeRef {\n type: string;\n key: string;\n}\n\n/** Canonical string id for a node reference: `\"type:key\"`. */\nexport type NodeId = string;\n\n/** Make the canonical `\"type:key\"` id for a node reference. */\nexport function nodeId(ref: NodeRef): NodeId {\n return `${ref.type}:${ref.key}`;\n}\n\n/** Compare two refs by canonical id (stable orderings everywhere). */\nexport function byNodeId(a: NodeRef, b: NodeRef): number {\n const ia = nodeId(a);\n const ib = nodeId(b);\n return ia < ib ? -1 : ia > ib ? 1 : 0;\n}\n\n/** Indexed view of one GraphFragment. */\nexport interface GraphIndex {\n /** The node for an id, if present in the fragment. */\n node(id: NodeId): GraphNode | undefined;\n /** Forward-label adjacency: targets of `label` edges out of `id`. */\n out(id: NodeId, label: string): NodeRef[];\n /** Every node of one entity type. */\n nodesOf(type: string): GraphNode[];\n}\n\n/** Build the index (one pass over nodes + edges). */\nexport function indexGraph(fragment: GraphFragment): GraphIndex {\n const nodes = new Map<NodeId, GraphNode>();\n const byType = new Map<string, GraphNode[]>();\n for (const n of fragment.nodes) {\n nodes.set(nodeId(n), n);\n const list = byType.get(n.type);\n list ? list.push(n) : byType.set(n.type, [n]);\n }\n const adj = new Map<string, NodeRef[]>();\n for (const e of fragment.edges) {\n const k = `${nodeId(e.from)}|${e.label}`;\n const list = adj.get(k);\n list ? list.push(e.to) : adj.set(k, [e.to]);\n }\n return {\n node: (id) => nodes.get(id),\n out: (id, label) => adj.get(`${id}|${label}`) ?? [],\n nodesOf: (type) => byType.get(type) ?? [],\n };\n}\n\n/**\n * Walk a sequence of forward edge labels from `start`, breadth-first, and\n * return the deduplicated frontier after the last hop, sorted by id.\n * An empty label list returns just `start`.\n */\nexport function resolvePath(g: GraphIndex, start: NodeRef, labels: readonly string[]): NodeRef[] {\n let frontier: NodeRef[] = [start];\n for (const label of labels) {\n const seen = new Set<NodeId>();\n const next: NodeRef[] = [];\n for (const ref of frontier) {\n for (const target of g.out(nodeId(ref), label)) {\n const id = nodeId(target);\n if (!seen.has(id)) {\n seen.add(id);\n next.push(target);\n }\n }\n }\n frontier = next;\n }\n return frontier.slice().sort(byNodeId);\n}\n","// Ontology-driven extraction of a timestamped event stream from a graph.\n// kg edges carry no properties — property-bearing relations are reified nodes\n// (funding_round, signal, …) with date attrs. An EventSpec names those event\n// entity types and how to reach their participants and geography.\nimport type { Value } from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport { indexGraph, nodeId, byNodeId, resolvePath } from \"./adjacency\";\nimport type { GraphIndex, NodeRef } from \"./adjacency\";\nimport { parseDateMs } from \"./time\";\n\n/**\n * Where an event's geography lives: walk `path` (forward edge labels) from the\n * event node, then read `attr` off the landing node — or, with no `attr`, use\n * the landing node's key (e.g. a `region` entity slug). With no `path`, the\n * event node itself is the landing node.\n */\nexport interface GeoSpec {\n path?: readonly string[];\n attr?: string;\n}\n\n/** How to turn one entity type into temporal events. */\nexport interface EventSpec {\n /** Event entity type, e.g. `\"funding_round\"` or `\"signal\"`. */\n type: string;\n /** Attr holding the event date (parsed with `parseDateMs`; undated nodes are skipped). */\n dateAttr: string;\n /** Attr holding a magnitude, parsed with `parseAmount`; unparseable → weight 1. */\n weightAttr?: string;\n /** Override hook for weights the attr grammar can't express. */\n weightOf?: (attrs: Record<string, Value>) => number | undefined;\n /** Edge-label paths from the event node to its participant entities. */\n attach?: readonly (readonly string[])[];\n /** Geography of the event (choropleth/hotspot keying). */\n geo?: GeoSpec;\n /** Destination geography — only for flow-shaped events (money moving A→B). */\n geoTo?: GeoSpec;\n}\n\n/** The extraction contract: one spec per event entity type. */\nexport interface TemporalConfig {\n events: EventSpec[];\n}\n\n/** One timestamped event extracted from the graph. */\nexport interface TemporalEvent {\n /** Epoch ms (UTC). */\n t: number;\n /** The spec's entity type. */\n kind: string;\n /** The event node itself. */\n node: NodeRef;\n /** Attached participant entities, deduped, sorted by id. */\n refs: NodeRef[];\n /** Magnitude (defaults to 1). */\n weight: number;\n /** Source geography key, when resolvable. */\n geo?: string;\n /** Destination geography key, when resolvable (flow events). */\n geoTo?: string;\n}\n\n/**\n * Parse a human money/quantity string to a number: `\"$12.5M\"` → 12500000,\n * `\"12,500,000\"` → 12500000, `\"500k\"` → 500000, numbers pass through.\n * Returns undefined for anything else (never NaN).\n */\nexport function parseAmount(v: unknown): number | undefined {\n if (typeof v === \"number\") return Number.isFinite(v) ? v : undefined;\n if (typeof v !== \"string\") return undefined;\n const m = /^\\$?\\s*([\\d,]+(?:\\.\\d+)?)\\s*([kKmMbB])?$/.exec(v.trim());\n if (!m) return undefined;\n const n = Number((m[1] as string).replace(/,/g, \"\")); // group 1 is non-optional\n if (!Number.isFinite(n)) return undefined;\n const suffix = (m[2] ?? \"\").toLowerCase();\n const mult = suffix === \"k\" ? 1e3 : suffix === \"m\" ? 1e6 : suffix === \"b\" ? 1e9 : 1;\n return n * mult;\n}\n\nfunction resolveGeo(g: GraphIndex, start: NodeRef, spec: GeoSpec): string | undefined {\n const landing = spec.path?.length ? resolvePath(g, start, spec.path)[0] : start;\n if (!landing) return undefined;\n if (spec.attr) {\n const v = g.node(nodeId(landing))?.attrs[spec.attr];\n return typeof v === \"string\" && v !== \"\" ? v : undefined;\n }\n return landing.key;\n}\n\n/**\n * Extract the event stream: for every spec, every node of that type with a\n * parseable date becomes one event. Sorted by time, tie-broken by node id —\n * the ordering is total and reproducible.\n */\nexport function extractEvents(fragment: GraphFragment, config: TemporalConfig): TemporalEvent[] {\n const g = indexGraph(fragment);\n const out: TemporalEvent[] = [];\n for (const spec of config.events) {\n for (const n of g.nodesOf(spec.type)) {\n const t = parseDateMs(n.attrs[spec.dateAttr]);\n if (t === null) continue;\n const self: NodeRef = { type: n.type, key: n.key };\n const seen = new Set<string>();\n const refs: NodeRef[] = [];\n for (const path of spec.attach ?? []) {\n for (const ref of resolvePath(g, self, path)) {\n const id = nodeId(ref);\n if (!seen.has(id)) {\n seen.add(id);\n refs.push(ref);\n }\n }\n }\n refs.sort(byNodeId);\n const weight =\n spec.weightOf?.(n.attrs) ??\n (spec.weightAttr ? parseAmount(n.attrs[spec.weightAttr]) : undefined) ??\n 1;\n out.push({\n t,\n kind: spec.type,\n node: self,\n refs,\n weight,\n geo: spec.geo ? resolveGeo(g, self, spec.geo) : undefined,\n geoTo: spec.geoTo ? resolveGeo(g, self, spec.geoTo) : undefined,\n });\n }\n }\n return out.sort((a, b) => a.t - b.t || byNodeId(a.node, b.node));\n}\n","// Time-bucketed rollups of an event stream — the input shape for burst\n// detection and for any chart with a time axis.\nimport type { TemporalEvent } from \"./events\";\nimport { bucketKey, bucketRange } from \"./time\";\nimport type { TimeBucket, TimeGrain } from \"./time\";\nimport { nodeId } from \"./adjacency\";\n\n/** One named series, index-aligned with its SeriesSet's buckets. */\nexport interface Series {\n key: string;\n values: number[];\n}\n\n/** A grain, its buckets, and every series over them (sorted by key). */\nexport interface SeriesSet {\n grain: TimeGrain;\n buckets: TimeBucket[];\n series: Series[];\n}\n\n/**\n * How to key events into series: `\"kind\"` (event type), `\"geo\"` (source\n * geography; geo-less events are dropped), `\"ref\"` (fan one event into one\n * increment per attached participant, keyed by node id), or a custom function\n * returning zero or more keys per event.\n */\nexport type SeriesKeyer =\n | \"kind\"\n | \"geo\"\n | \"ref\"\n | ((e: TemporalEvent) => string | readonly string[] | undefined);\n\n/** The series keys one event contributes to under a keyer (used by rollup and by hotspot contributor attachment). */\nexport function seriesKeys(e: TemporalEvent, by: SeriesKeyer): readonly string[] {\n if (by === \"kind\") return [e.kind];\n if (by === \"geo\") return e.geo === undefined ? [] : [e.geo];\n if (by === \"ref\") return e.refs.map(nodeId);\n const k = by(e);\n return k === undefined ? [] : typeof k === \"string\" ? [k] : k;\n}\n\n/**\n * Roll events up into bucketed series. The bucket range spans `from`..`to`\n * when given (zero-padded even where nothing happened — burst baselines need\n * the quiet buckets), else the events' own time span. Events outside the\n * range are ignored.\n */\nexport function rollup(\n events: readonly TemporalEvent[],\n opts: { grain: TimeGrain; by?: SeriesKeyer; measure?: \"count\" | \"weight\"; from?: number; to?: number },\n): SeriesSet {\n const { grain, by = \"kind\", measure = \"count\" } = opts;\n const from = opts.from ?? events[0]?.t;\n const to = opts.to ?? events[events.length - 1]?.t;\n if (from === undefined || to === undefined) return { grain, buckets: [], series: [] };\n\n const buckets = bucketRange(from, to, grain);\n const index = new Map(buckets.map((b, i) => [b.key, i]));\n const rows = new Map<string, number[]>();\n for (const e of events) {\n const i = index.get(bucketKey(e.t, grain));\n if (i === undefined) continue;\n for (const key of seriesKeys(e, by)) {\n let values = rows.get(key);\n if (!values) {\n values = new Array<number>(buckets.length).fill(0);\n rows.set(key, values);\n }\n values[i] = (values[i] as number) + (measure === \"count\" ? 1 : e.weight); // i from the bucket index map\n }\n }\n const series = [...rows.entries()]\n .map(([key, values]) => ({ key, values }))\n .sort((a, b) => (a.key < b.key ? -1 : 1));\n return { grain, buckets, series };\n}\n\n/** Per-bucket sum across every series in the set (the \"all\" line). */\nexport function totals(set: SeriesSet): Series {\n const values = new Array<number>(set.buckets.length).fill(0);\n for (const s of set.series) {\n s.values.forEach((v, i) => {\n values[i] = (values[i] as number) + v; // series are index-aligned with buckets\n });\n }\n return { key: \"total\", values };\n}\n","// Temporal burst detection: find time-sequence hotspots in bucketed series —\n// \"this county/entity/corridor suddenly deviates from its own baseline\" — and\n// surface them as ranked, explorable leads with contributing events attached.\n//\n// Method: rolling-baseline z-scores with a Poisson-consistent floor and\n// hysteresis runs. Chosen over Kleinberg's burst automaton deliberately: with\n// short series (18 monthly buckets) the automaton's cost parameters can't be\n// meaningfully estimated, while a z-run detector produces the same ranked-\n// window output shape and every score is hand-checkable. Revisit only if\n// bucket counts grow ~10×.\nimport type { TemporalEvent } from \"./events\";\nimport { seriesKeys } from \"./series\";\nimport type { SeriesSet, SeriesKeyer } from \"./series\";\nimport { bucketKey } from \"./time\";\n\n/** Tuning for {@link burstScores}/{@link detectBursts}. */\nexport interface BurstOptions {\n /** Trailing buckets forming the baseline (excluding the scored bucket). Default 6. */\n window?: number;\n /** z needed to OPEN a burst run. Default 2. */\n zEnter?: number;\n /** z needed to EXTEND an open run (hysteresis). Default 1. */\n zExit?: number;\n /** Minimum trailing buckets before scoring (cold start → z 0). Default 3. */\n minBaseline?: number;\n /**\n * Floor on the baseline sigma. sqrt(mean) is always applied (Poisson-\n * consistent for counts); this guards the all-zero baseline. Default 1.\n */\n minSigma?: number;\n}\n\n/**\n * Per-bucket burst z-scores for one series: (value − trailing mean) /\n * max(trailing stdev, sqrt(mean), minSigma). Cold-start buckets score 0.\n */\nexport function burstScores(values: readonly number[], opts: BurstOptions = {}): number[] {\n const { window = 6, minBaseline = 3, minSigma = 1 } = opts;\n return values.map((x, i) => {\n const from = Math.max(0, i - window);\n const trailing = values.slice(from, i);\n if (trailing.length < minBaseline) return 0;\n const mean = trailing.reduce((a, b) => a + b, 0) / trailing.length;\n const variance = trailing.reduce((a, b) => a + (b - mean) * (b - mean), 0) / trailing.length;\n const sigma = Math.max(Math.sqrt(variance), Math.sqrt(mean), minSigma);\n return (x - mean) / sigma;\n });\n}\n\n/** One detected burst: a maximal hysteresis run within one series. */\nexport interface Burst {\n seriesKey: string;\n /** Bucket indexes, inclusive. */\n start: number;\n end: number;\n /** Highest z in the run. */\n peak: number;\n /** Σ max(0, z) over the run — rewards both height and duration. */\n score: number;\n}\n\n/**\n * All bursts across a SeriesSet, ranked score desc (ties: earlier start,\n * then series key). A run opens at z ≥ zEnter and extends while z ≥ zExit.\n */\nexport function detectBursts(set: SeriesSet, opts: BurstOptions = {}): Burst[] {\n const { zEnter = 2, zExit = 1 } = opts;\n const out: Burst[] = [];\n for (const series of set.series) {\n const zs = burstScores(series.values, opts);\n let run: Burst | null = null;\n zs.forEach((z, i) => {\n if (run) {\n if (z >= zExit) {\n run.end = i;\n run.peak = Math.max(run.peak, z);\n run.score += Math.max(0, z);\n return;\n }\n out.push(run);\n run = null;\n }\n if (z >= zEnter) {\n run = { seriesKey: series.key, start: i, end: i, peak: z, score: z };\n }\n });\n if (run) out.push(run);\n }\n return out.sort(\n (a, b) => b.score - a.score || a.start - b.start || (a.seriesKey < b.seriesKey ? -1 : 1),\n );\n}\n\n/** A burst plus its bucket keys and top contributing events — a ranked lead. */\nexport interface Hotspot extends Burst {\n bucketKeys: string[];\n contributors: TemporalEvent[];\n}\n\n/**\n * Detect, rank, and enrich bursts into explorable leads. `by` must be the\n * same keyer the SeriesSet was rolled up with — it re-derives each event's\n * series keys to attach contributors (top per-lead by weight, then time).\n */\nexport function rankHotspots(\n set: SeriesSet,\n events: readonly TemporalEvent[],\n opts: BurstOptions & { by?: SeriesKeyer; top?: number; contributorsPer?: number } = {},\n): Hotspot[] {\n const { by = \"kind\", top = 20, contributorsPer = 5 } = opts;\n const index = new Map(set.buckets.map((b, i) => [b.key, i]));\n return detectBursts(set, opts)\n .slice(0, top)\n .map((burst) => {\n const inRun = events.filter((e) => {\n const i = index.get(bucketKey(e.t, set.grain));\n return i !== undefined && i >= burst.start && i <= burst.end && seriesKeys(e, by).includes(burst.seriesKey);\n });\n inRun.sort((a, b) => b.weight - a.weight || a.t - b.t);\n return {\n ...burst,\n bucketKeys: set.buckets.slice(burst.start, burst.end + 1).map((b) => b.key),\n contributors: inRun.slice(0, contributorsPer),\n };\n });\n}\n","// Replay structure: turn the event stream into a timeline of dated edges,\n// then materialize the graph as it stood at any instant — the substrate for\n// cluster evolution (communities + layout over snapshotSeries).\nimport { nodeId } from \"./adjacency\";\nimport type { NodeId } from \"./adjacency\";\nimport type { TemporalEvent } from \"./events\";\nimport type { TimeBucket } from \"./time\";\n\n// Events with more participants than this contribute no pairwise edges in\n// \"entities\" mode (a 50-ref digest would explode into 1225 edges of noise).\nconst PAIR_CAP = 12;\n\n/** One dated undirected edge (canonical order: a < b). */\nexport interface TimelineEdge {\n a: NodeId;\n b: NodeId;\n t: number;\n kind: string;\n weight: number;\n}\n\n/** The replayable structure built once from the event stream. */\nexport interface Timeline {\n events: TemporalEvent[];\n /** Every node ever seen, sorted by id. */\n nodes: NodeId[];\n /** Dated edges, time-ordered. */\n edges: TimelineEdge[];\n /** Node id → first time it appeared. */\n firstSeen: Map<NodeId, number>;\n}\n\n/**\n * Build the timeline. `\"entities\"` (default) projects each event onto its\n * participants — every ref pair co-occurring in one event becomes an edge\n * (companies↔investors↔people), and event nodes vanish. `\"full\"` keeps the\n * event node and links it to each ref (bipartite).\n */\nexport function buildTimeline(\n events: readonly TemporalEvent[],\n opts: { mode?: \"entities\" | \"full\" } = {},\n): Timeline {\n const mode = opts.mode ?? \"entities\";\n const firstSeen = new Map<NodeId, number>();\n const edges: TimelineEdge[] = [];\n const see = (id: NodeId, t: number) => {\n if (!firstSeen.has(id)) firstSeen.set(id, t);\n };\n\n for (const e of events) {\n const refIds = e.refs.map(nodeId);\n if (mode === \"full\") {\n const self = nodeId(e.node);\n see(self, e.t);\n for (const ref of refIds) {\n see(ref, e.t);\n const [a, b] = self < ref ? [self, ref] : [ref, self];\n edges.push({ a, b, t: e.t, kind: e.kind, weight: e.weight });\n }\n continue;\n }\n for (const ref of refIds) see(ref, e.t);\n if (refIds.length > PAIR_CAP) continue;\n for (let i = 0; i < refIds.length; i++) {\n for (let j = i + 1; j < refIds.length; j++) {\n const [a, b] = refIds[i]! < refIds[j]! ? [refIds[i]!, refIds[j]!] : [refIds[j]!, refIds[i]!];\n edges.push({ a, b, t: e.t, kind: e.kind, weight: e.weight });\n }\n }\n }\n edges.sort((x, y) => x.t - y.t || (x.a < y.a ? -1 : x.a > y.a ? 1 : x.b < y.b ? -1 : 1));\n return {\n events: [...events],\n nodes: [...firstSeen.keys()].sort(),\n edges,\n firstSeen,\n };\n}\n\n/** The graph as it stood at time `t`. */\nexport interface Snapshot {\n t: number;\n /** Nodes first seen at or before t, sorted. */\n nodes: NodeId[];\n /** Aggregated undirected edges (weight = co-occurrence count). */\n edges: Array<{ a: NodeId; b: NodeId; weight: number }>;\n /** Node id → its latest event time ≤ t (drives recency glow). */\n activity: Map<NodeId, number>;\n}\n\n/**\n * Materialize the graph at `t`: cumulative by default, or only edges within\n * the trailing `window` ms when given (rolling co-activity).\n */\nexport function snapshotAt(timeline: Timeline, t: number, opts: { window?: number } = {}): Snapshot {\n const since = opts.window === undefined ? -Infinity : t - opts.window;\n const agg = new Map<string, { a: NodeId; b: NodeId; weight: number }>();\n const activity = new Map<NodeId, number>();\n for (const e of timeline.edges) {\n if (e.t > t) break; // edges are time-ordered\n activity.set(e.a, e.t);\n activity.set(e.b, e.t);\n if (e.t <= since) continue;\n const key = `${e.a}|${e.b}`;\n const cell = agg.get(key);\n if (cell) cell.weight += 1;\n else agg.set(key, { a: e.a, b: e.b, weight: 1 });\n }\n const nodes = timeline.nodes.filter((id) => timeline.firstSeen.get(id)! <= t); // nodes ARE firstSeen's keys\n return { t, nodes, edges: [...agg.values()], activity };\n}\n\n/** One snapshot per bucket, each taken at the bucket's inclusive end. */\nexport function snapshotSeries(\n timeline: Timeline,\n buckets: readonly TimeBucket[],\n opts: { window?: number } = {},\n): Snapshot[] {\n return buckets.map((b) => snapshotAt(timeline, b.end - 1, opts));\n}\n","// Deterministic label propagation. Classic LPA needs random visit order to\n// break symmetry; here that randomness comes from one seeded shuffle stream,\n// so the same (nodes, edges, seed) always yields the same communities.\nimport { mulberry32, shuffle } from \"./prng\";\nimport type { NodeId } from \"./adjacency\";\n\n/**\n * Asynchronous label propagation with deterministic (seeded) tie-breaking:\n * - labels start as each node's own id;\n * - each round visits nodes in a seeded-shuffled order (one PRNG stream\n * reused across rounds) and adopts the label with the greatest total\n * incident edge weight; if the node's CURRENT label ties for the max it is\n * kept, otherwise a tied max is picked via the same seeded stream (a\n * lexicographic tie-break would systematically bleed communities toward\n * alphabetically small ids — verified on the two-triangles fixture);\n * - stops on a round with no change, or after `maxRounds`;\n * - finally each community's label is canonicalized to its smallest member\n * id, so the output is independent of propagation history.\n * Isolated nodes keep their own label (singleton communities).\n */\nexport function labelPropagation(\n nodes: readonly NodeId[],\n edges: ReadonlyArray<{ a: NodeId; b: NodeId; weight?: number }>,\n opts: { seed?: number; maxRounds?: number } = {},\n): Map<NodeId, string> {\n const { seed = 1, maxRounds = 20 } = opts;\n const sorted = [...nodes].sort();\n const labels = new Map<NodeId, string>(sorted.map((id) => [id, id]));\n\n const neighbors = new Map<NodeId, Array<{ id: NodeId; weight: number }>>();\n for (const e of edges) {\n if (!labels.has(e.a) || !labels.has(e.b) || e.a === e.b) continue;\n const w = e.weight ?? 1;\n let listA = neighbors.get(e.a);\n if (!listA) neighbors.set(e.a, (listA = []));\n listA.push({ id: e.b, weight: w });\n let listB = neighbors.get(e.b);\n if (!listB) neighbors.set(e.b, (listB = []));\n listB.push({ id: e.a, weight: w });\n }\n\n const rand = mulberry32(seed);\n for (let round = 0; round < maxRounds; round++) {\n const order = shuffle(sorted, rand);\n let changed = false;\n for (const id of order) {\n const incident = neighbors.get(id);\n if (!incident || incident.length === 0) continue;\n const votes = new Map<string, number>();\n for (const n of incident) {\n const label = labels.get(n.id)!;\n votes.set(label, (votes.get(label) ?? 0) + n.weight);\n }\n let best: string[] = [];\n let bestWeight = -Infinity;\n for (const [label, weight] of votes) {\n if (weight > bestWeight) {\n bestWeight = weight;\n best = [label];\n } else if (weight === bestWeight) {\n best.push(label);\n }\n }\n const current = labels.get(id)!;\n if (best.includes(current)) continue; // keep on tie — stability first\n best.sort();\n const pick = best.length === 1 ? best[0]! : best[Math.floor(rand() * best.length)]!;\n labels.set(id, pick);\n changed = true;\n }\n if (!changed) break;\n }\n\n // Canonicalize: community label = smallest member id.\n const members = new Map<string, NodeId[]>();\n for (const [id, label] of labels) {\n const list = members.get(label);\n list ? list.push(id) : members.set(label, [id]);\n }\n const canonical = new Map<string, string>();\n // Members collect in `sorted` iteration order, so ids[0] is the minimum.\n for (const [label, ids] of members) canonical.set(label, ids[0]!);\n return new Map(sorted.map((id) => [id, canonical.get(labels.get(id)!)!]));\n}\n","// Cluster continuity across time: match each frame's communities to the\n// previous frame's by Jaccard overlap so ids stay stable while clusters\n// drift — and record births, deaths, merges, and splits.\nimport type { NodeId } from \"./adjacency\";\n\n/** One cluster in one frame (members sorted). */\nexport interface Cluster {\n id: string;\n members: NodeId[];\n}\n\n/** All clusters at one instant, id-sorted. */\nexport interface ClusterFrame {\n t: number;\n clusters: Cluster[];\n}\n\n/** A structural change between consecutive frames. */\nexport type ClusterChange =\n | { kind: \"birth\"; t: number; id: string }\n | { kind: \"death\"; t: number; id: string }\n | { kind: \"merge\"; t: number; into: string; from: string[] }\n | { kind: \"split\"; t: number; from: string; into: string[] };\n\nconst jaccard = (a: Set<NodeId>, b: readonly NodeId[]) => {\n let inter = 0;\n for (const id of b) if (a.has(id)) inter++;\n return inter / (a.size + b.length - inter);\n};\n\n/**\n * Thread stable cluster ids through per-frame label maps. A current cluster\n * inherits the id of its best-Jaccard predecessor at ≥ minJaccard (ties:\n * more shared members, then smaller id); when several current clusters claim\n * one predecessor the best keeps the id and the rest record a split; a\n * current cluster absorbing ≥2 predecessors records a merge. Unmatched\n * current clusters are births (id = \"c:\" + smallest member); vanished ids\n * are deaths.\n */\nexport function trackClusters(\n frames: ReadonlyArray<{ t: number; labels: Map<NodeId, string> }>,\n opts: { minJaccard?: number } = {},\n): { frames: ClusterFrame[]; changes: ClusterChange[] } {\n const minJaccard = opts.minJaccard ?? 0.3;\n const outFrames: ClusterFrame[] = [];\n const changes: ClusterChange[] = [];\n let prev: Cluster[] = [];\n\n for (const frame of frames) {\n const byLabel = new Map<string, NodeId[]>();\n for (const [id, label] of frame.labels) {\n const list = byLabel.get(label);\n list ? list.push(id) : byLabel.set(label, [id]);\n }\n const current = [...byLabel.values()].map((members) => members.sort());\n current.sort((a, b) => (a[0]! < b[0]! ? -1 : 1));\n\n // Score every (current, prev) pair sharing members.\n const prevSets = prev.map((c) => ({ c, set: new Set(c.members) }));\n const matches: Array<{ ci: number; pi: number; j: number; shared: number }> = [];\n current.forEach((members, ci) => {\n prevSets.forEach(({ set }, pi) => {\n const j = jaccard(set, members);\n if (j >= minJaccard) {\n let shared = 0;\n for (const id of members) if (set.has(id)) shared++;\n matches.push({ ci, pi, j, shared });\n }\n });\n });\n matches.sort((a, b) => b.j - a.j || b.shared - a.shared || a.ci - b.ci);\n\n const idOf = new Map<number, string>(); // current index → stable id\n const claimedPrev = new Map<number, number>(); // prev index → winning current\n const matchedPrevBy = new Map<number, number[]>(); // current → prev indexes matched\n for (const m of matches) {\n const list = matchedPrevBy.get(m.ci);\n list ? list.push(m.pi) : matchedPrevBy.set(m.ci, [m.pi]);\n if (!idOf.has(m.ci) && !claimedPrev.has(m.pi)) {\n idOf.set(m.ci, prev[m.pi]!.id);\n claimedPrev.set(m.pi, m.ci);\n }\n }\n\n // New ids must not collide with inherited ids OR any id from the\n // previous frame (a birth must not reuse a just-died cluster's name).\n const usedIds = new Set([...idOf.values(), ...prev.map((c) => c.id)]);\n const clusters: Cluster[] = current.map((members, ci) => {\n let id = idOf.get(ci);\n if (!id) {\n id = `c:${members[0]}`;\n while (usedIds.has(id)) id += \"'\";\n usedIds.add(id);\n // The first frame is the baseline — only later appearances are births.\n if (outFrames.length > 0) changes.push({ kind: \"birth\", t: frame.t, id });\n }\n return { id, members };\n });\n\n // Splits: a prev cluster matched by several current ones.\n prevSets.forEach(({ c }, pi) => {\n const winners = matches.filter((m) => m.pi === pi).map((m) => m.ci);\n if (winners.length >= 2) {\n changes.push({ kind: \"split\", t: frame.t, from: c.id, into: winners.map((ci) => clusters[ci]!.id) });\n }\n });\n // Merges: a current cluster matching several prev ones.\n for (const [ci, pis] of matchedPrevBy) {\n if (pis.length >= 2) {\n changes.push({ kind: \"merge\", t: frame.t, into: clusters[ci]!.id, from: pis.map((pi) => prev[pi]!.id) });\n }\n }\n // Deaths: prev ids that no current cluster carries forward.\n const carried = new Set(clusters.map((c) => c.id));\n for (const c of prev) if (!carried.has(c.id)) changes.push({ kind: \"death\", t: frame.t, id: c.id });\n\n // Inherited ids can outrank later birth ids (a predecessor named for a\n // member that has since left), so frames re-sort by id.\n clusters.sort((a, b) => (a.id < b.id ? -1 : 1));\n outFrames.push({ t: frame.t, clusters });\n prev = clusters;\n }\n return { frames: outFrames, changes };\n}\n","// Deterministic force layout (Fruchterman–Reingold in the unit square).\n// Placement is a pure function of (node id, seed): initial positions come\n// from a per-identity hash disc, never from insertion order — so replays are\n// bit-identical, and warm starts make inter-frame motion meaningful\n// (the mindwalk property). Plain O(n²·iterations); practical to ~1–2k nodes.\nimport { hash32, mulberry32 } from \"./prng\";\nimport type { NodeId } from \"./adjacency\";\nimport type { Snapshot } from \"./snapshot\";\n\n/** A unit-square position (scale to pixels at draw time). */\nexport interface XY {\n x: number;\n y: number;\n}\n\n/** Tuning for {@link forceLayout}. */\nexport interface LayoutOptions {\n /** Force iterations. Default 300 (cold); use fewer for warm refinement. */\n iterations?: number;\n /** Seed mixed into every node's hash-derived initial position. Default 1. */\n seed?: number;\n /**\n * Repulsion multiplier. Default 0.1 — measured on a real ~800-node OSINT\n * graph as the point where nothing pins to the clamped boundary.\n */\n repulsion?: number;\n /** Pull toward the center, proportional to distance. Default 0.5. */\n gravity?: number;\n /** Warm-start positions (nodes present here start where they were). */\n positions?: ReadonlyMap<NodeId, XY>;\n}\n\nconst initialFor = (id: NodeId, seed: number): XY => {\n const rand = mulberry32(hash32(id) ^ (seed >>> 0));\n const theta = 2 * Math.PI * rand();\n const radius = 0.25 + 0.2 * rand();\n return { x: 0.5 + radius * Math.cos(theta), y: 0.5 + radius * Math.sin(theta) };\n};\n\n/**\n * Lay out one graph. Init priority per node: warm `positions` entry →\n * hash-disc from (id, seed). Forces per iteration (accumulated over\n * sorted-id order, applied synchronously): pairwise repulsion k²/d, edge\n * attraction d²/k scaled by min(weight, 4), center gravity. Linear cooling\n * caps displacement; results clamp to [0,1]².\n */\nexport function forceLayout(\n nodes: readonly NodeId[],\n edges: ReadonlyArray<{ a: NodeId; b: NodeId; weight?: number }>,\n opts: LayoutOptions = {},\n): Map<NodeId, XY> {\n const { iterations = 300, seed = 1, repulsion = 0.1, gravity = 0.5, positions } = opts;\n const ids = [...nodes].sort();\n const n = ids.length;\n if (n === 0) return new Map();\n const k = Math.sqrt(1 / n);\n\n const xs = new Float64Array(n);\n const ys = new Float64Array(n);\n const index = new Map<NodeId, number>();\n ids.forEach((id, i) => {\n index.set(id, i);\n const p = positions?.get(id) ?? initialFor(id, seed);\n xs[i] = p.x;\n ys[i] = p.y;\n });\n\n const links: Array<[number, number, number]> = [];\n for (const e of edges) {\n const a = index.get(e.a);\n const b = index.get(e.b);\n if (a === undefined || b === undefined || a === b) continue;\n links.push(a < b ? [a, b, Math.min(e.weight ?? 1, 4)] : [b, a, Math.min(e.weight ?? 1, 4)]);\n }\n // Canonical accumulation order — float addition isn't associative, so the\n // edge-array order must never leak into the result.\n links.sort((p, q) => p[0] - q[0] || p[1] - q[1] || p[2] - q[2]);\n\n const dx = new Float64Array(n);\n const dy = new Float64Array(n);\n for (let iter = 0; iter < iterations; iter++) {\n dx.fill(0);\n dy.fill(0);\n for (let i = 0; i < n; i++) {\n for (let j = i + 1; j < n; j++) {\n let vx = xs[i]! - xs[j]!;\n let vy = ys[i]! - ys[j]!;\n let d = Math.hypot(vx, vy);\n if (d < 1e-6) {\n // Deterministic epsilon separation for coincident nodes.\n const r = mulberry32(hash32(`${ids[i]}|${ids[j]}`))();\n vx = 1e-4 * Math.cos(r * 7);\n vy = 1e-4 * Math.sin(r * 7);\n d = 1e-4;\n }\n const force = (repulsion * k * k) / d;\n dx[i] = dx[i]! + (vx / d) * force;\n dy[i] = dy[i]! + (vy / d) * force;\n dx[j] = dx[j]! - (vx / d) * force;\n dy[j] = dy[j]! - (vy / d) * force;\n }\n }\n for (const [a, b, w] of links) {\n const vx = xs[a]! - xs[b]!;\n const vy = ys[a]! - ys[b]!;\n const d = Math.max(Math.hypot(vx, vy), 1e-6);\n const force = ((d * d) / k) * w;\n dx[a] = dx[a]! - (vx / d) * force;\n dy[a] = dy[a]! - (vy / d) * force;\n dx[b] = dx[b]! + (vx / d) * force;\n dy[b] = dy[b]! + (vy / d) * force;\n }\n const cap = 0.1 * (1 - iter / iterations) + 0.002;\n for (let i = 0; i < n; i++) {\n const gx = (0.5 - xs[i]!) * gravity;\n const gy = (0.5 - ys[i]!) * gravity;\n let mx = dx[i]! + gx;\n let my = dy[i]! + gy;\n const move = Math.hypot(mx, my);\n if (move > cap) {\n mx = (mx / move) * cap;\n my = (my / move) * cap;\n }\n xs[i] = Math.min(1, Math.max(0, xs[i]! + mx));\n ys[i] = Math.min(1, Math.max(0, ys[i]! + my));\n }\n }\n return new Map(ids.map((id, i) => [id, { x: xs[i]!, y: ys[i]! }]));\n}\n\n/**\n * Lay out a snapshot sequence: frame 0 runs cold (`iterations`), each later\n * frame warm-starts from the previous frame's positions and refines for\n * `warmIterations` — existing nodes drift instead of re-scrambling, so\n * inter-frame motion means something.\n */\nexport function layoutSequence(\n snapshots: readonly Snapshot[],\n opts: LayoutOptions & { warmIterations?: number } = {},\n): Map<NodeId, XY>[] {\n const { warmIterations = 60, ...base } = opts;\n const out: Map<NodeId, XY>[] = [];\n let prev: Map<NodeId, XY> | undefined = base.positions ? new Map(base.positions) : undefined;\n for (const snap of snapshots) {\n const frame = forceLayout(snap.nodes, snap.edges, {\n ...base,\n iterations: prev ? warmIterations : base.iterations,\n positions: prev,\n });\n out.push(frame);\n prev = frame;\n }\n return out;\n}\n","// One-call temporal analysis: fragment → a compact, JSON-native report of\n// hotspot leads, cluster evolution, and event density. Pure math (no db, no\n// LLM) — the report is what pipelines cache, routes serve, viewers render,\n// and the narrative stage describes.\nimport type { GraphFragment } from \"../types\";\nimport { nodeId } from \"./adjacency\";\nimport { extractEvents } from \"./events\";\nimport type { TemporalConfig, TemporalEvent } from \"./events\";\nimport { rollup } from \"./series\";\nimport { bucketRange } from \"./time\";\nimport { rankHotspots } from \"./hotspots\";\nimport type { BurstOptions } from \"./hotspots\";\nimport { buildTimeline, snapshotSeries } from \"./snapshot\";\nimport { labelPropagation } from \"./communities\";\nimport { trackClusters } from \"./clusters\";\nimport type { ClusterChange } from \"./clusters\";\n\nconst DAY = 86_400_000;\n\n/** Tuning for {@link buildTemporalReport}. */\nexport interface ReportOptions {\n /** Analysis anchor (epoch ms). Default: the latest event time. Inject for determinism. */\n now?: number;\n /** Weekly cluster/lead window. Default 155 days (~22 frames). */\n windowDays?: number;\n /** Monthly density-series span. Default 12 months. */\n monthlyMonths?: number;\n /** Ranked leads kept. Default 8. */\n topLeads?: number;\n /** Contributing events per lead. Default 3. */\n contributorsPer?: number;\n /** Trailing weeks of cluster changes kept. Default 4. */\n changeWeeks?: number;\n /** Burst detector tuning. Default `{ window: 6 }`. */\n burst?: BurstOptions;\n}\n\n/** One ranked hotspot lead: a bursting entity plus its evidence. */\nexport interface ReportLead {\n key: string;\n type: string;\n name: string;\n city?: string;\n sector?: string;\n score: number;\n peak: number;\n window: string[];\n contributors: Array<{ kind: string; key: string; title: string; date: string }>;\n}\n\n/** One cluster-evolution change, name-resolved for display. */\nexport interface ReportClusterChange {\n week: string;\n kind: \"birth\" | \"death\" | \"merge\" | \"split\";\n ids: string[];\n names: string[];\n}\n\n/** The cached/served analysis product — everything JSON-native and bounded (≲20KB). */\nexport interface TemporalReport {\n generatedAt: string;\n window: { from: string; to: string; weeks: string[] };\n totals: { events: number; nodes: number; edges: number };\n series: Array<{ kind: string; months: string[]; values: number[] }>;\n leads: ReportLead[];\n clusters: {\n frames: Array<{ week: string; count: number; top: Array<{ id: string; size: number; label: string }> }>;\n changes: ReportClusterChange[];\n };\n}\n\nconst round2 = (v: number) => Math.round(v * 100) / 100;\nconst isoDay = (t: number) => new Date(t).toISOString().slice(0, 10);\n\n/** Flatten a ClusterChange's ids, primary cluster first (merge target / split source). */\nexport function clusterChangeIds(change: ClusterChange): string[] {\n if (change.kind === \"merge\") return [change.into, ...change.from];\n if (change.kind === \"split\") return [change.from, ...change.into];\n return [change.id];\n}\n\n/** Build the full temporal report for a graph fragment. Empty input → zeroed report. */\nexport function buildTemporalReport(\n fragment: GraphFragment,\n config: TemporalConfig,\n opts: ReportOptions = {},\n): TemporalReport {\n const events = extractEvents(fragment, config);\n const now = opts.now ?? events[events.length - 1]?.t ?? 0;\n const generatedAt = new Date(now).toISOString();\n if (events.length === 0) {\n return {\n generatedAt,\n window: { from: \"\", to: \"\", weeks: [] },\n totals: { events: 0, nodes: 0, edges: 0 },\n series: [],\n leads: [],\n clusters: { frames: [], changes: [] },\n };\n }\n\n const attrsOf = new Map(fragment.nodes.map((n) => [nodeId(n), n.attrs]));\n const str = (id: string, attr: string): string | undefined => {\n const v = attrsOf.get(id)?.[attr];\n return typeof v === \"string\" && v !== \"\" ? v : undefined;\n };\n\n const windowDays = opts.windowDays ?? 155;\n const from = Math.max(events[0]!.t, now - windowDays * DAY);\n const weeks = bucketRange(from, now, \"week\");\n\n // Hotspot leads: weekly per-participant activity, ranked burst runs.\n const weekly = rollup(events, { grain: \"week\", by: \"ref\", from, to: now });\n const leads: ReportLead[] = rankHotspots(weekly, events, {\n by: \"ref\",\n top: opts.topLeads ?? 8,\n contributorsPer: opts.contributorsPer ?? 3,\n ...(opts.burst ?? { window: 6 }),\n }).map((h) => ({\n key: h.seriesKey,\n type: h.seriesKey.slice(0, Math.max(0, h.seriesKey.indexOf(\":\"))), // ref keys are always \"type:key\"\n name: str(h.seriesKey, \"name\") ?? h.seriesKey,\n ...(str(h.seriesKey, \"city\") ? { city: str(h.seriesKey, \"city\") } : {}),\n ...(str(h.seriesKey, \"sector\") ? { sector: str(h.seriesKey, \"sector\") } : {}),\n score: round2(h.score),\n peak: round2(h.peak),\n window: h.bucketKeys,\n contributors: h.contributors.map((e) => ({\n kind: e.kind,\n key: e.node.key,\n title: str(nodeId(e.node), \"title\") ?? str(nodeId(e.node), \"round\") ?? str(nodeId(e.node), \"name\") ?? e.node.key,\n date: isoDay(e.t),\n })),\n }));\n\n // Cluster evolution over weekly cumulative snapshots.\n const timeline = buildTimeline(events);\n const snapshots = snapshotSeries(timeline, weeks);\n const labelFrames = snapshots.map((s) => ({ t: s.t, labels: labelPropagation(s.nodes, s.edges, { seed: 1 }) }));\n const tracked = trackClusters(labelFrames);\n const labelFor = (members: readonly string[]) =>\n `${str(members[0]!, \"name\") ?? members[0]!}${members.length > 1 ? ` +${members.length - 1}` : \"\"}`;\n const frames = tracked.frames.map((f, i) => ({\n week: weeks[i]!.key,\n count: f.clusters.length,\n top: [...f.clusters]\n .sort((a, b) => b.members.length - a.members.length)\n .slice(0, 3)\n .map((c) => ({ id: c.id, size: c.members.length, label: labelFor(c.members) })),\n }));\n const changeFloor = weeks[Math.max(0, weeks.length - (opts.changeWeeks ?? 4))]!.start;\n // Change timestamps ARE frame timestamps (bucket.end − 1), so the containing\n // week always exists.\n const weekOf = (t: number) => weeks.find((w) => t >= w.start && t < w.end)!.key;\n const nameOfCluster = (frameIdx: number, id: string) => {\n for (let i = frameIdx; i >= 0; i--) {\n const c = tracked.frames[i]?.clusters.find((x) => x.id === id);\n if (c) return labelFor(c.members);\n }\n /* v8 ignore next 2 -- every change id exists in its own or a prior frame by trackClusters' construction */\n return id;\n };\n const changes: ReportClusterChange[] = tracked.changes\n .filter((c) => c.t >= changeFloor)\n .map((c) => {\n const frameIdx = tracked.frames.findIndex((f) => f.t === c.t);\n const ids = clusterChangeIds(c);\n return { week: weekOf(c.t), kind: c.kind, ids, names: ids.map((id) => nameOfCluster(frameIdx, id)) };\n });\n\n // Monthly density per event kind for the viewer's sparklines.\n const monthly = rollup(events, {\n grain: \"month\",\n by: \"kind\",\n from: now - (opts.monthlyMonths ?? 12) * 30 * DAY,\n to: now,\n });\n const months = monthly.buckets.map((b) => b.key);\n const series = monthly.series.map((s) => ({ kind: s.key, months, values: s.values }));\n\n return {\n generatedAt,\n window: { from: isoDay(weeks[0]!.start), to: isoDay(now), weeks: weeks.map((w) => w.key) },\n totals: { events: events.length, nodes: timeline.nodes.length, edges: timeline.edges.length },\n series,\n leads,\n clusters: { frames, changes },\n };\n}\n","// Deterministic narration materials for a TemporalReport. The LLM writes\n// prose; everything factual is computed here so the model interprets a fixed\n// list instead of inventing analysis. (The product voice/system prompt lives\n// with the pipeline, not in this library.)\nimport type { TemporalReport } from \"./report\";\n\n/** True when the report has anything worth narrating (any lead or recent cluster change). */\nexport function shouldNarrate(report: TemporalReport): boolean {\n return report.leads.length > 0 || report.clusters.changes.length > 0;\n}\n\n/**\n * The deterministic materials block handed to the narrative LLM call:\n * window/totals header, top leads with evidence, cluster changes, and\n * per-kind monthly density lines. No prose, no interpretation.\n */\nexport function buildNarrationMaterials(report: TemporalReport, opts: { topLeads?: number } = {}): string {\n const lines: string[] = [];\n const w = report.window;\n lines.push(`Window: ${w.weeks[0] ?? w.from}..${w.weeks[w.weeks.length - 1] ?? w.to} (weekly) · generated ${report.generatedAt.slice(0, 10)}`);\n lines.push(`Totals: ${report.totals.events} events · ${report.totals.nodes} nodes · ${report.totals.edges} edges`);\n\n const leads = report.leads.slice(0, opts.topLeads ?? 5);\n if (leads.length > 0) {\n lines.push(\"\", `HOTSPOT LEADS (top ${leads.length}):`);\n for (const lead of leads) {\n const where = lead.city ?? lead.sector;\n const evidence = lead.contributors[0];\n lines.push(\n `- ${lead.name} (${lead.type}${where ? `, ${where}` : \"\"}) — score ${lead.score}, peak z ${lead.peak}, ` +\n `window ${lead.window[0]}..${lead.window[lead.window.length - 1]}` +\n (evidence ? `; evidence: \"${evidence.title}\" (${evidence.date})` : \"\"),\n );\n }\n }\n\n if (report.clusters.changes.length > 0) {\n lines.push(\"\", `CLUSTER CHANGES (last weeks):`);\n for (const c of report.clusters.changes) {\n const detail =\n c.kind === \"merge\"\n ? `${c.names.slice(1).join(\", \")} into ${c.names[0]}`\n : c.kind === \"split\"\n ? `${c.names[0]} into ${c.names.slice(1).join(\", \")}`\n : c.names[0];\n lines.push(`- ${c.week} ${c.kind}: ${detail}`);\n }\n }\n\n if (report.series.length > 0) {\n lines.push(\"\", \"MONTHLY DENSITY:\");\n for (const s of report.series) {\n const span = s.months.length > 0 ? ` (${s.months[0]}..${s.months[s.months.length - 1]})` : \"\";\n lines.push(`- ${s.kind}: ${s.values.join(\" \")}${span}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/** First-line `# title` heuristic shared with the digest workflow. */\nexport function splitTitleBody(text: string, fallbackTitle: string): { title: string; body: string } {\n const trimmed = text.trim();\n const firstLine = trimmed.split(\"\\n\", 1)[0] ?? \"\";\n if (firstLine.startsWith(\"# \")) {\n return { title: firstLine.slice(2).trim() || fallbackTitle, body: trimmed.slice(firstLine.length).trim() };\n }\n return { title: fallbackTitle, body: trimmed };\n}\n","// Pipeline steering from temporal analysis: burst-ranked enrichment\n// candidates and burst-derived discovery queries. Pure helpers — the\n// pipeline supplies rows, cooldown state, and source config.\nimport type { SourceEntry } from \"../sources\";\nimport type { TemporalReport } from \"./report\";\n\n/**\n * Burst score per natural key for one entity type, from a report's leads:\n * `{ leads: [{key: \"company:acme\", score: 7.3}] }` → `{ acme: 7.3 }`.\n */\nexport function leadScores(report: Pick<TemporalReport, \"leads\">, type = \"company\"): Record<string, number> {\n const out: Record<string, number> = {};\n const prefix = `${type}:`;\n for (const lead of report.leads) {\n if (lead.key.startsWith(prefix)) out[lead.key.slice(prefix.length)] = lead.score;\n }\n return out;\n}\n\n/** One selected enrichment target. */\nexport interface EnrichmentCandidate {\n slug: string;\n name: string;\n score: number;\n}\n\n/**\n * Rank enrichment candidates: burst score descending, ties keep the input\n * (creation) order. Cooling rows are skipped at selection. Complete rows are\n * skipped UNLESS they carry a burst score — a bursting complete company\n * deserves a re-check. With no scores this reproduces the legacy behavior\n * exactly (input order, incomplete rows only).\n */\nexport function rankEnrichmentCandidates(\n rows: ReadonlyArray<Record<string, unknown>>,\n opts: {\n limit: number;\n scores?: Record<string, number>;\n cooling?: ReadonlySet<string>;\n isComplete?: (row: Record<string, unknown>) => boolean;\n },\n): EnrichmentCandidate[] {\n const scores = opts.scores ?? {};\n const isComplete = opts.isComplete ?? ((r) => Boolean(r.website && r.city && r.sector));\n const seen = new Set<string>();\n const eligible: EnrichmentCandidate[] = [];\n for (const row of rows) {\n const slug = typeof row.slug === \"string\" ? row.slug : \"\";\n if (!slug || seen.has(slug)) continue;\n seen.add(slug);\n const score = scores[slug] ?? 0;\n if (opts.cooling?.has(slug)) continue;\n if (score <= 0 && isComplete(row)) continue;\n eligible.push({ slug, name: typeof row.name === \"string\" ? row.name : slug, score });\n }\n // Stable sort: score desc; ties preserve input order.\n return eligible.sort((a, b) => b.score - a.score).slice(0, Math.max(0, opts.limit));\n}\n\nconst BURST_ORIGIN = \"burst\";\nconst MAX_QUERY_CHARS = 120;\n\n/**\n * Replace the synthesized burst-sweep source with a fresh one derived from\n * the report's top company leads (or remove it when there are none).\n * Hand-authored entries — anything without `origin: \"burst\"` — are returned\n * untouched, in order. Queries follow\n * `\"<name>\" <city|sector|startup> funding OR expansion OR hiring news`.\n */\nexport function synthesizeBurstSources(\n existing: readonly SourceEntry[],\n report: Pick<TemporalReport, \"leads\">,\n opts: { max?: number; cadenceHours?: number } = {},\n): SourceEntry[] {\n const max = opts.max ?? 4;\n const kept = existing.filter((s) => s.origin !== BURST_ORIGIN);\n const queries = report.leads\n .filter((lead) => lead.type === \"company\")\n .slice(0, max)\n .map((lead) => {\n const where = lead.city ?? lead.sector ?? \"startup\";\n return `\"${lead.name}\" ${where} funding OR expansion OR hiring news`.slice(0, MAX_QUERY_CHARS);\n });\n if (queries.length === 0) return kept;\n return [\n ...kept,\n {\n kind: \"web-search\",\n name: \"Burst sweep (auto)\",\n enabled: true,\n cadenceHours: opts.cadenceHours ?? 24,\n watch: \"burst\",\n origin: BURST_ORIGIN,\n queries,\n },\n ];\n}\n","// Geo flow frames: per-bucket aggregated A→B movements, ready for a flow-map\n// renderer. Only events carrying BOTH geo and geoTo participate.\nimport type { TemporalEvent } from \"./events\";\nimport { bucketKey, bucketRange } from \"./time\";\nimport type { TimeBucket, TimeGrain } from \"./time\";\n\n/** One aggregated movement between two geography keys within a bucket. */\nexport interface Flow {\n from: string;\n to: string;\n /** Summed event weights (e.g. dollars). */\n weight: number;\n /** Number of contributing events. */\n count: number;\n}\n\n/** All flows for one time bucket, sorted weight-desc then from/to. */\nexport interface FlowFrame {\n bucket: TimeBucket;\n flows: Flow[];\n}\n\n/**\n * Aggregate flow events into per-bucket frames over `from`..`to` (default:\n * the events' span). With `cumulative`, each frame includes everything up to\n * and including its bucket — replay-style accumulation.\n */\nexport function flowFrames(\n events: readonly TemporalEvent[],\n opts: { grain: TimeGrain; from?: number; to?: number; cumulative?: boolean },\n): FlowFrame[] {\n const moves = events.filter((e) => e.geo !== undefined && e.geoTo !== undefined);\n const from = opts.from ?? moves[0]?.t;\n const to = opts.to ?? moves[moves.length - 1]?.t;\n if (from === undefined || to === undefined) return [];\n\n const buckets = bucketRange(from, to, opts.grain);\n const index = new Map(buckets.map((b, i) => [b.key, i]));\n const perBucket: Map<string, Flow>[] = buckets.map(() => new Map());\n for (const e of moves) {\n const i = index.get(bucketKey(e.t, opts.grain));\n if (i === undefined) continue;\n const cell = perBucket[i] as Map<string, Flow>; // i from the bucket index map\n const key = `${e.geo}→${e.geoTo}`;\n const flow = cell.get(key);\n if (flow) {\n flow.weight += e.weight;\n flow.count += 1;\n } else {\n cell.set(key, { from: e.geo as string, to: e.geoTo as string, weight: e.weight, count: 1 });\n }\n }\n\n const sortFlows = (flows: Flow[]) =>\n flows.sort((a, b) => b.weight - a.weight || (a.from < b.from ? -1 : a.from > b.from ? 1 : a.to < b.to ? -1 : 1));\n\n if (!opts.cumulative) {\n return buckets.map((bucket, i) => ({\n bucket,\n flows: sortFlows([...(perBucket[i] as Map<string, Flow>).values()]),\n }));\n }\n const acc = new Map<string, Flow>();\n return buckets.map((bucket, i) => {\n for (const [key, flow] of perBucket[i] as Map<string, Flow>) {\n const running = acc.get(key);\n if (running) {\n running.weight += flow.weight;\n running.count += flow.count;\n } else {\n acc.set(key, { ...flow });\n }\n }\n return { bucket, flows: sortFlows([...acc.values()].map((f) => ({ ...f }))) };\n });\n}\n\n/** The frame's top-n flows by weight (already the frame's sort order). */\nexport function topFlows(frame: FlowFrame, n: number): Flow[] {\n return frame.flows.slice(0, n);\n}\n","// Spatial anomaly ranking: cross temporal bursts in a geo-keyed SeriesSet\n// with static per-region metrics (\"this county's formations are surging AND\n// it sits in a funding gap\") — the trigger vocabulary for agentic\n// investigation of places, complementing rankHotspots' entity leads.\nimport { detectBursts } from \"./hotspots\";\nimport type { Burst, BurstOptions } from \"./hotspots\";\nimport type { SeriesSet } from \"./series\";\n\n/**\n * One threshold rule over a region's static metrics. A rule matches when the\n * region has the metric and its value passes the comparison; regions or\n * metrics missing from the metric map never match (and never throw).\n */\nexport interface SpatialRule {\n /** Stable identifier, reported on findings (e.g. `\"capital-desert\"`). */\n id: string;\n /** Metric name looked up in the region's metric record. */\n metric: string;\n /** Comparison direction against {@link threshold}. */\n op: \">=\" | \"<=\";\n threshold: number;\n /** Additive score boost when matched (finding score × (1 + Σ weights)). Default 1. */\n weight?: number;\n}\n\n/** A bursting region, the rules it matched, and its boosted rank score. */\nexport interface SpatialFinding {\n /** The series key — the region identifier the events were rolled up by. */\n region: string;\n /** The region's strongest burst (highest score) in the set. */\n burst: Burst;\n /** Ids of the rules that matched, in the rules' given order. */\n matched: string[];\n /** `burst.score × (1 + Σ matched weights)` — rule context amplifies heat. */\n score: number;\n /** The region's values for every rule metric it has (evidence for narration). */\n metrics: Record<string, number>;\n}\n\n/** Ids of the rules a region's metric record matches, in rule order. */\nexport function matchRules(\n values: Record<string, number> | undefined,\n rules: readonly SpatialRule[],\n): string[] {\n if (!values) return [];\n return rules\n .filter((r) => {\n const v = values[r.metric];\n if (v === undefined) return false;\n return r.op === \">=\" ? v >= r.threshold : v <= r.threshold;\n })\n .map((r) => r.id);\n}\n\n/**\n * Rank spatial anomalies: detect bursts in a geo-keyed SeriesSet (one series\n * per region — roll up with `by: \"geo\"`), keep each region's strongest burst,\n * then amplify by matched metric rules. Deterministic: score desc, then\n * region asc. `requireMatch` drops regions matching no rule (default keeps\n * them — a burst is a finding even without metric context).\n */\nexport function spatialFindings(\n set: SeriesSet,\n metrics: Record<string, Record<string, number> | undefined>,\n rules: readonly SpatialRule[],\n opts: BurstOptions & { top?: number; requireMatch?: boolean } = {},\n): SpatialFinding[] {\n const { top = 20, requireMatch = false } = opts;\n const best = new Map<string, Burst>();\n for (const burst of detectBursts(set, opts)) {\n if (!best.has(burst.seriesKey)) best.set(burst.seriesKey, burst); // detectBursts is score-desc\n }\n const findings: SpatialFinding[] = [];\n for (const [region, burst] of best) {\n const values = metrics[region];\n const matched = matchRules(values, rules);\n if (requireMatch && matched.length === 0) continue;\n const boost = rules\n .filter((r) => matched.includes(r.id))\n .reduce((a, r) => a + (r.weight ?? 1), 0);\n const evidence: Record<string, number> = {};\n for (const r of rules) {\n const v = values?.[r.metric];\n if (v !== undefined) evidence[r.metric] = v;\n }\n findings.push({ region, burst, matched, score: burst.score * (1 + boost), metrics: evidence });\n }\n return findings\n .sort((a, b) => b.score - a.score || (a.region < b.region ? -1 : 1))\n .slice(0, top);\n}\n","// Connection lenses: project a heterogeneous graph onto ONE node universe\n// (companies, people, places…) under a chosen relatedness — shared bridges\n// (people, investors), spatial proximity, temporal cohort — and blend several\n// lenses into the weighted edge set a layout/community pass consumes. Pure\n// and deterministic; the viewer re-runs these live as sliders move.\nimport type { GraphFragment } from \"../types\";\nimport type { Snapshot } from \"./snapshot\";\nimport type { TimeBucket } from \"./time\";\nimport type { XY } from \"./layout\";\n\n/** One undirected weighted pair, with (capped) bridging node ids as evidence. */\nexport interface WeightedPair {\n a: string;\n b: string;\n w: number;\n via: string[];\n}\n\n/**\n * Bridge → target sets through a REIFIED relationship node (affiliation,\n * funding_round, record): every reified node with a `bridgeLabel` edge to the\n * bridge and a `targetLabel` edge to a target contributes that target to the\n * bridge's set. Ids are `type:key`.\n */\nexport function reifiedBridgeTargets(\n fragment: GraphFragment,\n opts: { reified: string; bridgeLabel: string; targetLabel: string },\n): Map<string, Set<string>> {\n const bridgesOf = new Map<string, string[]>(); // a round can have several investors\n const targetsOf = new Map<string, string[]>();\n for (const e of fragment.edges) {\n if (e.from.type !== opts.reified) continue;\n const to = `${e.to.type}:${e.to.key}`;\n if (e.label === opts.bridgeLabel) (bridgesOf.get(e.from.key) ?? bridgesOf.set(e.from.key, []).get(e.from.key))!.push(to);\n if (e.label === opts.targetLabel) (targetsOf.get(e.from.key) ?? targetsOf.set(e.from.key, []).get(e.from.key))!.push(to);\n }\n const out = new Map<string, Set<string>>();\n for (const [reifiedKey, bridges] of bridgesOf) {\n for (const bridge of bridges) {\n for (const target of targetsOf.get(reifiedKey) ?? []) {\n (out.get(bridge) ?? out.set(bridge, new Set()).get(bridge))!.add(target);\n }\n }\n }\n return out;\n}\n\n/** Direct-edge targets: `from` node id → set of `to` node ids for one label. */\nexport function directTargets(fragment: GraphFragment, label: string): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const e of fragment.edges) {\n if (e.label !== label) continue;\n const from = `${e.from.type}:${e.from.key}`;\n (out.get(from) ?? out.set(from, new Set()).get(from))!.add(`${e.to.type}:${e.to.key}`);\n }\n return out;\n}\n\n/** Compose A→B with B→C into A→C (e.g. person→companies ∘ company→counties). */\nexport function composeTargets(\n ab: Map<string, Set<string>>,\n bc: Map<string, Set<string>>,\n): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const [a, bs] of ab) {\n for (const b of bs) {\n for (const c of bc.get(b) ?? []) {\n (out.get(a) ?? out.set(a, new Set()).get(a))!.add(c);\n }\n }\n }\n return out;\n}\n\n/**\n * The bipartite projection: targets sharing a bridge become pairs, weight =\n * number of distinct shared bridges. `maxPerBridge` skips promiscuous hubs\n * (a bridge touching 40 targets says little about any pair). Deterministic\n * pair order (a < b, then sorted).\n */\nexport function projectBridges(\n bridges: Map<string, Set<string>>,\n opts: { maxPerBridge?: number; viaCap?: number } = {},\n): WeightedPair[] {\n const { maxPerBridge = 12, viaCap = 4 } = opts;\n const acc = new Map<string, { a: string; b: string; w: number; via: string[] }>();\n for (const [bridge, targetSet] of [...bridges.entries()].sort((x, y) => (x[0] < y[0] ? -1 : 1))) {\n const targets = [...targetSet].sort();\n if (targets.length < 2 || targets.length > maxPerBridge) continue;\n for (let i = 0; i < targets.length; i++) {\n for (let j = i + 1; j < targets.length; j++) {\n const key = `${targets[i]}|${targets[j]}`;\n const hit = acc.get(key) ?? { a: targets[i]!, b: targets[j]!, w: 0, via: [] };\n hit.w += 1;\n if (hit.via.length < viaCap) hit.via.push(bridge);\n acc.set(key, hit);\n }\n }\n }\n return [...acc.keys()].sort().map((k) => acc.get(k)!);\n}\n\n/**\n * Temporal cohort pairs: each node links to its k nearest neighbors in\n * arrival time, weight decaying with the gap (0.5 per halfLife). Nodes\n * without a date contribute nothing.\n */\nexport function timePairs(\n arrivals: Map<string, number>,\n opts: { k?: number; halfLifeDays?: number } = {},\n): WeightedPair[] {\n const { k = 4, halfLifeDays = 30 } = opts;\n const halfLife = halfLifeDays * 86_400_000;\n const rows = [...arrivals.entries()].filter(([, t]) => Number.isFinite(t)).sort((x, y) => x[1] - y[1] || (x[0] < y[0] ? -1 : 1));\n const acc = new Map<string, WeightedPair>();\n for (let i = 0; i < rows.length; i++) {\n for (let j = i + 1; j < Math.min(rows.length, i + 1 + k); j++) {\n const [idA, tA] = rows[i]!;\n const [idB, tB] = rows[j]!;\n const w = Math.pow(0.5, Math.abs(tB - tA) / halfLife);\n if (w < 0.01) break; // sorted by time — later neighbors are only farther\n const [a, b] = idA < idB ? [idA, idB] : [idB, idA];\n acc.set(`${a}|${b}`, { a, b, w, via: [] }); // i<j scan visits each pair once\n }\n }\n return [...acc.keys()].sort().map((key) => acc.get(key)!);\n}\n\n/**\n * Spatial proximity pairs: k nearest neighbors in normalized map space,\n * weight decaying with distance (0.5 per halfDist). Nodes without a\n * position contribute nothing. O(n²) — fine for the ≤1k-node universes\n * lenses operate on.\n */\nexport function geoPairs(\n positions: Map<string, XY>,\n opts: { k?: number; halfDist?: number } = {},\n): WeightedPair[] {\n const { k = 4, halfDist = 0.04 } = opts;\n const rows = [...positions.entries()].sort((x, y) => (x[0] < y[0] ? -1 : 1));\n const acc = new Map<string, WeightedPair>();\n for (let i = 0; i < rows.length; i++) {\n const dists: Array<{ j: number; d: number }> = [];\n for (let j = 0; j < rows.length; j++) {\n if (i === j) continue;\n const dx = rows[i]![1].x - rows[j]![1].x;\n const dy = rows[i]![1].y - rows[j]![1].y;\n dists.push({ j, d: Math.hypot(dx, dy) });\n }\n dists.sort((x, y) => x.d - y.d || x.j - y.j);\n for (const { j, d } of dists.slice(0, k)) {\n const w = Math.pow(0.5, d / halfDist);\n if (w < 0.01) continue;\n const [a, b] = rows[i]![0] < rows[j]![0] ? [rows[i]![0], rows[j]![0]] : [rows[j]![0], rows[i]![0]];\n const key = `${a}|${b}`;\n const prev = acc.get(key);\n if (!prev || prev.w < w) acc.set(key, { a, b, w, via: [] });\n }\n }\n return [...acc.keys()].sort().map((key) => acc.get(key)!);\n}\n\n/**\n * Blend lens edge sets: each set is normalized to a max weight of 1 (a lens\n * with big raw counts must not drown the others), scaled by its slider\n * weight, and summed per pair. Zero-weight lenses drop out entirely.\n */\nexport function blendPairs(sets: Array<{ pairs: WeightedPair[]; weight: number }>): WeightedPair[] {\n const acc = new Map<string, WeightedPair>();\n for (const { pairs, weight } of sets) {\n if (weight <= 0 || pairs.length === 0) continue;\n const max = Math.max(...pairs.map((p) => p.w));\n for (const p of pairs) {\n const key = `${p.a}|${p.b}`;\n const hit = acc.get(key) ?? { a: p.a, b: p.b, w: 0, via: [] };\n hit.w += (p.w / max) * weight;\n for (const v of p.via) if (hit.via.length < 6 && !hit.via.includes(v)) hit.via.push(v);\n acc.set(key, hit);\n }\n }\n return [...acc.keys()].sort().map((key) => acc.get(key)!);\n}\n\n/**\n * Fabricate the Snapshot series a lens graph replays: at each bucket end,\n * nodes that have arrived and the blended edges among them. Slots straight\n * into labelPropagation/trackClusters/layoutSequence.\n */\nexport function lensSnapshots(\n arrivals: Map<string, number>,\n pairs: WeightedPair[],\n buckets: readonly TimeBucket[],\n): Snapshot[] {\n const ids = [...arrivals.keys()].sort();\n return buckets.map((bucket) => {\n const t = bucket.end - 1;\n const nodes = ids.filter((id) => arrivals.get(id)! <= t);\n const active = new Set(nodes);\n const edges = pairs\n .filter((p) => active.has(p.a) && active.has(p.b))\n .map((p) => ({ a: p.a, b: p.b, weight: p.w }));\n const activity = new Map(nodes.map((id) => [id, arrivals.get(id)!]));\n return { t, nodes, edges, activity };\n });\n}\n","// Brokerage: which nodes connect otherwise-distinct groups? Scored against\n// the community partition (labelPropagation) already computed per frame:\n// a node's bridging weight is its edge weight INTO foreign communities,\n// amplified by how many distinct groups it touches. Pure and cheap (O(E))\n// — cheap enough to recompute every frame of a replay.\nimport type { NodeId } from \"./adjacency\";\n\n/** One node's brokerage: normalized score + the communities it connects. */\nexport interface Bridging {\n /** 0..1, 1 = the frame's strongest connector. */\n score: number;\n /** Communities this node touches through its edges (own community first). */\n groups: string[];\n}\n\n/**\n * Score every node's brokerage against a community partition. Edges inside a\n * community contribute nothing; an edge to a foreign community contributes\n * its weight, and the total is multiplied by the number of DISTINCT foreign\n * communities — a node wiring three groups together outranks one with the\n * same raw weight into a single neighbor group. Scores are normalized to\n * the frame's max (empty/one-community frames → all zeros).\n */\nexport function bridgingScores(\n edges: ReadonlyArray<{ a: NodeId; b: NodeId; weight?: number }>,\n labels: Map<NodeId, string>,\n): Map<NodeId, Bridging> {\n const cross = new Map<NodeId, number>();\n const foreign = new Map<NodeId, Set<string>>();\n const touch = (id: NodeId, otherLabel: string, w: number) => {\n cross.set(id, (cross.get(id) ?? 0) + w);\n (foreign.get(id) ?? foreign.set(id, new Set()).get(id))!.add(otherLabel);\n };\n for (const e of edges) {\n const la = labels.get(e.a);\n const lb = labels.get(e.b);\n if (la === undefined || lb === undefined || la === lb) continue;\n const w = e.weight ?? 1;\n touch(e.a, lb, w);\n touch(e.b, la, w);\n }\n let max = 0;\n const raw = new Map<NodeId, number>();\n for (const [id, w] of cross) {\n const r = w * foreign.get(id)!.size;\n raw.set(id, r);\n if (r > max) max = r;\n }\n const out = new Map<NodeId, Bridging>();\n for (const [id] of labels) {\n const r = raw.get(id) ?? 0;\n const own = labels.get(id)!;\n const groups = r > 0 ? [own, ...[...foreign.get(id)!].sort()] : [own];\n out.set(id, { score: max > 0 ? r / max : 0, groups });\n }\n return out;\n}\n\n/** The frame's strongest connectors, ranked (ties: id order). */\nexport function topBridges(\n bridging: Map<NodeId, Bridging>,\n n = 5,\n): Array<{ id: NodeId; score: number; groups: string[] }> {\n return [...bridging.entries()]\n .filter(([, b]) => b.score > 0)\n .sort((x, y) => y[1].score - x[1].score || (x[0] < y[0] ? -1 : 1))\n .slice(0, n)\n .map(([id, b]) => ({ id, score: Math.round(b.score * 100) / 100, groups: b.groups }));\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odla-ai/kg",
3
- "version": "0.2.4",
3
+ "version": "0.3.2",
4
4
  "description": "Ontology-driven knowledge-graph builder: pluggable source connectors, LLM extraction against a config-as-data ontology, and a provenance-preserving graph writer persisting into odla-db.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://odla.ai/docs/packages/kg",
@@ -18,6 +18,11 @@
18
18
  "types": "./dist/index.d.ts",
19
19
  "import": "./dist/index.js",
20
20
  "require": "./dist/index.cjs"
21
+ },
22
+ "./temporal": {
23
+ "types": "./dist/temporal/index.d.ts",
24
+ "import": "./dist/temporal/index.js",
25
+ "require": "./dist/temporal/index.cjs"
21
26
  }
22
27
  },
23
28
  "files": [
@@ -47,10 +52,11 @@
47
52
  "clean": "rm -rf dist",
48
53
  "typecheck": "tsc --noEmit",
49
54
  "test": "vitest run",
55
+ "coverage": "vitest run --coverage",
50
56
  "prepublishOnly": "npm run build"
51
57
  },
52
58
  "dependencies": {
53
- "@odla-ai/ai": "^0.4.0"
59
+ "@odla-ai/ai": "^0.5.0"
54
60
  },
55
61
  "peerDependencies": {
56
62
  "@odla-ai/db": "^0.6.0"
@@ -58,6 +64,7 @@
58
64
  "devDependencies": {
59
65
  "@odla-ai/db": "*",
60
66
  "@types/node": "^22.10.0",
67
+ "@vitest/coverage-v8": "^4.1.9",
61
68
  "tsup": "^8.3.0",
62
69
  "typescript": "^6.0.3",
63
70
  "vitest": "^4.1.9"