react-sync-ui 1.0.2 → 2.0.0
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/CHANGELOG.md +111 -0
- package/README.md +336 -193
- package/dist/index.d.ts +4 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +195 -7
- package/dist/index.js.map +1 -0
- package/dist/syncUI.d.ts +19 -19
- package/dist/syncUI.d.ts.map +1 -0
- package/package.json +87 -39
- package/src/index.ts +11 -2
- package/src/syncUI.tsx +403 -133
- package/dist/react-sync-ui.cjs.development.js +0 -115
- package/dist/react-sync-ui.cjs.development.js.map +0 -1
- package/dist/react-sync-ui.cjs.production.min.js +0 -2
- package/dist/react-sync-ui.cjs.production.min.js.map +0 -1
- package/dist/react-sync-ui.esm.js +0 -106
- package/dist/react-sync-ui.esm.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- **React peer dependency is now `^18.0.0 || ^19.0.0`** (was `>=16`). The
|
|
8
|
+
queue is exposed to React through `useSyncExternalStore`, which ships with
|
|
9
|
+
React 18.
|
|
10
|
+
- **ESM-only package.** `package.json` has `"type": "module"` and an `exports`
|
|
11
|
+
map; the only code entry is `dist/index.js` (+ `dist/index.d.ts`), next to
|
|
12
|
+
the `./package.json` subpath. The CJS build and `dist/react-sync-ui.esm.js`
|
|
13
|
+
are gone.
|
|
14
|
+
- **Calling a sync function before `<SyncUI />` is mounted no longer throws**
|
|
15
|
+
`You have to initialize <SyncUI />`. The item is queued and rendered as soon
|
|
16
|
+
as a host mounts. In development a `console.error` is logged if items are
|
|
17
|
+
still pending after 3 seconds and no `<SyncUI />` has ever mounted.
|
|
18
|
+
- **`resolve` / `reject` are bound to their own item and idempotent.** A second
|
|
19
|
+
call for the same item, or a call from a stale closure after the item has
|
|
20
|
+
left the queue, is a no-op instead of settling the _next_ caller's promise.
|
|
21
|
+
This also applies to `usePromiseQueue().head.resolve/reject`.
|
|
22
|
+
- **`reject()` with no reason now rejects with an `Error`** instead of
|
|
23
|
+
`undefined`: `new Error("react-sync-ui: rejected without a reason")`. The
|
|
24
|
+
idiomatic `catch (error) { toast(error.message) }` therefore no longer throws
|
|
25
|
+
a `TypeError` on top of the cancellation. Pass your own reason whenever the
|
|
26
|
+
caller has to tell one apart from another. Applies to `props.reject()` and to
|
|
27
|
+
`usePromiseQueue().head.reject()`.
|
|
28
|
+
- **`usePromiseQueue` rejects its pending items when the owning component
|
|
29
|
+
unmounts**, with
|
|
30
|
+
`new Error("react-sync-ui: usePromiseQueue unmounted with pending items")`.
|
|
31
|
+
That queue is created by the hook and dies with the component, so previously
|
|
32
|
+
every `await push(...)` stayed suspended forever. StrictMode's simulated
|
|
33
|
+
unmount/remount does not drain anything; only a real unmount does. The
|
|
34
|
+
`makeSyncUI` queue is unaffected — it lives in the factory and survives
|
|
35
|
+
`<SyncUI />` unmounting.
|
|
36
|
+
- `usePromiseQueue` is now exported from the package entry (previously only
|
|
37
|
+
reachable from `react-sync-ui/src/syncUI`).
|
|
38
|
+
- `reject` is typed as `(reason?: unknown) => void` (was `any`).
|
|
39
|
+
- `require("react-sync-ui")` now needs Node `>=20.19` / `>=22.12` (unflagged
|
|
40
|
+
`require(esm)`); on older Node use `import` or a dynamic `import()`.
|
|
41
|
+
|
|
42
|
+
### Fixes
|
|
43
|
+
|
|
44
|
+
- Promise settlement no longer happens inside a `setState` updater. Under
|
|
45
|
+
StrictMode / concurrent rendering the updater could be replayed against a
|
|
46
|
+
different queue and settle the wrong promise with the wrong value.
|
|
47
|
+
- `makeSyncUI` called after `<SyncUI />` has mounted (lazy-loaded chunk, Vite
|
|
48
|
+
HMR re-evaluating a module) now works; previously the first call threw.
|
|
49
|
+
- Mounting two `<SyncUI />` of the same factory no longer breaks pushes when
|
|
50
|
+
either one unmounts. Only the first mounted host renders; a dev warning is
|
|
51
|
+
logged for the duplicate.
|
|
52
|
+
- Unmounting `<SyncUI />` no longer loses queued items: the queue lives in the
|
|
53
|
+
factory closure, so pending promises survive a remount (StrictMode, HMR,
|
|
54
|
+
route changes).
|
|
55
|
+
- `<SyncUI />` inside a React 19.2 `<Activity mode="hidden">` subtree no
|
|
56
|
+
longer jams the queue: going back to `visible` re-reads the store and
|
|
57
|
+
restores the open dialog. (While hidden the host renders nothing, so the
|
|
58
|
+
dialog's own local state starts over; the queue is untouched.)
|
|
59
|
+
- A sync component that throws during render is no longer a poison pill. An
|
|
60
|
+
internal error boundary rejects that call's promise with the thrown error
|
|
61
|
+
and renders the next queued item; the app's own error boundary is not
|
|
62
|
+
triggered.
|
|
63
|
+
- Calling a sync function during server rendering only queues the item: the
|
|
64
|
+
dev "no `<SyncUI />` is mounted" warning is not logged and no 3 s timer is
|
|
65
|
+
scheduled when `typeof window === "undefined"`, so a Node process, lambda or
|
|
66
|
+
test worker is no longer held open. `<SyncUI />` renders nothing on the
|
|
67
|
+
server and hydrates cleanly, with pushes before and after hydration.
|
|
68
|
+
- A queued item whose component is missing from the registry (a module graph
|
|
69
|
+
reset under a live queue) is rejected with
|
|
70
|
+
`new Error("react-sync-ui: no component registered for this sync UI")`
|
|
71
|
+
instead of blocking itself and everything queued behind it forever. The
|
|
72
|
+
development `console.error` is still logged.
|
|
73
|
+
- The dev-only "no `<SyncUI />` is mounted" timer is cleared as soon as a host
|
|
74
|
+
mounts, instead of lingering for the rest of its 3 s.
|
|
75
|
+
- Dev-only warnings are gated by a plain `process.env.NODE_ENV` read, the same
|
|
76
|
+
convention React uses. Bundlers replace it, so the warnings are active in
|
|
77
|
+
development and dropped from production bundles. Importing the package with
|
|
78
|
+
no bundler and no `process` global throws at module load, as React does.
|
|
79
|
+
- Item keys use a monotonic counter instead of `Math.random()`.
|
|
80
|
+
- `<SyncUI />` renders only the head item's component instead of one wrapper
|
|
81
|
+
per registered component.
|
|
82
|
+
|
|
83
|
+
### Types
|
|
84
|
+
|
|
85
|
+
- Added `SyncUIProps`, `SyncUIComponent`, `SyncUIFactory`, `SyncUIFunction`
|
|
86
|
+
and `PromiseQueueAPI` type exports.
|
|
87
|
+
- `SyncUIFunction<InputData, ResolveValue = void>` names what `makeSyncUI`
|
|
88
|
+
returns — `(input: InputData) => Promise<ResolveValue>` — so wrappers,
|
|
89
|
+
context values and props types no longer have to re-spell the signature.
|
|
90
|
+
- `PromiseQueueAPI<InputData, ResolveValue = void>` defaults its second type
|
|
91
|
+
argument (`PromiseQueueAPI<Msg>` used to be a hard error), and its `head` is
|
|
92
|
+
now `SyncUIProps<InputData, ResolveValue>` instead of a structurally
|
|
93
|
+
identical inline type.
|
|
94
|
+
- The public generic parameters are uniformly named `InputData` /
|
|
95
|
+
`ResolveValue` (they show up verbatim in `dist/*.d.ts` and editor tooltips).
|
|
96
|
+
- `SyncUIComponent<Data, Result>` is `ComponentType<SyncUIProps<Data, Result>>`
|
|
97
|
+
instead of `(props) => ReactNode`, so `React.FC<SyncUIProps<...>>`, class
|
|
98
|
+
components, `memo()` and `forwardRef()` are all accepted by `makeSyncUI`.
|
|
99
|
+
(React 19's `FunctionComponent` returns `ReactNode | Promise<ReactNode>`,
|
|
100
|
+
which the old signature rejected.)
|
|
101
|
+
|
|
102
|
+
### Tooling
|
|
103
|
+
|
|
104
|
+
- Build: Vite 8 library mode (`vite build`) with `unplugin-dts`, replacing
|
|
105
|
+
tsdx. Sourcemaps included, output not minified.
|
|
106
|
+
- Tests: Vitest 4 + Testing Library 16 + jsdom, React 19 in devDependencies.
|
|
107
|
+
- ESLint 10 flat config with `typescript-eslint` and
|
|
108
|
+
`eslint-plugin-react-hooks` v7, Prettier, husky 9 pre-commit hook, size-limit
|
|
109
|
+
13, publint and are-the-types-wrong in `npm run check`.
|
|
110
|
+
- TypeScript 5.9, `jsx: react-jsx` (no `React` default import needed).
|
|
111
|
+
- The example app uses the native `<dialog>` element, no UI framework.
|