@aperture-browser/api-client 0.0.0-init.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 +35 -0
- package/dist/index.d.ts +767 -0
- package/dist/index.js +787 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as HttpBody from "effect/unstable/http/HttpBody";
|
|
4
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
5
|
+
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
6
|
+
import * as Api from "@aperture-browser/api-schema";
|
|
7
|
+
import * as Context from "effect/Context";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import * as PubSub from "effect/PubSub";
|
|
10
|
+
import * as Stream from "effect/Stream";
|
|
11
|
+
import * as Function from "effect/Function";
|
|
12
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
13
|
+
//#region src/authorization/service.ts
|
|
14
|
+
const TENANT_HEADER = "X-Aperture-Tenant-Id";
|
|
15
|
+
/** The browser's own login session, sent as a cookie. */
|
|
16
|
+
const webSessionCredentials = {
|
|
17
|
+
kind: "session",
|
|
18
|
+
authorityType: null,
|
|
19
|
+
tenantId: null,
|
|
20
|
+
selectedTenantId: null
|
|
21
|
+
};
|
|
22
|
+
/** Which tenant, if any, a request acts for under the given credentials. */
|
|
23
|
+
function resolveTenantHeader(credentials, mode) {
|
|
24
|
+
if (mode === "none") return;
|
|
25
|
+
if (mode === "optional") return credentials.selectedTenantId ?? void 0;
|
|
26
|
+
if (credentials.authorityType === "tenant") {
|
|
27
|
+
if (credentials.kind === "bearer") return;
|
|
28
|
+
return credentials.tenantId ?? void 0;
|
|
29
|
+
}
|
|
30
|
+
if (credentials.authorityType === "system_admin") return credentials.selectedTenantId ?? void 0;
|
|
31
|
+
}
|
|
32
|
+
const Authorization = {
|
|
33
|
+
anonymous: {},
|
|
34
|
+
webSession: { credentials: webSessionCredentials },
|
|
35
|
+
of: (credentials) => ({ credentials }),
|
|
36
|
+
tenantScoped: (credentials) => ({
|
|
37
|
+
credentials,
|
|
38
|
+
tenantHeader: "tenant-scoped"
|
|
39
|
+
})
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Authenticates API calls. Inside `authorize`, every request sent through the HttpClient
|
|
43
|
+
* that `authorizedHttpClientLayer` provides carries that call's credentials and tenant, so
|
|
44
|
+
* callers never touch headers.
|
|
45
|
+
*/
|
|
46
|
+
var ApiAuthorization = class extends Context.Service()("@aperture-browser/api-client/ApiAuthorization") {};
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/auth/schemas.ts
|
|
49
|
+
const passkeyAuthenticatorTransport = Schema.Literals([
|
|
50
|
+
"ble",
|
|
51
|
+
"cable",
|
|
52
|
+
"hybrid",
|
|
53
|
+
"internal",
|
|
54
|
+
"nfc",
|
|
55
|
+
"smart-card",
|
|
56
|
+
"usb"
|
|
57
|
+
]);
|
|
58
|
+
const passkeyCredentialDescriptor = Schema.Struct({
|
|
59
|
+
id: Schema.String,
|
|
60
|
+
type: Schema.Literal("public-key"),
|
|
61
|
+
transports: Schema.optionalKey(Schema.mutable(Schema.Array(passkeyAuthenticatorTransport)))
|
|
62
|
+
});
|
|
63
|
+
const passkeyExtensions = Schema.optionalKey(Schema.Struct({
|
|
64
|
+
appid: Schema.optionalKey(Schema.String),
|
|
65
|
+
credProps: Schema.optionalKey(Schema.Boolean),
|
|
66
|
+
hmacCreateSecret: Schema.optionalKey(Schema.Boolean),
|
|
67
|
+
minPinLength: Schema.optionalKey(Schema.Boolean)
|
|
68
|
+
}));
|
|
69
|
+
const passkeyHint = Schema.Literals([
|
|
70
|
+
"hybrid",
|
|
71
|
+
"security-key",
|
|
72
|
+
"client-device"
|
|
73
|
+
]);
|
|
74
|
+
const passkeyUserVerification = Schema.Literals([
|
|
75
|
+
"discouraged",
|
|
76
|
+
"preferred",
|
|
77
|
+
"required"
|
|
78
|
+
]);
|
|
79
|
+
const LoginMethods = Schema.Struct({ methods: Schema.Array(Schema.Union([
|
|
80
|
+
Schema.Struct({ type: Schema.Literal("password") }),
|
|
81
|
+
Schema.Struct({ type: Schema.Literal("api_token") }),
|
|
82
|
+
Schema.Struct({ type: Schema.Literal("passkey") }),
|
|
83
|
+
Schema.Struct({
|
|
84
|
+
type: Schema.Literal("oidc"),
|
|
85
|
+
id: Schema.String,
|
|
86
|
+
name: Schema.String,
|
|
87
|
+
loginUrl: Schema.String
|
|
88
|
+
})
|
|
89
|
+
])) });
|
|
90
|
+
const PasskeyLoginOptions = Schema.Struct({ publicKey: Schema.Struct({
|
|
91
|
+
challenge: Schema.String,
|
|
92
|
+
timeout: Schema.optionalKey(Schema.Number),
|
|
93
|
+
rpId: Schema.optionalKey(Schema.String),
|
|
94
|
+
allowCredentials: Schema.optionalKey(Schema.mutable(Schema.Array(passkeyCredentialDescriptor))),
|
|
95
|
+
userVerification: Schema.optionalKey(passkeyUserVerification),
|
|
96
|
+
hints: Schema.optionalKey(Schema.mutable(Schema.Array(passkeyHint))),
|
|
97
|
+
extensions: passkeyExtensions
|
|
98
|
+
}) });
|
|
99
|
+
const PasskeyRegistrationOptions = Schema.Struct({ publicKey: Schema.Struct({
|
|
100
|
+
rp: Schema.Struct({
|
|
101
|
+
id: Schema.optionalKey(Schema.String),
|
|
102
|
+
name: Schema.String
|
|
103
|
+
}),
|
|
104
|
+
user: Schema.Struct({
|
|
105
|
+
id: Schema.String,
|
|
106
|
+
name: Schema.String,
|
|
107
|
+
displayName: Schema.String
|
|
108
|
+
}),
|
|
109
|
+
challenge: Schema.String,
|
|
110
|
+
pubKeyCredParams: Schema.mutable(Schema.Array(Schema.Struct({
|
|
111
|
+
alg: Schema.Literals([
|
|
112
|
+
-7,
|
|
113
|
+
-8,
|
|
114
|
+
-35,
|
|
115
|
+
-36,
|
|
116
|
+
-37,
|
|
117
|
+
-38,
|
|
118
|
+
-39,
|
|
119
|
+
-257,
|
|
120
|
+
-258,
|
|
121
|
+
-259
|
|
122
|
+
]),
|
|
123
|
+
type: Schema.Literal("public-key")
|
|
124
|
+
}))),
|
|
125
|
+
timeout: Schema.optionalKey(Schema.Number),
|
|
126
|
+
excludeCredentials: Schema.optionalKey(Schema.mutable(Schema.Array(passkeyCredentialDescriptor))),
|
|
127
|
+
authenticatorSelection: Schema.optionalKey(Schema.Struct({
|
|
128
|
+
authenticatorAttachment: Schema.optionalKey(Schema.Literals(["platform", "cross-platform"])),
|
|
129
|
+
requireResidentKey: Schema.optionalKey(Schema.Boolean),
|
|
130
|
+
residentKey: Schema.optionalKey(Schema.Literals([
|
|
131
|
+
"discouraged",
|
|
132
|
+
"preferred",
|
|
133
|
+
"required"
|
|
134
|
+
])),
|
|
135
|
+
userVerification: Schema.optionalKey(passkeyUserVerification)
|
|
136
|
+
})),
|
|
137
|
+
hints: Schema.optionalKey(Schema.mutable(Schema.Array(passkeyHint))),
|
|
138
|
+
attestation: Schema.optionalKey(Schema.Literals([
|
|
139
|
+
"direct",
|
|
140
|
+
"enterprise",
|
|
141
|
+
"indirect",
|
|
142
|
+
"none"
|
|
143
|
+
])),
|
|
144
|
+
attestationFormats: Schema.optionalKey(Schema.mutable(Schema.Array(Schema.Literals([
|
|
145
|
+
"fido-u2f",
|
|
146
|
+
"packed",
|
|
147
|
+
"android-safetynet",
|
|
148
|
+
"android-key",
|
|
149
|
+
"tpm",
|
|
150
|
+
"apple",
|
|
151
|
+
"none"
|
|
152
|
+
])))),
|
|
153
|
+
extensions: passkeyExtensions
|
|
154
|
+
}) });
|
|
155
|
+
const Passkey = Schema.Struct({
|
|
156
|
+
id: Schema.String,
|
|
157
|
+
name: Schema.String,
|
|
158
|
+
createdAt: Schema.String,
|
|
159
|
+
lastUsedAt: Schema.NullOr(Schema.String)
|
|
160
|
+
});
|
|
161
|
+
const Passkeys = Schema.Struct({ passkeys: Schema.Array(Passkey) });
|
|
162
|
+
const PasskeyMutation = Schema.Struct({ passkey: Passkey });
|
|
163
|
+
const PasswordLoginResponse = Schema.Struct({ mfaRequired: Schema.Boolean });
|
|
164
|
+
const SecurityStatus = Schema.Struct({
|
|
165
|
+
hasPassword: Schema.Boolean,
|
|
166
|
+
totpEnabled: Schema.Boolean,
|
|
167
|
+
recoveryCodesRemaining: Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))
|
|
168
|
+
});
|
|
169
|
+
const TOTPEnrollment = Schema.Struct({
|
|
170
|
+
secret: Schema.String,
|
|
171
|
+
otpauthUrl: Schema.String,
|
|
172
|
+
qrCodeDataUrl: Schema.String
|
|
173
|
+
});
|
|
174
|
+
const RecoveryCodes = Schema.Struct({ recoveryCodes: Schema.Array(Schema.String) });
|
|
175
|
+
//#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
|
+
//#region src/auth/layer.ts
|
|
181
|
+
const jsonBody = (body) => ({ body: HttpBody.jsonUnsafe(body) });
|
|
182
|
+
const makeAuthApi = Effect.gen(function* () {
|
|
183
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
184
|
+
const { authorize } = yield* ApiAuthorization;
|
|
185
|
+
const http = HttpClient.filterStatusOk(httpClient);
|
|
186
|
+
const api = Api.make(httpClient);
|
|
187
|
+
const anonymous = authorize(Authorization.anonymous);
|
|
188
|
+
const webSession = authorize(Authorization.webSession);
|
|
189
|
+
const listLoginMethods = Effect.fn("AuthApi.listLoginMethods")(function* () {
|
|
190
|
+
return yield* http.get("/auth/login-methods").pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(LoginMethods)), anonymous);
|
|
191
|
+
});
|
|
192
|
+
const loginWithPassword = Effect.fn("AuthApi.loginWithPassword")(function* (email, password) {
|
|
193
|
+
return yield* http.post("/auth/password/login", jsonBody({
|
|
194
|
+
email,
|
|
195
|
+
password
|
|
196
|
+
})).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PasswordLoginResponse)), anonymous);
|
|
197
|
+
});
|
|
198
|
+
const completePasswordMFA = Effect.fn("AuthApi.completePasswordMFA")(function* (code) {
|
|
199
|
+
yield* http.post("/auth/password/login/mfa", jsonBody({ code })).pipe(anonymous);
|
|
200
|
+
});
|
|
201
|
+
const loginWithAPIToken = Effect.fn("AuthApi.loginWithAPIToken")(function* (token) {
|
|
202
|
+
yield* http.post("/auth/token/login", jsonBody({ token })).pipe(anonymous);
|
|
203
|
+
});
|
|
204
|
+
const beginPasskeyLogin = Effect.fn("AuthApi.beginPasskeyLogin")(function* () {
|
|
205
|
+
return yield* http.post("/auth/passkeys/login/options").pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PasskeyLoginOptions)), anonymous);
|
|
206
|
+
});
|
|
207
|
+
const finishPasskeyLogin = Effect.fn("AuthApi.finishPasskeyLogin")(function* (credential) {
|
|
208
|
+
yield* http.post("/auth/passkeys/login/finish", jsonBody(credential)).pipe(anonymous);
|
|
209
|
+
});
|
|
210
|
+
const acceptUserInvitation = Effect.fn("AuthApi.acceptUserInvitation")(function* (token, password) {
|
|
211
|
+
yield* http.post("/auth/invitations/accept", jsonBody({
|
|
212
|
+
token,
|
|
213
|
+
password
|
|
214
|
+
})).pipe(anonymous);
|
|
215
|
+
});
|
|
216
|
+
const logoutWebSession = Effect.fn("AuthApi.logoutWebSession")(function* () {
|
|
217
|
+
yield* http.post("/auth/logout").pipe(webSession);
|
|
218
|
+
});
|
|
219
|
+
const getAuthMe = Effect.fn("AuthApi.getAuthMe")(function* (selectedTenantId = null, credentials = webSessionCredentials) {
|
|
220
|
+
return yield* api.getCurrentPrincipal(void 0).pipe(authorize({
|
|
221
|
+
credentials: {
|
|
222
|
+
...credentials,
|
|
223
|
+
selectedTenantId
|
|
224
|
+
},
|
|
225
|
+
tenantHeader: "optional"
|
|
226
|
+
}));
|
|
227
|
+
});
|
|
228
|
+
const getSecurityStatus = Effect.fn("AuthApi.getSecurityStatus")(function* () {
|
|
229
|
+
return yield* http.get("/auth/security").pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(SecurityStatus)), webSession);
|
|
230
|
+
});
|
|
231
|
+
const setPassword = Effect.fn("AuthApi.setPassword")(function* (currentPassword, newPassword) {
|
|
232
|
+
yield* http.put("/auth/password", jsonBody({
|
|
233
|
+
currentPassword,
|
|
234
|
+
newPassword
|
|
235
|
+
})).pipe(webSession);
|
|
236
|
+
});
|
|
237
|
+
const listPasskeys = Effect.fn("AuthApi.listPasskeys")(function* () {
|
|
238
|
+
return yield* http.get("/auth/passkeys").pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Passkeys)), webSession);
|
|
239
|
+
});
|
|
240
|
+
const beginPasskeyRegistration = Effect.fn("AuthApi.beginPasskeyRegistration")(function* (name) {
|
|
241
|
+
return yield* http.post("/auth/passkeys/registration/options", jsonBody({ name })).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PasskeyRegistrationOptions)), webSession);
|
|
242
|
+
});
|
|
243
|
+
const finishPasskeyRegistration = Effect.fn("AuthApi.finishPasskeyRegistration")(function* (credential) {
|
|
244
|
+
return yield* http.post("/auth/passkeys/registration/finish", jsonBody(credential)).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PasskeyMutation)), webSession);
|
|
245
|
+
});
|
|
246
|
+
const renamePasskey = Effect.fn("AuthApi.renamePasskey")(function* (passkeyId, name) {
|
|
247
|
+
return yield* http.patch(`/auth/passkeys/${encodeURIComponent(passkeyId)}`, jsonBody({ name })).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(PasskeyMutation)), webSession);
|
|
248
|
+
});
|
|
249
|
+
const deletePasskey = Effect.fn("AuthApi.deletePasskey")(function* (passkeyId) {
|
|
250
|
+
yield* http.del(`/auth/passkeys/${encodeURIComponent(passkeyId)}`).pipe(webSession);
|
|
251
|
+
});
|
|
252
|
+
const beginTOTPEnrollment = Effect.fn("AuthApi.beginTOTPEnrollment")(function* () {
|
|
253
|
+
return yield* http.post("/auth/totp/enrollment/options").pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(TOTPEnrollment)), webSession);
|
|
254
|
+
});
|
|
255
|
+
const completeTOTPEnrollment = Effect.fn("AuthApi.completeTOTPEnrollment")(function* (code) {
|
|
256
|
+
return yield* http.post("/auth/totp/enrollment/finish", jsonBody({ code })).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(RecoveryCodes)), webSession);
|
|
257
|
+
});
|
|
258
|
+
const regenerateRecoveryCodes = Effect.fn("AuthApi.regenerateRecoveryCodes")(function* (code) {
|
|
259
|
+
return yield* http.post("/auth/totp/recovery-codes", jsonBody({ code })).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(RecoveryCodes)), webSession);
|
|
260
|
+
});
|
|
261
|
+
const disableTOTP = Effect.fn("AuthApi.disableTOTP")(function* (code) {
|
|
262
|
+
yield* http.post("/auth/totp/disable", jsonBody({ code })).pipe(webSession);
|
|
263
|
+
});
|
|
264
|
+
return AuthApi.of({
|
|
265
|
+
listLoginMethods,
|
|
266
|
+
loginWithPassword,
|
|
267
|
+
completePasswordMFA,
|
|
268
|
+
loginWithAPIToken,
|
|
269
|
+
beginPasskeyLogin,
|
|
270
|
+
finishPasskeyLogin,
|
|
271
|
+
acceptUserInvitation,
|
|
272
|
+
logoutWebSession,
|
|
273
|
+
getAuthMe,
|
|
274
|
+
getSecurityStatus,
|
|
275
|
+
setPassword,
|
|
276
|
+
listPasskeys,
|
|
277
|
+
beginPasskeyRegistration,
|
|
278
|
+
finishPasskeyRegistration,
|
|
279
|
+
renamePasskey,
|
|
280
|
+
deletePasskey,
|
|
281
|
+
beginTOTPEnrollment,
|
|
282
|
+
completeTOTPEnrollment,
|
|
283
|
+
regenerateRecoveryCodes,
|
|
284
|
+
disableTOTP
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
const authApiLayer = Layer.effect(AuthApi, makeAuthApi);
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/errors.ts
|
|
290
|
+
const ApiErrorBody = Schema.Struct({ error: Schema.Struct({
|
|
291
|
+
code: Schema.String,
|
|
292
|
+
message: Schema.String
|
|
293
|
+
}) });
|
|
294
|
+
/**
|
|
295
|
+
* A failed API request. `code` and `message` come from the server's error body when it
|
|
296
|
+
* sent one; `status` is 0 when no HTTP status applies.
|
|
297
|
+
*/
|
|
298
|
+
var ApiRequestError = class extends Schema.TaggedError()("ApiRequestError", {
|
|
299
|
+
code: Schema.String,
|
|
300
|
+
message: Schema.String,
|
|
301
|
+
status: Schema.Number
|
|
302
|
+
}) {};
|
|
303
|
+
const invalidResponse = (status) => new ApiRequestError({
|
|
304
|
+
code: "internal_error",
|
|
305
|
+
message: "Invalid response",
|
|
306
|
+
status
|
|
307
|
+
});
|
|
308
|
+
const networkError = new ApiRequestError({
|
|
309
|
+
code: "network_error",
|
|
310
|
+
message: "The server could not be reached",
|
|
311
|
+
status: 0
|
|
312
|
+
});
|
|
313
|
+
/**
|
|
314
|
+
* Maps HTTP and decoding failures to ApiRequestError. Error responses keep the code and
|
|
315
|
+
* message of the server's error body.
|
|
316
|
+
*/
|
|
317
|
+
const toApiRequestError = (self) => self.pipe(Effect.catchReasons("HttpClientError", {
|
|
318
|
+
StatusCodeError: ({ response }) => HttpClientResponse.schemaBodyJson(ApiErrorBody)(response).pipe(Effect.map(({ error }) => new ApiRequestError({
|
|
319
|
+
...error,
|
|
320
|
+
status: response.status
|
|
321
|
+
})), Effect.orElseSucceed(() => new ApiRequestError({
|
|
322
|
+
code: "internal_error",
|
|
323
|
+
message: "Request failed",
|
|
324
|
+
status: response.status
|
|
325
|
+
})), Effect.flatMap(Effect.fail)),
|
|
326
|
+
DecodeError: ({ response }) => Effect.fail(invalidResponse(response.status))
|
|
327
|
+
}), Effect.catchTags({
|
|
328
|
+
HttpClientError: () => Effect.fail(networkError),
|
|
329
|
+
SchemaError: () => Effect.fail(invalidResponse(0))
|
|
330
|
+
}));
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/authorization/layer.ts
|
|
333
|
+
/** The authorization of the call a request belongs to; set by `authorize`. */
|
|
334
|
+
const CurrentAuthorization = Context.Reference("@aperture-browser/api-client/CurrentAuthorization", { defaultValue: () => ({}) });
|
|
335
|
+
const authorizeRequest = (request) => CurrentAuthorization.useSync(({ credentials, bearerToken, tenantHeader = "none" }) => {
|
|
336
|
+
const token = bearerToken ?? (credentials?.kind === "bearer" ? credentials.token.trim() : void 0);
|
|
337
|
+
const tenantId = credentials ? resolveTenantHeader(credentials, tenantHeader) : void 0;
|
|
338
|
+
return request.pipe(HttpClientRequest.acceptJson, token ? HttpClientRequest.bearerToken(token) : Function.identity, tenantId ? HttpClientRequest.setHeader(TENANT_HEADER, tenantId) : HttpClientRequest.removeHeader(TENANT_HEADER));
|
|
339
|
+
});
|
|
340
|
+
const sessionAuthenticationFailureCodes = /* @__PURE__ */ new Set([
|
|
341
|
+
"authentication_required",
|
|
342
|
+
"invalid_authentication_token",
|
|
343
|
+
"authentication_token_expired",
|
|
344
|
+
"authentication_token_revoked",
|
|
345
|
+
"user_disabled"
|
|
346
|
+
]);
|
|
347
|
+
/**
|
|
348
|
+
* Decorates the HttpClient so each request carries the authorization of the `authorize`
|
|
349
|
+
* call it runs in.
|
|
350
|
+
*/
|
|
351
|
+
const authorizedHttpClientLayer = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, HttpClient.mapRequestEffect(authorizeRequest)));
|
|
352
|
+
const makeApiAuthorization = Effect.gen(function* () {
|
|
353
|
+
const failures = yield* PubSub.unbounded();
|
|
354
|
+
const authorize = (authorization) => (self) => self.pipe(toApiRequestError, Effect.tapError((error) => authorization.credentials?.kind === "session" && sessionAuthenticationFailureCodes.has(error.code) ? PubSub.publish(failures, void 0) : Effect.void), Effect.provideService(CurrentAuthorization, authorization));
|
|
355
|
+
return ApiAuthorization.of({
|
|
356
|
+
authorize,
|
|
357
|
+
sessionAuthenticationFailures: Stream.fromPubSub(failures)
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
const apiAuthorizationLayer = Layer.effect(ApiAuthorization, makeApiAuthorization);
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/query.ts
|
|
363
|
+
/** Encodes tag filters as the parallel tagKey, tagOperator and tagValue query lists. */
|
|
364
|
+
const tagQuery = (tags) => ({
|
|
365
|
+
tagKey: tags?.map((tag) => tag.key),
|
|
366
|
+
tagOperator: tags?.map((tag) => tag.operator),
|
|
367
|
+
tagValue: tags?.map((tag) => tag.values.join(","))
|
|
368
|
+
});
|
|
369
|
+
/** Drops empty strings and empty list items, which mean "no filter". */
|
|
370
|
+
const compactQuery = (query) => Object.fromEntries(Object.entries(query).flatMap(([key, value]) => {
|
|
371
|
+
if (value === void 0 || value === null || value === "") return [];
|
|
372
|
+
if (!Array.isArray(value)) return [[key, value]];
|
|
373
|
+
const items = value.filter((item) => item !== "");
|
|
374
|
+
return items.length > 0 ? [[key, items]] : [];
|
|
375
|
+
}));
|
|
376
|
+
//#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
|
+
//#region src/events/layer.ts
|
|
382
|
+
const makeEventsApi = Effect.gen(function* () {
|
|
383
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
384
|
+
const { authorize } = yield* ApiAuthorization;
|
|
385
|
+
const api = Api.make(httpClient);
|
|
386
|
+
const listEvents = Effect.fn("EventsApi.listEvents")(function* (credentials, params = {}) {
|
|
387
|
+
return yield* api.listEvents({ params: compactQuery({
|
|
388
|
+
limit: params.limit,
|
|
389
|
+
cursor: params.cursor,
|
|
390
|
+
resourceType: params.resourceType,
|
|
391
|
+
resourceId: params.resourceId
|
|
392
|
+
}) }).pipe(authorize(Authorization.tenantScoped(credentials)));
|
|
393
|
+
});
|
|
394
|
+
return EventsApi.of({ listEvents });
|
|
395
|
+
});
|
|
396
|
+
const eventsApiLayer = Layer.effect(EventsApi, makeEventsApi);
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/sessions/schemas.ts
|
|
399
|
+
const positiveInt = Schema.Number.check(Schema.isInt(), Schema.isGreaterThan(0));
|
|
400
|
+
const emptyArray = Effect.succeed([]);
|
|
401
|
+
const BrowserStatus = Schema.Struct({
|
|
402
|
+
sessionId: Schema.String,
|
|
403
|
+
cdpUrl: Schema.String,
|
|
404
|
+
media: Api.SessionMedia,
|
|
405
|
+
targets: Schema.Array(Schema.Struct({
|
|
406
|
+
targetId: Schema.String,
|
|
407
|
+
generation: positiveInt,
|
|
408
|
+
state: Schema.Literals([
|
|
409
|
+
"pending",
|
|
410
|
+
"ready",
|
|
411
|
+
"unavailable",
|
|
412
|
+
"closed"
|
|
413
|
+
]),
|
|
414
|
+
title: Schema.String,
|
|
415
|
+
url: Schema.String,
|
|
416
|
+
viewport: Schema.Struct({
|
|
417
|
+
width: positiveInt,
|
|
418
|
+
height: positiveInt,
|
|
419
|
+
deviceScaleFactor: Schema.Number.check(Schema.isGreaterThan(0)),
|
|
420
|
+
contentWidth: positiveInt,
|
|
421
|
+
contentHeight: positiveInt,
|
|
422
|
+
canvasWidth: positiveInt,
|
|
423
|
+
canvasHeight: positiveInt
|
|
424
|
+
})
|
|
425
|
+
})).pipe(Schema.withDecodingDefaultKey(emptyArray))
|
|
426
|
+
});
|
|
427
|
+
/** A recording as the live session reports it. */
|
|
428
|
+
const Recording = Schema.Struct({
|
|
429
|
+
recordingId: Schema.String,
|
|
430
|
+
mode: Schema.Literals(["tab", "viewer"]),
|
|
431
|
+
targetId: Schema.String,
|
|
432
|
+
captureGeneration: positiveInt,
|
|
433
|
+
status: Schema.Literals([
|
|
434
|
+
"starting",
|
|
435
|
+
"running",
|
|
436
|
+
"stopped",
|
|
437
|
+
"failed"
|
|
438
|
+
]),
|
|
439
|
+
stopReason: Schema.optionalKey(Schema.String),
|
|
440
|
+
path: Schema.String,
|
|
441
|
+
startedAt: Schema.String,
|
|
442
|
+
stoppedAt: Schema.optionalKey(Schema.String),
|
|
443
|
+
sizeBytes: Schema.optionalKey(Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))),
|
|
444
|
+
fps: positiveInt,
|
|
445
|
+
bitrateKbps: positiveInt,
|
|
446
|
+
codec: Schema.String
|
|
447
|
+
});
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/sessions/service.ts
|
|
450
|
+
/** Browser sessions, their live browser, and their recordings. */
|
|
451
|
+
var SessionsApi = class extends Context.Service()("@aperture-browser/api-client/SessionsApi") {};
|
|
452
|
+
//#endregion
|
|
453
|
+
//#region src/sessions/layer.ts
|
|
454
|
+
const contentDispositionFilename = (header) => header?.match(/filename="([^"]+)"/)?.[1] ?? null;
|
|
455
|
+
const makeSessionsApi = Effect.gen(function* () {
|
|
456
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
457
|
+
const { authorize } = yield* ApiAuthorization;
|
|
458
|
+
const http = HttpClient.filterStatusOk(httpClient);
|
|
459
|
+
const api = Api.make(httpClient);
|
|
460
|
+
const tenantScoped = (credentials) => authorize(Authorization.tenantScoped(credentials));
|
|
461
|
+
const listSessions = Effect.fn("SessionsApi.listSessions")(function* (credentials, params = {}) {
|
|
462
|
+
return yield* api.listSessions({ params: compactQuery({
|
|
463
|
+
limit: params.limit,
|
|
464
|
+
cursor: params.cursor,
|
|
465
|
+
includeDeleted: params.includeDeleted || void 0,
|
|
466
|
+
status: params.status,
|
|
467
|
+
...tagQuery(params.tags)
|
|
468
|
+
}) }).pipe(tenantScoped(credentials));
|
|
469
|
+
});
|
|
470
|
+
const getSession = Effect.fn("SessionsApi.getSession")(function* (credentials, sessionId) {
|
|
471
|
+
return yield* api.getSession(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
472
|
+
});
|
|
473
|
+
const getSessionsBulk = Effect.fn("SessionsApi.getSessionsBulk")(function* (credentials, sessionIds) {
|
|
474
|
+
return yield* api.getSessionsBulk({ payload: { ids: sessionIds } }).pipe(tenantScoped(credentials));
|
|
475
|
+
});
|
|
476
|
+
const createSession = Effect.fn("SessionsApi.createSession")(function* (credentials, input, options = {}) {
|
|
477
|
+
return yield* api.createSession({
|
|
478
|
+
params: compactQuery({ waitForReady: options.waitForReady }),
|
|
479
|
+
payload: {
|
|
480
|
+
baseSnapshotName: input.baseSnapshotName ?? null,
|
|
481
|
+
label: input.label ?? null,
|
|
482
|
+
browser: {
|
|
483
|
+
channel: input.browser.channel,
|
|
484
|
+
args: input.browser.args ?? []
|
|
485
|
+
},
|
|
486
|
+
initialTargets: input.initialTargets ?? [],
|
|
487
|
+
...input.storageState === void 0 ? {} : { storageState: input.storageState },
|
|
488
|
+
tags: input.tags ?? {}
|
|
489
|
+
}
|
|
490
|
+
}).pipe(tenantScoped(credentials));
|
|
491
|
+
});
|
|
492
|
+
const deleteSession = Effect.fn("SessionsApi.deleteSession")(function* (credentials, sessionId) {
|
|
493
|
+
return yield* api.deleteSession(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
494
|
+
});
|
|
495
|
+
const reopenSession = Effect.fn("SessionsApi.reopenSession")(function* (credentials, sessionId) {
|
|
496
|
+
return yield* api.reopenSession(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
497
|
+
});
|
|
498
|
+
const suspendSession = Effect.fn("SessionsApi.suspendSession")(function* (credentials, sessionId) {
|
|
499
|
+
return yield* api.suspendSession(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
500
|
+
});
|
|
501
|
+
const rotateSessionToken = Effect.fn("SessionsApi.rotateSessionToken")(function* (credentials, sessionId) {
|
|
502
|
+
return yield* api.rotateSessionToken(sessionId, void 0).pipe(tenantScoped(credentials));
|
|
503
|
+
});
|
|
504
|
+
const rotateCollaborationCapability = Effect.fn("SessionsApi.rotateCollaborationCapability")(function* (credentials, sessionId, role) {
|
|
505
|
+
return yield* api.rotateCollaborationCapability(sessionId, role, void 0).pipe(tenantScoped(credentials));
|
|
506
|
+
});
|
|
507
|
+
const replaceSessionTags = Effect.fn("SessionsApi.replaceSessionTags")(function* (credentials, sessionId, tags) {
|
|
508
|
+
return yield* api.replaceSessionTags(sessionId, { payload: { tags } }).pipe(tenantScoped(credentials));
|
|
509
|
+
});
|
|
510
|
+
const promoteSession = Effect.fn("SessionsApi.promoteSession")(function* (credentials, sessionId, input) {
|
|
511
|
+
return yield* api.promoteSession(sessionId, { payload: {
|
|
512
|
+
name: input.name,
|
|
513
|
+
description: input.description ?? null,
|
|
514
|
+
force: input.force ?? false,
|
|
515
|
+
tags: input.tags ?? {}
|
|
516
|
+
} }).pipe(tenantScoped(credentials));
|
|
517
|
+
});
|
|
518
|
+
const getBrowserChannels = Effect.fn("SessionsApi.getBrowserChannels")(function* (credentials) {
|
|
519
|
+
return yield* api.listBrowserChannels(void 0).pipe(tenantScoped(credentials));
|
|
520
|
+
});
|
|
521
|
+
const getBrowserStatus = Effect.fn("SessionsApi.getBrowserStatus")(function* (credentials, sessionId, sessionToken) {
|
|
522
|
+
return yield* http.get(`/sessions/${encodeURIComponent(sessionId)}/browser/status`).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(BrowserStatus)), authorize({
|
|
523
|
+
credentials,
|
|
524
|
+
bearerToken: sessionToken
|
|
525
|
+
}));
|
|
526
|
+
});
|
|
527
|
+
const downloadSessionRecording = Effect.fn("SessionsApi.downloadSessionRecording")(function* (credentials, sessionId, recordingId, sessionToken) {
|
|
528
|
+
return yield* http.get(`/sessions/${encodeURIComponent(sessionId)}/recordings/${encodeURIComponent(recordingId)}/content`).pipe(Effect.flatMap((response) => Effect.map(response.arrayBuffer, (body) => ({
|
|
529
|
+
blob: new Blob([body], { type: response.headers["content-type"] ?? "" }),
|
|
530
|
+
filename: contentDispositionFilename(response.headers["content-disposition"])
|
|
531
|
+
}))), authorize({
|
|
532
|
+
credentials,
|
|
533
|
+
bearerToken: sessionToken,
|
|
534
|
+
tenantHeader: "tenant-scoped"
|
|
535
|
+
}));
|
|
536
|
+
});
|
|
537
|
+
return SessionsApi.of({
|
|
538
|
+
listSessions,
|
|
539
|
+
getSession,
|
|
540
|
+
getSessionsBulk,
|
|
541
|
+
createSession,
|
|
542
|
+
deleteSession,
|
|
543
|
+
reopenSession,
|
|
544
|
+
suspendSession,
|
|
545
|
+
rotateSessionToken,
|
|
546
|
+
rotateCollaborationCapability,
|
|
547
|
+
replaceSessionTags,
|
|
548
|
+
promoteSession,
|
|
549
|
+
getBrowserChannels,
|
|
550
|
+
getBrowserStatus,
|
|
551
|
+
downloadSessionRecording
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
const sessionsApiLayer = Layer.effect(SessionsApi, makeSessionsApi);
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/snapshots/service.ts
|
|
557
|
+
/** Retained snapshots that new sessions start from. */
|
|
558
|
+
var SnapshotsApi = class extends Context.Service()("@aperture-browser/api-client/SnapshotsApi") {};
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/snapshots/layer.ts
|
|
561
|
+
const makeSnapshotsApi = Effect.gen(function* () {
|
|
562
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
563
|
+
const { authorize } = yield* ApiAuthorization;
|
|
564
|
+
const api = Api.make(httpClient);
|
|
565
|
+
const tenantScoped = (credentials) => authorize(Authorization.tenantScoped(credentials));
|
|
566
|
+
const listSnapshots = Effect.fn("SnapshotsApi.listSnapshots")(function* (credentials, params = {}) {
|
|
567
|
+
return yield* api.listSnapshots({ params: compactQuery({
|
|
568
|
+
limit: params.limit,
|
|
569
|
+
cursor: params.cursor,
|
|
570
|
+
includeDeleted: params.includeDeleted || void 0,
|
|
571
|
+
deleted: params.deleted,
|
|
572
|
+
name: params.name,
|
|
573
|
+
...tagQuery(params.tags)
|
|
574
|
+
}) }).pipe(tenantScoped(credentials));
|
|
575
|
+
});
|
|
576
|
+
const updateSnapshot = Effect.fn("SnapshotsApi.updateSnapshot")(function* (credentials, name, input) {
|
|
577
|
+
return yield* api.updateSnapshot(name, { payload: input }).pipe(tenantScoped(credentials));
|
|
578
|
+
});
|
|
579
|
+
const replaceSnapshotTags = Effect.fn("SnapshotsApi.replaceSnapshotTags")(function* (credentials, name, tags) {
|
|
580
|
+
return yield* api.replaceSnapshotTags(name, { payload: { tags } }).pipe(tenantScoped(credentials));
|
|
581
|
+
});
|
|
582
|
+
const deleteSnapshot = Effect.fn("SnapshotsApi.deleteSnapshot")(function* (credentials, name) {
|
|
583
|
+
return yield* api.deleteSnapshot(name, void 0).pipe(tenantScoped(credentials));
|
|
584
|
+
});
|
|
585
|
+
const restoreSnapshot = Effect.fn("SnapshotsApi.restoreSnapshot")(function* (credentials, name) {
|
|
586
|
+
return yield* api.restoreSnapshot(name, void 0).pipe(tenantScoped(credentials));
|
|
587
|
+
});
|
|
588
|
+
return SnapshotsApi.of({
|
|
589
|
+
listSnapshots,
|
|
590
|
+
updateSnapshot,
|
|
591
|
+
replaceSnapshotTags,
|
|
592
|
+
deleteSnapshot,
|
|
593
|
+
restoreSnapshot
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
const snapshotsApiLayer = Layer.effect(SnapshotsApi, makeSnapshotsApi);
|
|
597
|
+
//#endregion
|
|
598
|
+
//#region src/tenants/service.ts
|
|
599
|
+
/** Tenant administration. */
|
|
600
|
+
var TenantsApi = class extends Context.Service()("@aperture-browser/api-client/TenantsApi") {};
|
|
601
|
+
//#endregion
|
|
602
|
+
//#region src/tenants/layer.ts
|
|
603
|
+
const makeTenantsApi = Effect.gen(function* () {
|
|
604
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
605
|
+
const { authorize } = yield* ApiAuthorization;
|
|
606
|
+
const api = Api.make(httpClient);
|
|
607
|
+
const listTenants = Effect.fn("TenantsApi.listTenants")(function* (credentials, params = {}) {
|
|
608
|
+
return yield* api.listTenants({ params: compactQuery({
|
|
609
|
+
limit: params.limit,
|
|
610
|
+
cursor: params.cursor,
|
|
611
|
+
includeDeleted: params.includeDeleted || void 0,
|
|
612
|
+
deleted: params.deleted
|
|
613
|
+
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
614
|
+
});
|
|
615
|
+
const createTenant = Effect.fn("TenantsApi.createTenant")(function* (credentials, input) {
|
|
616
|
+
return yield* api.createTenant({ payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
617
|
+
});
|
|
618
|
+
const updateTenant = Effect.fn("TenantsApi.updateTenant")(function* (credentials, tenantId, input) {
|
|
619
|
+
return yield* api.updateTenant(tenantId, { payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
620
|
+
});
|
|
621
|
+
const deleteTenant = Effect.fn("TenantsApi.deleteTenant")(function* (credentials, tenantId) {
|
|
622
|
+
return yield* api.deleteTenant(tenantId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
623
|
+
});
|
|
624
|
+
const restoreTenant = Effect.fn("TenantsApi.restoreTenant")(function* (credentials, tenantId) {
|
|
625
|
+
return yield* api.restoreTenant(tenantId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
626
|
+
});
|
|
627
|
+
return TenantsApi.of({
|
|
628
|
+
listTenants,
|
|
629
|
+
createTenant,
|
|
630
|
+
updateTenant,
|
|
631
|
+
deleteTenant,
|
|
632
|
+
restoreTenant
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
const tenantsApiLayer = Layer.effect(TenantsApi, makeTenantsApi);
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/tokens/service.ts
|
|
638
|
+
/** API tokens, issued either deployment-wide or within the current tenant. */
|
|
639
|
+
var TokensApi = class extends Context.Service()("@aperture-browser/api-client/TokensApi") {};
|
|
640
|
+
//#endregion
|
|
641
|
+
//#region src/tokens/layer.ts
|
|
642
|
+
const makeTokensApi = Effect.gen(function* () {
|
|
643
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
644
|
+
const { authorize } = yield* ApiAuthorization;
|
|
645
|
+
const api = Api.make(httpClient);
|
|
646
|
+
const listAdminTokens = Effect.fn("TokensApi.listAdminTokens")(function* (credentials, params = {}) {
|
|
647
|
+
return yield* api.listAdminTokens({ params: compactQuery({
|
|
648
|
+
limit: params.limit,
|
|
649
|
+
cursor: params.cursor,
|
|
650
|
+
tenantId: params.tenantId,
|
|
651
|
+
name: params.name,
|
|
652
|
+
authorityType: params.authorityType,
|
|
653
|
+
revoked: params.revoked,
|
|
654
|
+
scope: params.scope
|
|
655
|
+
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
656
|
+
});
|
|
657
|
+
const createAdminToken = Effect.fn("TokensApi.createAdminToken")(function* (credentials, input) {
|
|
658
|
+
const systemAdmin = input.authorityType === "system_admin";
|
|
659
|
+
return yield* api.createAdminToken({ payload: {
|
|
660
|
+
name: input.name,
|
|
661
|
+
authorityType: input.authorityType,
|
|
662
|
+
tenantId: input.tenantId ?? null,
|
|
663
|
+
scopes: input.scopes,
|
|
664
|
+
resourceMode: systemAdmin ? "all" : input.resourceMode,
|
|
665
|
+
resourceGrants: systemAdmin ? [] : input.resourceGrants,
|
|
666
|
+
expiresAt: input.expiresAt ?? null
|
|
667
|
+
} }).pipe(authorize(Authorization.of(credentials)));
|
|
668
|
+
});
|
|
669
|
+
const revokeAdminToken = Effect.fn("TokensApi.revokeAdminToken")(function* (credentials, tokenId) {
|
|
670
|
+
yield* api.revokeAdminToken(tokenId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
671
|
+
});
|
|
672
|
+
const listTenantTokens = Effect.fn("TokensApi.listTenantTokens")(function* (credentials, params = {}) {
|
|
673
|
+
return yield* api.listTenantTokens({ params: compactQuery({
|
|
674
|
+
limit: params.limit,
|
|
675
|
+
cursor: params.cursor,
|
|
676
|
+
name: params.name,
|
|
677
|
+
revoked: params.revoked,
|
|
678
|
+
scope: params.scope
|
|
679
|
+
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
680
|
+
});
|
|
681
|
+
const createTenantToken = Effect.fn("TokensApi.createTenantToken")(function* (credentials, input) {
|
|
682
|
+
return yield* api.createTenantToken({ payload: {
|
|
683
|
+
name: input.name,
|
|
684
|
+
scopes: input.scopes,
|
|
685
|
+
resourceMode: input.resourceMode,
|
|
686
|
+
resourceGrants: input.resourceGrants,
|
|
687
|
+
expiresAt: input.expiresAt ?? null
|
|
688
|
+
} }).pipe(authorize(Authorization.of(credentials)));
|
|
689
|
+
});
|
|
690
|
+
const revokeTenantToken = Effect.fn("TokensApi.revokeTenantToken")(function* (credentials, tokenId) {
|
|
691
|
+
yield* api.revokeTenantToken(tokenId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
692
|
+
});
|
|
693
|
+
return TokensApi.of({
|
|
694
|
+
listAdminTokens,
|
|
695
|
+
createAdminToken,
|
|
696
|
+
revokeAdminToken,
|
|
697
|
+
listTenantTokens,
|
|
698
|
+
createTenantToken,
|
|
699
|
+
revokeTenantToken
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
const tokensApiLayer = Layer.effect(TokensApi, makeTokensApi);
|
|
703
|
+
//#endregion
|
|
704
|
+
//#region src/users/service.ts
|
|
705
|
+
/** User administration and tenant memberships. */
|
|
706
|
+
var UsersApi = class extends Context.Service()("@aperture-browser/api-client/UsersApi") {};
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/users/layer.ts
|
|
709
|
+
const makeUsersApi = Effect.gen(function* () {
|
|
710
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
711
|
+
const { authorize } = yield* ApiAuthorization;
|
|
712
|
+
const api = Api.make(httpClient);
|
|
713
|
+
const listUsers = Effect.fn("UsersApi.listUsers")(function* (credentials, params = {}) {
|
|
714
|
+
return yield* api.listUsers({ params: compactQuery({
|
|
715
|
+
limit: params.limit,
|
|
716
|
+
cursor: params.cursor,
|
|
717
|
+
query: params.query,
|
|
718
|
+
disabled: params.disabled
|
|
719
|
+
}) }).pipe(authorize(Authorization.of(credentials)));
|
|
720
|
+
});
|
|
721
|
+
const createUser = Effect.fn("UsersApi.createUser")(function* (credentials, input) {
|
|
722
|
+
return yield* api.createUser({ payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
723
|
+
});
|
|
724
|
+
const getUser = Effect.fn("UsersApi.getUser")(function* (credentials, userId) {
|
|
725
|
+
return yield* api.getUser(userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
726
|
+
});
|
|
727
|
+
const updateUser = Effect.fn("UsersApi.updateUser")(function* (credentials, userId, input) {
|
|
728
|
+
return yield* api.updateUser(userId, { payload: input }).pipe(authorize(Authorization.of(credentials)));
|
|
729
|
+
});
|
|
730
|
+
const createUserInvitation = Effect.fn("UsersApi.createUserInvitation")(function* (credentials, userId) {
|
|
731
|
+
return yield* api.createUserInvitation(userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
732
|
+
});
|
|
733
|
+
const disableUser = Effect.fn("UsersApi.disableUser")(function* (credentials, userId) {
|
|
734
|
+
return yield* api.disableUser(userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
735
|
+
});
|
|
736
|
+
const restoreUser = Effect.fn("UsersApi.restoreUser")(function* (credentials, userId) {
|
|
737
|
+
return yield* api.restoreUser(userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
738
|
+
});
|
|
739
|
+
const listUserMemberships = Effect.fn("UsersApi.listUserMemberships")(function* (credentials, userId) {
|
|
740
|
+
return yield* api.listUserMemberships(userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
741
|
+
});
|
|
742
|
+
const upsertTenantMembership = Effect.fn("UsersApi.upsertTenantMembership")(function* (credentials, tenantId, userId, scopes) {
|
|
743
|
+
const payload = { scopes };
|
|
744
|
+
return yield* api.upsertTenantMembership(tenantId, userId, { payload }).pipe(authorize(Authorization.of(credentials)));
|
|
745
|
+
});
|
|
746
|
+
const deleteTenantMembership = Effect.fn("UsersApi.deleteTenantMembership")(function* (credentials, tenantId, userId) {
|
|
747
|
+
yield* api.deleteTenantMembership(tenantId, userId, void 0).pipe(authorize(Authorization.of(credentials)));
|
|
748
|
+
});
|
|
749
|
+
return UsersApi.of({
|
|
750
|
+
listUsers,
|
|
751
|
+
createUser,
|
|
752
|
+
getUser,
|
|
753
|
+
updateUser,
|
|
754
|
+
createUserInvitation,
|
|
755
|
+
disableUser,
|
|
756
|
+
restoreUser,
|
|
757
|
+
listUserMemberships,
|
|
758
|
+
upsertTenantMembership,
|
|
759
|
+
deleteTenantMembership
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
const usersApiLayer = Layer.effect(UsersApi, makeUsersApi);
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/layer.ts
|
|
765
|
+
/**
|
|
766
|
+
* Every API service, sharing one ApiAuthorization. The services send requests through the
|
|
767
|
+
* HttpClient the app provides, decorated with each call's authorization.
|
|
768
|
+
*/
|
|
769
|
+
const apiClientLayer = Layer.mergeAll(authApiLayer, tenantsApiLayer, usersApiLayer, sessionsApiLayer, snapshotsApiLayer, tokensApiLayer, eventsApiLayer).pipe(Layer.provide(authorizedHttpClientLayer), Layer.provideMerge(apiAuthorizationLayer));
|
|
770
|
+
const baseUrlLayer = (baseUrl) => Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, HttpClient.mapRequest(HttpClientRequest.prependUrl(baseUrl))));
|
|
771
|
+
//#endregion
|
|
772
|
+
//#region src/pagination.ts
|
|
773
|
+
function getNextPageParam(page) {
|
|
774
|
+
return page.meta.hasMore ? page.meta.nextCursor : void 0;
|
|
775
|
+
}
|
|
776
|
+
function flattenInfinitePages(pages) {
|
|
777
|
+
return pages?.flatMap((page) => page.data) ?? [];
|
|
778
|
+
}
|
|
779
|
+
const defaultListLimit = 50;
|
|
780
|
+
const listQueryDefaults = { refetchOnWindowFocus: true };
|
|
781
|
+
//#endregion
|
|
782
|
+
//#region src/schemas.ts
|
|
783
|
+
const SessionStatus = Api.SessionStatus;
|
|
784
|
+
//#endregion
|
|
785
|
+
export { ApiAuthorization, ApiErrorBody, ApiRequestError, AuthApi, Authorization, BrowserStatus, EventsApi, LoginMethods, Passkey, PasskeyLoginOptions, PasskeyMutation, PasskeyRegistrationOptions, Passkeys, PasswordLoginResponse, Recording, RecoveryCodes, SecurityStatus, SessionStatus, SessionsApi, SnapshotsApi, TENANT_HEADER, TOTPEnrollment, TenantsApi, TokensApi, UsersApi, apiAuthorizationLayer, apiClientLayer, authApiLayer, authorizedHttpClientLayer, baseUrlLayer, compactQuery, defaultListLimit, eventsApiLayer, flattenInfinitePages, getNextPageParam, listQueryDefaults, makeApiAuthorization, makeAuthApi, makeEventsApi, makeSessionsApi, makeSnapshotsApi, makeTenantsApi, makeTokensApi, makeUsersApi, resolveTenantHeader, sessionsApiLayer, snapshotsApiLayer, tagQuery, tenantsApiLayer, toApiRequestError, tokensApiLayer, usersApiLayer, webSessionCredentials };
|
|
786
|
+
|
|
787
|
+
//# sourceMappingURL=index.js.map
|