Skip to content

refactor: extract storage identity helpers#171

Closed
ndycode wants to merge 7 commits intorefactor/pr3-storage-error-helpersfrom
refactor/pr3-storage-identity-helpers
Closed

refactor: extract storage identity helpers#171
ndycode wants to merge 7 commits intorefactor/pr3-storage-error-helpersfrom
refactor/pr3-storage-identity-helpers

Conversation

@ndycode
Copy link
Owner

@ndycode ndycode commented Mar 21, 2026

Summary

  • extract storage identity normalization helpers out of lib/storage.ts into a dedicated helper module
  • keep the existing matching and deduplication behavior intact while continuing the storage split in small slices

What Changed

  • added lib/storage/identity.ts with account identity normalization and key-building helpers
  • updated lib/storage.ts to import and re-export the public identity helpers so the current surface stays intact
  • kept the existing storage matching and deduplication behavior covered by the storage suite

Validation

  • npm run test -- test/storage.test.ts
  • npm run lint
  • npm run typecheck
  • npm run build

Risk and Rollback

  • Risk level: low
  • Rollback plan: revert c8bcf13 to restore the inline identity helper implementation

Additional Notes

  • this continues the storage split after the extracted storage error-hint module in the same isolated worktree and stacked review flow

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

this pr extracts account identity normalization helpers from lib/storage.ts into a dedicated lib/storage/identity.ts module as the next slice of the ongoing storage split. it also adds sha-256 hashing on the refresh-token-only key path (previously raw plaintext) with an inline comment explaining the rationale, and backs it with explicit vitest coverage.

key changes:

  • lib/storage/identity.ts added: normalizeEmailKey, getAccountIdentityKey, toAccountIdentityRef, AccountIdentityRef, and private helpers normalizeAccountIdKey, normalizeRefreshTokenKey, hashRefreshTokenKey
  • lib/storage.ts re-exports getAccountIdentityKey and normalizeEmailKey to keep the existing public surface intact; imports toAccountIdentityRef and AccountIdentityRef for internal use only
  • toStorageError is newly re-exported from storage.ts (previously importable only from storage/error-hints.ts directly); the test import is updated to match
  • the refresh-token-only fallback key format changes from refresh:{plaintext} to refresh:{sha256} — the prior review thread concern about this semantic break is now documented in a code comment and covered by a test that verifies the hash and asserts the raw token does not appear in the output
  • toAccountIdentityRef(null | undefined) and the all-blank getAccountIdentityKey returning undefined paths lack vitest coverage despite the function being newly exported with a null-accepting signature

Confidence Score: 4/5

  • safe to merge — no runtime regressions on any production path; hash format change is documented and tested; one non-blocking coverage gap remains
  • both prior review concerns are now addressed: the hash format change has an inline rationale comment and a dedicated test asserting the raw token does not appear in the key, and the main production path (accountId + email) is unaffected. the only remaining gap is missing vitest coverage for the null/undefined input path of the newly-exported toAccountIdentityRef and the all-blank getAccountIdentityKey returning undefined — neither path can cause a runtime error, making this non-blocking.
  • lib/storage/identity.ts — exported surface (toAccountIdentityRef, AccountIdentityRef) remains broader than the prior-session baseline; intentional by author but worth confirming before landing

Important Files Changed

Filename Overview
lib/storage/identity.ts new module correctly extracts identity normalization and key-building helpers; sha-256 hashing on the refresh-token-only path is documented and tested; toAccountIdentityRef and AccountIdentityRef are exported (public surface expansion already noted in prior thread)
lib/storage.ts cleanly removes inline identity helpers and re-exports getAccountIdentityKey/normalizeEmailKey from identity.ts; also newly re-exports toStorageError (intentional, driven by test consolidation); createHash import retained for unrelated use at line 573
test/storage.test.ts adds solid coverage for all four getAccountIdentityKey branches and normalizeEmailKey; removes leftover TDD stub comments; missing coverage for toAccountIdentityRef(null/undefined) and all-blank key returning undefined

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["caller: getAccountIdentityKey(account)"] --> B["toAccountIdentityRef(account)"]
    B --> C["normalizeAccountIdKey(accountId)"]
    B --> D["normalizeEmailKey(email)"]
    B --> E["normalizeRefreshTokenKey(refreshToken)"]
    C --> F["AccountIdentityRef\n{ accountId, emailKey, refreshToken }"]
    D --> F
    E --> F
    F --> G{accountId AND emailKey?}
    G -- yes --> H["account:{id}::email:{emailKey}"]
    G -- no --> I{accountId only?}
    I -- yes --> J["account:{id}"]
    I -- no --> K{emailKey only?}
    K -- yes --> L["email:{emailKey}"]
    K -- no --> M{refreshToken?}
    M -- yes --> N["hashRefreshTokenKey(refreshToken)"]
    N --> O["refresh:{sha256hash}"]
    M -- no --> P["undefined"]
Loading

Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: test/storage.test.ts
Line: 93-154

Comment:
**missing vitest coverage for newly-exported `toAccountIdentityRef` edge cases**

`toAccountIdentityRef` is now a publicly exported function that explicitly accepts `null | undefined`. the null/undefined branch is never exercised. similarly, `getAccountIdentityKey` returning `undefined` when all three fields are blank is not covered.

given the 80% threshold and this being a newly promoted public API, both paths should have explicit tests:

```
it("returns all-undefined ref for null account", () => {
    // toAccountIdentityRef(null) -> all fields undefined
});

it("returns undefined key when all fields are blank", () => {
    // getAccountIdentityKey({ accountId: " ", email: " ", refreshToken: " " }) -> undefined
});
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (6): Last reviewed commit: "fix: drop unused storage identity import" | Re-trigger Greptile

@chatgpt-codex-connector
Copy link

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

extracted identity normalization into a new module and re-exported selected helpers and an error helper from lib/storage.ts. tests updated to exercise email normalization, identity-key construction, refresh-token hashing, and to import toStorageError via lib/storage.ts (see lib/storage.ts:1, lib/storage/identity.ts:1, test/storage.test.ts:1).

Changes

Cohort / File(s) Summary
identity module
lib/storage/identity.ts
new module implementing AccountIdentityRef plus normalizeEmailKey, toAccountIdentityRef, getAccountIdentityKey. provides trimming/lowercasing rules and tiered key construction (account+email → account → email → refresh:).
storage surface & re-exports
lib/storage.ts
removed local identity helpers and types, re-exports getAccountIdentityKey and normalizeEmailKey from ./storage/identity.js, and re-exports toStorageError (alongside formatStorageErrorHint) from ./storage/error-hints.js. downstream matching logic now imports identity helpers instead of using local impl.
tests
test/storage.test.ts
added describe("account identity keys") tests for email normalization, identity key precedence, and refresh-token SHA-256 hashing. updated storage error hints tests to import toStorageError via ../lib/storage.js.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

concerns to validate:

  • missing regression tests for some extracted helpers: confirm unit coverage specifically for lib/storage/identity.ts edge cases (empty strings, whitespace-only, unusual unicode emails) — test file added but verify coverage granularity (see test/storage.test.ts:1).
  • windows edge cases: normalizeEmailKey lowercases and trims but may need unicode normalization (idna) and consistent normalization for non-ascii domains. verify any downstream serialization to filenames or paths on windows.
  • concurrency risks: toAccountIdentityRef usage patterns should be checked for accidental mutation or caching of returned refs. ensure callers don't rely on mutability or memoize without defensive copies.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed title follows conventional commits format (refactor type, lowercase imperative, 42 chars ≤ 72 limit) and accurately summarizes the main extraction of storage identity helpers.
Description check ✅ Passed PR description is well-structured with summary, changes, validation checklist, risk/rollback plan, and additional notes. Most required template sections are completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/pr3-storage-identity-helpers
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch refactor/pr3-storage-identity-helpers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai bot previously requested changes Mar 21, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/storage/identity.ts`:
- Around line 51-62: getAccountIdentityKey currently embeds the raw refresh
token (via toAccountIdentityRef -> refreshToken branch) which can leak if
logged; change getAccountIdentityKey to never return the raw token: either
remove the refreshToken fallback entirely (preferable) or, if fallback is
required, replace the raw token with a deterministic cryptographic hash (use
Node crypto.createHash('sha256').update(trimmedToken).digest('hex')) before
composing the key; update references to
toAccountIdentityRef/getAccountIdentityKey accordingly. Add Vitest unit tests
for getAccountIdentityKey covering all four branches (accountId+emailKey,
accountId only, emailKey only, refreshToken fallback) and assert the returned
key never contains the raw refresh token (and when hashing is used, assert
expected hash format), and ensure callers (e.g., functions in
oc-chatgpt-import-adapter.ts) do not log the raw token.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c871af2b-dd8d-4adf-a18a-a15518e75e71

📥 Commits

Reviewing files that changed from the base of the PR and between aa77ab7 and c8bcf13.

📒 Files selected for processing (2)
  • lib/storage.ts
  • lib/storage/identity.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (1)
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage.ts
  • lib/storage/identity.ts
🔇 Additional comments (3)
lib/storage/identity.ts (1)

1-49: lgtm on types and normalization helpers.

the three normalize functions and toAccountIdentityRef correctly handle empty/null inputs and trim whitespace. normalizeEmailKey lowercases which preserves case-insensitive dedup behavior in lib/storage.ts.

lib/storage.ts (2)

18-26: clean extraction with backward-compatible re-exports.

the re-exports at lines 18-21 preserve the public api surface so existing consumers of lib/storage.ts continue to work. the .js extension in the import path is correct for esm module resolution.


1164-1166: new options type looks fine.

AccountMatchOptions with the optional allowUniqueAccountIdFallbackWithoutEmail flag is used by findUniqueAccountIdMatchIndex (line 1285) and findMatchingAccountIndex (line 1326). the default behavior remains unchanged since the option defaults to undefined/false.

@ndycode ndycode added the passed label Mar 22, 2026
coderabbitai[bot]
coderabbitai bot previously requested changes Mar 22, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/storage.ts`:
- Around line 25-29: The import normalizeEmailKey is unused and should be
removed from the import list; keep AccountIdentityRef and toAccountIdentityRef
(which already calls normalizeEmailKey internally) and leave the existing
re-export in place. Edit the import statement that currently lists
normalizeEmailKey so it only imports the necessary symbols (AccountIdentityRef,
toAccountIdentityRef), then run the linter/tsc to verify there are no
unused-import errors left.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 83ecdaa2-5050-4d83-9e44-f1c13c02bd5d

📥 Commits

Reviewing files that changed from the base of the PR and between c8bcf13 and da29022.

📒 Files selected for processing (3)
  • lib/storage.ts
  • lib/storage/identity.ts
  • test/storage.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (2)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/storage.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage/identity.ts
  • lib/storage.ts
🔇 Additional comments (7)
lib/storage/identity.ts (2)

38-40: refresh token hashing now prevents leakage — past concern resolved.

the hashRefreshTokenKey function at lib/storage/identity.ts:38-40 uses sha256 to hash the refresh token before embedding it in the identity key at line 67. this addresses the previous review concern about raw token exposure in logs or storage keys.

test coverage at test/storage.test.ts:137-150 validates the hashing behavior and confirms the raw token is not present in the output.

Also applies to: 64-68


1-70: implementation is clean with no logging that could leak tokens or emails.

the module has no log.* calls, so there's no risk of leaking refreshToken or email values. the normalization helpers correctly handle undefined/empty/whitespace-only inputs.

lib/storage.ts (2)

19-23: re-exports preserve the public api surface — good.

barrel re-exports at lib/storage.ts:19-23 maintain backward compatibility for consumers importing getAccountIdentityKey, normalizeEmailKey, toStorageError, and formatStorageErrorHint from lib/storage.js.


1144-1146: local AccountMatchOptions type is correctly scoped.

type remains internal to this module and supports the identity matching logic. no issues.

test/storage.test.ts (3)

1-1: imports updated to use barrel re-exports — good.

test/storage.test.ts:16,22,30 now import getAccountIdentityKey, normalizeEmailKey, and toStorageError from ../lib/storage.js instead of direct paths. this validates the re-export surface and matches the refactor intent.

Also applies to: 16-16, 22-22, 30-30


97-151: test coverage for identity key logic is thorough.

the "account identity keys" block at test/storage.test.ts:97-151 covers:

  • email normalization (trim + lowercase) at lines 98-100
  • undefined/blank email handling at lines 102-105
  • composite key (accountId + email) at lines 107-115
  • accountId-only fallback at lines 117-125
  • email-only fallback at lines 127-135
  • refresh-token sha256 hashing at lines 137-150

line 149 explicitly asserts the raw token is not present in the output — good defensive check against regression.


137-150: refresh-token hashing test computes expected hash independently — deterministic.

test/storage.test.ts:139-141 computes the expected sha256 hash using createHash from node:crypto, then compares against getAccountIdentityKey output. this avoids coupling the test to implementation details while validating correctness.

@ndycode ndycode dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] March 22, 2026 15:43

All review threads are resolved and follow-up commits addressed this stale automated change request.

@ndycode
Copy link
Owner Author

ndycode commented Mar 23, 2026

Closing because this work is now included in main via #318 and #319.

@ndycode ndycode closed this Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant