lazypock 0.1.6 → 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/cli.cjs +9 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.global.js +9 -2
- package/dist/cli.global.js.map +1 -1
- package/dist/cli.js +9 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +421 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +266 -2
- package/dist/index.d.ts +266 -2
- package/dist/index.global.js +419 -9
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +419 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cache.ts +314 -0
- package/src/cli.ts +12 -3
- package/src/collection.ts +26 -3
- package/src/http.ts +126 -0
- package/src/index.ts +5 -0
- package/src/lazypock.ts +168 -7
- package/src/types.ts +12 -0
package/dist/index.js
CHANGED
|
@@ -17,6 +17,201 @@ var ApiError = class extends Error {
|
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
// src/cache.ts
|
|
21
|
+
var CacheStore = class {
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
this.memory = /* @__PURE__ */ new Map();
|
|
24
|
+
this.hits = 0;
|
|
25
|
+
this.misses = 0;
|
|
26
|
+
this.namespaceEntries = /* @__PURE__ */ new Map();
|
|
27
|
+
/** Key → set of prefix tags registered for that key (e.g. `getList:posts`). */
|
|
28
|
+
this.prefixEntries = /* @__PURE__ */ new Map();
|
|
29
|
+
this.ttl = config.defaultTTL ?? 6e4;
|
|
30
|
+
this.persistence = config.store;
|
|
31
|
+
this.maxEntries = config.maxEntries ?? 500;
|
|
32
|
+
}
|
|
33
|
+
/** Resolve the effective TTL: request override → global default. */
|
|
34
|
+
resolveTTL(ttl) {
|
|
35
|
+
return ttl && ttl > 0 ? ttl : this.ttl;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read a cached value. Fast sync path (memory) with async persistence
|
|
39
|
+
* fallback for adapters whose `get` returns a Promise.
|
|
40
|
+
* @param key Cache key (e.g. `"GET /posts?page=1"`).
|
|
41
|
+
* @returns The cached value, or undefined when absent/expired (the hit is
|
|
42
|
+
* cleared on expiry so a stale value is never served).
|
|
43
|
+
*/
|
|
44
|
+
async get(key) {
|
|
45
|
+
const mem = this.memory.get(key);
|
|
46
|
+
if (mem !== void 0) {
|
|
47
|
+
if (Date.now() > mem.expiresAt) {
|
|
48
|
+
this.delete(key);
|
|
49
|
+
this.misses++;
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
this.memory.delete(key);
|
|
53
|
+
this.memory.set(key, mem);
|
|
54
|
+
this.hits++;
|
|
55
|
+
return mem.value;
|
|
56
|
+
}
|
|
57
|
+
if (this.persistence) {
|
|
58
|
+
const entry = await this.readPersisted(key);
|
|
59
|
+
if (entry) {
|
|
60
|
+
if (Date.now() > entry.expiresAt) {
|
|
61
|
+
this.delete(key);
|
|
62
|
+
this.misses++;
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
this.hits++;
|
|
66
|
+
return entry.value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.misses++;
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Store a value.
|
|
74
|
+
* @param key Cache key.
|
|
75
|
+
* @param value The response payload.
|
|
76
|
+
* @param ttlOverride Optional TTL override (ms).
|
|
77
|
+
* @param namespace Optional namespace for group invalidation.
|
|
78
|
+
*/
|
|
79
|
+
set(key, value, ttlOverride, namespace, tags) {
|
|
80
|
+
const expiresAt = Date.now() + this.resolveTTL(ttlOverride);
|
|
81
|
+
const entry = { value, expiresAt, namespace, tags };
|
|
82
|
+
this.memory.set(key, entry);
|
|
83
|
+
if (this.memory.size > this.maxEntries) {
|
|
84
|
+
const oldest = this.memory.keys().next().value;
|
|
85
|
+
if (oldest !== void 0) this.delete(oldest);
|
|
86
|
+
}
|
|
87
|
+
if (namespace) {
|
|
88
|
+
let keys = this.namespaceEntries.get(namespace);
|
|
89
|
+
if (!keys) {
|
|
90
|
+
keys = /* @__PURE__ */ new Set();
|
|
91
|
+
this.namespaceEntries.set(namespace, keys);
|
|
92
|
+
}
|
|
93
|
+
keys.add(key);
|
|
94
|
+
}
|
|
95
|
+
for (const tag of tags ?? []) {
|
|
96
|
+
let keys = this.prefixEntries.get(tag);
|
|
97
|
+
if (!keys) {
|
|
98
|
+
keys = /* @__PURE__ */ new Set();
|
|
99
|
+
this.prefixEntries.set(tag, keys);
|
|
100
|
+
}
|
|
101
|
+
keys.add(key);
|
|
102
|
+
}
|
|
103
|
+
if (this.persistence) {
|
|
104
|
+
void this.persistence.set(this.persistKey(key), JSON.stringify(entry));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Invalidate entries belonging to a namespace (e.g. a collection name).
|
|
109
|
+
* Also clears the namespace index entry.
|
|
110
|
+
*/
|
|
111
|
+
invalidate(namespace) {
|
|
112
|
+
const keys = Array.from(this.namespaceEntries.get(namespace) ?? []);
|
|
113
|
+
for (const key of keys) this.delete(key);
|
|
114
|
+
this.namespaceEntries.delete(namespace);
|
|
115
|
+
}
|
|
116
|
+
/** Remove a single key. */
|
|
117
|
+
delete(key) {
|
|
118
|
+
const entry = this.memory.get(key);
|
|
119
|
+
if (entry?.namespace) {
|
|
120
|
+
const set = this.namespaceEntries.get(entry.namespace);
|
|
121
|
+
if (set) {
|
|
122
|
+
set.delete(key);
|
|
123
|
+
if (set.size === 0) this.namespaceEntries.delete(entry.namespace);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const tag of entry?.tags ?? []) {
|
|
127
|
+
const set = this.prefixEntries.get(tag);
|
|
128
|
+
if (set) {
|
|
129
|
+
set.delete(key);
|
|
130
|
+
if (set.size === 0) this.prefixEntries.delete(tag);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.memory.delete(key);
|
|
134
|
+
if (this.persistence) {
|
|
135
|
+
void this.persistence.remove(this.persistKey(key));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Delete every entry whose key starts with `prefix`.
|
|
140
|
+
*
|
|
141
|
+
* Useful for fine-grained invalidation, e.g.:
|
|
142
|
+
* ```ts
|
|
143
|
+
* client.cache.deleteByPrefix('getList:posts'); // delete all getList cache
|
|
144
|
+
* client.cache.deleteByPrefix('getOne:posts'); // delete all getOne cache
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
deleteByPrefix(prefix) {
|
|
148
|
+
if (!prefix) return;
|
|
149
|
+
const tagged = this.prefixEntries.get(prefix);
|
|
150
|
+
if (tagged) {
|
|
151
|
+
for (const key of Array.from(tagged)) this.delete(key);
|
|
152
|
+
this.prefixEntries.delete(prefix);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
for (const key of Array.from(this.memory.keys())) {
|
|
156
|
+
if (key.startsWith(prefix)) this.delete(key);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Drop every cached entry (memory + persistence). */
|
|
160
|
+
clear() {
|
|
161
|
+
this.memory.clear();
|
|
162
|
+
this.namespaceEntries.clear();
|
|
163
|
+
this.prefixEntries.clear();
|
|
164
|
+
}
|
|
165
|
+
/** Cache hit/miss/entry statistics. */
|
|
166
|
+
stats() {
|
|
167
|
+
return { hits: this.hits, misses: this.misses, entries: this.memory.size };
|
|
168
|
+
}
|
|
169
|
+
persistKey(key) {
|
|
170
|
+
return "lazypock:cache:" + key;
|
|
171
|
+
}
|
|
172
|
+
async readPersisted(key) {
|
|
173
|
+
if (!this.persistence) return void 0;
|
|
174
|
+
const raw = await this.persistence.get(this.persistKey(key));
|
|
175
|
+
if (raw == null) return void 0;
|
|
176
|
+
try {
|
|
177
|
+
const entry = JSON.parse(raw);
|
|
178
|
+
this.memory.set(key, entry);
|
|
179
|
+
if (entry.namespace) {
|
|
180
|
+
let keys = this.namespaceEntries.get(entry.namespace);
|
|
181
|
+
if (!keys) {
|
|
182
|
+
keys = /* @__PURE__ */ new Set();
|
|
183
|
+
this.namespaceEntries.set(entry.namespace, keys);
|
|
184
|
+
}
|
|
185
|
+
keys.add(key);
|
|
186
|
+
}
|
|
187
|
+
for (const tag of entry.tags ?? []) {
|
|
188
|
+
let keys = this.prefixEntries.get(tag);
|
|
189
|
+
if (!keys) {
|
|
190
|
+
keys = /* @__PURE__ */ new Set();
|
|
191
|
+
this.prefixEntries.set(tag, keys);
|
|
192
|
+
}
|
|
193
|
+
keys.add(key);
|
|
194
|
+
}
|
|
195
|
+
return entry;
|
|
196
|
+
} catch {
|
|
197
|
+
void this.persistence.remove(this.persistKey(key));
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
function resolveCacheDirective(opts) {
|
|
203
|
+
if (!opts) return null;
|
|
204
|
+
if (typeof opts.ttl === "number" && opts.ttl > 0) {
|
|
205
|
+
return { enabled: true, ttl: opts.ttl };
|
|
206
|
+
}
|
|
207
|
+
const c = opts.cache;
|
|
208
|
+
if (c === void 0) return null;
|
|
209
|
+
if (c === true) return { enabled: true };
|
|
210
|
+
if (c === false) return { enabled: false };
|
|
211
|
+
if (typeof c === "number") return { enabled: true, ttl: c > 0 ? c : void 0 };
|
|
212
|
+
return { enabled: true, ttl: c.ttl, key: c.key };
|
|
213
|
+
}
|
|
214
|
+
|
|
20
215
|
// src/http.ts
|
|
21
216
|
function isAbortError(err) {
|
|
22
217
|
return err instanceof Error && (err.name === "AbortError" || err.message === "Aborted");
|
|
@@ -27,6 +222,8 @@ var HttpClient = class {
|
|
|
27
222
|
* @param authStore The auth store providing the token for Authorization headers.
|
|
28
223
|
*/
|
|
29
224
|
constructor(baseUrl, authStore) {
|
|
225
|
+
/** Master switch resolved from CacheConfig.enabled. */
|
|
226
|
+
this.cacheEnabled = false;
|
|
30
227
|
/**
|
|
31
228
|
* Abort controllers for in-flight requests, keyed by their cancellation key
|
|
32
229
|
* (default `METHOD path`). A new request with the same key aborts the
|
|
@@ -39,6 +236,18 @@ var HttpClient = class {
|
|
|
39
236
|
this.authStore = authStore;
|
|
40
237
|
this.defaultFetch = globalThis.fetch.bind(globalThis);
|
|
41
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Attach a cache store + master switch.
|
|
241
|
+
* Called by the client constructor when cache config is present.
|
|
242
|
+
*/
|
|
243
|
+
setCache(cache, enabled) {
|
|
244
|
+
this.cache = cache;
|
|
245
|
+
this.cacheEnabled = enabled;
|
|
246
|
+
}
|
|
247
|
+
/** Whether the global cache flag is on (requests opt in/out individually too). */
|
|
248
|
+
get cacheIsEnabled() {
|
|
249
|
+
return this.cacheEnabled;
|
|
250
|
+
}
|
|
42
251
|
async refreshAuth() {
|
|
43
252
|
const collection = this.authStore.collectionName;
|
|
44
253
|
if (!collection) return null;
|
|
@@ -115,10 +324,25 @@ var HttpClient = class {
|
|
|
115
324
|
* @throws {ApiError} On non-2xx responses or when the request is aborted
|
|
116
325
|
* (aborted requests throw an `ApiError` with `isAbort === true`).
|
|
117
326
|
*/
|
|
327
|
+
/** Invalidate a namespace (collection name). No-op when cache is off. */
|
|
328
|
+
invalidateCache(namespace) {
|
|
329
|
+
this.cache?.invalidate(namespace);
|
|
330
|
+
}
|
|
331
|
+
/** Current cache statistics (hits/misses/entries), or null when disabled. */
|
|
332
|
+
cacheStats() {
|
|
333
|
+
return this.cache ? this.cache.stats() : null;
|
|
334
|
+
}
|
|
118
335
|
async request(method, path, body, options) {
|
|
119
336
|
if (this.authStore.isExpired && this.authStore.collectionName) {
|
|
120
337
|
await this.refreshAuth();
|
|
121
338
|
}
|
|
339
|
+
const cacheDirective = resolveCacheDirective(options);
|
|
340
|
+
const wantCache = cacheDirective !== null ? cacheDirective.enabled : this.cacheEnabled;
|
|
341
|
+
const cacheKey = method === "GET" && this.cache && wantCache ? this.cacheKeyFor(method, path, options?.params) : null;
|
|
342
|
+
if (cacheKey !== null) {
|
|
343
|
+
const hit = await this.cache?.get(cacheKey);
|
|
344
|
+
if (hit !== void 0) return hit;
|
|
345
|
+
}
|
|
122
346
|
let requestKey = options?.requestKey === void 0 ? options?.cancelKey ?? `${method} ${path}` : options.requestKey;
|
|
123
347
|
if (options?.autoCancel === false) requestKey = null;
|
|
124
348
|
let controller = null;
|
|
@@ -202,8 +426,63 @@ var HttpClient = class {
|
|
|
202
426
|
res.status
|
|
203
427
|
);
|
|
204
428
|
}
|
|
429
|
+
if (cacheKey !== null && this.cache) {
|
|
430
|
+
const namespace = this.namespaceFromPath(path);
|
|
431
|
+
const ttl = cacheDirective?.ttl;
|
|
432
|
+
const tags = this.cacheTagsFor(path, namespace);
|
|
433
|
+
this.cache.set(
|
|
434
|
+
cacheKey,
|
|
435
|
+
data,
|
|
436
|
+
ttl,
|
|
437
|
+
namespace ?? void 0,
|
|
438
|
+
tags
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
if (method !== "GET" && this.cache) {
|
|
442
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
443
|
+
const ns = this.namespaceFromPath(path);
|
|
444
|
+
if (ns) namespaces.add(ns);
|
|
445
|
+
for (const extra of options?.invalidate ?? []) {
|
|
446
|
+
if (extra) namespaces.add(extra);
|
|
447
|
+
}
|
|
448
|
+
for (const nsName of namespaces) this.cache.invalidate(nsName);
|
|
449
|
+
}
|
|
205
450
|
return data;
|
|
206
451
|
}
|
|
452
|
+
// ── Cache key/namespace helpers ──
|
|
453
|
+
/** Build a token-scoped cache key: `METHOD path|token-hash|params`. */
|
|
454
|
+
cacheKeyFor(method, path, params) {
|
|
455
|
+
const token = this.authStore.token || "anon";
|
|
456
|
+
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
457
|
+
return `${method} ${path}${qs}|${token}`;
|
|
458
|
+
}
|
|
459
|
+
/** Best-effort namespace (collection name) from a REST path. */
|
|
460
|
+
namespaceFromPath(path) {
|
|
461
|
+
const clean = path.split("?")[0];
|
|
462
|
+
const parts = clean.split("/").filter(Boolean);
|
|
463
|
+
if (parts.length === 0) return void 0;
|
|
464
|
+
if (parts[0] === "collections" || parts[0] === "_superusers") {
|
|
465
|
+
return parts[0];
|
|
466
|
+
}
|
|
467
|
+
return parts[0];
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Semantic prefix tags for `deleteByPrefix`, derived from the REST shape:
|
|
471
|
+
* - `/{collection}?...` → `getList:{collection}`
|
|
472
|
+
* - `/{collection}/{id}` → `getOne:{collection}`
|
|
473
|
+
* - `/collections?...` / `/collections/{id}` → `collections:getList` / `collections:getOne`
|
|
474
|
+
*/
|
|
475
|
+
cacheTagsFor(path, namespace) {
|
|
476
|
+
if (!namespace) return [];
|
|
477
|
+
const clean = path.split("?")[0];
|
|
478
|
+
const parts = clean.split("/").filter(Boolean);
|
|
479
|
+
if (parts[0] === "collections" || parts[0] === "_superusers") {
|
|
480
|
+
const op2 = parts.length >= 2 ? "getOne" : "getList";
|
|
481
|
+
return [`${namespace}:${op2}`];
|
|
482
|
+
}
|
|
483
|
+
const op = parts.length >= 2 ? "getOne" : "getList";
|
|
484
|
+
return [`${op}:${namespace}`];
|
|
485
|
+
}
|
|
207
486
|
/**
|
|
208
487
|
* HTTP GET.
|
|
209
488
|
* @param path URL path.
|
|
@@ -410,19 +689,42 @@ var CollectionService = class {
|
|
|
410
689
|
* @param options Query params (`filter`, `sort`, `expand`, `fields`) + request options.
|
|
411
690
|
*/
|
|
412
691
|
getList(page = 1, perPage = 30, options) {
|
|
413
|
-
const {
|
|
692
|
+
const {
|
|
693
|
+
requestKey,
|
|
694
|
+
autoCancel,
|
|
695
|
+
cancelKey,
|
|
696
|
+
fetch,
|
|
697
|
+
headers,
|
|
698
|
+
signal,
|
|
699
|
+
cache,
|
|
700
|
+
ttl,
|
|
701
|
+
invalidate,
|
|
702
|
+
params,
|
|
703
|
+
...queryParams
|
|
704
|
+
} = options ?? {};
|
|
414
705
|
const qs = new URLSearchParams(
|
|
415
706
|
Object.fromEntries(
|
|
416
707
|
Object.entries({
|
|
417
708
|
page: String(page),
|
|
418
709
|
perPage: String(perPage),
|
|
419
|
-
...
|
|
710
|
+
...queryParams
|
|
420
711
|
}).map(([k, v]) => [k, String(v)])
|
|
421
712
|
)
|
|
422
713
|
).toString();
|
|
423
714
|
return this.http.get(
|
|
424
715
|
"/" + this.encodeId(this.collectionName) + "?" + qs,
|
|
425
|
-
{
|
|
716
|
+
{
|
|
717
|
+
requestKey,
|
|
718
|
+
autoCancel,
|
|
719
|
+
cancelKey,
|
|
720
|
+
fetch,
|
|
721
|
+
headers,
|
|
722
|
+
signal,
|
|
723
|
+
cache,
|
|
724
|
+
ttl,
|
|
725
|
+
invalidate,
|
|
726
|
+
params
|
|
727
|
+
}
|
|
426
728
|
);
|
|
427
729
|
}
|
|
428
730
|
/**
|
|
@@ -1071,6 +1373,50 @@ var LazypockClient = class {
|
|
|
1071
1373
|
*/
|
|
1072
1374
|
constructor(options) {
|
|
1073
1375
|
this.collectionCache = /* @__PURE__ */ new Map();
|
|
1376
|
+
/** Namespace → realtime unsubscribe; used for realtime-driven invalidation. */
|
|
1377
|
+
this.realtimeInvalidators = /* @__PURE__ */ new Map();
|
|
1378
|
+
// ── Query cache (opt-in by default; opt-out per request) ──
|
|
1379
|
+
/**
|
|
1380
|
+
* Configure the query cache at runtime (also a namespace for cache
|
|
1381
|
+
* management methods).
|
|
1382
|
+
*
|
|
1383
|
+
* ```ts
|
|
1384
|
+
* client.cache({ enabled: true, defaultTTL: 30_000 });
|
|
1385
|
+
* client.cache.deleteByPrefix('getList:posts'); // all list caches for posts
|
|
1386
|
+
* client.cache.deleteByPrefix('getOne:posts'); // all one-record caches
|
|
1387
|
+
* ```
|
|
1388
|
+
*
|
|
1389
|
+
* When enabled, GET requests cache their payload; mutations invalidate the
|
|
1390
|
+
* affected collection automatically. Individual requests can opt out with
|
|
1391
|
+
* `{ cache: false }` or override the TTL with `{ ttl: ms }`.
|
|
1392
|
+
*/
|
|
1393
|
+
this.cache = Object.assign(
|
|
1394
|
+
((config) => {
|
|
1395
|
+
if (!this.cacheStore) {
|
|
1396
|
+
this.cacheStore = new CacheStore({
|
|
1397
|
+
defaultTTL: config?.defaultTTL,
|
|
1398
|
+
store: config?.store,
|
|
1399
|
+
maxEntries: config?.maxEntries
|
|
1400
|
+
});
|
|
1401
|
+
this.http.setCache(this.cacheStore, config?.enabled ?? true);
|
|
1402
|
+
} else {
|
|
1403
|
+
if (config?.enabled !== void 0) {
|
|
1404
|
+
this.http.setCache(this.cacheStore, config.enabled);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
return this;
|
|
1408
|
+
}),
|
|
1409
|
+
{
|
|
1410
|
+
deleteByPrefix: (prefix) => this.cacheStore?.deleteByPrefix(prefix),
|
|
1411
|
+
invalidate: (namespace) => this.cacheStore?.invalidate(namespace),
|
|
1412
|
+
clear: () => {
|
|
1413
|
+
this.cacheStore?.clear();
|
|
1414
|
+
for (const unsub of this.realtimeInvalidators.values()) unsub();
|
|
1415
|
+
this.realtimeInvalidators.clear();
|
|
1416
|
+
},
|
|
1417
|
+
stats: () => this.cacheStore ? this.cacheStore.stats() : null
|
|
1418
|
+
}
|
|
1419
|
+
);
|
|
1074
1420
|
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
1075
1421
|
this.authStore = options.authStore ?? new AuthStore(options.storage ?? memoryStorage);
|
|
1076
1422
|
this.http = new HttpClient(baseUrl, this.authStore);
|
|
@@ -1080,6 +1426,14 @@ var LazypockClient = class {
|
|
|
1080
1426
|
}
|
|
1081
1427
|
this.files = new FilesService(this.http);
|
|
1082
1428
|
this.collections = new CollectionsService(this.http, this.realtime);
|
|
1429
|
+
if (options.cache) {
|
|
1430
|
+
this.cacheStore = new CacheStore({
|
|
1431
|
+
defaultTTL: options.cache.defaultTTL,
|
|
1432
|
+
store: options.cache.store,
|
|
1433
|
+
maxEntries: options.cache.maxEntries
|
|
1434
|
+
});
|
|
1435
|
+
this.http.setCache(this.cacheStore, options.cache.enabled ?? false);
|
|
1436
|
+
}
|
|
1083
1437
|
if (options.types?.schemas) {
|
|
1084
1438
|
this.schemaByName = new Map(
|
|
1085
1439
|
options.types.schemas.map((s) => [s.name, s])
|
|
@@ -1160,6 +1514,49 @@ var LazypockClient = class {
|
|
|
1160
1514
|
this.http.cancelAllRequests();
|
|
1161
1515
|
return this;
|
|
1162
1516
|
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Drop every cached entry (all collections / namespaces).
|
|
1519
|
+
* Also disables realtime-driven invalidation subscriptions.
|
|
1520
|
+
*/
|
|
1521
|
+
clearCache() {
|
|
1522
|
+
this.cacheStore?.clear();
|
|
1523
|
+
for (const unsub of this.realtimeInvalidators.values()) unsub();
|
|
1524
|
+
this.realtimeInvalidators.clear();
|
|
1525
|
+
return this;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Invalidate cached entries for a collection (or custom namespace).
|
|
1529
|
+
* Runs automatically on mutations — call explicitly when data changed
|
|
1530
|
+
* out-of-band (e.g. another client wrote to the same collection).
|
|
1531
|
+
*/
|
|
1532
|
+
invalidateCache(namespace) {
|
|
1533
|
+
this.cacheStore?.invalidate(namespace);
|
|
1534
|
+
return this;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Cache hit/miss/entry statistics.
|
|
1538
|
+
* Returns null when caching was never configured.
|
|
1539
|
+
*/
|
|
1540
|
+
cacheStats() {
|
|
1541
|
+
return this.cacheStore ? this.cacheStore.stats() : null;
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Subscribe a collection's cache to realtime invalidation: any inbound
|
|
1545
|
+
* create/update/delete event for the collection clears its cached entries.
|
|
1546
|
+
* Returns an unsubscribe function.
|
|
1547
|
+
*/
|
|
1548
|
+
invalidateCacheOnRealtime(collectionName) {
|
|
1549
|
+
if (!this.cacheStore) {
|
|
1550
|
+
this.cache({ enabled: false });
|
|
1551
|
+
}
|
|
1552
|
+
const existing = this.realtimeInvalidators.get(collectionName);
|
|
1553
|
+
if (existing) return existing;
|
|
1554
|
+
const unsub = this.collection(collectionName).subscribe(() => {
|
|
1555
|
+
this.cacheStore?.invalidate(collectionName);
|
|
1556
|
+
});
|
|
1557
|
+
this.realtimeInvalidators.set(collectionName, unsub);
|
|
1558
|
+
return unsub;
|
|
1559
|
+
}
|
|
1163
1560
|
// ── Auth ──
|
|
1164
1561
|
/** Check whether any superuser exists (for login vs setup screen routing). */
|
|
1165
1562
|
async checkSuperuser() {
|
|
@@ -1203,10 +1600,17 @@ var LazypockClient = class {
|
|
|
1203
1600
|
this.authStore.set(data.token, data.record);
|
|
1204
1601
|
}
|
|
1205
1602
|
} else {
|
|
1206
|
-
|
|
1207
|
-
"/
|
|
1208
|
-
|
|
1209
|
-
|
|
1603
|
+
try {
|
|
1604
|
+
data = await this.http.post("/_superusers/auth-with-password", {
|
|
1605
|
+
identity: email,
|
|
1606
|
+
password
|
|
1607
|
+
});
|
|
1608
|
+
} catch {
|
|
1609
|
+
data = null;
|
|
1610
|
+
}
|
|
1611
|
+
if (!data) {
|
|
1612
|
+
data = await this.http.post("/superusers/login", { email, password });
|
|
1613
|
+
}
|
|
1210
1614
|
if (data) {
|
|
1211
1615
|
this.authStore.setCollectionName(null);
|
|
1212
1616
|
this.authStore.set(data.token, null);
|
|
@@ -1214,9 +1618,13 @@ var LazypockClient = class {
|
|
|
1214
1618
|
}
|
|
1215
1619
|
return data;
|
|
1216
1620
|
}
|
|
1217
|
-
/**
|
|
1621
|
+
/**
|
|
1622
|
+
* Fetch the current authenticated identity (superuser OR auth collection user).
|
|
1623
|
+
* Uses `GET /api/me` (PocketBase parity) — works with both superuser tokens
|
|
1624
|
+
* and auth collection user tokens.
|
|
1625
|
+
*/
|
|
1218
1626
|
async me(options) {
|
|
1219
|
-
const data = await this.http.get("/
|
|
1627
|
+
const data = await this.http.get("/me", options);
|
|
1220
1628
|
if (data) {
|
|
1221
1629
|
this.authStore.set(this.authStore.token, data);
|
|
1222
1630
|
}
|
|
@@ -1285,6 +1693,7 @@ function createClient(options) {
|
|
|
1285
1693
|
export {
|
|
1286
1694
|
ApiError,
|
|
1287
1695
|
AuthStore,
|
|
1696
|
+
CacheStore,
|
|
1288
1697
|
CollectionService,
|
|
1289
1698
|
CollectionsService,
|
|
1290
1699
|
FilesService,
|
|
@@ -1300,6 +1709,7 @@ export {
|
|
|
1300
1709
|
getFileUrl,
|
|
1301
1710
|
getScaleUrl,
|
|
1302
1711
|
getThumbUrl,
|
|
1712
|
+
resolveCacheDirective,
|
|
1303
1713
|
schemaFieldType,
|
|
1304
1714
|
wsUrlFromBaseUrl
|
|
1305
1715
|
};
|