@registrystack/client 0.28.0 → 0.29.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.
package/README.md CHANGED
@@ -1,12 +1,21 @@
1
- # `@registrystack/client`
1
+ # @registrystack/client
2
2
 
3
3
  One versioned Node.js package for the Discovery, Evidence, Relay, and Base
4
4
  Registry Engine client APIs in Registry Stack.
5
5
 
6
+ ## Install
7
+
6
8
  ```sh
7
9
  npm install "@registrystack/client@<version>"
8
10
  ```
9
11
 
12
+ Requires Node.js 22.12 or newer. Supported targets are macOS arm64, Linux
13
+ arm64 with glibc, and Linux x64 with glibc; installing the package pulls in
14
+ one platform-specific optional dependency containing all four native
15
+ bindings.
16
+
17
+ ## Usage
18
+
10
19
  ```js
11
20
  const { discovery, evidence, relay, breg } = require('@registrystack/client');
12
21
 
@@ -15,6 +24,20 @@ const registry = new breg.BaseRegistryClient({
15
24
  });
16
25
  ```
17
26
 
27
+ ## Products
28
+
29
+ - `discovery`: Registry Discovery, bounded search and resolution over a
30
+ curated index.
31
+ - `evidence`: Evidence Gateway, request and verify signed minimum-disclosure
32
+ assertions.
33
+ - `relay`: Registry Relay, scoped read-only APIs over existing sources.
34
+ - `breg`: Base Registry Engine, records, contracts, writes, and lifecycle.
35
+
36
+ Each product remains in its own namespace because its routing,
37
+ authentication, errors, and verification rules are different.
38
+
39
+ ## TypeScript
40
+
18
41
  A TypeScript consumer also needs the Node type definitions, and must name
19
42
  them. The published declarations use `Buffer`, a Node.js global, and
20
43
  TypeScript 6 does not load installed `@types` packages on its own, so a
@@ -37,29 +60,33 @@ npm install --save-dev "@types/node"
37
60
  The package does not declare that dependency itself, because a JavaScript
38
61
  consumer does not need it.
39
62
 
40
- Each product remains in its own namespace because its routing,
41
- authentication, errors, and verification rules are different. The package
42
- uses one platform-specific native dependency containing all four bindings.
43
- Supported targets are macOS arm64, Linux arm64 with glibc, and Linux x64 with
44
- glibc. Install the exact client version that matches the deployment.
45
-
46
- The unified package is published beginning with Registry Stack v0.26.1.
47
- Existing standalone client packages remain available for earlier versions,
48
- but the release process does not publish new standalone versions once this
49
- package is active.
50
-
51
- For server-side BReg applications, `breg.BaseRegistryClient.registryContract()`
52
- returns caller-filtered `operations` with typed field, request, and list-query
53
- descriptors. Descriptions do not grant authority: select opaque create, patch,
54
- and lifecycle authorities from that same contract before executing mutations.
55
-
56
- Use BReg methods ending in `Json` when values must cross Node without numeric
57
- coercion. They accept domain JSON text and return `valueJson` plus the usual
58
- trace, ETag, location, and opaque continuation. `lifecycleActionsJson` accepts a
59
- record envelope; opaque actions expose `bodyJson` and `reviewJson`. Decode these
60
- strings with a lossless JSON library when displaying wide numbers. Decimal
61
- fields remain fixed-scale strings, and null remains distinct from absence.
62
- Duplicate members and silently rounded literals are refused. Existing BReg
63
- integer restrictions still apply: `9007199254740992` is a supported write,
64
- whereas `9007199254740993` is refused before I/O. Do not round refused values.
65
- The client never retries a mutation automatically.
63
+ ## Base Registry Engine notes
64
+
65
+ `breg.BaseRegistryClient.registryContract()` returns caller-filtered
66
+ `operations` with typed field, request, and list-query descriptors; those
67
+ descriptions grant no authority, so select the opaque create, patch, and
68
+ lifecycle authorities from that same contract before executing a mutation.
69
+ Methods ending in `Json` keep values exact across the Node number boundary:
70
+ they accept domain JSON text and return `valueJson` alongside the usual
71
+ trace, ETag, location, and opaque continuation. The client never retries a
72
+ mutation automatically. See
73
+ [Exact JSON in Node](https://docs.registrystack.org/reference/client-api/#exact-json-in-node)
74
+ for the full rules.
75
+
76
+ ## Versioning
77
+
78
+ Install the exact client version that matches the deployment. The unified
79
+ package is published beginning with Registry Stack v0.26.1. Existing
80
+ standalone client packages remain available for earlier versions, but the
81
+ release process does not publish new standalone versions once this package
82
+ is active.
83
+
84
+ ## Documentation
85
+
86
+ - [Client API reference](https://docs.registrystack.org/reference/client-api/)
87
+ - [Repository](https://github.com/registrystack/registry-stack)
88
+ - [Issue tracker](https://github.com/registrystack/registry-stack/issues)
89
+
90
+ ## License
91
+
92
+ Apache-2.0
package/breg/client.d.ts CHANGED
@@ -224,6 +224,56 @@ export interface BRegOperationDescriptor {
224
224
  }
225
225
  }
226
226
 
227
+ export type BRegAttachmentVerificationStatus = 'notRequired' | 'pending' | 'approved' | 'rejected'
228
+
229
+ export type BRegAttachmentClassification = 'public' | 'internal' | 'restricted'
230
+
231
+ /** Engine-owned state of one filled slot, read from a record's domain data. */
232
+ export interface BRegAttachmentState {
233
+ readonly slotIdentifier: string
234
+ readonly proposalVersion: SafeInteger
235
+ readonly erased: boolean
236
+ readonly byteSize: SafeInteger
237
+ readonly sha256: string
238
+ readonly contentType: string | null
239
+ readonly uploadedAt: string | null
240
+ readonly uploadedBy: string | null
241
+ readonly verificationStatus: BRegAttachmentVerificationStatus | null
242
+ }
243
+
244
+ /** What one record projection says about one slot. */
245
+ export type BRegAttachmentSlotValue =
246
+ | { readonly kind: 'not_selected'; readonly value: null }
247
+ | { readonly kind: 'empty'; readonly value: null }
248
+ | { readonly kind: 'filled'; readonly value: BRegAttachmentState }
249
+
250
+ /** Opaque bytes already accepted by one slot's served upload policy. */
251
+ export declare class BRegAttachmentUpload {
252
+ private constructor()
253
+ private readonly __opaque: void
254
+ readonly contentType: string
255
+ readonly byteSize: SafeInteger
256
+ }
257
+
258
+ /** Opaque governed attachment slot selected from metadata fetched by this client source. */
259
+ export declare class BRegAttachmentSlot {
260
+ private constructor()
261
+ private readonly __opaque: void
262
+ readonly slotIdentifier: string
263
+ readonly entityIdentifier: string
264
+ readonly accessProfile: string
265
+ readonly requiredForSubmit: boolean
266
+ readonly maximumBytes: SafeInteger
267
+ readonly contentTypes: ReadonlyArray<string>
268
+ readonly classification: BRegAttachmentClassification
269
+ readonly canDownload: boolean
270
+ readonly canUpload: boolean
271
+ readonly canRemove: boolean
272
+ acceptsContentType(contentType: string): boolean
273
+ prepareUpload(contentType: string, body: Buffer): BRegAttachmentUpload
274
+ valueIn(record: RecordEnvelope, format?: RecordFormat | null): BRegAttachmentSlotValue
275
+ }
276
+
227
277
  /** Opaque write authority selected from metadata fetched by this client source. */
228
278
  export declare class BRegCreateBinding {
229
279
  private constructor()
@@ -242,6 +292,7 @@ export declare class BRegLifecycleAuthority {
242
292
 
243
293
  /** Opaque executable action promoted from a metadata authority and one record. */
244
294
  export declare class BRegLifecycleAction {
295
+ withReason(reason: string): BRegLifecycleAction
245
296
  private constructor()
246
297
  private readonly __opaque: void
247
298
  readonly operation: string
@@ -266,6 +317,7 @@ export declare class BRegMetadata {
266
317
  selectCreate(operationIdentifier: string, expectedProfile: string): BRegCreateBinding
267
318
  selectPatch(operationIdentifier: string, expectedProfile: string): BRegPatchBinding
268
319
  selectLifecycle(entityIdentifier: string, expectedProfile: string): BRegLifecycleAuthority
320
+ selectAttachments(entityIdentifier: string, expectedProfile: string): ReadonlyArray<BRegAttachmentSlot>
269
321
  }
270
322
 
271
323
  export interface BaseRegistryClientFailure extends Error {
@@ -303,12 +355,17 @@ export declare class BaseRegistryClient {
303
355
  createRecord(binding: BRegCreateBinding, data: JsonObject, idempotencyKey: string, format?: RecordFormat | null): Promise<CompleteOutcome<RecordEnvelope>>
304
356
  patchRecord(binding: BRegPatchBinding, recordIdentifier: string, etag: string, operations: ReadonlyArray<PatchOperation | RemovePatchOperation>, idempotencyKey: string, format?: RecordFormat | null): Promise<CompleteOutcome<RecordEnvelope>>
305
357
  lifecycleActions(authority: BRegLifecycleAuthority, record: RecordEnvelope, format?: RecordFormat | null): ReadonlyArray<BRegLifecycleAction>
358
+ uploadAttachment(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, upload: BRegAttachmentUpload, idempotencyKey: string, format?: RecordFormat | null): Promise<CompleteOutcome<RecordEnvelope>>
359
+ downloadAttachment(slot: BRegAttachmentSlot, recordIdentifier: string, proposalVersion: SafeInteger): Promise<RawOutcome>
360
+ deleteAttachment(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, idempotencyKey: string, format?: RecordFormat | null): Promise<CompleteOutcome<RecordEnvelope>>
306
361
  getRecordJson(entityRoute: string, recordIdentifier: string, options?: RecordOptions | null): Promise<JsonOutcome>
307
362
  listRecordsJson(entityRoute: string, options?: ListOptions | null): Promise<JsonOutcome>
308
363
  continueListJson(continuation: ListContinuation): Promise<JsonOutcome>
309
364
  lookupRecordJson(entityRoute: string, selector: string, valuesJson?: string | null, options?: RecordOptions | null): Promise<JsonOutcome>
310
365
  createRecordJson(binding: BRegCreateBinding, dataJson: string, idempotencyKey: string, format?: RecordFormat | null): Promise<JsonOutcome>
311
366
  patchRecordJson(binding: BRegPatchBinding, recordIdentifier: string, etag: string, operationsJson: string, idempotencyKey: string, format?: RecordFormat | null): Promise<JsonOutcome>
367
+ uploadAttachmentJson(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, upload: BRegAttachmentUpload, idempotencyKey: string, format?: RecordFormat | null): Promise<JsonOutcome>
368
+ deleteAttachmentJson(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, idempotencyKey: string, format?: RecordFormat | null): Promise<JsonOutcome>
312
369
  lifecycleActionsJson(authority: BRegLifecycleAuthority, recordJson: string, format?: RecordFormat | null): ReadonlyArray<BRegLifecycleAction>
313
370
  executeLifecycleActionJson(action: BRegLifecycleAction, idempotencyKey: string): Promise<JsonOutcome>
314
371
  executeLifecycleAction(action: BRegLifecycleAction, idempotencyKey: string): Promise<CompleteOutcome<LifecycleReceipt>>
package/breg/client.js CHANGED
@@ -141,10 +141,22 @@ for (const [method, jsonIndexes, requiredIndexes] of [
141
141
  ['getRecord', [2]], ['listRecords', [1]], ['continueList', [0], [0]],
142
142
  ['lookupRecord', [2, 3]], ['createRecord', [1], [1]],
143
143
  ['patchRecord', [3], [3]], ['executeLifecycleAction'],
144
+ ['uploadAttachment'], ['downloadAttachment'], ['deleteAttachment'],
144
145
  ['getRecordJson', [2]], ['listRecordsJson', [1]], ['continueListJson', [0], [0]],
145
146
  ['lookupRecordJson', [3]], ['createRecordJson'], ['patchRecordJson'], ['executeLifecycleActionJson'],
147
+ ['uploadAttachmentJson'], ['deleteAttachmentJson'],
146
148
  ]) wrapAsync(method, jsonIndexes, requiredIndexes);
147
149
 
150
+ const withReason = native.BRegLifecycleAction.prototype.withReason;
151
+ native.BRegLifecycleAction.prototype.withReason = function (reason) {
152
+ try {
153
+ if (typeof reason !== 'string' || !reason.isWellFormed()) throw inputError('invalid_request');
154
+ return withReason.call(this, reason);
155
+ } catch (error) {
156
+ throw normalize(error, 'invalid_request');
157
+ }
158
+ };
159
+
148
160
  const lifecycleActionsJson = native.BaseRegistryClient.prototype.lifecycleActionsJson;
149
161
  native.BaseRegistryClient.prototype.lifecycleActionsJson = function (...args) {
150
162
  try { return lifecycleActionsJson.apply(this, args); }
@@ -161,7 +173,27 @@ native.BaseRegistryClient.prototype.lifecycleActions = function (...args) {
161
173
  }
162
174
  };
163
175
 
164
- for (const name of ['selectCreate', 'selectPatch', 'selectLifecycle']) {
176
+ const valueIn = native.BRegAttachmentSlot.prototype.valueIn;
177
+ native.BRegAttachmentSlot.prototype.valueIn = function (...args) {
178
+ try {
179
+ return valueIn.apply(this, sanitizeArguments(args, new Set([0]), new Set([0])));
180
+ } catch (error) {
181
+ throw normalize(error, 'invalid_request');
182
+ }
183
+ };
184
+
185
+ for (const name of ['prepareUpload', 'acceptsContentType']) {
186
+ const original = native.BRegAttachmentSlot.prototype[name];
187
+ native.BRegAttachmentSlot.prototype[name] = function (...args) {
188
+ try {
189
+ return original.apply(this, args);
190
+ } catch (error) {
191
+ throw normalize(error, 'invalid_request');
192
+ }
193
+ };
194
+ }
195
+
196
+ for (const name of ['selectCreate', 'selectPatch', 'selectLifecycle', 'selectAttachments']) {
165
197
  const original = native.BRegMetadata.prototype[name];
166
198
  native.BRegMetadata.prototype[name] = function (...args) {
167
199
  try {
@@ -190,4 +222,6 @@ module.exports = {
190
222
  BRegPatchBinding: native.BRegPatchBinding,
191
223
  BRegLifecycleAuthority: native.BRegLifecycleAuthority,
192
224
  BRegLifecycleAction: native.BRegLifecycleAction,
225
+ BRegAttachmentSlot: native.BRegAttachmentSlot,
226
+ BRegAttachmentUpload: native.BRegAttachmentUpload,
193
227
  };
package/breg/index.d.ts CHANGED
@@ -14,6 +14,15 @@ export declare class BaseRegistryClient {
14
14
  lookupRecord(entityRoute: string, selector: string, values?: any | undefined | null, options?: any | undefined | null): Promise<CompleteOutcome>
15
15
  createRecord(binding: BRegCreateBinding, data: any, idempotencyKey: string, formatValue?: string | undefined | null): Promise<CompleteOutcome>
16
16
  patchRecord(binding: BRegPatchBinding, recordIdentifier: string, etag: string, operations: any, idempotencyKey: string, formatValue?: string | undefined | null): Promise<CompleteOutcome>
17
+ /**
18
+ * Replace one governed attachment slot with exact bytes. The prepared
19
+ * upload already satisfies the slot's served size and content-type policy.
20
+ */
21
+ uploadAttachment(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, upload: BRegAttachmentUpload, idempotencyKey: string, formatValue?: string | undefined | null): Promise<CompleteOutcome>
22
+ /** Read the exact bytes one governed slot holds for one proposal version. */
23
+ downloadAttachment(slot: BRegAttachmentSlot, recordIdentifier: string, proposalVersion: number): Promise<RawOutcome>
24
+ /** Empty one governed attachment slot. */
25
+ deleteAttachment(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, idempotencyKey: string, formatValue?: string | undefined | null): Promise<CompleteOutcome>
17
26
  lifecycleActions(authority: BRegLifecycleAuthority, record: any, formatValue?: string | undefined | null): Array<BRegLifecycleAction>
18
27
  executeLifecycleAction(action: BRegLifecycleAction, idempotencyKey: string): Promise<CompleteOutcome>
19
28
  getRecordJson(entityRoute: string, recordIdentifier: string, options?: any | undefined | null): Promise<JsonOutcome>
@@ -22,16 +31,48 @@ export declare class BaseRegistryClient {
22
31
  lookupRecordJson(entityRoute: string, selector: string, valuesJson?: string | undefined | null, options?: any | undefined | null): Promise<JsonOutcome>
23
32
  createRecordJson(binding: BRegCreateBinding, dataJson: string, idempotencyKey: string, formatValue?: string | undefined | null): Promise<JsonOutcome>
24
33
  patchRecordJson(binding: BRegPatchBinding, recordIdentifier: string, etag: string, operationsJson: string, idempotencyKey: string, formatValue?: string | undefined | null): Promise<JsonOutcome>
34
+ uploadAttachmentJson(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, upload: BRegAttachmentUpload, idempotencyKey: string, formatValue?: string | undefined | null): Promise<JsonOutcome>
35
+ deleteAttachmentJson(slot: BRegAttachmentSlot, recordIdentifier: string, etag: string, idempotencyKey: string, formatValue?: string | undefined | null): Promise<JsonOutcome>
25
36
  lifecycleActionsJson(authority: BRegLifecycleAuthority, recordJson: string, formatValue?: string | undefined | null): Array<BRegLifecycleAction>
26
37
  executeLifecycleActionJson(action: BRegLifecycleAction, idempotencyKey: string): Promise<JsonOutcome>
27
38
  }
28
39
 
40
+ /** Opaque governed attachment slot selected from metadata fetched by this client source. */
41
+ export declare class BRegAttachmentSlot {
42
+ get slotIdentifier(): string
43
+ get entityIdentifier(): string
44
+ get accessProfile(): string
45
+ get requiredForSubmit(): boolean
46
+ /** Largest body the served slot policy accepts, in bytes. */
47
+ get maximumBytes(): number
48
+ get contentTypes(): Array<string>
49
+ /** Authored sensitivity of this slot's content. */
50
+ get classification(): string
51
+ get canDownload(): boolean
52
+ get canUpload(): boolean
53
+ get canRemove(): boolean
54
+ acceptsContentType(contentType: string): boolean
55
+ /** Bind exact bytes to this slot. Refusals happen here, before any request. */
56
+ prepareUpload(contentType: string, body: Buffer): BRegAttachmentUpload
57
+ /** Read this slot's engine-owned state out of one Registry Record envelope. */
58
+ valueIn(record: any, formatValue?: string | undefined | null): any
59
+ }
60
+ export type AttachmentSlot = BRegAttachmentSlot
61
+
62
+ /** Opaque bytes already accepted by one slot's served upload policy. */
63
+ export declare class BRegAttachmentUpload {
64
+ get contentType(): string
65
+ get byteSize(): number
66
+ }
67
+ export type AttachmentUpload = BRegAttachmentUpload
68
+
29
69
  export declare class BRegCreateBinding {
30
70
 
31
71
  }
32
72
  export type CreateBinding = BRegCreateBinding
33
73
 
34
74
  export declare class BRegLifecycleAction {
75
+ withReason(reason: string): BRegLifecycleAction
35
76
  get bodyJson(): string
36
77
  get reviewJson(): string | null
37
78
  get operation(): string
@@ -58,6 +99,7 @@ export declare class BRegMetadata {
58
99
  selectCreate(operationIdentifier: string, expectedProfile: string): BRegCreateBinding
59
100
  selectPatch(operationIdentifier: string, expectedProfile: string): BRegPatchBinding
60
101
  selectLifecycle(entityIdentifier: string, expectedProfile: string): BRegLifecycleAuthority
102
+ selectAttachments(entityIdentifier: string, expectedProfile: string): Array<BRegAttachmentSlot>
61
103
  }
62
104
  export type Metadata = BRegMetadata
63
105
 
package/native.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const PACKAGE_VERSION = '0.28.0';
3
+ const PACKAGE_VERSION = '0.29.0';
4
4
  const PRODUCTS = new Set(['discovery', 'evidence', 'relay', 'breg']);
5
5
 
6
6
  function target() {
package/package.json CHANGED
@@ -1,15 +1,28 @@
1
1
  {
2
2
  "name": "@registrystack/client",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Unified Node.js client for Registry Stack products.",
5
5
  "license": "Apache-2.0",
6
+ "homepage": "https://docs.registrystack.org/reference/client-api/",
7
+ "bugs": {
8
+ "url": "https://github.com/registrystack/registry-stack/issues"
9
+ },
10
+ "keywords": [
11
+ "registry",
12
+ "registrystack",
13
+ "discovery",
14
+ "evidence",
15
+ "relay",
16
+ "breg"
17
+ ],
6
18
  "publishConfig": {
7
19
  "access": "public",
8
20
  "provenance": true
9
21
  },
10
22
  "repository": {
11
23
  "type": "git",
12
- "url": "https://github.com/registrystack/registry-stack"
24
+ "url": "https://github.com/registrystack/registry-stack",
25
+ "directory": "crates/registry-stack-client-node"
13
26
  },
14
27
  "main": "index.js",
15
28
  "types": "index.d.ts",
@@ -42,8 +55,8 @@
42
55
  "typescript": "6.0.3"
43
56
  },
44
57
  "optionalDependencies": {
45
- "@registrystack/client-darwin-arm64": "0.28.0",
46
- "@registrystack/client-linux-arm64-gnu": "0.28.0",
47
- "@registrystack/client-linux-x64-gnu": "0.28.0"
58
+ "@registrystack/client-darwin-arm64": "0.29.0",
59
+ "@registrystack/client-linux-arm64-gnu": "0.29.0",
60
+ "@registrystack/client-linux-x64-gnu": "0.29.0"
48
61
  }
49
62
  }