@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.
package/README.md CHANGED
@@ -1,5 +1,27 @@
1
- # @registrystack/client
1
+ # `@registrystack/client`
2
2
 
3
- This inert `0.0.0` release reserves the package identity for the unified Registry Stack client.
3
+ One versioned Node.js package for the Discovery, Evidence, Relay, and Base
4
+ Registry Engine client APIs in Registry Stack.
4
5
 
5
- Install a real Registry Stack release when version `0.26.1` or later is available. This bootstrap package contains no executable code.
6
+ ```sh
7
+ npm install "@registrystack/client@<version>"
8
+ ```
9
+
10
+ ```js
11
+ const { discovery, evidence, relay, breg } = require('@registrystack/client');
12
+
13
+ const registry = new breg.BaseRegistryClient({
14
+ baseUrl: 'https://registry.example.invalid/',
15
+ });
16
+ ```
17
+
18
+ Each product remains in its own namespace because its routing,
19
+ authentication, errors, and verification rules are different. The package
20
+ uses one platform-specific native dependency containing all four bindings.
21
+ Supported targets are macOS arm64, Linux arm64 with glibc, and Linux x64 with
22
+ glibc. Install the exact client version that matches the deployment.
23
+
24
+ The unified package is published beginning with Registry Stack v0.26.1.
25
+ Existing standalone client packages remain available for earlier versions,
26
+ but the release process does not publish new standalone versions once this
27
+ package is active.
@@ -0,0 +1,231 @@
1
+ export type JsonScalar = string | number | boolean | null
2
+ export type JsonValue = JsonScalar | ReadonlyArray<JsonValue> | { readonly [key: string]: JsonValue }
3
+ export type JsonObject = { readonly [key: string]: JsonValue }
4
+ /** An integer that satisfies `Number.isSafeInteger` and the option's documented bounds. */
5
+ export type SafeInteger = number
6
+
7
+ export interface PrivateJwk {
8
+ readonly kty: string
9
+ readonly kid: string
10
+ readonly alg: string
11
+ readonly [member: string]: JsonValue
12
+ }
13
+
14
+ export interface PrivateKeyJwtConfig {
15
+ tokenEndpoint: string
16
+ clientId: string
17
+ clientKey: PrivateJwk
18
+ audience?: string | null
19
+ assertionLifetimeSeconds?: SafeInteger | null
20
+ refreshMarginSeconds?: SafeInteger | null
21
+ requestTimeoutMilliseconds?: SafeInteger | null
22
+ connectTimeoutMilliseconds?: SafeInteger | null
23
+ userAgent?: string | null
24
+ trustedRootCertificates?: string | null
25
+ }
26
+
27
+ export type BaseRegistryAuthorization =
28
+ | { static: string }
29
+ | { privateKeyJwt: PrivateKeyJwtConfig }
30
+
31
+ export interface BaseRegistryClientConfig {
32
+ baseUrl: string
33
+ authorization?: BaseRegistryAuthorization | null
34
+ requestTimeoutMilliseconds?: SafeInteger | null
35
+ connectTimeoutMilliseconds?: SafeInteger | null
36
+ maxResponseBytes?: SafeInteger | null
37
+ userAgent?: string | null
38
+ trustedRootCertificates?: string | null
39
+ }
40
+
41
+ export type RecordFormat = 'json' | 'json-ld'
42
+
43
+ export interface RecordOptions {
44
+ select?: ReadonlyArray<string> | null
45
+ accessProfile?: string | null
46
+ format?: RecordFormat | null
47
+ }
48
+
49
+ export interface ListOptions extends RecordOptions {
50
+ top?: SafeInteger | null
51
+ filter?: string | null
52
+ orderby?: string | null
53
+ count?: boolean | null
54
+ }
55
+
56
+ export interface ListContinuation {
57
+ route: string
58
+ skiptoken: string
59
+ format: RecordFormat
60
+ accessProfile?: string | null
61
+ registryIdentifier: string
62
+ datasetIdentifier: string
63
+ entityTypeIdentifier: string
64
+ }
65
+
66
+ export interface RegistryRecord {
67
+ recordIdentifier: string
68
+ revisionIdentifier: string
69
+ domainData: Readonly<Record<string, JsonValue>>
70
+ readonly [member: string]: JsonValue | undefined
71
+ }
72
+
73
+ export interface RecordMetadata {
74
+ registryIdentifier: string
75
+ datasetIdentifier: string
76
+ entityTypeIdentifier: string
77
+ readonly [member: string]: JsonValue | undefined
78
+ }
79
+
80
+ export interface RecordEnvelope {
81
+ data: RegistryRecord
82
+ meta: RecordMetadata
83
+ '@context'?: string
84
+ }
85
+
86
+ export interface RecordPageInfo {
87
+ nextCursor: string | null
88
+ readonly [member: string]: JsonValue | undefined
89
+ }
90
+
91
+ export interface RecordCollection {
92
+ items: ReadonlyArray<RegistryRecord>
93
+ pageInfo: RecordPageInfo
94
+ meta: RecordMetadata
95
+ count?: SafeInteger
96
+ '@context'?: string
97
+ }
98
+
99
+ export interface ProbeStatus { status: string }
100
+
101
+ export interface CompleteOutcome<T> {
102
+ kind: 'complete'
103
+ value: T
104
+ traceId: string
105
+ etag?: string
106
+ location?: string
107
+ }
108
+
109
+ export interface PageOutcome {
110
+ kind: 'complete'
111
+ value: RecordCollection
112
+ continuation?: ListContinuation
113
+ traceId: string
114
+ etag?: string
115
+ }
116
+
117
+ export interface RawOutcome {
118
+ kind: 'complete'
119
+ body: Buffer
120
+ mediaType: string
121
+ traceId: string
122
+ etag?: string
123
+ }
124
+
125
+ export interface PatchOperation {
126
+ op: 'add' | 'replace' | 'test'
127
+ field: string
128
+ value: JsonValue
129
+ }
130
+
131
+ export interface RemovePatchOperation {
132
+ op: 'remove'
133
+ field: string
134
+ }
135
+
136
+ export interface LifecycleReviewTarget {
137
+ entityIdentifier: string
138
+ recordIdentifier: string
139
+ operation: 'create' | 'patch'
140
+ baseRevision?: SafeInteger
141
+ before?: Readonly<Record<string, JsonValue>>
142
+ after: Readonly<Record<string, JsonValue>>
143
+ }
144
+
145
+ export interface LifecycleReview { targets: ReadonlyArray<LifecycleReviewTarget> }
146
+
147
+ export interface LifecycleReceipt extends JsonObject {
148
+ id: string
149
+ revision: SafeInteger
150
+ snapshot: string
151
+ request: JsonObject
152
+ }
153
+
154
+ /** Opaque write authority selected from metadata fetched by this client source. */
155
+ export declare class BRegCreateBinding {
156
+ private constructor()
157
+ private readonly __opaque: void
158
+ }
159
+ /** Opaque write authority selected from metadata fetched by this client source. */
160
+ export declare class BRegPatchBinding {
161
+ private constructor()
162
+ private readonly __opaque: void
163
+ }
164
+ /** Opaque lifecycle authority selected from metadata fetched by this client source. */
165
+ export declare class BRegLifecycleAuthority {
166
+ private constructor()
167
+ private readonly __opaque: void
168
+ }
169
+
170
+ /** Opaque executable action promoted from a metadata authority and one record. */
171
+ export declare class BRegLifecycleAction {
172
+ private constructor()
173
+ private readonly __opaque: void
174
+ readonly operation: string
175
+ readonly stage: string | null
176
+ readonly href: string
177
+ readonly body: JsonObject
178
+ readonly review: LifecycleReview | null
179
+ }
180
+
181
+ /** Caller-filtered metadata bound to the exact client source that fetched it. */
182
+ export declare class BRegMetadata {
183
+ private constructor()
184
+ private readonly __opaque: void
185
+ readonly registryIdentifier: string
186
+ readonly registryVersion: string
187
+ readonly registryRevision: string
188
+ readonly traceId: string
189
+ readonly etag: string | null
190
+ selectCreate(operationIdentifier: string, expectedProfile: string): BRegCreateBinding
191
+ selectPatch(operationIdentifier: string, expectedProfile: string): BRegPatchBinding
192
+ selectLifecycle(entityIdentifier: string, expectedProfile: string): BRegLifecycleAuthority
193
+ }
194
+
195
+ export interface BaseRegistryClientFailure extends Error {
196
+ readonly kind: string
197
+ readonly code?: string
198
+ readonly planRefusal?: string
199
+ readonly status?: number
200
+ readonly traceId?: string
201
+ readonly transportKind?: string
202
+ readonly tokenKind?: string
203
+ }
204
+
205
+ export declare class BaseRegistryClientError extends Error implements BaseRegistryClientFailure {
206
+ readonly kind: string
207
+ readonly code?: string
208
+ readonly planRefusal?: string
209
+ readonly status?: number
210
+ readonly traceId?: string
211
+ readonly transportKind?: string
212
+ readonly tokenKind?: string
213
+ }
214
+
215
+ export declare class BaseRegistryClient {
216
+ constructor(config: BaseRegistryClientConfig)
217
+ health(): Promise<CompleteOutcome<ProbeStatus>>
218
+ ready(): Promise<CompleteOutcome<ProbeStatus>>
219
+ openapi(accessProfile?: string | null): Promise<RawOutcome>
220
+ registryMetadata(accessProfile?: string | null): Promise<RawOutcome>
221
+ registryContract(accessProfile?: string | null): Promise<BRegMetadata>
222
+ entitySchema(entityIdentifier: string, accessProfile?: string | null): Promise<RawOutcome>
223
+ getRecord(entityRoute: string, recordIdentifier: string, options?: RecordOptions | null): Promise<CompleteOutcome<RecordEnvelope>>
224
+ listRecords(entityRoute: string, options?: ListOptions | null): Promise<PageOutcome>
225
+ continueList(continuation: ListContinuation): Promise<PageOutcome>
226
+ lookupRecord(entityRoute: string, selector: string, values?: JsonObject | null, options?: RecordOptions | null): Promise<CompleteOutcome<RecordEnvelope>>
227
+ createRecord(binding: BRegCreateBinding, data: JsonObject, idempotencyKey: string, format?: RecordFormat | null): Promise<CompleteOutcome<RecordEnvelope>>
228
+ patchRecord(binding: BRegPatchBinding, recordIdentifier: string, etag: string, operations: ReadonlyArray<PatchOperation | RemovePatchOperation>, idempotencyKey: string, format?: RecordFormat | null): Promise<CompleteOutcome<RecordEnvelope>>
229
+ lifecycleActions(authority: BRegLifecycleAuthority, record: RecordEnvelope, format?: RecordFormat | null): ReadonlyArray<BRegLifecycleAction>
230
+ executeLifecycleAction(action: BRegLifecycleAction, idempotencyKey: string): Promise<CompleteOutcome<LifecycleReceipt>>
231
+ }
package/breg/client.js ADDED
@@ -0,0 +1,185 @@
1
+ 'use strict';
2
+
3
+ const { types: { isProxy } } = require('node:util');
4
+ const native = require('./index');
5
+
6
+ const MAX_JSON_DEPTH = 128;
7
+ const MAX_JSON_NODES = 100_000;
8
+ const MAX_JSON_STRING_BYTES = 4 * 1024 * 1024;
9
+
10
+ class BaseRegistryClientError extends Error {
11
+ constructor(envelope) {
12
+ super(envelope.message);
13
+ this.name = 'BaseRegistryClientError';
14
+ this.kind = envelope.kind;
15
+ for (const field of ['code', 'planRefusal', 'status', 'traceId', 'transportKind', 'tokenKind']) {
16
+ if (envelope[field] !== undefined && envelope[field] !== null) this[field] = envelope[field];
17
+ }
18
+ }
19
+ }
20
+
21
+ function normalize(error, fallbackKind) {
22
+ if (error instanceof BaseRegistryClientError) return error;
23
+ if (error instanceof Error && typeof error.message === 'string') {
24
+ try {
25
+ const envelope = JSON.parse(error.message);
26
+ if (envelope && typeof envelope === 'object' && typeof envelope.kind === 'string') {
27
+ return new BaseRegistryClientError(envelope);
28
+ }
29
+ } catch {
30
+ // napi argument conversion errors are not mapped Rust error envelopes.
31
+ }
32
+ }
33
+ if (fallbackKind) {
34
+ return new BaseRegistryClientError({
35
+ kind: fallbackKind,
36
+ message: fallbackKind === 'configuration'
37
+ ? 'Base Registry client configuration is invalid'
38
+ : 'Base Registry client arguments are invalid',
39
+ });
40
+ }
41
+ return error;
42
+ }
43
+
44
+ function inputError(kind) {
45
+ return new BaseRegistryClientError({
46
+ kind,
47
+ message: kind === 'configuration'
48
+ ? 'Base Registry client configuration is invalid'
49
+ : 'Base Registry client arguments are invalid',
50
+ });
51
+ }
52
+
53
+ function chargeString(value, budget, kind) {
54
+ budget.stringBytes += Buffer.byteLength(value, 'utf8');
55
+ if (budget.stringBytes > MAX_JSON_STRING_BYTES) throw inputError(kind);
56
+ }
57
+
58
+ function cloneJson(value, budget, depth, kind) {
59
+ if (depth > MAX_JSON_DEPTH) throw inputError(kind);
60
+ budget.nodes += 1;
61
+ if (budget.nodes > MAX_JSON_NODES) throw inputError(kind);
62
+ if (value === null || typeof value === 'boolean') return value;
63
+ if (typeof value === 'string') {
64
+ chargeString(value, budget, kind);
65
+ return value;
66
+ }
67
+ if (typeof value === 'number') {
68
+ if (!Number.isFinite(value)) throw inputError(kind);
69
+ return value;
70
+ }
71
+ if (value === undefined || typeof value !== 'object' || isProxy(value)) throw inputError(kind);
72
+
73
+ const prototype = Object.getPrototypeOf(value);
74
+ const array = Array.isArray(value);
75
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) {
76
+ throw inputError(kind);
77
+ }
78
+ if (budget.active.has(value)) throw inputError(kind);
79
+ budget.active.add(value);
80
+ try {
81
+ const keys = Reflect.ownKeys(value);
82
+ if (keys.some((key) => typeof key === 'symbol')) throw inputError(kind);
83
+ if (array) {
84
+ if (value.length + budget.nodes > MAX_JSON_NODES) throw inputError(kind);
85
+ const clone = new Array(value.length);
86
+ let count = 0;
87
+ for (const key of keys) {
88
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
89
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw inputError(kind);
90
+ if (key === 'length') continue;
91
+ chargeString(key, budget, kind);
92
+ const index = Number(key);
93
+ if (!descriptor.enumerable || !Number.isInteger(index) || index < 0
94
+ || index >= value.length || String(index) !== key) throw inputError(kind);
95
+ clone[index] = cloneJson(descriptor.value, budget, depth + 1, kind);
96
+ count += 1;
97
+ }
98
+ if (count !== value.length) throw inputError(kind);
99
+ return clone;
100
+ }
101
+ const clone = Object.create(null);
102
+ for (const key of keys) {
103
+ chargeString(key, budget, kind);
104
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
105
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw inputError(kind);
106
+ if (!descriptor.enumerable) continue;
107
+ clone[key] = cloneJson(descriptor.value, budget, depth + 1, kind);
108
+ }
109
+ return clone;
110
+ } finally {
111
+ budget.active.delete(value);
112
+ }
113
+ }
114
+
115
+ function sanitizeArguments(args, jsonIndexes, requiredIndexes) {
116
+ const budget = { nodes: 0, stringBytes: 0, active: new WeakSet() };
117
+ return args.map((value, index) => {
118
+ if (!jsonIndexes.has(index)) return value;
119
+ if (value === undefined || value === null) {
120
+ if (requiredIndexes.has(index)) throw inputError('invalid_request');
121
+ return value;
122
+ }
123
+ return cloneJson(value, budget, 0, 'invalid_request');
124
+ });
125
+ }
126
+
127
+ function wrapAsync(name, jsonIndexes = [], requiredIndexes = []) {
128
+ const original = native.BaseRegistryClient.prototype[name];
129
+ native.BaseRegistryClient.prototype[name] = function (...args) {
130
+ try {
131
+ const sanitized = sanitizeArguments(args, new Set(jsonIndexes), new Set(requiredIndexes));
132
+ return original.apply(this, sanitized).catch((error) => { throw normalize(error); });
133
+ } catch (error) {
134
+ throw normalize(error, 'invalid_request');
135
+ }
136
+ };
137
+ }
138
+
139
+ for (const [method, jsonIndexes, requiredIndexes] of [
140
+ ['health'], ['ready'], ['openapi'], ['registryMetadata'], ['registryContract'], ['entitySchema'],
141
+ ['getRecord', [2]], ['listRecords', [1]], ['continueList', [0], [0]],
142
+ ['lookupRecord', [2, 3]], ['createRecord', [1], [1]],
143
+ ['patchRecord', [3], [3]], ['executeLifecycleAction'],
144
+ ]) wrapAsync(method, jsonIndexes, requiredIndexes);
145
+
146
+ const lifecycleActions = native.BaseRegistryClient.prototype.lifecycleActions;
147
+ native.BaseRegistryClient.prototype.lifecycleActions = function (...args) {
148
+ try {
149
+ const sanitized = sanitizeArguments(args, new Set([1]), new Set([1]));
150
+ return lifecycleActions.apply(this, sanitized);
151
+ } catch (error) {
152
+ throw normalize(error, 'invalid_request');
153
+ }
154
+ };
155
+
156
+ for (const name of ['selectCreate', 'selectPatch', 'selectLifecycle']) {
157
+ const original = native.BRegMetadata.prototype[name];
158
+ native.BRegMetadata.prototype[name] = function (...args) {
159
+ try {
160
+ return original.apply(this, args);
161
+ } catch (error) {
162
+ throw normalize(error, 'invalid_request');
163
+ }
164
+ };
165
+ }
166
+
167
+ class BaseRegistryClient extends native.BaseRegistryClient {
168
+ constructor(config) {
169
+ try {
170
+ super(cloneJson(config, { nodes: 0, stringBytes: 0, active: new WeakSet() }, 0, 'configuration'));
171
+ } catch (error) {
172
+ throw normalize(error, 'configuration');
173
+ }
174
+ }
175
+ }
176
+
177
+ module.exports = {
178
+ BaseRegistryClient,
179
+ BaseRegistryClientError,
180
+ BRegMetadata: native.BRegMetadata,
181
+ BRegCreateBinding: native.BRegCreateBinding,
182
+ BRegPatchBinding: native.BRegPatchBinding,
183
+ BRegLifecycleAuthority: native.BRegLifecycleAuthority,
184
+ BRegLifecycleAction: native.BRegLifecycleAction,
185
+ };
@@ -0,0 +1,79 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+ export declare class BaseRegistryClient {
4
+ constructor(config: any)
5
+ health(): Promise<CompleteOutcome>
6
+ ready(): Promise<CompleteOutcome>
7
+ openapi(accessProfile?: string | undefined | null): Promise<RawOutcome>
8
+ registryMetadata(accessProfile?: string | undefined | null): Promise<RawOutcome>
9
+ registryContract(accessProfile?: string | undefined | null): Promise<BRegMetadata>
10
+ entitySchema(entityIdentifier: string, accessProfile?: string | undefined | null): Promise<RawOutcome>
11
+ getRecord(entityRoute: string, recordIdentifier: string, options?: any | undefined | null): Promise<CompleteOutcome>
12
+ listRecords(entityRoute: string, options?: any | undefined | null): Promise<PageOutcome>
13
+ continueList(value: any): Promise<PageOutcome>
14
+ lookupRecord(entityRoute: string, selector: string, values?: any | undefined | null, options?: any | undefined | null): Promise<CompleteOutcome>
15
+ createRecord(binding: BRegCreateBinding, data: any, idempotencyKey: string, formatValue?: string | undefined | null): Promise<CompleteOutcome>
16
+ patchRecord(binding: BRegPatchBinding, recordIdentifier: string, etag: string, operations: any, idempotencyKey: string, formatValue?: string | undefined | null): Promise<CompleteOutcome>
17
+ lifecycleActions(authority: BRegLifecycleAuthority, record: any, formatValue?: string | undefined | null): Array<BRegLifecycleAction>
18
+ executeLifecycleAction(action: BRegLifecycleAction, idempotencyKey: string): Promise<CompleteOutcome>
19
+ }
20
+
21
+ export declare class BRegCreateBinding {
22
+
23
+ }
24
+ export type CreateBinding = BRegCreateBinding
25
+
26
+ export declare class BRegLifecycleAction {
27
+ get operation(): string
28
+ get stage(): string | null
29
+ get href(): string
30
+ get body(): any
31
+ get review(): any | null
32
+ }
33
+ export type LifecycleAction = BRegLifecycleAction
34
+
35
+ export declare class BRegLifecycleAuthority {
36
+
37
+ }
38
+ export type LifecycleAuthority = BRegLifecycleAuthority
39
+
40
+ export declare class BRegMetadata {
41
+ get registryIdentifier(): string
42
+ get registryVersion(): string
43
+ get registryRevision(): string
44
+ get traceId(): string
45
+ get etag(): string | null
46
+ selectCreate(operationIdentifier: string, expectedProfile: string): BRegCreateBinding
47
+ selectPatch(operationIdentifier: string, expectedProfile: string): BRegPatchBinding
48
+ selectLifecycle(entityIdentifier: string, expectedProfile: string): BRegLifecycleAuthority
49
+ }
50
+ export type Metadata = BRegMetadata
51
+
52
+ export declare class BRegPatchBinding {
53
+
54
+ }
55
+ export type PatchBinding = BRegPatchBinding
56
+
57
+ export interface CompleteOutcome {
58
+ kind: string
59
+ value: any
60
+ traceId: string
61
+ etag?: string
62
+ location?: string
63
+ }
64
+
65
+ export interface PageOutcome {
66
+ kind: string
67
+ value: any
68
+ continuation?: any
69
+ traceId: string
70
+ etag?: string
71
+ }
72
+
73
+ export interface RawOutcome {
74
+ kind: string
75
+ body: Buffer
76
+ mediaType: string
77
+ traceId: string
78
+ etag?: string
79
+ }
package/breg/index.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('../native').load('breg');