@frenchbaas/js 0.1.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/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # @frenchbaas/js
2
+
3
+ SDK JavaScript officiel pour [FrenchBaas](https://frenchbaas.fr) — le BaaS français.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @frenchbaas/js
9
+ ```
10
+
11
+ ## Démarrage rapide
12
+
13
+ ```js
14
+ import { FrenchBaas } from '@frenchbaas/js'
15
+
16
+ const client = new FrenchBaas({
17
+ url: 'https://api.frenchbaas.fr',
18
+ apiKey: 'votre-clé-api',
19
+ })
20
+ ```
21
+
22
+ ## Auth
23
+
24
+ ```js
25
+ // Inscription
26
+ const { user } = await client.auth.signUp({ email: 'a@b.com', password: 'secret' })
27
+
28
+ // Connexion
29
+ const { user } = await client.auth.login({ email: 'a@b.com', password: 'secret' })
30
+
31
+ // Déconnexion
32
+ await client.auth.logout()
33
+
34
+ // État
35
+ client.auth.getUser() // { id, email } | null
36
+ client.auth.isLoggedIn() // boolean
37
+ ```
38
+
39
+ ## Collections (documents)
40
+
41
+ ```js
42
+ const col = client.collection('collection-uuid')
43
+
44
+ // Lire (paginé)
45
+ const { data, meta } = await col.get({ page: 1, perPage: 20 })
46
+
47
+ // Créer
48
+ const doc = await col.create({ title: 'Hello', published: true })
49
+
50
+ // Mettre à jour
51
+ const doc = await col.update('doc-id', { title: 'Modifié' })
52
+
53
+ // Supprimer
54
+ await col.delete('doc-id')
55
+
56
+ // Schéma de la collection
57
+ const schema = await col.schema()
58
+ ```
59
+
60
+ ## Gestion des erreurs
61
+
62
+ ```js
63
+ import { AuthError, ValidationError, NotFoundError, QuotaError, NetworkError } from '@frenchbaas/js'
64
+
65
+ try {
66
+ await client.collection('col-id').create({ title: 'Test' })
67
+ } catch (e) {
68
+ if (e instanceof AuthError) console.error('Non connecté')
69
+ if (e instanceof ValidationError) console.error('Données invalides', e.errors)
70
+ if (e instanceof NotFoundError) console.error('Introuvable')
71
+ if (e instanceof QuotaError) console.error('Quota dépassé')
72
+ if (e instanceof NetworkError) console.error('Hors ligne')
73
+ }
74
+ ```
75
+
76
+ ## Options
77
+
78
+ ```js
79
+ // Persister la session dans localStorage (rechargement de page)
80
+ const client = new FrenchBaas({
81
+ url: 'https://api.frenchbaas.fr',
82
+ apiKey: 'votre-clé-api',
83
+ storage: 'localStorage', // défaut : 'memory'
84
+ })
85
+ ```
86
+
87
+ ## Fonctionnalités automatiques
88
+
89
+ - **Refresh token transparent** — access token rafraîchi automatiquement avant expiration
90
+ - **Retry sur 401** — relance la requête après refresh sans intervention du développeur
91
+ - **Déduplication** — plusieurs requêtes simultanées ne déclenchent qu'un seul refresh
92
+ - **Erreurs typées** — chaque erreur HTTP a sa classe TypeScript dédiée
93
+ - **Zero dépendance** — aucune dépendance runtime
94
+ - **TypeScript** — types inclus
95
+
96
+ ## License
97
+
98
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,541 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthError: () => AuthError,
24
+ FrenchBaas: () => FrenchBaasClient,
25
+ FrenchBaasError: () => FrenchBaasError,
26
+ NetworkError: () => NetworkError,
27
+ NotFoundError: () => NotFoundError,
28
+ QuotaError: () => QuotaError,
29
+ RateLimitError: () => RateLimitError,
30
+ ServerError: () => ServerError,
31
+ ValidationError: () => ValidationError
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/errors.ts
36
+ var FrenchBaasError = class extends Error {
37
+ constructor(message, status) {
38
+ super(message);
39
+ this.status = status;
40
+ this.name = "FrenchBaasError";
41
+ Object.setPrototypeOf(this, new.target.prototype);
42
+ }
43
+ };
44
+ var AuthError = class extends FrenchBaasError {
45
+ constructor(message, status) {
46
+ super(message, status);
47
+ this.name = "AuthError";
48
+ Object.setPrototypeOf(this, new.target.prototype);
49
+ }
50
+ };
51
+ var NetworkError = class extends FrenchBaasError {
52
+ constructor(message = "Impossible de joindre le serveur FrenchBaas.") {
53
+ super(message);
54
+ this.name = "NetworkError";
55
+ Object.setPrototypeOf(this, new.target.prototype);
56
+ }
57
+ };
58
+ var ValidationError = class extends FrenchBaasError {
59
+ constructor(message, errors = []) {
60
+ super(message, 422);
61
+ this.errors = errors;
62
+ this.name = "ValidationError";
63
+ Object.setPrototypeOf(this, new.target.prototype);
64
+ }
65
+ };
66
+ var NotFoundError = class extends FrenchBaasError {
67
+ constructor(message = "Ressource introuvable.") {
68
+ super(message, 404);
69
+ this.name = "NotFoundError";
70
+ Object.setPrototypeOf(this, new.target.prototype);
71
+ }
72
+ };
73
+ var QuotaError = class extends FrenchBaasError {
74
+ constructor(message) {
75
+ super(message, 403);
76
+ this.name = "QuotaError";
77
+ Object.setPrototypeOf(this, new.target.prototype);
78
+ }
79
+ };
80
+ var RateLimitError = class extends FrenchBaasError {
81
+ constructor(message = "Trop de requ\xEAtes. Veuillez r\xE9essayer dans quelques instants.") {
82
+ super(message, 429);
83
+ this.name = "RateLimitError";
84
+ Object.setPrototypeOf(this, new.target.prototype);
85
+ }
86
+ };
87
+ var ServerError = class extends FrenchBaasError {
88
+ constructor(message = "Erreur interne du serveur.", status) {
89
+ super(message, status);
90
+ this.name = "ServerError";
91
+ Object.setPrototypeOf(this, new.target.prototype);
92
+ }
93
+ };
94
+
95
+ // src/auth.ts
96
+ var AuthModule = class {
97
+ constructor(_http, _tokenStore, _baseUrl, _apiKey) {
98
+ this._http = _http;
99
+ this._tokenStore = _tokenStore;
100
+ this._baseUrl = _baseUrl;
101
+ this._apiKey = _apiKey;
102
+ this._http.setRefreshFn(() => this._refresh());
103
+ }
104
+ /**
105
+ * Inscrit un nouvel utilisateur final.
106
+ * Stocke automatiquement les tokens après inscription réussie.
107
+ */
108
+ async signUp(params) {
109
+ const res = await this._http.post("/sdk/auth/signup", params);
110
+ return this._storeAndReturn(res);
111
+ }
112
+ /**
113
+ * Connecte un utilisateur existant.
114
+ * Stocke automatiquement les tokens après connexion réussie.
115
+ */
116
+ async login(params) {
117
+ const res = await this._http.post("/sdk/auth/login", params);
118
+ return this._storeAndReturn(res);
119
+ }
120
+ /**
121
+ * Déconnecte l'utilisateur et efface tous les tokens stockés.
122
+ * L'appel API est best-effort (stateless côté serveur).
123
+ */
124
+ async logout() {
125
+ try {
126
+ await this._http.post("/sdk/auth/logout");
127
+ } finally {
128
+ this._tokenStore.clear();
129
+ }
130
+ }
131
+ /**
132
+ * Retourne l'utilisateur courant depuis la mémoire (synchrone).
133
+ * Retourne null si non connecté ou si la page a été rechargée
134
+ * avec la stratégie 'memory'.
135
+ */
136
+ getUser() {
137
+ return this._tokenStore.user;
138
+ }
139
+ /**
140
+ * Indique si l'utilisateur a une session active (refresh token valide).
141
+ */
142
+ isLoggedIn() {
143
+ return this._tokenStore.refreshToken !== null && !this._tokenStore.isRefreshTokenExpired();
144
+ }
145
+ /**
146
+ * Refresh interne — appelé automatiquement par HttpClient.
147
+ * Utilise un fetch direct pour éviter la récursion infinie.
148
+ * Le refresh token expiré → logout propre + AuthError.
149
+ */
150
+ async _refresh() {
151
+ const refreshToken = this._tokenStore.refreshToken;
152
+ if (!refreshToken || this._tokenStore.isRefreshTokenExpired()) {
153
+ this._tokenStore.clear();
154
+ throw new AuthError("Session expir\xE9e. Veuillez vous reconnecter.", 401);
155
+ }
156
+ let response;
157
+ try {
158
+ response = await fetch(`${this._baseUrl}/sdk/auth/refresh`, {
159
+ method: "POST",
160
+ headers: {
161
+ "Content-Type": "application/json",
162
+ "Accept": "application/json",
163
+ "X-Api-Key": this._apiKey
164
+ },
165
+ body: JSON.stringify({ refresh_token: refreshToken })
166
+ });
167
+ } catch {
168
+ throw new NetworkError();
169
+ }
170
+ if (!response.ok) {
171
+ this._tokenStore.clear();
172
+ let msg = "Session expir\xE9e. Veuillez vous reconnecter.";
173
+ try {
174
+ const body2 = await response.json();
175
+ if (body2.error) msg = body2.error;
176
+ } catch {
177
+ }
178
+ throw new AuthError(msg, 401);
179
+ }
180
+ const body = await response.json();
181
+ this._tokenStore.updateTokens(
182
+ body.data.access_token,
183
+ body.data.refresh_token
184
+ );
185
+ }
186
+ _storeAndReturn(res) {
187
+ const { access_token, refresh_token, user } = res.data;
188
+ if (!user) throw new AuthError("R\xE9ponse serveur invalide : utilisateur manquant.");
189
+ this._tokenStore.set({ accessToken: access_token, refreshToken: refresh_token, user });
190
+ return { user, access_token, refresh_token };
191
+ }
192
+ };
193
+
194
+ // src/collection.ts
195
+ var CollectionClient = class {
196
+ constructor(_collectionId, _http) {
197
+ this._collectionId = _collectionId;
198
+ this._http = _http;
199
+ }
200
+ /**
201
+ * Liste les documents de la collection avec pagination.
202
+ * La visibilité (public/authenticated/private) est gérée côté serveur.
203
+ *
204
+ * @param options.page Numéro de page (défaut: 1)
205
+ * @param options.perPage Documents par page, max 100 (défaut: 50)
206
+ */
207
+ async get(options = {}) {
208
+ const params = {
209
+ page: options.page,
210
+ per_page: options.perPage
211
+ };
212
+ const res = await this._http.get(
213
+ `/sdk/collections/${this._collectionId}/documents`,
214
+ params
215
+ );
216
+ return { data: res.data, meta: res.meta };
217
+ }
218
+ /**
219
+ * Crée un nouveau document dans la collection.
220
+ * Les champs sont validés contre le schéma côté serveur.
221
+ *
222
+ * @param data Données du document (doit respecter le schéma de la collection)
223
+ */
224
+ async create(data) {
225
+ const res = await this._http.post(
226
+ `/sdk/collections/${this._collectionId}/documents`,
227
+ { data }
228
+ );
229
+ return res.data;
230
+ }
231
+ /**
232
+ * Met à jour partiellement un document (merge).
233
+ * Seuls les champs fournis sont mis à jour, les autres restent inchangés.
234
+ *
235
+ * - Collection private : seul le créateur peut modifier.
236
+ * - Collection authenticated/public : tout utilisateur authentifié peut modifier.
237
+ *
238
+ * @param documentId UUID du document à modifier
239
+ * @param data Champs à mettre à jour
240
+ */
241
+ async update(documentId, data) {
242
+ const res = await this._http.patch(
243
+ `/sdk/documents/${documentId}`,
244
+ { data }
245
+ );
246
+ return res.data;
247
+ }
248
+ /**
249
+ * Supprime un document.
250
+ *
251
+ * - Collection private : seul le créateur peut supprimer.
252
+ * - Collection authenticated/public : tout utilisateur authentifié peut supprimer.
253
+ *
254
+ * @param documentId UUID du document à supprimer
255
+ */
256
+ async delete(documentId) {
257
+ await this._http.delete(`/sdk/documents/${documentId}`);
258
+ }
259
+ /**
260
+ * Retourne le schéma de la collection (nom des champs, types, visibilité).
261
+ * Utile pour construire des formulaires dynamiques.
262
+ */
263
+ async schema() {
264
+ const res = await this._http.get(
265
+ `/sdk/collections/${this._collectionId}/schema`
266
+ );
267
+ return res.data;
268
+ }
269
+ };
270
+
271
+ // src/http.ts
272
+ var HttpClient = class {
273
+ constructor(_baseUrl, _apiKey, _tokenStore) {
274
+ this._baseUrl = _baseUrl;
275
+ this._apiKey = _apiKey;
276
+ this._tokenStore = _tokenStore;
277
+ this._refreshFn = null;
278
+ this._refreshing = null;
279
+ }
280
+ /** Enregistre la fonction de refresh (injectée par AuthModule). */
281
+ setRefreshFn(fn) {
282
+ this._refreshFn = fn;
283
+ }
284
+ async get(path, params) {
285
+ const url = new URL(this._baseUrl + path);
286
+ if (params) {
287
+ for (const [k, v] of Object.entries(params)) {
288
+ if (v !== void 0) url.searchParams.set(k, String(v));
289
+ }
290
+ }
291
+ return this._request("GET", url.toString(), void 0, true);
292
+ }
293
+ async post(path, body) {
294
+ return this._request("POST", this._baseUrl + path, body, true);
295
+ }
296
+ async patch(path, body) {
297
+ return this._request("PATCH", this._baseUrl + path, body, true);
298
+ }
299
+ async delete(path) {
300
+ return this._request("DELETE", this._baseUrl + path, void 0, true);
301
+ }
302
+ async _request(method, url, body, canRetry) {
303
+ if (canRetry && this._tokenStore.accessToken && this._tokenStore.isAccessTokenExpired()) {
304
+ await this._triggerRefresh();
305
+ }
306
+ const response = await this._fetch(method, url, body);
307
+ if (response.status === 401 && canRetry && this._refreshFn && !this._tokenStore.isRefreshTokenExpired()) {
308
+ try {
309
+ await this._triggerRefresh();
310
+ return this._request(method, url, body, false);
311
+ } catch {
312
+ throw new AuthError("Session expir\xE9e. Veuillez vous reconnecter.", 401);
313
+ }
314
+ }
315
+ return this._parseResponse(response);
316
+ }
317
+ async _fetch(method, url, body) {
318
+ const headers = {
319
+ "Content-Type": "application/json",
320
+ "Accept": "application/json",
321
+ "X-Api-Key": this._apiKey
322
+ };
323
+ const token = this._tokenStore.accessToken;
324
+ if (token) headers["Authorization"] = `Bearer ${token}`;
325
+ try {
326
+ return await fetch(url, {
327
+ method,
328
+ headers,
329
+ body: body !== void 0 ? JSON.stringify(body) : void 0
330
+ });
331
+ } catch {
332
+ throw new NetworkError();
333
+ }
334
+ }
335
+ /**
336
+ * Déclenche le refresh en s'assurant qu'un seul refresh tourne à la fois
337
+ * même si plusieurs requêtes l'appellent simultanément.
338
+ */
339
+ _triggerRefresh() {
340
+ if (!this._refreshFn) return Promise.resolve();
341
+ if (this._refreshing) return this._refreshing;
342
+ this._refreshing = this._refreshFn().finally(() => {
343
+ this._refreshing = null;
344
+ });
345
+ return this._refreshing;
346
+ }
347
+ async _parseResponse(response) {
348
+ let body;
349
+ try {
350
+ body = await response.json();
351
+ } catch {
352
+ if (!response.ok) {
353
+ throw new ServerError(`Erreur serveur (${response.status})`, response.status);
354
+ }
355
+ throw new ServerError("R\xE9ponse invalide du serveur.");
356
+ }
357
+ if (response.ok) return body;
358
+ const err = body;
359
+ const msg = err.error ?? "Une erreur est survenue.";
360
+ switch (response.status) {
361
+ case 400:
362
+ throw new ValidationError(msg);
363
+ case 401:
364
+ throw new AuthError(msg, 401);
365
+ case 403:
366
+ if (err.quota || /quota|limit|dépass/i.test(msg)) {
367
+ throw new QuotaError(msg);
368
+ }
369
+ throw new AuthError(msg, 403);
370
+ case 404:
371
+ throw new NotFoundError(msg);
372
+ case 413:
373
+ throw new ValidationError(msg);
374
+ case 422: {
375
+ const details = err.errors ?? [];
376
+ throw new ValidationError(details.length ? details.join(", ") : msg, details);
377
+ }
378
+ case 429:
379
+ throw new RateLimitError();
380
+ default:
381
+ if (response.status >= 500) throw new ServerError(msg, response.status);
382
+ throw new ServerError(msg, response.status);
383
+ }
384
+ }
385
+ };
386
+
387
+ // src/token.ts
388
+ var KEYS = {
389
+ access: "frenchbaas_access_token",
390
+ refresh: "frenchbaas_refresh_token",
391
+ user: "frenchbaas_user"
392
+ };
393
+ var TokenStore = class {
394
+ constructor(strategy = "memory") {
395
+ this._state = { accessToken: null, refreshToken: null, user: null };
396
+ this._strategy = strategy;
397
+ if (strategy === "localStorage") {
398
+ this._loadFromStorage();
399
+ }
400
+ }
401
+ get accessToken() {
402
+ return this._state.accessToken;
403
+ }
404
+ get refreshToken() {
405
+ return this._state.refreshToken;
406
+ }
407
+ get user() {
408
+ return this._state.user;
409
+ }
410
+ /** Stocke access token, refresh token et optionnellement l'utilisateur. */
411
+ set(tokens) {
412
+ this._state.accessToken = tokens.accessToken;
413
+ this._state.refreshToken = tokens.refreshToken;
414
+ if (tokens.user !== void 0) this._state.user = tokens.user;
415
+ if (this._strategy === "localStorage") this._persist();
416
+ }
417
+ /** Met à jour uniquement les tokens (appelé après un refresh — sans user). */
418
+ updateTokens(accessToken, refreshToken) {
419
+ this._state.accessToken = accessToken;
420
+ this._state.refreshToken = refreshToken;
421
+ if (this._strategy === "localStorage") this._persist();
422
+ }
423
+ /** Efface tous les tokens et l'utilisateur (appelé lors du logout). */
424
+ clear() {
425
+ this._state = { accessToken: null, refreshToken: null, user: null };
426
+ if (this._strategy === "localStorage") {
427
+ try {
428
+ localStorage.removeItem(KEYS.access);
429
+ localStorage.removeItem(KEYS.refresh);
430
+ localStorage.removeItem(KEYS.user);
431
+ } catch {
432
+ }
433
+ }
434
+ }
435
+ /**
436
+ * Vérifie si l'access token est expiré ou expire dans moins de 30 secondes.
437
+ * Retourne true si absent ou invalide.
438
+ */
439
+ isAccessTokenExpired() {
440
+ return this._isExpired(this._state.accessToken, 3e4);
441
+ }
442
+ /**
443
+ * Vérifie si le refresh token est expiré.
444
+ * Retourne true si absent ou invalide.
445
+ */
446
+ isRefreshTokenExpired() {
447
+ return this._isExpired(this._state.refreshToken, 0);
448
+ }
449
+ _isExpired(token, bufferMs) {
450
+ if (!token) return true;
451
+ try {
452
+ const payload = decodeJwtPayload(token);
453
+ return payload.exp * 1e3 < Date.now() + bufferMs;
454
+ } catch {
455
+ return true;
456
+ }
457
+ }
458
+ _persist() {
459
+ try {
460
+ if (this._state.accessToken) localStorage.setItem(KEYS.access, this._state.accessToken);
461
+ if (this._state.refreshToken) localStorage.setItem(KEYS.refresh, this._state.refreshToken);
462
+ if (this._state.user) localStorage.setItem(KEYS.user, JSON.stringify(this._state.user));
463
+ } catch {
464
+ }
465
+ }
466
+ _loadFromStorage() {
467
+ try {
468
+ this._state.accessToken = localStorage.getItem(KEYS.access);
469
+ this._state.refreshToken = localStorage.getItem(KEYS.refresh);
470
+ const raw = localStorage.getItem(KEYS.user);
471
+ this._state.user = raw ? JSON.parse(raw) : null;
472
+ } catch {
473
+ }
474
+ }
475
+ };
476
+ function decodeJwtPayload(token) {
477
+ const parts = token.split(".");
478
+ if (parts.length !== 3) throw new Error("JWT invalide : format incorrect");
479
+ const base64 = (parts[1] ?? "").replace(/-/g, "+").replace(/_/g, "/");
480
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
481
+ let json;
482
+ if (typeof atob !== "undefined") {
483
+ json = atob(padded);
484
+ } else {
485
+ json = globalThis.Buffer.from(padded, "base64").toString("utf-8");
486
+ }
487
+ const payload = JSON.parse(json);
488
+ if (typeof payload.exp !== "number") throw new Error("JWT invalide : champ exp manquant");
489
+ return payload;
490
+ }
491
+
492
+ // src/client.ts
493
+ var FrenchBaasClient = class {
494
+ constructor(config) {
495
+ if (!config.url) throw new Error('[FrenchBaas] La propri\xE9t\xE9 "url" est requise.');
496
+ if (!config.apiKey) throw new Error('[FrenchBaas] La propri\xE9t\xE9 "apiKey" est requise.');
497
+ const baseUrl = config.url.replace(/\/$/, "");
498
+ this._tokenStore = new TokenStore(config.storage ?? "memory");
499
+ this._http = new HttpClient(baseUrl, config.apiKey, this._tokenStore);
500
+ this.auth = new AuthModule(this._http, this._tokenStore, baseUrl, config.apiKey);
501
+ }
502
+ /**
503
+ * Retourne un client pour une collection spécifique.
504
+ *
505
+ * Le type générique T permet de typer les données des documents :
506
+ * ```ts
507
+ * const posts = client.collection<{ title: string; published: boolean }>('uuid')
508
+ * const doc = await posts.create({ title: 'Hello', published: true })
509
+ * // doc.data.title est typé string ✓
510
+ * ```
511
+ *
512
+ * @param collectionId UUID de la collection (visible dans le Dashboard)
513
+ */
514
+ collection(collectionId) {
515
+ if (!collectionId || typeof collectionId !== "string") {
516
+ throw new Error("[FrenchBaas] collection() requiert un UUID de collection valide.");
517
+ }
518
+ return new CollectionClient(collectionId, this._http);
519
+ }
520
+ /**
521
+ * Retourne la spécification OpenAPI du projet.
522
+ * Contient toutes les collections, leurs schémas et les endpoints disponibles.
523
+ * Utile pour de la génération de code ou de la documentation dynamique.
524
+ */
525
+ async getOpenApiSpec() {
526
+ return this._http.get("/sdk/openapi");
527
+ }
528
+ };
529
+ // Annotate the CommonJS export names for ESM import in node:
530
+ 0 && (module.exports = {
531
+ AuthError,
532
+ FrenchBaas,
533
+ FrenchBaasError,
534
+ NetworkError,
535
+ NotFoundError,
536
+ QuotaError,
537
+ RateLimitError,
538
+ ServerError,
539
+ ValidationError
540
+ });
541
+ //# sourceMappingURL=index.cjs.map