@atrib/verify 0.2.3 → 0.2.4
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/README.md +5 -5
- package/dist/resolve-identity.d.ts +195 -25
- package/dist/resolve-identity.d.ts.map +1 -1
- package/dist/resolve-identity.js +666 -3
- package/dist/resolve-identity.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ const result = await verifier.verify(recommendationDoc)
|
|
|
28
28
|
|
|
29
29
|
`valid === true` means **both** the document's Ed25519 signature verified against the calculator's published key **and** the local recalculation produced the same distribution within `1e-9`. Either failing flips `valid` to `false` and the specific failure is reported in `signatureOk` / `calcMatch` / `warnings` so you know exactly what went wrong.
|
|
30
30
|
|
|
31
|
-
## What `verify()` actually does (per spec §5.5.2)
|
|
31
|
+
## What `verify()` actually does (per spec [§5.5.2](../../atrib-spec.md#552-verifying-a-settlement-recommendation))
|
|
32
32
|
|
|
33
33
|
1. **Resolves the calculator's public key** from `recommendationDoc.calculated_by`. For the well-known `resolve.atrib.dev` service, the key is fetched from the `/pubkey` endpoint. For other calculators, the merchant supplies the key out-of-band.
|
|
34
34
|
2. **Verifies the Ed25519 signature** over the JCS-canonicalized recommendation document (excluding the `signature` field).
|
|
@@ -39,7 +39,7 @@ const result = await verifier.verify(recommendationDoc)
|
|
|
39
39
|
|
|
40
40
|
The key invariant per spec [§4.6](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#46-the-calculation-algorithm): any party with the same graph and the same policy MUST get the same distribution. If they don't, either the calculator cheated, the document was tampered with, or one party has a buggy implementation. Either way the merchant should not pay against this document.
|
|
41
41
|
|
|
42
|
-
## Post-hoc calculation (§5.5.3)
|
|
42
|
+
## Post-hoc calculation ([§5.5.3](../../atrib-spec.md#553-post-hoc-calculation-no-agent-sdk))
|
|
43
43
|
|
|
44
44
|
If the agent that drove the session was not atrib-aware (no `@atrib/agent` middleware), the merchant can still produce a signed recommendation after the fact, as long as the tools were attributed:
|
|
45
45
|
|
|
@@ -98,14 +98,14 @@ const result = await verifyRecord(record, {
|
|
|
98
98
|
|
|
99
99
|
- `provenance`: `{ token, upstream_record_hash, upstream_resolved }` per session-genesis record carrying `provenance_token` ([D044](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d044-provenance_token-field-for-cross-session-causal-anchoring) / [§1.2.6](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#126-provenance_token)). The 16-byte token truncation is irreversible: `upstream_record_hash` populates only when the caller supplies a candidate whose canonical-form SHA-256[:16] matches the token.
|
|
100
100
|
- `informed_by_resolution`: `{ resolved: string[], dangling: string[] }` per record carrying `informed_by` ([D041](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d041-informed_by-linking-primitive-and-informed_by-edge-type) / [§1.2.5](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#125-informed_by)). Dangling references are flagged but do not fail verification: they signal "the verifier has not seen upstream context," not "the record is invalid."
|
|
101
|
-
- `posture`: `{ timestamp_granularity, timestamp_consistent, timestamp_granularity_explicit, args_commitment_form, result_commitment_form, tool_name_form }` ([D045](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d045-privacy-postures-normative-spec-section) / [D061](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d061-add-tool_name-args_hash-result_hash-fields-to-§121) / [§8.2](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#82-opaque-name-posture) / [§8.3](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#83-salted-commitment-posture) / [§8.4](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#84-coarsened-timing-posture)). Always populated. Surfaces (a) the declared timing granularity, whether the timestamp value structurally matches the spec's trailing-zero invariant, and whether the field was explicitly set vs defaulted; (b) the structurally-detected `args_hash` / `result_hash` commitment scheme: `'salted-sha256'` when `args_salt` / `result_salt` is present, `'plain-sha256'` otherwise (the `'hmac-sha256'` variant from §8.3 is signaled out-of-band and is not structurally detectable); and (c) the §8.2 `tool_name_form`: `'hashed'` when `tool_name` matches `^sha256:[0-9a-f]{64}$`, `'plain'` for any other present value, `null` when the field is absent. Per D061 the §8.2 verbatim-vs-opaque distinction is NOT structurally detectable — both surface as `'plain'`.
|
|
101
|
+
- `posture`: `{ timestamp_granularity, timestamp_consistent, timestamp_granularity_explicit, args_commitment_form, result_commitment_form, tool_name_form }` ([D045](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d045-privacy-postures-normative-spec-section) / [D061](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d061-add-tool_name-args_hash-result_hash-fields-to-§121) / [§8.2](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#82-opaque-name-posture) / [§8.3](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#83-salted-commitment-posture) / [§8.4](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#84-coarsened-timing-posture)). Always populated. Surfaces (a) the declared timing granularity, whether the timestamp value structurally matches the spec's trailing-zero invariant, and whether the field was explicitly set vs defaulted; (b) the structurally-detected `args_hash` / `result_hash` commitment scheme: `'salted-sha256'` when `args_salt` / `result_salt` is present, `'plain-sha256'` otherwise (the `'hmac-sha256'` variant from [§8.3](../../atrib-spec.md#83-salted-commitment-posture) is signaled out-of-band and is not structurally detectable); and (c) the [§8.2](../../atrib-spec.md#82-opaque-name-posture) `tool_name_form`: `'hashed'` when `tool_name` matches `^sha256:[0-9a-f]{64}$`, `'plain'` for any other present value, `null` when the field is absent. Per [D061](../../DECISIONS.md#d061-add-tool_name-args_hash-result_hash-fields-to-121) the [§8.2](../../atrib-spec.md#82-opaque-name-posture) verbatim-vs-opaque distinction is NOT structurally detectable — both surface as `'plain'`.
|
|
102
102
|
- `capability_check`: `{ envelope, in_envelope, mismatches, unresolvable }` ([D051](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d051-capability-scoped-records-via-directory-published-envelopes) / [§6.7](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#67-capability-declarations)). Populated only when the caller passes a resolved `identityClaim` in options. Checks the record's `event_type` against the envelope's `event_types` allowlist and the record's `timestamp` against `expires_at`. `tool_names` (against tool_call records), `max_amount`, and `counterparties` (against transaction records) flag `unresolvable: true` because the constraints depend on data not yet on the standard record shape (`tool_name`) or out-of-band protocol events (payment amount + counterparty). Per [§6.7.3](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#673-out-of-envelope-records) out-of-envelope is a signal, not invalidation: mismatches do not flip `valid` to false. The caller is responsible for fetching the active envelope at the record's timestamp via `@atrib/directory`'s `lookup()` (or a cached equivalent); `@atrib/verify` intentionally has no `@atrib/directory` dependency.
|
|
103
|
-
- `cross_attestation`: `{ signers_count, signers_valid, missing }` ([D052](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d052-cross-attestation-requirement-for-transaction-records) / [§1.7.6](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#176-cross-attestation-requirement-for-transaction-records)). Populated only on transaction records (`event_type = transaction`). Each entry in `signers[]` is verified against the cross-attestation canonical bytes (JCS form with `signers: []` and the top-level `signature` field omitted, per §1.7.6). `missing: true` when fewer than 2 signers verify — atrib's normative minimum. Per §1.7.6 missing is a SIGNAL not invalidation: `valid` stays true if the underlying signature path holds. Legacy single-signer transaction records (no `signers[]` array, only top-level `signature`) surface as `signers_count: 0, missing: true` so consumers can flag them while accepting the cryptographic validity.
|
|
103
|
+
- `cross_attestation`: `{ signers_count, signers_valid, missing }` ([D052](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d052-cross-attestation-requirement-for-transaction-records) / [§1.7.6](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#176-cross-attestation-requirement-for-transaction-records)). Populated only on transaction records (`event_type = transaction`). Each entry in `signers[]` is verified against the cross-attestation canonical bytes (JCS form with `signers: []` and the top-level `signature` field omitted, per [§1.7.6](../../atrib-spec.md#176-cross-attestation-requirement-for-transaction-records)). `missing: true` when fewer than 2 signers verify — atrib's normative minimum. Per [§1.7.6](../../atrib-spec.md#176-cross-attestation-requirement-for-transaction-records) missing is a SIGNAL not invalidation: `valid` stays true if the underlying signature path holds. Legacy single-signer transaction records (no `signers[]` array, only top-level `signature`) surface as `signers_count: 0, missing: true` so consumers can flag them while accepting the cryptographic validity.
|
|
104
104
|
|
|
105
105
|
**Pending per-record annotations** (tracked as a Pending decision in [DECISIONS.md P005](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#p005-reconcile-atribverify-readme-per-record-annotations-with-actual-code-surface)):
|
|
106
106
|
|
|
107
107
|
- `cross_log_proof_count` / `cross_log_threshold_met` / `cross_log_equivocation_detected` ([D050](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d050-cross-log-replication-for-equivocation-defense) / [§2.11](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#211-cross-log-replication)): requires multi-log proof-bundle parsing and trusted-log-set config.
|
|
108
|
-
(Note: `tool_name_form`, `args_commitment_form`, and `result_commitment_form` per §8.2
|
|
108
|
+
(Note: `tool_name_form`, `args_commitment_form`, and `result_commitment_form` per [§8.2](../../atrib-spec.md#82-opaque-name-posture)/[§8.3](../../atrib-spec.md#83-salted-commitment-posture) are all now implemented under `posture` above. [D061](../../DECISIONS.md#d061-add-tool_name-args_hash-result_hash-fields-to-121) added `tool_name`, `args_hash`, and `result_hash` to the [§1.2.1](../../atrib-spec.md#121-field-definitions) canonical record schema, completing the structural inputs.)
|
|
109
109
|
|
|
110
110
|
Each pending annotation is its own ADR scope when external consumers need it.
|
|
111
111
|
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Identity resolution per spec §6.3 — the 9-step verifier consultation
|
|
3
|
-
* algorithm. The verifier consults the directory for a record's
|
|
4
|
-
* creator_key and surfaces an `identity_resolution` object.
|
|
5
|
-
*
|
|
6
|
-
* This implementation is the LEGIBLE PARTIAL: steps 6 (directory
|
|
7
|
-
* lookup), 8 (parse claim), and 9 (revocation cross-check) are wired
|
|
8
|
-
* end-to-end. Steps 1-5 (anchor freshness, witness coverage, directory
|
|
9
|
-
* checkpoint signature, append-only consistency) and step 7 (AKD proof
|
|
10
|
-
* validation) require heavier cryptographic infrastructure and are
|
|
11
|
-
* surfaced as explicit warnings rather than falsely claimed as passing.
|
|
12
|
-
*
|
|
13
|
-
* Per §5.8 degradation contract: this function never throws. Network
|
|
14
|
-
* failures, malformed responses, and timeout conditions all produce
|
|
15
|
-
* warnings in the returned object.
|
|
16
|
-
*/
|
|
17
1
|
import type { RevocationEntry } from './revocations.js';
|
|
18
2
|
/** Identity claim shape from spec §6.1. Mirrored here to avoid an import cycle with @atrib/directory. */
|
|
19
3
|
export interface IdentityClaim {
|
|
@@ -43,26 +27,128 @@ export interface KeyRevocationStatus {
|
|
|
43
27
|
/** True when the record being resolved was signed AFTER revocation (post-revocation). */
|
|
44
28
|
since_revocation: boolean;
|
|
45
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Anchor surface populated by spec §6.3 step 1 + step 2.
|
|
32
|
+
*
|
|
33
|
+
* The verifier discovers the anchor commitment on the log by querying
|
|
34
|
+
* `directory_anchor` records in the directory's reserved context_id
|
|
35
|
+
* (= sha256(directory_origin)[:16]) filtered by creator_key + timestamp.
|
|
36
|
+
* It then fetches the body (with directory_root + directory_epoch +
|
|
37
|
+
* signature) via the supplied `fetchAnchorBody` callback. The current
|
|
38
|
+
* routing target is directory-node's `/v6/anchors/<hash>` endpoint;
|
|
39
|
+
* after the §2.12 record-body archive layer ships (D070 placeholder
|
|
40
|
+
* ADR), the same callback can route to the standard archive endpoint
|
|
41
|
+
* with no change to resolveIdentity.
|
|
42
|
+
*
|
|
43
|
+
* `anchor_witness_count` and `anchor_freshness_ok` are populated when
|
|
44
|
+
* the relevant inputs are available (witness threshold config for the
|
|
45
|
+
* former; `freshnessThresholdMs` for the latter); otherwise they're
|
|
46
|
+
* `null` to distinguish "not checked" from "checked + clean."
|
|
47
|
+
*/
|
|
48
|
+
export interface AnchorSurface {
|
|
49
|
+
anchor_record_hash: string;
|
|
50
|
+
checkpoint_version: number;
|
|
51
|
+
anchor_timestamp: number;
|
|
52
|
+
anchor_age_ms: number;
|
|
53
|
+
anchor_witness_count: number | null;
|
|
54
|
+
anchor_freshness_ok: boolean | null;
|
|
55
|
+
}
|
|
46
56
|
export interface IdentityResolution {
|
|
47
57
|
identity_resolved: IdentityClaim | null;
|
|
48
58
|
identity_resolution_method: IdentityResolutionMethod;
|
|
49
59
|
capability_envelope: CapabilityEnvelope | null;
|
|
50
60
|
key_revocation_status: KeyRevocationStatus | null;
|
|
51
61
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* verifier process. Currently null for both reasons; warnings flag
|
|
57
|
-
* which one applies.
|
|
62
|
+
* §6.3 step 7. `true` when the AKD lookup proof verifies against the
|
|
63
|
+
* directory's anchored root, `false` when verification rejects (which
|
|
64
|
+
* triggers the hard-failure rejection path), `null` when not attempted
|
|
65
|
+
* (callback or vrf pubkey omitted, or anchor fetch failed mid-flight).
|
|
58
66
|
*/
|
|
59
67
|
lookup_proof_valid: boolean | null;
|
|
60
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* §6.3 step 5. `true` when the audit proof between the prior anchor
|
|
70
|
+
* and the current anchor verifies, `false` when verification rejects
|
|
71
|
+
* (triggers hard-failure rejection), `null` when not attempted.
|
|
72
|
+
*/
|
|
61
73
|
append_only_consistent: boolean | null;
|
|
62
|
-
/**
|
|
63
|
-
|
|
74
|
+
/**
|
|
75
|
+
* §6.3 step 1 + step 2. Populated when `directoryOperatorKey` +
|
|
76
|
+
* `logEndpoint` + `fetchAnchorBody` are all supplied AND a recent
|
|
77
|
+
* directory_anchor record was discovered on the log. `null` otherwise
|
|
78
|
+
* (a step-1 warning carries the reason).
|
|
79
|
+
*/
|
|
80
|
+
anchor: AnchorSurface | null;
|
|
81
|
+
/**
|
|
82
|
+
* §6.3 step 4. `true` when the directory operator's Ed25519
|
|
83
|
+
* signature on the anchor body verifies, `false` when it doesn't
|
|
84
|
+
* (triggers §6.3 step 4 HARD failure rejection — a faulty operator
|
|
85
|
+
* is not a soft signal). `null` when the check wasn't attempted
|
|
86
|
+
* (no anchor body discovered, no `directoryOperatorKey` supplied).
|
|
87
|
+
*/
|
|
88
|
+
directory_checkpoint_signature_valid: boolean | null;
|
|
64
89
|
warnings: string[];
|
|
65
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Inputs to the verifyLookupProof callback. Mirrors the signature of
|
|
93
|
+
* `@atrib/directory`'s `verifyLookupProof` so callers can pass it through
|
|
94
|
+
* directly. We re-declare the shape here (rather than importing from
|
|
95
|
+
* `@atrib/directory`) to keep `@atrib/verify` independent of the WASM
|
|
96
|
+
* bridge — same dependency-inversion pattern used for `upstreamCandidate`,
|
|
97
|
+
* `informedByCandidates`, and `identityClaim` elsewhere in this package.
|
|
98
|
+
*/
|
|
99
|
+
export interface VerifyLookupProofInput {
|
|
100
|
+
vrfPublicKey: Uint8Array;
|
|
101
|
+
rootHash: Uint8Array;
|
|
102
|
+
currentEpoch: number;
|
|
103
|
+
label: string;
|
|
104
|
+
proof: Uint8Array;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Inputs to the verifyAuditProof callback. Mirrors `@atrib/directory`'s
|
|
108
|
+
* `verifyAuditProof` signature; same dependency-inversion pattern as
|
|
109
|
+
* `verifyLookupProof`.
|
|
110
|
+
*/
|
|
111
|
+
export interface VerifyAuditProofInput {
|
|
112
|
+
/** Sequence of 32-byte root hashes, one per epoch boundary. */
|
|
113
|
+
rootHashes: Uint8Array[];
|
|
114
|
+
/** Bincode-serialized append-only proof bytes. */
|
|
115
|
+
proof: Uint8Array;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Signed `directory_anchor` record body. Returned by `fetchAnchorBody`.
|
|
119
|
+
* Shape mirrors `services/directory-node/src/anchor.ts:AnchorRecord`
|
|
120
|
+
* but is re-declared here so `@atrib/verify` doesn't import from a
|
|
121
|
+
* service package.
|
|
122
|
+
*/
|
|
123
|
+
export interface AnchorBody {
|
|
124
|
+
chain_root: string;
|
|
125
|
+
content_id: string;
|
|
126
|
+
context_id: string;
|
|
127
|
+
creator_key: string;
|
|
128
|
+
event_type: string;
|
|
129
|
+
metadata: {
|
|
130
|
+
directory_origin: string;
|
|
131
|
+
directory_root: string;
|
|
132
|
+
directory_epoch: number;
|
|
133
|
+
};
|
|
134
|
+
spec_version: string;
|
|
135
|
+
timestamp: number;
|
|
136
|
+
signature: string;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Log-side commitment shape returned by `GET /v1/by-context/<hex>` on
|
|
140
|
+
* log-node. Re-declared here so `@atrib/verify` doesn't import from
|
|
141
|
+
* `@atrib/log-node`. Only the fields step 1 reads are typed; the rest
|
|
142
|
+
* are tolerated.
|
|
143
|
+
*/
|
|
144
|
+
export interface AnchorCommitment {
|
|
145
|
+
record_hash: string;
|
|
146
|
+
log_index: number;
|
|
147
|
+
creator_key: string;
|
|
148
|
+
context_id: string;
|
|
149
|
+
timestamp_ms: number;
|
|
150
|
+
event_type: string;
|
|
151
|
+
}
|
|
66
152
|
export interface ResolveIdentityOptions {
|
|
67
153
|
/** Directory base URL (e.g., https://directory.atrib.dev/v6). */
|
|
68
154
|
directoryEndpoint?: string;
|
|
@@ -79,6 +165,90 @@ export interface ResolveIdentityOptions {
|
|
|
79
165
|
signal?: AbortSignal;
|
|
80
166
|
/** Override fetch (testing). */
|
|
81
167
|
fetchImpl?: typeof fetch;
|
|
168
|
+
/**
|
|
169
|
+
* Directory operator's VRF public key (32 bytes). Required for step 7
|
|
170
|
+
* AKD lookup proof verification. When omitted, step 7 stays warning-only.
|
|
171
|
+
*
|
|
172
|
+
* For the reference HardCodedAkdVRF backend exposed by `@atrib/directory`,
|
|
173
|
+
* use `directoryVrfPublicKey()` from that package. Production directories
|
|
174
|
+
* swapping the VRF backend publish their own VRF pubkey out of band.
|
|
175
|
+
*/
|
|
176
|
+
directoryVrfPublicKey?: Uint8Array;
|
|
177
|
+
/**
|
|
178
|
+
* Callback that performs AKD lookup proof verification (spec §6.3 step 7).
|
|
179
|
+
* Should be `verifyLookupProof` from `@atrib/directory`. When omitted,
|
|
180
|
+
* step 7 stays warning-only. When supplied AND `directoryVrfPublicKey`
|
|
181
|
+
* is also supplied, the verifier fetches the directory's anchor +
|
|
182
|
+
* decodes the lookup proof + calls this callback to validate.
|
|
183
|
+
*
|
|
184
|
+
* The callback returns `true` for a valid proof, `false` for an
|
|
185
|
+
* internally-consistent-but-invalid proof, and throws for malformed
|
|
186
|
+
* input. The verifier surfaces all three outcomes appropriately
|
|
187
|
+
* (true → `lookup_proof_valid: true`; false → reject per §6.3
|
|
188
|
+
* step 7 HARD failure semantics; throw → warning + null).
|
|
189
|
+
*/
|
|
190
|
+
verifyLookupProof?: (input: VerifyLookupProofInput) => boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Directory operator's Ed25519 public key (43-char base64url). Required
|
|
193
|
+
* for §6.3 step 1 anchor discovery: the verifier filters
|
|
194
|
+
* `directory_anchor` log entries by this key. Production deployments
|
|
195
|
+
* publish the operator key alongside the directory origin.
|
|
196
|
+
*/
|
|
197
|
+
directoryOperatorKey?: string;
|
|
198
|
+
/**
|
|
199
|
+
* Tessera log endpoint (e.g., `https://log.atrib.dev/v1`). Required
|
|
200
|
+
* for §6.3 step 1 to query `directory_anchor` records via
|
|
201
|
+
* `GET /v1/by-context/<hex>`. When omitted, steps 1 + 2 + 5 are
|
|
202
|
+
* warning-only.
|
|
203
|
+
*/
|
|
204
|
+
logEndpoint?: string;
|
|
205
|
+
/**
|
|
206
|
+
* Callback that retrieves a `directory_anchor` record body by its
|
|
207
|
+
* `record_hash`. The verifier fetches the COMMITMENT from the log,
|
|
208
|
+
* then uses this callback to fetch the BODY (which carries
|
|
209
|
+
* directory_root + directory_epoch + signature). Returns `null` when
|
|
210
|
+
* the body isn't available.
|
|
211
|
+
*
|
|
212
|
+
* The current routing target is `GET /v6/anchors/<hash>` on the
|
|
213
|
+
* directory itself. After the §2.12 record-body archive layer ships
|
|
214
|
+
* (D070 placeholder ADR), production deployments swap the callback
|
|
215
|
+
* to route to the standard archive endpoint without any change to
|
|
216
|
+
* resolveIdentity.
|
|
217
|
+
*/
|
|
218
|
+
fetchAnchorBody?: (recordHash: string) => Promise<AnchorBody | null>;
|
|
219
|
+
/**
|
|
220
|
+
* §6.3 step 2: anchor freshness threshold (ms). When set, the
|
|
221
|
+
* verifier sets `anchor_freshness_ok` based on whether
|
|
222
|
+
* `anchor_age_ms ≤ freshnessThresholdMs`. When omitted, no threshold
|
|
223
|
+
* is applied and `anchor_freshness_ok` stays `null`.
|
|
224
|
+
*/
|
|
225
|
+
freshnessThresholdMs?: number;
|
|
226
|
+
/**
|
|
227
|
+
* Callback that performs AKD audit proof verification (spec §6.3 step 5).
|
|
228
|
+
* Should be `verifyAuditProof` from `@atrib/directory`. When omitted,
|
|
229
|
+
* step 5 stays warning-only. When supplied AND step 1 surfaces an
|
|
230
|
+
* anchor pair (current + prior), the verifier fetches the audit proof
|
|
231
|
+
* from the directory and calls this callback.
|
|
232
|
+
*
|
|
233
|
+
* Returns `true` for a valid proof, `false` for invalid (triggers
|
|
234
|
+
* §6.3 step 5 HARD failure rejection), throws for malformed input.
|
|
235
|
+
*/
|
|
236
|
+
verifyAuditProof?: (input: VerifyAuditProofInput) => Promise<boolean>;
|
|
237
|
+
/**
|
|
238
|
+
* §6.3 step 3: minimum count of valid witness cosignatures required
|
|
239
|
+
* on the log's checkpoint covering the anchor. When set, the
|
|
240
|
+
* verifier fetches the checkpoint, parses cosignature lines, counts
|
|
241
|
+
* lines whose origin differs from the log's own (witness signatures),
|
|
242
|
+
* and surfaces a step-3-witness-insufficient warning if below
|
|
243
|
+
* threshold. When omitted, step 3 stays warning-only.
|
|
244
|
+
*
|
|
245
|
+
* Note. Cryptographic verification of each witness signature against
|
|
246
|
+
* a configured trusted-witness set is a separate enhancement; the
|
|
247
|
+
* current implementation counts cosignature lines as a proxy. See
|
|
248
|
+
* `step-3-witness-not-cryptographically-verified` warning surfaced
|
|
249
|
+
* alongside the count.
|
|
250
|
+
*/
|
|
251
|
+
witnessThreshold?: number;
|
|
82
252
|
}
|
|
83
253
|
export declare function resolveIdentity(creatorKey: string, opts?: ResolveIdentityOptions): Promise<IdentityResolution>;
|
|
84
254
|
//# sourceMappingURL=resolve-identity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-identity.d.ts","sourceRoot":"","sources":["../src/resolve-identity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve-identity.d.ts","sourceRoot":"","sources":["../src/resolve-identity.ts"],"names":[],"mappings":"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"}
|
package/dist/resolve-identity.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Identity resolution per spec §6.3 — the 9-step verifier consultation
|
|
4
|
+
* algorithm. The verifier consults the directory for a record's
|
|
5
|
+
* creator_key and surfaces an `identity_resolution` object.
|
|
6
|
+
*
|
|
7
|
+
* This implementation is the LEGIBLE PARTIAL: steps 6 (directory
|
|
8
|
+
* lookup), 8 (parse claim), and 9 (revocation cross-check) are wired
|
|
9
|
+
* end-to-end. Steps 1-5 (anchor freshness, witness coverage, directory
|
|
10
|
+
* checkpoint signature, append-only consistency) and step 7 (AKD proof
|
|
11
|
+
* validation) require heavier cryptographic infrastructure and are
|
|
12
|
+
* surfaced as explicit warnings rather than falsely claimed as passing.
|
|
13
|
+
*
|
|
14
|
+
* Per §5.8 degradation contract: this function never throws. Network
|
|
15
|
+
* failures, malformed responses, and timeout conditions all produce
|
|
16
|
+
* warnings in the returned object.
|
|
17
|
+
*/
|
|
18
|
+
import * as ed25519 from '@noble/ed25519';
|
|
19
|
+
import canonicalize from 'canonicalize';
|
|
2
20
|
const DEFAULT_DIRECTORY = 'https://directory.atrib.dev/v6';
|
|
3
21
|
export async function resolveIdentity(creatorKey, opts = {}) {
|
|
4
22
|
const warnings = [];
|
|
@@ -33,6 +51,7 @@ export async function resolveIdentity(creatorKey, opts = {}) {
|
|
|
33
51
|
capability_envelope: null,
|
|
34
52
|
key_revocation_status: status,
|
|
35
53
|
lookup_proof_valid: null,
|
|
54
|
+
directory_checkpoint_signature_valid: null,
|
|
36
55
|
append_only_consistent: null,
|
|
37
56
|
anchor: null,
|
|
38
57
|
warnings,
|
|
@@ -52,6 +71,7 @@ export async function resolveIdentity(creatorKey, opts = {}) {
|
|
|
52
71
|
capability_envelope: null,
|
|
53
72
|
key_revocation_status: status,
|
|
54
73
|
lookup_proof_valid: null,
|
|
74
|
+
directory_checkpoint_signature_valid: null,
|
|
55
75
|
append_only_consistent: null,
|
|
56
76
|
anchor: null,
|
|
57
77
|
warnings,
|
|
@@ -65,6 +85,7 @@ export async function resolveIdentity(creatorKey, opts = {}) {
|
|
|
65
85
|
capability_envelope: null,
|
|
66
86
|
key_revocation_status: applyRevocationOnly(creatorKey, opts, warnings),
|
|
67
87
|
lookup_proof_valid: null,
|
|
88
|
+
directory_checkpoint_signature_valid: null,
|
|
68
89
|
append_only_consistent: null,
|
|
69
90
|
anchor: null,
|
|
70
91
|
warnings,
|
|
@@ -82,22 +103,539 @@ export async function resolveIdentity(creatorKey, opts = {}) {
|
|
|
82
103
|
capability_envelope: null,
|
|
83
104
|
key_revocation_status: applyRevocationOnly(creatorKey, opts, warnings),
|
|
84
105
|
lookup_proof_valid: null,
|
|
106
|
+
directory_checkpoint_signature_valid: null,
|
|
85
107
|
append_only_consistent: null,
|
|
86
108
|
anchor: null,
|
|
87
109
|
warnings,
|
|
88
110
|
};
|
|
89
111
|
}
|
|
112
|
+
// Step 1 (anchor discovery on the log) + Step 2 (anchor freshness threshold).
|
|
113
|
+
// Runs when `directoryOperatorKey` + `logEndpoint` + `fetchAnchorBody` are
|
|
114
|
+
// all supplied. Discovers the most recent directory_anchor record on the
|
|
115
|
+
// log (in the directory's reserved context_id), fetches its body via
|
|
116
|
+
// the supplied callback, cross-checks the body's metadata, and populates
|
|
117
|
+
// the `anchor` output field.
|
|
118
|
+
//
|
|
119
|
+
// T (the record's timestamp) defaults to `recordTimestamp` opt; falls back
|
|
120
|
+
// to `Date.now()` when unset (verifying a record produced just-in-time).
|
|
121
|
+
const T = typeof opts.recordTimestamp === 'number' ? opts.recordTimestamp : Date.now();
|
|
122
|
+
let anchor = null;
|
|
123
|
+
let anchorBody = null;
|
|
124
|
+
let priorAnchorBody = null;
|
|
125
|
+
let directorySignatureValid = null;
|
|
126
|
+
if (opts.logEndpoint && opts.directoryOperatorKey && opts.fetchAnchorBody) {
|
|
127
|
+
const stepOneResult = await runStepOne({
|
|
128
|
+
logEndpoint: opts.logEndpoint,
|
|
129
|
+
directoryOperatorKey: opts.directoryOperatorKey,
|
|
130
|
+
fetchAnchorBody: opts.fetchAnchorBody,
|
|
131
|
+
directoryEndpoint,
|
|
132
|
+
recordTimestamp: T,
|
|
133
|
+
freshnessThresholdMs: opts.freshnessThresholdMs,
|
|
134
|
+
fetchFn,
|
|
135
|
+
signal: opts.signal,
|
|
136
|
+
warnings,
|
|
137
|
+
});
|
|
138
|
+
if (stepOneResult) {
|
|
139
|
+
anchor = stepOneResult.anchor;
|
|
140
|
+
anchorBody = stepOneResult.currentBody;
|
|
141
|
+
priorAnchorBody = stepOneResult.priorBody;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Step 4 (directory checkpoint signature verification). HARD failure
|
|
145
|
+
// path per spec §6.3: a directory operator returning an invalidly-
|
|
146
|
+
// signed anchor body is a fault, not a soft signal — reject the
|
|
147
|
+
// entire query. Step 4 runs only when step 1 surfaced a body AND
|
|
148
|
+
// `directoryOperatorKey` was supplied (which step 1 already required).
|
|
149
|
+
if (anchorBody && opts.directoryOperatorKey) {
|
|
150
|
+
const ok = await verifyAnchorSignature(anchorBody, opts.directoryOperatorKey, warnings);
|
|
151
|
+
directorySignatureValid = ok;
|
|
152
|
+
if (ok) {
|
|
153
|
+
const idx = warnings.findIndex((w) => w.startsWith('step-4-checkpoint-signature-not-checked'));
|
|
154
|
+
if (idx >= 0)
|
|
155
|
+
warnings.splice(idx, 1);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// §6.3 step 4 hard-failure: reject. Anchor + step-4 result stay
|
|
159
|
+
// populated so consumers see WHY the rejection happened.
|
|
160
|
+
return {
|
|
161
|
+
identity_resolved: null,
|
|
162
|
+
identity_resolution_method: 'rejected',
|
|
163
|
+
capability_envelope: null,
|
|
164
|
+
key_revocation_status: applyRevocationOnly(creatorKey, opts, warnings),
|
|
165
|
+
lookup_proof_valid: null,
|
|
166
|
+
append_only_consistent: null,
|
|
167
|
+
anchor,
|
|
168
|
+
directory_checkpoint_signature_valid: false,
|
|
169
|
+
warnings,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Step 3 (witness coverage on the log's checkpoint). Soft signal:
|
|
174
|
+
// counts cosignature lines on the latest log checkpoint whose origin
|
|
175
|
+
// differs from the log's own (= witness signatures). Compares against
|
|
176
|
+
// `witnessThreshold`. Implemented as a count-only check; cryptographic
|
|
177
|
+
// verification of each witness signature is a separate enhancement.
|
|
178
|
+
// log-node currently produces single-signer checkpoints (no witnesses
|
|
179
|
+
// cosigning), so the actual count is 0 against any production
|
|
180
|
+
// checkpoint as of the time of writing — that's honest data, not
|
|
181
|
+
// a bug; the threshold semantic still works once witnesses come online.
|
|
182
|
+
if (opts.logEndpoint && anchor) {
|
|
183
|
+
const witnessCount = await runStepThree(opts.logEndpoint, opts.witnessThreshold, fetchFn, opts.signal, warnings);
|
|
184
|
+
if (witnessCount !== null) {
|
|
185
|
+
// anchor is non-null here (we checked above); update its witness count.
|
|
186
|
+
anchor = { ...anchor, anchor_witness_count: witnessCount };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Step 7 (AKD lookup proof verification). Only attempted when the
|
|
190
|
+
// caller supplies both `verifyLookupProof` (the bridge wrapper from
|
|
191
|
+
// `@atrib/directory`) and `directoryVrfPublicKey`. When supplied AND
|
|
192
|
+
// the proof is missing/malformed, we surface a warning and proceed
|
|
193
|
+
// (soft signal). When the proof verifies as invalid, §6.3 step 7
|
|
194
|
+
// mandates a HARD failure: the result is rejected.
|
|
195
|
+
//
|
|
196
|
+
// When step 1 surfaced a log-anchored body, step 7 verifies against
|
|
197
|
+
// the LOG-ANCHORED root (stronger; catches directory body forgery).
|
|
198
|
+
// Otherwise it falls back to the directory's self-reported `/anchor`.
|
|
199
|
+
let lookupProofValid = null;
|
|
200
|
+
if (opts.verifyLookupProof && opts.directoryVrfPublicKey) {
|
|
201
|
+
const stepSevenOutcome = await runStepSeven(creatorKey, directoryEndpoint, lookupBody.proof, opts.verifyLookupProof, opts.directoryVrfPublicKey, fetchFn, opts.signal, warnings, anchorBody);
|
|
202
|
+
if (stepSevenOutcome === 'rejected') {
|
|
203
|
+
return {
|
|
204
|
+
identity_resolved: null,
|
|
205
|
+
identity_resolution_method: 'rejected',
|
|
206
|
+
capability_envelope: null,
|
|
207
|
+
key_revocation_status: applyRevocationOnly(creatorKey, opts, warnings),
|
|
208
|
+
lookup_proof_valid: false,
|
|
209
|
+
append_only_consistent: null,
|
|
210
|
+
anchor,
|
|
211
|
+
directory_checkpoint_signature_valid: directorySignatureValid,
|
|
212
|
+
warnings,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
lookupProofValid = stepSevenOutcome; // true | null
|
|
216
|
+
}
|
|
217
|
+
// Step 5 (append-only consistency). Only attempted when step 1 surfaced
|
|
218
|
+
// both a current AND a prior anchor body (need a pair for audit_verify),
|
|
219
|
+
// AND `verifyAuditProof` callback is supplied.
|
|
220
|
+
let appendOnlyConsistent = null;
|
|
221
|
+
if (opts.verifyAuditProof && anchorBody && priorAnchorBody) {
|
|
222
|
+
const stepFiveOutcome = await runStepFive({
|
|
223
|
+
currentBody: anchorBody,
|
|
224
|
+
priorBody: priorAnchorBody,
|
|
225
|
+
directoryEndpoint,
|
|
226
|
+
verifyAuditProof: opts.verifyAuditProof,
|
|
227
|
+
fetchFn,
|
|
228
|
+
signal: opts.signal,
|
|
229
|
+
warnings,
|
|
230
|
+
});
|
|
231
|
+
if (stepFiveOutcome === 'rejected') {
|
|
232
|
+
// §6.3 step 5 hard-failure path: append-only consistency violated.
|
|
233
|
+
return {
|
|
234
|
+
identity_resolved: null,
|
|
235
|
+
identity_resolution_method: 'rejected',
|
|
236
|
+
capability_envelope: null,
|
|
237
|
+
key_revocation_status: applyRevocationOnly(creatorKey, opts, warnings),
|
|
238
|
+
lookup_proof_valid: lookupProofValid,
|
|
239
|
+
append_only_consistent: false,
|
|
240
|
+
anchor,
|
|
241
|
+
directory_checkpoint_signature_valid: directorySignatureValid,
|
|
242
|
+
warnings,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
appendOnlyConsistent = stepFiveOutcome; // true | null
|
|
246
|
+
}
|
|
90
247
|
return {
|
|
91
248
|
identity_resolved: claim,
|
|
92
249
|
identity_resolution_method: 'directory_lookup',
|
|
93
250
|
capability_envelope: claim.capabilities ?? null,
|
|
94
251
|
key_revocation_status: applyRevocationOnly(creatorKey, opts, warnings),
|
|
95
|
-
lookup_proof_valid:
|
|
96
|
-
append_only_consistent:
|
|
97
|
-
anchor
|
|
252
|
+
lookup_proof_valid: lookupProofValid,
|
|
253
|
+
append_only_consistent: appendOnlyConsistent,
|
|
254
|
+
anchor,
|
|
255
|
+
directory_checkpoint_signature_valid: directorySignatureValid,
|
|
98
256
|
warnings,
|
|
99
257
|
};
|
|
100
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Compute the directory's reserved context_id from its origin per the
|
|
261
|
+
* pattern in services/directory-node/src/anchor.ts: sha256(origin)
|
|
262
|
+
* truncated to the first 16 bytes (32 hex chars).
|
|
263
|
+
*/
|
|
264
|
+
function deriveDirectoryContextId(origin) {
|
|
265
|
+
// Use SubtleCrypto if available to avoid importing @noble/hashes here.
|
|
266
|
+
// (The SDK callers already import @noble/hashes; this function lives
|
|
267
|
+
// in the verify package which we keep dep-light.)
|
|
268
|
+
const enc = new TextEncoder().encode(origin);
|
|
269
|
+
// Synchronous SHA-256 via @noble/hashes is already a transitive dep
|
|
270
|
+
// (through @atrib/mcp); use it to keep the call sync.
|
|
271
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
272
|
+
const { sha256 } = require('@noble/hashes/sha2.js');
|
|
273
|
+
const digest = sha256(enc);
|
|
274
|
+
return Array.from(digest.slice(0, 16))
|
|
275
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
276
|
+
.join('');
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Spec §6.3 step 1: discover the most recent `directory_anchor` record
|
|
280
|
+
* on the log whose timestamp is ≤ T (the verifying record's timestamp),
|
|
281
|
+
* fetch its body, cross-check the body's signed metadata, and populate
|
|
282
|
+
* the verifier's `anchor` output field. Step 2 (freshness threshold)
|
|
283
|
+
* piggybacks since we have anchor.timestamp + recordTimestamp here.
|
|
284
|
+
*
|
|
285
|
+
* Side effects (only on the warnings array):
|
|
286
|
+
* - Removes the up-front step-1-anchor-not-checked warning when
|
|
287
|
+
* anchor discovery succeeds (a more specific warning replaces it
|
|
288
|
+
* when the body fetch fails or the cross-check rejects).
|
|
289
|
+
*
|
|
290
|
+
* Returns null when discovery couldn't be completed (log fetch
|
|
291
|
+
* failure, no anchor matches, body fetch fails). Returns a
|
|
292
|
+
* StepOneSuccess when the anchor is discovered + body retrieved +
|
|
293
|
+
* metadata cross-check passes.
|
|
294
|
+
*
|
|
295
|
+
* Cross-check (lightweight sanity): body's metadata.directory_origin
|
|
296
|
+
* must be a non-empty string; body's metadata.directory_epoch must be
|
|
297
|
+
* a number; body's signature must be a non-empty string.
|
|
298
|
+
* Stronger: re-canonicalize the body and verify the signature against
|
|
299
|
+
* the operator's pubkey (deferred to a follow-on commit; the log's
|
|
300
|
+
* inclusion proof already authenticates the hash, so the signature
|
|
301
|
+
* re-verify is defense-in-depth, not load-bearing for step 1).
|
|
302
|
+
*/
|
|
303
|
+
async function runStepOne(opts) {
|
|
304
|
+
const directoryOrigin = await fetchDirectoryOrigin(opts.directoryEndpoint, opts.fetchFn, opts.signal, opts.warnings);
|
|
305
|
+
if (!directoryOrigin)
|
|
306
|
+
return null;
|
|
307
|
+
const contextHex = deriveDirectoryContextId(directoryOrigin);
|
|
308
|
+
let entries;
|
|
309
|
+
try {
|
|
310
|
+
const url = `${opts.logEndpoint.replace(/\/$/, '')}/by-context/${contextHex}`;
|
|
311
|
+
const res = await opts.fetchFn(url, {
|
|
312
|
+
headers: { accept: 'application/json' },
|
|
313
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
314
|
+
});
|
|
315
|
+
if (res.status === 404) {
|
|
316
|
+
opts.warnings.push('step-1-anchor-not-found: no directory_anchor records in the directory\'s context_id on the log');
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
if (!res.ok) {
|
|
320
|
+
opts.warnings.push(`step-1-log-fetch-error: ${res.status} ${res.statusText}`);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
const body = await res.json();
|
|
324
|
+
entries = body.entries ?? [];
|
|
325
|
+
}
|
|
326
|
+
catch (e) {
|
|
327
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
328
|
+
opts.warnings.push(`step-1-log-fetch-error: ${msg}`);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
// Filter by event_type=directory_anchor + creator_key match + timestamp ≤ T.
|
|
332
|
+
// The handleByContext response is newest-first.
|
|
333
|
+
const matches = entries.filter((e) => e.event_type === 'directory_anchor' &&
|
|
334
|
+
e.creator_key === opts.directoryOperatorKey &&
|
|
335
|
+
e.timestamp_ms <= opts.recordTimestamp);
|
|
336
|
+
if (matches.length === 0) {
|
|
337
|
+
opts.warnings.push('step-1-anchor-not-found: no directory_anchor on the log matches the operator key + timestamp window');
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
const current = matches[0]; // newest-first → first match is the most recent
|
|
341
|
+
const prior = matches[1] ?? null; // second-most-recent, if any
|
|
342
|
+
// Fetch the body for the current anchor (and predecessor when present).
|
|
343
|
+
const recordHashStr = current.record_hash.startsWith('sha256:') ? current.record_hash : `sha256:${current.record_hash}`;
|
|
344
|
+
let currentBody;
|
|
345
|
+
try {
|
|
346
|
+
currentBody = await opts.fetchAnchorBody(recordHashStr);
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
opts.warnings.push(`step-1-body-fetch-error: ${e instanceof Error ? e.message : String(e)}`);
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
if (!currentBody) {
|
|
353
|
+
opts.warnings.push(`step-1-body-not-available: anchor ${recordHashStr} present on log but body not retrievable from directory or archive`);
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
// Cross-check: body's signed metadata must be self-consistent. Strong
|
|
357
|
+
// checks (signature re-verify against operator pubkey) are deferred;
|
|
358
|
+
// log inclusion already authenticates the hash, so this catches body
|
|
359
|
+
// forgery scenarios without re-implementing Ed25519 verify here.
|
|
360
|
+
if (typeof currentBody.metadata?.directory_origin !== 'string' ||
|
|
361
|
+
currentBody.metadata.directory_origin.length === 0 ||
|
|
362
|
+
typeof currentBody.metadata?.directory_epoch !== 'number' ||
|
|
363
|
+
typeof currentBody.metadata?.directory_root !== 'string' ||
|
|
364
|
+
typeof currentBody.signature !== 'string' ||
|
|
365
|
+
currentBody.signature.length === 0) {
|
|
366
|
+
opts.warnings.push('step-1-body-malformed: anchor body missing required metadata fields');
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
if (currentBody.creator_key !== opts.directoryOperatorKey) {
|
|
370
|
+
opts.warnings.push('step-1-body-creator-mismatch: anchor body creator_key does not match directoryOperatorKey');
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
// Optional predecessor body for step 5; tolerate failure since step 5
|
|
374
|
+
// can stay warning-only without it.
|
|
375
|
+
let priorBody = null;
|
|
376
|
+
if (prior) {
|
|
377
|
+
const priorHashStr = prior.record_hash.startsWith('sha256:') ? prior.record_hash : `sha256:${prior.record_hash}`;
|
|
378
|
+
try {
|
|
379
|
+
priorBody = await opts.fetchAnchorBody(priorHashStr);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// Soft: step 5 will note its absence.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const anchorAgeMs = opts.recordTimestamp - current.timestamp_ms;
|
|
386
|
+
const freshnessOk = typeof opts.freshnessThresholdMs === 'number'
|
|
387
|
+
? anchorAgeMs <= opts.freshnessThresholdMs
|
|
388
|
+
: null;
|
|
389
|
+
if (typeof opts.freshnessThresholdMs === 'number' && freshnessOk === false) {
|
|
390
|
+
opts.warnings.push(`step-2-anchor-stale: anchor_age_ms=${anchorAgeMs} > threshold=${opts.freshnessThresholdMs}`);
|
|
391
|
+
}
|
|
392
|
+
const surface = {
|
|
393
|
+
anchor_record_hash: recordHashStr,
|
|
394
|
+
checkpoint_version: currentBody.metadata.directory_epoch,
|
|
395
|
+
anchor_timestamp: current.timestamp_ms,
|
|
396
|
+
anchor_age_ms: anchorAgeMs,
|
|
397
|
+
anchor_witness_count: null, // step 3, deferred
|
|
398
|
+
anchor_freshness_ok: freshnessOk,
|
|
399
|
+
};
|
|
400
|
+
// Remove the up-front step-1 warning since we did discover one.
|
|
401
|
+
const idx = opts.warnings.findIndex((w) => w.startsWith('step-1-anchor-not-checked'));
|
|
402
|
+
if (idx >= 0)
|
|
403
|
+
opts.warnings.splice(idx, 1);
|
|
404
|
+
return { anchor: surface, currentBody, priorBody };
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Fetch the directory's origin string from its `/anchor` endpoint.
|
|
408
|
+
* The origin is what we hash to compute the directory's reserved
|
|
409
|
+
* context_id for log-side anchor discovery.
|
|
410
|
+
*
|
|
411
|
+
* Returns null on any error (warnings array gets a step-1-origin-fetch entry).
|
|
412
|
+
*/
|
|
413
|
+
async function fetchDirectoryOrigin(directoryEndpoint, fetchFn, signal, warnings) {
|
|
414
|
+
try {
|
|
415
|
+
const url = `${directoryEndpoint.replace(/\/$/, '')}/anchor`;
|
|
416
|
+
const res = await fetchFn(url, {
|
|
417
|
+
headers: { accept: 'application/json' },
|
|
418
|
+
...(signal ? { signal } : {}),
|
|
419
|
+
});
|
|
420
|
+
if (!res.ok) {
|
|
421
|
+
warnings.push(`step-1-origin-fetch-error: ${res.status} ${res.statusText}`);
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
const body = await res.json();
|
|
425
|
+
if (typeof body.directory_origin !== 'string' || body.directory_origin.length === 0) {
|
|
426
|
+
warnings.push('step-1-origin-missing: directory /anchor response missing directory_origin');
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
return body.directory_origin;
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
433
|
+
warnings.push(`step-1-origin-fetch-error: ${msg}`);
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Spec §6.3 step 5: append-only consistency check. Fetches the audit
|
|
439
|
+
* proof between the prior anchor's epoch and the current anchor's
|
|
440
|
+
* epoch from the directory's `/v6/audit-proof` endpoint, then runs
|
|
441
|
+
* verify_audit_proof against the [prior_root, current_root] pair.
|
|
442
|
+
*
|
|
443
|
+
* Returns:
|
|
444
|
+
* - `true` when the audit proof verifies
|
|
445
|
+
* - `null` when verification couldn't be attempted (fetch or
|
|
446
|
+
* decode error, callback throws)
|
|
447
|
+
* - `'rejected'` when verification rejects → §6.3 step 5 HARD failure
|
|
448
|
+
*/
|
|
449
|
+
async function runStepFive(opts) {
|
|
450
|
+
const fromEpoch = opts.priorBody.metadata.directory_epoch;
|
|
451
|
+
const toEpoch = opts.currentBody.metadata.directory_epoch;
|
|
452
|
+
if (toEpoch <= fromEpoch) {
|
|
453
|
+
opts.warnings.push(`step-5-invalid-epoch-range: prior=${fromEpoch} >= current=${toEpoch}`);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
// Fetch audit proof from the directory.
|
|
457
|
+
let proofB64u;
|
|
458
|
+
try {
|
|
459
|
+
const url = `${opts.directoryEndpoint.replace(/\/$/, '')}/audit-proof?from=${fromEpoch}&to=${toEpoch}`;
|
|
460
|
+
const res = await opts.fetchFn(url, {
|
|
461
|
+
headers: { accept: 'application/json' },
|
|
462
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
463
|
+
});
|
|
464
|
+
if (!res.ok) {
|
|
465
|
+
opts.warnings.push(`step-5-audit-proof-fetch-error: ${res.status} ${res.statusText}`);
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
const body = await res.json();
|
|
469
|
+
if (typeof body.proof !== 'string' || body.proof.length === 0) {
|
|
470
|
+
opts.warnings.push('step-5-audit-proof-missing: directory /audit-proof response missing proof field');
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
proofB64u = body.proof;
|
|
474
|
+
}
|
|
475
|
+
catch (e) {
|
|
476
|
+
opts.warnings.push(`step-5-audit-proof-fetch-error: ${e instanceof Error ? e.message : String(e)}`);
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
// Decode roots + proof.
|
|
480
|
+
let priorRoot;
|
|
481
|
+
let currentRoot;
|
|
482
|
+
let proof;
|
|
483
|
+
try {
|
|
484
|
+
priorRoot = hexToBytes(opts.priorBody.metadata.directory_root);
|
|
485
|
+
currentRoot = hexToBytes(opts.currentBody.metadata.directory_root);
|
|
486
|
+
if (priorRoot.length !== 32 || currentRoot.length !== 32) {
|
|
487
|
+
throw new Error('directory_root must be 32 bytes');
|
|
488
|
+
}
|
|
489
|
+
proof = base64urlToBytes(proofB64u);
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
opts.warnings.push(`step-5-input-decode-error: ${e instanceof Error ? e.message : String(e)}`);
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
let verified;
|
|
496
|
+
try {
|
|
497
|
+
verified = await opts.verifyAuditProof({
|
|
498
|
+
rootHashes: [priorRoot, currentRoot],
|
|
499
|
+
proof,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
catch (e) {
|
|
503
|
+
opts.warnings.push(`step-5-verify-threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
if (verified) {
|
|
507
|
+
const idx = opts.warnings.findIndex((w) => w.startsWith('step-5-append-only-not-checked'));
|
|
508
|
+
if (idx >= 0)
|
|
509
|
+
opts.warnings.splice(idx, 1);
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
opts.warnings.push('step-5-audit-proof-invalid: audit proof did not verify against the prior + current anchored roots');
|
|
513
|
+
return 'rejected';
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Runs spec §6.3 step 7 — AKD lookup proof verification — against the
|
|
517
|
+
* directory's currently-anchored root.
|
|
518
|
+
*
|
|
519
|
+
* Returns:
|
|
520
|
+
* - `true` when the proof verified
|
|
521
|
+
* - `null` when verification couldn't be attempted (anchor fetch
|
|
522
|
+
* failed, proof was missing or undecodable, callback threw)
|
|
523
|
+
* - `'rejected'` when the proof verified as INVALID — §6.3 step 7
|
|
524
|
+
* hard-failure path; caller short-circuits the result
|
|
525
|
+
*
|
|
526
|
+
* On `true`, removes the `step-7-akd-proof-not-validated` warning that
|
|
527
|
+
* was pushed up front; the warning becomes inaccurate once we've
|
|
528
|
+
* actually validated. The step-1 anchor cross-check warning stays
|
|
529
|
+
* regardless: this implementation trusts the directory's self-reported
|
|
530
|
+
* `/anchor` root rather than cross-checking against a log-anchored
|
|
531
|
+
* `directory_anchor` record.
|
|
532
|
+
*/
|
|
533
|
+
async function runStepSeven(creatorKey, directoryEndpoint, proofB64u, verifyLookupProof, vrfPublicKey, fetchFn, signal, warnings,
|
|
534
|
+
/**
|
|
535
|
+
* Optional log-anchored body from step 1. When supplied, step 7
|
|
536
|
+
* verifies the lookup proof against the LOG-ANCHORED root + epoch
|
|
537
|
+
* (stronger; catches directory body forgery). When null, falls back
|
|
538
|
+
* to the directory's self-reported `/anchor` (still useful but
|
|
539
|
+
* weaker — depends on the directory being honest about its current
|
|
540
|
+
* state). The fallback path is tracked by the step-1 warnings
|
|
541
|
+
* since step 1 is what discovers the log-anchored body in the first
|
|
542
|
+
* place.
|
|
543
|
+
*/
|
|
544
|
+
logAnchoredBody) {
|
|
545
|
+
if (typeof proofB64u !== 'string' || proofB64u.length === 0) {
|
|
546
|
+
warnings.push('step-7-proof-missing: directory lookup did not return a proof field');
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
// Source the (root, epoch) pair: prefer log-anchored body when supplied.
|
|
550
|
+
let rootHashHex;
|
|
551
|
+
let currentEpoch;
|
|
552
|
+
if (logAnchoredBody) {
|
|
553
|
+
rootHashHex = logAnchoredBody.metadata.directory_root;
|
|
554
|
+
currentEpoch = logAnchoredBody.metadata.directory_epoch;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
let anchorResp;
|
|
558
|
+
try {
|
|
559
|
+
const url = `${directoryEndpoint.replace(/\/$/, '')}/anchor`;
|
|
560
|
+
const res = await fetchFn(url, {
|
|
561
|
+
headers: { accept: 'application/json' },
|
|
562
|
+
...(signal ? { signal } : {}),
|
|
563
|
+
});
|
|
564
|
+
if (!res.ok) {
|
|
565
|
+
warnings.push(`step-7-anchor-fetch-error: ${res.status} ${res.statusText}`);
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
anchorResp = await res.json();
|
|
569
|
+
}
|
|
570
|
+
catch (e) {
|
|
571
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
572
|
+
warnings.push(`step-7-anchor-fetch-error: ${msg}`);
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
if (typeof anchorResp.epoch !== 'number' || typeof anchorResp.root_hash !== 'string') {
|
|
576
|
+
warnings.push('step-7-anchor-malformed: anchor response missing epoch or root_hash');
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
rootHashHex = anchorResp.root_hash;
|
|
580
|
+
currentEpoch = anchorResp.epoch;
|
|
581
|
+
}
|
|
582
|
+
let rootHash;
|
|
583
|
+
let proof;
|
|
584
|
+
try {
|
|
585
|
+
rootHash = hexToBytes(rootHashHex);
|
|
586
|
+
if (rootHash.length !== 32) {
|
|
587
|
+
throw new Error(`root_hash must be 32 bytes (got ${rootHash.length})`);
|
|
588
|
+
}
|
|
589
|
+
proof = base64urlToBytes(proofB64u);
|
|
590
|
+
}
|
|
591
|
+
catch (e) {
|
|
592
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
593
|
+
warnings.push(`step-7-input-decode-error: ${msg}`);
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
let verified;
|
|
597
|
+
try {
|
|
598
|
+
verified = verifyLookupProof({
|
|
599
|
+
vrfPublicKey,
|
|
600
|
+
rootHash,
|
|
601
|
+
currentEpoch,
|
|
602
|
+
label: creatorKey,
|
|
603
|
+
proof,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
catch (e) {
|
|
607
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
608
|
+
warnings.push(`step-7-verify-threw: ${msg}`);
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
if (verified) {
|
|
612
|
+
// We did the check; the up-front warning is now inaccurate.
|
|
613
|
+
const idx = warnings.findIndex((w) => w.startsWith('step-7-akd-proof-not-validated'));
|
|
614
|
+
if (idx >= 0)
|
|
615
|
+
warnings.splice(idx, 1);
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
warnings.push('step-7-akd-proof-invalid: AKD lookup proof did not verify against the directory\'s anchored root');
|
|
619
|
+
return 'rejected';
|
|
620
|
+
}
|
|
621
|
+
/** Decode a hex string into bytes. Throws on odd-length or non-hex input. */
|
|
622
|
+
function hexToBytes(hex) {
|
|
623
|
+
if (hex.length % 2 !== 0)
|
|
624
|
+
throw new Error('hex string must have even length');
|
|
625
|
+
const out = new Uint8Array(hex.length / 2);
|
|
626
|
+
for (let i = 0; i < out.length; i++) {
|
|
627
|
+
const byte = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
628
|
+
if (Number.isNaN(byte))
|
|
629
|
+
throw new Error(`invalid hex at offset ${i * 2}`);
|
|
630
|
+
out[i] = byte;
|
|
631
|
+
}
|
|
632
|
+
return out;
|
|
633
|
+
}
|
|
634
|
+
/** Decode a base64url string into bytes. Pads as needed. */
|
|
635
|
+
function base64urlToBytes(s) {
|
|
636
|
+
const padLen = (4 - (s.length % 4)) % 4;
|
|
637
|
+
return new Uint8Array(Buffer.from(s + '='.repeat(padLen), 'base64url'));
|
|
638
|
+
}
|
|
101
639
|
/**
|
|
102
640
|
* Step 9 helper: cross-check the revocation registry. since_revocation
|
|
103
641
|
* is true when the record's log_index is strictly greater than the
|
|
@@ -118,4 +656,129 @@ function applyRevocationOnly(creatorKey, opts, warnings) {
|
|
|
118
656
|
since_revocation: sinceRevocation,
|
|
119
657
|
};
|
|
120
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Spec §6.3 step 4 — verify the directory operator's Ed25519 signature
|
|
661
|
+
* on the anchor record body.
|
|
662
|
+
*
|
|
663
|
+
* The signed bytes are the canonical (JCS-style) JSON of the body MINUS
|
|
664
|
+
* the `signature` field. The atrib substrate uses `canonicalize` (RFC
|
|
665
|
+
* 8785 JCS); the directory's own emitDirectoryAnchor canonicalizes the
|
|
666
|
+
* unsigned shape THEN appends the signature, so re-canonicalizing the
|
|
667
|
+
* body without `signature` reproduces the bytes the operator signed.
|
|
668
|
+
*
|
|
669
|
+
* Returns `true` for a valid signature, `false` otherwise. Pushes a
|
|
670
|
+
* `step-4-signature-invalid` warning on rejection so consumers see why.
|
|
671
|
+
* Errors during canonicalization, base64url decode, or ed25519 verify
|
|
672
|
+
* all return `false` — a fault during step 4 is not a soft signal,
|
|
673
|
+
* per spec §6.3 ("a directory operator returning an invalidly-signed
|
|
674
|
+
* checkpoint is a fault").
|
|
675
|
+
*/
|
|
676
|
+
async function verifyAnchorSignature(body, expectedOperatorKey, warnings) {
|
|
677
|
+
// Re-canonicalize without the signature field. Order matches the
|
|
678
|
+
// emit-side: every non-signature field is included.
|
|
679
|
+
const { signature, ...unsigned } = body;
|
|
680
|
+
const canonical = canonicalize(unsigned);
|
|
681
|
+
if (typeof canonical !== 'string') {
|
|
682
|
+
warnings.push('step-4-canonicalize-failed: anchor body could not be canonicalized');
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
let sigBytes;
|
|
686
|
+
let pubBytes;
|
|
687
|
+
try {
|
|
688
|
+
sigBytes = base64urlToBytes(signature);
|
|
689
|
+
pubBytes = base64urlToBytes(expectedOperatorKey);
|
|
690
|
+
}
|
|
691
|
+
catch (e) {
|
|
692
|
+
warnings.push(`step-4-decode-failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
if (sigBytes.length !== 64 || pubBytes.length !== 32) {
|
|
696
|
+
warnings.push(`step-4-decode-failed: signature must be 64 bytes (got ${sigBytes.length}), pubkey 32 bytes (got ${pubBytes.length})`);
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
let ok = false;
|
|
700
|
+
try {
|
|
701
|
+
ok = await ed25519.verifyAsync(sigBytes, new TextEncoder().encode(canonical), pubBytes);
|
|
702
|
+
}
|
|
703
|
+
catch (e) {
|
|
704
|
+
warnings.push(`step-4-verify-threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
if (!ok) {
|
|
708
|
+
warnings.push('step-4-signature-invalid: directory operator signature on anchor body did not verify');
|
|
709
|
+
}
|
|
710
|
+
return ok;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Spec §6.3 step 3 — count witness cosignatures on the log's latest
|
|
714
|
+
* checkpoint. Soft signal.
|
|
715
|
+
*
|
|
716
|
+
* Fetches `GET /v1/checkpoint` from the log, parses the C2SP signed-note
|
|
717
|
+
* format, counts cosignature lines whose origin differs from the log's
|
|
718
|
+
* own (= witnesses, not the log signing itself). Compares against
|
|
719
|
+
* `threshold` and surfaces a step-3-witness-insufficient warning if
|
|
720
|
+
* below.
|
|
721
|
+
*
|
|
722
|
+
* Returns the count when discoverable; `null` when the count couldn't
|
|
723
|
+
* be determined (fetch failure, malformed checkpoint, etc.).
|
|
724
|
+
*
|
|
725
|
+
* Implementation note. This is a count-only check: each cosignature
|
|
726
|
+
* line is parsed for shape and origin, but the signatures are NOT
|
|
727
|
+
* cryptographically verified against a configured trusted-witness
|
|
728
|
+
* set. The `step-3-witness-not-cryptographically-verified` warning
|
|
729
|
+
* surfaces alongside the count to make this honest. Verifying each
|
|
730
|
+
* signature against trusted witness vkeys is a separate enhancement.
|
|
731
|
+
*/
|
|
732
|
+
async function runStepThree(logEndpoint, threshold, fetchFn, signal, warnings) {
|
|
733
|
+
let checkpointText;
|
|
734
|
+
try {
|
|
735
|
+
const url = `${logEndpoint.replace(/\/$/, '')}/checkpoint`;
|
|
736
|
+
const res = await fetchFn(url, {
|
|
737
|
+
headers: { accept: 'text/plain' },
|
|
738
|
+
...(signal ? { signal } : {}),
|
|
739
|
+
});
|
|
740
|
+
if (!res.ok) {
|
|
741
|
+
warnings.push(`step-3-checkpoint-fetch-error: ${res.status} ${res.statusText}`);
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
checkpointText = await res.text();
|
|
745
|
+
}
|
|
746
|
+
catch (e) {
|
|
747
|
+
warnings.push(`step-3-checkpoint-fetch-error: ${e instanceof Error ? e.message : String(e)}`);
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
// Parse the C2SP signed-note format: `body\n\n<signature lines>`.
|
|
751
|
+
// The first line of the body is the log's origin string; cosignature
|
|
752
|
+
// lines whose origin matches are the log's own signature, not witness
|
|
753
|
+
// cosignatures.
|
|
754
|
+
const blankSep = checkpointText.indexOf('\n\n');
|
|
755
|
+
if (blankSep < 0) {
|
|
756
|
+
warnings.push('step-3-checkpoint-malformed: signed-note body/signature separator not found');
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
const body = checkpointText.slice(0, blankSep);
|
|
760
|
+
const sigBlock = checkpointText.slice(blankSep + 2);
|
|
761
|
+
const logOrigin = body.split('\n', 1)[0] ?? '';
|
|
762
|
+
let witnessCount = 0;
|
|
763
|
+
for (const line of sigBlock.split('\n')) {
|
|
764
|
+
if (!line.trim())
|
|
765
|
+
continue;
|
|
766
|
+
// Each signature line starts with em-dash + space + origin + space + sigToken.
|
|
767
|
+
const match = line.match(/^[—\-] (\S+) (\S+)\s*$/);
|
|
768
|
+
if (!match)
|
|
769
|
+
continue;
|
|
770
|
+
const sigOrigin = match[1];
|
|
771
|
+
if (sigOrigin !== logOrigin)
|
|
772
|
+
witnessCount += 1;
|
|
773
|
+
}
|
|
774
|
+
warnings.push('step-3-witness-not-cryptographically-verified: witness cosignature count is line-based, not cryptographically verified against a trusted-witness set');
|
|
775
|
+
if (typeof threshold === 'number' && witnessCount < threshold) {
|
|
776
|
+
warnings.push(`step-3-witness-insufficient: actual=${witnessCount}, required=${threshold}`);
|
|
777
|
+
}
|
|
778
|
+
// Remove the up-front step-3-witness-not-checked warning since we did parse the checkpoint.
|
|
779
|
+
const idx = warnings.findIndex((w) => w.startsWith('step-3-witness-not-checked'));
|
|
780
|
+
if (idx >= 0)
|
|
781
|
+
warnings.splice(idx, 1);
|
|
782
|
+
return witnessCount;
|
|
783
|
+
}
|
|
121
784
|
//# sourceMappingURL=resolve-identity.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-identity.js","sourceRoot":"","sources":["../src/resolve-identity.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolve-identity.js","sourceRoot":"","sources":["../src/resolve-identity.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,YAAY,MAAM,cAAc,CAAA;AA0QvC,MAAM,iBAAiB,GAAG,gCAAgC,CAAA;AAE1D,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,OAA+B,EAAE;IAEjC,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,iBAAiB,CAAA;IACrE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAA;IAEvC,oEAAoE;IACpE,kEAAkE;IAClE,uBAAuB;IACvB,QAAQ,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAA;IAChG,QAAQ,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAA;IACjG,QAAQ,CAAC,IAAI,CAAC,6GAA6G,CAAC,CAAA;IAC5H,QAAQ,CAAC,IAAI,CAAC,6FAA6F,CAAC,CAAA;IAC5G,QAAQ,CAAC,IAAI,CAAC,+FAA+F,CAAC,CAAA;IAE9G,2BAA2B;IAC3B,IAAI,UAAU,GAAiF,EAAE,CAAA;IACjG,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAA;QAC9F,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;YAC7B,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC,CAAA;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,8DAA8D;YAC9D,UAAU,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;QAC/B,CAAC;aAAM,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;YACxE,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YAC9D,OAAO;gBACL,iBAAiB,EAAE,IAAI;gBACvB,0BAA0B,EAAE,UAAU;gBACtC,mBAAmB,EAAE,IAAI;gBACzB,qBAAqB,EAAE,MAAM;gBAC7B,kBAAkB,EAAE,IAAI;gBACxB,oCAAoC,EAAE,IAAI;gBAC1C,sBAAsB,EAAE,IAAI;gBAC5B,MAAM,EAAE,IAAI;gBACZ,QAAQ;aACT,CAAA;QACH,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,MAAM,GAAG,CAAC,IAAI,EAAuB,CAAA;QACpD,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACtD,QAAQ,CAAC,IAAI,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAA;QAC/C,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC9D,OAAO;YACL,iBAAiB,EAAE,IAAI;YACvB,0BAA0B,EAAE,UAAU;YACtC,mBAAmB,EAAE,IAAI;YACzB,qBAAqB,EAAE,MAAM;YAC7B,kBAAkB,EAAE,IAAI;YACtB,oCAAoC,EAAE,IAAI;YAC5C,sBAAsB,EAAE,IAAI;YAC5B,MAAM,EAAE,IAAI;YACZ,QAAQ;SACT,CAAA;IACH,CAAC;IAED,wBAAwB;IACxB,IAAI,UAAU,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO;YACL,iBAAiB,EAAE,IAAI;YACvB,0BAA0B,EAAE,qBAAqB;YACjD,mBAAmB,EAAE,IAAI;YACzB,qBAAqB,EAAE,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC;YACtE,kBAAkB,EAAE,IAAI;YACtB,oCAAoC,EAAE,IAAI;YAC5C,sBAAsB,EAAE,IAAI;YAC5B,MAAM,EAAE,IAAI;YACZ,QAAQ;SACT,CAAA;IACH,CAAC;IAED,oEAAoE;IACpE,kEAAkE;IAClE,kEAAkE;IAClE,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAA;IAC9B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QAC5E,QAAQ,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAA;QAC1G,OAAO;YACL,iBAAiB,EAAE,IAAI;YACvB,0BAA0B,EAAE,UAAU;YACtC,mBAAmB,EAAE,IAAI;YACzB,qBAAqB,EAAE,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC;YACtE,kBAAkB,EAAE,IAAI;YACtB,oCAAoC,EAAE,IAAI;YAC5C,sBAAsB,EAAE,IAAI;YAC5B,MAAM,EAAE,IAAI;YACZ,QAAQ;SACT,CAAA;IACH,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,yEAAyE;IACzE,qEAAqE;IACrE,yEAAyE;IACzE,6BAA6B;IAC7B,EAAE;IACF,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,CAAC,GAAG,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;IACtF,IAAI,MAAM,GAAyB,IAAI,CAAA;IACvC,IAAI,UAAU,GAAsB,IAAI,CAAA;IACxC,IAAI,eAAe,GAAsB,IAAI,CAAA;IAC7C,IAAI,uBAAuB,GAAmB,IAAI,CAAA;IAClD,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QAC1E,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC;YACrC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;YAC/C,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,iBAAiB;YACjB,eAAe,EAAE,CAAC;YAClB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;YAC/C,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ;SACT,CAAC,CAAA;QACF,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,GAAG,aAAa,CAAC,MAAM,CAAA;YAC7B,UAAU,GAAG,aAAa,CAAC,WAAW,CAAA;YACtC,eAAe,GAAG,aAAa,CAAC,SAAS,CAAA;QAC3C,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,mEAAmE;IACnE,gEAAgE;IAChE,iEAAiE;IACjE,uEAAuE;IACvE,IAAI,UAAU,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5C,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,UAAU,EAAE,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAA;QACvF,uBAAuB,GAAG,EAAE,CAAA;QAC5B,IAAI,EAAE,EAAE,CAAC;YACP,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,yCAAyC,CAAC,CAAC,CAAA;YAC9F,IAAI,GAAG,IAAI,CAAC;gBAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACvC,CAAC;aAAM,CAAC;YACN,gEAAgE;YAChE,yDAAyD;YACzD,OAAO;gBACL,iBAAiB,EAAE,IAAI;gBACvB,0BAA0B,EAAE,UAAU;gBACtC,mBAAmB,EAAE,IAAI;gBACzB,qBAAqB,EAAE,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC;gBACtE,kBAAkB,EAAE,IAAI;gBACxB,sBAAsB,EAAE,IAAI;gBAC5B,MAAM;gBACN,oCAAoC,EAAE,KAAK;gBAC3C,QAAQ;aACT,CAAA;QACH,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,qEAAqE;IACrE,sEAAsE;IACtE,uEAAuE;IACvE,oEAAoE;IACpE,sEAAsE;IACtE,8DAA8D;IAC9D,iEAAiE;IACjE,wEAAwE;IACxE,IAAI,IAAI,CAAC,WAAW,IAAI,MAAM,EAAE,CAAC;QAC/B,MAAM,YAAY,GAAG,MAAM,YAAY,CACrC,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,gBAAgB,EACrB,OAAO,EACP,IAAI,CAAC,MAAM,EACX,QAAQ,CACT,CAAA;QACD,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,wEAAwE;YACxE,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,oBAAoB,EAAE,YAAY,EAAE,CAAA;QAC5D,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,oEAAoE;IACpE,qEAAqE;IACrE,mEAAmE;IACnE,iEAAiE;IACjE,mDAAmD;IACnD,EAAE;IACF,oEAAoE;IACpE,oEAAoE;IACpE,sEAAsE;IACtE,IAAI,gBAAgB,GAAmB,IAAI,CAAA;IAC3C,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACzD,MAAM,gBAAgB,GAAG,MAAM,YAAY,CACzC,UAAU,EACV,iBAAiB,EACjB,UAAU,CAAC,KAAK,EAChB,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,qBAAqB,EAC1B,OAAO,EACP,IAAI,CAAC,MAAM,EACX,QAAQ,EACR,UAAU,CACX,CAAA;QACD,IAAI,gBAAgB,KAAK,UAAU,EAAE,CAAC;YACpC,OAAO;gBACL,iBAAiB,EAAE,IAAI;gBACvB,0BAA0B,EAAE,UAAU;gBACtC,mBAAmB,EAAE,IAAI;gBACzB,qBAAqB,EAAE,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC;gBACtE,kBAAkB,EAAE,KAAK;gBACzB,sBAAsB,EAAE,IAAI;gBAC5B,MAAM;gBACN,oCAAoC,EAAE,uBAAuB;gBAC7D,QAAQ;aACT,CAAA;QACH,CAAC;QACD,gBAAgB,GAAG,gBAAgB,CAAA,CAAC,cAAc;IACpD,CAAC;IAED,wEAAwE;IACxE,yEAAyE;IACzE,+CAA+C;IAC/C,IAAI,oBAAoB,GAAmB,IAAI,CAAA;IAC/C,IAAI,IAAI,CAAC,gBAAgB,IAAI,UAAU,IAAI,eAAe,EAAE,CAAC;QAC3D,MAAM,eAAe,GAAG,MAAM,WAAW,CAAC;YACxC,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,eAAe;YAC1B,iBAAiB;YACjB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ;SACT,CAAC,CAAA;QACF,IAAI,eAAe,KAAK,UAAU,EAAE,CAAC;YACnC,mEAAmE;YACnE,OAAO;gBACL,iBAAiB,EAAE,IAAI;gBACvB,0BAA0B,EAAE,UAAU;gBACtC,mBAAmB,EAAE,IAAI;gBACzB,qBAAqB,EAAE,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC;gBACtE,kBAAkB,EAAE,gBAAgB;gBACpC,sBAAsB,EAAE,KAAK;gBAC7B,MAAM;gBACN,oCAAoC,EAAE,uBAAuB;gBAC7D,QAAQ;aACT,CAAA;QACH,CAAC;QACD,oBAAoB,GAAG,eAAe,CAAA,CAAC,cAAc;IACvD,CAAC;IAED,OAAO;QACL,iBAAiB,EAAE,KAAK;QACxB,0BAA0B,EAAE,kBAAkB;QAC9C,mBAAmB,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;QAC/C,qBAAqB,EAAE,mBAAmB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC;QACtE,kBAAkB,EAAE,gBAAgB;QACpC,sBAAsB,EAAE,oBAAoB;QAC5C,MAAM;QACN,oCAAoC,EAAE,uBAAuB;QAC7D,QAAQ;KACT,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,wBAAwB,CAAC,MAAc;IAC9C,uEAAuE;IACvE,qEAAqE;IACrE,kDAAkD;IAClD,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5C,oEAAoE;IACpE,sDAAsD;IACtD,iEAAiE;IACjE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,uBAAuB,CAAiD,CAAA;IACnG,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAA;AACb,CAAC;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,KAAK,UAAU,UAAU,CAAC,IAAmB;IAC3C,MAAM,eAAe,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IACpH,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAA;IACjC,MAAM,UAAU,GAAG,wBAAwB,CAAC,eAAe,CAAC,CAAA;IAE5D,IAAI,OAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,UAAU,EAAE,CAAA;QAC7E,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAClC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC,CAAA;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,gGAAgG,CAAC,CAAA;YACpH,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;YAC7E,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAsC,CAAA;QACjE,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAA;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACtD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAA;QACpD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,6EAA6E;IAC7E,gDAAgD;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAC5B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,UAAU,KAAK,kBAAkB;QACnC,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,oBAAoB;QAC3C,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,eAAe,CACzC,CAAA;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qGAAqG,CAAC,CAAA;QACzH,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAE,CAAA,CAAC,gDAAgD;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA,CAAC,6BAA6B;IAE9D,wEAAwE;IACxE,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,OAAO,CAAC,WAAW,EAAE,CAAA;IACvH,IAAI,WAA8B,CAAA;IAClC,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,CAAA;IACzD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,4BAA4B,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAC5F,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qCAAqC,aAAa,oEAAoE,CAAC,CAAA;QAC1I,OAAO,IAAI,CAAA;IACb,CAAC;IAED,sEAAsE;IACtE,qEAAqE;IACrE,qEAAqE;IACrE,iEAAiE;IACjE,IACE,OAAO,WAAW,CAAC,QAAQ,EAAE,gBAAgB,KAAK,QAAQ;QAC1D,WAAW,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC;QAClD,OAAO,WAAW,CAAC,QAAQ,EAAE,eAAe,KAAK,QAAQ;QACzD,OAAO,WAAW,CAAC,QAAQ,EAAE,cAAc,KAAK,QAAQ;QACxD,OAAO,WAAW,CAAC,SAAS,KAAK,QAAQ;QACzC,WAAW,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAClC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAA;QACzF,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,CAAC,WAAW,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1D,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAA;QAC/G,OAAO,IAAI,CAAA;IACb,CAAC;IAED,sEAAsE;IACtE,oCAAoC;IACpC,IAAI,SAAS,GAAsB,IAAI,CAAA;IACvC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,WAAW,EAAE,CAAA;QAChH,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;QACxC,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,YAAY,CAAA;IAC/D,MAAM,WAAW,GACf,OAAO,IAAI,CAAC,oBAAoB,KAAK,QAAQ;QAC3C,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,oBAAoB;QAC1C,CAAC,CAAC,IAAI,CAAA;IACV,IAAI,OAAO,IAAI,CAAC,oBAAoB,KAAK,QAAQ,IAAI,WAAW,KAAK,KAAK,EAAE,CAAC;QAC3E,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,sCAAsC,WAAW,gBAAgB,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAA;IAClH,CAAC;IAED,MAAM,OAAO,GAAkB;QAC7B,kBAAkB,EAAE,aAAa;QACjC,kBAAkB,EAAE,WAAW,CAAC,QAAQ,CAAC,eAAe;QACxD,gBAAgB,EAAE,OAAO,CAAC,YAAY;QACtC,aAAa,EAAE,WAAW;QAC1B,oBAAoB,EAAE,IAAI,EAAE,mBAAmB;QAC/C,mBAAmB,EAAE,WAAW;KACjC,CAAA;IAED,gEAAgE;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,2BAA2B,CAAC,CAAC,CAAA;IACrF,IAAI,GAAG,IAAI,CAAC;QAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAE1C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAA;AACpD,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,oBAAoB,CACjC,iBAAyB,EACzB,OAAqB,EACrB,MAA+B,EAC/B,QAAkB;IAElB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAA;QAC5D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;YAC7B,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,QAAQ,CAAC,IAAI,CAAC,8BAA8B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;YAC3E,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAmC,CAAA;QAC9D,IAAI,OAAO,IAAI,CAAC,gBAAgB,KAAK,QAAQ,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpF,QAAQ,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAA;YAC3F,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,IAAI,CAAC,gBAAgB,CAAA;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACtD,QAAQ,CAAC,IAAI,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAA;QAClD,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAYD;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,WAAW,CAAC,IAAoB;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAA;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,eAAe,CAAA;IACzD,IAAI,OAAO,IAAI,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,qCAAqC,SAAS,eAAe,OAAO,EAAE,CAAC,CAAA;QAC1F,OAAO,IAAI,CAAA;IACb,CAAC;IAED,wCAAwC;IACxC,IAAI,SAAiB,CAAA;IACrB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,qBAAqB,SAAS,OAAO,OAAO,EAAE,CAAA;QACtG,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAClC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,mCAAmC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;YACrF,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAwB,CAAA;QACnD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAA;YACrG,OAAO,IAAI,CAAA;QACb,CAAC;QACD,SAAS,GAAG,IAAI,CAAC,KAAK,CAAA;IACxB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,mCAAmC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACnG,OAAO,IAAI,CAAA;IACb,CAAC;IAED,wBAAwB;IACxB,IAAI,SAAqB,CAAA;IACzB,IAAI,WAAuB,CAAA;IAC3B,IAAI,KAAiB,CAAA;IACrB,IAAI,CAAC;QACH,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;QAC9D,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;QAClE,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE,IAAI,WAAW,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YACzD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;QACpD,CAAC;QACD,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IACrC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,8BAA8B,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAC9F,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,QAAiB,CAAA;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC;YACrC,UAAU,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC;YACpC,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACxF,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,gCAAgC,CAAC,CAAC,CAAA;QAC1F,IAAI,GAAG,IAAI,CAAC;YAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QAC1C,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,mGAAmG,CAAC,CAAA;IACvH,OAAO,UAAU,CAAA;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,KAAK,UAAU,YAAY,CACzB,UAAkB,EAClB,iBAAyB,EACzB,SAA6B,EAC7B,iBAA6D,EAC7D,YAAwB,EACxB,OAAqB,EACrB,MAA+B,EAC/B,QAAkB;AAClB;;;;;;;;;GASG;AACH,eAAkC;IAElC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5D,QAAQ,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAA;QACpF,OAAO,IAAI,CAAA;IACb,CAAC;IAED,yEAAyE;IACzE,IAAI,WAAmB,CAAA;IACvB,IAAI,YAAoB,CAAA;IACxB,IAAI,eAAe,EAAE,CAAC;QACpB,WAAW,GAAG,eAAe,CAAC,QAAQ,CAAC,cAAc,CAAA;QACrD,YAAY,GAAG,eAAe,CAAC,QAAQ,CAAC,eAAe,CAAA;IACzD,CAAC;SAAM,CAAC;QACN,IAAI,UAAkD,CAAA;QACtD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAA;YAC5D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;gBAC7B,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;gBACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9B,CAAC,CAAA;YACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,QAAQ,CAAC,IAAI,CAAC,8BAA8B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;gBAC3E,OAAO,IAAI,CAAA;YACb,CAAC;YACD,UAAU,GAAG,MAAM,GAAG,CAAC,IAAI,EAAuB,CAAA;QACpD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YACtD,QAAQ,CAAC,IAAI,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAA;YAClD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,UAAU,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACrF,QAAQ,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAA;YACpF,OAAO,IAAI,CAAA;QACb,CAAC;QACD,WAAW,GAAG,UAAU,CAAC,SAAS,CAAA;QAClC,YAAY,GAAG,UAAU,CAAC,KAAK,CAAA;IACjC,CAAC;IAED,IAAI,QAAoB,CAAA;IACxB,IAAI,KAAiB,CAAA;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;QAClC,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,mCAAmC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;QACxE,CAAC;QACD,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IACrC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACtD,QAAQ,CAAC,IAAI,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAA;QAClD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,QAAiB,CAAA;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,iBAAiB,CAAC;YAC3B,YAAY;YACZ,QAAQ;YACR,YAAY;YACZ,KAAK,EAAE,UAAU;YACjB,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACtD,QAAQ,CAAC,IAAI,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAA;QAC5C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,4DAA4D;QAC5D,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,gCAAgC,CAAC,CAAC,CAAA;QACrF,IAAI,GAAG,IAAI,CAAC;YAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACrC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,kGAAkG,CAAC,CAAA;IACjH,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,6EAA6E;AAC7E,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IAC7E,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACtD,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IACf,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,4DAA4D;AAC5D,SAAS,gBAAgB,CAAC,CAAS;IACjC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACvC,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAA;AACzE,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAC1B,UAAkB,EAClB,IAA4B,EAC5B,QAAkB;IAElB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAA;QAC/E,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IAC9C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,eAAe,GACnB,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ,IAAI,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,SAAS,CAAA;IAClF,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,iBAAiB;QAC/B,oBAAoB,EAAE,KAAK,CAAC,SAAS;QACrC,gBAAgB,EAAE,eAAe;KAClC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,KAAK,UAAU,qBAAqB,CAClC,IAAgB,EAChB,mBAA2B,EAC3B,QAAkB;IAElB,iEAAiE;IACjE,oDAAoD;IACpD,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAA;IACvC,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;IACxC,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAA;QACnF,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,QAAoB,CAAA;IACxB,IAAI,QAAoB,CAAA;IACxB,IAAI,CAAC;QACH,QAAQ,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;QACtC,QAAQ,GAAG,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IAClD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,CAAC,yBAAyB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACpF,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC,yDAAyD,QAAQ,CAAC,MAAM,2BAA2B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;QACpI,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,EAAE,GAAG,KAAK,CAAA;IACd,IAAI,CAAC;QACH,EAAE,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAA;IACzF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACnF,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,QAAQ,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAA;IACvG,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,KAAK,UAAU,YAAY,CACzB,WAAmB,EACnB,SAA6B,EAC7B,OAAqB,EACrB,MAA+B,EAC/B,QAAkB;IAElB,IAAI,cAAsB,CAAA;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAA;QAC1D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;YAC7B,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE;YACjC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,QAAQ,CAAC,IAAI,CAAC,kCAAkC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;YAC/E,OAAO,IAAI,CAAA;QACb,CAAC;QACD,cAAc,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,CAAC,kCAAkC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAC7F,OAAO,IAAI,CAAA;IACb,CAAC;IAED,kEAAkE;IAClE,qEAAqE;IACrE,sEAAsE;IACtE,gBAAgB;IAChB,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/C,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAA;QAC5F,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAA;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAE9C,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAQ;QAC1B,+EAA+E;QAC/E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;QAClD,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAW,CAAA;QACpC,IAAI,SAAS,KAAK,SAAS;YAAE,YAAY,IAAI,CAAC,CAAA;IAChD,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,sJAAsJ,CAAC,CAAA;IAErK,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,YAAY,GAAG,SAAS,EAAE,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC,uCAAuC,YAAY,cAAc,SAAS,EAAE,CAAC,CAAA;IAC7F,CAAC;IAED,4FAA4F;IAC5F,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,4BAA4B,CAAC,CAAC,CAAA;IACjF,IAAI,GAAG,IAAI,CAAC;QAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAErC,OAAO,YAAY,CAAA;AACrB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atrib/verify",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@noble/ed25519": "^2.3.0",
|
|
31
31
|
"@noble/hashes": "^2.2.0",
|
|
32
32
|
"canonicalize": "^2.1.0",
|
|
33
|
-
"@atrib/mcp": "0.
|
|
33
|
+
"@atrib/mcp": "0.6.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^25.6.0",
|