import { e as GraphDef } from '../types-BynPp5kU.cjs'; export { C as ContributionDiagnostic, a as ContributionDiagnosticState, b as ContributionRepairEntry, c as ContributionRepairResult } from '../types-BynPp5kU.cjs'; import { b as Store } from '../store-3nfPQK5j.cjs'; export { B as BatchReadBuilder, C as CompiledOneStatementRead } from '../store-3nfPQK5j.cjs'; import { z } from 'zod'; import { I as IngestionImportTarget } from '../ingestion-import-target-C5sHNFJF.cjs'; import '../searchable-C7Xt6g45.cjs'; import '../resolve-CoYqHqno.cjs'; /** * Graph interchange format types. * * Defines Zod schemas for importing and exporting graph data. * The format is designed to be: * - JSON-serializable for API transport * - Validated via Zod at runtime * - Exportable as JSON Schema for LLM/API documentation */ /** * Current interchange format version — the value every export writes. * * Read-side compatibility policy: the format is versioned separately from what * it *accepts*. A 1.0 document is a structurally valid 2.0 document — the only * 2.0 addition is the optional `identity` section — so import and * {@link GraphDataSchema} accept both `"1.0"` and `"2.0"` (see * {@link AcceptedFormatVersionSchema}), while exports keep writing this * constant. Bump this only when exports must emit a new version; add the old * value to the accepted set rather than dropping read compatibility. */ declare const FORMAT_VERSION: "2.0"; /** * Interchange format for a node. * * Properties are stored as a record to allow schema-agnostic transport. * Validation against the actual schema happens during import. */ declare const InterchangeNodeSchema: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; properties: z.ZodRecord; validFrom: z.ZodOptional>; validTo: z.ZodOptional; meta: z.ZodOptional; createdAt: z.ZodOptional; updatedAt: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>; type InterchangeNode = z.infer; /** * Interchange format for an edge. * * Endpoint references are objects with kind and id, rather than * composite strings, for clarity and type safety. */ declare const InterchangeEdgeSchema: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; from: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; to: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; properties: z.ZodDefault>; validFrom: z.ZodOptional>; validTo: z.ZodOptional; meta: z.ZodOptional; updatedAt: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>; type InterchangeEdge = z.infer; declare const IdentityInterchangeModeSchema: z.ZodEnum<{ state: "state"; archival: "archival"; }>; type IdentityInterchangeMode = z.infer; declare const InterchangeIdentityAssertionSchema: z.ZodObject<{ id: z.ZodString; relation: z.ZodEnum<{ same: "same"; different: "different"; }>; a: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; b: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; validFrom: z.ZodISODateTime; validTo: z.ZodOptional; endedBy: z.ZodOptional>; }, z.core.$strip>; type InterchangeIdentityAssertion = z.infer; declare const InterchangeIdentitySchema: z.ZodObject<{ profile: z.ZodLiteral<"typegraph-identity-v1">; mode: z.ZodEnum<{ state: "state"; archival: "archival"; }>; assertions: z.ZodArray; a: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; b: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; validFrom: z.ZodISODateTime; validTo: z.ZodOptional; endedBy: z.ZodOptional>; }, z.core.$strip>>; }, z.core.$strip>; type InterchangeIdentity = z.infer; /** * Strategy for handling conflicts when a node/edge ID already exists. * * - `skip`: Keep existing, ignore incoming * - `update`: Merge incoming properties into existing * - `error`: Throw an error on conflict */ declare const ConflictStrategySchema: z.ZodEnum<{ error: "error"; update: "update"; skip: "skip"; }>; type ConflictStrategy = z.infer; /** * Strategy for handling properties not defined in the schema. * * - `error`: Throw a validation error * - `strip`: Remove unknown properties silently * - `allow`: Pass through to storage (relies on backend behavior) */ declare const UnknownPropertyStrategySchema: z.ZodEnum<{ allow: "allow"; error: "error"; strip: "strip"; }>; type UnknownPropertyStrategy = z.infer; /** * Options for importing graph data. */ declare const ImportOptionsSchema: z.ZodObject<{ onConflict: z.ZodEnum<{ error: "error"; update: "update"; skip: "skip"; }>; onUnknownProperty: z.ZodDefault>; validateReferences: z.ZodDefault; batchSize: z.ZodDefault; refreshStatistics: z.ZodOptional; onStreamChunkError: z.ZodDefault>; }, z.core.$strip>; /** * Caller-facing options: fields with schema defaults are optional. * `importGraph` parses these once at its boundary; internal stages * consume {@link ResolvedImportOptions} with every default applied. */ type ImportOptions = z.input; /** * Source metadata for graph data. * * Uses discriminated union to capture different origin types: * - `typegraph-export`: Exported from a TypeGraph store * - `external`: From a third-party system */ declare const GraphDataSourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ type: z.ZodLiteral<"typegraph-export">; graphId: z.ZodString; schemaVersion: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"external">; description: z.ZodOptional; }, z.core.$strip>], "type">; type GraphDataSource = z.infer; /** * Complete graph data interchange format. * * The envelope contains metadata about the data source and format version, * plus arrays of nodes and edges to import. */ declare const GraphDataSchema: z.ZodObject<{ formatVersion: z.ZodEnum<{ "2.0": "2.0"; "1.0": "1.0"; }>; exportedAt: z.ZodISODateTime; source: z.ZodDiscriminatedUnion<[z.ZodObject<{ type: z.ZodLiteral<"typegraph-export">; graphId: z.ZodString; schemaVersion: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"external">; description: z.ZodOptional; }, z.core.$strip>], "type">; nodes: z.ZodArray; validFrom: z.ZodOptional>; validTo: z.ZodOptional; meta: z.ZodOptional; createdAt: z.ZodOptional; updatedAt: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>>; edges: z.ZodArray; to: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; properties: z.ZodDefault>; validFrom: z.ZodOptional>; validTo: z.ZodOptional; meta: z.ZodOptional; updatedAt: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>>; identity: z.ZodOptional; mode: z.ZodEnum<{ state: "state"; archival: "archival"; }>; assertions: z.ZodArray; a: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; b: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; validFrom: z.ZodISODateTime; validTo: z.ZodOptional; endedBy: z.ZodOptional>; }, z.core.$strip>>; }, z.core.$strip>>; }, z.core.$strip>; type GraphData = z.infer; /** The metadata envelope emitted before streamed graph entities. */ declare const GraphDataHeaderSchema: z.ZodObject<{ source: z.ZodDiscriminatedUnion<[z.ZodObject<{ type: z.ZodLiteral<"typegraph-export">; graphId: z.ZodString; schemaVersion: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"external">; description: z.ZodOptional; }, z.core.$strip>], "type">; formatVersion: z.ZodEnum<{ "2.0": "2.0"; "1.0": "1.0"; }>; exportedAt: z.ZodISODateTime; identity: z.ZodOptional; profile: z.ZodLiteral<"typegraph-identity-v1">; }, z.core.$strip>>; }, z.core.$strip>; type GraphDataHeader = z.infer; /** * One bounded unit in the streamed interchange protocol. Nodes always precede * edges, so an importer can validate endpoints from rows already written to its * target without retaining all prior nodes in memory. */ declare const GraphInterchangeChunkSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ type: z.ZodLiteral<"header">; header: z.ZodObject<{ source: z.ZodDiscriminatedUnion<[z.ZodObject<{ type: z.ZodLiteral<"typegraph-export">; graphId: z.ZodString; schemaVersion: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"external">; description: z.ZodOptional; }, z.core.$strip>], "type">; formatVersion: z.ZodEnum<{ "2.0": "2.0"; "1.0": "1.0"; }>; exportedAt: z.ZodISODateTime; identity: z.ZodOptional; profile: z.ZodLiteral<"typegraph-identity-v1">; }, z.core.$strip>>; }, z.core.$strip>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"nodes">; nodes: z.ZodArray; validFrom: z.ZodOptional>; validTo: z.ZodOptional; meta: z.ZodOptional; createdAt: z.ZodOptional; updatedAt: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"edges">; edges: z.ZodArray; to: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; properties: z.ZodDefault>; validFrom: z.ZodOptional>; validTo: z.ZodOptional; meta: z.ZodOptional; updatedAt: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"identity">; assertions: z.ZodArray; a: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; b: z.ZodObject<{ kind: z.ZodString; id: z.ZodString; }, z.core.$strip>; validFrom: z.ZodISODateTime; validTo: z.ZodOptional; endedBy: z.ZodOptional>; }, z.core.$strip>>; }, z.core.$strip>], "type">; type GraphInterchangeChunk = z.infer; /** * An error that occurred during import. */ declare const ImportErrorSchema: z.ZodObject<{ entityType: z.ZodEnum<{ node: "node"; edge: "edge"; identity: "identity"; }>; kind: z.ZodString; id: z.ZodString; error: z.ZodString; }, z.core.$strip>; type ImportError = z.infer; /** * Result of an import operation. * * Contains counts of created/updated/skipped entities and any errors. */ declare const ImportResultSchema: z.ZodObject<{ success: z.ZodBoolean; nodes: z.ZodObject<{ created: z.ZodNumber; updated: z.ZodNumber; skipped: z.ZodNumber; }, z.core.$strip>; edges: z.ZodObject<{ created: z.ZodNumber; updated: z.ZodNumber; skipped: z.ZodNumber; }, z.core.$strip>; identity: z.ZodObject<{ created: z.ZodNumber; skipped: z.ZodNumber; }, z.core.$strip>; errors: z.ZodArray; kind: z.ZodString; id: z.ZodString; error: z.ZodString; }, z.core.$strip>>; }, z.core.$strip>; type ImportResult = z.infer; /** * Options for exporting graph data. */ declare const ExportOptionsSchema: z.ZodObject<{ nodeKinds: z.ZodOptional>; edgeKinds: z.ZodOptional>; includeTemporal: z.ZodDefault; includeMeta: z.ZodDefault; includeDeleted: z.ZodDefault; identityMode: z.ZodDefault>; signal: z.ZodOptional>; }, z.core.$strip>; /** Export options with defaults applied (output type) */ type ExportOptions = z.infer; /** Export options as accepted by exportGraph (input type with optional defaults) */ type ExportOptionsInput = z.input; /** * Export options for {@link exportGraphStream}. `batchSize` bounds the number * of node or edge entities retained for one yielded chunk. */ declare const ExportStreamOptionsSchema: z.ZodObject<{ nodeKinds: z.ZodOptional>; edgeKinds: z.ZodOptional>; includeTemporal: z.ZodDefault; includeMeta: z.ZodDefault; includeDeleted: z.ZodDefault; identityMode: z.ZodDefault>; signal: z.ZodOptional>; idleTimeoutMs: z.ZodOptional; batchSize: z.ZodDefault; }, z.core.$strip>; type ExportStreamOptions = z.infer; type ExportStreamOptionsInput = z.input; /** * Export graph data from a store. * * @param store - The graph store to export from * @param options - Export configuration * @returns Graph data in interchange format * * @example * ```typescript * const data = await exportGraph(store, { * nodeKinds: ["Person", "Organization"], * includeMeta: true, * }); * * // Write to file * await fs.writeFile("backup.json", JSON.stringify(data, null, 2)); * ``` */ declare function exportGraph(store: Store, options?: ExportOptionsInput): Promise; /** * Exports a graph as bounded node and edge chunks. The stream always yields one * header, then every node chunk, then every edge chunk. Consumers that write to * a network, file, or fresh working copy can process one chunk at a time rather * than materializing a graph-sized {@link GraphData} value. * * ## One snapshot, WHERE THE BACKEND HAS TRANSACTIONS * * On a backend reporting `capabilities.execution.interactiveTransactions`, the whole export — * header, every node page, every edge page, every identity page — is read * inside ONE `repeatable_read` / `read_only` transaction, so a slow consumer * still gets one point in time rather than a mixture of the graph as it was at * the first chunk and as it is at the last. * * A backend WITHOUT transactions (SQLite `transactionMode: "none"`, the * session-less HTTP Postgres drivers) opens no such transaction: its export * paginates statement by statement, and a write committed mid-stream can appear * in the pages that follow. There is no way to offer the guarantee on an engine * that cannot frame the reads, so it is a declared capability gap rather than * something the stream pretends to. Callers needing a coherent export from such * a backend must quiesce writes for its duration. * * ## Stopping a stream you will not finish * * While a transactional stream is open it holds that snapshot transaction, and * on a serialized connection it holds the connection's one EXCLUSIVE stream * lease with it. Every cooperative exit settles both, because each runs the * generator's `finally`: `break` or `throw` out of a `for await`, and an * explicit `iterator.return()`. (A non-transactional stream holds neither, so * it has nothing to strand — but it still owes its consumer an answer, which * the same cancellation path gives it.) * * A consumer that pulls `next()` and then simply DROPS the iterator has no * cooperative exit — async-generator `finally` blocks do not run on garbage * collection — so it must pass {@link ExportOptions.signal} and abort it, or * configure {@link ExportStreamOptions.idleTimeoutMs}. The idle clock covers * only time after a chunk is delivered and before the consumer asks for the * next one; database read time is not consumer idleness. Without either * mechanism, the snapshot transaction stays open for the life of the process * and every later interchange stream on that connection is refused on behalf * of a stream nobody is reading. * * ### Why there is no garbage-collection safety net (#429) * * A `FinalizationRegistry` on the iterable cannot close this: it is not merely * unreliable here, it can never fire. The producer is interruptible in exactly * one place — it is parked in {@link RendezvousChannel.push} waiting for the * consumer, not in the database — so any cleanup state capable of settling an * abandoned stream has to reach that channel. A registry holds its held value * STRONGLY, and holding anything that reaches the channel's scope keeps the * abandoned generator permanently reachable: measured on Node 24, publishing * just `channel.abort` is enough to stop the stream being collected, while * publishing an unrelated object is not. The net's own bookkeeping would * therefore be what kept the entry from ever firing — a safety promise that * reads as protection and is not there. Explicit cancellation and the idle * timeout are the mechanisms, and they are contracts rather than hints. */ declare function exportGraphStream(store: Store, options?: ExportStreamOptionsInput): AsyncIterable; /** * Graph data import functionality. * * Imports nodes and edges from the interchange format into a store, * with configurable conflict resolution and validation. * * ## A write asserts EVERY component its verdict read * * `onConflict: "update"` is a read-then-write pair: this module PROBES the * stored row, decides from what it finds, and then writes. Under PostgreSQL * READ COMMITTED a concurrent `hardDelete` + recreate re-resolves the probed key * between those two statements, so any part of the verdict that is not restated * in the UPDATE's own `WHERE` is a decision that can land on a row it was never * computed for — and the import reports success, because the write did affect a * row. The failure is invisible and only appears under a race, which is why it * has taken three rounds to enumerate: * * - the edge's KIND — the probe is keyed on `(graph_id, id)` alone, so the row * under an id may be a different edge entirely; * - the edge's ENDPOINTS — kind is not an identity, and an upsert that resolved * an edge BY its endpoints must say so; * - the effective `valid_from` of BOTH entities — {@link * validateUpdateValidityWindow} decides from the stored lower bound, so a * recreate carrying a different one turns that verdict into a write that * ignores the document's `validFrom` or persists `valid_to < valid_from`. * * The rest of what these legs read, and where each is asserted: * * - the row EXISTS (the `getNode` / `getEdge` probe): restated as the * `(graph_id, kind, id)` / `(graph_id, id)` predicate every UPDATE carries. * - the row is LIVE (`isLiveNodeRow` / `deleted_at === undefined`, which is what * routes a tombstone to `skipped` instead of to the update): restated as the * `deleted_at IS NULL` conjunction on the non-resurrecting UPDATE leg. Import * never resurrects, so it never builds the `IS NOT NULL` leg. * - `onConflict: "skip"` / `"error"`: verdict-independent by construction — * they write nothing. * - the row's PROPS, read as the `oldProps` side of the uniqueness diff: the * ONE input with no portable SQL predicate behind it (a props blob is TEXT on * SQLite and `jsonb` on PostgreSQL, and neither comparison is stable under * key reordering). It is bounded rather than asserted: the sidecar writes now * run AFTER the primary update returns a row (see `applyNodeUpdate`), so a * verdict-invalidating recreate that changes the lower bound takes the * sidecars with it. The residual window is a recreate that reproduces the * probed `valid_from` exactly while changing props — noted here so the next * round starts from the list rather than from the symptom. */ /** A normal Store or an opaque ingestion branch that interchange may stage. */ type ImportTarget = Store | IngestionImportTarget; /** * Import graph data into a store or opaque ingestion branch. * * Nodes are imported first to satisfy edge reference validation. * The import runs within a transaction for atomicity when supported. * * Refused with a typed {@link ConfigurationError} when the target writes through * a serialized database connection another long-lived interchange operation * holds — the same refusal, codes and `details.heldBy` / `details.requested` as * {@link importGraphStream}. Hand-rolling a stream as `for await (const chunk of * exportGraphStream(source)) await importGraph(target, ...)` on one such * connection is therefore refused on the first chunk instead of nesting a write * transaction inside the export's open snapshot. * * Also refused, with `CONSTRAINT_WRITE_FENCE_UNSUPPORTED`, when the backend * reports `execution.interactiveTransactions: false` AND the graph owes a claim that must be written * before the row it gates — any unique constraint of any scope, any node kind * with a `disjointWith` partner, or any edge kind whose cardinality is not * `many`. An import writes those reservations like every other writer and takes * no per-graph lock, so without a transaction a failure between a reservation * and its row would leave a key blocked with no repair path. The verdict is per * GRAPH rather than per payload, and it is reached before the first row: a * streamed import cannot commit part of itself and only then discover it could * not be fenced. * * @param target - The graph store or ingestion branch to import into * @param data - Graph data in interchange format * @param options - Import configuration * @returns Import statistics and any errors * * @example * ```typescript * const result = await importGraph(store, data, { * onConflict: "update", * onUnknownProperty: "strip", * }); * * console.log(`Created ${result.nodes.created} nodes`); * ``` */ declare function importGraph(target: ImportTarget, data: GraphData, rawOptions: ImportOptions): Promise; /** * Imports a header-first stream of bounded interchange chunks. * * Each chunk is committed through the same implementation as an in-memory * {@link importGraph}, so validation and conflict semantics stay identical; the * chunk calls skip only that function's own lease claim, which this loop already * holds on their behalf. * Chunks are individually atomic on transactional backends; a consumer needing * all-or-nothing behavior can import into a disposable working-copy backend and * publish it only after this function succeeds. * * Nodes must precede edges. Once a node chunk commits, edge validation reads the * target store rather than retaining every imported node id in memory. * * Refused with a typed {@link ConfigurationError} when the target writes through * a serialized database connection that another long-lived interchange stream * already holds — either because this stream came from a snapshot export on that * connection, or because ANY export snapshot or streaming import holds it when * the first chunk arrives (which covers a stream the caller has wrapped). The * error's `details.heldBy` names the holder's kind and `details.code` the * condition: `INTERCHANGE_SHARED_SERIALIZED_BACKEND_SNAPSHOT` (or * `INTERCHANGE_SAME_SQLITE_BACKEND_SNAPSHOT`) behind an export snapshot, * `INTERCHANGE_SERIALIZED_IMPORT_IN_PROGRESS` behind another import. * * Once accepted, the import holds that connection's one stream lease for the * whole call — every chunk AND the trailing statistics refresh, which is a * write like any other — so any export snapshot or second import that tries to * start while a write of this import is still to come is the side refused, and * this import keeps running instead of stalling against a transaction it can * never write past. * * Connections we cannot observe are not detected: two clients dialed at one * server, or two SQLite handles on one file, are independent and are not * refused. Neither is a connection whose driver we cannot positively identify as * single-connection — see the residual gap documented in * `backend/transaction-resource.ts`. */ declare function importGraphStream(target: ImportTarget, chunks: AsyncIterable, rawOptions: ImportOptions): Promise; /** Counts committed by a trusted initial import. */ type TrustedImportResult = Readonly<{ nodes: number; edges: number; }>; /** * Atomically imports a header-first stream into a fresh, dedicated database. * * This is an intentionally trusted path. It checks stream ordering and kind * names, but it does not validate properties, references, cardinality, or * conflicts. The caller must guarantee those invariants. Use * {@link importGraphStream} for untrusted data. * * Trusted of the DATA, not of the connection: this holds ONE write transaction * open for the entire stream, which makes it a long-lived import holder exactly * like {@link importGraphStream}, and it is guarded the same way — the source * stream's own backend is checked against the target before the first chunk, and * the target connection's exclusive stream lease is held for the whole import. * `trustedImportGraphStream(target, exportGraphStream(source))` over one * serialized connection is therefore refused with the shared typed * {@link ConfigurationError} (codes and `details.heldBy` / `details.requested` * as documented on `importGraphStream`) rather than reaching the driver as a * nested BEGIN. */ declare function trustedImportGraphStream(store: Store, chunks: AsyncIterable | Iterable): Promise; /** In-memory convenience wrapper around {@link trustedImportGraphStream}. */ declare function trustedImportGraph(store: Store, data: GraphData): Promise; export { type ConflictStrategy, ConflictStrategySchema, type ExportOptions, type ExportOptionsInput, ExportOptionsSchema, type ExportStreamOptions, type ExportStreamOptionsInput, ExportStreamOptionsSchema, FORMAT_VERSION, type GraphData, type GraphDataHeader, GraphDataHeaderSchema, GraphDataSchema, type GraphDataSource, GraphDataSourceSchema, type GraphInterchangeChunk, GraphInterchangeChunkSchema, type IdentityInterchangeMode, IdentityInterchangeModeSchema, type ImportError, ImportErrorSchema, type ImportOptions, ImportOptionsSchema, type ImportResult, ImportResultSchema, type ImportTarget, IngestionImportTarget, type InterchangeEdge, InterchangeEdgeSchema, type InterchangeIdentity, type InterchangeIdentityAssertion, InterchangeIdentityAssertionSchema, InterchangeIdentitySchema, type InterchangeNode, InterchangeNodeSchema, type TrustedImportResult, type UnknownPropertyStrategy, UnknownPropertyStrategySchema, exportGraph, exportGraphStream, importGraph, importGraphStream, trustedImportGraph, trustedImportGraphStream };