lazypock 0.2.0 → 0.3.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 +107 -0
- package/dist/index.cjs +404 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +261 -1
- package/dist/index.d.ts +261 -1
- package/dist/index.global.js +402 -3
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +402 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cache.ts +314 -0
- package/src/collection.ts +26 -3
- package/src/http.ts +126 -0
- package/src/index.ts +5 -0
- package/src/lazypock.ts +144 -0
- package/src/types.ts +12 -0
package/src/lazypock.ts
CHANGED
|
@@ -32,6 +32,8 @@ import { CollectionsService } from "./collections";
|
|
|
32
32
|
import type { CollectionSchema, SchemaField } from "./schema";
|
|
33
33
|
import { generateTypes, collectionTypeName } from "./codegen";
|
|
34
34
|
import { fieldTypeScriptType, fieldTypeKind, schemaFieldType } from "./typegen";
|
|
35
|
+
import { CacheStore, type CacheConfig, type CacheRequestOptions } from "./cache";
|
|
36
|
+
import { resolveCacheDirective } from "./cache";
|
|
35
37
|
|
|
36
38
|
export {
|
|
37
39
|
AuthStore,
|
|
@@ -65,6 +67,25 @@ export type {
|
|
|
65
67
|
CollectionSchema,
|
|
66
68
|
SchemaField,
|
|
67
69
|
};
|
|
70
|
+
export type { CacheConfig, CacheRequestOptions };
|
|
71
|
+
export { CacheStore, resolveCacheDirective };
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Callable cache namespace: `client.cache(config)` configures, and
|
|
75
|
+
* `client.cache.deleteByPrefix(...)` etc. manage cached entries.
|
|
76
|
+
*/
|
|
77
|
+
export interface CacheController {
|
|
78
|
+
/** Configure the query cache at runtime. */
|
|
79
|
+
(config?: CacheConfig): LazypockClient;
|
|
80
|
+
/** Delete every entry whose key starts with `prefix` (e.g. `getList:posts`). */
|
|
81
|
+
deleteByPrefix(prefix: string): void;
|
|
82
|
+
/** Invalidate a collection's cached entries (alias of invalidateCache). */
|
|
83
|
+
invalidate(namespace: string): void;
|
|
84
|
+
/** Drop every cached entry. */
|
|
85
|
+
clear(): void;
|
|
86
|
+
/** Cache hit/miss/entry stats, or null when never configured. */
|
|
87
|
+
stats(): { hits: number; misses: number; entries: number } | null;
|
|
88
|
+
}
|
|
68
89
|
|
|
69
90
|
/** Options for constructing a {@link LazypockClient}. */
|
|
70
91
|
export interface LazypockClientOptions {
|
|
@@ -76,6 +97,24 @@ export interface LazypockClientOptions {
|
|
|
76
97
|
authStore?: AuthStore;
|
|
77
98
|
/** Real-time service for Phoenix Channel WebSocket subscriptions */
|
|
78
99
|
realtime?: RealtimeService;
|
|
100
|
+
/**
|
|
101
|
+
* Query cache configuration. Disabled by default.
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* const client = createClient({
|
|
105
|
+
* baseUrl: '...',
|
|
106
|
+
* cache: {
|
|
107
|
+
* enabled: true,
|
|
108
|
+
* defaultTTL: 30_000,
|
|
109
|
+
* store: myStorage, // optional persistence (same interface as auth)
|
|
110
|
+
* },
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* When enabled, readable GETs are cached. Requests can opt out via
|
|
115
|
+
* `{ cache: false }`, or opt in with a custom TTL via `{ ttl: ms }`.
|
|
116
|
+
*/
|
|
117
|
+
cache?: CacheConfig;
|
|
79
118
|
/**
|
|
80
119
|
* Optional schema types for generating typed services at runtime.
|
|
81
120
|
* When provided, `collection()` returns a service whose create/update
|
|
@@ -110,6 +149,9 @@ export class LazypockClient {
|
|
|
110
149
|
readonly files: FilesService;
|
|
111
150
|
private collectionCache = new Map<string, CollectionService>();
|
|
112
151
|
private schemaByName?: Map<string, CollectionSchema>;
|
|
152
|
+
private cacheStore?: CacheStore;
|
|
153
|
+
/** Namespace → realtime unsubscribe; used for realtime-driven invalidation. */
|
|
154
|
+
private realtimeInvalidators = new Map<string, () => void>();
|
|
113
155
|
|
|
114
156
|
/**
|
|
115
157
|
* Create a new Lazypock client.
|
|
@@ -127,6 +169,14 @@ export class LazypockClient {
|
|
|
127
169
|
}
|
|
128
170
|
this.files = new FilesService(this.http);
|
|
129
171
|
this.collections = new CollectionsService(this.http, this.realtime);
|
|
172
|
+
if (options.cache) {
|
|
173
|
+
this.cacheStore = new CacheStore({
|
|
174
|
+
defaultTTL: options.cache.defaultTTL,
|
|
175
|
+
store: options.cache.store,
|
|
176
|
+
maxEntries: options.cache.maxEntries,
|
|
177
|
+
});
|
|
178
|
+
this.http.setCache(this.cacheStore, options.cache.enabled ?? false);
|
|
179
|
+
}
|
|
130
180
|
if (options.types?.schemas) {
|
|
131
181
|
this.schemaByName = new Map(
|
|
132
182
|
options.types.schemas.map((s) => [s.name, s]),
|
|
@@ -215,6 +265,100 @@ export class LazypockClient {
|
|
|
215
265
|
return this;
|
|
216
266
|
}
|
|
217
267
|
|
|
268
|
+
// ── Query cache (opt-in by default; opt-out per request) ──
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Configure the query cache at runtime (also a namespace for cache
|
|
272
|
+
* management methods).
|
|
273
|
+
*
|
|
274
|
+
* ```ts
|
|
275
|
+
* client.cache({ enabled: true, defaultTTL: 30_000 });
|
|
276
|
+
* client.cache.deleteByPrefix('getList:posts'); // all list caches for posts
|
|
277
|
+
* client.cache.deleteByPrefix('getOne:posts'); // all one-record caches
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* When enabled, GET requests cache their payload; mutations invalidate the
|
|
281
|
+
* affected collection automatically. Individual requests can opt out with
|
|
282
|
+
* `{ cache: false }` or override the TTL with `{ ttl: ms }`.
|
|
283
|
+
*/
|
|
284
|
+
readonly cache: CacheController = Object.assign(
|
|
285
|
+
((config?: CacheConfig) => {
|
|
286
|
+
if (!this.cacheStore) {
|
|
287
|
+
// Lazy-create so `.cache({ enabled: true })` works even when the
|
|
288
|
+
// constructor wasn't given cache config.
|
|
289
|
+
this.cacheStore = new CacheStore({
|
|
290
|
+
defaultTTL: config?.defaultTTL,
|
|
291
|
+
store: config?.store,
|
|
292
|
+
maxEntries: config?.maxEntries,
|
|
293
|
+
});
|
|
294
|
+
this.http.setCache(this.cacheStore, config?.enabled ?? true);
|
|
295
|
+
} else {
|
|
296
|
+
if (config?.enabled !== undefined) {
|
|
297
|
+
this.http.setCache(this.cacheStore, config.enabled);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return this;
|
|
301
|
+
}) as (config?: CacheConfig) => LazypockClient,
|
|
302
|
+
{
|
|
303
|
+
deleteByPrefix: (prefix: string) => this.cacheStore?.deleteByPrefix(prefix),
|
|
304
|
+
invalidate: (namespace: string) => this.cacheStore?.invalidate(namespace),
|
|
305
|
+
clear: () => {
|
|
306
|
+
this.cacheStore?.clear();
|
|
307
|
+
for (const unsub of this.realtimeInvalidators.values()) unsub();
|
|
308
|
+
this.realtimeInvalidators.clear();
|
|
309
|
+
},
|
|
310
|
+
stats: () => (this.cacheStore ? this.cacheStore.stats() : null),
|
|
311
|
+
},
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Drop every cached entry (all collections / namespaces).
|
|
316
|
+
* Also disables realtime-driven invalidation subscriptions.
|
|
317
|
+
*/
|
|
318
|
+
clearCache(): this {
|
|
319
|
+
this.cacheStore?.clear();
|
|
320
|
+
for (const unsub of this.realtimeInvalidators.values()) unsub();
|
|
321
|
+
this.realtimeInvalidators.clear();
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Invalidate cached entries for a collection (or custom namespace).
|
|
327
|
+
* Runs automatically on mutations — call explicitly when data changed
|
|
328
|
+
* out-of-band (e.g. another client wrote to the same collection).
|
|
329
|
+
*/
|
|
330
|
+
invalidateCache(namespace: string): this {
|
|
331
|
+
this.cacheStore?.invalidate(namespace);
|
|
332
|
+
return this;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Cache hit/miss/entry statistics.
|
|
337
|
+
* Returns null when caching was never configured.
|
|
338
|
+
*/
|
|
339
|
+
cacheStats(): { hits: number; misses: number; entries: number } | null {
|
|
340
|
+
return this.cacheStore ? this.cacheStore.stats() : null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Subscribe a collection's cache to realtime invalidation: any inbound
|
|
345
|
+
* create/update/delete event for the collection clears its cached entries.
|
|
346
|
+
* Returns an unsubscribe function.
|
|
347
|
+
*/
|
|
348
|
+
invalidateCacheOnRealtime(collectionName: string): () => void {
|
|
349
|
+
if (!this.cacheStore) {
|
|
350
|
+
// ensure a store exists so invalidation has somewhere to go
|
|
351
|
+
this.cache({ enabled: false });
|
|
352
|
+
}
|
|
353
|
+
const existing = this.realtimeInvalidators.get(collectionName);
|
|
354
|
+
if (existing) return existing;
|
|
355
|
+
const unsub = this.collection(collectionName).subscribe(() => {
|
|
356
|
+
this.cacheStore?.invalidate(collectionName);
|
|
357
|
+
});
|
|
358
|
+
this.realtimeInvalidators.set(collectionName, unsub);
|
|
359
|
+
return unsub;
|
|
360
|
+
}
|
|
361
|
+
|
|
218
362
|
// ── Auth ──
|
|
219
363
|
|
|
220
364
|
/** Check whether any superuser exists (for login vs setup screen routing). */
|
package/src/types.ts
CHANGED
|
@@ -65,6 +65,18 @@ export interface RequestOptions {
|
|
|
65
65
|
signal?: AbortSignal;
|
|
66
66
|
/** Custom fetch implementation (for RN or test mocking) */
|
|
67
67
|
fetch?: typeof globalThis.fetch;
|
|
68
|
+
/**
|
|
69
|
+
* Cache control for this request (see {@link CacheRequestOptions}).
|
|
70
|
+
* Resolved against the client's global cache config when unset.
|
|
71
|
+
*/
|
|
72
|
+
cache?: boolean | number | { ttl?: number; key?: string };
|
|
73
|
+
/** Alias of `cache: <ms>` — cache this GET for `ttl` milliseconds. */
|
|
74
|
+
ttl?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Extra cache namespaces to invalidate when this mutation succeeds.
|
|
77
|
+
* The current collection is always invalidated automatically.
|
|
78
|
+
*/
|
|
79
|
+
invalidate?: string[];
|
|
68
80
|
/**
|
|
69
81
|
* Request identifier used by the auto-cancellation mechanism.
|
|
70
82
|
*
|