@disclosureos/records 1.1.0 → 2.0.0-beta.0
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/CHANGELOG.md +13 -0
- package/README.md +9 -0
- package/dist/experimental/v2/index.d.ts +4419 -0
- package/dist/experimental/v2/index.js +645 -0
- package/dist/experimental/v2/index.js.map +1 -0
- package/package.json +16 -4
- package/schema/experimental/claim-history-0.1.0.schema.json +6545 -0
- package/schema/experimental/observation-0.1.0.schema.json +6040 -0
- package/schema/experimental/v2-primitives-0.2.0.schema.json +1761 -0
- package/schema/experimental/v2-primitives.schema.json +828 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// src/experimental/v2/primitives.ts
|
|
4
|
+
var text = z.string().min(1).regex(/\S/, "must contain non-whitespace text");
|
|
5
|
+
var identifier = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
|
|
6
|
+
var sourceRef = z.string().regex(/^(source|product):[^\s:]+$/);
|
|
7
|
+
var valueRef = z.string().regex(/^(source|product|assertion):[^\s:]+$/);
|
|
8
|
+
var sourceRefs = z.array(valueRef).min(1);
|
|
9
|
+
var instantString = z.iso.datetime({ offset: true }).regex(/^(?!0000)/).regex(/T[0-9]{2}:[0-9]{2}:[0-9]{2}/).regex(/(?:Z|[+-][0-9]{2}:[0-9]{2})(?![\s\S])/);
|
|
10
|
+
var EventTimePointSchema = z.discriminatedUnion("kind", [
|
|
11
|
+
z.strictObject({ kind: z.literal("year"), value: z.string().regex(/^(?!0000)[0-9]{4}$/) }),
|
|
12
|
+
z.strictObject({ kind: z.literal("month"), value: z.string().regex(/^(?!0000)[0-9]{4}-(0[1-9]|1[0-2])$/) }),
|
|
13
|
+
z.strictObject({ kind: z.literal("date"), value: z.iso.date().regex(/^(?!0000)/) }),
|
|
14
|
+
z.strictObject({ kind: z.literal("instant"), value: instantString, timeScale: z.literal("UTC") })
|
|
15
|
+
]);
|
|
16
|
+
var EventTimeIntervalSchema = z.strictObject({
|
|
17
|
+
kind: z.literal("interval"),
|
|
18
|
+
start: EventTimePointSchema,
|
|
19
|
+
end: EventTimePointSchema
|
|
20
|
+
});
|
|
21
|
+
var EventTimeValueSchema = z.union([EventTimePointSchema, EventTimeIntervalSchema]);
|
|
22
|
+
var assertionRef = z.string().regex(/^assertion:[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
23
|
+
var ValueSelectionSchema = z.strictObject({
|
|
24
|
+
assertionRef,
|
|
25
|
+
consideredAssertionRefs: z.array(assertionRef).min(1),
|
|
26
|
+
methodRef: z.string().regex(/^method:[^\s:]+(?![\s\S])/),
|
|
27
|
+
methodVersion: text,
|
|
28
|
+
evaluatedBy: text,
|
|
29
|
+
evaluatedAt: instantString,
|
|
30
|
+
rationale: text
|
|
31
|
+
});
|
|
32
|
+
var PositionValueSchema = z.strictObject({
|
|
33
|
+
latitude: z.number().min(-90).max(90),
|
|
34
|
+
longitude: z.number().min(-180).max(180),
|
|
35
|
+
datum: z.literal("WGS84")
|
|
36
|
+
});
|
|
37
|
+
function envelope(value) {
|
|
38
|
+
return z.discriminatedUnion("state", [
|
|
39
|
+
z.strictObject({ state: z.literal("known"), value, sourceRefs, selection: ValueSelectionSchema.optional() }),
|
|
40
|
+
z.strictObject({ state: z.literal("approximate"), value, sourceRefs, precision: text, selection: ValueSelectionSchema.optional() }),
|
|
41
|
+
z.strictObject({ state: z.literal("unknown"), reason: z.enum(["not_recorded", "not_collected", "unavailable"]) }),
|
|
42
|
+
z.strictObject({ state: z.literal("redacted"), reason: text })
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
var EventTimeSchema = envelope(EventTimeValueSchema);
|
|
46
|
+
var PositionSchema = envelope(PositionValueSchema);
|
|
47
|
+
var SourceProvenanceSchema = z.strictObject({
|
|
48
|
+
sourceRef,
|
|
49
|
+
sourceDigest: z.strictObject({ algorithm: z.literal("sha256"), value: z.string().regex(/^[a-f0-9]{64}$/) }).optional(),
|
|
50
|
+
locator: z.discriminatedUnion("kind", [
|
|
51
|
+
z.strictObject({ kind: z.literal("page"), page: z.number().int().min(1) }),
|
|
52
|
+
z.strictObject({ kind: z.literal("time_range"), startSeconds: z.number().min(0), endSeconds: z.number().min(0) }),
|
|
53
|
+
z.strictObject({ kind: z.literal("json_pointer"), pointer: z.string().regex(/^(?:\/(?:[^~]|~[01])*)?(?![\s\S])/) })
|
|
54
|
+
]).optional(),
|
|
55
|
+
attributedTo: text.optional(),
|
|
56
|
+
extractedBy: text.optional()
|
|
57
|
+
});
|
|
58
|
+
var ValueAssertionSchema = z.discriminatedUnion("field", [
|
|
59
|
+
z.strictObject({ id: identifier, field: z.literal("eventTime"), value: EventTimeValueSchema, provenance: SourceProvenanceSchema }),
|
|
60
|
+
z.strictObject({ id: identifier, field: z.literal("position"), value: PositionValueSchema, provenance: SourceProvenanceSchema })
|
|
61
|
+
]);
|
|
62
|
+
var PrimitiveRecordSchema = z.strictObject({
|
|
63
|
+
id: identifier,
|
|
64
|
+
eventTime: EventTimeSchema,
|
|
65
|
+
position: PositionSchema,
|
|
66
|
+
assertions: z.array(ValueAssertionSchema).optional()
|
|
67
|
+
});
|
|
68
|
+
var pointer = (path) => path.map((p) => `/${String(p).replace(/~/g, "~0").replace(/\//g, "~1")}`).join("");
|
|
69
|
+
function instantParts(value) {
|
|
70
|
+
const dot = value.indexOf(".", 10);
|
|
71
|
+
if (dot === -1) return { milliseconds: Date.parse(value), fraction: "" };
|
|
72
|
+
let end = dot + 1;
|
|
73
|
+
while (end < value.length && value[end] >= "0" && value[end] <= "9") end++;
|
|
74
|
+
return { milliseconds: Date.parse(value.slice(0, dot) + value.slice(end)), fraction: value.slice(dot + 1, end) };
|
|
75
|
+
}
|
|
76
|
+
function comparePoints(start, end) {
|
|
77
|
+
if (start.kind !== "instant" || end.kind !== "instant") return start.value < end.value ? -1 : start.value > end.value ? 1 : 0;
|
|
78
|
+
const a = instantParts(start.value);
|
|
79
|
+
const b = instantParts(end.value);
|
|
80
|
+
if (!Number.isFinite(a.milliseconds) || !Number.isFinite(b.milliseconds)) return NaN;
|
|
81
|
+
if (a.milliseconds !== b.milliseconds) return a.milliseconds < b.milliseconds ? -1 : 1;
|
|
82
|
+
const digits = Math.max(a.fraction.length, b.fraction.length);
|
|
83
|
+
const af = a.fraction.padEnd(digits, "0");
|
|
84
|
+
const bf = b.fraction.padEnd(digits, "0");
|
|
85
|
+
return af < bf ? -1 : af > bf ? 1 : 0;
|
|
86
|
+
}
|
|
87
|
+
function checkTime(value, path, issues) {
|
|
88
|
+
if (value.kind === "instant" && !Number.isFinite(instantParts(value.value).milliseconds)) {
|
|
89
|
+
issues.push({ code: "TIME.INSTANT", stage: "semantic", pointer: path, message: "Instant cannot be resolved with its declared UTC offset." });
|
|
90
|
+
}
|
|
91
|
+
if (value.kind !== "interval") return;
|
|
92
|
+
checkTime(value.start, `${path}/start`, issues);
|
|
93
|
+
checkTime(value.end, `${path}/end`, issues);
|
|
94
|
+
if (value.start.kind !== value.end.kind) {
|
|
95
|
+
issues.push({ code: "TIME.PRECISION_MISMATCH", stage: "semantic", pointer: path, message: "Interval endpoints must use the same kind of calendar precision or both be instants." });
|
|
96
|
+
} else if (comparePoints(value.start, value.end) > 0) {
|
|
97
|
+
issues.push({ code: "TIME.INTERVAL", stage: "semantic", pointer: `${path}/end`, message: "Interval end precedes its start." });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function parsePrimitiveRecord(input) {
|
|
101
|
+
const result = PrimitiveRecordSchema.safeParse(input);
|
|
102
|
+
if (!result.success) return { success: false, uncheckedRefs: [], issues: result.error.issues.map((issue) => ({
|
|
103
|
+
code: "STRUCT.VALUE",
|
|
104
|
+
stage: "structural",
|
|
105
|
+
pointer: pointer(issue.path),
|
|
106
|
+
message: issue.message
|
|
107
|
+
})) };
|
|
108
|
+
const data = result.data;
|
|
109
|
+
const issues = [];
|
|
110
|
+
const unchecked = /* @__PURE__ */ new Set();
|
|
111
|
+
const assertions = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const [index, assertion] of (data.assertions ?? []).entries()) {
|
|
113
|
+
if (assertions.has(assertion.id)) issues.push({ code: "REF.UNIQUE_ID", stage: "semantic", pointer: `/assertions/${index}/id`, message: "Assertion ID is duplicated." });
|
|
114
|
+
else assertions.set(assertion.id, assertion);
|
|
115
|
+
unchecked.add(assertion.provenance.sourceRef);
|
|
116
|
+
if (assertion.field === "eventTime") checkTime(assertion.value, `/assertions/${index}/value`, issues);
|
|
117
|
+
const locator = assertion.provenance.locator;
|
|
118
|
+
if (locator?.kind === "time_range" && locator.endSeconds < locator.startSeconds) issues.push({ code: "TIME.INTERVAL", stage: "semantic", pointer: `/assertions/${index}/provenance/locator/endSeconds`, message: "Locator end precedes its start." });
|
|
119
|
+
}
|
|
120
|
+
for (const field of ["eventTime", "position"]) {
|
|
121
|
+
const value = data[field];
|
|
122
|
+
if (value.state !== "known" && value.state !== "approximate") continue;
|
|
123
|
+
if (field === "eventTime" && (data.eventTime.state === "known" || data.eventTime.state === "approximate")) checkTime(data.eventTime.value, "/eventTime/value", issues);
|
|
124
|
+
for (const [index, ref2] of value.sourceRefs.entries()) {
|
|
125
|
+
if (!ref2.startsWith("assertion:")) {
|
|
126
|
+
unchecked.add(ref2);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const target = assertions.get(ref2.slice("assertion:".length));
|
|
130
|
+
if (!target) issues.push({ code: "REF.LOCAL_RESOLUTION", stage: "semantic", pointer: `/${field}/sourceRefs/${index}`, message: "Local assertion does not exist." });
|
|
131
|
+
else if (target.field !== field) issues.push({ code: "REF.FIELD_MISMATCH", stage: "semantic", pointer: `/${field}/sourceRefs/${index}`, message: "Assertion concerns a different field." });
|
|
132
|
+
}
|
|
133
|
+
const selection = value.selection;
|
|
134
|
+
if (!selection) continue;
|
|
135
|
+
const path = `/${field}/selection`;
|
|
136
|
+
unchecked.add(selection.methodRef);
|
|
137
|
+
if (!Number.isFinite(instantParts(selection.evaluatedAt).milliseconds)) issues.push({ code: "TIME.INSTANT", stage: "semantic", pointer: `${path}/evaluatedAt`, message: "Selection timestamp cannot be resolved with its declared UTC offset." });
|
|
138
|
+
if (!selection.consideredAssertionRefs.includes(selection.assertionRef)) issues.push({ code: "SELECTION.INPUTS", stage: "semantic", pointer: `${path}/assertionRef`, message: "Selected assertion must be among the considered inputs." });
|
|
139
|
+
if (!value.sourceRefs.includes(selection.assertionRef)) issues.push({ code: "SELECTION.SOURCE_REF", stage: "semantic", pointer: `/${field}/sourceRefs`, message: "Field provenance must cite the selected assertion." });
|
|
140
|
+
const refs = /* @__PURE__ */ new Set();
|
|
141
|
+
for (const [index, ref2] of selection.consideredAssertionRefs.entries()) {
|
|
142
|
+
if (refs.has(ref2)) issues.push({ code: "REF.UNIQUE_ID", stage: "semantic", pointer: `${path}/consideredAssertionRefs/${index}`, message: "Considered assertion is repeated." });
|
|
143
|
+
refs.add(ref2);
|
|
144
|
+
const target = assertions.get(ref2.slice("assertion:".length));
|
|
145
|
+
if (!target) issues.push({ code: "REF.LOCAL_RESOLUTION", stage: "semantic", pointer: `${path}/consideredAssertionRefs/${index}`, message: "Considered assertion does not exist." });
|
|
146
|
+
else if (target.field !== field) issues.push({ code: "REF.FIELD_MISMATCH", stage: "semantic", pointer: `${path}/consideredAssertionRefs/${index}`, message: "Considered assertion concerns a different field." });
|
|
147
|
+
}
|
|
148
|
+
const selected = assertions.get(selection.assertionRef.slice("assertion:".length));
|
|
149
|
+
if (!selected) issues.push({ code: "REF.LOCAL_RESOLUTION", stage: "semantic", pointer: `${path}/assertionRef`, message: "Selected assertion does not exist." });
|
|
150
|
+
else if (selected.field !== field) issues.push({ code: "REF.FIELD_MISMATCH", stage: "semantic", pointer: `${path}/assertionRef`, message: "Selected assertion concerns a different field." });
|
|
151
|
+
else if (JSON.stringify(selected.value) !== JSON.stringify(value.value)) issues.push({ code: "SELECTION.VALUE_MISMATCH", stage: "semantic", pointer: `/${field}/value`, message: "Selected value must equal the cited source value; derived values need a separate contract." });
|
|
152
|
+
}
|
|
153
|
+
return issues.length ? { success: false, issues, uncheckedRefs: [...unchecked] } : { success: true, data, issues, uncheckedRefs: [...unchecked] };
|
|
154
|
+
}
|
|
155
|
+
var PRIMITIVES_SCHEMA_ID = "urn:disclosureos:experimental:records-v2-primitives:0.2.0";
|
|
156
|
+
function primitivesJsonSchema() {
|
|
157
|
+
return { ...z.toJSONSchema(PrimitiveRecordSchema, { target: "draft-2020-12" }), $id: PRIMITIVES_SCHEMA_ID };
|
|
158
|
+
}
|
|
159
|
+
var text2 = z.string().min(1).regex(/\S/, "must contain non-whitespace text");
|
|
160
|
+
var id = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
161
|
+
var ref = (kinds) => z.string().regex(new RegExp(`^(?:${kinds.join("|")}):[A-Za-z0-9][A-Za-z0-9._-]*(?![\\s\\S])`));
|
|
162
|
+
var sourceProductRef = ref(["source", "product"]);
|
|
163
|
+
var valueRef2 = ref(["source", "product", "assertion"]);
|
|
164
|
+
var sourceRefs2 = z.array(valueRef2).min(1);
|
|
165
|
+
var access = z.enum(["public", "restricted", "withheld", "unknown"]);
|
|
166
|
+
var digest = z.strictObject({ algorithm: z.literal("sha256"), value: z.string().regex(/^[a-f0-9]{64}(?![\s\S])/) });
|
|
167
|
+
var quantified = { magnitude: z.number().min(0), unit: text2, sourceRefs: z.array(sourceProductRef).min(1) };
|
|
168
|
+
var QuantitativeUncertaintySchema = z.discriminatedUnion("kind", [
|
|
169
|
+
z.strictObject({ kind: z.literal("unknown"), reason: z.enum(["not_reported", "not_characterized", "unavailable"]) }),
|
|
170
|
+
z.strictObject({ kind: z.literal("standard"), ...quantified }),
|
|
171
|
+
z.strictObject({ kind: z.literal("expanded"), ...quantified, coverageFactor: z.number().positive(), coverageProbability: z.number().gt(0).lte(1).optional() }),
|
|
172
|
+
z.strictObject({ kind: z.literal("bound"), ...quantified })
|
|
173
|
+
]);
|
|
174
|
+
var MeasuredQuantitySchema = z.strictObject({
|
|
175
|
+
value: z.number(),
|
|
176
|
+
unit: text2,
|
|
177
|
+
uncertainty: QuantitativeUncertaintySchema
|
|
178
|
+
});
|
|
179
|
+
var definition = z.discriminatedUnion("state", [
|
|
180
|
+
z.strictObject({ state: z.literal("identified"), authority: text2, code: text2, revision: text2.optional() }),
|
|
181
|
+
z.strictObject({ state: z.literal("described"), description: text2 }),
|
|
182
|
+
z.strictObject({ state: z.literal("unknown"), reason: text2 })
|
|
183
|
+
]);
|
|
184
|
+
var ReferenceFrameSchema = z.discriminatedUnion("kind", [
|
|
185
|
+
z.strictObject({ id, kind: z.literal("geodetic"), definition, angularUnit: z.literal("deg"), vertical: z.strictObject({ definition, unit: text2 }).optional() }),
|
|
186
|
+
z.strictObject({ id, kind: z.literal("cartesian"), definition, unit: text2 })
|
|
187
|
+
]);
|
|
188
|
+
var FramedPositionSchema = z.discriminatedUnion("kind", [
|
|
189
|
+
PositionValueSchema.omit({ datum: true }).extend({
|
|
190
|
+
kind: z.literal("geodetic"),
|
|
191
|
+
frameRef: ref(["frame"]),
|
|
192
|
+
altitude: MeasuredQuantitySchema.optional(),
|
|
193
|
+
uncertainty: z.strictObject({ latitude: QuantitativeUncertaintySchema, longitude: QuantitativeUncertaintySchema }).optional()
|
|
194
|
+
}),
|
|
195
|
+
z.strictObject({ kind: z.literal("cartesian"), frameRef: ref(["frame"]), x: MeasuredQuantitySchema, y: MeasuredQuantitySchema, z: MeasuredQuantitySchema })
|
|
196
|
+
]);
|
|
197
|
+
function envelope2(value) {
|
|
198
|
+
return z.discriminatedUnion("state", [
|
|
199
|
+
z.strictObject({ state: z.literal("known"), value, sourceRefs: sourceRefs2, selection: ValueSelectionSchema.optional() }),
|
|
200
|
+
z.strictObject({ state: z.literal("approximate"), value, sourceRefs: sourceRefs2, selection: ValueSelectionSchema.optional(), precision: text2 }),
|
|
201
|
+
z.strictObject({ state: z.literal("unknown"), reason: z.enum(["not_recorded", "not_collected", "unavailable"]) }),
|
|
202
|
+
z.strictObject({ state: z.literal("redacted"), reason: text2 })
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
var timeEnvelope = envelope2(EventTimeValueSchema);
|
|
206
|
+
var ObservationTimeSchema = z.discriminatedUnion("state", [
|
|
207
|
+
timeEnvelope.options[0].extend({ uncertainty: QuantitativeUncertaintySchema.optional() }),
|
|
208
|
+
timeEnvelope.options[1].extend({ uncertainty: QuantitativeUncertaintySchema.optional() }),
|
|
209
|
+
timeEnvelope.options[2],
|
|
210
|
+
timeEnvelope.options[3]
|
|
211
|
+
]);
|
|
212
|
+
var ObservationPositionSchema = envelope2(FramedPositionSchema);
|
|
213
|
+
var ObservationSourceSchema = z.strictObject({
|
|
214
|
+
id,
|
|
215
|
+
kind: z.enum(["document", "testimony", "instrument_data", "other", "unknown"]),
|
|
216
|
+
access,
|
|
217
|
+
title: text2.optional(),
|
|
218
|
+
uri: z.url().optional(),
|
|
219
|
+
digest: digest.optional()
|
|
220
|
+
});
|
|
221
|
+
var product = { id, format: text2, access, uri: z.url().optional(), digest: digest.optional() };
|
|
222
|
+
var ObservationProductSchema = z.discriminatedUnion("kind", [
|
|
223
|
+
z.strictObject({ ...product, kind: z.literal("raw"), sourceRefs: z.array(ref(["source"])).min(1) }),
|
|
224
|
+
z.strictObject({ ...product, kind: z.literal("derived"), generatedBy: ref(["process"]) })
|
|
225
|
+
]);
|
|
226
|
+
var ObservationMethodSchema = z.strictObject({ id, version: text2, description: text2.optional(), uri: z.url().optional() });
|
|
227
|
+
var provenance = SourceProvenanceSchema.extend({ sourceRef: sourceProductRef, sourceDigest: digest.optional() });
|
|
228
|
+
var ObservationAssertionSchema = z.discriminatedUnion("field", [
|
|
229
|
+
z.strictObject({ id, field: z.literal("eventTime"), value: EventTimeValueSchema, provenance }),
|
|
230
|
+
z.strictObject({ id, field: z.literal("position"), value: FramedPositionSchema, provenance }),
|
|
231
|
+
z.strictObject({ id, field: z.literal("measurement"), measurementId: id, value: MeasuredQuantitySchema, provenance })
|
|
232
|
+
]);
|
|
233
|
+
var ObservationMeasurementSchema = z.strictObject({
|
|
234
|
+
id,
|
|
235
|
+
quantity: text2,
|
|
236
|
+
value: MeasuredQuantitySchema,
|
|
237
|
+
sourceRefs: sourceRefs2,
|
|
238
|
+
frameRef: ref(["frame"]).optional(),
|
|
239
|
+
selection: ValueSelectionSchema.optional()
|
|
240
|
+
});
|
|
241
|
+
var ProcessingActivitySchema = z.strictObject({
|
|
242
|
+
id,
|
|
243
|
+
methodRef: ref(["method"]),
|
|
244
|
+
methodVersion: text2,
|
|
245
|
+
performedBy: text2,
|
|
246
|
+
performedAt: instantString,
|
|
247
|
+
inputRefs: z.array(valueRef2).min(1),
|
|
248
|
+
outputRefs: z.array(ref(["product"])).min(1)
|
|
249
|
+
});
|
|
250
|
+
var EXPERIMENTAL_OBSERVATION_SCHEMA_ID = "urn:disclosureos:experimental:observation:0.1.0";
|
|
251
|
+
var ExperimentalObservationSchema = z.strictObject({
|
|
252
|
+
kind: z.literal("observation"),
|
|
253
|
+
schemaVersion: z.literal("0.1.0"),
|
|
254
|
+
id,
|
|
255
|
+
status: z.enum(["draft", "published", "withdrawn"]),
|
|
256
|
+
createdAt: instantString,
|
|
257
|
+
updatedAt: instantString,
|
|
258
|
+
summary: text2.optional(),
|
|
259
|
+
eventTime: ObservationTimeSchema,
|
|
260
|
+
position: ObservationPositionSchema,
|
|
261
|
+
sources: z.array(ObservationSourceSchema),
|
|
262
|
+
products: z.array(ObservationProductSchema),
|
|
263
|
+
methods: z.array(ObservationMethodSchema),
|
|
264
|
+
frames: z.array(ReferenceFrameSchema),
|
|
265
|
+
assertions: z.array(ObservationAssertionSchema),
|
|
266
|
+
measurements: z.array(ObservationMeasurementSchema),
|
|
267
|
+
processing: z.array(ProcessingActivitySchema),
|
|
268
|
+
extensions: z.record(z.string().regex(/^[a-z][a-z0-9_-]*(?:\.[a-z0-9_-]+)+(?![\s\S])/), z.json()).optional()
|
|
269
|
+
});
|
|
270
|
+
function experimentalObservationJsonSchema() {
|
|
271
|
+
return { ...z.toJSONSchema(ExperimentalObservationSchema, { target: "draft-2020-12" }), $id: EXPERIMENTAL_OBSERVATION_SCHEMA_ID };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/experimental/v2/observation.ts
|
|
275
|
+
var pointer2 = (path) => path.map((p) => `/${String(p).replace(/~/g, "~0").replace(/\//g, "~1")}`).join("");
|
|
276
|
+
var key = (ref2) => ref2.slice(ref2.indexOf(":") + 1);
|
|
277
|
+
function parseExperimentalObservation(input) {
|
|
278
|
+
const parsed = ExperimentalObservationSchema.safeParse(input);
|
|
279
|
+
if (!parsed.success) return {
|
|
280
|
+
success: false,
|
|
281
|
+
checks: { structural: "failed", semantic: "not_checked", profile: "not_checked", external: "not_checked" },
|
|
282
|
+
uncheckedRefs: [],
|
|
283
|
+
issues: parsed.error.issues.map((issue) => ({ code: "STRUCT.VALUE", stage: "structural", severity: "error", pointer: pointer2(issue.path), message: issue.message }))
|
|
284
|
+
};
|
|
285
|
+
const data = parsed.data;
|
|
286
|
+
const issues = [];
|
|
287
|
+
const graph = /* @__PURE__ */ new Map();
|
|
288
|
+
const problem = (code, path, message) => issues.push({ code, stage: "semantic", severity: "error", pointer: path, message });
|
|
289
|
+
function index(items, prefix, collection) {
|
|
290
|
+
const result = /* @__PURE__ */ new Map();
|
|
291
|
+
for (const [i, item] of items.entries()) {
|
|
292
|
+
if (result.has(item.id)) problem("REF.UNIQUE_ID", `/${collection}/${i}/id`, `Duplicate ${prefix} ID.`);
|
|
293
|
+
else {
|
|
294
|
+
result.set(item.id, item);
|
|
295
|
+
graph.set(`${prefix}:${item.id}`, /* @__PURE__ */ new Set());
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
const sources = index(data.sources, "source", "sources");
|
|
301
|
+
const products = index(data.products, "product", "products");
|
|
302
|
+
const methods = index(data.methods, "method", "methods");
|
|
303
|
+
const frames = index(data.frames, "frame", "frames");
|
|
304
|
+
const assertions = index(data.assertions, "assertion", "assertions");
|
|
305
|
+
index(data.measurements, "measurement", "measurements");
|
|
306
|
+
const processes = index(data.processing, "process", "processing");
|
|
307
|
+
const processOutputs = new Map(data.processing.map((process) => [process.id, new Set(process.outputRefs)]));
|
|
308
|
+
function resolve(ref2, path, owner) {
|
|
309
|
+
if (!graph.has(ref2)) {
|
|
310
|
+
problem("REF.LOCAL_RESOLUTION", path, `Undeclared local reference: ${ref2}`);
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (owner) graph.get(owner)?.add(ref2);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
function references(refs, path, owner) {
|
|
317
|
+
const seen = /* @__PURE__ */ new Set();
|
|
318
|
+
for (const [i, ref2] of refs.entries()) {
|
|
319
|
+
if (seen.has(ref2)) problem("REF.UNIQUE_ID", `${path}/${i}`, "Reference is repeated.");
|
|
320
|
+
seen.add(ref2);
|
|
321
|
+
resolve(ref2, `${path}/${i}`, owner);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function method(ref2, version, path, owner) {
|
|
325
|
+
if (resolve(ref2, `${path}/methodRef`, owner) && methods.get(key(ref2))?.version !== version) {
|
|
326
|
+
problem("METHOD.VERSION_MISMATCH", `${path}/methodVersion`, "Method version differs from the local declaration.");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function time(value, path) {
|
|
330
|
+
const local = [];
|
|
331
|
+
checkTime(value, path, local);
|
|
332
|
+
for (const issue of local) problem(issue.code, issue.pointer, issue.message);
|
|
333
|
+
}
|
|
334
|
+
function instant(value, path) {
|
|
335
|
+
time({ kind: "instant", value}, path);
|
|
336
|
+
}
|
|
337
|
+
function uncertainty(value, expectedUnit, path, owner) {
|
|
338
|
+
if (value.kind === "unknown") return;
|
|
339
|
+
const units = typeof expectedUnit === "string" ? [expectedUnit] : expectedUnit;
|
|
340
|
+
if (!units.includes(value.unit)) problem("UNIT.MISMATCH", `${path}/unit`, "Uncertainty unit does not match the declared quantity; no conversion is inferred.");
|
|
341
|
+
references(value.sourceRefs, `${path}/sourceRefs`, owner);
|
|
342
|
+
}
|
|
343
|
+
function quantity(value, path, owner, expectedUnit) {
|
|
344
|
+
if (expectedUnit !== void 0 && value.unit !== expectedUnit) problem("UNIT.MISMATCH", `${path}/unit`, "Quantity unit differs from its reference frame.");
|
|
345
|
+
uncertainty(value.uncertainty, value.unit, `${path}/uncertainty`, owner);
|
|
346
|
+
}
|
|
347
|
+
function position(value, path, owner) {
|
|
348
|
+
resolve(value.frameRef, `${path}/frameRef`, owner);
|
|
349
|
+
const frame = frames.get(key(value.frameRef));
|
|
350
|
+
if (frame && frame.kind !== value.kind) problem("FRAME.KIND_MISMATCH", `${path}/frameRef`, "Position and reference frame use different coordinate systems.");
|
|
351
|
+
if (value.kind === "geodetic") {
|
|
352
|
+
if (value.uncertainty) {
|
|
353
|
+
uncertainty(value.uncertainty.latitude, "deg", `${path}/uncertainty/latitude`, owner);
|
|
354
|
+
uncertainty(value.uncertainty.longitude, "deg", `${path}/uncertainty/longitude`, owner);
|
|
355
|
+
}
|
|
356
|
+
if (value.altitude) {
|
|
357
|
+
if (frame?.kind === "geodetic" && !frame.vertical) problem("FRAME.VERTICAL_REFERENCE", `${path}/altitude`, "Altitude requires an explicit vertical reference and unit.");
|
|
358
|
+
quantity(value.altitude, `${path}/altitude`, owner, frame?.kind === "geodetic" ? frame.vertical?.unit : void 0);
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
for (const axis of ["x", "y", "z"]) quantity(value[axis], `${path}/${axis}`, owner, frame?.kind === "cartesian" ? frame.unit : void 0);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function matchesField(ref2, field, measurementId) {
|
|
365
|
+
const assertion = assertions.get(key(ref2));
|
|
366
|
+
return assertion?.field === field && (assertion.field !== "measurement" || assertion.measurementId === measurementId);
|
|
367
|
+
}
|
|
368
|
+
function fieldReferences(refs, field, path, owner, measurementId) {
|
|
369
|
+
references(refs, path, owner);
|
|
370
|
+
for (const [i, ref2] of refs.entries()) {
|
|
371
|
+
if (ref2.startsWith("assertion:") && assertions.has(key(ref2)) && !matchesField(ref2, field, measurementId)) problem("REF.FIELD_MISMATCH", `${path}/${i}`, "Assertion concerns a different field or measurement.");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function selection(value, selectedValue, refs, field, path, owner, measurementId) {
|
|
375
|
+
if (!value) {
|
|
376
|
+
if (refs.some((ref2) => ref2.startsWith("assertion:"))) problem("SELECTION.REQUIRED", `${path}/selection`, "A curated value citing local assertions requires an explicit selection record.");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const selectionPath = `${path}/selection`;
|
|
380
|
+
instant(value.evaluatedAt, `${selectionPath}/evaluatedAt`);
|
|
381
|
+
method(value.methodRef, value.methodVersion, selectionPath, owner);
|
|
382
|
+
fieldReferences(value.consideredAssertionRefs, field, `${selectionPath}/consideredAssertionRefs`, owner, measurementId);
|
|
383
|
+
resolve(value.assertionRef, `${selectionPath}/assertionRef`, owner);
|
|
384
|
+
if (!value.consideredAssertionRefs.includes(value.assertionRef)) problem("SELECTION.INPUTS", `${selectionPath}/assertionRef`, "Selected assertion must be among considered inputs.");
|
|
385
|
+
if (!refs.includes(value.assertionRef)) problem("SELECTION.SOURCE_REF", `${path}/sourceRefs`, "Field must cite the selected assertion.");
|
|
386
|
+
const target = assertions.get(key(value.assertionRef));
|
|
387
|
+
if (target && !matchesField(value.assertionRef, field, measurementId)) problem("REF.FIELD_MISMATCH", `${selectionPath}/assertionRef`, "Selected assertion concerns a different field or measurement.");
|
|
388
|
+
else if (target && JSON.stringify(target.value) !== JSON.stringify(selectedValue)) problem("SELECTION.VALUE_MISMATCH", `${path}/value`, "Selection must copy the cited assertion value exactly; processing is declared separately.");
|
|
389
|
+
}
|
|
390
|
+
instant(data.createdAt, "/createdAt");
|
|
391
|
+
instant(data.updatedAt, "/updatedAt");
|
|
392
|
+
if (comparePoints({ kind: "instant", value: data.createdAt}, { kind: "instant", value: data.updatedAt}) > 0) problem("TIME.RECORD_ORDER", "/updatedAt", "Record update precedes record creation.");
|
|
393
|
+
for (const [i, product2] of data.products.entries()) {
|
|
394
|
+
const owner = `product:${product2.id}`;
|
|
395
|
+
if (product2.kind === "raw") references(product2.sourceRefs, `/products/${i}/sourceRefs`, owner);
|
|
396
|
+
else {
|
|
397
|
+
resolve(product2.generatedBy, `/products/${i}/generatedBy`, owner);
|
|
398
|
+
const producer = processes.get(key(product2.generatedBy));
|
|
399
|
+
if (producer && !processOutputs.get(producer.id)?.has(owner)) problem("PROCESS.OUTPUT_MISMATCH", `/products/${i}/generatedBy`, "Producer does not list this derived product as an output.");
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
for (const [i, assertion] of data.assertions.entries()) {
|
|
403
|
+
const path = `/assertions/${i}`;
|
|
404
|
+
const owner = `assertion:${assertion.id}`;
|
|
405
|
+
resolve(assertion.provenance.sourceRef, `${path}/provenance/sourceRef`, owner);
|
|
406
|
+
const origin = assertion.provenance.sourceRef.startsWith("source:") ? sources.get(key(assertion.provenance.sourceRef)) : products.get(key(assertion.provenance.sourceRef));
|
|
407
|
+
if (assertion.provenance.sourceDigest && origin?.digest && assertion.provenance.sourceDigest.value !== origin.digest.value) problem("SOURCE.DIGEST_MISMATCH", `${path}/provenance/sourceDigest`, "Declared digest differs from the inventory declaration; neither has been verified against bytes.");
|
|
408
|
+
const locator = assertion.provenance.locator;
|
|
409
|
+
if (locator?.kind === "time_range" && locator.endSeconds < locator.startSeconds) problem("TIME.INTERVAL", `${path}/provenance/locator/endSeconds`, "Locator end precedes its start.");
|
|
410
|
+
if (assertion.field === "eventTime") time(assertion.value, `${path}/value`);
|
|
411
|
+
else if (assertion.field === "position") position(assertion.value, `${path}/value`, owner);
|
|
412
|
+
else {
|
|
413
|
+
resolve(`measurement:${assertion.measurementId}`, `${path}/measurementId`);
|
|
414
|
+
quantity(assertion.value, `${path}/value`, owner);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (data.eventTime.state === "known" || data.eventTime.state === "approximate") {
|
|
418
|
+
time(data.eventTime.value, "/eventTime/value");
|
|
419
|
+
fieldReferences(data.eventTime.sourceRefs, "eventTime", "/eventTime/sourceRefs");
|
|
420
|
+
if (data.eventTime.uncertainty) uncertainty(data.eventTime.uncertainty, ["s", "ms", "us", "ns"], "/eventTime/uncertainty");
|
|
421
|
+
selection(data.eventTime.selection, data.eventTime.value, data.eventTime.sourceRefs, "eventTime", "/eventTime");
|
|
422
|
+
}
|
|
423
|
+
if (data.position.state === "known" || data.position.state === "approximate") {
|
|
424
|
+
position(data.position.value, "/position/value");
|
|
425
|
+
fieldReferences(data.position.sourceRefs, "position", "/position/sourceRefs");
|
|
426
|
+
selection(data.position.selection, data.position.value, data.position.sourceRefs, "position", "/position");
|
|
427
|
+
}
|
|
428
|
+
for (const [i, measurement] of data.measurements.entries()) {
|
|
429
|
+
const path = `/measurements/${i}`;
|
|
430
|
+
const owner = `measurement:${measurement.id}`;
|
|
431
|
+
if (measurement.frameRef) resolve(measurement.frameRef, `${path}/frameRef`, owner);
|
|
432
|
+
quantity(measurement.value, `${path}/value`, owner);
|
|
433
|
+
fieldReferences(measurement.sourceRefs, "measurement", `${path}/sourceRefs`, owner, measurement.id);
|
|
434
|
+
selection(measurement.selection, measurement.value, measurement.sourceRefs, "measurement", path, owner, measurement.id);
|
|
435
|
+
}
|
|
436
|
+
for (const [i, process] of data.processing.entries()) {
|
|
437
|
+
const path = `/processing/${i}`;
|
|
438
|
+
const owner = `process:${process.id}`;
|
|
439
|
+
instant(process.performedAt, `${path}/performedAt`);
|
|
440
|
+
method(process.methodRef, process.methodVersion, path, owner);
|
|
441
|
+
references(process.inputRefs, `${path}/inputRefs`, owner);
|
|
442
|
+
references(process.outputRefs, `${path}/outputRefs`);
|
|
443
|
+
for (const [j, ref2] of process.outputRefs.entries()) {
|
|
444
|
+
const product2 = products.get(key(ref2));
|
|
445
|
+
if (product2 && (product2.kind !== "derived" || product2.generatedBy !== owner)) problem("PROCESS.OUTPUT_MISMATCH", `${path}/outputRefs/${j}`, "Output must be a derived product identifying this producer.");
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const indegrees = new Map([...graph.keys()].map((ref2) => [ref2, 0]));
|
|
449
|
+
for (const dependencies of graph.values()) for (const ref2 of dependencies) indegrees.set(ref2, (indegrees.get(ref2) ?? 0) + 1);
|
|
450
|
+
const queue = [...indegrees].filter(([, degree]) => degree === 0).map(([ref2]) => ref2);
|
|
451
|
+
for (let i = 0; i < queue.length; i++) {
|
|
452
|
+
for (const dependency of graph.get(queue[i]) ?? []) {
|
|
453
|
+
const degree = indegrees.get(dependency) - 1;
|
|
454
|
+
indegrees.set(dependency, degree);
|
|
455
|
+
if (degree === 0) queue.push(dependency);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (queue.length !== graph.size) problem("PROCESS.CYCLE", "/processing", "Local processing/provenance dependencies contain a cycle.");
|
|
459
|
+
const checks = { structural: "passed", semantic: issues.length ? "failed" : "passed", profile: "not_checked", external: "not_checked" };
|
|
460
|
+
const uncheckedRefs = [...sources.keys()].map((id3) => `source:${id3}`).concat(
|
|
461
|
+
[...products.keys()].map((id3) => `product:${id3}`),
|
|
462
|
+
[...methods.keys()].map((id3) => `method:${id3}`),
|
|
463
|
+
[...frames.keys()].map((id3) => `frame:${id3}`)
|
|
464
|
+
);
|
|
465
|
+
return issues.length ? { success: false, issues, checks, uncheckedRefs } : { success: true, data, issues, checks, uncheckedRefs };
|
|
466
|
+
}
|
|
467
|
+
var text3 = z.string().min(1).regex(/\S/, "must contain non-whitespace text");
|
|
468
|
+
var id2 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
469
|
+
var claimRef = z.string().regex(/^claim:[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
470
|
+
var inputRef = z.string().regex(/^(?:claim|source|product|assertion|measurement):[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
471
|
+
var ClaimSubjectSchema = z.discriminatedUnion("kind", [
|
|
472
|
+
z.strictObject({ kind: z.literal("observation") }),
|
|
473
|
+
z.strictObject({ kind: z.literal("measurement"), measurementId: id2 })
|
|
474
|
+
]);
|
|
475
|
+
var common = {
|
|
476
|
+
id: id2,
|
|
477
|
+
recordedAt: instantString,
|
|
478
|
+
topic: text3,
|
|
479
|
+
subject: ClaimSubjectSchema,
|
|
480
|
+
supersedes: z.array(claimRef).min(1).optional()
|
|
481
|
+
};
|
|
482
|
+
var SourceStatementSchema = z.strictObject({
|
|
483
|
+
...common,
|
|
484
|
+
kind: z.literal("source_assertion"),
|
|
485
|
+
text: text3,
|
|
486
|
+
reportedLevel: text3.optional(),
|
|
487
|
+
provenance: ObservationAssertionSchema.options[0].shape.provenance
|
|
488
|
+
});
|
|
489
|
+
var assessment = { ...common, kind: z.literal("assessment"), inputRefs: z.array(inputRef), rationale: text3 };
|
|
490
|
+
var UnassessedClaimSchema = z.strictObject({ ...assessment, status: z.literal("unassessed") });
|
|
491
|
+
var AssessedClaimSchema = z.strictObject({
|
|
492
|
+
...assessment,
|
|
493
|
+
status: z.literal("assessed"),
|
|
494
|
+
outcome: z.enum(["reported", "confirmed", "absent", "inconclusive"]),
|
|
495
|
+
evaluatedBy: text3,
|
|
496
|
+
evaluatedAt: instantString,
|
|
497
|
+
methodRef: z.string().regex(/^method:[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/),
|
|
498
|
+
methodVersion: text3,
|
|
499
|
+
confidence: z.number().min(0).max(1).optional()
|
|
500
|
+
});
|
|
501
|
+
var HistoricalClaimSchema = z.union([SourceStatementSchema, UnassessedClaimSchema, AssessedClaimSchema]);
|
|
502
|
+
var EXPERIMENTAL_CLAIM_HISTORY_SCHEMA_ID = "urn:disclosureos:experimental:claim-history:0.1.0";
|
|
503
|
+
var CLAIM_HISTORY_RULESET_VERSION = "0.1.0";
|
|
504
|
+
var ExperimentalClaimHistorySchema = z.strictObject({
|
|
505
|
+
kind: z.literal("claim_history"),
|
|
506
|
+
schemaVersion: z.literal("0.1.0"),
|
|
507
|
+
id: id2,
|
|
508
|
+
observation: ExperimentalObservationSchema,
|
|
509
|
+
claims: z.array(HistoricalClaimSchema),
|
|
510
|
+
extensions: ExperimentalObservationSchema.shape.extensions
|
|
511
|
+
});
|
|
512
|
+
function experimentalClaimHistoryJsonSchema() {
|
|
513
|
+
return { ...z.toJSONSchema(ExperimentalClaimHistorySchema, { target: "draft-2020-12" }), $id: EXPERIMENTAL_CLAIM_HISTORY_SCHEMA_ID };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/experimental/v2/claim-history.ts
|
|
517
|
+
var point = (value) => ({ kind: "instant", value, timeScale: "UTC" });
|
|
518
|
+
var pointer3 = (path) => path.map((p) => `/${String(p).replace(/~/g, "~0").replace(/\//g, "~1")}`).join("");
|
|
519
|
+
function parseExperimentalClaimHistory(input) {
|
|
520
|
+
const contract = { kind: "claim_history", schemaId: EXPERIMENTAL_CLAIM_HISTORY_SCHEMA_ID, rulesetVersion: CLAIM_HISTORY_RULESET_VERSION };
|
|
521
|
+
const parsed = ExperimentalClaimHistorySchema.safeParse(input);
|
|
522
|
+
if (!parsed.success) return {
|
|
523
|
+
success: false,
|
|
524
|
+
contract,
|
|
525
|
+
checks: { structural: "failed", semantic: "not_checked", profile: "not_checked", external: "not_checked" },
|
|
526
|
+
uncheckedRefs: [],
|
|
527
|
+
issues: parsed.error.issues.map((issue) => ({ code: "STRUCT.VALUE", stage: "structural", severity: "error", pointer: pointer3(issue.path), message: issue.message }))
|
|
528
|
+
};
|
|
529
|
+
const data = parsed.data;
|
|
530
|
+
const observationResult = parseExperimentalObservation(data.observation);
|
|
531
|
+
const issues = observationResult.issues.map((issue) => ({ ...issue, pointer: `/observation${issue.pointer}` }));
|
|
532
|
+
const problem = (code, path, message) => issues.push({ code, stage: "semantic", severity: "error", pointer: path, message });
|
|
533
|
+
const claims = /* @__PURE__ */ new Map();
|
|
534
|
+
const graph = /* @__PURE__ */ new Map();
|
|
535
|
+
const superseded = /* @__PURE__ */ new Set();
|
|
536
|
+
const sources = new Map(data.observation.sources.map((item) => [`source:${item.id}`, item]));
|
|
537
|
+
const products = new Map(data.observation.products.map((item) => [`product:${item.id}`, item]));
|
|
538
|
+
const methods = new Map(data.observation.methods.map((item) => [`method:${item.id}`, item]));
|
|
539
|
+
const measurements = new Set(data.observation.measurements.map((item) => `measurement:${item.id}`));
|
|
540
|
+
const available = /* @__PURE__ */ new Set([
|
|
541
|
+
...sources.keys(),
|
|
542
|
+
...products.keys(),
|
|
543
|
+
...measurements,
|
|
544
|
+
...data.observation.assertions.map((item) => `assertion:${item.id}`)
|
|
545
|
+
]);
|
|
546
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
547
|
+
const ref2 = `claim:${claim.id}`;
|
|
548
|
+
if (claims.has(ref2)) problem("REF.UNIQUE_ID", `/claims/${i}/id`, "Duplicate claim ID; each revision must have its own ID.");
|
|
549
|
+
else {
|
|
550
|
+
claims.set(ref2, claim);
|
|
551
|
+
graph.set(ref2, /* @__PURE__ */ new Set());
|
|
552
|
+
available.add(ref2);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function instant(value, path) {
|
|
556
|
+
const local = [];
|
|
557
|
+
checkTime(point(value), path, local);
|
|
558
|
+
for (const issue of local) problem(issue.code, issue.pointer, issue.message);
|
|
559
|
+
}
|
|
560
|
+
function resolve(ref2, path, owner) {
|
|
561
|
+
if (!available.has(ref2)) problem("REF.LOCAL_RESOLUTION", path, `Undeclared local reference: ${ref2}`);
|
|
562
|
+
else if (owner && claims.has(ref2)) graph.get(owner)?.add(ref2);
|
|
563
|
+
}
|
|
564
|
+
function refs(values, path, owner) {
|
|
565
|
+
const seen = /* @__PURE__ */ new Set();
|
|
566
|
+
for (const [i, ref2] of values.entries()) {
|
|
567
|
+
if (seen.has(ref2)) problem("REF.UNIQUE_ID", `${path}/${i}`, "Reference is repeated.");
|
|
568
|
+
seen.add(ref2);
|
|
569
|
+
resolve(ref2, `${path}/${i}`, owner);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
573
|
+
const path = `/claims/${i}`;
|
|
574
|
+
const owner = `claim:${claim.id}`;
|
|
575
|
+
instant(claim.recordedAt, `${path}/recordedAt`);
|
|
576
|
+
if (claim.subject.kind === "measurement") resolve(`measurement:${claim.subject.measurementId}`, `${path}/subject/measurementId`);
|
|
577
|
+
if (claim.supersedes) {
|
|
578
|
+
refs(claim.supersedes, `${path}/supersedes`, owner);
|
|
579
|
+
for (const [j, ref2] of claim.supersedes.entries()) {
|
|
580
|
+
const previous = claims.get(ref2);
|
|
581
|
+
if (!previous) continue;
|
|
582
|
+
superseded.add(ref2);
|
|
583
|
+
if (previous.kind !== claim.kind || previous.topic !== claim.topic || JSON.stringify(previous.subject) !== JSON.stringify(claim.subject)) {
|
|
584
|
+
problem("CLAIM.SUPERSESSION_SCOPE", `${path}/supersedes/${j}`, "A revision must retain the claim kind, topic, and subject. Record a different claim separately.");
|
|
585
|
+
}
|
|
586
|
+
if (previous.kind === "assessment" && previous.status === "assessed" && claim.kind === "assessment" && (claim.status !== "assessed" || previous.evaluatedBy !== claim.evaluatedBy)) {
|
|
587
|
+
problem("CLAIM.REVISION_ATTRIBUTION", `${path}/supersedes/${j}`, "An assessed revision must retain its evaluator; other evaluators record independent assessments.");
|
|
588
|
+
}
|
|
589
|
+
if (comparePoints(point(previous.recordedAt), point(claim.recordedAt)) > 0) {
|
|
590
|
+
problem("CLAIM.REVISION_ORDER", `${path}/recordedAt`, "A revision cannot be recorded before the statement it supersedes.");
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (claim.kind === "source_assertion") {
|
|
595
|
+
resolve(claim.provenance.sourceRef, `${path}/provenance/sourceRef`);
|
|
596
|
+
const source = sources.get(claim.provenance.sourceRef) ?? products.get(claim.provenance.sourceRef);
|
|
597
|
+
if (source?.digest && claim.provenance.sourceDigest && source.digest.value !== claim.provenance.sourceDigest.value) {
|
|
598
|
+
problem("SOURCE.DIGEST_MISMATCH", `${path}/provenance/sourceDigest`, "Digest differs from the inventory declaration; neither is verified against bytes.");
|
|
599
|
+
}
|
|
600
|
+
const locator = claim.provenance.locator;
|
|
601
|
+
if (locator?.kind === "time_range" && locator.endSeconds < locator.startSeconds) problem("TIME.INTERVAL", `${path}/provenance/locator/endSeconds`, "Locator end precedes its start.");
|
|
602
|
+
} else {
|
|
603
|
+
refs(claim.inputRefs, `${path}/inputRefs`, owner);
|
|
604
|
+
if (claim.status === "assessed") {
|
|
605
|
+
instant(claim.evaluatedAt, `${path}/evaluatedAt`);
|
|
606
|
+
if (comparePoints(point(claim.evaluatedAt), point(claim.recordedAt)) > 0) problem("CLAIM.EVALUATION_ORDER", `${path}/evaluatedAt`, "Evaluation cannot occur after the assessment was recorded.");
|
|
607
|
+
const method = methods.get(claim.methodRef);
|
|
608
|
+
if (!method) problem("REF.LOCAL_RESOLUTION", `${path}/methodRef`, "Assessment method is not declared in the Observation inventory.");
|
|
609
|
+
else if (method.version !== claim.methodVersion) problem("METHOD.VERSION_MISMATCH", `${path}/methodVersion`, "Assessment method version differs from its inventory declaration.");
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
const indegrees = new Map([...graph.keys()].map((ref2) => [ref2, 0]));
|
|
614
|
+
for (const edges of graph.values()) for (const ref2 of edges) indegrees.set(ref2, indegrees.get(ref2) + 1);
|
|
615
|
+
const queue = [...indegrees].filter(([, degree]) => degree === 0).map(([ref2]) => ref2);
|
|
616
|
+
for (let i = 0; i < queue.length; i++) for (const ref2 of graph.get(queue[i]) ?? []) {
|
|
617
|
+
const degree = indegrees.get(ref2) - 1;
|
|
618
|
+
indegrees.set(ref2, degree);
|
|
619
|
+
if (degree === 0) queue.push(ref2);
|
|
620
|
+
}
|
|
621
|
+
if (queue.length !== graph.size) problem("CLAIM.DEPENDENCY_CYCLE", "/claims", "Claim inputs and supersession must form an acyclic history.");
|
|
622
|
+
const validation = {
|
|
623
|
+
contract,
|
|
624
|
+
issues,
|
|
625
|
+
uncheckedRefs: observationResult.uncheckedRefs,
|
|
626
|
+
checks: { structural: "passed", semantic: issues.length ? "failed" : "passed", profile: "not_checked", external: "not_checked" }
|
|
627
|
+
};
|
|
628
|
+
return issues.length ? { ...validation, success: false } : {
|
|
629
|
+
...validation,
|
|
630
|
+
success: true,
|
|
631
|
+
data,
|
|
632
|
+
currentClaimRefs: [...claims.keys()].filter((ref2) => !superseded.has(ref2))
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/experimental/v2/utc.ts
|
|
637
|
+
function compareUtcInstants(left, right) {
|
|
638
|
+
if (!instantString.safeParse(left).success || !instantString.safeParse(right).success) return void 0;
|
|
639
|
+
const result = comparePoints({ kind: "instant", value: left}, { kind: "instant", value: right});
|
|
640
|
+
return result === -1 || result === 0 || result === 1 ? result : void 0;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export { AssessedClaimSchema, CLAIM_HISTORY_RULESET_VERSION, ClaimSubjectSchema, EXPERIMENTAL_CLAIM_HISTORY_SCHEMA_ID, EXPERIMENTAL_OBSERVATION_SCHEMA_ID, EventTimeIntervalSchema, EventTimePointSchema, EventTimeSchema, EventTimeValueSchema, ExperimentalClaimHistorySchema, ExperimentalObservationSchema, FramedPositionSchema, HistoricalClaimSchema, MeasuredQuantitySchema, ObservationAssertionSchema, ObservationMeasurementSchema, ObservationMethodSchema, ObservationPositionSchema, ObservationProductSchema, ObservationSourceSchema, ObservationTimeSchema, PRIMITIVES_SCHEMA_ID, PositionSchema, PositionValueSchema, PrimitiveRecordSchema, ProcessingActivitySchema, QuantitativeUncertaintySchema, ReferenceFrameSchema, SourceProvenanceSchema, SourceStatementSchema, UnassessedClaimSchema, ValueAssertionSchema, ValueSelectionSchema, compareUtcInstants, experimentalClaimHistoryJsonSchema, experimentalObservationJsonSchema, parseExperimentalClaimHistory, parseExperimentalObservation, parsePrimitiveRecord, primitivesJsonSchema };
|
|
644
|
+
//# sourceMappingURL=index.js.map
|
|
645
|
+
//# sourceMappingURL=index.js.map
|