@platformatic/next 2.20.0 → 2.21.0-alpha.2

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.
@@ -42,20 +42,50 @@ export class CacheHandler {
42
42
  #logger
43
43
  #store
44
44
  #subprefix
45
+ #meta
45
46
  #maxTTL
46
47
 
47
- constructor () {
48
- this.#logger = this.#createLogger()
49
- this.#config = globalThis.platformatic.config.cache
50
- this.#store = getConnection(this.#config.url)
51
- this.#maxTTL = this.#config.maxTTL
52
- this.#subprefix = this.#getSubprefix()
48
+ constructor (options) {
49
+ options ??= {}
50
+
51
+ this.#config = options.config
52
+ this.#logger = options.logger
53
+ this.#store = options.store
54
+ this.#maxTTL = options.maxTTL
55
+ this.#subprefix = options.subprefix
56
+ this.#meta = options.meta
57
+
58
+ if (globalThis.platformatic) {
59
+ this.#config ??= globalThis.platformatic.config.cache
60
+ this.#logger ??= this.#createPlatformaticLogger()
61
+ this.#store ??= getConnection(this.#config.url)
62
+ this.#maxTTL ??= this.#config.maxTTL
63
+ this.#subprefix ??= this.#getPlatformaticSubprefix()
64
+ this.#meta ??= this.#getPlatformaticMeta()
65
+ } else {
66
+ this.#config ??= {}
67
+ this.#maxTTL ??= 86_400
68
+ this.#subprefix ??= ''
69
+ this.#meta ??= {}
70
+ }
71
+
72
+ if (!this.#config) {
73
+ throw new Error('Please provide a the "config" option.')
74
+ }
75
+
76
+ if (!this.#logger) {
77
+ throw new Error('Please provide a the "logger" option.')
78
+ }
79
+
80
+ if (!this.#store) {
81
+ throw new Error('Please provide a the "store" option.')
82
+ }
53
83
  }
54
84
 
55
- async get (cacheKey) {
85
+ async get (cacheKey, _, isRedisKey) {
56
86
  this.#logger.trace({ key: cacheKey }, 'get')
57
87
 
58
- const key = this.#keyFor(cacheKey, sections.values)
88
+ const key = isRedisKey ? cacheKey : this.#keyFor(cacheKey, sections.values)
59
89
 
60
90
  let rawValue
61
91
  try {
@@ -96,13 +126,21 @@ export class CacheHandler {
96
126
  return value
97
127
  }
98
128
 
99
- async set (cacheKey, value, { tags, revalidate }) {
129
+ async set (cacheKey, value, { tags, revalidate }, isRedisKey) {
100
130
  this.#logger.trace({ key: cacheKey, value, tags, revalidate }, 'set')
101
131
 
132
+ const key = isRedisKey ? cacheKey : this.#keyFor(cacheKey, sections.values)
133
+
102
134
  try {
103
135
  // Compute the parameters to save
104
- const key = this.#keyFor(cacheKey, sections.values)
105
- const data = this.#serialize({ value, tags, lastModified: Date.now(), revalidate, maxTTL: this.#maxTTL })
136
+ const data = this.#serialize({
137
+ value,
138
+ tags,
139
+ lastModified: Date.now(),
140
+ revalidate,
141
+ maxTTL: this.#maxTTL,
142
+ ...this.#meta
143
+ })
106
144
  const expire = Math.min(revalidate, this.#maxTTL)
107
145
 
108
146
  if (expire < 1) {
@@ -131,6 +169,56 @@ export class CacheHandler {
131
169
  }
132
170
  }
133
171
 
172
+ async remove (cacheKey, isRedisKey) {
173
+ this.#logger.trace({ key: cacheKey }, 'remove')
174
+
175
+ const key = isRedisKey ? cacheKey : this.#keyFor(cacheKey, sections.values)
176
+
177
+ let rawValue
178
+ try {
179
+ rawValue = await this.#store.get(key)
180
+
181
+ if (!rawValue) {
182
+ return
183
+ }
184
+ } catch (e) {
185
+ this.#logger.error({ err: ensureLoggableError(e) }, 'Cannot read cache value from Valkey')
186
+ throw new Error('Cannot read cache value from Valkey', { cause: e })
187
+ }
188
+
189
+ let value
190
+ try {
191
+ value = this.#deserialize(rawValue)
192
+ } catch (e) {
193
+ this.#logger.error({ err: ensureLoggableError(e) }, 'Cannot deserialize cache value from Valkey')
194
+
195
+ // Avoid useless reads the next time
196
+ // Note that since the value was unserializable, we don't know its tags and thus
197
+ // we cannot remove it from the tags sets. TTL will take care of them.
198
+ await this.#store.del(key)
199
+
200
+ throw new Error('Cannot deserialize cache value from Valkey', { cause: e })
201
+ }
202
+
203
+ try {
204
+ const promises = []
205
+ promises.push(this.#store.del(key))
206
+
207
+ if (Array.isArray(value.tags)) {
208
+ for (const tag of value.tags) {
209
+ const tagsKey = this.#keyFor(tag, sections.tags)
210
+ this.#store.srem(tagsKey, key, 'gt')
211
+ }
212
+ }
213
+
214
+ // Execute all the operations
215
+ await Promise.all(promises)
216
+ } catch (e) {
217
+ this.#logger.error({ err: ensureLoggableError(e) }, 'Cannot remove cache value from Valkey')
218
+ throw new Error('Cannot remove cache value from Valkey', { cause: e })
219
+ }
220
+ }
221
+
134
222
  async revalidateTag (tags) {
135
223
  this.#logger.trace({ tags }, 'revalidateTag')
136
224
 
@@ -189,7 +277,7 @@ export class CacheHandler {
189
277
  await Promise.all(promises)
190
278
  }
191
279
 
192
- #createLogger () {
280
+ #createPlatformaticLogger () {
193
281
  const pinoOptions = {
194
282
  level: globalThis.platformatic?.logLevel ?? 'info'
195
283
  }
@@ -205,7 +293,7 @@ export class CacheHandler {
205
293
  return pino(pinoOptions)
206
294
  }
207
295
 
208
- #getSubprefix () {
296
+ #getPlatformaticSubprefix () {
209
297
  const root = fileURLToPath(globalThis.platformatic.root)
210
298
 
211
299
  return existsSync(resolve(root, '.next/BUILD_ID'))
@@ -213,6 +301,13 @@ export class CacheHandler {
213
301
  : 'development'
214
302
  }
215
303
 
304
+ #getPlatformaticMeta () {
305
+ return {
306
+ serviceId: globalThis.platformatic.serviceId,
307
+ workerId: globalThis.platformatic.workerId
308
+ }
309
+ }
310
+
216
311
  #keyFor (key, section) {
217
312
  return keyFor(this.#config.prefix, this.#subprefix, section, key)
218
313
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/next",
3
- "version": "2.20.0",
3
+ "version": "2.21.0-alpha.2",
4
4
  "description": "Platformatic Next.js Stackable",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,9 +23,9 @@
23
23
  "iovalkey": "^0.2.1",
24
24
  "msgpackr": "^1.11.2",
25
25
  "semver": "^7.6.3",
26
- "@platformatic/basic": "2.20.0",
27
- "@platformatic/utils": "2.20.0",
28
- "@platformatic/config": "2.20.0"
26
+ "@platformatic/basic": "2.21.0-alpha.2",
27
+ "@platformatic/utils": "2.21.0-alpha.2",
28
+ "@platformatic/config": "2.21.0-alpha.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@fastify/reply-from": "^11.0.0",
@@ -41,8 +41,8 @@
41
41
  "react-dom": "^18.3.1",
42
42
  "typescript": "^5.5.4",
43
43
  "ws": "^8.18.0",
44
- "@platformatic/composer": "2.20.0",
45
- "@platformatic/service": "2.20.0"
44
+ "@platformatic/composer": "2.21.0-alpha.2",
45
+ "@platformatic/service": "2.21.0-alpha.2"
46
46
  },
47
47
  "scripts": {
48
48
  "test": "npm run lint && borp --concurrency=1 --no-timeout",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/next/2.20.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/next/2.21.0-alpha.2.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Next.js Stackable",
5
5
  "type": "object",