@tinycloud/node-sdk 3.6.0-beta.3 → 3.6.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +6 -0
  2. package/dist/{TinyCloudNode-N4mfZI_k.d.cts → TinyCloudNode-B5WCe_ps.d.ts} +3 -250
  3. package/dist/{TinyCloudNode-N4mfZI_k.d.ts → TinyCloudNode-CIGurdHw.d.cts} +3 -250
  4. package/dist/{authority-BgZpUFss.d.cts → authority-Beg1WM2U.d.cts} +1 -1
  5. package/dist/{authority-C670-9su.d.ts → authority-Vh7ike0I.d.ts} +1 -1
  6. package/dist/core.d.cts +3 -2
  7. package/dist/core.d.ts +3 -2
  8. package/dist/credential-pending-store.cjs +401 -0
  9. package/dist/credential-pending-store.cjs.map +1 -0
  10. package/dist/credential-pending-store.d.cts +42 -0
  11. package/dist/credential-pending-store.d.ts +42 -0
  12. package/dist/credential-pending-store.js +389 -0
  13. package/dist/credential-pending-store.js.map +1 -0
  14. package/dist/credentials-core.cjs +309 -0
  15. package/dist/credentials-core.cjs.map +1 -0
  16. package/dist/credentials-core.d.cts +337 -0
  17. package/dist/credentials-core.d.ts +337 -0
  18. package/dist/credentials-core.js +294 -0
  19. package/dist/credentials-core.js.map +1 -0
  20. package/dist/credentials.cjs +746 -0
  21. package/dist/credentials.cjs.map +1 -0
  22. package/dist/credentials.d.cts +4 -0
  23. package/dist/credentials.d.ts +4 -0
  24. package/dist/credentials.js +732 -0
  25. package/dist/credentials.js.map +1 -0
  26. package/dist/delegation-0LKZkmP2.d.cts +251 -0
  27. package/dist/delegation-0LKZkmP2.d.ts +251 -0
  28. package/dist/email-proof-Blhdm4ox.d.cts +155 -0
  29. package/dist/email-proof-Blhdm4ox.d.ts +155 -0
  30. package/dist/index.d.cts +2 -1
  31. package/dist/index.d.ts +2 -1
  32. package/dist/replication/authority.d.cts +3 -2
  33. package/dist/replication/authority.d.ts +3 -2
  34. package/package.json +18 -2
@@ -0,0 +1,746 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/credentials/index.ts
21
+ var credentials_exports = {};
22
+ __export(credentials_exports, {
23
+ DEFAULT_EMAIL_PROOF_PROFILE: () => DEFAULT_EMAIL_PROOF_PROFILE,
24
+ EMAIL_PROOF_CREDENTIAL_TYPE: () => EMAIL_PROOF_CREDENTIAL_TYPE,
25
+ EMAIL_PROOF_PENDING_MAX_MS: () => EMAIL_PROOF_PENDING_MAX_MS,
26
+ EmailProofError: () => EmailProofError,
27
+ OpenCredentialsHttpTransport: () => OpenCredentialsHttpTransport,
28
+ acquireEmailProof: () => acquireEmailProof,
29
+ completeEmailProof: () => completeEmailProof,
30
+ createEd25519HolderSigner: () => createEd25519HolderSigner,
31
+ createMemoryEmailProofPendingStore: () => createMemoryEmailProofPendingStore,
32
+ emailProofRequirement: () => emailProofRequirement,
33
+ interpretCredentialFlow: () => interpretCredentialFlow,
34
+ mailboxSubject: () => mailboxSubject,
35
+ requestEmailProofCode: () => requestEmailProofCode
36
+ });
37
+ module.exports = __toCommonJS(credentials_exports);
38
+
39
+ // src/credentials/interpreter.ts
40
+ var import_sdk_core = require("@tinycloud/sdk-core");
41
+ var delay = (ms, signal) => new Promise((resolve, reject) => {
42
+ const timer = setTimeout(resolve, ms);
43
+ signal?.addEventListener("abort", () => {
44
+ clearTimeout(timer);
45
+ reject(new DOMException("Aborted", "AbortError"));
46
+ }, { once: true });
47
+ });
48
+ function mailboxSubject(requirement) {
49
+ const email = requirement.claims.email;
50
+ return typeof email === "string" && email.length > 0 ? Object.freeze({ kind: "email", value: email }) : void 0;
51
+ }
52
+ async function interpretCredentialFlow(input) {
53
+ const steps = new Map(input.descriptor.steps.map((step) => [step.type, step]));
54
+ const completed = /* @__PURE__ */ new Set();
55
+ let lastTransition = "";
56
+ for (; ; ) {
57
+ input.signal?.throwIfAborted();
58
+ const state = await input.transport.state(input.requestId, input.verifier, input.signal);
59
+ if (state.requestId !== input.requestId) throw new import_sdk_core.CredentialError("REQUEST_SUBSTITUTED", "Credential request identity changed");
60
+ if (state.state === "expired") throw new import_sdk_core.CredentialError("REQUEST_EXPIRED", "Credential request expired", { state: state.state, correlationId: state.correlationId });
61
+ if (state.state === "canceled") throw new import_sdk_core.CredentialError("CANCELED", "Credential request was canceled", { state: state.state, correlationId: state.correlationId });
62
+ if (state.state === "issuer_unready") throw new import_sdk_core.CredentialError("ISSUER_UNREADY", "Credential issuer is not ready", { retryAfterMs: state.retryAfterMs, state: state.state, correlationId: state.correlationId });
63
+ if (state.state === "complete") return;
64
+ if (state.state === "ready_to_issue" || state.state === "issued") {
65
+ if (state.state === "ready_to_issue" && !completed.has("issue")) {
66
+ await input.transport.issue(input.requestId, input.verifier, input.signal);
67
+ completed.add("issue");
68
+ } else await (input.onWait?.() ?? delay(state.retryAfterMs ?? 50, input.signal));
69
+ continue;
70
+ }
71
+ const next = state.nextStep;
72
+ if (!next) {
73
+ await (input.onWait?.() ?? delay(state.retryAfterMs ?? 50, input.signal));
74
+ continue;
75
+ }
76
+ const declared = steps.get(next.type);
77
+ if (next.id !== next.type || !declared || declared.type !== next.type || declared.version !== next.version) throw new import_sdk_core.CredentialError("REQUEST_SUBSTITUTED", "Server selected an undeclared credential step");
78
+ if (completed.has(next.id) || state.transitionId === lastTransition) {
79
+ await (input.onWait?.() ?? delay(state.retryAfterMs ?? 50, input.signal));
80
+ continue;
81
+ }
82
+ lastTransition = state.transitionId;
83
+ if (next.type === "holder_signature") {
84
+ input.onProgress?.({ state: "signing", stepId: next.id, correlationId: state.correlationId });
85
+ const binding = (0, import_sdk_core.validateCredentialHolderBinding)(await input.transport.holderBinding(input.requestId, input.verifier, input.signal));
86
+ if (binding.requestId !== input.requestId || binding.descriptorDigest !== input.descriptorDigest || binding.requirementDigest !== input.requirementDigest || binding.holderDid !== input.holderDid || binding.issuer !== input.descriptor.issuer.did || binding.issuerKid !== input.descriptor.issuer.kid || binding.profile !== input.descriptor.profile || binding.openerOrigin !== input.openerOrigin || binding.completionOrigin !== input.openerOrigin || binding.audience !== "tinycloud://credentials") throw new import_sdk_core.CredentialError("REQUEST_SUBSTITUTED", "Holder binding was substituted");
87
+ const bytes = (0, import_sdk_core.holderBindingSigningBytes)(binding);
88
+ let signature = await input.signing.autoSign?.(binding, bytes, input.signal);
89
+ if (signature === void 0) {
90
+ if (!input.signing.requestApproval) throw new import_sdk_core.CredentialError("SIGNATURE_REJECTED", "OpenKey approval is required");
91
+ try {
92
+ signature = await input.signing.requestApproval(binding, bytes, input.signal);
93
+ } catch (cause) {
94
+ if (cause instanceof import_sdk_core.CredentialError) throw cause;
95
+ throw new import_sdk_core.CredentialError("SIGNATURE_REJECTED", "OpenKey approval was rejected", { cause });
96
+ }
97
+ }
98
+ if (!(signature instanceof Uint8Array) || signature.length === 0) throw new import_sdk_core.CredentialError("SIGNATURE_REJECTED", "OpenKey signature is invalid");
99
+ await input.transport.submitHolderSignature(input.requestId, input.verifier, (0, import_sdk_core.encodeBase64Url)(signature), input.signal, input.signing.holderKey);
100
+ } else {
101
+ input.onProgress?.({ state: next.type === "collect_input" ? "collecting" : "proving", stepId: next.id, correlationId: state.correlationId });
102
+ const handler = input.handlers?.[next.type];
103
+ const inlineHandler = input.proofHandler;
104
+ if (!handler && !inlineHandler) {
105
+ await (input.onWait?.() ?? delay(state.retryAfterMs ?? 50, input.signal));
106
+ continue;
107
+ }
108
+ if (next.constraints.challengeRequired === true) await input.transport.beginStep(input.requestId, input.verifier, next.type, input.signal);
109
+ if (handler) {
110
+ const proof = await handler({ descriptor: input.descriptor, requirement: input.requirement, stepId: next.id, constraints: next.constraints, signal: input.signal });
111
+ await input.transport.submitStep(input.requestId, input.verifier, next.id, proof, input.signal);
112
+ } else {
113
+ let feedback;
114
+ for (let attempt = 1; ; attempt += 1) {
115
+ const proof = await inlineHandler({
116
+ stepId: next.id,
117
+ constraints: next.constraints,
118
+ display: {
119
+ title: input.descriptor.display.title,
120
+ description: input.descriptor.display.description,
121
+ consent: input.descriptor.display.consent,
122
+ progressLabel: input.descriptor.accessibility.progressLabel,
123
+ errorLiveRegion: input.descriptor.accessibility.errorLiveRegion
124
+ },
125
+ inputs: input.descriptor.inputs.map(({ id, label, schema }) => ({ id, label, schema })),
126
+ ...feedback === void 0 ? {} : { feedback },
127
+ signal: input.signal
128
+ });
129
+ try {
130
+ await input.transport.submitStep(input.requestId, input.verifier, next.id, proof, input.signal);
131
+ break;
132
+ } catch (cause) {
133
+ if (!(cause instanceof import_sdk_core.CredentialError) || cause.code !== "PROOF_REJECTED") throw cause;
134
+ const attemptsRemaining = input.descriptor.lifecycle.maxProofAttempts - attempt;
135
+ if (next.type !== "mailbox_otp" || attemptsRemaining <= 0) throw new import_sdk_core.CredentialError("VERIFICATION_FAILED", "The proof was not accepted", { state: attemptsRemaining <= 0 ? "proof_attempts_exhausted" : "proof_rejected", cause });
136
+ feedback = { kind: "rejected", attemptsRemaining };
137
+ }
138
+ }
139
+ }
140
+ }
141
+ completed.add(next.id);
142
+ }
143
+ }
144
+
145
+ // src/credentials/transport.ts
146
+ var import_sdk_core2 = require("@tinycloud/sdk-core");
147
+ function object(value, label) {
148
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", `${label} is invalid`);
149
+ return value;
150
+ }
151
+ function opaque(value, label) {
152
+ if (typeof value !== "string" || !/^[A-Za-z0-9_-]{16,128}$/.test(value)) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", `${label} is invalid`);
153
+ return value;
154
+ }
155
+ function timestamp(value, label) {
156
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", `${label} is invalid`);
157
+ return new Date(value).toISOString();
158
+ }
159
+ function jwtPayload(credential) {
160
+ const part = credential.split("~", 1)[0]?.split(".")[1];
161
+ if (!part) throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "Issued SD-JWT is invalid");
162
+ try {
163
+ return object(JSON.parse(new TextDecoder().decode((0, import_sdk_core2.decodeBase64Url)(part))), "SD-JWT payload");
164
+ } catch (cause) {
165
+ if (cause instanceof import_sdk_core2.CredentialError) throw cause;
166
+ throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "Issued SD-JWT is invalid", { cause });
167
+ }
168
+ }
169
+ var SERVER_CODES = /* @__PURE__ */ new Set(["REQUEST_EXPIRED", "ISSUER_UNREADY", "UNSUPPORTED_PROFILE", "UNSUPPORTED_VERSION", "SIGNATURE_REJECTED"]);
170
+ function serverError(value, response, honorServerRecoverable) {
171
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
172
+ const body = value;
173
+ const keys = Object.keys(body).sort();
174
+ if (keys.join(",") !== "code,correlationId,recoverable,state,type" || body.type !== "tinycloud.credentials/error/v1" || typeof body.code !== "string" || typeof body.recoverable !== "boolean" || typeof body.state !== "string" || typeof body.correlationId !== "string") return void 0;
175
+ if (body.code === "PROOF_REJECTED") return body.recoverable ? new import_sdk_core2.CredentialError("PROOF_REJECTED", "The proof was not accepted", { state: body.state, correlationId: body.correlationId }) : new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "The proof attempt limit was reached", { state: "proof_attempts_exhausted", correlationId: body.correlationId });
176
+ if (body.code === "RATE_LIMITED") return new import_sdk_core2.CredentialError("ISSUER_UNREADY", "Too many verification codes were requested", { state: "rate_limited", correlationId: body.correlationId, retryAfterMs: Number(response.headers.get("retry-after")) * 1e3 || void 0, recoverable: honorServerRecoverable ? body.recoverable : void 0 });
177
+ if (body.code === "CHALLENGE_EXPIRED") return new import_sdk_core2.CredentialError("REQUEST_EXPIRED", "The credential challenge expired", { state: body.state, correlationId: body.correlationId, recoverable: honorServerRecoverable ? body.recoverable : void 0 });
178
+ if (!SERVER_CODES.has(body.code)) return void 0;
179
+ const code = body.code;
180
+ return new import_sdk_core2.CredentialError(code, `OpenCredentials rejected the request: ${code}`, {
181
+ state: body.state,
182
+ correlationId: body.correlationId,
183
+ retryAfterMs: Number(response.headers.get("retry-after")) * 1e3 || void 0,
184
+ recoverable: honorServerRecoverable ? body.recoverable : void 0
185
+ });
186
+ }
187
+ var OpenCredentialsHttpTransport = class {
188
+ constructor(descriptor, fetchFn = globalThis.fetch.bind(globalThis), options = {}) {
189
+ this.descriptor = descriptor;
190
+ this.fetchFn = fetchFn;
191
+ this.options = options;
192
+ this.nonces = /* @__PURE__ */ new Map();
193
+ this.bindings = /* @__PURE__ */ new Map();
194
+ }
195
+ url(endpoint, id) {
196
+ return new URL((0, import_sdk_core2.credentialEndpointPath)(endpoint, id), this.descriptor.issuer.origin).href;
197
+ }
198
+ async request(endpoint, input) {
199
+ let response;
200
+ try {
201
+ const init = { method: input.method ?? "GET", credentials: "omit", cache: "no-store", redirect: "error", referrerPolicy: "no-referrer", signal: input.signal, headers: { accept: "application/json", ...input.body === void 0 ? {} : { "content-type": "application/json" }, ...input.verifier === void 0 ? {} : { authorization: `Bearer ${input.verifier}` } }, ...input.body === void 0 ? {} : { body: JSON.stringify(input.body) } };
202
+ response = await this.fetchFn(this.url(endpoint, input.id), init);
203
+ } catch (cause) {
204
+ if (cause instanceof DOMException && cause.name === "AbortError") throw new import_sdk_core2.CredentialError("CANCELED", "Credential acquisition was canceled", { cause });
205
+ throw new import_sdk_core2.CredentialError("OFFLINE", "OpenCredentials is unavailable", { cause });
206
+ }
207
+ if (!response.ok) {
208
+ let parsed;
209
+ try {
210
+ parsed = await response.json();
211
+ } catch {
212
+ parsed = void 0;
213
+ }
214
+ const typed = serverError(parsed, response, this.options.honorServerRecoverable === true);
215
+ if (typed) throw typed;
216
+ if (response.status === 410) throw new import_sdk_core2.CredentialError("REQUEST_EXPIRED", "Credential request expired");
217
+ if (response.status === 409) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential request was replayed or substituted");
218
+ if (response.status === 503) throw new import_sdk_core2.CredentialError("ISSUER_UNREADY", "Credential issuer is not ready", { retryAfterMs: Number(response.headers.get("retry-after")) * 1e3 || void 0 });
219
+ throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "OpenCredentials rejected the request");
220
+ }
221
+ if (response.status === 204) return void 0;
222
+ try {
223
+ return await response.json();
224
+ } catch (cause) {
225
+ throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "OpenCredentials response is invalid", { cause });
226
+ }
227
+ }
228
+ async create(input) {
229
+ const body = object(await this.request("request", { method: "POST", body: { protocol: input.descriptor.protocol, profile: input.descriptor.profile, profileVersion: input.descriptor.profileVersion, descriptorDigest: input.descriptorDigest, requirementDigest: input.requirementDigest, holderDid: input.holderDid, inputs: input.inputs ?? input.requirement.claims, audience: "tinycloud://credentials", openerOrigin: input.openerOrigin, completionOrigin: input.openerOrigin, completionContext: "sdk-acquisition", completionVerifierChallenge: input.completionVerifierChallenge }, signal: input.signal }), "create response");
230
+ if (body.type !== "tinycloud.credentials/acquisition-request/v1" || body.protocol !== input.descriptor.protocol || body.next !== "challenge" || body.endpoint !== "challenge") throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "create response is invalid");
231
+ const requestId = opaque(body.requestId, "requestId");
232
+ return { requestId, locator: requestId, expiresAt: timestamp(body.expiresAt, "expiresAt"), correlationId: requestId };
233
+ }
234
+ proofStep() {
235
+ return this.descriptor.steps.some((step) => step.type === "mailbox_otp") ? "mailbox_otp" : "collect_input";
236
+ }
237
+ async state(id, verifier, signal) {
238
+ const raw = object(await this.request("state", { id, verifier, signal }), "state response");
239
+ if (raw.type !== "tinycloud.credentials/acquisition-state/v1" || raw.requestId !== id || raw.profile !== this.descriptor.profile || raw.profileVersion !== 1) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential state response is substituted");
240
+ const base = { type: "OpenCredentialsAcquisitionState", version: 1, requestId: id, transitionId: `${String(raw.state)}:${String(raw.resultPreviouslyRead ?? false)}`, correlationId: id };
241
+ if (raw.state === "challenge_required") {
242
+ const type = this.proofStep();
243
+ return { ...base, state: "pending", nextStep: { id: type, type, version: 1, constraints: { challengeRequired: true } } };
244
+ }
245
+ if (raw.state === "proof_required") {
246
+ const type = this.proofStep();
247
+ this.nonces.set(id, opaque(raw.challengeNonce, "challengeNonce"));
248
+ return { ...base, state: "pending", nextStep: { id: type, type, version: 1, constraints: {} } };
249
+ }
250
+ if (raw.state === "holder_binding_required" || raw.state === "holder_signature_required") return { ...base, state: "pending", nextStep: { id: "holder_signature", type: "holder_signature", version: 1, constraints: {} } };
251
+ if (raw.state === "ready_to_issue") return { ...base, state: "ready_to_issue" };
252
+ if (raw.state === "issued") return { ...base, state: "complete" };
253
+ throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential state is unsupported");
254
+ }
255
+ async beginStep(id, verifier, stepId, signal) {
256
+ const step = this.proofStep();
257
+ if (stepId !== step) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential challenge step is substituted");
258
+ const challenge = object(await this.request("challenge", { id, verifier, method: "POST", body: { step, stepVersion: 1 }, signal }), "challenge response");
259
+ if (challenge.type !== "tinycloud.credentials/challenge/v1" || challenge.step !== step || challenge.stepVersion !== 1) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential challenge is substituted");
260
+ this.nonces.set(id, opaque(challenge.challengeNonce, "challengeNonce"));
261
+ }
262
+ async submitStep(id, verifier, stepId, proof, signal) {
263
+ const step = this.proofStep();
264
+ if (stepId !== step) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential proof step is substituted");
265
+ const nonce = this.nonces.get(id);
266
+ if (!nonce) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Credential challenge is missing");
267
+ await this.request("proof", { id, verifier, method: "POST", body: { step, stepVersion: 1, challengeNonce: nonce, proof }, signal });
268
+ }
269
+ async holderBinding(id, verifier, signal) {
270
+ const raw = object(await this.request("holder_binding", { id, verifier, signal }), "holder binding response");
271
+ if (raw.type !== "tinycloud.credentials/holder-binding-request/v1" || raw.signingDomain !== "tinycloud.credentials/holder-binding/v1" || raw.alg !== "EdDSA" || typeof raw.canonical !== "string") throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Holder binding response is invalid");
272
+ const binding = (0, import_sdk_core2.validateCredentialHolderBinding)(raw.binding);
273
+ if ((0, import_sdk_core2.encodeBase64Url)((0, import_sdk_core2.holderBindingCanonicalBytes)(binding)) !== raw.canonical) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Holder binding canonical bytes were substituted");
274
+ this.bindings.set(id, binding);
275
+ return binding;
276
+ }
277
+ async submitHolderSignature(id, verifier, signature, signal, key) {
278
+ const binding = this.bindings.get(id);
279
+ if (!binding) throw new import_sdk_core2.CredentialError("REQUEST_SUBSTITUTED", "Holder binding was not retrieved");
280
+ const kid = key?.kid ?? `${binding.holderDid}#${binding.holderDid.replace("did:key:", "")}`;
281
+ await this.request("holder_signature", { id, verifier, method: "POST", body: { alg: key?.alg ?? "EdDSA", kid, signature }, signal });
282
+ }
283
+ async issue(id, verifier, signal) {
284
+ await this.request("issue", { id, verifier, method: "POST", signal });
285
+ }
286
+ async result(id, verifier, signal) {
287
+ const raw = object(await this.request("result", { id, verifier, signal }), "result response");
288
+ if (raw.type !== "tinycloud.credentials/result/v1" || raw.protocol !== this.descriptor.protocol || raw.profile !== this.descriptor.profile || raw.profileVersion !== 1 || raw.format !== "vc+sd-jwt" || raw.descriptorDigest !== await (0, import_sdk_core2.canonicalDigest)(this.descriptor) || raw.issuer !== this.descriptor.issuer.did || raw.issuerKid !== this.descriptor.issuer.kid || typeof raw.credential !== "string" || await (0, import_sdk_core2.sha256Base64Url)(raw.credential) !== raw.credentialDigest) throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "Issued result metadata is invalid");
289
+ const holder = object(raw.holderBinding, "result holder binding");
290
+ if (raw.subject !== holder.did || holder.signingDomain !== "tinycloud.credentials/holder-binding/v1") throw new import_sdk_core2.CredentialError("HOLDER_MISMATCH", "Issued holder binding is invalid");
291
+ const claimsRaw = object(raw.claims, "result claims");
292
+ const claims = {};
293
+ for (const [name, value] of Object.entries(claimsRaw)) {
294
+ if (typeof value !== "string") throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "Issued claims are invalid");
295
+ claims[name] = value;
296
+ }
297
+ const validity = object(raw.validity, "result validity");
298
+ const payload = jwtPayload(raw.credential);
299
+ if (typeof payload.jti !== "string") throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "Credential id is invalid");
300
+ const issuedAt = timestamp(validity.issuedAt, "issuedAt");
301
+ const expiresAt = timestamp(validity.expiresAt, "expiresAt");
302
+ const status = object(raw.status, "result status");
303
+ if (status.type !== "none" || status.freshnessSeconds !== this.descriptor.status.freshnessSeconds) throw new import_sdk_core2.CredentialError("VERIFICATION_FAILED", "Credential status is invalid");
304
+ return { type: "OpenCredentialsIssuedCredential", version: 1, protocol: this.descriptor.protocol, profile: { id: this.descriptor.profile, version: 1 }, credentialType: { id: this.descriptor.format.vct, version: 1 }, schema: this.descriptor.format.vct, format: "vc+sd-jwt", issuerDid: raw.issuer, issuerKid: raw.issuerKid, subjectDid: raw.subject, holderDid: raw.subject, claims, claimsDigest: await (0, import_sdk_core2.canonicalDigest)(claims), descriptorDigest: raw.descriptorDigest, credentialId: payload.jti, issuedAt, notBefore: issuedAt, expiresAt, status: { method: "none", freshnessSeconds: status.freshnessSeconds }, credential: raw.credential };
305
+ }
306
+ async issuerMetadata(signal) {
307
+ return await this.request("issuer_metadata", { signal });
308
+ }
309
+ async checkStatus(status, _signal) {
310
+ return status.method === "none" && status.freshnessSeconds === this.descriptor.status.freshnessSeconds;
311
+ }
312
+ };
313
+
314
+ // src/credentials/email-proof.ts
315
+ var import_sdk_core3 = require("@tinycloud/sdk-core");
316
+ var EMAIL_PROOF_PINS = Object.freeze({
317
+ witnessOrigin: "https://witness.credentials.org",
318
+ issuerDid: "did:web:issuer.credentials.org",
319
+ issuerKid: "did:web:issuer.credentials.org#controller",
320
+ issuerX: "eEJI4xEobto4HtQ7Pg9R1vBOwcpfGRTlXDG5QfqLnGQ",
321
+ descriptorDigest: "1tg-qphmKBVtNwzVg9xyz-xxqt_xtMXAsQyXw46m8S0"
322
+ });
323
+ var EMAIL_PROOF_CREDENTIAL_TYPE = { id: "opencredentials.email/v1", version: 1 };
324
+ var EMAIL_PROOF_PENDING_MAX_MS = 15 * 6e4;
325
+ var DISCOVERY_URL = `${EMAIL_PROOF_PINS.witnessOrigin}/.well-known/opencredentials`;
326
+ var EMAIL_PROOF_ISSUER_KEYS = [
327
+ {
328
+ kid: EMAIL_PROOF_PINS.issuerKid,
329
+ alg: "EdDSA",
330
+ jwk: { kty: "OKP", crv: "Ed25519", x: EMAIL_PROOF_PINS.issuerX },
331
+ validFrom: "2020-01-01T00:00:00Z",
332
+ validUntil: "2100-01-01T00:00:00Z"
333
+ }
334
+ ];
335
+ var DEFAULT_EMAIL_PROOF_PROFILE = "tinycloud.email-proof/v1";
336
+ var PROFILE_PINS = {
337
+ "tinycloud.email-proof/v1": { descriptorDigest: EMAIL_PROOF_PINS.descriptorDigest, requiredStep: "mailbox_otp", enabled: true }
338
+ };
339
+ var OPENER_ORIGIN = "https://localhost";
340
+ var STEP_TIMEOUT_MS = 6e4;
341
+ var HOLDER_DID_PREFIX = "did:key:";
342
+ var HOLDER_DID_PATTERN = /^did:key:z6Mk[^#]+$/;
343
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
344
+ var INSPECT = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
345
+ var RECOVERABILITY = {
346
+ ADDRESS_INVALID: false,
347
+ HOLDER_INVALID: false,
348
+ CODE_INVALID: true,
349
+ RATE_LIMITED: true,
350
+ ISSUER_UNAVAILABLE: true,
351
+ NETWORK: true,
352
+ PROFILE_UNAVAILABLE: false,
353
+ CODE_REJECTED: true,
354
+ CODE_ATTEMPTS_EXHAUSTED: false,
355
+ SIGNATURE_REJECTED: true,
356
+ CREDENTIAL_INVALID: false,
357
+ SESSION_EXPIRED: false,
358
+ SESSION_INVALID: false,
359
+ CANCELED: false,
360
+ FAILED: false
361
+ };
362
+ var MESSAGES = {
363
+ ADDRESS_INVALID: "Email address is invalid.",
364
+ HOLDER_INVALID: "Holder must be a did:key Ed25519 identifier.",
365
+ CODE_INVALID: "Verification code must be 8 digits.",
366
+ ISSUER_UNAVAILABLE: "Email verification is unavailable right now.",
367
+ NETWORK: "Could not reach the email verification service.",
368
+ PROFILE_UNAVAILABLE: "The witness does not offer the email verification profile.",
369
+ CODE_REJECTED: "Verification code was not accepted.",
370
+ CODE_ATTEMPTS_EXHAUSTED: "Too many wrong codes. Request a new code.",
371
+ SIGNATURE_REJECTED: "Holder signature was rejected.",
372
+ CREDENTIAL_INVALID: "Issued credential failed verification.",
373
+ SESSION_EXPIRED: "Email verification request expired. Request a new code.",
374
+ SESSION_INVALID: "Email verification request is no longer valid. Request a new code.",
375
+ CANCELED: "Email verification was canceled.",
376
+ FAILED: "Email verification failed."
377
+ };
378
+ function rateLimitMessage(retryAfterMs) {
379
+ const head = "Too many codes were requested for this address.";
380
+ if (retryAfterMs === void 0 || !Number.isFinite(retryAfterMs) || retryAfterMs <= 0) return `${head} Try again in about an hour.`;
381
+ const minutes = Math.max(1, Math.ceil(retryAfterMs / 6e4));
382
+ return `${head} Try again in ${minutes} ${minutes === 1 ? "minute" : "minutes"}.`;
383
+ }
384
+ var EmailProofError = class extends Error {
385
+ constructor(step, code, retryAfterMs, recoverable = RECOVERABILITY[code]) {
386
+ super(code === "RATE_LIMITED" ? rateLimitMessage(retryAfterMs) : MESSAGES[code]);
387
+ this.name = "EmailProofError";
388
+ this.step = step;
389
+ this.code = code;
390
+ this.retryAfterMs = retryAfterMs;
391
+ this.recoverable = recoverable;
392
+ }
393
+ };
394
+ function createEd25519HolderSigner(did, sign) {
395
+ const fragment = did.indexOf("#");
396
+ const bare = fragment === -1 ? did : did.slice(0, fragment);
397
+ if (fragment !== -1 && did.slice(fragment + 1) !== bare.slice(HOLDER_DID_PREFIX.length)) throw new EmailProofError("request", "HOLDER_INVALID");
398
+ if (!HOLDER_DID_PATTERN.test(bare)) throw new EmailProofError("request", "HOLDER_INVALID");
399
+ return { did: bare, alg: "EdDSA", kid: holderKid(bare), sign };
400
+ }
401
+ function restoreEmailProofPending(fields) {
402
+ const shown = {
403
+ type: fields.type,
404
+ version: fields.version,
405
+ profile: fields.profile,
406
+ address: fields.address,
407
+ requestId: fields.requestId,
408
+ holderDid: fields.holderDid,
409
+ descriptorDigest: fields.descriptorDigest,
410
+ requirementDigest: fields.requirementDigest,
411
+ openerOrigin: fields.openerOrigin,
412
+ createdAt: fields.createdAt,
413
+ expiresAt: fields.expiresAt
414
+ };
415
+ const pending = { ...shown };
416
+ hide(pending, "verifier", fields.verifier);
417
+ hide(pending, "toJSON", () => ({ ...shown }));
418
+ hide(pending, INSPECT, () => ({ ...shown, verifier: "[redacted]" }));
419
+ return Object.freeze(pending);
420
+ }
421
+ function hide(target, key, value) {
422
+ Object.defineProperty(target, key, { value, enumerable: false, writable: false, configurable: false });
423
+ }
424
+ function pendingDeadlineMs(pending) {
425
+ return Math.min(Date.parse(pending.expiresAt), Date.parse(pending.createdAt) + EMAIL_PROOF_PENDING_MAX_MS);
426
+ }
427
+ function createMemoryEmailProofPendingStore() {
428
+ const byRequest = /* @__PURE__ */ new Map();
429
+ const prune = (now) => {
430
+ for (const [requestId, pending] of byRequest) if (pendingDeadlineMs(pending) <= now.getTime()) byRequest.delete(requestId);
431
+ };
432
+ return {
433
+ async findLive(address, now) {
434
+ prune(now);
435
+ let newest;
436
+ for (const pending of byRequest.values()) {
437
+ if (pending.address === address && (!newest || Date.parse(pending.createdAt) > Date.parse(newest.createdAt))) newest = pending;
438
+ }
439
+ return newest;
440
+ },
441
+ async put(pending) {
442
+ byRequest.set(pending.requestId, pending);
443
+ },
444
+ async delete(requestId) {
445
+ byRequest.delete(requestId);
446
+ },
447
+ async get(requestId, now) {
448
+ prune(now);
449
+ return byRequest.get(requestId);
450
+ }
451
+ };
452
+ }
453
+ var CodeRequested = class extends Error {
454
+ };
455
+ var pauseForCode = async () => {
456
+ throw new CodeRequested("code requested");
457
+ };
458
+ async function requestEmailProofCode(input) {
459
+ const profile = input.profile ?? DEFAULT_EMAIL_PROOF_PROFILE;
460
+ profilePin(profile, "request");
461
+ const address = normalizeAddress(input.address);
462
+ assertHolder(input.holder, "request");
463
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
464
+ const signal = input.signal ?? AbortSignal.timeout(STEP_TIMEOUT_MS);
465
+ const requirement = emailRequirement(address, profile);
466
+ const requirementDigest = await (0, import_sdk_core3.credentialRequirementDigest)(requirement);
467
+ const descriptor = await witnessDescriptor(profile, requirement, { fetch: input.fetch, signal }, "request");
468
+ const descriptorDigest = await (0, import_sdk_core3.canonicalDigest)(descriptor);
469
+ const verifier = randomVerifier();
470
+ const transport = new OpenCredentialsHttpTransport(descriptor, input.fetch, { honorServerRecoverable: true });
471
+ const createdAtMs = now().getTime();
472
+ let created;
473
+ try {
474
+ created = await transport.create({
475
+ descriptor,
476
+ descriptorDigest,
477
+ requirement,
478
+ requirementDigest,
479
+ holderDid: input.holder.did,
480
+ openerOrigin: OPENER_ORIGIN,
481
+ completionVerifierChallenge: await (0, import_sdk_core3.sha256Base64Url)(verifier),
482
+ signal
483
+ });
484
+ } catch (error) {
485
+ throw mapError(error, "request", signal);
486
+ }
487
+ const serverExpiry = Date.parse(created.expiresAt);
488
+ if (!Number.isFinite(serverExpiry)) throw new EmailProofError("request", "FAILED");
489
+ const pending = restoreEmailProofPending({
490
+ type: "TinyCloudEmailProofPending",
491
+ version: 1,
492
+ profile,
493
+ address,
494
+ requestId: created.requestId,
495
+ verifier,
496
+ holderDid: input.holder.did,
497
+ descriptorDigest,
498
+ requirementDigest,
499
+ openerOrigin: OPENER_ORIGIN,
500
+ createdAt: new Date(createdAtMs).toISOString(),
501
+ expiresAt: new Date(Math.min(serverExpiry, createdAtMs + EMAIL_PROOF_PENDING_MAX_MS)).toISOString()
502
+ });
503
+ try {
504
+ await interpretCredentialFlow({
505
+ descriptor,
506
+ requirement,
507
+ requestId: created.requestId,
508
+ verifier,
509
+ holderDid: input.holder.did,
510
+ descriptorDigest,
511
+ requirementDigest,
512
+ openerOrigin: OPENER_ORIGIN,
513
+ transport,
514
+ signing: holderSigning(input.holder),
515
+ handlers: { mailbox_otp: pauseForCode },
516
+ signal
517
+ });
518
+ } catch (error) {
519
+ if (error instanceof CodeRequested) return pending;
520
+ throw mapError(error, "request", signal);
521
+ }
522
+ throw new EmailProofError("request", "FAILED");
523
+ }
524
+ async function completeEmailProof(input) {
525
+ return completeWithIssuerKeys(input, EMAIL_PROOF_ISSUER_KEYS);
526
+ }
527
+ async function completeWithIssuerKeys(input, issuerKeys) {
528
+ const { pending } = input;
529
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
530
+ if (!isPending(pending)) throw new EmailProofError("session", "SESSION_INVALID");
531
+ if (pendingDeadlineMs(pending) <= now().getTime()) throw new EmailProofError("session", "SESSION_EXPIRED");
532
+ if (!/^\d{8}$/.test(input.code)) throw new EmailProofError("code", "CODE_INVALID");
533
+ assertHolder(input.holder, "code");
534
+ if (input.holder.did !== pending.holderDid) throw new EmailProofError("session", "SESSION_INVALID");
535
+ const signal = input.signal ?? AbortSignal.timeout(STEP_TIMEOUT_MS);
536
+ const requirement = emailRequirement(pending.address, pending.profile);
537
+ if (await (0, import_sdk_core3.credentialRequirementDigest)(requirement) !== pending.requirementDigest) throw new EmailProofError("session", "SESSION_INVALID");
538
+ const descriptor = await witnessDescriptor(pending.profile, requirement, { fetch: input.fetch, signal }, "session");
539
+ const descriptorDigest = await (0, import_sdk_core3.canonicalDigest)(descriptor);
540
+ if (descriptorDigest !== pending.descriptorDigest) throw new EmailProofError("session", "SESSION_INVALID");
541
+ const transport = new OpenCredentialsHttpTransport(descriptor, input.fetch, { honorServerRecoverable: true });
542
+ try {
543
+ const state = await transport.state(pending.requestId, pending.verifier, signal);
544
+ if (state.nextStep?.constraints.challengeRequired === true) throw new EmailProofError("session", "SESSION_INVALID");
545
+ await interpretCredentialFlow({
546
+ descriptor,
547
+ requirement,
548
+ requestId: pending.requestId,
549
+ verifier: pending.verifier,
550
+ holderDid: pending.holderDid,
551
+ descriptorDigest,
552
+ requirementDigest: pending.requirementDigest,
553
+ openerOrigin: pending.openerOrigin,
554
+ transport,
555
+ signing: holderSigning(input.holder),
556
+ handlers: { mailbox_otp: async () => ({ otp: input.code }) },
557
+ signal
558
+ });
559
+ } catch (error) {
560
+ throw mapError(error, "code", signal);
561
+ }
562
+ try {
563
+ const envelope = await transport.result(pending.requestId, pending.verifier, signal);
564
+ const verified = await (0, import_sdk_core3.verifyIssuedCredential)({
565
+ envelope,
566
+ descriptor,
567
+ descriptorDigest,
568
+ requirement,
569
+ holderDid: pending.holderDid,
570
+ issuerMetadata: pinnedIssuerMetadata(issuerKeys),
571
+ now: now(),
572
+ checkStatus: (status, statusSignal) => transport.checkStatus(status, statusSignal),
573
+ signal
574
+ });
575
+ return { envelope, verified, credentialId: verified.credentialId, issuedAt: verified.issuedAt, expiresAt: verified.expiresAt };
576
+ } catch (error) {
577
+ throw mapError(error, "credential", signal);
578
+ }
579
+ }
580
+ async function acquireEmailProof(input) {
581
+ return acquireWithIssuerKeys(input, EMAIL_PROOF_ISSUER_KEYS);
582
+ }
583
+ async function acquireWithIssuerKeys(input, issuerKeys) {
584
+ const address = normalizeAddress(input.address);
585
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
586
+ const store = input.pendingStore;
587
+ let pending = store ? await store.findLive(address, now()) : void 0;
588
+ if (!pending) {
589
+ pending = await requestEmailProofCode({ ...input, address });
590
+ await store?.put(pending);
591
+ }
592
+ const shown = restoreEmailProofPending(pending);
593
+ if (!input.getCode) return { status: "code-requested", pending: shown };
594
+ const code = await input.getCode({ pending: shown });
595
+ try {
596
+ const result = await completeWithIssuerKeys({ ...input, pending, code }, issuerKeys);
597
+ const warnings = [];
598
+ if (store) {
599
+ try {
600
+ await store.delete(pending.requestId);
601
+ } catch {
602
+ warnings.push("The pending request could not be deleted after success; it expires at its deadline.");
603
+ }
604
+ }
605
+ return { status: "issued", result, warnings };
606
+ } catch (error) {
607
+ if (!(error instanceof EmailProofError && error.recoverable)) await store?.delete(pending.requestId).catch(() => void 0);
608
+ throw error;
609
+ }
610
+ }
611
+ function normalizeAddress(address) {
612
+ const value = address.trim();
613
+ if (value.length > 254 || !EMAIL_PATTERN.test(value)) throw new EmailProofError("request", "ADDRESS_INVALID");
614
+ return value;
615
+ }
616
+ function holderKid(did) {
617
+ return `${did}#${did.slice(HOLDER_DID_PREFIX.length)}`;
618
+ }
619
+ function assertHolder(holder, step) {
620
+ if (!HOLDER_DID_PATTERN.test(holder.did) || holder.alg !== "EdDSA" || holder.kid !== holderKid(holder.did)) throw new EmailProofError(step, "HOLDER_INVALID");
621
+ }
622
+ function profilePin(profile, step) {
623
+ const entry = Object.prototype.hasOwnProperty.call(PROFILE_PINS, profile) ? PROFILE_PINS[profile] : void 0;
624
+ if (!entry || !entry.enabled) throw new EmailProofError(step, "PROFILE_UNAVAILABLE");
625
+ return entry;
626
+ }
627
+ function emailRequirement(address, profile) {
628
+ return (0, import_sdk_core3.createEmailCredentialRequirement)({ email: address, profile: { id: profile, version: 1 }, credentialType: EMAIL_PROOF_CREDENTIAL_TYPE });
629
+ }
630
+ function emailProofRequirement(address) {
631
+ return emailRequirement(normalizeAddress(address), DEFAULT_EMAIL_PROOF_PROFILE);
632
+ }
633
+ async function witnessDescriptor(profile, requirement, options, step) {
634
+ const pin = profilePin(profile, step);
635
+ let descriptor;
636
+ try {
637
+ descriptor = await discoverCatalogDescriptor(requirement, DISCOVERY_URL, { fetch: options.fetch, signal: options.signal });
638
+ } catch (error) {
639
+ throw discoveryFailure(error, options.signal, step);
640
+ }
641
+ if (descriptor.issuer.origin !== EMAIL_PROOF_PINS.witnessOrigin || descriptor.issuer.did !== EMAIL_PROOF_PINS.issuerDid || !descriptor.steps.some((candidate) => candidate.type === pin.requiredStep)) {
642
+ throw new EmailProofError(step, "PROFILE_UNAVAILABLE");
643
+ }
644
+ if (await (0, import_sdk_core3.canonicalDigest)(descriptor) !== pin.descriptorDigest) throw new EmailProofError(step, "PROFILE_UNAVAILABLE");
645
+ return descriptor;
646
+ }
647
+ async function discoverCatalogDescriptor(requirement, discoveryUrl, options = {}) {
648
+ let url;
649
+ try {
650
+ url = new URL(discoveryUrl);
651
+ } catch {
652
+ throw new import_sdk_core3.CredentialError("UNSUPPORTED_PROFILE", "Discovery URL is invalid");
653
+ }
654
+ if (url.protocol !== "https:" || !["/.well-known/opencredentials", "/v1/credential-types"].includes(url.pathname) || url.search || url.hash) throw new import_sdk_core3.CredentialError("UNSUPPORTED_PROFILE", "Discovery URL is not an allowed catalog endpoint");
655
+ const init = { credentials: "omit", cache: "no-store", redirect: "error", referrerPolicy: "no-referrer", signal: options.signal };
656
+ let response;
657
+ try {
658
+ response = await (options.fetch ?? globalThis.fetch.bind(globalThis))(url, init);
659
+ } catch (cause) {
660
+ throw new import_sdk_core3.CredentialError("OFFLINE", "Credential discovery is unavailable", { cause });
661
+ }
662
+ if (!response.ok) throw new import_sdk_core3.CredentialError(response.status === 503 ? "ISSUER_UNREADY" : "OFFLINE", "Credential discovery is unavailable");
663
+ const catalog = await response.json();
664
+ if (catalog.type !== "tinycloud.credentials/catalog/v1" || catalog.protocol !== "tinycloud.credentials/acquisition/v1" || catalog.catalogVersion !== 1 || !Array.isArray(catalog.profiles)) throw new import_sdk_core3.CredentialError("DESCRIPTOR_INVALID", "Credential catalog is invalid");
665
+ for (const entryValue of catalog.profiles) {
666
+ if (typeof entryValue !== "object" || entryValue === null || Array.isArray(entryValue)) continue;
667
+ const entry = entryValue;
668
+ if (entry.supported !== true || entry.enabled !== true || entry.readiness !== "ready" && entry.readiness !== "degraded") continue;
669
+ try {
670
+ const candidate = (0, import_sdk_core3.validateCredentialFlowDescriptor)(entry.descriptor);
671
+ if ((0, import_sdk_core3.descriptorSatisfiesRequirement)(candidate, requirement) && entry.descriptorDigest === await (0, import_sdk_core3.canonicalDigest)(candidate)) return candidate;
672
+ } catch {
673
+ }
674
+ }
675
+ throw new import_sdk_core3.CredentialError("UNSUPPORTED_PROFILE", "No available credential profile satisfies the requirement");
676
+ }
677
+ function discoveryFailure(error, signal, step) {
678
+ if (signal.aborted && signal.reason?.name !== "TimeoutError") return new EmailProofError(step, "CANCELED");
679
+ return mapError(error, step, signal);
680
+ }
681
+ function pinnedIssuerMetadata(keys) {
682
+ return { type: "OpenCredentialsIssuerMetadata", version: 1, origin: EMAIL_PROOF_PINS.witnessOrigin, issuerDid: EMAIL_PROOF_PINS.issuerDid, keys, cache: { maxAgeSeconds: 3600, etag: "pinned" } };
683
+ }
684
+ function holderSigning(holder) {
685
+ return { autoSign: async (_binding, bytes) => holder.sign(bytes), holderKey: { alg: "EdDSA", kid: holder.kid } };
686
+ }
687
+ function randomVerifier() {
688
+ return (0, import_sdk_core3.encodeBase64Url)(crypto.getRandomValues(new Uint8Array(32)));
689
+ }
690
+ function isPending(value) {
691
+ if (typeof value !== "object" || value === null) return false;
692
+ const pending = value;
693
+ return pending.type === "TinyCloudEmailProofPending" && pending.version === 1 && pending.openerOrigin === OPENER_ORIGIN && [pending.profile, pending.address, pending.requestId, pending.verifier, pending.holderDid, pending.descriptorDigest, pending.requirementDigest, pending.openerOrigin, pending.createdAt, pending.expiresAt].every((field) => typeof field === "string") && Number.isFinite(Date.parse(pending.createdAt)) && Number.isFinite(Date.parse(pending.expiresAt));
694
+ }
695
+ function mapError(value, step, signal) {
696
+ if (value instanceof EmailProofError) return value;
697
+ if (value instanceof DOMException && value.name === "TimeoutError") return new EmailProofError(step, "NETWORK");
698
+ const error = (0, import_sdk_core3.credentialError)(value);
699
+ const details = error.details;
700
+ switch (error.code) {
701
+ case "ISSUER_UNREADY":
702
+ return details.state === "rate_limited" ? new EmailProofError(step, "RATE_LIMITED", details.retryAfterMs, error.recoverable) : new EmailProofError(step, "ISSUER_UNAVAILABLE", void 0, error.recoverable);
703
+ case "OFFLINE":
704
+ if (signal?.aborted && signal.reason?.name !== "TimeoutError") return new EmailProofError(step, "CANCELED");
705
+ return new EmailProofError(step, "NETWORK");
706
+ case "UNSUPPORTED_PROFILE":
707
+ case "DESCRIPTOR_INVALID":
708
+ return new EmailProofError(step, "PROFILE_UNAVAILABLE");
709
+ case "PROOF_REJECTED":
710
+ return new EmailProofError("code", "CODE_REJECTED", void 0, true);
711
+ case "VERIFICATION_FAILED":
712
+ if (details.state === "proof_attempts_exhausted") return new EmailProofError("code", "CODE_ATTEMPTS_EXHAUSTED");
713
+ if (step === "credential") return new EmailProofError("credential", "CREDENTIAL_INVALID");
714
+ if (step === "code") return new EmailProofError("code", "CODE_REJECTED", void 0, false);
715
+ return new EmailProofError(step, "FAILED");
716
+ case "HOLDER_MISMATCH":
717
+ return new EmailProofError("credential", "CREDENTIAL_INVALID");
718
+ case "REQUEST_EXPIRED":
719
+ return new EmailProofError("session", "SESSION_EXPIRED");
720
+ case "REQUEST_SUBSTITUTED":
721
+ return new EmailProofError("session", "SESSION_INVALID");
722
+ case "SIGNATURE_REJECTED":
723
+ return new EmailProofError("credential", "SIGNATURE_REJECTED", void 0, error.recoverable);
724
+ case "CANCELED":
725
+ return new EmailProofError(step, "CANCELED");
726
+ default:
727
+ return new EmailProofError(step, "FAILED");
728
+ }
729
+ }
730
+ // Annotate the CommonJS export names for ESM import in node:
731
+ 0 && (module.exports = {
732
+ DEFAULT_EMAIL_PROOF_PROFILE,
733
+ EMAIL_PROOF_CREDENTIAL_TYPE,
734
+ EMAIL_PROOF_PENDING_MAX_MS,
735
+ EmailProofError,
736
+ OpenCredentialsHttpTransport,
737
+ acquireEmailProof,
738
+ completeEmailProof,
739
+ createEd25519HolderSigner,
740
+ createMemoryEmailProofPendingStore,
741
+ emailProofRequirement,
742
+ interpretCredentialFlow,
743
+ mailboxSubject,
744
+ requestEmailProofCode
745
+ });
746
+ //# sourceMappingURL=credentials.cjs.map