@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.
- package/dist/chunk-V3N2GRYU.js +73 -0
- package/dist/chunk-V3N2GRYU.js.map +1 -0
- package/dist/index.cjs +136 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -250
- package/dist/index.d.ts +44 -250
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -1
- package/dist/sources-nJyD0JUP.d.cts +256 -0
- package/dist/sources-nJyD0JUP.d.ts +256 -0
- package/dist/temporal/index.cjs +1086 -0
- package/dist/temporal/index.cjs.map +1 -0
- package/dist/temporal/index.d.cts +690 -0
- package/dist/temporal/index.d.ts +690 -0
- package/dist/temporal/index.js +1004 -0
- package/dist/temporal/index.js.map +1 -0
- package/package.json +9 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseDateMs
|
|
3
|
+
} from "./chunk-V3N2GRYU.js";
|
|
4
|
+
|
|
1
5
|
// src/types.ts
|
|
2
6
|
function emptyUsage() {
|
|
3
7
|
return { calls: 0, inputTokens: 0, outputTokens: 0 };
|
|
@@ -144,6 +148,110 @@ async function writeFragment(db, fragment, ontology, mutationId) {
|
|
|
144
148
|
return { txId, ops: ops.length };
|
|
145
149
|
}
|
|
146
150
|
|
|
151
|
+
// src/graph/export.ts
|
|
152
|
+
function planLinks(ontology, type, labels) {
|
|
153
|
+
return labels.map((label) => {
|
|
154
|
+
for (const edge of Object.values(ontology.edges)) {
|
|
155
|
+
if (edge.forwardLabel === label && edge.from === type) {
|
|
156
|
+
return { label, counterpart: edge.to, direction: "forward", forwardLabel: edge.forwardLabel };
|
|
157
|
+
}
|
|
158
|
+
if (edge.reverseLabel === label && edge.to === type) {
|
|
159
|
+
return { label, counterpart: edge.from, direction: "reverse", forwardLabel: edge.forwardLabel };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new Error(`exportFragment: no ontology edge matches link "${label}" on "${type}"`);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
var isScalar = (v) => v === null || typeof v !== "object" && typeof v !== "function";
|
|
166
|
+
function pickAttrs(row, spec, links) {
|
|
167
|
+
const out = {};
|
|
168
|
+
for (const [k, v] of Object.entries(row)) {
|
|
169
|
+
if (spec.attrs ? !spec.attrs.includes(k) : k === "id" || links.includes(k) || !isScalar(v)) continue;
|
|
170
|
+
if (isScalar(v) && v !== null) out[k] = v;
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
var RETRYABLE_WITHOUT_WHERE = /query_scan_limit|query_too_complex/;
|
|
175
|
+
async function exportFragment(db, ontology, spec) {
|
|
176
|
+
const keys = naturalKeysOf(ontology);
|
|
177
|
+
const pageSize = Math.min(1e3, Math.max(1, spec.pageSize ?? 200));
|
|
178
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
179
|
+
const edges = /* @__PURE__ */ new Map();
|
|
180
|
+
const putNode = (node) => {
|
|
181
|
+
const id = `${node.type}:${node.key}`;
|
|
182
|
+
const existing = nodes.get(id);
|
|
183
|
+
if (!existing || Object.keys(node.attrs).length > Object.keys(existing.attrs).length) nodes.set(id, node);
|
|
184
|
+
};
|
|
185
|
+
for (const [type, entity] of Object.entries(spec.entities)) {
|
|
186
|
+
const naturalKey = keys[type];
|
|
187
|
+
if (!naturalKey) throw new Error(`exportFragment: no ontology entity "${type}"`);
|
|
188
|
+
const links = planLinks(ontology, type, entity.links ?? []);
|
|
189
|
+
const limit = entity.limit ?? 1e3;
|
|
190
|
+
const fromMs = entity.window ? parseDateMs(entity.window.from) : null;
|
|
191
|
+
let useWhere = entity.window?.indexed === true;
|
|
192
|
+
let offset = 0;
|
|
193
|
+
let taken = 0;
|
|
194
|
+
while (taken < limit) {
|
|
195
|
+
const opts = {
|
|
196
|
+
limit: Math.min(pageSize, limit - taken),
|
|
197
|
+
offset,
|
|
198
|
+
order: { [naturalKey]: "asc" }
|
|
199
|
+
};
|
|
200
|
+
if (useWhere && entity.window) opts.where = { [entity.window.dateAttr]: { $gte: entity.window.from } };
|
|
201
|
+
const query = {
|
|
202
|
+
[type]: { $: opts, ...Object.fromEntries(links.map((l) => [l.label, {}])) }
|
|
203
|
+
};
|
|
204
|
+
let rows;
|
|
205
|
+
try {
|
|
206
|
+
rows = (await db.query(query))[type] ?? [];
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (useWhere && RETRYABLE_WITHOUT_WHERE.test(String(err.message ?? err))) {
|
|
209
|
+
useWhere = false;
|
|
210
|
+
offset = 0;
|
|
211
|
+
taken = 0;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
for (const row of rows) {
|
|
217
|
+
const key = row[naturalKey];
|
|
218
|
+
if (key === void 0 || key === null) continue;
|
|
219
|
+
if (entity.window && fromMs !== null) {
|
|
220
|
+
const t = parseDateMs(row[entity.window.dateAttr]);
|
|
221
|
+
if (t !== null && t < fromMs) continue;
|
|
222
|
+
}
|
|
223
|
+
const rootRef = { type, key: String(key) };
|
|
224
|
+
putNode({ ...rootRef, attrs: pickAttrs(row, entity, entity.links ?? []) });
|
|
225
|
+
for (const plan of links) {
|
|
226
|
+
const nestedKeyAttr = keys[plan.counterpart];
|
|
227
|
+
if (!nestedKeyAttr) throw new Error(`exportFragment: no ontology entity "${plan.counterpart}"`);
|
|
228
|
+
const raw = row[plan.label];
|
|
229
|
+
const nestedRows = Array.isArray(raw) ? raw : raw ? [raw] : [];
|
|
230
|
+
for (const nested of nestedRows) {
|
|
231
|
+
const nestedKey = nested[nestedKeyAttr];
|
|
232
|
+
if (nestedKey === void 0 || nestedKey === null) continue;
|
|
233
|
+
const nestedRef = { type: plan.counterpart, key: String(nestedKey) };
|
|
234
|
+
putNode({ ...nestedRef, attrs: pickAttrs(nested, {}, []) });
|
|
235
|
+
const edge = plan.direction === "forward" ? { from: rootRef, to: nestedRef, label: plan.forwardLabel } : { from: nestedRef, to: rootRef, label: plan.forwardLabel };
|
|
236
|
+
edges.set(`${edge.from.type}:${edge.from.key}|${edge.label}|${edge.to.type}:${edge.to.key}`, edge);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
taken++;
|
|
240
|
+
if (taken >= limit) break;
|
|
241
|
+
}
|
|
242
|
+
if (rows.length < pageSize) break;
|
|
243
|
+
offset += rows.length;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const byRef = (a, b) => a.type < b.type ? -1 : a.type > b.type ? 1 : a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
|
|
247
|
+
return {
|
|
248
|
+
nodes: [...nodes.values()].sort(byRef),
|
|
249
|
+
edges: [...edges.values()].sort(
|
|
250
|
+
(a, b) => byRef(a.from, b.from) || (a.label < b.label ? -1 : a.label > b.label ? 1 : 0) || byRef(a.to, b.to)
|
|
251
|
+
)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
147
255
|
// src/graph/resolve.ts
|
|
148
256
|
async function searchEntities(db, type, q, limit = 5) {
|
|
149
257
|
if (!q.trim()) return [];
|
|
@@ -763,6 +871,7 @@ function allJobs(sources) {
|
|
|
763
871
|
// src/state/kv.ts
|
|
764
872
|
var SEEN_TTL_S = 120 * 24 * 60 * 60;
|
|
765
873
|
var ENRICHED_TTL_S = 30 * 24 * 60 * 60;
|
|
874
|
+
var TEMPORAL_TTL_S = 120 * 24 * 60 * 60;
|
|
766
875
|
var Store = class {
|
|
767
876
|
constructor(kv) {
|
|
768
877
|
this.kv = kv;
|
|
@@ -809,6 +918,22 @@ var Store = class {
|
|
|
809
918
|
async setConfigSources(list) {
|
|
810
919
|
await this.kv.put("config:sources", JSON.stringify(list));
|
|
811
920
|
}
|
|
921
|
+
/** Cache the latest temporal report (`temporal:latest`) plus a dated history copy. */
|
|
922
|
+
async setTemporalReport(report, date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
|
|
923
|
+
const raw = JSON.stringify(report);
|
|
924
|
+
await this.kv.put("temporal:latest", raw);
|
|
925
|
+
await this.kv.put(`temporal:${date}`, raw, { expirationTtl: TEMPORAL_TTL_S });
|
|
926
|
+
}
|
|
927
|
+
/** The cached latest temporal report, or null when absent/unparseable. */
|
|
928
|
+
async getTemporalReport() {
|
|
929
|
+
const raw = await this.kv.get("temporal:latest");
|
|
930
|
+
if (!raw) return null;
|
|
931
|
+
try {
|
|
932
|
+
return JSON.parse(raw);
|
|
933
|
+
} catch {
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
812
937
|
};
|
|
813
938
|
|
|
814
939
|
// src/workflow/helpers.ts
|
|
@@ -895,6 +1020,7 @@ export {
|
|
|
895
1020
|
createProvider,
|
|
896
1021
|
emptyUsage,
|
|
897
1022
|
enabledSources,
|
|
1023
|
+
exportFragment,
|
|
898
1024
|
extractJson,
|
|
899
1025
|
extractJsonArray,
|
|
900
1026
|
fetchText,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/ontology.ts","../src/graph/merge.ts","../src/graph/writer.ts","../src/graph/resolve.ts","../src/llm/provider.ts","../src/llm/json.ts","../src/util/content.ts","../src/connectors/rss.ts","../src/connectors/url.ts","../src/connectors/gdelt.ts","../src/util/identity.ts","../src/connectors/fred.ts","../src/connectors/edgar.ts","../src/connectors/govdata.ts","../src/connectors/web-search.ts","../src/connectors/registry.ts","../src/sources.ts","../src/state/kv.ts","../src/workflow/helpers.ts","../src/pipeline/run-items.ts"],"sourcesContent":["import type { Value } from \"@odla-ai/db\";\n\n/**\n * The output of a connector: one acquired item. Exactly one of `text` or\n * `fragment` is set — `text` goes through LLM extraction, `fragment` is already\n * ontology-typed and goes straight to the graph writer (structured feeds).\n */\nexport interface RawItem {\n url: string;\n title: string;\n date?: string;\n sourceName: string;\n sourceKind: string; // connector id: \"url\" | \"rss\" | \"edgar\" | ...\n watch?: string | null;\n text?: string;\n fragment?: GraphFragment;\n}\n\n/** A graph node = an odla-db entity in a namespace, keyed by a natural key. */\nexport interface GraphNode {\n type: string; // namespace, e.g. \"company\"\n key: string; // natural-key value (slug, urlHash, ...) — used for Lookup upsert\n attrs: Record<string, Value>; // node properties (the natural-key attr is added by the writer)\n}\n\n/** A graph edge = an odla-db `link` from one node to another by forward label. */\nexport interface GraphEdge {\n from: { type: string; key: string };\n to: { type: string; key: string };\n label: string; // forward link label, e.g. \"mentions\"\n}\n\n/** The single shape the graph writer consumes, whatever produced it. */\nexport interface GraphFragment {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\n/** What an extractor turns a text RawItem into: a fragment plus a prose summary. */\nexport interface ExtractResult {\n fragment: GraphFragment;\n summary: string;\n}\n\n/** Provider-agnostic LLM usage accounting. */\nexport interface Usage {\n calls: number;\n inputTokens: number;\n outputTokens: number;\n}\n\n/** Create zeroed provider-agnostic usage totals for one pipeline run. */\nexport function emptyUsage(): Usage {\n return { calls: 0, inputTokens: 0, outputTokens: 0 };\n}\n\n/** Mutate an accumulator by adding call, input-token, and output-token totals. */\nexport function addUsage(into: Usage, more: Usage): void {\n into.calls += more.calls;\n into.inputTokens += more.inputTokens;\n into.outputTokens += more.outputTokens;\n}\n","import type { SerializedAttr, SerializedEntity, SerializedLink, SerializedSchema, Cardinality } from \"@odla-ai/db\";\nimport type { NaturalKeys } from \"./graph/writer\";\n\n/** How a scalar attribute is reconciled when a node is re-discovered. */\nexport type MergeRule =\n | \"replace\" // last write wins (default)\n | \"prefer\" // first non-empty value sticks (stable facts like a name)\n | \"earliest\" // keep the smaller value (first_seen)\n | \"latest\"; // keep the larger value (last_seen)\n\nexport type AttrType = SerializedAttr[\"type\"]; // \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\"\n\nexport interface AttrSpec {\n type: AttrType;\n unique?: boolean;\n indexed?: boolean;\n optional?: boolean;\n /** The pipeline NEVER writes this attr (hand-edited in a UI); omitted from every update. */\n human?: boolean;\n /** Provenance rule for re-discovery. Default \"replace\". */\n merge?: MergeRule;\n /** Shown to the LLM when this attr appears in an extraction schema. */\n desc?: string;\n}\n\nexport interface EntitySpec {\n /** Unique attr used for Lookup upsert (MERGE key). Must be `unique` in attrs. */\n naturalKey: string;\n attrs: Record<string, AttrSpec>;\n /** FTS fields for fuzzy resolution (P5). */\n searchable?: string[];\n}\n\nexport interface EdgeSpec {\n from: string; // entity type\n to: string; // entity type\n forwardLabel: string; // link label on the `from` side (what the `link` op carries)\n reverseLabel: string;\n forwardHas?: Cardinality; // default \"many\"\n reverseHas?: Cardinality; // default \"many\"\n}\n\nexport interface Ontology {\n entities: Record<string, EntitySpec>;\n edges: Record<string, EdgeSpec>; // keyed by edge name\n}\n\n/** node type → natural-key attr name (for the graph writer's Lookup refs). */\nexport function naturalKeysOf(o: Ontology): NaturalKeys {\n const m: NaturalKeys = {};\n for (const [type, e] of Object.entries(o.entities)) m[type] = e.naturalKey;\n return m;\n}\n\n/** Attr name → merge rule, for every entity type (used by the provenance writer). */\nexport function mergeRulesOf(o: Ontology): Record<string, Record<string, MergeRule>> {\n const out: Record<string, Record<string, MergeRule>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = {};\n for (const [attr, spec] of Object.entries(e.attrs)) out[type][attr] = spec.merge ?? \"replace\";\n }\n return out;\n}\n\n/** Attr names the pipeline must never write, per entity type. */\nexport function humanAttrsOf(o: Ontology): Record<string, Set<string>> {\n const out: Record<string, Set<string>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = new Set(Object.entries(e.attrs).filter(([, a]) => a.human).map(([n]) => n));\n }\n return out;\n}\n\n/** Compile the ontology to an odla-db SerializedSchema to POST at /app/:id/schema. */\nexport function toSerializedSchema(o: Ontology): SerializedSchema {\n const entities: Record<string, SerializedEntity> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n const attrs: Record<string, SerializedAttr> = {};\n for (const [name, a] of Object.entries(e.attrs)) {\n attrs[name] = {\n type: a.type,\n unique: !!a.unique,\n indexed: !!a.indexed,\n optional: !!a.optional,\n };\n }\n entities[type] = { attrs };\n }\n const links: Record<string, SerializedLink> = {};\n for (const [name, ed] of Object.entries(o.edges)) {\n links[name] = {\n forward: { on: ed.from, has: ed.forwardHas ?? \"many\", label: ed.forwardLabel },\n reverse: { on: ed.to, has: ed.reverseHas ?? \"many\", label: ed.reverseLabel },\n };\n }\n return { entities, links };\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { MergeRule } from \"../ontology\";\n\nfunction isEmpty(v: Value | undefined): boolean {\n return v === undefined || v === null || v === \"\";\n}\n\n/**\n * Reconcile one scalar attribute when a node is re-discovered. Port of agnacl's\n * provenance rules (utils/markdown.ts). Empty incoming never clobbers; empty\n * existing is filled. Ordering rules compare stringwise (ISO dates sort right).\n */\nexport function mergeScalar(\n rule: MergeRule,\n existing: Value | undefined,\n incoming: Value | undefined,\n): Value | undefined {\n if (isEmpty(incoming)) return existing;\n if (isEmpty(existing)) return incoming;\n switch (rule) {\n case \"prefer\":\n return existing; // first non-empty value sticks\n case \"earliest\":\n return String(incoming) < String(existing) ? incoming : existing;\n case \"latest\":\n return String(incoming) > String(existing) ? incoming : existing;\n case \"replace\":\n default:\n return incoming;\n }\n}\n\n/**\n * Merge an incoming attr set into an existing one per the type's rules. `human`\n * attrs are excluded upstream (never in `incoming`); this only reconciles what\n * the pipeline owns. Returns the attrs to write (update leaves unlisted attrs intact).\n */\nexport function mergeAttrs(\n rules: Record<string, MergeRule>,\n existing: Record<string, Value>,\n incoming: Record<string, Value>,\n): Record<string, Value> {\n const out: Record<string, Value> = {};\n for (const [attr, val] of Object.entries(incoming)) {\n const merged = mergeScalar(rules[attr] ?? \"replace\", existing[attr], val);\n if (merged !== undefined) out[attr] = merged;\n }\n return out;\n}\n","import type {\n AdminDb,\n Op,\n EntityRef,\n Value,\n InstaQLQuery,\n QueryResult,\n} from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport {\n naturalKeysOf,\n mergeRulesOf,\n humanAttrsOf,\n type Ontology,\n} from \"../ontology\";\nimport { mergeAttrs } from \"./merge\";\n\n/** node type → unique natural-key attribute name. */\nexport type NaturalKeys = Record<string, string>;\n\n/**\n * Pure fragment → ops mapping (no read/merge). Node upserts by Lookup natural\n * key, then links — nodes always before links so `link` targets resolve.\n * Used by tests and as the building block for the provenance path below.\n */\nexport function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[] {\n const ref = (type: string, key: string): EntityRef => {\n const attr = keys[type];\n if (!attr) throw new Error(`No natural key configured for node type \"${type}\"`);\n return { ns: type, attr, value: key };\n };\n const ops: Op[] = [];\n for (const n of fragment.nodes) {\n const attr = keys[n.type];\n if (!attr) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n ops.push({ t: \"update\", ns: n.type, id: { ns: n.type, attr, value: n.key }, attrs: { ...n.attrs, [attr]: n.key } });\n }\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n return ops;\n}\n\n/**\n * Provenance-preserving write. Reads existing nodes by natural key (one batched\n * query), merges scalar attrs per the ontology's rules, drops human-owned\n * attrs, then writes merged node updates + all links in one atomic, idempotent\n * transaction. `mutationId` must be deterministic so odla-db can deduplicate\n * the write on retry; this does not make preceding reads or external effects\n * exactly-once.\n */\nexport async function writeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n mutationId: string,\n): Promise<{ txId: number; ops: number }> {\n const keys = naturalKeysOf(ontology);\n const rules = mergeRulesOf(ontology);\n const humans = humanAttrsOf(ontology);\n\n // 1. Collapse duplicate nodes within the fragment (same type+key), dropping human attrs.\n const byType = new Map<string, Map<string, Record<string, Value>>>();\n for (const n of fragment.nodes) {\n if (!keys[n.type]) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n const human = humans[n.type] ?? new Set<string>();\n const clean: Record<string, Value> = {};\n for (const [k, v] of Object.entries(n.attrs)) if (!human.has(k)) clean[k] = v;\n const typeMap = byType.get(n.type) ?? new Map<string, Record<string, Value>>();\n const prev = typeMap.get(n.key);\n typeMap.set(n.key, prev ? mergeAttrs(rules[n.type]!, prev, clean) : clean);\n byType.set(n.type, typeMap);\n }\n\n // 2. Batch-read existing rows per type by natural key (one query, filtered per level).\n const query: InstaQLQuery = {};\n for (const [type, m] of byType) {\n query[type] = { $: { where: { [keys[type]!]: { $in: [...m.keys()] } } } };\n }\n const existing: QueryResult = Object.keys(query).length ? await db.query(query) : {};\n const priorOf = (type: string, key: string): Record<string, Value> => {\n const attr = keys[type]!;\n for (const row of existing[type] ?? []) {\n if (row[attr] === key) return row as unknown as Record<string, Value>;\n }\n return {};\n };\n\n // 3. Merged node updates first, then links.\n const ops: Op[] = [];\n for (const [type, m] of byType) {\n const attr = keys[type]!;\n for (const [key, incoming] of m) {\n const merged = mergeAttrs(rules[type]!, priorOf(type, key), incoming);\n ops.push({ t: \"update\", ns: type, id: { ns: type, attr, value: key }, attrs: { ...merged, [attr]: key } });\n }\n }\n const ref = (type: string, key: string): EntityRef => ({ ns: type, attr: keys[type]!, value: key });\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n\n if (ops.length === 0) return { txId: 0, ops: 0 };\n const { txId } = await db.transact(ops, { mutationId });\n return { txId, ops: ops.length };\n}\n","import type { AdminDb, Entity } from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport type { Ontology } from \"../ontology\";\n\n/** FTS5 search over a namespace's text fields (odla-db QueryOpts.search). */\nexport async function searchEntities(db: AdminDb, type: string, q: string, limit = 5): Promise<Entity[]> {\n if (!q.trim()) return [];\n const res = await db.query({ [type]: { $: { search: q, limit } } });\n return (res[type] ?? []) as Entity[];\n}\n\nconst norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, \"\");\n\n/**\n * Resolve a name to an existing entity's natural key via FTS — for merging a\n * re-discovery that slugified differently. Conservative: only returns a hit when\n * the top match's name/key normalizes identically to the query.\n */\nexport async function resolveName(\n db: AdminDb,\n type: string,\n keyAttr: string,\n name: string,\n): Promise<string | null> {\n const target = norm(name);\n if (!target) return null;\n for (const h of await searchEntities(db, type, name, 3)) {\n const key = h[keyAttr];\n const hname = h[\"name\"];\n if (typeof key !== \"string\") continue;\n if ((typeof hname === \"string\" && norm(hname) === target) || norm(key) === target) return key;\n }\n return null;\n}\n\n/**\n * Canonicalize a fragment's searchable nodes against existing entities via FTS,\n * remapping node keys (and the edges that reference them) to the canonical key.\n * Used on the lower-volume discovery/enrich paths, not high-volume ingest.\n */\nexport async function canonicalizeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n): Promise<GraphFragment> {\n const remap = new Map<string, string>(); // `${type}:${key}` -> canonical key\n for (const n of fragment.nodes) {\n const spec = ontology.entities[n.type];\n if (!spec?.searchable?.length) continue;\n const name = typeof n.attrs.name === \"string\" ? n.attrs.name : \"\";\n if (!name) continue;\n const canon = await resolveName(db, n.type, spec.naturalKey, name);\n if (canon && canon !== n.key) remap.set(`${n.type}:${n.key}`, canon);\n }\n if (remap.size === 0) return fragment;\n\n const rk = (type: string, key: string): string => remap.get(`${type}:${key}`) ?? key;\n return {\n nodes: fragment.nodes.map((n) => ({ ...n, key: rk(n.type, n.key) })),\n edges: fragment.edges.map((e) => ({\n from: { type: e.from.type, key: rk(e.from.type, e.from.key) },\n to: { type: e.to.type, key: rk(e.to.type, e.to.key) },\n label: e.label,\n })),\n };\n}\n","import { init, extractText, type Ai } from \"@odla-ai/ai\";\nimport type { Usage } from \"../types\";\n\n/**\n * The LLM seam, now a thin adapter over @odla-ai/ai (which was itself\n * generalized from this package's original Provider). Three verbs stay:\n * extract (forced structured tool call), search (web-search-enabled text),\n * write (plain generation). Callers never see vendor or oracle types.\n */\n\nexport type ProviderFamily = \"claude\" | \"openai\";\n\n/** Role → model tiers per family — config as data. `cheap` is for gate-style\n * calls, `extract` for structured extraction (and prose writing), `search`\n * for web-search-enabled calls. */\nexport const MODEL_TIERS: Record<ProviderFamily, { cheap: string; extract: string; search: string }> = {\n claude: { cheap: \"claude-haiku-4-5\", extract: \"claude-sonnet-5\", search: \"claude-sonnet-5\" },\n openai: { cheap: \"gpt-5-nano\", extract: \"gpt-5-mini\", search: \"gpt-5-search-api\" },\n};\n\n/** A forced structured tool call: schema in, validated object out. */\nexport interface ExtractCall {\n system: string;\n user: string;\n tool: { name: string; description?: string; parameters: Record<string, unknown> };\n model?: string;\n maxTokens?: number;\n}\n\n/** A web-search-enabled call: the model searches and returns text (caller tolerant-parses). */\nexport interface SearchCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\n/** A plain text-generation call (no tools, no search) — e.g. digest prose. */\nexport interface WriteCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\nexport interface Provider {\n id: ProviderFamily;\n /** The underlying @odla-ai/ai client — escape hatch for advanced use. */\n ai: Ai;\n extract<T = unknown>(c: ExtractCall): Promise<{ value: T; usage: Usage }>;\n search(c: SearchCall): Promise<{ text: string; usage: Usage }>;\n write(c: WriteCall): Promise<{ text: string; usage: Usage }>;\n}\n\nexport interface LlmConfig {\n provider: ProviderFamily;\n anthropicKey?: string;\n openaiKey?: string;\n /** Inject a pre-built Ai (tests, custom catalog); keys are ignored when set. */\n ai?: Ai;\n}\n\nfunction toUsage(u: { inputTokens: number; outputTokens: number }): Usage {\n return { calls: 1, inputTokens: u.inputTokens, outputTokens: u.outputTokens };\n}\n\n/** The library primitive: an explicit config in, the three-verb seam out. */\nexport function createProvider(cfg: LlmConfig): Provider {\n const tiers = MODEL_TIERS[cfg.provider];\n const ai = cfg.ai ?? init({ keys: { anthropic: cfg.anthropicKey, openai: cfg.openaiKey } });\n return {\n id: cfg.provider,\n ai,\n async extract<T = unknown>(c: ExtractCall) {\n const { value, usage } = await ai.extract<T>({\n model: c.model ?? tiers.extract,\n system: c.system,\n user: c.user,\n tool: c.tool,\n maxTokens: c.maxTokens,\n });\n return { value, usage: toUsage(usage) };\n },\n async search(c: SearchCall) {\n const { text, usage } = await ai.search({\n model: c.model ?? tiers.search,\n system: c.system,\n user: c.user,\n maxTokens: c.maxTokens,\n });\n return { text, usage: toUsage(usage) };\n },\n async write(c: WriteCall) {\n const res = await ai.chat({\n model: c.model ?? tiers.extract,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n maxTokens: c.maxTokens ?? 2048,\n });\n return { text: extractText(res.content), usage: toUsage(res.usage) };\n },\n };\n}\n\n/** The env vars the provider factory reads — a structural slice, not the\n * worker Env, so any host (worker, script, test) can supply plain strings. */\nexport interface LlmEnv {\n LLM_PROVIDER?: string; // \"claude\" | \"openai\"\n ANTHROPIC_API_KEY?: string;\n OPENAI_API_KEY?: string;\n}\n\n/** Build the configured provider from host env, failing before use when its key is missing. */\nexport function getProvider(env: LlmEnv): Provider {\n const which: ProviderFamily = (env.LLM_PROVIDER || \"claude\").toLowerCase() === \"openai\" ? \"openai\" : \"claude\";\n if (which === \"openai\" && !env.OPENAI_API_KEY) throw new Error(\"LLM_PROVIDER=openai but OPENAI_API_KEY is unset\");\n if (which === \"claude\" && !env.ANTHROPIC_API_KEY) throw new Error(\"LLM_PROVIDER=claude but ANTHROPIC_API_KEY is unset\");\n return createProvider({ provider: which, anthropicKey: env.ANTHROPIC_API_KEY, openaiKey: env.OPENAI_API_KEY });\n}\n","/**\n * Tolerant JSON extraction from LLM prose — web-search models return JSON inside\n * explanatory text. Ported from agnacl services/enrichment.ts.\n */\n\nexport function extractJson<T = unknown>(text: string): T | null {\n const m = text.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]) as T;\n } catch {\n return null;\n }\n}\n\n/** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */\nexport function extractJsonArray<T = unknown>(text: string): T[] {\n const m = text.match(/\\[[\\s\\S]*\\]/);\n if (!m) return [];\n try {\n const v = JSON.parse(m[0]);\n return Array.isArray(v) ? (v as T[]) : [];\n } catch {\n return [];\n }\n}\n","/**\n * Dependency-free HTML → text extraction (Workers-isolate friendly: no DOM/jsdom).\n * Ported from agnacl services/content-extractor.ts.\n */\n\nconst MAX_CHARS = 8000;\n\n/** Strip scripts/styles/tags and collapse whitespace; cap length. */\nexport function htmlToText(html: string, maxChars = MAX_CHARS): string {\n let t = html;\n t = t.replace(/<script[\\s\\S]*?<\\/script>/gi, \" \");\n t = t.replace(/<style[\\s\\S]*?<\\/style>/gi, \" \");\n t = t.replace(/<!--[\\s\\S]*?-->/g, \" \");\n t = t.replace(/<\\/(p|div|section|article|li|h[1-6]|br)>/gi, \"\\n\");\n t = t.replace(/<[^>]+>/g, \" \");\n t = decodeEntities(t);\n t = t.replace(/[ \\t\\f\\r]+/g, \" \").replace(/\\n\\s*\\n\\s*/g, \"\\n\\n\").trim();\n return t.slice(0, maxChars);\n}\n\nfunction decodeEntities(s: string): string {\n return s\n .replace(/ /gi, \" \")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .replace(/&#(\\d+);/g, (_, d) => String.fromCharCode(Number(d)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCharCode(parseInt(h, 16)));\n}\n\n/** Fetch a URL and return readable text. Best-effort: returns \"\" on any failure. */\nexport async function fetchText(url: string, maxChars = MAX_CHARS): Promise<string> {\n try {\n const res = await fetch(url, {\n headers: {\n \"user-agent\": \"odla-kg/0.1 (+https://github.com/cory/odla-kg)\",\n accept: \"text/html,application/xhtml+xml\",\n },\n });\n if (!res.ok) return \"\";\n const ct = res.headers.get(\"content-type\") || \"\";\n const body = await res.text();\n if (ct.includes(\"html\") || /<\\w+[\\s>]/.test(body.slice(0, 200))) {\n return htmlToText(body, maxChars);\n }\n return body.slice(0, maxChars);\n } catch {\n return \"\";\n }\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { htmlToText } from \"../util/content\";\n\n/** Dependency-free RSS/Atom parsing, ported from agnacl services/rss.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\n\nfunction extractTag(xml: string, tag: string): string {\n const cdata = xml.match(new RegExp(`<${tag}[^>]*>\\\\s*<!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)\\\\]\\\\]>\\\\s*</${tag}>`, \"i\"));\n if (cdata) return cdata[1]!.trim();\n const m = xml.match(new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction extractAttr(xml: string, tag: string, attr: string): string {\n const m = xml.match(new RegExp(`<${tag}[^>]*\\\\s${attr}=[\"']([^\"']*)[\"']`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction splitItems(xml: string): string[] {\n return xml.match(/<item[\\s>][\\s\\S]*?<\\/item>/gi) || xml.match(/<entry[\\s>][\\s\\S]*?<\\/entry>/gi) || [];\n}\n\ninterface RssCfg {\n url: string;\n name: string;\n watch?: string | null;\n}\n\nexport const rssConnector: SourceConnector<RssCfg> = {\n id: \"rss\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): RssCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"rss connector requires a `url`\");\n return { url: r.url, name: String(r.name ?? r.url), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n let xml: string;\n try {\n const res = await fetch(cfg.url, { headers: { \"User-Agent\": UA } });\n if (!res.ok) {\n if (res.status === 429 || res.status === 403 || res.status >= 500) return [];\n throw new Error(`feed ${cfg.url} → ${res.status}`);\n }\n xml = await res.text();\n } catch (e) {\n console.log(`rss fetch failed (non-blocking): ${cfg.url}: ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n for (const raw of splitItems(xml)) {\n const title = extractTag(raw, \"title\");\n let url = extractTag(raw, \"link\") || extractAttr(raw, \"link\", \"href\");\n if (!title || !url) continue;\n url = url.trim();\n const date =\n extractTag(raw, \"pubDate\") || extractTag(raw, \"published\") || extractTag(raw, \"updated\") || undefined;\n const content =\n extractTag(raw, \"content:encoded\") ||\n extractTag(raw, \"content\") ||\n extractTag(raw, \"description\") ||\n extractTag(raw, \"summary\") ||\n \"\";\n let text = htmlToText(content);\n // Thin feed body → fetch the article page.\n if (text.length < 200) {\n const page = await ctx.fetchText(url);\n if (page.length > text.length) text = page;\n }\n out.push({ url, title, date, sourceName: cfg.name, sourceKind: \"rss\", watch: cfg.watch, text });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector } from \"./types\";\nimport { fetchText } from \"../util/content\";\n\n/** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */\nexport async function fetchUrl(url: string): Promise<RawItem> {\n const text = await fetchText(url);\n const firstLine = text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\";\n let host = url;\n try {\n host = new URL(url).hostname;\n } catch {\n /* keep raw url as the source label */\n }\n return { url, title: (firstLine || url).slice(0, 200).trim(), sourceName: host, sourceKind: \"url\", text };\n}\n\ninterface UrlCfg {\n url: string;\n watch?: string | null;\n}\n\nexport const urlConnector: SourceConnector<UrlCfg> = {\n id: \"url\",\n capabilities: { fetch: true, structured: false },\n parseConfig(raw): UrlCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"url connector requires a `url`\");\n return { url: r.url, watch: (r.watch as string | null) ?? null };\n },\n async fetch(cfg): Promise<RawItem[]> {\n const item = await fetchUrl(cfg.url);\n if (cfg.watch !== undefined) item.watch = cfg.watch;\n return [item];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/** GDELT DOC 2.0 standing-query connector (text). Ported from agnacl services/gdelt.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\nconst MAX_ITEMS = 15;\n\ninterface GdeltCfg {\n query: string;\n name: string;\n watch?: string | null;\n}\n\nfunction isoFromSeendate(s: string): string | undefined {\n const m = s.match(/^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z?$/);\n return m ? `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}Z` : undefined;\n}\n\nexport const gdeltConnector: SourceConnector<GdeltCfg> = {\n id: \"gdelt\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): GdeltCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.query !== \"string\" || !r.query) throw new Error(\"gdelt connector requires a `query`\");\n return { query: r.query, name: String(r.name ?? `GDELT: ${r.query}`), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const api =\n `https://api.gdeltproject.org/api/v2/doc/doc?query=${encodeURIComponent(cfg.query)}` +\n `&mode=artlist&format=json&maxrecords=40×pan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n // GDELT is a best-effort external source; without a deadline a slow or wedged endpoint hangs\n // the whole workflow run. Bound it and let the catch below treat a timeout like any other miss.\n const res = await fetch(api, { headers: { \"User-Agent\": UA }, signal: AbortSignal.timeout(15_000) });\n if (!res.ok) return [];\n const text = await res.text(); // GDELT sometimes returns text errors with 200\n try {\n articles = (JSON.parse(text) as { articles?: typeof articles }).articles ?? [];\n } catch {\n return [];\n }\n } catch (e) {\n console.log(`gdelt fetch failed (non-blocking): ${e}`);\n return [];\n }\n\n const seen = new Set<string>();\n const picked = articles\n .filter((a) => a?.url && a?.title && a.language === \"English\" && !seen.has(a.url!) && (seen.add(a.url!), true))\n .slice(0, MAX_ITEMS);\n\n const out: RawItem[] = [];\n for (const a of picked) {\n const body = await ctx.fetchText(a.url!); // GDELT gives no body; fetch the article\n if (body.length < 200) continue;\n out.push({\n url: a.url!,\n title: a.title!,\n date: isoFromSeendate(a.seendate ?? \"\"),\n sourceName: cfg.name,\n sourceKind: \"gdelt\",\n watch: cfg.watch,\n text: body,\n });\n }\n return out;\n },\n};\n","/**\n * Identity + dedup helpers. The slug / urlHash a node resolves to IS its\n * natural key for Lookup-based upserts, so these must be stable and pure.\n * Ported from agnacl (utils/markdown.ts slugify, services/kv.ts normalizeUrl).\n */\n\n/** NFKD-normalize, strip accents, lowercase, non-alphanumerics → \"-\", cap 60. */\nexport function slugify(input: string): string {\n return input\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60)\n .replace(/-+$/g, \"\");\n}\n\n/** Drop tracking params, hash, and trailing slash so the same page maps to one key. */\nexport function normalizeUrl(raw: string): string {\n try {\n const u = new URL(raw);\n u.hash = \"\";\n const drop = [/^utm_/i, /^ref$/i, /^ref_/i, /^fbclid$/i, /^gclid$/i, /^mc_/i];\n for (const key of [...u.searchParams.keys()]) {\n if (drop.some((re) => re.test(key))) u.searchParams.delete(key);\n }\n let s = u.toString();\n s = s.replace(/\\/$/, \"\");\n return s;\n } catch {\n return raw.trim().replace(/#.*$/, \"\").replace(/\\/$/, \"\");\n }\n}\n\n/** Stable short key for a URL (FNV-1a 32-bit, base36). Used as the `signal` natural key. */\nexport function urlHash(raw: string): string {\n const s = normalizeUrl(raw);\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).toString(36);\n}\n\n/**\n * Reject descriptive phrases masquerading as people (\"three craftsmen\",\n * \"the founders\"). A real name is 2-4 capitalized tokens, no digits.\n * Ported from agnacl utils/names.ts (isLikelyPersonName).\n */\nexport function isLikelyPersonName(name: string): boolean {\n const n = name.trim();\n if (!n || /\\d/.test(n)) return false;\n const parts = n.split(/\\s+/);\n if (parts.length < 2 || parts.length > 4) return false;\n return parts.every((p) => /^[A-Z][a-zA-Z'’.-]*$/.test(p));\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify } from \"../util/identity\";\n\n/**\n * FRED economic indicators — a STRUCTURED connector: it maps series directly to\n * graph nodes (indicator + region) and emits RawItem.fragment, so the ingest\n * workflow writes it without any LLM extraction. Ported from agnacl services/fred.ts.\n */\ninterface FredCfg {\n series: Array<{ id: string; label: string; unit?: string }>;\n region?: string;\n regionName?: string;\n}\n\nasync function fetchSeries(\n seriesId: string,\n apiKey: string,\n start: string,\n): Promise<Array<{ date: string; value: number }>> {\n const url =\n `https://api.stlouisfed.org/fred/series/observations?series_id=${encodeURIComponent(seriesId)}` +\n `&api_key=${encodeURIComponent(apiKey)}&file_type=json&observation_start=${encodeURIComponent(start)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) return [];\n const data = (await res.json()) as { observations?: Array<{ date?: string; value?: string }> };\n const out: Array<{ date: string; value: number }> = [];\n for (const o of data.observations ?? []) {\n if (!o?.date || o.value === undefined || o.value === \".\") continue;\n const v = Number(o.value);\n if (Number.isFinite(v)) out.push({ date: o.date, value: v });\n }\n return out;\n } catch (e) {\n console.log(`fred fetch failed for ${seriesId} (non-blocking): ${e}`);\n return [];\n }\n}\n\nexport const fredConnector: SourceConnector<FredCfg> = {\n id: \"fred\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): FredCfg {\n const r = raw as Record<string, unknown>;\n const series = Array.isArray(r.series) ? (r.series as FredCfg[\"series\"]) : [];\n if (!series.length) throw new Error(\"fred connector requires a non-empty `series`\");\n return { series, region: r.region as string | undefined, regionName: r.regionName as string | undefined };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const apiKey = ctx.secret(\"FRED_API_KEY\");\n if (!apiKey) {\n console.log(\"fred: FRED_API_KEY unset — skipping\");\n return [];\n }\n const start = new Date(ctx.now.getTime() - 400 * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const out: RawItem[] = [];\n\n for (const s of cfg.series) {\n const obs = await fetchSeries(s.id, apiKey, start);\n if (!obs.length) continue;\n const latest = obs[obs.length - 1]!;\n const prior = obs.length > 1 ? obs[obs.length - 2] : undefined;\n\n const attrs: Record<string, Value> = { label: s.label, latest: String(latest.value), as_of: latest.date };\n if (prior) attrs.prior = String(prior.value);\n if (s.unit) attrs.unit = s.unit;\n\n const fragment: GraphFragment = { nodes: [{ type: \"indicator\", key: s.id, attrs }], edges: [] };\n if (cfg.region) {\n const rslug = slugify(cfg.region);\n if (rslug) {\n fragment.nodes.push({ type: \"region\", key: rslug, attrs: { name: cfg.regionName ?? cfg.region } });\n fragment.edges.push({ from: { type: \"region\", key: rslug }, to: { type: \"indicator\", key: s.id }, label: \"has_indicator\" });\n }\n }\n\n out.push({\n url: `https://fred.stlouisfed.org/series/${s.id}#${latest.date}`,\n title: `${s.label}: ${latest.value} (${latest.date})`,\n date: latest.date,\n sourceName: \"FRED\",\n sourceKind: \"fred\",\n fragment,\n });\n }\n return out;\n },\n};\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify, isLikelyPersonName } from \"../util/identity\";\n\n/**\n * SEC EDGAR Form D — a STRUCTURED connector (RawItem.fragment, no LLM). A lean\n * port of agnacl services/edgar.ts: first page of Form D filings for the given\n * states, each mapped to company + funding_round (+ people, region). SEC\n * requires an identifying User-Agent (env.EDGAR_USER_AGENT) on every request.\n */\nconst SEARCH_BASE = \"https://efts.sec.gov/LATEST/search-index\";\nconst MAX_FILINGS = 10;\n\ninterface EdgarCfg {\n locationCodes: string[];\n cityFilter?: string[];\n days?: number;\n}\n\nconst cikTrim = (cik: string): string => cik.replace(/^0+/, \"\") || cik;\nconst indexUrl = (cik: string, adsh: string): string =>\n `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/${adsh}-index.htm`;\n\nfunction tagText(scope: string, tag: string): string {\n const m = scope.match(new RegExp(`<${tag}[^>]*>\\\\s*([\\\\s\\\\S]*?)\\\\s*</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\ninterface EdgarHit {\n _source?: {\n adsh?: string;\n ciks?: string[];\n display_names?: string[];\n file_date?: string;\n biz_states?: string[];\n biz_locations?: string[];\n };\n}\n\nexport const edgarConnector: SourceConnector<EdgarCfg> = {\n id: \"edgar\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): EdgarCfg {\n const r = raw as Record<string, unknown>;\n const codes = Array.isArray(r.locationCodes) ? (r.locationCodes as string[]) : [];\n if (!codes.length) throw new Error('edgar connector requires `locationCodes` (e.g. [\"UT\"])');\n return {\n locationCodes: codes,\n cityFilter: Array.isArray(r.cityFilter) ? (r.cityFilter as string[]) : undefined,\n days: typeof r.days === \"number\" ? r.days : 7,\n };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const ua = ctx.secret(\"EDGAR_USER_AGENT\");\n if (!ua) {\n console.log(\"edgar: EDGAR_USER_AGENT unset — skipping\");\n return [];\n }\n const enddt = ctx.now.toISOString().slice(0, 10);\n const startdt = new Date(ctx.now.getTime() - (cfg.days ?? 7) * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const searchUrl =\n `${SEARCH_BASE}?q=&forms=D&locationCodes=${encodeURIComponent(cfg.locationCodes.join(\",\"))}` +\n `&startdt=${startdt}&enddt=${enddt}`;\n\n let hits: EdgarHit[] = [];\n try {\n const res = await fetch(searchUrl, { headers: { \"User-Agent\": ua } });\n if (!res.ok) return [];\n hits = ((await res.json()) as { hits?: { hits?: EdgarHit[] } }).hits?.hits ?? [];\n } catch (e) {\n console.log(`edgar search failed (non-blocking): ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n const seen = new Set<string>();\n for (const hit of hits.slice(0, MAX_FILINGS)) {\n const src = hit._source ?? {};\n const adsh = src.adsh ?? \"\";\n if (!adsh || seen.has(adsh)) continue;\n seen.add(adsh);\n const cik = src.ciks?.[0] ?? \"\";\n const name = (src.display_names?.[0] ?? \"\").replace(/\\s*\\(CIK\\s*\\d+\\)\\s*$/i, \"\").trim();\n if (!name || !cik) continue;\n\n const loc = src.biz_locations?.[0] ?? \"\";\n let city = loc.trim();\n let state = src.biz_states?.[0] ?? \"\";\n const comma = loc.lastIndexOf(\",\");\n if (comma >= 0) {\n city = loc.slice(0, comma).trim();\n state = loc.slice(comma + 1).trim() || state;\n }\n if (cfg.cityFilter?.length && !cfg.cityFilter.some((c) => c.toLowerCase() === city.toLowerCase())) continue;\n\n const url = indexUrl(cik, adsh);\n const fileDate = src.file_date ?? \"\";\n\n // Filing detail (offering amount, industry, related persons)\n let industry = \"\";\n let offering = \"\";\n const persons: Array<{ name: string; roles: string[] }> = [];\n try {\n const detailUrl = `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/primary_doc.xml`;\n const dres = await fetch(detailUrl, { headers: { \"User-Agent\": ua } });\n if (dres.ok) {\n const xml = await dres.text();\n industry = tagText(xml, \"industryGroupType\");\n offering = tagText(xml, \"totalOfferingAmount\");\n for (const block of xml.match(/<relatedPersonInfo>[\\s\\S]*?<\\/relatedPersonInfo>/gi) ?? []) {\n const pname = [tagText(block, \"firstName\"), tagText(block, \"lastName\")]\n .filter((p) => p && p !== \"-\")\n .join(\" \")\n .trim();\n if (!pname) continue;\n const roles: string[] = [];\n const relRe = /<relationship>\\s*([\\s\\S]*?)\\s*<\\/relationship>/gi;\n let m: RegExpExecArray | null;\n while ((m = relRe.exec(block)) !== null) {\n const r = m[1]!.trim();\n if (r && !roles.includes(r)) roles.push(r);\n }\n persons.push({ name: pname, roles });\n }\n }\n } catch {\n /* fall back to search-hit metadata */\n }\n\n const cslug = slugify(name);\n if (!cslug) continue;\n const f: GraphFragment = { nodes: [], edges: [] };\n const cattrs: Record<string, Value> = { name };\n if (city) cattrs.city = city;\n if (state) cattrs.region = state;\n if (industry) cattrs.sector = industry;\n if (fileDate) {\n cattrs.first_seen = fileDate;\n cattrs.last_seen = fileDate;\n }\n f.nodes.push({ type: \"company\", key: cslug, attrs: cattrs });\n\n if (state) {\n const rslug = slugify(state);\n if (rslug) {\n f.nodes.push({ type: \"region\", key: rslug, attrs: { name: state } });\n f.edges.push({ from: { type: \"company\", key: cslug }, to: { type: \"region\", key: rslug }, label: \"located_in\" });\n }\n }\n\n const rkey = `${cslug}:form-d:${fileDate}`;\n const rattrs: Record<string, Value> = { round: \"Form D\", source_url: url };\n if (offering) rattrs.amount = offering;\n if (fileDate) rattrs.date = fileDate;\n f.nodes.push({ type: \"funding_round\", key: rkey, attrs: rattrs });\n f.edges.push({ from: { type: \"funding_round\", key: rkey }, to: { type: \"company\", key: cslug }, label: \"round_of\" });\n\n for (const p of persons) {\n if (!isLikelyPersonName(p.name)) continue;\n const pslug = slugify(p.name);\n if (!pslug) continue;\n const pattrs: Record<string, Value> = { name: p.name };\n if (p.roles.length) pattrs.role = p.roles.join(\", \");\n f.nodes.push({ type: \"person\", key: pslug, attrs: pattrs });\n f.edges.push({ from: { type: \"person\", key: pslug }, to: { type: \"company\", key: cslug }, label: \"works_at\" });\n }\n\n out.push({\n url,\n title: `Form D: ${name}${offering ? ` — ${offering}` : \"\"}`,\n date: fileDate || undefined,\n sourceName: \"SEC EDGAR\",\n sourceKind: \"edgar\",\n fragment: f,\n });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/**\n * TEMPLATE — a government / open-data connector.\n *\n * This exists to demonstrate the extensibility contract: adding a new source is\n * a self-contained module implementing SourceConnector + ONE line in the\n * registry + config entries in config/sources.ts. The cron, ingest workflow,\n * extractor, and graph writer are untouched — they only ever speak RawItem /\n * GraphFragment.\n *\n * To make it real, fetch your dataset and return either:\n * - text-bearing RawItems ({ ...meta, text }) → the LLM extracts a graph\n * - fragment-bearing RawItems ({ ...meta, fragment }) → straight to the writer\n * (map records to ontology nodes/edges yourself — see fred.ts / edgar.ts).\n * Returns [] until implemented, so wiring it up is harmless.\n */\ninterface GovDataCfg {\n dataset: string;\n watch?: string | null;\n}\n\nexport const govDataConnector: SourceConnector<GovDataCfg> = {\n id: \"gov-data\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): GovDataCfg {\n const r = raw as Record<string, unknown>;\n return { dataset: String(r.dataset ?? \"\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, _ctx: ConnectorContext): Promise<RawItem[]> {\n console.log(`gov-data: template connector for dataset \"${cfg.dataset}\" — implement fetch() to emit RawItems`);\n return [];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { urlHash } from \"../util/identity\";\n\n/**\n * LLM-native web search — a DISCOVERY connector. Each standing query is run\n * through the provider's web-search tool; the prose result becomes a text\n * RawItem that flows through the normal extractor (so discovered companies land\n * in the graph the same way feed items do). Discovery-only: fetch() is a no-op.\n */\ninterface WebSearchCfg {\n queries: string[];\n name: string;\n watch?: string | null;\n}\n\nconst SWEEP_SYSTEM =\n \"You are a research scout building a knowledge graph of entrepreneurs, startups, and business trends. Search the web and report concrete, real companies and the people leading them, with specifics (what they do, where, who founded them). Tie every claim to something you actually found — never invent names, numbers, or companies. If a search turns up nothing solid, say so plainly.\";\n\nexport const webSearchConnector: SourceConnector<WebSearchCfg> = {\n id: \"web-search\",\n capabilities: { fetch: false, discover: true, structured: false },\n\n parseConfig(raw): WebSearchCfg {\n const r = raw as Record<string, unknown>;\n const queries = Array.isArray(r.queries)\n ? (r.queries.filter((q) => typeof q === \"string\") as string[])\n : typeof r.query === \"string\"\n ? [r.query]\n : [];\n if (!queries.length) throw new Error(\"web-search connector requires `queries` (or a `query`)\");\n return { queries, name: String(r.name ?? \"web-search\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(): Promise<RawItem[]> {\n return []; // discovery-only\n },\n\n async discover(cfg, ctx: ConnectorContext): Promise<RawItem[]> {\n const provider = ctx.provider();\n const today = ctx.now.toISOString().slice(0, 10);\n const out: RawItem[] = [];\n for (const q of cfg.queries) {\n const { text } = await provider.search({\n system: SWEEP_SYSTEM,\n user: `Search task: ${q}\\nToday's date: ${today}\\n\\nReport what you find, naming specific companies and the real people leading them.`,\n });\n if (text.trim().length < 80) continue;\n out.push({\n url: `search://web-search/${urlHash(q)}#${today}`, // synthetic; changes daily so re-sweeps aren't deduped away\n title: q,\n date: today,\n sourceName: cfg.name,\n sourceKind: \"web-search\",\n watch: cfg.watch,\n text,\n });\n }\n return out;\n },\n};\n","import type { SourceConnector, ConnectorContext } from \"./types\";\nimport type { Ontology } from \"../ontology\";\nimport type { Provider } from \"../llm/provider\";\nimport { fetchText } from \"../util/content\";\nimport { rssConnector } from \"./rss\";\nimport { urlConnector } from \"./url\";\nimport { gdeltConnector } from \"./gdelt\";\nimport { fredConnector } from \"./fred\";\nimport { edgarConnector } from \"./edgar\";\nimport { govDataConnector } from \"./govdata\";\nimport { webSearchConnector } from \"./web-search\";\n\n/**\n * The connector registry. Adding a source (RSS, a government feed, a new API)\n * is: implement SourceConnector in a module, add one line here, add config\n * entries to config/sources.ts. The cron, workflow, extractor, and writer are\n * untouched — they only speak RawItem / GraphFragment.\n */\nconst REGISTRY = new Map<string, SourceConnector>([\n [rssConnector.id, rssConnector as SourceConnector],\n [urlConnector.id, urlConnector as SourceConnector],\n [gdeltConnector.id, gdeltConnector as SourceConnector],\n [fredConnector.id, fredConnector as SourceConnector], // structured (RawItem.fragment)\n [edgarConnector.id, edgarConnector as SourceConnector], // structured (RawItem.fragment)\n [webSearchConnector.id, webSearchConnector as SourceConnector], // discovery (web search)\n [govDataConnector.id, govDataConnector as SourceConnector], // template — zero core change to add\n]);\n\n/** Resolve a built-in source connector by its stable registry id. */\nexport function getConnector(id: string): SourceConnector | undefined {\n return REGISTRY.get(id);\n}\n\n/** List registered connector ids in deterministic insertion order. */\nexport function connectorIds(): string[] {\n return [...REGISTRY.keys()];\n}\n\n/** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */\nexport interface ContextConfig {\n ontology: Ontology;\n provider: () => Provider;\n /** Named secrets (API keys, user agents) — e.g. the worker Env, or a plain object. */\n secrets?: Record<string, string | undefined>;\n now?: Date;\n}\n\n/** Create a host-supplied connector context with lazy provider construction. */\nexport function makeContext(cfg: ContextConfig): ConnectorContext {\n let provider: Provider | undefined; // lazy: only discovery connectors need it\n return {\n now: cfg.now ?? new Date(),\n fetchText: (u) => fetchText(u),\n secret: (name) => cfg.secrets?.[name],\n ontology: cfg.ontology,\n provider: () => (provider ??= cfg.provider()),\n };\n}\n","import type { ConnectorJob } from \"./connectors/types\";\n\n/**\n * Sources are DATA, not code. Each entry names a connector (`kind`) + its config\n * + scheduling. Add a feed → add an entry; no redeploy of the pipeline. P5 moves\n * this into KV behind a /config admin endpoint so edits don't need a deploy at all.\n */\nexport interface SourceEntry {\n kind: string; // connector id (registry key)\n name: string;\n enabled?: boolean; // default true\n cadenceHours?: number; // default 1 — runs when UTC hour % cadence === 0\n watch?: string | null;\n // connector-specific config (rss: url; more in P3):\n url?: string;\n query?: string;\n [key: string]: unknown;\n}\n\n/** Return source entries not explicitly disabled, preserving configured order. */\nexport function enabledSources(sources: SourceEntry[]): SourceEntry[] {\n return sources.filter((s) => s.enabled !== false);\n}\n\nfunction toJob(s: SourceEntry): ConnectorJob {\n return { kind: s.kind, name: s.name, watch: s.watch ?? null, cfg: s };\n}\n\n/** Enabled sources whose cadence lands on this UTC hour. */\nexport function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[] {\n return enabledSources(sources)\n .filter((s) => hour % Math.max(1, Math.floor(s.cadenceHours ?? 1)) === 0)\n .map(toJob);\n}\n\n/** Every enabled source, ignoring cadence (manual /trigger?kind=sources). */\nexport function allJobs(sources: SourceEntry[]): ConnectorJob[] {\n return enabledSources(sources).map(toJob);\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceEntry } from \"../sources\";\nimport { urlHash } from \"../util/identity\";\n\nconst SEEN_TTL_S = 120 * 24 * 60 * 60; // 120 days\nconst ENRICHED_TTL_S = 30 * 24 * 60 * 60; // 30-day enrichment cooldown\n\n/**\n * The slice of a KV store the pipeline needs. Cloudflare's KVNamespace\n * satisfies it structurally; tests pass an in-memory map. Keeping the library\n * off Workers types is what lets it publish as a plain isomorphic package.\n */\nexport interface KVLike {\n get(key: string): Promise<string | null>;\n put(key: string, value: string, opts?: { expirationTtl?: number }): Promise<void>;\n}\n\n/**\n * Operational state in KV (never the graph itself — that lives in odla-db).\n * Ported/trimmed from agnacl services/kv.ts: seen-URL dedup + lastrun stamps.\n */\nexport class Store {\n constructor(private kv: KVLike) {}\n\n private seenKey(url: string): string {\n return `seen:${urlHash(url)}`;\n }\n\n async hasSeen(url: string): Promise<boolean> {\n return (await this.kv.get(this.seenKey(url))) !== null;\n }\n\n async markSeen(url: string): Promise<void> {\n await this.kv.put(this.seenKey(url), \"1\", { expirationTtl: SEEN_TTL_S });\n }\n\n /** Drop items whose URL we've already processed (checked BEFORE any LLM work). */\n async filterNew(items: RawItem[]): Promise<RawItem[]> {\n const flags = await Promise.all(items.map((it) => this.hasSeen(it.url)));\n return items.filter((_, i) => !flags[i]);\n }\n\n async setLastRun(kind: string, at = new Date().toISOString()): Promise<void> {\n await this.kv.put(`lastrun:${kind}`, at);\n }\n\n async getLastRun(kind: string): Promise<string | null> {\n return this.kv.get(`lastrun:${kind}`);\n }\n\n /** Per-entity enrichment cooldown so we don't re-search the same node constantly. */\n async isEnrichCooling(key: string): Promise<boolean> {\n return (await this.kv.get(`enriched:${key}`)) !== null;\n }\n\n async markEnriched(key: string): Promise<void> {\n await this.kv.put(`enriched:${key}`, new Date().toISOString(), { expirationTtl: ENRICHED_TTL_S });\n }\n\n /** Runtime source config (edit without redeploy). null = fall back to the static SOURCES. */\n async getConfigSources(): Promise<SourceEntry[] | null> {\n const raw = await this.kv.get(\"config:sources\");\n if (!raw) return null;\n try {\n const v = JSON.parse(raw);\n return Array.isArray(v) ? (v as SourceEntry[]) : null;\n } catch {\n return null;\n }\n }\n\n async setConfigSources(list: SourceEntry[]): Promise<void> {\n await this.kv.put(\"config:sources\", JSON.stringify(list));\n }\n}\n","/**\n * Cloudflare Workflow hardening — ported verbatim from agnacl\n * (utils/workflow-helpers.ts). resilientStep adds structured error logging\n * without changing retry semantics; the step configs replace the engine's slow\n * default retry with fast exponential backoff.\n */\n\nexport async function resilientStep<T>(stepName: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const stack = err instanceof Error ? err.stack : undefined;\n console.error(`[STEP_ERROR] ${stepName}: ${msg}`);\n if (stack) console.error(`[STEP_STACK] ${stepName}: ${stack}`);\n throw err;\n }\n}\n\n/** I/O-bound steps: KV reads, feed fetches. */\nexport const IO_STEP_CONFIG = {\n retries: { limit: 3, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"2 minutes\" as const,\n};\n\n/** Graph write steps. */\nexport const COMMIT_STEP_CONFIG = {\n retries: { limit: 3, delay: \"3 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"90 seconds\" as const,\n};\n\n/** LLM generation steps (can be slow). */\nexport const LLM_STEP_CONFIG = {\n retries: { limit: 2, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"3 minutes\" as const,\n};\n\n/** Retry/timeout shape of the step configs above. */\nexport interface StepConfig {\n retries?: { limit: number; delay: string; backoff?: string };\n timeout?: string;\n}\n\n/**\n * Structural view of Cloudflare's WorkflowStep. WorkflowStep.do constrains its\n * result to Rpc.Serializable<T>, which cannot terminate (TS2589) on the\n * recursive Value type now that the @odla/* workspace packages give\n * @odla-ai/db's re-exports real definitions. A live WorkflowStep satisfies\n * this shape at runtime; widen through asStepLike at the workflow boundary.\n */\nexport interface StepLike {\n do<T>(name: string, fn: () => Promise<T>): Promise<T>;\n do<T>(name: string, config: StepConfig, fn: () => Promise<T>): Promise<T>;\n}\n\n/** Widen a WorkflowStep to StepLike (type-level only, no runtime cost). */\nexport function asStepLike(step: unknown): StepLike {\n return step as StepLike;\n}\n","import { LLM_STEP_CONFIG, type StepLike, resilientStep } from \"../workflow/helpers\";\nimport type { ExtractResult, RawItem } from \"../types\";\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { Store } from \"../state/kv\";\nimport type { Provider } from \"../llm/provider\";\nimport type { Ontology } from \"../ontology\";\nimport { writeFragment } from \"../graph/writer\";\nimport { canonicalizeFragment } from \"../graph/resolve\";\nimport { urlHash } from \"../util/identity\";\n\n/** Everything the shared ingest loop needs, supplied by the host. The\n * extractor is the ontology's domain craft (e.g. the OSINT extractor); the\n * library stays ontology-agnostic. */\nexport interface PipelineDeps {\n db: AdminDb;\n provider: Provider;\n ontology: Ontology;\n extract: (provider: Provider, item: RawItem) => Promise<ExtractResult | null>;\n store: Store;\n}\n\n/**\n * Shared per-item processing: each RawItem is its own step (non-blocking,\n * replay-cached). Structured items (fragment) skip the LLM; text items extract.\n * `resolve` FTS-canonicalizes new nodes against existing ones (discovery path).\n * Used by both the ingest (fetch) and discover (web-search) workflows.\n */\nexport async function ingestFresh(\n step: StepLike,\n deps: PipelineDeps,\n fresh: RawItem[],\n kind: string,\n resolve = false,\n): Promise<void> {\n const { db, provider, ontology, extract, store } = deps;\n if (fresh.length === 0) {\n await store.setLastRun(kind);\n return;\n }\n\n for (const item of fresh) {\n const id = urlHash(item.url);\n await step.do(`item-${id}`, LLM_STEP_CONFIG, () =>\n resilientStep(`item-${id}`, async () => {\n try {\n let fragment = item.fragment;\n if (!fragment) {\n const r = await extract(provider, item);\n if (!r) {\n await store.markSeen(item.url);\n return { skipped: true };\n }\n fragment = r.fragment;\n }\n if (resolve) fragment = await canonicalizeFragment(db, fragment, ontology);\n const { txId, ops } = await writeFragment(db, fragment, ontology, `ingest:${id}:v1`);\n await store.markSeen(item.url);\n return { txId, ops };\n } catch (e) {\n console.log(`ingest item failed (non-blocking): ${item.url}: ${e instanceof Error ? e.message : e}`);\n return { error: true };\n }\n }),\n );\n }\n\n await step.do(`stamp-${kind}`, () =>\n resilientStep(`stamp-${kind}`, async () => {\n await store.setLastRun(kind);\n return { ok: true };\n }),\n );\n}\n"],"mappings":"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","names":["UA"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/ontology.ts","../src/graph/merge.ts","../src/graph/writer.ts","../src/graph/export.ts","../src/graph/resolve.ts","../src/llm/provider.ts","../src/llm/json.ts","../src/util/content.ts","../src/connectors/rss.ts","../src/connectors/url.ts","../src/connectors/gdelt.ts","../src/util/identity.ts","../src/connectors/fred.ts","../src/connectors/edgar.ts","../src/connectors/govdata.ts","../src/connectors/web-search.ts","../src/connectors/registry.ts","../src/sources.ts","../src/state/kv.ts","../src/workflow/helpers.ts","../src/pipeline/run-items.ts"],"sourcesContent":["import type { Value } from \"@odla-ai/db\";\n\n/**\n * The output of a connector: one acquired item. Exactly one of `text` or\n * `fragment` is set — `text` goes through LLM extraction, `fragment` is already\n * ontology-typed and goes straight to the graph writer (structured feeds).\n */\nexport interface RawItem {\n url: string;\n title: string;\n date?: string;\n sourceName: string;\n sourceKind: string; // connector id: \"url\" | \"rss\" | \"edgar\" | ...\n watch?: string | null;\n text?: string;\n fragment?: GraphFragment;\n}\n\n/** A graph node = an odla-db entity in a namespace, keyed by a natural key. */\nexport interface GraphNode {\n type: string; // namespace, e.g. \"company\"\n key: string; // natural-key value (slug, urlHash, ...) — used for Lookup upsert\n attrs: Record<string, Value>; // node properties (the natural-key attr is added by the writer)\n}\n\n/** A graph edge = an odla-db `link` from one node to another by forward label. */\nexport interface GraphEdge {\n from: { type: string; key: string };\n to: { type: string; key: string };\n label: string; // forward link label, e.g. \"mentions\"\n}\n\n/** The single shape the graph writer consumes, whatever produced it. */\nexport interface GraphFragment {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\n/** What an extractor turns a text RawItem into: a fragment plus a prose summary. */\nexport interface ExtractResult {\n fragment: GraphFragment;\n summary: string;\n}\n\n/** Provider-agnostic LLM usage accounting. */\nexport interface Usage {\n calls: number;\n inputTokens: number;\n outputTokens: number;\n}\n\n/** Create zeroed provider-agnostic usage totals for one pipeline run. */\nexport function emptyUsage(): Usage {\n return { calls: 0, inputTokens: 0, outputTokens: 0 };\n}\n\n/** Mutate an accumulator by adding call, input-token, and output-token totals. */\nexport function addUsage(into: Usage, more: Usage): void {\n into.calls += more.calls;\n into.inputTokens += more.inputTokens;\n into.outputTokens += more.outputTokens;\n}\n","import type { SerializedAttr, SerializedEntity, SerializedLink, SerializedSchema, Cardinality } from \"@odla-ai/db\";\nimport type { NaturalKeys } from \"./graph/writer\";\n\n/** How a scalar attribute is reconciled when a node is re-discovered. */\nexport type MergeRule =\n | \"replace\" // last write wins (default)\n | \"prefer\" // first non-empty value sticks (stable facts like a name)\n | \"earliest\" // keep the smaller value (first_seen)\n | \"latest\"; // keep the larger value (last_seen)\n\nexport type AttrType = SerializedAttr[\"type\"]; // \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\"\n\nexport interface AttrSpec {\n type: AttrType;\n unique?: boolean;\n indexed?: boolean;\n optional?: boolean;\n /** The pipeline NEVER writes this attr (hand-edited in a UI); omitted from every update. */\n human?: boolean;\n /** Provenance rule for re-discovery. Default \"replace\". */\n merge?: MergeRule;\n /** Shown to the LLM when this attr appears in an extraction schema. */\n desc?: string;\n}\n\nexport interface EntitySpec {\n /** Unique attr used for Lookup upsert (MERGE key). Must be `unique` in attrs. */\n naturalKey: string;\n attrs: Record<string, AttrSpec>;\n /** FTS fields for fuzzy resolution (P5). */\n searchable?: string[];\n}\n\nexport interface EdgeSpec {\n from: string; // entity type\n to: string; // entity type\n forwardLabel: string; // link label on the `from` side (what the `link` op carries)\n reverseLabel: string;\n forwardHas?: Cardinality; // default \"many\"\n reverseHas?: Cardinality; // default \"many\"\n}\n\nexport interface Ontology {\n entities: Record<string, EntitySpec>;\n edges: Record<string, EdgeSpec>; // keyed by edge name\n}\n\n/** node type → natural-key attr name (for the graph writer's Lookup refs). */\nexport function naturalKeysOf(o: Ontology): NaturalKeys {\n const m: NaturalKeys = {};\n for (const [type, e] of Object.entries(o.entities)) m[type] = e.naturalKey;\n return m;\n}\n\n/** Attr name → merge rule, for every entity type (used by the provenance writer). */\nexport function mergeRulesOf(o: Ontology): Record<string, Record<string, MergeRule>> {\n const out: Record<string, Record<string, MergeRule>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = {};\n for (const [attr, spec] of Object.entries(e.attrs)) out[type][attr] = spec.merge ?? \"replace\";\n }\n return out;\n}\n\n/** Attr names the pipeline must never write, per entity type. */\nexport function humanAttrsOf(o: Ontology): Record<string, Set<string>> {\n const out: Record<string, Set<string>> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n out[type] = new Set(Object.entries(e.attrs).filter(([, a]) => a.human).map(([n]) => n));\n }\n return out;\n}\n\n/** Compile the ontology to an odla-db SerializedSchema to POST at /app/:id/schema. */\nexport function toSerializedSchema(o: Ontology): SerializedSchema {\n const entities: Record<string, SerializedEntity> = {};\n for (const [type, e] of Object.entries(o.entities)) {\n const attrs: Record<string, SerializedAttr> = {};\n for (const [name, a] of Object.entries(e.attrs)) {\n attrs[name] = {\n type: a.type,\n unique: !!a.unique,\n indexed: !!a.indexed,\n optional: !!a.optional,\n };\n }\n entities[type] = { attrs };\n }\n const links: Record<string, SerializedLink> = {};\n for (const [name, ed] of Object.entries(o.edges)) {\n links[name] = {\n forward: { on: ed.from, has: ed.forwardHas ?? \"many\", label: ed.forwardLabel },\n reverse: { on: ed.to, has: ed.reverseHas ?? \"many\", label: ed.reverseLabel },\n };\n }\n return { entities, links };\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { MergeRule } from \"../ontology\";\n\nfunction isEmpty(v: Value | undefined): boolean {\n return v === undefined || v === null || v === \"\";\n}\n\n/**\n * Reconcile one scalar attribute when a node is re-discovered. Port of agnacl's\n * provenance rules (utils/markdown.ts). Empty incoming never clobbers; empty\n * existing is filled. Ordering rules compare stringwise (ISO dates sort right).\n */\nexport function mergeScalar(\n rule: MergeRule,\n existing: Value | undefined,\n incoming: Value | undefined,\n): Value | undefined {\n if (isEmpty(incoming)) return existing;\n if (isEmpty(existing)) return incoming;\n switch (rule) {\n case \"prefer\":\n return existing; // first non-empty value sticks\n case \"earliest\":\n return String(incoming) < String(existing) ? incoming : existing;\n case \"latest\":\n return String(incoming) > String(existing) ? incoming : existing;\n case \"replace\":\n default:\n return incoming;\n }\n}\n\n/**\n * Merge an incoming attr set into an existing one per the type's rules. `human`\n * attrs are excluded upstream (never in `incoming`); this only reconciles what\n * the pipeline owns. Returns the attrs to write (update leaves unlisted attrs intact).\n */\nexport function mergeAttrs(\n rules: Record<string, MergeRule>,\n existing: Record<string, Value>,\n incoming: Record<string, Value>,\n): Record<string, Value> {\n const out: Record<string, Value> = {};\n for (const [attr, val] of Object.entries(incoming)) {\n const merged = mergeScalar(rules[attr] ?? \"replace\", existing[attr], val);\n if (merged !== undefined) out[attr] = merged;\n }\n return out;\n}\n","import type {\n AdminDb,\n Op,\n EntityRef,\n Value,\n InstaQLQuery,\n QueryResult,\n} from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport {\n naturalKeysOf,\n mergeRulesOf,\n humanAttrsOf,\n type Ontology,\n} from \"../ontology\";\nimport { mergeAttrs } from \"./merge\";\n\n/** node type → unique natural-key attribute name. */\nexport type NaturalKeys = Record<string, string>;\n\n/**\n * Pure fragment → ops mapping (no read/merge). Node upserts by Lookup natural\n * key, then links — nodes always before links so `link` targets resolve.\n * Used by tests and as the building block for the provenance path below.\n */\nexport function fragmentToOps(fragment: GraphFragment, keys: NaturalKeys): Op[] {\n const ref = (type: string, key: string): EntityRef => {\n const attr = keys[type];\n if (!attr) throw new Error(`No natural key configured for node type \"${type}\"`);\n return { ns: type, attr, value: key };\n };\n const ops: Op[] = [];\n for (const n of fragment.nodes) {\n const attr = keys[n.type];\n if (!attr) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n ops.push({ t: \"update\", ns: n.type, id: { ns: n.type, attr, value: n.key }, attrs: { ...n.attrs, [attr]: n.key } });\n }\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n return ops;\n}\n\n/**\n * Provenance-preserving write. Reads existing nodes by natural key (one batched\n * query), merges scalar attrs per the ontology's rules, drops human-owned\n * attrs, then writes merged node updates + all links in one atomic, idempotent\n * transaction. `mutationId` must be deterministic so odla-db can deduplicate\n * the write on retry; this does not make preceding reads or external effects\n * exactly-once.\n */\nexport async function writeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n mutationId: string,\n): Promise<{ txId: number; ops: number }> {\n const keys = naturalKeysOf(ontology);\n const rules = mergeRulesOf(ontology);\n const humans = humanAttrsOf(ontology);\n\n // 1. Collapse duplicate nodes within the fragment (same type+key), dropping human attrs.\n const byType = new Map<string, Map<string, Record<string, Value>>>();\n for (const n of fragment.nodes) {\n if (!keys[n.type]) throw new Error(`No natural key configured for node type \"${n.type}\"`);\n const human = humans[n.type] ?? new Set<string>();\n const clean: Record<string, Value> = {};\n for (const [k, v] of Object.entries(n.attrs)) if (!human.has(k)) clean[k] = v;\n const typeMap = byType.get(n.type) ?? new Map<string, Record<string, Value>>();\n const prev = typeMap.get(n.key);\n typeMap.set(n.key, prev ? mergeAttrs(rules[n.type]!, prev, clean) : clean);\n byType.set(n.type, typeMap);\n }\n\n // 2. Batch-read existing rows per type by natural key (one query, filtered per level).\n const query: InstaQLQuery = {};\n for (const [type, m] of byType) {\n query[type] = { $: { where: { [keys[type]!]: { $in: [...m.keys()] } } } };\n }\n const existing: QueryResult = Object.keys(query).length ? await db.query(query) : {};\n const priorOf = (type: string, key: string): Record<string, Value> => {\n const attr = keys[type]!;\n for (const row of existing[type] ?? []) {\n if (row[attr] === key) return row as unknown as Record<string, Value>;\n }\n return {};\n };\n\n // 3. Merged node updates first, then links.\n const ops: Op[] = [];\n for (const [type, m] of byType) {\n const attr = keys[type]!;\n for (const [key, incoming] of m) {\n const merged = mergeAttrs(rules[type]!, priorOf(type, key), incoming);\n ops.push({ t: \"update\", ns: type, id: { ns: type, attr, value: key }, attrs: { ...merged, [attr]: key } });\n }\n }\n const ref = (type: string, key: string): EntityRef => ({ ns: type, attr: keys[type]!, value: key });\n for (const e of fragment.edges) {\n ops.push({ t: \"link\", ns: e.from.type, id: ref(e.from.type, e.from.key), label: e.label, target: ref(e.to.type, e.to.key) });\n }\n\n if (ops.length === 0) return { txId: 0, ops: 0 };\n const { txId } = await db.transact(ops, { mutationId });\n return { txId, ops: ops.length };\n}\n","// Read a GraphFragment back OUT of odla-db — the inverse of the writer, and\n// the bridge from the live graph to the pure @odla-ai/kg/temporal analytics.\n// Reads are per-type offset walks with link labels as nested InstaQL keys\n// (odla-db has no cross-relation where; filter per level). Reverse labels are\n// normalized to FORWARD edges so the fragment satisfies resolvePath's\n// forward-only traversal.\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { GraphFragment, GraphNode, GraphEdge } from \"../types\";\nimport type { Ontology } from \"../ontology\";\nimport { naturalKeysOf } from \"../ontology\";\nimport { parseDateMs } from \"../temporal/time\";\n\n/** One entity type to export. */\nexport interface ExportEntitySpec {\n /** Link labels (forward or reverse, as queried in InstaQL) to pull as nested rows. */\n links?: readonly string[];\n /** Attr allowlist kept on nodes. Default: every scalar attr except `id` and links. */\n attrs?: readonly string[];\n /**\n * Date window. Filtering is client-side by default; set `indexed: true`\n * (only when the attr really is indexed) to also push a\n * `where { [dateAttr]: { $gte: from } }` to the server — odla-db silently\n * returns zero rows for `where` on unindexed attrs.\n */\n window?: { dateAttr: string; from: string; indexed?: boolean };\n /** Row cap for this type across the offset walk. Default 1000. */\n limit?: number;\n}\n\n/** What {@link exportFragment} should read. */\nexport interface ExportSpec {\n entities: Record<string, ExportEntitySpec>;\n /** Rows per page in the offset walk (1–1000). Default 200. */\n pageSize?: number;\n}\n\ninterface LinkPlan {\n label: string;\n counterpart: string;\n /** Forward: root --forwardLabel--> nested. Reverse: nested --forwardLabel--> root. */\n direction: \"forward\" | \"reverse\";\n forwardLabel: string;\n}\n\nfunction planLinks(ontology: Ontology, type: string, labels: readonly string[]): LinkPlan[] {\n return labels.map((label) => {\n for (const edge of Object.values(ontology.edges)) {\n if (edge.forwardLabel === label && edge.from === type) {\n return { label, counterpart: edge.to, direction: \"forward\" as const, forwardLabel: edge.forwardLabel };\n }\n if (edge.reverseLabel === label && edge.to === type) {\n return { label, counterpart: edge.from, direction: \"reverse\" as const, forwardLabel: edge.forwardLabel };\n }\n }\n throw new Error(`exportFragment: no ontology edge matches link \"${label}\" on \"${type}\"`);\n });\n}\n\nconst isScalar = (v: unknown) => v === null || (typeof v !== \"object\" && typeof v !== \"function\");\n\nfunction pickAttrs(row: Record<string, unknown>, spec: ExportEntitySpec, links: readonly string[]) {\n const out: GraphNode[\"attrs\"] = {};\n for (const [k, v] of Object.entries(row)) {\n if (spec.attrs ? !spec.attrs.includes(k) : k === \"id\" || links.includes(k) || !isScalar(v)) continue;\n if (isScalar(v) && v !== null) out[k] = v as GraphNode[\"attrs\"][string];\n }\n return out;\n}\n\nconst RETRYABLE_WITHOUT_WHERE = /query_scan_limit|query_too_complex/;\n\n/**\n * Export (a window of) the graph as a `GraphFragment` — nodes and forward\n * edges — ready for `extractEvents` and the rest of `@odla-ai/kg/temporal`.\n *\n * Each entity type is read with a stable offset walk (ordered by its natural\n * key) carrying its requested link labels as nested relations. Date windows\n * filter client-side; for attrs declared `indexed` the filter also runs\n * server-side, and if that indexed filter ever exceeds the scan budget\n * (`query_scan_limit`) the walk restarts without `where`.\n * Nodes dedupe by `type:key` keeping the richer attr set; output ordering is\n * deterministic (nodes by type/key, edges by from/label/to).\n */\nexport async function exportFragment(db: AdminDb, ontology: Ontology, spec: ExportSpec): Promise<GraphFragment> {\n const keys = naturalKeysOf(ontology);\n const pageSize = Math.min(1000, Math.max(1, spec.pageSize ?? 200));\n const nodes = new Map<string, GraphNode>();\n const edges = new Map<string, GraphEdge>();\n\n const putNode = (node: GraphNode) => {\n const id = `${node.type}:${node.key}`;\n const existing = nodes.get(id);\n if (!existing || Object.keys(node.attrs).length > Object.keys(existing.attrs).length) nodes.set(id, node);\n };\n\n for (const [type, entity] of Object.entries(spec.entities)) {\n const naturalKey = keys[type];\n if (!naturalKey) throw new Error(`exportFragment: no ontology entity \"${type}\"`);\n const links = planLinks(ontology, type, entity.links ?? []);\n const limit = entity.limit ?? 1000;\n const fromMs = entity.window ? parseDateMs(entity.window.from) : null;\n let useWhere = entity.window?.indexed === true;\n let offset = 0;\n let taken = 0;\n\n while (taken < limit) {\n const opts: Record<string, unknown> = {\n limit: Math.min(pageSize, limit - taken),\n offset,\n order: { [naturalKey]: \"asc\" },\n };\n if (useWhere && entity.window) opts.where = { [entity.window.dateAttr]: { $gte: entity.window.from } };\n const query = {\n [type]: { $: opts, ...Object.fromEntries(links.map((l) => [l.label, {}])) },\n };\n\n let rows: Array<Record<string, unknown>>;\n try {\n rows = ((await db.query(query)) as Record<string, unknown[]>)[type] as Array<Record<string, unknown>> ?? [];\n } catch (err) {\n if (useWhere && RETRYABLE_WITHOUT_WHERE.test(String((err as Error).message ?? err))) {\n // Unindexed attr blew the scan budget — restart this type's walk\n // without `where` (offsets aren't comparable across filters; the\n // dedupe maps make re-reads harmless) and filter client-side.\n useWhere = false;\n offset = 0;\n taken = 0;\n continue;\n }\n throw err;\n }\n\n for (const row of rows) {\n const key = row[naturalKey];\n if (key === undefined || key === null) continue; // defensive: unkeyed row\n if (entity.window && fromMs !== null) {\n const t = parseDateMs(row[entity.window.dateAttr]);\n if (t !== null && t < fromMs) continue;\n }\n const rootRef = { type, key: String(key) };\n putNode({ ...rootRef, attrs: pickAttrs(row, entity, entity.links ?? []) });\n\n for (const plan of links) {\n const nestedKeyAttr = keys[plan.counterpart];\n if (!nestedKeyAttr) throw new Error(`exportFragment: no ontology entity \"${plan.counterpart}\"`);\n // has-many relations come back as arrays, has-one as a bare object.\n const raw = row[plan.label];\n const nestedRows = Array.isArray(raw) ? raw : raw ? [raw] : [];\n for (const nested of nestedRows as Array<Record<string, unknown>>) {\n const nestedKey = nested[nestedKeyAttr];\n if (nestedKey === undefined || nestedKey === null) continue;\n const nestedRef = { type: plan.counterpart, key: String(nestedKey) };\n putNode({ ...nestedRef, attrs: pickAttrs(nested, {}, []) });\n const edge: GraphEdge =\n plan.direction === \"forward\"\n ? { from: rootRef, to: nestedRef, label: plan.forwardLabel }\n : { from: nestedRef, to: rootRef, label: plan.forwardLabel };\n edges.set(`${edge.from.type}:${edge.from.key}|${edge.label}|${edge.to.type}:${edge.to.key}`, edge);\n }\n }\n taken++;\n if (taken >= limit) break;\n }\n\n if (rows.length < pageSize) break;\n offset += rows.length;\n }\n }\n\n const byRef = (a: { type: string; key: string }, b: { type: string; key: string }) =>\n a.type < b.type ? -1 : a.type > b.type ? 1 : a.key < b.key ? -1 : a.key > b.key ? 1 : 0;\n return {\n nodes: [...nodes.values()].sort(byRef),\n edges: [...edges.values()].sort(\n (a, b) => byRef(a.from, b.from) || (a.label < b.label ? -1 : a.label > b.label ? 1 : 0) || byRef(a.to, b.to),\n ),\n };\n}\n","import type { AdminDb, Entity } from \"@odla-ai/db\";\nimport type { GraphFragment } from \"../types\";\nimport type { Ontology } from \"../ontology\";\n\n/** FTS5 search over a namespace's text fields (odla-db QueryOpts.search). */\nexport async function searchEntities(db: AdminDb, type: string, q: string, limit = 5): Promise<Entity[]> {\n if (!q.trim()) return [];\n const res = await db.query({ [type]: { $: { search: q, limit } } });\n return (res[type] ?? []) as Entity[];\n}\n\nconst norm = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, \"\");\n\n/**\n * Resolve a name to an existing entity's natural key via FTS — for merging a\n * re-discovery that slugified differently. Conservative: only returns a hit when\n * the top match's name/key normalizes identically to the query.\n */\nexport async function resolveName(\n db: AdminDb,\n type: string,\n keyAttr: string,\n name: string,\n): Promise<string | null> {\n const target = norm(name);\n if (!target) return null;\n for (const h of await searchEntities(db, type, name, 3)) {\n const key = h[keyAttr];\n const hname = h[\"name\"];\n if (typeof key !== \"string\") continue;\n if ((typeof hname === \"string\" && norm(hname) === target) || norm(key) === target) return key;\n }\n return null;\n}\n\n/**\n * Canonicalize a fragment's searchable nodes against existing entities via FTS,\n * remapping node keys (and the edges that reference them) to the canonical key.\n * Used on the lower-volume discovery/enrich paths, not high-volume ingest.\n */\nexport async function canonicalizeFragment(\n db: AdminDb,\n fragment: GraphFragment,\n ontology: Ontology,\n): Promise<GraphFragment> {\n const remap = new Map<string, string>(); // `${type}:${key}` -> canonical key\n for (const n of fragment.nodes) {\n const spec = ontology.entities[n.type];\n if (!spec?.searchable?.length) continue;\n const name = typeof n.attrs.name === \"string\" ? n.attrs.name : \"\";\n if (!name) continue;\n const canon = await resolveName(db, n.type, spec.naturalKey, name);\n if (canon && canon !== n.key) remap.set(`${n.type}:${n.key}`, canon);\n }\n if (remap.size === 0) return fragment;\n\n const rk = (type: string, key: string): string => remap.get(`${type}:${key}`) ?? key;\n return {\n nodes: fragment.nodes.map((n) => ({ ...n, key: rk(n.type, n.key) })),\n edges: fragment.edges.map((e) => ({\n from: { type: e.from.type, key: rk(e.from.type, e.from.key) },\n to: { type: e.to.type, key: rk(e.to.type, e.to.key) },\n label: e.label,\n })),\n };\n}\n","import { init, extractText, type Ai } from \"@odla-ai/ai\";\nimport type { Usage } from \"../types\";\n\n/**\n * The LLM seam, now a thin adapter over @odla-ai/ai (which was itself\n * generalized from this package's original Provider). Three verbs stay:\n * extract (forced structured tool call), search (web-search-enabled text),\n * write (plain generation). Callers never see vendor or oracle types.\n */\n\nexport type ProviderFamily = \"claude\" | \"openai\";\n\n/** Role → model tiers per family — config as data. `cheap` is for gate-style\n * calls, `extract` for structured extraction (and prose writing), `search`\n * for web-search-enabled calls. */\nexport const MODEL_TIERS: Record<ProviderFamily, { cheap: string; extract: string; search: string }> = {\n claude: { cheap: \"claude-haiku-4-5\", extract: \"claude-sonnet-5\", search: \"claude-sonnet-5\" },\n openai: { cheap: \"gpt-5-nano\", extract: \"gpt-5-mini\", search: \"gpt-5-search-api\" },\n};\n\n/** A forced structured tool call: schema in, validated object out. */\nexport interface ExtractCall {\n system: string;\n user: string;\n tool: { name: string; description?: string; parameters: Record<string, unknown> };\n model?: string;\n maxTokens?: number;\n}\n\n/** A web-search-enabled call: the model searches and returns text (caller tolerant-parses). */\nexport interface SearchCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\n/** A plain text-generation call (no tools, no search) — e.g. digest prose. */\nexport interface WriteCall {\n system: string;\n user: string;\n model?: string;\n maxTokens?: number;\n}\n\nexport interface Provider {\n id: ProviderFamily;\n /** The underlying @odla-ai/ai client — escape hatch for advanced use. */\n ai: Ai;\n extract<T = unknown>(c: ExtractCall): Promise<{ value: T; usage: Usage }>;\n search(c: SearchCall): Promise<{ text: string; usage: Usage }>;\n write(c: WriteCall): Promise<{ text: string; usage: Usage }>;\n}\n\nexport interface LlmConfig {\n provider: ProviderFamily;\n anthropicKey?: string;\n openaiKey?: string;\n /** Inject a pre-built Ai (tests, custom catalog); keys are ignored when set. */\n ai?: Ai;\n}\n\nfunction toUsage(u: { inputTokens: number; outputTokens: number }): Usage {\n return { calls: 1, inputTokens: u.inputTokens, outputTokens: u.outputTokens };\n}\n\n/** The library primitive: an explicit config in, the three-verb seam out. */\nexport function createProvider(cfg: LlmConfig): Provider {\n const tiers = MODEL_TIERS[cfg.provider];\n const ai = cfg.ai ?? init({ keys: { anthropic: cfg.anthropicKey, openai: cfg.openaiKey } });\n return {\n id: cfg.provider,\n ai,\n async extract<T = unknown>(c: ExtractCall) {\n const { value, usage } = await ai.extract<T>({\n model: c.model ?? tiers.extract,\n system: c.system,\n user: c.user,\n tool: c.tool,\n maxTokens: c.maxTokens,\n });\n return { value, usage: toUsage(usage) };\n },\n async search(c: SearchCall) {\n const { text, usage } = await ai.search({\n model: c.model ?? tiers.search,\n system: c.system,\n user: c.user,\n maxTokens: c.maxTokens,\n });\n return { text, usage: toUsage(usage) };\n },\n async write(c: WriteCall) {\n const res = await ai.chat({\n model: c.model ?? tiers.extract,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n maxTokens: c.maxTokens ?? 2048,\n });\n return { text: extractText(res.content), usage: toUsage(res.usage) };\n },\n };\n}\n\n/** The env vars the provider factory reads — a structural slice, not the\n * worker Env, so any host (worker, script, test) can supply plain strings. */\nexport interface LlmEnv {\n LLM_PROVIDER?: string; // \"claude\" | \"openai\"\n ANTHROPIC_API_KEY?: string;\n OPENAI_API_KEY?: string;\n}\n\n/** Build the configured provider from host env, failing before use when its key is missing. */\nexport function getProvider(env: LlmEnv): Provider {\n const which: ProviderFamily = (env.LLM_PROVIDER || \"claude\").toLowerCase() === \"openai\" ? \"openai\" : \"claude\";\n if (which === \"openai\" && !env.OPENAI_API_KEY) throw new Error(\"LLM_PROVIDER=openai but OPENAI_API_KEY is unset\");\n if (which === \"claude\" && !env.ANTHROPIC_API_KEY) throw new Error(\"LLM_PROVIDER=claude but ANTHROPIC_API_KEY is unset\");\n return createProvider({ provider: which, anthropicKey: env.ANTHROPIC_API_KEY, openaiKey: env.OPENAI_API_KEY });\n}\n","/**\n * Tolerant JSON extraction from LLM prose — web-search models return JSON inside\n * explanatory text. Ported from agnacl services/enrichment.ts.\n */\n\nexport function extractJson<T = unknown>(text: string): T | null {\n const m = text.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]) as T;\n } catch {\n return null;\n }\n}\n\n/** Tolerantly parse the first JSON-array-shaped span in model prose, or return an empty array. */\nexport function extractJsonArray<T = unknown>(text: string): T[] {\n const m = text.match(/\\[[\\s\\S]*\\]/);\n if (!m) return [];\n try {\n const v = JSON.parse(m[0]);\n return Array.isArray(v) ? (v as T[]) : [];\n } catch {\n return [];\n }\n}\n","/**\n * Dependency-free HTML → text extraction (Workers-isolate friendly: no DOM/jsdom).\n * Ported from agnacl services/content-extractor.ts.\n */\n\nconst MAX_CHARS = 8000;\n\n/** Strip scripts/styles/tags and collapse whitespace; cap length. */\nexport function htmlToText(html: string, maxChars = MAX_CHARS): string {\n let t = html;\n t = t.replace(/<script[\\s\\S]*?<\\/script>/gi, \" \");\n t = t.replace(/<style[\\s\\S]*?<\\/style>/gi, \" \");\n t = t.replace(/<!--[\\s\\S]*?-->/g, \" \");\n t = t.replace(/<\\/(p|div|section|article|li|h[1-6]|br)>/gi, \"\\n\");\n t = t.replace(/<[^>]+>/g, \" \");\n t = decodeEntities(t);\n t = t.replace(/[ \\t\\f\\r]+/g, \" \").replace(/\\n\\s*\\n\\s*/g, \"\\n\\n\").trim();\n return t.slice(0, maxChars);\n}\n\nfunction decodeEntities(s: string): string {\n return s\n .replace(/ /gi, \" \")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .replace(/&#(\\d+);/g, (_, d) => String.fromCharCode(Number(d)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCharCode(parseInt(h, 16)));\n}\n\n/** Fetch a URL and return readable text. Best-effort: returns \"\" on any failure. */\nexport async function fetchText(url: string, maxChars = MAX_CHARS): Promise<string> {\n try {\n const res = await fetch(url, {\n headers: {\n \"user-agent\": \"odla-kg/0.1 (+https://github.com/cory/odla-kg)\",\n accept: \"text/html,application/xhtml+xml\",\n },\n });\n if (!res.ok) return \"\";\n const ct = res.headers.get(\"content-type\") || \"\";\n const body = await res.text();\n if (ct.includes(\"html\") || /<\\w+[\\s>]/.test(body.slice(0, 200))) {\n return htmlToText(body, maxChars);\n }\n return body.slice(0, maxChars);\n } catch {\n return \"\";\n }\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { htmlToText } from \"../util/content\";\n\n/** Dependency-free RSS/Atom parsing, ported from agnacl services/rss.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\n\nfunction extractTag(xml: string, tag: string): string {\n const cdata = xml.match(new RegExp(`<${tag}[^>]*>\\\\s*<!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)\\\\]\\\\]>\\\\s*</${tag}>`, \"i\"));\n if (cdata) return cdata[1]!.trim();\n const m = xml.match(new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction extractAttr(xml: string, tag: string, attr: string): string {\n const m = xml.match(new RegExp(`<${tag}[^>]*\\\\s${attr}=[\"']([^\"']*)[\"']`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\nfunction splitItems(xml: string): string[] {\n return xml.match(/<item[\\s>][\\s\\S]*?<\\/item>/gi) || xml.match(/<entry[\\s>][\\s\\S]*?<\\/entry>/gi) || [];\n}\n\ninterface RssCfg {\n url: string;\n name: string;\n watch?: string | null;\n}\n\nexport const rssConnector: SourceConnector<RssCfg> = {\n id: \"rss\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): RssCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"rss connector requires a `url`\");\n return { url: r.url, name: String(r.name ?? r.url), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n let xml: string;\n try {\n const res = await fetch(cfg.url, { headers: { \"User-Agent\": UA } });\n if (!res.ok) {\n if (res.status === 429 || res.status === 403 || res.status >= 500) return [];\n throw new Error(`feed ${cfg.url} → ${res.status}`);\n }\n xml = await res.text();\n } catch (e) {\n console.log(`rss fetch failed (non-blocking): ${cfg.url}: ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n for (const raw of splitItems(xml)) {\n const title = extractTag(raw, \"title\");\n let url = extractTag(raw, \"link\") || extractAttr(raw, \"link\", \"href\");\n if (!title || !url) continue;\n url = url.trim();\n const date =\n extractTag(raw, \"pubDate\") || extractTag(raw, \"published\") || extractTag(raw, \"updated\") || undefined;\n const content =\n extractTag(raw, \"content:encoded\") ||\n extractTag(raw, \"content\") ||\n extractTag(raw, \"description\") ||\n extractTag(raw, \"summary\") ||\n \"\";\n let text = htmlToText(content);\n // Thin feed body → fetch the article page.\n if (text.length < 200) {\n const page = await ctx.fetchText(url);\n if (page.length > text.length) text = page;\n }\n out.push({ url, title, date, sourceName: cfg.name, sourceKind: \"rss\", watch: cfg.watch, text });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector } from \"./types\";\nimport { fetchText } from \"../util/content\";\n\n/** Fetch one URL → a text-bearing RawItem. Used both directly (/trigger) and as a connector. */\nexport async function fetchUrl(url: string): Promise<RawItem> {\n const text = await fetchText(url);\n const firstLine = text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\";\n let host = url;\n try {\n host = new URL(url).hostname;\n } catch {\n /* keep raw url as the source label */\n }\n return { url, title: (firstLine || url).slice(0, 200).trim(), sourceName: host, sourceKind: \"url\", text };\n}\n\ninterface UrlCfg {\n url: string;\n watch?: string | null;\n}\n\nexport const urlConnector: SourceConnector<UrlCfg> = {\n id: \"url\",\n capabilities: { fetch: true, structured: false },\n parseConfig(raw): UrlCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.url !== \"string\" || !r.url) throw new Error(\"url connector requires a `url`\");\n return { url: r.url, watch: (r.watch as string | null) ?? null };\n },\n async fetch(cfg): Promise<RawItem[]> {\n const item = await fetchUrl(cfg.url);\n if (cfg.watch !== undefined) item.watch = cfg.watch;\n return [item];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/** GDELT DOC 2.0 standing-query connector (text). Ported from agnacl services/gdelt.ts. */\nconst UA = \"Mozilla/5.0 (compatible; odla-kg/0.1)\";\nconst MAX_ITEMS = 15;\n\ninterface GdeltCfg {\n query: string;\n name: string;\n watch?: string | null;\n}\n\nfunction isoFromSeendate(s: string): string | undefined {\n const m = s.match(/^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z?$/);\n return m ? `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}Z` : undefined;\n}\n\nexport const gdeltConnector: SourceConnector<GdeltCfg> = {\n id: \"gdelt\",\n capabilities: { fetch: true, structured: false },\n\n parseConfig(raw): GdeltCfg {\n const r = raw as Record<string, unknown>;\n if (typeof r.query !== \"string\" || !r.query) throw new Error(\"gdelt connector requires a `query`\");\n return { query: r.query, name: String(r.name ?? `GDELT: ${r.query}`), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const api =\n `https://api.gdeltproject.org/api/v2/doc/doc?query=${encodeURIComponent(cfg.query)}` +\n `&mode=artlist&format=json&maxrecords=40×pan=2d`;\n\n let articles: Array<{ url?: string; title?: string; seendate?: string; language?: string }> = [];\n try {\n // GDELT is a best-effort external source; without a deadline a slow or wedged endpoint hangs\n // the whole workflow run. Bound it and let the catch below treat a timeout like any other miss.\n const res = await fetch(api, { headers: { \"User-Agent\": UA }, signal: AbortSignal.timeout(15_000) });\n if (!res.ok) return [];\n const text = await res.text(); // GDELT sometimes returns text errors with 200\n try {\n articles = (JSON.parse(text) as { articles?: typeof articles }).articles ?? [];\n } catch {\n return [];\n }\n } catch (e) {\n console.log(`gdelt fetch failed (non-blocking): ${e}`);\n return [];\n }\n\n const seen = new Set<string>();\n const picked = articles\n .filter((a) => a?.url && a?.title && a.language === \"English\" && !seen.has(a.url!) && (seen.add(a.url!), true))\n .slice(0, MAX_ITEMS);\n\n const out: RawItem[] = [];\n for (const a of picked) {\n const body = await ctx.fetchText(a.url!); // GDELT gives no body; fetch the article\n if (body.length < 200) continue;\n out.push({\n url: a.url!,\n title: a.title!,\n date: isoFromSeendate(a.seendate ?? \"\"),\n sourceName: cfg.name,\n sourceKind: \"gdelt\",\n watch: cfg.watch,\n text: body,\n });\n }\n return out;\n },\n};\n","/**\n * Identity + dedup helpers. The slug / urlHash a node resolves to IS its\n * natural key for Lookup-based upserts, so these must be stable and pure.\n * Ported from agnacl (utils/markdown.ts slugify, services/kv.ts normalizeUrl).\n */\n\n/** NFKD-normalize, strip accents, lowercase, non-alphanumerics → \"-\", cap 60. */\nexport function slugify(input: string): string {\n return input\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60)\n .replace(/-+$/g, \"\");\n}\n\n/** Drop tracking params, hash, and trailing slash so the same page maps to one key. */\nexport function normalizeUrl(raw: string): string {\n try {\n const u = new URL(raw);\n u.hash = \"\";\n const drop = [/^utm_/i, /^ref$/i, /^ref_/i, /^fbclid$/i, /^gclid$/i, /^mc_/i];\n for (const key of [...u.searchParams.keys()]) {\n if (drop.some((re) => re.test(key))) u.searchParams.delete(key);\n }\n let s = u.toString();\n s = s.replace(/\\/$/, \"\");\n return s;\n } catch {\n return raw.trim().replace(/#.*$/, \"\").replace(/\\/$/, \"\");\n }\n}\n\n/** Stable short key for a URL (FNV-1a 32-bit, base36). Used as the `signal` natural key. */\nexport function urlHash(raw: string): string {\n const s = normalizeUrl(raw);\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).toString(36);\n}\n\n/**\n * Reject descriptive phrases masquerading as people (\"three craftsmen\",\n * \"the founders\"). A real name is 2-4 capitalized tokens, no digits.\n * Ported from agnacl utils/names.ts (isLikelyPersonName).\n */\nexport function isLikelyPersonName(name: string): boolean {\n const n = name.trim();\n if (!n || /\\d/.test(n)) return false;\n const parts = n.split(/\\s+/);\n if (parts.length < 2 || parts.length > 4) return false;\n return parts.every((p) => /^[A-Z][a-zA-Z'’.-]*$/.test(p));\n}\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify } from \"../util/identity\";\n\n/**\n * FRED economic indicators — a STRUCTURED connector: it maps series directly to\n * graph nodes (indicator + region) and emits RawItem.fragment, so the ingest\n * workflow writes it without any LLM extraction. Ported from agnacl services/fred.ts.\n */\ninterface FredCfg {\n series: Array<{ id: string; label: string; unit?: string }>;\n region?: string;\n regionName?: string;\n}\n\nasync function fetchSeries(\n seriesId: string,\n apiKey: string,\n start: string,\n): Promise<Array<{ date: string; value: number }>> {\n const url =\n `https://api.stlouisfed.org/fred/series/observations?series_id=${encodeURIComponent(seriesId)}` +\n `&api_key=${encodeURIComponent(apiKey)}&file_type=json&observation_start=${encodeURIComponent(start)}`;\n try {\n const res = await fetch(url);\n if (!res.ok) return [];\n const data = (await res.json()) as { observations?: Array<{ date?: string; value?: string }> };\n const out: Array<{ date: string; value: number }> = [];\n for (const o of data.observations ?? []) {\n if (!o?.date || o.value === undefined || o.value === \".\") continue;\n const v = Number(o.value);\n if (Number.isFinite(v)) out.push({ date: o.date, value: v });\n }\n return out;\n } catch (e) {\n console.log(`fred fetch failed for ${seriesId} (non-blocking): ${e}`);\n return [];\n }\n}\n\nexport const fredConnector: SourceConnector<FredCfg> = {\n id: \"fred\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): FredCfg {\n const r = raw as Record<string, unknown>;\n const series = Array.isArray(r.series) ? (r.series as FredCfg[\"series\"]) : [];\n if (!series.length) throw new Error(\"fred connector requires a non-empty `series`\");\n return { series, region: r.region as string | undefined, regionName: r.regionName as string | undefined };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const apiKey = ctx.secret(\"FRED_API_KEY\");\n if (!apiKey) {\n console.log(\"fred: FRED_API_KEY unset — skipping\");\n return [];\n }\n const start = new Date(ctx.now.getTime() - 400 * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const out: RawItem[] = [];\n\n for (const s of cfg.series) {\n const obs = await fetchSeries(s.id, apiKey, start);\n if (!obs.length) continue;\n const latest = obs[obs.length - 1]!;\n const prior = obs.length > 1 ? obs[obs.length - 2] : undefined;\n\n const attrs: Record<string, Value> = { label: s.label, latest: String(latest.value), as_of: latest.date };\n if (prior) attrs.prior = String(prior.value);\n if (s.unit) attrs.unit = s.unit;\n\n const fragment: GraphFragment = { nodes: [{ type: \"indicator\", key: s.id, attrs }], edges: [] };\n if (cfg.region) {\n const rslug = slugify(cfg.region);\n if (rslug) {\n fragment.nodes.push({ type: \"region\", key: rslug, attrs: { name: cfg.regionName ?? cfg.region } });\n fragment.edges.push({ from: { type: \"region\", key: rslug }, to: { type: \"indicator\", key: s.id }, label: \"has_indicator\" });\n }\n }\n\n out.push({\n url: `https://fred.stlouisfed.org/series/${s.id}#${latest.date}`,\n title: `${s.label}: ${latest.value} (${latest.date})`,\n date: latest.date,\n sourceName: \"FRED\",\n sourceKind: \"fred\",\n fragment,\n });\n }\n return out;\n },\n};\n","import type { Value } from \"@odla-ai/db\";\nimport type { RawItem, GraphFragment } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { slugify, isLikelyPersonName } from \"../util/identity\";\n\n/**\n * SEC EDGAR Form D — a STRUCTURED connector (RawItem.fragment, no LLM). A lean\n * port of agnacl services/edgar.ts: first page of Form D filings for the given\n * states, each mapped to company + funding_round (+ people, region). SEC\n * requires an identifying User-Agent (env.EDGAR_USER_AGENT) on every request.\n */\nconst SEARCH_BASE = \"https://efts.sec.gov/LATEST/search-index\";\nconst MAX_FILINGS = 10;\n\ninterface EdgarCfg {\n locationCodes: string[];\n cityFilter?: string[];\n days?: number;\n}\n\nconst cikTrim = (cik: string): string => cik.replace(/^0+/, \"\") || cik;\nconst indexUrl = (cik: string, adsh: string): string =>\n `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/${adsh}-index.htm`;\n\nfunction tagText(scope: string, tag: string): string {\n const m = scope.match(new RegExp(`<${tag}[^>]*>\\\\s*([\\\\s\\\\S]*?)\\\\s*</${tag}>`, \"i\"));\n return m ? m[1]!.trim() : \"\";\n}\n\ninterface EdgarHit {\n _source?: {\n adsh?: string;\n ciks?: string[];\n display_names?: string[];\n file_date?: string;\n biz_states?: string[];\n biz_locations?: string[];\n };\n}\n\nexport const edgarConnector: SourceConnector<EdgarCfg> = {\n id: \"edgar\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): EdgarCfg {\n const r = raw as Record<string, unknown>;\n const codes = Array.isArray(r.locationCodes) ? (r.locationCodes as string[]) : [];\n if (!codes.length) throw new Error('edgar connector requires `locationCodes` (e.g. [\"UT\"])');\n return {\n locationCodes: codes,\n cityFilter: Array.isArray(r.cityFilter) ? (r.cityFilter as string[]) : undefined,\n days: typeof r.days === \"number\" ? r.days : 7,\n };\n },\n\n async fetch(cfg, ctx): Promise<RawItem[]> {\n const ua = ctx.secret(\"EDGAR_USER_AGENT\");\n if (!ua) {\n console.log(\"edgar: EDGAR_USER_AGENT unset — skipping\");\n return [];\n }\n const enddt = ctx.now.toISOString().slice(0, 10);\n const startdt = new Date(ctx.now.getTime() - (cfg.days ?? 7) * 24 * 3600 * 1000).toISOString().slice(0, 10);\n const searchUrl =\n `${SEARCH_BASE}?q=&forms=D&locationCodes=${encodeURIComponent(cfg.locationCodes.join(\",\"))}` +\n `&startdt=${startdt}&enddt=${enddt}`;\n\n let hits: EdgarHit[] = [];\n try {\n const res = await fetch(searchUrl, { headers: { \"User-Agent\": ua } });\n if (!res.ok) return [];\n hits = ((await res.json()) as { hits?: { hits?: EdgarHit[] } }).hits?.hits ?? [];\n } catch (e) {\n console.log(`edgar search failed (non-blocking): ${e}`);\n return [];\n }\n\n const out: RawItem[] = [];\n const seen = new Set<string>();\n for (const hit of hits.slice(0, MAX_FILINGS)) {\n const src = hit._source ?? {};\n const adsh = src.adsh ?? \"\";\n if (!adsh || seen.has(adsh)) continue;\n seen.add(adsh);\n const cik = src.ciks?.[0] ?? \"\";\n const name = (src.display_names?.[0] ?? \"\").replace(/\\s*\\(CIK\\s*\\d+\\)\\s*$/i, \"\").trim();\n if (!name || !cik) continue;\n\n const loc = src.biz_locations?.[0] ?? \"\";\n let city = loc.trim();\n let state = src.biz_states?.[0] ?? \"\";\n const comma = loc.lastIndexOf(\",\");\n if (comma >= 0) {\n city = loc.slice(0, comma).trim();\n state = loc.slice(comma + 1).trim() || state;\n }\n if (cfg.cityFilter?.length && !cfg.cityFilter.some((c) => c.toLowerCase() === city.toLowerCase())) continue;\n\n const url = indexUrl(cik, adsh);\n const fileDate = src.file_date ?? \"\";\n\n // Filing detail (offering amount, industry, related persons)\n let industry = \"\";\n let offering = \"\";\n const persons: Array<{ name: string; roles: string[] }> = [];\n try {\n const detailUrl = `https://www.sec.gov/Archives/edgar/data/${cikTrim(cik)}/${adsh.replace(/-/g, \"\")}/primary_doc.xml`;\n const dres = await fetch(detailUrl, { headers: { \"User-Agent\": ua } });\n if (dres.ok) {\n const xml = await dres.text();\n industry = tagText(xml, \"industryGroupType\");\n offering = tagText(xml, \"totalOfferingAmount\");\n for (const block of xml.match(/<relatedPersonInfo>[\\s\\S]*?<\\/relatedPersonInfo>/gi) ?? []) {\n const pname = [tagText(block, \"firstName\"), tagText(block, \"lastName\")]\n .filter((p) => p && p !== \"-\")\n .join(\" \")\n .trim();\n if (!pname) continue;\n const roles: string[] = [];\n const relRe = /<relationship>\\s*([\\s\\S]*?)\\s*<\\/relationship>/gi;\n let m: RegExpExecArray | null;\n while ((m = relRe.exec(block)) !== null) {\n const r = m[1]!.trim();\n if (r && !roles.includes(r)) roles.push(r);\n }\n persons.push({ name: pname, roles });\n }\n }\n } catch {\n /* fall back to search-hit metadata */\n }\n\n const cslug = slugify(name);\n if (!cslug) continue;\n const f: GraphFragment = { nodes: [], edges: [] };\n const cattrs: Record<string, Value> = { name };\n if (city) cattrs.city = city;\n if (state) cattrs.region = state;\n if (industry) cattrs.sector = industry;\n if (fileDate) {\n cattrs.first_seen = fileDate;\n cattrs.last_seen = fileDate;\n }\n f.nodes.push({ type: \"company\", key: cslug, attrs: cattrs });\n\n if (state) {\n const rslug = slugify(state);\n if (rslug) {\n f.nodes.push({ type: \"region\", key: rslug, attrs: { name: state } });\n f.edges.push({ from: { type: \"company\", key: cslug }, to: { type: \"region\", key: rslug }, label: \"located_in\" });\n }\n }\n\n const rkey = `${cslug}:form-d:${fileDate}`;\n const rattrs: Record<string, Value> = { round: \"Form D\", source_url: url };\n if (offering) rattrs.amount = offering;\n if (fileDate) rattrs.date = fileDate;\n f.nodes.push({ type: \"funding_round\", key: rkey, attrs: rattrs });\n f.edges.push({ from: { type: \"funding_round\", key: rkey }, to: { type: \"company\", key: cslug }, label: \"round_of\" });\n\n for (const p of persons) {\n if (!isLikelyPersonName(p.name)) continue;\n const pslug = slugify(p.name);\n if (!pslug) continue;\n const pattrs: Record<string, Value> = { name: p.name };\n if (p.roles.length) pattrs.role = p.roles.join(\", \");\n f.nodes.push({ type: \"person\", key: pslug, attrs: pattrs });\n f.edges.push({ from: { type: \"person\", key: pslug }, to: { type: \"company\", key: cslug }, label: \"works_at\" });\n }\n\n out.push({\n url,\n title: `Form D: ${name}${offering ? ` — ${offering}` : \"\"}`,\n date: fileDate || undefined,\n sourceName: \"SEC EDGAR\",\n sourceKind: \"edgar\",\n fragment: f,\n });\n }\n return out;\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\n\n/**\n * TEMPLATE — a government / open-data connector.\n *\n * This exists to demonstrate the extensibility contract: adding a new source is\n * a self-contained module implementing SourceConnector + ONE line in the\n * registry + config entries in config/sources.ts. The cron, ingest workflow,\n * extractor, and graph writer are untouched — they only ever speak RawItem /\n * GraphFragment.\n *\n * To make it real, fetch your dataset and return either:\n * - text-bearing RawItems ({ ...meta, text }) → the LLM extracts a graph\n * - fragment-bearing RawItems ({ ...meta, fragment }) → straight to the writer\n * (map records to ontology nodes/edges yourself — see fred.ts / edgar.ts).\n * Returns [] until implemented, so wiring it up is harmless.\n */\ninterface GovDataCfg {\n dataset: string;\n watch?: string | null;\n}\n\nexport const govDataConnector: SourceConnector<GovDataCfg> = {\n id: \"gov-data\",\n capabilities: { fetch: true, structured: true },\n\n parseConfig(raw): GovDataCfg {\n const r = raw as Record<string, unknown>;\n return { dataset: String(r.dataset ?? \"\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(cfg, _ctx: ConnectorContext): Promise<RawItem[]> {\n console.log(`gov-data: template connector for dataset \"${cfg.dataset}\" — implement fetch() to emit RawItems`);\n return [];\n },\n};\n","import type { RawItem } from \"../types\";\nimport type { SourceConnector, ConnectorContext } from \"./types\";\nimport { urlHash } from \"../util/identity\";\n\n/**\n * LLM-native web search — a DISCOVERY connector. Each standing query is run\n * through the provider's web-search tool; the prose result becomes a text\n * RawItem that flows through the normal extractor (so discovered companies land\n * in the graph the same way feed items do). Discovery-only: fetch() is a no-op.\n */\ninterface WebSearchCfg {\n queries: string[];\n name: string;\n watch?: string | null;\n}\n\nconst SWEEP_SYSTEM =\n \"You are a research scout building a knowledge graph of entrepreneurs, startups, and business trends. Search the web and report concrete, real companies and the people leading them, with specifics (what they do, where, who founded them). Tie every claim to something you actually found — never invent names, numbers, or companies. If a search turns up nothing solid, say so plainly.\";\n\nexport const webSearchConnector: SourceConnector<WebSearchCfg> = {\n id: \"web-search\",\n capabilities: { fetch: false, discover: true, structured: false },\n\n parseConfig(raw): WebSearchCfg {\n const r = raw as Record<string, unknown>;\n const queries = Array.isArray(r.queries)\n ? (r.queries.filter((q) => typeof q === \"string\") as string[])\n : typeof r.query === \"string\"\n ? [r.query]\n : [];\n if (!queries.length) throw new Error(\"web-search connector requires `queries` (or a `query`)\");\n return { queries, name: String(r.name ?? \"web-search\"), watch: (r.watch as string | null) ?? null };\n },\n\n async fetch(): Promise<RawItem[]> {\n return []; // discovery-only\n },\n\n async discover(cfg, ctx: ConnectorContext): Promise<RawItem[]> {\n const provider = ctx.provider();\n const today = ctx.now.toISOString().slice(0, 10);\n const out: RawItem[] = [];\n for (const q of cfg.queries) {\n const { text } = await provider.search({\n system: SWEEP_SYSTEM,\n user: `Search task: ${q}\\nToday's date: ${today}\\n\\nReport what you find, naming specific companies and the real people leading them.`,\n });\n if (text.trim().length < 80) continue;\n out.push({\n url: `search://web-search/${urlHash(q)}#${today}`, // synthetic; changes daily so re-sweeps aren't deduped away\n title: q,\n date: today,\n sourceName: cfg.name,\n sourceKind: \"web-search\",\n watch: cfg.watch,\n text,\n });\n }\n return out;\n },\n};\n","import type { SourceConnector, ConnectorContext } from \"./types\";\nimport type { Ontology } from \"../ontology\";\nimport type { Provider } from \"../llm/provider\";\nimport { fetchText } from \"../util/content\";\nimport { rssConnector } from \"./rss\";\nimport { urlConnector } from \"./url\";\nimport { gdeltConnector } from \"./gdelt\";\nimport { fredConnector } from \"./fred\";\nimport { edgarConnector } from \"./edgar\";\nimport { govDataConnector } from \"./govdata\";\nimport { webSearchConnector } from \"./web-search\";\n\n/**\n * The connector registry. Adding a source (RSS, a government feed, a new API)\n * is: implement SourceConnector in a module, add one line here, add config\n * entries to config/sources.ts. The cron, workflow, extractor, and writer are\n * untouched — they only speak RawItem / GraphFragment.\n */\nconst REGISTRY = new Map<string, SourceConnector>([\n [rssConnector.id, rssConnector as SourceConnector],\n [urlConnector.id, urlConnector as SourceConnector],\n [gdeltConnector.id, gdeltConnector as SourceConnector],\n [fredConnector.id, fredConnector as SourceConnector], // structured (RawItem.fragment)\n [edgarConnector.id, edgarConnector as SourceConnector], // structured (RawItem.fragment)\n [webSearchConnector.id, webSearchConnector as SourceConnector], // discovery (web search)\n [govDataConnector.id, govDataConnector as SourceConnector], // template — zero core change to add\n]);\n\n/** Resolve a built-in source connector by its stable registry id. */\nexport function getConnector(id: string): SourceConnector | undefined {\n return REGISTRY.get(id);\n}\n\n/** List registered connector ids in deterministic insertion order. */\nexport function connectorIds(): string[] {\n return [...REGISTRY.keys()];\n}\n\n/** Everything a ConnectorContext needs, supplied by the host (worker, script, test). */\nexport interface ContextConfig {\n ontology: Ontology;\n provider: () => Provider;\n /** Named secrets (API keys, user agents) — e.g. the worker Env, or a plain object. */\n secrets?: Record<string, string | undefined>;\n now?: Date;\n}\n\n/** Create a host-supplied connector context with lazy provider construction. */\nexport function makeContext(cfg: ContextConfig): ConnectorContext {\n let provider: Provider | undefined; // lazy: only discovery connectors need it\n return {\n now: cfg.now ?? new Date(),\n fetchText: (u) => fetchText(u),\n secret: (name) => cfg.secrets?.[name],\n ontology: cfg.ontology,\n provider: () => (provider ??= cfg.provider()),\n };\n}\n","import type { ConnectorJob } from \"./connectors/types\";\n\n/**\n * Sources are DATA, not code. Each entry names a connector (`kind`) + its config\n * + scheduling. Add a feed → add an entry; no redeploy of the pipeline. P5 moves\n * this into KV behind a /config admin endpoint so edits don't need a deploy at all.\n */\nexport interface SourceEntry {\n kind: string; // connector id (registry key)\n name: string;\n enabled?: boolean; // default true\n cadenceHours?: number; // default 1 — runs when UTC hour % cadence === 0\n watch?: string | null;\n // connector-specific config (rss: url; more in P3):\n url?: string;\n query?: string;\n [key: string]: unknown;\n}\n\n/** Return source entries not explicitly disabled, preserving configured order. */\nexport function enabledSources(sources: SourceEntry[]): SourceEntry[] {\n return sources.filter((s) => s.enabled !== false);\n}\n\nfunction toJob(s: SourceEntry): ConnectorJob {\n return { kind: s.kind, name: s.name, watch: s.watch ?? null, cfg: s };\n}\n\n/** Enabled sources whose cadence lands on this UTC hour. */\nexport function jobsForHour(sources: SourceEntry[], hour: number): ConnectorJob[] {\n return enabledSources(sources)\n .filter((s) => hour % Math.max(1, Math.floor(s.cadenceHours ?? 1)) === 0)\n .map(toJob);\n}\n\n/** Every enabled source, ignoring cadence (manual /trigger?kind=sources). */\nexport function allJobs(sources: SourceEntry[]): ConnectorJob[] {\n return enabledSources(sources).map(toJob);\n}\n","import type { RawItem } from \"../types\";\nimport type { SourceEntry } from \"../sources\";\nimport { urlHash } from \"../util/identity\";\n\nconst SEEN_TTL_S = 120 * 24 * 60 * 60; // 120 days\nconst ENRICHED_TTL_S = 30 * 24 * 60 * 60; // 30-day enrichment cooldown\nconst TEMPORAL_TTL_S = 120 * 24 * 60 * 60; // dated temporal-report history copies\n\n/**\n * The slice of a KV store the pipeline needs. Cloudflare's KVNamespace\n * satisfies it structurally; tests pass an in-memory map. Keeping the library\n * off Workers types is what lets it publish as a plain isomorphic package.\n */\nexport interface KVLike {\n get(key: string): Promise<string | null>;\n put(key: string, value: string, opts?: { expirationTtl?: number }): Promise<void>;\n}\n\n/**\n * Operational state in KV (never the graph itself — that lives in odla-db).\n * Ported/trimmed from agnacl services/kv.ts: seen-URL dedup + lastrun stamps.\n */\nexport class Store {\n constructor(private kv: KVLike) {}\n\n private seenKey(url: string): string {\n return `seen:${urlHash(url)}`;\n }\n\n async hasSeen(url: string): Promise<boolean> {\n return (await this.kv.get(this.seenKey(url))) !== null;\n }\n\n async markSeen(url: string): Promise<void> {\n await this.kv.put(this.seenKey(url), \"1\", { expirationTtl: SEEN_TTL_S });\n }\n\n /** Drop items whose URL we've already processed (checked BEFORE any LLM work). */\n async filterNew(items: RawItem[]): Promise<RawItem[]> {\n const flags = await Promise.all(items.map((it) => this.hasSeen(it.url)));\n return items.filter((_, i) => !flags[i]);\n }\n\n async setLastRun(kind: string, at = new Date().toISOString()): Promise<void> {\n await this.kv.put(`lastrun:${kind}`, at);\n }\n\n async getLastRun(kind: string): Promise<string | null> {\n return this.kv.get(`lastrun:${kind}`);\n }\n\n /** Per-entity enrichment cooldown so we don't re-search the same node constantly. */\n async isEnrichCooling(key: string): Promise<boolean> {\n return (await this.kv.get(`enriched:${key}`)) !== null;\n }\n\n async markEnriched(key: string): Promise<void> {\n await this.kv.put(`enriched:${key}`, new Date().toISOString(), { expirationTtl: ENRICHED_TTL_S });\n }\n\n /** Runtime source config (edit without redeploy). null = fall back to the static SOURCES. */\n async getConfigSources(): Promise<SourceEntry[] | null> {\n const raw = await this.kv.get(\"config:sources\");\n if (!raw) return null;\n try {\n const v = JSON.parse(raw);\n return Array.isArray(v) ? (v as SourceEntry[]) : null;\n } catch {\n return null;\n }\n }\n\n async setConfigSources(list: SourceEntry[]): Promise<void> {\n await this.kv.put(\"config:sources\", JSON.stringify(list));\n }\n\n /** Cache the latest temporal report (`temporal:latest`) plus a dated history copy. */\n async setTemporalReport(report: unknown, date = new Date().toISOString().slice(0, 10)): Promise<void> {\n const raw = JSON.stringify(report);\n await this.kv.put(\"temporal:latest\", raw);\n await this.kv.put(`temporal:${date}`, raw, { expirationTtl: TEMPORAL_TTL_S });\n }\n\n /** The cached latest temporal report, or null when absent/unparseable. */\n async getTemporalReport<T = unknown>(): Promise<T | null> {\n const raw = await this.kv.get(\"temporal:latest\");\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n }\n}\n","/**\n * Cloudflare Workflow hardening — ported verbatim from agnacl\n * (utils/workflow-helpers.ts). resilientStep adds structured error logging\n * without changing retry semantics; the step configs replace the engine's slow\n * default retry with fast exponential backoff.\n */\n\nexport async function resilientStep<T>(stepName: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const stack = err instanceof Error ? err.stack : undefined;\n console.error(`[STEP_ERROR] ${stepName}: ${msg}`);\n if (stack) console.error(`[STEP_STACK] ${stepName}: ${stack}`);\n throw err;\n }\n}\n\n/** I/O-bound steps: KV reads, feed fetches. */\nexport const IO_STEP_CONFIG = {\n retries: { limit: 3, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"2 minutes\" as const,\n};\n\n/** Graph write steps. */\nexport const COMMIT_STEP_CONFIG = {\n retries: { limit: 3, delay: \"3 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"90 seconds\" as const,\n};\n\n/** LLM generation steps (can be slow). */\nexport const LLM_STEP_CONFIG = {\n retries: { limit: 2, delay: \"5 seconds\" as const, backoff: \"exponential\" as const },\n timeout: \"3 minutes\" as const,\n};\n\n/** Retry/timeout shape of the step configs above. */\nexport interface StepConfig {\n retries?: { limit: number; delay: string; backoff?: string };\n timeout?: string;\n}\n\n/**\n * Structural view of Cloudflare's WorkflowStep. WorkflowStep.do constrains its\n * result to Rpc.Serializable<T>, which cannot terminate (TS2589) on the\n * recursive Value type now that the @odla/* workspace packages give\n * @odla-ai/db's re-exports real definitions. A live WorkflowStep satisfies\n * this shape at runtime; widen through asStepLike at the workflow boundary.\n */\nexport interface StepLike {\n do<T>(name: string, fn: () => Promise<T>): Promise<T>;\n do<T>(name: string, config: StepConfig, fn: () => Promise<T>): Promise<T>;\n}\n\n/** Widen a WorkflowStep to StepLike (type-level only, no runtime cost). */\nexport function asStepLike(step: unknown): StepLike {\n return step as StepLike;\n}\n","import { LLM_STEP_CONFIG, type StepLike, resilientStep } from \"../workflow/helpers\";\nimport type { ExtractResult, RawItem } from \"../types\";\nimport type { AdminDb } from \"@odla-ai/db\";\nimport type { Store } from \"../state/kv\";\nimport type { Provider } from \"../llm/provider\";\nimport type { Ontology } from \"../ontology\";\nimport { writeFragment } from \"../graph/writer\";\nimport { canonicalizeFragment } from \"../graph/resolve\";\nimport { urlHash } from \"../util/identity\";\n\n/** Everything the shared ingest loop needs, supplied by the host. The\n * extractor is the ontology's domain craft (e.g. the OSINT extractor); the\n * library stays ontology-agnostic. */\nexport interface PipelineDeps {\n db: AdminDb;\n provider: Provider;\n ontology: Ontology;\n extract: (provider: Provider, item: RawItem) => Promise<ExtractResult | null>;\n store: Store;\n}\n\n/**\n * Shared per-item processing: each RawItem is its own step (non-blocking,\n * replay-cached). Structured items (fragment) skip the LLM; text items extract.\n * `resolve` FTS-canonicalizes new nodes against existing ones (discovery path).\n * Used by both the ingest (fetch) and discover (web-search) workflows.\n */\nexport async function ingestFresh(\n step: StepLike,\n deps: PipelineDeps,\n fresh: RawItem[],\n kind: string,\n resolve = false,\n): Promise<void> {\n const { db, provider, ontology, extract, store } = deps;\n if (fresh.length === 0) {\n await store.setLastRun(kind);\n return;\n }\n\n for (const item of fresh) {\n const id = urlHash(item.url);\n await step.do(`item-${id}`, LLM_STEP_CONFIG, () =>\n resilientStep(`item-${id}`, async () => {\n try {\n let fragment = item.fragment;\n if (!fragment) {\n const r = await extract(provider, item);\n if (!r) {\n await store.markSeen(item.url);\n return { skipped: true };\n }\n fragment = r.fragment;\n }\n if (resolve) fragment = await canonicalizeFragment(db, fragment, ontology);\n const { txId, ops } = await writeFragment(db, fragment, ontology, `ingest:${id}:v1`);\n await store.markSeen(item.url);\n return { txId, ops };\n } catch (e) {\n console.log(`ingest item failed (non-blocking): ${item.url}: ${e instanceof Error ? e.message : e}`);\n return { error: true };\n }\n }),\n );\n }\n\n await step.do(`stamp-${kind}`, () =>\n resilientStep(`stamp-${kind}`, async () => {\n await store.setLastRun(kind);\n return { ok: true };\n }),\n );\n}\n"],"mappings":";;;;;AAoDO,SAAS,aAAoB;AAClC,SAAO,EAAE,OAAO,GAAG,aAAa,GAAG,cAAc,EAAE;AACrD;AAGO,SAAS,SAAS,MAAa,MAAmB;AACvD,OAAK,SAAS,KAAK;AACnB,OAAK,eAAe,KAAK;AACzB,OAAK,gBAAgB,KAAK;AAC5B;;;ACbO,SAAS,cAAc,GAA0B;AACtD,QAAM,IAAiB,CAAC;AACxB,aAAW,CAAC,MAAM,CAAC,KAAK,OAAO,QAAQ,EAAE,QAAQ,EAAG,GAAE,IAAI,IAAI,EAAE;AAChE,SAAO;AACT;AAGO,SAAS,aAAa,GAAwD;AACnF,QAAM,MAAiD,CAAC;AACxD,aAAW,CAAC,MAAM,CAAC,KAAK,OAAO,QAAQ,EAAE,QAAQ,GAAG;AAClD,QAAI,IAAI,IAAI,CAAC;AACb,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,EAAE,KAAK,EAAG,KAAI,IAAI,EAAE,IAAI,IAAI,KAAK,SAAS;AAAA,EACtF;AACA,SAAO;AACT;AAGO,SAAS,aAAa,GAA0C;AACrE,QAAM,MAAmC,CAAC;AAC1C,aAAW,CAAC,MAAM,CAAC,KAAK,OAAO,QAAQ,EAAE,QAAQ,GAAG;AAClD,QAAI,IAAI,IAAI,IAAI,IAAI,OAAO,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAAA,EACxF;AACA,SAAO;AACT;AAGO,SAAS,mBAAmB,GAA+B;AAChE,QAAM,WAA6C,CAAC;AACpD,aAAW,CAAC,MAAM,CAAC,KAAK,OAAO,QAAQ,EAAE,QAAQ,GAAG;AAClD,UAAM,QAAwC,CAAC;AAC/C,eAAW,CAAC,MAAM,CAAC,KAAK,OAAO,QAAQ,EAAE,KAAK,GAAG;AAC/C,YAAM,IAAI,IAAI;AAAA,QACZ,MAAM,EAAE;AAAA,QACR,QAAQ,CAAC,CAAC,EAAE;AAAA,QACZ,SAAS,CAAC,CAAC,EAAE;AAAA,QACb,UAAU,CAAC,CAAC,EAAE;AAAA,MAChB;AAAA,IACF;AACA,aAAS,IAAI,IAAI,EAAE,MAAM;AAAA,EAC3B;AACA,QAAM,QAAwC,CAAC;AAC/C,aAAW,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,EAAE,KAAK,GAAG;AAChD,UAAM,IAAI,IAAI;AAAA,MACZ,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,GAAG,cAAc,QAAQ,OAAO,GAAG,aAAa;AAAA,MAC7E,SAAS,EAAE,IAAI,GAAG,IAAI,KAAK,GAAG,cAAc,QAAQ,OAAO,GAAG,aAAa;AAAA,IAC7E;AAAA,EACF;AACA,SAAO,EAAE,UAAU,MAAM;AAC3B;;;AC7FA,SAAS,QAAQ,GAA+B;AAC9C,SAAO,MAAM,UAAa,MAAM,QAAQ,MAAM;AAChD;AAOO,SAAS,YACd,MACA,UACA,UACmB;AACnB,MAAI,QAAQ,QAAQ,EAAG,QAAO;AAC9B,MAAI,QAAQ,QAAQ,EAAG,QAAO;AAC9B,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO,OAAO,QAAQ,IAAI,OAAO,QAAQ,IAAI,WAAW;AAAA,IAC1D,KAAK;AACH,aAAO,OAAO,QAAQ,IAAI,OAAO,QAAQ,IAAI,WAAW;AAAA,IAC1D,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;AAOO,SAAS,WACd,OACA,UACA,UACuB;AACvB,QAAM,MAA6B,CAAC;AACpC,aAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAClD,UAAM,SAAS,YAAY,MAAM,IAAI,KAAK,WAAW,SAAS,IAAI,GAAG,GAAG;AACxE,QAAI,WAAW,OAAW,KAAI,IAAI,IAAI;AAAA,EACxC;AACA,SAAO;AACT;;;ACvBO,SAAS,cAAc,UAAyB,MAAyB;AAC9E,QAAM,MAAM,CAAC,MAAc,QAA2B;AACpD,UAAM,OAAO,KAAK,IAAI;AACtB,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,4CAA4C,IAAI,GAAG;AAC9E,WAAO,EAAE,IAAI,MAAM,MAAM,OAAO,IAAI;AAAA,EACtC;AACA,QAAM,MAAY,CAAC;AACnB,aAAW,KAAK,SAAS,OAAO;AAC9B,UAAM,OAAO,KAAK,EAAE,IAAI;AACxB,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,4CAA4C,EAAE,IAAI,GAAG;AAChF,QAAI,KAAK,EAAE,GAAG,UAAU,IAAI,EAAE,MAAM,IAAI,EAAE,IAAI,EAAE,MAAM,MAAM,OAAO,EAAE,IAAI,GAAG,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC;AAAA,EACpH;AACA,aAAW,KAAK,SAAS,OAAO;AAC9B,QAAI,KAAK,EAAE,GAAG,QAAQ,IAAI,EAAE,KAAK,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,EAAE,KAAK,GAAG,GAAG,OAAO,EAAE,OAAO,QAAQ,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC;AAAA,EAC7H;AACA,SAAO;AACT;AAUA,eAAsB,cACpB,IACA,UACA,UACA,YACwC;AACxC,QAAM,OAAO,cAAc,QAAQ;AACnC,QAAM,QAAQ,aAAa,QAAQ;AACnC,QAAM,SAAS,aAAa,QAAQ;AAGpC,QAAM,SAAS,oBAAI,IAAgD;AACnE,aAAW,KAAK,SAAS,OAAO;AAC9B,QAAI,CAAC,KAAK,EAAE,IAAI,EAAG,OAAM,IAAI,MAAM,4CAA4C,EAAE,IAAI,GAAG;AACxF,UAAM,QAAQ,OAAO,EAAE,IAAI,KAAK,oBAAI,IAAY;AAChD,UAAM,QAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAAE,KAAK,EAAG,KAAI,CAAC,MAAM,IAAI,CAAC,EAAG,OAAM,CAAC,IAAI;AAC5E,UAAM,UAAU,OAAO,IAAI,EAAE,IAAI,KAAK,oBAAI,IAAmC;AAC7E,UAAM,OAAO,QAAQ,IAAI,EAAE,GAAG;AAC9B,YAAQ,IAAI,EAAE,KAAK,OAAO,WAAW,MAAM,EAAE,IAAI,GAAI,MAAM,KAAK,IAAI,KAAK;AACzE,WAAO,IAAI,EAAE,MAAM,OAAO;AAAA,EAC5B;AAGA,QAAM,QAAsB,CAAC;AAC7B,aAAW,CAAC,MAAM,CAAC,KAAK,QAAQ;AAC9B,UAAM,IAAI,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,KAAK,IAAI,CAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE;AAAA,EAC1E;AACA,QAAM,WAAwB,OAAO,KAAK,KAAK,EAAE,SAAS,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;AACnF,QAAM,UAAU,CAAC,MAAc,QAAuC;AACpE,UAAM,OAAO,KAAK,IAAI;AACtB,eAAW,OAAO,SAAS,IAAI,KAAK,CAAC,GAAG;AACtC,UAAI,IAAI,IAAI,MAAM,IAAK,QAAO;AAAA,IAChC;AACA,WAAO,CAAC;AAAA,EACV;AAGA,QAAM,MAAY,CAAC;AACnB,aAAW,CAAC,MAAM,CAAC,KAAK,QAAQ;AAC9B,UAAM,OAAO,KAAK,IAAI;AACtB,eAAW,CAAC,KAAK,QAAQ,KAAK,GAAG;AAC/B,YAAM,SAAS,WAAW,MAAM,IAAI,GAAI,QAAQ,MAAM,GAAG,GAAG,QAAQ;AACpE,UAAI,KAAK,EAAE,GAAG,UAAU,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,MAAM,OAAO,IAAI,GAAG,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;AAAA,IAC3G;AAAA,EACF;AACA,QAAM,MAAM,CAAC,MAAc,SAA4B,EAAE,IAAI,MAAM,MAAM,KAAK,IAAI,GAAI,OAAO,IAAI;AACjG,aAAW,KAAK,SAAS,OAAO;AAC9B,QAAI,KAAK,EAAE,GAAG,QAAQ,IAAI,EAAE,KAAK,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,EAAE,KAAK,GAAG,GAAG,OAAO,EAAE,OAAO,QAAQ,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC;AAAA,EAC7H;AAEA,MAAI,IAAI,WAAW,EAAG,QAAO,EAAE,MAAM,GAAG,KAAK,EAAE;AAC/C,QAAM,EAAE,KAAK,IAAI,MAAM,GAAG,SAAS,KAAK,EAAE,WAAW,CAAC;AACtD,SAAO,EAAE,MAAM,KAAK,IAAI,OAAO;AACjC;;;AC7DA,SAAS,UAAU,UAAoB,MAAc,QAAuC;AAC1F,SAAO,OAAO,IAAI,CAAC,UAAU;AAC3B,eAAW,QAAQ,OAAO,OAAO,SAAS,KAAK,GAAG;AAChD,UAAI,KAAK,iBAAiB,SAAS,KAAK,SAAS,MAAM;AACrD,eAAO,EAAE,OAAO,aAAa,KAAK,IAAI,WAAW,WAAoB,cAAc,KAAK,aAAa;AAAA,MACvG;AACA,UAAI,KAAK,iBAAiB,SAAS,KAAK,OAAO,MAAM;AACnD,eAAO,EAAE,OAAO,aAAa,KAAK,MAAM,WAAW,WAAoB,cAAc,KAAK,aAAa;AAAA,MACzG;AAAA,IACF;AACA,UAAM,IAAI,MAAM,kDAAkD,KAAK,SAAS,IAAI,GAAG;AAAA,EACzF,CAAC;AACH;AAEA,IAAM,WAAW,CAAC,MAAe,MAAM,QAAS,OAAO,MAAM,YAAY,OAAO,MAAM;AAEtF,SAAS,UAAU,KAA8B,MAAwB,OAA0B;AACjG,QAAM,MAA0B,CAAC;AACjC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACxC,QAAI,KAAK,QAAQ,CAAC,KAAK,MAAM,SAAS,CAAC,IAAI,MAAM,QAAQ,MAAM,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,EAAG;AAC5F,QAAI,SAAS,CAAC,KAAK,MAAM,KAAM,KAAI,CAAC,IAAI;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,IAAM,0BAA0B;AAchC,eAAsB,eAAe,IAAa,UAAoB,MAA0C;AAC9G,QAAM,OAAO,cAAc,QAAQ;AACnC,QAAM,WAAW,KAAK,IAAI,KAAM,KAAK,IAAI,GAAG,KAAK,YAAY,GAAG,CAAC;AACjE,QAAM,QAAQ,oBAAI,IAAuB;AACzC,QAAM,QAAQ,oBAAI,IAAuB;AAEzC,QAAM,UAAU,CAAC,SAAoB;AACnC,UAAM,KAAK,GAAG,KAAK,IAAI,IAAI,KAAK,GAAG;AACnC,UAAM,WAAW,MAAM,IAAI,EAAE;AAC7B,QAAI,CAAC,YAAY,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,OAAO,KAAK,SAAS,KAAK,EAAE,OAAQ,OAAM,IAAI,IAAI,IAAI;AAAA,EAC1G;AAEA,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,QAAQ,GAAG;AAC1D,UAAM,aAAa,KAAK,IAAI;AAC5B,QAAI,CAAC,WAAY,OAAM,IAAI,MAAM,uCAAuC,IAAI,GAAG;AAC/E,UAAM,QAAQ,UAAU,UAAU,MAAM,OAAO,SAAS,CAAC,CAAC;AAC1D,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,SAAS,OAAO,SAAS,YAAY,OAAO,OAAO,IAAI,IAAI;AACjE,QAAI,WAAW,OAAO,QAAQ,YAAY;AAC1C,QAAI,SAAS;AACb,QAAI,QAAQ;AAEZ,WAAO,QAAQ,OAAO;AACpB,YAAM,OAAgC;AAAA,QACpC,OAAO,KAAK,IAAI,UAAU,QAAQ,KAAK;AAAA,QACvC;AAAA,QACA,OAAO,EAAE,CAAC,UAAU,GAAG,MAAM;AAAA,MAC/B;AACA,UAAI,YAAY,OAAO,OAAQ,MAAK,QAAQ,EAAE,CAAC,OAAO,OAAO,QAAQ,GAAG,EAAE,MAAM,OAAO,OAAO,KAAK,EAAE;AACrG,YAAM,QAAQ;AAAA,QACZ,CAAC,IAAI,GAAG,EAAE,GAAG,MAAM,GAAG,OAAO,YAAY,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;AAAA,MAC5E;AAEA,UAAI;AACJ,UAAI;AACF,gBAAS,MAAM,GAAG,MAAM,KAAK,GAAiC,IAAI,KAAuC,CAAC;AAAA,MAC5G,SAAS,KAAK;AACZ,YAAI,YAAY,wBAAwB,KAAK,OAAQ,IAAc,WAAW,GAAG,CAAC,GAAG;AAInF,qBAAW;AACX,mBAAS;AACT,kBAAQ;AACR;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAEA,iBAAW,OAAO,MAAM;AACtB,cAAM,MAAM,IAAI,UAAU;AAC1B,YAAI,QAAQ,UAAa,QAAQ,KAAM;AACvC,YAAI,OAAO,UAAU,WAAW,MAAM;AACpC,gBAAM,IAAI,YAAY,IAAI,OAAO,OAAO,QAAQ,CAAC;AACjD,cAAI,MAAM,QAAQ,IAAI,OAAQ;AAAA,QAChC;AACA,cAAM,UAAU,EAAE,MAAM,KAAK,OAAO,GAAG,EAAE;AACzC,gBAAQ,EAAE,GAAG,SAAS,OAAO,UAAU,KAAK,QAAQ,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC;AAEzE,mBAAW,QAAQ,OAAO;AACxB,gBAAM,gBAAgB,KAAK,KAAK,WAAW;AAC3C,cAAI,CAAC,cAAe,OAAM,IAAI,MAAM,uCAAuC,KAAK,WAAW,GAAG;AAE9F,gBAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,gBAAM,aAAa,MAAM,QAAQ,GAAG,IAAI,MAAM,MAAM,CAAC,GAAG,IAAI,CAAC;AAC7D,qBAAW,UAAU,YAA8C;AACjE,kBAAM,YAAY,OAAO,aAAa;AACtC,gBAAI,cAAc,UAAa,cAAc,KAAM;AACnD,kBAAM,YAAY,EAAE,MAAM,KAAK,aAAa,KAAK,OAAO,SAAS,EAAE;AACnE,oBAAQ,EAAE,GAAG,WAAW,OAAO,UAAU,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;AAC1D,kBAAM,OACJ,KAAK,cAAc,YACf,EAAE,MAAM,SAAS,IAAI,WAAW,OAAO,KAAK,aAAa,IACzD,EAAE,MAAM,WAAW,IAAI,SAAS,OAAO,KAAK,aAAa;AAC/D,kBAAM,IAAI,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,GAAG,IAAI,IAAI;AAAA,UACnG;AAAA,QACF;AACA;AACA,YAAI,SAAS,MAAO;AAAA,MACtB;AAEA,UAAI,KAAK,SAAS,SAAU;AAC5B,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,GAAkC,MAC/C,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI;AACxF,SAAO;AAAA,IACL,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,KAAK,KAAK;AAAA,IACrC,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE;AAAA,MACzB,CAAC,GAAG,MAAM,MAAM,EAAE,MAAM,EAAE,IAAI,MAAM,EAAE,QAAQ,EAAE,QAAQ,KAAK,EAAE,QAAQ,EAAE,QAAQ,IAAI,MAAM,MAAM,EAAE,IAAI,EAAE,EAAE;AAAA,IAC7G;AAAA,EACF;AACF;;;AC5KA,eAAsB,eAAe,IAAa,MAAc,GAAW,QAAQ,GAAsB;AACvG,MAAI,CAAC,EAAE,KAAK,EAAG,QAAO,CAAC;AACvB,QAAM,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM,EAAE,EAAE,CAAC;AAClE,SAAQ,IAAI,IAAI,KAAK,CAAC;AACxB;AAEA,IAAM,OAAO,CAAC,MAAsB,EAAE,YAAY,EAAE,QAAQ,eAAe,EAAE;AAO7E,eAAsB,YACpB,IACA,MACA,SACA,MACwB;AACxB,QAAM,SAAS,KAAK,IAAI;AACxB,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,KAAK,MAAM,eAAe,IAAI,MAAM,MAAM,CAAC,GAAG;AACvD,UAAM,MAAM,EAAE,OAAO;AACrB,UAAM,QAAQ,EAAE,MAAM;AACtB,QAAI,OAAO,QAAQ,SAAU;AAC7B,QAAK,OAAO,UAAU,YAAY,KAAK,KAAK,MAAM,UAAW,KAAK,GAAG,MAAM,OAAQ,QAAO;AAAA,EAC5F;AACA,SAAO;AACT;AAOA,eAAsB,qBACpB,IACA,UACA,UACwB;AACxB,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,KAAK,SAAS,OAAO;AAC9B,UAAM,OAAO,SAAS,SAAS,EAAE,IAAI;AACrC,QAAI,CAAC,MAAM,YAAY,OAAQ;AAC/B,UAAM,OAAO,OAAO,EAAE,MAAM,SAAS,WAAW,EAAE,MAAM,OAAO;AAC/D,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,MAAM,YAAY,IAAI,EAAE,MAAM,KAAK,YAAY,IAAI;AACjE,QAAI,SAAS,UAAU,EAAE,IAAK,OAAM,IAAI,GAAG,EAAE,IAAI,IAAI,EAAE,GAAG,IAAI,KAAK;AAAA,EACrE;AACA,MAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,QAAM,KAAK,CAAC,MAAc,QAAwB,MAAM,IAAI,GAAG,IAAI,IAAI,GAAG,EAAE,KAAK;AACjF,SAAO;AAAA,IACL,OAAO,SAAS,MAAM,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,KAAK,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;AAAA,IACnE,OAAO,SAAS,MAAM,IAAI,CAAC,OAAO;AAAA,MAChC,MAAM,EAAE,MAAM,EAAE,KAAK,MAAM,KAAK,GAAG,EAAE,KAAK,MAAM,EAAE,KAAK,GAAG,EAAE;AAAA,MAC5D,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE;AAAA,MACpD,OAAO,EAAE;AAAA,IACX,EAAE;AAAA,EACJ;AACF;;;ACjEA,SAAS,MAAM,mBAA4B;AAepC,IAAM,cAA0F;AAAA,EACrG,QAAQ,EAAE,OAAO,oBAAoB,SAAS,mBAAmB,QAAQ,kBAAkB;AAAA,EAC3F,QAAQ,EAAE,OAAO,cAAc,SAAS,cAAc,QAAQ,mBAAmB;AACnF;AA4CA,SAAS,QAAQ,GAAyD;AACxE,SAAO,EAAE,OAAO,GAAG,aAAa,EAAE,aAAa,cAAc,EAAE,aAAa;AAC9E;AAGO,SAAS,eAAe,KAA0B;AACvD,QAAM,QAAQ,YAAY,IAAI,QAAQ;AACtC,QAAM,KAAK,IAAI,MAAM,KAAK,EAAE,MAAM,EAAE,WAAW,IAAI,cAAc,QAAQ,IAAI,UAAU,EAAE,CAAC;AAC1F,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR;AAAA,IACA,MAAM,QAAqB,GAAgB;AACzC,YAAM,EAAE,OAAO,MAAM,IAAI,MAAM,GAAG,QAAW;AAAA,QAC3C,OAAO,EAAE,SAAS,MAAM;AAAA,QACxB,QAAQ,EAAE;AAAA,QACV,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,WAAW,EAAE;AAAA,MACf,CAAC;AACD,aAAO,EAAE,OAAO,OAAO,QAAQ,KAAK,EAAE;AAAA,IACxC;AAAA,IACA,MAAM,OAAO,GAAe;AAC1B,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,OAAO;AAAA,QACtC,OAAO,EAAE,SAAS,MAAM;AAAA,QACxB,QAAQ,EAAE;AAAA,QACV,MAAM,EAAE;AAAA,QACR,WAAW,EAAE;AAAA,MACf,CAAC;AACD,aAAO,EAAE,MAAM,OAAO,QAAQ,KAAK,EAAE;AAAA,IACvC;AAAA,IACA,MAAM,MAAM,GAAc;AACxB,YAAM,MAAM,MAAM,GAAG,KAAK;AAAA,QACxB,OAAO,EAAE,SAAS,MAAM;AAAA,QACxB,QAAQ,EAAE;AAAA,QACV,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,EAAE,KAAK,CAAC;AAAA,QAC5C,WAAW,EAAE,aAAa;AAAA,MAC5B,CAAC;AACD,aAAO,EAAE,MAAM,YAAY,IAAI,OAAO,GAAG,OAAO,QAAQ,IAAI,KAAK,EAAE;AAAA,IACrE;AAAA,EACF;AACF;AAWO,SAAS,YAAY,KAAuB;AACjD,QAAM,SAAyB,IAAI,gBAAgB,UAAU,YAAY,MAAM,WAAW,WAAW;AACrG,MAAI,UAAU,YAAY,CAAC,IAAI,eAAgB,OAAM,IAAI,MAAM,iDAAiD;AAChH,MAAI,UAAU,YAAY,CAAC,IAAI,kBAAmB,OAAM,IAAI,MAAM,oDAAoD;AACtH,SAAO,eAAe,EAAE,UAAU,OAAO,cAAc,IAAI,mBAAmB,WAAW,IAAI,eAAe,CAAC;AAC/G;;;ACjHO,SAAS,YAAyB,MAAwB;AAC/D,QAAM,IAAI,KAAK,MAAM,aAAa;AAClC,MAAI,CAAC,EAAG,QAAO;AACf,MAAI;AACF,WAAO,KAAK,MAAM,EAAE,CAAC,CAAC;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAA8B,MAAmB;AAC/D,QAAM,IAAI,KAAK,MAAM,aAAa;AAClC,MAAI,CAAC,EAAG,QAAO,CAAC;AAChB,MAAI;AACF,UAAM,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;AACzB,WAAO,MAAM,QAAQ,CAAC,IAAK,IAAY,CAAC;AAAA,EAC1C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;ACpBA,IAAM,YAAY;AAGX,SAAS,WAAW,MAAc,WAAW,WAAmB;AACrE,MAAI,IAAI;AACR,MAAI,EAAE,QAAQ,+BAA+B,GAAG;AAChD,MAAI,EAAE,QAAQ,6BAA6B,GAAG;AAC9C,MAAI,EAAE,QAAQ,oBAAoB,GAAG;AACrC,MAAI,EAAE,QAAQ,8CAA8C,IAAI;AAChE,MAAI,EAAE,QAAQ,YAAY,GAAG;AAC7B,MAAI,eAAe,CAAC;AACpB,MAAI,EAAE,QAAQ,eAAe,GAAG,EAAE,QAAQ,eAAe,MAAM,EAAE,KAAK;AACtE,SAAO,EAAE,MAAM,GAAG,QAAQ;AAC5B;AAEA,SAAS,eAAe,GAAmB;AACzC,SAAO,EACJ,QAAQ,YAAY,GAAG,EACvB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,GAAG,EACrB,QAAQ,YAAY,GAAG,EACvB,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,aAAa,CAAC,GAAG,MAAM,OAAO,aAAa,OAAO,CAAC,CAAC,CAAC,EAC7D,QAAQ,qBAAqB,CAAC,GAAG,MAAM,OAAO,aAAa,SAAS,GAAG,EAAE,CAAC,CAAC;AAChF;AAGA,eAAsB,UAAU,KAAa,WAAW,WAA4B;AAClF,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,SAAS;AAAA,QACP,cAAc;AAAA,QACd,QAAQ;AAAA,MACV;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,KAAK,IAAI,QAAQ,IAAI,cAAc,KAAK;AAC9C,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,GAAG,SAAS,MAAM,KAAK,YAAY,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,GAAG;AAC/D,aAAO,WAAW,MAAM,QAAQ;AAAA,IAClC;AACA,WAAO,KAAK,MAAM,GAAG,QAAQ;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC9CA,IAAM,KAAK;AAEX,SAAS,WAAW,KAAa,KAAqB;AACpD,QAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,IAAI,GAAG,mDAAmD,GAAG,KAAK,GAAG,CAAC;AACzG,MAAI,MAAO,QAAO,MAAM,CAAC,EAAG,KAAK;AACjC,QAAM,IAAI,IAAI,MAAM,IAAI,OAAO,IAAI,GAAG,uBAAuB,GAAG,KAAK,GAAG,CAAC;AACzE,SAAO,IAAI,EAAE,CAAC,EAAG,KAAK,IAAI;AAC5B;AAEA,SAAS,YAAY,KAAa,KAAa,MAAsB;AACnE,QAAM,IAAI,IAAI,MAAM,IAAI,OAAO,IAAI,GAAG,WAAW,IAAI,qBAAqB,GAAG,CAAC;AAC9E,SAAO,IAAI,EAAE,CAAC,EAAG,KAAK,IAAI;AAC5B;AAEA,SAAS,WAAW,KAAuB;AACzC,SAAO,IAAI,MAAM,8BAA8B,KAAK,IAAI,MAAM,gCAAgC,KAAK,CAAC;AACtG;AAQO,IAAM,eAAwC;AAAA,EACnD,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,MAAM,YAAY,MAAM;AAAA,EAE/C,YAAY,KAAa;AACvB,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,QAAQ,YAAY,CAAC,EAAE,IAAK,OAAM,IAAI,MAAM,gCAAgC;AACzF,WAAO,EAAE,KAAK,EAAE,KAAK,MAAM,OAAO,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAQ,EAAE,SAA2B,KAAK;AAAA,EAChG;AAAA,EAEA,MAAM,MAAM,KAAK,KAAyB;AACxC,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,cAAc,GAAG,EAAE,CAAC;AAClE,UAAI,CAAC,IAAI,IAAI;AACX,YAAI,IAAI,WAAW,OAAO,IAAI,WAAW,OAAO,IAAI,UAAU,IAAK,QAAO,CAAC;AAC3E,cAAM,IAAI,MAAM,QAAQ,IAAI,GAAG,WAAM,IAAI,MAAM,EAAE;AAAA,MACnD;AACA,YAAM,MAAM,IAAI,KAAK;AAAA,IACvB,SAAS,GAAG;AACV,cAAQ,IAAI,oCAAoC,IAAI,GAAG,KAAK,CAAC,EAAE;AAC/D,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,MAAiB,CAAC;AACxB,eAAW,OAAO,WAAW,GAAG,GAAG;AACjC,YAAM,QAAQ,WAAW,KAAK,OAAO;AACrC,UAAI,MAAM,WAAW,KAAK,MAAM,KAAK,YAAY,KAAK,QAAQ,MAAM;AACpE,UAAI,CAAC,SAAS,CAAC,IAAK;AACpB,YAAM,IAAI,KAAK;AACf,YAAM,OACJ,WAAW,KAAK,SAAS,KAAK,WAAW,KAAK,WAAW,KAAK,WAAW,KAAK,SAAS,KAAK;AAC9F,YAAM,UACJ,WAAW,KAAK,iBAAiB,KACjC,WAAW,KAAK,SAAS,KACzB,WAAW,KAAK,aAAa,KAC7B,WAAW,KAAK,SAAS,KACzB;AACF,UAAI,OAAO,WAAW,OAAO;AAE7B,UAAI,KAAK,SAAS,KAAK;AACrB,cAAM,OAAO,MAAM,IAAI,UAAU,GAAG;AACpC,YAAI,KAAK,SAAS,KAAK,OAAQ,QAAO;AAAA,MACxC;AACA,UAAI,KAAK,EAAE,KAAK,OAAO,MAAM,YAAY,IAAI,MAAM,YAAY,OAAO,OAAO,IAAI,OAAO,KAAK,CAAC;AAAA,IAChG;AACA,WAAO;AAAA,EACT;AACF;;;ACxEA,eAAsB,SAAS,KAA+B;AAC5D,QAAM,OAAO,MAAM,UAAU,GAAG;AAChC,QAAM,YAAY,KAAK,MAAM,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK;AACvE,MAAI,OAAO;AACX,MAAI;AACF,WAAO,IAAI,IAAI,GAAG,EAAE;AAAA,EACtB,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,KAAK,QAAQ,aAAa,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,GAAG,YAAY,MAAM,YAAY,OAAO,KAAK;AAC1G;AAOO,IAAM,eAAwC;AAAA,EACnD,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,MAAM,YAAY,MAAM;AAAA,EAC/C,YAAY,KAAa;AACvB,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,QAAQ,YAAY,CAAC,EAAE,IAAK,OAAM,IAAI,MAAM,gCAAgC;AACzF,WAAO,EAAE,KAAK,EAAE,KAAK,OAAQ,EAAE,SAA2B,KAAK;AAAA,EACjE;AAAA,EACA,MAAM,MAAM,KAAyB;AACnC,UAAM,OAAO,MAAM,SAAS,IAAI,GAAG;AACnC,QAAI,IAAI,UAAU,OAAW,MAAK,QAAQ,IAAI;AAC9C,WAAO,CAAC,IAAI;AAAA,EACd;AACF;;;AC/BA,IAAMA,MAAK;AACX,IAAM,YAAY;AAQlB,SAAS,gBAAgB,GAA+B;AACtD,QAAM,IAAI,EAAE,MAAM,iDAAiD;AACnE,SAAO,IAAI,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM;AAClE;AAEO,IAAM,iBAA4C;AAAA,EACvD,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,MAAM,YAAY,MAAM;AAAA,EAE/C,YAAY,KAAe;AACzB,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,UAAU,YAAY,CAAC,EAAE,MAAO,OAAM,IAAI,MAAM,oCAAoC;AACjG,WAAO,EAAE,OAAO,EAAE,OAAO,MAAM,OAAO,EAAE,QAAQ,UAAU,EAAE,KAAK,EAAE,GAAG,OAAQ,EAAE,SAA2B,KAAK;AAAA,EAClH;AAAA,EAEA,MAAM,MAAM,KAAK,KAAyB;AACxC,UAAM,MACJ,qDAAqD,mBAAmB,IAAI,KAAK,CAAC;AAGpF,QAAI,WAA0F,CAAC;AAC/F,QAAI;AAGF,YAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,cAAcA,IAAG,GAAG,QAAQ,YAAY,QAAQ,IAAM,EAAE,CAAC;AACnG,UAAI,CAAC,IAAI,GAAI,QAAO,CAAC;AACrB,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI;AACF,mBAAY,KAAK,MAAM,IAAI,EAAqC,YAAY,CAAC;AAAA,MAC/E,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF,SAAS,GAAG;AACV,cAAQ,IAAI,sCAAsC,CAAC,EAAE;AACrD,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,SAAS,SACZ,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,SAAS,EAAE,aAAa,aAAa,CAAC,KAAK,IAAI,EAAE,GAAI,MAAM,KAAK,IAAI,EAAE,GAAI,GAAG,KAAK,EAC7G,MAAM,GAAG,SAAS;AAErB,UAAM,MAAiB,CAAC;AACxB,eAAW,KAAK,QAAQ;AACtB,YAAM,OAAO,MAAM,IAAI,UAAU,EAAE,GAAI;AACvC,UAAI,KAAK,SAAS,IAAK;AACvB,UAAI,KAAK;AAAA,QACP,KAAK,EAAE;AAAA,QACP,OAAO,EAAE;AAAA,QACT,MAAM,gBAAgB,EAAE,YAAY,EAAE;AAAA,QACtC,YAAY,IAAI;AAAA,QAChB,YAAY;AAAA,QACZ,OAAO,IAAI;AAAA,QACX,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;;;AChEO,SAAS,QAAQ,OAAuB;AAC7C,SAAO,MACJ,UAAU,MAAM,EAChB,QAAQ,oBAAoB,EAAE,EAC9B,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,EACX,QAAQ,QAAQ,EAAE;AACvB;AAGO,SAAS,aAAa,KAAqB;AAChD,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,MAAE,OAAO;AACT,UAAM,OAAO,CAAC,UAAU,UAAU,UAAU,aAAa,YAAY,OAAO;AAC5E,eAAW,OAAO,CAAC,GAAG,EAAE,aAAa,KAAK,CAAC,GAAG;AAC5C,UAAI,KAAK,KAAK,CAAC,OAAO,GAAG,KAAK,GAAG,CAAC,EAAG,GAAE,aAAa,OAAO,GAAG;AAAA,IAChE;AACA,QAAI,IAAI,EAAE,SAAS;AACnB,QAAI,EAAE,QAAQ,OAAO,EAAE;AACvB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,IAAI,KAAK,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,EAAE;AAAA,EACzD;AACF;AAGO,SAAS,QAAQ,KAAqB;AAC3C,QAAM,IAAI,aAAa,GAAG;AAC1B,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,SAAK,EAAE,WAAW,CAAC;AACnB,QAAI,KAAK,KAAK,GAAG,QAAU;AAAA,EAC7B;AACA,UAAQ,MAAM,GAAG,SAAS,EAAE;AAC9B;AAOO,SAAS,mBAAmB,MAAuB;AACxD,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,CAAC,KAAK,KAAK,KAAK,CAAC,EAAG,QAAO;AAC/B,QAAM,QAAQ,EAAE,MAAM,KAAK;AAC3B,MAAI,MAAM,SAAS,KAAK,MAAM,SAAS,EAAG,QAAO;AACjD,SAAO,MAAM,MAAM,CAAC,MAAM,uBAAuB,KAAK,CAAC,CAAC;AAC1D;;;ACzCA,eAAe,YACb,UACA,QACA,OACiD;AACjD,QAAM,MACJ,iEAAiE,mBAAmB,QAAQ,CAAC,YACjF,mBAAmB,MAAM,CAAC,qCAAqC,mBAAmB,KAAK,CAAC;AACtG,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,QAAI,CAAC,IAAI,GAAI,QAAO,CAAC;AACrB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,UAAM,MAA8C,CAAC;AACrD,eAAW,KAAK,KAAK,gBAAgB,CAAC,GAAG;AACvC,UAAI,CAAC,GAAG,QAAQ,EAAE,UAAU,UAAa,EAAE,UAAU,IAAK;AAC1D,YAAM,IAAI,OAAO,EAAE,KAAK;AACxB,UAAI,OAAO,SAAS,CAAC,EAAG,KAAI,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,CAAC;AAAA,IAC7D;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,YAAQ,IAAI,yBAAyB,QAAQ,oBAAoB,CAAC,EAAE;AACpE,WAAO,CAAC;AAAA,EACV;AACF;AAEO,IAAM,gBAA0C;AAAA,EACrD,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,MAAM,YAAY,KAAK;AAAA,EAE9C,YAAY,KAAc;AACxB,UAAM,IAAI;AACV,UAAM,SAAS,MAAM,QAAQ,EAAE,MAAM,IAAK,EAAE,SAA+B,CAAC;AAC5E,QAAI,CAAC,OAAO,OAAQ,OAAM,IAAI,MAAM,8CAA8C;AAClF,WAAO,EAAE,QAAQ,QAAQ,EAAE,QAA8B,YAAY,EAAE,WAAiC;AAAA,EAC1G;AAAA,EAEA,MAAM,MAAM,KAAK,KAAyB;AACxC,UAAM,SAAS,IAAI,OAAO,cAAc;AACxC,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI,0CAAqC;AACjD,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,IAAI,MAAM,KAAK,OAAO,GAAI,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC5F,UAAM,MAAiB,CAAC;AAExB,eAAW,KAAK,IAAI,QAAQ;AAC1B,YAAM,MAAM,MAAM,YAAY,EAAE,IAAI,QAAQ,KAAK;AACjD,UAAI,CAAC,IAAI,OAAQ;AACjB,YAAM,SAAS,IAAI,IAAI,SAAS,CAAC;AACjC,YAAM,QAAQ,IAAI,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC,IAAI;AAErD,YAAM,QAA+B,EAAE,OAAO,EAAE,OAAO,QAAQ,OAAO,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK;AACxG,UAAI,MAAO,OAAM,QAAQ,OAAO,MAAM,KAAK;AAC3C,UAAI,EAAE,KAAM,OAAM,OAAO,EAAE;AAE3B,YAAM,WAA0B,EAAE,OAAO,CAAC,EAAE,MAAM,aAAa,KAAK,EAAE,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE;AAC9F,UAAI,IAAI,QAAQ;AACd,cAAM,QAAQ,QAAQ,IAAI,MAAM;AAChC,YAAI,OAAO;AACT,mBAAS,MAAM,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,OAAO,EAAE,MAAM,IAAI,cAAc,IAAI,OAAO,EAAE,CAAC;AACjG,mBAAS,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,KAAK,MAAM,GAAG,IAAI,EAAE,MAAM,aAAa,KAAK,EAAE,GAAG,GAAG,OAAO,gBAAgB,CAAC;AAAA,QAC5H;AAAA,MACF;AAEA,UAAI,KAAK;AAAA,QACP,KAAK,sCAAsC,EAAE,EAAE,IAAI,OAAO,IAAI;AAAA,QAC9D,OAAO,GAAG,EAAE,KAAK,KAAK,OAAO,KAAK,KAAK,OAAO,IAAI;AAAA,QAClD,MAAM,OAAO;AAAA,QACb,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;;;AChFA,IAAM,cAAc;AACpB,IAAM,cAAc;AAQpB,IAAM,UAAU,CAAC,QAAwB,IAAI,QAAQ,OAAO,EAAE,KAAK;AACnE,IAAM,WAAW,CAAC,KAAa,SAC7B,2CAA2C,QAAQ,GAAG,CAAC,IAAI,KAAK,QAAQ,MAAM,EAAE,CAAC,IAAI,IAAI;AAE3F,SAAS,QAAQ,OAAe,KAAqB;AACnD,QAAM,IAAI,MAAM,MAAM,IAAI,OAAO,IAAI,GAAG,+BAA+B,GAAG,KAAK,GAAG,CAAC;AACnF,SAAO,IAAI,EAAE,CAAC,EAAG,KAAK,IAAI;AAC5B;AAaO,IAAM,iBAA4C;AAAA,EACvD,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,MAAM,YAAY,KAAK;AAAA,EAE9C,YAAY,KAAe;AACzB,UAAM,IAAI;AACV,UAAM,QAAQ,MAAM,QAAQ,EAAE,aAAa,IAAK,EAAE,gBAA6B,CAAC;AAChF,QAAI,CAAC,MAAM,OAAQ,OAAM,IAAI,MAAM,wDAAwD;AAC3F,WAAO;AAAA,MACL,eAAe;AAAA,MACf,YAAY,MAAM,QAAQ,EAAE,UAAU,IAAK,EAAE,aAA0B;AAAA,MACvE,MAAM,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAK,KAAyB;AACxC,UAAM,KAAK,IAAI,OAAO,kBAAkB;AACxC,QAAI,CAAC,IAAI;AACP,cAAQ,IAAI,+CAA0C;AACtD,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ,IAAI,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE;AAC/C,UAAM,UAAU,IAAI,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,QAAQ,KAAK,KAAK,OAAO,GAAI,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC1G,UAAM,YACJ,GAAG,WAAW,6BAA6B,mBAAmB,IAAI,cAAc,KAAK,GAAG,CAAC,CAAC,YAC9E,OAAO,UAAU,KAAK;AAEpC,QAAI,OAAmB,CAAC;AACxB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,WAAW,EAAE,SAAS,EAAE,cAAc,GAAG,EAAE,CAAC;AACpE,UAAI,CAAC,IAAI,GAAI,QAAO,CAAC;AACrB,cAAS,MAAM,IAAI,KAAK,GAAwC,MAAM,QAAQ,CAAC;AAAA,IACjF,SAAS,GAAG;AACV,cAAQ,IAAI,uCAAuC,CAAC,EAAE;AACtD,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,MAAiB,CAAC;AACxB,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,OAAO,KAAK,MAAM,GAAG,WAAW,GAAG;AAC5C,YAAM,MAAM,IAAI,WAAW,CAAC;AAC5B,YAAM,OAAO,IAAI,QAAQ;AACzB,UAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,EAAG;AAC7B,WAAK,IAAI,IAAI;AACb,YAAM,MAAM,IAAI,OAAO,CAAC,KAAK;AAC7B,YAAM,QAAQ,IAAI,gBAAgB,CAAC,KAAK,IAAI,QAAQ,yBAAyB,EAAE,EAAE,KAAK;AACtF,UAAI,CAAC,QAAQ,CAAC,IAAK;AAEnB,YAAM,MAAM,IAAI,gBAAgB,CAAC,KAAK;AACtC,UAAI,OAAO,IAAI,KAAK;AACpB,UAAI,QAAQ,IAAI,aAAa,CAAC,KAAK;AACnC,YAAM,QAAQ,IAAI,YAAY,GAAG;AACjC,UAAI,SAAS,GAAG;AACd,eAAO,IAAI,MAAM,GAAG,KAAK,EAAE,KAAK;AAChC,gBAAQ,IAAI,MAAM,QAAQ,CAAC,EAAE,KAAK,KAAK;AAAA,MACzC;AACA,UAAI,IAAI,YAAY,UAAU,CAAC,IAAI,WAAW,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,KAAK,YAAY,CAAC,EAAG;AAEnG,YAAM,MAAM,SAAS,KAAK,IAAI;AAC9B,YAAM,WAAW,IAAI,aAAa;AAGlC,UAAI,WAAW;AACf,UAAI,WAAW;AACf,YAAM,UAAoD,CAAC;AAC3D,UAAI;AACF,cAAM,YAAY,2CAA2C,QAAQ,GAAG,CAAC,IAAI,KAAK,QAAQ,MAAM,EAAE,CAAC;AACnG,cAAM,OAAO,MAAM,MAAM,WAAW,EAAE,SAAS,EAAE,cAAc,GAAG,EAAE,CAAC;AACrE,YAAI,KAAK,IAAI;AACX,gBAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,qBAAW,QAAQ,KAAK,mBAAmB;AAC3C,qBAAW,QAAQ,KAAK,qBAAqB;AAC7C,qBAAW,SAAS,IAAI,MAAM,oDAAoD,KAAK,CAAC,GAAG;AACzF,kBAAM,QAAQ,CAAC,QAAQ,OAAO,WAAW,GAAG,QAAQ,OAAO,UAAU,CAAC,EACnE,OAAO,CAAC,MAAM,KAAK,MAAM,GAAG,EAC5B,KAAK,GAAG,EACR,KAAK;AACR,gBAAI,CAAC,MAAO;AACZ,kBAAM,QAAkB,CAAC;AACzB,kBAAM,QAAQ;AACd,gBAAI;AACJ,oBAAQ,IAAI,MAAM,KAAK,KAAK,OAAO,MAAM;AACvC,oBAAM,IAAI,EAAE,CAAC,EAAG,KAAK;AACrB,kBAAI,KAAK,CAAC,MAAM,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,YAC3C;AACA,oBAAQ,KAAK,EAAE,MAAM,OAAO,MAAM,CAAC;AAAA,UACrC;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,YAAM,QAAQ,QAAQ,IAAI;AAC1B,UAAI,CAAC,MAAO;AACZ,YAAM,IAAmB,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAChD,YAAM,SAAgC,EAAE,KAAK;AAC7C,UAAI,KAAM,QAAO,OAAO;AACxB,UAAI,MAAO,QAAO,SAAS;AAC3B,UAAI,SAAU,QAAO,SAAS;AAC9B,UAAI,UAAU;AACZ,eAAO,aAAa;AACpB,eAAO,YAAY;AAAA,MACrB;AACA,QAAE,MAAM,KAAK,EAAE,MAAM,WAAW,KAAK,OAAO,OAAO,OAAO,CAAC;AAE3D,UAAI,OAAO;AACT,cAAM,QAAQ,QAAQ,KAAK;AAC3B,YAAI,OAAO;AACT,YAAE,MAAM,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,OAAO,EAAE,MAAM,MAAM,EAAE,CAAC;AACnE,YAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,KAAK,MAAM,GAAG,IAAI,EAAE,MAAM,UAAU,KAAK,MAAM,GAAG,OAAO,aAAa,CAAC;AAAA,QACjH;AAAA,MACF;AAEA,YAAM,OAAO,GAAG,KAAK,WAAW,QAAQ;AACxC,YAAM,SAAgC,EAAE,OAAO,UAAU,YAAY,IAAI;AACzE,UAAI,SAAU,QAAO,SAAS;AAC9B,UAAI,SAAU,QAAO,OAAO;AAC5B,QAAE,MAAM,KAAK,EAAE,MAAM,iBAAiB,KAAK,MAAM,OAAO,OAAO,CAAC;AAChE,QAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,KAAK,KAAK,GAAG,IAAI,EAAE,MAAM,WAAW,KAAK,MAAM,GAAG,OAAO,WAAW,CAAC;AAEnH,iBAAW,KAAK,SAAS;AACvB,YAAI,CAAC,mBAAmB,EAAE,IAAI,EAAG;AACjC,cAAM,QAAQ,QAAQ,EAAE,IAAI;AAC5B,YAAI,CAAC,MAAO;AACZ,cAAM,SAAgC,EAAE,MAAM,EAAE,KAAK;AACrD,YAAI,EAAE,MAAM,OAAQ,QAAO,OAAO,EAAE,MAAM,KAAK,IAAI;AACnD,UAAE,MAAM,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,OAAO,OAAO,CAAC;AAC1D,UAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,KAAK,MAAM,GAAG,IAAI,EAAE,MAAM,WAAW,KAAK,MAAM,GAAG,OAAO,WAAW,CAAC;AAAA,MAC/G;AAEA,UAAI,KAAK;AAAA,QACP;AAAA,QACA,OAAO,WAAW,IAAI,GAAG,WAAW,WAAM,QAAQ,KAAK,EAAE;AAAA,QACzD,MAAM,YAAY;AAAA,QAClB,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;;;AC9JO,IAAM,mBAAgD;AAAA,EAC3D,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,MAAM,YAAY,KAAK;AAAA,EAE9C,YAAY,KAAiB;AAC3B,UAAM,IAAI;AACV,WAAO,EAAE,SAAS,OAAO,EAAE,WAAW,EAAE,GAAG,OAAQ,EAAE,SAA2B,KAAK;AAAA,EACvF;AAAA,EAEA,MAAM,MAAM,KAAK,MAA4C;AAC3D,YAAQ,IAAI,6CAA6C,IAAI,OAAO,6CAAwC;AAC5G,WAAO,CAAC;AAAA,EACV;AACF;;;ACpBA,IAAM,eACJ;AAEK,IAAM,qBAAoD;AAAA,EAC/D,IAAI;AAAA,EACJ,cAAc,EAAE,OAAO,OAAO,UAAU,MAAM,YAAY,MAAM;AAAA,EAEhE,YAAY,KAAmB;AAC7B,UAAM,IAAI;AACV,UAAM,UAAU,MAAM,QAAQ,EAAE,OAAO,IAClC,EAAE,QAAQ,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,IAC9C,OAAO,EAAE,UAAU,WACjB,CAAC,EAAE,KAAK,IACR,CAAC;AACP,QAAI,CAAC,QAAQ,OAAQ,OAAM,IAAI,MAAM,wDAAwD;AAC7F,WAAO,EAAE,SAAS,MAAM,OAAO,EAAE,QAAQ,YAAY,GAAG,OAAQ,EAAE,SAA2B,KAAK;AAAA,EACpG;AAAA,EAEA,MAAM,QAA4B;AAChC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAM,SAAS,KAAK,KAA2C;AAC7D,UAAM,WAAW,IAAI,SAAS;AAC9B,UAAM,QAAQ,IAAI,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE;AAC/C,UAAM,MAAiB,CAAC;AACxB,eAAW,KAAK,IAAI,SAAS;AAC3B,YAAM,EAAE,KAAK,IAAI,MAAM,SAAS,OAAO;AAAA,QACrC,QAAQ;AAAA,QACR,MAAM,gBAAgB,CAAC;AAAA,gBAAmB,KAAK;AAAA;AAAA;AAAA,MACjD,CAAC;AACD,UAAI,KAAK,KAAK,EAAE,SAAS,GAAI;AAC7B,UAAI,KAAK;AAAA,QACP,KAAK,uBAAuB,QAAQ,CAAC,CAAC,IAAI,KAAK;AAAA;AAAA,QAC/C,OAAO;AAAA,QACP,MAAM;AAAA,QACN,YAAY,IAAI;AAAA,QAChB,YAAY;AAAA,QACZ,OAAO,IAAI;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;;;AC1CA,IAAM,WAAW,oBAAI,IAA6B;AAAA,EAChD,CAAC,aAAa,IAAI,YAA+B;AAAA,EACjD,CAAC,aAAa,IAAI,YAA+B;AAAA,EACjD,CAAC,eAAe,IAAI,cAAiC;AAAA,EACrD,CAAC,cAAc,IAAI,aAAgC;AAAA;AAAA,EACnD,CAAC,eAAe,IAAI,cAAiC;AAAA;AAAA,EACrD,CAAC,mBAAmB,IAAI,kBAAqC;AAAA;AAAA,EAC7D,CAAC,iBAAiB,IAAI,gBAAmC;AAAA;AAC3D,CAAC;AAGM,SAAS,aAAa,IAAyC;AACpE,SAAO,SAAS,IAAI,EAAE;AACxB;AAGO,SAAS,eAAyB;AACvC,SAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAC5B;AAYO,SAAS,YAAY,KAAsC;AAChE,MAAI;AACJ,SAAO;AAAA,IACL,KAAK,IAAI,OAAO,oBAAI,KAAK;AAAA,IACzB,WAAW,CAAC,MAAM,UAAU,CAAC;AAAA,IAC7B,QAAQ,CAAC,SAAS,IAAI,UAAU,IAAI;AAAA,IACpC,UAAU,IAAI;AAAA,IACd,UAAU,MAAO,aAAa,IAAI,SAAS;AAAA,EAC7C;AACF;;;ACrCO,SAAS,eAAe,SAAuC;AACpE,SAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,YAAY,KAAK;AAClD;AAEA,SAAS,MAAM,GAA8B;AAC3C,SAAO,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,MAAM,OAAO,EAAE,SAAS,MAAM,KAAK,EAAE;AACtE;AAGO,SAAS,YAAY,SAAwB,MAA8B;AAChF,SAAO,eAAe,OAAO,EAC1B,OAAO,CAAC,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,gBAAgB,CAAC,CAAC,MAAM,CAAC,EACvE,IAAI,KAAK;AACd;AAGO,SAAS,QAAQ,SAAwC;AAC9D,SAAO,eAAe,OAAO,EAAE,IAAI,KAAK;AAC1C;;;AClCA,IAAM,aAAa,MAAM,KAAK,KAAK;AACnC,IAAM,iBAAiB,KAAK,KAAK,KAAK;AACtC,IAAM,iBAAiB,MAAM,KAAK,KAAK;AAgBhC,IAAM,QAAN,MAAY;AAAA,EACjB,YAAoB,IAAY;AAAZ;AAAA,EAAa;AAAA,EAAb;AAAA,EAEZ,QAAQ,KAAqB;AACnC,WAAO,QAAQ,QAAQ,GAAG,CAAC;AAAA,EAC7B;AAAA,EAEA,MAAM,QAAQ,KAA+B;AAC3C,WAAQ,MAAM,KAAK,GAAG,IAAI,KAAK,QAAQ,GAAG,CAAC,MAAO;AAAA,EACpD;AAAA,EAEA,MAAM,SAAS,KAA4B;AACzC,UAAM,KAAK,GAAG,IAAI,KAAK,QAAQ,GAAG,GAAG,KAAK,EAAE,eAAe,WAAW,CAAC;AAAA,EACzE;AAAA;AAAA,EAGA,MAAM,UAAU,OAAsC;AACpD,UAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM,IAAI,CAAC,OAAO,KAAK,QAAQ,GAAG,GAAG,CAAC,CAAC;AACvE,WAAO,MAAM,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAAA,EACzC;AAAA,EAEA,MAAM,WAAW,MAAc,MAAK,oBAAI,KAAK,GAAE,YAAY,GAAkB;AAC3E,UAAM,KAAK,GAAG,IAAI,WAAW,IAAI,IAAI,EAAE;AAAA,EACzC;AAAA,EAEA,MAAM,WAAW,MAAsC;AACrD,WAAO,KAAK,GAAG,IAAI,WAAW,IAAI,EAAE;AAAA,EACtC;AAAA;AAAA,EAGA,MAAM,gBAAgB,KAA+B;AACnD,WAAQ,MAAM,KAAK,GAAG,IAAI,YAAY,GAAG,EAAE,MAAO;AAAA,EACpD;AAAA,EAEA,MAAM,aAAa,KAA4B;AAC7C,UAAM,KAAK,GAAG,IAAI,YAAY,GAAG,KAAI,oBAAI,KAAK,GAAE,YAAY,GAAG,EAAE,eAAe,eAAe,CAAC;AAAA,EAClG;AAAA;AAAA,EAGA,MAAM,mBAAkD;AACtD,UAAM,MAAM,MAAM,KAAK,GAAG,IAAI,gBAAgB;AAC9C,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI;AACF,YAAM,IAAI,KAAK,MAAM,GAAG;AACxB,aAAO,MAAM,QAAQ,CAAC,IAAK,IAAsB;AAAA,IACnD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiB,MAAoC;AACzD,UAAM,KAAK,GAAG,IAAI,kBAAkB,KAAK,UAAU,IAAI,CAAC;AAAA,EAC1D;AAAA;AAAA,EAGA,MAAM,kBAAkB,QAAiB,QAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAAkB;AACpG,UAAM,MAAM,KAAK,UAAU,MAAM;AACjC,UAAM,KAAK,GAAG,IAAI,mBAAmB,GAAG;AACxC,UAAM,KAAK,GAAG,IAAI,YAAY,IAAI,IAAI,KAAK,EAAE,eAAe,eAAe,CAAC;AAAA,EAC9E;AAAA;AAAA,EAGA,MAAM,oBAAoD;AACxD,UAAM,MAAM,MAAM,KAAK,GAAG,IAAI,iBAAiB;AAC/C,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI;AACF,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACtFA,eAAsB,cAAiB,UAAkB,IAAkC;AACzF,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,QAAQ,eAAe,QAAQ,IAAI,QAAQ;AACjD,YAAQ,MAAM,gBAAgB,QAAQ,KAAK,GAAG,EAAE;AAChD,QAAI,MAAO,SAAQ,MAAM,gBAAgB,QAAQ,KAAK,KAAK,EAAE;AAC7D,UAAM;AAAA,EACR;AACF;AAGO,IAAM,iBAAiB;AAAA,EAC5B,SAAS,EAAE,OAAO,GAAG,OAAO,aAAsB,SAAS,cAAuB;AAAA,EAClF,SAAS;AACX;AAGO,IAAM,qBAAqB;AAAA,EAChC,SAAS,EAAE,OAAO,GAAG,OAAO,aAAsB,SAAS,cAAuB;AAAA,EAClF,SAAS;AACX;AAGO,IAAM,kBAAkB;AAAA,EAC7B,SAAS,EAAE,OAAO,GAAG,OAAO,aAAsB,SAAS,cAAuB;AAAA,EAClF,SAAS;AACX;AAqBO,SAAS,WAAW,MAAyB;AAClD,SAAO;AACT;;;AC/BA,eAAsB,YACpB,MACA,MACA,OACA,MACA,UAAU,OACK;AACf,QAAM,EAAE,IAAI,UAAU,UAAU,SAAS,MAAM,IAAI;AACnD,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,MAAM,WAAW,IAAI;AAC3B;AAAA,EACF;AAEA,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,QAAQ,KAAK,GAAG;AAC3B,UAAM,KAAK;AAAA,MAAG,QAAQ,EAAE;AAAA,MAAI;AAAA,MAAiB,MAC3C,cAAc,QAAQ,EAAE,IAAI,YAAY;AACtC,YAAI;AACF,cAAI,WAAW,KAAK;AACpB,cAAI,CAAC,UAAU;AACb,kBAAM,IAAI,MAAM,QAAQ,UAAU,IAAI;AACtC,gBAAI,CAAC,GAAG;AACN,oBAAM,MAAM,SAAS,KAAK,GAAG;AAC7B,qBAAO,EAAE,SAAS,KAAK;AAAA,YACzB;AACA,uBAAW,EAAE;AAAA,UACf;AACA,cAAI,QAAS,YAAW,MAAM,qBAAqB,IAAI,UAAU,QAAQ;AACzE,gBAAM,EAAE,MAAM,IAAI,IAAI,MAAM,cAAc,IAAI,UAAU,UAAU,UAAU,EAAE,KAAK;AACnF,gBAAM,MAAM,SAAS,KAAK,GAAG;AAC7B,iBAAO,EAAE,MAAM,IAAI;AAAA,QACrB,SAAS,GAAG;AACV,kBAAQ,IAAI,sCAAsC,KAAK,GAAG,KAAK,aAAa,QAAQ,EAAE,UAAU,CAAC,EAAE;AACnG,iBAAO,EAAE,OAAO,KAAK;AAAA,QACvB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,KAAK;AAAA,IAAG,SAAS,IAAI;AAAA,IAAI,MAC7B,cAAc,SAAS,IAAI,IAAI,YAAY;AACzC,YAAM,MAAM,WAAW,IAAI;AAC3B,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB,CAAC;AAAA,EACH;AACF;","names":["UA"]}
|