@prairielearn/cache 2.0.20 → 2.0.21

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,15 @@
1
1
  # @prairielearn/cache
2
2
 
3
+ ## 2.0.21
4
+
5
+ ### Patch Changes
6
+
7
+ - 23adb05: Upgrade all JavaScript dependencies
8
+ - Updated dependencies [a7d1ad9]
9
+ - Updated dependencies [23adb05]
10
+ - @prairielearn/sentry@4.0.0
11
+ - @prairielearn/logger@2.0.18
12
+
3
13
  ## 2.0.20
4
14
 
5
15
  ### Patch Changes
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,MAAM,MAAM,sBAAsB,CAAC;AAE/C,MAAM,OAAO,KAAK;IAChB,OAAO,GAAG,KAAK,CAAC;IAChB,IAAI,GAAG,MAAM,CAAC;IACd,WAAW,CAA4B;IACvC,WAAW,CAAS;IACpB,SAAS,GAAG,EAAE,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,MAIV;QACC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACvC,aAAa;YACb,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YACpE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,WAAW,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACnC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;gBACjC,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,WAAW,GAAG,IAAI,QAAQ,CAAC;gBAC9B,sEAAsE;gBACtE,wCAAwC;gBACxC,GAAG,EAAE,IAAI;aACV,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAU,EAAE,QAAgB;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QAEvC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC1E,MAAM;YACR,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,kEAAkE;gBAClE,8DAA8D;gBAC9D,qBAAqB;gBACrB,EAAE;gBACF,+DAA+D;gBAC/D,iDAAiD;gBACjD,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,IAAI,CAAC,WAAW;qBACb,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC;qBACrD,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAClF,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QAEvC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACnC,MAAM;YACR,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACtC,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAE/B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QAEvC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,OAAO,CAAC,CAAC,CAAC;gBACR,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;gBACvE,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;gBACzB,MAAM;YACR,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,IAAI,MAAM,GAAG,GAAG,CAAC;gBACjB,GAAG,CAAC;oBACF,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;oBACvE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CACvC,MAAM,EACN,OAAO,EACP,GAAG,IAAI,CAAC,SAAS,GAAG,EACpB,OAAO,EACP,IAAI,CACL,CAAC;oBACF,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBAElB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACtB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACpB,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;wBACvE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACnC,CAAC;gBACH,CAAC,QAAQ,MAAM,KAAK,GAAG,EAAE;gBACzB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;YACvE,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QAChC,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC","sourcesContent":["import assert from 'node:assert';\n\nimport { Redis } from 'ioredis';\nimport { LRUCache } from 'lru-cache';\n\nimport { logger } from '@prairielearn/logger';\nimport * as Sentry from '@prairielearn/sentry';\n\nexport class Cache {\n enabled = false;\n type = 'none';\n memoryCache?: LRUCache<string, string>;\n redisClient?: Redis;\n keyPrefix = '';\n\n async init(config: {\n type: 'none' | 'memory' | 'redis';\n keyPrefix: string;\n redisUrl?: string | null;\n }) {\n this.type = config.type;\n this.keyPrefix = config.keyPrefix;\n if (!this.type || this.type === 'none') {\n // No caching\n this.enabled = false;\n return;\n }\n\n if (this.type === 'redis') {\n if (!config.redisUrl) throw new Error('redisUrl not set in config');\n this.enabled = true;\n this.redisClient = new Redis(config.redisUrl);\n this.redisClient.on('error', (err) => {\n logger.error('Redis error', err);\n Sentry.captureException(err);\n });\n } else if (this.type === 'memory') {\n this.enabled = true;\n this.memoryCache = new LRUCache({\n // The in-memory cache is really only suited for development, so we'll\n // hardcode a relatively low limit here.\n max: 1000,\n });\n } else {\n throw new Error(`Unknown cache type \"${this.type}\"`);\n }\n }\n\n set(key: string, value: any, maxAgeMS: number) {\n if (!this.enabled) return;\n\n const scopedKey = this.keyPrefix + key;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n this.memoryCache.set(scopedKey, JSON.stringify(value), { ttl: maxAgeMS });\n break;\n }\n\n case 'redis': {\n // This returns a promise, but we don't want to wait for this data\n // to reach the cache before continuing, and we don't *really*\n // care if it errors.\n //\n // We don't log the error because it contains the cached value,\n // which can be huge and which fills up the logs.\n assert(this.redisClient, 'Redis client is enabled but not configured');\n this.redisClient\n .set(scopedKey, JSON.stringify(value), 'PX', maxAgeMS)\n .catch((_err) => logger.error('Cache set error', { key, scopedKey, maxAgeMS }));\n break;\n }\n }\n }\n\n async del(key: string) {\n if (!this.enabled) return;\n\n const scopedKey = this.keyPrefix + key;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n this.memoryCache.delete(scopedKey);\n break;\n }\n\n case 'redis': {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n await this.redisClient.del(scopedKey);\n break;\n }\n }\n }\n\n /**\n * Returns the value for the corresponding key if it exists in the cache; null otherwise.\n */\n async get(key: string): Promise<any> {\n if (!this.enabled) return null;\n\n const scopedKey = this.keyPrefix + key;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n const value = this.memoryCache.get(scopedKey);\n if (typeof value === 'string') {\n return JSON.parse(value);\n }\n return undefined;\n }\n\n case 'redis': {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n const value = await this.redisClient.get(scopedKey);\n if (typeof value === 'string') {\n return JSON.parse(value);\n }\n return undefined;\n }\n\n default: {\n return null;\n }\n }\n }\n\n /**\n * Clear all entries from the cache.\n */\n async reset() {\n if (!this.enabled) return;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n this.memoryCache.clear();\n break;\n }\n\n case 'redis': {\n let cursor = '0';\n do {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n const reply = await this.redisClient.scan(\n cursor,\n 'MATCH',\n `${this.keyPrefix}*`,\n 'COUNT',\n 1000,\n );\n cursor = reply[0];\n\n const keys = reply[1];\n if (keys.length > 0) {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n await this.redisClient.del(keys);\n }\n } while (cursor !== '0');\n break;\n }\n }\n }\n /**\n * Releases any connections associated with the cache.\n */\n async close() {\n if (!this.enabled) return;\n this.enabled = false;\n\n if (this.type === 'redis') {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n await this.redisClient.quit();\n }\n }\n}\n\nexport const cache = new Cache();\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert';\n\nimport { Redis } from 'ioredis';\nimport { LRUCache } from 'lru-cache';\n\nimport { logger } from '@prairielearn/logger';\nimport * as Sentry from '@prairielearn/sentry';\n\nexport class Cache {\n enabled = false;\n type = 'none';\n memoryCache?: LRUCache<string, string>;\n redisClient?: Redis;\n keyPrefix = '';\n\n async init(config: {\n type: 'none' | 'memory' | 'redis';\n keyPrefix: string;\n redisUrl?: string | null;\n }) {\n this.type = config.type;\n this.keyPrefix = config.keyPrefix;\n if (!this.type || this.type === 'none') {\n // No caching\n this.enabled = false;\n return;\n }\n\n if (this.type === 'redis') {\n if (!config.redisUrl) throw new Error('redisUrl not set in config');\n this.enabled = true;\n this.redisClient = new Redis(config.redisUrl);\n this.redisClient.on('error', (err) => {\n logger.error('Redis error', err);\n Sentry.captureException(err);\n });\n } else if (this.type === 'memory') {\n this.enabled = true;\n this.memoryCache = new LRUCache({\n // The in-memory cache is really only suited for development, so we'll\n // hardcode a relatively low limit here.\n max: 1000,\n });\n } else {\n throw new Error(`Unknown cache type \"${this.type}\"`);\n }\n }\n\n set(key: string, value: any, maxAgeMS: number) {\n if (!this.enabled) return;\n\n const scopedKey = this.keyPrefix + key;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n this.memoryCache.set(scopedKey, JSON.stringify(value), { ttl: maxAgeMS });\n break;\n }\n\n case 'redis': {\n // This returns a promise, but we don't want to wait for this data\n // to reach the cache before continuing, and we don't *really*\n // care if it errors.\n //\n // We don't log the error because it contains the cached value,\n // which can be huge and which fills up the logs.\n assert(this.redisClient, 'Redis client is enabled but not configured');\n this.redisClient\n .set(scopedKey, JSON.stringify(value), 'PX', maxAgeMS)\n .catch((_err) => logger.error('Cache set error', { key, scopedKey, maxAgeMS }));\n break;\n }\n }\n }\n\n async del(key: string) {\n if (!this.enabled) return;\n\n const scopedKey = this.keyPrefix + key;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n this.memoryCache.delete(scopedKey);\n break;\n }\n\n case 'redis': {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n await this.redisClient.del(scopedKey);\n break;\n }\n }\n }\n\n /**\n * Returns the value for the corresponding key if it exists in the cache; null otherwise.\n */\n async get(key: string): Promise<any> {\n if (!this.enabled) return null;\n\n const scopedKey = this.keyPrefix + key;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n const value = this.memoryCache.get(scopedKey);\n if (typeof value === 'string') {\n return JSON.parse(value);\n }\n return undefined;\n }\n\n case 'redis': {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n const value = await this.redisClient.get(scopedKey);\n if (typeof value === 'string') {\n return JSON.parse(value);\n }\n return undefined;\n }\n\n default: {\n return null;\n }\n }\n }\n\n /**\n * Clear all entries from the cache.\n */\n async reset() {\n if (!this.enabled) return;\n\n switch (this.type) {\n case 'memory': {\n assert(this.memoryCache, 'Memory cache is enabled but not configured');\n this.memoryCache.clear();\n break;\n }\n\n case 'redis': {\n let cursor = '0';\n do {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n const reply = await this.redisClient.scan(\n cursor,\n 'MATCH',\n `${this.keyPrefix}*`,\n 'COUNT',\n 1000,\n );\n cursor = reply[0];\n\n const keys = reply[1];\n if (keys.length > 0) {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n await this.redisClient.del(keys);\n }\n } while (cursor !== '0');\n break;\n }\n }\n }\n\n /**\n * Releases any connections associated with the cache.\n */\n async close() {\n if (!this.enabled) return;\n this.enabled = false;\n\n if (this.type === 'redis') {\n assert(this.redisClient, 'Redis client is enabled but not configured');\n await this.redisClient.quit();\n }\n }\n}\n\nexport const cache = new Cache();\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/cache",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -8,11 +8,11 @@
8
8
  "dev": "tsc --watch --preserveWatchOutput"
9
9
  },
10
10
  "dependencies": {
11
- "@prairielearn/logger": "^2.0.17",
12
- "@prairielearn/sentry": "^3.0.7",
13
- "ioredis": "^5.6.1",
11
+ "@prairielearn/logger": "^2.0.18",
12
+ "@prairielearn/sentry": "^4.0.0",
13
+ "ioredis": "^5.7.0",
14
14
  "lru-cache": "^11.1.0",
15
- "zod": "^3.25.67"
15
+ "zod": "^3.25.76"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@prairielearn/tsconfig": "^0.0.0",
package/src/index.ts CHANGED
@@ -163,6 +163,7 @@ export class Cache {
163
163
  }
164
164
  }
165
165
  }
166
+
166
167
  /**
167
168
  * Releases any connections associated with the cache.
168
169
  */