@bun-native/react 0.33.0 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  React renderer for Bun Native desktop windows.
4
4
 
5
- Part of [Bun Native](https://github.com/0xPlayerOne/bun-native), an experimental developer preview. Version 0.33.0 was built from [fa8bef754122](https://github.com/0xPlayerOne/bun-native/tree/fa8bef754122621d4235eb06e30bb1085b4f7b1e) and published from GitHub Actions with npm provenance. Use the Bun revision pinned in that commit's toolchain.json. Start with the [getting-started guide](https://github.com/0xPlayerOne/bun-native/blob/fa8bef754122621d4235eb06e30bb1085b4f7b1e/docs/getting-started.md); `bun-native host install` fetches the matching release host. The license notice is in LICENSE.
5
+ Part of [Bun Native](https://github.com/0xPlayerOne/bun-native), an experimental developer preview. Version 0.33.1 was built from [c56fdd675365](https://github.com/0xPlayerOne/bun-native/tree/c56fdd6753653a10144a4167c1d3dfb3c38a0647) and published from GitHub Actions with npm provenance. Use the Bun revision pinned in that commit's toolchain.json. Start with the [getting-started guide](https://github.com/0xPlayerOne/bun-native/blob/c56fdd6753653a10144a4167c1d3dfb3c38a0647/docs/getting-started.md); `bun-native host install` fetches the matching release host. The license notice is in LICENSE.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "commit": "fa8bef754122621d4235eb06e30bb1085b4f7b1e",
4
- "tree": "f805e2eb19a4e10fc29d1dc827cf5bc617b5773b",
3
+ "commit": "c56fdd6753653a10144a4167c1d3dfb3c38a0647",
4
+ "tree": "c6e81b3f9d041e11dd0413b5fdd7a50910383854",
5
5
  "files": {
6
6
  "package.json": {
7
- "bytes": 2164,
8
- "sha256": "db50b6f81f25c113a46f8c9b038bc548a92effaeb4ad9562e5e95044baa7b66f"
7
+ "bytes": 2115,
8
+ "sha256": "caf866d56f3cd7530d5735ecbf62512e9e22526bc44c75a4b48a18c26352c0b8"
9
9
  },
10
10
  "packages/react/package.json": {
11
11
  "bytes": 402,
@@ -32,16 +32,16 @@
32
32
  "sha256": "4458ba8196591d768d0c7d1a333e47fb2902f44b3e848b9fd7289f68092b8502"
33
33
  },
34
34
  "packages/react/src/mount.ts": {
35
- "bytes": 7530,
36
- "sha256": "73b271cb21be8774674a428550cd3cab42c065b6b8286f7c1c914e86fe65addd"
35
+ "bytes": 7595,
36
+ "sha256": "259ed7e24ef6f2943c889deadea3c337e627c3633c514a62dd2c1de781c3097f"
37
37
  },
38
38
  "packages/react/src/reconciler.ts": {
39
- "bytes": 9500,
40
- "sha256": "f29e9099e177009760406e4164bf8b740715d89bf6bc12b450cb4d575880615a"
39
+ "bytes": 8349,
40
+ "sha256": "6036ac4f997f965f0bfbcf73719d6cdf8a59e6706bc9f25e1e3d3db1777e8a6f"
41
41
  },
42
42
  "packages/react/src/root-errors.ts": {
43
- "bytes": 963,
44
- "sha256": "2380b9bcc709010f9f09bef444fda47fdda9f64f1fd22d0a4b351069ce09826a"
43
+ "bytes": 1234,
44
+ "sha256": "963071b9e1086f551fef172ee6d53a78d07aeaa674f6d3bf746f8774fabfe780"
45
45
  },
46
46
  "LICENSE": {
47
47
  "bytes": 1075,
@@ -49,8 +49,8 @@
49
49
  }
50
50
  },
51
51
  "toolchain": {
52
- "bytes": 3012,
53
- "sha256": "00c3f707aae8abe9ca75cd2e162f5e1c7c34362a03675da39afead60d856066a"
52
+ "bytes": 3234,
53
+ "sha256": "9f4d9362cb4cc38c0f4e705295789a0cbfec46ffdd007e0541b9441a7c3003a7"
54
54
  },
55
55
  "dependencyLock": {
56
56
  "bytes": 28357,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bun-native/react",
3
- "version": "0.33.0",
3
+ "version": "0.33.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -19,13 +19,15 @@
19
19
  },
20
20
  "description": "React renderer for Bun Native desktop windows.",
21
21
  "dependencies": {
22
- "@bun-native/core": "0.33.0",
23
- "@bun-native/renderer": "0.33.0",
24
- "react": "19.3.1",
22
+ "@bun-native/core": "0.33.1",
23
+ "@bun-native/renderer": "0.33.1",
25
24
  "react-reconciler": "0.34.0",
26
- "@types/react": "19.3.0",
27
25
  "@types/react-reconciler": "0.34.1"
28
26
  },
27
+ "peerDependencies": {
28
+ "react": "19.3.1",
29
+ "@types/react": "19.3.0"
30
+ },
29
31
  "repository": {
30
32
  "type": "git",
31
33
  "url": "git+https://github.com/0xPlayerOne/bun-native.git",
package/src/mount.ts CHANGED
@@ -2,7 +2,7 @@ import { NativeStateError, type NativeWindow, type Style } from '@bun-native/cor
2
2
  import { createRenderRoot, type RenderOptions, type RenderRoot } from '@bun-native/renderer'
3
3
  import type { ErrorInfo, ReactNode } from 'react'
4
4
  import { ConcurrentRoot } from 'react-reconciler/constants'
5
- import { reconciler } from './reconciler.ts'
5
+ import { dispatchDiscrete, reconciler } from './reconciler.ts'
6
6
  import { createRootErrors } from './root-errors.ts'
7
7
  import { createCommitBarrier } from './commit-barrier.ts'
8
8
 
@@ -57,7 +57,7 @@ class Root implements ReactRenderRoot {
57
57
  this.errors.uncaught,
58
58
  this.errors.caught,
59
59
  this.errors.recoverable,
60
- this.errors.transition,
60
+ this.errors.transitionIndicator,
61
61
  null
62
62
  )
63
63
  onCleanup(() => {
@@ -82,7 +82,8 @@ class Root implements ReactRenderRoot {
82
82
  if (this.failure !== undefined) throw this.failure
83
83
  reconciler.flushPassiveEffects()
84
84
  },
85
- style === undefined ? {} : { style }
85
+ style === undefined ? {} : { style },
86
+ { dispatch: dispatchDiscrete }
86
87
  )
87
88
  this.ready = this.native.ready.then(() => {
88
89
  if (this.phase !== 'active')
package/src/reconciler.ts CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  replaceText,
9
9
  setProperty,
10
10
  type ElementTag,
11
- type EventProperty,
12
11
  type Node,
13
12
  type PropertyName,
14
13
  type RendererNode,
@@ -48,11 +47,6 @@ type Config = Reconciler.HostConfig<
48
47
  const context: HostContext = Object.freeze({})
49
48
  let updatePriority: Reconciler.EventPriority = NoEventPriority
50
49
  let activeEvent: { type: string; observedAt: number } | undefined
51
- type EventBinding = {
52
- listener: Listener<NativeUIEvent> | undefined
53
- dispatch: Listener<NativeUIEvent>
54
- }
55
- const bindings = new WeakMap<Node, Map<EventProperty, EventBinding>>()
56
50
 
57
51
  function unsupported(feature: string): never {
58
52
  throw new UnsupportedFeatureError('react-' + feature)
@@ -65,52 +59,30 @@ const noInstance = () => null
65
59
  const returnFalse = () => false
66
60
  const hostContext = () => context
67
61
 
68
- function createBinding(listener: Listener<NativeUIEvent>): EventBinding {
69
- const binding: EventBinding = { listener, dispatch }
70
- function dispatch(event: NativeUIEvent): void | Promise<void> {
71
- const current = binding.listener
72
- if (!current) return
73
- const previous = activeEvent
74
- activeEvent = { type: event.type, observedAt: performance.now() }
75
- try {
76
- return reconciler.discreteUpdates(current, event, undefined, undefined, undefined)
77
- } finally {
78
- activeEvent = previous
79
- reconciler.flushSyncWork()
80
- }
81
- }
82
- return binding
83
- }
84
-
85
- function updateEvent(node: Node, name: EventProperty, value: unknown): void {
86
- const registered = bindings.get(node)
87
- const existing = registered?.get(name)
88
- if (value === undefined) {
89
- if (existing) existing.listener = undefined
90
- registered?.delete(name)
91
- setProperty(node, name, undefined)
92
- return
93
- }
94
- if (typeof value !== 'function') throw new TypeError('Native event listeners must be functions')
95
- // The callable boundary is checked here; core supplies the validated event.
96
- const listener = value as Listener<NativeUIEvent>
97
- if (existing) {
98
- // React replaces inline closures on ordinary rerenders. Keep the node's
99
- // native type/phase registration alive so queued events retain their ordering.
100
- existing.listener = listener
101
- return
62
+ /**
63
+ * Run a native callback as a discrete React update. The shared renderer keeps
64
+ * one registration per node/type/phase and calls the latest committed handler,
65
+ * so inline closures replaced on ordinary rerenders keep queued events.
66
+ */
67
+ export function dispatchDiscrete(
68
+ listener: Listener<NativeUIEvent>,
69
+ event: NativeUIEvent
70
+ ): void | Promise<void> {
71
+ const previous = activeEvent
72
+ activeEvent = { type: event.type, observedAt: performance.now() }
73
+ try {
74
+ return reconciler.discreteUpdates(listener, event, undefined, undefined, undefined)
75
+ } finally {
76
+ activeEvent = previous
77
+ reconciler.flushSyncWork()
102
78
  }
103
- const binding = createBinding(listener)
104
- setProperty(node, name, binding.dispatch)
105
- const next = registered ?? new Map<EventProperty, EventBinding>()
106
- next.set(name, binding)
107
- bindings.set(node, next)
108
79
  }
109
80
 
110
81
  function updateProperty(node: Node, name: string, value: unknown): void {
111
- if (isEventProperty(name)) updateEvent(node, name, value)
82
+ if (isEventProperty(name) && value !== undefined && typeof value !== 'function')
83
+ throw new TypeError('Native event listeners must be functions')
112
84
  // React props arrive as strings; the renderer rejects unknown names at runtime.
113
- else setProperty(node, name as PropertyName, value)
85
+ setProperty(node, name as PropertyName, value)
114
86
  }
115
87
 
116
88
  const reserved = (name: string) => name === 'children' || name === 'ref' || name === 'key'
@@ -200,8 +172,6 @@ const host: Config = {
200
172
  prepareScopeUpdate: unsupported.bind(null, 'scope'),
201
173
  getInstanceFromScope: noInstance,
202
174
  detachDeletedInstance(node) {
203
- for (const binding of bindings.get(node)?.values() ?? []) binding.listener = undefined
204
- bindings.delete(node)
205
175
  node.children.length = 0
206
176
  node.parent = undefined
207
177
  for (const name of Object.keys(node.properties)) {
@@ -6,16 +6,21 @@ export interface RootErrorOwner {
6
6
  report: ((error: unknown, info: Readonly<ErrorInfo>) => void) | undefined
7
7
  }
8
8
 
9
- function ignore(): void {}
10
-
11
9
  /** Created outside the Root constructor so callback scopes cannot capture its arguments. */
12
10
  export function createRootErrors(owner: RootErrorOwner) {
13
11
  return {
14
12
  uncaught(error: unknown): void {
15
13
  owner.terminate?.(error)
16
14
  },
17
- caught: ignore,
18
- transition: ignore,
15
+ /**
16
+ * An error a boundary handled is logged like React DOM's default instead
17
+ * of disappearing; it does not end the root.
18
+ */
19
+ caught(error: unknown): void {
20
+ if (owner.terminate) console.error(error)
21
+ },
22
+ /** React's default transition indicator: native views show none. */
23
+ transitionIndicator(): void {},
19
24
  recoverable(error: unknown, info: ErrorInfo): void {
20
25
  const report = owner.report
21
26
  if (!report) return