@openstax/ts-utils 1.40.2 → 1.41.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 (31) hide show
  1. package/dist/cjs/services/accountsGateway/index.d.ts +3 -0
  2. package/dist/cjs/services/accountsGateway/index.js +1 -0
  3. package/dist/cjs/services/documentStore/dynamoEncoding.js +2 -2
  4. package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +0 -1
  5. package/dist/cjs/services/documentStore/unversioned/file-system.js +0 -19
  6. package/dist/cjs/services/documentStore/versioned/file-system.d.ts +0 -1
  7. package/dist/cjs/services/documentStore/versioned/file-system.js +1 -12
  8. package/dist/cjs/services/lrsGateway/batching.d.ts +46 -0
  9. package/dist/cjs/services/lrsGateway/batching.js +106 -0
  10. package/dist/cjs/services/lrsGateway/file-system.js +52 -2
  11. package/dist/cjs/services/lrsGateway/index.d.ts +13 -0
  12. package/dist/cjs/services/lrsGateway/index.js +151 -55
  13. package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
  14. package/dist/esm/services/accountsGateway/index.d.ts +3 -0
  15. package/dist/esm/services/accountsGateway/index.js +1 -0
  16. package/dist/esm/services/documentStore/dynamoEncoding.js +2 -2
  17. package/dist/esm/services/documentStore/unversioned/file-system.d.ts +0 -1
  18. package/dist/esm/services/documentStore/unversioned/file-system.js +0 -19
  19. package/dist/esm/services/documentStore/versioned/file-system.d.ts +0 -1
  20. package/dist/esm/services/documentStore/versioned/file-system.js +1 -12
  21. package/dist/esm/services/lrsGateway/batching.d.ts +46 -0
  22. package/dist/esm/services/lrsGateway/batching.js +102 -0
  23. package/dist/esm/services/lrsGateway/file-system.js +52 -2
  24. package/dist/esm/services/lrsGateway/index.d.ts +13 -0
  25. package/dist/esm/services/lrsGateway/index.js +151 -22
  26. package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
  27. package/package.json +1 -1
  28. package/dist/cjs/services/documentStore/fileSystemAssert.d.ts +0 -1
  29. package/dist/cjs/services/documentStore/fileSystemAssert.js +0 -14
  30. package/dist/esm/services/documentStore/fileSystemAssert.d.ts +0 -1
  31. package/dist/esm/services/documentStore/fileSystemAssert.js +0 -10
@@ -13,6 +13,7 @@ interface Initializer<C> {
13
13
  }
14
14
  export type FindUserPayload = ({
15
15
  external_id: string;
16
+ role?: string;
16
17
  } | {
17
18
  uuid: string;
18
19
  }) & {
@@ -45,10 +46,12 @@ export type FindUserResponse = (FindOrCreateUserResponse & {
45
46
  export type LinkUserPayload = {
46
47
  userId: number;
47
48
  externalId: string;
49
+ role?: string;
48
50
  };
49
51
  export type LinkUserResponse = {
50
52
  user_id: number;
51
53
  external_id: string;
54
+ role?: string;
52
55
  };
53
56
  export type SearchUsersPayload = {
54
57
  q: string;
@@ -53,6 +53,7 @@ export const accountsGateway = (initializer) => (configProvider) => {
53
53
  body: {
54
54
  external_id: body.externalId,
55
55
  user_id: body.userId,
56
+ ...(body.role && { role: body.role }),
56
57
  }
57
58
  });
58
59
  const searchUsers = async (payload) => request(METHOD.GET, `users?${queryString.stringify(payload)}`, {});
@@ -12,7 +12,7 @@ export const encodeDynamoAttribute = (value) => {
12
12
  if (value === null) {
13
13
  return { NULL: true };
14
14
  }
15
- if (value instanceof Array) {
15
+ if (Array.isArray(value)) {
16
16
  return { L: value.map(encodeDynamoAttribute) };
17
17
  }
18
18
  if (isPlainObject(value)) {
@@ -20,7 +20,7 @@ export const encodeDynamoAttribute = (value) => {
20
20
  }
21
21
  throw new Error(`unknown attribute type ${typeof value} with value ${value}.`);
22
22
  };
23
- export const encodeDynamoDocument = (base) => Object.fromEntries(Object.entries(base).map(([key, value]) => ([key, encodeDynamoAttribute(value)])));
23
+ export const encodeDynamoDocument = (base) => Object.fromEntries(Object.entries(base).filter(([, value]) => value !== undefined).map(([key, value]) => ([key, encodeDynamoAttribute(value)])));
24
24
  export const decodeDynamoAttribute = (value) => {
25
25
  if (value.S !== undefined) {
26
26
  return value.S;
@@ -8,7 +8,6 @@ interface Initializer<C> {
8
8
  export declare const fileSystemUnversionedDocumentStore: <C extends string = "fileSystem">(initializer: Initializer<C>) => <T extends TDocument<T>>() => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }) => <K extends keyof T>(_: {}, hashKey: K, options?: {
9
9
  afterWrite?: (item: T) => void | Promise<void>;
10
10
  batchAfterWrite?: (items: T[]) => void | Promise<void>;
11
- skipAssert?: boolean;
12
11
  }) => {
13
12
  loadAllDocumentsTheBadWay: () => Promise<T[]>;
14
13
  getItemsByField: (key: keyof T, value: T[K], pageKey?: string) => Promise<{
@@ -4,13 +4,10 @@ import { hashValue } from '../../..';
4
4
  import { resolveConfigValue } from '../../../config';
5
5
  import { ConflictError, NotFoundError } from '../../../errors';
6
6
  import { ifDefined, isDefined } from '../../../guards';
7
- import { assertNoUndefined } from '../fileSystemAssert';
8
7
  export const fileSystemUnversionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey, options) => {
9
- var _a;
10
8
  const tableName = resolveConfigValue(configProvider[initializer.configSpace || 'fileSystem'].tableName);
11
9
  const tablePath = tableName.then((table) => path.join(initializer.dataDir, table));
12
10
  const { mkdir, readdir, readFile, writeFile } = ifDefined(initializer.fs, fsModule);
13
- const skipAssert = (_a = options === null || options === void 0 ? void 0 : options.skipAssert) !== null && _a !== void 0 ? _a : false;
14
11
  const mkTableDir = new Promise((resolve, reject) => tablePath.then((path) => mkdir(path, { recursive: true }, (err) => err && err.code !== 'EEXIST' ? reject(err) : resolve())));
15
12
  const hashFilename = (value) => `${hashValue(value)}.json`;
16
13
  const filePath = async (filename) => path.join(await tablePath, filename);
@@ -45,8 +42,6 @@ export const fileSystemUnversionedDocumentStore = (initializer) => () => (config
45
42
  return {
46
43
  loadAllDocumentsTheBadWay,
47
44
  getItemsByField: async (key, value, pageKey) => {
48
- if (!skipAssert)
49
- assertNoUndefined(value, [key]);
50
45
  const pageSize = 10;
51
46
  const items = await loadAllDocumentsTheBadWay();
52
47
  const filteredItems = items.filter((item) => item[key] === value);
@@ -59,20 +54,14 @@ export const fileSystemUnversionedDocumentStore = (initializer) => () => (config
59
54
  },
60
55
  batchGetItem: async (ids) => {
61
56
  const items = await Promise.all(ids.map((id) => {
62
- if (!skipAssert)
63
- assertNoUndefined(id, ['id']);
64
57
  return load(hashFilename(id));
65
58
  }));
66
59
  return items.filter(isDefined);
67
60
  },
68
61
  getItem: (id) => {
69
- if (!skipAssert)
70
- assertNoUndefined(id, ['id']);
71
62
  return load(hashFilename(id));
72
63
  },
73
64
  incrementItemAttribute: async (id, attribute) => {
74
- if (!skipAssert)
75
- assertNoUndefined(id, ['id']);
76
65
  const filename = hashFilename(id);
77
66
  const path = await filePath(filename);
78
67
  await mkTableDir;
@@ -103,15 +92,11 @@ export const fileSystemUnversionedDocumentStore = (initializer) => () => (config
103
92
  throw new NotFoundError(`Item with ${hashKey.toString()} "${id}" does not exist`);
104
93
  }
105
94
  const newItem = { ...data, ...item };
106
- if (!skipAssert)
107
- assertNoUndefined(newItem);
108
95
  return new Promise((resolve, reject) => {
109
96
  writeFile(path, JSON.stringify(newItem, null, 2), (err) => err ? reject(err) : resolve(newItem));
110
97
  });
111
98
  },
112
99
  putItem: async (item) => {
113
- if (!skipAssert)
114
- assertNoUndefined(item);
115
100
  const path = await filePath(hashFilename(item[hashKey]));
116
101
  await mkTableDir;
117
102
  return new Promise((resolve, reject) => {
@@ -119,8 +104,6 @@ export const fileSystemUnversionedDocumentStore = (initializer) => () => (config
119
104
  });
120
105
  },
121
106
  createItem: async (item) => {
122
- if (!skipAssert)
123
- assertNoUndefined(item);
124
107
  const hashed = hashFilename(item[hashKey]);
125
108
  const existingItem = await load(hashed);
126
109
  if (existingItem) {
@@ -141,8 +124,6 @@ export const fileSystemUnversionedDocumentStore = (initializer) => () => (config
141
124
  // Process items sequentially to ensure consistent conflict detection
142
125
  // Note: concurrency parameter is ignored for filesystem to avoid race conditions
143
126
  for (const item of items) {
144
- if (!skipAssert)
145
- assertNoUndefined(item);
146
127
  try {
147
128
  const hashed = hashFilename(item[hashKey]);
148
129
  const existingItem = await load(hashed);
@@ -8,7 +8,6 @@ interface Initializer<C> {
8
8
  }
9
9
  export declare const fileSystemVersionedDocumentStore: <C extends string = "fileSystem">(initializer: Initializer<C>) => <T extends VersionedTDocument<T>>() => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }) => <K extends keyof T, A extends undefined | ((...a: any[]) => Promise<VersionedDocumentAuthor>)>(_: {}, hashKey: K, options?: {
10
10
  getAuthor?: A;
11
- skipAssert?: boolean;
12
11
  }) => {
13
12
  loadAllDocumentsTheBadWay: () => Promise<T[]>;
14
13
  getVersions: (id: T[K], startVersion?: number) => Promise<{
@@ -1,10 +1,7 @@
1
- import { assertNoUndefined } from '../fileSystemAssert';
2
1
  import { fileSystemUnversionedDocumentStore } from '../unversioned/file-system';
3
2
  const PAGE_LIMIT = 5;
4
3
  export const fileSystemVersionedDocumentStore = (initializer) => () => (configProvider) => (_, hashKey, options) => {
5
- var _a;
6
- const skipAssert = (_a = options === null || options === void 0 ? void 0 : options.skipAssert) !== null && _a !== void 0 ? _a : false;
7
- const unversionedDocuments = fileSystemUnversionedDocumentStore(initializer)()(configProvider)({}, 'id', { skipAssert });
4
+ const unversionedDocuments = fileSystemUnversionedDocumentStore(initializer)()(configProvider)({}, 'id');
8
5
  return {
9
6
  loadAllDocumentsTheBadWay: () => {
10
7
  return unversionedDocuments.loadAllDocumentsTheBadWay().then(documents => documents.map(document => {
@@ -12,8 +9,6 @@ export const fileSystemVersionedDocumentStore = (initializer) => () => (configPr
12
9
  }));
13
10
  },
14
11
  getVersions: async (id, startVersion) => {
15
- if (!skipAssert)
16
- assertNoUndefined(id, ['id']);
17
12
  const item = await unversionedDocuments.getItem(id);
18
13
  const versions = item === null || item === void 0 ? void 0 : item.items.reverse();
19
14
  if (!versions) {
@@ -28,8 +23,6 @@ export const fileSystemVersionedDocumentStore = (initializer) => () => (configPr
28
23
  };
29
24
  },
30
25
  getItem: async (id, timestamp) => {
31
- if (!skipAssert)
32
- assertNoUndefined(id, ['id']);
33
26
  const item = await unversionedDocuments.getItem(id);
34
27
  if (timestamp) {
35
28
  return item === null || item === void 0 ? void 0 : item.items.find(version => version.timestamp === timestamp);
@@ -45,8 +38,6 @@ export const fileSystemVersionedDocumentStore = (initializer) => () => (configPr
45
38
  save: async (changes) => {
46
39
  var _a;
47
40
  const document = { ...item, ...changes, timestamp, author };
48
- if (!skipAssert)
49
- assertNoUndefined(document);
50
41
  const container = (_a = await unversionedDocuments.getItem(document[hashKey])) !== null && _a !== void 0 ? _a : { id: document[hashKey], items: [] };
51
42
  const updated = { ...container, items: [...container.items, document] };
52
43
  await unversionedDocuments.putItem(updated);
@@ -58,8 +49,6 @@ export const fileSystemVersionedDocumentStore = (initializer) => () => (configPr
58
49
  var _a;
59
50
  const author = (options === null || options === void 0 ? void 0 : options.getAuthor) ? await options.getAuthor(...authorArgs) : authorArgs[0];
60
51
  const document = { ...item, timestamp: new Date().getTime(), author };
61
- if (!skipAssert)
62
- assertNoUndefined(document);
63
52
  const container = (_a = await unversionedDocuments.getItem(document[hashKey])) !== null && _a !== void 0 ? _a : { id: document[hashKey], items: [] };
64
53
  const updated = { ...container, items: [...container.items, document] };
65
54
  await unversionedDocuments.putItem(updated);
@@ -0,0 +1,46 @@
1
+ import { GenericFetch, Response } from '../../fetch';
2
+ import { METHOD } from '../../routing';
3
+ export interface BatchRequest {
4
+ id: string;
5
+ path: string;
6
+ method: METHOD;
7
+ queryParams?: Record<string, string>;
8
+ headers?: Record<string, string>;
9
+ body?: string;
10
+ }
11
+ export interface BatchResponse {
12
+ id: string;
13
+ status: number;
14
+ headers: Record<string, string>;
15
+ body: string;
16
+ }
17
+ interface BatcherConfig {
18
+ batchEndpoint: string;
19
+ getAuthHeader: () => Promise<string>;
20
+ getHost: () => Promise<string>;
21
+ }
22
+ /**
23
+ * Handles automatic request batching using microtask queue scheduling.
24
+ * Batches concurrent requests within the same event loop tick.
25
+ */
26
+ export declare class RequestBatcher {
27
+ private fetcher;
28
+ private config;
29
+ private pendingRequests;
30
+ private flushScheduled;
31
+ private requestCounter;
32
+ constructor(fetcher: GenericFetch, config: BatcherConfig);
33
+ /**
34
+ * Queue a request for batching. Returns a promise that resolves with the response.
35
+ * Automatically flushes the batch on the next microtask.
36
+ */
37
+ queueRequest(options: Omit<BatchRequest, 'id'>): Promise<Response>;
38
+ singleRequest(options: Omit<BatchRequest, 'id'>): Promise<Response>;
39
+ /**
40
+ * Flush all pending requests. If only one request is pending, makes a direct HTTP call.
41
+ * Otherwise, sends a batch request to the batch API endpoint.
42
+ */
43
+ private flush;
44
+ private flushHandler;
45
+ }
46
+ export {};
@@ -0,0 +1,102 @@
1
+ import { METHOD } from '../../routing';
2
+ /**
3
+ * Handles automatic request batching using microtask queue scheduling.
4
+ * Batches concurrent requests within the same event loop tick.
5
+ */
6
+ export class RequestBatcher {
7
+ constructor(fetcher, config) {
8
+ this.fetcher = fetcher;
9
+ this.config = config;
10
+ this.pendingRequests = [];
11
+ this.flushScheduled = false;
12
+ this.requestCounter = 0;
13
+ }
14
+ /**
15
+ * Queue a request for batching. Returns a promise that resolves with the response.
16
+ * Automatically flushes the batch on the next microtask.
17
+ */
18
+ queueRequest(options) {
19
+ const id = `req_${++this.requestCounter}`;
20
+ return new Promise((resolve, reject) => {
21
+ this.pendingRequests.push({
22
+ ...options,
23
+ id, resolve, reject,
24
+ });
25
+ // Schedule microtask flush if not already scheduled
26
+ if (!this.flushScheduled) {
27
+ this.flushScheduled = true;
28
+ queueMicrotask(() => this.flush());
29
+ }
30
+ });
31
+ }
32
+ async singleRequest(options) {
33
+ const { path, method, queryParams, headers, body } = options;
34
+ const host = await this.config.getHost();
35
+ const query = new URLSearchParams(queryParams || {}).toString();
36
+ const url = host.replace(/\/+$/, '') + path + (query ? `?${query}` : '');
37
+ return await this.fetcher(url, { method, headers, body });
38
+ }
39
+ /**
40
+ * Flush all pending requests. If only one request is pending, makes a direct HTTP call.
41
+ * Otherwise, sends a batch request to the batch API endpoint.
42
+ */
43
+ flush() {
44
+ this.flushScheduled = false;
45
+ const requests = this.pendingRequests;
46
+ this.pendingRequests = [];
47
+ this.flushHandler(requests).catch(error => {
48
+ // If batch request fails entirely, reject all pending requests
49
+ requests.forEach(req => req.reject(error));
50
+ });
51
+ }
52
+ async flushHandler(requests) {
53
+ if (requests.length === 0) {
54
+ return;
55
+ }
56
+ // Single request optimization: bypass batching
57
+ if (requests.length === 1) {
58
+ return this.singleRequest(requests[0]).then(requests[0].resolve);
59
+ }
60
+ // Multiple requests: use batch API
61
+ const host = await this.config.getHost();
62
+ const authHeader = await this.config.getAuthHeader();
63
+ // Build batch request payload
64
+ const batchRequests = requests.map(({ id, path, method, queryParams, headers, body }) => {
65
+ return { id, path, method, queryParams, headers, body };
66
+ });
67
+ // Send batch request
68
+ const batchUrl = host.replace(/\/+$/, '') + this.config.batchEndpoint;
69
+ const batchResponse = await this.fetcher(batchUrl, {
70
+ method: METHOD.POST,
71
+ headers: {
72
+ Authorization: authHeader,
73
+ 'Content-Type': 'application/json',
74
+ 'X-Experience-API-Version': '1.0.0',
75
+ },
76
+ body: JSON.stringify({ requests: batchRequests }),
77
+ });
78
+ if (batchResponse.status !== 200) {
79
+ const errorText = await batchResponse.text();
80
+ // all requests will be rejected in the catch
81
+ throw new Error(`Batch request failed with status ${batchResponse.status}: ${errorText}`);
82
+ }
83
+ const batchResult = await batchResponse.json();
84
+ // Map responses back to original requests
85
+ const responseMap = new Map(batchResult.responses.map(r => [r.id, r]));
86
+ requests.forEach(req => {
87
+ const batchResp = responseMap.get(req.id);
88
+ if (!batchResp) {
89
+ req.reject(new Error(`No response for request ${req.id} in batch`));
90
+ return;
91
+ }
92
+ req.resolve({
93
+ status: batchResp.status,
94
+ headers: {
95
+ get: (name) => { var _a; return ((_a = Object.entries(batchResp.headers).find(([key]) => key.toLowerCase() === name.toLowerCase())) === null || _a === void 0 ? void 0 : _a[1]) || null; },
96
+ },
97
+ json: async () => JSON.parse(batchResp.body),
98
+ text: async () => batchResp.body,
99
+ });
100
+ });
101
+ }
102
+ }
@@ -6,14 +6,24 @@ import { assertDefined } from '../../assertions';
6
6
  import { resolveConfigValue } from '../../config';
7
7
  import { UnauthorizedError } from '../../errors';
8
8
  import { ifDefined } from '../../guards';
9
+ import { hashValue } from '../../misc/hashValue';
9
10
  const pageSize = 5;
10
11
  export const fileSystemLrsGateway = (initializer) => (configProvider) => ({ authProvider }) => {
11
12
  const name = resolveConfigValue(configProvider[initializer.configSpace || 'fileSystem'].name);
12
13
  const filePath = name.then((fileName) => path.join(initializer.dataDir, fileName));
13
14
  const { readFile, writeFile } = ifDefined(initializer.fs, fsModule);
14
15
  let data;
15
- const load = filePath.then(path => new Promise(resolve => {
16
- readFile(path, (err, readData) => {
16
+ let stateData;
17
+ /**
18
+ * Creates a composite key for state storage.
19
+ */
20
+ const makeStateKey = (activityId, agent, stateId, registration) => {
21
+ const agentName = typeof agent === 'string' ? agent : agent.account.name;
22
+ return hashValue({ activityId, agentName, stateId, registration });
23
+ };
24
+ const stateFilePath = name.then((fileName) => path.join(initializer.dataDir, `${fileName}-state`));
25
+ const load = filePath.then(filePath => new Promise(resolve => {
26
+ readFile(filePath, (err, readData) => {
17
27
  if (err) {
18
28
  console.error(err);
19
29
  }
@@ -31,7 +41,27 @@ export const fileSystemLrsGateway = (initializer) => (configProvider) => ({ auth
31
41
  resolve();
32
42
  });
33
43
  }));
44
+ const loadState = stateFilePath.then(stateFilePath => new Promise(resolve => {
45
+ readFile(stateFilePath, (err, readData) => {
46
+ if (err) {
47
+ // File not existing is expected on first run
48
+ }
49
+ else {
50
+ try {
51
+ const parsed = JSON.parse(readData.toString());
52
+ if (typeof parsed === 'object' && parsed !== null && !(parsed instanceof Array)) {
53
+ stateData = parsed;
54
+ }
55
+ }
56
+ catch (e) {
57
+ console.error(e);
58
+ }
59
+ }
60
+ resolve();
61
+ });
62
+ }));
34
63
  let previousSave;
64
+ let previousStateSave;
35
65
  const putXapiStatements = async (statements) => {
36
66
  const user = assertDefined(await authProvider.getUser(), new UnauthorizedError);
37
67
  const statementsWithDefaults = statements.map(statement => ({
@@ -101,10 +131,30 @@ export const fileSystemLrsGateway = (initializer) => (configProvider) => ({ auth
101
131
  statements: allResults.slice(0, pageSize)
102
132
  };
103
133
  };
134
+ const getState = async (activityId, agent, stateId, registration) => {
135
+ var _a;
136
+ await loadState;
137
+ const key = makeStateKey(activityId, agent, stateId, registration);
138
+ return (_a = stateData === null || stateData === void 0 ? void 0 : stateData[key]) !== null && _a !== void 0 ? _a : null;
139
+ };
140
+ const setState = async (activityId, agent, stateId, body, registration) => {
141
+ await loadState;
142
+ await previousStateSave;
143
+ const key = makeStateKey(activityId, agent, stateId, registration);
144
+ const path = await stateFilePath;
145
+ const save = previousStateSave = new Promise(resolve => {
146
+ stateData = stateData || {};
147
+ stateData[key] = body;
148
+ writeFile(path, JSON.stringify(stateData, null, 2), () => resolve());
149
+ });
150
+ await save;
151
+ };
104
152
  return {
105
153
  putXapiStatements,
106
154
  getAllXapiStatements,
107
155
  getXapiStatements,
108
156
  getMoreXapiStatements,
157
+ getState,
158
+ setState,
109
159
  };
110
160
  };
@@ -10,6 +10,17 @@ type Config = {
10
10
  interface Initializer<C> {
11
11
  configSpace?: C;
12
12
  fetch: GenericFetch;
13
+ enableBatching?: boolean;
14
+ }
15
+ export interface XapiAgent {
16
+ objectType: 'Agent';
17
+ account: {
18
+ homePage: string;
19
+ name: string;
20
+ };
21
+ }
22
+ export interface StateDocument {
23
+ [key: string]: any;
13
24
  }
14
25
  export interface XapiStatement {
15
26
  actor: {
@@ -118,5 +129,7 @@ export declare const lrsGateway: <C extends string = "lrs">(initializer: Initial
118
129
  }) & {
119
130
  fetchUntil?: (statements: XapiStatement[]) => boolean;
120
131
  }) => Promise<XapiStatement[]>;
132
+ getState: (activityId: string, agent: string | XapiAgent, stateId: string, registration?: string) => Promise<StateDocument | null>;
133
+ setState: (activityId: string, agent: string | XapiAgent, stateId: string, body: StateDocument, registration?: string) => Promise<void>;
121
134
  };
122
135
  export {};