@flowerforce/flowerbase-client 0.1.1-beta.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/CHANGELOG.md +0 -0
- package/LICENSE +3 -0
- package/README.md +209 -0
- package/dist/app.d.ts +85 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +461 -0
- package/dist/bson.d.ts +8 -0
- package/dist/bson.d.ts.map +1 -0
- package/dist/bson.js +10 -0
- package/dist/credentials.d.ts +8 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +30 -0
- package/dist/functions.d.ts +6 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +47 -0
- package/dist/http.d.ts +35 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +170 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/mongo.d.ts +4 -0
- package/dist/mongo.d.ts.map +1 -0
- package/dist/mongo.js +106 -0
- package/dist/session.d.ts +18 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +105 -0
- package/dist/session.native.d.ts +14 -0
- package/dist/session.native.d.ts.map +1 -0
- package/dist/session.native.js +76 -0
- package/dist/types.d.ts +97 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/user.d.ts +37 -0
- package/dist/user.d.ts.map +1 -0
- package/dist/user.js +125 -0
- package/dist/watch.d.ts +3 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +139 -0
- package/jest.config.ts +13 -0
- package/package.json +41 -0
- package/project.json +11 -0
- package/rollup.config.js +17 -0
- package/src/__tests__/auth.test.ts +213 -0
- package/src/__tests__/compat.test.ts +22 -0
- package/src/__tests__/functions.test.ts +312 -0
- package/src/__tests__/mongo.test.ts +83 -0
- package/src/__tests__/session.test.ts +597 -0
- package/src/__tests__/watch.test.ts +336 -0
- package/src/app.ts +562 -0
- package/src/bson.ts +6 -0
- package/src/credentials.ts +31 -0
- package/src/functions.ts +56 -0
- package/src/http.ts +221 -0
- package/src/index.ts +15 -0
- package/src/mongo.ts +112 -0
- package/src/session.native.ts +89 -0
- package/src/session.ts +114 -0
- package/src/types.ts +114 -0
- package/src/user.ts +150 -0
- package/src/watch.ts +156 -0
- package/tsconfig.json +34 -0
- package/tsconfig.spec.json +13 -0
package/dist/app.js
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.App = void 0;
|
|
4
|
+
const functions_1 = require("./functions");
|
|
5
|
+
const http_1 = require("./http");
|
|
6
|
+
const session_1 = require("./session");
|
|
7
|
+
const credentials_1 = require("./credentials");
|
|
8
|
+
const user_1 = require("./user");
|
|
9
|
+
const API_PREFIX = '/api/client/v2.0';
|
|
10
|
+
class App {
|
|
11
|
+
constructor(idOrConfig) {
|
|
12
|
+
this.usersById = new Map();
|
|
13
|
+
this.sessionsByUserId = new Map();
|
|
14
|
+
this.usersOrder = [];
|
|
15
|
+
this.profilesByUserId = new Map();
|
|
16
|
+
this.listeners = new Set();
|
|
17
|
+
const config = typeof idOrConfig === 'string' ? { id: idOrConfig } : idOrConfig;
|
|
18
|
+
this.id = config.id;
|
|
19
|
+
this.baseUrl = (config.baseUrl ?? '').replace(/\/$/, '');
|
|
20
|
+
this.timeout = config.timeout ?? 10000;
|
|
21
|
+
this.sessionManager = new session_1.SessionManager(this.id);
|
|
22
|
+
const persistedSessionsByUser = this.sessionManager.getSessionsByUser();
|
|
23
|
+
for (const [userId, session] of Object.entries(persistedSessionsByUser)) {
|
|
24
|
+
this.sessionsByUserId.set(userId, session);
|
|
25
|
+
}
|
|
26
|
+
this.usersOrder = this.sessionManager.getUsersOrder();
|
|
27
|
+
for (const userId of this.sessionsByUserId.keys()) {
|
|
28
|
+
if (!this.usersOrder.includes(userId)) {
|
|
29
|
+
this.usersOrder.push(userId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
for (const userId of this.usersOrder) {
|
|
33
|
+
this.getOrCreateUser(userId);
|
|
34
|
+
}
|
|
35
|
+
const currentSession = this.sessionManager.get();
|
|
36
|
+
if (currentSession?.userId) {
|
|
37
|
+
this.sessionsByUserId.set(currentSession.userId, currentSession);
|
|
38
|
+
this.getOrCreateUser(currentSession.userId);
|
|
39
|
+
this.touchUser(currentSession.userId);
|
|
40
|
+
this.persistSessionsByUser();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.setCurrentSessionFromOrder();
|
|
44
|
+
}
|
|
45
|
+
this.sessionBootstrapPromise = this.bootstrapSessionOnLoad();
|
|
46
|
+
this.emailPasswordAuth = {
|
|
47
|
+
registerUser: ({ email, password }) => this.postProvider('/local-userpass/register', { email, password }),
|
|
48
|
+
confirmUser: ({ token, tokenId }) => this.postProvider('/local-userpass/confirm', { token, tokenId }),
|
|
49
|
+
resendConfirmationEmail: ({ email }) => this.postProvider('/local-userpass/confirm/send', { email }),
|
|
50
|
+
retryCustomConfirmation: ({ email }) => this.postProvider('/local-userpass/confirm/call', { email }),
|
|
51
|
+
sendResetPasswordEmail: (input) => this.postProvider('/local-userpass/reset/send', {
|
|
52
|
+
email: typeof input === 'string' ? input : input.email
|
|
53
|
+
}),
|
|
54
|
+
callResetPasswordFunction: (input, passwordOrArg, ...args) => {
|
|
55
|
+
if (typeof input === 'string') {
|
|
56
|
+
return this.postProvider('/local-userpass/reset/call', {
|
|
57
|
+
email: input,
|
|
58
|
+
password: passwordOrArg,
|
|
59
|
+
arguments: args
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return this.postProvider('/local-userpass/reset/call', {
|
|
63
|
+
email: input.email,
|
|
64
|
+
password: input.password,
|
|
65
|
+
arguments: [passwordOrArg, ...args].filter((value) => value !== undefined)
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
resetPassword: ({ token, tokenId, password }) => this.postProvider('/local-userpass/reset', { token, tokenId, password })
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
static getApp(appIdOrConfig) {
|
|
72
|
+
const appId = typeof appIdOrConfig === 'string' ? appIdOrConfig : appIdOrConfig.id;
|
|
73
|
+
if (appId in App.appCache) {
|
|
74
|
+
return App.appCache[appId];
|
|
75
|
+
}
|
|
76
|
+
const app = new App(appIdOrConfig);
|
|
77
|
+
App.appCache[appId] = app;
|
|
78
|
+
return app;
|
|
79
|
+
}
|
|
80
|
+
get currentUser() {
|
|
81
|
+
for (const userId of this.usersOrder) {
|
|
82
|
+
const user = this.usersById.get(userId);
|
|
83
|
+
if (user?.state === 'active') {
|
|
84
|
+
return user;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
get allUsers() {
|
|
90
|
+
const activeUsers = [];
|
|
91
|
+
const loggedOutUsers = [];
|
|
92
|
+
for (const userId of this.usersOrder) {
|
|
93
|
+
const user = this.usersById.get(userId);
|
|
94
|
+
if (!user)
|
|
95
|
+
continue;
|
|
96
|
+
if (user.state === 'active') {
|
|
97
|
+
activeUsers.push(userId);
|
|
98
|
+
}
|
|
99
|
+
else if (user.state === 'logged-out') {
|
|
100
|
+
loggedOutUsers.push(userId);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const users = Object.fromEntries([...activeUsers, ...loggedOutUsers].map((userId) => [userId, this.usersById.get(userId)]));
|
|
104
|
+
return users;
|
|
105
|
+
}
|
|
106
|
+
persistSessionsByUser() {
|
|
107
|
+
this.sessionManager.setSessionsByUser(Object.fromEntries(this.sessionsByUserId.entries()));
|
|
108
|
+
}
|
|
109
|
+
persistUsersOrder() {
|
|
110
|
+
this.sessionManager.setUsersOrder(this.usersOrder);
|
|
111
|
+
}
|
|
112
|
+
touchUser(userId) {
|
|
113
|
+
this.usersOrder = [userId, ...this.usersOrder.filter((id) => id !== userId)];
|
|
114
|
+
this.persistUsersOrder();
|
|
115
|
+
}
|
|
116
|
+
removeUserFromOrder(userId) {
|
|
117
|
+
this.usersOrder = this.usersOrder.filter((id) => id !== userId);
|
|
118
|
+
this.persistUsersOrder();
|
|
119
|
+
}
|
|
120
|
+
setSessionForUser(session) {
|
|
121
|
+
this.sessionsByUserId.set(session.userId, session);
|
|
122
|
+
this.sessionManager.set(session);
|
|
123
|
+
this.persistSessionsByUser();
|
|
124
|
+
}
|
|
125
|
+
clearSessionForUser(userId) {
|
|
126
|
+
this.sessionsByUserId.delete(userId);
|
|
127
|
+
this.persistSessionsByUser();
|
|
128
|
+
}
|
|
129
|
+
setCurrentSessionFromOrder() {
|
|
130
|
+
for (const userId of this.usersOrder) {
|
|
131
|
+
const session = this.sessionsByUserId.get(userId);
|
|
132
|
+
if (session) {
|
|
133
|
+
this.sessionManager.set(session);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
this.sessionManager.clear();
|
|
138
|
+
}
|
|
139
|
+
notifyListeners(userId) {
|
|
140
|
+
for (const callback of Array.from(this.listeners)) {
|
|
141
|
+
try {
|
|
142
|
+
callback();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Listener failures should not break auth/session lifecycle.
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (userId) {
|
|
149
|
+
this.usersById.get(userId)?.notifyListeners();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
providerUrl(path) {
|
|
153
|
+
return `${this.baseUrl}${API_PREFIX}/app/${this.id}/auth/providers${path}`;
|
|
154
|
+
}
|
|
155
|
+
authUrl(path) {
|
|
156
|
+
return `${this.baseUrl}${API_PREFIX}/auth${path}`;
|
|
157
|
+
}
|
|
158
|
+
functionsUrl(path = '/call') {
|
|
159
|
+
return `${this.baseUrl}${API_PREFIX}/app/${this.id}/functions${path}`;
|
|
160
|
+
}
|
|
161
|
+
async createSession(refreshToken) {
|
|
162
|
+
return (0, http_1.requestJson)({
|
|
163
|
+
url: this.authUrl('/session'),
|
|
164
|
+
method: 'POST',
|
|
165
|
+
bearerToken: refreshToken,
|
|
166
|
+
timeout: this.timeout
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async bootstrapSessionOnLoad() {
|
|
170
|
+
const session = this.sessionManager.get();
|
|
171
|
+
if (!session || typeof localStorage === 'undefined') {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const result = await this.createSession(session.refreshToken);
|
|
176
|
+
this.setSessionForUser({
|
|
177
|
+
...session,
|
|
178
|
+
accessToken: result.access_token
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
this.clearSessionForUser(session.userId);
|
|
183
|
+
this.setCurrentSessionFromOrder();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async ensureSessionBootstrapped() {
|
|
187
|
+
await this.sessionBootstrapPromise;
|
|
188
|
+
}
|
|
189
|
+
async setLoggedInUser(data, providerType, profileEmail) {
|
|
190
|
+
const sessionResult = await this.createSession(data.refresh_token);
|
|
191
|
+
const session = {
|
|
192
|
+
accessToken: sessionResult.access_token,
|
|
193
|
+
refreshToken: data.refresh_token,
|
|
194
|
+
userId: data.user_id
|
|
195
|
+
};
|
|
196
|
+
this.setSessionForUser(session);
|
|
197
|
+
const user = this.getOrCreateUser(data.user_id);
|
|
198
|
+
user.setProviderType(providerType);
|
|
199
|
+
this.touchUser(data.user_id);
|
|
200
|
+
if (profileEmail) {
|
|
201
|
+
user.profile = { email: profileEmail };
|
|
202
|
+
}
|
|
203
|
+
this.notifyListeners(data.user_id);
|
|
204
|
+
return user;
|
|
205
|
+
}
|
|
206
|
+
getOrCreateUser(userId) {
|
|
207
|
+
const existing = this.usersById.get(userId);
|
|
208
|
+
if (existing) {
|
|
209
|
+
return existing;
|
|
210
|
+
}
|
|
211
|
+
const user = new user_1.User(this, userId);
|
|
212
|
+
this.usersById.set(userId, user);
|
|
213
|
+
return user;
|
|
214
|
+
}
|
|
215
|
+
async logIn(credentials) {
|
|
216
|
+
if (credentials.provider === 'local-userpass') {
|
|
217
|
+
const result = await this.postProvider('/local-userpass/login', {
|
|
218
|
+
username: credentials.email,
|
|
219
|
+
password: credentials.password
|
|
220
|
+
});
|
|
221
|
+
return this.setLoggedInUser(result, 'local-userpass', credentials.email);
|
|
222
|
+
}
|
|
223
|
+
if (credentials.provider === 'anon-user') {
|
|
224
|
+
const result = await this.postProvider('/anon-user/login', {});
|
|
225
|
+
return this.setLoggedInUser(result, 'anon-user');
|
|
226
|
+
}
|
|
227
|
+
if (credentials.provider === 'custom-function') {
|
|
228
|
+
const result = await this.postProvider('/custom-function/login', credentials.payload);
|
|
229
|
+
return this.setLoggedInUser(result, 'custom-function');
|
|
230
|
+
}
|
|
231
|
+
if (credentials.provider === 'custom-token') {
|
|
232
|
+
const result = await this.postProvider('/custom-token/login', { token: credentials.token });
|
|
233
|
+
return this.setLoggedInUser(result, 'custom-token');
|
|
234
|
+
}
|
|
235
|
+
const unsupportedProvider = credentials;
|
|
236
|
+
throw new Error(`Unsupported credentials provider: ${JSON.stringify(unsupportedProvider)}`);
|
|
237
|
+
}
|
|
238
|
+
switchUser(nextUser) {
|
|
239
|
+
const knownUser = this.usersById.get(nextUser.id);
|
|
240
|
+
if (!knownUser) {
|
|
241
|
+
throw new Error('The user was never logged into this app');
|
|
242
|
+
}
|
|
243
|
+
this.touchUser(nextUser.id);
|
|
244
|
+
const session = this.sessionsByUserId.get(nextUser.id);
|
|
245
|
+
if (session) {
|
|
246
|
+
this.sessionManager.set(session);
|
|
247
|
+
}
|
|
248
|
+
this.notifyListeners(nextUser.id);
|
|
249
|
+
}
|
|
250
|
+
async removeUser(user) {
|
|
251
|
+
const knownUser = this.usersById.get(user.id);
|
|
252
|
+
if (!knownUser) {
|
|
253
|
+
throw new Error('The user was never logged into this app');
|
|
254
|
+
}
|
|
255
|
+
if (this.sessionsByUserId.has(user.id)) {
|
|
256
|
+
await this.logoutUser(user.id);
|
|
257
|
+
}
|
|
258
|
+
this.usersById.delete(user.id);
|
|
259
|
+
this.removeUserFromOrder(user.id);
|
|
260
|
+
this.profilesByUserId.delete(user.id);
|
|
261
|
+
this.clearSessionForUser(user.id);
|
|
262
|
+
this.setCurrentSessionFromOrder();
|
|
263
|
+
this.notifyListeners(user.id);
|
|
264
|
+
}
|
|
265
|
+
async deleteUser(user) {
|
|
266
|
+
await this.requestWithAccessToken((accessToken) => (0, http_1.requestJson)({
|
|
267
|
+
url: this.authUrl('/delete'),
|
|
268
|
+
method: 'DELETE',
|
|
269
|
+
bearerToken: accessToken,
|
|
270
|
+
timeout: this.timeout
|
|
271
|
+
}), user.id);
|
|
272
|
+
await this.removeUser(user);
|
|
273
|
+
}
|
|
274
|
+
getSessionOrThrow(userId) {
|
|
275
|
+
const targetUserId = userId ?? this.currentUser?.id;
|
|
276
|
+
const session = targetUserId ? this.sessionsByUserId.get(targetUserId) : this.sessionManager.get();
|
|
277
|
+
if (!session) {
|
|
278
|
+
throw new Error('User is not authenticated');
|
|
279
|
+
}
|
|
280
|
+
return session;
|
|
281
|
+
}
|
|
282
|
+
getSession(userId) {
|
|
283
|
+
if (userId) {
|
|
284
|
+
return this.sessionsByUserId.get(userId) ?? null;
|
|
285
|
+
}
|
|
286
|
+
return this.sessionManager.get();
|
|
287
|
+
}
|
|
288
|
+
hasUser(userId) {
|
|
289
|
+
return this.usersById.has(userId);
|
|
290
|
+
}
|
|
291
|
+
getProfileSnapshot(userId) {
|
|
292
|
+
return this.profilesByUserId.get(userId);
|
|
293
|
+
}
|
|
294
|
+
async postProvider(path, body) {
|
|
295
|
+
return (0, http_1.requestJson)({
|
|
296
|
+
url: this.providerUrl(path),
|
|
297
|
+
method: 'POST',
|
|
298
|
+
body,
|
|
299
|
+
timeout: this.timeout
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async requestWithAccessToken(operation, userId) {
|
|
303
|
+
const firstSession = this.getSessionOrThrow(userId);
|
|
304
|
+
try {
|
|
305
|
+
return await operation(firstSession.accessToken);
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
if (!(error instanceof http_1.FlowerbaseHttpError) || error.status !== 401) {
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
await this.refreshAccessToken(userId);
|
|
312
|
+
const refreshedSession = this.getSessionOrThrow(userId);
|
|
313
|
+
return operation(refreshedSession.accessToken);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async callFunction(name, args, userId) {
|
|
317
|
+
await this.ensureSessionBootstrapped();
|
|
318
|
+
const payload = {
|
|
319
|
+
name,
|
|
320
|
+
arguments: args
|
|
321
|
+
};
|
|
322
|
+
const result = await this.requestWithAccessToken((accessToken) => (0, http_1.requestJson)({
|
|
323
|
+
url: this.functionsUrl('/call'),
|
|
324
|
+
method: 'POST',
|
|
325
|
+
body: payload,
|
|
326
|
+
bearerToken: accessToken,
|
|
327
|
+
timeout: this.timeout
|
|
328
|
+
}), userId);
|
|
329
|
+
return (0, functions_1.normalizeFunctionResponse)(result);
|
|
330
|
+
}
|
|
331
|
+
async callFunctionStreaming(name, args, userId) {
|
|
332
|
+
await this.ensureSessionBootstrapped();
|
|
333
|
+
const payload = {
|
|
334
|
+
name,
|
|
335
|
+
arguments: args
|
|
336
|
+
};
|
|
337
|
+
const resolveSession = () => this.getSessionOrThrow(userId);
|
|
338
|
+
const refreshSession = () => this.refreshAccessToken(userId);
|
|
339
|
+
const timeout = this.timeout;
|
|
340
|
+
const url = this.functionsUrl('/call');
|
|
341
|
+
return {
|
|
342
|
+
async *[Symbol.asyncIterator]() {
|
|
343
|
+
let didRefresh = false;
|
|
344
|
+
while (true) {
|
|
345
|
+
const session = resolveSession();
|
|
346
|
+
let stream;
|
|
347
|
+
try {
|
|
348
|
+
stream = await (0, http_1.requestStream)({
|
|
349
|
+
url,
|
|
350
|
+
method: 'POST',
|
|
351
|
+
body: payload,
|
|
352
|
+
bearerToken: session.accessToken,
|
|
353
|
+
timeout
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
if (!didRefresh && error instanceof http_1.FlowerbaseHttpError && error.status === 401) {
|
|
358
|
+
await refreshSession();
|
|
359
|
+
didRefresh = true;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
for await (const chunk of stream) {
|
|
366
|
+
yield chunk;
|
|
367
|
+
}
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
if (!didRefresh && error instanceof http_1.FlowerbaseHttpError && error.status === 401) {
|
|
372
|
+
await refreshSession();
|
|
373
|
+
didRefresh = true;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
async callService(name, args, service = 'mongodb-atlas', userId) {
|
|
383
|
+
await this.ensureSessionBootstrapped();
|
|
384
|
+
const payload = {
|
|
385
|
+
name,
|
|
386
|
+
service,
|
|
387
|
+
arguments: args
|
|
388
|
+
};
|
|
389
|
+
return this.requestWithAccessToken((accessToken) => (0, http_1.requestJson)({
|
|
390
|
+
url: this.functionsUrl('/call'),
|
|
391
|
+
method: 'POST',
|
|
392
|
+
body: payload,
|
|
393
|
+
bearerToken: accessToken,
|
|
394
|
+
timeout: this.timeout
|
|
395
|
+
}), userId);
|
|
396
|
+
}
|
|
397
|
+
async getProfile(userId) {
|
|
398
|
+
await this.ensureSessionBootstrapped();
|
|
399
|
+
const profile = await this.requestWithAccessToken((accessToken) => (0, http_1.requestJson)({
|
|
400
|
+
url: this.authUrl('/profile'),
|
|
401
|
+
method: 'GET',
|
|
402
|
+
bearerToken: accessToken,
|
|
403
|
+
timeout: this.timeout
|
|
404
|
+
}), userId);
|
|
405
|
+
const session = this.getSessionOrThrow(userId);
|
|
406
|
+
this.profilesByUserId.set(session.userId, profile);
|
|
407
|
+
return profile;
|
|
408
|
+
}
|
|
409
|
+
async refreshAccessToken(userId) {
|
|
410
|
+
await this.ensureSessionBootstrapped();
|
|
411
|
+
const session = this.getSessionOrThrow(userId);
|
|
412
|
+
try {
|
|
413
|
+
const result = await this.createSession(session.refreshToken);
|
|
414
|
+
this.setSessionForUser({
|
|
415
|
+
...session,
|
|
416
|
+
accessToken: result.access_token
|
|
417
|
+
});
|
|
418
|
+
this.touchUser(session.userId);
|
|
419
|
+
this.notifyListeners(session.userId);
|
|
420
|
+
return result.access_token;
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
this.clearSessionForUser(session.userId);
|
|
424
|
+
this.setCurrentSessionFromOrder();
|
|
425
|
+
this.notifyListeners(session.userId);
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
async logoutUser(userId) {
|
|
430
|
+
const session = this.getSession(userId ?? this.currentUser?.id);
|
|
431
|
+
try {
|
|
432
|
+
if (session) {
|
|
433
|
+
await (0, http_1.requestJson)({
|
|
434
|
+
url: this.authUrl('/session'),
|
|
435
|
+
method: 'DELETE',
|
|
436
|
+
bearerToken: session.refreshToken,
|
|
437
|
+
timeout: this.timeout
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
finally {
|
|
442
|
+
if (session) {
|
|
443
|
+
this.clearSessionForUser(session.userId);
|
|
444
|
+
this.notifyListeners(session.userId);
|
|
445
|
+
}
|
|
446
|
+
this.setCurrentSessionFromOrder();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
addListener(callback) {
|
|
450
|
+
this.listeners.add(callback);
|
|
451
|
+
}
|
|
452
|
+
removeListener(callback) {
|
|
453
|
+
this.listeners.delete(callback);
|
|
454
|
+
}
|
|
455
|
+
removeAllListeners() {
|
|
456
|
+
this.listeners.clear();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
exports.App = App;
|
|
460
|
+
App.appCache = {};
|
|
461
|
+
App.Credentials = credentials_1.Credentials;
|
package/dist/bson.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EJSON, ObjectId, BSON as RawBSON } from 'bson';
|
|
2
|
+
declare const ObjectID: typeof ObjectId;
|
|
3
|
+
declare const BSON: typeof RawBSON & {
|
|
4
|
+
ObjectId: typeof ObjectId;
|
|
5
|
+
ObjectID: typeof ObjectId;
|
|
6
|
+
};
|
|
7
|
+
export { BSON, EJSON, ObjectId, ObjectID };
|
|
8
|
+
//# sourceMappingURL=bson.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bson.d.ts","sourceRoot":"","sources":["../src/bson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,MAAM,CAAA;AAEvD,QAAA,MAAM,QAAQ,iBAAW,CAAA;AACzB,QAAA,MAAM,IAAI;;;CAAqD,CAAA;AAE/D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA"}
|
package/dist/bson.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectID = exports.ObjectId = exports.EJSON = exports.BSON = void 0;
|
|
4
|
+
const bson_1 = require("bson");
|
|
5
|
+
Object.defineProperty(exports, "EJSON", { enumerable: true, get: function () { return bson_1.EJSON; } });
|
|
6
|
+
Object.defineProperty(exports, "ObjectId", { enumerable: true, get: function () { return bson_1.ObjectId; } });
|
|
7
|
+
const ObjectID = bson_1.ObjectId;
|
|
8
|
+
exports.ObjectID = ObjectID;
|
|
9
|
+
const BSON = Object.assign({}, bson_1.BSON, { ObjectId: bson_1.ObjectId, ObjectID });
|
|
10
|
+
exports.BSON = BSON;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CredentialsLike } from './types';
|
|
2
|
+
export declare class Credentials {
|
|
3
|
+
static emailPassword(email: string, password: string): CredentialsLike;
|
|
4
|
+
static anonymous(): CredentialsLike;
|
|
5
|
+
static function(payload: Record<string, unknown>): CredentialsLike;
|
|
6
|
+
static jwt(token: string): CredentialsLike;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAEzC,qBAAa,WAAW;IACtB,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe;IAQtE,MAAM,CAAC,SAAS,IAAI,eAAe;IAMnC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAOlE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe;CAM3C"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Credentials = void 0;
|
|
4
|
+
class Credentials {
|
|
5
|
+
static emailPassword(email, password) {
|
|
6
|
+
return {
|
|
7
|
+
provider: 'local-userpass',
|
|
8
|
+
email,
|
|
9
|
+
password
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
static anonymous() {
|
|
13
|
+
return {
|
|
14
|
+
provider: 'anon-user'
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
static function(payload) {
|
|
18
|
+
return {
|
|
19
|
+
provider: 'custom-function',
|
|
20
|
+
payload
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
static jwt(token) {
|
|
24
|
+
return {
|
|
25
|
+
provider: 'custom-token',
|
|
26
|
+
token
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.Credentials = Credentials;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const normalizeFunctionResponse: (value: unknown) => any;
|
|
2
|
+
export declare const createFunctionsProxy: (callFunction: (name: string, args: unknown[]) => Promise<unknown>, callFunctionStreaming: (name: string, args: unknown[]) => Promise<AsyncIterable<Uint8Array>>) => Record<string, (...args: unknown[]) => Promise<unknown>> & {
|
|
3
|
+
callFunction: (name: string, ...args: unknown[]) => Promise<unknown>;
|
|
4
|
+
callFunctionStreaming: (name: string, ...args: unknown[]) => Promise<AsyncIterable<Uint8Array>>;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=functions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../src/functions.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,yBAAyB,UAAW,OAAO,QAWvD,CAAA;AAED,eAAO,MAAM,oBAAoB,iBACjB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,OAAO,CAAC,yBAC1C,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,cAAc,UAAU,CAAC,CAAC,KAC3F,OAAO,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,OAAO,CAAC,CAAC,GAAG;IAC5D,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,OAAO,CAAC,CAAA;IACpE,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,cAAc,UAAU,CAAC,CAAC,CAAA;CAoB9F,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFunctionsProxy = exports.normalizeFunctionResponse = void 0;
|
|
4
|
+
const bson_1 = require("./bson");
|
|
5
|
+
const RESERVED_PROXY_KEYS = new Set([
|
|
6
|
+
'toJSON',
|
|
7
|
+
'then',
|
|
8
|
+
'catch',
|
|
9
|
+
'finally',
|
|
10
|
+
'constructor',
|
|
11
|
+
'__proto__',
|
|
12
|
+
'prototype'
|
|
13
|
+
]);
|
|
14
|
+
const deserialize = (value) => {
|
|
15
|
+
if (!value || typeof value !== 'object')
|
|
16
|
+
return value;
|
|
17
|
+
return bson_1.EJSON.deserialize(value);
|
|
18
|
+
};
|
|
19
|
+
const normalizeFunctionResponse = (value) => {
|
|
20
|
+
if (typeof value === 'string') {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(value);
|
|
23
|
+
return deserialize(parsed);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return deserialize(value);
|
|
30
|
+
};
|
|
31
|
+
exports.normalizeFunctionResponse = normalizeFunctionResponse;
|
|
32
|
+
const createFunctionsProxy = (callFunction, callFunctionStreaming) => new Proxy({}, {
|
|
33
|
+
get: (_, key) => {
|
|
34
|
+
if (typeof key !== 'string')
|
|
35
|
+
return undefined;
|
|
36
|
+
if (RESERVED_PROXY_KEYS.has(key))
|
|
37
|
+
return undefined;
|
|
38
|
+
if (key === 'callFunction') {
|
|
39
|
+
return (name, ...args) => callFunction(name, args);
|
|
40
|
+
}
|
|
41
|
+
if (key === 'callFunctionStreaming') {
|
|
42
|
+
return (name, ...args) => callFunctionStreaming(name, args);
|
|
43
|
+
}
|
|
44
|
+
return (...args) => callFunction(key, args);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
exports.createFunctionsProxy = createFunctionsProxy;
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare class MongoDBRealmError extends Error {
|
|
2
|
+
readonly method: string;
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly statusCode: number;
|
|
5
|
+
readonly statusText: string;
|
|
6
|
+
readonly error: string | undefined;
|
|
7
|
+
readonly errorCode: string | undefined;
|
|
8
|
+
readonly link: string | undefined;
|
|
9
|
+
readonly payload?: unknown;
|
|
10
|
+
constructor(params: {
|
|
11
|
+
method: string;
|
|
12
|
+
url: string;
|
|
13
|
+
statusCode: number;
|
|
14
|
+
statusText: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
errorCode?: string;
|
|
17
|
+
link?: string;
|
|
18
|
+
payload?: unknown;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export declare class FlowerbaseHttpError extends MongoDBRealmError {
|
|
22
|
+
readonly status: number;
|
|
23
|
+
constructor(params: ConstructorParameters<typeof MongoDBRealmError>[0]);
|
|
24
|
+
}
|
|
25
|
+
type RequestParams = {
|
|
26
|
+
url: string;
|
|
27
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
28
|
+
body?: unknown;
|
|
29
|
+
bearerToken?: string;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
};
|
|
32
|
+
export declare const requestJson: <T = unknown>({ url, method, body, bearerToken, timeout }: RequestParams) => Promise<T>;
|
|
33
|
+
export declare const requestStream: ({ url, method, body, bearerToken, timeout }: RequestParams) => Promise<AsyncIterable<Uint8Array>>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAmDA,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;gBAEd,MAAM,EAAE;QAClB,MAAM,EAAE,MAAM,CAAA;QACd,GAAG,EAAE,MAAM,CAAA;QACX,UAAU,EAAE,MAAM,CAAA;QAClB,UAAU,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,OAAO,CAAA;KAClB;CAYF;AAED,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAEX,MAAM,EAAE,qBAAqB,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC;CAKvE;AAED,KAAK,aAAa,GAAG;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;IACpD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAsBD,eAAO,MAAM,WAAW,6DAMrB,aAAa,KAAG,QAAQ,CAAC,CAkC3B,CAAA;AAED,eAAO,MAAM,aAAa,gDAMvB,aAAa,KAAG,QAAQ,cAAc,UAAU,CAAC,CAkDnD,CAAA"}
|