inray 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE.md +1 -0
  2. package/README.md +132 -32
  3. package/dist/caching/mod.d.ts +2 -0
  4. package/dist/caching/mod.d.ts.map +1 -0
  5. package/dist/caching/mod.js +2 -0
  6. package/dist/caching/mod.js.map +1 -0
  7. package/dist/caching/src/cache.d.ts +89 -0
  8. package/dist/caching/src/cache.d.ts.map +1 -0
  9. package/dist/caching/src/cache.js +309 -0
  10. package/dist/caching/src/cache.js.map +1 -0
  11. package/dist/caching/src/hashing/cache.d.ts +92 -0
  12. package/dist/caching/src/hashing/cache.d.ts.map +1 -0
  13. package/dist/caching/src/hashing/cache.js +119 -0
  14. package/dist/caching/src/hashing/cache.js.map +1 -0
  15. package/dist/caching/src/hashing/hooks/eventified.d.ts +157 -0
  16. package/dist/caching/src/hashing/hooks/eventified.d.ts.map +1 -0
  17. package/dist/caching/src/hashing/hooks/eventified.js +333 -0
  18. package/dist/caching/src/hashing/hooks/eventified.js.map +1 -0
  19. package/dist/caching/src/hashing/hooks/index.d.ts +199 -0
  20. package/dist/caching/src/hashing/hooks/index.d.ts.map +1 -0
  21. package/dist/caching/src/hashing/hooks/index.js +383 -0
  22. package/dist/caching/src/hashing/hooks/index.js.map +1 -0
  23. package/dist/caching/src/hashing/hooks/types.d.ts +217 -0
  24. package/dist/caching/src/hashing/hooks/types.d.ts.map +1 -0
  25. package/dist/caching/src/hashing/hooks/types.js +10 -0
  26. package/dist/caching/src/hashing/hooks/types.js.map +1 -0
  27. package/dist/caching/src/hashing/index.d.ts +236 -0
  28. package/dist/caching/src/hashing/index.d.ts.map +1 -0
  29. package/dist/caching/src/hashing/index.js +447 -0
  30. package/dist/caching/src/hashing/index.js.map +1 -0
  31. package/dist/caching/src/hashing/providers/crc.d.ts +15 -0
  32. package/dist/caching/src/hashing/providers/crc.d.ts.map +1 -0
  33. package/dist/caching/src/hashing/providers/crc.js +49 -0
  34. package/dist/caching/src/hashing/providers/crc.js.map +1 -0
  35. package/dist/caching/src/hashing/providers/crypto.d.ts +19 -0
  36. package/dist/caching/src/hashing/providers/crypto.d.ts.map +1 -0
  37. package/dist/caching/src/hashing/providers/crypto.js +28 -0
  38. package/dist/caching/src/hashing/providers/crypto.js.map +1 -0
  39. package/dist/caching/src/hashing/providers/djb2.d.ts +60 -0
  40. package/dist/caching/src/hashing/providers/djb2.d.ts.map +1 -0
  41. package/dist/caching/src/hashing/providers/djb2.js +92 -0
  42. package/dist/caching/src/hashing/providers/djb2.js.map +1 -0
  43. package/dist/caching/src/hashing/providers/fnv1.d.ts +33 -0
  44. package/dist/caching/src/hashing/providers/fnv1.d.ts.map +1 -0
  45. package/dist/caching/src/hashing/providers/fnv1.js +68 -0
  46. package/dist/caching/src/hashing/providers/fnv1.js.map +1 -0
  47. package/dist/caching/src/hashing/providers/murmur.d.ts +81 -0
  48. package/dist/caching/src/hashing/providers/murmur.d.ts.map +1 -0
  49. package/dist/caching/src/hashing/providers/murmur.js +173 -0
  50. package/dist/caching/src/hashing/providers/murmur.js.map +1 -0
  51. package/dist/caching/src/hashing/providers.d.ts +126 -0
  52. package/dist/caching/src/hashing/providers.d.ts.map +1 -0
  53. package/dist/caching/src/hashing/providers.js +163 -0
  54. package/dist/caching/src/hashing/providers.js.map +1 -0
  55. package/dist/caching/src/hashing/types.d.ts +340 -0
  56. package/dist/caching/src/hashing/types.d.ts.map +1 -0
  57. package/dist/caching/src/hashing/types.js +10 -0
  58. package/dist/caching/src/hashing/types.js.map +1 -0
  59. package/dist/caching/src/keyValue/capabilities.d.ts +161 -0
  60. package/dist/caching/src/keyValue/capabilities.d.ts.map +1 -0
  61. package/dist/caching/src/keyValue/capabilities.js +334 -0
  62. package/dist/caching/src/keyValue/capabilities.js.map +1 -0
  63. package/dist/caching/src/keyValue/event-manager.d.ts +27 -0
  64. package/dist/caching/src/keyValue/event-manager.d.ts.map +1 -0
  65. package/dist/caching/src/keyValue/event-manager.js +88 -0
  66. package/dist/caching/src/keyValue/event-manager.js.map +1 -0
  67. package/dist/caching/src/keyValue/generic-store.d.ts +243 -0
  68. package/dist/caching/src/keyValue/generic-store.d.ts.map +1 -0
  69. package/dist/caching/src/keyValue/generic-store.js +295 -0
  70. package/dist/caching/src/keyValue/generic-store.js.map +1 -0
  71. package/dist/caching/src/keyValue/hooks-manager.d.ts +21 -0
  72. package/dist/caching/src/keyValue/hooks-manager.d.ts.map +1 -0
  73. package/dist/caching/src/keyValue/hooks-manager.js +59 -0
  74. package/dist/caching/src/keyValue/hooks-manager.js.map +1 -0
  75. package/dist/caching/src/keyValue/index.d.ts +334 -0
  76. package/dist/caching/src/keyValue/index.d.ts.map +1 -0
  77. package/dist/caching/src/keyValue/index.js +796 -0
  78. package/dist/caching/src/keyValue/index.js.map +1 -0
  79. package/dist/caching/src/keyValue/serialize/serialize.d.ts +12 -0
  80. package/dist/caching/src/keyValue/serialize/serialize.d.ts.map +1 -0
  81. package/dist/caching/src/keyValue/serialize/serialize.js +64 -0
  82. package/dist/caching/src/keyValue/serialize/serialize.js.map +1 -0
  83. package/dist/caching/src/keyValue/stats-manager.d.ts +27 -0
  84. package/dist/caching/src/keyValue/stats-manager.d.ts.map +1 -0
  85. package/dist/caching/src/keyValue/stats-manager.js +64 -0
  86. package/dist/caching/src/keyValue/stats-manager.js.map +1 -0
  87. package/dist/caching/src/keyValue/types.d.ts +22 -0
  88. package/dist/caching/src/keyValue/types.d.ts.map +1 -0
  89. package/dist/caching/src/keyValue/types.js +11 -0
  90. package/dist/caching/src/keyValue/types.js.map +1 -0
  91. package/dist/caching/src/keyv-adapter.d.ts +40 -0
  92. package/dist/caching/src/keyv-adapter.d.ts.map +1 -0
  93. package/dist/caching/src/keyv-adapter.js +56 -0
  94. package/dist/caching/src/keyv-adapter.js.map +1 -0
  95. package/dist/caching/src/utils/cache-tags.d.ts +275 -0
  96. package/dist/caching/src/utils/cache-tags.d.ts.map +1 -0
  97. package/dist/caching/src/utils/cache-tags.js +407 -0
  98. package/dist/caching/src/utils/cache-tags.js.map +1 -0
  99. package/dist/caching/src/utils/cacheable-item-types.d.ts +35 -0
  100. package/dist/caching/src/utils/cacheable-item-types.d.ts.map +1 -0
  101. package/dist/caching/src/utils/cacheable-item-types.js +10 -0
  102. package/dist/caching/src/utils/cacheable-item-types.js.map +1 -0
  103. package/dist/caching/src/utils/coalesce-async.d.ts +29 -0
  104. package/dist/caching/src/utils/coalesce-async.d.ts.map +1 -0
  105. package/dist/caching/src/utils/coalesce-async.js +88 -0
  106. package/dist/caching/src/utils/coalesce-async.js.map +1 -0
  107. package/dist/caching/src/utils/hash.d.ts +55 -0
  108. package/dist/caching/src/utils/hash.d.ts.map +1 -0
  109. package/dist/caching/src/utils/hash.js +116 -0
  110. package/dist/caching/src/utils/hash.js.map +1 -0
  111. package/dist/caching/src/utils/index.d.ts +24 -0
  112. package/dist/caching/src/utils/index.d.ts.map +1 -0
  113. package/dist/caching/src/utils/index.js +21 -0
  114. package/dist/caching/src/utils/index.js.map +1 -0
  115. package/dist/caching/src/utils/is-keyv-instance.d.ts +10 -0
  116. package/dist/caching/src/utils/is-keyv-instance.d.ts.map +1 -0
  117. package/dist/caching/src/utils/is-keyv-instance.js +37 -0
  118. package/dist/caching/src/utils/is-keyv-instance.js.map +1 -0
  119. package/dist/caching/src/utils/is-object.d.ts +10 -0
  120. package/dist/caching/src/utils/is-object.d.ts.map +1 -0
  121. package/dist/caching/src/utils/is-object.js +13 -0
  122. package/dist/caching/src/utils/is-object.js.map +1 -0
  123. package/dist/caching/src/utils/less-than.d.ts +10 -0
  124. package/dist/caching/src/utils/less-than.d.ts.map +1 -0
  125. package/dist/caching/src/utils/less-than.js +13 -0
  126. package/dist/caching/src/utils/less-than.js.map +1 -0
  127. package/dist/caching/src/utils/memoize.d.ts +107 -0
  128. package/dist/caching/src/utils/memoize.d.ts.map +1 -0
  129. package/dist/caching/src/utils/memoize.js +201 -0
  130. package/dist/caching/src/utils/memoize.js.map +1 -0
  131. package/dist/caching/src/utils/mock-cacheable.d.ts +28 -0
  132. package/dist/caching/src/utils/mock-cacheable.d.ts.map +1 -0
  133. package/dist/caching/src/utils/mock-cacheable.js +87 -0
  134. package/dist/caching/src/utils/mock-cacheable.js.map +1 -0
  135. package/dist/caching/src/utils/run-if-fn.d.ts +12 -0
  136. package/dist/caching/src/utils/run-if-fn.d.ts.map +1 -0
  137. package/dist/caching/src/utils/run-if-fn.js +12 -0
  138. package/dist/caching/src/utils/run-if-fn.js.map +1 -0
  139. package/dist/caching/src/utils/shorthand-time.d.ts +27 -0
  140. package/dist/caching/src/utils/shorthand-time.d.ts.map +1 -0
  141. package/dist/caching/src/utils/shorthand-time.js +96 -0
  142. package/dist/caching/src/utils/shorthand-time.js.map +1 -0
  143. package/dist/caching/src/utils/sleep.d.ts +10 -0
  144. package/dist/caching/src/utils/sleep.d.ts.map +1 -0
  145. package/dist/caching/src/utils/sleep.js +11 -0
  146. package/dist/caching/src/utils/sleep.js.map +1 -0
  147. package/dist/caching/src/utils/stats.d.ts +354 -0
  148. package/dist/caching/src/utils/stats.d.ts.map +1 -0
  149. package/dist/caching/src/utils/stats.js +593 -0
  150. package/dist/caching/src/utils/stats.js.map +1 -0
  151. package/dist/caching/src/utils/ttl.d.ts +58 -0
  152. package/dist/caching/src/utils/ttl.d.ts.map +1 -0
  153. package/dist/caching/src/utils/ttl.js +77 -0
  154. package/dist/caching/src/utils/ttl.js.map +1 -0
  155. package/package.json +11 -3
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Original library: @cacheable/utils (v2.4.2)
3
+ * - Copyright (c) Jared Wray
4
+ *
5
+ * Modified and integrated in "Inray":
6
+ * - Evgeniy Bushin
7
+ */
8
+ /***/
9
+ import type { Keyv } from '../keyValue/index.ts';
10
+ /**
11
+ * Options for constructing a {@link CacheTags}.
12
+ * @typedef {Object} CacheTagsOptions
13
+ * @property {Keyv} store - The Keyv store used to persist tag versions and key snapshots.
14
+ * @property {string} [namespace] - An optional namespace that isolates this service's tags
15
+ * and keys from others sharing the same store. Defaults to `"default"`.
16
+ * @property {boolean} [enabled] - Whether the service is enabled. While disabled, every method
17
+ * is a no-op: read methods return their neutral value ({@link CacheTags.isKeyFresh} returns
18
+ * `true`, {@link CacheTags.isKeyStale} returns `false`, etc.) and writes are skipped. The
19
+ * service must be explicitly enabled to use tags. Defaults to `true`.
20
+ * @property {(error: unknown) => void} [onError] - Invoked with errors from non-blocking
21
+ * (fire-and-forget) operations, which cannot be thrown to the caller. Defaults to ignoring them.
22
+ */
23
+ export type CacheTagsOptions = {
24
+ store: Keyv;
25
+ namespace?: string;
26
+ enabled?: boolean;
27
+ onError?: (error: unknown) => void;
28
+ };
29
+ /**
30
+ * Options for {@link CacheTags.setKeyTags}.
31
+ * @typedef {Object} SetKeyTagsOptions
32
+ * @property {number} [ttl] - Time-to-live in milliseconds for the key's tag snapshot. Should
33
+ * match the TTL of the cached value it tracks so the snapshot expires alongside it. If omitted,
34
+ * the snapshot does not expire.
35
+ * @property {boolean} [nonBlocking] - When `true`, the snapshot write is fire-and-forget:
36
+ * the call resolves immediately and failures are reported via the `onError` option.
37
+ */
38
+ export type SetKeyTagsOptions = {
39
+ ttl?: number;
40
+ nonBlocking?: boolean;
41
+ };
42
+ /**
43
+ * Options for {@link CacheTags.removeKey} and {@link CacheTags.removeKeys}.
44
+ * @typedef {Object} RemoveKeysOptions
45
+ * @property {boolean} [nonBlocking] - When `true`, the removal is fire-and-forget:
46
+ * the call resolves immediately and failures are reported via the `onError` option.
47
+ */
48
+ export type RemoveKeysOptions = {
49
+ nonBlocking?: boolean;
50
+ };
51
+ /**
52
+ * The metadata stored for a tagged key. It records the version of each tag at the moment the key
53
+ * was written, allowing {@link CacheTags.isKeyFresh} to detect later invalidations.
54
+ * @typedef {Object} KeyTagEntry
55
+ * @property {Record<string, number>} tags - A snapshot mapping each tag name to its version at set time.
56
+ */
57
+ export type KeyTagEntry = {
58
+ tags: Record<string, number>;
59
+ };
60
+ /**
61
+ * Provides tag-based cache invalidation on top of any {@link Keyv} store. It is store-agnostic and
62
+ * requires no adapter changes.
63
+ *
64
+ * The service uses a lazy invalidation model rather than scanning and deleting keys. Each tag has a
65
+ * monotonically increasing version counter; {@link CacheTags.invalidateTag} simply increments
66
+ * it. When a key is tagged via {@link CacheTags.setKeyTags}, a snapshot of its tags' current
67
+ * versions is stored alongside it. {@link CacheTags.isKeyFresh} compares that snapshot against
68
+ * the live versions — if any tag has been incremented since, the key is considered stale. Stale
69
+ * entries are not deleted explicitly; they are expected to fall out of the cache via their TTL.
70
+ *
71
+ * This keeps invalidation constant-time regardless of how many keys reference a tag, at the cost of
72
+ * one additional `isKeyFresh` read per cache lookup.
73
+ *
74
+ * The service can be disabled via the `enabled` option or property so integrations pay no cost for
75
+ * untagged workloads: while disabled, every method is a no-op — reads return their neutral value
76
+ * and writes are skipped. The service must be explicitly enabled to use tags; it never enables
77
+ * itself, which keeps behavior consistent across distributed instances sharing a store.
78
+ *
79
+ * All metadata is written under a reserved prefix so it cannot collide with user keys:
80
+ * - `--cacheable--tags--:<namespace>:tag:<tag>` → integer version counter (stored without TTL).
81
+ * - `--cacheable--tags--:<namespace>:key:<key>` → the {@link KeyTagEntry} snapshot.
82
+ *
83
+ * Note: the read-version-then-write-snapshot sequence in `setKeyTags` is not atomic across
84
+ * processes. A concurrent `invalidateTag` running between the read and the write can leave a freshly
85
+ * written key referencing a stale version.
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const cacheTags = new CacheTags({ store: new Keyv(), namespace: 'app' });
90
+ * await cacheTags.setKeyTags('user:42', ['users', 'org:7'], { ttl: 3600000 });
91
+ * await cacheTags.isKeyFresh('user:42'); // true
92
+ * await cacheTags.invalidateTag('users');
93
+ * await cacheTags.isKeyFresh('user:42'); // false
94
+ * ```
95
+ */
96
+ export declare class CacheTags {
97
+ private readonly _store;
98
+ private readonly _namespace;
99
+ private _enabled;
100
+ private readonly _onError?;
101
+ /**
102
+ * Creates a new tag service.
103
+ * @param {CacheTagsOptions} options - The store, optional namespace, enabled state, and
104
+ * non-blocking error handler to use.
105
+ */
106
+ constructor(options: CacheTagsOptions);
107
+ /**
108
+ * The Keyv store backing this service.
109
+ * @returns {Keyv} The store provided to the constructor.
110
+ */
111
+ get store(): Keyv;
112
+ /**
113
+ * The namespace isolating this service's tags and keys within the store.
114
+ * @returns {string} The configured namespace, or `"default"` if none was provided.
115
+ */
116
+ get namespace(): string;
117
+ /**
118
+ * Whether the service is enabled. While disabled, every method is a no-op — read methods
119
+ * return their neutral value and writes are skipped — so integrations pay no extra store
120
+ * reads for untagged workloads. The service must be explicitly enabled to use tags; it never
121
+ * enables itself.
122
+ * @returns {boolean} Whether the service is enabled.
123
+ */
124
+ get enabled(): boolean;
125
+ /**
126
+ * Sets whether the service is enabled.
127
+ * @param {boolean} enabled Whether the service is enabled.
128
+ */
129
+ set enabled(enabled: boolean);
130
+ /**
131
+ * Builds the reserved store key under which a tag's version counter is stored.
132
+ * @param tag - The tag name.
133
+ * @returns {string} The namespaced store key for the tag's version.
134
+ */
135
+ private tagKey;
136
+ /**
137
+ * Builds the reserved store key under which a cache key's tag snapshot is stored.
138
+ * @param key - The cache key being tagged.
139
+ * @returns {string} The namespaced store key for the key's snapshot.
140
+ */
141
+ private keyEntryKey;
142
+ /**
143
+ * Builds the common prefix shared by every key-snapshot entry in this namespace. Used to filter
144
+ * key entries when iterating the store.
145
+ * @returns {string} The namespaced key-entry prefix.
146
+ */
147
+ private keyPrefix;
148
+ /**
149
+ * Reads the current version of a single tag.
150
+ * @param tag - The tag name.
151
+ * @returns {Promise<number>} The tag's version, or `0` if it has never been invalidated.
152
+ */
153
+ private getTagVersion;
154
+ /**
155
+ * Reads the current versions of multiple tags in a single batched store read.
156
+ * @param tags - The tag names to look up.
157
+ * @returns {Promise<number[]>} The versions in the same order as `tags`; entries that have never
158
+ * been invalidated resolve to `0`. Returns an empty array when `tags` is empty.
159
+ */
160
+ private getTagVersions;
161
+ /**
162
+ * Reports a fire-and-forget failure to the `onError` handler, if one was provided.
163
+ * @param error - The error raised by the non-blocking operation.
164
+ */
165
+ private handleNonBlockingError;
166
+ /**
167
+ * Reads the version snapshot of each tag and writes the key's tag snapshot to the store.
168
+ * @param key - The cache key to tag.
169
+ * @param tags - The tags to associate with the key.
170
+ * @param ttl - Time-to-live in milliseconds for the snapshot.
171
+ * @returns {Promise<void>} Resolves once the snapshot has been written.
172
+ */
173
+ private writeKeyTags;
174
+ /**
175
+ * Associates a cache key with a set of tags by recording a snapshot of each tag's current
176
+ * version. Call this whenever you write a fresh value to the cache. Duplicate tags are ignored.
177
+ * No-op while the service is disabled.
178
+ * @param key - The cache key to tag.
179
+ * @param tags - The tags to associate with the key.
180
+ * @param {SetKeyTagsOptions} [options] - Optional settings, such as a `ttl` for the snapshot or
181
+ * `nonBlocking` to fire-and-forget the write.
182
+ * @returns {Promise<void>} Resolves once the snapshot has been written, or immediately when
183
+ * `nonBlocking` is set.
184
+ */
185
+ setKeyTags(key: string, tags: string[], options?: SetKeyTagsOptions): Promise<void>;
186
+ /**
187
+ * Removes a key's tag snapshot. After this, {@link CacheTags.isKeyFresh} returns `false`
188
+ * for the key. Use when the cached value itself is deleted. No-op while the service is
189
+ * disabled.
190
+ * @param key - The cache key whose snapshot should be removed.
191
+ * @param {RemoveKeysOptions} [options] - Optional settings, such as `nonBlocking` to
192
+ * fire-and-forget the removal.
193
+ * @returns {Promise<void>} Resolves once the snapshot has been deleted, or immediately when
194
+ * `nonBlocking` is set.
195
+ */
196
+ removeKey(key: string, options?: RemoveKeysOptions): Promise<void>;
197
+ /**
198
+ * Removes multiple keys' tag snapshots in a single batched store delete. After this,
199
+ * {@link CacheTags.isKeyFresh} returns `false` for each key. An empty list is a no-op, as is
200
+ * the entire call while the service is disabled.
201
+ * @param keys - The cache keys whose snapshots should be removed.
202
+ * @param {RemoveKeysOptions} [options] - Optional settings, such as `nonBlocking` to
203
+ * fire-and-forget the removal.
204
+ * @returns {Promise<void>} Resolves once the snapshots have been deleted, or immediately when
205
+ * `nonBlocking` is set.
206
+ */
207
+ removeKeys(keys: string[], options?: RemoveKeysOptions): Promise<void>;
208
+ /**
209
+ * Determines whether a key's cached value can still be trusted. A key is fresh only when a
210
+ * snapshot exists for it and every tag in that snapshot still has the version it had at set time.
211
+ * A key with no tags is trivially fresh. Call this before returning a value from your cache.
212
+ * Always returns `true` while the service is disabled.
213
+ * @param key - The cache key to check.
214
+ * @returns {Promise<boolean>} `true` if the key is still fresh; `false` if it is unknown or any of
215
+ * its tags has been invalidated since the snapshot was taken.
216
+ */
217
+ isKeyFresh(key: string): Promise<boolean>;
218
+ /**
219
+ * Determines whether a key's cached value is known to be stale due to tag invalidation. This is
220
+ * the complement of {@link CacheTags.isKeyFresh} for tagged keys, but treats keys without a
221
+ * snapshot as not stale — making it safe to call for every cache lookup, including keys that were
222
+ * never tagged. Always returns `false` while the service is disabled.
223
+ * @param key - The cache key to check.
224
+ * @returns {Promise<boolean>} `true` only when a snapshot exists for the key and at least one of
225
+ * its tags has been invalidated since the snapshot was taken; `false` otherwise (including when
226
+ * the key has no snapshot).
227
+ */
228
+ isKeyStale(key: string): Promise<boolean>;
229
+ /**
230
+ * Determines which of the given keys are known to be stale due to tag invalidation, using two
231
+ * batched store reads regardless of how many keys are checked: one for the snapshots and one for
232
+ * the union of their tag versions. Keys without a snapshot are not considered stale. Returns an
233
+ * empty array while the service is disabled.
234
+ * @param keys - The cache keys to check.
235
+ * @returns {Promise<string[]>} The subset of `keys` whose snapshot references at least one tag
236
+ * that has been invalidated since the snapshot was taken.
237
+ */
238
+ getStaleKeys(keys: string[]): Promise<string[]>;
239
+ /**
240
+ * Returns the tags currently associated with a key. Returns `undefined` while the service is
241
+ * disabled.
242
+ * @param key - The cache key to look up.
243
+ * @returns {Promise<string[] | undefined>} The tag names from the key's snapshot, or `undefined`
244
+ * if the key has no snapshot.
245
+ */
246
+ getTags(key: string): Promise<string[] | undefined>;
247
+ /**
248
+ * Returns all cache keys whose snapshot references the given tag. This scans every key entry in
249
+ * the namespace via the Keyv iterator, making it an `O(N)` operation intended for debugging and
250
+ * tests rather than hot paths. Returns an empty array if the underlying store exposes no iterator
251
+ * or while the service is disabled.
252
+ * @param tag - The tag to search for.
253
+ * @returns {Promise<string[]>} The cache keys (with the reserved prefix stripped) referencing the tag.
254
+ */
255
+ getKeysByTag(tag: string): Promise<string[]>;
256
+ /**
257
+ * Invalidates a single tag by incrementing its version counter. Every key whose snapshot
258
+ * references this tag becomes stale immediately. Runs in constant time regardless of how many
259
+ * keys reference the tag. No-op while the service is disabled.
260
+ * @param tag - The tag to invalidate.
261
+ * @returns {Promise<string[]>} A single-element array containing the invalidated tag, or an
262
+ * empty array while the service is disabled.
263
+ */
264
+ invalidateTag(tag: string): Promise<string[]>;
265
+ /**
266
+ * Invalidates multiple tags by incrementing each of their version counters in a single batched
267
+ * store write. Duplicate tags are bumped once. An empty list is a no-op, as is the entire call
268
+ * while the service is disabled.
269
+ * @param tags - The tags to invalidate.
270
+ * @returns {Promise<string[]>} The `tags` argument as provided (including any duplicates), or
271
+ * an empty array while the service is disabled.
272
+ */
273
+ invalidateTags(tags: string[]): Promise<string[]>;
274
+ }
275
+ //# sourceMappingURL=cache-tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-tags.d.ts","sourceRoot":"","sources":["../../../../src/caching/src/utils/cache-tags.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Original library: @cacheable/utils (v2.4.2)
3
+ * - Copyright (c) Jared Wray
4
+ *
5
+ * Modified and integrated in "Inray":
6
+ * - Evgeniy Bushin
7
+ */
8
+ /***/
9
+ /**
10
+ * Prefix applied to every store key written by the service so its metadata cannot collide with
11
+ * user-supplied cache keys.
12
+ */
13
+ const RESERVED_PREFIX = '--cacheable--tags--';
14
+ /** Namespace used when none is supplied to the constructor. */
15
+ const DEFAULT_NAMESPACE = 'default';
16
+ /**
17
+ * Provides tag-based cache invalidation on top of any {@link Keyv} store. It is store-agnostic and
18
+ * requires no adapter changes.
19
+ *
20
+ * The service uses a lazy invalidation model rather than scanning and deleting keys. Each tag has a
21
+ * monotonically increasing version counter; {@link CacheTags.invalidateTag} simply increments
22
+ * it. When a key is tagged via {@link CacheTags.setKeyTags}, a snapshot of its tags' current
23
+ * versions is stored alongside it. {@link CacheTags.isKeyFresh} compares that snapshot against
24
+ * the live versions — if any tag has been incremented since, the key is considered stale. Stale
25
+ * entries are not deleted explicitly; they are expected to fall out of the cache via their TTL.
26
+ *
27
+ * This keeps invalidation constant-time regardless of how many keys reference a tag, at the cost of
28
+ * one additional `isKeyFresh` read per cache lookup.
29
+ *
30
+ * The service can be disabled via the `enabled` option or property so integrations pay no cost for
31
+ * untagged workloads: while disabled, every method is a no-op — reads return their neutral value
32
+ * and writes are skipped. The service must be explicitly enabled to use tags; it never enables
33
+ * itself, which keeps behavior consistent across distributed instances sharing a store.
34
+ *
35
+ * All metadata is written under a reserved prefix so it cannot collide with user keys:
36
+ * - `--cacheable--tags--:<namespace>:tag:<tag>` → integer version counter (stored without TTL).
37
+ * - `--cacheable--tags--:<namespace>:key:<key>` → the {@link KeyTagEntry} snapshot.
38
+ *
39
+ * Note: the read-version-then-write-snapshot sequence in `setKeyTags` is not atomic across
40
+ * processes. A concurrent `invalidateTag` running between the read and the write can leave a freshly
41
+ * written key referencing a stale version.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const cacheTags = new CacheTags({ store: new Keyv(), namespace: 'app' });
46
+ * await cacheTags.setKeyTags('user:42', ['users', 'org:7'], { ttl: 3600000 });
47
+ * await cacheTags.isKeyFresh('user:42'); // true
48
+ * await cacheTags.invalidateTag('users');
49
+ * await cacheTags.isKeyFresh('user:42'); // false
50
+ * ```
51
+ */
52
+ export class CacheTags {
53
+ _store;
54
+ _namespace;
55
+ _enabled;
56
+ _onError;
57
+ /**
58
+ * Creates a new tag service.
59
+ * @param {CacheTagsOptions} options - The store, optional namespace, enabled state, and
60
+ * non-blocking error handler to use.
61
+ */
62
+ constructor(options) {
63
+ this._store = options.store;
64
+ this._namespace = options.namespace ?? DEFAULT_NAMESPACE;
65
+ this._enabled = options.enabled ?? true;
66
+ this._onError = options.onError;
67
+ }
68
+ /**
69
+ * The Keyv store backing this service.
70
+ * @returns {Keyv} The store provided to the constructor.
71
+ */
72
+ get store() {
73
+ return this._store;
74
+ }
75
+ /**
76
+ * The namespace isolating this service's tags and keys within the store.
77
+ * @returns {string} The configured namespace, or `"default"` if none was provided.
78
+ */
79
+ get namespace() {
80
+ return this._namespace;
81
+ }
82
+ /**
83
+ * Whether the service is enabled. While disabled, every method is a no-op — read methods
84
+ * return their neutral value and writes are skipped — so integrations pay no extra store
85
+ * reads for untagged workloads. The service must be explicitly enabled to use tags; it never
86
+ * enables itself.
87
+ * @returns {boolean} Whether the service is enabled.
88
+ */
89
+ get enabled() {
90
+ return this._enabled;
91
+ }
92
+ /**
93
+ * Sets whether the service is enabled.
94
+ * @param {boolean} enabled Whether the service is enabled.
95
+ */
96
+ set enabled(enabled) {
97
+ this._enabled = enabled;
98
+ }
99
+ /**
100
+ * Builds the reserved store key under which a tag's version counter is stored.
101
+ * @param tag - The tag name.
102
+ * @returns {string} The namespaced store key for the tag's version.
103
+ */
104
+ tagKey(tag) {
105
+ return `${RESERVED_PREFIX}:${this._namespace}:tag:${tag}`;
106
+ }
107
+ /**
108
+ * Builds the reserved store key under which a cache key's tag snapshot is stored.
109
+ * @param key - The cache key being tagged.
110
+ * @returns {string} The namespaced store key for the key's snapshot.
111
+ */
112
+ keyEntryKey(key) {
113
+ return `${RESERVED_PREFIX}:${this._namespace}:key:${key}`;
114
+ }
115
+ /**
116
+ * Builds the common prefix shared by every key-snapshot entry in this namespace. Used to filter
117
+ * key entries when iterating the store.
118
+ * @returns {string} The namespaced key-entry prefix.
119
+ */
120
+ keyPrefix() {
121
+ return `${RESERVED_PREFIX}:${this._namespace}:key:`;
122
+ }
123
+ /**
124
+ * Reads the current version of a single tag.
125
+ * @param tag - The tag name.
126
+ * @returns {Promise<number>} The tag's version, or `0` if it has never been invalidated.
127
+ */
128
+ async getTagVersion(tag) {
129
+ const version = await this._store.get(this.tagKey(tag));
130
+ return typeof version === 'number' ? version : 0;
131
+ }
132
+ /**
133
+ * Reads the current versions of multiple tags in a single batched store read.
134
+ * @param tags - The tag names to look up.
135
+ * @returns {Promise<number[]>} The versions in the same order as `tags`; entries that have never
136
+ * been invalidated resolve to `0`. Returns an empty array when `tags` is empty.
137
+ */
138
+ async getTagVersions(tags) {
139
+ if (tags.length === 0) {
140
+ return [];
141
+ }
142
+ const tagKeys = tags.map((tag) => this.tagKey(tag));
143
+ const raw = await this._store.get(tagKeys);
144
+ return tags.map((_, i) => {
145
+ const value = raw?.[i];
146
+ return typeof value === 'number' ? value : 0;
147
+ });
148
+ }
149
+ /**
150
+ * Reports a fire-and-forget failure to the `onError` handler, if one was provided.
151
+ * @param error - The error raised by the non-blocking operation.
152
+ */
153
+ handleNonBlockingError(error) {
154
+ this._onError?.(error);
155
+ }
156
+ /**
157
+ * Reads the version snapshot of each tag and writes the key's tag snapshot to the store.
158
+ * @param key - The cache key to tag.
159
+ * @param tags - The tags to associate with the key.
160
+ * @param ttl - Time-to-live in milliseconds for the snapshot.
161
+ * @returns {Promise<void>} Resolves once the snapshot has been written.
162
+ */
163
+ async writeKeyTags(key, tags, ttl) {
164
+ const uniqueTags = [...new Set(tags)];
165
+ const versions = await this.getTagVersions(uniqueTags);
166
+ const snapshot = {};
167
+ for (let i = 0; i < uniqueTags.length; i++) {
168
+ snapshot[uniqueTags[i]] = versions[i];
169
+ }
170
+ const entry = { tags: snapshot };
171
+ await this._store.set(this.keyEntryKey(key), entry, ttl);
172
+ }
173
+ /**
174
+ * Associates a cache key with a set of tags by recording a snapshot of each tag's current
175
+ * version. Call this whenever you write a fresh value to the cache. Duplicate tags are ignored.
176
+ * No-op while the service is disabled.
177
+ * @param key - The cache key to tag.
178
+ * @param tags - The tags to associate with the key.
179
+ * @param {SetKeyTagsOptions} [options] - Optional settings, such as a `ttl` for the snapshot or
180
+ * `nonBlocking` to fire-and-forget the write.
181
+ * @returns {Promise<void>} Resolves once the snapshot has been written, or immediately when
182
+ * `nonBlocking` is set.
183
+ */
184
+ async setKeyTags(key, tags, options) {
185
+ if (!this._enabled) {
186
+ return;
187
+ }
188
+ const work = this.writeKeyTags(key, tags, options?.ttl);
189
+ if (options?.nonBlocking) {
190
+ work.catch((error) => {
191
+ this.handleNonBlockingError(error);
192
+ });
193
+ return;
194
+ }
195
+ await work;
196
+ }
197
+ /**
198
+ * Removes a key's tag snapshot. After this, {@link CacheTags.isKeyFresh} returns `false`
199
+ * for the key. Use when the cached value itself is deleted. No-op while the service is
200
+ * disabled.
201
+ * @param key - The cache key whose snapshot should be removed.
202
+ * @param {RemoveKeysOptions} [options] - Optional settings, such as `nonBlocking` to
203
+ * fire-and-forget the removal.
204
+ * @returns {Promise<void>} Resolves once the snapshot has been deleted, or immediately when
205
+ * `nonBlocking` is set.
206
+ */
207
+ async removeKey(key, options) {
208
+ await this.removeKeys([key], options);
209
+ }
210
+ /**
211
+ * Removes multiple keys' tag snapshots in a single batched store delete. After this,
212
+ * {@link CacheTags.isKeyFresh} returns `false` for each key. An empty list is a no-op, as is
213
+ * the entire call while the service is disabled.
214
+ * @param keys - The cache keys whose snapshots should be removed.
215
+ * @param {RemoveKeysOptions} [options] - Optional settings, such as `nonBlocking` to
216
+ * fire-and-forget the removal.
217
+ * @returns {Promise<void>} Resolves once the snapshots have been deleted, or immediately when
218
+ * `nonBlocking` is set.
219
+ */
220
+ async removeKeys(keys, options) {
221
+ if (!this._enabled || keys.length === 0) {
222
+ return;
223
+ }
224
+ const entryKeys = keys.map((key) => this.keyEntryKey(key));
225
+ const work = this._store.deleteMany(entryKeys);
226
+ if (options?.nonBlocking) {
227
+ work.catch((error) => {
228
+ this.handleNonBlockingError(error);
229
+ });
230
+ return;
231
+ }
232
+ await work;
233
+ }
234
+ /**
235
+ * Determines whether a key's cached value can still be trusted. A key is fresh only when a
236
+ * snapshot exists for it and every tag in that snapshot still has the version it had at set time.
237
+ * A key with no tags is trivially fresh. Call this before returning a value from your cache.
238
+ * Always returns `true` while the service is disabled.
239
+ * @param key - The cache key to check.
240
+ * @returns {Promise<boolean>} `true` if the key is still fresh; `false` if it is unknown or any of
241
+ * its tags has been invalidated since the snapshot was taken.
242
+ */
243
+ async isKeyFresh(key) {
244
+ if (!this._enabled) {
245
+ return true;
246
+ }
247
+ const entry = await this._store.get(this.keyEntryKey(key));
248
+ if (!entry?.tags) {
249
+ return false;
250
+ }
251
+ const tags = Object.keys(entry.tags);
252
+ const currentVersions = await this.getTagVersions(tags);
253
+ for (let i = 0; i < tags.length; i++) {
254
+ if (currentVersions[i] !== entry.tags[tags[i]]) {
255
+ return false;
256
+ }
257
+ }
258
+ return true;
259
+ }
260
+ /**
261
+ * Determines whether a key's cached value is known to be stale due to tag invalidation. This is
262
+ * the complement of {@link CacheTags.isKeyFresh} for tagged keys, but treats keys without a
263
+ * snapshot as not stale — making it safe to call for every cache lookup, including keys that were
264
+ * never tagged. Always returns `false` while the service is disabled.
265
+ * @param key - The cache key to check.
266
+ * @returns {Promise<boolean>} `true` only when a snapshot exists for the key and at least one of
267
+ * its tags has been invalidated since the snapshot was taken; `false` otherwise (including when
268
+ * the key has no snapshot).
269
+ */
270
+ async isKeyStale(key) {
271
+ if (!this._enabled) {
272
+ return false;
273
+ }
274
+ const staleKeys = await this.getStaleKeys([key]);
275
+ return staleKeys.length > 0;
276
+ }
277
+ /**
278
+ * Determines which of the given keys are known to be stale due to tag invalidation, using two
279
+ * batched store reads regardless of how many keys are checked: one for the snapshots and one for
280
+ * the union of their tag versions. Keys without a snapshot are not considered stale. Returns an
281
+ * empty array while the service is disabled.
282
+ * @param keys - The cache keys to check.
283
+ * @returns {Promise<string[]>} The subset of `keys` whose snapshot references at least one tag
284
+ * that has been invalidated since the snapshot was taken.
285
+ */
286
+ async getStaleKeys(keys) {
287
+ if (!this._enabled || keys.length === 0) {
288
+ return [];
289
+ }
290
+ const entryKeys = keys.map((key) => this.keyEntryKey(key));
291
+ const entries = await this._store.get(entryKeys);
292
+ const tagSet = new Set();
293
+ for (const entry of entries) {
294
+ if (entry?.tags) {
295
+ for (const tag of Object.keys(entry.tags)) {
296
+ tagSet.add(tag);
297
+ }
298
+ }
299
+ }
300
+ const tags = [...tagSet];
301
+ const versions = await this.getTagVersions(tags);
302
+ const currentVersions = new Map();
303
+ for (let i = 0; i < tags.length; i++) {
304
+ currentVersions.set(tags[i], versions[i]);
305
+ }
306
+ const staleKeys = [];
307
+ for (const [i, entry] of entries.entries()) {
308
+ if (!entry?.tags) {
309
+ continue;
310
+ }
311
+ for (const [tag, version] of Object.entries(entry.tags)) {
312
+ if (currentVersions.get(tag) !== version) {
313
+ staleKeys.push(keys[i]);
314
+ break;
315
+ }
316
+ }
317
+ }
318
+ return staleKeys;
319
+ }
320
+ /**
321
+ * Returns the tags currently associated with a key. Returns `undefined` while the service is
322
+ * disabled.
323
+ * @param key - The cache key to look up.
324
+ * @returns {Promise<string[] | undefined>} The tag names from the key's snapshot, or `undefined`
325
+ * if the key has no snapshot.
326
+ */
327
+ async getTags(key) {
328
+ if (!this._enabled) {
329
+ return undefined;
330
+ }
331
+ const entry = await this._store.get(this.keyEntryKey(key));
332
+ if (!entry?.tags) {
333
+ return undefined;
334
+ }
335
+ return Object.keys(entry.tags);
336
+ }
337
+ /**
338
+ * Returns all cache keys whose snapshot references the given tag. This scans every key entry in
339
+ * the namespace via the Keyv iterator, making it an `O(N)` operation intended for debugging and
340
+ * tests rather than hot paths. Returns an empty array if the underlying store exposes no iterator
341
+ * or while the service is disabled.
342
+ * @param tag - The tag to search for.
343
+ * @returns {Promise<string[]>} The cache keys (with the reserved prefix stripped) referencing the tag.
344
+ */
345
+ async getKeysByTag(tag) {
346
+ const result = [];
347
+ if (!this._enabled) {
348
+ return result;
349
+ }
350
+ const prefix = this.keyPrefix();
351
+ const iterator = this._store.iterator?.(this._store.namespace);
352
+ if (!iterator) {
353
+ return result;
354
+ }
355
+ for await (const [storedKey, value] of iterator) {
356
+ if (typeof storedKey !== 'string' || !storedKey.startsWith(prefix)) {
357
+ continue;
358
+ }
359
+ const entry = value;
360
+ if (entry?.tags && Object.hasOwn(entry.tags, tag)) {
361
+ result.push(storedKey.slice(prefix.length));
362
+ }
363
+ }
364
+ return result;
365
+ }
366
+ /**
367
+ * Invalidates a single tag by incrementing its version counter. Every key whose snapshot
368
+ * references this tag becomes stale immediately. Runs in constant time regardless of how many
369
+ * keys reference the tag. No-op while the service is disabled.
370
+ * @param tag - The tag to invalidate.
371
+ * @returns {Promise<string[]>} A single-element array containing the invalidated tag, or an
372
+ * empty array while the service is disabled.
373
+ */
374
+ async invalidateTag(tag) {
375
+ if (!this._enabled) {
376
+ return [];
377
+ }
378
+ const current = await this.getTagVersion(tag);
379
+ await this._store.set(this.tagKey(tag), current + 1);
380
+ return [tag];
381
+ }
382
+ /**
383
+ * Invalidates multiple tags by incrementing each of their version counters in a single batched
384
+ * store write. Duplicate tags are bumped once. An empty list is a no-op, as is the entire call
385
+ * while the service is disabled.
386
+ * @param tags - The tags to invalidate.
387
+ * @returns {Promise<string[]>} The `tags` argument as provided (including any duplicates), or
388
+ * an empty array while the service is disabled.
389
+ */
390
+ async invalidateTags(tags) {
391
+ if (!this._enabled) {
392
+ return [];
393
+ }
394
+ const uniqueTags = [...new Set(tags)];
395
+ if (uniqueTags.length === 0) {
396
+ return tags;
397
+ }
398
+ const versions = await this.getTagVersions(uniqueTags);
399
+ const kvPairs = [];
400
+ for (let i = 0; i < uniqueTags.length; i++) {
401
+ kvPairs.push({ key: this.tagKey(uniqueTags[i]), value: versions[i] + 1 });
402
+ }
403
+ await this._store.setMany(kvPairs);
404
+ return tags;
405
+ }
406
+ }
407
+ //# sourceMappingURL=cache-tags.js.map