@registrystack/client 0.37.0 → 0.39.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,7 +1,7 @@
1
1
  # @registrystack/client
2
2
 
3
- One versioned Node.js package for the Discovery, Evidence, Relay, Base Registry
4
- Engine, and Casework client APIs in Registry Stack.
3
+ One versioned Node.js package for the Discovery, Evidence, Base Registry
4
+ Engine, Casework, and Messaging client APIs in Registry Stack.
5
5
 
6
6
  ## Install
7
7
 
@@ -17,7 +17,7 @@ bindings.
17
17
  ## Usage
18
18
 
19
19
  ```js
20
- const { discovery, evidence, relay, breg, casework } = require('@registrystack/client');
20
+ const { discovery, evidence, 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
@@ -33,11 +36,12 @@ const work = new casework.CaseworkClient({
33
36
  curated index.
34
37
  - `evidence`: Evidence Gateway, request and verify signed minimum-disclosure
35
38
  assertions.
36
- - `relay`: Registry Relay, scoped read-only APIs over existing sources.
37
39
  - `breg`: Base Registry Engine, records, contracts, writes, lifecycle, and typed
38
40
  applied-request result navigation.
39
41
  - `casework`: Registry Casework, staff inbox, claims, drafts, decisions,
40
42
  recovery, history, holdings, and directory bootstrap.
43
+ - `messaging`: Registry Messaging, submit, inspect, cancel, and preview
44
+ institution-owned messages.
41
45
 
42
46
  Each product remains in its own namespace because its routing,
43
47
  authentication, errors, and verification rules are different.
@@ -99,6 +103,9 @@ is active.
99
103
  The `casework` namespace is part of the unified package beginning with
100
104
  Registry Stack v0.30.0.
101
105
 
106
+ The `messaging` namespace is part of the unified package beginning with
107
+ Registry Stack v0.38.0.
108
+
102
109
  ## Casework notes
103
110
 
104
111
  The Casework module is for a trusted server host. Each call takes that request's
package/breg/client.d.ts CHANGED
@@ -349,8 +349,17 @@ export interface RawOutcome {
349
349
  mediaType: string
350
350
  traceId: string
351
351
  etag?: string
352
+ reprDigest?: string
352
353
  }
353
354
 
355
+ export type StatisticsFormat = 'json' | 'csv'
356
+ export type StatisticsReleaseStatus = 'provisional' | 'final'
357
+ export type StatisticsReleaseSelection = 'any' | 'final'
358
+ export type StatisticsWithdrawalReason =
359
+ | 'computation-error'
360
+ | 'source-data-error'
361
+ | 'disclosure-risk'
362
+
354
363
  export interface PatchOperation {
355
364
  op: 'add' | 'replace' | 'test'
356
365
  field: string
@@ -986,6 +995,8 @@ export interface BaseRegistryClientFailure extends Error {
986
995
  readonly code?: string
987
996
  readonly planRefusal?: string
988
997
  readonly refusalCode?: string
998
+ readonly reasonCode?: string
999
+ readonly fieldPath?: string
989
1000
  readonly status?: number
990
1001
  readonly traceId?: string
991
1002
  readonly transportKind?: string
@@ -997,6 +1008,8 @@ export declare class BaseRegistryClientError extends Error implements BaseRegist
997
1008
  readonly code?: string
998
1009
  readonly planRefusal?: string
999
1010
  readonly refusalCode?: string
1011
+ readonly reasonCode?: string
1012
+ readonly fieldPath?: string
1000
1013
  readonly status?: number
1001
1014
  readonly traceId?: string
1002
1015
  readonly transportKind?: string
@@ -1009,6 +1022,13 @@ export declare class BaseRegistryClient {
1009
1022
  ready(): Promise<CompleteOutcome<ProbeStatus>>
1010
1023
  openapi(accessProfile?: string | null): Promise<RawOutcome>
1011
1024
  registryMetadata(accessProfile?: string | null): Promise<RawOutcome>
1025
+ statisticsLive(dataset: string, from?: string | null, to?: string | null, accessProfile?: string | null, format?: StatisticsFormat | null): Promise<RawOutcome>
1026
+ statisticsReleases(dataset: string, top?: SafeInteger | null, skipToken?: string | null, accessProfile?: string | null): Promise<RawOutcome>
1027
+ statisticsLatestRelease(dataset: string, period: string, selection: StatisticsReleaseSelection, accessProfile?: string | null, format?: StatisticsFormat | null): Promise<RawOutcome>
1028
+ statisticsReleaseVersion(dataset: string, period: string, version: SafeInteger, accessProfile?: string | null, format?: StatisticsFormat | null): Promise<RawOutcome>
1029
+ statisticsReleaseSeries(dataset: string, from: string, to: string, selection: StatisticsReleaseSelection, accessProfile?: string | null, format?: StatisticsFormat | null): Promise<RawOutcome>
1030
+ statisticsPublish(dataset: string, period: string, status: StatisticsReleaseStatus, accessProfile: string, idempotencyKey: string): Promise<RawOutcome>
1031
+ statisticsWithdraw(dataset: string, period: string, version: SafeInteger, reason: StatisticsWithdrawalReason, accessProfile: string, idempotencyKey: string): Promise<RawOutcome>
1012
1032
  registryContract(accessProfile?: string | null): Promise<BRegMetadata>
1013
1033
  entitySchema(entityIdentifier: string, accessProfile?: string | null): Promise<RawOutcome>
1014
1034
  recordRevisions(entityRoute: string, recordIdentifier: string, accessProfile?: string | null): Promise<RawOutcome>
package/breg/client.js CHANGED
@@ -12,9 +12,17 @@ class BaseRegistryClientError extends Error {
12
12
  super(envelope.message);
13
13
  this.name = 'BaseRegistryClientError';
14
14
  this.kind = envelope.kind;
15
- for (const field of ['code', 'planRefusal', 'refusalCode', 'status', 'traceId', 'transportKind', 'tokenKind']) {
15
+ for (const field of ['code', 'planRefusal', 'refusalCode', 'reasonCode', 'status', 'traceId', 'transportKind', 'tokenKind']) {
16
16
  if (envelope[field] !== undefined && envelope[field] !== null) this[field] = envelope[field];
17
17
  }
18
+ if (envelope.fieldPath !== undefined && envelope.fieldPath !== null) {
19
+ Object.defineProperty(this, 'fieldPath', {
20
+ value: envelope.fieldPath,
21
+ configurable: true,
22
+ enumerable: false,
23
+ writable: true,
24
+ });
25
+ }
18
26
  }
19
27
  }
20
28
 
@@ -197,6 +205,9 @@ function wrapAsync(name, jsonIndexes = [], requiredIndexes = []) {
197
205
 
198
206
  for (const [method, jsonIndexes, requiredIndexes] of [
199
207
  ['health'], ['ready'], ['openapi'], ['registryMetadata'], ['registryContract'], ['entitySchema'],
208
+ ['statisticsLive'], ['statisticsReleases'], ['statisticsLatestRelease'],
209
+ ['statisticsReleaseVersion'], ['statisticsReleaseSeries'], ['statisticsPublish'],
210
+ ['statisticsWithdraw'],
200
211
  ['recordRevisions'], ['getRecordRevision', [3]],
201
212
  ['getRecord', [2]], ['listRecords', [1]], ['continueList', [0], [0]],
202
213
  ['getGeoJsonRecord', [2]], ['listGeoJsonRecords', [1]], ['continueGeoJsonList', [0], [0]],
package/breg/index.d.ts CHANGED
@@ -7,6 +7,13 @@ export declare class BaseRegistryClient {
7
7
  ready(): Promise<CompleteOutcome>
8
8
  openapi(accessProfile?: string | undefined | null): Promise<RawOutcome>
9
9
  registryMetadata(accessProfile?: string | undefined | null): Promise<RawOutcome>
10
+ statisticsLive(dataset: string, from?: string | undefined | null, to?: string | undefined | null, accessProfile?: string | undefined | null, format?: string | undefined | null): Promise<RawOutcome>
11
+ statisticsReleases(dataset: string, top?: number | undefined | null, skipToken?: string | undefined | null, accessProfile?: string | undefined | null): Promise<RawOutcome>
12
+ statisticsLatestRelease(dataset: string, period: string, selection: string, accessProfile?: string | undefined | null, format?: string | undefined | null): Promise<RawOutcome>
13
+ statisticsReleaseVersion(dataset: string, period: string, version: number, accessProfile?: string | undefined | null, format?: string | undefined | null): Promise<RawOutcome>
14
+ statisticsReleaseSeries(dataset: string, from: string, to: string, selection: string, accessProfile?: string | undefined | null, format?: string | undefined | null): Promise<RawOutcome>
15
+ statisticsPublish(dataset: string, period: string, status: string, accessProfile: string, idempotencyKey: string): Promise<RawOutcome>
16
+ statisticsWithdraw(dataset: string, period: string, version: number, reason: string, accessProfile: string, idempotencyKey: string): Promise<RawOutcome>
10
17
  registryContract(accessProfile?: string | undefined | null): Promise<BRegMetadata>
11
18
  entitySchema(entityIdentifier: string, accessProfile?: string | undefined | null): Promise<RawOutcome>
12
19
  /** Retrieve one bounded first page of record revisions as inert JSON. */
@@ -369,6 +376,7 @@ export interface RawOutcome {
369
376
  mediaType: string
370
377
  traceId: string
371
378
  etag?: string
379
+ reprDigest?: string
372
380
  }
373
381
 
374
382
  /** Authenticated BReg webhook metadata and exact body. */
@@ -1,4 +1,4 @@
1
- export type ServiceKind = 'evidence' | 'relay';
1
+ export type ServiceKind = 'evidence';
2
2
 
3
3
  export interface DiscoveryClientOptions {
4
4
  baseUrl: string;
@@ -32,8 +32,6 @@ export interface ServiceFilters {
32
32
  jurisdiction?: string[];
33
33
  conformsTo?: string[];
34
34
  evidenceType?: string[];
35
- semanticClass?: string[];
36
- operationFamily?: string[];
37
35
  }
38
36
 
39
37
  export interface EvidenceServiceQuery {
@@ -43,17 +41,6 @@ export interface EvidenceServiceQuery {
43
41
  conformsTo?: string[];
44
42
  }
45
43
 
46
- interface RelayServiceQueryOptions {
47
- jurisdiction?: string;
48
- serviceIds?: string[];
49
- conformsTo?: string[];
50
- }
51
-
52
- export type RelayServiceQuery = RelayServiceQueryOptions & (
53
- | { semanticClassId: string; operationFamilyId?: string }
54
- | { semanticClassId?: string; operationFamilyId: string }
55
- );
56
-
57
44
  export interface ServiceRecord {
58
45
  recordId: string;
59
46
  bindingId: string;
@@ -83,10 +70,7 @@ export interface ServiceSearchResponse {
83
70
  items: ServiceRecord[];
84
71
  }
85
72
 
86
- export type MatchedCapability =
87
- | { kind: 'evidence-type'; id: string }
88
- | { kind: 'semantic-class'; id: string }
89
- | { kind: 'operation-family'; id: string };
73
+ export type MatchedCapability = { kind: 'evidence-type'; id: string };
90
74
 
91
75
  export interface SelectionRequest {
92
76
  recordId: string;
@@ -110,15 +94,6 @@ export interface EvidenceSelectionRequest {
110
94
  resolution?: EvidenceResolutionContext;
111
95
  }
112
96
 
113
- export type RelayCapabilityMatch =
114
- | { semanticClassId: string; operationFamilyId?: string }
115
- | { semanticClassId?: string; operationFamilyId: string };
116
-
117
- export interface RelaySelectionRequest {
118
- recordId: string;
119
- capabilityMatch: RelayCapabilityMatch;
120
- }
121
-
122
97
  export interface CommonServiceSelection extends Omit<ServiceRecord, 'title' | 'description'> {
123
98
  matchedCapability: MatchedCapability;
124
99
  catalogRevision: string;
@@ -131,14 +106,6 @@ export interface EvidenceServiceSelection extends CommonServiceSelection {
131
106
  evidenceResolution?: EvidenceResolutionContext;
132
107
  }
133
108
 
134
- export interface RelayServiceSelection extends CommonServiceSelection {
135
- serviceKind: 'relay';
136
- matchedCapability:
137
- | { kind: 'semantic-class'; id: string }
138
- | { kind: 'operation-family'; id: string };
139
- relayCapabilityMatch: RelayCapabilityMatch;
140
- }
141
-
142
109
  export type ServiceSelection = CommonServiceSelection;
143
110
 
144
111
  export type DiscoveryClientErrorKind =
@@ -168,7 +135,6 @@ export class DiscoveryClient {
168
135
  resolveEvidenceTypes(request: EvidenceTypeResolveRequest): Promise<EvidenceTypeResolveResponse>;
169
136
  searchServices(filters?: ServiceFilters): Promise<ServiceSearchResponse>;
170
137
  searchEvidenceServices(query: EvidenceServiceQuery): Promise<ServiceSearchResponse>;
171
- searchRelayServices(query: RelayServiceQuery): Promise<ServiceSearchResponse>;
172
138
  selectExact(response: ServiceSearchResponse, request: SelectionRequest): CommonServiceSelection;
173
139
  selectEvidenceAlternative(
174
140
  response: EvidenceTypeResolveResponse,
@@ -178,10 +144,6 @@ export class DiscoveryClient {
178
144
  response: ServiceSearchResponse,
179
145
  request: EvidenceSelectionRequest,
180
146
  ): EvidenceServiceSelection;
181
- selectRelayService(
182
- response: ServiceSearchResponse,
183
- request: RelaySelectionRequest,
184
- ): RelayServiceSelection;
185
147
  }
186
148
 
187
149
  export function selectExact(
@@ -199,11 +161,6 @@ export function selectEvidenceService(
199
161
  request: EvidenceSelectionRequest,
200
162
  ): EvidenceServiceSelection;
201
163
 
202
- export function selectRelayService(
203
- response: ServiceSearchResponse,
204
- request: RelaySelectionRequest,
205
- ): RelayServiceSelection;
206
-
207
164
  /**
208
165
  * Validate closed shape and capability binding only.
209
166
  *
@@ -222,14 +222,6 @@ class DiscoveryClient {
222
222
  }
223
223
  }
224
224
 
225
- async searchRelayServices(query) {
226
- try {
227
- return await this.#inner.searchRelayServices(requestValue(query));
228
- } catch (error) {
229
- throw normalize(error, 'query');
230
- }
231
- }
232
-
233
225
  selectExact(response, request) {
234
226
  try {
235
227
  return this.#inner.selectExact(responseValue(response), requestValue(request));
@@ -246,9 +238,6 @@ class DiscoveryClient {
246
238
  return selectEvidenceService(response, request);
247
239
  }
248
240
 
249
- selectRelayService(response, request) {
250
- return selectRelayService(response, request);
251
- }
252
241
  }
253
242
 
254
243
  function selectExact(response, request) {
@@ -278,14 +267,6 @@ function selectEvidenceService(response, request) {
278
267
  }
279
268
  }
280
269
 
281
- function selectRelayService(response, request) {
282
- try {
283
- return native.selectRelayService(responseValue(response), requestValue(request));
284
- } catch (error) {
285
- throw normalize(error, 'query');
286
- }
287
- }
288
-
289
270
  function validateSelectionStructure(selection) {
290
271
  try {
291
272
  return native.validateSelectionStructure(requestValue(selection));
@@ -323,7 +304,6 @@ module.exports = {
323
304
  selectEvidenceAlternative,
324
305
  selectEvidenceService,
325
306
  selectExact,
326
- selectRelayService,
327
307
  validateSelection,
328
308
  validateSelectionStructure,
329
309
  };
@@ -6,11 +6,9 @@ export declare class DiscoveryClient {
6
6
  resolveEvidenceTypes(request: any): Promise<any>
7
7
  searchServices(filters: any): Promise<any>
8
8
  searchEvidenceServices(query: any): Promise<any>
9
- searchRelayServices(query: any): Promise<any>
10
9
  selectExact(response: any, request: any): any
11
10
  selectEvidenceAlternative(response: any, evidenceTypeListId?: string | undefined | null): any
12
11
  selectEvidenceService(response: any, request: any): any
13
- selectRelayService(response: any, request: any): any
14
12
  }
15
13
 
16
14
  export interface DiscoveryClientOptions {
@@ -29,8 +27,6 @@ export declare function selectEvidenceService(response: any, request: any): any
29
27
 
30
28
  export declare function selectExact(response: any, request: any): any
31
29
 
32
- export declare function selectRelayService(response: any, request: any): any
33
-
34
30
  export declare function validateSelection(selection: any): any
35
31
 
36
32
  export declare function validateSelectionStructure(selection: any): any
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as discovery from './discovery/client'
2
2
  import * as evidence from './evidence/client'
3
- import * as relay from './relay/client'
4
3
  import * as breg from './breg/client'
5
4
  import * as casework from './casework/client'
5
+ import * as messaging from './messaging/client'
6
6
 
7
- export { discovery, evidence, relay, breg, casework }
7
+ export { discovery, evidence, breg, casework, messaging }
package/index.js CHANGED
@@ -5,6 +5,6 @@
5
5
  // consumer imports, and `require()` sees the same five properties either way.
6
6
  exports.discovery = require('./discovery/client');
7
7
  exports.evidence = require('./evidence/client');
8
- exports.relay = require('./relay/client');
9
8
  exports.breg = require('./breg/client');
10
9
  exports.casework = require('./casework/client');
10
+ 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
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('../native').load('messaging');
package/native.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const PACKAGE_VERSION = '0.37.0';
4
- const PRODUCTS = new Set(['discovery', 'evidence', 'relay', 'breg', 'casework']);
3
+ const PACKAGE_VERSION = '0.39.0';
4
+ const PRODUCTS = new Set(['discovery', 'evidence', 'breg', 'casework', 'messaging']);
5
5
 
6
6
  function target() {
7
7
  if (process.platform === 'darwin' && process.arch === 'arm64') return 'darwin-arm64';