@aperture-browser/api-client 0.0.0-init.0 → 0.0.0-nightly.82.2e4f6368108f
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/README.md +108 -10
- package/dist/index.d.ts +386 -392
- package/dist/index.js +367 -30
- package/dist/index.js.map +1 -1
- package/dist/passkeys.d.ts +30 -0
- package/dist/passkeys.js +54 -0
- package/dist/passkeys.js.map +1 -0
- package/dist/service-DhkerN7u.d.ts +296 -0
- package/dist/service-riqZPQHq.js +8 -0
- package/dist/service-riqZPQHq.js.map +1 -0
- package/package.json +13 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as AuthApi } from "./service-riqZPQHq.js";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as Layer from "effect/Layer";
|
|
3
4
|
import * as HttpBody from "effect/unstable/http/HttpBody";
|
|
@@ -10,6 +11,9 @@ import * as PubSub from "effect/PubSub";
|
|
|
10
11
|
import * as Stream from "effect/Stream";
|
|
11
12
|
import * as Function from "effect/Function";
|
|
12
13
|
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
14
|
+
import * as Option from "effect/Option";
|
|
15
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
16
|
+
import * as Random from "effect/Random";
|
|
13
17
|
//#region src/authorization/service.ts
|
|
14
18
|
const TENANT_HEADER = "X-Aperture-Tenant-Id";
|
|
15
19
|
/** The browser's own login session, sent as a cookie. */
|
|
@@ -173,10 +177,6 @@ const TOTPEnrollment = Schema.Struct({
|
|
|
173
177
|
});
|
|
174
178
|
const RecoveryCodes = Schema.Struct({ recoveryCodes: Schema.Array(Schema.String) });
|
|
175
179
|
//#endregion
|
|
176
|
-
//#region src/auth/service.ts
|
|
177
|
-
/** Browser login, account security, and the current principal. */
|
|
178
|
-
var AuthApi = class extends Context.Service()("@aperture-browser/api-client/AuthApi") {};
|
|
179
|
-
//#endregion
|
|
180
180
|
//#region src/auth/layer.ts
|
|
181
181
|
const jsonBody = (body) => ({ body: HttpBody.jsonUnsafe(body) });
|
|
182
182
|
const makeAuthApi = Effect.gen(function* () {
|
|
@@ -292,6 +292,13 @@ const ApiErrorBody = Schema.Struct({ error: Schema.Struct({
|
|
|
292
292
|
message: Schema.String
|
|
293
293
|
}) });
|
|
294
294
|
/**
|
|
295
|
+
* The error body of a live-session route that failed inside the running session. It carries
|
|
296
|
+
* no stable code, so the resulting ApiRequestError uses `live_session_error` and callers
|
|
297
|
+
* branch on its status.
|
|
298
|
+
*/
|
|
299
|
+
const LiveSessionErrorBody = Schema.Struct({ error: Schema.String });
|
|
300
|
+
const ErrorResponseBody = Schema.Union([ApiErrorBody, LiveSessionErrorBody]);
|
|
301
|
+
/**
|
|
295
302
|
* A failed API request. `code` and `message` come from the server's error body when it
|
|
296
303
|
* sent one; `status` is 0 when no HTTP status applies.
|
|
297
304
|
*/
|
|
@@ -315,7 +322,11 @@ const networkError = new ApiRequestError({
|
|
|
315
322
|
* message of the server's error body.
|
|
316
323
|
*/
|
|
317
324
|
const toApiRequestError = (self) => self.pipe(Effect.catchReasons("HttpClientError", {
|
|
318
|
-
StatusCodeError: ({ response }) => HttpClientResponse.schemaBodyJson(
|
|
325
|
+
StatusCodeError: ({ response }) => HttpClientResponse.schemaBodyJson(ErrorResponseBody)(response).pipe(Effect.map(({ error }) => typeof error === "string" ? new ApiRequestError({
|
|
326
|
+
code: "live_session_error",
|
|
327
|
+
message: error,
|
|
328
|
+
status: response.status
|
|
329
|
+
}) : new ApiRequestError({
|
|
319
330
|
...error,
|
|
320
331
|
status: response.status
|
|
321
332
|
})), Effect.orElseSucceed(() => new ApiRequestError({
|
|
@@ -359,6 +370,43 @@ const makeApiAuthorization = Effect.gen(function* () {
|
|
|
359
370
|
});
|
|
360
371
|
const apiAuthorizationLayer = Layer.effect(ApiAuthorization, makeApiAuthorization);
|
|
361
372
|
//#endregion
|
|
373
|
+
//#region src/events/service.ts
|
|
374
|
+
/** The tenant's resource event log, and the deployment-wide audit log. */
|
|
375
|
+
var EventsApi = class extends Context.Service()("@aperture-browser/api-client/EventsApi") {};
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/health/service.ts
|
|
378
|
+
/** The instance's unauthenticated health check. */
|
|
379
|
+
var HealthApi = class extends Context.Service()("@aperture-browser/api-client/HealthApi") {};
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/pagination.ts
|
|
382
|
+
function getNextPageParam(page) {
|
|
383
|
+
return page.meta.hasMore ? page.meta.nextCursor : void 0;
|
|
384
|
+
}
|
|
385
|
+
function flattenInfinitePages(pages) {
|
|
386
|
+
return pages?.flatMap((page) => page.data) ?? [];
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Derives `stream` and `listAll` from the call that fetches one page. That call must also
|
|
390
|
+
* accept a bare cursor, which every filter allows because all its fields are optional.
|
|
391
|
+
*/
|
|
392
|
+
function paginated(list) {
|
|
393
|
+
const stream = (credentials, filter) => Stream.paginate(void 0, (cursor) => list(credentials, filter === void 0 ? { cursor } : {
|
|
394
|
+
...filter,
|
|
395
|
+
cursor
|
|
396
|
+
}).pipe(Effect.map((page) => {
|
|
397
|
+
const next = getNextPageParam(page);
|
|
398
|
+
return [page.data, next === void 0 ? Option.none() : Option.some(next)];
|
|
399
|
+
})));
|
|
400
|
+
const listAll = (credentials, filter) => Stream.runCollect(stream(credentials, filter));
|
|
401
|
+
return {
|
|
402
|
+
list,
|
|
403
|
+
stream,
|
|
404
|
+
listAll
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const defaultListLimit = 50;
|
|
408
|
+
const listQueryDefaults = { refetchOnWindowFocus: true };
|
|
409
|
+
//#endregion
|
|
362
410
|
//#region src/query.ts
|
|
363
411
|
/** Encodes tag filters as the parallel tagKey, tagOperator and tagValue query lists. */
|
|
364
412
|
const tagQuery = (tags) => ({
|
|
@@ -374,10 +422,6 @@ const compactQuery = (query) => Object.fromEntries(Object.entries(query).flatMap
|
|
|
374
422
|
return items.length > 0 ? [[key, items]] : [];
|
|
375
423
|
}));
|
|
376
424
|
//#endregion
|
|
377
|
-
//#region src/events/service.ts
|
|
378
|
-
/** The tenant's resource event log. */
|
|
379
|
-
var EventsApi = class extends Context.Service()("@aperture-browser/api-client/EventsApi") {};
|
|
380
|
-
//#endregion
|
|
381
425
|
//#region src/events/layer.ts
|
|
382
426
|
const makeEventsApi = Effect.gen(function* () {
|
|
383
427
|
const httpClient = yield* HttpClient.HttpClient;
|
|
@@ -391,10 +435,43 @@ const makeEventsApi = Effect.gen(function* () {
|
|
|
391
435
|
resourceId: params.resourceId
|
|
392
436
|
}) }).pipe(authorize(Authorization.tenantScoped(credentials)));
|
|
393
437
|
});
|
|
394
|
-
|
|
438
|
+
const listAuditEvents = Effect.fn("EventsApi.listAuditEvents")(function* (credentials, params = {}) {
|
|
439
|
+
return yield* api.listAuditEvents({ params: compactQuery({
|
|
440
|
+
limit: params.limit,
|
|
441
|
+
cursor: params.cursor,
|
|
442
|
+
tenantId: params.tenantId,
|
|
443
|
+
actorType: params.actorType,
|
|
444
|
+
actorId: params.actorId,
|
|
445
|
+
action: params.action,
|
|
446
|
+
resourceType: params.resourceType,
|
|
447
|
+
resourceId: params.resourceId
|
|
448
|
+
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
449
|
+
});
|
|
450
|
+
const events = paginated(listEvents);
|
|
451
|
+
const auditEvents = paginated(listAuditEvents);
|
|
452
|
+
return EventsApi.of({
|
|
453
|
+
listEvents,
|
|
454
|
+
streamEvents: events.stream,
|
|
455
|
+
listAllEvents: events.listAll,
|
|
456
|
+
listAuditEvents,
|
|
457
|
+
streamAuditEvents: auditEvents.stream,
|
|
458
|
+
listAllAuditEvents: auditEvents.listAll
|
|
459
|
+
});
|
|
395
460
|
});
|
|
396
461
|
const eventsApiLayer = Layer.effect(EventsApi, makeEventsApi);
|
|
397
462
|
//#endregion
|
|
463
|
+
//#region src/health/layer.ts
|
|
464
|
+
const makeHealthApi = Effect.gen(function* () {
|
|
465
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
466
|
+
const { authorize } = yield* ApiAuthorization;
|
|
467
|
+
const api = Api.make(httpClient);
|
|
468
|
+
const getHealth = Effect.fn("HealthApi.getHealth")(function* () {
|
|
469
|
+
return yield* api.getHealth(void 0).pipe(authorize(Authorization.anonymous));
|
|
470
|
+
});
|
|
471
|
+
return HealthApi.of({ getHealth });
|
|
472
|
+
});
|
|
473
|
+
const healthApiLayer = Layer.effect(HealthApi, makeHealthApi);
|
|
474
|
+
//#endregion
|
|
398
475
|
//#region src/sessions/schemas.ts
|
|
399
476
|
const positiveInt = Schema.Number.check(Schema.isInt(), Schema.isGreaterThan(0));
|
|
400
477
|
const emptyArray = Effect.succeed([]);
|
|
@@ -424,8 +501,7 @@ const BrowserStatus = Schema.Struct({
|
|
|
424
501
|
})
|
|
425
502
|
})).pipe(Schema.withDecodingDefaultKey(emptyArray))
|
|
426
503
|
});
|
|
427
|
-
|
|
428
|
-
const Recording = Schema.Struct({
|
|
504
|
+
const recordingFields = {
|
|
429
505
|
recordingId: Schema.String,
|
|
430
506
|
mode: Schema.Literals(["tab", "viewer"]),
|
|
431
507
|
targetId: Schema.String,
|
|
@@ -437,21 +513,75 @@ const Recording = Schema.Struct({
|
|
|
437
513
|
"failed"
|
|
438
514
|
]),
|
|
439
515
|
stopReason: Schema.optionalKey(Schema.String),
|
|
440
|
-
|
|
516
|
+
sandboxPath: Schema.optionalKey(Schema.String),
|
|
517
|
+
/** @deprecated Read `relativePath`. Sessions started before it existed send only this. */
|
|
518
|
+
path: Schema.optionalKey(Schema.String),
|
|
441
519
|
startedAt: Schema.String,
|
|
442
520
|
stoppedAt: Schema.optionalKey(Schema.String),
|
|
443
521
|
sizeBytes: Schema.optionalKey(Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))),
|
|
444
522
|
fps: positiveInt,
|
|
445
523
|
bitrateKbps: positiveInt,
|
|
446
524
|
codec: Schema.String
|
|
447
|
-
}
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* A recording as the live session reports it. Sessions that are still running an
|
|
528
|
+
* older wrapper send only `path`, which decoding carries over into `relativePath`.
|
|
529
|
+
*/
|
|
530
|
+
const Recording = Schema.Struct({
|
|
531
|
+
...recordingFields,
|
|
532
|
+
relativePath: Schema.optionalKey(Schema.String)
|
|
533
|
+
}).pipe(Schema.decodeTo(Schema.Struct({
|
|
534
|
+
...recordingFields,
|
|
535
|
+
relativePath: Schema.String
|
|
536
|
+
}), SchemaTransformation.transform({
|
|
537
|
+
decode: ({ relativePath, ...recording }) => ({
|
|
538
|
+
...recording,
|
|
539
|
+
relativePath: relativePath ?? recording.path ?? ""
|
|
540
|
+
}),
|
|
541
|
+
encode: (recording) => recording
|
|
542
|
+
})));
|
|
448
543
|
//#endregion
|
|
449
544
|
//#region src/sessions/service.ts
|
|
450
545
|
/** Browser sessions, their live browser, and their recordings. */
|
|
451
546
|
var SessionsApi = class extends Context.Service()("@aperture-browser/api-client/SessionsApi") {};
|
|
452
547
|
//#endregion
|
|
548
|
+
//#region src/sessions/uploads.ts
|
|
549
|
+
const encoder = new TextEncoder();
|
|
550
|
+
const quotedFilename = (name) => name.replaceAll("\"", "%22").replaceAll("\r", "%0D").replaceAll("\n", "%0A");
|
|
551
|
+
const contentStream = (content) => {
|
|
552
|
+
if (content instanceof Blob) return Stream.fromReadableStream({
|
|
553
|
+
evaluate: () => content.stream(),
|
|
554
|
+
onError: (cause) => cause
|
|
555
|
+
});
|
|
556
|
+
if (content instanceof Uint8Array) return Stream.succeed(content);
|
|
557
|
+
return content;
|
|
558
|
+
};
|
|
559
|
+
const streamedMultipart = Effect.fn("SessionsApi.streamedMultipart")(function* (files) {
|
|
560
|
+
const boundary = `aperture-upload-${(yield* Random.nextIntBetween(0, Number.MAX_SAFE_INTEGER)).toString(36)}`;
|
|
561
|
+
const parts = files.map((file) => {
|
|
562
|
+
const header = `--${boundary}\r\nContent-Disposition: form-data; name="files"; filename="${quotedFilename(file.name)}"\r\nContent-Type: application/octet-stream\r
|
|
563
|
+
\r
|
|
564
|
+
`;
|
|
565
|
+
return Stream.succeed(encoder.encode(header)).pipe(Stream.concat(contentStream(file.content)), Stream.concat(Stream.succeed(encoder.encode("\r\n"))));
|
|
566
|
+
});
|
|
567
|
+
const body = Stream.fromIterable(parts).pipe(Stream.flatten(), Stream.concat(Stream.succeed(encoder.encode(`--${boundary}--\r\n`))));
|
|
568
|
+
return HttpBody.stream(body, `multipart/form-data; boundary=${boundary}`);
|
|
569
|
+
});
|
|
570
|
+
/**
|
|
571
|
+
* The multipart body of an upload. FormData works with every fetch, so it is used unless a
|
|
572
|
+
* content is a stream, which needs the body itself to be streamed.
|
|
573
|
+
*/
|
|
574
|
+
const uploadBody = Effect.fn("SessionsApi.uploadBody")(function* (files) {
|
|
575
|
+
const form = new FormData();
|
|
576
|
+
for (const file of files) if (file.content instanceof Blob) form.append("files", file.content, file.name);
|
|
577
|
+
else if (file.content instanceof Uint8Array) form.append("files", new Blob([new Uint8Array(file.content)]), file.name);
|
|
578
|
+
else return yield* streamedMultipart(files);
|
|
579
|
+
return HttpBody.formData(form);
|
|
580
|
+
});
|
|
581
|
+
//#endregion
|
|
453
582
|
//#region src/sessions/layer.ts
|
|
454
583
|
const contentDispositionFilename = (header) => header?.match(/filename="([^"]+)"/)?.[1] ?? null;
|
|
584
|
+
const recordingContentPath = (sessionId, recordingId) => `/sessions/${encodeURIComponent(sessionId)}/recordings/${encodeURIComponent(recordingId)}/content`;
|
|
455
585
|
const makeSessionsApi = Effect.gen(function* () {
|
|
456
586
|
const httpClient = yield* HttpClient.HttpClient;
|
|
457
587
|
const { authorize } = yield* ApiAuthorization;
|
|
@@ -467,6 +597,7 @@ const makeSessionsApi = Effect.gen(function* () {
|
|
|
467
597
|
...tagQuery(params.tags)
|
|
468
598
|
}) }).pipe(tenantScoped(credentials));
|
|
469
599
|
});
|
|
600
|
+
const sessions = paginated(listSessions);
|
|
470
601
|
const getSession = Effect.fn("SessionsApi.getSession")(function* (credentials, sessionId) {
|
|
471
602
|
return yield* api.getSession(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
472
603
|
});
|
|
@@ -485,7 +616,8 @@ const makeSessionsApi = Effect.gen(function* () {
|
|
|
485
616
|
},
|
|
486
617
|
initialTargets: input.initialTargets ?? [],
|
|
487
618
|
...input.storageState === void 0 ? {} : { storageState: input.storageState },
|
|
488
|
-
tags: input.tags ?? {}
|
|
619
|
+
tags: input.tags ?? {},
|
|
620
|
+
...input.proxy === void 0 ? {} : { proxy: input.proxy }
|
|
489
621
|
}
|
|
490
622
|
}).pipe(tenantScoped(credentials));
|
|
491
623
|
});
|
|
@@ -507,6 +639,9 @@ const makeSessionsApi = Effect.gen(function* () {
|
|
|
507
639
|
const replaceSessionTags = Effect.fn("SessionsApi.replaceSessionTags")(function* (credentials, sessionId, tags) {
|
|
508
640
|
return yield* api.replaceSessionTags(sessionId, { payload: { tags } }).pipe(tenantScoped(credentials));
|
|
509
641
|
});
|
|
642
|
+
const updateSessionProxy = Effect.fn("SessionsApi.updateSessionProxy")(function* (credentials, sessionId, proxy) {
|
|
643
|
+
return yield* api.updateSessionProxy(sessionId, { payload: proxy }).pipe(tenantScoped(credentials));
|
|
644
|
+
});
|
|
510
645
|
const promoteSession = Effect.fn("SessionsApi.promoteSession")(function* (credentials, sessionId, input) {
|
|
511
646
|
return yield* api.promoteSession(sessionId, { payload: {
|
|
512
647
|
name: input.name,
|
|
@@ -515,17 +650,45 @@ const makeSessionsApi = Effect.gen(function* () {
|
|
|
515
650
|
tags: input.tags ?? {}
|
|
516
651
|
} }).pipe(tenantScoped(credentials));
|
|
517
652
|
});
|
|
653
|
+
const getSessionCursor = Effect.fn("SessionsApi.getSessionCursor")(function* (credentials, sessionId) {
|
|
654
|
+
return yield* api.getSessionCursor(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
655
|
+
});
|
|
656
|
+
const setSessionCursor = Effect.fn("SessionsApi.setSessionCursor")(function* (credentials, sessionId, visible) {
|
|
657
|
+
return yield* api.setSessionCursor(sessionId, { payload: { visible } }).pipe(tenantScoped(credentials));
|
|
658
|
+
});
|
|
659
|
+
const listSessionRecordings = Effect.fn("SessionsApi.listSessionRecordings")(function* (credentials, sessionId) {
|
|
660
|
+
return yield* api.listSessionRecordings(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
661
|
+
});
|
|
662
|
+
const createSessionRecording = Effect.fn("SessionsApi.createSessionRecording")(function* (credentials, sessionId, input) {
|
|
663
|
+
return yield* api.createSessionRecording(sessionId, { payload: input }).pipe(tenantScoped(credentials));
|
|
664
|
+
});
|
|
665
|
+
const getSessionRecording = Effect.fn("SessionsApi.getSessionRecording")(function* (credentials, sessionId, recordingId) {
|
|
666
|
+
return yield* api.getSessionRecording(sessionId, recordingId, void 0).pipe(tenantScoped(credentials));
|
|
667
|
+
});
|
|
668
|
+
const retargetSessionRecording = Effect.fn("SessionsApi.retargetSessionRecording")(function* (credentials, sessionId, recordingId, targetId) {
|
|
669
|
+
return yield* api.retargetSessionRecording(sessionId, recordingId, { payload: { targetId } }).pipe(tenantScoped(credentials));
|
|
670
|
+
});
|
|
671
|
+
const stopSessionRecording = Effect.fn("SessionsApi.stopSessionRecording")(function* (credentials, sessionId, recordingId) {
|
|
672
|
+
return yield* api.stopSessionRecording(sessionId, recordingId, void 0).pipe(tenantScoped(credentials));
|
|
673
|
+
});
|
|
674
|
+
const listSessionFiles = Effect.fn("SessionsApi.listSessionFiles")(function* (credentials, sessionId) {
|
|
675
|
+
return yield* api.listSessionFiles(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
676
|
+
});
|
|
677
|
+
const createSessionFileDownloadURL = Effect.fn("SessionsApi.createSessionFileDownloadURL")(function* (credentials, sessionId, input) {
|
|
678
|
+
return yield* api.createSessionFileDownloadURL(sessionId, { payload: input }).pipe(tenantScoped(credentials));
|
|
679
|
+
});
|
|
518
680
|
const getBrowserChannels = Effect.fn("SessionsApi.getBrowserChannels")(function* (credentials) {
|
|
519
681
|
return yield* api.listBrowserChannels(void 0).pipe(tenantScoped(credentials));
|
|
520
682
|
});
|
|
521
683
|
const getBrowserStatus = Effect.fn("SessionsApi.getBrowserStatus")(function* (credentials, sessionId, sessionToken) {
|
|
522
684
|
return yield* http.get(`/sessions/${encodeURIComponent(sessionId)}/browser/status`).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(BrowserStatus)), authorize({
|
|
523
685
|
credentials,
|
|
524
|
-
bearerToken: sessionToken
|
|
686
|
+
bearerToken: sessionToken,
|
|
687
|
+
tenantHeader: "tenant-scoped"
|
|
525
688
|
}));
|
|
526
689
|
});
|
|
527
690
|
const downloadSessionRecording = Effect.fn("SessionsApi.downloadSessionRecording")(function* (credentials, sessionId, recordingId, sessionToken) {
|
|
528
|
-
return yield* http.get(
|
|
691
|
+
return yield* http.get(recordingContentPath(sessionId, recordingId)).pipe(Effect.flatMap((response) => Effect.map(response.arrayBuffer, (body) => ({
|
|
529
692
|
blob: new Blob([body], { type: response.headers["content-type"] ?? "" }),
|
|
530
693
|
filename: contentDispositionFilename(response.headers["content-disposition"])
|
|
531
694
|
}))), authorize({
|
|
@@ -534,8 +697,49 @@ const makeSessionsApi = Effect.gen(function* () {
|
|
|
534
697
|
tenantHeader: "tenant-scoped"
|
|
535
698
|
}));
|
|
536
699
|
});
|
|
700
|
+
const streamSessionRecording = (credentials, sessionId, recordingId, sessionToken) => http.get(recordingContentPath(sessionId, recordingId)).pipe(authorize({
|
|
701
|
+
credentials,
|
|
702
|
+
bearerToken: sessionToken,
|
|
703
|
+
tenantHeader: "tenant-scoped"
|
|
704
|
+
}), Effect.map((response) => response.stream.pipe(Stream.catchTag("HttpClientError", (error) => Stream.fromEffect(toApiRequestError(Effect.fail(error)))))), Stream.unwrap);
|
|
705
|
+
const uploadSessionFiles = Effect.fn("SessionsApi.uploadSessionFiles")(function* (credentials, sessionId, files, options = {}) {
|
|
706
|
+
const body = yield* uploadBody(files);
|
|
707
|
+
return (yield* http.post(`/api/sessions/${encodeURIComponent(sessionId)}/files`, {
|
|
708
|
+
body,
|
|
709
|
+
urlParams: compactQuery({ directory: options.directory })
|
|
710
|
+
}).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Api.UploadSessionFiles201)), tenantScoped(credentials))).files;
|
|
711
|
+
});
|
|
712
|
+
const deleteSessionFile = Effect.fn("SessionsApi.deleteSessionFile")(function* (credentials, sessionId, relativePath, options = {}) {
|
|
713
|
+
yield* api.deleteSessionFile(sessionId, { params: compactQuery({
|
|
714
|
+
relativePath,
|
|
715
|
+
recursive: options.recursive
|
|
716
|
+
}) }).pipe(tenantScoped(credentials));
|
|
717
|
+
});
|
|
718
|
+
const createSessionDirectory = Effect.fn("SessionsApi.createSessionDirectory")(function* (credentials, sessionId, relativePath) {
|
|
719
|
+
return yield* api.createSessionDirectory(sessionId, { payload: { relativePath } }).pipe(tenantScoped(credentials));
|
|
720
|
+
});
|
|
721
|
+
const moveSessionFile = Effect.fn("SessionsApi.moveSessionFile")(function* (credentials, sessionId, input) {
|
|
722
|
+
return yield* api.moveSessionFile(sessionId, { payload: input }).pipe(tenantScoped(credentials));
|
|
723
|
+
});
|
|
724
|
+
const uploadLiveSessionFiles = Effect.fn("SessionsApi.uploadLiveSessionFiles")(function* (credentials, sessionId, files, sessionToken) {
|
|
725
|
+
const body = yield* uploadBody(files);
|
|
726
|
+
return (yield* http.post(`/sessions/${encodeURIComponent(sessionId)}/uploads`, { body }).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Api.UploadLiveSessionFiles201)), authorize({
|
|
727
|
+
credentials,
|
|
728
|
+
bearerToken: sessionToken,
|
|
729
|
+
tenantHeader: "tenant-scoped"
|
|
730
|
+
}))).files;
|
|
731
|
+
});
|
|
732
|
+
const setSessionViewport = Effect.fn("SessionsApi.setSessionViewport")(function* (credentials, sessionId, input, sessionToken) {
|
|
733
|
+
return yield* api.setSessionViewport(sessionId, { payload: input }).pipe(authorize({
|
|
734
|
+
credentials,
|
|
735
|
+
bearerToken: sessionToken,
|
|
736
|
+
tenantHeader: "tenant-scoped"
|
|
737
|
+
}));
|
|
738
|
+
});
|
|
537
739
|
return SessionsApi.of({
|
|
538
740
|
listSessions,
|
|
741
|
+
streamSessions: sessions.stream,
|
|
742
|
+
listAllSessions: sessions.listAll,
|
|
539
743
|
getSession,
|
|
540
744
|
getSessionsBulk,
|
|
541
745
|
createSession,
|
|
@@ -545,10 +749,27 @@ const makeSessionsApi = Effect.gen(function* () {
|
|
|
545
749
|
rotateSessionToken,
|
|
546
750
|
rotateCollaborationCapability,
|
|
547
751
|
replaceSessionTags,
|
|
752
|
+
updateSessionProxy,
|
|
548
753
|
promoteSession,
|
|
754
|
+
getSessionCursor,
|
|
755
|
+
setSessionCursor,
|
|
756
|
+
listSessionRecordings,
|
|
757
|
+
createSessionRecording,
|
|
758
|
+
getSessionRecording,
|
|
759
|
+
retargetSessionRecording,
|
|
760
|
+
stopSessionRecording,
|
|
761
|
+
listSessionFiles,
|
|
762
|
+
createSessionFileDownloadURL,
|
|
549
763
|
getBrowserChannels,
|
|
550
764
|
getBrowserStatus,
|
|
551
|
-
downloadSessionRecording
|
|
765
|
+
downloadSessionRecording,
|
|
766
|
+
streamSessionRecording,
|
|
767
|
+
uploadSessionFiles,
|
|
768
|
+
uploadLiveSessionFiles,
|
|
769
|
+
deleteSessionFile,
|
|
770
|
+
moveSessionFile,
|
|
771
|
+
createSessionDirectory,
|
|
772
|
+
setSessionViewport
|
|
552
773
|
});
|
|
553
774
|
});
|
|
554
775
|
const sessionsApiLayer = Layer.effect(SessionsApi, makeSessionsApi);
|
|
@@ -573,6 +794,13 @@ const makeSnapshotsApi = Effect.gen(function* () {
|
|
|
573
794
|
...tagQuery(params.tags)
|
|
574
795
|
}) }).pipe(tenantScoped(credentials));
|
|
575
796
|
});
|
|
797
|
+
const snapshots = paginated(listSnapshots);
|
|
798
|
+
const getSnapshotByName = Effect.fn("SnapshotsApi.getSnapshotByName")(function* (credentials, name) {
|
|
799
|
+
return yield* snapshots.stream(credentials, {
|
|
800
|
+
name,
|
|
801
|
+
limit: 100
|
|
802
|
+
}).pipe(Stream.filter((candidate) => candidate.name === name), Stream.runHead);
|
|
803
|
+
});
|
|
576
804
|
const updateSnapshot = Effect.fn("SnapshotsApi.updateSnapshot")(function* (credentials, name, input) {
|
|
577
805
|
return yield* api.updateSnapshot(name, { payload: input }).pipe(tenantScoped(credentials));
|
|
578
806
|
});
|
|
@@ -587,6 +815,9 @@ const makeSnapshotsApi = Effect.gen(function* () {
|
|
|
587
815
|
});
|
|
588
816
|
return SnapshotsApi.of({
|
|
589
817
|
listSnapshots,
|
|
818
|
+
streamSnapshots: snapshots.stream,
|
|
819
|
+
listAllSnapshots: snapshots.listAll,
|
|
820
|
+
getSnapshotByName,
|
|
590
821
|
updateSnapshot,
|
|
591
822
|
replaceSnapshotTags,
|
|
592
823
|
deleteSnapshot,
|
|
@@ -596,7 +827,7 @@ const makeSnapshotsApi = Effect.gen(function* () {
|
|
|
596
827
|
const snapshotsApiLayer = Layer.effect(SnapshotsApi, makeSnapshotsApi);
|
|
597
828
|
//#endregion
|
|
598
829
|
//#region src/tenants/service.ts
|
|
599
|
-
/** Tenant administration. */
|
|
830
|
+
/** Tenant administration, and the tenant a tenant token belongs to. */
|
|
600
831
|
var TenantsApi = class extends Context.Service()("@aperture-browser/api-client/TenantsApi") {};
|
|
601
832
|
//#endregion
|
|
602
833
|
//#region src/tenants/layer.ts
|
|
@@ -612,6 +843,13 @@ const makeTenantsApi = Effect.gen(function* () {
|
|
|
612
843
|
deleted: params.deleted
|
|
613
844
|
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
614
845
|
});
|
|
846
|
+
const tenants = paginated(listTenants);
|
|
847
|
+
const getCurrentTenant = Effect.fn("TenantsApi.getCurrentTenant")(function* (credentials) {
|
|
848
|
+
return yield* api.getTenant(void 0).pipe(authorize(Authorization.of(credentials)));
|
|
849
|
+
});
|
|
850
|
+
const updateCurrentTenant = Effect.fn("TenantsApi.updateCurrentTenant")(function* (credentials, input) {
|
|
851
|
+
return yield* api.updateSelectedTenant({ payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
852
|
+
});
|
|
615
853
|
const createTenant = Effect.fn("TenantsApi.createTenant")(function* (credentials, input) {
|
|
616
854
|
return yield* api.createTenant({ payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
617
855
|
});
|
|
@@ -626,10 +864,14 @@ const makeTenantsApi = Effect.gen(function* () {
|
|
|
626
864
|
});
|
|
627
865
|
return TenantsApi.of({
|
|
628
866
|
listTenants,
|
|
867
|
+
streamTenants: tenants.stream,
|
|
868
|
+
listAllTenants: tenants.listAll,
|
|
629
869
|
createTenant,
|
|
630
870
|
updateTenant,
|
|
631
871
|
deleteTenant,
|
|
632
|
-
restoreTenant
|
|
872
|
+
restoreTenant,
|
|
873
|
+
getCurrentTenant,
|
|
874
|
+
updateCurrentTenant
|
|
633
875
|
});
|
|
634
876
|
});
|
|
635
877
|
const tenantsApiLayer = Layer.effect(TenantsApi, makeTenantsApi);
|
|
@@ -654,6 +896,7 @@ const makeTokensApi = Effect.gen(function* () {
|
|
|
654
896
|
scope: params.scope
|
|
655
897
|
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
656
898
|
});
|
|
899
|
+
const adminTokens = paginated(listAdminTokens);
|
|
657
900
|
const createAdminToken = Effect.fn("TokensApi.createAdminToken")(function* (credentials, input) {
|
|
658
901
|
const systemAdmin = input.authorityType === "system_admin";
|
|
659
902
|
return yield* api.createAdminToken({ payload: {
|
|
@@ -678,6 +921,7 @@ const makeTokensApi = Effect.gen(function* () {
|
|
|
678
921
|
scope: params.scope
|
|
679
922
|
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
680
923
|
});
|
|
924
|
+
const tenantTokens = paginated(listTenantTokens);
|
|
681
925
|
const createTenantToken = Effect.fn("TokensApi.createTenantToken")(function* (credentials, input) {
|
|
682
926
|
return yield* api.createTenantToken({ payload: {
|
|
683
927
|
name: input.name,
|
|
@@ -692,9 +936,13 @@ const makeTokensApi = Effect.gen(function* () {
|
|
|
692
936
|
});
|
|
693
937
|
return TokensApi.of({
|
|
694
938
|
listAdminTokens,
|
|
939
|
+
streamAdminTokens: adminTokens.stream,
|
|
940
|
+
listAllAdminTokens: adminTokens.listAll,
|
|
695
941
|
createAdminToken,
|
|
696
942
|
revokeAdminToken,
|
|
697
943
|
listTenantTokens,
|
|
944
|
+
streamTenantTokens: tenantTokens.stream,
|
|
945
|
+
listAllTenantTokens: tenantTokens.listAll,
|
|
698
946
|
createTenantToken,
|
|
699
947
|
revokeTenantToken
|
|
700
948
|
});
|
|
@@ -718,6 +966,7 @@ const makeUsersApi = Effect.gen(function* () {
|
|
|
718
966
|
disabled: params.disabled
|
|
719
967
|
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
720
968
|
});
|
|
969
|
+
const users = paginated(listUsers);
|
|
721
970
|
const createUser = Effect.fn("UsersApi.createUser")(function* (credentials, input) {
|
|
722
971
|
return yield* api.createUser({ payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
723
972
|
});
|
|
@@ -739,6 +988,9 @@ const makeUsersApi = Effect.gen(function* () {
|
|
|
739
988
|
const listUserMemberships = Effect.fn("UsersApi.listUserMemberships")(function* (credentials, userId) {
|
|
740
989
|
return yield* api.listUserMemberships(userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
741
990
|
});
|
|
991
|
+
const listTenantMemberships = Effect.fn("UsersApi.listTenantMemberships")(function* (credentials, tenantId) {
|
|
992
|
+
return yield* api.listTenantMemberships(tenantId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
993
|
+
});
|
|
742
994
|
const upsertTenantMembership = Effect.fn("UsersApi.upsertTenantMembership")(function* (credentials, tenantId, userId, scopes) {
|
|
743
995
|
const payload = { scopes };
|
|
744
996
|
return yield* api.upsertTenantMembership(tenantId, userId, { payload }).pipe(authorize(Authorization.of(credentials)));
|
|
@@ -748,6 +1000,8 @@ const makeUsersApi = Effect.gen(function* () {
|
|
|
748
1000
|
});
|
|
749
1001
|
return UsersApi.of({
|
|
750
1002
|
listUsers,
|
|
1003
|
+
streamUsers: users.stream,
|
|
1004
|
+
listAllUsers: users.listAll,
|
|
751
1005
|
createUser,
|
|
752
1006
|
getUser,
|
|
753
1007
|
updateUser,
|
|
@@ -755,6 +1009,7 @@ const makeUsersApi = Effect.gen(function* () {
|
|
|
755
1009
|
disableUser,
|
|
756
1010
|
restoreUser,
|
|
757
1011
|
listUserMemberships,
|
|
1012
|
+
listTenantMemberships,
|
|
758
1013
|
upsertTenantMembership,
|
|
759
1014
|
deleteTenantMembership
|
|
760
1015
|
});
|
|
@@ -766,22 +1021,104 @@ const usersApiLayer = Layer.effect(UsersApi, makeUsersApi);
|
|
|
766
1021
|
* Every API service, sharing one ApiAuthorization. The services send requests through the
|
|
767
1022
|
* HttpClient the app provides, decorated with each call's authorization.
|
|
768
1023
|
*/
|
|
769
|
-
const apiClientLayer = Layer.mergeAll(authApiLayer, tenantsApiLayer, usersApiLayer, sessionsApiLayer, snapshotsApiLayer, tokensApiLayer, eventsApiLayer).pipe(Layer.provide(authorizedHttpClientLayer), Layer.provideMerge(apiAuthorizationLayer));
|
|
1024
|
+
const apiClientLayer = Layer.mergeAll(authApiLayer, tenantsApiLayer, usersApiLayer, sessionsApiLayer, snapshotsApiLayer, tokensApiLayer, eventsApiLayer, healthApiLayer).pipe(Layer.provide(authorizedHttpClientLayer), Layer.provideMerge(apiAuthorizationLayer));
|
|
770
1025
|
const baseUrlLayer = (baseUrl) => Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, HttpClient.mapRequest(HttpClientRequest.prependUrl(baseUrl))));
|
|
771
1026
|
//#endregion
|
|
772
|
-
//#region src/
|
|
773
|
-
|
|
774
|
-
|
|
1027
|
+
//#region src/client.ts
|
|
1028
|
+
/** Binds `credentials` as the first argument of every method of `service`. */
|
|
1029
|
+
function withCredentials(service, credentials) {
|
|
1030
|
+
const methods = Object.entries(service);
|
|
1031
|
+
return Object.fromEntries(methods.map(([name, method]) => [name, (...args) => method(credentials, ...args)]));
|
|
775
1032
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1033
|
+
/**
|
|
1034
|
+
* The credentialed API services with one set of credentials bound, for callers that always
|
|
1035
|
+
* act as the same principal, such as a server holding one API token.
|
|
1036
|
+
*/
|
|
1037
|
+
var ApertureClient = class extends Context.Service()("@aperture-browser/api-client/ApertureClient") {};
|
|
1038
|
+
const makeApertureClient = Effect.fn("makeApertureClient")(function* (credentials) {
|
|
1039
|
+
const auth = yield* AuthApi;
|
|
1040
|
+
return ApertureClient.of({
|
|
1041
|
+
sessions: withCredentials(yield* SessionsApi, credentials),
|
|
1042
|
+
snapshots: withCredentials(yield* SnapshotsApi, credentials),
|
|
1043
|
+
tenants: withCredentials(yield* TenantsApi, credentials),
|
|
1044
|
+
users: withCredentials(yield* UsersApi, credentials),
|
|
1045
|
+
tokens: withCredentials(yield* TokensApi, credentials),
|
|
1046
|
+
events: withCredentials(yield* EventsApi, credentials),
|
|
1047
|
+
auth: { getAuthMe: () => auth.getAuthMe(credentials.selectedTenantId, credentials) },
|
|
1048
|
+
health: yield* HealthApi
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
/** ApertureClient acting with `credentials`, over the HttpClient the app provides. */
|
|
1052
|
+
const apertureClientLayer = (credentials) => Layer.effect(ApertureClient, makeApertureClient(credentials)).pipe(Layer.provide(apiClientLayer));
|
|
781
1053
|
//#endregion
|
|
782
1054
|
//#region src/schemas.ts
|
|
783
1055
|
const SessionStatus = Api.SessionStatus;
|
|
784
1056
|
//#endregion
|
|
785
|
-
|
|
1057
|
+
//#region src/sessions/tree.ts
|
|
1058
|
+
/**
|
|
1059
|
+
* The top-level directories the session writes into. They always exist and cannot be
|
|
1060
|
+
* deleted, moved or renamed.
|
|
1061
|
+
*/
|
|
1062
|
+
const sessionFileRootDirectories = [
|
|
1063
|
+
"downloads",
|
|
1064
|
+
"recordings",
|
|
1065
|
+
"uploads",
|
|
1066
|
+
"outputs"
|
|
1067
|
+
];
|
|
1068
|
+
const emptyDirectory = () => ({
|
|
1069
|
+
directory: void 0,
|
|
1070
|
+
directories: /* @__PURE__ */ new Map(),
|
|
1071
|
+
files: []
|
|
1072
|
+
});
|
|
1073
|
+
function directoryAt(root, segments) {
|
|
1074
|
+
let directory = root;
|
|
1075
|
+
for (const segment of segments) {
|
|
1076
|
+
let child = directory.directories.get(segment);
|
|
1077
|
+
if (child === void 0) {
|
|
1078
|
+
child = emptyDirectory();
|
|
1079
|
+
directory.directories.set(segment, child);
|
|
1080
|
+
}
|
|
1081
|
+
directory = child;
|
|
1082
|
+
}
|
|
1083
|
+
return directory;
|
|
1084
|
+
}
|
|
1085
|
+
function toNodes(directory, parentPath) {
|
|
1086
|
+
const directories = [...directory.directories.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([name, child]) => {
|
|
1087
|
+
const relativePath = parentPath === "" ? name : `${parentPath}/${name}`;
|
|
1088
|
+
return {
|
|
1089
|
+
kind: "directory",
|
|
1090
|
+
name,
|
|
1091
|
+
relativePath,
|
|
1092
|
+
directory: child.directory,
|
|
1093
|
+
children: toNodes(child, relativePath)
|
|
1094
|
+
};
|
|
1095
|
+
});
|
|
1096
|
+
const files = [...directory.files].sort((left, right) => left.name.localeCompare(right.name));
|
|
1097
|
+
return [...directories, ...files];
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Arranges `listSessionFiles` results into a tree: directories first, then files, each
|
|
1101
|
+
* sorted by name. Empty directories appear because the listing includes them.
|
|
1102
|
+
*/
|
|
1103
|
+
function sessionFileTree(entries) {
|
|
1104
|
+
const root = emptyDirectory();
|
|
1105
|
+
for (const entry of entries) {
|
|
1106
|
+
const segments = entry.relativePath.split("/");
|
|
1107
|
+
if (entry.type === "directory") {
|
|
1108
|
+
directoryAt(root, segments).directory = entry;
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const name = segments.pop() ?? entry.relativePath;
|
|
1112
|
+
directoryAt(root, segments).files.push({
|
|
1113
|
+
kind: "file",
|
|
1114
|
+
name,
|
|
1115
|
+
relativePath: entry.relativePath,
|
|
1116
|
+
file: entry
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
return toNodes(root, "");
|
|
1120
|
+
}
|
|
1121
|
+
//#endregion
|
|
1122
|
+
export { ApertureClient, ApiAuthorization, ApiErrorBody, ApiRequestError, AuthApi, Authorization, BrowserStatus, EventsApi, HealthApi, LiveSessionErrorBody, LoginMethods, Passkey, PasskeyLoginOptions, PasskeyMutation, PasskeyRegistrationOptions, Passkeys, PasswordLoginResponse, Recording, RecoveryCodes, SecurityStatus, SessionStatus, SessionsApi, SnapshotsApi, TENANT_HEADER, TOTPEnrollment, TenantsApi, TokensApi, UsersApi, apertureClientLayer, apiAuthorizationLayer, apiClientLayer, authApiLayer, authorizedHttpClientLayer, baseUrlLayer, compactQuery, defaultListLimit, eventsApiLayer, flattenInfinitePages, getNextPageParam, healthApiLayer, listQueryDefaults, makeApertureClient, makeApiAuthorization, makeAuthApi, makeEventsApi, makeHealthApi, makeSessionsApi, makeSnapshotsApi, makeTenantsApi, makeTokensApi, makeUsersApi, paginated, resolveTenantHeader, sessionFileRootDirectories, sessionFileTree, sessionsApiLayer, snapshotsApiLayer, tagQuery, tenantsApiLayer, toApiRequestError, tokensApiLayer, usersApiLayer, webSessionCredentials, withCredentials };
|
|
786
1123
|
|
|
787
1124
|
//# sourceMappingURL=index.js.map
|