{"version":3,"sources":["../src/store/memory.ts","../src/store/postgres.ts","../src/store/redis.ts"],"sourcesContent":["import type { ArcaStore } from \"./types\";\n\n/** In-process store for tests; reservations do not survive a restart. */\nexport function createMemoryStore(): ArcaStore {\n  const values = new Map<string, string>();\n  const locks = new Map<string, Promise<void>>();\n  return {\n    get: (key) => Promise.resolve(values.get(key) ?? null),\n    set(key, value) {\n      values.set(key, value);\n      return Promise.resolve();\n    },\n    add(key, value) {\n      if (values.has(key)) {\n        return Promise.resolve(false);\n      }\n      values.set(key, value);\n      return Promise.resolve(true);\n    },\n    delete(key) {\n      values.delete(key);\n      return Promise.resolve();\n    },\n    async withLock(key, fn) {\n      const previous = locks.get(key) ?? Promise.resolve();\n      let release: () => void = () => undefined;\n      const current = new Promise<void>((resolve) => {\n        release = resolve;\n      });\n      const queued = previous.catch(() => undefined).then(() => current);\n      locks.set(key, queued);\n      await previous.catch(() => undefined);\n      try {\n        return await fn();\n      } finally {\n        release();\n        if (locks.get(key) === queued) {\n          locks.delete(key);\n        }\n      }\n    },\n  };\n}\n","import { ArcaConfigurationError } from \"../errors\";\nimport { ARCA_LEASE_MS, withLease } from \"./lock\";\nimport { type ArcaStore, storeCall } from \"./types\";\n\ntype Row = { key?: string; value?: string };\n/** Uses the caller's SQL client; provision the table before use. */\nexport function createPostgresStore({\n  query,\n  table = \"arca_store\",\n}: {\n  query: (text: string, params: string[]) => Promise<Row[] | { rows: Row[] }>;\n  table?: string;\n}): ArcaStore {\n  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(table)) {\n    throw new ArcaConfigurationError(\"Invalid ARCA store table identifier.\");\n  }\n  const name = `\"${table}\"`;\n  const lease = Math.round(ARCA_LEASE_MS / 1000);\n  const run = (text: string, params: string[]) =>\n    storeCall(async () => {\n      const result = await query(text, params);\n      return Array.isArray(result) ? result : result.rows;\n    });\n  return {\n    async get(key) {\n      return (\n        (await run(`SELECT value FROM ${name} WHERE key = $1`, [key]))[0]\n          ?.value ?? null\n      );\n    },\n    async set(key, value) {\n      await run(\n        `INSERT INTO ${name} (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now()`,\n        [key, value]\n      );\n    },\n    async add(key, value) {\n      return (\n        (\n          await run(\n            `INSERT INTO ${name} (key, value) VALUES ($1, $2) ON CONFLICT DO NOTHING RETURNING key`,\n            [key, value]\n          )\n        ).length > 0\n      );\n    },\n    async delete(key) {\n      await run(`DELETE FROM ${name} WHERE key = $1`, [key]);\n    },\n    withLock(key, fn) {\n      // A lease row, not an advisory lock: it survives a transaction-mode\n      // pooler, where a session-scoped lock would be taken on another backend.\n      return withLease(\n        key,\n        {\n          async acquire(owner) {\n            const taken = await run(\n              `INSERT INTO ${name} (key, value) VALUES ($1, $2) ON CONFLICT DO NOTHING RETURNING key`,\n              [key, owner]\n            );\n            if (taken.length > 0) {\n              return true;\n            }\n            const expired = await run(\n              `UPDATE ${name} SET value = $2, updated_at = now() WHERE key = $1 AND updated_at < now() - interval '${lease} seconds' RETURNING key`,\n              [key, owner]\n            );\n            return expired.length > 0;\n          },\n          async renew(owner) {\n            await run(\n              `UPDATE ${name} SET updated_at = now() WHERE key = $1 AND value = $2`,\n              [key, owner]\n            );\n          },\n          async release(owner) {\n            await run(`DELETE FROM ${name} WHERE key = $1 AND value = $2`, [\n              key,\n              owner,\n            ]);\n          },\n        },\n        fn\n      );\n    },\n  };\n}\n","import { ArcaConfigurationError } from \"../errors\";\nimport { ARCA_LEASE_MS, withLease } from \"./lock\";\nimport { type ArcaStore, storeCall } from \"./types\";\n\ntype RedisClient = {\n  get(key: string): Promise<unknown>;\n  set(key: string, value: string): Promise<unknown>;\n  call?(...args: string[]): Promise<unknown>;\n  del?(key: string): Promise<unknown>;\n};\n/** Uses ioredis or Upstash without importing either driver. */\nexport function createRedisStore(\n  client: RedisClient,\n  options: { flavor?: \"ioredis\" | \"upstash\" } = {}\n): ArcaStore {\n  const flavor =\n    options.flavor ??\n    (typeof client.call === \"function\" ? \"ioredis\" : \"upstash\");\n  if (flavor === \"ioredis\" && !client.call) {\n    throw new ArcaConfigurationError(\n      \"ARCA Redis ioredis flavor requires client.call.\"\n    );\n  }\n  // The public structural type accepts ioredis's overloaded set method.\n  // Only the Upstash flavor invokes its options-object overload.\n  const upstashSet = client.set as (\n    key: string,\n    value: string,\n    options: { nx?: true; xx?: true; px?: number }\n  ) => Promise<unknown>;\n  const read = async (key: string) => {\n    const value = await client.get(key);\n    if (value === null || value === undefined) {\n      return null;\n    }\n    // Upstash can deserialize stored JSON automatically.\n    return typeof value === \"string\" ? value : JSON.stringify(value);\n  };\n  // A lease, so a crashed holder frees the key; a renewal keeps a live one.\n  const lease = (key: string, owner: string, mode: \"nx\" | \"xx\") =>\n    storeCall(\n      async () =>\n        (flavor === \"ioredis\"\n          ? await client.call?.(\n              \"SET\",\n              key,\n              owner,\n              mode.toUpperCase(),\n              \"PX\",\n              String(ARCA_LEASE_MS)\n            )\n          : await upstashSet.call(client, key, owner, {\n              [mode]: true,\n              px: ARCA_LEASE_MS,\n            })) === \"OK\"\n    );\n  return {\n    get: (key) => storeCall(() => read(key)),\n    set: (key, value) =>\n      storeCall(async () => {\n        await client.set(key, value);\n      }),\n    add: (key, value) =>\n      storeCall(\n        async () =>\n          (flavor === \"ioredis\"\n            ? await client.call?.(\"SET\", key, value, \"NX\")\n            : await upstashSet.call(client, key, value, { nx: true })) === \"OK\"\n      ),\n    ...(client.del\n      ? {\n          delete: (key: string) =>\n            storeCall(async () => {\n              await client.del?.(key);\n            }),\n          withLock: <T>(key: string, fn: () => Promise<T>) =>\n            withLease(\n              key,\n              {\n                acquire: (owner) => lease(key, owner, \"nx\"),\n                renew: async (owner) => {\n                  // Check and set: only the holder extends its own lease.\n                  if ((await storeCall(() => read(key))) === owner) {\n                    await lease(key, owner, \"xx\");\n                  }\n                },\n                release: async (owner) => {\n                  await storeCall(async () => {\n                    if ((await read(key)) === owner) {\n                      await client.del?.(key);\n                    }\n                  });\n                },\n              },\n              fn\n            ),\n        }\n      : {}),\n  };\n}\n"],"mappings":"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","names":[]}