@registrystack/client 0.38.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.
@@ -55,7 +55,7 @@ function inputError(kind) {
55
55
 
56
56
  function localAcceptanceError() {
57
57
  return new DiscoveryClientError({
58
- kind: 'local_acceptance_refused',
58
+ kind: 'local-acceptance-refused',
59
59
  message: 'the relying application refused the advertised service',
60
60
  });
61
61
  }
@@ -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
@@ -351,7 +351,7 @@ export interface EvidenceRequestBatchSpec {
351
351
  * is `protocol`
352
352
  * - `code`: `denied`, `protocol` (optional), `verification`, and any `token`
353
353
  * failure whose `tokenKind` is `refused`
354
- * - `traceId`: `denied`, `not_available`, `protocol` (all optional)
354
+ * - `traceId`: `denied`, `not-available`, `protocol` (all optional)
355
355
  * - `retryAfterSeconds`: `denied`, `protocol` (both optional)
356
356
  * - `transportKind`: `transport`, and any `token` failure whose `tokenKind`
357
357
  * is `transport`
@@ -154,11 +154,8 @@ export declare class EvidenceClient {
154
154
  export declare class PreparedEvidenceRequest {
155
155
  /** The nonce this request carries. Retain it with the transaction record. */
156
156
  get requestNonce(): string
157
- /**
158
- * The closed verification policy, with the subject set as `prepare` left
159
- * it.
160
- */
161
- get policyDocument(): any
157
+ /** The complete enveloped policy, with the subject set as `prepare` left it. */
158
+ get policyDocument(): { apiVersion: 'id.registrystack.org/formats/evidence/verification-policy/v1'; kind: 'EvidenceVerificationPolicy'; [field: string]: unknown }
162
159
  /**
163
160
  * `"acceptFirstUse"` or `{ pinned: [{ role, binding }, ...] }`, exactly as
164
161
  * this request was prepared.
@@ -178,7 +175,7 @@ export declare class PreparedEvidenceRequestBatch {
178
175
  /** Independently generated item nonces in request order. */
179
176
  get requestNonces(): Array<string>
180
177
  /** Independently closed policy documents in request order. */
181
- get policyDocuments(): Array<any>
178
+ get policyDocuments(): Array<{ apiVersion: 'id.registrystack.org/formats/evidence/verification-policy/v1'; kind: 'EvidenceVerificationPolicy'; [field: string]: unknown }>
182
179
  /** Subject-verification stances in request order. */
183
180
  get subjectExpectations(): Array<any>
184
181
  /** Number of positional requests in this batch. */
package/index.d.ts CHANGED
@@ -1,8 +1,8 @@
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'
6
5
  import * as messaging from './messaging/client'
6
+ import * as scheduling from './scheduling/client'
7
7
 
8
- export { discovery, evidence, relay, breg, casework, messaging }
8
+ export { discovery, evidence, breg, casework, messaging, scheduling }
package/index.js CHANGED
@@ -5,7 +5,7 @@
5
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
- exports.relay = require('./relay/client');
9
8
  exports.breg = require('./breg/client');
10
9
  exports.casework = require('./casework/client');
11
10
  exports.messaging = require('./messaging/client');
11
+ exports.scheduling = require('./scheduling/client');
@@ -14,6 +14,13 @@ export interface MessagingClientConfig {
14
14
  maxResponseBytes?: SafeInteger | null
15
15
  userAgent?: string | null
16
16
  trustedRootCertificates?: string | null
17
+ /**
18
+ * How many times a submission whose outcome is unknown is resent,
19
+ * identically and under the same idempotency key, before its error is
20
+ * returned: a whole number from 0 to 2, default 2; 0 sends it once. Any
21
+ * other value is a `configuration` error.
22
+ */
23
+ maxMutationRetries?: SafeInteger | null
17
24
  }
18
25
 
19
26
  export type Channel = 'email' | 'sms'
@@ -113,10 +120,21 @@ export type MessagingProblemCode =
113
120
  | 'template.locale-unavailable'
114
121
  | 'template.not-found'
115
122
  | 'template.render-refused'
116
- export type MessagingProtocolFailure = 'header_bounds' | 'trace_context' | 'media_type' | 'body' | 'problem' | 'status' | 'protocol'
123
+ export type MessagingProtocolFailure = 'header-bounds' | 'trace-context' | 'media-type' | 'body' | 'problem' | 'status' | 'protocol'
117
124
 
118
125
  export class MessagingClientError extends Error {
119
- readonly kind: 'configuration' | 'invalid_request' | 'transport' | 'problem' | 'protocol'
126
+ readonly kind: 'configuration' | 'invalid-request' | 'transport' | 'problem' | 'protocol'
127
+ /**
128
+ * Whether the request may have taken effect although this error was
129
+ * raised: a timeout or broken exchange after sending, an unusable answer,
130
+ * or a 5xx. When true, recover a keyed request by sending it again under
131
+ * the same idempotency key; a new key could apply it twice. False for a
132
+ * configuration or request defect, a connection never established, and
133
+ * every 4xx refusal. It is false for `410 idempotency.expired` too, but
134
+ * there an earlier submission under the key was accepted and its message
135
+ * may have been sent: reconcile that submission before choosing a new key.
136
+ */
137
+ readonly outcomeUnknown: boolean
120
138
  readonly code?: MessagingProblemCode
121
139
  readonly title?: string
122
140
  readonly detail?: string
@@ -124,8 +142,9 @@ export class MessagingClientError extends Error {
124
142
  readonly traceId?: string
125
143
  /**
126
144
  * 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.
145
+ * `Retry-After`, at most one day. Absent on every other failure. The
146
+ * client never waits on a 429 or retries it; the wait is the caller's to
147
+ * honor.
129
148
  */
130
149
  readonly retryAfterSeconds?: SafeInteger
131
150
  readonly transportKind?: string
@@ -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.38.0';
4
- const PRODUCTS = new Set(['discovery', 'evidence', 'relay', '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.38.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/",
@@ -12,10 +12,10 @@
12
12
  "registrystack",
13
13
  "discovery",
14
14
  "evidence",
15
- "relay",
16
15
  "breg",
17
16
  "casework",
18
- "messaging"
17
+ "messaging",
18
+ "scheduling"
19
19
  ],
20
20
  "publishConfig": {
21
21
  "access": "public",
@@ -40,10 +40,10 @@
40
40
  "native.js",
41
41
  "discovery",
42
42
  "evidence",
43
- "relay",
44
43
  "breg",
45
44
  "casework",
46
- "messaging"
45
+ "messaging",
46
+ "scheduling"
47
47
  ],
48
48
  "engines": {
49
49
  "node": ">=22.12.0"
@@ -55,12 +55,12 @@
55
55
  "check:types": "tsc --noEmit --strict --skipLibCheck false --types node --moduleResolution node16 --module node16 --target es2022 index.d.ts __test__/facade.test-d.ts"
56
56
  },
57
57
  "devDependencies": {
58
- "@types/node": "22.20.4",
58
+ "@types/node": "22.20.5",
59
59
  "typescript": "6.0.3"
60
60
  },
61
61
  "optionalDependencies": {
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"
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"
65
65
  }
66
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
+ }