Cloudflare Workers + D1 port — full plan implemented (Phases 1-6)#46
Draft
Copilot wants to merge 4 commits into
Draft
Cloudflare Workers + D1 port — full plan implemented (Phases 1-6)#46Copilot wants to merge 4 commits into
Copilot wants to merge 4 commits into
Conversation
…ma, decoupled seed metadata Agent-Logs-Url: https://github.com/mieweb/FHIRTogether/sessions/2252d845-5e27-4169-a851-baa9fbb2b5f4 Co-authored-by: wreiske <6295044+wreiske@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
wreiske
May 20, 2026 14:56
View session
Member
|
@copilot finish this out. full plan implemented. |
Agent-Logs-Url: https://github.com/mieweb/FHIRTogether/sessions/cedf39e3-9357-4125-9e1d-a91fd3a4849c Co-authored-by: wreiske <6295044+wreiske@users.noreply.github.com>
…mport for Node fallback Agent-Logs-Url: https://github.com/mieweb/FHIRTogether/sessions/cedf39e3-9357-4125-9e1d-a91fd3a4849c Co-authored-by: wreiske <6295044+wreiske@users.noreply.github.com>
Contributor
Author
Done — all 6 phases are now implemented in commits fe0c569 (Phase 1) → 87098da (Phases 2-6) → 91dd312 (review fixes). What landed:
Validation: 120/120 unit tests pass (104 existing + 16 new), |
Agent-Logs-Url: https://github.com/mieweb/FHIRTogether/sessions/cedf39e3-9357-4125-9e1d-a91fd3a4849c Co-authored-by: wreiske <6295044+wreiske@users.noreply.github.com>
Copilot
AI
changed the title
Phase 1: refactor storage seam for Cloudflare Workers + D1 port
Cloudflare Workers + D1 port — full plan implemented (Phases 1-6)
May 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the Cloudflare Workers + D1 port plan in full. The repo now supports a dual-deployment topology — Node + Fastify + SQLite (full feature set) and Cloudflare Workers + Hono + D1 (REST + HL7-over-HTTP + cron) — both sharing one
FhirStoreinterface and one SQL schema.Phase 1 — Storage seam refactor
src/store/index.tsexposescreateStore(backend, options)— the only place concreteFhirStoreimplementations are imported. Uses dynamicimport()so the nativebetter-sqlite3path is not pulled into the Workers bundle.src/server.tscallscreateStore(STORE_BACKEND, …)instead ofnew SqliteStore().SqliteStore.initialize()intomigrations/0001_initial.sql— single source of truth for both SQLite (read at startup) and D1 (wrangler d1 migrations apply). The in-place v2→v3 / v4→v5 column migrations stay inSqliteStoresince they only matter for upgrading existing Node databases.SqliteStoreintosrc/examples/seedMetadata.ts. The store no longer touchesfs— the date offset is injected via a newdateOffsetProviderconstructor option.src/config.tsadds aloadConfig(env)shim that reads eitherprocess.env(Node) or a Workersenvbinding.Phase 2 — D1 backend
src/store/d1Store.ts(~1,100 lines) — full async port ofSqliteStoreagainst aD1Databasebinding.better-sqlite3's synchronous API (stmt.get/all/run) becomes D1's async API (stmt.bind(...).first/all/run); transactions becomedb.batch([...]).initialize()is a read-only schema-version check — D1 migrations run out-of-band viawrangler d1 migrations apply.src/util/hash.ts— cross-runtime crypto shim:sha256Hex/randomHex/timingSafeEqualHexusing WebCrypto on Workers and a dynamicimport('node:crypto')fallback on Node. Replacescrypto.createHash/crypto.randomBytes/crypto.timingSafeEqual.src/__tests__/d1Store.test.ts+src/__tests__/fakeD1.ts— shared contract test suite runningD1Storeagainst an in-memory SQLite wrapped in a D1-shaped adapter (no Miniflare required in CI). Covers system CRUD, MSH find-or-create, schedule/slot lifecycle, slot holds, HL7 log.src/__tests__/hash.test.ts— verifies the WebCrypto shim matches Nodecryptobyte-for-byte.Phase 3 — Workers entry point
src/worker.tsexportsfetchandscheduledhandlers, using Hono as a thin router (per the plan's option (b) — thin route adapter rather than fighting Fastify-on-Workers). Implements/Schedule,/Slot,/Appointment(GET list, GET by id, POST create),/System,/Location,/health,/,/_schema. FHIR search responses use the properBundleshape via amakeBundle()helper.hono(^4.12.18) as a runtime dep;wrangler(^3.114.17, vuln-free patched line) and@cloudflare/workers-typesas devDeps so Node devs are unaffected.Phase 4 — Cron Triggers
scheduledhandler runs all three idempotent maintenance jobs (cleanupExpiredHolds,evaporateExpiredSystems,cleanupHL7MessageLog) on every cron tick — the cron expression inwrangler.tomlcontrols frequency, not which jobs run. Default is hourly; bump if tighter expired-hold latency is needed. Work is wrapped inctx.waitUntil(...)so the cron tick can return promptly. Same store methods the NodesetIntervaljobs use — no duplication.Phase 5 — MLLP deployment story
/hl7/siuendpoint.src/worker.tsdeliberately does not importsrc/hl7/socket.ts. The hybrid option (companion VM forwards MLLP →/hl7/siu) is documented as an alternative.Phase 6 —
wrangler.toml+ deploywrangler.tomlconfigures the D1 binding (asenv.DB), non-secret config via[vars], hourly cron trigger,nodejs_compat, and observability. Secrets are set viawrangler secret put.deploy:worker,dev:worker,d1:migrate:local,d1:migrate:remote.docs/CLOUDFLARE_WORKERS.mdrewritten as a completion + Quick Start reference.Security notes
randomHex(16)= 128 bits of entropy fromglobalThis.crypto.getRandomValues.sha256Hexis used only for matching high-entropy random API keys (not user passwords) — same pattern as the existingSqliteStore. An inline comment triages CodeQL'sjs/insufficient-password-hashfalse positive..bind(...)).Adding a new backend after this PR
FhirStorefromsrc/types/fhir.ts.caseto the switch insrc/store/index.ts.