simcapture-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/index.cjs +549 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +694 -0
- package/dist/index.d.ts +694 -0
- package/dist/index.js +527 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SimCapture responses are largely opaque — the OpenAPI contract marks most as
|
|
3
|
+
* `additionalProperties: true` because the old code only read a handful of
|
|
4
|
+
* fields. `Passthrough<T>` types the fields we rely on while preserving the
|
|
5
|
+
* rest, so callers keep access to everything the API returns.
|
|
6
|
+
*/
|
|
7
|
+
type Passthrough<T = Record<string, unknown>> = T & Record<string, unknown>;
|
|
8
|
+
/** Fully opaque object response (no fields documented). */
|
|
9
|
+
type Unknown = Record<string, unknown>;
|
|
10
|
+
|
|
11
|
+
/** `GET /organizations` — one organization (tenant program/cohort). */
|
|
12
|
+
type Organization = Passthrough<{
|
|
13
|
+
organizationId: string;
|
|
14
|
+
clientId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
active: boolean;
|
|
17
|
+
inUse: boolean;
|
|
18
|
+
locked: boolean;
|
|
19
|
+
sort: number;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
interface ScenarioLearningObjective {
|
|
23
|
+
description: string;
|
|
24
|
+
sort: number;
|
|
25
|
+
}
|
|
26
|
+
interface ScenarioAsset {
|
|
27
|
+
assetId: string;
|
|
28
|
+
clientId: string;
|
|
29
|
+
assetMimeTypeId: string;
|
|
30
|
+
name: string;
|
|
31
|
+
uri: string;
|
|
32
|
+
displayName: string;
|
|
33
|
+
description: string;
|
|
34
|
+
createdTs: string;
|
|
35
|
+
updatedTs: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `GET /scenarios/{id}` — one scenario. Many `canHave*` / `has*` boolean flags
|
|
39
|
+
* are returned; the listed fields cover the scalars callers read, and the
|
|
40
|
+
* `Passthrough` index signature preserves the rest.
|
|
41
|
+
*/
|
|
42
|
+
type Scenario = Passthrough<{
|
|
43
|
+
scenarioId: string;
|
|
44
|
+
clientId: string;
|
|
45
|
+
organizationId: string;
|
|
46
|
+
originatingScenarioId: string | null;
|
|
47
|
+
passingCutoffId: string | null;
|
|
48
|
+
active: boolean;
|
|
49
|
+
inUse: boolean;
|
|
50
|
+
privateTitle: string;
|
|
51
|
+
publicTitle: string | null;
|
|
52
|
+
overview: string | null;
|
|
53
|
+
setupInstructions: string;
|
|
54
|
+
/** Decimal string, e.g. `"0.000000"`. */
|
|
55
|
+
setupTimeMinutes: string;
|
|
56
|
+
takedownTimeMinutes: string;
|
|
57
|
+
recordingDeletionDays: number;
|
|
58
|
+
createdTs: string;
|
|
59
|
+
updatedTs: string;
|
|
60
|
+
acl: unknown[];
|
|
61
|
+
organization?: Organization;
|
|
62
|
+
simulators?: unknown[];
|
|
63
|
+
scenarioAnnotations?: unknown[];
|
|
64
|
+
learningObjectives?: ScenarioLearningObjective[];
|
|
65
|
+
courseItems?: Unknown[];
|
|
66
|
+
inventoryManagementItems?: unknown[];
|
|
67
|
+
assets?: ScenarioAsset[];
|
|
68
|
+
roles?: Unknown[];
|
|
69
|
+
}>;
|
|
70
|
+
interface ScenarioSetupUpdate {
|
|
71
|
+
setupInstructions?: string;
|
|
72
|
+
setupTimeMinutes?: number;
|
|
73
|
+
takedownTimeMinutes?: number;
|
|
74
|
+
inventoryManagementItems?: Unknown[];
|
|
75
|
+
}
|
|
76
|
+
interface ScenarioDetailsUpdate {
|
|
77
|
+
privateTitle?: string;
|
|
78
|
+
overview?: string;
|
|
79
|
+
learningObjectives?: (string | null)[];
|
|
80
|
+
canHaveSimulator?: boolean;
|
|
81
|
+
simulatorIds?: string[];
|
|
82
|
+
canHaveExamFlow?: boolean;
|
|
83
|
+
isParticipantDriven?: boolean;
|
|
84
|
+
isSelfRecorded?: boolean;
|
|
85
|
+
}
|
|
86
|
+
interface ScenarioAttachmentParams {
|
|
87
|
+
/** Tenant client id sent as `client-id` query param. */
|
|
88
|
+
clientId: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface FindCoursesQuery {
|
|
92
|
+
count: boolean;
|
|
93
|
+
page: number;
|
|
94
|
+
active: boolean;
|
|
95
|
+
filters: {
|
|
96
|
+
organization?: string[];
|
|
97
|
+
[key: string]: unknown;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
interface CourseOwner {
|
|
101
|
+
userId: string;
|
|
102
|
+
userName: string;
|
|
103
|
+
firstName: string;
|
|
104
|
+
middleName: string | null;
|
|
105
|
+
lastName: string;
|
|
106
|
+
}
|
|
107
|
+
/** One course row in the `POST /courses` page. */
|
|
108
|
+
type CourseRow = Passthrough<{
|
|
109
|
+
courseId: string;
|
|
110
|
+
clientId: string;
|
|
111
|
+
privateTitle: string;
|
|
112
|
+
publicTitle: string | null;
|
|
113
|
+
overview: string | null;
|
|
114
|
+
active: boolean;
|
|
115
|
+
inUse: boolean;
|
|
116
|
+
createdTs: string;
|
|
117
|
+
updatedTs: string;
|
|
118
|
+
releaseStartTs: string | null;
|
|
119
|
+
releaseEndTs: string | null;
|
|
120
|
+
courseStatusId?: string;
|
|
121
|
+
organizationId?: string;
|
|
122
|
+
owner: CourseOwner;
|
|
123
|
+
organization: {
|
|
124
|
+
organizationId: string;
|
|
125
|
+
name: string;
|
|
126
|
+
};
|
|
127
|
+
courseStatus: {
|
|
128
|
+
courseStatusId: string;
|
|
129
|
+
courseStatusRef: string;
|
|
130
|
+
active: boolean;
|
|
131
|
+
};
|
|
132
|
+
acl: unknown[];
|
|
133
|
+
}>;
|
|
134
|
+
interface CoursesPage {
|
|
135
|
+
rows: CourseRow[];
|
|
136
|
+
count?: number;
|
|
137
|
+
[key: string]: unknown;
|
|
138
|
+
}
|
|
139
|
+
interface CourseItemsParams {
|
|
140
|
+
includeScenarios?: boolean;
|
|
141
|
+
includeEvaluationTemplates?: boolean;
|
|
142
|
+
hasScenarioReflections?: boolean;
|
|
143
|
+
hasScenarioEvaluations?: boolean;
|
|
144
|
+
}
|
|
145
|
+
/** One entry of `GET /courses/{id}/items`. */
|
|
146
|
+
type CourseItem = Passthrough<{
|
|
147
|
+
clientId: string;
|
|
148
|
+
courseId: string;
|
|
149
|
+
courseItemId: string;
|
|
150
|
+
evaluationTemplateId: string | null;
|
|
151
|
+
scenarioId: string | null;
|
|
152
|
+
sortPosition: number;
|
|
153
|
+
evaluationTemplate: unknown | null;
|
|
154
|
+
scenario: Scenario | null;
|
|
155
|
+
}>;
|
|
156
|
+
/** `GET /courses/{id}/items` returns an array of course items. */
|
|
157
|
+
type CourseItems = CourseItem[];
|
|
158
|
+
|
|
159
|
+
/** Which SimCapture server a request targets. */
|
|
160
|
+
type SimCaptureServer = "api" | "inventory";
|
|
161
|
+
interface HttpRequest {
|
|
162
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
163
|
+
/** Path relative to the target server's base URL, e.g. `/reservations`. */
|
|
164
|
+
path: string;
|
|
165
|
+
server?: SimCaptureServer;
|
|
166
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
167
|
+
body?: unknown;
|
|
168
|
+
/** Extra headers merged on top of the auth token header. */
|
|
169
|
+
headers?: Record<string, string>;
|
|
170
|
+
/** When true the auth token header is NOT attached (used for `/auth`). */
|
|
171
|
+
skipAuth?: boolean;
|
|
172
|
+
/** Response payload type; `blob` is used for attachment downloads. */
|
|
173
|
+
responseType?: "json" | "blob";
|
|
174
|
+
}
|
|
175
|
+
interface HttpResponse<T> {
|
|
176
|
+
status: number;
|
|
177
|
+
data: T;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Transport port. The domain/application layers depend on this interface, not
|
|
181
|
+
* axios — which keeps them framework-free and trivially testable with fakes.
|
|
182
|
+
*/
|
|
183
|
+
interface HttpClient {
|
|
184
|
+
request<T>(req: HttpRequest): Promise<HttpResponse<T>>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Immutable login credentials. `clientSubdomain` defaults to the UPCH tenant
|
|
189
|
+
* (`simulacion-upch`), matching what the old services hard-coded.
|
|
190
|
+
*/
|
|
191
|
+
declare class AuthCredentials {
|
|
192
|
+
static readonly DEFAULT_SUBDOMAIN = "simulacion-upch";
|
|
193
|
+
readonly username: string;
|
|
194
|
+
readonly password: string;
|
|
195
|
+
readonly clientSubdomain: string;
|
|
196
|
+
constructor(params: {
|
|
197
|
+
username: string;
|
|
198
|
+
password: string;
|
|
199
|
+
clientSubdomain?: string;
|
|
200
|
+
});
|
|
201
|
+
toAuthRequest(): {
|
|
202
|
+
username: string;
|
|
203
|
+
password: string;
|
|
204
|
+
clientSubdomain: string;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* SimCapture session token.
|
|
210
|
+
*
|
|
211
|
+
* `POST /auth` returns only `{ token }` with no expiry, so we apply a *soft* TTL
|
|
212
|
+
* to proactively refresh before the server would reject it. The authoritative
|
|
213
|
+
* invalidation remains the 401 path (see Authenticator). `ttlMs <= 0` disables
|
|
214
|
+
* the soft expiry and the token is reused until a 401.
|
|
215
|
+
*/
|
|
216
|
+
declare class Token {
|
|
217
|
+
readonly value: string;
|
|
218
|
+
/** Epoch ms after which the token is considered stale, or null when no TTL. */
|
|
219
|
+
readonly expiresAt: number | null;
|
|
220
|
+
constructor(value: string, expiresAt?: number | null);
|
|
221
|
+
static issue(value: string, ttlMs: number, now?: number): Token;
|
|
222
|
+
isExpired(now?: number): boolean;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Caches the active session token. Default impl is in-memory; the port lets a
|
|
227
|
+
* consumer swap in a shared store (e.g. Redis) if several instances must reuse
|
|
228
|
+
* one SimCapture session.
|
|
229
|
+
*/
|
|
230
|
+
interface TokenStore {
|
|
231
|
+
get(): Token | null;
|
|
232
|
+
set(token: Token): void;
|
|
233
|
+
clear(): void;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
interface AuthenticatorOptions {
|
|
237
|
+
http: HttpClient;
|
|
238
|
+
store: TokenStore;
|
|
239
|
+
credentials: AuthCredentials;
|
|
240
|
+
tokenTtlMs: number;
|
|
241
|
+
now?: () => number;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Owns the SimCapture session. Fixes the old "login on every call" anti-pattern:
|
|
245
|
+
*
|
|
246
|
+
* - tokens are cached and reused until the soft TTL elapses or a 401 occurs;
|
|
247
|
+
* - concurrent callers share a single in-flight `/auth` request;
|
|
248
|
+
* - on 401 the token is cleared, re-issued **once**, and the request retried once.
|
|
249
|
+
*/
|
|
250
|
+
declare class Authenticator {
|
|
251
|
+
private readonly http;
|
|
252
|
+
private readonly store;
|
|
253
|
+
private readonly credentials;
|
|
254
|
+
private readonly tokenTtlMs;
|
|
255
|
+
private readonly now;
|
|
256
|
+
private inFlightLogin;
|
|
257
|
+
constructor(options: AuthenticatorOptions);
|
|
258
|
+
/** Returns a valid token, logging in only when none is cached or it is stale. */
|
|
259
|
+
getToken(): Promise<Token>;
|
|
260
|
+
/** Forces a fresh login, deduping concurrent calls into one `/auth` request. */
|
|
261
|
+
login(): Promise<Token>;
|
|
262
|
+
/**
|
|
263
|
+
* Sends `req` with the cached token attached. On a 401 it refreshes the token
|
|
264
|
+
* once and retries the request a single time; a second 401 propagates.
|
|
265
|
+
*/
|
|
266
|
+
authorizedRequest<T>(req: HttpRequest): Promise<HttpResponse<T>>;
|
|
267
|
+
private requestToken;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Shared base: every resource sends authorized requests through the Authenticator. */
|
|
271
|
+
declare abstract class BaseResource {
|
|
272
|
+
protected readonly auth: Authenticator;
|
|
273
|
+
protected constructor(auth: Authenticator);
|
|
274
|
+
protected exec<T>(req: HttpRequest): Promise<T>;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
declare class CoursesResource extends BaseResource {
|
|
278
|
+
constructor(auth: Authenticator);
|
|
279
|
+
/** `POST /courses` — one page of results. */
|
|
280
|
+
find(query: FindCoursesQuery): Promise<CoursesPage>;
|
|
281
|
+
/** `GET /courses/{courseId}/items` — scenarios / evaluation templates. */
|
|
282
|
+
getItems(courseId: string, params?: CourseItemsParams): Promise<CourseItems>;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** `GET /items` and `GET /item/{id}` (inventory server) — one inventory item. */
|
|
286
|
+
type InventoryItem = Passthrough<{
|
|
287
|
+
itemId: string;
|
|
288
|
+
name: string;
|
|
289
|
+
clientId: string;
|
|
290
|
+
isConsumable: boolean;
|
|
291
|
+
isArchived: boolean;
|
|
292
|
+
quantity: number;
|
|
293
|
+
lowStockQuantity: number;
|
|
294
|
+
/** Decimal string, e.g. `"80.51"`. */
|
|
295
|
+
cost: string | null;
|
|
296
|
+
locationId: string | null;
|
|
297
|
+
manufacturer: string | null;
|
|
298
|
+
modelNumber: string | null;
|
|
299
|
+
supplier: string | null;
|
|
300
|
+
purchaseDate: string | null;
|
|
301
|
+
notes: string | null;
|
|
302
|
+
imageId: string | null;
|
|
303
|
+
createdTs: string;
|
|
304
|
+
updatedTs: string;
|
|
305
|
+
inUse: boolean;
|
|
306
|
+
}>;
|
|
307
|
+
interface InventoryItemEdit {
|
|
308
|
+
name?: string;
|
|
309
|
+
isConsumable?: boolean;
|
|
310
|
+
quantity?: number;
|
|
311
|
+
lowStockQuantity?: number;
|
|
312
|
+
locationId?: string;
|
|
313
|
+
manufacturer?: string;
|
|
314
|
+
modelNumber?: string;
|
|
315
|
+
supplier?: string;
|
|
316
|
+
purchaseDate?: string;
|
|
317
|
+
cost?: number;
|
|
318
|
+
notes?: string;
|
|
319
|
+
imageId?: string;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Inventory subsystem — every call targets server B (`inventoryUrl`). */
|
|
323
|
+
declare class InventoryResource extends BaseResource {
|
|
324
|
+
constructor(auth: Authenticator);
|
|
325
|
+
/** `GET /items` (server B) */
|
|
326
|
+
findAll(): Promise<InventoryItem[]>;
|
|
327
|
+
/** `GET /item/{itemId}` (server B) */
|
|
328
|
+
findOne(itemId: string): Promise<InventoryItem>;
|
|
329
|
+
/** `POST /item/{itemId}/edit` (server B) — stock adjustment. */
|
|
330
|
+
edit(itemId: string, body: InventoryItemEdit): Promise<InventoryItem>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** `GET /locations` — one physical location/room. */
|
|
334
|
+
type Location = Passthrough<{
|
|
335
|
+
locationId: string;
|
|
336
|
+
clientId: string;
|
|
337
|
+
name: string;
|
|
338
|
+
active: boolean;
|
|
339
|
+
inUse: boolean;
|
|
340
|
+
sort: number;
|
|
341
|
+
parentLocationId: string | null;
|
|
342
|
+
captureHostId: string | null;
|
|
343
|
+
}>;
|
|
344
|
+
interface ListLocationsParams {
|
|
345
|
+
/** e.g. `sort` — matches the existing `?order=sort` call. */
|
|
346
|
+
order?: string;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
declare class LocationsResource extends BaseResource {
|
|
350
|
+
constructor(auth: Authenticator);
|
|
351
|
+
/** `GET /locations?order=...` */
|
|
352
|
+
findAll(params?: ListLocationsParams): Promise<Location[]>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
interface NotificationRequest {
|
|
356
|
+
recipients: string[];
|
|
357
|
+
notification: {
|
|
358
|
+
subject?: string;
|
|
359
|
+
body?: string;
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
declare class NotificationsResource extends BaseResource {
|
|
364
|
+
constructor(auth: Authenticator);
|
|
365
|
+
/** `POST /notifications` — send an email / notification. */
|
|
366
|
+
send(body: NotificationRequest): Promise<void>;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
declare class OrganizationsResource extends BaseResource {
|
|
370
|
+
constructor(auth: Authenticator);
|
|
371
|
+
/** `GET /organizations` */
|
|
372
|
+
findAll(): Promise<Organization[]>;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
interface ReservationLocation {
|
|
376
|
+
locationId: string;
|
|
377
|
+
clientId: string;
|
|
378
|
+
name: string;
|
|
379
|
+
parentLocationId: string | null;
|
|
380
|
+
sort: number;
|
|
381
|
+
active: boolean;
|
|
382
|
+
captureHostId: string | null;
|
|
383
|
+
}
|
|
384
|
+
interface ReservationCourseRef {
|
|
385
|
+
courseId: string;
|
|
386
|
+
publicTitle: string | null;
|
|
387
|
+
privateTitle: string;
|
|
388
|
+
active: boolean;
|
|
389
|
+
acl?: unknown[];
|
|
390
|
+
}
|
|
391
|
+
interface ReservationScenarioRef {
|
|
392
|
+
scenarioId: string;
|
|
393
|
+
publicTitle: string | null;
|
|
394
|
+
privateTitle: string;
|
|
395
|
+
active: boolean;
|
|
396
|
+
acl?: unknown[];
|
|
397
|
+
}
|
|
398
|
+
/** `GET /reservations` and `GET /reservation/{id}` — one reservation. */
|
|
399
|
+
type Reservation = Passthrough<{
|
|
400
|
+
reservationId: string;
|
|
401
|
+
clientId: string;
|
|
402
|
+
organizationId: string;
|
|
403
|
+
ownerUserId: string;
|
|
404
|
+
title: string;
|
|
405
|
+
publicNotes: string;
|
|
406
|
+
/** May embed JSON inside `<{...}>`. */
|
|
407
|
+
privateNotes: string;
|
|
408
|
+
startTs: string;
|
|
409
|
+
endTs: string;
|
|
410
|
+
/** Decimal string, e.g. `"0.000000"`. */
|
|
411
|
+
setupTimeMinutes: string;
|
|
412
|
+
takedownTimeMinutes: string;
|
|
413
|
+
setupInstructions: string;
|
|
414
|
+
color: string;
|
|
415
|
+
reservationRequestId: string | null;
|
|
416
|
+
isSelfEnroll: boolean;
|
|
417
|
+
expectedNumberOfParticipants: number;
|
|
418
|
+
shouldConsumeInvByRes: boolean;
|
|
419
|
+
seriesId: string | null;
|
|
420
|
+
locations: ReservationLocation[];
|
|
421
|
+
simulators: Passthrough[];
|
|
422
|
+
courses: ReservationCourseRef[];
|
|
423
|
+
scenarios: ReservationScenarioRef[];
|
|
424
|
+
organization: Passthrough<{
|
|
425
|
+
organizationId: string;
|
|
426
|
+
clientId: string;
|
|
427
|
+
}>;
|
|
428
|
+
supportStaff: unknown[];
|
|
429
|
+
users: unknown[];
|
|
430
|
+
selfEnrollments: unknown[] | null;
|
|
431
|
+
/** Present only in the list response. */
|
|
432
|
+
inventoryManagementItems?: unknown[];
|
|
433
|
+
}>;
|
|
434
|
+
interface ListReservationsParams {
|
|
435
|
+
/** ISO-8601 timestamp; only reservations ending after this are returned. */
|
|
436
|
+
endAfterTs: string;
|
|
437
|
+
/** ISO-8601 timestamp; only reservations starting before this are returned. */
|
|
438
|
+
startBeforeTs: string;
|
|
439
|
+
/** `false` fetches the full reservation detail. Defaults to `false`. */
|
|
440
|
+
limitFetchedReservationDetails?: boolean;
|
|
441
|
+
}
|
|
442
|
+
interface ReservationOverviewUpdate {
|
|
443
|
+
reservationColor?: string;
|
|
444
|
+
reservationTitle?: string;
|
|
445
|
+
locationIds?: string[];
|
|
446
|
+
reservationStartTs?: string;
|
|
447
|
+
reservationEndTs?: string;
|
|
448
|
+
[key: string]: unknown;
|
|
449
|
+
}
|
|
450
|
+
interface ReservationSetupUpdate {
|
|
451
|
+
reservationSetupInstructions?: string;
|
|
452
|
+
reservationSetupTimeMinutes?: number;
|
|
453
|
+
reservationTakedownTimeMinutes?: number;
|
|
454
|
+
inventoryManagementItems?: Unknown[];
|
|
455
|
+
shouldConsumeInvByRes?: boolean;
|
|
456
|
+
reservationPrivateNotes?: string;
|
|
457
|
+
simulatorIds?: string[];
|
|
458
|
+
}
|
|
459
|
+
/** Body for `PUT /reservation/{id}` — either an overview or a setup update. */
|
|
460
|
+
type ReservationUpdate = ReservationOverviewUpdate | ReservationSetupUpdate;
|
|
461
|
+
/** Body for `PUT /reservation/{id}/details` (ENGINEERING-MS flow). */
|
|
462
|
+
interface ReservationDetailsUpdate {
|
|
463
|
+
organizationId?: string;
|
|
464
|
+
simulators?: Unknown[];
|
|
465
|
+
expectedNumberOfParticipants?: number;
|
|
466
|
+
privateNotes?: string;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
declare class ReservationsResource extends BaseResource {
|
|
470
|
+
constructor(auth: Authenticator);
|
|
471
|
+
/** `GET /reservations?endAfterTs=&startBeforeTs=&limitFetchedReservationDetails=` */
|
|
472
|
+
findAll(params: ListReservationsParams): Promise<Reservation[]>;
|
|
473
|
+
/** `GET /reservation/{id}` */
|
|
474
|
+
findOne(reservationId: string): Promise<Reservation>;
|
|
475
|
+
/** `PUT /reservation/{id}` — overview or setup update. */
|
|
476
|
+
update(reservationId: string, body: ReservationUpdate): Promise<Reservation>;
|
|
477
|
+
/** `PUT /reservation/{id}/details` (ENGINEERING-MS flow). */
|
|
478
|
+
updateDetails(reservationId: string, body: ReservationDetailsUpdate): Promise<Reservation>;
|
|
479
|
+
/** `DELETE /reservation/{id}` */
|
|
480
|
+
delete(reservationId: string): Promise<void>;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
declare class ScenariosResource extends BaseResource {
|
|
484
|
+
constructor(auth: Authenticator);
|
|
485
|
+
/** `GET /scenarios/{id}` */
|
|
486
|
+
findOne(scenarioId: string): Promise<Scenario>;
|
|
487
|
+
/** `PUT /scenarios/{id}` — setup update. */
|
|
488
|
+
updateSetup(scenarioId: string, body: ScenarioSetupUpdate): Promise<Scenario>;
|
|
489
|
+
/** `PUT /scenarios/{id}/details` — detail update. */
|
|
490
|
+
updateDetails(scenarioId: string, body: ScenarioDetailsUpdate): Promise<Scenario>;
|
|
491
|
+
/**
|
|
492
|
+
* `GET /scenarios/{id}/attachments/{assetName}` — returns the raw PDF bytes.
|
|
493
|
+
* The token is sent both as a query param and the header (matching the API).
|
|
494
|
+
*/
|
|
495
|
+
getAttachment(scenarioId: string, assetName: string, params: ScenarioAttachmentParams): Promise<ArrayBuffer>;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** `GET /simulators` and `GET /simulators/{id}` — one simulator/equipment unit. */
|
|
499
|
+
type Simulator = Passthrough<{
|
|
500
|
+
simulatorId: string;
|
|
501
|
+
simulatorExternalId: string;
|
|
502
|
+
clientId: string;
|
|
503
|
+
name: string;
|
|
504
|
+
active: boolean;
|
|
505
|
+
inUse: boolean;
|
|
506
|
+
applicationId: string | null;
|
|
507
|
+
/** Stringified JSON, e.g. `{"estado":"OPERATIVO","qr":true}`. */
|
|
508
|
+
notes: string;
|
|
509
|
+
}>;
|
|
510
|
+
interface SimulatorModel {
|
|
511
|
+
simulatorModelId: string;
|
|
512
|
+
name: string;
|
|
513
|
+
configOptions: SimulatorConfigOption[];
|
|
514
|
+
simulatorCompany: {
|
|
515
|
+
simulatorCompanyId: string;
|
|
516
|
+
name: string;
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/** `GET /simulator-configs/{id}` — simulator plus its model/config metadata. */
|
|
520
|
+
type SimulatorConfig = Passthrough<{
|
|
521
|
+
simulatorId: string;
|
|
522
|
+
simulatorExternalId: string;
|
|
523
|
+
clientId: string;
|
|
524
|
+
name: string;
|
|
525
|
+
active: boolean;
|
|
526
|
+
notes: string;
|
|
527
|
+
simulatorModel: SimulatorModel;
|
|
528
|
+
configOptions: SimulatorConfigOption[];
|
|
529
|
+
}>;
|
|
530
|
+
interface SimulatorConfigOption {
|
|
531
|
+
key: string;
|
|
532
|
+
value: string;
|
|
533
|
+
}
|
|
534
|
+
interface SimulatorUpdate {
|
|
535
|
+
name?: string;
|
|
536
|
+
notes?: string;
|
|
537
|
+
simulatorModelId?: string;
|
|
538
|
+
configOptions?: SimulatorConfigOption[];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
declare class SimulatorsResource extends BaseResource {
|
|
542
|
+
constructor(auth: Authenticator);
|
|
543
|
+
/** `GET /simulators` */
|
|
544
|
+
findAll(): Promise<Simulator[]>;
|
|
545
|
+
/** `GET /simulators/{id}` */
|
|
546
|
+
findOne(simulatorId: string): Promise<Simulator>;
|
|
547
|
+
/** `PUT /simulators/{id}` */
|
|
548
|
+
update(simulatorId: string, body: SimulatorUpdate): Promise<Simulator>;
|
|
549
|
+
/** `GET /simulator-configs/{id}` */
|
|
550
|
+
getConfig(simulatorId: string): Promise<SimulatorConfig>;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
interface SimCaptureConfig {
|
|
554
|
+
/** Main API base URL, e.g. `https://api.simcapture.com`. */
|
|
555
|
+
apiUrl: string;
|
|
556
|
+
/** Inventory subsystem base URL (server B). */
|
|
557
|
+
inventoryUrl: string;
|
|
558
|
+
credentials: {
|
|
559
|
+
username: string;
|
|
560
|
+
password: string;
|
|
561
|
+
/** Defaults to `simulacion-upch`. */
|
|
562
|
+
clientSubdomain?: string;
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* Soft token TTL in ms. The token is proactively refreshed once stale; a 401
|
|
566
|
+
* still forces a refresh regardless. `0` disables the soft TTL.
|
|
567
|
+
* Default: 30 minutes.
|
|
568
|
+
*/
|
|
569
|
+
tokenTtlMs?: number;
|
|
570
|
+
/** Per-request timeout in ms. Default: 30s. */
|
|
571
|
+
timeoutMs?: number;
|
|
572
|
+
}
|
|
573
|
+
declare const SIMCAPTURE_DEFAULTS: {
|
|
574
|
+
readonly apiUrl: "https://api.simcapture.com";
|
|
575
|
+
readonly inventoryUrl: "https://inventory-management-production.us-east-1.simcapture.com";
|
|
576
|
+
readonly tokenTtlMs: number;
|
|
577
|
+
readonly timeoutMs: number;
|
|
578
|
+
};
|
|
579
|
+
interface ResolvedSimCaptureConfig {
|
|
580
|
+
apiUrl: string;
|
|
581
|
+
inventoryUrl: string;
|
|
582
|
+
credentials: {
|
|
583
|
+
username: string;
|
|
584
|
+
password: string;
|
|
585
|
+
clientSubdomain?: string;
|
|
586
|
+
};
|
|
587
|
+
tokenTtlMs: number;
|
|
588
|
+
timeoutMs: number;
|
|
589
|
+
}
|
|
590
|
+
/** Fills defaults and fails fast on missing required values. */
|
|
591
|
+
declare function resolveConfig(config: SimCaptureConfig): ResolvedSimCaptureConfig;
|
|
592
|
+
|
|
593
|
+
interface SimCaptureClientDeps {
|
|
594
|
+
/** Override the transport (e.g. for tests). Defaults to AxiosHttpClient. */
|
|
595
|
+
httpClient?: HttpClient;
|
|
596
|
+
/** Override the token cache. Defaults to InMemoryTokenStore. */
|
|
597
|
+
tokenStore?: TokenStore;
|
|
598
|
+
/** Override the clock (tests). */
|
|
599
|
+
now?: () => number;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Single entrypoint to SimCapture. Wires the infrastructure adapters into the
|
|
603
|
+
* application use-cases (composition root) and exposes one resource per domain.
|
|
604
|
+
*
|
|
605
|
+
* ```ts
|
|
606
|
+
* const sc = new SimCaptureClient({
|
|
607
|
+
* apiUrl: process.env.SIMCAPTURE_API!,
|
|
608
|
+
* inventoryUrl: process.env.SIMCAPTURE_INVENTORY_API!,
|
|
609
|
+
* credentials: { username: process.env.SIMCAPTURE_USER!, password: process.env.SIMCAPTURE_PASSWORD! },
|
|
610
|
+
* });
|
|
611
|
+
* const orgs = await sc.organizations.findAll();
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
declare class SimCaptureClient {
|
|
615
|
+
readonly organizations: OrganizationsResource;
|
|
616
|
+
readonly locations: LocationsResource;
|
|
617
|
+
readonly reservations: ReservationsResource;
|
|
618
|
+
readonly courses: CoursesResource;
|
|
619
|
+
readonly scenarios: ScenariosResource;
|
|
620
|
+
readonly simulators: SimulatorsResource;
|
|
621
|
+
readonly notifications: NotificationsResource;
|
|
622
|
+
readonly inventory: InventoryResource;
|
|
623
|
+
private readonly authenticator;
|
|
624
|
+
constructor(config: SimCaptureConfig, deps?: SimCaptureClientDeps);
|
|
625
|
+
/** Force a fresh login. Rarely needed — auth is handled lazily per request. */
|
|
626
|
+
login(): Promise<void>;
|
|
627
|
+
/** Returns a valid session token, logging in only if none is cached. */
|
|
628
|
+
getToken(): Promise<{
|
|
629
|
+
token: string;
|
|
630
|
+
}>;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Error raised when a SimCapture request fails. Unlike the old per-service
|
|
635
|
+
* `RpcException({ status: 400 })`, this preserves the real upstream HTTP status
|
|
636
|
+
* and response body so callers can translate it faithfully.
|
|
637
|
+
*/
|
|
638
|
+
declare class SimCaptureError extends Error {
|
|
639
|
+
/** HTTP status returned by SimCapture, or 0 when the request never completed. */
|
|
640
|
+
readonly status: number;
|
|
641
|
+
/** Stable machine-readable code for branching (e.g. `AUTH_FAILED`). */
|
|
642
|
+
readonly code: SimCaptureErrorCode;
|
|
643
|
+
/** Raw upstream response body, when available. */
|
|
644
|
+
readonly body: unknown;
|
|
645
|
+
constructor(params: {
|
|
646
|
+
message: string;
|
|
647
|
+
status: number;
|
|
648
|
+
code: SimCaptureErrorCode;
|
|
649
|
+
body?: unknown;
|
|
650
|
+
cause?: unknown;
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
type SimCaptureErrorCode = "AUTH_FAILED" | "UNAUTHORIZED" | "NETWORK" | "UPSTREAM_ERROR" | "CONFIG_INVALID";
|
|
654
|
+
|
|
655
|
+
interface AxiosHttpClientOptions {
|
|
656
|
+
apiUrl: string;
|
|
657
|
+
inventoryUrl: string;
|
|
658
|
+
timeoutMs: number;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Transport adapter. Pure request/response over two axios instances (main API +
|
|
662
|
+
* inventory server). It is auth-agnostic: the caller decides whether a token
|
|
663
|
+
* header is attached. Upstream failures are normalised into `SimCaptureError`
|
|
664
|
+
* preserving the real status and body.
|
|
665
|
+
*/
|
|
666
|
+
declare class AxiosHttpClient implements HttpClient {
|
|
667
|
+
private readonly instances;
|
|
668
|
+
constructor(options: AxiosHttpClientOptions);
|
|
669
|
+
request<T>(req: HttpRequest): Promise<HttpResponse<T>>;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/** Default token store: keeps the active token in process memory. */
|
|
673
|
+
declare class InMemoryTokenStore implements TokenStore {
|
|
674
|
+
private token;
|
|
675
|
+
get(): Token | null;
|
|
676
|
+
set(token: Token): void;
|
|
677
|
+
clear(): void;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
interface AuthRequest {
|
|
681
|
+
username: string;
|
|
682
|
+
password: string;
|
|
683
|
+
clientSubdomain: string;
|
|
684
|
+
}
|
|
685
|
+
/** `POST /auth` response. Only `token` is consumed by the authenticator. */
|
|
686
|
+
interface AuthResponse {
|
|
687
|
+
token: string;
|
|
688
|
+
clientId?: string;
|
|
689
|
+
userId?: string;
|
|
690
|
+
name?: string;
|
|
691
|
+
institutionName?: string;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
export { AuthCredentials, type AuthRequest, type AuthResponse, Authenticator, AxiosHttpClient, type CourseItem, type CourseItems, type CourseItemsParams, type CourseOwner, type CourseRow, type CoursesPage, CoursesResource, type FindCoursesQuery, type HttpClient, type HttpRequest, type HttpResponse, InMemoryTokenStore, type InventoryItem, type InventoryItemEdit, InventoryResource, type ListLocationsParams, type ListReservationsParams, type Location, LocationsResource, type NotificationRequest, NotificationsResource, type Organization, OrganizationsResource, type Passthrough, type Reservation, type ReservationCourseRef, type ReservationDetailsUpdate, type ReservationLocation, type ReservationOverviewUpdate, type ReservationScenarioRef, type ReservationSetupUpdate, type ReservationUpdate, ReservationsResource, type ResolvedSimCaptureConfig, SIMCAPTURE_DEFAULTS, type Scenario, type ScenarioAsset, type ScenarioAttachmentParams, type ScenarioDetailsUpdate, type ScenarioLearningObjective, type ScenarioSetupUpdate, ScenariosResource, SimCaptureClient, type SimCaptureClientDeps, type SimCaptureConfig, SimCaptureError, type SimCaptureErrorCode, type SimCaptureServer, type Simulator, type SimulatorConfig, type SimulatorConfigOption, type SimulatorModel, type SimulatorUpdate, SimulatorsResource, Token, type TokenStore, type Unknown, resolveConfig };
|