{"version":3,"file":"index.cjs","names":["parsePolicyRow","validatePolicy","parseRoleRow","validateRole"],"sources":["../../../src/adapters/redis/index.ts"],"sourcesContent":["import type { AccessControl, IamAdapter, IamPrimitives, IamRequest } from '../../core/types'\nimport { parsePolicyRow, parseRoleRow, validatePolicy, validateRole } from '../../core/validate'\n\n/** IamRedis adapter integration types. Type-only namespace - zero bundle cost. */\nexport namespace IamRedis {\n  /**\n   * Describes the minimal IamRedis client surface used by {@link IamRedisAdapter}.\n   *\n   * Both ioredis and node-redis (v4+) implement these methods, so consumers can\n   * pass either without a hard dependency.\n   */\n  export interface ILike {\n    get(key: string): Promise<string | null>\n    set(key: string, value: string): Promise<unknown>\n    del(...keys: string[]): Promise<number>\n    hset(key: string, field: string, value: string): Promise<number>\n    hget(key: string, field: string): Promise<string | null>\n    hdel(key: string, ...fields: string[]): Promise<number>\n    hkeys(key: string): Promise<string[]>\n    hvals(key: string): Promise<string[]>\n    hgetall(key: string): Promise<Record<string, string>>\n    sadd(key: string, ...members: string[]): Promise<number>\n    srem(key: string, ...members: string[]): Promise<number>\n    smembers(key: string): Promise<string[]>\n    /** Optional Lua EVAL for cross-process atomic RMW on assignments; targets ioredis positional shape. */\n    eval?(script: string, numkeys: number, ...keysAndArgs: string[]): Promise<unknown>\n  }\n\n  /** Describes the configuration required to construct a {@link IamRedisAdapter}. */\n  export interface IConfig<TClient extends ILike = ILike> {\n    /** Provides the IamRedis client instance (ioredis, node-redis v4+, or compatible). */\n    client: TClient\n    /** Optional key prefix that namespaces every duck-iam key. */\n    keyPrefix?: string\n    /**\n     * Invoked when a stored row fails JSON parse or shape validation. The\n     * malformed row is dropped from the result set; the rest are returned\n     * intact. Wire this to your alerting pipeline so corrupt rows do not\n     * silently vanish from authorization decisions.\n     */\n    onPolicyError?: (err: Error, ctx: { adapter: 'redis'; rowId: string }) => void\n  }\n}\n\n/**\n * IamRedis-backed adapter using hashes + sets. Layout (with `keyPrefix`):\n * `${p}policies` (hash), `${p}roles` (hash), `${p}assignments:${id}` (set: `roleId\\x00scope`), `${p}attrs:${id}` (JSON).\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TRole - Constrains valid role strings.\n * @template TScope - Constrains valid scope strings.\n */\nexport class IamRedisAdapter<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n  TClient extends IamRedis.ILike = IamRedis.ILike,\n> implements IamAdapter.IAdapter<TAction, TResource, TRole, TScope>\n{\n  private _client: TClient\n  private _prefix: string\n  private _onPolicyError?: (err: Error, ctx: { adapter: 'redis'; rowId: string }) => void\n  /**\n   * Per-assignment-key serialisation. Read-modify-write on the legacy\n   * migration path can race with concurrent `revokeRole` and resurrect a\n   * just-deleted assignment. Without `EVAL`/`MULTI` in the minimal\n   * `IamRedis.ILike` interface, the soundest in-process fix is to serialise\n   * writes against a single assignments key behind a chained promise.\n   * Cross-process races remain - operators running multiple writer\n   * processes should rely on the Lua `eval` path when available.\n   */\n  private _assignmentWriteLocks = new Map<string, Promise<unknown>>()\n\n  /**\n   * Creates a new IamRedis-backed adapter.\n   *\n   * @param config - Provides the client and optional key prefix.\n   */\n  constructor(config: IamRedis.IConfig<TClient>) {\n    this._client = config.client\n    this._prefix = config.keyPrefix ?? ''\n    this._onPolicyError = config.onPolicyError\n  }\n\n  /**\n   * Parse + validate a stored JSON blob. Returns `null` on parse error or\n   * shape mismatch and routes the failure through `onPolicyError` (or the\n   * console as a last resort) so the malformed row never reaches the engine.\n   */\n  private _safeParsePolicy(raw: string, rowId: string): AccessControl.IPolicy<TAction, TResource, TRole> | null {\n    let parsed: unknown\n    try {\n      parsed = JSON.parse(raw)\n    } catch (err) {\n      this._reportPolicyError(err instanceof Error ? err : new Error(String(err)), rowId)\n      return null\n    }\n    const policy = parsePolicyRow<TAction, TResource, TRole>(parsed)\n    if (policy === null) {\n      const issues = validatePolicy(parsed)\n        .issues.map((i) => i.message)\n        .join('; ')\n      this._reportPolicyError(new Error(`Invalid policy \"${rowId}\": ${issues}`), rowId)\n      return null\n    }\n    return policy\n  }\n\n  private _safeParseRole(raw: string, rowId: string): AccessControl.IRole<TAction, TResource, TRole, TScope> | null {\n    let parsed: unknown\n    try {\n      parsed = JSON.parse(raw)\n    } catch (err) {\n      this._reportPolicyError(err instanceof Error ? err : new Error(String(err)), rowId)\n      return null\n    }\n    const role = parseRoleRow<TAction, TResource, TRole, TScope>(parsed)\n    if (role === null) {\n      const issues = validateRole(parsed)\n        .issues.map((i) => i.message)\n        .join('; ')\n      this._reportPolicyError(new Error(`Invalid role \"${rowId}\": ${issues}`), rowId)\n      return null\n    }\n    return role\n  }\n\n  private _reportPolicyError(err: Error, rowId: string): void {\n    if (this._onPolicyError) {\n      this._onPolicyError(err, { adapter: 'redis', rowId })\n      return\n    }\n    // eslint-disable-next-line no-console\n    console.warn(`[@gentleduck/iam:redis] dropped malformed row \"${rowId}\": ${err.message}`)\n  }\n\n  // -- key helpers --\n\n  private _policiesKey(): string {\n    return `${this._prefix}policies`\n  }\n  private _rolesKey(): string {\n    return `${this._prefix}roles`\n  }\n  private _assignmentsKey(subjectId: string): string {\n    return `${this._prefix}assignments:${subjectId}`\n  }\n  private _attrsKey(subjectId: string): string {\n    return `${this._prefix}attrs:${subjectId}`\n  }\n\n  /**\n   * Separator between role and scope in an encoded assignment set member.\n   *\n   * NUL (`0x00`) is rejected at encode time so it cannot appear in any\n   * `TRole` / `TScope` string, preventing decode collisions that would drift\n   * privileges (e.g. a space-separated `'admin user'` round-tripping as\n   * `('admin', 'user ')`).\n   */\n  private static readonly _SEP = '\\0'\n\n  /**\n   * Detects entries written by versions of this adapter that used a literal\n   * space as separator. Format: exactly one `0x20`, no `0x00` byte. On read,\n   * such entries are transparently re-encoded with the NUL separator and the\n   * legacy form is removed from the set.\n   *\n   * False positives are scoped to subjects whose role/scope strings happened\n   * to contain spaces - exactly the cases that were silently broken before -\n   * so the migration corrects rather than corrupts them.\n   */\n  private _isLegacyEncoded(member: string): boolean {\n    if (member.includes(IamRedisAdapter._SEP)) return false\n    const first = member.indexOf(' ')\n    if (first === -1) return false\n    return member.indexOf(' ', first + 1) === -1\n  }\n\n  private _encodeAssignment(roleId: TRole, scope?: TScope | null): string {\n    const r = roleId as string\n    const s = (scope ?? '') as string\n    if (r.includes(IamRedisAdapter._SEP) || s.includes(IamRedisAdapter._SEP)) {\n      throw new Error('[@gentleduck/iam:redis] role / scope must not contain NUL bytes')\n    }\n    return `${r}${IamRedisAdapter._SEP}${s}`\n  }\n  private _decodeAssignment(member: string): { role: TRole; scope?: TScope } {\n    const sep = member.indexOf(IamRedisAdapter._SEP)\n    if (sep === -1) {\n      // Legacy-format fallback: older versions used a space separator.\n      // Accept exactly-one-space entries here; the caller\n      // (`_migrateLegacyAssignment`) re-encodes them on first read.\n      if (this._isLegacyEncoded(member)) {\n        const legacySep = member.indexOf(' ')\n        const role = member.slice(0, legacySep) as TRole\n        const scope = member.slice(legacySep + 1)\n        return scope === '' ? { role } : { role, scope: scope as TScope }\n      }\n      return { role: member as TRole }\n    }\n    const role = member.slice(0, sep) as TRole\n    const scope = member.slice(sep + 1)\n    return scope === '' ? { role } : { role, scope: scope as TScope }\n  }\n\n  /**\n   * Serialise an async task against a specific assignments key. Chains onto\n   * any in-flight task for the same key so concurrent callers see a strict\n   * happens-before order, defeating the migrate-vs-revoke race in a\n   * single-process deployment.\n   */\n  private _runSerialised<T>(key: string, task: () => Promise<T>): Promise<T> {\n    const prev = this._assignmentWriteLocks.get(key) ?? Promise.resolve()\n    const next = prev.then(task, task)\n    // Tail swallows the rejection only on the chain copy; caller owns `next`'s.\n    const tail = next.then(\n      () => undefined,\n      () => undefined,\n    )\n    const settled = tail.finally(() => {\n      if (this._assignmentWriteLocks.get(key) === settled) this._assignmentWriteLocks.delete(key)\n    })\n    this._assignmentWriteLocks.set(key, settled)\n    return next\n  }\n\n  /**\n   * One-shot migration: convert any legacy space-separated assignment members\n   * for `subjectId` to the NUL-separated form. Idempotent and best-effort -\n   * a migration failure must not block authorization, so any errors are\n   * surfaced through `_reportPolicyError` and the original entries left in\n   * place to be retried on the next read.\n   */\n  private async _migrateLegacyAssignment(subjectId: string, members: string[]): Promise<void> {\n    const legacy = members.filter((m) => this._isLegacyEncoded(m))\n    if (legacy.length === 0) return\n    const key = this._assignmentsKey(subjectId)\n    // Prefer Lua EVAL for true atomicity (cross-process safe).\n    // Falls back to in-process serialisation when client lacks eval.\n    if (typeof this._client.eval === 'function') {\n      await this._migrateLegacyAssignmentLua(key, subjectId, legacy)\n      return\n    }\n    // Single-process serialisation fallback.\n    await this._runSerialised(key, async () => {\n      try {\n        const current = await this._client.smembers(key)\n        const stillLegacy = current.filter((m) => this._isLegacyEncoded(m))\n        if (stillLegacy.length === 0) return\n        const reEncoded: string[] = []\n        for (const m of stillLegacy) {\n          const decoded = this._decodeAssignment(m)\n          reEncoded.push(this._encodeAssignment(decoded.role, decoded.scope))\n        }\n        await this._client.sadd(key, ...reEncoded)\n        await this._client.srem(key, ...stillLegacy)\n      } catch (err) {\n        this._reportPolicyError(err instanceof Error ? err : new Error(String(err)), `assignments:${subjectId}`)\n      }\n    })\n  }\n\n  /** Cross-process atomic legacy-assignment migration via IamRedis EVAL; ARGV pairs `[migrated, legacy]`. */\n  private static readonly _MIGRATE_LUA = `\n    local key = KEYS[1]\n    for i = 1, #ARGV, 2 do\n      local migrated = ARGV[i]\n      local legacy = ARGV[i + 1]\n      redis.call('SADD', key, migrated)\n      redis.call('SREM', key, legacy)\n    end\n    return 'OK'\n  `\n\n  private async _migrateLegacyAssignmentLua(key: string, subjectId: string, legacy: string[]): Promise<void> {\n    try {\n      const args: string[] = []\n      for (const m of legacy) {\n        const decoded = this._decodeAssignment(m)\n        args.push(this._encodeAssignment(decoded.role, decoded.scope))\n        args.push(m)\n      }\n      const evalFn = this._client.eval\n      if (!evalFn) return\n      await evalFn.call(this._client, IamRedisAdapter._MIGRATE_LUA, 1, key, ...args)\n    } catch (err) {\n      this._reportPolicyError(err instanceof Error ? err : new Error(String(err)), `assignments:${subjectId}`)\n    }\n  }\n\n  /**\n   * Lists every policy stored in the IamRedis hash.\n   *\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns All policies decoded from the `policies` hash.\n   */\n  async listPolicies(_opts?: IamAdapter.IReadOptions): Promise<AccessControl.IPolicy<TAction, TResource, TRole>[]> {\n    const entries = await this._client.hgetall(this._policiesKey())\n    const out: AccessControl.IPolicy<TAction, TResource, TRole>[] = []\n    for (const [rowId, raw] of Object.entries(entries)) {\n      const parsed = this._safeParsePolicy(raw, rowId)\n      if (parsed) out.push(parsed)\n    }\n    return out\n  }\n\n  /**\n   * Fetches a single policy by ID.\n   *\n   * @param id - Identifies the policy to look up.\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns The matching policy or `null` when absent.\n   */\n  async getPolicy(\n    id: string,\n    _opts?: IamAdapter.IReadOptions,\n  ): Promise<AccessControl.IPolicy<TAction, TResource, TRole> | null> {\n    const value = await this._client.hget(this._policiesKey(), id)\n    return value ? this._safeParsePolicy(value, id) : null\n  }\n\n  /**\n   * Stores or overwrites a policy under its ID.\n   *\n   * @param p - Provides the policy to persist.\n   * @returns Resolves once the HSET completes.\n   */\n  async savePolicy(p: AccessControl.IPolicy<TAction, TResource, TRole>): Promise<void> {\n    await this._client.hset(this._policiesKey(), p.id, JSON.stringify(p))\n  }\n\n  /**\n   * Removes a policy by ID.\n   *\n   * @param id - Identifies the policy to delete.\n   * @returns Resolves once the HDEL completes.\n   */\n  async deletePolicy(id: string): Promise<void> {\n    await this._client.hdel(this._policiesKey(), id)\n  }\n\n  /**\n   * Lists every role stored in the IamRedis hash.\n   *\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns All roles decoded from the `roles` hash.\n   */\n  async listRoles(_opts?: IamAdapter.IReadOptions): Promise<AccessControl.IRole<TAction, TResource, TRole, TScope>[]> {\n    const entries = await this._client.hgetall(this._rolesKey())\n    const out: AccessControl.IRole<TAction, TResource, TRole, TScope>[] = []\n    for (const [rowId, raw] of Object.entries(entries)) {\n      const parsed = this._safeParseRole(raw, rowId)\n      if (parsed) out.push(parsed)\n    }\n    return out\n  }\n\n  /**\n   * Fetches a single role by ID.\n   *\n   * @param id - Identifies the role to look up.\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns The matching role or `null` when absent.\n   */\n  async getRole(\n    id: string,\n    _opts?: IamAdapter.IReadOptions,\n  ): Promise<AccessControl.IRole<TAction, TResource, TRole, TScope> | null> {\n    const value = await this._client.hget(this._rolesKey(), id)\n    return value ? this._safeParseRole(value, id) : null\n  }\n\n  /**\n   * Stores or overwrites a role under its ID.\n   *\n   * @param r - Provides the role to persist.\n   * @returns Resolves once the HSET completes.\n   */\n  async saveRole(r: AccessControl.IRole<TAction, TResource, TRole, TScope>): Promise<void> {\n    await this._client.hset(this._rolesKey(), r.id, JSON.stringify(r))\n  }\n\n  /**\n   * Removes a role by ID.\n   *\n   * @param id - Identifies the role to delete.\n   * @returns Resolves once the HDEL completes.\n   */\n  async deleteRole(id: string): Promise<void> {\n    await this._client.hdel(this._rolesKey(), id)\n  }\n\n  /**\n   * Lists deduplicated role IDs assigned to a subject.\n   *\n   * @param subjectId - Identifies the subject whose roles are read.\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns Deduplicated array of role IDs.\n   */\n  async getSubjectRoles(subjectId: string, _opts?: IamAdapter.IReadOptions): Promise<TRole[]> {\n    const members = await this._client.smembers(this._assignmentsKey(subjectId))\n    const roles = new Set<TRole>()\n    for (const m of members) {\n      const decoded = this._decodeAssignment(m)\n      // Contract: unscoped (global) roles only - same as file/memory\n      // adapters. Scoped assignments are surfaced via getSubjectScopedRoles.\n      if (decoded.scope !== undefined) continue\n      roles.add(decoded.role)\n    }\n    await this._migrateLegacyAssignment(subjectId, members)\n    return Array.from(roles)\n  }\n\n  /**\n   * Lists scoped role assignments for a subject (excludes unscoped).\n   *\n   * @param subjectId - Identifies the subject whose scoped roles are read.\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns Array of `(role, scope)` pairs.\n   */\n  async getSubjectScopedRoles(\n    subjectId: string,\n    _opts?: IamAdapter.IReadOptions,\n  ): Promise<IamRequest.IScopedRole<TRole, TScope>[]> {\n    const members = await this._client.smembers(this._assignmentsKey(subjectId))\n    const out: IamRequest.IScopedRole<TRole, TScope>[] = []\n    for (const m of members) {\n      const decoded = this._decodeAssignment(m)\n      if (decoded.scope !== undefined) out.push({ role: decoded.role, scope: decoded.scope })\n    }\n    await this._migrateLegacyAssignment(subjectId, members)\n    return out\n  }\n\n  /**\n   * Grants a role to a subject, optionally restricted to a scope.\n   *\n   * Idempotent thanks to IamRedis set semantics.\n   *\n   * @param subjectId - Identifies the subject receiving the role.\n   * @param roleId - Specifies the role being granted.\n   * @param scope - Optional scope binding the assignment.\n   * @returns Resolves once the SADD completes.\n   */\n  async assignRole(subjectId: string, roleId: TRole, scope?: TScope): Promise<void> {\n    const key = this._assignmentsKey(subjectId)\n    // Serialise against the migration path.\n    await this._runSerialised(key, () => this._client.sadd(key, this._encodeAssignment(roleId, scope)))\n  }\n\n  /**\n   * Removes role assignments matching the given filters.\n   *\n   * Omitting `scope` removes every assignment for the role regardless of scope.\n   *\n   * @param subjectId - Identifies the subject losing the role.\n   * @param roleId - Specifies the role being revoked.\n   * @param scope - Optional scope filter to narrow the delete.\n   * @returns Resolves once the SREM completes.\n   */\n  async revokeRole(subjectId: string, roleId: TRole, scope?: TScope): Promise<void> {\n    const key = this._assignmentsKey(subjectId)\n    // Serialise against migration so a racing _migrateLegacyAssignment\n    // cannot SADD the migrated form after our SREM lands.\n    await this._runSerialised(key, async () => {\n      if (scope !== undefined) {\n        // Cover BOTH encodings so a partially-migrated set is cleaned in one go.\n        const migrated = this._encodeAssignment(roleId, scope)\n        const legacy = `${roleId} ${scope}`\n        await this._client.srem(key, migrated, legacy)\n        return\n      }\n      const members = await this._client.smembers(key)\n      const targets = members.filter((m) => this._decodeAssignment(m).role === roleId)\n      if (targets.length > 0) {\n        await this._client.srem(key, ...targets)\n      }\n    })\n  }\n\n  /**\n   * Fetches the attribute bag stored for a subject.\n   *\n   * @param subjectId - Identifies the subject whose attributes are read.\n   * @param _opts - Ignored read options accepted for interface compatibility.\n   * @returns The subject's attributes or `{}` when none are recorded.\n   */\n  async getSubjectAttributes(subjectId: string, _opts?: IamAdapter.IReadOptions): Promise<IamPrimitives.Attributes> {\n    const value = await this._client.get(this._attrsKey(subjectId))\n    if (!value) return {}\n    let parsed: unknown\n    try {\n      parsed = JSON.parse(value)\n    } catch (err) {\n      // Corruption != empty; returning {} would silently strip ABAC and flip allow->deny.\n      this._reportPolicyError(err instanceof Error ? err : new Error(String(err)), subjectId)\n      throw new Error(`[@gentleduck/iam:redis] corrupted attributes for \"${subjectId}\" (JSON parse failed)`)\n    }\n    if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n      this._reportPolicyError(new Error(`Attributes for \"${subjectId}\" must be a JSON object`), subjectId)\n      throw new Error(`[@gentleduck/iam:redis] corrupted attributes for \"${subjectId}\" (not a JSON object)`)\n    }\n    return parsed as IamPrimitives.Attributes\n  }\n\n  /**\n   * Shallow-merges new attributes into the subject's existing bag.\n   *\n   * @param subjectId - Identifies the subject whose attributes are written.\n   * @param attrs - Provides the partial attribute patch to merge in.\n   * @returns Resolves once the SET completes.\n   */\n  async setSubjectAttributes(subjectId: string, attrs: IamPrimitives.Attributes): Promise<void> {\n    // Admin write path is the one place a corrupt existing blob must NOT\n    // lock the operator out of recovering. Treat corrupt as `{}` and log\n    // through _reportPolicyError so the operator still sees the signal.\n    let existing: IamPrimitives.Attributes\n    try {\n      existing = await this.getSubjectAttributes(subjectId)\n    } catch (err) {\n      this._reportPolicyError(err instanceof Error ? err : new Error(String(err)), subjectId)\n      existing = {}\n    }\n    const merged = { ...existing, ...attrs }\n    await this._client.set(this._attrsKey(subjectId), JSON.stringify(merged))\n  }\n}\n"],"mappings":"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"}