@flowerforce/flowerbase-client 0.1.1-beta.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.
- package/CHANGELOG.md +0 -0
- package/LICENSE +3 -0
- package/README.md +198 -0
- package/dist/app.d.ts +40 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +186 -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 +7 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +24 -0
- package/dist/functions.d.ts +3 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +30 -0
- package/dist/http.d.ts +15 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +74 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/mongo.d.ts +4 -0
- package/dist/mongo.d.ts.map +1 -0
- package/dist/mongo.js +61 -0
- package/dist/session.d.ts +12 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +53 -0
- package/dist/session.native.d.ts +14 -0
- package/dist/session.native.d.ts.map +1 -0
- package/dist/session.native.js +81 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/user.d.ts +17 -0
- package/dist/user.d.ts.map +1 -0
- package/dist/user.js +30 -0
- package/dist/watch.d.ts +3 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +138 -0
- package/jest.config.ts +13 -0
- package/package.json +30 -0
- package/project.json +11 -0
- package/rollup.config.js +17 -0
- package/src/__tests__/auth.test.ts +164 -0
- package/src/__tests__/compat.test.ts +12 -0
- package/src/__tests__/functions.test.ts +76 -0
- package/src/__tests__/mongo.test.ts +48 -0
- package/src/__tests__/session.test.ts +103 -0
- package/src/__tests__/watch.test.ts +138 -0
- package/src/app.ts +235 -0
- package/src/bson.ts +6 -0
- package/src/credentials.ts +24 -0
- package/src/functions.ts +32 -0
- package/src/http.ts +92 -0
- package/src/index.ts +14 -0
- package/src/mongo.ts +63 -0
- package/src/session.native.ts +98 -0
- package/src/session.ts +59 -0
- package/src/types.ts +84 -0
- package/src/user.ts +39 -0
- package/src/watch.ts +150 -0
- package/tsconfig.json +34 -0
- package/tsconfig.spec.json +13 -0
package/CHANGELOG.md
ADDED
|
File without changes
|
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# @flowerforce/flowerbase-client
|
|
2
|
+
|
|
3
|
+
Client TypeScript leggero per usare Flowerbase con API in stile Realm:
|
|
4
|
+
|
|
5
|
+
- autenticazione (`local-userpass`, `anon-user`, `custom-function`)
|
|
6
|
+
- chiamate funzioni (`user.functions.<name>(...)`)
|
|
7
|
+
- accesso a MongoDB Atlas service (`user.mongoClient("mongodb-atlas")`)
|
|
8
|
+
- change stream via `watch()` con async iterator
|
|
9
|
+
- supporto BSON/EJSON (`ObjectId`, `Date`, ecc.)
|
|
10
|
+
|
|
11
|
+
## Installazione
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i @flowerforce/flowerbase-client
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { App, Credentials } from '@flowerforce/flowerbase-client'
|
|
21
|
+
|
|
22
|
+
const app = new App({
|
|
23
|
+
id: 'my-app-id',
|
|
24
|
+
baseUrl: 'http://localhost:8000',
|
|
25
|
+
timeout: 10000
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
await app.logIn(Credentials.emailPassword('user@example.com', 'secret'))
|
|
29
|
+
|
|
30
|
+
const user = app.currentUser
|
|
31
|
+
if (!user) throw new Error('User not logged in')
|
|
32
|
+
|
|
33
|
+
const result = await user.functions.myFunction('hello')
|
|
34
|
+
console.log(result)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configurazione `App`
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
new App({
|
|
41
|
+
id: string, // app id Flowerbase
|
|
42
|
+
baseUrl: string, // URL base backend (es: http://localhost:8000)
|
|
43
|
+
timeout?: number // default 10000
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Autenticazione
|
|
48
|
+
|
|
49
|
+
### Local user/pass
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
await app.logIn(Credentials.emailPassword(email, password))
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Anonymous
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
await app.logIn(Credentials.anonymous())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Custom function auth
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
await app.logIn(
|
|
65
|
+
Credentials.function({
|
|
66
|
+
username: 'demo',
|
|
67
|
+
pin: '1234'
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Utility `emailPasswordAuth`
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
await app.emailPasswordAuth.registerUser({ email, password })
|
|
76
|
+
await app.emailPasswordAuth.sendResetPasswordEmail(email)
|
|
77
|
+
await app.emailPasswordAuth.callResetPasswordFunction(email, newPassword, extraArg1, extraArg2)
|
|
78
|
+
await app.emailPasswordAuth.resetPassword({ token, tokenId, password })
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Current user
|
|
82
|
+
|
|
83
|
+
Dopo il login:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const user = app.currentUser
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Interfaccia principale:
|
|
90
|
+
|
|
91
|
+
- `user.id`
|
|
92
|
+
- `user.functions.<functionName>(...args)`
|
|
93
|
+
- `user.mongoClient('mongodb-atlas')`
|
|
94
|
+
- `user.refreshAccessToken()`
|
|
95
|
+
- `user.refreshCustomData()`
|
|
96
|
+
- `user.logOut()`
|
|
97
|
+
|
|
98
|
+
## Funzioni server
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const response = await user.functions.calculateScore({ workspaceId: 'w1' })
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Le risposte sono normalizzate lato client per gestire payload JSON/EJSON.
|
|
105
|
+
|
|
106
|
+
## Mongo service
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const mongo = user.mongoClient('mongodb-atlas')
|
|
110
|
+
const collection = mongo.db('mydb').collection('todos')
|
|
111
|
+
|
|
112
|
+
const one = await collection.findOne({ done: false })
|
|
113
|
+
const many = await collection.find({ done: false })
|
|
114
|
+
|
|
115
|
+
await collection.insertOne({ title: 'Task', createdAt: new Date() })
|
|
116
|
+
await collection.updateOne({ title: 'Task' }, { $set: { done: true } })
|
|
117
|
+
await collection.deleteOne({ title: 'Task' })
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Metodi disponibili su `collection`:
|
|
121
|
+
|
|
122
|
+
- `find(query?, options?)`
|
|
123
|
+
- `findOne(query?, options?)`
|
|
124
|
+
- `insertOne(document, options?)`
|
|
125
|
+
- `updateOne(filter, update, options?)`
|
|
126
|
+
- `updateMany(filter, update, options?)`
|
|
127
|
+
- `deleteOne(filter, options?)`
|
|
128
|
+
- `watch(pipeline?, options?)`
|
|
129
|
+
|
|
130
|
+
## Watch / Change streams
|
|
131
|
+
|
|
132
|
+
`watch()` restituisce un async iterator con reconnect automatico e metodo `close()`.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const stream = collection.watch()
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
for await (const change of stream) {
|
|
139
|
+
console.log(change)
|
|
140
|
+
}
|
|
141
|
+
} finally {
|
|
142
|
+
stream.close()
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## BSON / EJSON
|
|
147
|
+
|
|
148
|
+
Il client esporta anche:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { BSON, EJSON, ObjectId, ObjectID } from '@flowerforce/flowerbase-client'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Il layer Mongo client serializza query/opzioni con EJSON e deserializza le risposte, così tipi BSON come `ObjectId` e `Date` restano coerenti con l'uso Realm-like.
|
|
155
|
+
|
|
156
|
+
## Sessione
|
|
157
|
+
|
|
158
|
+
La sessione (`accessToken`, `refreshToken`, `userId`) viene salvata con chiave:
|
|
159
|
+
|
|
160
|
+
- `flowerbase:<appId>:session`
|
|
161
|
+
|
|
162
|
+
Storage usato:
|
|
163
|
+
|
|
164
|
+
- `localStorage` se disponibile (browser)
|
|
165
|
+
- memory store fallback (ambienti senza `localStorage`)
|
|
166
|
+
|
|
167
|
+
Su bootstrap dell'app viene tentato un refresh automatico dell'access token usando il refresh token salvato.
|
|
168
|
+
|
|
169
|
+
## Logout
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
await user.logOut()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Invia `DELETE /auth/session` con refresh token e pulisce la sessione locale.
|
|
176
|
+
|
|
177
|
+
## Tipi esportati
|
|
178
|
+
|
|
179
|
+
- `AppConfig`
|
|
180
|
+
- `CredentialsLike`
|
|
181
|
+
- `UserLike`
|
|
182
|
+
- `MongoClientLike`
|
|
183
|
+
- `CollectionLike`
|
|
184
|
+
- `WatchAsyncIterator`
|
|
185
|
+
|
|
186
|
+
## Build e test (workspace)
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm run build --workspace @flowerforce/flowerbase-client
|
|
190
|
+
npm run test --workspace @flowerforce/flowerbase-client
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Oppure dal package:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm run build
|
|
197
|
+
npm test
|
|
198
|
+
```
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AppConfig, CredentialsLike, ProfileData, SessionData } from './types';
|
|
2
|
+
import { User } from './user';
|
|
3
|
+
export declare class App {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly baseUrl: string;
|
|
6
|
+
readonly timeout: number;
|
|
7
|
+
private readonly sessionManager;
|
|
8
|
+
currentUser: User | null;
|
|
9
|
+
private readonly sessionBootstrapPromise;
|
|
10
|
+
emailPasswordAuth: {
|
|
11
|
+
registerUser: (input: {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
}) => Promise<unknown>;
|
|
15
|
+
sendResetPasswordEmail: (email: string) => Promise<unknown>;
|
|
16
|
+
callResetPasswordFunction: (email: string, password: string, ...args: unknown[]) => Promise<unknown>;
|
|
17
|
+
resetPassword: (input: {
|
|
18
|
+
token: string;
|
|
19
|
+
tokenId: string;
|
|
20
|
+
password: string;
|
|
21
|
+
}) => Promise<unknown>;
|
|
22
|
+
};
|
|
23
|
+
constructor(config: AppConfig);
|
|
24
|
+
private providerUrl;
|
|
25
|
+
private authUrl;
|
|
26
|
+
private functionsUrl;
|
|
27
|
+
private createSession;
|
|
28
|
+
private bootstrapSessionOnLoad;
|
|
29
|
+
private ensureSessionBootstrapped;
|
|
30
|
+
private setLoggedInUser;
|
|
31
|
+
logIn(credentials: CredentialsLike): Promise<User>;
|
|
32
|
+
getSessionOrThrow(): SessionData;
|
|
33
|
+
postProvider<T = unknown>(path: string, body: unknown): Promise<T>;
|
|
34
|
+
callFunction(name: string, args: unknown[]): Promise<any>;
|
|
35
|
+
callService(name: string, args: unknown[]): Promise<unknown>;
|
|
36
|
+
getProfile(): Promise<ProfileData>;
|
|
37
|
+
refreshAccessToken(): Promise<string>;
|
|
38
|
+
logoutUser(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAuB,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACnG,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAc7B,qBAAa,GAAG;IACd,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,WAAW,EAAE,IAAI,GAAG,IAAI,CAAO;IAC/B,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAe;IAEvD,iBAAiB,EAAE;QACjB,YAAY,EAAE,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;QAC9E,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;QAC3D,yBAAyB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;QACpG,aAAa,EAAE,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KACjG,CAAA;gBAEW,MAAM,EAAE,SAAS;IAwB7B,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,YAAY;YAIN,aAAa;YASb,sBAAsB;YAkBtB,yBAAyB;YAIzB,eAAe;IAevB,KAAK,CAAC,WAAW,EAAE,eAAe;IAkBxC,iBAAiB;IAQX,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IASlE,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;IAmB1C,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;IAkBzC,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC;IAWlC,kBAAkB;IAoBlB,UAAU;CAgBjB"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
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 user_1 = require("./user");
|
|
8
|
+
const API_PREFIX = '/api/client/v2.0';
|
|
9
|
+
class App {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.currentUser = null;
|
|
12
|
+
this.id = config.id;
|
|
13
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
14
|
+
this.timeout = config.timeout ?? 10000;
|
|
15
|
+
this.sessionManager = new session_1.SessionManager(this.id);
|
|
16
|
+
const session = this.sessionManager.get();
|
|
17
|
+
if (session?.userId) {
|
|
18
|
+
this.currentUser = new user_1.User(this, session.userId);
|
|
19
|
+
}
|
|
20
|
+
this.sessionBootstrapPromise = this.bootstrapSessionOnLoad();
|
|
21
|
+
this.emailPasswordAuth = {
|
|
22
|
+
registerUser: ({ email, password }) => this.postProvider('/local-userpass/register', { email, password }),
|
|
23
|
+
sendResetPasswordEmail: (email) => this.postProvider('/local-userpass/reset/send', { email }),
|
|
24
|
+
callResetPasswordFunction: (email, password, ...args) => this.postProvider('/local-userpass/reset/call', { email, password, arguments: args }),
|
|
25
|
+
resetPassword: ({ token, tokenId, password }) => this.postProvider('/local-userpass/reset', { token, tokenId, password })
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
providerUrl(path) {
|
|
29
|
+
return `${this.baseUrl}${API_PREFIX}/app/${this.id}/auth/providers${path}`;
|
|
30
|
+
}
|
|
31
|
+
authUrl(path) {
|
|
32
|
+
return `${this.baseUrl}${API_PREFIX}/auth${path}`;
|
|
33
|
+
}
|
|
34
|
+
functionsUrl(path = '/call') {
|
|
35
|
+
return `${this.baseUrl}${API_PREFIX}/app/${this.id}/functions${path}`;
|
|
36
|
+
}
|
|
37
|
+
async createSession(refreshToken) {
|
|
38
|
+
return (0, http_1.requestJson)({
|
|
39
|
+
url: this.authUrl('/session'),
|
|
40
|
+
method: 'POST',
|
|
41
|
+
bearerToken: refreshToken,
|
|
42
|
+
timeout: this.timeout
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async bootstrapSessionOnLoad() {
|
|
46
|
+
const session = this.sessionManager.get();
|
|
47
|
+
if (!session || typeof localStorage === 'undefined') {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const result = await this.createSession(session.refreshToken);
|
|
52
|
+
this.sessionManager.set({
|
|
53
|
+
...session,
|
|
54
|
+
accessToken: result.access_token
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
this.sessionManager.clear();
|
|
59
|
+
this.currentUser = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async ensureSessionBootstrapped() {
|
|
63
|
+
await this.sessionBootstrapPromise;
|
|
64
|
+
}
|
|
65
|
+
async setLoggedInUser(data, profileEmail) {
|
|
66
|
+
const sessionResult = await this.createSession(data.refresh_token);
|
|
67
|
+
const session = {
|
|
68
|
+
accessToken: sessionResult.access_token,
|
|
69
|
+
refreshToken: data.refresh_token,
|
|
70
|
+
userId: data.user_id
|
|
71
|
+
};
|
|
72
|
+
this.sessionManager.set(session);
|
|
73
|
+
this.currentUser = new user_1.User(this, data.user_id);
|
|
74
|
+
if (profileEmail) {
|
|
75
|
+
this.currentUser.profile = { email: profileEmail };
|
|
76
|
+
}
|
|
77
|
+
return this.currentUser;
|
|
78
|
+
}
|
|
79
|
+
async logIn(credentials) {
|
|
80
|
+
if (credentials.provider === 'local-userpass') {
|
|
81
|
+
const result = await this.postProvider('/local-userpass/login', {
|
|
82
|
+
username: credentials.email,
|
|
83
|
+
password: credentials.password
|
|
84
|
+
});
|
|
85
|
+
return this.setLoggedInUser(result, credentials.email);
|
|
86
|
+
}
|
|
87
|
+
if (credentials.provider === 'anon-user') {
|
|
88
|
+
const result = await this.postProvider('/anon-user/login', {});
|
|
89
|
+
return this.setLoggedInUser(result);
|
|
90
|
+
}
|
|
91
|
+
const result = await this.postProvider('/custom-function/login', credentials.payload);
|
|
92
|
+
return this.setLoggedInUser(result);
|
|
93
|
+
}
|
|
94
|
+
getSessionOrThrow() {
|
|
95
|
+
const session = this.sessionManager.get();
|
|
96
|
+
if (!session) {
|
|
97
|
+
throw new Error('User is not authenticated');
|
|
98
|
+
}
|
|
99
|
+
return session;
|
|
100
|
+
}
|
|
101
|
+
async postProvider(path, body) {
|
|
102
|
+
return (0, http_1.requestJson)({
|
|
103
|
+
url: this.providerUrl(path),
|
|
104
|
+
method: 'POST',
|
|
105
|
+
body,
|
|
106
|
+
timeout: this.timeout
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async callFunction(name, args) {
|
|
110
|
+
await this.ensureSessionBootstrapped();
|
|
111
|
+
const session = this.getSessionOrThrow();
|
|
112
|
+
const payload = {
|
|
113
|
+
name,
|
|
114
|
+
arguments: args
|
|
115
|
+
};
|
|
116
|
+
const result = await (0, http_1.requestJson)({
|
|
117
|
+
url: this.functionsUrl('/call'),
|
|
118
|
+
method: 'POST',
|
|
119
|
+
body: payload,
|
|
120
|
+
bearerToken: session.accessToken,
|
|
121
|
+
timeout: this.timeout
|
|
122
|
+
});
|
|
123
|
+
return (0, functions_1.normalizeFunctionResponse)(result);
|
|
124
|
+
}
|
|
125
|
+
async callService(name, args) {
|
|
126
|
+
await this.ensureSessionBootstrapped();
|
|
127
|
+
const session = this.getSessionOrThrow();
|
|
128
|
+
const payload = {
|
|
129
|
+
name,
|
|
130
|
+
service: 'mongodb-atlas',
|
|
131
|
+
arguments: args
|
|
132
|
+
};
|
|
133
|
+
return (0, http_1.requestJson)({
|
|
134
|
+
url: this.functionsUrl('/call'),
|
|
135
|
+
method: 'POST',
|
|
136
|
+
body: payload,
|
|
137
|
+
bearerToken: session.accessToken,
|
|
138
|
+
timeout: this.timeout
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async getProfile() {
|
|
142
|
+
await this.ensureSessionBootstrapped();
|
|
143
|
+
const session = this.getSessionOrThrow();
|
|
144
|
+
return (0, http_1.requestJson)({
|
|
145
|
+
url: this.authUrl('/profile'),
|
|
146
|
+
method: 'GET',
|
|
147
|
+
bearerToken: session.accessToken,
|
|
148
|
+
timeout: this.timeout
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async refreshAccessToken() {
|
|
152
|
+
await this.ensureSessionBootstrapped();
|
|
153
|
+
const session = this.getSessionOrThrow();
|
|
154
|
+
try {
|
|
155
|
+
const result = await this.createSession(session.refreshToken);
|
|
156
|
+
this.sessionManager.set({
|
|
157
|
+
...session,
|
|
158
|
+
accessToken: result.access_token
|
|
159
|
+
});
|
|
160
|
+
return result.access_token;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
this.sessionManager.clear();
|
|
164
|
+
this.currentUser = null;
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async logoutUser() {
|
|
169
|
+
const session = this.sessionManager.get();
|
|
170
|
+
try {
|
|
171
|
+
if (session) {
|
|
172
|
+
await (0, http_1.requestJson)({
|
|
173
|
+
url: this.authUrl('/session'),
|
|
174
|
+
method: 'DELETE',
|
|
175
|
+
bearerToken: session.refreshToken,
|
|
176
|
+
timeout: this.timeout
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
this.sessionManager.clear();
|
|
182
|
+
this.currentUser = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.App = App;
|
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,7 @@
|
|
|
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
|
+
}
|
|
7
|
+
//# 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;CAMnE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
|
24
|
+
exports.Credentials = Credentials;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const normalizeFunctionResponse: (value: unknown) => any;
|
|
2
|
+
export declare const createFunctionsProxy: (callFunction: (name: string, args: unknown[]) => Promise<unknown>) => Record<string, (...args: unknown[]) => Promise<unknown>>;
|
|
3
|
+
//# sourceMappingURL=functions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../src/functions.ts"],"names":[],"mappings":"AAOA,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,KAChE,OAAO,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,OAAO,CAAC,CASK,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 deserialize = (value) => {
|
|
6
|
+
if (!value || typeof value !== 'object')
|
|
7
|
+
return value;
|
|
8
|
+
return bson_1.EJSON.deserialize(value);
|
|
9
|
+
};
|
|
10
|
+
const normalizeFunctionResponse = (value) => {
|
|
11
|
+
if (typeof value === 'string') {
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(value);
|
|
14
|
+
return deserialize(parsed);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return deserialize(value);
|
|
21
|
+
};
|
|
22
|
+
exports.normalizeFunctionResponse = normalizeFunctionResponse;
|
|
23
|
+
const createFunctionsProxy = (callFunction) => new Proxy({}, {
|
|
24
|
+
get: (_, key) => {
|
|
25
|
+
if (typeof key !== 'string')
|
|
26
|
+
return undefined;
|
|
27
|
+
return (...args) => callFunction(key, args);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
exports.createFunctionsProxy = createFunctionsProxy;
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class FlowerbaseHttpError extends Error {
|
|
2
|
+
status: number;
|
|
3
|
+
payload?: unknown;
|
|
4
|
+
constructor(message: string, status: number, payload?: unknown);
|
|
5
|
+
}
|
|
6
|
+
type RequestParams = {
|
|
7
|
+
url: string;
|
|
8
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
9
|
+
body?: unknown;
|
|
10
|
+
bearerToken?: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
};
|
|
13
|
+
export declare const requestJson: <T = unknown>({ url, method, body, bearerToken, timeout }: RequestParams) => Promise<T>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;gBAEL,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAM/D;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,CA6C3B,CAAA"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestJson = exports.FlowerbaseHttpError = void 0;
|
|
4
|
+
class FlowerbaseHttpError extends Error {
|
|
5
|
+
constructor(message, status, payload) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'FlowerbaseHttpError';
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.payload = payload;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.FlowerbaseHttpError = FlowerbaseHttpError;
|
|
13
|
+
const parseBody = async (response) => {
|
|
14
|
+
const text = await response.text();
|
|
15
|
+
if (!text)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const timeoutSignal = (timeout = 10000) => {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
27
|
+
return {
|
|
28
|
+
signal: controller.signal,
|
|
29
|
+
clear: () => clearTimeout(timer)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const requestJson = async ({ url, method = 'GET', body, bearerToken, timeout }) => {
|
|
33
|
+
const { signal, clear } = timeoutSignal(timeout);
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
method,
|
|
37
|
+
headers: {
|
|
38
|
+
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
39
|
+
...(bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {})
|
|
40
|
+
},
|
|
41
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
42
|
+
signal
|
|
43
|
+
});
|
|
44
|
+
const payload = await parseBody(response);
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
let parsedErrorMessage = null;
|
|
47
|
+
if (payload && typeof payload === 'object' && 'error' in payload && typeof payload.error === 'string') {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(payload.error);
|
|
50
|
+
if (parsed && typeof parsed === 'object' && 'message' in parsed && typeof parsed.message === 'string') {
|
|
51
|
+
parsedErrorMessage = parsed.message;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
parsedErrorMessage = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const message = parsedErrorMessage ||
|
|
59
|
+
(payload && typeof payload === 'object' && 'message' in payload && typeof payload.message === 'string'
|
|
60
|
+
? payload.message
|
|
61
|
+
: null) ||
|
|
62
|
+
(payload && typeof payload === 'object' && 'error' in payload && typeof payload.error === 'string'
|
|
63
|
+
? payload.error
|
|
64
|
+
: null) ||
|
|
65
|
+
`HTTP ${response.status}`;
|
|
66
|
+
throw new FlowerbaseHttpError(message, response.status, payload);
|
|
67
|
+
}
|
|
68
|
+
return payload;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
clear();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
exports.requestJson = requestJson;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BSON, EJSON, ObjectId, ObjectID } from './bson';
|
|
2
|
+
export { App } from './app';
|
|
3
|
+
export { User } from './user';
|
|
4
|
+
export { Credentials } from './credentials';
|
|
5
|
+
export { BSON, EJSON, ObjectId, ObjectID };
|
|
6
|
+
export type { AppConfig, CredentialsLike, UserLike, MongoClientLike, CollectionLike, WatchAsyncIterator } from './types';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;AAC1C,YAAY,EACV,SAAS,EACT,eAAe,EACf,QAAQ,EACR,eAAe,EACf,cAAc,EACd,kBAAkB,EACnB,MAAM,SAAS,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectID = exports.ObjectId = exports.EJSON = exports.BSON = exports.Credentials = exports.User = exports.App = void 0;
|
|
4
|
+
const bson_1 = require("./bson");
|
|
5
|
+
Object.defineProperty(exports, "BSON", { enumerable: true, get: function () { return bson_1.BSON; } });
|
|
6
|
+
Object.defineProperty(exports, "EJSON", { enumerable: true, get: function () { return bson_1.EJSON; } });
|
|
7
|
+
Object.defineProperty(exports, "ObjectId", { enumerable: true, get: function () { return bson_1.ObjectId; } });
|
|
8
|
+
Object.defineProperty(exports, "ObjectID", { enumerable: true, get: function () { return bson_1.ObjectID; } });
|
|
9
|
+
var app_1 = require("./app");
|
|
10
|
+
Object.defineProperty(exports, "App", { enumerable: true, get: function () { return app_1.App; } });
|
|
11
|
+
var user_1 = require("./user");
|
|
12
|
+
Object.defineProperty(exports, "User", { enumerable: true, get: function () { return user_1.User; } });
|
|
13
|
+
var credentials_1 = require("./credentials");
|
|
14
|
+
Object.defineProperty(exports, "Credentials", { enumerable: true, get: function () { return credentials_1.Credentials; } });
|
package/dist/mongo.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongo.d.ts","sourceRoot":"","sources":["../src/mongo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAA;AAoBzD,eAAO,MAAM,iBAAiB,QAAS,GAAG,KAAG,eAwC3C,CAAA"}
|