bff-store 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.
Files changed (65) hide show
  1. package/.claude/settings.local.json +45 -0
  2. package/CONTEXT.md +53 -0
  3. package/README.md +223 -0
  4. package/dist/cli.js +32577 -0
  5. package/dist/index.d.mts +232 -0
  6. package/dist/index.d.ts +232 -0
  7. package/dist/index.mjs +430 -0
  8. package/dist/package.json +62 -0
  9. package/dist/server/entry.d.mts +94 -0
  10. package/dist/server/entry.d.ts +94 -0
  11. package/dist/server/entry.js +573 -0
  12. package/dist/server/entry.mjs +533 -0
  13. package/dist/server-V7WCW4ZB.mjs +530 -0
  14. package/dist/storage/jsonl-entry.d.mts +42 -0
  15. package/dist/storage/jsonl-entry.d.ts +42 -0
  16. package/dist/storage/jsonl-entry.js +112 -0
  17. package/dist/storage/jsonl-entry.mjs +74 -0
  18. package/dist/storage/mongodb-entry.d.mts +40 -0
  19. package/dist/storage/mongodb-entry.d.ts +40 -0
  20. package/dist/storage/mongodb-entry.js +114 -0
  21. package/dist/storage/mongodb-entry.mjs +86 -0
  22. package/docs/BUG_DIAGNOSIS_REMOTE_STORAGE_OPTIONS.md +104 -0
  23. package/docs/BUG_FIX_REMOTE_STORAGE_OPTIONS.md +63 -0
  24. package/docs/BUG_FIX_SESSION_2026-06-03.md +171 -0
  25. package/docs/IMPLEMENTATION.md +333 -0
  26. package/docs/PLAN.md +153 -0
  27. package/docs/REMOTE_STORAGE_CONFIG.md +125 -0
  28. package/docs/SIDECAR_SERVER.md +184 -0
  29. package/package.json +62 -0
  30. package/scripts/adapt-dist-package.js +33 -0
  31. package/src/atomCreator.ts +76 -0
  32. package/src/createStore.ts +77 -0
  33. package/src/debouncer.ts +84 -0
  34. package/src/index.ts +35 -0
  35. package/src/server/cli.ts +62 -0
  36. package/src/server/entityIdCache.ts +57 -0
  37. package/src/server/entry.ts +12 -0
  38. package/src/server/handlers.ts +271 -0
  39. package/src/server/index.ts +182 -0
  40. package/src/server/router.ts +74 -0
  41. package/src/server.ts +5 -0
  42. package/src/storage/adapters/remoteStorage.ts +70 -0
  43. package/src/storage/base.ts +28 -0
  44. package/src/storage/index.ts +9 -0
  45. package/src/storage/jsonl-entry.ts +9 -0
  46. package/src/storage/jsonl.ts +111 -0
  47. package/src/storage/memory.ts +49 -0
  48. package/src/storage/mongodb-entry.ts +9 -0
  49. package/src/storage/mongodb.ts +132 -0
  50. package/src/storage/protocol.ts +170 -0
  51. package/src/storage/transport.ts +95 -0
  52. package/src/types.ts +76 -0
  53. package/src/useStore.ts +83 -0
  54. package/tests/atomCreator.test.ts +153 -0
  55. package/tests/createStore.test.ts +126 -0
  56. package/tests/debouncer.test.ts +125 -0
  57. package/tests/server.test.ts +158 -0
  58. package/tests/storage/jsonl.test.ts +132 -0
  59. package/tests/storage/memory.test.ts +101 -0
  60. package/tests/storage/mongodb.test.ts +40 -0
  61. package/tests/storage/remoteStorage.test.ts +126 -0
  62. package/tests/useStore.test.tsx +147 -0
  63. package/tsconfig.json +18 -0
  64. package/tsup.config.ts +53 -0
  65. package/vitest.config.ts +14 -0
@@ -0,0 +1,147 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import React from 'react';
3
+ import { renderHook, act } from '@testing-library/react';
4
+ import { useStore } from '../src/useStore';
5
+ import { createStore } from '../src/createStore';
6
+ import { memoryStorage } from '../src/storage/memory';
7
+
8
+ const originalWarn = console.warn;
9
+ beforeEach(() => {
10
+ console.warn = () => {};
11
+ });
12
+ afterEach(() => {
13
+ console.warn = originalWarn;
14
+ });
15
+
16
+ describe('useStore', () => {
17
+ describe('basic usage', () => {
18
+ it('should return store values and setters', () => {
19
+ const adapter = memoryStorage();
20
+ const config = [
21
+ { key: 'theme', defaultValue: 'dark' },
22
+ { key: 'name', defaultValue: 'John' },
23
+ ] as const;
24
+
25
+ const store = createStore('entity-1', config, { storage: adapter });
26
+
27
+ const { result } = renderHook(() => useStore(store));
28
+
29
+ expect(result.current.theme).toBe('dark');
30
+ expect(result.current.name).toBe('John');
31
+ expect(typeof result.current.setTheme).toBe('function');
32
+ expect(typeof result.current.setName).toBe('function');
33
+ });
34
+
35
+ it('should include isLoading', () => {
36
+ const adapter = memoryStorage();
37
+ const config = [{ key: 'theme', defaultValue: 'dark' }] as const;
38
+
39
+ const store = createStore('entity-1', config, { storage: adapter });
40
+
41
+ const { result } = renderHook(() => useStore(store));
42
+
43
+ expect('isLoading' in result.current).toBe(true);
44
+ });
45
+
46
+ it('should update value when atom changes', async () => {
47
+ const adapter = memoryStorage();
48
+ const config = [{ key: 'theme', defaultValue: 'dark' }] as const;
49
+
50
+ const store = createStore('entity-1', config, { storage: adapter });
51
+
52
+ const { result } = renderHook(() => useStore(store));
53
+
54
+ await act(async () => {
55
+ result.current.setTheme('light');
56
+ });
57
+
58
+ expect(result.current.theme).toBe('light');
59
+ });
60
+ });
61
+
62
+ describe('setter naming', () => {
63
+ it('should derive setter name by capitalizing first letter', () => {
64
+ const adapter = memoryStorage();
65
+ const config = [{ key: 'theme', defaultValue: 'dark' }] as const;
66
+
67
+ const store = createStore('entity-1', config, { storage: adapter });
68
+
69
+ const { result } = renderHook(() => useStore(store));
70
+
71
+ expect(typeof result.current.setTheme).toBe('function');
72
+ });
73
+
74
+ it('should handle camelCase keys', () => {
75
+ const adapter = memoryStorage();
76
+ const config = [{ key: 'userName', defaultValue: '' }] as const;
77
+
78
+ const store = createStore('entity-1', config, { storage: adapter });
79
+
80
+ const { result } = renderHook(() => useStore(store));
81
+
82
+ expect(typeof result.current.setUserName).toBe('function');
83
+ });
84
+
85
+ it('should handle single character keys', () => {
86
+ const adapter = memoryStorage();
87
+ const config = [{ key: 'x', defaultValue: 0 }] as const;
88
+
89
+ const store = createStore('entity-1', config, { storage: adapter });
90
+
91
+ const { result } = renderHook(() => useStore(store));
92
+
93
+ expect(typeof result.current.setX).toBe('function');
94
+ });
95
+ });
96
+
97
+ describe('with empty config', () => {
98
+ it('should return only isLoading', () => {
99
+ const adapter = memoryStorage();
100
+ const config = [] as const;
101
+
102
+ const store = createStore('entity-1', config, { storage: adapter });
103
+
104
+ const { result } = renderHook(() => useStore(store));
105
+
106
+ expect(result.current.isLoading).toBeDefined();
107
+ const keys = Object.keys(result.current).filter((k) => k !== 'isLoading');
108
+ expect(keys).toHaveLength(0);
109
+ });
110
+ });
111
+
112
+ describe('loading state', () => {
113
+ it('should initially show loading state', () => {
114
+ const adapter = memoryStorage();
115
+ const config = [{ key: 'theme', defaultValue: 'dark' }] as const;
116
+
117
+ const store = createStore('entity-1', config, { storage: adapter });
118
+
119
+ const { result } = renderHook(() => useStore(store));
120
+
121
+ expect(result.current.isLoading).toBe(true);
122
+ });
123
+ });
124
+
125
+ describe('multiple atoms', () => {
126
+ it('should handle many atoms', () => {
127
+ const adapter = memoryStorage();
128
+ const config = [
129
+ { key: 'a', defaultValue: 1 },
130
+ { key: 'b', defaultValue: 2 },
131
+ { key: 'c', defaultValue: 3 },
132
+ { key: 'd', defaultValue: 4 },
133
+ { key: 'e', defaultValue: 5 },
134
+ ] as const;
135
+
136
+ const store = createStore('entity-1', config, { storage: adapter });
137
+
138
+ const { result } = renderHook(() => useStore(store));
139
+
140
+ expect(result.current.a).toBe(1);
141
+ expect(result.current.b).toBe(2);
142
+ expect(result.current.c).toBe(3);
143
+ expect(result.current.d).toBe(4);
144
+ expect(result.current.e).toBe(5);
145
+ });
146
+ });
147
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "lib": ["ES2020", "DOM"],
6
+ "moduleResolution": "bundler",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "declaration": true,
12
+ "declarationMap": true,
13
+ "outDir": "./dist",
14
+ "rootDir": "./src"
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig([
4
+ // Main library entry - clean build without CLI or server
5
+ {
6
+ entry: ['src/index.ts'],
7
+ format: ['esm'],
8
+ dts: true,
9
+ outDir: 'dist',
10
+ splitting: true,
11
+ sourcemap: false,
12
+ clean: true,
13
+ external: ['react', 'jotai', 'mongodb'],
14
+ },
15
+ // MongoDB storage adapter - separate bundle with mongodb included
16
+ {
17
+ entry: ['src/storage/mongodb-entry.ts'],
18
+ format: ['cjs', 'esm'],
19
+ dts: true,
20
+ outDir: 'dist/storage',
21
+ splitting: false,
22
+ sourcemap: false,
23
+ external: ['react', 'jotai'],
24
+ },
25
+ // JSONL storage adapter - separate bundle with Node.js dependencies
26
+ {
27
+ entry: ['src/storage/jsonl-entry.ts'],
28
+ format: ['cjs', 'esm'],
29
+ dts: true,
30
+ outDir: 'dist/storage',
31
+ splitting: false,
32
+ sourcemap: false,
33
+ external: ['react', 'jotai'],
34
+ },
35
+ // Server entry - separate bundle with all dependencies bundled
36
+ {
37
+ entry: ['src/server/entry.ts'],
38
+ format: ['cjs', 'esm'],
39
+ dts: true,
40
+ outDir: 'dist/server',
41
+ splitting: false,
42
+ sourcemap: false,
43
+ external: ['react', 'jotai'],
44
+ },
45
+ // Server CLI entry - separate CJS bundle with all dependencies
46
+ {
47
+ entry: ['src/server/cli.ts'],
48
+ format: ['cjs'],
49
+ dts: false,
50
+ outDir: 'dist',
51
+ noExternal: ['mongodb'],
52
+ },
53
+ ]);
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'jsdom',
7
+ include: ['tests/**/*.test.{ts,tsx}'],
8
+ coverage: {
9
+ reporter: ['text', 'json', 'html'],
10
+ include: ['src/**/*.ts'],
11
+ exclude: ['src/index.ts'],
12
+ },
13
+ },
14
+ });