@registrystack/client 0.0.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,250 @@
1
+ // The package's actual entry point. `index.d.ts` is auto-generated by
2
+ // `napi build --platform` on every build and is what `check:types` diffs
3
+ // byte-for-byte; the hand-written surface below is checked separately, by
4
+ // `__test__/drift.test.js`, against the real module.
5
+ export * from './index'
6
+
7
+ import {
8
+ EvidenceClient as NativeEvidenceClient,
9
+ PreparedEvidenceRequest,
10
+ PreparedEvidenceRequestBatch,
11
+ } from './index'
12
+
13
+ /** The signed response encoding closed before one request is sent. */
14
+ export type EvidenceResponseFormat = 'signed-jws' | 'sd-jwt-vc'
15
+
16
+ /** An opaque, versioned subject-continuity receipt owned by the application. */
17
+ export interface SubjectBindingReceipt {
18
+ readonly schema: 'registry.evidence-subject-binding-receipt/v1'
19
+ readonly [field: string]: unknown
20
+ }
21
+
22
+ /**
23
+ * The hand-written progressive result view refines napi-rs's JSON-shaped
24
+ * generated declarations without pretending it can infer a domain-specific
25
+ * output type. Its discriminants mirror the native result exactly.
26
+ */
27
+ export type ProgressiveSubjectContinuity =
28
+ | { readonly status: 'firstUse'; readonly receipt: SubjectBindingReceipt }
29
+ | { readonly status: 'matched'; readonly receipt: SubjectBindingReceipt }
30
+
31
+ export interface ProgressiveVerifiedResult {
32
+ readonly evidence: unknown
33
+ /** The exchange's trace identifier, `null` when the response carried none. */
34
+ readonly traceId: string | null
35
+ readonly values: Readonly<Record<string, unknown>>
36
+ readonly value: unknown
37
+ readonly subjectContinuity: ProgressiveSubjectContinuity
38
+ }
39
+
40
+ /** A signed-JWS result. The credential member of the native result is `null`. */
41
+ export interface ProgressiveVerifiedAssertion extends ProgressiveVerifiedResult {
42
+ readonly assertion: Buffer
43
+ readonly credential: null
44
+ }
45
+
46
+ /** An SD-JWT VC result. The assertion member of the native result is `null`. */
47
+ export interface ProgressiveVerifiedAudienceScopedCredential extends ProgressiveVerifiedResult {
48
+ readonly assertion: null
49
+ readonly credential: string
50
+ }
51
+
52
+ export type ProgressiveResult =
53
+ | ({ readonly responseFormat: 'signed-jws' } & ProgressiveVerifiedAssertion)
54
+ | ({ readonly responseFormat: 'sd-jwt-vc' } & ProgressiveVerifiedAudienceScopedCredential)
55
+
56
+ export type AudienceScopedRequest = {
57
+ requirement: string
58
+ responseFormat?: EvidenceResponseFormat
59
+ /** One request-origin subject. Node keeps the selector map explicit. */
60
+ selectors?: Readonly<Record<string, string | number | boolean>>
61
+ /** Multiple roles, or a request whose selector field needs a reserved name. */
62
+ subjects?: Readonly<Record<string, Readonly<Record<string, string | number | boolean>>>>
63
+ /** A prior receipt the application chose to retain. Nothing is persisted by this package. */
64
+ bindingReceipt?: SubjectBindingReceipt
65
+ }
66
+
67
+ /**
68
+ * The pinned verification key set, as a JWKS document. It is a trust input:
69
+ * the client never fetches one to verify a response it already holds.
70
+ */
71
+ export interface TrustedJwks {
72
+ keys: ReadonlyArray<Readonly<Record<string, unknown>>>
73
+ readonly [field: string]: unknown
74
+ }
75
+
76
+ /**
77
+ * A private-key-JWT client-credentials exchange the client performs for
78
+ * itself. `clientKey` holds the private half; it stays in this process and
79
+ * reaches nothing but the token endpoint.
80
+ */
81
+ export interface PrivateKeyJwtConfig {
82
+ tokenEndpoint: string
83
+ clientId: string
84
+ clientKey: Readonly<Record<string, unknown>>
85
+ audience?: string
86
+ assertionLifetimeSeconds?: number
87
+ refreshMarginSeconds?: number
88
+ requestTimeoutMs?: number
89
+ connectTimeoutMs?: number
90
+ userAgent?: string
91
+ /** Additional trust roots for the token endpoint, as a PEM bundle. */
92
+ trustedRootCertificates?: string
93
+ }
94
+
95
+ /**
96
+ * Exactly one credential source. A configuration naming both is refused, so a
97
+ * merge of two authentication settings cannot silently pick one.
98
+ */
99
+ export type EvidenceTokenConfig =
100
+ | { static: string }
101
+ | { privateKeyJwt: PrivateKeyJwtConfig }
102
+
103
+ /** The configuration `new EvidenceClient(...)` reads. */
104
+ export interface EvidenceClientConfig {
105
+ baseUrl: string
106
+ trustedJwks: TrustedJwks
107
+ revokedKeyIds: ReadonlyArray<string>
108
+ token: EvidenceTokenConfig
109
+ requestTimeoutMs?: number
110
+ connectTimeoutMs?: number
111
+ userAgent?: string
112
+ /** Additional trust roots for the deployment, as a PEM bundle. */
113
+ trustedRootCertificates?: string
114
+ maxResponseBytes?: number
115
+ maxMetadataBytes?: number
116
+ }
117
+
118
+ /**
119
+ * The client `client.js` exports, which subclasses the generated one so every
120
+ * mapped failure normalizes to an `EvidenceClientError`. Declaring it as a
121
+ * subclass, rather than as an interface of the same name, is what keeps the
122
+ * generated construct signature reachable: a same-named interface would
123
+ * shadow the re-exported class and leave `new EvidenceClient(...)` untyped.
124
+ *
125
+ * The members below only narrow generated declarations that cross as
126
+ * JSON-shaped `any`; every other member is inherited unchanged.
127
+ */
128
+ export declare class EvidenceClient extends NativeEvidenceClient {
129
+ constructor(config: EvidenceClientConfig)
130
+ prepare(spec: EvidenceRequestSpec): PreparedEvidenceRequest
131
+ prepareBatch(spec: EvidenceRequestBatchSpec): PreparedEvidenceRequestBatch
132
+ request(request: AudienceScopedRequest): Promise<ProgressiveResult>
133
+ /**
134
+ * Read an application-owned profile. `privateKeyJwk` is optional and is
135
+ * intended for an in-memory secret-manager result; it is never retained by
136
+ * the JavaScript wrapper.
137
+ */
138
+ static fromProfile(path: string, privateKeyJwk?: Readonly<Record<string, unknown>>): EvidenceClient
139
+ }
140
+
141
+ /**
142
+ * A holder public key a request may present, as a public JWK.
143
+ *
144
+ * Public material only. There is no member here for a private key half, and
145
+ * a key carrying one (`d`, or any other private JWK member) is refused with
146
+ * that stated as the reason: the private half stays with whoever holds it,
147
+ * and this package never asks for it, holds it, or sends it.
148
+ */
149
+ export interface HolderPublicKey {
150
+ kty: 'EC'
151
+ crv: 'P-256'
152
+ /** The x coordinate, unpadded base64url. */
153
+ x: string
154
+ /** The y coordinate, unpadded base64url. */
155
+ y: string
156
+ alg?: 'ES256' | null
157
+ kid?: string | null
158
+ }
159
+
160
+ /** The request-spec input accepted by `EvidenceClient.prepare`. */
161
+ export interface EvidenceRequestSpec {
162
+ responseFormat: EvidenceResponseFormat
163
+ requirement: string
164
+ purpose: string
165
+ audience: string
166
+ evidenceType: string
167
+ issuedBy: string
168
+ providedBy: string
169
+ configurationRevision: string
170
+ expectedAssuranceProfile: unknown
171
+ subjects: ReadonlyArray<{
172
+ role: string
173
+ selectorProfile: string
174
+ selectorValues?: Readonly<Record<string, string | number | boolean>> | null
175
+ }>
176
+ /**
177
+ * Holder public keys this request presents, in the order the caller wants
178
+ * them answered. Omit it (or pass an empty array) to present none, which is
179
+ * the request this package has always sent.
180
+ *
181
+ * A request presenting several keys can be answered with one credential per
182
+ * key, in that same order; see `SdJwtVcBatchResponse`.
183
+ */
184
+ holderKeys?: ReadonlyArray<HolderPublicKey>
185
+ expectedOutputs: ReadonlyArray<Readonly<Record<string, unknown>>>
186
+ maximumAssertionLifetimeSeconds: number
187
+ clockSkewSeconds: number
188
+ subjectExpectations:
189
+ | 'acceptFirstUse'
190
+ | { pinned: ReadonlyArray<{ role: string; binding: string }> }
191
+ }
192
+
193
+ /** One positional item in a multi-subject Evidence request batch. */
194
+ export interface EvidenceRequestBatchItemSpec {
195
+ subjects: ReadonlyArray<{
196
+ role: string
197
+ selectorProfile: string
198
+ selectorValues?: Readonly<Record<string, string | number | boolean>> | null
199
+ }>
200
+ subjectExpectations:
201
+ | 'acceptFirstUse'
202
+ | { pinned: ReadonlyArray<{ role: string; binding: string }> }
203
+ }
204
+
205
+ /**
206
+ * The common request inputs and ordered item inputs accepted by
207
+ * `EvidenceClient.prepareBatch`.
208
+ */
209
+ export interface EvidenceRequestBatchSpec {
210
+ requirement: string
211
+ purpose: string
212
+ audience: string
213
+ evidenceType: string
214
+ issuedBy: string
215
+ providedBy: string
216
+ configurationRevision: string
217
+ expectedAssuranceProfile: unknown
218
+ expectedOutputs: ReadonlyArray<Readonly<Record<string, unknown>>>
219
+ maximumAssertionLifetimeSeconds: number
220
+ clockSkewSeconds: number
221
+ items: ReadonlyArray<EvidenceRequestBatchItemSpec>
222
+ }
223
+
224
+ /**
225
+ * The stable eight-kind error envelope every mapped Evidence Node failure
226
+ * carries, as properties on a thrown `Error` (see `src/lib.rs`'s
227
+ * `to_napi_error` and `src/convert.rs`'s `map_client_error` for the Rust side
228
+ * of this contract).
229
+ *
230
+ * `kind` is always present. The rest are present only when the underlying
231
+ * failure carries them:
232
+ * - `status`: `denied`, `protocol`, and any `token` failure whose `tokenKind`
233
+ * is `protocol`
234
+ * - `code`: `denied`, `protocol` (optional), `verification`, and any `token`
235
+ * failure whose `tokenKind` is `refused`
236
+ * - `traceId`: `denied`, `not_available`, `protocol` (all optional)
237
+ * - `retryAfterSeconds`: `denied`, `protocol` (both optional)
238
+ * - `transportKind`: `transport`, and any `token` failure whose `tokenKind`
239
+ * is `transport`
240
+ * - `tokenKind`: every `token` failure
241
+ */
242
+ export declare class EvidenceClientError extends Error {
243
+ readonly kind: string
244
+ readonly status?: number
245
+ readonly code?: string
246
+ readonly traceId?: string
247
+ readonly retryAfterSeconds?: number
248
+ readonly transportKind?: string
249
+ readonly tokenKind?: string
250
+ }
@@ -0,0 +1,239 @@
1
+ 'use strict';
2
+
3
+ // The package's actual entry point. `index.js`/`index.d.ts` are regenerated
4
+ // by `napi build --platform` on every build (see `package.json`'s `build`
5
+ // and `build:debug` scripts) and stay untouched here; this file sits on top
6
+ // of them so a hand edit never gets silently overwritten by the next build.
7
+
8
+ const native = require('./index');
9
+
10
+ /**
11
+ * The stable eight-kind error envelope every mapped Evidence Node failure
12
+ * carries (see `crates/registry-evidence-client-node/src/convert.rs`'s
13
+ * `map_client_error`), as properties on a thrown `Error` rather than as JSON
14
+ * text a caller has to `JSON.parse` out of `.message`.
15
+ *
16
+ * `kind` is always present; `status`, `code`, `traceId`,
17
+ * `retryAfterSeconds`, `transportKind`, and `tokenKind` are present only when
18
+ * the underlying failure carries them.
19
+ */
20
+ class EvidenceClientError extends Error {
21
+ constructor(envelope) {
22
+ super(envelope.message);
23
+ this.name = 'EvidenceClientError';
24
+ this.kind = envelope.kind;
25
+ for (const field of [
26
+ 'status',
27
+ 'code',
28
+ 'traceId',
29
+ 'retryAfterSeconds',
30
+ 'transportKind',
31
+ 'tokenKind',
32
+ ]) {
33
+ if (envelope[field] !== undefined) {
34
+ this[field] = envelope[field];
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ /**
41
+ * The native layer throws every mapped Evidence failure as an ordinary
42
+ * `Error` whose `message` is the JSON envelope described above
43
+ * (`src/lib.rs`'s `to_napi_error`). Every other native failure (a
44
+ * serialization defect, a caught panic, napi's own argument-type checking)
45
+ * throws a plain, non-JSON reason instead, and is left exactly as thrown:
46
+ * only a recognized envelope becomes an `EvidenceClientError`, so a caller
47
+ * cannot mistake "some other native failure" for one of the eight stable
48
+ * kinds.
49
+ */
50
+ function normalize(error) {
51
+ if (!(error instanceof Error) || typeof error.message !== 'string') {
52
+ return error;
53
+ }
54
+ let envelope;
55
+ try {
56
+ envelope = JSON.parse(error.message);
57
+ } catch {
58
+ return error;
59
+ }
60
+ if (envelope === null || typeof envelope !== 'object' || typeof envelope.kind !== 'string') {
61
+ return error;
62
+ }
63
+ return new EvidenceClientError(envelope);
64
+ }
65
+
66
+ function wrapSync(prototype, name) {
67
+ const original = prototype[name];
68
+ prototype[name] = function (...args) {
69
+ try {
70
+ return original.apply(this, args);
71
+ } catch (error) {
72
+ throw normalize(error);
73
+ }
74
+ };
75
+ }
76
+
77
+ function wrapAsync(prototype, name) {
78
+ const original = prototype[name];
79
+ prototype[name] = function (...args) {
80
+ return original.apply(this, args).catch((error) => {
81
+ throw normalize(error);
82
+ });
83
+ };
84
+ }
85
+
86
+ // The public Node API exposes the opaque native result directly, with the
87
+ // encoding tag next to its artifact, so the one-call tutorial can read
88
+ // `result.value`. Its getters stay native: notably, only reading an ambiguous
89
+ // `value` fails.
90
+ function wrapProgressiveRequest(prototype) {
91
+ const original = prototype.request;
92
+ prototype.request = function (...args) {
93
+ try {
94
+ return original.apply(this, args).then(
95
+ (result) => result,
96
+ (error) => {
97
+ throw normalize(error);
98
+ },
99
+ );
100
+ } catch (error) {
101
+ throw normalize(error);
102
+ }
103
+ };
104
+ }
105
+
106
+ function wrapGetter(prototype, name) {
107
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
108
+ const originalGet = descriptor.get;
109
+ Object.defineProperty(prototype, name, {
110
+ ...descriptor,
111
+ get() {
112
+ try {
113
+ return originalGet.call(this);
114
+ } catch (error) {
115
+ throw normalize(error);
116
+ }
117
+ },
118
+ });
119
+ }
120
+
121
+ // Patch the native prototypes directly, in place, rather than wrapping
122
+ // instances in a delegating object or a `Proxy`: only the throw/reject path
123
+ // is touched here, so every argument and return value crossing these methods
124
+ // keeps its exact native identity. This matters concretely for `send`'s
125
+ // single-send guard: it depends on the `PreparedEvidenceRequest` object
126
+ // handed back to a later `send`/`verify` call being the very same native
127
+ // object `prepare` returned, not a copy or a wrapper around it (see
128
+ // `__test__/happy-path.test.js`'s single-send-guard test, which asserts this
129
+ // identity directly).
130
+ wrapSync(native.EvidenceClient.prototype, 'prepare');
131
+ wrapSync(native.EvidenceClient.prototype, 'prepareBatch');
132
+ wrapAsync(native.EvidenceClient.prototype, 'discover');
133
+ wrapAsync(native.EvidenceClient.prototype, 'fetchJwks');
134
+ wrapAsync(native.EvidenceClient.prototype, 'send');
135
+ wrapAsync(native.EvidenceClient.prototype, 'sendBatch');
136
+ wrapSync(native.EvidenceClient.prototype, 'verify');
137
+ wrapSync(native.EvidenceClient.prototype, 'verifyBatch');
138
+ wrapAsync(native.EvidenceClient.prototype, 'requestAndVerify');
139
+ wrapAsync(native.EvidenceClient.prototype, 'requestAndVerifyBatch');
140
+ wrapProgressiveRequest(native.EvidenceClient.prototype);
141
+ wrapAsync(native.EvidenceClient.prototype, 'refreshMetadata');
142
+ wrapSync(native.EvidenceClient.prototype, 'verifyAsOf');
143
+ wrapSync(native.EvidenceClient.prototype, 'verifyBatchAsOf');
144
+
145
+ wrapGetter(native.PreparedEvidenceRequest.prototype, 'requestNonce');
146
+ wrapGetter(native.PreparedEvidenceRequest.prototype, 'policyDocument');
147
+ wrapGetter(native.PreparedEvidenceRequest.prototype, 'subjectExpectations');
148
+
149
+ wrapGetter(native.PreparedEvidenceRequestBatch.prototype, 'requestNonces');
150
+ wrapGetter(native.PreparedEvidenceRequestBatch.prototype, 'policyDocuments');
151
+ wrapGetter(native.PreparedEvidenceRequestBatch.prototype, 'subjectExpectations');
152
+ wrapGetter(native.PreparedEvidenceRequestBatch.prototype, 'count');
153
+
154
+ wrapGetter(native.RawEvidenceResponse.prototype, 'body');
155
+ wrapGetter(native.RawEvidenceResponse.prototype, 'traceId');
156
+
157
+ wrapGetter(native.RawEvidenceRequestBatchResponse.prototype, 'body');
158
+ wrapGetter(native.RawEvidenceRequestBatchResponse.prototype, 'traceId');
159
+
160
+ for (const name of [
161
+ 'responseFormat',
162
+ 'evidence',
163
+ 'traceId',
164
+ 'assertion',
165
+ 'credential',
166
+ 'values',
167
+ 'value',
168
+ 'subjectContinuity',
169
+ ]) {
170
+ wrapGetter(native.AudienceScopedResult.prototype, name);
171
+ }
172
+
173
+ wrapSync(native.SdJwtVcBatchResponse.prototype, 'credentialForHolderKey');
174
+ wrapGetter(native.SdJwtVcBatchResponse.prototype, 'credentials');
175
+ wrapGetter(native.SdJwtVcBatchResponse.prototype, 'count');
176
+
177
+ /**
178
+ * The two classes this module wraps rather than patches in place: a
179
+ * constructor's own throw happens before any instance exists, so there is no
180
+ * prototype method to patch ahead of time the way there is for every other
181
+ * method above.
182
+ *
183
+ * Subclassing is safe here in a way it would not be for
184
+ * `PreparedEvidenceRequest`: nothing hands an `EvidenceClient` or an
185
+ * `SdJwtVcBatchResponse` back into a later native call for an identity check
186
+ * to depend on, and every prototype member above is already patched on the
187
+ * native prototype, which these subclasses inherit unchanged, so
188
+ * `instanceof native.EvidenceClient` still holds for their instances.
189
+ */
190
+ class EvidenceClient extends native.EvidenceClient {
191
+ constructor(config) {
192
+ try {
193
+ super(config);
194
+ } catch (error) {
195
+ throw normalize(error);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Construct the progressive client from an application-owned profile.
201
+ *
202
+ * The native implementation reads the profile, resolves any secret
203
+ * reference, discovers metadata, and owns every trust decision. Keeping
204
+ * that work below this wrapper is intentional: JavaScript never receives a
205
+ * key set or a subject-continuity store to accidentally retain.
206
+ */
207
+ static fromProfile(path, privateKeyJwk) {
208
+ try {
209
+ const client = privateKeyJwk === undefined
210
+ ? native.EvidenceClient.fromProfile(path)
211
+ : native.EvidenceClient.fromProfile(path, privateKeyJwk);
212
+ Object.setPrototypeOf(client, this.prototype);
213
+ return client;
214
+ } catch (error) {
215
+ throw normalize(error);
216
+ }
217
+ }
218
+ }
219
+
220
+ class SdJwtVcBatchResponse extends native.SdJwtVcBatchResponse {
221
+ constructor(body) {
222
+ try {
223
+ super(body);
224
+ } catch (error) {
225
+ throw normalize(error);
226
+ }
227
+ }
228
+ }
229
+
230
+ module.exports = {
231
+ EvidenceClient,
232
+ EvidenceClientError,
233
+ PreparedEvidenceRequest: native.PreparedEvidenceRequest,
234
+ PreparedEvidenceRequestBatch: native.PreparedEvidenceRequestBatch,
235
+ RawEvidenceResponse: native.RawEvidenceResponse,
236
+ RawEvidenceRequestBatchResponse: native.RawEvidenceRequestBatchResponse,
237
+ AudienceScopedResult: native.AudienceScopedResult,
238
+ SdJwtVcBatchResponse,
239
+ };