lazypock 0.2.0 → 0.4.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 +135 -0
- package/dist/index.cjs +509 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +284 -1
- package/dist/index.d.ts +284 -1
- package/dist/index.global.js +507 -10
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +507 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cache.ts +314 -0
- package/src/collection.ts +71 -5
- package/src/collections.ts +41 -5
- package/src/http.ts +190 -0
- package/src/index.ts +5 -0
- package/src/lazypock.ts +144 -0
- package/src/types.ts +23 -0
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,21 @@ interface RequestOptions {
|
|
|
50
50
|
signal?: AbortSignal;
|
|
51
51
|
/** Custom fetch implementation (for RN or test mocking) */
|
|
52
52
|
fetch?: typeof globalThis.fetch;
|
|
53
|
+
/**
|
|
54
|
+
* Cache control for this request (see {@link CacheRequestOptions}).
|
|
55
|
+
* Resolved against the client's global cache config when unset.
|
|
56
|
+
*/
|
|
57
|
+
cache?: boolean | number | {
|
|
58
|
+
ttl?: number;
|
|
59
|
+
key?: string;
|
|
60
|
+
};
|
|
61
|
+
/** Alias of `cache: <ms>` — cache this GET for `ttl` milliseconds. */
|
|
62
|
+
ttl?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Extra cache namespaces to invalidate when this mutation succeeds.
|
|
65
|
+
* The current collection is always invalidated automatically.
|
|
66
|
+
*/
|
|
67
|
+
invalidate?: string[];
|
|
53
68
|
/**
|
|
54
69
|
* Request identifier used by the auto-cancellation mechanism.
|
|
55
70
|
*
|
|
@@ -72,6 +87,17 @@ interface RequestOptions {
|
|
|
72
87
|
* Alias of `requestKey` (PocketBase `$cancelKey` compat).
|
|
73
88
|
*/
|
|
74
89
|
cancelKey?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Coalesce concurrent identical requests (same `requestKey`) onto a single
|
|
92
|
+
* in-flight promise instead of aborting the earlier one.
|
|
93
|
+
*
|
|
94
|
+
* When enabled, a request arriving while another with the same key is still
|
|
95
|
+
* pending awaits the same result — no duplicate network request, and the
|
|
96
|
+
* caller of the first request never sees an abort rejection.
|
|
97
|
+
*
|
|
98
|
+
* @default false (auto-cancellation aborts the earlier duplicate)
|
|
99
|
+
*/
|
|
100
|
+
singleFlight?: boolean;
|
|
75
101
|
}
|
|
76
102
|
declare class ApiError extends Error {
|
|
77
103
|
readonly data: unknown;
|
|
@@ -158,16 +184,160 @@ declare class AuthStore {
|
|
|
158
184
|
private notify;
|
|
159
185
|
}
|
|
160
186
|
|
|
187
|
+
/** Options for enabling/customising the client's query cache. */
|
|
188
|
+
interface CacheConfig {
|
|
189
|
+
/**
|
|
190
|
+
* Master switch. When `true`, readable GET requests are cached with the
|
|
191
|
+
* default TTL unless a request opts out via `{ cache: false }`.
|
|
192
|
+
*
|
|
193
|
+
* When `false` (default), caching is disabled unless a request opts in
|
|
194
|
+
* via `{ cache: true }` or `{ ttl: <ms> }`. Opt-in works regardless.
|
|
195
|
+
*/
|
|
196
|
+
enabled?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Default time-to-live for cached entries, in milliseconds.
|
|
199
|
+
* @default 60_000 (1 minute)
|
|
200
|
+
*/
|
|
201
|
+
defaultTTL?: number;
|
|
202
|
+
/**
|
|
203
|
+
* Optional persistence backend (same interface as AuthStore's storage).
|
|
204
|
+
* Defaults to an in-memory Map — swap for `localStorage` / `AsyncStorage`
|
|
205
|
+
* to keep the cache across page reloads / app restarts.
|
|
206
|
+
*/
|
|
207
|
+
store?: StorageAdapter;
|
|
208
|
+
/**
|
|
209
|
+
* Max number of entries to keep in memory (LRU eviction).
|
|
210
|
+
* @default 500
|
|
211
|
+
*/
|
|
212
|
+
maxEntries?: number;
|
|
213
|
+
/**
|
|
214
|
+
* When true and the client has an active realtime subscription for a
|
|
215
|
+
* collection, inbound create/update/delete events invalidate that
|
|
216
|
+
* collection's cached entries automatically.
|
|
217
|
+
* @default false — only local mutations invalidate (explicit + predictable)
|
|
218
|
+
*/
|
|
219
|
+
invalidateOnRealtime?: boolean;
|
|
220
|
+
}
|
|
221
|
+
/** Per-request cache controls (mixed into {@link RequestOptions}). */
|
|
222
|
+
interface CacheRequestOptions {
|
|
223
|
+
/**
|
|
224
|
+
* Cache control for this request:
|
|
225
|
+
* - `true` — cache with the default (or global) TTL
|
|
226
|
+
* - `false` — always fetch fresh, bypass cache (and don't store the result)
|
|
227
|
+
* - a number — cache with this TTL in milliseconds
|
|
228
|
+
* - an object — `{ ttl, key }` for finer control
|
|
229
|
+
*
|
|
230
|
+
* When unset, the global `cache.enabled` flag decides.
|
|
231
|
+
*/
|
|
232
|
+
cache?: boolean | number | {
|
|
233
|
+
ttl?: number;
|
|
234
|
+
key?: string;
|
|
235
|
+
};
|
|
236
|
+
/** Alias of `cache: <ms>` (convenience, reads naturally). */
|
|
237
|
+
ttl?: number;
|
|
238
|
+
/**
|
|
239
|
+
* Extra cache namespaces to invalidate when this mutation succeeds.
|
|
240
|
+
* The current collection is always invalidated automatically.
|
|
241
|
+
* @example create({ ... }, { invalidate: ['users'] })
|
|
242
|
+
*/
|
|
243
|
+
invalidate?: string[];
|
|
244
|
+
}
|
|
245
|
+
/** LRU-ish memory store + optional persistent adapter hybrid. */
|
|
246
|
+
declare class CacheStore {
|
|
247
|
+
private memory;
|
|
248
|
+
private readonly ttl;
|
|
249
|
+
private readonly persistence?;
|
|
250
|
+
private readonly maxEntries;
|
|
251
|
+
private hits;
|
|
252
|
+
private misses;
|
|
253
|
+
private namespaceEntries;
|
|
254
|
+
/** Key → set of prefix tags registered for that key (e.g. `getList:posts`). */
|
|
255
|
+
private prefixEntries;
|
|
256
|
+
constructor(config?: {
|
|
257
|
+
defaultTTL?: number;
|
|
258
|
+
store?: StorageAdapter;
|
|
259
|
+
maxEntries?: number;
|
|
260
|
+
});
|
|
261
|
+
/** Resolve the effective TTL: request override → global default. */
|
|
262
|
+
private resolveTTL;
|
|
263
|
+
/**
|
|
264
|
+
* Read a cached value. Fast sync path (memory) with async persistence
|
|
265
|
+
* fallback for adapters whose `get` returns a Promise.
|
|
266
|
+
* @param key Cache key (e.g. `"GET /posts?page=1"`).
|
|
267
|
+
* @returns The cached value, or undefined when absent/expired (the hit is
|
|
268
|
+
* cleared on expiry so a stale value is never served).
|
|
269
|
+
*/
|
|
270
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
271
|
+
/**
|
|
272
|
+
* Store a value.
|
|
273
|
+
* @param key Cache key.
|
|
274
|
+
* @param value The response payload.
|
|
275
|
+
* @param ttlOverride Optional TTL override (ms).
|
|
276
|
+
* @param namespace Optional namespace for group invalidation.
|
|
277
|
+
*/
|
|
278
|
+
set(key: string, value: unknown, ttlOverride?: number, namespace?: string, tags?: string[]): void;
|
|
279
|
+
/**
|
|
280
|
+
* Invalidate entries belonging to a namespace (e.g. a collection name).
|
|
281
|
+
* Also clears the namespace index entry.
|
|
282
|
+
*/
|
|
283
|
+
invalidate(namespace: string): void;
|
|
284
|
+
/** Remove a single key. */
|
|
285
|
+
delete(key: string): void;
|
|
286
|
+
/**
|
|
287
|
+
* Delete every entry whose key starts with `prefix`.
|
|
288
|
+
*
|
|
289
|
+
* Useful for fine-grained invalidation, e.g.:
|
|
290
|
+
* ```ts
|
|
291
|
+
* client.cache.deleteByPrefix('getList:posts'); // delete all getList cache
|
|
292
|
+
* client.cache.deleteByPrefix('getOne:posts'); // delete all getOne cache
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
deleteByPrefix(prefix: string): void;
|
|
296
|
+
/** Drop every cached entry (memory + persistence). */
|
|
297
|
+
clear(): void;
|
|
298
|
+
/** Cache hit/miss/entry statistics. */
|
|
299
|
+
stats(): {
|
|
300
|
+
hits: number;
|
|
301
|
+
misses: number;
|
|
302
|
+
entries: number;
|
|
303
|
+
};
|
|
304
|
+
private persistKey;
|
|
305
|
+
private readPersisted;
|
|
306
|
+
}
|
|
307
|
+
/** Resolve per-request cache options into a usable directive. */
|
|
308
|
+
declare function resolveCacheDirective(opts?: {
|
|
309
|
+
cache?: boolean | number | {
|
|
310
|
+
ttl?: number;
|
|
311
|
+
key?: string;
|
|
312
|
+
};
|
|
313
|
+
ttl?: number;
|
|
314
|
+
}): {
|
|
315
|
+
enabled: boolean;
|
|
316
|
+
ttl?: number;
|
|
317
|
+
key?: string;
|
|
318
|
+
} | null;
|
|
319
|
+
|
|
161
320
|
declare class HttpClient {
|
|
162
321
|
private baseUrl;
|
|
163
322
|
private authStore;
|
|
164
323
|
private defaultFetch;
|
|
324
|
+
/** Optional query cache store (wired when the client enables caching). */
|
|
325
|
+
private cache?;
|
|
326
|
+
/** Master switch resolved from CacheConfig.enabled. */
|
|
327
|
+
private cacheEnabled;
|
|
165
328
|
/**
|
|
166
329
|
* Abort controllers for in-flight requests, keyed by their cancellation key
|
|
167
330
|
* (default `METHOD path`). A new request with the same key aborts the
|
|
168
331
|
* previous one — PocketBase-style auto-cancellation of duplicated requests.
|
|
169
332
|
*/
|
|
170
333
|
private cancelControllers;
|
|
334
|
+
/**
|
|
335
|
+
* In-flight request promises, keyed by cancellation key. When auto-cancellation
|
|
336
|
+
* would abort a pending duplicate, the newer request instead awaits the same
|
|
337
|
+
* promise — single-flight coalescing (no duplicate network request, no
|
|
338
|
+
* spurious abort rejection for the caller).
|
|
339
|
+
*/
|
|
340
|
+
private inflight;
|
|
171
341
|
/** Global toggle for the auto-cancellation behaviour (default: on). */
|
|
172
342
|
private enableAutoCancellation;
|
|
173
343
|
/**
|
|
@@ -175,6 +345,13 @@ declare class HttpClient {
|
|
|
175
345
|
* @param authStore The auth store providing the token for Authorization headers.
|
|
176
346
|
*/
|
|
177
347
|
constructor(baseUrl: string, authStore: AuthStore);
|
|
348
|
+
/**
|
|
349
|
+
* Attach a cache store + master switch.
|
|
350
|
+
* Called by the client constructor when cache config is present.
|
|
351
|
+
*/
|
|
352
|
+
setCache(cache: CacheStore, enabled: boolean): void;
|
|
353
|
+
/** Whether the global cache flag is on (requests opt in/out individually too). */
|
|
354
|
+
get cacheIsEnabled(): boolean;
|
|
178
355
|
private refreshAuth;
|
|
179
356
|
/**
|
|
180
357
|
* Globally enable or disable auto-cancellation of duplicated pending requests.
|
|
@@ -204,7 +381,31 @@ declare class HttpClient {
|
|
|
204
381
|
* @throws {ApiError} On non-2xx responses or when the request is aborted
|
|
205
382
|
* (aborted requests throw an `ApiError` with `isAbort === true`).
|
|
206
383
|
*/
|
|
384
|
+
/** Invalidate a namespace (collection name). No-op when cache is off. */
|
|
385
|
+
invalidateCache(namespace: string): void;
|
|
386
|
+
/** Current cache statistics (hits/misses/entries), or null when disabled. */
|
|
387
|
+
cacheStats(): {
|
|
388
|
+
hits: number;
|
|
389
|
+
misses: number;
|
|
390
|
+
entries: number;
|
|
391
|
+
} | null;
|
|
207
392
|
request<T = unknown>(method: Method, path: string, body?: unknown, options?: RequestOptions): Promise<T | null>;
|
|
393
|
+
/**
|
|
394
|
+
* Execute the actual HTTP request (fetch + parse + cache). Called by {@link request}
|
|
395
|
+
* as the inner in-flight unit so single-flight callers can reuse the promise.
|
|
396
|
+
*/
|
|
397
|
+
private doRequest;
|
|
398
|
+
/** Build a token-scoped cache key: `METHOD path|token-hash|params`. */
|
|
399
|
+
private cacheKeyFor;
|
|
400
|
+
/** Best-effort namespace (collection name) from a REST path. */
|
|
401
|
+
private namespaceFromPath;
|
|
402
|
+
/**
|
|
403
|
+
* Semantic prefix tags for `deleteByPrefix`, derived from the REST shape:
|
|
404
|
+
* - `/{collection}?...` → `getList:{collection}`
|
|
405
|
+
* - `/{collection}/{id}` → `getOne:{collection}`
|
|
406
|
+
* - `/collections?...` / `/collections/{id}` → `collections:getList` / `collections:getOne`
|
|
407
|
+
*/
|
|
408
|
+
private cacheTagsFor;
|
|
208
409
|
/**
|
|
209
410
|
* HTTP GET.
|
|
210
411
|
* @param path URL path.
|
|
@@ -681,6 +882,26 @@ declare function fieldTypeKind(field: SchemaField): FieldTypeKind;
|
|
|
681
882
|
*/
|
|
682
883
|
declare function schemaFieldType(field: SchemaField): unknown;
|
|
683
884
|
|
|
885
|
+
/**
|
|
886
|
+
* Callable cache namespace: `client.cache(config)` configures, and
|
|
887
|
+
* `client.cache.deleteByPrefix(...)` etc. manage cached entries.
|
|
888
|
+
*/
|
|
889
|
+
interface CacheController {
|
|
890
|
+
/** Configure the query cache at runtime. */
|
|
891
|
+
(config?: CacheConfig): LazypockClient;
|
|
892
|
+
/** Delete every entry whose key starts with `prefix` (e.g. `getList:posts`). */
|
|
893
|
+
deleteByPrefix(prefix: string): void;
|
|
894
|
+
/** Invalidate a collection's cached entries (alias of invalidateCache). */
|
|
895
|
+
invalidate(namespace: string): void;
|
|
896
|
+
/** Drop every cached entry. */
|
|
897
|
+
clear(): void;
|
|
898
|
+
/** Cache hit/miss/entry stats, or null when never configured. */
|
|
899
|
+
stats(): {
|
|
900
|
+
hits: number;
|
|
901
|
+
misses: number;
|
|
902
|
+
entries: number;
|
|
903
|
+
} | null;
|
|
904
|
+
}
|
|
684
905
|
/** Options for constructing a {@link LazypockClient}. */
|
|
685
906
|
interface LazypockClientOptions {
|
|
686
907
|
/** API base URL (e.g. 'http://localhost:4000/api') */
|
|
@@ -691,6 +912,24 @@ interface LazypockClientOptions {
|
|
|
691
912
|
authStore?: AuthStore;
|
|
692
913
|
/** Real-time service for Phoenix Channel WebSocket subscriptions */
|
|
693
914
|
realtime?: RealtimeService;
|
|
915
|
+
/**
|
|
916
|
+
* Query cache configuration. Disabled by default.
|
|
917
|
+
*
|
|
918
|
+
* ```ts
|
|
919
|
+
* const client = createClient({
|
|
920
|
+
* baseUrl: '...',
|
|
921
|
+
* cache: {
|
|
922
|
+
* enabled: true,
|
|
923
|
+
* defaultTTL: 30_000,
|
|
924
|
+
* store: myStorage, // optional persistence (same interface as auth)
|
|
925
|
+
* },
|
|
926
|
+
* });
|
|
927
|
+
* ```
|
|
928
|
+
*
|
|
929
|
+
* When enabled, readable GETs are cached. Requests can opt out via
|
|
930
|
+
* `{ cache: false }`, or opt in with a custom TTL via `{ ttl: ms }`.
|
|
931
|
+
*/
|
|
932
|
+
cache?: CacheConfig;
|
|
694
933
|
/**
|
|
695
934
|
* Optional schema types for generating typed services at runtime.
|
|
696
935
|
* When provided, `collection()` returns a service whose create/update
|
|
@@ -724,6 +963,9 @@ declare class LazypockClient {
|
|
|
724
963
|
readonly files: FilesService;
|
|
725
964
|
private collectionCache;
|
|
726
965
|
private schemaByName?;
|
|
966
|
+
private cacheStore?;
|
|
967
|
+
/** Namespace → realtime unsubscribe; used for realtime-driven invalidation. */
|
|
968
|
+
private realtimeInvalidators;
|
|
727
969
|
/**
|
|
728
970
|
* Create a new Lazypock client.
|
|
729
971
|
* @param options Configuration options.
|
|
@@ -778,6 +1020,47 @@ declare class LazypockClient {
|
|
|
778
1020
|
cancelRequest(requestKey: string): this;
|
|
779
1021
|
/** Abort all pending requests. */
|
|
780
1022
|
cancelAllRequests(): this;
|
|
1023
|
+
/**
|
|
1024
|
+
* Configure the query cache at runtime (also a namespace for cache
|
|
1025
|
+
* management methods).
|
|
1026
|
+
*
|
|
1027
|
+
* ```ts
|
|
1028
|
+
* client.cache({ enabled: true, defaultTTL: 30_000 });
|
|
1029
|
+
* client.cache.deleteByPrefix('getList:posts'); // all list caches for posts
|
|
1030
|
+
* client.cache.deleteByPrefix('getOne:posts'); // all one-record caches
|
|
1031
|
+
* ```
|
|
1032
|
+
*
|
|
1033
|
+
* When enabled, GET requests cache their payload; mutations invalidate the
|
|
1034
|
+
* affected collection automatically. Individual requests can opt out with
|
|
1035
|
+
* `{ cache: false }` or override the TTL with `{ ttl: ms }`.
|
|
1036
|
+
*/
|
|
1037
|
+
readonly cache: CacheController;
|
|
1038
|
+
/**
|
|
1039
|
+
* Drop every cached entry (all collections / namespaces).
|
|
1040
|
+
* Also disables realtime-driven invalidation subscriptions.
|
|
1041
|
+
*/
|
|
1042
|
+
clearCache(): this;
|
|
1043
|
+
/**
|
|
1044
|
+
* Invalidate cached entries for a collection (or custom namespace).
|
|
1045
|
+
* Runs automatically on mutations — call explicitly when data changed
|
|
1046
|
+
* out-of-band (e.g. another client wrote to the same collection).
|
|
1047
|
+
*/
|
|
1048
|
+
invalidateCache(namespace: string): this;
|
|
1049
|
+
/**
|
|
1050
|
+
* Cache hit/miss/entry statistics.
|
|
1051
|
+
* Returns null when caching was never configured.
|
|
1052
|
+
*/
|
|
1053
|
+
cacheStats(): {
|
|
1054
|
+
hits: number;
|
|
1055
|
+
misses: number;
|
|
1056
|
+
entries: number;
|
|
1057
|
+
} | null;
|
|
1058
|
+
/**
|
|
1059
|
+
* Subscribe a collection's cache to realtime invalidation: any inbound
|
|
1060
|
+
* create/update/delete event for the collection clears its cached entries.
|
|
1061
|
+
* Returns an unsubscribe function.
|
|
1062
|
+
*/
|
|
1063
|
+
invalidateCacheOnRealtime(collectionName: string): () => void;
|
|
781
1064
|
/** Check whether any superuser exists (for login vs setup screen routing). */
|
|
782
1065
|
checkSuperuser(): Promise<{
|
|
783
1066
|
has_superuser: boolean;
|
|
@@ -870,4 +1153,4 @@ declare class TypedClient<TCollections extends LazypockCollections = LazypockCol
|
|
|
870
1153
|
*/
|
|
871
1154
|
declare function createClient<TCollections extends LazypockCollections = LazypockCollections>(options: LazypockClientOptions): TypedClient<TCollections>;
|
|
872
1155
|
|
|
873
|
-
export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CollectionSchema, CollectionService, type CollectionsMessage, CollectionsService, type CreateData, type FileRecord, FilesService, HttpClient, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, type RealtimeCallback, type RealtimeMessage, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, getScaleUrl, getThumbUrl, schemaFieldType, wsUrlFromBaseUrl };
|
|
1156
|
+
export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CacheConfig, type CacheRequestOptions, CacheStore, type CollectionSchema, CollectionService, type CollectionsMessage, CollectionsService, type CreateData, type FileRecord, FilesService, HttpClient, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, type RealtimeCallback, type RealtimeMessage, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, getScaleUrl, getThumbUrl, resolveCacheDirective, schemaFieldType, wsUrlFromBaseUrl };
|