Skip to content

feat: add local-first offline reads - #54

Merged
rdlabo merged 1 commit into
mainfrom
agent/offline-local-first-reads
Aug 11, 2026
Merged

feat: add local-first offline reads#54
rdlabo merged 1 commit into
mainfrom
agent/offline-local-first-reads

Conversation

@rdlabo

@rdlabo rdlabo commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add an opt-in local-first stale-while-revalidate read strategy
  • start local replica reads and remote transport concurrently while preserving local-then-remote emission order
  • keep transport, projection, fallback, and cancellation error boundaries explicit

Compatibility

  • existing read policies remain network-first by default
  • mutation handling is unchanged
  • consumers must remain subscribed for remote revalidation; firstValueFrom/take(1) intentionally cancel it

Verification

  • Kit full test suite: 708 passed
  • focused interceptor tests: 28 passed
  • Kit lint passed
  • diff-check passed
  • manager review: APPROVE
  • independent acceptance review: APPROVE

Open in Devin Review

@netlify

netlify Bot commented Aug 10, 2026

Copy link
Copy Markdown

Deploy Preview for rdlabo-ionic-angular-library ready!

Name Link
🔨 Latest commit 0887346
🔍 Latest deploy log https://app.netlify.com/projects/rdlabo-ionic-angular-library/deploys/6a7a6554da0a5900082e74f1
😎 Deploy Preview https://deploy-preview-54--rdlabo-ionic-angular-library.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +113 to +123
return defer(() =>
from(plan.readLocal()).pipe(
catchError((localError: unknown) => {
errorHandler.handleError(localError);
return of(null);
}),
),
).pipe(
concatMap((local) => (local ? tryProjectLocal(local, plan, errorHandler) : of(null))),
take(1),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 ローカルキャッシュ読み取りが同期的に失敗するとGETリクエスト全体が失敗する

ローカル読み取りの失敗を受け止める処理が実行される前に、読み取り呼び出し自体が即座に失敗し得る形で書かれている(defer(() => from(plan.readLocal()).pipe(catchError(...))) at projects/kit/offline/src/lib/offline.interceptor.ts:113-119)ため、ネットワークが正常でも取得が丸ごとエラーになる。
Impact: ローカル保存領域の初期化不良などで読み取りが即座に失敗した場合、オンラインでも画面がデータを取得できずエラー表示になる。

同期throwがcatchErrorの外側で発生する仕組み

readLocal()Promise を返す型だが、async でない実装(例: readLocal() { return this.db.query(...) }this.db が未初期化)では同期的に例外を投げ得る。その場合、例外は defer のファクトリ内で発生するため、ファクトリが返す予定だった observable に付いている catchError は生成されず、エラーは resolveLocalAttempt からそのまま外側へ伝播し、readLocalFirst の selector がエラー終了して transport も解約される。

対照的に、network-first 側の OfflineRequestFallbackService.handleprojects/kit/offline/src/lib/offline.interceptor.ts:212-216)では catchErrordefer の外側に置かれているため同期throwも捕捉され、ErrorHandler へ報告した上で元のHTTPエラーへフォールバックする。local-first だけ挙動が非対称になっている。

Suggested change
return defer(() =>
from(plan.readLocal()).pipe(
catchError((localError: unknown) => {
errorHandler.handleError(localError);
return of(null);
}),
),
).pipe(
concatMap((local) => (local ? tryProjectLocal(local, plan, errorHandler) : of(null))),
take(1),
);
return defer(() => from(plan.readLocal()))
.pipe(
catchError((localError: unknown) => {
errorHandler.handleError(localError);
return of(null);
}),
)
.pipe(
concatMap((local) => (local ? tryProjectLocal(local, plan, errorHandler) : of(null))),
take(1),
);
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@rdlabo
rdlabo merged commit 3b6da35 into main Aug 11, 2026
12 checks passed
@rdlabo
rdlabo deleted the agent/offline-local-first-reads branch August 11, 2026 00:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant