@registrystack/client 0.0.0 → 0.26.1

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,181 @@
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
+ /** The signed response encoding closed before one request is sent. */
8
+ export type EvidenceResponseFormat = 'signed-jws' | 'sd-jwt-vc'
9
+
10
+ /** An opaque, versioned subject-continuity receipt owned by the application. */
11
+ export interface SubjectBindingReceipt {
12
+ readonly schema: 'registry.evidence-subject-binding-receipt/v1'
13
+ readonly [field: string]: unknown
14
+ }
15
+
16
+ /**
17
+ * The hand-written progressive result view refines napi-rs's JSON-shaped
18
+ * generated declarations without pretending it can infer a domain-specific
19
+ * output type. Its discriminants mirror the native result exactly.
20
+ */
21
+ export type ProgressiveSubjectContinuity =
22
+ | { readonly status: 'firstUse'; readonly receipt: SubjectBindingReceipt }
23
+ | { readonly status: 'matched'; readonly receipt: SubjectBindingReceipt }
24
+
25
+ export interface ProgressiveVerifiedResult {
26
+ readonly evidence: unknown
27
+ readonly traceId?: string
28
+ readonly values: Readonly<Record<string, unknown>>
29
+ readonly value: unknown
30
+ readonly subjectContinuity: ProgressiveSubjectContinuity
31
+ }
32
+
33
+ export interface ProgressiveVerifiedAssertion extends ProgressiveVerifiedResult {
34
+ readonly assertion: Buffer
35
+ }
36
+
37
+ export interface ProgressiveVerifiedAudienceScopedCredential extends ProgressiveVerifiedResult {
38
+ readonly credential: string
39
+ }
40
+
41
+ export type ProgressiveResult =
42
+ | ({ readonly responseFormat: 'signed-jws' } & ProgressiveVerifiedAssertion)
43
+ | ({ readonly responseFormat: 'sd-jwt-vc' } & ProgressiveVerifiedAudienceScopedCredential)
44
+
45
+ export type AudienceScopedRequest = {
46
+ requirement: string
47
+ responseFormat?: EvidenceResponseFormat
48
+ /** One request-origin subject. Node keeps the selector map explicit. */
49
+ selectors?: Readonly<Record<string, string | number | boolean>>
50
+ /** Multiple roles, or a request whose selector field needs a reserved name. */
51
+ subjects?: Readonly<Record<string, Readonly<Record<string, string | number | boolean>>>>
52
+ /** A prior receipt the application chose to retain. Nothing is persisted by this package. */
53
+ bindingReceipt?: SubjectBindingReceipt
54
+ }
55
+
56
+ /** Progressive methods merged into the native `EvidenceClient` declaration. */
57
+ export interface EvidenceClient {
58
+ refreshMetadata(): Promise<void>
59
+ request(request: AudienceScopedRequest): Promise<ProgressiveResult>
60
+ }
61
+
62
+ /** Progressive factories merged into the native `EvidenceClient` value. */
63
+ export namespace EvidenceClient {
64
+ /**
65
+ * Read an application-owned profile. `privateKeyJwk` is optional and is
66
+ * intended for an in-memory secret-manager result; it is never retained by
67
+ * the JavaScript wrapper.
68
+ */
69
+ function fromProfile(path: string, privateKeyJwk?: Readonly<Record<string, unknown>>): EvidenceClient
70
+ }
71
+
72
+ /**
73
+ * A holder public key a request may present, as a public JWK.
74
+ *
75
+ * Public material only. There is no member here for a private key half, and
76
+ * a key carrying one (`d`, or any other private JWK member) is refused with
77
+ * that stated as the reason: the private half stays with whoever holds it,
78
+ * and this package never asks for it, holds it, or sends it.
79
+ */
80
+ export interface HolderPublicKey {
81
+ kty: 'EC'
82
+ crv: 'P-256'
83
+ /** The x coordinate, unpadded base64url. */
84
+ x: string
85
+ /** The y coordinate, unpadded base64url. */
86
+ y: string
87
+ alg?: 'ES256' | null
88
+ kid?: string | null
89
+ }
90
+
91
+ /** The request-spec input accepted by `EvidenceClient.prepare`. */
92
+ export interface EvidenceRequestSpec {
93
+ responseFormat: EvidenceResponseFormat
94
+ requirement: string
95
+ purpose: string
96
+ audience: string
97
+ evidenceType: string
98
+ issuedBy: string
99
+ providedBy: string
100
+ configurationRevision: string
101
+ expectedAssuranceProfile: unknown
102
+ subjects: ReadonlyArray<{
103
+ role: string
104
+ selectorProfile: string
105
+ selectorValues?: Readonly<Record<string, string | number | boolean>> | null
106
+ }>
107
+ /**
108
+ * Holder public keys this request presents, in the order the caller wants
109
+ * them answered. Omit it (or pass an empty array) to present none, which is
110
+ * the request this package has always sent.
111
+ *
112
+ * A request presenting several keys can be answered with one credential per
113
+ * key, in that same order; see `SdJwtVcBatchResponse`.
114
+ */
115
+ holderKeys?: ReadonlyArray<HolderPublicKey>
116
+ expectedOutputs: ReadonlyArray<Readonly<Record<string, unknown>>>
117
+ maximumAssertionLifetimeSeconds: number
118
+ clockSkewSeconds: number
119
+ subjectExpectations:
120
+ | 'acceptFirstUse'
121
+ | { pinned: ReadonlyArray<{ role: string; binding: string }> }
122
+ }
123
+
124
+ /** One positional item in a multi-subject Evidence request batch. */
125
+ export interface EvidenceRequestBatchItemSpec {
126
+ subjects: ReadonlyArray<{
127
+ role: string
128
+ selectorProfile: string
129
+ selectorValues?: Readonly<Record<string, string | number | boolean>> | null
130
+ }>
131
+ subjectExpectations:
132
+ | 'acceptFirstUse'
133
+ | { pinned: ReadonlyArray<{ role: string; binding: string }> }
134
+ }
135
+
136
+ /**
137
+ * The common request inputs and ordered item inputs accepted by
138
+ * `EvidenceClient.prepareBatch`.
139
+ */
140
+ export interface EvidenceRequestBatchSpec {
141
+ requirement: string
142
+ purpose: string
143
+ audience: string
144
+ evidenceType: string
145
+ issuedBy: string
146
+ providedBy: string
147
+ configurationRevision: string
148
+ expectedAssuranceProfile: unknown
149
+ expectedOutputs: ReadonlyArray<Readonly<Record<string, unknown>>>
150
+ maximumAssertionLifetimeSeconds: number
151
+ clockSkewSeconds: number
152
+ items: ReadonlyArray<EvidenceRequestBatchItemSpec>
153
+ }
154
+
155
+ /**
156
+ * The stable eight-kind error envelope every mapped Evidence Node failure
157
+ * carries, as properties on a thrown `Error` (see `src/lib.rs`'s
158
+ * `to_napi_error` and `src/convert.rs`'s `map_client_error` for the Rust side
159
+ * of this contract).
160
+ *
161
+ * `kind` is always present. The rest are present only when the underlying
162
+ * failure carries them:
163
+ * - `status`: `denied`, `protocol`, and any `token` failure whose `tokenKind`
164
+ * is `protocol`
165
+ * - `code`: `denied`, `protocol` (optional), `verification`, and any `token`
166
+ * failure whose `tokenKind` is `refused`
167
+ * - `traceId`: `denied`, `not_available`, `protocol` (all optional)
168
+ * - `retryAfterSeconds`: `denied`, `protocol` (both optional)
169
+ * - `transportKind`: `transport`, and any `token` failure whose `tokenKind`
170
+ * is `transport`
171
+ * - `tokenKind`: every `token` failure
172
+ */
173
+ export declare class EvidenceClientError extends Error {
174
+ readonly kind: string
175
+ readonly status?: number
176
+ readonly code?: string
177
+ readonly traceId?: string
178
+ readonly retryAfterSeconds?: number
179
+ readonly transportKind?: string
180
+ readonly tokenKind?: string
181
+ }
@@ -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
+ };