@signalium/query 0.1.0 → 1.0.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 (113) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/EntityMap.js +2 -2
  3. package/dist/cjs/EntityMap.js.map +1 -1
  4. package/dist/cjs/NetworkManager.js +105 -0
  5. package/dist/cjs/NetworkManager.js.map +1 -0
  6. package/dist/cjs/QueryClient.js +390 -76
  7. package/dist/cjs/QueryClient.js.map +1 -1
  8. package/dist/cjs/QueryStore.js +295 -3
  9. package/dist/cjs/QueryStore.js.map +1 -1
  10. package/dist/cjs/index.js +16 -1
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/package.json +3 -0
  13. package/dist/cjs/parseEntities.js +3 -0
  14. package/dist/cjs/parseEntities.js.map +1 -1
  15. package/dist/cjs/proxy.js +19 -0
  16. package/dist/cjs/proxy.js.map +1 -1
  17. package/dist/cjs/query.js +40 -2
  18. package/dist/cjs/query.js.map +1 -1
  19. package/dist/cjs/stores/async.js +6 -0
  20. package/dist/cjs/stores/async.js.map +1 -0
  21. package/dist/cjs/stores/sync.js +7 -0
  22. package/dist/cjs/stores/sync.js.map +1 -0
  23. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  24. package/dist/cjs/type-utils.js +3 -0
  25. package/dist/cjs/type-utils.js.map +1 -0
  26. package/dist/cjs/types.js +19 -1
  27. package/dist/cjs/types.js.map +1 -1
  28. package/dist/esm/EntityMap.js +3 -3
  29. package/dist/esm/EntityMap.js.map +1 -1
  30. package/dist/esm/NetworkManager.d.ts +48 -0
  31. package/dist/esm/NetworkManager.d.ts.map +1 -0
  32. package/dist/esm/NetworkManager.js +101 -0
  33. package/dist/esm/NetworkManager.js.map +1 -0
  34. package/dist/esm/QueryClient.d.ts +81 -25
  35. package/dist/esm/QueryClient.d.ts.map +1 -1
  36. package/dist/esm/QueryClient.js +390 -76
  37. package/dist/esm/QueryClient.js.map +1 -1
  38. package/dist/esm/QueryStore.d.ts +64 -2
  39. package/dist/esm/QueryStore.d.ts.map +1 -1
  40. package/dist/esm/QueryStore.js +293 -2
  41. package/dist/esm/QueryStore.js.map +1 -1
  42. package/dist/esm/index.d.ts +5 -3
  43. package/dist/esm/index.d.ts.map +1 -1
  44. package/dist/esm/index.js +3 -1
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/parseEntities.d.ts.map +1 -1
  47. package/dist/esm/parseEntities.js +3 -0
  48. package/dist/esm/parseEntities.js.map +1 -1
  49. package/dist/esm/proxy.d.ts +6 -0
  50. package/dist/esm/proxy.d.ts.map +1 -1
  51. package/dist/esm/proxy.js +18 -0
  52. package/dist/esm/proxy.js.map +1 -1
  53. package/dist/esm/query.d.ts +30 -29
  54. package/dist/esm/query.d.ts.map +1 -1
  55. package/dist/esm/query.js +39 -3
  56. package/dist/esm/query.js.map +1 -1
  57. package/dist/esm/stores/async.d.ts +2 -0
  58. package/dist/esm/stores/async.d.ts.map +1 -0
  59. package/dist/esm/stores/async.js +2 -0
  60. package/dist/esm/stores/async.js.map +1 -0
  61. package/dist/esm/stores/sync.d.ts +2 -0
  62. package/dist/esm/stores/sync.d.ts.map +1 -0
  63. package/dist/esm/stores/sync.js +2 -0
  64. package/dist/esm/stores/sync.js.map +1 -0
  65. package/dist/esm/type-utils.d.ts +12 -0
  66. package/dist/esm/type-utils.d.ts.map +1 -0
  67. package/dist/esm/type-utils.js +2 -0
  68. package/dist/esm/type-utils.js.map +1 -0
  69. package/dist/esm/types.d.ts +62 -5
  70. package/dist/esm/types.d.ts.map +1 -1
  71. package/dist/esm/types.js +18 -0
  72. package/dist/esm/types.js.map +1 -1
  73. package/index.d.ts +1 -0
  74. package/package.json +25 -4
  75. package/stores/async.d.ts +1 -0
  76. package/stores/async.js +15 -0
  77. package/stores/sync.d.ts +1 -0
  78. package/stores/sync.js +15 -0
  79. package/.turbo/turbo-build.log +0 -12
  80. package/ENTITY_STORE_DESIGN.md +0 -386
  81. package/dist/tsconfig.esm.tsbuildinfo +0 -1
  82. package/src/EntityMap.ts +0 -63
  83. package/src/QueryClient.ts +0 -482
  84. package/src/QueryStore.ts +0 -322
  85. package/src/__tests__/caching-persistence.test.ts +0 -983
  86. package/src/__tests__/entity-system.test.ts +0 -556
  87. package/src/__tests__/gc-time.test.ts +0 -327
  88. package/src/__tests__/mock-fetch.test.ts +0 -186
  89. package/src/__tests__/parse-entities.test.ts +0 -425
  90. package/src/__tests__/path-interpolation.test.ts +0 -225
  91. package/src/__tests__/reactivity.test.ts +0 -424
  92. package/src/__tests__/refetch-interval.test.ts +0 -262
  93. package/src/__tests__/rest-query-api.test.ts +0 -568
  94. package/src/__tests__/stale-time.test.ts +0 -357
  95. package/src/__tests__/type-to-string.test.ts +0 -129
  96. package/src/__tests__/utils.ts +0 -258
  97. package/src/__tests__/validation-edge-cases.test.ts +0 -821
  98. package/src/errors.ts +0 -124
  99. package/src/index.ts +0 -7
  100. package/src/parseEntities.ts +0 -213
  101. package/src/pathInterpolator.ts +0 -74
  102. package/src/proxy.ts +0 -257
  103. package/src/query.ts +0 -164
  104. package/src/react/__tests__/basic.test.tsx +0 -926
  105. package/src/react/__tests__/component.test.tsx +0 -984
  106. package/src/react/__tests__/utils.tsx +0 -71
  107. package/src/typeDefs.ts +0 -351
  108. package/src/types.ts +0 -132
  109. package/src/utils.ts +0 -66
  110. package/tsconfig.cjs.json +0 -14
  111. package/tsconfig.esm.json +0 -13
  112. package/tsconfig.json +0 -20
  113. package/vitest.config.ts +0 -65
@@ -1,258 +0,0 @@
1
- import { watchOnce, watcher, withContexts } from 'signalium';
2
- import { QueryClient, QueryClientContext } from '../QueryClient.js';
3
- import { QueryStore } from '../QueryStore.js';
4
- import { EntityStore } from '../EntityMap.js';
5
-
6
- // Re-export watchOnce for convenience
7
- export { watchOnce };
8
-
9
- interface MockFetchOptions {
10
- status?: number;
11
- headers?: Record<string, string>;
12
- delay?: number;
13
- error?: Error;
14
- jsonError?: Error;
15
- }
16
-
17
- interface MockFetch {
18
- (url: string, options?: RequestInit): Promise<Response>;
19
-
20
- get(url: string, response: unknown, opts?: MockFetchOptions): void;
21
- post(url: string, response: unknown, opts?: MockFetchOptions): void;
22
- put(url: string, response: unknown, opts?: MockFetchOptions): void;
23
- delete(url: string, response: unknown, opts?: MockFetchOptions): void;
24
- patch(url: string, response: unknown, opts?: MockFetchOptions): void;
25
-
26
- reset(): void;
27
- calls: Array<{ url: string; options: RequestInit }>;
28
- }
29
-
30
- interface MockRoute {
31
- url: string;
32
- method: string;
33
- response: unknown;
34
- options: MockFetchOptions;
35
- used: boolean;
36
- }
37
-
38
- /**
39
- * Creates a mock fetch function with a fluent API for setting up responses.
40
- *
41
- * @example
42
- * const fetch = createMockFetch();
43
- * fetch.get('/users/123', { id: 123, name: 'Alice' });
44
- * fetch.post('/users', { id: 456, name: 'Bob' }, { status: 201 });
45
- *
46
- * const response = await fetch('/users/123', { method: 'GET' });
47
- * const data = await response.json(); // { id: 123, name: 'Alice' }
48
- */
49
- export function createMockFetch(): MockFetch {
50
- const routes: MockRoute[] = [];
51
- const calls: Array<{ url: string; options: RequestInit }> = [];
52
-
53
- const matchRoute = (url: string, method: string): MockRoute | undefined => {
54
- const isMatch = (r: MockRoute): boolean => {
55
- if (r.method !== method) return false;
56
-
57
- // Simple pattern: check if the route URL is a prefix or matches the base path
58
- const routeBase = r.url.split('?')[0];
59
- const urlBase = url.split('?')[0];
60
-
61
- // Check if URL starts with the route (for exact matches)
62
- if (urlBase === routeBase) return true;
63
-
64
- // Check if route contains path params [...]
65
- if (r.url.includes('[')) {
66
- const routeParts = routeBase.split('/');
67
- const urlParts = urlBase.split('/');
68
-
69
- if (routeParts.length !== urlParts.length) return false;
70
-
71
- return routeParts.every((part, i) => {
72
- if (part.startsWith('[') && part.endsWith(']')) return true;
73
- return part === urlParts[i];
74
- });
75
- }
76
-
77
- return false;
78
- };
79
-
80
- // First try to find an unused match
81
- const unusedMatch = routes.find(r => !r.used && isMatch(r));
82
- if (unusedMatch) return unusedMatch;
83
-
84
- // If no unused matches, reuse the last matching route
85
- for (let i = routes.length - 1; i >= 0; i--) {
86
- if (isMatch(routes[i])) {
87
- return routes[i];
88
- }
89
- }
90
-
91
- return undefined;
92
- };
93
-
94
- const mockFetch = async (url: string, options: RequestInit = {}): Promise<Response> => {
95
- const method = (options.method || 'GET').toUpperCase();
96
-
97
- calls.push({ url, options });
98
-
99
- const route = matchRoute(url, method);
100
-
101
- if (!route) {
102
- throw new Error(
103
- `No mock response configured for ${method} ${url}\n` +
104
- `Available routes:\n${routes.map(r => ` ${r.method} ${r.url}`).join('\n')}`,
105
- );
106
- }
107
-
108
- route.used = true;
109
-
110
- if (route.options.delay) {
111
- await new Promise(resolve => setTimeout(resolve, route.options.delay));
112
- }
113
-
114
- if (route.options.error) {
115
- throw route.options.error;
116
- }
117
-
118
- const status = route.options.status ?? 200;
119
- const headers = route.options.headers ?? {};
120
-
121
- // Resolve response if it's a function
122
- const resolveResponse = async () => {
123
- if (typeof route.response === 'function') {
124
- return await route.response();
125
- }
126
- return route.response;
127
- };
128
-
129
- // Create a mock Response object
130
- const response = {
131
- ok: status >= 200 && status < 300,
132
- status,
133
- statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
134
- headers: new Headers(headers),
135
- json: async () => {
136
- if (route.options.jsonError) {
137
- throw route.options.jsonError;
138
- }
139
- return await resolveResponse();
140
- },
141
- text: async () => JSON.stringify(await resolveResponse()),
142
- blob: async () => new Blob([JSON.stringify(await resolveResponse())]),
143
- arrayBuffer: async () => new TextEncoder().encode(JSON.stringify(await resolveResponse())).buffer,
144
- clone: () => response,
145
- } as Response;
146
-
147
- return response;
148
- };
149
-
150
- const addRoute = (method: string, url: string, response: unknown, opts: MockFetchOptions = {}) => {
151
- routes.push({
152
- url,
153
- method: method.toUpperCase(),
154
- response,
155
- options: opts,
156
- used: false,
157
- });
158
- };
159
-
160
- mockFetch.get = (url: string, response: unknown, opts?: MockFetchOptions) => {
161
- addRoute('GET', url, response, opts);
162
- };
163
-
164
- mockFetch.post = (url: string, response: unknown, opts?: MockFetchOptions) => {
165
- addRoute('POST', url, response, opts);
166
- };
167
-
168
- mockFetch.put = (url: string, response: unknown, opts?: MockFetchOptions) => {
169
- addRoute('PUT', url, response, opts);
170
- };
171
-
172
- mockFetch.delete = (url: string, response: unknown, opts?: MockFetchOptions) => {
173
- addRoute('DELETE', url, response, opts);
174
- };
175
-
176
- mockFetch.patch = (url: string, response: unknown, opts?: MockFetchOptions) => {
177
- addRoute('PATCH', url, response, opts);
178
- };
179
-
180
- mockFetch.reset = () => {
181
- routes.length = 0;
182
- calls.length = 0;
183
- };
184
-
185
- mockFetch.calls = calls;
186
-
187
- return mockFetch as MockFetch;
188
- }
189
-
190
- /**
191
- * Creates a test watcher that tracks all values emitted by a reactive function.
192
- * Returns an object with the values array and an unsubscribe function.
193
- *
194
- * Note: This creates a continuous watcher. For one-time execution, use `watchOnce` instead.
195
- */
196
- export function createTestWatcher<T>(fn: () => T): {
197
- values: T[];
198
- unsub: () => void;
199
- } {
200
- const values: T[] = [];
201
-
202
- const w = watcher(() => {
203
- const value = fn();
204
- values.push(value);
205
- });
206
-
207
- const unsub = w.addListener(() => {});
208
-
209
- return { values, unsub };
210
- }
211
-
212
- /**
213
- * Test helper that combines query client context injection and automatic watcher cleanup.
214
- * Wraps the test in a watcher and awaits it, keeping relays active during the test.
215
- *
216
- * @example
217
- * await testWithClient(client, async () => {
218
- * const relay = getItem({ id: '1' });
219
- * await relay;
220
- * expect(relay.value).toBeDefined();
221
- * // Watcher is automatically cleaned up
222
- * });
223
- */
224
- export async function testWithClient(client: QueryClient, fn: () => Promise<void>): Promise<void> {
225
- return withContexts([[QueryClientContext, client]], () => watchOnce(fn));
226
- }
227
-
228
- export const sleep = (ms: number = 0) =>
229
- new Promise(resolve => {
230
- setTimeout(() => {
231
- resolve(true);
232
- }, ms);
233
- });
234
-
235
- /**
236
- * Test helper to access the internal store of a QueryClient.
237
- * Uses bracket notation to bypass TypeScript access checks.
238
- */
239
- export function getClientStore(client: QueryClient): QueryStore {
240
- return client['store'];
241
- }
242
-
243
- /**
244
- * Test helper to access the internal entity map of a QueryClient.
245
- * Uses bracket notation to bypass TypeScript access checks.
246
- */
247
- export function getClientEntityMap(client: QueryClient): EntityStore {
248
- return client['entityMap'];
249
- }
250
-
251
- /**
252
- * Test helper to get the size of the entity map.
253
- * EntityStore doesn't expose a size property, so we access the internal map.
254
- */
255
- export function getEntityMapSize(client: QueryClient): number {
256
- const entityMap = getClientEntityMap(client);
257
- return entityMap['map'].size;
258
- }