@cubist-labs/cubesigner-sdk 0.1.50 → 0.2.2

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.
Files changed (47) hide show
  1. package/README.md +66 -13
  2. package/dist/src/client.d.ts +434 -7
  3. package/dist/src/client.js +1022 -18
  4. package/dist/src/ethers/index.d.ts +2 -4
  5. package/dist/src/ethers/index.js +11 -9
  6. package/dist/src/fido.d.ts +76 -0
  7. package/dist/src/fido.js +148 -0
  8. package/dist/src/index.d.ts +102 -30
  9. package/dist/src/index.js +126 -72
  10. package/dist/src/key.d.ts +15 -45
  11. package/dist/src/key.js +31 -93
  12. package/dist/src/mfa.d.ts +85 -14
  13. package/dist/src/mfa.js +158 -40
  14. package/dist/src/org.d.ts +237 -123
  15. package/dist/src/org.js +108 -213
  16. package/dist/src/paginator.d.ts +76 -0
  17. package/dist/src/paginator.js +99 -0
  18. package/dist/src/role.d.ts +76 -74
  19. package/dist/src/role.js +79 -136
  20. package/dist/src/schema.d.ts +1672 -520
  21. package/dist/src/schema.js +1 -1
  22. package/dist/src/schema_types.d.ts +103 -0
  23. package/dist/src/schema_types.js +3 -0
  24. package/dist/src/session/session_manager.js +2 -2
  25. package/dist/src/session/session_storage.js +1 -1
  26. package/dist/src/session/signer_session_manager.d.ts +16 -29
  27. package/dist/src/session/signer_session_manager.js +27 -78
  28. package/dist/src/signer_session.d.ts +232 -125
  29. package/dist/src/signer_session.js +149 -250
  30. package/dist/src/util.d.ts +20 -0
  31. package/dist/src/util.js +31 -2
  32. package/package.json +13 -11
  33. package/src/client.ts +1217 -7
  34. package/src/ethers/index.ts +11 -18
  35. package/src/index.ts +149 -101
  36. package/src/key.ts +28 -121
  37. package/src/mfa.ts +202 -0
  38. package/src/org.ts +126 -275
  39. package/src/paginator.ts +122 -0
  40. package/src/role.ts +108 -181
  41. package/src/schema.ts +1673 -520
  42. package/src/schema_types.ts +103 -0
  43. package/src/session/session_manager.ts +2 -2
  44. package/src/session/session_storage.ts +1 -1
  45. package/src/session/signer_session_manager.ts +38 -108
  46. package/src/signer_session.ts +164 -323
  47. package/src/util.ts +41 -0
package/src/org.ts CHANGED
@@ -1,14 +1,26 @@
1
- import { CubeSigner, MfaRequestInfo } from ".";
2
- import { components, paths } from "./client";
3
- import { assertOk } from "./util";
1
+ import { OrgInfo } from "./schema_types";
2
+ import { CubeSignerClient } from "./client";
4
3
  import { KeyType, Key } from "./key";
5
- import { MfaPolicy, Role, RoleInfo } from "./role";
4
+ import { Role } from "./role";
5
+ import { PageOpts } from "./paginator";
6
6
 
7
7
  /** Organization id */
8
8
  export type OrgId = string;
9
9
 
10
10
  /** Org-wide policy */
11
- export type OrgPolicy = SourceIpAllowlistPolicy | OriginAllowlistPolicy | MaxDailyUnstakePolicy;
11
+ export type OrgPolicy =
12
+ | SourceIpAllowlistPolicy
13
+ | OidcAuthSourcesPolicy
14
+ | OriginAllowlistPolicy
15
+ | MaxDailyUnstakePolicy;
16
+
17
+ /**
18
+ * Provides an allowlist of OIDC Issuers and audiences that are allowed to authenticate into this org.
19
+ * @example {"OidcAuthSources": { "https://accounts.google.com": [ "1234.apps.googleusercontent.com" ]}}
20
+ */
21
+ export interface OidcAuthSourcesPolicy {
22
+ OidcAuthSources: Record<string, string[]>;
23
+ }
12
24
 
13
25
  /**
14
26
  * Only allow requests from the specified origins.
@@ -34,27 +46,10 @@ export interface MaxDailyUnstakePolicy {
34
46
  MaxDailyUnstake: number;
35
47
  }
36
48
 
37
- type OrgInfo = components["schemas"]["OrgInfo"];
38
- type UserIdInfo = components["schemas"]["UserIdInfo"];
39
- type UpdateOrgRequest =
40
- paths["/v0/org/{org_id}"]["patch"]["requestBody"]["content"]["application/json"];
41
- type UpdateOrgResponse =
42
- paths["/v0/org/{org_id}"]["patch"]["responses"]["200"]["content"]["application/json"];
43
-
44
- export type OidcIdentity = components["schemas"]["OIDCIdentity"];
45
- export type MemberRole = components["schemas"]["MemberRole"];
46
-
47
- /** Options for a new OIDC user */
48
- export interface CreateOidcUserOptions {
49
- /** The role of an OIDC user, default is "Alien" */
50
- memberRole?: MemberRole;
51
- /** Optional MFA policy to associate with the user account */
52
- mfaPolicy?: MfaPolicy;
53
- }
54
-
55
49
  /** An organization. */
56
50
  export class Org {
57
- readonly #cs: CubeSigner;
51
+ readonly #csc: CubeSignerClient;
52
+
58
53
  /**
59
54
  * The ID of the organization.
60
55
  * @example Org#124dfe3e-3bbd-487d-80c0-53c55e8ab87a
@@ -64,47 +59,48 @@ export class Org {
64
59
  /**
65
60
  * @description The org id
66
61
  * @example Org#c3b9379c-4e8c-4216-bd0a-65ace53cf98f
67
- * */
62
+ */
68
63
  get id(): OrgId {
69
64
  return this.#id;
70
65
  }
71
66
 
72
67
  /** Human-readable name for the org */
73
68
  async name(): Promise<string | undefined> {
74
- const data = await this.fetch();
69
+ const data = await this.#csc.orgGet();
75
70
  return data.name ?? undefined;
76
71
  }
77
72
 
78
- /** Set the human-readable name for the org.
73
+ /**
74
+ * Set the human-readable name for the org.
79
75
  * @param {string} name The new human-readable name for the org (must be alphanumeric).
80
76
  * @example my_org_name
81
- * */
77
+ */
82
78
  async setName(name: string) {
83
79
  if (!/^[a-zA-Z0-9_]{3,30}$/.test(name)) {
84
80
  throw new Error("Org name must be alphanumeric and between 3 and 30 characters");
85
81
  }
86
- await this.update({ name });
82
+ await this.#csc.orgUpdate({ name });
87
83
  }
88
84
 
89
85
  /** Is the org enabled? */
90
86
  async enabled(): Promise<boolean> {
91
- const data = await this.fetch();
87
+ const data = await this.#csc.orgGet();
92
88
  return data.enabled;
93
89
  }
94
90
 
95
91
  /** Enable the org. */
96
92
  async enable() {
97
- await this.update({ enabled: true });
93
+ await this.#csc.orgUpdate({ enabled: true });
98
94
  }
99
95
 
100
96
  /** Disable the org. */
101
97
  async disable() {
102
- await this.update({ enabled: false });
98
+ await this.#csc.orgUpdate({ enabled: false });
103
99
  }
104
100
 
105
101
  /** Get the policy for the org. */
106
102
  async policy(): Promise<OrgPolicy[]> {
107
- const data = await this.fetch();
103
+ const data = await this.#csc.orgGet();
108
104
  return (data.policy ?? []) as unknown as OrgPolicy[];
109
105
  }
110
106
 
@@ -113,337 +109,192 @@ export class Org {
113
109
  * */
114
110
  async setPolicy(policy: OrgPolicy[]) {
115
111
  const p = policy as unknown as Record<string, never>[];
116
- await this.update({ policy: p });
112
+ await this.#csc.orgUpdate({ policy: p });
117
113
  }
118
114
 
119
- /** Create a new signing key.
115
+ /**
116
+ * Create a new signing key.
120
117
  * @param {KeyType} type The type of key to create.
121
118
  * @param {string?} ownerId The owner of the key. Defaults to the session's user.
122
119
  * @return {Key[]} The new keys.
123
- * */
120
+ */
124
121
  async createKey(type: KeyType, ownerId?: string): Promise<Key> {
125
- return (await Key.createKeys(this.#cs, this.id, type, 1, ownerId))[0];
122
+ return (await this.createKeys(type, 1, ownerId))[0];
126
123
  }
127
124
 
128
- /** Create new signing keys.
125
+ /**
126
+ * Create new signing keys.
129
127
  * @param {KeyType} type The type of key to create.
130
- * @param {nummber} count The number of keys to create.
128
+ * @param {number} count The number of keys to create.
131
129
  * @param {string?} ownerId The owner of the keys. Defaults to the session's user.
132
130
  * @return {Key[]} The new keys.
133
- * */
131
+ */
134
132
  async createKeys(type: KeyType, count: number, ownerId?: string): Promise<Key[]> {
135
- return Key.createKeys(this.#cs, this.id, type, count, ownerId);
133
+ const keys = await this.#csc.keysCreate(type, count, ownerId);
134
+ return keys.map((k) => new Key(this.#csc, k));
136
135
  }
137
136
 
138
137
  /**
139
- * Derives a key of the given type using the given derivation path and mnemonic.
138
+ * Derive a key of the given type using the given derivation path and mnemonic.
139
+ * The owner of the derived key will be the owner of the mnemonic.
140
140
  *
141
141
  * @param {KeyType} type Type of key to derive from the mnemonic.
142
142
  * @param {string} derivationPath Mnemonic derivation path used to generate new key.
143
143
  * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
144
- * @param {string} ownerId optional owner of the derived key.
145
144
  *
146
145
  * @return {Key} newly derived key.
147
146
  */
148
- async deriveKey(
149
- type: KeyType,
150
- derivationPath: string,
151
- mnemonicId: string,
152
- ownerId?: string,
153
- ): Promise<Key> {
154
- return (
155
- await Key.deriveKeys(this.#cs, this.id, type, [derivationPath], mnemonicId, ownerId)
156
- )[0];
147
+ async deriveKey(type: KeyType, derivationPath: string, mnemonicId: string): Promise<Key> {
148
+ return (await this.deriveKeys(type, [derivationPath], mnemonicId))[0];
157
149
  }
158
150
 
159
151
  /**
160
- * Derives a set of keys of the given type using the given derivation paths and mnemonic.
152
+ * Derive a set of keys of the given type using the given derivation paths and mnemonic.
153
+ *
154
+ * The owner of the derived keys will be the owner of the mnemonic.
161
155
  *
162
156
  * @param {KeyType} type Type of key to derive from the mnemonic.
163
157
  * @param {string[]} derivationPaths Mnemonic derivation paths used to generate new key.
164
158
  * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
165
- * @param {string} ownerId optional owner of the derived key.
166
159
  *
167
160
  * @return {Key[]} newly derived keys.
168
161
  */
169
- async deriveKeys(
170
- type: KeyType,
171
- derivationPaths: string[],
172
- mnemonicId: string,
173
- ownerId?: string,
174
- ): Promise<Key[]> {
175
- return await Key.deriveKeys(this.#cs, this.#id, type, derivationPaths, mnemonicId, ownerId);
162
+ async deriveKeys(type: KeyType, derivationPaths: string[], mnemonicId: string): Promise<Key[]> {
163
+ const keys = await this.#csc.keysDerive(type, derivationPaths, mnemonicId);
164
+ return keys.map((k) => new Key(this.#csc, k));
176
165
  }
177
- /**
178
- * Create a new user in the organization and sends an invitation to that user
179
- * @param {string} email Email of the user
180
- * @param {string} name The full name of the user
181
- */
182
- async createUser(email: string, name: string): Promise<void> {
183
- const resp = await (
184
- await this.#cs.management()
185
- ).post("/v0/org/{org_id}/invite", {
186
- params: { path: { org_id: this.id } },
187
- body: {
188
- email,
189
- name,
190
- skip_email: false,
191
- },
192
- parseAs: "json",
193
- });
194
- assertOk(resp);
166
+
167
+ /** Create a new user in the organization and sends an invitation to that user. */
168
+ get createUser() {
169
+ return this.#csc.orgUserInvite.bind(this.#csc);
195
170
  }
196
171
 
197
- /**
198
- * Create a new OIDC user
199
- * @param {OidcIdentity} identity The identity of the OIDC user
200
- * @param {string} email Email of the OIDC user
201
- * @param {CreateOidcUserOptions} opts Additional options for new OIDC users
202
- * @return {string} User id of the new user
203
- */
204
- async createOidcUser(
205
- identity: OidcIdentity,
206
- email: string,
207
- opts: CreateOidcUserOptions = {},
208
- ): Promise<string> {
209
- const resp = await (
210
- await this.#cs.management()
211
- ).post("/v0/org/{org_id}/users", {
212
- params: { path: { org_id: this.id } },
213
- body: {
214
- identity,
215
- role: opts.memberRole ?? "Alien",
216
- email: email,
217
- mfa_policy: opts.mfaPolicy ?? null,
218
- },
219
- parseAs: "json",
220
- });
221
- return assertOk(resp).user_id;
172
+ /** Create a new OIDC user */
173
+ get createOidcUser() {
174
+ return this.#csc.orgUserCreateOidc.bind(this.#csc);
222
175
  }
223
176
 
224
- /**
225
- * Delete an existing OIDC user
226
- * @param {OidcIdentity} identity The identity of the OIDC user
227
- */
228
- async deleteOidcUser(identity: OidcIdentity) {
229
- const resp = await (
230
- await this.#cs.management()
231
- ).del("/v0/org/{org_id}/users/oidc", {
232
- params: { path: { org_id: this.id } },
233
- body: identity,
234
- parseAs: "json",
235
- });
236
- return assertOk(resp);
177
+ /** Delete an existing OIDC user */
178
+ get deleteOidcUser() {
179
+ return this.#csc.orgUserDeleteOidc.bind(this.#csc);
237
180
  }
238
181
 
239
- /**
240
- * List users in the organization
241
- * @return {UserIdInfo[]} List of users
242
- */
243
- async users(): Promise<UserIdInfo[]> {
244
- const resp = await (
245
- await this.#cs.management()
246
- ).get("/v0/org/{org_id}/users", {
247
- params: { path: { org_id: this.id } },
248
- parseAs: "json",
249
- });
250
- return assertOk(resp).users;
182
+ /** Checks if a given proof of OIDC authentication is valid. */
183
+ get verifyIdentity() {
184
+ return this.#csc.identityVerify.bind(this.#csc);
251
185
  }
252
186
 
253
- /** Get a key by id.
187
+ /** List users in the organization */
188
+ get users() {
189
+ return this.#csc.orgUsersList.bind(this.#csc);
190
+ }
191
+
192
+ /**
193
+ * Get a key by id.
254
194
  * @param {string} keyId The id of the key to get.
255
195
  * @return {Key} The key.
256
- * */
196
+ */
257
197
  async getKey(keyId: string): Promise<Key> {
258
- return await Key.getKey(this.#cs, this.id, keyId);
198
+ const keyInfo = await this.#csc.keyGet(keyId);
199
+ return new Key(this.#csc, keyInfo);
259
200
  }
260
201
 
261
- /** Get all keys in the org.
202
+ /**
203
+ * Get all keys in the org.
262
204
  * @param {KeyType?} type Optional key type to filter list for.
205
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
263
206
  * @return {Key} The key.
264
- * */
265
- async keys(type?: KeyType): Promise<Key[]> {
266
- const resp = await (
267
- await this.#cs.management()
268
- ).get("/v0/org/{org_id}/keys", {
269
- params: {
270
- path: { org_id: this.id },
271
- query: type ? { key_type: type } : undefined,
272
- },
273
- parseAs: "json",
274
- });
275
- const data = assertOk(resp);
276
- return data.keys.map((k) => new Key(this.#cs, this.id, k));
207
+ */
208
+ async keys(type?: KeyType, page?: PageOpts): Promise<Key[]> {
209
+ const paginator = this.#csc.keysList(type, page);
210
+ const keys = await paginator.fetch();
211
+ return keys.map((k) => new Key(this.#csc, k));
277
212
  }
278
213
 
279
- /** Create a new role.
214
+ /**
215
+ * Create a new role.
216
+ *
280
217
  * @param {string?} name The name of the role.
281
218
  * @return {Role} The new role.
282
- * */
219
+ */
283
220
  async createRole(name?: string): Promise<Role> {
284
- return Role.createRole(this.#cs, this.id, name);
221
+ const roleId = await this.#csc.roleCreate(name);
222
+ const roleInfo = await this.#csc.roleGet(roleId);
223
+ return new Role(this.#csc, roleInfo);
285
224
  }
286
225
 
287
- /** Get a role by id or name.
226
+ /**
227
+ * Get a role by id or name.
228
+ *
288
229
  * @param {string} roleId The id or name of the role to get.
289
230
  * @return {Role} The role.
290
- * */
231
+ */
291
232
  async getRole(roleId: string): Promise<Role> {
292
- return Role.getRole(this.#cs, this.id, roleId);
233
+ const roleInfo = await this.#csc.roleGet(roleId);
234
+ return new Role(this.#csc, roleInfo);
293
235
  }
294
236
 
295
- /** List all roles in the org.
237
+ /**
238
+ * List all roles in the org.
239
+ *
240
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
296
241
  * @return {Role[]} The roles.
297
- * */
298
- async listRoles(): Promise<Role[]> {
299
- return Org.roles(this.#cs, this.id);
242
+ */
243
+ async listRoles(page?: PageOpts): Promise<Role[]> {
244
+ const roles = await this.#csc.rolesList(page).fetch();
245
+ return roles.map((r) => new Role(this.#csc, r));
300
246
  }
301
247
 
302
- /** List all users in the org.
303
- * @return {User[]} The users.
304
- * */
305
- async listUsers(): Promise<UserIdInfo[]> {
306
- return Org.users(this.#cs, this.id);
248
+ /** List all users in the org. */
249
+ get listUsers() {
250
+ return this.#csc.orgUsersList.bind(this.#csc);
307
251
  }
308
252
 
309
253
  /**
310
254
  * Get a pending MFA request by its id.
311
- * @param {string} mfaId The id of the MFA request.
312
- * @return {Promise<MfaRequestInfo>} The MFA request.
313
255
  *
314
256
  * @deprecated Use {@link getMfaInfo()} instead.
315
257
  */
316
- async mfaGet(mfaId: string): Promise<MfaRequestInfo> {
317
- return await this.getMfaInfo(mfaId);
258
+ get mfaGet() {
259
+ return this.#csc.mfaGet.bind(this.#csc);
318
260
  }
319
261
 
320
262
  /**
321
263
  * Approve a pending MFA request.
322
264
  *
323
- * @param {string} mfaId The id of the MFA request.
324
- * @return {Promise<MfaRequestInfo>} The MFA request.
325
- *
326
265
  * @deprecated Use {@link approveMfaRequest()} instead.
327
266
  */
328
- async mfaApprove(mfaId: string): Promise<MfaRequestInfo> {
329
- return await this.approveMfaRequest(mfaId);
267
+ get mfaApprove() {
268
+ return this.#csc.mfaApprove.bind(this.#csc);
330
269
  }
331
270
 
332
- /**
333
- * Get a pending MFA request by its id.
334
- * @param {string} mfaId The id of the MFA request.
335
- * @return {Promise<MfaRequestInfo>} The MFA request.
336
- */
337
- async getMfaInfo(mfaId: string): Promise<MfaRequestInfo> {
338
- const resp = await (
339
- await this.#cs.management()
340
- ).get("/v0/org/{org_id}/mfa/{mfa_id}", {
341
- params: { path: { org_id: this.#id, mfa_id: mfaId } },
342
- });
343
- return assertOk(resp);
271
+ /** Get a pending MFA request by its id. */
272
+ get getMfaInfo() {
273
+ return this.#csc.mfaGet.bind(this.#csc);
344
274
  }
345
275
 
346
- /**
347
- * Approve a pending MFA request.
348
- *
349
- * @param {string} mfaId The id of the MFA request.
350
- * @return {Promise<MfaRequestInfo>} The MFA request.
351
- */
352
- async approveMfaRequest(mfaId: string): Promise<MfaRequestInfo> {
353
- return Org.mfaApprove(this.#cs, this.#id, mfaId);
276
+ /** List pending MFA requests accessible to the current user. */
277
+ get listMfaInfos() {
278
+ return this.#csc.mfaList.bind(this.#csc);
279
+ }
280
+
281
+ /** Approve a pending MFA request. */
282
+ get approveMfaRequest() {
283
+ return this.#csc.mfaApprove.bind(this.#csc);
354
284
  }
355
285
 
356
286
  // --------------------------------------------------------------------------
357
287
  // -- INTERNAL --------------------------------------------------------------
358
288
  // --------------------------------------------------------------------------
359
289
 
360
- /** Create a new org.
361
- * @param {CubeSigner} cs The CubeSigner instance.
290
+ /**
291
+ * Create a new org.
292
+ * @param {CubeSignerClient} csc The CubeSigner instance.
362
293
  * @param {OrgInfo} data The JSON response from the API server.
363
294
  * @internal
364
- * */
365
- constructor(cs: CubeSigner, data: OrgInfo) {
366
- this.#cs = cs;
367
- this.#id = data.org_id;
368
- }
369
-
370
- /**
371
- * Approve a pending MFA request.
372
- *
373
- * @param {CubeSigner} cs The CubeSigner instance to use for requests
374
- * @param {string} orgId The org id of the MFA request
375
- * @param {string} mfaId The id of the MFA request
376
- * @return {Promise<MfaRequestInfo>} The result of the MFA request
377
295
  */
378
- static async mfaApprove(cs: CubeSigner, orgId: string, mfaId: string): Promise<MfaRequestInfo> {
379
- const resp = await (
380
- await cs.management()
381
- ).patch("/v0/org/{org_id}/mfa/{mfa_id}", {
382
- params: { path: { org_id: orgId, mfa_id: mfaId } },
383
- });
384
- return assertOk(resp);
385
- }
386
-
387
- /** Fetch org info.
388
- * @return {OrgInfo} The org info.
389
- * */
390
- private async fetch(): Promise<OrgInfo> {
391
- const resp = await (
392
- await this.#cs.management()
393
- ).get("/v0/org/{org_id}", {
394
- params: { path: { org_id: this.id } },
395
- parseAs: "json",
396
- });
397
- const data = assertOk(resp);
398
- return data;
399
- }
400
-
401
- /** Update the org.
402
- * @param {UpdateOrgRequest} request The JSON request to send to the API server.
403
- * @return {UpdateOrgResponse} The JSON response from the API server.
404
- * */
405
- private async update(request: UpdateOrgRequest): Promise<UpdateOrgResponse> {
406
- const resp = await (
407
- await this.#cs.management()
408
- ).patch("/v0/org/{org_id}", {
409
- params: { path: { org_id: this.id } },
410
- body: request,
411
- parseAs: "json",
412
- });
413
- return assertOk(resp);
414
- }
415
-
416
- /** List roles.
417
- * @param {CubeSigner} cs The CubeSigner instance to use for signing.
418
- * @param {string} orgId The id of the organization to which the role belongs.
419
- * @return {Role[]} Org roles.
420
- * @internal
421
- * */
422
- private static async roles(cs: CubeSigner, orgId: string): Promise<Role[]> {
423
- const resp = await (
424
- await cs.management()
425
- ).get("/v0/org/{org_id}/roles", {
426
- params: { path: { org_id: orgId } },
427
- parseAs: "json",
428
- });
429
- const data = assertOk(resp);
430
- return data.roles.map((r: RoleInfo) => new Role(cs, orgId, r));
431
- }
432
-
433
- /** List users.
434
- * @param {CubeSigner} cs The CubeSigner instance to use for signing.
435
- * @param {string} orgId The id of the organization to which the role belongs.
436
- * @return {User[]} Org users.
437
- * @internal
438
- * */
439
- private static async users(cs: CubeSigner, orgId: string): Promise<UserIdInfo[]> {
440
- const resp = await (
441
- await cs.management()
442
- ).get("/v0/org/{org_id}/users", {
443
- params: { path: { org_id: orgId } },
444
- parseAs: "json",
445
- });
446
- const data = assertOk(resp);
447
- return data.users;
296
+ constructor(csc: CubeSignerClient, data: OrgInfo) {
297
+ this.#csc = csc.withOrg(data.org_id);
298
+ this.#id = data.org_id;
448
299
  }
449
300
  }
@@ -0,0 +1,122 @@
1
+ /** Pagination options. */
2
+ export interface PageOpts {
3
+ /** Max number of items per page. */
4
+ size?: number;
5
+ /**
6
+ * Starting point (i.e., 'last_evaluated_key' from the previous page).
7
+ * Omit to start from the beginning.
8
+ */
9
+ start?: string;
10
+ /** Iterate until retrieving the entire result set. */
11
+ all: boolean;
12
+ }
13
+
14
+ /** Static constructors for `IPage` */
15
+ export class Page {
16
+ /**
17
+ * The default is to fetch the entire result set
18
+ * (by repeatedly calling the remote endpoint until all pages are retrieved).
19
+ *
20
+ * @return {PageOpts} Pagination options.
21
+ */
22
+ static default(): PageOpts {
23
+ return <PageOpts>{
24
+ all: true,
25
+ };
26
+ }
27
+ }
28
+
29
+ export interface PageQueryArgs {
30
+ /**
31
+ * Max number of items to return per page.
32
+ *
33
+ * The actual number of returned items may be less that this, even if there exist more
34
+ * data in the result set. To reliably determine if more data is left in the result set,
35
+ * inspect the [UnencryptedLastEvalKey] value in the response object.
36
+ */
37
+ "page.size"?: number;
38
+
39
+ /**
40
+ * The start of the page.
41
+ *
42
+ * Omit to start from the beginning; otherwise, only specify the exact
43
+ * value previously returned as 'last_evaluated_key' from the same endpoint.
44
+ */
45
+ "page.start"?: string | null;
46
+ }
47
+
48
+ export type ListFn<U> = (pageQueryArgs: PageQueryArgs) => Promise<U>;
49
+ export type ItemsFn<U, T> = (resp: U) => T[];
50
+ export type LastFn<U> = (resp: U) => string | null | undefined;
51
+
52
+ /**
53
+ * Helper class for fetching paginated results.
54
+ */
55
+ export class Paginator<U, T> {
56
+ readonly #listFn: ListFn<U>;
57
+ readonly #itemsFn: ItemsFn<U, T>;
58
+ readonly #lastFn: LastFn<U>;
59
+ #opts: PageOpts;
60
+ #last: string | null | undefined;
61
+ #done: boolean;
62
+
63
+ /**
64
+ * @param {PageOpts} pageOpts Pagination options
65
+ * @param {ListFn<U>} listFn Calls a remote endpoint that returns a paginated response
66
+ * @param {ItemsFn<U, T>} itemsFn Extracts items from the paginated response
67
+ * @param {LastFn<U>} lastFn Extracts the last evaluated key from the paginated response
68
+ */
69
+ constructor(pageOpts: PageOpts, listFn: ListFn<U>, itemsFn: ItemsFn<U, T>, lastFn: LastFn<U>) {
70
+ this.#listFn = listFn;
71
+ this.#itemsFn = itemsFn;
72
+ this.#lastFn = lastFn;
73
+ this.#opts = pageOpts;
74
+ this.#last = pageOpts.start;
75
+ this.#done = false;
76
+ }
77
+
78
+ /**
79
+ * Fetches either a single page or the entire result set, depending on
80
+ * the `all` property of the pagination options.
81
+ *
82
+ * @return {Promise<T[]>} A single page or the entire result set.
83
+ */
84
+ async fetch(): Promise<T[]> {
85
+ return this.#opts.all ? await this.fetchAll() : await this.fetchPage();
86
+ }
87
+
88
+ /**
89
+ * Fetches a single page of the result set from where it previously left off.
90
+ * Mutates self to remember where it left off.
91
+ *
92
+ * @return {Promise<T[]>} The next page of the result set.
93
+ */
94
+ async fetchPage(): Promise<T[]> {
95
+ if (this.#done) {
96
+ return [];
97
+ }
98
+
99
+ const resp = await this.#listFn({
100
+ "page.size": this.#opts.size,
101
+ "page.start": this.#last,
102
+ });
103
+ this.#last = this.#lastFn(resp);
104
+ this.#done = !this.#last;
105
+ return this.#itemsFn(resp);
106
+ }
107
+
108
+ /**
109
+ * Fetches the entire result set starting from where it previously left off
110
+ * by iterating through the pages returned by the remote end.
111
+ *
112
+ * @return {Promise<T[]>} The entire result set.
113
+ */
114
+ async fetchAll(): Promise<T[]> {
115
+ const result = [];
116
+ while (!this.#done) {
117
+ const items = await this.fetchPage();
118
+ result.push(...items);
119
+ }
120
+ return result;
121
+ }
122
+ }