@registrystack/client 0.39.0 → 0.41.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.
@@ -12,12 +12,19 @@ class MessagingClientError extends Error {
12
12
  super(envelope.message);
13
13
  this.name = 'MessagingClientError';
14
14
  this.kind = envelope.kind;
15
+ this.outcomeUnknown = envelope.outcomeUnknown === true;
15
16
  for (const field of ['code', 'title', 'detail', 'status', 'traceId', 'retryAfterSeconds', 'transportKind', 'protocolFailure']) {
16
17
  if (envelope[field] !== undefined && envelope[field] !== null) this[field] = envelope[field];
17
18
  }
18
19
  }
19
20
  }
20
21
 
22
+ const FALLBACK_MESSAGES = {
23
+ configuration: 'Messaging client configuration is invalid',
24
+ 'invalid-request': 'Messaging client arguments are invalid',
25
+ protocol: 'Registry Messaging client failed',
26
+ };
27
+
21
28
  function normalized(error, fallbackKind) {
22
29
  if (error instanceof MessagingClientError) return error;
23
30
  if (error instanceof Error && typeof error.message === 'string') {
@@ -31,49 +38,51 @@ function normalized(error, fallbackKind) {
31
38
  if (fallbackKind) {
32
39
  return new MessagingClientError({
33
40
  kind: fallbackKind,
34
- message: fallbackKind === 'configuration'
35
- ? 'Messaging client configuration is invalid'
36
- : 'Messaging client arguments are invalid',
41
+ message: FALLBACK_MESSAGES[fallbackKind],
42
+ // A rejection after the native client took the call may follow an
43
+ // exchange, so it leaves the outcome unknown as a native protocol
44
+ // failure does.
45
+ outcomeUnknown: fallbackKind === 'protocol',
37
46
  });
38
47
  }
39
48
  return error;
40
49
  }
41
50
 
42
- function cloneJson(value, budget, depth) {
43
- if (depth > MAX_JSON_DEPTH || ++budget.nodes > MAX_JSON_NODES) throw normalized({}, 'invalid_request');
51
+ function cloneJson(value, budget, depth, kind) {
52
+ if (depth > MAX_JSON_DEPTH || ++budget.nodes > MAX_JSON_NODES) throw normalized({}, kind);
44
53
  if (value === null || typeof value === 'boolean') return value;
45
54
  if (typeof value === 'string') {
46
55
  budget.bytes += Buffer.byteLength(value, 'utf8');
47
- if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, 'invalid_request');
56
+ if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, kind);
48
57
  return value;
49
58
  }
50
59
  if (typeof value === 'number') {
51
60
  if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value))) {
52
- throw normalized({}, 'invalid_request');
61
+ throw normalized({}, kind);
53
62
  }
54
63
  return value;
55
64
  }
56
65
  if (value === undefined || typeof value !== 'object' || isProxy(value)) {
57
- throw normalized({}, 'invalid_request');
66
+ throw normalized({}, kind);
58
67
  }
59
68
  const array = Array.isArray(value);
60
69
  const prototype = Object.getPrototypeOf(value);
61
70
  if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) {
62
- throw normalized({}, 'invalid_request');
71
+ throw normalized({}, kind);
63
72
  }
64
- if (budget.active.has(value)) throw normalized({}, 'invalid_request');
73
+ if (budget.active.has(value)) throw normalized({}, kind);
65
74
  budget.active.add(value);
66
75
  try {
67
- if (array) return value.map((member) => cloneJson(member, budget, depth + 1));
76
+ if (array) return value.map((member) => cloneJson(member, budget, depth + 1, kind));
68
77
  const result = Object.create(null);
69
78
  for (const key of Reflect.ownKeys(value)) {
70
- if (typeof key !== 'string') throw normalized({}, 'invalid_request');
79
+ if (typeof key !== 'string') throw normalized({}, kind);
71
80
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
72
- if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw normalized({}, 'invalid_request');
81
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw normalized({}, kind);
73
82
  if (descriptor.enumerable) {
74
83
  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);
84
+ if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, kind);
85
+ result[key] = cloneJson(descriptor.value, budget, depth + 1, kind);
77
86
  }
78
87
  }
79
88
  return result;
@@ -82,8 +91,10 @@ function cloneJson(value, budget, depth) {
82
91
  }
83
92
  }
84
93
 
85
- function sanitize(value) {
86
- return cloneJson(value, { nodes: 0, bytes: 0, active: new WeakSet() }, 0);
94
+ // A refusal is reported as `kind`: `configuration` for the constructor's
95
+ // settings, `invalid-request` for a method's arguments.
96
+ function sanitize(value, kind) {
97
+ return cloneJson(value, { nodes: 0, bytes: 0, active: new WeakSet() }, 0, kind);
87
98
  }
88
99
 
89
100
  const NativeMessagingClient = native.MessagingClient;
@@ -91,7 +102,7 @@ const NativeMessagingClient = native.MessagingClient;
91
102
  class MessagingClient {
92
103
  constructor(config) {
93
104
  try {
94
- this.native = new NativeMessagingClient(sanitize(config));
105
+ this.native = new NativeMessagingClient(sanitize(config, 'configuration'));
95
106
  } catch (error) {
96
107
  throw normalized(error, 'configuration');
97
108
  }
@@ -102,6 +113,7 @@ for (const [method, jsonIndexes] of [
102
113
  ['health', []],
103
114
  ['ready', []],
104
115
  ['submit', [2]],
116
+ ['messageReceipt', [2]],
105
117
  ['message', []],
106
118
  ['cancel', []],
107
119
  ['preview', [3]],
@@ -109,11 +121,11 @@ for (const [method, jsonIndexes] of [
109
121
  MessagingClient.prototype[method] = function (...args) {
110
122
  try {
111
123
  for (const index of jsonIndexes) {
112
- if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index]);
124
+ if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index], 'invalid-request');
113
125
  }
114
- return this.native[method](...args).catch((error) => { throw normalized(error); });
126
+ return this.native[method](...args).catch((error) => { throw normalized(error, 'protocol'); });
115
127
  } catch (error) {
116
- throw normalized(error, 'invalid_request');
128
+ throw normalized(error, 'invalid-request');
117
129
  }
118
130
  };
119
131
  }
@@ -2,24 +2,16 @@
2
2
  /* eslint-disable */
3
3
 
4
4
  export declare class MessagingClient {
5
- constructor(config: MessagingClientConfig)
5
+ constructor(config: any)
6
6
  health(): Promise<MessagingOutcome>
7
7
  ready(): Promise<MessagingOutcome>
8
8
  submit(token: string, idempotencyKey: string, request: any): Promise<MessagingOutcome>
9
+ messageReceipt(token: string, idempotencyKey: string, request: any): Promise<MessagingOutcome>
9
10
  message(token: string, messageId: string): Promise<MessagingOutcome>
10
11
  cancel(token: string, messageId: string): Promise<MessagingOutcome>
11
12
  preview(token: string, templateId: string, version: string, request: any): Promise<MessagingOutcome>
12
13
  }
13
14
 
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
15
  export interface MessagingOutcome {
24
16
  kind: string
25
17
  value: any
package/native.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const PACKAGE_VERSION = '0.39.0';
4
- const PRODUCTS = new Set(['discovery', 'evidence', 'breg', 'casework', 'messaging']);
3
+ const PACKAGE_VERSION = '0.41.0';
4
+ const PRODUCTS = new Set(['discovery', 'evidence', 'breg', 'casework', 'messaging', 'scheduling']);
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.39.0",
3
+ "version": "0.41.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
  "breg",
16
16
  "casework",
17
- "messaging"
17
+ "messaging",
18
+ "scheduling"
18
19
  ],
19
20
  "publishConfig": {
20
21
  "access": "public",
@@ -41,10 +42,11 @@
41
42
  "evidence",
42
43
  "breg",
43
44
  "casework",
44
- "messaging"
45
+ "messaging",
46
+ "scheduling"
45
47
  ],
46
48
  "engines": {
47
- "node": ">=22.12.0"
49
+ "node": ">=24.11.0"
48
50
  },
49
51
  "scripts": {
50
52
  "sync": "python3 ../../release/scripts/sync-registry-client-node.py",
@@ -53,12 +55,12 @@
53
55
  "check:types": "tsc --noEmit --strict --skipLibCheck false --types node --moduleResolution node16 --module node16 --target es2022 index.d.ts __test__/facade.test-d.ts"
54
56
  },
55
57
  "devDependencies": {
56
- "@types/node": "22.20.4",
58
+ "@types/node": "24.19.1",
57
59
  "typescript": "6.0.3"
58
60
  },
59
61
  "optionalDependencies": {
60
- "@registrystack/client-darwin-arm64": "0.39.0",
61
- "@registrystack/client-linux-arm64-gnu": "0.39.0",
62
- "@registrystack/client-linux-x64-gnu": "0.39.0"
62
+ "@registrystack/client-darwin-arm64": "0.41.0",
63
+ "@registrystack/client-linux-arm64-gnu": "0.41.0",
64
+ "@registrystack/client-linux-x64-gnu": "0.41.0"
63
65
  }
64
66
  }
@@ -0,0 +1,229 @@
1
+ /** An integer for which `Number.isSafeInteger` is true. */
2
+ export type SafeInteger = number
3
+ /**
4
+ * An RFC 3339 instant. Instants the binding sends are parsed and normalized
5
+ * to UTC before any request; instants it answers are UTC.
6
+ */
7
+ export type Instant = string
8
+ export type JsonScalar = string | number | boolean | null
9
+ export type JsonValue = JsonScalar | ReadonlyArray<JsonValue> | { readonly [key: string]: JsonValue }
10
+
11
+ export interface SchedulingClientConfig {
12
+ baseUrl: string
13
+ requestTimeoutMilliseconds?: SafeInteger | null
14
+ connectTimeoutMilliseconds?: SafeInteger | null
15
+ maxResponseBytes?: SafeInteger | null
16
+ userAgent?: string | null
17
+ trustedRootCertificates?: string | null
18
+ /**
19
+ * How many times a keyed command (hold, appointment create, reschedule,
20
+ * cancel) whose outcome is unknown is resent, identically and under the
21
+ * same idempotency key, before its error is returned: a whole number from 0
22
+ * to 2, default 2; 0 sends it once. Any other value is a `configuration`
23
+ * error.
24
+ */
25
+ maxMutationRetries?: SafeInteger | null
26
+ }
27
+
28
+ /** An opaque record link Scheduling retains with a hold or appointment. */
29
+ export interface ExternalReference {
30
+ product: string
31
+ recordType: string
32
+ identifier: string
33
+ }
34
+
35
+ export interface PartyCounts { recipients: SafeInteger; attendees: SafeInteger }
36
+
37
+ /** One admission: the offering, start, and party a hold or booking asks capacity for. */
38
+ export interface AdmissionRequest {
39
+ offering: string
40
+ start: Instant
41
+ party: PartyCounts
42
+ channel?: string | null
43
+ duplicateKey?: string | null
44
+ policyRevision: SafeInteger
45
+ windowRevision?: SafeInteger | null
46
+ capabilities: ReadonlyArray<string>
47
+ prerequisites: ReadonlyArray<string>
48
+ externalReferences?: ReadonlyArray<ExternalReference>
49
+ }
50
+
51
+ /** Exactly one of `hold` (confirm a held claim) or `admission` (book directly). */
52
+ export type CreateAppointmentRequest =
53
+ | { hold: string; admission?: null }
54
+ | { admission: AdmissionRequest; hold?: null }
55
+
56
+ export interface RescheduleAppointmentRequest { observedRevision: SafeInteger; admission: AdmissionRequest }
57
+ export interface CancelAppointmentRequest { observedRevision: SafeInteger; reason?: string | null }
58
+
59
+ export interface AvailabilityQuery {
60
+ start?: Instant | null
61
+ end?: Instant | null
62
+ cursor?: string | null
63
+ limit?: SafeInteger | null
64
+ }
65
+ export interface PageQuery { cursor?: string | null; limit?: SafeInteger | null }
66
+
67
+ export interface SchedulingServiceDocument { schedulingId: string; policyRevision: SafeInteger; policyDigest: string }
68
+ export interface ServiceDocument { id: string; label: string }
69
+ export type SchedulingMode = 'exact-time' | 'arrival-window'
70
+ export interface ReminderDocument { minutesBefore: SafeInteger }
71
+ export interface WindowDocument { id: string; revision: SafeInteger; start: Instant; end: Instant; units: SafeInteger }
72
+ export interface OfferingDocument {
73
+ id: string
74
+ service: string
75
+ label: string
76
+ mode: SchedulingMode
77
+ location: string
78
+ leadTimeMinutes: SafeInteger
79
+ horizonDays: SafeInteger
80
+ cancellationCutoffMinutes: SafeInteger
81
+ durationMinutes: SafeInteger | null
82
+ bufferBeforeMinutes: SafeInteger | null
83
+ bufferAfterMinutes: SafeInteger | null
84
+ startIncrementMinutes: SafeInteger | null
85
+ maxRecipients: SafeInteger | null
86
+ window: WindowDocument | null
87
+ reminders: ReadonlyArray<ReminderDocument>
88
+ requiresCapabilities: ReadonlyArray<string>
89
+ prerequisites: ReadonlyArray<string>
90
+ }
91
+ export interface ResourceDocument { resourceId: string; pool: string; capabilities: ReadonlyArray<string>; available: boolean }
92
+ export interface LocationDocument { locationId: string; timezone: string }
93
+
94
+ /** Exact-time offerings answer grid slots; arrival-window offerings answer windows. */
95
+ export type AvailabilityEntry =
96
+ | { kind: 'slot'; start: Instant; end: Instant; free: SafeInteger }
97
+ | { kind: 'window'; window: string; start: Instant; end: Instant; remaining: SafeInteger; channelRemaining: SafeInteger | null }
98
+
99
+ export interface PageDocument<T> { items: ReadonlyArray<T>; nextCursor: string | null }
100
+
101
+ export interface HoldDocument {
102
+ holdId: string
103
+ offering: string
104
+ start: Instant
105
+ end: Instant
106
+ resource: string | null
107
+ units: SafeInteger
108
+ expiresAt: Instant
109
+ policyRevision: SafeInteger
110
+ externalReferences: ReadonlyArray<ExternalReference>
111
+ }
112
+
113
+ export type AppointmentState = 'confirmed' | 'cancelled'
114
+ export interface AppointmentDocument {
115
+ appointmentId: string
116
+ offering: string
117
+ start: Instant
118
+ end: Instant
119
+ resource: string | null
120
+ units: SafeInteger
121
+ channel: string | null
122
+ revision: SafeInteger
123
+ state: AppointmentState
124
+ policyRevision: SafeInteger
125
+ createdAt: Instant
126
+ cancelledAt: Instant | null
127
+ externalReferences: ReadonlyArray<ExternalReference>
128
+ }
129
+
130
+ export interface AppointmentHistoryEntryDocument {
131
+ eventId: string
132
+ kind: string
133
+ revision: SafeInteger
134
+ occurredAt: Instant
135
+ actor: string | null
136
+ detail: JsonValue
137
+ }
138
+
139
+ export interface ExplainDocument {
140
+ offering: string
141
+ start: Instant
142
+ publicCode?: string
143
+ detailedCode?: string
144
+ explanation?: string
145
+ }
146
+
147
+ export interface SchedulingOutcome<T> { kind: 'complete'; value: T; traceId: string }
148
+
149
+ /** The closed catalogue: the client answers any other code as a protocol failure. */
150
+ export type SchedulingProblemCode =
151
+ | 'authentication.refused'
152
+ | 'booking.duplicate-active'
153
+ | 'cancellation.cutoff-passed'
154
+ | 'capability.unmatched'
155
+ | 'capacity.exhausted'
156
+ | 'cursor.expired'
157
+ | 'cursor.invalid'
158
+ | 'eligibility.unavailable'
159
+ | 'hold.expired'
160
+ | 'hold.released'
161
+ | 'hook.unavailable'
162
+ | 'horizon.outside'
163
+ | 'idempotency.expired'
164
+ | 'idempotency.key-reused'
165
+ | 'location.closed'
166
+ | 'operation.not-authorized'
167
+ | 'party.capacity-inadequate'
168
+ | 'policy.changed'
169
+ | 'precondition.failed'
170
+ | 'precondition.required'
171
+ | 'prerequisite.missing'
172
+ | 'profile.not-authorized'
173
+ | 'receipt.unresolved'
174
+ | 'request.body-too-large'
175
+ | 'request.invalid'
176
+ | 'request.method-not-allowed'
177
+ | 'request.not-found'
178
+ | 'request.unprocessable'
179
+ | 'request.unsupported-media-type'
180
+ | 'resource.unavailable'
181
+ | 'revision.mismatch'
182
+ | 'schedule.unpublished'
183
+ | 'service.unavailable'
184
+ export type SchedulingProtocolFailure = 'header-bounds' | 'trace-context' | 'media-type' | 'body' | 'problem' | 'status' | 'protocol'
185
+
186
+ export class SchedulingClientError extends Error {
187
+ readonly kind: 'configuration' | 'invalid-request' | 'transport' | 'problem' | 'protocol'
188
+ /**
189
+ * Whether the request may have taken effect although this error was
190
+ * raised: a timeout or broken exchange after sending, an unusable answer,
191
+ * or a 5xx. When true, recover a keyed request by sending it again under
192
+ * the same idempotency key; a new key could apply it twice. False for a
193
+ * configuration or request defect, a connection never established, and
194
+ * every 4xx refusal. It is false for `410 idempotency.expired` too, but
195
+ * there an earlier attempt under the key was answered and may have
196
+ * committed: read an appointment by its external reference or identifier
197
+ * before choosing a new key. A hold cannot be read and expires on its own,
198
+ * so start a new request.
199
+ */
200
+ readonly outcomeUnknown: boolean
201
+ readonly code?: SchedulingProblemCode
202
+ readonly title?: string
203
+ readonly detail?: string
204
+ readonly status?: SafeInteger
205
+ readonly traceId?: string
206
+ readonly transportKind?: string
207
+ readonly protocolFailure?: SchedulingProtocolFailure
208
+ }
209
+
210
+ export class SchedulingClient {
211
+ constructor(config: SchedulingClientConfig)
212
+ getScheduling(token: string): Promise<SchedulingOutcome<SchedulingServiceDocument>>
213
+ listServices(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<ServiceDocument>>>
214
+ listOfferings(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<OfferingDocument>>>
215
+ availability(token: string, offering: string, query?: AvailabilityQuery | null): Promise<SchedulingOutcome<PageDocument<AvailabilityEntry>>>
216
+ explain(token: string, offering: string, start: Instant): Promise<SchedulingOutcome<ExplainDocument>>
217
+ createHold(token: string, idempotencyKey: string, request: AdmissionRequest): Promise<SchedulingOutcome<HoldDocument>>
218
+ releaseHold(token: string, holdId: string): Promise<SchedulingOutcome<null>>
219
+ createAppointment(token: string, idempotencyKey: string, request: CreateAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
220
+ /** Observe the original success using the same body/key and current token; never resend or mutate. */
221
+ appointmentReceipt(token: string, idempotencyKey: string, request: CreateAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
222
+ getAppointment(token: string, appointmentId: string): Promise<SchedulingOutcome<AppointmentDocument>>
223
+ listAppointments(token: string, reference: ExternalReference, page?: PageQuery | null): Promise<SchedulingOutcome<PageDocument<AppointmentDocument>>>
224
+ rescheduleAppointment(token: string, appointmentId: string, idempotencyKey: string, request: RescheduleAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
225
+ cancelAppointment(token: string, appointmentId: string, idempotencyKey: string, request: CancelAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
226
+ appointmentHistory(token: string, appointmentId: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<AppointmentHistoryEntryDocument>>>
227
+ listResources(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<ResourceDocument>>>
228
+ listLocations(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<LocationDocument>>>
229
+ }
@@ -0,0 +1,142 @@
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 SchedulingClientError extends Error {
11
+ constructor(envelope) {
12
+ super(envelope.message);
13
+ this.name = 'SchedulingClientError';
14
+ this.kind = envelope.kind;
15
+ this.outcomeUnknown = envelope.outcomeUnknown === true;
16
+ for (const field of ['code', 'title', 'detail', 'status', 'traceId', 'transportKind', 'protocolFailure']) {
17
+ if (envelope[field] !== undefined && envelope[field] !== null) this[field] = envelope[field];
18
+ }
19
+ }
20
+ }
21
+
22
+ const FALLBACK_MESSAGES = {
23
+ configuration: 'Scheduling client configuration is invalid',
24
+ 'invalid-request': 'Scheduling client arguments are invalid',
25
+ protocol: 'Registry Scheduling client failed',
26
+ };
27
+
28
+ function normalized(error, fallbackKind) {
29
+ if (error instanceof SchedulingClientError) return error;
30
+ if (error instanceof Error && typeof error.message === 'string') {
31
+ try {
32
+ const envelope = JSON.parse(error.message);
33
+ if (envelope && typeof envelope.kind === 'string') return new SchedulingClientError(envelope);
34
+ } catch {
35
+ // napi argument conversion errors have no mapped envelope.
36
+ }
37
+ }
38
+ if (fallbackKind) {
39
+ return new SchedulingClientError({
40
+ kind: fallbackKind,
41
+ message: FALLBACK_MESSAGES[fallbackKind],
42
+ // A rejection after the native client took the call may follow an
43
+ // exchange, so it leaves the outcome unknown as a native protocol
44
+ // failure does.
45
+ outcomeUnknown: fallbackKind === 'protocol',
46
+ });
47
+ }
48
+ return error;
49
+ }
50
+
51
+ function cloneJson(value, budget, depth, kind) {
52
+ if (depth > MAX_JSON_DEPTH || ++budget.nodes > MAX_JSON_NODES) throw normalized({}, kind);
53
+ if (value === null || typeof value === 'boolean') return value;
54
+ if (typeof value === 'string') {
55
+ budget.bytes += Buffer.byteLength(value, 'utf8');
56
+ if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, kind);
57
+ return value;
58
+ }
59
+ if (typeof value === 'number') {
60
+ if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value))) {
61
+ throw normalized({}, kind);
62
+ }
63
+ return value;
64
+ }
65
+ if (value === undefined || typeof value !== 'object' || isProxy(value)) {
66
+ throw normalized({}, kind);
67
+ }
68
+ const array = Array.isArray(value);
69
+ const prototype = Object.getPrototypeOf(value);
70
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) {
71
+ throw normalized({}, kind);
72
+ }
73
+ if (budget.active.has(value)) throw normalized({}, kind);
74
+ budget.active.add(value);
75
+ try {
76
+ if (array) return value.map((member) => cloneJson(member, budget, depth + 1, kind));
77
+ const result = Object.create(null);
78
+ for (const key of Reflect.ownKeys(value)) {
79
+ if (typeof key !== 'string') throw normalized({}, kind);
80
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
81
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw normalized({}, kind);
82
+ if (descriptor.enumerable) {
83
+ budget.bytes += Buffer.byteLength(key, 'utf8');
84
+ if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, kind);
85
+ result[key] = cloneJson(descriptor.value, budget, depth + 1, kind);
86
+ }
87
+ }
88
+ return result;
89
+ } finally {
90
+ budget.active.delete(value);
91
+ }
92
+ }
93
+
94
+ // A refusal is reported as `kind`: `configuration` for the constructor's
95
+ // settings, `invalid-request` for a method's arguments.
96
+ function sanitize(value, kind) {
97
+ return cloneJson(value, { nodes: 0, bytes: 0, active: new WeakSet() }, 0, kind);
98
+ }
99
+
100
+ const NativeSchedulingClient = native.SchedulingClient;
101
+
102
+ class SchedulingClient {
103
+ constructor(config) {
104
+ try {
105
+ this.native = new NativeSchedulingClient(sanitize(config, 'configuration'));
106
+ } catch (error) {
107
+ throw normalized(error, 'configuration');
108
+ }
109
+ }
110
+ }
111
+
112
+ for (const [method, jsonIndexes] of [
113
+ ['getScheduling', []],
114
+ ['listServices', []],
115
+ ['listOfferings', []],
116
+ ['availability', [2]],
117
+ ['explain', []],
118
+ ['createHold', [2]],
119
+ ['releaseHold', []],
120
+ ['createAppointment', [2]],
121
+ ['appointmentReceipt', [2]],
122
+ ['getAppointment', []],
123
+ ['listAppointments', [1, 2]],
124
+ ['rescheduleAppointment', [3]],
125
+ ['cancelAppointment', [3]],
126
+ ['appointmentHistory', []],
127
+ ['listResources', []],
128
+ ['listLocations', []],
129
+ ]) {
130
+ SchedulingClient.prototype[method] = function (...args) {
131
+ try {
132
+ for (const index of jsonIndexes) {
133
+ if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index], 'invalid-request');
134
+ }
135
+ return this.native[method](...args).catch((error) => { throw normalized(error, 'protocol'); });
136
+ } catch (error) {
137
+ throw normalized(error, 'invalid-request');
138
+ }
139
+ };
140
+ }
141
+
142
+ module.exports = { SchedulingClient, SchedulingClientError };
@@ -0,0 +1,28 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+
4
+ export declare class SchedulingClient {
5
+ constructor(config: any)
6
+ getScheduling(token: string): Promise<SchedulingOutcome>
7
+ listServices(token: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
8
+ listOfferings(token: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
9
+ availability(token: string, offering: string, query?: any | undefined | null): Promise<SchedulingOutcome>
10
+ explain(token: string, offering: string, start: string): Promise<SchedulingOutcome>
11
+ createHold(token: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
12
+ releaseHold(token: string, holdId: string): Promise<SchedulingOutcome>
13
+ createAppointment(token: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
14
+ appointmentReceipt(token: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
15
+ getAppointment(token: string, appointmentId: string): Promise<SchedulingOutcome>
16
+ listAppointments(token: string, reference: any, page?: any | undefined | null): Promise<SchedulingOutcome>
17
+ rescheduleAppointment(token: string, appointmentId: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
18
+ cancelAppointment(token: string, appointmentId: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
19
+ appointmentHistory(token: string, appointmentId: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
20
+ listResources(token: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
21
+ listLocations(token: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
22
+ }
23
+
24
+ export interface SchedulingOutcome {
25
+ kind: string
26
+ value: any
27
+ traceId: string
28
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('../native').load('scheduling');