bff-store 0.1.0 → 0.1.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/.claude/settings.local.json +14 -1
- package/README.md +196 -63
- package/dist/cli.js +11 -9
- package/dist/index.d.mts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.mjs +88 -10
- package/dist/package.json +3 -6
- package/dist/server/entry.d.mts +1 -1
- package/dist/server/entry.d.ts +1 -1
- package/dist/server/entry.js +31992 -23
- package/dist/server/entry.mjs +32016 -13
- package/dist/{server-V7WCW4ZB.mjs → server-2WMLXQ23.mjs} +11 -9
- package/dist/storage/jsonl-entry.js +1 -1
- package/dist/storage/jsonl-entry.mjs +1 -1
- package/dist/storage/mongodb-entry.js +31986 -10
- package/dist/storage/mongodb-entry.mjs +32008 -8
- package/docs/BUG_FIX_MONGODB_CHILD_PROCESS.md +129 -0
- package/docs/FRONTEND_BACKEND_ISOLATION.md +150 -0
- package/docs/IMPLEMENTATION_OVERVIEW.md +231 -0
- package/docs/LOCAL_LINK_STRATEGIES.md +117 -0
- package/docs/bugs/01-server-async-startup.high.md +34 -0
- package/docs/bugs/03-storage-cache-eviction.md +40 -0
- package/docs/bugs/04-http-error-body-loss.md +38 -0
- package/docs/bugs/05-debouncer-ms-ignored.md +41 -0
- package/docs/bugs/07-jsonl-key-collision.high.md +31 -0
- package/docs/bugs/08-dead-code-getStorageForRequest.md +18 -0
- package/docs/bugs/09-waitForLoad-infinite.md +37 -0
- package/docs/bugs/10-parseBody-silent-failure.md +32 -0
- package/docs/bugs/11-mongodb-storage-leak.high.md +35 -0
- package/docs/bugs/12-setEntityId-stale-closure.high.md +35 -0
- package/docs/bugs/14-dead-code-isServerRunning.md +18 -0
- package/docs/bugs/15-unmount-data-loss.md +37 -0
- package/docs/bugs/17-unused-parameter.md +26 -0
- package/docs/bugs/18-storage-get-silent-error.md +28 -0
- package/docs/bugs/README.md +35 -0
- package/package.json +3 -6
- package/src/atomCreator.ts +10 -2
- package/src/createStore.ts +19 -1
- package/src/debouncer.ts +5 -2
- package/src/environment.ts +11 -0
- package/src/index.ts +8 -1
- package/src/nodeStore.ts +86 -0
- package/src/server/handlers.ts +7 -24
- package/src/server/index.ts +0 -4
- package/src/storage/adapters/remoteStorage.ts +2 -1
- package/src/storage/jsonl.ts +3 -1
- package/src/storage/mongodb.ts +6 -6
- package/src/storage/transport.ts +24 -3
- package/tests/storage/jsonl.test.ts +18 -2
- package/tsconfig.json +1 -0
- package/docs/BUG_DIAGNOSIS_REMOTE_STORAGE_OPTIONS.md +0 -104
- package/docs/BUG_FIX_REMOTE_STORAGE_OPTIONS.md +0 -63
- package/docs/BUG_FIX_SESSION_2026-06-03.md +0 -171
- /package/docs/{SIDECAR_SERVER.md → BFF_SIDECAR_SERVER.md} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -30,8 +30,9 @@ var DebouncerMap = class {
|
|
|
30
30
|
}
|
|
31
31
|
getDebouncer(key, ms) {
|
|
32
32
|
let debouncer = this.debouncers.get(key);
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const effectiveMs = ms ?? this.defaultMs;
|
|
34
|
+
if (!debouncer || debouncer.ms !== effectiveMs) {
|
|
35
|
+
debouncer = createDebouncer(effectiveMs);
|
|
35
36
|
this.debouncers.set(key, debouncer);
|
|
36
37
|
}
|
|
37
38
|
return debouncer;
|
|
@@ -73,12 +74,18 @@ function createPersistedAtom(config, entityId, storage, options) {
|
|
|
73
74
|
if (value !== null && value !== void 0) {
|
|
74
75
|
setValue(value);
|
|
75
76
|
}
|
|
76
|
-
}).catch(
|
|
77
|
+
}).catch((err) => {
|
|
78
|
+
console.error(`[bff-store] Failed to load atom "${config.key}":`, err);
|
|
79
|
+
}).finally(() => {
|
|
77
80
|
setTimeout(() => {
|
|
78
81
|
const store = getDefaultStore();
|
|
79
82
|
store.set(loadingAtom, false);
|
|
80
83
|
}, 0);
|
|
81
84
|
});
|
|
85
|
+
return () => {
|
|
86
|
+
const debounceKey = `${entityId}:${config.key}`;
|
|
87
|
+
debouncerMap.cancel(debounceKey);
|
|
88
|
+
};
|
|
82
89
|
};
|
|
83
90
|
const writeAtom = atom(
|
|
84
91
|
(get) => get(baseAtom),
|
|
@@ -104,12 +111,13 @@ function createPersistedAtom(config, entityId, storage, options) {
|
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
// src/createStore.ts
|
|
114
|
+
var serverInitPromise = null;
|
|
107
115
|
function createStore(entityId, config, options) {
|
|
108
116
|
const adapter = options?.storage;
|
|
109
117
|
const debounceMs = options?.debounceMs ?? 800;
|
|
110
118
|
if (adapter?.name === "remote" && typeof window === "undefined" && typeof process !== "undefined") {
|
|
111
|
-
import("./server-
|
|
112
|
-
startServer().catch((err) => {
|
|
119
|
+
import("./server-2WMLXQ23.mjs").then(({ startServer }) => {
|
|
120
|
+
serverInitPromise = startServer().then(() => void 0).catch((err) => {
|
|
113
121
|
console.error("[bff-store] Failed to auto-start server:", err);
|
|
114
122
|
});
|
|
115
123
|
});
|
|
@@ -138,6 +146,9 @@ function createStore(entityId, config, options) {
|
|
|
138
146
|
loadingAtoms
|
|
139
147
|
};
|
|
140
148
|
}
|
|
149
|
+
function waitForServer() {
|
|
150
|
+
return serverInitPromise;
|
|
151
|
+
}
|
|
141
152
|
|
|
142
153
|
// src/useStore.ts
|
|
143
154
|
import { useAtom } from "jotai";
|
|
@@ -224,7 +235,13 @@ var HttpTransport = class {
|
|
|
224
235
|
async get(url) {
|
|
225
236
|
const res = await fetch(url);
|
|
226
237
|
if (!res.ok) {
|
|
227
|
-
|
|
238
|
+
let detail = res.statusText;
|
|
239
|
+
try {
|
|
240
|
+
const body = await res.clone().json();
|
|
241
|
+
detail = body?.error ?? body?.message ?? detail;
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
throw new Error(`GET ${url} failed: ${detail}`);
|
|
228
245
|
}
|
|
229
246
|
return res.json();
|
|
230
247
|
}
|
|
@@ -235,14 +252,26 @@ var HttpTransport = class {
|
|
|
235
252
|
body: JSON.stringify(body)
|
|
236
253
|
});
|
|
237
254
|
if (!res.ok) {
|
|
238
|
-
|
|
255
|
+
let detail = res.statusText;
|
|
256
|
+
try {
|
|
257
|
+
const errBody = await res.clone().json();
|
|
258
|
+
detail = errBody?.error ?? errBody?.message ?? detail;
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
throw new Error(`POST ${url} failed: ${detail}`);
|
|
239
262
|
}
|
|
240
263
|
return res.json();
|
|
241
264
|
}
|
|
242
265
|
async delete(url) {
|
|
243
266
|
const res = await fetch(url, { method: "DELETE" });
|
|
244
267
|
if (!res.ok) {
|
|
245
|
-
|
|
268
|
+
let detail = res.statusText;
|
|
269
|
+
try {
|
|
270
|
+
const errBody = await res.clone().json();
|
|
271
|
+
detail = errBody?.error ?? errBody?.message ?? detail;
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
throw new Error(`DELETE ${url} failed: ${detail}`);
|
|
246
275
|
}
|
|
247
276
|
}
|
|
248
277
|
};
|
|
@@ -404,7 +433,7 @@ function remoteStorage(options = {}) {
|
|
|
404
433
|
mongoDb: options.mongoDb,
|
|
405
434
|
jsonlDir: options.jsonlDir
|
|
406
435
|
};
|
|
407
|
-
const protocol = options.protocol ?? new RestStorageProtocol(baseUrl, entityId
|
|
436
|
+
const protocol = options.protocol ?? new RestStorageProtocol(baseUrl, entityId, backendConfig);
|
|
408
437
|
const storage = createStorageWithProtocol(transport, protocol);
|
|
409
438
|
const adapter = {
|
|
410
439
|
storage,
|
|
@@ -415,16 +444,65 @@ function remoteStorage(options = {}) {
|
|
|
415
444
|
};
|
|
416
445
|
return adapter;
|
|
417
446
|
}
|
|
447
|
+
|
|
448
|
+
// src/environment.ts
|
|
449
|
+
function isNode() {
|
|
450
|
+
return typeof window === "undefined" && typeof process !== "undefined";
|
|
451
|
+
}
|
|
452
|
+
function isBrowser() {
|
|
453
|
+
return typeof window !== "undefined";
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/nodeStore.ts
|
|
457
|
+
import { getDefaultStore as getDefaultStore3 } from "jotai";
|
|
458
|
+
function createNodeStore(entityId, config, options) {
|
|
459
|
+
const store = createStore(entityId, config, options);
|
|
460
|
+
const jotaiStore = getDefaultStore3();
|
|
461
|
+
async function waitForLoad(timeoutMs = 5e3) {
|
|
462
|
+
for (const atom2 of Object.values(store.atoms)) {
|
|
463
|
+
jotaiStore.sub(atom2, () => {
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return new Promise((resolve, reject) => {
|
|
467
|
+
const loadingAtoms = Object.values(store.loadingAtoms);
|
|
468
|
+
const stillLoading = () => loadingAtoms.some((atom2) => jotaiStore.get(atom2));
|
|
469
|
+
if (!stillLoading()) {
|
|
470
|
+
resolve();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const timeout = setTimeout(() => {
|
|
474
|
+
clearInterval(interval);
|
|
475
|
+
reject(new Error(`waitForLoad timed out after ${timeoutMs}ms`));
|
|
476
|
+
}, timeoutMs);
|
|
477
|
+
const interval = setInterval(() => {
|
|
478
|
+
if (!stillLoading()) {
|
|
479
|
+
clearInterval(interval);
|
|
480
|
+
clearTimeout(timeout);
|
|
481
|
+
resolve();
|
|
482
|
+
}
|
|
483
|
+
}, 10);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
atoms: store.atoms,
|
|
488
|
+
loadingAtoms: store.loadingAtoms,
|
|
489
|
+
waitForLoad
|
|
490
|
+
};
|
|
491
|
+
}
|
|
418
492
|
export {
|
|
419
493
|
HttpTransport,
|
|
420
494
|
RestStorageProtocol,
|
|
421
495
|
createMemoryStorage,
|
|
496
|
+
createNodeStore,
|
|
422
497
|
createPersistedAtom,
|
|
423
498
|
remoteStorage as createRemoteStorage,
|
|
424
499
|
createStorageFromTransport,
|
|
425
500
|
createStorageWithProtocol,
|
|
426
501
|
createStore,
|
|
502
|
+
isBrowser,
|
|
503
|
+
isNode,
|
|
427
504
|
memoryStorage,
|
|
428
505
|
remoteStorage,
|
|
429
|
-
useStore
|
|
506
|
+
useStore,
|
|
507
|
+
waitForServer
|
|
430
508
|
};
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bff-store",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A jotai-based state management library with pluggable storage adapters",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./index.d.ts",
|
|
11
|
-
"import": "./index.mjs"
|
|
12
|
-
"require": "./index.js"
|
|
11
|
+
"import": "./index.mjs"
|
|
13
12
|
},
|
|
14
13
|
"./jsonl": {
|
|
15
14
|
"types": "./storage/jsonl-entry.d.ts",
|
|
@@ -50,13 +49,11 @@
|
|
|
50
49
|
"@types/react": "^19.2.16",
|
|
51
50
|
"jotai": "^2.6.0",
|
|
52
51
|
"jsdom": "^29.1.1",
|
|
52
|
+
"mongodb": "^6.21.0",
|
|
53
53
|
"react": "^18.2.0",
|
|
54
54
|
"react-dom": "^18.2.0",
|
|
55
55
|
"tsup": "^8.0.0",
|
|
56
56
|
"typescript": "^5.0.0",
|
|
57
57
|
"vitest": "^1.0.0"
|
|
58
|
-
},
|
|
59
|
-
"dependencies": {
|
|
60
|
-
"mongodb": "^6.21.0"
|
|
61
58
|
}
|
|
62
59
|
}
|
package/dist/server/entry.d.mts
CHANGED
|
@@ -62,7 +62,7 @@ declare function createStorageHandlers(options: StorageHandlersOptions): {
|
|
|
62
62
|
handleDelete: (req: IncomingMessage, res: ServerResponse, params?: Record<string, string>) => Promise<void>;
|
|
63
63
|
handleBatchGet: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
64
64
|
handleBatchSet: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
65
|
-
handleHealth: (
|
|
65
|
+
handleHealth: (_req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
/**
|
package/dist/server/entry.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ declare function createStorageHandlers(options: StorageHandlersOptions): {
|
|
|
62
62
|
handleDelete: (req: IncomingMessage, res: ServerResponse, params?: Record<string, string>) => Promise<void>;
|
|
63
63
|
handleBatchGet: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
64
64
|
handleBatchSet: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
65
|
-
handleHealth: (
|
|
65
|
+
handleHealth: (_req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
/**
|