@affiant/core 0.1.0-alpha.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/LICENSE +202 -0
- package/README.md +354 -0
- package/dist/context.d.ts +136 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +30 -0
- package/dist/context.js.map +1 -0
- package/dist/docket/entry.d.ts +421 -0
- package/dist/docket/entry.d.ts.map +1 -0
- package/dist/docket/entry.js +155 -0
- package/dist/docket/entry.js.map +1 -0
- package/dist/docket/expiry.d.ts +82 -0
- package/dist/docket/expiry.d.ts.map +1 -0
- package/dist/docket/expiry.js +106 -0
- package/dist/docket/expiry.js.map +1 -0
- package/dist/docket/memory.d.ts +163 -0
- package/dist/docket/memory.d.ts.map +1 -0
- package/dist/docket/memory.js +528 -0
- package/dist/docket/memory.js.map +1 -0
- package/dist/docket/store.d.ts +387 -0
- package/dist/docket/store.d.ts.map +1 -0
- package/dist/docket/store.js +51 -0
- package/dist/docket/store.js.map +1 -0
- package/dist/errors.d.ts +153 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +164 -0
- package/dist/errors.js.map +1 -0
- package/dist/gate/coverage.d.ts +152 -0
- package/dist/gate/coverage.d.ts.map +1 -0
- package/dist/gate/coverage.js +114 -0
- package/dist/gate/coverage.js.map +1 -0
- package/dist/gate/decide.d.ts +207 -0
- package/dist/gate/decide.d.ts.map +1 -0
- package/dist/gate/decide.js +559 -0
- package/dist/gate/decide.js.map +1 -0
- package/dist/gate/gate.d.ts +212 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +175 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/gate/pipeline.d.ts +285 -0
- package/dist/gate/pipeline.d.ts.map +1 -0
- package/dist/gate/pipeline.js +515 -0
- package/dist/gate/pipeline.js.map +1 -0
- package/dist/gate/policy.d.ts +272 -0
- package/dist/gate/policy.d.ts.map +1 -0
- package/dist/gate/policy.js +396 -0
- package/dist/gate/policy.js.map +1 -0
- package/dist/gate/wrap.d.ts +107 -0
- package/dist/gate/wrap.d.ts.map +1 -0
- package/dist/gate/wrap.js +164 -0
- package/dist/gate/wrap.js.map +1 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/model/affidavit.d.ts +354 -0
- package/dist/model/affidavit.d.ts.map +1 -0
- package/dist/model/affidavit.js +417 -0
- package/dist/model/affidavit.js.map +1 -0
- package/dist/model/amendments.d.ts +160 -0
- package/dist/model/amendments.d.ts.map +1 -0
- package/dist/model/amendments.js +183 -0
- package/dist/model/amendments.js.map +1 -0
- package/dist/model/canonical.d.ts +311 -0
- package/dist/model/canonical.d.ts.map +1 -0
- package/dist/model/canonical.js +665 -0
- package/dist/model/canonical.js.map +1 -0
- package/dist/model/money.d.ts +127 -0
- package/dist/model/money.d.ts.map +1 -0
- package/dist/model/money.js +177 -0
- package/dist/model/money.js.map +1 -0
- package/dist/model/provenance.d.ts +315 -0
- package/dist/model/provenance.d.ts.map +1 -0
- package/dist/model/provenance.js +223 -0
- package/dist/model/provenance.js.map +1 -0
- package/dist/ports.d.ts +269 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +34 -0
- package/dist/ports.js.map +1 -0
- package/dist/store-memory.d.ts +21 -0
- package/dist/store-memory.d.ts.map +1 -0
- package/dist/store-memory.js +20 -0
- package/dist/store-memory.js.map +1 -0
- package/dist/telemetry-keys.d.ts +65 -0
- package/dist/telemetry-keys.d.ts.map +1 -0
- package/dist/telemetry-keys.js +72 -0
- package/dist/telemetry-keys.js.map +1 -0
- package/dist/telemetry.d.ts +77 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +43 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/testing.d.ts +574 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +1291 -0
- package/dist/testing.js.map +1 -0
- package/package.json +75 -0
- package/telemetry-keys.json +92 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Affidavit: the sworn evidence record for one proposed write, its fields, the
|
|
3
|
+
* three confidence numbers, and the mapping to and from the wire shape.
|
|
4
|
+
*
|
|
5
|
+
* **Rules served: AF-1** (`fields[]` carries only the proposed fields; unknown
|
|
6
|
+
* provenance is present and tagged `Empty`, a field the operation does not propose
|
|
7
|
+
* is absent), **AF-2** (three confidence numbers, `aggregateConfidence` the
|
|
8
|
+
* *minimum* over proposed fields with `Empty` counting as `0`), **AF-3**
|
|
9
|
+
* (`entityId` non-null ⇔ update; an update carries a `previousValue` key on every
|
|
10
|
+
* proposed field, `null` where the field had no stored value), **SR-2** (money is a
|
|
11
|
+
* decimal string plus an ISO 4217 code — the shape only; the validator is pull
|
|
12
|
+
* request C3).
|
|
13
|
+
*
|
|
14
|
+
* Why AF-2 is a minimum and not a mean: a mean that first discards every `Empty`
|
|
15
|
+
* field lets a mostly-empty Affidavit report high confidence, which is the exact
|
|
16
|
+
* hole once provenance authorises writes. The shipped .NET projection computes a
|
|
17
|
+
* mean over non-`Empty` fields; the parity manifest names it.
|
|
18
|
+
*
|
|
19
|
+
* Nothing here reads a clock or a database. `createdAt` is passed in and
|
|
20
|
+
* `previousValue` comes from the host's projection port.
|
|
21
|
+
*
|
|
22
|
+
* @packageDocumentation
|
|
23
|
+
*/
|
|
24
|
+
import { PROTOCOL_VERSION } from "@affiant/contract";
|
|
25
|
+
import { chainOf, emptyTag, mintTag, } from "./provenance.js";
|
|
26
|
+
/**
|
|
27
|
+
* Whether `value` is a {@link JsonValue}: no `undefined`, no function, no symbol, no
|
|
28
|
+
* `NaN` or infinity, no cycle.
|
|
29
|
+
*
|
|
30
|
+
* Checked deeply, because a field value is carried onto a record a person is asked
|
|
31
|
+
* to swear to and later hashed into a canonical form (SR-1); a value that
|
|
32
|
+
* serialises to something different from what the reviewer saw is the failure mode
|
|
33
|
+
* this closes.
|
|
34
|
+
*/
|
|
35
|
+
export function isJsonValue(value) {
|
|
36
|
+
return isJsonValueWithin(value, new WeakSet());
|
|
37
|
+
}
|
|
38
|
+
function isJsonValueWithin(value, seen) {
|
|
39
|
+
if (value === null)
|
|
40
|
+
return true;
|
|
41
|
+
switch (typeof value) {
|
|
42
|
+
case "string":
|
|
43
|
+
case "boolean":
|
|
44
|
+
return true;
|
|
45
|
+
case "number":
|
|
46
|
+
return Number.isFinite(value);
|
|
47
|
+
case "object":
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const object = value;
|
|
53
|
+
if (seen.has(object))
|
|
54
|
+
return false;
|
|
55
|
+
seen.add(object);
|
|
56
|
+
try {
|
|
57
|
+
if (Array.isArray(object)) {
|
|
58
|
+
return object.every((element) => isJsonValueWithin(element, seen));
|
|
59
|
+
}
|
|
60
|
+
if (Object.getPrototypeOf(object) !== Object.prototype &&
|
|
61
|
+
Object.getPrototypeOf(object) !== null) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return Object.values(object).every((element) => isJsonValueWithin(element, seen));
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
seen.delete(object);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* {@link isJsonValue} as an assertion, for a boundary that must not carry the value
|
|
72
|
+
* through.
|
|
73
|
+
*
|
|
74
|
+
* The wire types already say a field value is a {@link JsonValue}; a value that
|
|
75
|
+
* arrived over a network makes that a claim rather than a fact, and this is the
|
|
76
|
+
* boundary where the claim is checked.
|
|
77
|
+
*/
|
|
78
|
+
function asJsonValue(value, where) {
|
|
79
|
+
if (!isJsonValue(value)) {
|
|
80
|
+
throw new RangeError(`${where} is not a JSON value`);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Money (SR-2)
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Money is one definition, and it lives in `./money.ts` — the SR-2 module, which
|
|
89
|
+
* owns the {@link Money} shape, the decimal-string and ISO 4217 validators, and the
|
|
90
|
+
* scale check a host declares. Re-exported here because an Affidavit is where a
|
|
91
|
+
* caller meets a monetary field, and because the export path `@affiant/core` offers
|
|
92
|
+
* should not depend on which pull request wrote which half.
|
|
93
|
+
*
|
|
94
|
+
* Note that `isMoney` **validates**: it is `false` for `{ amount: "1e3" }` and for
|
|
95
|
+
* `{ currency: "usd" }`, not only for a missing property. A shape-only guard on a
|
|
96
|
+
* public surface would answer "yes, money" for values SR-2 refuses, which is the
|
|
97
|
+
* wrong answer to give a host that is about to swear to one.
|
|
98
|
+
*/
|
|
99
|
+
export { isMoney } from "./money.js";
|
|
100
|
+
/** Every {@link AffidavitFieldKind}. */
|
|
101
|
+
export const AFFIDAVIT_FIELD_KINDS = [
|
|
102
|
+
"text",
|
|
103
|
+
"number",
|
|
104
|
+
"date",
|
|
105
|
+
"enum",
|
|
106
|
+
];
|
|
107
|
+
/**
|
|
108
|
+
* The three confidence numbers over `fields` (AF-2).
|
|
109
|
+
*
|
|
110
|
+
* - `aggregateConfidence` is the **minimum** over every proposed field, with an
|
|
111
|
+
* `Empty` field counting as `0` whatever its tag says — so it is `0` exactly when
|
|
112
|
+
* some proposed field has unknown provenance.
|
|
113
|
+
* - `populatedConfidence` is the minimum over the non-`Empty` fields, and **`null`**
|
|
114
|
+
* when there are none. `null` rather than `0`: "there is nothing populated to be
|
|
115
|
+
* confident about" is a different statement from "the populated fields are
|
|
116
|
+
* worthless", and a card that showed `0` would say the second.
|
|
117
|
+
* - `emptyFieldCount` counts the `Empty` fields.
|
|
118
|
+
*
|
|
119
|
+
* An Affidavit with no fields at all reports `0`, `null`, `0`. It has nothing to
|
|
120
|
+
* swear to, and the substance gate refuses it before it is ever filed (GT-3).
|
|
121
|
+
*
|
|
122
|
+
* Neither the protocol nor this package defines a threshold on any of the three.
|
|
123
|
+
*/
|
|
124
|
+
export function computeConfidence(fields) {
|
|
125
|
+
let aggregate = null;
|
|
126
|
+
let populated = null;
|
|
127
|
+
let emptyFieldCount = 0;
|
|
128
|
+
for (const field of fields) {
|
|
129
|
+
const tag = field.provenance.current;
|
|
130
|
+
const contribution = tag.source === "Empty" ? 0 : tag.confidence;
|
|
131
|
+
aggregate = aggregate === null ? contribution : Math.min(aggregate, contribution);
|
|
132
|
+
if (tag.source === "Empty") {
|
|
133
|
+
emptyFieldCount += 1;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
populated = populated === null ? tag.confidence : Math.min(populated, tag.confidence);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
aggregateConfidence: aggregate ?? 0,
|
|
141
|
+
populatedConfidence: populated,
|
|
142
|
+
emptyFieldCount,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Build an Affidavit for `op` from `fields`, enforcing AF-1 and AF-3 and computing
|
|
147
|
+
* the three numbers (AF-2).
|
|
148
|
+
*
|
|
149
|
+
* **AF-1.** `op.fields` is the authority on what is proposed, and the field list
|
|
150
|
+
* must cover it exactly. A field the operation does not propose is a `RangeError`
|
|
151
|
+
* rather than a silently `Empty`-tagged row (that is the "absent, never
|
|
152
|
+
* `Empty`-tagged" half of the rule, made unfakeable); a proposed field with no
|
|
153
|
+
* entry is also a `RangeError`, because the way to say "proposed, provenance
|
|
154
|
+
* unknown" is to pass the field with no `provenance` and get an `Empty` tag.
|
|
155
|
+
*
|
|
156
|
+
* **AF-3.** `op.kind` and `op.entityId` must agree — an update names its entity, a
|
|
157
|
+
* create names none — and on an update every field must carry the `previousValue`
|
|
158
|
+
* key, holding `null` where the entity had no stored value. On a create every
|
|
159
|
+
* `previousValue` is `null`.
|
|
160
|
+
*
|
|
161
|
+
* @throws RangeError on any AF-1 or AF-3 violation, naming the field.
|
|
162
|
+
*/
|
|
163
|
+
export function buildAffidavit(op, fields, meta) {
|
|
164
|
+
// AF-3, the shape half. `Operation` is a discriminated union, so a type-checked
|
|
165
|
+
// caller cannot get here wrong; a JavaScript one can.
|
|
166
|
+
if (op.kind === "update" && (op.entityId === null || op.entityId === undefined)) {
|
|
167
|
+
throw new RangeError("AF-3: an update-shaped operation must name the entity it updates");
|
|
168
|
+
}
|
|
169
|
+
if (op.kind === "create" && op.entityId !== null) {
|
|
170
|
+
throw new RangeError("AF-3: a create-shaped operation has no entityId");
|
|
171
|
+
}
|
|
172
|
+
const proposed = new Set(op.fields);
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
const built = fields.map((input) => {
|
|
175
|
+
if (!proposed.has(input.name)) {
|
|
176
|
+
throw new RangeError(`AF-1: field ${JSON.stringify(input.name)} is not proposed by the operation; ` +
|
|
177
|
+
`a field the operation does not propose is absent from the Affidavit, never Empty-tagged`);
|
|
178
|
+
}
|
|
179
|
+
if (seen.has(input.name)) {
|
|
180
|
+
throw new RangeError(`AF-1: field ${JSON.stringify(input.name)} is proposed twice`);
|
|
181
|
+
}
|
|
182
|
+
seen.add(input.name);
|
|
183
|
+
const carriesPreviousValue = Object.hasOwn(input, "previousValue");
|
|
184
|
+
if (op.kind === "update" && !carriesPreviousValue) {
|
|
185
|
+
throw new RangeError(`AF-3: update field ${JSON.stringify(input.name)} must carry a previousValue key; ` +
|
|
186
|
+
`pass null where the entity had no stored value`);
|
|
187
|
+
}
|
|
188
|
+
if (op.kind === "create" && carriesPreviousValue && input.previousValue !== null) {
|
|
189
|
+
throw new RangeError(`AF-3: create field ${JSON.stringify(input.name)} must have previousValue null`);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
name: input.name,
|
|
193
|
+
kind: input.kind,
|
|
194
|
+
value: input.value,
|
|
195
|
+
previousValue: op.kind === "create" ? null : (input.previousValue ?? null),
|
|
196
|
+
// AF-1: unknown provenance is a recorded absence, not a missing tag.
|
|
197
|
+
provenance: input.provenance ?? chainOf(emptyTag(meta.createdAt, "Provenance unknown")),
|
|
198
|
+
isMandatory: input.isMandatory,
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
for (const name of op.fields) {
|
|
202
|
+
if (!seen.has(name)) {
|
|
203
|
+
throw new RangeError(`AF-1: proposed field ${JSON.stringify(name)} has no entry; ` +
|
|
204
|
+
`a proposed field with unknown provenance is present and Empty-tagged, never omitted`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return withConfidence({
|
|
208
|
+
protocolVersion: meta.protocolVersion ?? PROTOCOL_VERSION,
|
|
209
|
+
operationType: op.kind,
|
|
210
|
+
entityType: op.entityType,
|
|
211
|
+
entityId: op.entityId,
|
|
212
|
+
conversationTurn: meta.conversationTurn ?? null,
|
|
213
|
+
createdAt: meta.createdAt,
|
|
214
|
+
}, built);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Assemble an Affidavit from its parts with the three numbers computed over
|
|
218
|
+
* `fields` (AF-2, and AF-4 when the fields have been amended).
|
|
219
|
+
*
|
|
220
|
+
* Exported because the amendment path needs exactly this: same identity, same
|
|
221
|
+
* order, recomputed numbers.
|
|
222
|
+
*/
|
|
223
|
+
export function withConfidence(core, fields) {
|
|
224
|
+
const numbers = computeConfidence(fields);
|
|
225
|
+
return {
|
|
226
|
+
protocolVersion: core.protocolVersion,
|
|
227
|
+
operationType: core.operationType,
|
|
228
|
+
entityType: core.entityType,
|
|
229
|
+
entityId: core.entityId,
|
|
230
|
+
fields,
|
|
231
|
+
aggregateConfidence: numbers.aggregateConfidence,
|
|
232
|
+
populatedConfidence: numbers.populatedConfidence,
|
|
233
|
+
emptyFieldCount: numbers.emptyFieldCount,
|
|
234
|
+
conversationTurn: core.conversationTurn,
|
|
235
|
+
createdAt: core.createdAt,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Lift the {@link WireCarry} off a card envelope, so a round trip can put it back.
|
|
240
|
+
*
|
|
241
|
+
* The two optional slots come back as empty arrays rather than `undefined`: a
|
|
242
|
+
* caller reading them wants something to iterate, and `toWire`'s companion,
|
|
243
|
+
* {@link presentationToWire}, turns an empty one back into an omitted property.
|
|
244
|
+
*/
|
|
245
|
+
export function wireCarryOf(card) {
|
|
246
|
+
return {
|
|
247
|
+
warnings: card.warnings ?? [],
|
|
248
|
+
requiresConfirmation: card.requiresConfirmation,
|
|
249
|
+
presentation: card.presentation ?? [],
|
|
250
|
+
hostOperation: card.hostOperation ?? null,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The card envelope's three optional presentation slots as the wire spells them:
|
|
255
|
+
* **absent** when there is nothing to say, never `null`.
|
|
256
|
+
*
|
|
257
|
+
* Nothing swears to any of them, so a producer with nothing to say says nothing —
|
|
258
|
+
* the distinction the schemas draw between a value that is sometimes missing
|
|
259
|
+
* (spelled `null`) and a property that is meaningful only sometimes (spelled by its
|
|
260
|
+
* absence).
|
|
261
|
+
*/
|
|
262
|
+
export function presentationToWire(carry) {
|
|
263
|
+
return {
|
|
264
|
+
...(carry.presentation.length > 0 ? { presentation: carry.presentation } : {}),
|
|
265
|
+
...(carry.warnings.length > 0 ? { warnings: carry.warnings } : {}),
|
|
266
|
+
...(carry.hostOperation === null ? {} : { hostOperation: carry.hostOperation }),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Read a wire Affidavit into the core model.
|
|
271
|
+
*
|
|
272
|
+
* **A payload from another protocol version is refused, never guessed at.** The
|
|
273
|
+
* `0.0.1-seed` shape the shipped .NET framework still sends is a different
|
|
274
|
+
* document — the host's verb where the shape belongs, one confidence number rather
|
|
275
|
+
* than three, the warnings and the confirmation flag on the record, the
|
|
276
|
+
* presentation on each field — and reading it as though it were this one would
|
|
277
|
+
* produce a record that swore to things nobody said. SR-4 puts the version on the
|
|
278
|
+
* envelope precisely so a consumer can tell: a differing **major** is refused, and
|
|
279
|
+
* so is an older minor whose shape this version does not describe. A newer minor is
|
|
280
|
+
* accepted, because a minor only adds.
|
|
281
|
+
*
|
|
282
|
+
* Two mappings are worth stating. **The operation shape is derived, not trusted:**
|
|
283
|
+
* `entityId === null` is a create and anything else is an update (AF-3), so a
|
|
284
|
+
* record whose `operationType` disagrees with its own `entityId` still lands on the
|
|
285
|
+
* right side of the rule. **A grade is carried, never raised:** a binding read off
|
|
286
|
+
* the wire is recorded as it arrived, and whether a tag may be relied on is PV-4's
|
|
287
|
+
* call, not the reader's.
|
|
288
|
+
*
|
|
289
|
+
* @throws RangeError if the payload names another protocol version, if a field
|
|
290
|
+
* value is not a JSON value, or if the wire shape violates AF-1 or AF-3.
|
|
291
|
+
*/
|
|
292
|
+
export function fromWire(wire, stamp) {
|
|
293
|
+
assertReadableVersion(wire.protocolVersion);
|
|
294
|
+
const entityId = wire.entityId;
|
|
295
|
+
const names = wire.fields.map((field) => field.name);
|
|
296
|
+
const op = entityId === null
|
|
297
|
+
? { kind: "create", entityType: wire.entityType, entityId: null, fields: names }
|
|
298
|
+
: { kind: "update", entityType: wire.entityType, entityId, fields: names };
|
|
299
|
+
const fields = wire.fields.map((field) => ({
|
|
300
|
+
name: field.name,
|
|
301
|
+
kind: field.kind,
|
|
302
|
+
value: asJsonValue(field.value, `field ${JSON.stringify(field.name)} value`),
|
|
303
|
+
previousValue: asJsonValue(field.previousValue, `field ${JSON.stringify(field.name)} previousValue`),
|
|
304
|
+
provenance: chainFromWire(field.provenance, stamp.at),
|
|
305
|
+
isMandatory: field.isMandatory,
|
|
306
|
+
}));
|
|
307
|
+
return buildAffidavit(op, fields, {
|
|
308
|
+
createdAt: stamp.at,
|
|
309
|
+
conversationTurn: stamp.conversationTurn ?? wire.conversationTurn ?? null,
|
|
310
|
+
// SR-4: the version the record arrived under, not this package's own. A newer
|
|
311
|
+
// minor is readable (a minor only adds), and rewriting it to `PROTOCOL_VERSION`
|
|
312
|
+
// would make `toWire(fromWire(x))` a different document from `x` — and, since
|
|
313
|
+
// SR-1's form is over the record as the schema defines it, a different hash.
|
|
314
|
+
protocolVersion: wire.protocolVersion,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Refuse a payload this version cannot read (SR-4).
|
|
319
|
+
*
|
|
320
|
+
* A differing major is a different protocol. An older minor is a shape this
|
|
321
|
+
* version does not describe — `0.0.1-seed` is the one that exists, and it is not a
|
|
322
|
+
* subset of v0.1 but a different document. A newer minor is accepted and not
|
|
323
|
+
* warned about here: a minor only adds, and the caller who wants to know can
|
|
324
|
+
* compare `PROTOCOL_VERSION` itself.
|
|
325
|
+
*/
|
|
326
|
+
function assertReadableVersion(version) {
|
|
327
|
+
// A payload with no version at all is the seed, which predates the property.
|
|
328
|
+
// Checked as data rather than trusted from the type: this is a boundary, and the
|
|
329
|
+
// type is a claim about what arrived rather than a fact.
|
|
330
|
+
if (typeof version === "string") {
|
|
331
|
+
const [major, minor] = version.split(".");
|
|
332
|
+
const [targetMajor, targetMinor] = PROTOCOL_VERSION.split(".");
|
|
333
|
+
if (major === targetMajor && Number(minor) >= Number(targetMinor))
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
throw new RangeError(`SR-4: this package reads protocol ${PROTOCOL_VERSION} and was handed ` +
|
|
337
|
+
`${version === undefined ? "a payload carrying no protocolVersion at all" : JSON.stringify(version)}. ` +
|
|
338
|
+
`The 0.0.1-seed wire is a different document — the host's verb where the operation shape belongs, ` +
|
|
339
|
+
`one confidence number rather than three, the warnings and the presentation on the record rather ` +
|
|
340
|
+
`than on the card envelope — and is deliberately not converted: reading it as this shape would ` +
|
|
341
|
+
`produce a record that swore to things nobody said. Translate it at the host boundary instead; ` +
|
|
342
|
+
`its schemas are exported from @affiant/contract/schemas as seedSchemas.`);
|
|
343
|
+
}
|
|
344
|
+
function tagFromWire(tag, at) {
|
|
345
|
+
return mintTag({
|
|
346
|
+
source: tag.source,
|
|
347
|
+
confidence: tag.confidence,
|
|
348
|
+
note: tag.note,
|
|
349
|
+
at: tag.at ?? at,
|
|
350
|
+
conversationTurn: tag.conversationTurn,
|
|
351
|
+
// PV-5: a binding is carried exactly as it arrived. Nothing here raises a
|
|
352
|
+
// grade; whether an unbound tag above `Conversation` may be relied on is
|
|
353
|
+
// PV-4's call.
|
|
354
|
+
binding: tag.binding,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function chainFromWire(chain, at) {
|
|
358
|
+
return {
|
|
359
|
+
current: tagFromWire(chain.current, at),
|
|
360
|
+
prior: chain.prior.map((tag) => tagFromWire(tag, at)),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Write a core Affidavit back out in the wire shape.
|
|
365
|
+
*
|
|
366
|
+
* Everything on the wire record is the record: the operation's **shape** rather
|
|
367
|
+
* than the host's verb (AF-3), the three confidence numbers (AF-2), the version
|
|
368
|
+
* the envelope conforms to (SR-4), and each field's value, previous value and
|
|
369
|
+
* whole provenance chain. The presentation a surface needs goes on the card
|
|
370
|
+
* envelope instead — see {@link WireCarry}.
|
|
371
|
+
*
|
|
372
|
+
* `aggregateConfidence` is **the computed value** (AF-2). An implementation that
|
|
373
|
+
* wrote back whatever number it was handed would let a mean computed elsewhere
|
|
374
|
+
* travel under a name the rule defines as a minimum.
|
|
375
|
+
*
|
|
376
|
+
* `protocolVersion` is **the record's own** (SR-4), no longer stamped here. It used
|
|
377
|
+
* to be invented on the way out, which made the wire document and the runtime model
|
|
378
|
+
* two different records — the wire one carrying a property the model did not have —
|
|
379
|
+
* and SR-1's canonical form is over the record as the schema defines it, so the two
|
|
380
|
+
* hashed differently. The parameter is kept for a caller re-emitting a record under
|
|
381
|
+
* an explicit version, and defaults to the version the record carries.
|
|
382
|
+
*/
|
|
383
|
+
export function toWire(affidavit, protocolVersion = affidavit.protocolVersion) {
|
|
384
|
+
return {
|
|
385
|
+
protocolVersion,
|
|
386
|
+
operationType: affidavit.operationType,
|
|
387
|
+
entityType: affidavit.entityType,
|
|
388
|
+
entityId: affidavit.entityId,
|
|
389
|
+
fields: affidavit.fields.map((field) => ({
|
|
390
|
+
name: field.name,
|
|
391
|
+
kind: field.kind,
|
|
392
|
+
value: field.value,
|
|
393
|
+
previousValue: field.previousValue,
|
|
394
|
+
provenance: {
|
|
395
|
+
current: tagToWire(field.provenance.current),
|
|
396
|
+
prior: field.provenance.prior.map(tagToWire),
|
|
397
|
+
},
|
|
398
|
+
isMandatory: field.isMandatory,
|
|
399
|
+
})),
|
|
400
|
+
aggregateConfidence: affidavit.aggregateConfidence,
|
|
401
|
+
populatedConfidence: affidavit.populatedConfidence,
|
|
402
|
+
emptyFieldCount: affidavit.emptyFieldCount,
|
|
403
|
+
conversationTurn: affidavit.conversationTurn,
|
|
404
|
+
createdAt: affidavit.createdAt,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function tagToWire(tag) {
|
|
408
|
+
return {
|
|
409
|
+
source: tag.source,
|
|
410
|
+
confidence: tag.confidence,
|
|
411
|
+
note: tag.note,
|
|
412
|
+
at: tag.at,
|
|
413
|
+
conversationTurn: tag.conversationTurn,
|
|
414
|
+
binding: tag.binding ?? null,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
//# sourceMappingURL=affidavit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"affidavit.js","sourceRoot":"","sources":["../../src/model/affidavit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amendments: what a reviewer's correction means, and what it does to the record.
|
|
3
|
+
*
|
|
4
|
+
* **Rules served: DK-2** (in an amendment map `null` means *cleared* and an absent
|
|
5
|
+
* key means *untouched*, and an implementation never conflates them), **AF-1** (a
|
|
6
|
+
* field with unknown provenance is present and tagged `Empty`; a field the operation
|
|
7
|
+
* does not propose is absent), **AF-2** (the three numbers, with `Empty` counting as
|
|
8
|
+
* `0`), **AF-4** (an accepted amendment recomputes the three confidence numbers, and
|
|
9
|
+
* the amended field's provenance is the reviewer's act — never the machine's
|
|
10
|
+
* pre-correction tag), **PV-2** (that act carries a `reviewer-act` binding naming
|
|
11
|
+
* the Docket decision it was made on).
|
|
12
|
+
*
|
|
13
|
+
* ## What clearing a field does to the numbers
|
|
14
|
+
*
|
|
15
|
+
* A cleared field has no value, so it cannot have confidence in one. Writing the
|
|
16
|
+
* reviewer's `UserStated`/`1.0` tag over an emptied field would make the three
|
|
17
|
+
* numbers *rise* as a reviewer wiped the Affidavit — clear every field and the record
|
|
18
|
+
* reports perfect confidence over nothing, which is the arithmetic hole AF-2 exists
|
|
19
|
+
* to close, arriving through the amendment path. So a clear is resolved against the
|
|
20
|
+
* field rather than pasted onto it:
|
|
21
|
+
*
|
|
22
|
+
* - **A mandatory field stays and is tagged `Empty`** (confidence `0`, AF-1). The
|
|
23
|
+
* entity still requires it, so it is still proposed and still on the card — now
|
|
24
|
+
* visibly with nothing behind it, counted in `emptyFieldCount` and dragging
|
|
25
|
+
* `aggregateConfidence` to `0`.
|
|
26
|
+
* - **An optional field is removed from `fields[]`** (AF-1). A reviewer clearing an
|
|
27
|
+
* optional field is saying "do not write this one", which is a field the write no
|
|
28
|
+
* longer proposes, and AF-1 says a field the operation does not propose is absent
|
|
29
|
+
* rather than present-and-`Empty`.
|
|
30
|
+
*
|
|
31
|
+
* Either way the reviewer's act is not lost: the `Empty` tag carries the same
|
|
32
|
+
* `reviewer-act` binding and the same note a set would (PV-2), and the machine's
|
|
33
|
+
* displaced tag stays in the chain behind it.
|
|
34
|
+
*
|
|
35
|
+
* Why DK-2 needs saying at all: `undefined` and `null` are one keystroke apart in
|
|
36
|
+
* JavaScript and `{ Status: undefined }` reads to a careless consumer as "clear
|
|
37
|
+
* Status", when it is in fact "say nothing about Status". Conflating them silently
|
|
38
|
+
* either wipes a field nobody asked to wipe or ignores a correction a person made
|
|
39
|
+
* on purpose. So this module resolves a map into {@link Amendment} values — `set`
|
|
40
|
+
* or `clear`, both explicit — and a field nobody amended is simply not in the
|
|
41
|
+
* resolved list. `undefined` under a key is refused, loudly.
|
|
42
|
+
*
|
|
43
|
+
* Why AF-4 needs saying: the shipped demo hosts return an amended Affidavit still
|
|
44
|
+
* carrying the machine's pre-correction confidence, so a card can show a corrected
|
|
45
|
+
* value under a number that was never about that value.
|
|
46
|
+
*
|
|
47
|
+
* @packageDocumentation
|
|
48
|
+
*/
|
|
49
|
+
import { type ProvenanceTag } from "./provenance.js";
|
|
50
|
+
import { type Affidavit, type JsonValue } from "./affidavit.js";
|
|
51
|
+
/**
|
|
52
|
+
* A reviewer's corrections, keyed by {@link AffidavitField.name}.
|
|
53
|
+
*
|
|
54
|
+
* `null` under a key clears the field; a key that is not present leaves the field
|
|
55
|
+
* untouched (DK-2). `undefined` under a key means nothing and is refused by
|
|
56
|
+
* {@link resolveAmendments}.
|
|
57
|
+
*/
|
|
58
|
+
export type AmendmentMap = {
|
|
59
|
+
readonly [fieldName: string]: JsonValue | null;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* One resolved correction. DK-2's two meanings, made into two shapes so no reader
|
|
63
|
+
* has to decide what a value means: `clear` carries no value because there is no
|
|
64
|
+
* value, and `set` always carries one.
|
|
65
|
+
*
|
|
66
|
+
* "Untouched" has no shape here on purpose — it is the absence of an entry.
|
|
67
|
+
*/
|
|
68
|
+
export type Amendment = {
|
|
69
|
+
readonly kind: "set";
|
|
70
|
+
readonly value: JsonValue;
|
|
71
|
+
} | {
|
|
72
|
+
readonly kind: "clear";
|
|
73
|
+
};
|
|
74
|
+
/** One field's correction, as {@link resolveAmendments} returns it. */
|
|
75
|
+
export interface ResolvedAmendment {
|
|
76
|
+
/** The field being amended. */
|
|
77
|
+
readonly name: string;
|
|
78
|
+
/** What the reviewer did to it. */
|
|
79
|
+
readonly amendment: Amendment;
|
|
80
|
+
}
|
|
81
|
+
/** Whether `map` says anything at all about `fieldName` (DK-2). */
|
|
82
|
+
export declare function hasAmendment(map: AmendmentMap, fieldName: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve `map` into explicit {@link Amendment} values, in the map's own key order.
|
|
85
|
+
*
|
|
86
|
+
* @throws RangeError if a key is present holding `undefined` — the one thing an
|
|
87
|
+
* amendment map may not say, because it is indistinguishable at a glance
|
|
88
|
+
* from both of DK-2's meanings and is neither.
|
|
89
|
+
*/
|
|
90
|
+
export declare function resolveAmendments(map: AmendmentMap): readonly ResolvedAmendment[];
|
|
91
|
+
/**
|
|
92
|
+
* The decision a set of amendments was made on: which Docket entry, when, and by
|
|
93
|
+
* whom.
|
|
94
|
+
*
|
|
95
|
+
* `entryId` and `decisionAt` become the `reviewer-act` binding (PV-2) — the pointer
|
|
96
|
+
* an auditor follows years later to the decision that changed the value. `by` is
|
|
97
|
+
* the person, carried in the tag's note for the card; the authoritative record of
|
|
98
|
+
* who approved is the attestation on the Docket entry (AZ-1, pull request C6), not
|
|
99
|
+
* a string on a tag.
|
|
100
|
+
*/
|
|
101
|
+
export interface ReviewerAct {
|
|
102
|
+
/** The Docket entry the decision was made on. */
|
|
103
|
+
readonly entryId: string;
|
|
104
|
+
/** When the decision was made, as an ISO 8601 instant. */
|
|
105
|
+
readonly decisionAt: string;
|
|
106
|
+
/** Who made it, as the host identifies them. */
|
|
107
|
+
readonly by: string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The provenance tag an accepted amendment puts in force on the field it names
|
|
111
|
+
* (PV-2, AF-2).
|
|
112
|
+
*
|
|
113
|
+
* One function so there is one answer. The canonical form (SR-1) is defined over
|
|
114
|
+
* "the Affidavit and its accepted amendments", and an implementation that minted a
|
|
115
|
+
* *nearly* identical tag on the serialization path would produce different bytes
|
|
116
|
+
* from the same decision — which is the one thing a canonical form exists to make
|
|
117
|
+
* impossible. `model/canonical.ts` calls this, and so does {@link applyAmendments}.
|
|
118
|
+
*
|
|
119
|
+
* A set mints `UserStated` at confidence `1`: a reviewer typing a value is the
|
|
120
|
+
* person stating it, and PV-3 makes that the one place the grade is legitimate. A
|
|
121
|
+
* clear mints `Empty` at confidence `0`: an emptied field has no value to be
|
|
122
|
+
* confident in, so a clearing can never raise a confidence number.
|
|
123
|
+
*
|
|
124
|
+
* Either way the binding is `reviewer-act`, naming the decision *and the instant*,
|
|
125
|
+
* so an auditor can follow a clearing as readily as a correction.
|
|
126
|
+
*
|
|
127
|
+
* @param amendment What the reviewer did to the field.
|
|
128
|
+
* @param act The decision it arrived on.
|
|
129
|
+
* @param conversationTurn The turn the Affidavit belongs to, carried onto the tag.
|
|
130
|
+
*/
|
|
131
|
+
export declare function amendmentTag(amendment: Amendment, act: ReviewerAct, conversationTurn: number | null): ProvenanceTag;
|
|
132
|
+
/**
|
|
133
|
+
* Apply `map` to `affidavit` as `act`, returning a new Affidavit.
|
|
134
|
+
*
|
|
135
|
+
* What happens to a field the reviewer **set**: its value becomes the amended value,
|
|
136
|
+
* and a `UserStated` tag carrying a `reviewer-act` binding goes **on top of** its
|
|
137
|
+
* chain (PV-2, AF-4). On top rather than merged: a reviewer's correction is not a
|
|
138
|
+
* confidence contest it might lose to the machine's own tag, and the displaced tag
|
|
139
|
+
* stays in `prior` so the card can still show what the machine had proposed.
|
|
140
|
+
*
|
|
141
|
+
* What happens to a field the reviewer **cleared** (`null`, DK-2): a mandatory field
|
|
142
|
+
* keeps its place with value `null` under an `Empty` tag at confidence `0`, and an
|
|
143
|
+
* optional field leaves `fields[]` entirely. See the module note above for why the
|
|
144
|
+
* reviewer's `1.0` is not written over an emptied field.
|
|
145
|
+
*
|
|
146
|
+
* What happens to everything else: nothing. A field the map does not name keeps its
|
|
147
|
+
* value, its `previousValue` and its whole chain, byte for byte (DK-2). And
|
|
148
|
+
* `previousValue` never moves — it is what the entity holds now, which an
|
|
149
|
+
* amendment does not change.
|
|
150
|
+
*
|
|
151
|
+
* The three numbers are recomputed over the amended fields (AF-4).
|
|
152
|
+
*
|
|
153
|
+
* @throws RangeError if `map` names a field the Affidavit does not propose. Not an
|
|
154
|
+
* {@link AffiantError}: the error-code registry names refusals the gate
|
|
155
|
+
* makes at runtime about a proposal's substance or a decider's identity,
|
|
156
|
+
* and this is a caller passing a field name that is not there — a
|
|
157
|
+
* programming error, in the same class as an out-of-range index.
|
|
158
|
+
*/
|
|
159
|
+
export declare function applyAmendments(affidavit: Affidavit, map: AmendmentMap, act: ReviewerAct): Affidavit;
|
|
160
|
+
//# sourceMappingURL=amendments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amendments.d.ts","sourceRoot":"","sources":["../../src/model/amendments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;CAAE,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnF,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,iBAAiB,EAAE,CAiBjF;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,WAAW,EAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAC9B,aAAa,CAYf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,WAAW,GACf,SAAS,CAoDX"}
|