@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +0 -0
  2. package/LICENSE +3 -0
  3. package/README.md +198 -0
  4. package/dist/app.d.ts +40 -0
  5. package/dist/app.d.ts.map +1 -0
  6. package/dist/app.js +186 -0
  7. package/dist/bson.d.ts +8 -0
  8. package/dist/bson.d.ts.map +1 -0
  9. package/dist/bson.js +10 -0
  10. package/dist/credentials.d.ts +7 -0
  11. package/dist/credentials.d.ts.map +1 -0
  12. package/dist/credentials.js +24 -0
  13. package/dist/functions.d.ts +3 -0
  14. package/dist/functions.d.ts.map +1 -0
  15. package/dist/functions.js +30 -0
  16. package/dist/http.d.ts +15 -0
  17. package/dist/http.d.ts.map +1 -0
  18. package/dist/http.js +74 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +14 -0
  22. package/dist/mongo.d.ts +4 -0
  23. package/dist/mongo.d.ts.map +1 -0
  24. package/dist/mongo.js +61 -0
  25. package/dist/session.d.ts +12 -0
  26. package/dist/session.d.ts.map +1 -0
  27. package/dist/session.js +53 -0
  28. package/dist/session.native.d.ts +14 -0
  29. package/dist/session.native.d.ts.map +1 -0
  30. package/dist/session.native.js +81 -0
  31. package/dist/types.d.ts +73 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +2 -0
  34. package/dist/user.d.ts +17 -0
  35. package/dist/user.d.ts.map +1 -0
  36. package/dist/user.js +30 -0
  37. package/dist/watch.d.ts +3 -0
  38. package/dist/watch.d.ts.map +1 -0
  39. package/dist/watch.js +138 -0
  40. package/jest.config.ts +13 -0
  41. package/package.json +30 -0
  42. package/project.json +11 -0
  43. package/rollup.config.js +17 -0
  44. package/src/__tests__/auth.test.ts +164 -0
  45. package/src/__tests__/compat.test.ts +12 -0
  46. package/src/__tests__/functions.test.ts +76 -0
  47. package/src/__tests__/mongo.test.ts +48 -0
  48. package/src/__tests__/session.test.ts +103 -0
  49. package/src/__tests__/watch.test.ts +138 -0
  50. package/src/app.ts +235 -0
  51. package/src/bson.ts +6 -0
  52. package/src/credentials.ts +24 -0
  53. package/src/functions.ts +32 -0
  54. package/src/http.ts +92 -0
  55. package/src/index.ts +14 -0
  56. package/src/mongo.ts +63 -0
  57. package/src/session.native.ts +98 -0
  58. package/src/session.ts +59 -0
  59. package/src/types.ts +84 -0
  60. package/src/user.ts +39 -0
  61. package/src/watch.ts +150 -0
  62. package/tsconfig.json +34 -0
  63. package/tsconfig.spec.json +13 -0
package/dist/mongo.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMongoClient = void 0;
4
+ const bson_1 = require("./bson");
5
+ const watch_1 = require("./watch");
6
+ const serialize = (value) => bson_1.EJSON.serialize(value, { relaxed: false });
7
+ const deserialize = (value) => {
8
+ if (!value || typeof value !== 'object')
9
+ return value;
10
+ return bson_1.EJSON.deserialize(value);
11
+ };
12
+ const mapResult = (value) => {
13
+ if (typeof value === 'string') {
14
+ try {
15
+ return deserialize(JSON.parse(value));
16
+ }
17
+ catch {
18
+ return value;
19
+ }
20
+ }
21
+ return deserialize(value);
22
+ };
23
+ const createMongoClient = (app) => ({
24
+ db: (database) => ({
25
+ collection: (collection) => {
26
+ const callService = async (name, args) => {
27
+ const result = await app.callService(name, [
28
+ {
29
+ database,
30
+ collection,
31
+ ...serialize(args[0] ?? {}),
32
+ ...(args[1] !== undefined ? { options: serialize(args[1]) } : {})
33
+ }
34
+ ]);
35
+ return mapResult(result);
36
+ };
37
+ return {
38
+ find: (query = {}, options = {}) => callService('find', [{ query, options }]),
39
+ findOne: (query = {}, options = {}) => callService('findOne', [{ query, options }]),
40
+ insertOne: (document, options = {}) => callService('insertOne', [{ document, options }]),
41
+ updateOne: (filter, update, options = {}) => callService('updateOne', [{ filter, update, options }]),
42
+ updateMany: (filter, update, options = {}) => callService('updateMany', [{ filter, update, options }]),
43
+ deleteOne: (filter, options = {}) => callService('deleteOne', [{ query: filter, options }]),
44
+ watch: (pipeline = [], options = {}) => {
45
+ const session = app.getSessionOrThrow();
46
+ return (0, watch_1.createWatchIterator)({
47
+ appId: app.id,
48
+ baseUrl: app.baseUrl,
49
+ accessToken: session.accessToken,
50
+ database,
51
+ collection,
52
+ pipeline,
53
+ options,
54
+ timeout: app.timeout
55
+ });
56
+ }
57
+ };
58
+ }
59
+ })
60
+ });
61
+ exports.createMongoClient = createMongoClient;
@@ -0,0 +1,12 @@
1
+ import { SessionData } from './types';
2
+ export declare class SessionManager {
3
+ private readonly key;
4
+ private readonly storage;
5
+ private session;
6
+ constructor(appId: string);
7
+ load(): SessionData | null;
8
+ get(): SessionData | null;
9
+ set(session: SessionData): void;
10
+ clear(): void;
11
+ }
12
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAwBrC,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,OAAO,CAA2B;gBAE9B,KAAK,EAAE,MAAM;IAKzB,IAAI,IAAI,WAAW,GAAG,IAAI;IAW1B,GAAG;IAIH,GAAG,CAAC,OAAO,EAAE,WAAW;IAKxB,KAAK;CAIN"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const memoryStore = new Map();
5
+ const getStorage = () => {
6
+ if (typeof localStorage !== 'undefined') {
7
+ return {
8
+ getItem: (key) => localStorage.getItem(key),
9
+ setItem: (key, value) => localStorage.setItem(key, value),
10
+ removeItem: (key) => localStorage.removeItem(key)
11
+ };
12
+ }
13
+ return {
14
+ getItem: (key) => memoryStore.get(key) ?? null,
15
+ setItem: (key, value) => {
16
+ memoryStore.set(key, value);
17
+ },
18
+ removeItem: (key) => {
19
+ memoryStore.delete(key);
20
+ }
21
+ };
22
+ };
23
+ class SessionManager {
24
+ constructor(appId) {
25
+ this.storage = getStorage();
26
+ this.session = null;
27
+ this.key = `flowerbase:${appId}:session`;
28
+ this.session = this.load();
29
+ }
30
+ load() {
31
+ const raw = this.storage.getItem(this.key);
32
+ if (!raw)
33
+ return null;
34
+ try {
35
+ return JSON.parse(raw);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ get() {
42
+ return this.session;
43
+ }
44
+ set(session) {
45
+ this.session = session;
46
+ this.storage.setItem(this.key, JSON.stringify(session));
47
+ }
48
+ clear() {
49
+ this.session = null;
50
+ this.storage.removeItem(this.key);
51
+ }
52
+ }
53
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,14 @@
1
+ import { SessionData } from './types';
2
+ export declare class SessionManager {
3
+ private readonly key;
4
+ private session;
5
+ private readonly asyncStorage;
6
+ private hydrationPromise;
7
+ constructor(appId: string);
8
+ private hydrateFromAsyncStorage;
9
+ load(): SessionData | null;
10
+ get(): SessionData | null;
11
+ set(session: SessionData): void;
12
+ clear(): void;
13
+ }
14
+ //# sourceMappingURL=session.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.native.d.ts","sourceRoot":"","sources":["../src/session.native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA+BrC,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,KAAK,EAAE,MAAM;IAMzB,OAAO,CAAC,uBAAuB;IAwB/B,IAAI,IAAI,WAAW,GAAG,IAAI;IAI1B,GAAG;IAIH,GAAG,CAAC,OAAO,EAAE,WAAW;IAYxB,KAAK;CAUN"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const memoryStore = new Map();
5
+ const getAsyncStorage = () => {
6
+ try {
7
+ const req = (0, eval)('require');
8
+ const asyncStorageModule = req('@react-native-async-storage/async-storage');
9
+ return asyncStorageModule?.default ?? null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ };
15
+ const parseSession = (raw) => {
16
+ if (!raw)
17
+ return null;
18
+ try {
19
+ return JSON.parse(raw);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ };
25
+ class SessionManager {
26
+ constructor(appId) {
27
+ this.session = null;
28
+ this.asyncStorage = getAsyncStorage();
29
+ this.hydrationPromise = null;
30
+ this.key = `flowerbase:${appId}:session`;
31
+ this.session = this.load();
32
+ void this.hydrateFromAsyncStorage();
33
+ }
34
+ hydrateFromAsyncStorage() {
35
+ if (!this.asyncStorage) {
36
+ return Promise.resolve();
37
+ }
38
+ if (this.hydrationPromise) {
39
+ return this.hydrationPromise;
40
+ }
41
+ this.hydrationPromise = this.asyncStorage
42
+ .getItem(this.key)
43
+ .then((raw) => {
44
+ const parsed = parseSession(raw);
45
+ if (!parsed)
46
+ return;
47
+ this.session = parsed;
48
+ memoryStore.set(this.key, JSON.stringify(parsed));
49
+ })
50
+ .catch(() => {
51
+ // Ignore storage read failures and keep memory fallback.
52
+ });
53
+ return this.hydrationPromise;
54
+ }
55
+ load() {
56
+ return parseSession(memoryStore.get(this.key) ?? null);
57
+ }
58
+ get() {
59
+ return this.session;
60
+ }
61
+ set(session) {
62
+ this.session = session;
63
+ const raw = JSON.stringify(session);
64
+ memoryStore.set(this.key, raw);
65
+ if (this.asyncStorage) {
66
+ void this.asyncStorage.setItem(this.key, raw).catch(() => {
67
+ // Ignore write failures and keep memory fallback.
68
+ });
69
+ }
70
+ }
71
+ clear() {
72
+ this.session = null;
73
+ memoryStore.delete(this.key);
74
+ if (this.asyncStorage) {
75
+ void this.asyncStorage.removeItem(this.key).catch(() => {
76
+ // Ignore delete failures and keep memory fallback.
77
+ });
78
+ }
79
+ }
80
+ }
81
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,73 @@
1
+ export type AppConfig = {
2
+ id: string;
3
+ baseUrl: string;
4
+ timeout?: number;
5
+ };
6
+ export type CredentialsLike = {
7
+ provider: 'local-userpass';
8
+ email: string;
9
+ password: string;
10
+ } | {
11
+ provider: 'anon-user';
12
+ } | {
13
+ provider: 'custom-function';
14
+ payload: Record<string, unknown>;
15
+ };
16
+ export type SessionData = {
17
+ accessToken: string;
18
+ refreshToken: string;
19
+ userId: string;
20
+ };
21
+ export type ProfileData = {
22
+ _id?: string;
23
+ identities?: unknown[];
24
+ type?: string;
25
+ custom_data?: Record<string, unknown>;
26
+ data?: Record<string, unknown>;
27
+ };
28
+ export type FunctionCallPayload = {
29
+ name: string;
30
+ arguments: unknown[];
31
+ service?: string;
32
+ };
33
+ export type WatchConfig = {
34
+ appId: string;
35
+ baseUrl: string;
36
+ accessToken: string;
37
+ database: string;
38
+ collection: string;
39
+ pipeline?: unknown[];
40
+ options?: Record<string, unknown>;
41
+ timeout?: number;
42
+ };
43
+ export type WatchAsyncIterator<TChange = unknown> = AsyncIterableIterator<TChange> & {
44
+ close: () => void;
45
+ };
46
+ export interface CollectionLike {
47
+ find: (query?: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
48
+ findOne: (query?: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
49
+ insertOne: (document: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
50
+ updateOne: (filter: Record<string, unknown>, update: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
51
+ updateMany: (filter: Record<string, unknown>, update: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
52
+ deleteOne: (filter: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
53
+ watch: (pipeline?: unknown[], options?: Record<string, unknown>) => WatchAsyncIterator<unknown>;
54
+ }
55
+ export interface MongoDbLike {
56
+ collection: (name: string) => CollectionLike;
57
+ }
58
+ export interface MongoClientLike {
59
+ db: (name: string) => MongoDbLike;
60
+ }
61
+ export interface UserLike {
62
+ id: string;
63
+ profile?: {
64
+ email?: string;
65
+ [key: string]: unknown;
66
+ };
67
+ functions: Record<string, (...args: unknown[]) => Promise<unknown>>;
68
+ logOut: () => Promise<void>;
69
+ refreshAccessToken: () => Promise<string>;
70
+ refreshCustomData: () => Promise<ProfileData>;
71
+ mongoClient: (serviceName: string) => MongoClientLike;
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/user.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { App } from './app';
2
+ import { MongoClientLike, ProfileData, UserLike } from './types';
3
+ export declare class User implements UserLike {
4
+ id: string;
5
+ profile?: {
6
+ email?: string;
7
+ [key: string]: unknown;
8
+ };
9
+ private readonly app;
10
+ functions: Record<string, (...args: unknown[]) => Promise<unknown>>;
11
+ constructor(app: App, id: string);
12
+ logOut(): Promise<void>;
13
+ refreshAccessToken(): Promise<string>;
14
+ refreshCustomData(): Promise<ProfileData>;
15
+ mongoClient(serviceName: string): MongoClientLike;
16
+ }
17
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../src/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAGhC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEhE,qBAAa,IAAK,YAAW,QAAQ;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAA,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IACnD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAK;IAEzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;gBAEvD,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM;IAM1B,MAAM;IAIN,kBAAkB;IAIlB,iBAAiB,IAAI,OAAO,CAAC,WAAW,CAAC;IAM/C,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe;CAMlD"}
package/dist/user.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.User = void 0;
4
+ const functions_1 = require("./functions");
5
+ const mongo_1 = require("./mongo");
6
+ class User {
7
+ constructor(app, id) {
8
+ this.app = app;
9
+ this.id = id;
10
+ this.functions = (0, functions_1.createFunctionsProxy)((name, args) => this.app.callFunction(name, args));
11
+ }
12
+ async logOut() {
13
+ await this.app.logoutUser();
14
+ }
15
+ async refreshAccessToken() {
16
+ return this.app.refreshAccessToken();
17
+ }
18
+ async refreshCustomData() {
19
+ const profile = await this.app.getProfile();
20
+ this.profile = profile.data;
21
+ return profile.custom_data || {};
22
+ }
23
+ mongoClient(serviceName) {
24
+ if (serviceName !== 'mongodb-atlas') {
25
+ throw new Error(`Unsupported service "${serviceName}"`);
26
+ }
27
+ return (0, mongo_1.createMongoClient)(this.app);
28
+ }
29
+ }
30
+ exports.User = User;
@@ -0,0 +1,3 @@
1
+ import { WatchAsyncIterator, WatchConfig } from './types';
2
+ export declare const createWatchIterator: (config: WatchConfig) => WatchAsyncIterator<unknown>;
3
+ //# sourceMappingURL=watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAoCzD,eAAO,MAAM,mBAAmB,WAAY,WAAW,KAAG,mBAAmB,OAAO,CAiHnF,CAAA"}
package/dist/watch.js ADDED
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWatchIterator = void 0;
4
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
+ const createWatchRequest = ({ database, collection, pipeline = [], options = {} }) => ({
6
+ name: 'watch',
7
+ service: 'mongodb-atlas',
8
+ arguments: [
9
+ {
10
+ database,
11
+ collection,
12
+ pipeline,
13
+ options
14
+ }
15
+ ]
16
+ });
17
+ const toBase64 = (input) => {
18
+ if (typeof btoa === 'function') {
19
+ return btoa(input);
20
+ }
21
+ throw new Error('Base64 encoder not available in current runtime');
22
+ };
23
+ const parseSsePayload = (line) => {
24
+ if (!line.startsWith('data:'))
25
+ return null;
26
+ const raw = line.slice(5).trim();
27
+ if (!raw)
28
+ return null;
29
+ try {
30
+ return JSON.parse(raw);
31
+ }
32
+ catch {
33
+ return raw;
34
+ }
35
+ };
36
+ const createWatchIterator = (config) => {
37
+ let closed = false;
38
+ let activeController = null;
39
+ const queue = [];
40
+ const waiters = [];
41
+ const enqueue = (value) => {
42
+ const waiter = waiters.shift();
43
+ if (waiter) {
44
+ waiter({ done: false, value });
45
+ return;
46
+ }
47
+ queue.push(value);
48
+ };
49
+ const close = () => {
50
+ if (closed)
51
+ return;
52
+ closed = true;
53
+ activeController?.abort();
54
+ while (waiters.length > 0) {
55
+ const resolve = waiters.shift();
56
+ resolve?.({ done: true, value: undefined });
57
+ }
58
+ };
59
+ const run = async () => {
60
+ let attempts = 0;
61
+ while (!closed) {
62
+ const controller = new AbortController();
63
+ activeController = controller;
64
+ const request = createWatchRequest(config);
65
+ const encoded = toBase64(JSON.stringify(request));
66
+ const url = `${config.baseUrl}/api/client/v2.0/app/${config.appId}/functions/call?baas_request=${encodeURIComponent(encoded)}`;
67
+ try {
68
+ const response = await fetch(url, {
69
+ method: 'GET',
70
+ headers: {
71
+ Authorization: `Bearer ${config.accessToken}`,
72
+ Accept: 'text/event-stream'
73
+ },
74
+ signal: controller.signal
75
+ });
76
+ if (!response.ok || !response.body) {
77
+ throw new Error(`Watch request failed (${response.status})`);
78
+ }
79
+ attempts = 0;
80
+ const reader = response.body.getReader();
81
+ const decoder = new TextDecoder();
82
+ let buffer = '';
83
+ while (!closed) {
84
+ const { done, value } = await reader.read();
85
+ if (done)
86
+ break;
87
+ buffer += decoder.decode(value, { stream: true });
88
+ const lines = buffer.split('\n');
89
+ buffer = lines.pop() ?? '';
90
+ for (const line of lines) {
91
+ const parsed = parseSsePayload(line);
92
+ if (parsed !== null) {
93
+ enqueue(parsed);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ catch {
99
+ if (closed) {
100
+ break;
101
+ }
102
+ }
103
+ if (closed) {
104
+ break;
105
+ }
106
+ attempts += 1;
107
+ const backoff = Math.min(5000, 250 * 2 ** (attempts - 1));
108
+ await sleep(backoff);
109
+ }
110
+ };
111
+ void run();
112
+ return {
113
+ [Symbol.asyncIterator]() {
114
+ return this;
115
+ },
116
+ next() {
117
+ if (queue.length > 0) {
118
+ return Promise.resolve({ done: false, value: queue.shift() });
119
+ }
120
+ if (closed) {
121
+ return Promise.resolve({ done: true, value: undefined });
122
+ }
123
+ return new Promise((resolve) => {
124
+ waiters.push(resolve);
125
+ });
126
+ },
127
+ return() {
128
+ close();
129
+ return Promise.resolve({ done: true, value: undefined });
130
+ },
131
+ throw(error) {
132
+ close();
133
+ return Promise.reject(error);
134
+ },
135
+ close
136
+ };
137
+ };
138
+ exports.createWatchIterator = createWatchIterator;
package/jest.config.ts ADDED
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ preset: '../../jest.preset.js',
3
+ transform: {
4
+ '^.+\\.[tj]s$': [
5
+ 'ts-jest',
6
+ {
7
+ tsconfig: '<rootDir>/tsconfig.spec.json'
8
+ }
9
+ ]
10
+ },
11
+ testEnvironment: 'node',
12
+ testMatch: ['<rootDir>/src/**/*.test.ts']
13
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@flowerforce/flowerbase-client",
3
+ "version": "0.1.1-beta.2",
4
+ "description": "Client for Flowerbase",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "keywords": [],
8
+ "author": "",
9
+ "license": "MIT",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/flowerforce/flowerbase.git"
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "test": "npx jest",
25
+ "build": "rm -rf dist/ && tsc"
26
+ },
27
+ "dependencies": {
28
+ "bson": "^6.10.4"
29
+ }
30
+ }
package/project.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "flowerbase-client",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/flowerbase-client/src",
5
+ "projectType": "library",
6
+ "tags": [
7
+ "flowerbase",
8
+ "client"
9
+ ],
10
+ "targets": {}
11
+ }
@@ -0,0 +1,17 @@
1
+ const { withNx } = require('@nx/rollup/with-nx')
2
+
3
+ module.exports = withNx(
4
+ {
5
+ main: './src/index.ts',
6
+ outputPath: './dist',
7
+ tsConfig: './tsconfig.lib.json',
8
+ compiler: 'tsc',
9
+ format: ['cjs', 'esm'],
10
+ generateExportsField: true
11
+ },
12
+ {
13
+ // Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options
14
+ // e.g.
15
+ // output: { sourcemap: true },
16
+ }
17
+ )