@registrystack/client 0.39.0 → 0.40.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
  }
@@ -109,11 +120,11 @@ for (const [method, jsonIndexes] of [
109
120
  MessagingClient.prototype[method] = function (...args) {
110
121
  try {
111
122
  for (const index of jsonIndexes) {
112
- if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index]);
123
+ if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index], 'invalid-request');
113
124
  }
114
- return this.native[method](...args).catch((error) => { throw normalized(error); });
125
+ return this.native[method](...args).catch((error) => { throw normalized(error, 'protocol'); });
115
126
  } catch (error) {
116
- throw normalized(error, 'invalid_request');
127
+ throw normalized(error, 'invalid-request');
117
128
  }
118
129
  };
119
130
  }
@@ -2,7 +2,7 @@
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>
@@ -11,15 +11,6 @@ export declare class MessagingClient {
11
11
  preview(token: string, templateId: string, version: string, request: any): Promise<MessagingOutcome>
12
12
  }
13
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
14
  export interface MessagingOutcome {
24
15
  kind: string
25
16
  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.40.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.40.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,7 +42,8 @@
41
42
  "evidence",
42
43
  "breg",
43
44
  "casework",
44
- "messaging"
45
+ "messaging",
46
+ "scheduling"
45
47
  ],
46
48
  "engines": {
47
49
  "node": ">=22.12.0"
@@ -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": "22.20.5",
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.40.0",
63
+ "@registrystack/client-linux-arm64-gnu": "0.40.0",
64
+ "@registrystack/client-linux-x64-gnu": "0.40.0"
63
65
  }
64
66
  }
@@ -0,0 +1,226 @@
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
+ | 'request.body-too-large'
174
+ | 'request.invalid'
175
+ | 'request.method-not-allowed'
176
+ | 'request.not-found'
177
+ | 'request.unprocessable'
178
+ | 'request.unsupported-media-type'
179
+ | 'resource.unavailable'
180
+ | 'revision.mismatch'
181
+ | 'schedule.unpublished'
182
+ | 'service.unavailable'
183
+ export type SchedulingProtocolFailure = 'header-bounds' | 'trace-context' | 'media-type' | 'body' | 'problem' | 'status' | 'protocol'
184
+
185
+ export class SchedulingClientError extends Error {
186
+ readonly kind: 'configuration' | 'invalid-request' | 'transport' | 'problem' | 'protocol'
187
+ /**
188
+ * Whether the request may have taken effect although this error was
189
+ * raised: a timeout or broken exchange after sending, an unusable answer,
190
+ * or a 5xx. When true, recover a keyed request by sending it again under
191
+ * the same idempotency key; a new key could apply it twice. False for a
192
+ * configuration or request defect, a connection never established, and
193
+ * every 4xx refusal. It is false for `410 idempotency.expired` too, but
194
+ * there an earlier attempt under the key was answered and may have
195
+ * committed: read an appointment by its external reference or identifier
196
+ * before choosing a new key. A hold cannot be read and expires on its own,
197
+ * so start a new request.
198
+ */
199
+ readonly outcomeUnknown: boolean
200
+ readonly code?: SchedulingProblemCode
201
+ readonly title?: string
202
+ readonly detail?: string
203
+ readonly status?: SafeInteger
204
+ readonly traceId?: string
205
+ readonly transportKind?: string
206
+ readonly protocolFailure?: SchedulingProtocolFailure
207
+ }
208
+
209
+ export class SchedulingClient {
210
+ constructor(config: SchedulingClientConfig)
211
+ getScheduling(token: string): Promise<SchedulingOutcome<SchedulingServiceDocument>>
212
+ listServices(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<ServiceDocument>>>
213
+ listOfferings(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<OfferingDocument>>>
214
+ availability(token: string, offering: string, query?: AvailabilityQuery | null): Promise<SchedulingOutcome<PageDocument<AvailabilityEntry>>>
215
+ explain(token: string, offering: string, start: Instant): Promise<SchedulingOutcome<ExplainDocument>>
216
+ createHold(token: string, idempotencyKey: string, request: AdmissionRequest): Promise<SchedulingOutcome<HoldDocument>>
217
+ releaseHold(token: string, holdId: string): Promise<SchedulingOutcome<null>>
218
+ createAppointment(token: string, idempotencyKey: string, request: CreateAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
219
+ getAppointment(token: string, appointmentId: string): Promise<SchedulingOutcome<AppointmentDocument>>
220
+ listAppointments(token: string, reference: ExternalReference, page?: PageQuery | null): Promise<SchedulingOutcome<PageDocument<AppointmentDocument>>>
221
+ rescheduleAppointment(token: string, appointmentId: string, idempotencyKey: string, request: RescheduleAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
222
+ cancelAppointment(token: string, appointmentId: string, idempotencyKey: string, request: CancelAppointmentRequest): Promise<SchedulingOutcome<AppointmentDocument>>
223
+ appointmentHistory(token: string, appointmentId: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<AppointmentHistoryEntryDocument>>>
224
+ listResources(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<ResourceDocument>>>
225
+ listLocations(token: string, cursor?: string | null): Promise<SchedulingOutcome<PageDocument<LocationDocument>>>
226
+ }
@@ -0,0 +1,141 @@
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
+ ['getAppointment', []],
122
+ ['listAppointments', [1, 2]],
123
+ ['rescheduleAppointment', [3]],
124
+ ['cancelAppointment', [3]],
125
+ ['appointmentHistory', []],
126
+ ['listResources', []],
127
+ ['listLocations', []],
128
+ ]) {
129
+ SchedulingClient.prototype[method] = function (...args) {
130
+ try {
131
+ for (const index of jsonIndexes) {
132
+ if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index], 'invalid-request');
133
+ }
134
+ return this.native[method](...args).catch((error) => { throw normalized(error, 'protocol'); });
135
+ } catch (error) {
136
+ throw normalized(error, 'invalid-request');
137
+ }
138
+ };
139
+ }
140
+
141
+ module.exports = { SchedulingClient, SchedulingClientError };
@@ -0,0 +1,27 @@
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
+ getAppointment(token: string, appointmentId: string): Promise<SchedulingOutcome>
15
+ listAppointments(token: string, reference: any, page?: any | undefined | null): Promise<SchedulingOutcome>
16
+ rescheduleAppointment(token: string, appointmentId: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
17
+ cancelAppointment(token: string, appointmentId: string, idempotencyKey: string, request: any): Promise<SchedulingOutcome>
18
+ appointmentHistory(token: string, appointmentId: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
19
+ listResources(token: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
20
+ listLocations(token: string, cursor?: string | undefined | null): Promise<SchedulingOutcome>
21
+ }
22
+
23
+ export interface SchedulingOutcome {
24
+ kind: string
25
+ value: any
26
+ traceId: string
27
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('../native').load('scheduling');