@openparser/lineage 1.0.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/LICENSE +201 -0
  3. package/README.md +211 -0
  4. package/dist/activity.d.ts +74 -0
  5. package/dist/activity.d.ts.map +1 -0
  6. package/dist/approval.d.ts +24 -0
  7. package/dist/approval.d.ts.map +1 -0
  8. package/dist/builder.d.ts +48 -0
  9. package/dist/builder.d.ts.map +1 -0
  10. package/dist/common.d.ts +34 -0
  11. package/dist/common.d.ts.map +1 -0
  12. package/dist/confidence.d.ts +61 -0
  13. package/dist/confidence.d.ts.map +1 -0
  14. package/dist/derivation.d.ts +163 -0
  15. package/dist/derivation.d.ts.map +1 -0
  16. package/dist/document.d.ts +412 -0
  17. package/dist/document.d.ts.map +1 -0
  18. package/dist/entity.d.ts +126 -0
  19. package/dist/entity.d.ts.map +1 -0
  20. package/dist/fields.d.ts +76 -0
  21. package/dist/fields.d.ts.map +1 -0
  22. package/dist/index.d.ts +21 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +870 -0
  25. package/dist/labels.d.ts +11 -0
  26. package/dist/labels.d.ts.map +1 -0
  27. package/dist/profile/grounding.d.ts +34 -0
  28. package/dist/profile/grounding.d.ts.map +1 -0
  29. package/dist/profile/index.d.ts +14 -0
  30. package/dist/profile/index.d.ts.map +1 -0
  31. package/dist/profile/index.js +1298 -0
  32. package/dist/profile/labels.d.ts +10 -0
  33. package/dist/profile/labels.d.ts.map +1 -0
  34. package/dist/profile/recognition.d.ts +70 -0
  35. package/dist/profile/recognition.d.ts.map +1 -0
  36. package/dist/profile/transform-validation.d.ts +250 -0
  37. package/dist/profile/transform-validation.d.ts.map +1 -0
  38. package/dist/prov.d.ts +40 -0
  39. package/dist/prov.d.ts.map +1 -0
  40. package/dist/relation.d.ts +27 -0
  41. package/dist/relation.d.ts.map +1 -0
  42. package/dist/traverse.d.ts +12 -0
  43. package/dist/traverse.d.ts.map +1 -0
  44. package/dist/validate.d.ts +14 -0
  45. package/dist/validate.d.ts.map +1 -0
  46. package/lineage.schema.json +625 -0
  47. package/package.json +71 -0
  48. package/src/activity.ts +65 -0
  49. package/src/approval.ts +28 -0
  50. package/src/builder.ts +129 -0
  51. package/src/common.ts +58 -0
  52. package/src/confidence.ts +72 -0
  53. package/src/derivation.ts +81 -0
  54. package/src/document.ts +82 -0
  55. package/src/entity.ts +87 -0
  56. package/src/fields.ts +163 -0
  57. package/src/index.ts +123 -0
  58. package/src/labels.ts +13 -0
  59. package/src/profile/grounding.ts +74 -0
  60. package/src/profile/index.ts +67 -0
  61. package/src/profile/labels.ts +28 -0
  62. package/src/profile/recognition.ts +149 -0
  63. package/src/profile/transform-validation.ts +1379 -0
  64. package/src/prov.ts +205 -0
  65. package/src/relation.ts +32 -0
  66. package/src/traverse.ts +91 -0
  67. package/src/validate.ts +221 -0
package/dist/index.js ADDED
@@ -0,0 +1,870 @@
1
+ // src/activity.ts
2
+ import { z as z2 } from "zod";
3
+
4
+ // src/common.ts
5
+ import { z } from "zod";
6
+ var LineageIdSchema = z.string().trim().min(1).max(512);
7
+ var JsonValueSchema = z.json();
8
+ var AttributesSchema = z.record(z.string().min(1), JsonValueSchema);
9
+ function jsonValuesEqual(left, right) {
10
+ if (left === right) return true;
11
+ if (left === null || right === null) return false;
12
+ if (typeof left !== "object" || typeof right !== "object") return false;
13
+ return canonicalJson(left) === canonicalJson(right);
14
+ }
15
+ function canonicalJson(value) {
16
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
17
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
18
+ const entries = Object.entries(value).filter(([, member]) => member !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, member]) => `${JSON.stringify(key)}:${canonicalJson(member)}`);
19
+ return `{${entries.join(",")}}`;
20
+ }
21
+ var JsonPointerSchema = z.string().max(2048).regex(/^(?:\/(?:[^~/]|~0|~1)*)*$/, "path must be an RFC 6901 JSON Pointer");
22
+ var LINEAGE_FORMAT = "lineage@1";
23
+ var LineageFormatSchema = z.literal(LINEAGE_FORMAT);
24
+ var TimestampSchema = z.iso.datetime({ offset: true }).max(64);
25
+
26
+ // src/activity.ts
27
+ var AssociationSchema = z2.object({
28
+ agent: LineageIdSchema,
29
+ role: z2.string().trim().min(1).max(128).optional(),
30
+ attributes: AttributesSchema.optional()
31
+ }).strict();
32
+ var ACTIVITY_STATUSES = ["scheduled", "started", "ended", "failed", "cancelled"];
33
+ var ActivityStatusSchema = z2.enum(ACTIVITY_STATUSES);
34
+ var ImplementationSchema = z2.object({
35
+ type: z2.string().trim().min(1).max(128).optional(),
36
+ name: z2.string().trim().min(1).max(512).optional(),
37
+ version: z2.string().trim().min(1).max(128).optional(),
38
+ uri: z2.string().trim().min(1).max(4096).optional(),
39
+ attributes: AttributesSchema.optional()
40
+ }).strict();
41
+ var ActivitySchema = z2.object({
42
+ type: z2.string().trim().min(1).max(256),
43
+ name: z2.string().trim().min(1).max(512).optional(),
44
+ status: ActivityStatusSchema.optional(),
45
+ startedAt: TimestampSchema.optional(),
46
+ endedAt: TimestampSchema.optional(),
47
+ implementation: ImplementationSchema.optional(),
48
+ parameters: z2.record(z2.string().min(1), JsonValueSchema).optional(),
49
+ associations: z2.array(AssociationSchema).max(64).optional(),
50
+ attributes: AttributesSchema.optional()
51
+ }).strict();
52
+ var AgentSchema = z2.object({
53
+ type: z2.string().trim().min(1).max(128).optional(),
54
+ name: z2.string().trim().min(1).max(512).optional(),
55
+ attributes: AttributesSchema.optional()
56
+ }).strict();
57
+
58
+ // src/approval.ts
59
+ import { z as z3 } from "zod";
60
+ var ApprovalAssertionSchema = z3.object({
61
+ /** Agent in `LineageDocument.agents` that signed off. */
62
+ agent: LineageIdSchema,
63
+ at: z3.string().datetime(),
64
+ note: z3.string().trim().min(1).max(2e3).optional(),
65
+ attributes: AttributesSchema.optional()
66
+ }).strict();
67
+
68
+ // src/derivation.ts
69
+ import { z as z5 } from "zod";
70
+
71
+ // src/confidence.ts
72
+ import { z as z4 } from "zod";
73
+ var ConfidenceScaleSchema = z4.object({
74
+ min: z4.number().finite(),
75
+ max: z4.number().finite()
76
+ }).strict().superRefine((scale, ctx) => {
77
+ if (!(scale.max > scale.min)) {
78
+ ctx.addIssue({
79
+ code: "custom",
80
+ message: "confidence scale max must be greater than min",
81
+ path: ["max"]
82
+ });
83
+ }
84
+ });
85
+ var CONFIDENCE_KINDS = ["reported", "derived", "assessed"];
86
+ var ConfidenceKindSchema = z4.enum(CONFIDENCE_KINDS);
87
+ var ConfidenceSourceSchema = z4.object({
88
+ type: z4.enum(["entity", "activity", "agent"]),
89
+ id: LineageIdSchema
90
+ }).strict();
91
+ var ConfidenceAssertionSchema = z4.object({
92
+ score: z4.number().finite(),
93
+ scale: ConfidenceScaleSchema,
94
+ kind: ConfidenceKindSchema,
95
+ /** What the score measures (e.g. recognition, classification, quality). */
96
+ scope: z4.string().trim().min(1).max(128),
97
+ /** Optional finer grain within scope (e.g. field, page, token). */
98
+ granularity: z4.string().trim().min(1).max(128).optional(),
99
+ /** When false, scores must not be treated as calibrated probabilities. */
100
+ calibrated: z4.boolean().default(false),
101
+ /** Entities, activities, or agents that substantiate or produced this assertion. */
102
+ sources: z4.array(ConfidenceSourceSchema).max(256).optional(),
103
+ method: z4.string().trim().min(1).max(256).optional(),
104
+ sampleCount: z4.number().int().positive().optional(),
105
+ attributes: AttributesSchema.optional()
106
+ }).strict().superRefine((assertion, ctx) => {
107
+ const { score, scale } = assertion;
108
+ if (score < scale.min || score > scale.max) {
109
+ ctx.addIssue({
110
+ code: "custom",
111
+ message: `confidence score ${score} is outside scale [${scale.min}, ${scale.max}]`,
112
+ path: ["score"]
113
+ });
114
+ }
115
+ });
116
+
117
+ // src/derivation.ts
118
+ var DERIVATION_EFFECTS = ["direct", "indirect"];
119
+ var MAX_DERIVATION_INPUTS = 1024;
120
+ var DerivationEffectSchema = z5.enum(DERIVATION_EFFECTS);
121
+ var DerivationInputSchema = z5.object({
122
+ entity: LineageIdSchema,
123
+ role: z5.string().trim().min(1).max(128).optional(),
124
+ effect: DerivationEffectSchema.default("direct"),
125
+ confidence: ConfidenceAssertionSchema.optional(),
126
+ attributes: AttributesSchema.optional()
127
+ }).strict();
128
+ var TransformationSchema = z5.object({
129
+ type: z5.string().trim().min(1).max(128).optional(),
130
+ description: z5.string().trim().min(1).max(4096).optional(),
131
+ expression: z5.string().trim().min(1).max(8192).optional(),
132
+ masking: z5.boolean().optional(),
133
+ attributes: AttributesSchema.optional()
134
+ }).strict();
135
+ var DerivationSchema = z5.object({
136
+ id: LineageIdSchema.optional(),
137
+ output: LineageIdSchema,
138
+ activity: LineageIdSchema,
139
+ inputs: z5.array(DerivationInputSchema).max(MAX_DERIVATION_INPUTS).default([]),
140
+ transformation: TransformationSchema.optional(),
141
+ confidence: ConfidenceAssertionSchema.optional(),
142
+ attributes: AttributesSchema.optional()
143
+ }).strict();
144
+ function derivationId(derivation) {
145
+ return derivation.id ?? `derivation:${derivation.output}`;
146
+ }
147
+
148
+ // src/document.ts
149
+ import { z as z8 } from "zod";
150
+
151
+ // src/entity.ts
152
+ import { z as z6 } from "zod";
153
+ var AttributionSchema = z6.object({
154
+ agent: LineageIdSchema,
155
+ role: z6.string().trim().min(1).max(128).optional(),
156
+ attributes: AttributesSchema.optional()
157
+ }).strict();
158
+ var DigestSchema = z6.object({
159
+ algorithm: z6.string().trim().min(1).max(64),
160
+ value: z6.string().trim().min(1).max(512)
161
+ }).strict();
162
+ var LocatorSchema = z6.object({
163
+ uri: z6.string().trim().min(1).max(4096),
164
+ mediaType: z6.string().trim().min(1).max(256).optional(),
165
+ digest: DigestSchema.optional(),
166
+ attributes: AttributesSchema.optional()
167
+ }).strict();
168
+ var SelectorSchema = z6.object({
169
+ type: z6.string().trim().min(1).max(128),
170
+ value: JsonValueSchema,
171
+ attributes: AttributesSchema.optional()
172
+ }).strict();
173
+ var ENTITY_KINDS = ["value", "artifact", "evidence", "decision", "collection"];
174
+ var EntityKindSchema = z6.enum(ENTITY_KINDS);
175
+ var EntitySchema = z6.object({
176
+ kind: EntityKindSchema,
177
+ name: z6.string().trim().min(1).max(512).optional(),
178
+ /** Inline JSON value when the entity is a materialized datum. */
179
+ value: JsonValueSchema.optional(),
180
+ /** Unambiguous location within a larger JSON value (RFC 6901). */
181
+ path: JsonPointerSchema.optional(),
182
+ /** Optional JSON Schema (or profile schema object) describing `value`. */
183
+ schema: z6.record(z6.string(), JsonValueSchema).optional(),
184
+ locator: LocatorSchema.optional(),
185
+ selector: SelectorSchema.optional(),
186
+ digest: DigestSchema.optional(),
187
+ confidence: z6.array(ConfidenceAssertionSchema).max(64).optional(),
188
+ /**
189
+ * Who stands behind this value. Standing is per entity and does not
190
+ * propagate. OpenParser extraction review projects this from confirmations.
191
+ */
192
+ approvals: z6.array(ApprovalAssertionSchema).max(64).optional(),
193
+ attributions: z6.array(AttributionSchema).max(64).optional(),
194
+ attributes: AttributesSchema.optional()
195
+ }).strict();
196
+
197
+ // src/relation.ts
198
+ import { z as z7 } from "zod";
199
+ var RELATION_TYPES = ["member_of", "specialization_of", "alternate_of"];
200
+ var RelationTypeSchema = z7.enum(RELATION_TYPES);
201
+ var RelationSchema = z7.object({
202
+ type: RelationTypeSchema,
203
+ source: LineageIdSchema,
204
+ target: LineageIdSchema,
205
+ attributes: AttributesSchema.optional()
206
+ }).strict().superRefine((relation, ctx) => {
207
+ if (relation.source === relation.target) {
208
+ ctx.addIssue({
209
+ code: "custom",
210
+ message: "relation source and target must be distinct",
211
+ path: ["target"]
212
+ });
213
+ }
214
+ });
215
+
216
+ // src/validate.ts
217
+ var LineageValidationError = class extends Error {
218
+ issues;
219
+ constructor(issues) {
220
+ super(issues.join("; "));
221
+ this.name = "LineageValidationError";
222
+ this.issues = issues;
223
+ }
224
+ };
225
+ function validateLineageGraph(doc) {
226
+ const issues = [];
227
+ const entityIds = new Set(Object.keys(doc.entities));
228
+ const activityIds = new Set(Object.keys(doc.activities));
229
+ const agentIds = new Set(Object.keys(doc.agents));
230
+ const idsOfKind = { entity: entityIds, activity: activityIds, agent: agentIds };
231
+ const requireRef = (kind, id, where) => {
232
+ if (!idsOfKind[kind].has(id)) issues.push(`${where}: ${kind} "${id}" does not exist`);
233
+ };
234
+ const requireConfidenceRefs = (confidence, where) => {
235
+ for (const source of confidence?.sources ?? []) {
236
+ requireRef(source.type, source.id, `${where}.sources`);
237
+ }
238
+ };
239
+ const claimed = /* @__PURE__ */ new Map();
240
+ for (const [bucket, ids] of [
241
+ ["entities", entityIds],
242
+ ["activities", activityIds],
243
+ ["agents", agentIds]
244
+ ]) {
245
+ for (const id of ids) {
246
+ const prior = claimed.get(id);
247
+ if (prior) {
248
+ issues.push(`id "${id}" collides across ${prior} and ${bucket}`);
249
+ } else {
250
+ claimed.set(id, bucket);
251
+ }
252
+ }
253
+ }
254
+ const derivationIds = /* @__PURE__ */ new Set();
255
+ const outputProducers = /* @__PURE__ */ new Map();
256
+ for (let i = 0; i < doc.derivations.length; i++) {
257
+ const derivation = doc.derivations[i];
258
+ const path = `derivations[${i}]`;
259
+ const id = derivationId(derivation);
260
+ if (derivationIds.has(id)) {
261
+ issues.push(`${path}: duplicate derivation id "${id}"`);
262
+ }
263
+ derivationIds.add(id);
264
+ if (claimed.has(id)) {
265
+ issues.push(`${path}: derivation id "${id}" collides with ${claimed.get(id)}`);
266
+ }
267
+ if (!entityIds.has(derivation.output)) {
268
+ issues.push(`${path}: output entity "${derivation.output}" does not exist`);
269
+ } else {
270
+ const prior = outputProducers.get(derivation.output);
271
+ if (prior) {
272
+ issues.push(
273
+ `${path}: entity "${derivation.output}" already has producer derivation "${prior}" (one producer per derived output)`
274
+ );
275
+ } else {
276
+ outputProducers.set(derivation.output, id);
277
+ }
278
+ }
279
+ requireRef("activity", derivation.activity, path);
280
+ const inputEntities = /* @__PURE__ */ new Set();
281
+ for (let j = 0; j < derivation.inputs.length; j++) {
282
+ const input = derivation.inputs[j];
283
+ const inputPath = `${path}.inputs[${j}]`;
284
+ requireRef("entity", input.entity, inputPath);
285
+ if (input.entity === derivation.output) {
286
+ issues.push(`${inputPath}: output cannot be its own input`);
287
+ }
288
+ if (inputEntities.has(input.entity)) {
289
+ issues.push(`${inputPath}: duplicate input entity "${input.entity}"`);
290
+ }
291
+ inputEntities.add(input.entity);
292
+ requireConfidenceRefs(input.confidence, `${inputPath}.confidence`);
293
+ }
294
+ requireConfidenceRefs(derivation.confidence, `${path}.confidence`);
295
+ }
296
+ const seenOutputs = /* @__PURE__ */ new Set();
297
+ for (let i = 0; i < doc.outputs.length; i++) {
298
+ const outputId = doc.outputs[i];
299
+ if (!entityIds.has(outputId)) {
300
+ issues.push(`outputs[${i}]: entity "${outputId}" does not exist`);
301
+ }
302
+ if (seenOutputs.has(outputId)) {
303
+ issues.push(`outputs[${i}]: duplicate entity "${outputId}"`);
304
+ }
305
+ seenOutputs.add(outputId);
306
+ }
307
+ for (const [entityId, entity] of Object.entries(doc.entities)) {
308
+ const entityPath = `entities.${entityId}`;
309
+ entity.attributions?.forEach((attribution, i) => {
310
+ requireRef("agent", attribution.agent, `${entityPath}.attributions[${i}]`);
311
+ });
312
+ entity.approvals?.forEach((approval, i) => {
313
+ requireRef("agent", approval.agent, `${entityPath}.approvals[${i}]`);
314
+ });
315
+ entity.confidence?.forEach((assertion, i) => {
316
+ requireConfidenceRefs(assertion, `${entityPath}.confidence[${i}]`);
317
+ });
318
+ }
319
+ for (const [activityId, activity] of Object.entries(doc.activities)) {
320
+ activity.associations?.forEach((association, i) => {
321
+ requireRef("agent", association.agent, `activities.${activityId}.associations[${i}]`);
322
+ });
323
+ const { startedAt, endedAt } = activity;
324
+ if (startedAt !== void 0 && endedAt !== void 0) {
325
+ const startedMs = Date.parse(startedAt);
326
+ const endedMs = Date.parse(endedAt);
327
+ if (Number.isNaN(startedMs) || Number.isNaN(endedMs)) {
328
+ issues.push(
329
+ `activities.${activityId}: startedAt or endedAt is not a valid RFC 3339 timestamp`
330
+ );
331
+ } else if (endedMs < startedMs) {
332
+ issues.push(`activities.${activityId}: endedAt precedes startedAt`);
333
+ }
334
+ }
335
+ }
336
+ for (let i = 0; i < doc.relations.length; i++) {
337
+ const relation = doc.relations[i];
338
+ const path = `relations[${i}]`;
339
+ requireRef("entity", relation.source, `${path}.source`);
340
+ requireRef("entity", relation.target, `${path}.target`);
341
+ if (relation.type === "member_of" && entityIds.has(relation.target) && doc.entities[relation.target]?.kind !== "collection") {
342
+ issues.push(`${path}: member_of target "${relation.target}" must be a collection entity`);
343
+ }
344
+ }
345
+ const dependents = /* @__PURE__ */ new Map();
346
+ for (const id of entityIds) dependents.set(id, []);
347
+ for (const derivation of doc.derivations) {
348
+ if (!entityIds.has(derivation.output)) continue;
349
+ for (const input of derivation.inputs) {
350
+ if (!entityIds.has(input.entity)) continue;
351
+ dependents.get(input.entity).push(derivation.output);
352
+ }
353
+ }
354
+ const visiting = /* @__PURE__ */ new Set();
355
+ const visited = /* @__PURE__ */ new Set();
356
+ const cyclePath = [];
357
+ const visit = (id) => {
358
+ if (visited.has(id)) return false;
359
+ if (visiting.has(id)) {
360
+ cyclePath.push(id);
361
+ return true;
362
+ }
363
+ visiting.add(id);
364
+ for (const next of dependents.get(id) ?? []) {
365
+ if (visit(next)) {
366
+ cyclePath.push(id);
367
+ return true;
368
+ }
369
+ }
370
+ visiting.delete(id);
371
+ visited.add(id);
372
+ return false;
373
+ };
374
+ for (const id of entityIds) {
375
+ if (visit(id)) {
376
+ const cycle = [...new Set(cyclePath.reverse())].join(" \u2192 ");
377
+ issues.push(`entity dependency cycle detected: ${cycle}`);
378
+ break;
379
+ }
380
+ }
381
+ if (issues.length > 0) {
382
+ throw new LineageValidationError(issues);
383
+ }
384
+ }
385
+
386
+ // src/document.ts
387
+ var LineageDocumentShapeSchema = z8.object({
388
+ format: LineageFormatSchema,
389
+ id: LineageIdSchema.optional(),
390
+ /** Profile URIs or short names declaring extension semantics. */
391
+ profiles: z8.array(z8.string().trim().min(1).max(1024)).max(64).optional(),
392
+ entities: z8.record(LineageIdSchema, EntitySchema).default({}),
393
+ activities: z8.record(LineageIdSchema, ActivitySchema).default({}),
394
+ agents: z8.record(LineageIdSchema, AgentSchema).default({}),
395
+ derivations: z8.array(DerivationSchema).max(1e5).default([]),
396
+ relations: z8.array(RelationSchema).max(1e5).default([]),
397
+ /** Entity ids that are primary outputs of this document. */
398
+ outputs: z8.array(LineageIdSchema).max(1e4).default([]),
399
+ attributes: AttributesSchema.optional()
400
+ }).strict();
401
+ function parseLineageDocument(input) {
402
+ const doc = LineageDocumentShapeSchema.parse(input);
403
+ validateLineageGraph(doc);
404
+ return doc;
405
+ }
406
+ function safeParseLineageDocument(input) {
407
+ const shape = LineageDocumentShapeSchema.safeParse(input);
408
+ if (!shape.success) {
409
+ return { success: false, error: shape.error };
410
+ }
411
+ try {
412
+ validateLineageGraph(shape.data);
413
+ return { success: true, data: shape.data };
414
+ } catch (error) {
415
+ if (error instanceof LineageValidationError) {
416
+ return { success: false, error };
417
+ }
418
+ throw error;
419
+ }
420
+ }
421
+ var LineageDocumentSchema = LineageDocumentShapeSchema.superRefine((doc, ctx) => {
422
+ try {
423
+ validateLineageGraph(doc);
424
+ } catch (error) {
425
+ if (error instanceof LineageValidationError) {
426
+ for (const message of error.issues) {
427
+ ctx.addIssue({ code: "custom", message });
428
+ }
429
+ return;
430
+ }
431
+ throw error;
432
+ }
433
+ });
434
+
435
+ // src/builder.ts
436
+ var LineageBuilder = class {
437
+ id;
438
+ profiles;
439
+ entities = /* @__PURE__ */ new Map();
440
+ activities = /* @__PURE__ */ new Map();
441
+ agents = /* @__PURE__ */ new Map();
442
+ derivations = [];
443
+ relations;
444
+ outputs = [];
445
+ attributes;
446
+ withId(id) {
447
+ this.id = id;
448
+ return this;
449
+ }
450
+ withProfiles(...profiles) {
451
+ this.profiles = [...this.profiles ?? [], ...profiles];
452
+ return this;
453
+ }
454
+ withAttributes(attributes) {
455
+ this.attributes = attributes;
456
+ return this;
457
+ }
458
+ entity(id, entity) {
459
+ if (this.entities.has(id)) {
460
+ throw new Error(`entity "${id}" already registered`);
461
+ }
462
+ this.entities.set(id, entity);
463
+ return this;
464
+ }
465
+ activity(id, activity) {
466
+ if (this.activities.has(id)) {
467
+ throw new Error(`activity "${id}" already registered`);
468
+ }
469
+ this.activities.set(id, activity);
470
+ return this;
471
+ }
472
+ agent(id, agent) {
473
+ if (this.agents.has(id)) {
474
+ throw new Error(`agent "${id}" already registered`);
475
+ }
476
+ this.agents.set(id, agent);
477
+ return this;
478
+ }
479
+ /**
480
+ * Record that `activityId` produced `outputId` from `inputs`.
481
+ */
482
+ derive(args) {
483
+ const id = derivationId(args);
484
+ if (this.derivations.some((d) => derivationId(d) === id)) {
485
+ throw new Error(`derivation "${id}" already registered`);
486
+ }
487
+ const inputs = (args.inputs ?? []).map(
488
+ (input) => typeof input === "string" ? { entity: input, effect: "direct" } : input
489
+ );
490
+ this.derivations.push({
491
+ ...args.id ? { id: args.id } : {},
492
+ output: args.output,
493
+ activity: args.activity,
494
+ inputs,
495
+ ...args.transformation ? { transformation: args.transformation } : {},
496
+ ...args.confidence ? { confidence: args.confidence } : {},
497
+ ...args.attributes ? { attributes: args.attributes } : {}
498
+ });
499
+ return this;
500
+ }
501
+ relation(relation) {
502
+ this.relations = [...this.relations ?? [], relation];
503
+ return this;
504
+ }
505
+ withOutputs(...entityIds) {
506
+ this.outputs = [...this.outputs, ...entityIds];
507
+ return this;
508
+ }
509
+ /** Assemble and strictly validate the document. */
510
+ build() {
511
+ return parseLineageDocument({
512
+ format: LINEAGE_FORMAT,
513
+ ...this.id ? { id: this.id } : {},
514
+ ...this.profiles && this.profiles.length > 0 ? { profiles: this.profiles } : {},
515
+ entities: Object.fromEntries(this.entities),
516
+ activities: Object.fromEntries(this.activities),
517
+ agents: Object.fromEntries(this.agents),
518
+ derivations: this.derivations,
519
+ ...this.relations && this.relations.length > 0 ? { relations: this.relations } : {},
520
+ outputs: this.outputs,
521
+ ...this.attributes ? { attributes: this.attributes } : {}
522
+ });
523
+ }
524
+ };
525
+
526
+ // src/prov.ts
527
+ var LINEAGE_PROV_NAMESPACE = "https://openparser.dev/ns/lineage#";
528
+ function lineageIdToProvQualifiedName(id) {
529
+ const encoded = [...new TextEncoder().encode(id)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
530
+ return `lineage:id_${encoded}`;
531
+ }
532
+ var qname = lineageIdToProvQualifiedName;
533
+ function toProvJson(doc) {
534
+ const out = {
535
+ prefix: {
536
+ prov: "http://www.w3.org/ns/prov#",
537
+ lineage: LINEAGE_PROV_NAMESPACE
538
+ }
539
+ };
540
+ const entities = {};
541
+ for (const [id, entity] of Object.entries(doc.entities)) {
542
+ const record = flattenAttrs(entity.attributes);
543
+ record["prov:type"] = entity.kind;
544
+ record["lineage:id"] = id;
545
+ if (entity.name) record["prov:label"] = entity.name;
546
+ if (entity.value !== void 0) record["lineage:value"] = entity.value;
547
+ if (entity.path !== void 0) record["lineage:path"] = entity.path;
548
+ if (entity.locator?.uri) record["prov:location"] = entity.locator.uri;
549
+ if (entity.digest) {
550
+ record["lineage:digestAlgorithm"] = entity.digest.algorithm;
551
+ record["lineage:digestValue"] = entity.digest.value;
552
+ }
553
+ entities[qname(id)] = record;
554
+ }
555
+ if (Object.keys(entities).length > 0) out.entity = entities;
556
+ const activities = {};
557
+ for (const [id, activity] of Object.entries(doc.activities)) {
558
+ const record = flattenAttrs(activity.attributes);
559
+ record["prov:type"] = activity.type;
560
+ record["lineage:id"] = id;
561
+ if (activity.name) record["prov:label"] = activity.name;
562
+ if (activity.startedAt) record["prov:startTime"] = activity.startedAt;
563
+ if (activity.endedAt) record["prov:endTime"] = activity.endedAt;
564
+ activities[qname(id)] = record;
565
+ }
566
+ if (Object.keys(activities).length > 0) out.activity = activities;
567
+ const agents = {};
568
+ for (const [id, agent] of Object.entries(doc.agents)) {
569
+ const record = flattenAttrs(agent.attributes);
570
+ record["lineage:id"] = id;
571
+ if (agent.type) record["prov:type"] = agent.type;
572
+ if (agent.name) record["prov:label"] = agent.name;
573
+ agents[qname(id)] = record;
574
+ }
575
+ if (Object.keys(agents).length > 0) out.agent = agents;
576
+ const wasDerivedFrom = {};
577
+ const wasGeneratedBy = {};
578
+ const used = {};
579
+ const wasAssociatedWith = {};
580
+ const wasAttributedTo = {};
581
+ const hadMember = {};
582
+ const specializationOf = {};
583
+ const alternateOf = {};
584
+ for (const derivation of doc.derivations) {
585
+ const id = derivationId(derivation);
586
+ const genId = qname(`gen:${id}`);
587
+ wasGeneratedBy[genId] = {
588
+ "prov:entity": qname(derivation.output),
589
+ "prov:activity": qname(derivation.activity)
590
+ };
591
+ for (let i = 0; i < derivation.inputs.length; i++) {
592
+ const input = derivation.inputs[i];
593
+ const derId = qname(`wdf:${id}:${i}`);
594
+ wasDerivedFrom[derId] = {
595
+ "prov:generatedEntity": qname(derivation.output),
596
+ "prov:usedEntity": qname(input.entity),
597
+ "prov:activity": qname(derivation.activity),
598
+ ...input.role ? { "prov:role": input.role } : {},
599
+ "lineage:effect": input.effect
600
+ };
601
+ const usedId = qname(`used:${id}:${i}`);
602
+ used[usedId] = {
603
+ "prov:activity": qname(derivation.activity),
604
+ "prov:entity": qname(input.entity),
605
+ ...input.role ? { "prov:role": input.role } : {}
606
+ };
607
+ }
608
+ }
609
+ for (const [activityId, activity] of Object.entries(doc.activities)) {
610
+ for (let i = 0; i < (activity.associations?.length ?? 0); i++) {
611
+ const association = activity.associations[i];
612
+ wasAssociatedWith[qname(`assoc:${activityId}:${i}`)] = {
613
+ "prov:activity": qname(activityId),
614
+ "prov:agent": qname(association.agent),
615
+ ...association.role ? { "prov:role": association.role } : {}
616
+ };
617
+ }
618
+ }
619
+ for (const [entityId, entity] of Object.entries(doc.entities)) {
620
+ for (let i = 0; i < (entity.attributions?.length ?? 0); i++) {
621
+ const attribution = entity.attributions[i];
622
+ wasAttributedTo[qname(`attr:${entityId}:${i}`)] = {
623
+ "prov:entity": qname(entityId),
624
+ "prov:agent": qname(attribution.agent),
625
+ ...attribution.role ? { "prov:role": attribution.role } : {}
626
+ };
627
+ }
628
+ }
629
+ for (let i = 0; i < (doc.relations?.length ?? 0); i++) {
630
+ const relation = doc.relations[i];
631
+ if (relation.type === "member_of") {
632
+ hadMember[qname(`mem:${i}`)] = {
633
+ "prov:collection": qname(relation.target),
634
+ "prov:entity": qname(relation.source)
635
+ };
636
+ } else if (relation.type === "specialization_of") {
637
+ specializationOf[qname(`spec:${i}`)] = {
638
+ "prov:specificEntity": qname(relation.source),
639
+ "prov:generalEntity": qname(relation.target)
640
+ };
641
+ } else if (relation.type === "alternate_of") {
642
+ alternateOf[qname(`alt:${i}`)] = {
643
+ "prov:alternate1": qname(relation.source),
644
+ "prov:alternate2": qname(relation.target)
645
+ };
646
+ }
647
+ }
648
+ if (Object.keys(wasDerivedFrom).length > 0) out.wasDerivedFrom = wasDerivedFrom;
649
+ if (Object.keys(wasGeneratedBy).length > 0) out.wasGeneratedBy = wasGeneratedBy;
650
+ if (Object.keys(used).length > 0) out.used = used;
651
+ if (Object.keys(wasAssociatedWith).length > 0) out.wasAssociatedWith = wasAssociatedWith;
652
+ if (Object.keys(wasAttributedTo).length > 0) out.wasAttributedTo = wasAttributedTo;
653
+ if (Object.keys(hadMember).length > 0) out.hadMember = hadMember;
654
+ if (Object.keys(specializationOf).length > 0) out.specializationOf = specializationOf;
655
+ if (Object.keys(alternateOf).length > 0) out.alternateOf = alternateOf;
656
+ return out;
657
+ }
658
+ function flattenAttrs(attrs) {
659
+ const out = {};
660
+ for (const [key, value] of Object.entries(attrs ?? {})) {
661
+ out[key.startsWith("prov:") || key.startsWith("lineage:") ? key : qname(`attribute:${key}`)] = value;
662
+ }
663
+ return out;
664
+ }
665
+
666
+ // src/traverse.ts
667
+ function buildForwardEdges(doc) {
668
+ const edges = /* @__PURE__ */ new Map();
669
+ for (const id of Object.keys(doc.entities)) edges.set(id, []);
670
+ for (const derivation of doc.derivations) {
671
+ if (!(derivation.output in doc.entities)) continue;
672
+ for (const input of derivation.inputs) {
673
+ if (!(input.entity in doc.entities)) continue;
674
+ edges.get(input.entity).push(derivation.output);
675
+ }
676
+ }
677
+ return edges;
678
+ }
679
+ function buildReverseEdges(doc) {
680
+ const edges = /* @__PURE__ */ new Map();
681
+ for (const id of Object.keys(doc.entities)) edges.set(id, []);
682
+ for (const derivation of doc.derivations) {
683
+ if (!(derivation.output in doc.entities)) continue;
684
+ for (const input of derivation.inputs) {
685
+ if (!(input.entity in doc.entities)) continue;
686
+ edges.get(derivation.output).push(input.entity);
687
+ }
688
+ }
689
+ return edges;
690
+ }
691
+ function topologicalEntityOrder(doc) {
692
+ const forward = buildForwardEdges(doc);
693
+ const indegree = /* @__PURE__ */ new Map();
694
+ for (const id of Object.keys(doc.entities)) indegree.set(id, 0);
695
+ for (const [, targets] of forward) {
696
+ for (const t of targets) {
697
+ indegree.set(t, (indegree.get(t) ?? 0) + 1);
698
+ }
699
+ }
700
+ const queue = [];
701
+ for (const id of Object.keys(doc.entities)) {
702
+ if ((indegree.get(id) ?? 0) === 0) queue.push(id);
703
+ }
704
+ const order = [];
705
+ while (queue.length > 0) {
706
+ const id = queue.shift();
707
+ order.push(id);
708
+ for (const next of forward.get(id) ?? []) {
709
+ const nextDeg = (indegree.get(next) ?? 0) - 1;
710
+ indegree.set(next, nextDeg);
711
+ if (nextDeg === 0) queue.push(next);
712
+ }
713
+ }
714
+ if (order.length !== Object.keys(doc.entities).length) {
715
+ throw new Error("entity dependency graph contains a cycle");
716
+ }
717
+ return order;
718
+ }
719
+ function collectReachable(start, edges) {
720
+ if (!edges.has(start)) return [];
721
+ const seen = /* @__PURE__ */ new Set();
722
+ const stack = [...edges.get(start) ?? []];
723
+ while (stack.length > 0) {
724
+ const id = stack.pop();
725
+ if (seen.has(id)) continue;
726
+ seen.add(id);
727
+ for (const next of edges.get(id) ?? []) {
728
+ if (!seen.has(next)) stack.push(next);
729
+ }
730
+ }
731
+ return [...seen];
732
+ }
733
+ function entityAncestors(doc, entityId) {
734
+ return collectReachable(entityId, buildReverseEdges(doc));
735
+ }
736
+ function entityDescendants(doc, entityId) {
737
+ return collectReachable(entityId, buildForwardEdges(doc));
738
+ }
739
+
740
+ // src/fields.ts
741
+ function fieldPointer(entity) {
742
+ return entity.path ?? "";
743
+ }
744
+ function fieldConfidence(entity) {
745
+ const assertions = entity.confidence;
746
+ if (!assertions || assertions.length === 0) return void 0;
747
+ return assertions.find((assertion) => assertion.scope === "extraction") ?? assertions[0];
748
+ }
749
+ function dottedPathFromPointer(pointer) {
750
+ if (pointer === "" || pointer === "/") return "";
751
+ return pointer.replace(/^\//, "").split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~")).join(".");
752
+ }
753
+ function pointerFromDottedPath(dottedPath) {
754
+ if (dottedPath === "") return "";
755
+ return `/${dottedPath.split(".").map((segment) => segment.replaceAll("~", "~0").replaceAll("/", "~1")).join("/")}`;
756
+ }
757
+ function fieldEntityByPointer(doc, pointer) {
758
+ const matching = Object.entries(doc.entities).filter(([, entity]) => entity.path === pointer);
759
+ if (matching.length === 0) return void 0;
760
+ const matchingIds = new Set(matching.map(([id]) => id));
761
+ const outputId = doc.outputs.find((id) => matchingIds.has(id));
762
+ if (outputId) {
763
+ const entity = doc.entities[outputId];
764
+ if (entity) return { id: outputId, entity };
765
+ }
766
+ const ranked = matching.toSorted(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
767
+ const decision = ranked.find(([, entity]) => entity.kind === "decision");
768
+ const chosen = decision ?? ranked[0];
769
+ return chosen ? { id: chosen[0], entity: chosen[1] } : void 0;
770
+ }
771
+ function toField(id, entity) {
772
+ const pointer = fieldPointer(entity);
773
+ const confidence = fieldConfidence(entity);
774
+ return {
775
+ id,
776
+ pointer,
777
+ dottedPath: dottedPathFromPointer(pointer),
778
+ value: entity.value,
779
+ confidence,
780
+ confidenceOrigin: confidence ? confidence.kind : "not_reported",
781
+ entity
782
+ };
783
+ }
784
+ function fields(doc) {
785
+ const result = [];
786
+ for (const id of doc.outputs) {
787
+ const entity = doc.entities[id];
788
+ if (!entity) continue;
789
+ if (fieldPointer(entity) === "") continue;
790
+ result.push(toField(id, entity));
791
+ }
792
+ return result;
793
+ }
794
+ function fieldTrace(doc, pointer) {
795
+ const field = fields(doc).find((candidate) => candidate.pointer === pointer);
796
+ if (!field) return void 0;
797
+ const ancestorIds = entityAncestors(doc, field.id);
798
+ const evidence = ancestorIds.filter((id) => {
799
+ const entity = doc.entities[id];
800
+ return entity?.kind === "collection" || entity?.kind === "evidence";
801
+ }).sort().map((id) => ({ id, entity: doc.entities[id] }));
802
+ const relevant = /* @__PURE__ */ new Set([field.id, ...ancestorIds]);
803
+ const activities = [
804
+ ...new Set(doc.derivations.filter((d) => relevant.has(d.output)).map((d) => d.activity))
805
+ ].sort();
806
+ return { field, evidence, activities };
807
+ }
808
+
809
+ // src/labels.ts
810
+ function activityLabel(activity) {
811
+ return activity.name ?? activity.type;
812
+ }
813
+ export {
814
+ ACTIVITY_STATUSES,
815
+ ActivitySchema,
816
+ ActivityStatusSchema,
817
+ AgentSchema,
818
+ ApprovalAssertionSchema,
819
+ AssociationSchema,
820
+ AttributesSchema,
821
+ AttributionSchema,
822
+ CONFIDENCE_KINDS,
823
+ ConfidenceAssertionSchema,
824
+ ConfidenceKindSchema,
825
+ ConfidenceScaleSchema,
826
+ ConfidenceSourceSchema,
827
+ DERIVATION_EFFECTS,
828
+ DerivationEffectSchema,
829
+ DerivationInputSchema,
830
+ DerivationSchema,
831
+ DigestSchema,
832
+ ENTITY_KINDS,
833
+ EntityKindSchema,
834
+ EntitySchema,
835
+ ImplementationSchema,
836
+ JsonPointerSchema,
837
+ JsonValueSchema,
838
+ LINEAGE_FORMAT,
839
+ LINEAGE_PROV_NAMESPACE,
840
+ LineageBuilder,
841
+ LineageDocumentSchema,
842
+ LineageDocumentShapeSchema,
843
+ LineageFormatSchema,
844
+ LineageIdSchema,
845
+ LineageValidationError,
846
+ LocatorSchema,
847
+ MAX_DERIVATION_INPUTS,
848
+ RELATION_TYPES,
849
+ RelationSchema,
850
+ RelationTypeSchema,
851
+ SelectorSchema,
852
+ TimestampSchema,
853
+ TransformationSchema,
854
+ activityLabel,
855
+ derivationId,
856
+ dottedPathFromPointer,
857
+ entityAncestors,
858
+ entityDescendants,
859
+ fieldEntityByPointer,
860
+ fieldTrace,
861
+ fields,
862
+ jsonValuesEqual,
863
+ lineageIdToProvQualifiedName,
864
+ parseLineageDocument,
865
+ pointerFromDottedPath,
866
+ safeParseLineageDocument,
867
+ toProvJson,
868
+ topologicalEntityOrder,
869
+ validateLineageGraph
870
+ };