{"version":3,"file":"cache.cjs","names":["Cache","entityKind","Table","OriginalName","Redis"],"sources":["../../../src/cache/upstash/cache.ts"],"sourcesContent":["import { Redis } from '@upstash/redis';\nimport type { MutationOption } from '~/cache/core/index.ts';\nimport { Cache } from '~/cache/core/index.ts';\nimport { entityKind, is } from '~/entity.ts';\nimport { OriginalName, Table } from '~/table.ts';\nimport type { CacheConfig } from '../core/types.ts';\n\nconst getByTagScript = `\nlocal tagsMapKey = KEYS[1] -- tags map key\nlocal tag        = ARGV[1] -- tag\n\nlocal compositeTableName = redis.call('HGET', tagsMapKey, tag)\nif not compositeTableName then\n  return nil\nend\n\nlocal value = redis.call('HGET', compositeTableName, tag)\nreturn value\n`;\n\nconst onMutateScript = `\nlocal tagsMapKey = KEYS[1] -- tags map key\nlocal tables     = {}      -- initialize tables array\nlocal tags       = ARGV    -- tags array\n\nfor i = 2, #KEYS do\n  tables[#tables + 1] = KEYS[i] -- add all keys except the first one to tables\nend\n\nif #tags > 0 then\n  for _, tag in ipairs(tags) do\n    if tag ~= nil and tag ~= '' then\n      local compositeTableName = redis.call('HGET', tagsMapKey, tag)\n      if compositeTableName then\n        redis.call('HDEL', compositeTableName, tag)\n      end\n    end\n  end\n  redis.call('HDEL', tagsMapKey, unpack(tags))\nend\n\nlocal keysToDelete = {}\n\nif #tables > 0 then\n  local compositeTableNames = redis.call('SUNION', unpack(tables))\n  for _, compositeTableName in ipairs(compositeTableNames) do\n    keysToDelete[#keysToDelete + 1] = compositeTableName\n  end\n  for _, table in ipairs(tables) do\n    keysToDelete[#keysToDelete + 1] = table\n  end\n  redis.call('DEL', unpack(keysToDelete))\nend\n`;\n\ntype Script = ReturnType<Redis['createScript']>;\n\ntype ExpireOptions = 'NX' | 'nx' | 'XX' | 'xx' | 'GT' | 'gt' | 'LT' | 'lt';\n\nexport class UpstashCache extends Cache {\n\tstatic override readonly [entityKind]: string = 'UpstashCache';\n\t/**\n\t * Prefix for sets which denote the composite table names for each unique table\n\t *\n\t * Example: In the composite table set of \"table1\", you may find\n\t * `${compositeTablePrefix}table1,table2` and `${compositeTablePrefix}table1,table3`\n\t */\n\tprivate static compositeTableSetPrefix = '__CTS__';\n\t/**\n\t * Prefix for hashes which map hash or tags to cache values\n\t */\n\tprivate static compositeTablePrefix = '__CT__';\n\t/**\n\t * Key which holds the mapping of tags to composite table names\n\t *\n\t * Using this tagsMapKey, you can find the composite table name for a given tag\n\t * and get the cache value for that tag:\n\t *\n\t * ```ts\n\t * const compositeTable = redis.hget(tagsMapKey, 'tag1')\n\t * console.log(compositeTable) // `${compositeTablePrefix}table1,table2`\n\t *\n\t * const cachevalue = redis.hget(compositeTable, 'tag1')\n\t */\n\tprivate static tagsMapKey = '__tagsMap__';\n\t/**\n\t * Queries whose auto invalidation is false aren't stored in their respective\n\t * composite table hashes because those hashes are deleted when a mutation\n\t * occurs on related tables.\n\t *\n\t * Instead, they are stored in a separate hash with the prefix\n\t * `__nonAutoInvalidate__` to prevent them from being deleted when a mutation\n\t */\n\tprivate static nonAutoInvalidateTablePrefix = '__nonAutoInvalidate__';\n\n\tprivate luaScripts: {\n\t\tgetByTagScript: Script;\n\t\tonMutateScript: Script;\n\t};\n\n\tprivate internalConfig: { seconds: number; hexOptions?: ExpireOptions };\n\n\tconstructor(public redis: Redis, config?: CacheConfig, protected useGlobally?: boolean) {\n\t\tsuper();\n\t\tthis.internalConfig = this.toInternalConfig(config);\n\t\tthis.luaScripts = {\n\t\t\tgetByTagScript: this.redis.createScript(getByTagScript, { readonly: true }),\n\t\t\tonMutateScript: this.redis.createScript(onMutateScript),\n\t\t};\n\t}\n\n\tpublic strategy() {\n\t\treturn this.useGlobally ? 'all' : 'explicit';\n\t}\n\n\tprivate toInternalConfig(config?: CacheConfig): { seconds: number; hexOptions?: ExpireOptions } {\n\t\treturn config\n\t\t\t? {\n\t\t\t\tseconds: config.ex!,\n\t\t\t\thexOptions: config.hexOptions,\n\t\t\t}\n\t\t\t: {\n\t\t\t\tseconds: 1,\n\t\t\t};\n\t}\n\n\toverride async get(\n\t\tkey: string,\n\t\ttables: string[],\n\t\tisTag: boolean = false,\n\t\tisAutoInvalidate?: boolean,\n\t): Promise<any[] | undefined> {\n\t\tif (!isAutoInvalidate) {\n\t\t\tconst result = await this.redis.hget(UpstashCache.nonAutoInvalidateTablePrefix, key);\n\t\t\treturn result === null ? undefined : result as any[];\n\t\t}\n\n\t\tif (isTag) {\n\t\t\tconst result = await this.luaScripts.getByTagScript.exec([UpstashCache.tagsMapKey], [key]);\n\t\t\treturn result === null ? undefined : result as any[];\n\t\t}\n\n\t\t// Normal cache lookup for the composite key\n\t\tconst compositeKey = this.getCompositeKey(tables);\n\t\tconst result = await this.redis.hget(compositeKey, key) ?? undefined; // Retrieve result for normal query\n\t\treturn result === null ? undefined : result as any[];\n\t}\n\n\toverride async put(\n\t\tkey: string,\n\t\tresponse: any,\n\t\ttables: string[],\n\t\tisTag: boolean = false,\n\t\tconfig?: CacheConfig,\n\t): Promise<void> {\n\t\tconst isAutoInvalidate = tables.length !== 0;\n\n\t\tconst pipeline = this.redis.pipeline();\n\t\tconst ttlSeconds = config && config.ex ? config.ex : this.internalConfig.seconds;\n\t\tconst hexOptions = config && config.hexOptions ? config.hexOptions : this.internalConfig?.hexOptions;\n\n\t\tif (!isAutoInvalidate) {\n\t\t\tif (isTag) {\n\t\t\t\tpipeline.hset(UpstashCache.tagsMapKey, { [key]: UpstashCache.nonAutoInvalidateTablePrefix });\n\t\t\t\tpipeline.hexpire(UpstashCache.tagsMapKey, key, ttlSeconds, hexOptions);\n\t\t\t}\n\n\t\t\tpipeline.hset(UpstashCache.nonAutoInvalidateTablePrefix, { [key]: response });\n\t\t\tpipeline.hexpire(UpstashCache.nonAutoInvalidateTablePrefix, key, ttlSeconds, hexOptions);\n\t\t\tawait pipeline.exec();\n\t\t\treturn;\n\t\t}\n\n\t\tconst compositeKey = this.getCompositeKey(tables);\n\n\t\tpipeline.hset(compositeKey, { [key]: response }); // Store the result with the tag under the composite key\n\t\tpipeline.hexpire(compositeKey, key, ttlSeconds, hexOptions); // Set expiration for the composite key\n\n\t\tif (isTag) {\n\t\t\tpipeline.hset(UpstashCache.tagsMapKey, { [key]: compositeKey }); // Store the tag and its composite key in the map\n\t\t\tpipeline.hexpire(UpstashCache.tagsMapKey, key, ttlSeconds, hexOptions); // Set expiration for the tag\n\t\t}\n\n\t\tfor (const table of tables) {\n\t\t\tpipeline.sadd(this.addTablePrefix(table), compositeKey);\n\t\t}\n\n\t\tawait pipeline.exec();\n\t}\n\n\toverride async onMutate(params: MutationOption) {\n\t\tconst tags = Array.isArray(params.tags) ? params.tags : params.tags ? [params.tags] : [];\n\t\tconst tables = Array.isArray(params.tables) ? params.tables : params.tables ? [params.tables] : [];\n\t\tconst tableNames: string[] = tables.map((table) => is(table, Table) ? table[OriginalName] : table as string);\n\n\t\tconst compositeTableSets = tableNames.map((table) => this.addTablePrefix(table));\n\t\tawait this.luaScripts.onMutateScript.exec([UpstashCache.tagsMapKey, ...compositeTableSets], tags);\n\t}\n\n\tprivate addTablePrefix = (table: string) => `${UpstashCache.compositeTableSetPrefix}${table}`;\n\tprivate getCompositeKey = (tables: string[]) => `${UpstashCache.compositeTablePrefix}${tables.sort().join(',')}`;\n}\n\nexport function upstashCache(\n\t{ url, token, config, global = false }: { url: string; token: string; config?: CacheConfig; global?: boolean },\n): UpstashCache {\n\tconst redis = new Redis({\n\t\turl,\n\t\ttoken,\n\t});\n\n\treturn new UpstashCache(redis, config, global);\n}\n"],"mappings":"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"}