Skip to content

sync(common-v8): cautious wave1 cherry-pick sync#58

Merged
miccy merged 2797 commits intomainfrom
sync/common-v8-2026-03-01-wave1
Mar 1, 2026
Merged

sync(common-v8): cautious wave1 cherry-pick sync#58
miccy merged 2797 commits intomainfrom
sync/common-v8-2026-03-01-wave1

Conversation

@miccy
Copy link
Contributor

@miccy miccy commented Mar 1, 2026

Summary

Cautious, commit-by-commit upstream/common-v8 sync wave from a history-separated upstream branch (no merge, no unrelated-history merge).

This PR ports low-risk/high-value pieces and explicitly defers high-risk runtime/dependency waves.

Candidate upstream commits and decisions

PORTED

  • e903c47d Add Set shim typings and refactor Abort polyfills
    • Ported as fork-equivalent: added packages/react-native/src/Polyfills.ts and packages/react-native/src/SetPrototypeShimModules.d.ts.
  • a4287569 Improved polyfills for web and React Native runtimes (selective)
    • Ported selective parts in fork-relevant paths:
      • packages/common/src/Polyfills.ts
      • packages/common/test/_browserSetup.ts
      • packages/react-native/test/Polyfills.test.ts
  • 8c4101ac Simplified cleaning (selective)
    • Ported safe root .gitignore patterns only.

SKIPPED

  • edf950f6 VSCode settings only (.vscode/settings.json)
  • c06944fa apps/web docs/minimal playground (not relevant in this fork layout for wave1)

DEFERRED

  • ec61d7fd Expo 55 mega-wave (large lockfile/deps churn)
  • 9364b02c shared sync resource management refactor (runtime-heavy)
  • Equivalent worker bootstrap from a4287569 in web worker was tested and reverted due regression:
    • @evolu/web (webkit) Worker.worker.test.ts failed with Object not disposable
    • deferred to wave2 runtime branch

Fork-compat adjustments included

  • Switched @evolu/common/polyfills imports to @evolu/common (fork export shape).
  • Added explicit set.prototype.* dependencies to packages/react-native/package.json.
  • Tightened Promise polyfill typing for TS 5.9/Node 24+ to keep build green.

Risks and mitigations

  • Risk: polyfill behavior differences across RN/web runtimes.
    • Mitigation: added/ran dedicated Polyfills.test.ts (19 tests).
  • Risk: web worker runtime regression from direct upstream mapping.
    • Mitigation: reverted problematic worker bootstrap equivalent in wave1; keep for wave2 investigation.
  • Risk: cleanup changes accidentally hiding important artifacts.
    • Mitigation: only additive, conservative .gitignore changes; no destructive clean script switch.

Validation (local)

Required gates from plan:

  • bun run build
  • bun run test
  • bun run lint ✅ (Biome warnings only in upstream-derived test style; no errors)
  • bun run coverage:lane:active
  • (cd examples/react-expo && bun run web:export)

Targeted checks:

  • bunx vitest run --project react-native packages/react-native/test/Polyfills.test.ts

Wave2 plan

Next branch: sync/common-v8-2026-03-01-wave2-runtime

  • Tackle deferred runtime refactor (9364b02c)
  • Re-evaluate selected parts of Expo55 wave (ec61d7fd) with explicit lockfile/dependency strategy
  • Revisit web worker polyfill bootstrap with architecture-specific adaptation + focused webkit worker tests

Summary by CodeRabbit

Poznámky k vydání

  • Nové funkce

    • Polyfilly pro React Native (Promise statické metody, AbortController/AbortSignal) veřejně dostupné.
    • Polyfilly pro metody Set prototype.
    • Nová asynchronní pomocná funkce arrayFromAsync.
  • Dokumentace

    • Přepsané tabulky v README a aktualizovaný obrázek.
  • Testy

    • Přidána rozsáhlá sada testů pro polyfily a abort/timeout; přesun některých testů.
  • Chores

    • Aktualizovány položky .gitignore a drobné verze devDependencies.

steida added 30 commits January 31, 2026 22:04
Replace the older MaybeAsync/OldTask APIs with a Task-based runner model across protocol, storage, relay and sync code. Key changes:

- applyProtocolMessageAsClient and applyProtocolMessageAsRelay now return Task and accept a run/deps when executed; call sites updated accordingly.
- Storage.writeMessages signature changed to return a Task; implementations (relay/client) updated to use run(...) and the new Mutex from Task instead of the OldTask mutex.
- Introduced test runner helpers (testCreateRunner) and updated tests to use using run = testCreateRunner() and await run(...) for Task execution. testCreateDeps usage was adjusted where appropriate.
- Replaced createMutexOld and other OldTask usages with createMutex/isAsync where needed and updated related imports and types (Task, Mutex, Awaitable, etc.).
- NodeJS relay bootstrap adjusted to use run.deps for console and to add storage into run deps.

These changes move the codebase to a uniform Task/dependency-runner pattern for better composition and consistent dependency injection; tests and call sites were updated to follow the new API.
Clarify the Lightweight feature description to reference the complete Hello World example size and update the tree-shaking test expectation to reflect the gzip size change (5694 -> 5698). This keeps UI copy accurate and test expectations in sync with current build outputs.
Change TaskDisposableStack APIs so deferred cleanup and adopt release callbacks must be Task<void, never, D> (no domain errors). This tightens type safety to prevent disposal handlers from returning domain errors. Added two tests that assert TypeScript errors if cleanup/release tasks return domain errors, and adjusted a semaphore test to silence console output during the run.
Replace the previous React Native detection with two explicit platform flags: isHermes (Hermes engine present) and isServer (no DOM). Update hasNodeBuffer to exclude Hermes environments instead of the prior React Native check. Add unit tests asserting isServer matches the runtime and that isHermes is false in the test environment.
Adjust expected gzip/raw sizes in packages/common/test/TreeShaking.test.ts to reflect current output: "task-example" gzip 5672 (was 5698), raw 15255 (was 15313); "type-object" gzip 1549 (was 1577), raw 4747 (was 4805). This updates the test snapshot to match the new bundle size measurements.
Rewrite WebSocket implementation to a Task-based API: createWebSocket now returns a Task<WebSocket>, uses Task/Schedule primitives (retry, callback, exponential backoff, jitter, maxDelay), adds shouldRetryOnClose, improves binary handling and docs, and removes legacy retry/old task code. Update tests to use an in-process test WebSocket server (_globalSetup.ts) and the Task runner (createRunner), modernize assertions, and cover various close/terminate/retry scenarios. Update vitest config to exclude additional patterns, add Playwright browser commands (startWsServer/stopWsServer) for browser tests, and tweak parallelism/snapshot settings.
Replace the old runMain pattern with a shutdown-aware createRunner that injects a shutdown promise into deps and installs global signal and error handlers (SIGINT, SIGTERM, SIGHUP, uncaughtException, unhandledRejection). Move Node.js runner wiring into packages/nodejs/src/Task.ts, resolve shutdown via process signals, and ensure listeners are cleaned up on runner dispose. Rename relay APIs: createNodeJsRelayBetterSqliteDeps -> createRelayDeps and createNodeJsRelay -> startRelay, update examples and the apps/relay entry to use startRelay and createRunner. Simplify task typing in packages/common (introduce AnyTask and use it in InferTask* helpers, add CreateRunner type, remove MainTask), and adjust the local-first Relay implementation to handle sqlite errors by logging and returning ok(stack). Update tests to reflect the new createRunner/shutdown behavior and to verify signal/error handling and listener cleanup. README updated to reflect the API rename.
Introduce a browser-specific Task runner and tests. Implements createRunner in packages/web/src/Task.ts that composes the common runner, creates a console child named "global", registers global "error" and "unhandledrejection" handlers (logging via createUnknownError), and removes those listeners when the runner is disposed. Adds vitest tests in packages/web/test/Task.test.ts to verify custom deps merging, mocked listener registration/removal, real-event logging for ErrorEvent and PromiseRejectionEvent, and that handlers are detached after disposal.
@socket-security
Copy link

socket-security bot commented Mar 1, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​better-sqlite3@​7.6.131001007181100
Added@​types/​ws@​8.18.11001007481100
Added@​types/​node@​25.3.31001008196100
Addedws@​8.19.09810010088100
Addedbetter-sqlite3@​12.6.210010010089100

View full report

@socket-security
Copy link

socket-security bot commented Mar 1, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Medium
Deprecated by its maintainer: npm prebuild-install

Reason: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.

From: ?npm/better-sqlite3@12.6.2npm/prebuild-install@7.1.3

ℹ Read more on: This package | This alert | What is a deprecated package?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Research the state of the package and determine if there are non-deprecated versions that can be used, or if it should be replaced with a new, supported solution.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/prebuild-install@7.1.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@miccy
Copy link
Contributor Author

miccy commented Mar 1, 2026

Update:

This addresses the Polyfills review points (Set shim side effects, exports/public entrypoint, abort patch semantics, lint directive removal).

steida added 6 commits March 1, 2026 19:41
Generated by expo-cli
Introduce arrayFromAsync, a small wrapper around Array.fromAsync that returns a ReadonlyArray and awaits promised items from async iterables or iterables of promises. Add tests covering async iterables, promised values in sync iterables, and readonly type assertions. Also update the changeset documentation to list the new function.
Add runtime polyfill packages and wire them into React Native polyfills: add array-from-async, array.prototype.tosorted, promise.try and promise.withresolvers to package.json and import/use their shims in packages/react-native/src/Polyfills.ts (install Array.fromAsync and toSorted, and use withResolvers/promiseTry shims). Remove the example runtime polyfill checker and its UI (examples/react-expo/runtime/polyfillRuntimeCheck.ts and the RuntimeChecks component in examples/react-expo/app/index.tsx) and delete SetPrototypeShimModules.d.ts. Update lockfile entries accordingly.
Refactor React Native Evolu deps so each worker gets its own Run and console store by introducing createWorkerRun; pass a per-worker console into createRun to avoid sharing console/store state across workers. Remove/disable the previous local auth helper (TODO to reimplement) and clean up imports. Also comment out the console level in the example app. Add unit tests (packages/react-native/test/shared.test.ts) to verify isolated runs and console-store entries for shared and DB workers.

This fixes duplicated console.log output.
Bump ESLint and related packages in the root and example package.json files and update packages/common test to match behavior changes. Regenerate bun.lock to reflect the updated dependency tree.
@miccy miccy marked this pull request as ready for review March 1, 2026 20:26
- Move Set shims to explicit idempotent install path\n- Export Polyfills entrypoint via package exports/index\n- Preserve first abort reason and avoid patching native reason runtimes\n- Remove no-op lint suppression
@miccy miccy force-pushed the sync/common-v8-2026-03-01-wave1 branch from 45636be to 81a9d0a Compare March 1, 2026 20:31
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45636bea72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
}

const sources = Array.from(new Set(signals));

Choose a reason for hiding this comment

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

P2 Badge Materialize AbortSignal.any inputs before reusing them

This implementation consumes signals in the aborted-check loop and then iterates it again via Array.from(new Set(signals)); for one-shot iterables (for example, a generator passed from JS), the second pass is empty, so no source listeners are registered and the returned signal never aborts when those inputs abort. That causes incorrect cancellation behavior in runtimes relying on this polyfill.

Useful? React with 👍 / 👎.

Copy link

@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

🧹 Nitpick comments (2)
.gitignore (1)

22-23: Zvažte konsolidaci duplicitních vzorů tsBuildInfo.

Řádky 22 a 23 obsahují dva vzory pro TypeScript build info soubory s různým velikostním rozlišením písmen (*.tsBuildInfo vs *.tsbuildinfo). TypeScript oficiálně generuje soubory s názvem .tsbuildinfo (malá písmena). Na souborových systémech nerozlišujících velikost písmen (macOS/Windows výchozí) jsou tyto vzory redundantní. Pro konzistenci a jednodušší údržbu doporučuji zachovat pouze lowercase verzi *.tsbuildinfo.

🧹 Navrhovaná úprava pro konsolidaci vzorů
-*.tsBuildInfo
 *.tsbuildinfo
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore around lines 22 - 23, V .gitignore jsou duplicitní vzory pro
TypeScript build info (*.tsBuildInfo a *.tsbuildinfo); nechte pouze oficiální
lowercase vzor *.tsbuildinfo a odstraňte nebo zaintepretujte řádek s
*.tsBuildInfo tak, aby zůstával jediný záznam *.tsbuildinfo (tj. upravte položky
odpovídající symbolům "*.tsBuildInfo" a "*.tsbuildinfo" tak, aby zůstal pouze
"*.tsbuildinfo").
packages/react-native/src/SetPrototypeShimModules.d.ts (1)

1-3: Přesuňte SetPrototypeShimModule ze globálního ambient scope.

Soubor .d.ts bez import/export se chová jako globální skript – top-level interface se stane globální deklarací a může kolidovat s ostatními deklaracemi v projektu. Bezpečnější je typ uzavřít přímo do jednotlivých declare module bloků.

♻️ Návrh úpravy
-interface SetPrototypeShimModule {
-  shim: () => void;
-}
-
 declare module "set.prototype.difference" {
-  const difference: SetPrototypeShimModule;
+  interface DifferenceShimModule {
+    shim: () => void;
+  }
+  const difference: DifferenceShimModule;
   export default difference;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-native/src/SetPrototypeShimModules.d.ts` around lines 1 - 3,
Přesuň top-level rozhraní SetPrototypeShimModule z globálního ambientního scope
tím, že ho zabalíte do vhodného modulu nebo jej exportujete; konkrétně upravte
obsah souboru tak, aby místo volně deklarovaného "interface
SetPrototypeShimModule { shim: () => void; }" použil buď "declare module
'react-native' { export interface SetPrototypeShimModule { shim: () => void; }
}" nebo jednoduše "export interface SetPrototypeShimModule { shim: () => void;
}" podle toho, zda má být typ součástí modulu nebo exportovaného API, čímž
odstraníte globální deklaraci a zabráníte kolizím.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/react-native/src/Polyfills.ts`:
- Around line 110-118: The code should add feature-detection for WeakRef before
using it to avoid "WeakRef is not a constructor" in environments that lack it;
keep calling installAbortReasonPolyfill and installAbortSignalStaticMethods for
AbortController/AbortSignal as-is (they are guaranteed in RN 0.84) but guard any
code that constructs or references WeakRef (e.g., where WeakRef is used on/near
the code that eventually throws) with a check like typeof globalThis.WeakRef ===
'function' (or skip/alternate behavior when missing) so
installAbortControllerPolyfills and functions such as
installAbortReasonPolyfill/installAbortSignalStaticMethods do not assume WeakRef
exists.

---

Nitpick comments:
In @.gitignore:
- Around line 22-23: V .gitignore jsou duplicitní vzory pro TypeScript build
info (*.tsBuildInfo a *.tsbuildinfo); nechte pouze oficiální lowercase vzor
*.tsbuildinfo a odstraňte nebo zaintepretujte řádek s *.tsBuildInfo tak, aby
zůstával jediný záznam *.tsbuildinfo (tj. upravte položky odpovídající symbolům
"*.tsBuildInfo" a "*.tsbuildinfo" tak, aby zůstal pouze "*.tsbuildinfo").

In `@packages/react-native/src/SetPrototypeShimModules.d.ts`:
- Around line 1-3: Přesuň top-level rozhraní SetPrototypeShimModule z globálního
ambientního scope tím, že ho zabalíte do vhodného modulu nebo jej exportujete;
konkrétně upravte obsah souboru tak, aby místo volně deklarovaného "interface
SetPrototypeShimModule { shim: () => void; }" použil buď "declare module
'react-native' { export interface SetPrototypeShimModule { shim: () => void; }
}" nebo jednoduše "export interface SetPrototypeShimModule { shim: () => void;
}" podle toho, zda má být typ součástí modulu nebo exportovaného API, čímž
odstraníte globální deklaraci a zabráníte kolizím.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45c6aac and 45636be.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • .gitignore
  • README.md
  • apps/relay/package.json
  • examples/react-nextjs/package.json
  • packages/common/src/Polyfills.ts
  • packages/common/test/_browserSetup.ts
  • packages/nodejs/package.json
  • packages/react-native/package.json
  • packages/react-native/src/Polyfills.ts
  • packages/react-native/src/SetPrototypeShimModules.d.ts
  • packages/react-native/src/index.ts
  • packages/react-native/test/Polyfills.test.ts
  • packages/web/package.json

Comment on lines +110 to +118
const installAbortControllerPolyfills = (): void => {
installAbortReasonPolyfill(
globalThis.AbortController as AbortControllerConstructor,
globalThis.AbortSignal as AbortSignalConstructor,
);
installAbortSignalStaticMethods(
globalThis.AbortController as AbortControllerConstructor,
globalThis.AbortSignal as AbortSignalConstructor,
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the Polyfills.ts file to understand the context
find . -name "Polyfills.ts" -path "*/react-native/*" | head -5

Repository: SQLoot/evolu-plan-b

Length of output: 103


🏁 Script executed:

# Search for web documentation about React Native 0.84 and Hermes support for AbortController, AbortSignal, WeakRef
web_search: React Native 0.84 Hermes AbortController AbortSignal WeakRef support

Repository: SQLoot/evolu-plan-b

Length of output: 114


🏁 Script executed:

# If we find the file, let's examine it
cat -n packages/react-native/src/Polyfills.ts | sed -n '100,130p'

Repository: SQLoot/evolu-plan-b

Length of output: 1189


🏁 Script executed:

# Also check lines 283-286
cat -n packages/react-native/src/Polyfills.ts | sed -n '275,295p'

Repository: SQLoot/evolu-plan-b

Length of output: 725


🌐 Web query:

React Native 0.84 Hermes engine AbortController AbortSignal WeakRef support

💡 Result:

React Native 0.84 + Hermes: support status

AbortController / AbortSignal

  • Supported as globals in React Native (including RN 0.84 on Hermes). React Native documents both AbortController and AbortSignal as available global Web-standard APIs. [1], [2]

WeakRef (and FinalizationRegistry)

  • Not something React Native guarantees/enables as a supported global in RN 0.84.
  • There’s an RN tracking issue noting that Hermes itself supports WeakRef, but React Native historically lacked the integration work to properly enable it (microtask-queue + draining microtasks). [3]
  • RN 0.84’s release post focuses on Hermes V1 being the default, but does not claim WeakRef is enabled/officially supported. [4]

Practical check (what you should do)

In your RN 0.84 app at runtime, verify:

  • typeof AbortController === 'function' (should be true) [1]
  • typeof AbortSignal === 'function' (should be true) [2]
  • typeof WeakRef === 'function' / typeof FinalizationRegistry === 'function' (don’t assume; depends on whether RN/Hermes integration exposes it in your build) [3]

Sources

[1] React Native docs: global AbortControllerhttps://reactnative.dev/docs/global-AbortController
[2] React Native docs: global AbortSignalhttps://reactnative.dev/docs/0.82/global-AbortSignal
[3] React Native GitHub issue: “Hermes WeakRef Support in React Native” (#42742) — facebook/react-native#42742
[4] React Native blog: “React Native 0.84 – Hermes V1 by Default” (Feb 11, 2026) — https://reactnative.dev/blog/2026/02/11/react-native-0.84


🏁 Script executed:

# Check package.json to understand the React Native version requirements
cat packages/react-native/package.json

Repository: SQLoot/evolu-plan-b

Length of output: 4004


🏁 Script executed:

# Search for any existing feature detection or comments about Hermes/API support in the codebase
rg -i "hermes|abortcontroller|weakref" packages/react-native/src --type ts -B 2 -A 2

Repository: SQLoot/evolu-plan-b

Length of output: 6488


🏁 Script executed:

# Check if there are any comments or documentation about target environments
head -50 packages/react-native/src/Polyfills.ts

Repository: SQLoot/evolu-plan-b

Length of output: 1754


Přidejte feature-detekci pro WeakRef; AbortController a AbortSignal jsou v React Native 0.84 garantovány.

V React Native 0.84 s Hermesem jsou AbortController a AbortSignal dostupné jako globální API a nevyžadují detekci. Oproti tomu WeakRef není React Native 0.84 garantován — v prostředí bez jeho podpory dojde k chybě WeakRef is not a constructor na řádku 285.

🛡️ Návrh úpravy
   return {
     add: (controller) => {
-      refs.push(new globalThis.WeakRef(controller));
+      if (typeof globalThis.WeakRef === "function") {
+        refs.push(new globalThis.WeakRef(controller));
+      } else {
+        refs.push({ deref: () => controller });
+      }
       cleanup();
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-native/src/Polyfills.ts` around lines 110 - 118, The code
should add feature-detection for WeakRef before using it to avoid "WeakRef is
not a constructor" in environments that lack it; keep calling
installAbortReasonPolyfill and installAbortSignalStaticMethods for
AbortController/AbortSignal as-is (they are guaranteed in RN 0.84) but guard any
code that constructs or references WeakRef (e.g., where WeakRef is used on/near
the code that eventually throws) with a check like typeof globalThis.WeakRef ===
'function' (or skip/alternate behavior when missing) so
installAbortControllerPolyfills and functions such as
installAbortReasonPolyfill/installAbortSignalStaticMethods do not assume WeakRef
exists.

miccy added 3 commits March 1, 2026 21:37
Source upstream commit: 904c47ea8fc7a464f906dd92435950740fa83ec3

Port scope: full.
Source upstream commit: 012108ce6d886f8a0c6873d6d0f01a6e5d864eb6

Port scope: selective (runtime + tests). Kept example app unchanged due fork divergence; aligned test path casing to shared.test.ts.
Source upstream commit: 58b0fe34f612c7630a6b4953f6df4b2160e93da2

Port scope: no-op in fork (log statement not present after prior divergence).
Copy link

@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.

♻️ Duplicate comments (1)
packages/react-native/src/Polyfills.ts (1)

283-286: ⚠️ Potential issue | 🟠 Major

Doplňte feature-detekci WeakRef před konstrukcí na Line 285.

Na Line 285 se volá new globalThis.WeakRef(controller) bez guardu. V runtime bez WeakRef to rozbije AbortSignal.any při prvním add.

🔧 Návrh úpravy
   return {
     add: (controller) => {
-      refs.push(new globalThis.WeakRef(controller));
+      if (typeof globalThis.WeakRef === "function") {
+        refs.push(new globalThis.WeakRef(controller));
+      } else {
+        refs.push({ deref: () => controller });
+      }
       cleanup();
     },
Is `WeakRef` guaranteed in React Native 0.84 across Hermes and JSC runtimes, or should libraries guard `new WeakRef(...)` with a runtime feature check?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-native/src/Polyfills.ts` around lines 283 - 286, The add
function currently constructs new globalThis.WeakRef(controller) without feature
detection; change it to check typeof globalThis?.WeakRef === 'function' before
calling new WeakRef and, if absent, store the raw controller (or a safe
fallback) into refs so AbortSignal.any won't throw; update the add arrow
function (and any place using refs/cleanup) to handle entries that may be
WeakRef objects or direct controller references and to unwrap accordingly when
cleaning up.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/react-native/src/Polyfills.ts`:
- Around line 283-286: The add function currently constructs new
globalThis.WeakRef(controller) without feature detection; change it to check
typeof globalThis?.WeakRef === 'function' before calling new WeakRef and, if
absent, store the raw controller (or a safe fallback) into refs so
AbortSignal.any won't throw; update the add arrow function (and any place using
refs/cleanup) to handle entries that may be WeakRef objects or direct controller
references and to unwrap accordingly when cleaning up.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45636be and 81a9d0a.

📒 Files selected for processing (3)
  • packages/react-native/package.json
  • packages/react-native/src/Polyfills.ts
  • packages/react-native/src/index.ts

miccy and others added 3 commits March 1, 2026 21:47
## Summary
Wave2 is a cautious runtime-focused sync from `upstream/common-v8` on
top of Wave1.

Scope is intentionally small and excludes ESLint v10.

## PORTED
- `904c47e` Add `arrayFromAsync` and tests
- `012108c` React Native: isolate worker runs and add tests (selective,
fork-compatible)
- `58b0fe3` Remove stray `console.log` of `Symbol.asyncDispose` (no-op
in this fork, tracked explicitly)

## DEFERRED
- `87f87d5` Add array/promise polyfills and remove runtime checks
(Wave2b)
- `0d6d853` Create `.gitignore` (non-blocking, defer with example
cleanup)

## SKIPPED
- `3c0f25c` ESLint v10 upgrade (explicitly out of scope)

## Risks and Mitigations
- RN worker/local-auth flow impact from `012108c`
- Mitigation: targeted RN shared test + full test run + expo web export
- Additive API export mismatch for `arrayFromAsync`
  - Mitigation: build + targeted array tests + full test run
- Lockfile drift
  - Mitigation: no extra dependency bumps added in this wave

## Test Results
- `bun run build` ✅
- `bun run test` ✅
- `bun run lint` ✅ (4 existing warnings in
`packages/react-native/test/Polyfills.test.ts`, no errors)
- `bun run coverage:lane:active` ✅
- `(cd examples/react-expo && bun run web:export)` ✅
- `bunx vitest run --project react-native
packages/react-native/test/shared.test.ts` ✅
- `bunx vitest run packages/common/test/Array.test.ts` ✅

## Notes
- PR is stacked on top of Wave1 (`#58`) and targets
`sync/common-v8-2026-03-01-wave1` as base.
- After Wave1 merge, this PR can be retargeted to `main`.
…nd downgrade `bplist-parser` to `0.3.1`.
Copy link

@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.

🧹 Nitpick comments (2)
packages/react-native/src/shared.ts (1)

88-126: Odstraňte zakomentovaný kód.

Tento blok obsahuje mrtvý kód, který není aktivní a přidává zbytečný šum. Pokud je zamýšlen jako reference pro budoucí implementaci, měl by být přesunut do dokumentace nebo issue trackeru.

🧹 Navrhované odstranění
 export const createSharedLocalAuth = (
   secureStorage: SecureStorage,
 ): LocalAuth =>
   createLocalAuth({
     randomBytes,
     secureStorage,
   });
-
-// import {
-//   createConsole,
-//   createLocalAuth,
-//   createRandomBytes,
-//   type CreateSqliteDriverDep,
-//   type LocalAuth,
-//   type ReloadAppDep,
-//   type SecureStorage,
-// } from "@evolu/common";
-// import type {
-//   // createDbWorkerForPlatform,
-//   // createDbWorkerForPlatform,
-//   EvoluDeps,
-// } from "@evolu/common/local-first";
-//
-// const _console = createConsole();
-// const randomBytes = createRandomBytes();
-//
-// export const createSharedEvoluDeps = (
-//   _deps: CreateSqliteDriverDep & ReloadAppDep,
-// ): EvoluDeps => {
-//   throw new Error("todo");
-// };
-//
-//   ({
-//   ...deps,
-//   console,
-//   sharedWorker: "TODO" as never,
-//   // createDbWorker: () =>
-//   //   createDbWorkerForPlatform({
-//   //     ...deps,
-//   //     console,
-//   //     createWebSocket,
-//   //     random: createRandom(),
-//   //     randomBytes,
-//   //     time: createTime(),
-//   //   }),
-//   randomBytes,
-// });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-native/src/shared.ts` around lines 88 - 126, Remove the large
commented-out block in packages/react-native/src/shared.ts (everything
referencing createConsole, createLocalAuth, createRandomBytes,
CreateSqliteDriverDep, EvoluDeps, createSharedEvoluDeps, _console, randomBytes,
and the commented createDbWorkerForPlatform code); delete these dead comments
and any unused commented identifiers, and if the content is needed as future
reference move it to a documentation file or an issue tracker instead of keeping
it in-source.
packages/react-native/test/shared.test.ts (1)

62-66: Zvažte explicitnější typování místo as never.

Type assertion as never na řádku 66 obchází typovou kontrolu. Pro lepší typovou bezpečnost zvažte vytvoření minimální mock struktury odpovídající požadovanému typu.

♻️ Navrhované zlepšení typování
     const deps = createEvoluDeps({
       reloadApp: vi.fn(),
       createSqliteDriver: vi.fn(),
-      console: { getLevel: () => "debug" },
-    } as never);
+      console: { getLevel: () => "debug" as const },
+    } as Parameters<typeof createEvoluDeps>[0]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-native/test/shared.test.ts` around lines 62 - 66, Replace the
unsafe `as never` cast by declaring a minimal mock object that matches the
dependency interface expected by createEvoluDeps: implement reloadApp (vi.fn()),
createSqliteDriver (vi.fn()), and console with getLevel(): string, and annotate
that object with the actual dependency type (or use Partial<DependencyType> then
cast to the dependency type) before passing it to createEvoluDeps; this removes
the `as never` and preserves type safety for createEvoluDeps, reloadApp,
createSqliteDriver, and console.getLevel.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react-native/src/shared.ts`:
- Around line 88-126: Remove the large commented-out block in
packages/react-native/src/shared.ts (everything referencing createConsole,
createLocalAuth, createRandomBytes, CreateSqliteDriverDep, EvoluDeps,
createSharedEvoluDeps, _console, randomBytes, and the commented
createDbWorkerForPlatform code); delete these dead comments and any unused
commented identifiers, and if the content is needed as future reference move it
to a documentation file or an issue tracker instead of keeping it in-source.

In `@packages/react-native/test/shared.test.ts`:
- Around line 62-66: Replace the unsafe `as never` cast by declaring a minimal
mock object that matches the dependency interface expected by createEvoluDeps:
implement reloadApp (vi.fn()), createSqliteDriver (vi.fn()), and console with
getLevel(): string, and annotate that object with the actual dependency type (or
use Partial<DependencyType> then cast to the dependency type) before passing it
to createEvoluDeps; this removes the `as never` and preserves type safety for
createEvoluDeps, reloadApp, createSqliteDriver, and console.getLevel.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81a9d0a and 8ad8292.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • .changeset/array-module-refactor.md
  • packages/common/src/Array.ts
  • packages/common/test/Array.test.ts
  • packages/react-native/src/shared.ts
  • packages/react-native/test/Shared.test.ts
  • packages/react-native/test/shared.test.ts
💤 Files with no reviewable changes (1)
  • packages/react-native/test/Shared.test.ts

Copy link

@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.

🧹 Nitpick comments (1)
packages/react-native/test/Polyfills.test.ts (1)

47-53: Mock addEventListener ignoruje parametr once.

Mock implementace addEventListener v TestAbortSignal ignoruje options.once. Pokud polyfill implementace používá { once: true } pro automatické odstranění listeneru po prvním zavolání, mock nebude odpovídat reálnému chování a testy mohou dávat falešně pozitivní výsledky.

♻️ Navrhovaná úprava pro podporu `once`
 public addEventListener(
   _type: string,
   listener: FakeAbortListener,
-  _options?: { readonly once?: boolean },
+  options?: { readonly once?: boolean },
 ): void {
-  this.listeners.add(listener);
+  if (options?.once) {
+    const wrappedListener = () => {
+      this.listeners.delete(wrappedListener);
+      listener();
+    };
+    this.listeners.add(wrappedListener);
+  } else {
+    this.listeners.add(listener);
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-native/test/Polyfills.test.ts` around lines 47 - 53, Mock
implementace addEventListener v TestAbortSignal ignoruje options.once; uprav ji
tak, aby při volání addEventListener(type, listener, { once: true }) vytvořila
obalovací funkci, která zavolá původní listener a ihned se sama odstraní z
this.listeners, a ulož mapování mezi původním listenerem a obalovací funkcí
(např. Map<FakeAbortListener, FakeAbortListener>) aby removeEventListener
fungovalo i pro once-listenery; aktualizuj také removeEventListener tak, že při
odstraňování zkontroluje mapu a odstraní případnou obalovací funkci místo (nebo
navíc k) původnímu listeneru.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react-native/test/Polyfills.test.ts`:
- Around line 47-53: Mock implementace addEventListener v TestAbortSignal
ignoruje options.once; uprav ji tak, aby při volání addEventListener(type,
listener, { once: true }) vytvořila obalovací funkci, která zavolá původní
listener a ihned se sama odstraní z this.listeners, a ulož mapování mezi
původním listenerem a obalovací funkcí (např. Map<FakeAbortListener,
FakeAbortListener>) aby removeEventListener fungovalo i pro once-listenery;
aktualizuj také removeEventListener tak, že při odstraňování zkontroluje mapu a
odstraní případnou obalovací funkci místo (nebo navíc k) původnímu listeneru.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ad8292 and 2aacb1a.

📒 Files selected for processing (2)
  • README.md
  • packages/react-native/test/Polyfills.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

@miccy miccy merged commit 0992b14 into main Mar 1, 2026
20 checks passed
@miccy miccy deleted the sync/common-v8-2026-03-01-wave1 branch March 1, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat New feature or request upstream Needs cherry-pick or merge from upstream

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants