@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.
@@ -13,15 +13,23 @@ export interface CaseworkClientConfig {
13
13
  maxResponseBytes?: SafeInteger | null
14
14
  userAgent?: string | null
15
15
  trustedRootCertificates?: string | null
16
+ /**
17
+ * How many times an idempotency-keyed mutation whose outcome is unknown is
18
+ * resent, identically and under the same key, before its error is
19
+ * returned: a whole number from 0 to 2, default 2; 0 sends it once. Reads,
20
+ * unkeyed operations, and review request create and cancel are never
21
+ * resent. Any other value is a `configuration` error.
22
+ */
23
+ maxMutationRetries?: SafeInteger | null
16
24
  }
17
25
 
18
- export type InboxView = 'mine' | 'my_teams' | 'team_holdings' | 'overdue' | 'completed_by_me'
26
+ export type InboxView = 'mine' | 'my-teams' | 'team-holdings' | 'overdue' | 'completed-by-me'
19
27
  export type OccurrenceKind = 'review' | 'application'
20
- export type OccurrenceState = 'open' | 'claimed' | 'waiting_applicant' | 'waiting_application' | 'synchronizing' | 'completed' | 'superseded' | 'cancelled'
28
+ export type OccurrenceState = 'open' | 'claimed' | 'waiting-applicant' | 'waiting-application' | 'synchronizing' | 'completed' | 'superseded' | 'cancelled'
21
29
  export type OperationName = string
22
30
  export type AttemptState = 'pending' | 'uncertain' | 'completed' | 'refused'
23
- export type PageStatus = 'complete' | 'budget_exhausted' | 'source_unavailable'
24
- export type HistoryKind = 'observed' | 'opened' | 'claimed' | 'assigned' | 'delegated' | 'caseload_moved' | 'clock_reminder' | 'clock_step_applied' | 'clock_recomputed' | 'released' | 'draft_saved' | 'attempt_reserved' | 'attempt_uncertain' | 'action_completed' | 'attempt_settled' | 'superseded' | 'completed'
31
+ export type PageStatus = 'complete' | 'budget-exhausted' | 'source-unavailable'
32
+ export type HistoryKind = 'observed' | 'opened' | 'claimed' | 'assigned' | 'delegated' | 'caseload-moved' | 'clock-reminder' | 'clock-step-applied' | 'clock-recomputed' | 'released' | 'draft-saved' | 'attempt-reserved' | 'attempt-uncertain' | 'action-completed' | 'attempt-settled' | 'superseded' | 'completed'
25
33
 
26
34
  export interface IssuerPrincipal { issuer: string; subject: string }
27
35
  export type TaskGrantBounds = { type: 'evidence'; requirement: string } | { type: 'breg'; permissions: ReadonlyArray<TaskPermission> } | { type: 'scheduling'; permissions: ReadonlyArray<SchedulingTaskPermission> }
@@ -29,15 +37,21 @@ export interface TaskPermission { collection: string; operations: ReadonlyArray<
29
37
  export interface SchedulingTaskPermission { service: string; location: string; actions: ReadonlyArray<string> }
30
38
  export interface EvidenceRequesterContext { requesterTags: ReadonlyArray<string>; audience: string }
31
39
  export interface TaskApprovalRequest { templateId: string; templateVersion: string }
40
+ /** Deferred authority keeps its approved deadline; execution credentials stay short-lived. */
41
+ export type TaskAuthorizationMode = 'immediate' | 'deferred'
32
42
  export interface TaskTemplatePreview {
33
43
  id: string; version: string; label: string; agent: IssuerPrincipal; client: string; resource: string; scopes: ReadonlyArray<string>; purpose: string
34
44
  evidenceContext?: EvidenceRequesterContext; bounds: TaskGrantBounds; subjects: { readonly [key: string]: Exclude<JsonScalar, null> }; lifetimeSeconds: SafeInteger
45
+ /** Omitted for the existing immediate mode. */
46
+ authorizationMode?: TaskAuthorizationMode
35
47
  }
36
48
  export interface TaskTemplatePreviews { itemRevision: SafeInteger; templates: ReadonlyArray<TaskTemplatePreview> }
37
49
  /** Grant metadata deliberately excludes stored subject values. */
38
50
  export interface TaskGrantView {
39
51
  id: string; templateId: string; templateVersion: string; agent: IssuerPrincipal; client: string; resource: string; scopes: ReadonlyArray<string>; purpose: string
40
52
  evidenceContext?: EvidenceRequesterContext; bounds: TaskGrantBounds; expiresAt: SafeInteger; invalidated: boolean
53
+ /** Omitted for the existing immediate mode. */
54
+ authorizationMode?: TaskAuthorizationMode
41
55
  }
42
56
  export interface TaskGrantList { grants: ReadonlyArray<TaskGrantView> }
43
57
  export interface TaskGrantRevocation { id: string; invalidated: boolean }
@@ -94,14 +108,14 @@ export interface WorkItem {
94
108
  }
95
109
  export interface Page<T> { items: ReadonlyArray<T>; nextCursor?: string; status: PageStatus }
96
110
  export interface WorkItemPage extends Page<WorkItem> { servedQueues: ReadonlyArray<string> }
97
- export type DirectoryTargetPurpose = 'assignment' | 'absence_person' | 'absence_cover'
111
+ export type DirectoryTargetPurpose = 'assignment' | 'absence-person' | 'absence-cover'
98
112
  export type DirectoryTargetsQuery = {
99
113
  cursor?: string
100
114
  limit?: SafeInteger
101
115
  } & (
102
116
  | { purpose: 'assignment'; queue: string; personIssuer?: never; personSubject?: never }
103
- | { purpose: 'absence_person'; queue?: never; personIssuer?: never; personSubject?: never }
104
- | { purpose: 'absence_cover'; queue?: never; personIssuer: string; personSubject: string }
117
+ | { purpose: 'absence-person'; queue?: never; personIssuer?: never; personSubject?: never }
118
+ | { purpose: 'absence-cover'; queue?: never; personIssuer: string; personSubject: string }
105
119
  )
106
120
  export type DirectoryTargetPage = Page<DirectoryMember>
107
121
  export type ListWorkItemsQuery = {
@@ -167,11 +181,11 @@ export interface HistoryEntryBase {
167
181
  profileId: string
168
182
  }
169
183
  export interface AttemptReservedHistoryEntry extends HistoryEntryBase {
170
- kind: 'attempt_reserved'
184
+ kind: 'attempt-reserved'
171
185
  detail: JsonObject & { attemptId: string; bindingReference: string; operation: OperationName }
172
186
  }
173
187
  /**
174
- * An attempt_uncertain detail takes one of three shapes: recovery left the
188
+ * An attempt-uncertain detail takes one of three shapes: recovery left the
175
189
  * attempt uncertain, the source definitively refused it, or an operator marked
176
190
  * an expired pending attempt uncertain. reason is the original decision reason.
177
191
  */
@@ -186,19 +200,19 @@ export type AttemptUncertainHistoryDetail = JsonObject & {
186
200
  | { operatorReason: string; decidedBy: string; originalActor: IssuerPrincipal; originalProfileId: string; definitivelyRefused?: never }
187
201
  )
188
202
  export interface AttemptUncertainHistoryEntry extends HistoryEntryBase {
189
- kind: 'attempt_uncertain'
203
+ kind: 'attempt-uncertain'
190
204
  detail: AttemptUncertainHistoryDetail
191
205
  }
192
206
  export interface ActionCompletedHistoryEntry extends HistoryEntryBase {
193
- kind: 'action_completed'
207
+ kind: 'action-completed'
194
208
  detail: JsonObject & { attemptId: string; bindingReference: string; sourceRevision: string; sourceReceipt: SourceReceipt }
195
209
  }
196
210
  export interface AttemptSettledHistoryEntry extends HistoryEntryBase {
197
- kind: 'attempt_settled'
198
- detail: JsonObject & { attemptId: string; bindingReference: string; operation: OperationName; outcome: 'applied' | 'not_applied'; reason: string; decidedBy: string }
211
+ kind: 'attempt-settled'
212
+ detail: JsonObject & { attemptId: string; bindingReference: string; operation: OperationName; outcome: 'applied' | 'not-applied'; reason: string; decidedBy: string }
199
213
  }
200
214
  export interface OtherHistoryEntry extends HistoryEntryBase {
201
- kind: Exclude<HistoryKind, 'attempt_reserved' | 'attempt_uncertain' | 'action_completed' | 'attempt_settled'>
215
+ kind: Exclude<HistoryKind, 'attempt-reserved' | 'attempt-uncertain' | 'action-completed' | 'attempt-settled'>
202
216
  detail: JsonValue
203
217
  }
204
218
  export type HistoryEntry = AttemptReservedHistoryEntry | AttemptUncertainHistoryEntry | ActionCompletedHistoryEntry | AttemptSettledHistoryEntry | OtherHistoryEntry
@@ -220,9 +234,9 @@ export interface CalendarPolicy { id: string; timezone: string; workingWeekdays:
220
234
  export interface ClockReminder { id: string; workingDaysBefore: SafeInteger }
221
235
  export interface ClockStep { id: string; because: string; at: 'due'; action: { reassign: { queue: string } } }
222
236
  export type ClockPolicy = {
223
- scope: 'subject'; id: string; anchor: 'firstSubmittedAt'; completeOn: 'reviewCompleted'; after: { elapsed: string }; pauseWhile: ReadonlyArray<'awaitingApplicant'>
237
+ type: 'subject'; id: string; anchor: 'first-submitted-at'; completeOn: 'review-completed'; after: { elapsed: string }; pauseWhile: ReadonlyArray<'awaiting-applicant'>
224
238
  } | {
225
- scope: 'activity'; id: string; anchor: 'stageEnteredAt'; calendar: string; after: { workingDays: SafeInteger }; dueTime: string;
239
+ type: 'activity'; id: string; anchor: 'stage-entered-at'; calendar: string; after: { workingDays: SafeInteger }; dueTime: string;
226
240
  atRisk?: { workingDaysBefore: SafeInteger }; reminders?: ReadonlyArray<ClockReminder>; steps?: ReadonlyArray<ClockStep>
227
241
  }
228
242
  export interface SourcePolicy {
@@ -252,14 +266,14 @@ export interface AbsenceInput { person: IssuerPrincipal; from: string; until: st
252
266
  export interface AbsenceRecord extends AbsenceInput { absenceId: string; revision: SafeInteger }
253
267
  export interface AbsencesQuery { cursor?: string; limit?: SafeInteger }
254
268
  export interface AbsenceList { directoryRevision: SafeInteger; items: ReadonlyArray<AbsenceRecord>; nextCursor?: string }
255
- export interface AssignmentContext { owner?: IssuerPrincipal; assignedBy?: IssuerPrincipal; absenceIds: ReadonlyArray<string>; staffingDiagnostic?: 'no_cover_available' }
269
+ export interface AssignmentContext { owner?: IssuerPrincipal; assignedBy?: IssuerPrincipal; absenceIds: ReadonlyArray<string>; staffingDiagnostic?: 'no-cover-available' }
256
270
  export interface AssignmentRequest { assignee: IssuerPrincipal; reason?: string }
257
271
  export interface DelegateRequest { delegate: IssuerPrincipal; reason?: string }
258
272
  export interface CaseloadMoveRequest { from: IssuerPrincipal; to: IssuerPrincipal; queueId?: string; reason: string }
259
273
  export interface CaseloadItemSelection { itemId: string; expectedRevision: SafeInteger }
260
274
  export interface CaseloadApplyRequest { movement: CaseloadMoveRequest; items: ReadonlyArray<CaseloadItemSelection> }
261
275
  export interface CaseloadPreviewQuery { cursor?: string; limit?: SafeInteger }
262
- export interface CaseloadItemResult { itemId: string; result: 'moved' | 'not_visible' | 'not_eligible' | 'attempt_in_progress' | 'conflict'; revision?: SafeInteger }
276
+ export interface CaseloadItemResult { itemId: string; result: 'moved' | 'not-visible' | 'not-eligible' | 'attempt-in-progress' | 'conflict'; revision?: SafeInteger }
263
277
  export interface BootstrapDirectoryRequest {
264
278
  teamId: string
265
279
  staff: ReadonlyArray<IssuerPrincipal>
@@ -316,10 +330,23 @@ export type KnownCaseworkProblemCode =
316
330
  | 'work-item.source-unavailable'
317
331
  | 'work-item.superseded'
318
332
  export type CaseworkProblemCode = KnownCaseworkProblemCode | (string & {})
319
- export type CaseworkProtocolFailure = 'header_bounds' | 'trace_context' | 'media_type' | 'body' | 'problem' | 'status' | 'protocol'
333
+ export type CaseworkProtocolFailure = 'header-bounds' | 'trace-context' | 'media-type' | 'body' | 'problem' | 'status' | 'protocol'
320
334
 
321
335
  export class CaseworkClientError extends Error {
322
- readonly kind: 'configuration' | 'invalid_request' | 'transport' | 'problem' | 'protocol'
336
+ readonly kind: 'configuration' | 'invalid-request' | 'transport' | 'problem' | 'protocol'
337
+ /**
338
+ * Whether the request may have taken effect although this error was
339
+ * raised: a timeout or broken exchange after sending, an unusable answer,
340
+ * or a 5xx. When true, recover a keyed request by sending it again under
341
+ * the same idempotency key; a new key could apply it twice. False for a
342
+ * configuration or request defect, a connection never established, and
343
+ * every 4xx refusal. It is false for `410 idempotency.expired` too, but
344
+ * there an earlier attempt under the key committed and only its stored
345
+ * response was erased by retention: reconcile the original operation, by
346
+ * reading the item, review request, or directory, before choosing a new
347
+ * key.
348
+ */
349
+ readonly outcomeUnknown: boolean
323
350
  readonly code?: CaseworkProblemCode
324
351
  readonly detail?: string
325
352
  readonly status?: SafeInteger
@@ -327,13 +354,13 @@ export class CaseworkClientError extends Error {
327
354
  readonly originalAttemptId?: string
328
355
  readonly validation?: {
329
356
  readonly path: string
330
- readonly reason: 'kind_not_allowed' | 'reference_invalid' | 'object_required' | 'maximum_bytes_exceeded' | 'maximum_depth_exceeded' | 'schema_mismatch' | 'outcome_not_declared' | 'reason_required' | 'text_invalid' | 'result_not_declared' | 'result_required' | 'field_not_declared' | 'constraint_invalid' | 'constraint_violated'
357
+ readonly reason: 'kind-not-allowed' | 'reference-invalid' | 'object-required' | 'maximum-bytes-exceeded' | 'maximum-depth-exceeded' | 'schema-mismatch' | 'outcome-not-declared' | 'reason-required' | 'text-invalid' | 'result-not-declared' | 'result-required' | 'field-not-declared' | 'constraint-invalid' | 'constraint-violated'
331
358
  }
332
359
  readonly transportKind?: string
333
360
  readonly protocolFailure?: CaseworkProtocolFailure
334
361
  }
335
362
 
336
- export type ClockRuntimeState = 'running' | 'paused' | 'completed' | 'cancelled' | 'verification_pending' | 'source_facts_missing'
363
+ export type ClockRuntimeState = 'running' | 'paused' | 'completed' | 'cancelled' | 'verification-pending' | 'source-facts-missing'
337
364
  export type ClockNextEffect =
338
365
  | { kind: 'reminder'; id: string; at: string }
339
366
  | { kind: 'reassign'; id: string; at: string; because: string; queueId: string }
@@ -382,7 +409,7 @@ export interface ReviewRequestAccepted {
382
409
  policy: ReviewPolicyBinding
383
410
  submissionDigest: ContentDigest
384
411
  }
385
- export type ReviewRequestLifecycle = 'reviewing' | 'approved' | 'rejected' | 'changes_requested' | 'answered' | 'cancelled' | 'superseded'
412
+ export type ReviewRequestLifecycle = 'reviewing' | 'approved' | 'rejected' | 'changes-requested' | 'answered' | 'cancelled' | 'superseded'
386
413
  export interface ReviewRequestView extends ReviewRequestAccepted {
387
414
  requesterReference: string
388
415
  lifecycle: ReviewRequestLifecycle
@@ -390,7 +417,7 @@ export interface ReviewRequestView extends ReviewRequestAccepted {
390
417
  createdAt: string
391
418
  updatedAt: string
392
419
  }
393
- export type ReviewResultStatus = 'approved' | 'rejected' | 'changes_requested' | 'answered' | 'cancelled' | 'superseded'
420
+ export type ReviewResultStatus = 'approved' | 'rejected' | 'changes-requested' | 'answered' | 'cancelled' | 'superseded'
394
421
  export interface ReviewResult extends ReviewRequestAccepted {
395
422
  resultId: Uuid
396
423
  status: ReviewResultStatus
@@ -407,11 +434,30 @@ export interface ReviewResultFeedPage { items: ReadonlyArray<ReviewResultFeedEnt
407
434
  export interface ReviewCancelRequest { subject: ReviewSubjectBinding; reason: string }
408
435
  export type ReviewCancelResponse =
409
436
  | { outcome: 'cancelled'; result: ReviewResult }
410
- | { outcome: 'already_terminal'; result: ReviewResult }
437
+ | { outcome: 'already-terminal'; result: ReviewResult }
411
438
  export interface ReviewPageQuery { cursor?: Uuid; limit?: SafeInteger }
412
- export interface ReviewTaskQuery extends ReviewPageQuery { queue?: string }
439
+ export type ReviewTaskOwnership = 'assigned-to-me' | 'unclaimed'
440
+ export interface ReviewTaskQuery extends ReviewPageQuery { queue?: string; ownership?: ReviewTaskOwnership }
441
+ export interface SupervisoryReviewTaskQuery extends ReviewPageQuery { queue?: string; requestId?: Uuid }
442
+ export interface OwnReviewDecisionQuery extends ReviewPageQuery { queue?: string }
413
443
  export interface WorkItemHistoryQuery { cursor?: string; limit?: SafeInteger }
414
444
  export type ReviewerTaskState = 'open' | { held: { holder: IssuerPrincipal } } | 'decided'
445
+ export type ReviewDecisionType = 'approve' | 'reject' | 'changes-requested' | 'answer'
446
+ export interface ReviewDecisionReceipt {
447
+ policy: ReviewPolicyBinding
448
+ decision: ReviewDecisionType
449
+ outcome?: string
450
+ outcomeLabel?: string
451
+ decidedAt: string
452
+ }
453
+ export interface OwnReviewDecision {
454
+ taskId: Uuid
455
+ requestId: Uuid
456
+ queue: string
457
+ requesterReference: string
458
+ decisionReceipt: ReviewDecisionReceipt
459
+ }
460
+ export interface OwnReviewDecisionPage { items: ReadonlyArray<OwnReviewDecision>; nextCursor?: Uuid; status: PageStatus }
415
461
  export interface ReviewerTask {
416
462
  taskId: Uuid
417
463
  requestId: Uuid
@@ -422,9 +468,20 @@ export interface ReviewerTask {
422
468
  eligibleProfiles: ReadonlyArray<string>
423
469
  state: ReviewerTaskState
424
470
  decidedByCaller?: boolean
471
+ decisionReceipt?: ReviewDecisionReceipt
425
472
  }
426
473
  export interface ReviewTaskPage { items: ReadonlyArray<ReviewerTask>; nextCursor?: Uuid; status: PageStatus }
427
- export type ReviewSourceBindingStatus = 'current' | 'binding_changed'
474
+ export type SupervisoryReviewTaskState = 'open' | 'held' | 'decided'
475
+ export interface SupervisoryReviewTask {
476
+ taskId: Uuid
477
+ requestId: Uuid
478
+ queue: string
479
+ revision: SafeInteger
480
+ state: SupervisoryReviewTaskState
481
+ accountabilityEventId?: Uuid
482
+ }
483
+ export interface SupervisoryReviewTaskPage { items: ReadonlyArray<SupervisoryReviewTask>; nextCursor?: Uuid; status: PageStatus }
484
+ export type ReviewSourceBindingStatus = 'current' | 'binding-changed'
428
485
  export interface ReviewSourceProjection { binding: SourceBinding; displayReference?: string; display: Readonly<Record<string, JsonValue>> }
429
486
  export type ReviewTaskContextData =
430
487
  | { strategy: 'submitted'; snapshot: Readonly<Record<string, JsonValue>> }
@@ -447,7 +504,7 @@ export interface ReviewClockOccurrence {
447
504
  clockId: string
448
505
  requestId: Uuid
449
506
  correlation: ReviewClockCorrelation
450
- state: 'running' | 'paused' | 'completed' | 'cancelled' | 'source_facts_missing'
507
+ state: 'running' | 'paused' | 'completed' | 'cancelled' | 'source-facts-missing'
451
508
  policyDigest: ContentDigest
452
509
  anchorAt: string
453
510
  dueAt?: string
@@ -458,7 +515,7 @@ export interface ReviewTaskDraftInput { body: JsonValue }
458
515
  export interface ReviewTaskDraft { taskId: Uuid; author: IssuerPrincipal; body: JsonValue; revision: SafeInteger; updatedAt: string }
459
516
  export type ReviewerDecision =
460
517
  | { type: 'approve' }
461
- | { type: 'reject' | 'changes_requested' | 'answer'; outcome: string; reason?: string | null; result?: JsonObject | null }
518
+ | { type: 'reject' | 'changes-requested' | 'answer'; outcome: string; reason?: string | null; result?: JsonObject | null }
462
519
  export interface ReviewTaskDecisionRequest { decision: ReviewerDecision }
463
520
  export type ReviewHistoryAudience = 'reviewers' | 'requester'
464
521
  export interface ReviewNoteRequest { audience: ReviewHistoryAudience; note: string }
@@ -468,6 +525,7 @@ export interface ReviewAccountabilityRecord {
468
525
  eventId: Uuid; requestId: Uuid; taskId: Uuid; actorRef: string; actor: IssuerPrincipal
469
526
  profileId: string; decision: string; privateReason?: string; resultDigest?: string
470
527
  occurredAt: string; retainedUntil: string
528
+ decisionReceipt?: ReviewDecisionReceipt
471
529
  }
472
530
  export interface ReviewStagePolicy { id: string; queue: string; decidingProfiles: ReadonlyArray<string>; requiredApprovals: SafeInteger; excludeInitiator?: boolean; excludePreviousStageReviewers?: boolean }
473
531
  export interface ReviewRetentionPolicy { terminalDays: SafeInteger; accountabilityDays: SafeInteger }
@@ -480,7 +538,7 @@ export interface ReviewKindPolicySnapshot {
480
538
  retention: ReviewRetentionPolicy
481
539
  displaySchema: JsonValue
482
540
  resultSchema?: JsonValue
483
- outcomes?: ReadonlyArray<{ id: string; label: string; settlement: 'rejected' | 'changes_requested' | 'answered'; reasonRequired: boolean; resultRequired?: boolean }>
541
+ outcomes?: ReadonlyArray<{ id: string; label: string; settlement: 'rejected' | 'changes-requested' | 'answered'; reasonRequired: boolean; resultRequired?: boolean }>
484
542
  }
485
543
 
486
544
  export class CaseworkClient {
@@ -494,6 +552,8 @@ export class CaseworkClient {
494
552
  reviewKinds(token: string, profile: string): Promise<CaseworkOutcome<ReadonlyArray<ReviewKindPolicySnapshot>>>
495
553
  reviewKind(token: string, profile: string, kindId: string): Promise<CaseworkOutcome<ReviewKindPolicySnapshot>>
496
554
  reviewTasks(token: string, profile: string, query?: ReviewTaskQuery | null, sourceProfile?: string | null): Promise<CaseworkOutcome<ReviewTaskPage>>
555
+ supervisoryReviewTasks(token: string, profile: string, query?: SupervisoryReviewTaskQuery | null, sourceProfile?: string | null): Promise<CaseworkOutcome<SupervisoryReviewTaskPage>>
556
+ ownReviewDecisions(token: string, profile: string, query?: OwnReviewDecisionQuery | null, sourceProfile?: string | null): Promise<CaseworkOutcome<OwnReviewDecisionPage>>
497
557
  reviewTask(token: string, profile: string, taskId: Uuid, sourceProfile?: string | null): Promise<CaseworkOutcome<ReviewerTask>>
498
558
  reviewTaskContext(token: string, profile: string, taskId: Uuid, sourceProfile?: string | null): Promise<CaseworkOutcome<ReviewTaskContext>>
499
559
  previewReviewTaskTemplates(token: string, profile: string, sourceProfile: string, taskId: Uuid): Promise<CaseworkOutcome<TaskTemplatePreviews>>
@@ -12,6 +12,7 @@ class CaseworkClientError extends Error {
12
12
  super(envelope.message);
13
13
  this.name = 'CaseworkClientError';
14
14
  this.kind = envelope.kind;
15
+ this.outcomeUnknown = envelope.outcomeUnknown === true;
15
16
  for (const field of ['code', 'detail', 'status', 'traceId', 'originalAttemptId', 'validation', 'transportKind', 'protocolFailure']) {
16
17
  if (envelope[field] !== undefined && envelope[field] !== null) this[field] = envelope[field];
17
18
  }
@@ -39,38 +40,38 @@ function normalized(error, fallbackKind) {
39
40
  return error;
40
41
  }
41
42
 
42
- function cloneJson(value, budget, depth) {
43
- if (depth > MAX_JSON_DEPTH || ++budget.nodes > MAX_JSON_NODES) throw normalized({}, 'invalid_request');
43
+ function cloneJson(value, budget, depth, kind) {
44
+ if (depth > MAX_JSON_DEPTH || ++budget.nodes > MAX_JSON_NODES) throw normalized({}, kind);
44
45
  if (value === null || typeof value === 'boolean') return value;
45
46
  if (typeof value === 'string') {
46
47
  budget.bytes += Buffer.byteLength(value, 'utf8');
47
- if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, 'invalid_request');
48
+ if (budget.bytes > MAX_JSON_STRING_BYTES) throw normalized({}, kind);
48
49
  return value;
49
50
  }
50
51
  if (typeof value === 'number') {
51
52
  if (!Number.isFinite(value) || (Number.isInteger(value) && !Number.isSafeInteger(value))) {
52
- throw normalized({}, 'invalid_request');
53
+ throw normalized({}, kind);
53
54
  }
54
55
  return value;
55
56
  }
56
57
  if (value === undefined || typeof value !== 'object' || isProxy(value)) {
57
- throw normalized({}, 'invalid_request');
58
+ throw normalized({}, kind);
58
59
  }
59
60
  const array = Array.isArray(value);
60
61
  const prototype = Object.getPrototypeOf(value);
61
62
  if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) {
62
- throw normalized({}, 'invalid_request');
63
+ throw normalized({}, kind);
63
64
  }
64
- if (budget.active.has(value)) throw normalized({}, 'invalid_request');
65
+ if (budget.active.has(value)) throw normalized({}, kind);
65
66
  budget.active.add(value);
66
67
  try {
67
- if (array) return value.map((member) => cloneJson(member, budget, depth + 1));
68
+ if (array) return value.map((member) => cloneJson(member, budget, depth + 1, kind));
68
69
  const result = Object.create(null);
69
70
  for (const key of Reflect.ownKeys(value)) {
70
- if (typeof key !== 'string') throw normalized({}, 'invalid_request');
71
+ if (typeof key !== 'string') throw normalized({}, kind);
71
72
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
72
- if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw normalized({}, 'invalid_request');
73
- if (descriptor.enumerable) result[key] = cloneJson(descriptor.value, budget, depth + 1);
73
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) throw normalized({}, kind);
74
+ if (descriptor.enumerable) result[key] = cloneJson(descriptor.value, budget, depth + 1, kind);
74
75
  }
75
76
  return result;
76
77
  } finally {
@@ -78,8 +79,10 @@ function cloneJson(value, budget, depth) {
78
79
  }
79
80
  }
80
81
 
81
- function sanitize(value) {
82
- return cloneJson(value, { nodes: 0, bytes: 0, active: new WeakSet() }, 0);
82
+ // A refusal is reported as `kind`: `configuration` for the constructor's
83
+ // settings, `invalid-request` for a method's arguments.
84
+ function sanitize(value, kind) {
85
+ return cloneJson(value, { nodes: 0, bytes: 0, active: new WeakSet() }, 0, kind);
83
86
  }
84
87
 
85
88
  const NativeCaseworkClient = native.CaseworkClient;
@@ -87,7 +90,7 @@ const NativeCaseworkClient = native.CaseworkClient;
87
90
  class CaseworkClient {
88
91
  constructor(config) {
89
92
  try {
90
- this.native = new NativeCaseworkClient(sanitize(config));
93
+ this.native = new NativeCaseworkClient(sanitize(config, 'configuration'));
91
94
  } catch (error) {
92
95
  throw normalized(error, 'configuration');
93
96
  }
@@ -97,7 +100,7 @@ class CaseworkClient {
97
100
  try {
98
101
  return this.native.taskAssertionEndpoint(grantId);
99
102
  } catch (error) {
100
- throw normalized(error, 'invalid_request');
103
+ throw normalized(error, 'invalid-request');
101
104
  }
102
105
  }
103
106
  }
@@ -112,6 +115,8 @@ for (const [method, jsonIndexes] of [
112
115
  ['reviewKinds', []],
113
116
  ['reviewKind', []],
114
117
  ['reviewTasks', [2]],
118
+ ['ownReviewDecisions', [2]],
119
+ ['supervisoryReviewTasks', [2]],
115
120
  ['reviewTask', []],
116
121
  ['reviewTaskContext', []],
117
122
  ['previewReviewTaskTemplates', []],
@@ -170,11 +175,11 @@ for (const [method, jsonIndexes] of [
170
175
  CaseworkClient.prototype[method] = function (...args) {
171
176
  try {
172
177
  for (const index of jsonIndexes) {
173
- if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index]);
178
+ if (args[index] !== undefined && args[index] !== null) args[index] = sanitize(args[index], 'invalid-request');
174
179
  }
175
180
  return this.native[method](...args).catch((error) => { throw normalized(error); });
176
181
  } catch (error) {
177
- throw normalized(error, 'invalid_request');
182
+ throw normalized(error, 'invalid-request');
178
183
  }
179
184
  };
180
185
  }
@@ -13,6 +13,8 @@ export declare class CaseworkClient {
13
13
  reviewKind(token: string, profile: string, kindId: string): Promise<CaseworkOutcome>
14
14
  reviewTasks(token: string, profile: string, query?: any | undefined | null, sourceProfile?: string | undefined | null): Promise<CaseworkOutcome>
15
15
  reviewTask(token: string, profile: string, taskId: string, sourceProfile?: string | undefined | null): Promise<CaseworkOutcome>
16
+ ownReviewDecisions(token: string, profile: string, query?: any | undefined | null, sourceProfile?: string | undefined | null): Promise<CaseworkOutcome>
17
+ supervisoryReviewTasks(token: string, profile: string, query?: any | undefined | null, sourceProfile?: string | undefined | null): Promise<CaseworkOutcome>
16
18
  reviewTaskContext(token: string, profile: string, taskId: string, sourceProfile?: string | undefined | null): Promise<CaseworkOutcome>
17
19
  previewReviewTaskTemplates(token: string, profile: string, sourceProfile: string, taskId: string): Promise<CaseworkOutcome>
18
20
  listReviewTaskGrants(token: string, profile: string, sourceProfile: string, taskId: string): Promise<CaseworkOutcome>
@@ -76,6 +78,11 @@ export interface CaseworkClientConfig {
76
78
  maxResponseBytes?: number
77
79
  userAgent?: string
78
80
  trustedRootCertificates?: string
81
+ /**
82
+ * How many times a keyed mutation whose outcome is unknown is resent under
83
+ * the same idempotency key: 0 to 2, default 2, and 0 disables the resend.
84
+ */
85
+ maxMutationRetries?: number
79
86
  }
80
87
 
81
88
  export interface CaseworkOutcome {
@@ -111,13 +111,13 @@ export type ServiceSelection = CommonServiceSelection;
111
111
  export type DiscoveryClientErrorKind =
112
112
  | 'configuration'
113
113
  | 'query'
114
- | 'no_matching_service'
115
- | 'ambiguous_selection'
116
- | 'no_matching_alternative'
117
- | 'ambiguous_alternative'
118
- | 'capability_mismatch'
119
- | 'local_acceptance_refused'
120
- | 'selection_changed'
114
+ | 'no-matching-service'
115
+ | 'ambiguous-selection'
116
+ | 'no-matching-alternative'
117
+ | 'ambiguous-alternative'
118
+ | 'capability-mismatch'
119
+ | 'local-acceptance-refused'
120
+ | 'selection-changed'
121
121
  | 'transport'
122
122
  | 'problem'
123
123
  | 'protocol'
@@ -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
  }
@@ -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
@@ -3,5 +3,6 @@ import * as evidence from './evidence/client'
3
3
  import * as breg from './breg/client'
4
4
  import * as casework from './casework/client'
5
5
  import * as messaging from './messaging/client'
6
+ import * as scheduling from './scheduling/client'
6
7
 
7
- export { discovery, evidence, breg, casework, messaging }
8
+ export { discovery, evidence, breg, casework, messaging, scheduling }
package/index.js CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  // One assignment per namespace, never a `module.exports` object literal: Node
4
4
  // scans this file for these statements to synthesise the named exports an ESM
5
- // consumer imports, and `require()` sees the same five properties either way.
5
+ // consumer imports, and `require()` sees the same six properties either way.
6
6
  exports.discovery = require('./discovery/client');
7
7
  exports.evidence = require('./evidence/client');
8
8
  exports.breg = require('./breg/client');
9
9
  exports.casework = require('./casework/client');
10
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'
@@ -102,6 +109,7 @@ export type MessagingProblemCode =
102
109
  | 'profile.not-authorized'
103
110
  | 'quota.exceeded'
104
111
  | 'rate-limit.exceeded'
112
+ | 'receipt.unresolved'
105
113
  | 'request.body-too-large'
106
114
  | 'request.invalid'
107
115
  | 'request.method-not-allowed'
@@ -113,10 +121,21 @@ export type MessagingProblemCode =
113
121
  | 'template.locale-unavailable'
114
122
  | 'template.not-found'
115
123
  | 'template.render-refused'
116
- export type MessagingProtocolFailure = 'header_bounds' | 'trace_context' | 'media_type' | 'body' | 'problem' | 'status' | 'protocol'
124
+ export type MessagingProtocolFailure = 'header-bounds' | 'trace-context' | 'media-type' | 'body' | 'problem' | 'status' | 'protocol'
117
125
 
118
126
  export class MessagingClientError extends Error {
119
- readonly kind: 'configuration' | 'invalid_request' | 'transport' | 'problem' | 'protocol'
127
+ readonly kind: 'configuration' | 'invalid-request' | 'transport' | 'problem' | 'protocol'
128
+ /**
129
+ * Whether the request may have taken effect although this error was
130
+ * raised: a timeout or broken exchange after sending, an unusable answer,
131
+ * or a 5xx. When true, recover a keyed request by sending it again under
132
+ * the same idempotency key; a new key could apply it twice. False for a
133
+ * configuration or request defect, a connection never established, and
134
+ * every 4xx refusal. It is false for `410 idempotency.expired` too, but
135
+ * there an earlier submission under the key was accepted and its message
136
+ * may have been sent: reconcile that submission before choosing a new key.
137
+ */
138
+ readonly outcomeUnknown: boolean
120
139
  readonly code?: MessagingProblemCode
121
140
  readonly title?: string
122
141
  readonly detail?: string
@@ -124,8 +143,9 @@ export class MessagingClientError extends Error {
124
143
  readonly traceId?: string
125
144
  /**
126
145
  * 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.
146
+ * `Retry-After`, at most one day. Absent on every other failure. The
147
+ * client never waits on a 429 or retries it; the wait is the caller's to
148
+ * honor.
129
149
  */
130
150
  readonly retryAfterSeconds?: SafeInteger
131
151
  readonly transportKind?: string
@@ -137,6 +157,8 @@ export class MessagingClient {
137
157
  health(): Promise<MessagingOutcome<null>>
138
158
  ready(): Promise<MessagingOutcome<null>>
139
159
  submit(token: string, idempotencyKey: string, request: SubmitMessageRequest): Promise<MessagingOutcome<MessageReceipt>>
160
+ /** Observe the original success using the same body/key and current token; never resend or mutate. */
161
+ messageReceipt(token: string, idempotencyKey: string, request: SubmitMessageRequest): Promise<MessagingOutcome<MessageReceipt>>
140
162
  message(token: string, messageId: MessageId): Promise<MessagingOutcome<MessageView>>
141
163
  cancel(token: string, messageId: MessageId): Promise<MessagingOutcome<MessageView>>
142
164
  preview(token: string, templateId: string, version: string, request: TemplatePreviewRequest): Promise<MessagingOutcome<TemplatePreview>>