epicenter-libs 3.11.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +345 -0
  2. package/LICENSE.md +37 -0
  3. package/README.md +134 -0
  4. package/dist/browser/AckExtension-e67c6a28.js +129 -0
  5. package/dist/browser/AckExtension-e67c6a28.js.map +1 -0
  6. package/dist/browser/ReloadExtension-b1e50033.js +253 -0
  7. package/dist/browser/ReloadExtension-b1e50033.js.map +1 -0
  8. package/dist/browser/cometd-eeabdcd4.js +3438 -0
  9. package/dist/browser/cometd-eeabdcd4.js.map +1 -0
  10. package/dist/browser/epicenter-2cce2971.js +6086 -0
  11. package/dist/browser/epicenter-2cce2971.js.map +1 -0
  12. package/dist/browser/epicenter.js +2 -0
  13. package/dist/browser/epicenter.js.map +1 -0
  14. package/dist/cjs/AckExtension-f5178e19.js +131 -0
  15. package/dist/cjs/AckExtension-f5178e19.js.map +1 -0
  16. package/dist/cjs/ReloadExtension-65b036ba.js +255 -0
  17. package/dist/cjs/ReloadExtension-65b036ba.js.map +1 -0
  18. package/dist/cjs/cometd-473408f4.js +3441 -0
  19. package/dist/cjs/cometd-473408f4.js.map +1 -0
  20. package/dist/cjs/epicenter-12ceb814.js +7248 -0
  21. package/dist/cjs/epicenter-12ceb814.js.map +1 -0
  22. package/dist/cjs/epicenter.js +54 -0
  23. package/dist/cjs/epicenter.js.map +1 -0
  24. package/dist/epicenter.js +9895 -0
  25. package/dist/epicenter.js.map +1 -0
  26. package/dist/epicenter.min.js +2 -0
  27. package/dist/epicenter.min.js.map +1 -0
  28. package/dist/module/AckExtension-6181d8b5.js +129 -0
  29. package/dist/module/AckExtension-6181d8b5.js.map +1 -0
  30. package/dist/module/ReloadExtension-eaa8c42c.js +253 -0
  31. package/dist/module/ReloadExtension-eaa8c42c.js.map +1 -0
  32. package/dist/module/cometd-af78008d.js +3438 -0
  33. package/dist/module/cometd-af78008d.js.map +1 -0
  34. package/dist/module/epicenter-9b8c92a9.js +7213 -0
  35. package/dist/module/epicenter-9b8c92a9.js.map +1 -0
  36. package/dist/module/epicenter.js +7 -0
  37. package/dist/module/epicenter.js.map +1 -0
  38. package/dist/types/adapters/account.d.ts +44 -0
  39. package/dist/types/adapters/admin.d.ts +33 -0
  40. package/dist/types/adapters/asset.d.ts +46 -0
  41. package/dist/types/adapters/authentication.d.ts +62 -0
  42. package/dist/types/adapters/channel.d.ts +39 -0
  43. package/dist/types/adapters/chat.d.ts +105 -0
  44. package/dist/types/adapters/cometd.d.ts +25 -0
  45. package/dist/types/adapters/email.d.ts +86 -0
  46. package/dist/types/adapters/episode.d.ts +91 -0
  47. package/dist/types/adapters/group.d.ts +273 -0
  48. package/dist/types/adapters/index.d.ts +21 -0
  49. package/dist/types/adapters/leaderboard.d.ts +68 -0
  50. package/dist/types/adapters/presence.d.ts +35 -0
  51. package/dist/types/adapters/project.d.ts +99 -0
  52. package/dist/types/adapters/recaptcha.d.ts +1 -0
  53. package/dist/types/adapters/run.d.ts +253 -0
  54. package/dist/types/adapters/task.d.ts +154 -0
  55. package/dist/types/adapters/time.d.ts +2 -0
  56. package/dist/types/adapters/user.d.ts +38 -0
  57. package/dist/types/adapters/vault.d.ts +94 -0
  58. package/dist/types/adapters/world.d.ts +230 -0
  59. package/dist/types/epicenter.d.ts +10 -0
  60. package/dist/types/utils/config.d.ts +90 -0
  61. package/dist/types/utils/constants.d.ts +290 -0
  62. package/dist/types/utils/cookies.d.ts +16 -0
  63. package/dist/types/utils/error-manager.d.ts +21 -0
  64. package/dist/types/utils/error.d.ts +4 -0
  65. package/dist/types/utils/fault.d.ts +17 -0
  66. package/dist/types/utils/helpers.d.ts +4 -0
  67. package/dist/types/utils/identification.d.ts +47 -0
  68. package/dist/types/utils/index.d.ts +11 -0
  69. package/dist/types/utils/result.d.ts +6 -0
  70. package/dist/types/utils/router.d.ts +157 -0
  71. package/dist/types/utils/store.d.ts +31 -0
  72. package/package.json +103 -0
  73. package/src/adapters/account.ts +104 -0
  74. package/src/adapters/admin.ts +53 -0
  75. package/src/adapters/asset.ts +195 -0
  76. package/src/adapters/authentication.ts +173 -0
  77. package/src/adapters/channel.ts +83 -0
  78. package/src/adapters/chat.ts +186 -0
  79. package/src/adapters/cometd.ts +297 -0
  80. package/src/adapters/email.ts +146 -0
  81. package/src/adapters/episode.ts +163 -0
  82. package/src/adapters/group.ts +511 -0
  83. package/src/adapters/index.ts +43 -0
  84. package/src/adapters/leaderboard.ts +122 -0
  85. package/src/adapters/presence.ts +63 -0
  86. package/src/adapters/project.ts +123 -0
  87. package/src/adapters/recaptcha.ts +11 -0
  88. package/src/adapters/run.ts +726 -0
  89. package/src/adapters/task.ts +213 -0
  90. package/src/adapters/time.ts +36 -0
  91. package/src/adapters/user.ts +75 -0
  92. package/src/adapters/vault.ts +232 -0
  93. package/src/adapters/world.ts +412 -0
  94. package/src/epicenter.ts +96 -0
  95. package/src/globals.d.ts +16 -0
  96. package/src/utils/config.ts +168 -0
  97. package/src/utils/constants.ts +324 -0
  98. package/src/utils/cookies.ts +71 -0
  99. package/src/utils/error-manager.ts +66 -0
  100. package/src/utils/error.ts +9 -0
  101. package/src/utils/fault.ts +39 -0
  102. package/src/utils/helpers.ts +7 -0
  103. package/src/utils/identification.ts +128 -0
  104. package/src/utils/index.ts +11 -0
  105. package/src/utils/result.ts +15 -0
  106. package/src/utils/router.ts +547 -0
  107. package/src/utils/store.ts +82 -0
@@ -0,0 +1,195 @@
1
+ import type { RoutingOptions } from '../utils/router';
2
+ import type { GenericScope } from '../utils/constants';
3
+
4
+ import fetch from 'cross-fetch';
5
+ import { Fault, Router, ROLE } from '../utils';
6
+
7
+ interface AssetScope extends GenericScope {
8
+ userKey?: string,
9
+ }
10
+
11
+ interface Asset {
12
+ file: string,
13
+ address: {
14
+ projectShortName: string,
15
+ groupName: string,
16
+ accountShortName: string,
17
+ worldKey: string,
18
+ episodeName: string,
19
+ },
20
+ scope: AssetScope,
21
+ }
22
+
23
+ interface AssetTicket {
24
+ url: string,
25
+ }
26
+
27
+
28
+ export async function create(
29
+ file: string,
30
+ scope: AssetScope,
31
+ optionals: {
32
+ readLock?: keyof typeof ROLE,
33
+ writeLock?: keyof typeof ROLE,
34
+ ttlSeconds?: number,
35
+ } & RoutingOptions = {}
36
+ ): Promise<AssetTicket> {
37
+ const { scopeBoundary, scopeKey, userKey } = scope;
38
+ const {
39
+ readLock, writeLock, ttlSeconds,
40
+ ...routingOptions
41
+ } = optionals;
42
+ return await new Router()
43
+ .post('/asset', {
44
+ body: {
45
+ file,
46
+ scope: {
47
+ scopeBoundary,
48
+ scopeKey,
49
+ userKey,
50
+ },
51
+ permit: {
52
+ readLock: readLock ?? ROLE.USER,
53
+ writeLock: writeLock ?? ROLE.USER,
54
+ },
55
+ ttlSeconds,
56
+ },
57
+ ...routingOptions,
58
+ }).then(({ body }) => body);
59
+ }
60
+
61
+ export async function update(
62
+ file: string,
63
+ scope: AssetScope,
64
+ optionals: {
65
+ readLock?: keyof typeof ROLE,
66
+ writeLock?: keyof typeof ROLE,
67
+ ttlSeconds?: number,
68
+ } & RoutingOptions = {}
69
+ ): Promise<AssetTicket> {
70
+ const { scopeBoundary, scopeKey, userKey } = scope;
71
+ const {
72
+ readLock, writeLock, ttlSeconds,
73
+ ...routingOptions
74
+ } = optionals;
75
+ return await new Router()
76
+ .patch('/asset', {
77
+ body: {
78
+ file,
79
+ scope: {
80
+ scopeBoundary,
81
+ scopeKey,
82
+ userKey,
83
+ },
84
+ permit: {
85
+ readLock: readLock ?? ROLE.USER,
86
+ writeLock: writeLock ?? ROLE.USER,
87
+ },
88
+ ttlSeconds,
89
+ },
90
+ ...routingOptions,
91
+ }).then(({ body }) => body);
92
+ }
93
+
94
+ export async function remove(
95
+ assetKey: string,
96
+ optionals: RoutingOptions = {}
97
+ ): Promise<void> {
98
+ return await new Router()
99
+ .delete(`/asset/${assetKey}`, optionals)
100
+ .then(({ body }) => body);
101
+ }
102
+
103
+ export async function removeFromScope(
104
+ scope: AssetScope,
105
+ optionals: RoutingOptions = {}
106
+ ): Promise<void> {
107
+ const { scopeBoundary, scopeKey, userKey } = scope;
108
+ const uriComponent = userKey ? `/${userKey}` : '';
109
+ return await new Router()
110
+ .delete(`/asset/in/${scopeBoundary}/${scopeKey}${uriComponent}`, optionals)
111
+ .then(({ body }) => body);
112
+ }
113
+
114
+ export async function get(
115
+ assetKey: string,
116
+ optionals: RoutingOptions = {}
117
+ ): Promise<Asset> {
118
+ const { server, accountShortName, projectShortName } = optionals;
119
+ return await new Router()
120
+ .withServer(server)
121
+ .withAccountShortName(accountShortName)
122
+ .withProjectShortName(projectShortName)
123
+ .get(`/asset/${assetKey}`)
124
+ .then(({ body }) => body);
125
+ }
126
+
127
+ export async function list(
128
+ scope: AssetScope,
129
+ optionals: {
130
+ filter?: string,
131
+ } & RoutingOptions = {}
132
+ ): Promise<Asset[]> {
133
+ const { scopeBoundary, scopeKey, userKey } = scope;
134
+ const {
135
+ filter,
136
+ ...routingOptions
137
+ } = optionals;
138
+ const uriComponent = userKey ? `/${userKey}` : '';
139
+ return await new Router()
140
+ .get(`/asset/in/${scopeBoundary}/${scopeKey}${uriComponent}/${filter ?? '*'}`, routingOptions)
141
+ .then(({ body }) => body);
142
+ }
143
+
144
+ export async function getURL(
145
+ assetKey: string,
146
+ optionals: RoutingOptions = {}
147
+ ): Promise<string> {
148
+ return await new Router()
149
+ .get(`/asset/url/${assetKey}`, optionals)
150
+ .then(({ body }) => body);
151
+ }
152
+
153
+ export async function getURLWithScope(
154
+ file: string,
155
+ scope: AssetScope,
156
+ optionals: RoutingOptions = {}
157
+ ): Promise<string> {
158
+ const { scopeBoundary, scopeKey, userKey } = scope;
159
+ const uriComponent = userKey ? `/${userKey}` : '';
160
+ return await new Router()
161
+ .get(`/asset/url/with/${scopeBoundary}/${scopeKey}${uriComponent}/${file}`, optionals)
162
+ .then(({ body }) => body);
163
+ }
164
+
165
+ const CONFLICT = 409;
166
+ export async function store(
167
+ file: File,
168
+ scope: AssetScope,
169
+ optionals: {
170
+ readLock?: keyof typeof ROLE,
171
+ writeLock?: keyof typeof ROLE,
172
+ ttlSeconds?: number,
173
+ overwrite?: boolean,
174
+ fileName?: string,
175
+ } & RoutingOptions = {}
176
+ ): Promise<void> {
177
+ const { overwrite, fileName, ...remaining } = optionals;
178
+ const name = fileName ?? file.name;
179
+ let presignedUrl = '';
180
+ try {
181
+ const response = await create(name, scope, remaining);
182
+ presignedUrl = response.url;
183
+ } catch (error) {
184
+ if (error instanceof Fault) {
185
+ const shouldUpdate = error.status === CONFLICT && overwrite;
186
+ if (!shouldUpdate) throw error;
187
+ const response = await update(name, scope, remaining);
188
+ presignedUrl = response.url;
189
+ } else {
190
+ throw error;
191
+ }
192
+ }
193
+ await fetch(presignedUrl, { method: 'PUT', body: file });
194
+ return;
195
+ }
@@ -0,0 +1,173 @@
1
+ import type { RoutingOptions } from '../utils/router';
2
+ import type { Session } from '../utils/identification';
3
+
4
+ import { Router, identification } from '../utils';
5
+ import cometdAdapter from './cometd';
6
+
7
+
8
+ interface UserCredentials {
9
+ handle: string,
10
+ password: string,
11
+ groupKey?: string,
12
+ }
13
+ interface AppCredentials {
14
+ secretKey: string,
15
+ }
16
+
17
+ /**
18
+ * Run API adapters -- use this to create, update, delete, and manage your runs
19
+ * @namespace authAdapter
20
+ */
21
+
22
+ /**
23
+ * Logs out of current Epicenter session.
24
+ * @example
25
+ * epicenter.authAdapter.logout()
26
+ * @returns promise resolving to successful logout
27
+ */
28
+ export async function logout(): Promise<void> {
29
+ identification.session = undefined;
30
+ await cometdAdapter.disconnect();
31
+ }
32
+
33
+ export async function login(
34
+ credentials: UserCredentials | AppCredentials,
35
+ optionals: { objectType?: string } & RoutingOptions = {}
36
+ ): Promise<Session> {
37
+ const { objectType, ...routingOptions } = optionals;
38
+ let payload;
39
+ if (Object.prototype.hasOwnProperty.call(credentials, 'handle')) {
40
+ const { handle, password, groupKey } = credentials as UserCredentials;
41
+ payload = { objectType: objectType ?? 'user', handle, password, groupKey: groupKey || undefined };
42
+ }
43
+ if (Object.prototype.hasOwnProperty.call(credentials, 'secretKey')) {
44
+ const { secretKey } = credentials as AppCredentials;
45
+ payload = { objectType: objectType ?? 'account', secretKey };
46
+ }
47
+ const session = await new Router()
48
+ .post('/authentication', {
49
+ inert: true,
50
+ includeAuthorization: false,
51
+ body: payload,
52
+ ...routingOptions,
53
+ }).then(({ body }) => body);
54
+ await logout();
55
+ identification.session = session;
56
+ return session;
57
+ }
58
+
59
+ /**
60
+ * Regenerates your epicenter session with the appropriate context. Allows users to update their session to the correct group, and admins to update their session with the correct account name. Will fail if the user/admin does not already belong to the group/account.
61
+ * @example
62
+ * // Changes the current user session to have a group context associated with the provided key
63
+ * epicenter.authAdapter.regenerate('00000165ad4e6a3cd22b993340b963820239');
64
+ * // Changes the current admin session to use the account context for the 'acme' account.
65
+ * epicenter.authAdapter.regenerate('acme', { objectType: 'admin' });
66
+ * @param groupOrAccount Group key or account name
67
+ * @param [optionals] Optional parameters
68
+ * @param [optionals.objectType] The object type to regenerate for. Uses objectType: 'user' by default.
69
+ * @returns promise resolving to the new session object
70
+ */
71
+ export async function regenerate(
72
+ groupOrAccount: string,
73
+ optionals: {
74
+ objectType?: string,
75
+ } & RoutingOptions = {}
76
+ ): Promise<Session> {
77
+ const {
78
+ objectType = 'user',
79
+ accountShortName,
80
+ ...routingOptions
81
+ } = optionals;
82
+
83
+ const session = await new Router()
84
+ .patch('/authentication', {
85
+ accountShortName: objectType === 'admin' ?
86
+ groupOrAccount :
87
+ accountShortName,
88
+ body: {
89
+ objectType,
90
+ groupKey: objectType === 'user' ?
91
+ groupOrAccount :
92
+ undefined,
93
+ },
94
+ ...routingOptions,
95
+ }).then(({ body }) => body);
96
+
97
+ await logout();
98
+ identification.session = session;
99
+ return session;
100
+ }
101
+
102
+ export async function sso(
103
+ optionals: RoutingOptions = {},
104
+ ): Promise<Session> {
105
+ const session = await new Router()
106
+ .get('/registration/sso', optionals)
107
+ .then(({ body }) => body);
108
+
109
+ identification.session = session;
110
+ return session;
111
+ }
112
+
113
+ export async function getSession(): Promise<Session> {
114
+ const { body } = await new Router().get('/authentication');
115
+ identification.session = body;
116
+ return body;
117
+ }
118
+
119
+ export function getLocalSession(): Session | undefined {
120
+ return identification.session;
121
+ }
122
+
123
+ export function setLocalSession(session: Session): Session {
124
+ return identification.session = session;
125
+ }
126
+
127
+ /**
128
+ * Sends a link to reset a user's password to their email
129
+ * @example
130
+ * const subject = 'Please reset your password for the Acme simulation';
131
+ * const url = 'https://forio.com/app/acme/simulations';
132
+ * const handle = 'testUser@test.com'
133
+ * epicenter.authAdapter.resetPassword(handle, { redirectURL, subject });
134
+ * @param handle Handle that user would use to login
135
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
136
+ * @param [optionals.redirectURL] Url to redirect to after password reset is completed. Must be in the forio domain otherwise an error will be thrown
137
+ * @param [optionals.subject] The subject of the email that will be sent
138
+ * @returns promise that resolves to undefined (indicating success)
139
+ */
140
+ export async function resetPassword(
141
+ handle: string,
142
+ optionals: {
143
+ redirectURL?: string,
144
+ subject?: string,
145
+ } & RoutingOptions = {}
146
+ ): Promise<void> {
147
+ const {
148
+ redirectURL, subject,
149
+ ...routingOptions
150
+ } = optionals;
151
+
152
+ return await new Router()
153
+ .post(`/authentication/password/user/${handle}`, {
154
+ ...routingOptions,
155
+ body: {
156
+ redirectUrl: redirectURL,
157
+ subject,
158
+ },
159
+ })
160
+ .then(({ body }) => body);
161
+ }
162
+
163
+ export async function verify(
164
+ token: string,
165
+ optionals: RoutingOptions = {},
166
+ ): Promise<Session> {
167
+ return await new Router()
168
+ .get('/verification', {
169
+ authorization: `Bearer ${token}`,
170
+ ...optionals,
171
+ })
172
+ .then(({ body }) => body);
173
+ }
@@ -0,0 +1,83 @@
1
+ import type { Callback, SubscriptionHandle, Message } from 'cometd';
2
+ import type { GenericScope } from '../utils/constants';
3
+
4
+ import { EpicenterError, SCOPE_BOUNDARY, PUSH_CATEGORY } from '../utils';
5
+ import cometdAdapter from './cometd';
6
+
7
+ interface ChannelScope extends GenericScope {
8
+ pushCategory: string,
9
+ }
10
+
11
+ const validateScope = (scope: ChannelScope) => {
12
+ if (!scope) throw new EpicenterError('No scope found where one was required');
13
+ const { scopeBoundary, scopeKey, pushCategory } = scope;
14
+ if (!scopeBoundary) throw new EpicenterError('Missing scope component: scopeBoundary');
15
+ if (!scopeKey) throw new EpicenterError('Missing scope component: scopeKey');
16
+ if (!pushCategory) throw new EpicenterError('Missing scope component: pushCategory');
17
+ if (!Object.prototype.hasOwnProperty.call(SCOPE_BOUNDARY, scopeBoundary)) throw new EpicenterError(`Invalid scope boundary: ${scopeBoundary}`);
18
+ if (!Object.prototype.hasOwnProperty.call(PUSH_CATEGORY, pushCategory)) throw new EpicenterError(`Invalid push category: ${pushCategory}`);
19
+ };
20
+
21
+ /**
22
+ * Used to subscribe to CometD channels. Pass in a channel scope to instantiate, if a subscription to that scope already exists it will use it.
23
+ * */
24
+ export default class Channel {
25
+
26
+ path: string;
27
+ update: Callback | undefined;
28
+ subscription: SubscriptionHandle | null = null;
29
+
30
+ /**
31
+ * Channel constructor
32
+ * @param scope object with the scope boundary, scope key, and push category. Defines the namespace for the channel
33
+ */
34
+ constructor(scope: ChannelScope) {
35
+ const { scopeBoundary, scopeKey, pushCategory } = scope;
36
+ validateScope(scope);
37
+ this.path = `/${scopeBoundary.toLowerCase()}/${scopeKey}/${pushCategory.toLowerCase()}`;
38
+ if (cometdAdapter.subscriptions.has(this.path)) {
39
+ this.subscription = cometdAdapter.subscriptions.get(this.path);
40
+ }
41
+ }
42
+
43
+ publish(content: FIXME): Promise<Message | Message[]> {
44
+ return cometdAdapter.publish(this, content);
45
+ }
46
+
47
+ /**
48
+ * Subscribes to the CometD channel, attaching a handler for any channel updates. If a subscription already exists it will first unsubscribe, ensuring that only one subscription is ever attached to the channel.
49
+ * @example
50
+ * import { Channel, authAdapter, SCOPE_BOUNDARY, PUSH_CATEGORY } from 'epicenter';
51
+ * const session = authAdapter.getLocalSession();
52
+ * const channel = new Channel({
53
+ * scopeBoundary: SCOPE_BOUNDARY.GROUP,
54
+ * scopeKey: session.groupKey,
55
+ * pushCategory: PUSH_CATEGORY.CHAT,
56
+ * }).subscribe((data) => {
57
+ * console.log(data.content);
58
+ * })
59
+ * @param update function that is called whenever a channel update occurs.
60
+ * @returns the subscription object returned by CometD after a sucessful subscribe.
61
+ */
62
+ async subscribe(
63
+ update: (data: unknown) => unknown,
64
+ options: { inert?: boolean } = {},
65
+ ): Promise<SubscriptionHandle> {
66
+ if (this.subscription) await this.unsubscribe();
67
+ this.update = update;
68
+ return cometdAdapter.add(this, update, options).then((subscription) => {
69
+ if (Array.isArray(subscription)) subscription = subscription[0];
70
+ this.subscription = subscription;
71
+ return subscription;
72
+ });
73
+ }
74
+
75
+ async unsubscribe(): Promise<void> {
76
+ if (this.subscription) {
77
+ await cometdAdapter.remove(this.subscription);
78
+ this.subscription = null;
79
+ }
80
+ }
81
+ }
82
+
83
+
@@ -0,0 +1,186 @@
1
+ import type { GenericScope, Permit } from 'utils/constants';
2
+ import type { RoutingOptions, Page, GenericSearchOptions } from 'utils/router';
3
+ import Router from 'utils/router';
4
+
5
+ interface ChatMessage {
6
+ senderKey: string,
7
+ receiverKey: string,
8
+ created: string,
9
+ id: string,
10
+ message: string,
11
+ }
12
+
13
+ interface Chat {
14
+ permit: Permit,
15
+ chatKey: string,
16
+ messages: ChatMessage[],
17
+ room: string,
18
+ scope: GenericScope,
19
+ }
20
+
21
+ /**
22
+ * Updates the permissions of a chat
23
+ * @param chatKey Key associated with the chat
24
+ * @param permit Permit object with the updated permissions
25
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
26
+ * @returns the newly updated chat
27
+ */
28
+ export async function updatePermit(
29
+ chatKey: string,
30
+ permit: Permit,
31
+ optionals: RoutingOptions = {}
32
+ ): Promise<Chat> {
33
+ return new Router()
34
+ .patch(`/chat/${chatKey}`, {
35
+ ...optionals,
36
+ body: { permit },
37
+ }).then(({ body }) => body);
38
+ }
39
+
40
+ /**
41
+ * Creates a chat
42
+ * @example
43
+ * import { chatAdapter, SCOPE_BOUNDARY, ROLE } from 'epicenter';
44
+ * chatAdapter.create(
45
+ * 'my-chat-room',
46
+ * { scopeBoundary: SCOPE_BOUNDARY.GROUP, scopeKey: '00000165ad4e6a3cd22b993340b963820239' },
47
+ * { readLock: ROLE.PARTICIPANT, writeLock: ROLE.PARTICIPANT }
48
+ * );
49
+ * @param room Name of the chat
50
+ * @param scope Scope of the chat; will not accept user scope
51
+ * @param permit Permissions for the chat
52
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
53
+ * @returns The chat created
54
+ */
55
+ export async function create(
56
+ room: string,
57
+ scope: GenericScope,
58
+ permit: Permit,
59
+ optionals: RoutingOptions = {}
60
+ ): Promise<Chat> {
61
+ return new Router()
62
+ .post('/chat', {
63
+ body: {
64
+ scope: {
65
+ scopeBoundary: scope.scopeBoundary,
66
+ scopeKey: scope.scopeKey,
67
+ },
68
+ permit,
69
+ room,
70
+ },
71
+ ...optionals,
72
+ }).then(({ body }) => body);
73
+ }
74
+
75
+
76
+ /**
77
+ * Gets a chat
78
+ * @example
79
+ * epicenter.chatAdapter.get('00000165ad4e6a3cd22b993340b963820239');
80
+ * @param chatKey Key of the associated chat
81
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
82
+ * @returns The chat corresponding with the provided key
83
+ */
84
+ export async function get(
85
+ chatKey: string,
86
+ optionals: RoutingOptions = {}
87
+ ): Promise<Chat> {
88
+ return new Router()
89
+ .get(`/chat/${chatKey}`, optionals)
90
+ .then(({ body }) => body);
91
+ }
92
+
93
+
94
+ /**
95
+ * Open search for chats, returns a page
96
+ * @example
97
+ * epicenter.chatAdapter.query({
98
+ * filter: [
99
+ * 'room|=my-chat-room|my-other-chat|room-three', // looks for any rooms with the names provided
100
+ * 'scopeBoundary=GROUP', // keeps the search within the group scope
101
+ * // 'scopeKey=00000165ad4e6a3cd22b993340b963820239', // used in conjunction with the scopeBoundary
102
+ * // 'chatKey=0000017dd3bf540e5ada5b1e058f08f20461', // searches for a specific chat
103
+ * // 'accountShortName=acme', // specifies the account, typically unnecessary
104
+ * // 'projectShortName=simulations', // specifies the project, typically unnecessary
105
+ * // 'groupName=my-group-name', // search based on group name
106
+ * // 'episodeName=my-episode-name', // search based on episode name
107
+ * 'created>=2022-01-03T20:30:53.054Z', // looks for any chats created after Jan 3rd 2022
108
+ * ],
109
+ * sort: ['+chat.created'], // sort all findings by the 'created' field (ascending)
110
+ * first: 3, // page should start with the 4th item found (defaults to 0)
111
+ * max: 10, // page should only include the first 10 items
112
+ * });
113
+ * @param searchOptions Search options -- for more on Epicenter search options go [here](NOOP link)
114
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
115
+ * @returns A page for the list of chats found
116
+ */
117
+ export async function query(
118
+ searchOptions: GenericSearchOptions,
119
+ optionals: RoutingOptions = {}
120
+ ): Promise<Page<Chat>> {
121
+ const { filter = [], sort = [], first = 0, max } = searchOptions;
122
+ const searchParams = {
123
+ filter: filter.join(';') || undefined,
124
+ sort: sort.join(';') || undefined,
125
+ first, max,
126
+ };
127
+ return await new Router()
128
+ .withSearchParams(searchParams)
129
+ .get('/chat', {
130
+ paginated: true,
131
+ ...optionals,
132
+ })
133
+ .then(({ body }) => body);
134
+ }
135
+
136
+ /**
137
+ * Sends a message to a chat
138
+ * @example
139
+ * epicenter.chatAdapter.sendMessage('0000017dd3bf540e5ada5b1e058f08f20461', 'hello');
140
+ * epicenter.chatAdapter.sendMessage('0000017dd3bf540e5ada5b1e058f08f20461', 'hello, privately', { userKey: '000001796733eef0842f4d6d960997018a33' });
141
+ * @param chatKey Key associated with the chat
142
+ * @param message Message text to send
143
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
144
+ * @param [optionals.userKey] Key of the user to send the message to. If omitted, will send as a public message
145
+ * @returns The chat message created
146
+ */
147
+ export async function sendMessage(
148
+ chatKey: string,
149
+ message: string,
150
+ optionals: { userKey?: string } & RoutingOptions = {}
151
+ ): Promise<ChatMessage> {
152
+ const { userKey, ...routingOptions } = optionals;
153
+ const uriComponent = userKey ? `/${userKey}` : '';
154
+ return new Router()
155
+ .put(`/chat/message/${chatKey}${uriComponent}`, {
156
+ body: { message },
157
+ ...routingOptions,
158
+ }).then(({ body }) => body);
159
+ }
160
+
161
+ /**
162
+ * Retrieves messages from for a given chat
163
+ * @example
164
+ * // gets the chat message with id: 5
165
+ * epicenter.chatAdapter.getMessages('0000017dd3bf540e5ada5b1e058f08f20461', { horizon: 5, maxRecords: 1 });
166
+ * // gets the 10 chat messages starting from id 5 (inclusive)
167
+ * epicenter.chatAdapter.getMessages('0000017dd3bf540e5ada5b1e058f08f20461', { horizon: 5, maxRecords: 10 });
168
+ * @param chatKey Key associated with the chat
169
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
170
+ * @param [optionals.maxRecords] Maximum number of messages to get
171
+ * @param [optionals.horizon] The message ID from which to start with; works backwards so if `maxRecords=20` and `horizon=50`, it will get the 20 messages starting from message ID 50, working backwards (50, 49, 48..., etc.). If this value is omitted the platform assumes it is the most recent message in the chat
172
+ * @returns The list chat messages requested
173
+ */
174
+ export async function getMessages(
175
+ chatKey: string,
176
+ optionals: {
177
+ maxRecords?: number,
178
+ horizon?: number,
179
+ } & RoutingOptions = {}
180
+ ): Promise<ChatMessage[]> {
181
+ const { maxRecords, horizon, ...routingOptions } = optionals;
182
+ return new Router()
183
+ .withSearchParams({ maxRecords, horizon })
184
+ .get(`/chat/message/${chatKey}`, routingOptions)
185
+ .then(({ body }) => body);
186
+ }