@baeta/cache 2.0.0-next.16 → 2.0.0-next.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @baeta/cache
2
2
 
3
+ ## 2.0.0-next.18
4
+
5
+ ### Patch Changes
6
+
7
+ - fix(deps): update all non-major dependencies by [@baeta-bot](https://github.com/apps/baeta-bot) in [#645](https://github.com/andreisergiu98/baeta/pull/645)
8
+
9
+ ## 2.0.0-next.17
10
+
11
+ ### Patch Changes
12
+
13
+ - Hash index tuples via ohash to avoid collision of different types by [@andreisergiu98](https://github.com/andreisergiu98) in [#591](https://github.com/andreisergiu98/baeta/pull/591)
14
+
15
+ - Add support for custom transport for logs and improve tracing by [@andreisergiu98](https://github.com/andreisergiu98) in [#616](https://github.com/andreisergiu98/baeta/pull/616)
16
+ - Updated dependencies [[`7cd558c`](https://github.com/andreisergiu98/baeta/commit/7cd558c2a2c1111df0b6de663b6db50ad881bc88)]:
17
+ - @baeta/util-log@2.0.0-next.6
18
+
3
19
  ## 2.0.0-next.16
4
20
 
5
21
  ### Major Changes
package/dist/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import DataLoader from "dataloader";
2
-
3
- //#region lib/item.d.ts
4
2
  /**
5
3
  * Reference type for cached items
6
4
  */
@@ -11,8 +9,6 @@ type ItemRef = string | number | bigint;
11
9
  type RefCompatibleItem = {
12
10
  id: string | number | bigint;
13
11
  };
14
- //#endregion
15
- //#region lib/query.d.ts
16
12
  type QueryIndex = [string, QueryIndexValue];
17
13
  type QueryIndexValue = string | number | boolean | null;
18
14
  type QueryArgs = Record<string, string | number | boolean | null | Array<string | number | boolean | null> | Record<string, string | number | boolean | null>>;
@@ -26,7 +22,7 @@ interface QueryTagWithIndexes extends QueryTag {
26
22
  interface QueryTagWithArgs extends QueryTag {
27
23
  args: QueryArgs;
28
24
  }
29
- type QueryArgsIndexes<Args extends QueryArgs, T = Record<string, never>> = { [K in keyof Args]?: Args[K] extends QueryIndexValue ? true : never } & Record<Exclude<keyof T, keyof Args>, never>;
25
+ type QueryArgsIndexes<Args extends QueryArgs, T = Record<string, never>> = { [K in keyof Args]?: Args[K] extends QueryIndexValue ? true : never; } & Record<Exclude<keyof T, keyof Args>, never>;
30
26
  type IndexedQueryArgs<Args extends QueryArgs, Indexes extends QueryArgsIndexes<Args>> = Pick<Args, keyof Indexes & keyof Args>;
31
27
  interface QueryTagWithData<T> extends QueryTagWithArgs {
32
28
  indexes: QueryIndex[];
@@ -77,8 +73,6 @@ type QueryHandle<Result extends QueryResult<Item>, Args extends QueryArgs, Index
77
73
  onDelete?: QueryOnDeleteHook<Item>;
78
74
  };
79
75
  };
80
- //#endregion
81
- //#region lib/key.d.ts
82
76
  type QueryCacheKey = `${QueryCacheKeyPrefix}:${QueryCacheKeyId}`;
83
77
  type QueryCacheIndexKey = `${QueryCacheKeyPrefix}:${QueryCacheIndexKeyId}`;
84
78
  type QueryCacheKeyId = `id:${string}`;
@@ -88,8 +82,6 @@ type ItemCacheKey = `${ItemCacheKeyPrefix}:${ItemCacheKeyId}`;
88
82
  type ItemCacheKeyId = `id:${string}`;
89
83
  type ItemCacheKeyPrefix = `${string}:${string}:item:${CacheRevisionId}`;
90
84
  type CacheRevisionId = `rev_${string}`;
91
- //#endregion
92
- //#region lib/client.d.ts
93
85
  interface CacheClientArgs<Item> {
94
86
  ttlMs: number;
95
87
  parse: (value: string) => Item | null;
@@ -120,7 +112,7 @@ interface CacheClientSaveOptions {
120
112
  */
121
113
  disableOverwrite?: boolean;
122
114
  }
123
- declare abstract class CacheClient {
115
+ export declare abstract class CacheClient {
124
116
  options?: CacheClientOptions;
125
117
  constructor(options?: CacheClientOptions);
126
118
  abstract getPartialItems<Item>(keys: ItemCacheKey[], options: CacheClientArgs<Item>): Promise<Array<Item | null>>;
@@ -132,8 +124,6 @@ declare abstract class CacheClient {
132
124
  abstract saveQuery<QueryMetadata>(key: QueryCacheKey, indexes: QueryCacheIndexKey[], metadata: QueryMetadata, options: CacheClientArgs<QueryMetadata>): Promise<void>;
133
125
  abstract deleteQueries<QueryMetadata>(indexes: QueryCacheIndexKey[], options: CacheClientArgs<QueryMetadata>): Promise<void>;
134
126
  }
135
- //#endregion
136
- //#region lib/store.d.ts
137
127
  interface CacheStoreOptions<Item> {
138
128
  name: string;
139
129
  client: CacheClient;
@@ -156,16 +146,7 @@ declare class CacheStore<Item> {
156
146
  protected readonly itemCacheArgs: CacheClientArgs<Item>;
157
147
  protected readonly queryCacheArgs: CacheClientArgs<QueryMetadata>;
158
148
  protected readonly getRef?: (source: Item) => ItemRef;
159
- constructor({
160
- name,
161
- client,
162
- namespace,
163
- revision,
164
- ttlMs,
165
- parse,
166
- serialize,
167
- getRef
168
- }: CacheStoreOptions<Item>);
149
+ constructor({ name, client, namespace, revision, ttlMs, parse, serialize, getRef }: CacheStoreOptions<Item>);
169
150
  protected loadAndParse: (refs: ReadonlyArray<ItemRef>) => Promise<Array<Item | null>>;
170
151
  protected loader: DataLoader<ItemRef, Item | null, ItemRef>;
171
152
  get(ref: ItemRef): Promise<Item | null>;
@@ -189,15 +170,11 @@ declare class CacheStore<Item> {
189
170
  getQueryKeyPrefix(query: QueryTag): QueryCacheKeyPrefix;
190
171
  getQueryKeyRevision(query: QueryTag): CacheRevisionId;
191
172
  }
192
- //#endregion
193
- //#region lib/define-query.d.ts
194
173
  type QueryFactory<Result extends QueryResult<Item>, Args extends QueryArgs, Indexes extends QueryArgsIndexes<Args, Indexes>, Item = ItemFromQueryResult<Result>> = (store: CacheStore<Item>, name: string) => QueryHandle<Result, Args, Indexes, Item>;
195
174
  type QueryMethod<T extends QueryFactory<any, any, any>> = T extends QueryFactory<infer Result, infer Args, any, any> ? (args: Args) => Promise<Result> : never;
196
175
  type QueryDefinitionMap<Item> = Record<string, QueryFactory<QueryResult<Item>, any, any, Item>>;
197
- type QueryMethodMap<Definitions extends QueryDefinitionMap<any>> = { [K in keyof Definitions]: QueryMethod<Definitions[K]> };
198
- declare function defineQuery<Result extends QueryResult<Item>, Args extends QueryArgs = Record<string, never>, Indexes extends QueryArgsIndexes<Args, Indexes> = Record<string, never>, Item = ItemFromQueryResult<Result>>(options: QueryOptions<Result, Args, Indexes, Item>): QueryFactory<Result, Args, Indexes, Item>;
199
- //#endregion
200
- //#region lib/options.d.ts
176
+ type QueryMethodMap<Definitions extends QueryDefinitionMap<any>> = { [K in keyof Definitions]: QueryMethod<Definitions[K]>; };
177
+ export declare function defineQuery<Result extends QueryResult<Item>, Args extends QueryArgs = Record<string, never>, Indexes extends QueryArgsIndexes<Args, Indexes> = Record<string, never>, Item = ItemFromQueryResult<Result>>(options: QueryOptions<Result, Args, Indexes, Item>): QueryFactory<Result, Args, Indexes, Item>;
201
178
  /**
202
179
  * Configuration options for cache stores
203
180
  */
@@ -250,8 +227,6 @@ type OptionalGetRef<Item> = {
250
227
  type RequiredGetRef<Item> = {
251
228
  getRef: (source: Item) => ItemRef;
252
229
  };
253
- //#endregion
254
- //#region lib/create-cache.d.ts
255
230
  type CacheHooksOptions = {
256
231
  /**
257
232
  * If true, hooks will be awaited. If false, hooks will be fired asynchronously and will not block cache methods.
@@ -283,8 +258,7 @@ type CreateCacheFactory<Item> = {
283
258
  build: () => CacheWithQueries<Item, QueryDefinitions>;
284
259
  };
285
260
  };
286
- declare function createCache<Item extends RefCompatibleItem>(client: CacheClient, options: CacheOptions<Item> & OptionalGetRef<Item>): CreateCacheFactory<Item>;
287
- declare function createCache<Item>(client: CacheClient, options: CacheOptions<Item> & RequiredGetRef<Item>): CreateCacheFactory<Item>;
288
- //#endregion
289
- export { type Cache, CacheClient, type CacheClientArgs, type CacheClientOptions, type CacheClientSaveOptions, type CacheHooksOptions, type CacheOptions, type CacheWithQueries, type CreateCacheFactory, type ItemCacheKey, type ItemRef, type OptionalGetRef, type QueryArgs, type QueryArgsIndexes, type QueryCacheIndexKey, type QueryCacheKey, type QueryIndexValue, type QueryOptions, type RefCompatibleItem, type RequiredGetRef, createCache, defineQuery };
261
+ export declare function createCache<Item extends RefCompatibleItem>(client: CacheClient, options: CacheOptions<Item> & OptionalGetRef<Item>): CreateCacheFactory<Item>;
262
+ export declare function createCache<Item>(client: CacheClient, options: CacheOptions<Item> & RequiredGetRef<Item>): CreateCacheFactory<Item>;
263
+ export type { Cache, CacheClientArgs, CacheClientOptions, CacheClientSaveOptions, CacheHooksOptions, CacheOptions, CacheWithQueries, CreateCacheFactory, ItemCacheKey, ItemRef, OptionalGetRef, QueryArgs, QueryArgsIndexes, QueryCacheIndexKey, QueryCacheKey, QueryIndexValue, QueryOptions, RefCompatibleItem, RequiredGetRef };
290
264
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,24 +1,23 @@
1
- import { log } from "@baeta/util-log";
1
+ import { createLogger } from "@baeta/util-log";
2
2
  import DataLoader from "dataloader";
3
- import { encodeBase64 } from "@baeta/util-encoding";
4
3
  import { hash } from "ohash";
5
- //#region lib/client.ts
4
+ // lib/client.ts
6
5
  var CacheClient = class {
7
6
  options;
8
7
  constructor(options) {
9
8
  this.options = options;
10
9
  }
11
10
  };
12
- //#endregion
13
- //#region lib/item.ts
11
+ // lib/logger.ts
12
+ const logger = createLogger("@baeta/cache");
13
+ // lib/item.ts
14
14
  /**
15
15
  * Asserts that a given reference is of a valid type (string, number, or bigint) and that it can be safely serialized.
16
16
  */
17
17
  function assertValidRefType(ref) {
18
18
  if (typeof ref !== "string" && typeof ref !== "number" && typeof ref !== "bigint") throw new TypeError("Reference must be string, number or bigint. Define getRef function in cache options");
19
19
  }
20
- //#endregion
21
- //#region lib/key.ts
20
+ // lib/key.ts
22
21
  function buildCacheRevisionId(revision) {
23
22
  return `rev_${revision}`;
24
23
  }
@@ -30,16 +29,13 @@ function buildQueryCacheKeyId(args) {
30
29
  }
31
30
  function buildQueryCacheIndexKeyId(index) {
32
31
  if (index == null) return "idx:";
33
- const key = index[0];
34
- const value = index[1];
35
- return `idx:${hash(`${key}=${value === null ? "null" : `_${encodeBase64(value.toString())}`}`)}`;
32
+ return `idx:${hash(index)}`;
36
33
  }
37
34
  function buildQueryCacheIndexKeysIds(indexes) {
38
35
  if (indexes.length === 0) return ["idx:"];
39
36
  return indexes.map((idx) => buildQueryCacheIndexKeyId(idx));
40
37
  }
41
- //#endregion
42
- //#region lib/utils.ts
38
+ // lib/utils.ts
43
39
  function arrayIsComplete(items) {
44
40
  if (items == null) return false;
45
41
  return items.every((item) => item != null);
@@ -61,8 +57,7 @@ function zip(array1, array2, fn) {
61
57
  for (let i = 0; i < array1.length; i++) result.push(fn(array1[i], array2[i]));
62
58
  return result;
63
59
  }
64
- //#endregion
65
- //#region lib/store.ts
60
+ // lib/store.ts
66
61
  var CacheStore = class {
67
62
  name;
68
63
  namespace;
@@ -72,6 +67,7 @@ var CacheStore = class {
72
67
  queryCacheArgs;
73
68
  getRef;
74
69
  constructor({ name, client, namespace, revision, ttlMs, parse, serialize, getRef }) {
70
+ if (ttlMs != null && (!Number.isFinite(ttlMs) || ttlMs <= 0)) throw new Error(`Invalid ttlMs for cache "${name}": ${ttlMs}. Expected a positive number of milliseconds.`);
75
71
  this.name = name;
76
72
  this.namespace = namespace ?? "baeta";
77
73
  this.revision = revision?.toString() ?? "default";
@@ -143,8 +139,10 @@ var CacheStore = class {
143
139
  const isList = Array.isArray(data);
144
140
  const nullableItems = isList ? data : [data];
145
141
  const items = nullableItems.filter((item) => item != null);
146
- if (items.length > 0) if (replaceExistingItems) await this.save(items);
147
- else await this.saveIfNotExists(items);
142
+ if (items.length > 0) {
143
+ if (replaceExistingItems) await this.save(items);
144
+ else await this.saveIfNotExists(items);
145
+ }
148
146
  const metadata = {
149
147
  refs: nullableItems.map((item) => {
150
148
  if (item == null) return null;
@@ -197,7 +195,11 @@ function parseItemSafely(value, parseItem) {
197
195
  try {
198
196
  return parseItem(value);
199
197
  } catch (err) {
200
- log.warn(err, "Failed to parse item, returning null");
198
+ logger.warn({
199
+ type: "parse-item",
200
+ message: "Failed to parse item, returning null",
201
+ error: err
202
+ });
201
203
  return null;
202
204
  }
203
205
  }
@@ -206,11 +208,18 @@ function parseQueryMetadataSafely(value) {
206
208
  try {
207
209
  parsed = JSON.parse(value);
208
210
  } catch (err) {
209
- log.warn(err, "Failed to parse query metadata, treating as cache miss");
211
+ logger.warn({
212
+ type: "parse-query-metadata",
213
+ message: "Failed to parse query metadata, treating as cache miss",
214
+ error: err
215
+ });
210
216
  return null;
211
217
  }
212
218
  if (!isValidQueryMetadata(parsed)) {
213
- log.warn("Query metadata has invalid shape, treating as cache miss");
219
+ logger.warn({
220
+ type: "parse-query-metadata",
221
+ message: "Query metadata has invalid shape, treating as cache miss"
222
+ });
214
223
  return null;
215
224
  }
216
225
  return parsed;
@@ -222,8 +231,7 @@ function isValidQueryMetadata(value) {
222
231
  if (typeof candidate.isList !== "boolean") return false;
223
232
  return candidate.refs.every((ref) => ref === null || typeof ref === "string");
224
233
  }
225
- //#endregion
226
- //#region lib/create-cache.ts
234
+ // lib/create-cache.ts
227
235
  function createCache(client, options) {
228
236
  const storeClient = new CacheStore({
229
237
  client,
@@ -341,14 +349,17 @@ async function fireHook(hook, value, options) {
341
349
  if (hook == null) return;
342
350
  if (options?.waitForHooks) await hook(value);
343
351
  else Promise.resolve().then(() => hook(value)).catch((err) => {
344
- log.error(err, "Error in cache hook");
352
+ logger.error({
353
+ type: "cache-hook",
354
+ message: "Error in cache hook",
355
+ error: err
356
+ });
345
357
  });
346
358
  }
347
359
  async function fireHooks(hooks, value, options) {
348
360
  await Promise.all(hooks.map((hook) => fireHook(hook, value, options)));
349
361
  }
350
- //#endregion
351
- //#region lib/define-query.ts
362
+ // lib/define-query.ts
352
363
  function defineQuery(options) {
353
364
  return (store, name) => {
354
365
  const tag = {
@@ -360,7 +371,11 @@ function defineQuery(options) {
360
371
  ...tag,
361
372
  args
362
373
  }).catch((err) => {
363
- log.error(err, `Failed to get query result for ${name}. Proceeding without caching.`);
374
+ logger.error({
375
+ type: "query-cache",
376
+ message: `Failed to get query result for ${name}. Proceeding without caching.`,
377
+ error: err
378
+ });
364
379
  return null;
365
380
  });
366
381
  if (fromCache != null) return fromCache.query;
@@ -371,7 +386,11 @@ function defineQuery(options) {
371
386
  data,
372
387
  indexes: buildIndexes(args, options.indexArgsBy)
373
388
  }, options.replaceExistingItems, options.ttlMs).catch((err) => {
374
- log.error(err, `Failed to save query result for ${name}. Proceeding without caching.`);
389
+ logger.error({
390
+ type: "query-cache",
391
+ message: `Failed to save query result for ${name}. Proceeding without caching.`,
392
+ error: err
393
+ });
375
394
  });
376
395
  return data;
377
396
  };
@@ -425,7 +444,6 @@ function buildIndexes(args, indexArgsBy) {
425
444
  }
426
445
  return indexes;
427
446
  }
428
- //#endregion
429
447
  export { CacheClient, createCache, defineQuery };
430
448
 
431
449
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../lib/client.ts","../lib/item.ts","../lib/key.ts","../lib/options.ts","../lib/utils.ts","../lib/store.ts","../lib/create-cache.ts","../lib/define-query.ts"],"sourcesContent":["import type { ItemCacheKey, QueryCacheIndexKey, QueryCacheKey } from './key.ts';\n\nexport interface CacheClientArgs<Item> {\n\tttlMs: number;\n\tparse: (value: string) => Item | null;\n\tserialize: (item: Item) => string;\n}\n\nexport interface CacheClientOptions {\n\t/**\n\t * Revision number for cache invalidation.\n\t * Incrementing this number will invalidate all existing cache entries for this store.\n\t * @defaultValue \"default\"\n\t */\n\trevision?: number;\n\t/**\n\t * Time-to-live in milliseconds\n\t * @defaultValue 3_600_000 (1 hour)\n\t */\n\tttlMs?: number;\n\t/**\n\t * Optional namespace to prefix all cache keys\n\t * @defaultValue \"baeta\"\n\t */\n\tnamespace?: string;\n}\n\nexport interface CacheClientSaveOptions {\n\t/**\n\t * If true, the save operation will not overwrite existing cache entries. Instead, it will only save entries that do not already exist in the cache.\n\t * @defaultValue false\n\t */\n\tdisableOverwrite?: boolean;\n}\n\nexport abstract class CacheClient {\n\toptions?: CacheClientOptions;\n\n\tconstructor(options?: CacheClientOptions) {\n\t\tthis.options = options;\n\t}\n\n\tabstract getPartialItems<Item>(\n\t\tkeys: ItemCacheKey[],\n\t\toptions: CacheClientArgs<Item>,\n\t): Promise<Array<Item | null>>;\n\n\tabstract saveItems<Item>(\n\t\titems: Array<[ItemCacheKey, Item]>,\n\t\toptions: CacheClientArgs<Item>,\n\t\tsaveOptions?: CacheClientSaveOptions,\n\t): Promise<void>;\n\n\tabstract saveItemsWithDiff<Item>(\n\t\titems: Array<[ItemCacheKey, Item]>,\n\t\toptions: CacheClientArgs<Item>,\n\t): Promise<Array<Item | null>>;\n\n\tabstract deleteItems<Item>(keys: ItemCacheKey[], options: CacheClientArgs<Item>): Promise<void>;\n\n\tabstract deleteItemsWithDiff<Item>(\n\t\tkeys: ItemCacheKey[],\n\t\toptions: CacheClientArgs<Item>,\n\t): Promise<Array<Item | null>>;\n\n\tabstract getQuery<QueryMetadata>(\n\t\tkey: QueryCacheKey,\n\t\toptions: CacheClientArgs<QueryMetadata>,\n\t): Promise<QueryMetadata | null>;\n\n\tabstract saveQuery<QueryMetadata>(\n\t\tkey: QueryCacheKey,\n\t\tindexes: QueryCacheIndexKey[],\n\t\tmetadata: QueryMetadata,\n\t\toptions: CacheClientArgs<QueryMetadata>,\n\t): Promise<void>;\n\n\tabstract deleteQueries<QueryMetadata>(\n\t\tindexes: QueryCacheIndexKey[],\n\t\toptions: CacheClientArgs<QueryMetadata>,\n\t): Promise<void>;\n}\n","/**\n * Reference type for cached items\n */\nexport type ItemRef = string | number | bigint;\n\n/**\n * Type constraint for objects that are compatible with default cache ref\n */\nexport type RefCompatibleItem = { id: string | number | bigint };\n\n/**\n * Asserts that a given reference is of a valid type (string, number, or bigint) and that it can be safely serialized.\n */\nexport function assertValidRefType(ref: unknown): asserts ref is string | number | bigint {\n\tif (typeof ref !== 'string' && typeof ref !== 'number' && typeof ref !== 'bigint') {\n\t\tthrow new TypeError(\n\t\t\t'Reference must be string, number or bigint. Define getRef function in cache options',\n\t\t);\n\t}\n}\n","import { encodeBase64 } from '@baeta/util-encoding';\nimport { hash } from 'ohash';\nimport type { QueryArgs, QueryIndex } from './query.ts';\n\nexport type QueryCacheKey = `${QueryCacheKeyPrefix}:${QueryCacheKeyId}`;\nexport type QueryCacheIndexKey = `${QueryCacheKeyPrefix}:${QueryCacheIndexKeyId}`;\nexport type QueryCacheKeyId = `id:${string}`;\nexport type QueryCacheIndexKeyId = `idx:${string}`;\nexport type QueryCacheKeyPrefix = `${string}:${string}:query:${string}:${CacheRevisionId}`; // $namespace:$storeName:query:$queryName:rev_$value\n\nexport type ItemCacheKey = `${ItemCacheKeyPrefix}:${ItemCacheKeyId}`;\nexport type ItemCacheKeyId = `id:${string}`;\nexport type ItemCacheKeyPrefix = `${string}:${string}:item:${CacheRevisionId}`; // $namespace:$storeName:item:rev_$value\n\nexport type CacheRevisionId = `rev_${string}`;\n\nexport function buildCacheRevisionId(revision: string): CacheRevisionId {\n\treturn `rev_${revision}`;\n}\n\nexport function buildItemCacheKeyId(id: string): ItemCacheKeyId {\n\treturn `id:${id}`;\n}\n\nexport function buildQueryCacheKeyId(args: QueryArgs): QueryCacheKeyId {\n\treturn `id:${hash(args)}`;\n}\n\nexport function buildQueryCacheIndexKeyId(index?: QueryIndex | undefined): QueryCacheIndexKeyId {\n\tif (index == null) {\n\t\treturn 'idx:';\n\t}\n\tconst key = index[0];\n\tconst value = index[1];\n\tconst safeValue = value === null ? 'null' : `_${encodeBase64(value.toString())}`;\n\treturn `idx:${hash(`${key}=${safeValue}`)}`;\n}\n\nexport function buildQueryCacheIndexKeysIds(indexes: QueryIndex[]): QueryCacheIndexKeyId[] {\n\tif (indexes.length === 0) {\n\t\treturn ['idx:'];\n\t}\n\treturn indexes.map((idx) => buildQueryCacheIndexKeyId(idx));\n}\n","import type { ItemRef } from './item.ts';\n\nexport const DEFAULT_REVISION = 'default';\nexport const DEFAULT_NAMESPACE = 'baeta';\nexport const DEFAULT_TTL_MS = 3_600_000; // 1 hour\n\n/**\n * Configuration options for cache stores\n */\nexport type CacheOptions<Item> = {\n\t/**\n\t * Unique name for the cache store. Used as a prefix for cache keys to avoid collisions.\n\t */\n\tname: string;\n\t/**\n\t * Revision number for cache invalidation.\n\t * Incrementing this number will invalidate all existing cache entries for this store.\n\t * @defaultValue CacheClient options or \"default\"\n\t */\n\trevision?: number;\n\t/**\n\t * Time-to-live in milliseconds\n\t * @defaultValue CacheClient options or 3_600_000 (1 hour)\n\t */\n\tttlMs?: number;\n\t/**\n\t * Optional namespace to prefix all cache keys\n\t * @defaultValue CacheClient options or \"baeta\"\n\t */\n\tnamespace?: string;\n\t/**\n\t * Function to parse stored strings back into source objects.\n\t * If it throws an error, the cache will be treated as a miss and the value will be deleted.\n\t */\n\tparse: (value: string) => Item;\n\t/**\n\t * Function to serialize source objects into strings for cache storage.\n\t */\n\tserialize: (value: Item) => string;\n\t/**\n\t * Hook that gets called with the updated items after an update operation.\n\t */\n\tonUpdate?: (item: Item[]) => void | Promise<void>;\n\t/**\n\t * Hook that gets called with the inserted items after an insert operation.\n\t */\n\tonInsert?: (item: Item[]) => void | Promise<void>;\n\t/**\n\t * Hook that gets called with the refs of deleted items after a delete operation.\n\t */\n\tonDelete?: (ref: ItemRef[]) => void | Promise<void>;\n};\n\nexport type OptionalGetRef<Item> = {\n\t/*\n\t * Optional function to extract a string reference from source objects.\n\t * Will use \"id\" property by default.\n\t */\n\tgetRef?: (source: Item) => ItemRef;\n};\n\nexport type RequiredGetRef<Item> = {\n\t/*\n\t * Function to extract a string reference from source objects.\n\t */\n\tgetRef: (source: Item) => ItemRef;\n};\n","import type { ItemRef } from './item.ts';\n\nexport function arrayIsComplete<T>(items: Array<T | null> | null): items is T[] {\n\tif (items == null) {\n\t\treturn false;\n\t}\n\treturn items.every((item) => item != null);\n}\n\nexport function alignItemsWithRefs<T>(partialRefs: Array<ItemRef | null>, items: T[]) {\n\tif (partialRefs.length === items.length) {\n\t\treturn items;\n\t}\n\tlet itemIndex = 0;\n\tconst aligned: Array<T | null> = [];\n\tfor (const ref of partialRefs) {\n\t\tif (ref == null) {\n\t\t\taligned.push(null);\n\t\t} else {\n\t\t\taligned.push(items[itemIndex++]);\n\t\t}\n\t}\n\treturn aligned;\n}\n\nexport function toArray<T>(item: T | T[]): T[] {\n\treturn Array.isArray(item) ? item : [item];\n}\n\nexport function zip<T, U, R>(array1: T[], array2: U[], fn: (a: T, b: U) => R): R[] {\n\tif (array1.length !== array2.length) {\n\t\tthrow new Error('Arrays must have the same length to be zipped');\n\t}\n\tconst result: R[] = [];\n\tfor (let i = 0; i < array1.length; i++) {\n\t\tresult.push(fn(array1[i], array2[i]));\n\t}\n\treturn result;\n}\n","import { log } from '@baeta/util-log';\nimport DataLoader from 'dataloader';\nimport type { CacheClient, CacheClientArgs } from './client.ts';\nimport { assertValidRefType, type ItemRef } from './item.ts';\nimport {\n\tbuildCacheRevisionId,\n\tbuildItemCacheKeyId,\n\tbuildQueryCacheIndexKeyId,\n\tbuildQueryCacheIndexKeysIds,\n\tbuildQueryCacheKeyId,\n\ttype CacheRevisionId,\n\ttype ItemCacheKey,\n\ttype ItemCacheKeyPrefix,\n\ttype QueryCacheIndexKey,\n\ttype QueryCacheKey,\n\ttype QueryCacheKeyPrefix,\n} from './key.ts';\nimport { DEFAULT_NAMESPACE, DEFAULT_REVISION, DEFAULT_TTL_MS } from './options.ts';\nimport type { QueryTag, QueryTagWithArgs, QueryTagWithData, QueryTagWithIndexes } from './query.ts';\nimport { alignItemsWithRefs, arrayIsComplete, toArray } from './utils.ts';\n\nexport interface CacheStoreOptions<Item> {\n\tname: string;\n\tclient: CacheClient;\n\tnamespace?: string;\n\trevision?: string | number;\n\tttlMs?: number;\n\tparse: (value: string) => Item;\n\tserialize: (item: Item) => string;\n\tgetRef?: (source: Item) => ItemRef;\n}\n\ntype QueryMetadata = { refs: Array<null | string>; isList: boolean };\n\nexport class CacheStore<Item> {\n\tprotected readonly name: string;\n\tprotected readonly namespace: string;\n\tprotected readonly revision: string;\n\n\tprotected readonly client: CacheClient;\n\tprotected readonly itemCacheArgs: CacheClientArgs<Item>;\n\tprotected readonly queryCacheArgs: CacheClientArgs<QueryMetadata>;\n\n\tprotected readonly getRef?: (source: Item) => ItemRef;\n\n\tconstructor({\n\t\tname,\n\t\tclient,\n\t\tnamespace,\n\t\trevision,\n\t\tttlMs,\n\t\tparse,\n\t\tserialize,\n\t\tgetRef,\n\t}: CacheStoreOptions<Item>) {\n\t\tthis.name = name;\n\t\tthis.namespace = namespace ?? DEFAULT_NAMESPACE;\n\t\tthis.revision = revision?.toString() ?? DEFAULT_REVISION;\n\t\tthis.client = client;\n\t\tthis.getRef = getRef;\n\t\tthis.itemCacheArgs = {\n\t\t\tttlMs: ttlMs ?? DEFAULT_TTL_MS,\n\t\t\tparse: (value: string) => parseItemSafely(value, parse),\n\t\t\tserialize: (item: Item) => serialize(item),\n\t\t};\n\t\tthis.queryCacheArgs = {\n\t\t\tttlMs: ttlMs ?? DEFAULT_TTL_MS,\n\t\t\tparse: (value: string) => parseQueryMetadataSafely(value),\n\t\t\tserialize: (meta: QueryMetadata) => JSON.stringify(meta),\n\t\t};\n\t}\n\n\tprotected loadAndParse = async (refs: ReadonlyArray<ItemRef>): Promise<Array<Item | null>> => {\n\t\tconst keys = refs.map((ref) => this.getItemKeyByRef(ref));\n\t\treturn await this.client.getPartialItems(keys, this.itemCacheArgs);\n\t};\n\n\tprotected loader = new DataLoader<ItemRef, Item | null>(this.loadAndParse, {\n\t\tcache: false,\n\t});\n\n\tasync get(ref: ItemRef): Promise<Item | null> {\n\t\treturn await this.loader.load(ref);\n\t}\n\n\tasync getMany(refs: ItemRef[]): Promise<Item[] | null> {\n\t\tconst results = await Promise.all(refs.map((r) => this.get(r)));\n\t\tif (!arrayIsComplete(results)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn results;\n\t}\n\n\tasync getPartial(refs: ItemRef[]): Promise<Array<Item | null>> {\n\t\treturn await Promise.all(refs.map((ref) => this.get(ref)));\n\t}\n\n\tasync save(item: Item | Item[]) {\n\t\tconst values = toArray(item);\n\t\tconst entries: Array<[ItemCacheKey, Item]> = values.map((item) => [\n\t\t\tthis.getItemKey(item),\n\t\t\titem,\n\t\t]);\n\t\treturn await this.client.saveItems(entries, this.itemCacheArgs);\n\t}\n\n\tasync saveIfNotExists(item: Item | Item[]) {\n\t\tconst values = toArray(item);\n\t\tconst entries: Array<[ItemCacheKey, Item]> = values.map((item) => [\n\t\t\tthis.getItemKey(item),\n\t\t\titem,\n\t\t]);\n\t\treturn await this.client.saveItems(entries, this.itemCacheArgs, {\n\t\t\tdisableOverwrite: true,\n\t\t});\n\t}\n\n\tasync saveWithDiff(item: Item | Item[]): Promise<Array<Item | null>> {\n\t\tconst values = toArray(item);\n\t\tconst entries: Array<[ItemCacheKey, Item]> = values.map((item) => [\n\t\t\tthis.getItemKey(item),\n\t\t\titem,\n\t\t]);\n\t\treturn await this.client.saveItemsWithDiff(entries, this.itemCacheArgs);\n\t}\n\n\tasync delete(ref: ItemRef | ItemRef[]) {\n\t\tconst refs = toArray(ref);\n\t\tconst keys = refs.map((r) => this.getItemKeyByRef(r));\n\t\tawait this.client.deleteItems(keys, this.itemCacheArgs);\n\t}\n\n\tasync deleteWithDiff(ref: ItemRef | ItemRef[]): Promise<Array<Item | null>> {\n\t\tconst refs = toArray(ref);\n\t\tconst keys = refs.map((r) => this.getItemKeyByRef(r));\n\t\treturn await this.client.deleteItemsWithDiff(keys, this.itemCacheArgs);\n\t}\n\n\tasync getQuery(query: QueryTagWithArgs): Promise<{ query: unknown } | null> {\n\t\tconst queryKey = this.getQueryKey(query);\n\t\tconst meta = await this.client.getQuery(queryKey, this.queryCacheArgs);\n\n\t\tif (meta == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst isList = meta.isList;\n\t\tconst nullableRefs = meta.refs;\n\t\tconst refs = nullableRefs.filter((ref) => ref != null);\n\t\tconst items = await this.getMany(refs);\n\n\t\tif (items == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst aligned = alignItemsWithRefs(nullableRefs, items);\n\t\tconst result = isList ? aligned : (aligned.at(0) ?? null);\n\n\t\treturn { query: result };\n\t}\n\n\tasync saveQuery(\n\t\tquery: QueryTagWithData<Item>,\n\t\treplaceExistingItems = false,\n\t\tttlMs?: number,\n\t): Promise<void> {\n\t\tconst data = query.data;\n\t\tconst queryKey = this.getQueryKey(query);\n\t\tconst queryIndexes = this.getQueryIndexes(query, true);\n\n\t\tconst isList = Array.isArray(data);\n\t\tconst nullableItems = isList ? data : [data];\n\t\tconst items = nullableItems.filter((item) => item != null);\n\n\t\tif (items.length > 0) {\n\t\t\tif (replaceExistingItems) {\n\t\t\t\tawait this.save(items);\n\t\t\t} else {\n\t\t\t\tawait this.saveIfNotExists(items);\n\t\t\t}\n\t\t}\n\n\t\tconst encodedRefs = nullableItems.map((item) => {\n\t\t\tif (item == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn this.getRefWithFallback(item).toString();\n\t\t});\n\n\t\tconst metadata = {\n\t\t\trefs: encodedRefs,\n\t\t\tisList,\n\t\t};\n\n\t\treturn await this.client.saveQuery(queryKey, queryIndexes, metadata, {\n\t\t\t...this.queryCacheArgs,\n\t\t\tttlMs: ttlMs ?? this.queryCacheArgs.ttlMs,\n\t\t});\n\t}\n\n\tdeleteQueries(query: QueryTagWithIndexes): Promise<void> {\n\t\tconst queryIndexes = this.getQueryIndexes(query);\n\t\treturn this.client.deleteQueries(queryIndexes, this.queryCacheArgs);\n\t}\n\n\tgetRefWithFallback(root: Item): ItemRef {\n\t\tif (this.getRef) {\n\t\t\treturn this.getRef(root);\n\t\t}\n\t\tif (root == null) {\n\t\t\tthrow new Error('Object is null or undefined, cannot get ref');\n\t\t}\n\t\tif (typeof root === 'object' && 'id' in root) {\n\t\t\tassertValidRefType(root.id);\n\t\t\treturn root.id.toString();\n\t\t}\n\t\tthrow new Error('Object does not have id. Define \"getRef\" function in cache options');\n\t}\n\n\tgetItemKey(item: Item): ItemCacheKey {\n\t\tconst ref = this.getRefWithFallback(item);\n\t\treturn this.getItemKeyByRef(ref);\n\t}\n\n\tgetItemKeyByRef(ref: ItemRef): ItemCacheKey {\n\t\tconst prefix: ItemCacheKeyPrefix = `${this.namespace}:{${this.name}:item:${buildCacheRevisionId(this.revision)}}`;\n\t\tconst keyId = buildItemCacheKeyId(ref.toString());\n\t\treturn `${prefix}:${keyId}`;\n\t}\n\n\tgetQueryKey(query: QueryTagWithArgs): QueryCacheKey {\n\t\tconst prefix = this.getQueryKeyPrefix(query);\n\t\treturn `${prefix}:${buildQueryCacheKeyId(query.args)}`;\n\t}\n\n\tgetQueryIndexes(query: QueryTagWithIndexes, ensureCatchAll = false): QueryCacheIndexKey[] {\n\t\tconst prefix = this.getQueryKeyPrefix(query);\n\t\tconst keyIndexes = buildQueryCacheIndexKeysIds(query.indexes);\n\t\tif (ensureCatchAll && query.indexes.length > 0) {\n\t\t\tkeyIndexes.push(buildQueryCacheIndexKeyId());\n\t\t}\n\t\treturn keyIndexes.map((keyIndex) => `${prefix}:${keyIndex}` as const);\n\t}\n\n\tgetQueryKeyPrefix(query: QueryTag): QueryCacheKeyPrefix {\n\t\treturn `${this.namespace}:{${this.name}:query:${query.name}:${this.getQueryKeyRevision(query)}}`;\n\t}\n\n\tgetQueryKeyRevision(query: QueryTag): CacheRevisionId {\n\t\tconst combinedRevision = `${this.revision}_${query.revision?.toString() ?? DEFAULT_REVISION}`;\n\t\treturn buildCacheRevisionId(combinedRevision);\n\t}\n}\n\nfunction parseItemSafely<T>(value: string, parseItem: (value: string) => T): T | null {\n\ttry {\n\t\treturn parseItem(value);\n\t} catch (err) {\n\t\tlog.warn(err, 'Failed to parse item, returning null');\n\t\treturn null;\n\t}\n}\n\nfunction parseQueryMetadataSafely(value: string): QueryMetadata | null {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (err) {\n\t\tlog.warn(err, 'Failed to parse query metadata, treating as cache miss');\n\t\treturn null;\n\t}\n\tif (!isValidQueryMetadata(parsed)) {\n\t\tlog.warn('Query metadata has invalid shape, treating as cache miss');\n\t\treturn null;\n\t}\n\treturn parsed;\n}\n\nfunction isValidQueryMetadata(value: unknown): value is QueryMetadata {\n\tif (value == null || typeof value !== 'object') {\n\t\treturn false;\n\t}\n\tconst candidate = value as { refs?: unknown; isList?: unknown };\n\tif (!Array.isArray(candidate.refs)) {\n\t\treturn false;\n\t}\n\tif (typeof candidate.isList !== 'boolean') {\n\t\treturn false;\n\t}\n\treturn candidate.refs.every((ref) => ref === null || typeof ref === 'string');\n}\n","import { log } from '@baeta/util-log';\nimport type { CacheClient } from './client.ts';\nimport type { QueryDefinitionMap, QueryMethod, QueryMethodMap } from './define-query.ts';\nimport type { ItemRef, RefCompatibleItem } from './item.ts';\nimport type { CacheOptions, OptionalGetRef, RequiredGetRef } from './options.ts';\nimport type { QueryOnDeleteHook, QueryOnInsertHook, QueryOnUpdateHook, QueryTag } from './query.ts';\nimport { CacheStore } from './store.ts';\nimport { toArray, zip } from './utils.ts';\n\nexport type CacheHooksOptions = {\n\t/**\n\t * If true, hooks will be awaited. If false, hooks will be fired asynchronously and will not block cache methods.\n\t * @defaultValue false\n\t */\n\twaitForHooks?: boolean;\n};\n\nexport type Cache<Item> = {\n\tget(ref: ItemRef): Promise<Item | null>;\n\tgetMany(refs: ItemRef[]): Promise<Item[] | null>;\n\tgetPartial(refs: ItemRef[]): Promise<Array<Item | null>>;\n\tupdate(item: Item | Item[]): Promise<void>;\n\tinsert(item: Item | Item[]): Promise<void>;\n\tdelete(ref: ItemRef | ItemRef[]): Promise<void>;\n};\n\nexport type CacheWithQueries<Item, QueryDefinitions extends QueryDefinitionMap<Item>> = {\n\tget(ref: ItemRef): Promise<Item | null>;\n\tgetMany(refs: ItemRef[]): Promise<Item[] | null>;\n\tgetPartial(refs: ItemRef[]): Promise<Array<Item | null>>;\n\tupdate(item: Item | Item[], options?: CacheHooksOptions): Promise<void>;\n\tinsert(item: Item | Item[], options?: CacheHooksOptions): Promise<void>;\n\tdelete(ref: ItemRef | ItemRef[], options?: CacheHooksOptions): Promise<void>;\n\tdeleteQueries(): Promise<void>;\n\tqueries: QueryMethodMap<QueryDefinitions>;\n};\n\nexport type CreateCacheFactory<Item> = {\n\tbuild: () => Cache<Item>;\n\twithQueries: <QueryDefinitions extends QueryDefinitionMap<Item>>(\n\t\tdefinitions: QueryDefinitions,\n\t) => {\n\t\tbuild: () => CacheWithQueries<Item, QueryDefinitions>;\n\t};\n};\n\nexport function createCache<Item extends RefCompatibleItem>(\n\tclient: CacheClient,\n\toptions: CacheOptions<Item> & OptionalGetRef<Item>,\n): CreateCacheFactory<Item>;\nexport function createCache<Item>(\n\tclient: CacheClient,\n\toptions: CacheOptions<Item> & RequiredGetRef<Item>,\n): CreateCacheFactory<Item>;\nexport function createCache<Item>(\n\tclient: CacheClient,\n\toptions: CacheOptions<Item> & OptionalGetRef<Item>,\n): CreateCacheFactory<Item> {\n\tconst storeClient = new CacheStore<Item>({\n\t\tclient,\n\t\tname: options.name,\n\t\tparse: options.parse,\n\t\tserialize: options.serialize,\n\t\tgetRef: options.getRef,\n\t\tttlMs: options.ttlMs ?? client.options?.ttlMs,\n\t\tnamespace: options.namespace ?? client.options?.namespace,\n\t\trevision: options.revision ?? client.options?.revision,\n\t});\n\treturn {\n\t\tbuild: () =>\n\t\t\tcreateCacheMethods(\n\t\t\t\tstoreClient,\n\t\t\t\t{\n\t\t\t\t\tonUpdateHook: options.onUpdate,\n\t\t\t\t\tonInsertHook: options.onInsert,\n\t\t\t\t\tonDeleteHook: options.onDelete,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tonUpdateHooks: [],\n\t\t\t\t\tonInsertHooks: [],\n\t\t\t\t\tonDeleteHooks: [],\n\t\t\t\t},\n\t\t\t),\n\t\twithQueries: (definitions) => {\n\t\t\treturn {\n\t\t\t\tbuild: () => createCacheMethodsWithQueries(storeClient, options, definitions),\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction createCacheMethods<Item>(\n\tstoreClient: CacheStore<Item>,\n\titemHooks: {\n\t\tonUpdateHook?: (item: Item[]) => void | Promise<void>;\n\t\tonInsertHook?: (item: Item[]) => void | Promise<void>;\n\t\tonDeleteHook?: (ref: ItemRef[]) => void | Promise<void>;\n\t},\n\tqueryHooks: {\n\t\tonUpdateHooks: Array<QueryOnUpdateHook<Item>>;\n\t\tonInsertHooks: Array<QueryOnInsertHook<Item>>;\n\t\tonDeleteHooks: Array<QueryOnDeleteHook<Item>>;\n\t},\n): Cache<Item> | Omit<CacheWithQueries<Item, any>, 'queries' | 'deleteQueries'> {\n\treturn {\n\t\tget: (ref) => storeClient.get(ref),\n\t\tgetMany: (refs) => storeClient.getMany(refs),\n\t\tgetPartial: (refs) => storeClient.getPartial(refs),\n\t\tupdate: async (item, options) => {\n\t\t\tif (queryHooks.onUpdateHooks.length === 0) {\n\t\t\t\tawait storeClient.save(item);\n\t\t\t\tawait fireHook(itemHooks.onUpdateHook, toArray(item), options);\n\t\t\t} else {\n\t\t\t\tconst items = toArray(item);\n\t\t\t\tconst previous = await storeClient.saveWithDiff(items);\n\t\t\t\tconst pairs = zip(items, previous, (next, previous) => ({ next, previous }));\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfireHook(itemHooks.onUpdateHook, items, options),\n\t\t\t\t\tfireHooks(queryHooks.onUpdateHooks, pairs, options),\n\t\t\t\t]);\n\t\t\t}\n\t\t},\n\t\tinsert: async (item, options) => {\n\t\t\tawait storeClient.save(item);\n\t\t\tif (queryHooks.onInsertHooks.length === 0) {\n\t\t\t\tawait fireHook(itemHooks.onInsertHook, toArray(item), options);\n\t\t\t} else {\n\t\t\t\tconst items = toArray(item);\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfireHook(itemHooks.onInsertHook, items, options),\n\t\t\t\t\tfireHooks(queryHooks.onInsertHooks, items, options),\n\t\t\t\t]);\n\t\t\t}\n\t\t},\n\t\tdelete: async (ref, options) => {\n\t\t\tif (queryHooks.onDeleteHooks.length === 0) {\n\t\t\t\tawait storeClient.delete(ref);\n\t\t\t\tawait fireHook(itemHooks.onDeleteHook, toArray(ref), options);\n\t\t\t} else {\n\t\t\t\tconst refs = toArray(ref);\n\t\t\t\tconst previous = await storeClient.deleteWithDiff(refs);\n\t\t\t\tawait storeClient.delete(refs);\n\t\t\t\tconst pairs = zip(refs, previous, (ref, previous) => ({ ref, previous }));\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfireHook(itemHooks.onDeleteHook, refs, options),\n\t\t\t\t\tfireHooks(queryHooks.onDeleteHooks, pairs, options),\n\t\t\t\t]);\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction createCacheMethodsWithQueries<Item, QueryDefinitions extends QueryDefinitionMap<Item>>(\n\tstoreClient: CacheStore<Item>,\n\toptions: CacheOptions<Item> & OptionalGetRef<Item>,\n\tdefinitions: QueryDefinitions,\n): CacheWithQueries<Item, QueryDefinitions> {\n\tconst { queries, queryTags, onUpdateHooks, onInsertHooks, onDeleteHooks } = createQueriesAndHooks<\n\t\tItem,\n\t\tQueryDefinitions\n\t>(definitions, storeClient);\n\treturn {\n\t\t...createCacheMethods(\n\t\t\tstoreClient,\n\t\t\t{\n\t\t\t\tonUpdateHook: options.onUpdate,\n\t\t\t\tonInsertHook: options.onInsert,\n\t\t\t\tonDeleteHook: options.onDelete,\n\t\t\t},\n\t\t\t{ onUpdateHooks, onInsertHooks, onDeleteHooks },\n\t\t),\n\t\tdeleteQueries: async () => {\n\t\t\tconst promises = queryTags.map((tag) =>\n\t\t\t\tstoreClient.deleteQueries({\n\t\t\t\t\t...tag,\n\t\t\t\t\tindexes: [],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait Promise.all(promises);\n\t\t},\n\t\tqueries,\n\t};\n}\n\nfunction createQueriesAndHooks<Item, QueryDefinitions extends QueryDefinitionMap<Item>>(\n\tdefinitions: QueryDefinitions | undefined,\n\tstore: CacheStore<Item>,\n) {\n\tconst queries: Partial<QueryMethodMap<QueryDefinitions>> = {};\n\tconst queryTags: QueryTag[] = [];\n\tconst onUpdateHooks: Array<QueryOnUpdateHook<Item>> = [];\n\tconst onInsertHooks: Array<QueryOnInsertHook<Item>> = [];\n\tconst onDeleteHooks: Array<QueryOnDeleteHook<Item>> = [];\n\tfor (const [queryName, buildQuery] of Object.entries(definitions ?? {})) {\n\t\tconst queryHandler = buildQuery(store, queryName);\n\t\tqueries[queryName as keyof QueryDefinitions] = queryHandler.call as QueryMethod<\n\t\t\tQueryDefinitions[keyof QueryDefinitions]\n\t\t>;\n\t\tqueryTags.push(queryHandler.tag);\n\t\tif (queryHandler.hooks.onUpdate) {\n\t\t\tonUpdateHooks.push(queryHandler.hooks.onUpdate);\n\t\t}\n\t\tif (queryHandler.hooks.onDelete) {\n\t\t\tonDeleteHooks.push(queryHandler.hooks.onDelete);\n\t\t}\n\t\tif (queryHandler.hooks.onInsert) {\n\t\t\tonInsertHooks.push(queryHandler.hooks.onInsert);\n\t\t}\n\t}\n\treturn {\n\t\tqueries: queries as QueryMethodMap<QueryDefinitions>,\n\t\tqueryTags,\n\t\tonUpdateHooks,\n\t\tonInsertHooks,\n\t\tonDeleteHooks,\n\t};\n}\n\nasync function fireHook<T>(\n\thook: ((v: T) => void | Promise<void>) | undefined,\n\tvalue: T,\n\toptions?: CacheHooksOptions,\n) {\n\tif (hook == null) {\n\t\treturn;\n\t}\n\tif (options?.waitForHooks) {\n\t\tawait hook(value);\n\t} else {\n\t\tPromise.resolve()\n\t\t\t.then(() => hook(value))\n\t\t\t.catch((err) => {\n\t\t\t\tlog.error(err, 'Error in cache hook');\n\t\t\t});\n\t}\n}\n\nasync function fireHooks<T>(\n\thooks: Array<(v: T) => Promise<void>>,\n\tvalue: T,\n\toptions?: CacheHooksOptions,\n) {\n\tawait Promise.all(hooks.map((hook) => fireHook(hook, value, options)));\n}\n","import { log } from '@baeta/util-log';\nimport type {\n\tIndexedQueryArgs,\n\tItemFromQueryResult,\n\tQueryArgs,\n\tQueryArgsIndexes,\n\tQueryHandle,\n\tQueryHelpers,\n\tQueryHelpersWithIndexes,\n\tQueryIndex,\n\tQueryOptions,\n\tQueryResult,\n\tQueryTag,\n} from './query.ts';\nimport type { CacheStore } from './store.ts';\n\nexport type QueryFactory<\n\tResult extends QueryResult<Item>,\n\tArgs extends QueryArgs,\n\tIndexes extends QueryArgsIndexes<Args, Indexes>,\n\tItem = ItemFromQueryResult<Result>,\n> = (store: CacheStore<Item>, name: string) => QueryHandle<Result, Args, Indexes, Item>;\n\nexport type QueryMethod<T extends QueryFactory<any, any, any>> =\n\tT extends QueryFactory<infer Result, infer Args, any, any>\n\t\t? (args: Args) => Promise<Result>\n\t\t: never;\n\nexport type QueryDefinitionMap<Item> = Record<\n\tstring,\n\tQueryFactory<QueryResult<Item>, any, any, Item>\n>;\n\nexport type QueryMethodMap<Definitions extends QueryDefinitionMap<any>> = {\n\t[K in keyof Definitions]: QueryMethod<Definitions[K]>;\n};\n\nexport function defineQuery<\n\tResult extends QueryResult<Item>,\n\tArgs extends QueryArgs = Record<string, never>,\n\tIndexes extends QueryArgsIndexes<Args, Indexes> = Record<string, never>,\n\tItem = ItemFromQueryResult<Result>,\n>(options: QueryOptions<Result, Args, Indexes, Item>): QueryFactory<Result, Args, Indexes, Item> {\n\treturn (store: CacheStore<Item>, name: string) => {\n\t\tconst tag: QueryTag = {\n\t\t\tname,\n\t\t\trevision: options.revision,\n\t\t};\n\n\t\tconst resolve = async (args: Args): Promise<Result> => {\n\t\t\tconst fromCache = await store\n\t\t\t\t.getQuery({\n\t\t\t\t\t...tag,\n\t\t\t\t\targs,\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tlog.error(err, `Failed to get query result for ${name}. Proceeding without caching.`);\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\n\t\t\tif (fromCache != null) {\n\t\t\t\treturn fromCache.query as Result;\n\t\t\t}\n\n\t\t\tconst data = await options.resolve(args);\n\n\t\t\tawait store\n\t\t\t\t.saveQuery(\n\t\t\t\t\t{\n\t\t\t\t\t\t...tag,\n\t\t\t\t\t\targs,\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\tindexes: buildIndexes(args, options.indexArgsBy),\n\t\t\t\t\t},\n\t\t\t\t\toptions.replaceExistingItems,\n\t\t\t\t\toptions.ttlMs,\n\t\t\t\t)\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tlog.error(err, `Failed to save query result for ${name}. Proceeding without caching.`);\n\t\t\t\t});\n\n\t\t\treturn data;\n\t\t};\n\n\t\tconst invalidateAll = async () => {\n\t\t\tawait store.deleteQueries({\n\t\t\t\t...tag,\n\t\t\t\tindexes: [],\n\t\t\t});\n\t\t};\n\n\t\tconst invalidateByArgs = async (\n\t\t\targs: Partial<IndexedQueryArgs<Args, Indexes>> | Partial<IndexedQueryArgs<Args, Indexes>>[],\n\t\t) => {\n\t\t\tconst argsArray = Array.isArray(args) ? args : [args];\n\t\t\tconst deletePromises = argsArray.map((args) =>\n\t\t\t\tstore.deleteQueries({\n\t\t\t\t\t...tag,\n\t\t\t\t\tindexes: buildIndexes(args, options.indexArgsBy),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait Promise.all(deletePromises);\n\t\t};\n\n\t\tconst helpers: QueryHelpersWithIndexes<Args, Indexes> = {\n\t\t\tinvalidateAll,\n\t\t\tinvalidateByArgs,\n\t\t};\n\t\tconst helpersCast: QueryHelpers<Args, Indexes> = helpers as QueryHelpers<Args, Indexes>;\n\n\t\tconst { onUpdate, onDelete, onInsert } = options;\n\n\t\tconst hooks = {\n\t\t\tonUpdate: hookWithHelpers(helpersCast, onUpdate),\n\t\t\tonInsert: hookWithHelpers(helpersCast, onInsert),\n\t\t\tonDelete: hookWithHelpers(helpersCast, onDelete),\n\t\t};\n\n\t\treturn {\n\t\t\tcall: resolve,\n\t\t\ttag,\n\t\t\thelpers,\n\t\t\thooks,\n\t\t};\n\t};\n}\n\nfunction hookWithHelpers<Helpers, Args>(\n\thelpers: Helpers,\n\thook?: (args: Args, helpers: Helpers) => void | Promise<void>,\n) {\n\tif (!hook) {\n\t\treturn undefined;\n\t}\n\tconst hookWithHelpers = async (args: Args) => {\n\t\tawait hook(args, helpers);\n\t};\n\treturn hookWithHelpers;\n}\n\nfunction buildIndexes<Args extends QueryArgs, Indexes extends QueryArgsIndexes<Args, Indexes>>(\n\targs: Partial<Args>,\n\tindexArgsBy?: Indexes,\n): QueryIndex[] {\n\tif (!indexArgsBy) {\n\t\treturn [];\n\t}\n\tconst indexes: QueryIndex[] = [];\n\tfor (const key in indexArgsBy) {\n\t\tif (!Object.hasOwn(args, key)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst value = args[key];\n\t\tif (value === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\ttypeof value !== 'string' &&\n\t\t\ttypeof value !== 'number' &&\n\t\t\ttypeof value !== 'boolean' &&\n\t\t\tvalue !== null\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Cache index '${key}' must be a primitive (string, number, boolean, or null) but received ${\n\t\t\t\t\tArray.isArray(value) ? 'array' : typeof value\n\t\t\t\t}.`,\n\t\t\t);\n\t\t}\n\t\tindexes.push([key, value]);\n\t}\n\treturn indexes;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../lib/client.ts","../lib/logger.ts","../lib/item.ts","../lib/key.ts","../lib/options.ts","../lib/utils.ts","../lib/store.ts","../lib/create-cache.ts","../lib/define-query.ts"],"sourcesContent":["import type { ItemCacheKey, QueryCacheIndexKey, QueryCacheKey } from './key.ts';\n\nexport interface CacheClientArgs<Item> {\n\tttlMs: number;\n\tparse: (value: string) => Item | null;\n\tserialize: (item: Item) => string;\n}\n\nexport interface CacheClientOptions {\n\t/**\n\t * Revision number for cache invalidation.\n\t * Incrementing this number will invalidate all existing cache entries for this store.\n\t * @defaultValue \"default\"\n\t */\n\trevision?: number;\n\t/**\n\t * Time-to-live in milliseconds\n\t * @defaultValue 3_600_000 (1 hour)\n\t */\n\tttlMs?: number;\n\t/**\n\t * Optional namespace to prefix all cache keys\n\t * @defaultValue \"baeta\"\n\t */\n\tnamespace?: string;\n}\n\nexport interface CacheClientSaveOptions {\n\t/**\n\t * If true, the save operation will not overwrite existing cache entries. Instead, it will only save entries that do not already exist in the cache.\n\t * @defaultValue false\n\t */\n\tdisableOverwrite?: boolean;\n}\n\nexport abstract class CacheClient {\n\toptions?: CacheClientOptions;\n\n\tconstructor(options?: CacheClientOptions) {\n\t\tthis.options = options;\n\t}\n\n\tabstract getPartialItems<Item>(\n\t\tkeys: ItemCacheKey[],\n\t\toptions: CacheClientArgs<Item>,\n\t): Promise<Array<Item | null>>;\n\n\tabstract saveItems<Item>(\n\t\titems: Array<[ItemCacheKey, Item]>,\n\t\toptions: CacheClientArgs<Item>,\n\t\tsaveOptions?: CacheClientSaveOptions,\n\t): Promise<void>;\n\n\tabstract saveItemsWithDiff<Item>(\n\t\titems: Array<[ItemCacheKey, Item]>,\n\t\toptions: CacheClientArgs<Item>,\n\t): Promise<Array<Item | null>>;\n\n\tabstract deleteItems<Item>(keys: ItemCacheKey[], options: CacheClientArgs<Item>): Promise<void>;\n\n\tabstract deleteItemsWithDiff<Item>(\n\t\tkeys: ItemCacheKey[],\n\t\toptions: CacheClientArgs<Item>,\n\t): Promise<Array<Item | null>>;\n\n\tabstract getQuery<QueryMetadata>(\n\t\tkey: QueryCacheKey,\n\t\toptions: CacheClientArgs<QueryMetadata>,\n\t): Promise<QueryMetadata | null>;\n\n\tabstract saveQuery<QueryMetadata>(\n\t\tkey: QueryCacheKey,\n\t\tindexes: QueryCacheIndexKey[],\n\t\tmetadata: QueryMetadata,\n\t\toptions: CacheClientArgs<QueryMetadata>,\n\t): Promise<void>;\n\n\tabstract deleteQueries<QueryMetadata>(\n\t\tindexes: QueryCacheIndexKey[],\n\t\toptions: CacheClientArgs<QueryMetadata>,\n\t): Promise<void>;\n}\n","import { createLogger } from '@baeta/util-log';\n\nexport const logger = createLogger('@baeta/cache');\n","/**\n * Reference type for cached items\n */\nexport type ItemRef = string | number | bigint;\n\n/**\n * Type constraint for objects that are compatible with default cache ref\n */\nexport type RefCompatibleItem = { id: string | number | bigint };\n\n/**\n * Asserts that a given reference is of a valid type (string, number, or bigint) and that it can be safely serialized.\n */\nexport function assertValidRefType(ref: unknown): asserts ref is string | number | bigint {\n\tif (typeof ref !== 'string' && typeof ref !== 'number' && typeof ref !== 'bigint') {\n\t\tthrow new TypeError(\n\t\t\t'Reference must be string, number or bigint. Define getRef function in cache options',\n\t\t);\n\t}\n}\n","import { hash } from 'ohash';\nimport type { QueryArgs, QueryIndex } from './query.ts';\n\nexport type QueryCacheKey = `${QueryCacheKeyPrefix}:${QueryCacheKeyId}`;\nexport type QueryCacheIndexKey = `${QueryCacheKeyPrefix}:${QueryCacheIndexKeyId}`;\nexport type QueryCacheKeyId = `id:${string}`;\nexport type QueryCacheIndexKeyId = `idx:${string}`;\nexport type QueryCacheKeyPrefix = `${string}:${string}:query:${string}:${CacheRevisionId}`; // $namespace:$storeName:query:$queryName:rev_$value\n\nexport type ItemCacheKey = `${ItemCacheKeyPrefix}:${ItemCacheKeyId}`;\nexport type ItemCacheKeyId = `id:${string}`;\nexport type ItemCacheKeyPrefix = `${string}:${string}:item:${CacheRevisionId}`; // $namespace:$storeName:item:rev_$value\n\nexport type CacheRevisionId = `rev_${string}`;\n\nexport function buildCacheRevisionId(revision: string): CacheRevisionId {\n\treturn `rev_${revision}`;\n}\n\nexport function buildItemCacheKeyId(id: string): ItemCacheKeyId {\n\treturn `id:${id}`;\n}\n\nexport function buildQueryCacheKeyId(args: QueryArgs): QueryCacheKeyId {\n\treturn `id:${hash(args)}`;\n}\n\nexport function buildQueryCacheIndexKeyId(index?: QueryIndex | undefined): QueryCacheIndexKeyId {\n\tif (index == null) {\n\t\treturn 'idx:';\n\t}\n\treturn `idx:${hash(index)}`;\n}\n\nexport function buildQueryCacheIndexKeysIds(indexes: QueryIndex[]): QueryCacheIndexKeyId[] {\n\tif (indexes.length === 0) {\n\t\treturn ['idx:'];\n\t}\n\treturn indexes.map((idx) => buildQueryCacheIndexKeyId(idx));\n}\n","import type { ItemRef } from './item.ts';\n\nexport const DEFAULT_REVISION = 'default';\nexport const DEFAULT_NAMESPACE = 'baeta';\nexport const DEFAULT_TTL_MS = 3_600_000; // 1 hour\n\n/**\n * Configuration options for cache stores\n */\nexport type CacheOptions<Item> = {\n\t/**\n\t * Unique name for the cache store. Used as a prefix for cache keys to avoid collisions.\n\t */\n\tname: string;\n\t/**\n\t * Revision number for cache invalidation.\n\t * Incrementing this number will invalidate all existing cache entries for this store.\n\t * @defaultValue CacheClient options or \"default\"\n\t */\n\trevision?: number;\n\t/**\n\t * Time-to-live in milliseconds\n\t * @defaultValue CacheClient options or 3_600_000 (1 hour)\n\t */\n\tttlMs?: number;\n\t/**\n\t * Optional namespace to prefix all cache keys\n\t * @defaultValue CacheClient options or \"baeta\"\n\t */\n\tnamespace?: string;\n\t/**\n\t * Function to parse stored strings back into source objects.\n\t * If it throws an error, the cache will be treated as a miss and the value will be deleted.\n\t */\n\tparse: (value: string) => Item;\n\t/**\n\t * Function to serialize source objects into strings for cache storage.\n\t */\n\tserialize: (value: Item) => string;\n\t/**\n\t * Hook that gets called with the updated items after an update operation.\n\t */\n\tonUpdate?: (item: Item[]) => void | Promise<void>;\n\t/**\n\t * Hook that gets called with the inserted items after an insert operation.\n\t */\n\tonInsert?: (item: Item[]) => void | Promise<void>;\n\t/**\n\t * Hook that gets called with the refs of deleted items after a delete operation.\n\t */\n\tonDelete?: (ref: ItemRef[]) => void | Promise<void>;\n};\n\nexport type OptionalGetRef<Item> = {\n\t/*\n\t * Optional function to extract a string reference from source objects.\n\t * Will use \"id\" property by default.\n\t */\n\tgetRef?: (source: Item) => ItemRef;\n};\n\nexport type RequiredGetRef<Item> = {\n\t/*\n\t * Function to extract a string reference from source objects.\n\t */\n\tgetRef: (source: Item) => ItemRef;\n};\n","import type { ItemRef } from './item.ts';\n\nexport function arrayIsComplete<T>(items: Array<T | null> | null): items is T[] {\n\tif (items == null) {\n\t\treturn false;\n\t}\n\treturn items.every((item) => item != null);\n}\n\nexport function alignItemsWithRefs<T>(partialRefs: Array<ItemRef | null>, items: T[]) {\n\tif (partialRefs.length === items.length) {\n\t\treturn items;\n\t}\n\tlet itemIndex = 0;\n\tconst aligned: Array<T | null> = [];\n\tfor (const ref of partialRefs) {\n\t\tif (ref == null) {\n\t\t\taligned.push(null);\n\t\t} else {\n\t\t\taligned.push(items[itemIndex++]);\n\t\t}\n\t}\n\treturn aligned;\n}\n\nexport function toArray<T>(item: T | T[]): T[] {\n\treturn Array.isArray(item) ? item : [item];\n}\n\nexport function zip<T, U, R>(array1: T[], array2: U[], fn: (a: T, b: U) => R): R[] {\n\tif (array1.length !== array2.length) {\n\t\tthrow new Error('Arrays must have the same length to be zipped');\n\t}\n\tconst result: R[] = [];\n\tfor (let i = 0; i < array1.length; i++) {\n\t\tresult.push(fn(array1[i], array2[i]));\n\t}\n\treturn result;\n}\n","import DataLoader from 'dataloader';\nimport type { CacheClient, CacheClientArgs } from './client.ts';\nimport { assertValidRefType, type ItemRef } from './item.ts';\nimport {\n\tbuildCacheRevisionId,\n\tbuildItemCacheKeyId,\n\tbuildQueryCacheIndexKeyId,\n\tbuildQueryCacheIndexKeysIds,\n\tbuildQueryCacheKeyId,\n\ttype CacheRevisionId,\n\ttype ItemCacheKey,\n\ttype ItemCacheKeyPrefix,\n\ttype QueryCacheIndexKey,\n\ttype QueryCacheKey,\n\ttype QueryCacheKeyPrefix,\n} from './key.ts';\nimport { logger } from './logger.ts';\nimport { DEFAULT_NAMESPACE, DEFAULT_REVISION, DEFAULT_TTL_MS } from './options.ts';\nimport type { QueryTag, QueryTagWithArgs, QueryTagWithData, QueryTagWithIndexes } from './query.ts';\nimport { alignItemsWithRefs, arrayIsComplete, toArray } from './utils.ts';\n\nexport interface CacheStoreOptions<Item> {\n\tname: string;\n\tclient: CacheClient;\n\tnamespace?: string;\n\trevision?: string | number;\n\tttlMs?: number;\n\tparse: (value: string) => Item;\n\tserialize: (item: Item) => string;\n\tgetRef?: (source: Item) => ItemRef;\n}\n\ntype QueryMetadata = { refs: Array<null | string>; isList: boolean };\n\nexport class CacheStore<Item> {\n\tprotected readonly name: string;\n\tprotected readonly namespace: string;\n\tprotected readonly revision: string;\n\n\tprotected readonly client: CacheClient;\n\tprotected readonly itemCacheArgs: CacheClientArgs<Item>;\n\tprotected readonly queryCacheArgs: CacheClientArgs<QueryMetadata>;\n\n\tprotected readonly getRef?: (source: Item) => ItemRef;\n\n\tconstructor({\n\t\tname,\n\t\tclient,\n\t\tnamespace,\n\t\trevision,\n\t\tttlMs,\n\t\tparse,\n\t\tserialize,\n\t\tgetRef,\n\t}: CacheStoreOptions<Item>) {\n\t\tif (ttlMs != null && (!Number.isFinite(ttlMs) || ttlMs <= 0)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid ttlMs for cache \"${name}\": ${ttlMs}. Expected a positive number of milliseconds.`,\n\t\t\t);\n\t\t}\n\t\tthis.name = name;\n\t\tthis.namespace = namespace ?? DEFAULT_NAMESPACE;\n\t\tthis.revision = revision?.toString() ?? DEFAULT_REVISION;\n\t\tthis.client = client;\n\t\tthis.getRef = getRef;\n\t\tthis.itemCacheArgs = {\n\t\t\tttlMs: ttlMs ?? DEFAULT_TTL_MS,\n\t\t\tparse: (value: string) => parseItemSafely(value, parse),\n\t\t\tserialize: (item: Item) => serialize(item),\n\t\t};\n\t\tthis.queryCacheArgs = {\n\t\t\tttlMs: ttlMs ?? DEFAULT_TTL_MS,\n\t\t\tparse: (value: string) => parseQueryMetadataSafely(value),\n\t\t\tserialize: (meta: QueryMetadata) => JSON.stringify(meta),\n\t\t};\n\t}\n\n\tprotected loadAndParse = async (refs: ReadonlyArray<ItemRef>): Promise<Array<Item | null>> => {\n\t\tconst keys = refs.map((ref) => this.getItemKeyByRef(ref));\n\t\treturn await this.client.getPartialItems(keys, this.itemCacheArgs);\n\t};\n\n\tprotected loader = new DataLoader<ItemRef, Item | null>(this.loadAndParse, {\n\t\tcache: false,\n\t});\n\n\tasync get(ref: ItemRef): Promise<Item | null> {\n\t\treturn await this.loader.load(ref);\n\t}\n\n\tasync getMany(refs: ItemRef[]): Promise<Item[] | null> {\n\t\tconst results = await Promise.all(refs.map((r) => this.get(r)));\n\t\tif (!arrayIsComplete(results)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn results;\n\t}\n\n\tasync getPartial(refs: ItemRef[]): Promise<Array<Item | null>> {\n\t\treturn await Promise.all(refs.map((ref) => this.get(ref)));\n\t}\n\n\tasync save(item: Item | Item[]) {\n\t\tconst values = toArray(item);\n\t\tconst entries: Array<[ItemCacheKey, Item]> = values.map((item) => [\n\t\t\tthis.getItemKey(item),\n\t\t\titem,\n\t\t]);\n\t\treturn await this.client.saveItems(entries, this.itemCacheArgs);\n\t}\n\n\tasync saveIfNotExists(item: Item | Item[]) {\n\t\tconst values = toArray(item);\n\t\tconst entries: Array<[ItemCacheKey, Item]> = values.map((item) => [\n\t\t\tthis.getItemKey(item),\n\t\t\titem,\n\t\t]);\n\t\treturn await this.client.saveItems(entries, this.itemCacheArgs, {\n\t\t\tdisableOverwrite: true,\n\t\t});\n\t}\n\n\tasync saveWithDiff(item: Item | Item[]): Promise<Array<Item | null>> {\n\t\tconst values = toArray(item);\n\t\tconst entries: Array<[ItemCacheKey, Item]> = values.map((item) => [\n\t\t\tthis.getItemKey(item),\n\t\t\titem,\n\t\t]);\n\t\treturn await this.client.saveItemsWithDiff(entries, this.itemCacheArgs);\n\t}\n\n\tasync delete(ref: ItemRef | ItemRef[]) {\n\t\tconst refs = toArray(ref);\n\t\tconst keys = refs.map((r) => this.getItemKeyByRef(r));\n\t\tawait this.client.deleteItems(keys, this.itemCacheArgs);\n\t}\n\n\tasync deleteWithDiff(ref: ItemRef | ItemRef[]): Promise<Array<Item | null>> {\n\t\tconst refs = toArray(ref);\n\t\tconst keys = refs.map((r) => this.getItemKeyByRef(r));\n\t\treturn await this.client.deleteItemsWithDiff(keys, this.itemCacheArgs);\n\t}\n\n\tasync getQuery(query: QueryTagWithArgs): Promise<{ query: unknown } | null> {\n\t\tconst queryKey = this.getQueryKey(query);\n\t\tconst meta = await this.client.getQuery(queryKey, this.queryCacheArgs);\n\n\t\tif (meta == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst isList = meta.isList;\n\t\tconst nullableRefs = meta.refs;\n\t\tconst refs = nullableRefs.filter((ref) => ref != null);\n\t\tconst items = await this.getMany(refs);\n\n\t\tif (items == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst aligned = alignItemsWithRefs(nullableRefs, items);\n\t\tconst result = isList ? aligned : (aligned.at(0) ?? null);\n\n\t\treturn { query: result };\n\t}\n\n\tasync saveQuery(\n\t\tquery: QueryTagWithData<Item>,\n\t\treplaceExistingItems = false,\n\t\tttlMs?: number,\n\t): Promise<void> {\n\t\tconst data = query.data;\n\t\tconst queryKey = this.getQueryKey(query);\n\t\tconst queryIndexes = this.getQueryIndexes(query, true);\n\n\t\tconst isList = Array.isArray(data);\n\t\tconst nullableItems = isList ? data : [data];\n\t\tconst items = nullableItems.filter((item) => item != null);\n\n\t\tif (items.length > 0) {\n\t\t\tif (replaceExistingItems) {\n\t\t\t\tawait this.save(items);\n\t\t\t} else {\n\t\t\t\tawait this.saveIfNotExists(items);\n\t\t\t}\n\t\t}\n\n\t\tconst encodedRefs = nullableItems.map((item) => {\n\t\t\tif (item == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn this.getRefWithFallback(item).toString();\n\t\t});\n\n\t\tconst metadata = {\n\t\t\trefs: encodedRefs,\n\t\t\tisList,\n\t\t};\n\n\t\treturn await this.client.saveQuery(queryKey, queryIndexes, metadata, {\n\t\t\t...this.queryCacheArgs,\n\t\t\tttlMs: ttlMs ?? this.queryCacheArgs.ttlMs,\n\t\t});\n\t}\n\n\tdeleteQueries(query: QueryTagWithIndexes): Promise<void> {\n\t\tconst queryIndexes = this.getQueryIndexes(query);\n\t\treturn this.client.deleteQueries(queryIndexes, this.queryCacheArgs);\n\t}\n\n\tgetRefWithFallback(root: Item): ItemRef {\n\t\tif (this.getRef) {\n\t\t\treturn this.getRef(root);\n\t\t}\n\t\tif (root == null) {\n\t\t\tthrow new Error('Object is null or undefined, cannot get ref');\n\t\t}\n\t\tif (typeof root === 'object' && 'id' in root) {\n\t\t\tassertValidRefType(root.id);\n\t\t\treturn root.id.toString();\n\t\t}\n\t\tthrow new Error('Object does not have id. Define \"getRef\" function in cache options');\n\t}\n\n\tgetItemKey(item: Item): ItemCacheKey {\n\t\tconst ref = this.getRefWithFallback(item);\n\t\treturn this.getItemKeyByRef(ref);\n\t}\n\n\tgetItemKeyByRef(ref: ItemRef): ItemCacheKey {\n\t\tconst prefix: ItemCacheKeyPrefix = `${this.namespace}:{${this.name}:item:${buildCacheRevisionId(this.revision)}}`;\n\t\tconst keyId = buildItemCacheKeyId(ref.toString());\n\t\treturn `${prefix}:${keyId}`;\n\t}\n\n\tgetQueryKey(query: QueryTagWithArgs): QueryCacheKey {\n\t\tconst prefix = this.getQueryKeyPrefix(query);\n\t\treturn `${prefix}:${buildQueryCacheKeyId(query.args)}`;\n\t}\n\n\tgetQueryIndexes(query: QueryTagWithIndexes, ensureCatchAll = false): QueryCacheIndexKey[] {\n\t\tconst prefix = this.getQueryKeyPrefix(query);\n\t\tconst keyIndexes = buildQueryCacheIndexKeysIds(query.indexes);\n\t\tif (ensureCatchAll && query.indexes.length > 0) {\n\t\t\tkeyIndexes.push(buildQueryCacheIndexKeyId());\n\t\t}\n\t\treturn keyIndexes.map((keyIndex) => `${prefix}:${keyIndex}` as const);\n\t}\n\n\tgetQueryKeyPrefix(query: QueryTag): QueryCacheKeyPrefix {\n\t\treturn `${this.namespace}:{${this.name}:query:${query.name}:${this.getQueryKeyRevision(query)}}`;\n\t}\n\n\tgetQueryKeyRevision(query: QueryTag): CacheRevisionId {\n\t\tconst combinedRevision = `${this.revision}_${query.revision?.toString() ?? DEFAULT_REVISION}`;\n\t\treturn buildCacheRevisionId(combinedRevision);\n\t}\n}\n\nfunction parseItemSafely<T>(value: string, parseItem: (value: string) => T): T | null {\n\ttry {\n\t\treturn parseItem(value);\n\t} catch (err) {\n\t\tlogger.warn({\n\t\t\ttype: 'parse-item',\n\t\t\tmessage: 'Failed to parse item, returning null',\n\t\t\terror: err,\n\t\t});\n\t\treturn null;\n\t}\n}\n\nfunction parseQueryMetadataSafely(value: string): QueryMetadata | null {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (err) {\n\t\tlogger.warn({\n\t\t\ttype: 'parse-query-metadata',\n\t\t\tmessage: 'Failed to parse query metadata, treating as cache miss',\n\t\t\terror: err,\n\t\t});\n\t\treturn null;\n\t}\n\tif (!isValidQueryMetadata(parsed)) {\n\t\tlogger.warn({\n\t\t\ttype: 'parse-query-metadata',\n\t\t\tmessage: 'Query metadata has invalid shape, treating as cache miss',\n\t\t});\n\t\treturn null;\n\t}\n\treturn parsed;\n}\n\nfunction isValidQueryMetadata(value: unknown): value is QueryMetadata {\n\tif (value == null || typeof value !== 'object') {\n\t\treturn false;\n\t}\n\tconst candidate = value as { refs?: unknown; isList?: unknown };\n\tif (!Array.isArray(candidate.refs)) {\n\t\treturn false;\n\t}\n\tif (typeof candidate.isList !== 'boolean') {\n\t\treturn false;\n\t}\n\treturn candidate.refs.every((ref) => ref === null || typeof ref === 'string');\n}\n","import type { CacheClient } from './client.ts';\nimport type { QueryDefinitionMap, QueryMethod, QueryMethodMap } from './define-query.ts';\nimport type { ItemRef, RefCompatibleItem } from './item.ts';\nimport { logger } from './logger.ts';\nimport type { CacheOptions, OptionalGetRef, RequiredGetRef } from './options.ts';\nimport type { QueryOnDeleteHook, QueryOnInsertHook, QueryOnUpdateHook, QueryTag } from './query.ts';\nimport { CacheStore } from './store.ts';\nimport { toArray, zip } from './utils.ts';\n\nexport type CacheHooksOptions = {\n\t/**\n\t * If true, hooks will be awaited. If false, hooks will be fired asynchronously and will not block cache methods.\n\t * @defaultValue false\n\t */\n\twaitForHooks?: boolean;\n};\n\nexport type Cache<Item> = {\n\tget(ref: ItemRef): Promise<Item | null>;\n\tgetMany(refs: ItemRef[]): Promise<Item[] | null>;\n\tgetPartial(refs: ItemRef[]): Promise<Array<Item | null>>;\n\tupdate(item: Item | Item[]): Promise<void>;\n\tinsert(item: Item | Item[]): Promise<void>;\n\tdelete(ref: ItemRef | ItemRef[]): Promise<void>;\n};\n\nexport type CacheWithQueries<Item, QueryDefinitions extends QueryDefinitionMap<Item>> = {\n\tget(ref: ItemRef): Promise<Item | null>;\n\tgetMany(refs: ItemRef[]): Promise<Item[] | null>;\n\tgetPartial(refs: ItemRef[]): Promise<Array<Item | null>>;\n\tupdate(item: Item | Item[], options?: CacheHooksOptions): Promise<void>;\n\tinsert(item: Item | Item[], options?: CacheHooksOptions): Promise<void>;\n\tdelete(ref: ItemRef | ItemRef[], options?: CacheHooksOptions): Promise<void>;\n\tdeleteQueries(): Promise<void>;\n\tqueries: QueryMethodMap<QueryDefinitions>;\n};\n\nexport type CreateCacheFactory<Item> = {\n\tbuild: () => Cache<Item>;\n\twithQueries: <QueryDefinitions extends QueryDefinitionMap<Item>>(\n\t\tdefinitions: QueryDefinitions,\n\t) => {\n\t\tbuild: () => CacheWithQueries<Item, QueryDefinitions>;\n\t};\n};\n\nexport function createCache<Item extends RefCompatibleItem>(\n\tclient: CacheClient,\n\toptions: CacheOptions<Item> & OptionalGetRef<Item>,\n): CreateCacheFactory<Item>;\nexport function createCache<Item>(\n\tclient: CacheClient,\n\toptions: CacheOptions<Item> & RequiredGetRef<Item>,\n): CreateCacheFactory<Item>;\nexport function createCache<Item>(\n\tclient: CacheClient,\n\toptions: CacheOptions<Item> & OptionalGetRef<Item>,\n): CreateCacheFactory<Item> {\n\tconst storeClient = new CacheStore<Item>({\n\t\tclient,\n\t\tname: options.name,\n\t\tparse: options.parse,\n\t\tserialize: options.serialize,\n\t\tgetRef: options.getRef,\n\t\tttlMs: options.ttlMs ?? client.options?.ttlMs,\n\t\tnamespace: options.namespace ?? client.options?.namespace,\n\t\trevision: options.revision ?? client.options?.revision,\n\t});\n\treturn {\n\t\tbuild: () =>\n\t\t\tcreateCacheMethods(\n\t\t\t\tstoreClient,\n\t\t\t\t{\n\t\t\t\t\tonUpdateHook: options.onUpdate,\n\t\t\t\t\tonInsertHook: options.onInsert,\n\t\t\t\t\tonDeleteHook: options.onDelete,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tonUpdateHooks: [],\n\t\t\t\t\tonInsertHooks: [],\n\t\t\t\t\tonDeleteHooks: [],\n\t\t\t\t},\n\t\t\t),\n\t\twithQueries: (definitions) => {\n\t\t\treturn {\n\t\t\t\tbuild: () => createCacheMethodsWithQueries(storeClient, options, definitions),\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction createCacheMethods<Item>(\n\tstoreClient: CacheStore<Item>,\n\titemHooks: {\n\t\tonUpdateHook?: (item: Item[]) => void | Promise<void>;\n\t\tonInsertHook?: (item: Item[]) => void | Promise<void>;\n\t\tonDeleteHook?: (ref: ItemRef[]) => void | Promise<void>;\n\t},\n\tqueryHooks: {\n\t\tonUpdateHooks: Array<QueryOnUpdateHook<Item>>;\n\t\tonInsertHooks: Array<QueryOnInsertHook<Item>>;\n\t\tonDeleteHooks: Array<QueryOnDeleteHook<Item>>;\n\t},\n): Cache<Item> | Omit<CacheWithQueries<Item, any>, 'queries' | 'deleteQueries'> {\n\treturn {\n\t\tget: (ref) => storeClient.get(ref),\n\t\tgetMany: (refs) => storeClient.getMany(refs),\n\t\tgetPartial: (refs) => storeClient.getPartial(refs),\n\t\tupdate: async (item, options) => {\n\t\t\tif (queryHooks.onUpdateHooks.length === 0) {\n\t\t\t\tawait storeClient.save(item);\n\t\t\t\tawait fireHook(itemHooks.onUpdateHook, toArray(item), options);\n\t\t\t} else {\n\t\t\t\tconst items = toArray(item);\n\t\t\t\tconst previous = await storeClient.saveWithDiff(items);\n\t\t\t\tconst pairs = zip(items, previous, (next, previous) => ({ next, previous }));\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfireHook(itemHooks.onUpdateHook, items, options),\n\t\t\t\t\tfireHooks(queryHooks.onUpdateHooks, pairs, options),\n\t\t\t\t]);\n\t\t\t}\n\t\t},\n\t\tinsert: async (item, options) => {\n\t\t\tawait storeClient.save(item);\n\t\t\tif (queryHooks.onInsertHooks.length === 0) {\n\t\t\t\tawait fireHook(itemHooks.onInsertHook, toArray(item), options);\n\t\t\t} else {\n\t\t\t\tconst items = toArray(item);\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfireHook(itemHooks.onInsertHook, items, options),\n\t\t\t\t\tfireHooks(queryHooks.onInsertHooks, items, options),\n\t\t\t\t]);\n\t\t\t}\n\t\t},\n\t\tdelete: async (ref, options) => {\n\t\t\tif (queryHooks.onDeleteHooks.length === 0) {\n\t\t\t\tawait storeClient.delete(ref);\n\t\t\t\tawait fireHook(itemHooks.onDeleteHook, toArray(ref), options);\n\t\t\t} else {\n\t\t\t\tconst refs = toArray(ref);\n\t\t\t\tconst previous = await storeClient.deleteWithDiff(refs);\n\t\t\t\tawait storeClient.delete(refs);\n\t\t\t\tconst pairs = zip(refs, previous, (ref, previous) => ({ ref, previous }));\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfireHook(itemHooks.onDeleteHook, refs, options),\n\t\t\t\t\tfireHooks(queryHooks.onDeleteHooks, pairs, options),\n\t\t\t\t]);\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction createCacheMethodsWithQueries<Item, QueryDefinitions extends QueryDefinitionMap<Item>>(\n\tstoreClient: CacheStore<Item>,\n\toptions: CacheOptions<Item> & OptionalGetRef<Item>,\n\tdefinitions: QueryDefinitions,\n): CacheWithQueries<Item, QueryDefinitions> {\n\tconst { queries, queryTags, onUpdateHooks, onInsertHooks, onDeleteHooks } = createQueriesAndHooks<\n\t\tItem,\n\t\tQueryDefinitions\n\t>(definitions, storeClient);\n\treturn {\n\t\t...createCacheMethods(\n\t\t\tstoreClient,\n\t\t\t{\n\t\t\t\tonUpdateHook: options.onUpdate,\n\t\t\t\tonInsertHook: options.onInsert,\n\t\t\t\tonDeleteHook: options.onDelete,\n\t\t\t},\n\t\t\t{ onUpdateHooks, onInsertHooks, onDeleteHooks },\n\t\t),\n\t\tdeleteQueries: async () => {\n\t\t\tconst promises = queryTags.map((tag) =>\n\t\t\t\tstoreClient.deleteQueries({\n\t\t\t\t\t...tag,\n\t\t\t\t\tindexes: [],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait Promise.all(promises);\n\t\t},\n\t\tqueries,\n\t};\n}\n\nfunction createQueriesAndHooks<Item, QueryDefinitions extends QueryDefinitionMap<Item>>(\n\tdefinitions: QueryDefinitions | undefined,\n\tstore: CacheStore<Item>,\n) {\n\tconst queries: Partial<QueryMethodMap<QueryDefinitions>> = {};\n\tconst queryTags: QueryTag[] = [];\n\tconst onUpdateHooks: Array<QueryOnUpdateHook<Item>> = [];\n\tconst onInsertHooks: Array<QueryOnInsertHook<Item>> = [];\n\tconst onDeleteHooks: Array<QueryOnDeleteHook<Item>> = [];\n\tfor (const [queryName, buildQuery] of Object.entries(definitions ?? {})) {\n\t\tconst queryHandler = buildQuery(store, queryName);\n\t\tqueries[queryName as keyof QueryDefinitions] = queryHandler.call as QueryMethod<\n\t\t\tQueryDefinitions[keyof QueryDefinitions]\n\t\t>;\n\t\tqueryTags.push(queryHandler.tag);\n\t\tif (queryHandler.hooks.onUpdate) {\n\t\t\tonUpdateHooks.push(queryHandler.hooks.onUpdate);\n\t\t}\n\t\tif (queryHandler.hooks.onDelete) {\n\t\t\tonDeleteHooks.push(queryHandler.hooks.onDelete);\n\t\t}\n\t\tif (queryHandler.hooks.onInsert) {\n\t\t\tonInsertHooks.push(queryHandler.hooks.onInsert);\n\t\t}\n\t}\n\treturn {\n\t\tqueries: queries as QueryMethodMap<QueryDefinitions>,\n\t\tqueryTags,\n\t\tonUpdateHooks,\n\t\tonInsertHooks,\n\t\tonDeleteHooks,\n\t};\n}\n\nasync function fireHook<T>(\n\thook: ((v: T) => void | Promise<void>) | undefined,\n\tvalue: T,\n\toptions?: CacheHooksOptions,\n) {\n\tif (hook == null) {\n\t\treturn;\n\t}\n\tif (options?.waitForHooks) {\n\t\tawait hook(value);\n\t} else {\n\t\tPromise.resolve()\n\t\t\t.then(() => hook(value))\n\t\t\t.catch((err) => {\n\t\t\t\tlogger.error({\n\t\t\t\t\ttype: 'cache-hook',\n\t\t\t\t\tmessage: 'Error in cache hook',\n\t\t\t\t\terror: err,\n\t\t\t\t});\n\t\t\t});\n\t}\n}\n\nasync function fireHooks<T>(\n\thooks: Array<(v: T) => Promise<void>>,\n\tvalue: T,\n\toptions?: CacheHooksOptions,\n) {\n\tawait Promise.all(hooks.map((hook) => fireHook(hook, value, options)));\n}\n","import { logger } from './logger.ts';\nimport type {\n\tIndexedQueryArgs,\n\tItemFromQueryResult,\n\tQueryArgs,\n\tQueryArgsIndexes,\n\tQueryHandle,\n\tQueryHelpers,\n\tQueryHelpersWithIndexes,\n\tQueryIndex,\n\tQueryOptions,\n\tQueryResult,\n\tQueryTag,\n} from './query.ts';\nimport type { CacheStore } from './store.ts';\n\nexport type QueryFactory<\n\tResult extends QueryResult<Item>,\n\tArgs extends QueryArgs,\n\tIndexes extends QueryArgsIndexes<Args, Indexes>,\n\tItem = ItemFromQueryResult<Result>,\n> = (store: CacheStore<Item>, name: string) => QueryHandle<Result, Args, Indexes, Item>;\n\nexport type QueryMethod<T extends QueryFactory<any, any, any>> =\n\tT extends QueryFactory<infer Result, infer Args, any, any>\n\t\t? (args: Args) => Promise<Result>\n\t\t: never;\n\nexport type QueryDefinitionMap<Item> = Record<\n\tstring,\n\tQueryFactory<QueryResult<Item>, any, any, Item>\n>;\n\nexport type QueryMethodMap<Definitions extends QueryDefinitionMap<any>> = {\n\t[K in keyof Definitions]: QueryMethod<Definitions[K]>;\n};\n\nexport function defineQuery<\n\tResult extends QueryResult<Item>,\n\tArgs extends QueryArgs = Record<string, never>,\n\tIndexes extends QueryArgsIndexes<Args, Indexes> = Record<string, never>,\n\tItem = ItemFromQueryResult<Result>,\n>(options: QueryOptions<Result, Args, Indexes, Item>): QueryFactory<Result, Args, Indexes, Item> {\n\treturn (store: CacheStore<Item>, name: string) => {\n\t\tconst tag: QueryTag = {\n\t\t\tname,\n\t\t\trevision: options.revision,\n\t\t};\n\n\t\tconst resolve = async (args: Args): Promise<Result> => {\n\t\t\tconst fromCache = await store\n\t\t\t\t.getQuery({\n\t\t\t\t\t...tag,\n\t\t\t\t\targs,\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tlogger.error({\n\t\t\t\t\t\ttype: 'query-cache',\n\t\t\t\t\t\tmessage: `Failed to get query result for ${name}. Proceeding without caching.`,\n\t\t\t\t\t\terror: err,\n\t\t\t\t\t});\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\n\t\t\tif (fromCache != null) {\n\t\t\t\treturn fromCache.query as Result;\n\t\t\t}\n\n\t\t\tconst data = await options.resolve(args);\n\n\t\t\tawait store\n\t\t\t\t.saveQuery(\n\t\t\t\t\t{\n\t\t\t\t\t\t...tag,\n\t\t\t\t\t\targs,\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\tindexes: buildIndexes(args, options.indexArgsBy),\n\t\t\t\t\t},\n\t\t\t\t\toptions.replaceExistingItems,\n\t\t\t\t\toptions.ttlMs,\n\t\t\t\t)\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tlogger.error({\n\t\t\t\t\t\ttype: 'query-cache',\n\t\t\t\t\t\tmessage: `Failed to save query result for ${name}. Proceeding without caching.`,\n\t\t\t\t\t\terror: err,\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\treturn data;\n\t\t};\n\n\t\tconst invalidateAll = async () => {\n\t\t\tawait store.deleteQueries({\n\t\t\t\t...tag,\n\t\t\t\tindexes: [],\n\t\t\t});\n\t\t};\n\n\t\tconst invalidateByArgs = async (\n\t\t\targs: Partial<IndexedQueryArgs<Args, Indexes>> | Partial<IndexedQueryArgs<Args, Indexes>>[],\n\t\t) => {\n\t\t\tconst argsArray = Array.isArray(args) ? args : [args];\n\t\t\tconst deletePromises = argsArray.map((args) =>\n\t\t\t\tstore.deleteQueries({\n\t\t\t\t\t...tag,\n\t\t\t\t\tindexes: buildIndexes(args, options.indexArgsBy),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait Promise.all(deletePromises);\n\t\t};\n\n\t\tconst helpers: QueryHelpersWithIndexes<Args, Indexes> = {\n\t\t\tinvalidateAll,\n\t\t\tinvalidateByArgs,\n\t\t};\n\t\tconst helpersCast: QueryHelpers<Args, Indexes> = helpers as QueryHelpers<Args, Indexes>;\n\n\t\tconst { onUpdate, onDelete, onInsert } = options;\n\n\t\tconst hooks = {\n\t\t\tonUpdate: hookWithHelpers(helpersCast, onUpdate),\n\t\t\tonInsert: hookWithHelpers(helpersCast, onInsert),\n\t\t\tonDelete: hookWithHelpers(helpersCast, onDelete),\n\t\t};\n\n\t\treturn {\n\t\t\tcall: resolve,\n\t\t\ttag,\n\t\t\thelpers,\n\t\t\thooks,\n\t\t};\n\t};\n}\n\nfunction hookWithHelpers<Helpers, Args>(\n\thelpers: Helpers,\n\thook?: (args: Args, helpers: Helpers) => void | Promise<void>,\n) {\n\tif (!hook) {\n\t\treturn undefined;\n\t}\n\tconst hookWithHelpers = async (args: Args) => {\n\t\tawait hook(args, helpers);\n\t};\n\treturn hookWithHelpers;\n}\n\nfunction buildIndexes<Args extends QueryArgs, Indexes extends QueryArgsIndexes<Args, Indexes>>(\n\targs: Partial<Args>,\n\tindexArgsBy?: Indexes,\n): QueryIndex[] {\n\tif (!indexArgsBy) {\n\t\treturn [];\n\t}\n\tconst indexes: QueryIndex[] = [];\n\tfor (const key in indexArgsBy) {\n\t\tif (!Object.hasOwn(args, key)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst value = args[key];\n\t\tif (value === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\ttypeof value !== 'string' &&\n\t\t\ttypeof value !== 'number' &&\n\t\t\ttypeof value !== 'boolean' &&\n\t\t\tvalue !== null\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Cache index '${key}' must be a primitive (string, number, boolean, or null) but received ${\n\t\t\t\t\tArray.isArray(value) ? 'array' : typeof value\n\t\t\t\t}.`,\n\t\t\t);\n\t\t}\n\t\tindexes.push([key, value]);\n\t}\n\treturn indexes;\n}\n"],"mappings":"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"}
@@ -1,5 +1,2 @@
1
- //#region sdk/batch.d.ts
2
- declare function doBatched<T>(items: T[], batchSize: number, fn: (batch: T[]) => Promise<void>): Promise<void>;
3
- //#endregion
4
- export { doBatched };
1
+ export declare function doBatched<T>(items: T[], batchSize: number, fn: (batch: T[]) => Promise<void>): Promise<void>;
5
2
  //# sourceMappingURL=index.d.ts.map
package/dist/sdk/index.js CHANGED
@@ -1,8 +1,7 @@
1
- //#region sdk/batch.ts
1
+ // sdk/batch.ts
2
2
  async function doBatched(items, batchSize, fn) {
3
3
  for (let i = 0; i < items.length; i += batchSize) await fn(items.slice(i, i + batchSize));
4
4
  }
5
- //#endregion
6
5
  export { doBatched };
7
6
 
8
7
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baeta/cache",
3
- "version": "2.0.0-next.16",
3
+ "version": "2.0.0-next.18",
4
4
  "description": "Type-safe caching for Baeta.",
5
5
  "keywords": [
6
6
  "baeta",
@@ -63,10 +63,9 @@
63
63
  "types": "tsc --noEmit"
64
64
  },
65
65
  "dependencies": {
66
- "@baeta/util-encoding": "^2.0.0-next.5",
67
- "@baeta/util-log": "^2.0.0-next.5",
66
+ "@baeta/util-log": "^2.0.0-next.6",
68
67
  "dataloader": "^2.2.3",
69
- "ohash": "^2.0.11"
68
+ "ohash": "^2.0.12"
70
69
  },
71
70
  "devDependencies": {
72
71
  "@baeta/builder": "^0.0.0",