@registrystack/client 0.37.0 → 0.38.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 +11 -3
- package/index.d.ts +2 -1
- package/index.js +2 -1
- package/messaging/client.d.ts +143 -0
- package/messaging/client.js +121 -0
- package/messaging/index.d.ts +27 -0
- package/messaging/index.js +3 -0
- package/native.js +2 -2
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @registrystack/client
|
|
2
2
|
|
|
3
3
|
One versioned Node.js package for the Discovery, Evidence, Relay, Base Registry
|
|
4
|
-
Engine, and
|
|
4
|
+
Engine, Casework, and Messaging client APIs in Registry Stack.
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
@@ -11,13 +11,13 @@ npm install "@registrystack/client@<version>"
|
|
|
11
11
|
|
|
12
12
|
Requires Node.js 22.12 or newer. Supported targets are macOS arm64, Linux
|
|
13
13
|
arm64 with glibc, and Linux x64 with glibc; installing the package pulls in
|
|
14
|
-
one platform-specific optional dependency containing all
|
|
14
|
+
one platform-specific optional dependency containing all six native
|
|
15
15
|
bindings.
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
|
19
19
|
```js
|
|
20
|
-
const { discovery, evidence, relay, breg, casework } = require('@registrystack/client');
|
|
20
|
+
const { discovery, evidence, relay, breg, casework, messaging } = require('@registrystack/client');
|
|
21
21
|
|
|
22
22
|
const registry = new breg.BaseRegistryClient({
|
|
23
23
|
baseUrl: 'https://registry.example.invalid/',
|
|
@@ -25,6 +25,9 @@ const registry = new breg.BaseRegistryClient({
|
|
|
25
25
|
const work = new casework.CaseworkClient({
|
|
26
26
|
baseUrl: 'https://casework.example.invalid/',
|
|
27
27
|
});
|
|
28
|
+
const messages = new messaging.MessagingClient({
|
|
29
|
+
baseUrl: 'https://messaging.example.invalid/',
|
|
30
|
+
});
|
|
28
31
|
```
|
|
29
32
|
|
|
30
33
|
## Products
|
|
@@ -38,6 +41,8 @@ const work = new casework.CaseworkClient({
|
|
|
38
41
|
applied-request result navigation.
|
|
39
42
|
- `casework`: Registry Casework, staff inbox, claims, drafts, decisions,
|
|
40
43
|
recovery, history, holdings, and directory bootstrap.
|
|
44
|
+
- `messaging`: Registry Messaging, submit, inspect, cancel, and preview
|
|
45
|
+
institution-owned messages.
|
|
41
46
|
|
|
42
47
|
Each product remains in its own namespace because its routing,
|
|
43
48
|
authentication, errors, and verification rules are different.
|
|
@@ -99,6 +104,9 @@ is active.
|
|
|
99
104
|
The `casework` namespace is part of the unified package beginning with
|
|
100
105
|
Registry Stack v0.30.0.
|
|
101
106
|
|
|
107
|
+
The `messaging` namespace is part of the unified package beginning with
|
|
108
|
+
Registry Stack v0.38.0.
|
|
109
|
+
|
|
102
110
|
## Casework notes
|
|
103
111
|
|
|
104
112
|
The Casework module is for a trusted server host. Each call takes that request's
|
package/index.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ import * as evidence from './evidence/client'
|
|
|
3
3
|
import * as relay from './relay/client'
|
|
4
4
|
import * as breg from './breg/client'
|
|
5
5
|
import * as casework from './casework/client'
|
|
6
|
+
import * as messaging from './messaging/client'
|
|
6
7
|
|
|
7
|
-
export { discovery, evidence, relay, breg, casework }
|
|
8
|
+
export { discovery, evidence, relay, breg, casework, messaging }
|
package/index.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// One assignment per namespace, never a `module.exports` object literal: Node
|
|
4
4
|
// scans this file for these statements to synthesise the named exports an ESM
|
|
5
|
-
// consumer imports, and `require()` sees the same
|
|
5
|
+
// consumer imports, and `require()` sees the same six properties either way.
|
|
6
6
|
exports.discovery = require('./discovery/client');
|
|
7
7
|
exports.evidence = require('./evidence/client');
|
|
8
8
|
exports.relay = require('./relay/client');
|
|
9
9
|
exports.breg = require('./breg/client');
|
|
10
10
|
exports.casework = require('./casework/client');
|
|
11
|
+
exports.messaging = require('./messaging/client');
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/** An integer for which `Number.isSafeInteger` is true. */
|
|
2
|
+
export type SafeInteger = number
|
|
3
|
+
/** A lowercase hyphenated UUID, checked by the native binding before any request is sent. */
|
|
4
|
+
export type MessageId = string
|
|
5
|
+
/** An RFC 3339 instant, parsed by the runtime. */
|
|
6
|
+
export type Instant = string
|
|
7
|
+
export type JsonScalar = string | number | boolean | null
|
|
8
|
+
export type JsonValue = JsonScalar | ReadonlyArray<JsonValue> | { readonly [key: string]: JsonValue }
|
|
9
|
+
|
|
10
|
+
export interface MessagingClientConfig {
|
|
11
|
+
baseUrl: string
|
|
12
|
+
requestTimeoutMilliseconds?: SafeInteger | null
|
|
13
|
+
connectTimeoutMilliseconds?: SafeInteger | null
|
|
14
|
+
maxResponseBytes?: SafeInteger | null
|
|
15
|
+
userAgent?: string | null
|
|
16
|
+
trustedRootCertificates?: string | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type Channel = 'email' | 'sms'
|
|
20
|
+
export type MessageStatus = 'queued' | 'sending' | 'submitted' | 'delivered' | 'failed' | 'expired' | 'cancelled' | 'unknown'
|
|
21
|
+
export type MessageDispatch = 'queued' | 'sending' | 'submitted' | 'failed' | 'unknown' | 'cancelled' | 'expired'
|
|
22
|
+
export type MessageReport = 'none' | 'sent' | 'delivered' | 'undelivered' | 'unavailable'
|
|
23
|
+
export type AttemptOutcome = 'in-progress' | 'accepted' | 'transient' | 'permanent' | 'maybe-sent' | 'interrupted'
|
|
24
|
+
|
|
25
|
+
/** Exactly one email address or one E.164 phone number. */
|
|
26
|
+
export type Recipient = { email: string; phone?: never } | { phone: string; email?: never }
|
|
27
|
+
export interface TemplateReference { id: string; version: string }
|
|
28
|
+
export interface DirectContent { subject?: string; text: string }
|
|
29
|
+
|
|
30
|
+
/** Exactly one of `template` or `content`; `locale` and `data` accompany a template. */
|
|
31
|
+
export interface SubmitMessageRequest {
|
|
32
|
+
senderProfile: string
|
|
33
|
+
to: Recipient
|
|
34
|
+
template?: TemplateReference
|
|
35
|
+
locale?: string
|
|
36
|
+
data?: JsonValue
|
|
37
|
+
content?: DirectContent
|
|
38
|
+
notBefore?: Instant
|
|
39
|
+
expiresAt?: Instant
|
|
40
|
+
correlationId?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MessageLinks { self: string; cancel: string }
|
|
44
|
+
export interface MessageReceipt { id: MessageId; status: MessageStatus; links: MessageLinks }
|
|
45
|
+
export interface AttemptSummary {
|
|
46
|
+
generation: SafeInteger
|
|
47
|
+
attempt: SafeInteger
|
|
48
|
+
outcome: AttemptOutcome
|
|
49
|
+
startedAt: Instant
|
|
50
|
+
finishedAt?: Instant
|
|
51
|
+
providerReference: boolean
|
|
52
|
+
}
|
|
53
|
+
export interface MessageView {
|
|
54
|
+
id: MessageId
|
|
55
|
+
status: MessageStatus
|
|
56
|
+
dispatch: MessageDispatch
|
|
57
|
+
report: MessageReport
|
|
58
|
+
reportedAt?: Instant
|
|
59
|
+
channel: Channel
|
|
60
|
+
senderProfile: string
|
|
61
|
+
to: Recipient
|
|
62
|
+
template?: TemplateReference
|
|
63
|
+
correlationId?: string
|
|
64
|
+
acceptedAt: Instant
|
|
65
|
+
notBefore?: Instant
|
|
66
|
+
expiresAt: Instant
|
|
67
|
+
updatedAt: Instant
|
|
68
|
+
attempts: ReadonlyArray<AttemptSummary>
|
|
69
|
+
links: MessageLinks
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface TemplatePreviewRequest { locale: string; data: JsonValue }
|
|
73
|
+
export type SmsEncoding = 'gsm7' | 'ucs2'
|
|
74
|
+
/** Septets for GSM-7, UTF-16 code units for UCS-2. */
|
|
75
|
+
export interface SegmentCount { encoding: SmsEncoding; units: SafeInteger; segments: SafeInteger }
|
|
76
|
+
export interface RenderedParts { subject?: string; text: string; html?: string }
|
|
77
|
+
export interface TemplatePreview {
|
|
78
|
+
template: TemplateReference
|
|
79
|
+
locale: string
|
|
80
|
+
channel: Channel
|
|
81
|
+
packageDigest: string
|
|
82
|
+
parts: RenderedParts
|
|
83
|
+
sms?: SegmentCount
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface MessagingOutcome<T> { kind: 'complete'; value: T; traceId: string }
|
|
87
|
+
|
|
88
|
+
/** The closed catalogue: the client answers any other code as a protocol failure. */
|
|
89
|
+
export type MessagingProblemCode =
|
|
90
|
+
| 'authentication.refused'
|
|
91
|
+
| 'callback.unreadable'
|
|
92
|
+
| 'callback.unverified'
|
|
93
|
+
| 'content.invalid'
|
|
94
|
+
| 'content.too-large'
|
|
95
|
+
| 'content.too-many-segments'
|
|
96
|
+
| 'idempotency.expired'
|
|
97
|
+
| 'idempotency.key-reused'
|
|
98
|
+
| 'message.dispatch-started'
|
|
99
|
+
| 'message.not-visible'
|
|
100
|
+
| 'message.terminal'
|
|
101
|
+
| 'operation.not-authorized'
|
|
102
|
+
| 'profile.not-authorized'
|
|
103
|
+
| 'quota.exceeded'
|
|
104
|
+
| 'rate-limit.exceeded'
|
|
105
|
+
| 'request.body-too-large'
|
|
106
|
+
| 'request.invalid'
|
|
107
|
+
| 'request.method-not-allowed'
|
|
108
|
+
| 'request.not-found'
|
|
109
|
+
| 'request.unprocessable'
|
|
110
|
+
| 'request.unsupported-media-type'
|
|
111
|
+
| 'service.unavailable'
|
|
112
|
+
| 'template.data-invalid'
|
|
113
|
+
| 'template.locale-unavailable'
|
|
114
|
+
| 'template.not-found'
|
|
115
|
+
| 'template.render-refused'
|
|
116
|
+
export type MessagingProtocolFailure = 'header_bounds' | 'trace_context' | 'media_type' | 'body' | 'problem' | 'status' | 'protocol'
|
|
117
|
+
|
|
118
|
+
export class MessagingClientError extends Error {
|
|
119
|
+
readonly kind: 'configuration' | 'invalid_request' | 'transport' | 'problem' | 'protocol'
|
|
120
|
+
readonly code?: MessagingProblemCode
|
|
121
|
+
readonly title?: string
|
|
122
|
+
readonly detail?: string
|
|
123
|
+
readonly status?: SafeInteger
|
|
124
|
+
readonly traceId?: string
|
|
125
|
+
/**
|
|
126
|
+
* Whole seconds a 429 limit refusal asked the caller to wait, from
|
|
127
|
+
* `Retry-After`, at most one day. Absent on every other failure; the
|
|
128
|
+
* client never waits or retries itself.
|
|
129
|
+
*/
|
|
130
|
+
readonly retryAfterSeconds?: SafeInteger
|
|
131
|
+
readonly transportKind?: string
|
|
132
|
+
readonly protocolFailure?: MessagingProtocolFailure
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export class MessagingClient {
|
|
136
|
+
constructor(config: MessagingClientConfig)
|
|
137
|
+
health(): Promise<MessagingOutcome<null>>
|
|
138
|
+
ready(): Promise<MessagingOutcome<null>>
|
|
139
|
+
submit(token: string, idempotencyKey: string, request: SubmitMessageRequest): Promise<MessagingOutcome<MessageReceipt>>
|
|
140
|
+
message(token: string, messageId: MessageId): Promise<MessagingOutcome<MessageView>>
|
|
141
|
+
cancel(token: string, messageId: MessageId): Promise<MessagingOutcome<MessageView>>
|
|
142
|
+
preview(token: string, templateId: string, version: string, request: TemplatePreviewRequest): Promise<MessagingOutcome<TemplatePreview>>
|
|
143
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { types: { isProxy } } = require('node:util');
|
|
4
|
+
const native = require('./index');
|
|
5
|
+
|
|
6
|
+
const MAX_JSON_DEPTH = 64;
|
|
7
|
+
const MAX_JSON_NODES = 20_000;
|
|
8
|
+
const MAX_JSON_STRING_BYTES = 1024 * 1024;
|
|
9
|
+
|
|
10
|
+
class MessagingClientError extends Error {
|
|
11
|
+
constructor(envelope) {
|
|
12
|
+
super(envelope.message);
|
|
13
|
+
this.name = 'MessagingClientError';
|
|
14
|
+
this.kind = envelope.kind;
|
|
15
|
+
for (const field of ['code', 'title', 'detail', 'status', 'traceId', 'retryAfterSeconds', 'transportKind', 'protocolFailure']) {
|
|
16
|
+
if (envelope[field] !== undefined && envelope[field] !== null) this[field] = envelope[field];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalized(error, fallbackKind) {
|
|
22
|
+
if (error instanceof MessagingClientError) 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.kind === 'string') return new MessagingClientError(envelope);
|
|
27
|
+
} catch {
|
|
28
|
+
// napi argument conversion errors have no mapped envelope.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (fallbackKind) {
|
|
32
|
+
return new MessagingClientError({
|
|
33
|
+
kind: fallbackKind,
|
|
34
|
+
message: fallbackKind === 'configuration'
|
|
35
|
+
? 'Messaging client configuration is invalid'
|
|
36
|
+
: 'Messaging client arguments are invalid',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function cloneJson(value, budget, depth) {
|
|
43
|
+
if (depth > MAX_JSON_DEPTH || ++budget.nodes > MAX_JSON_NODES) throw normalized({}, 'invalid_request');
|
|
44
|
+
if (value === null || typeof value === 'boolean') return value;
|
|
45
|
+
if (typeof value === 'string') {
|
|
46
|
+
budget.bytes += Buffer.byteLength(value, 'utf8');
|
|
47
|
+
if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, 'invalid_request');
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
if (typeof value === 'number') {
|
|
51
|
+
if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value))) {
|
|
52
|
+
throw normalized({}, 'invalid_request');
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
if (value === undefined || typeof value !== 'object' || isProxy(value)) {
|
|
57
|
+
throw normalized({}, 'invalid_request');
|
|
58
|
+
}
|
|
59
|
+
const array = Array.isArray(value);
|
|
60
|
+
const prototype = Object.getPrototypeOf(value);
|
|
61
|
+
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) {
|
|
62
|
+
throw normalized({}, 'invalid_request');
|
|
63
|
+
}
|
|
64
|
+
if (budget.active.has(value)) throw normalized({}, 'invalid_request');
|
|
65
|
+
budget.active.add(value);
|
|
66
|
+
try {
|
|
67
|
+
if (array) return value.map((member) => cloneJson(member, budget, depth + 1));
|
|
68
|
+
const result = Object.create(null);
|
|
69
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
70
|
+
if (typeof key !== 'string') throw normalized({}, 'invalid_request');
|
|
71
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
72
|
+
if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw normalized({}, 'invalid_request');
|
|
73
|
+
if (descriptor.enumerable) {
|
|
74
|
+
budget.bytes += Buffer.byteLength(key, 'utf8');
|
|
75
|
+
if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, 'invalid_request');
|
|
76
|
+
result[key] = cloneJson(descriptor.value, budget, depth + 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
} finally {
|
|
81
|
+
budget.active.delete(value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sanitize(value) {
|
|
86
|
+
return cloneJson(value, { nodes: 0, bytes: 0, active: new WeakSet() }, 0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const NativeMessagingClient = native.MessagingClient;
|
|
90
|
+
|
|
91
|
+
class MessagingClient {
|
|
92
|
+
constructor(config) {
|
|
93
|
+
try {
|
|
94
|
+
this.native = new NativeMessagingClient(sanitize(config));
|
|
95
|
+
} catch (error) {
|
|
96
|
+
throw normalized(error, 'configuration');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const [method, jsonIndexes] of [
|
|
102
|
+
['health', []],
|
|
103
|
+
['ready', []],
|
|
104
|
+
['submit', [2]],
|
|
105
|
+
['message', []],
|
|
106
|
+
['cancel', []],
|
|
107
|
+
['preview', [3]],
|
|
108
|
+
]) {
|
|
109
|
+
MessagingClient.prototype[method] = function (...args) {
|
|
110
|
+
try {
|
|
111
|
+
for (const index of jsonIndexes) {
|
|
112
|
+
if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index]);
|
|
113
|
+
}
|
|
114
|
+
return this.native[method](...args).catch((error) => { throw normalized(error); });
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw normalized(error, 'invalid_request');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = { MessagingClient, MessagingClientError };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* auto-generated by NAPI-RS */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
export declare class MessagingClient {
|
|
5
|
+
constructor(config: MessagingClientConfig)
|
|
6
|
+
health(): Promise<MessagingOutcome>
|
|
7
|
+
ready(): Promise<MessagingOutcome>
|
|
8
|
+
submit(token: string, idempotencyKey: string, request: any): Promise<MessagingOutcome>
|
|
9
|
+
message(token: string, messageId: string): Promise<MessagingOutcome>
|
|
10
|
+
cancel(token: string, messageId: string): Promise<MessagingOutcome>
|
|
11
|
+
preview(token: string, templateId: string, version: string, request: any): Promise<MessagingOutcome>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MessagingClientConfig {
|
|
15
|
+
baseUrl: string
|
|
16
|
+
requestTimeoutMilliseconds?: number
|
|
17
|
+
connectTimeoutMilliseconds?: number
|
|
18
|
+
maxResponseBytes?: number
|
|
19
|
+
userAgent?: string
|
|
20
|
+
trustedRootCertificates?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MessagingOutcome {
|
|
24
|
+
kind: string
|
|
25
|
+
value: any
|
|
26
|
+
traceId: string
|
|
27
|
+
}
|
package/native.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const PACKAGE_VERSION = '0.
|
|
4
|
-
const PRODUCTS = new Set(['discovery', 'evidence', 'relay', 'breg', 'casework']);
|
|
3
|
+
const PACKAGE_VERSION = '0.38.0';
|
|
4
|
+
const PRODUCTS = new Set(['discovery', 'evidence', 'relay', 'breg', 'casework', 'messaging']);
|
|
5
5
|
|
|
6
6
|
function target() {
|
|
7
7
|
if (process.platform === 'darwin' && process.arch === 'arm64') return 'darwin-arm64';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@registrystack/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Unified Node.js client for Registry Stack products.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://docs.registrystack.org/reference/client-api/",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"evidence",
|
|
15
15
|
"relay",
|
|
16
16
|
"breg",
|
|
17
|
-
"casework"
|
|
17
|
+
"casework",
|
|
18
|
+
"messaging"
|
|
18
19
|
],
|
|
19
20
|
"publishConfig": {
|
|
20
21
|
"access": "public",
|
|
@@ -41,7 +42,8 @@
|
|
|
41
42
|
"evidence",
|
|
42
43
|
"relay",
|
|
43
44
|
"breg",
|
|
44
|
-
"casework"
|
|
45
|
+
"casework",
|
|
46
|
+
"messaging"
|
|
45
47
|
],
|
|
46
48
|
"engines": {
|
|
47
49
|
"node": ">=22.12.0"
|
|
@@ -57,8 +59,8 @@
|
|
|
57
59
|
"typescript": "6.0.3"
|
|
58
60
|
},
|
|
59
61
|
"optionalDependencies": {
|
|
60
|
-
"@registrystack/client-darwin-arm64": "0.
|
|
61
|
-
"@registrystack/client-linux-arm64-gnu": "0.
|
|
62
|
-
"@registrystack/client-linux-x64-gnu": "0.
|
|
62
|
+
"@registrystack/client-darwin-arm64": "0.38.0",
|
|
63
|
+
"@registrystack/client-linux-arm64-gnu": "0.38.0",
|
|
64
|
+
"@registrystack/client-linux-x64-gnu": "0.38.0"
|
|
63
65
|
}
|
|
64
66
|
}
|