@pkhex-everywhere/react 0.1.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @pkhex-everywhere/react
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 92c350e: Document the public types, so editors and the API reference describe each type and field.
8
+ - 4fe8568: Require `@pkhex-everywhere/engine` from the matching minor as a peer, instead of any version.
9
+ - 92c350e: Link the READMEs and npm pages to the new documentation site.
10
+ - Updated dependencies [92c350e]
11
+ - Updated dependencies [55b1b68]
12
+ - Updated dependencies [92c350e]
13
+ - @pkhex-everywhere/engine@0.2.1
14
+
15
+ ## 0.2.0
16
+
17
+ ### Minor Changes
18
+
19
+ - fb0df12: Binary inputs take `Uint8Array`, `ArrayBuffer`, `Blob` or `File`, and binary outputs return `Uint8Array`. `game.load(data, fileName?)` takes the name from a `File`. Generated methods and hooks document whether they need a loaded save or an open draft. A missing draft fails with the new `no-draft` error code instead of `not-found`.
20
+ - 938f28c: `createEngine()` starts downloading the runtime as soon as it's created in a browser (`{ lazy: true }` opts out) and reports `engine.status` with file download progress through `engine.onStatusChange`. `wasmHost()` returns one shared runtime per page, so several engines on a page all get changes and events.
21
+ - eab17a2: `react` adds `useEngineStatus()` and `<RequireGame fallback>`, and `useQuery` rewrites `no-save` and `no-draft` messages to name the call and the fix. `engine` no longer exports `createClient`, `affects`, `queryTopics`, `Invoke`, `AssemblyExports` or `EngineExports`, and `engine.call` is gone.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [fb0df12]
26
+ - Updated dependencies [938f28c]
27
+ - Updated dependencies [38e32c9]
28
+ - Updated dependencies [eab17a2]
29
+ - @pkhex-everywhere/engine@0.2.0
30
+
3
31
  ## 0.1.0
4
32
 
5
33
  ### Minor Changes
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  React hooks for [`@pkhex-everywhere/engine`](https://www.npmjs.com/package/@pkhex-everywhere/engine). Hooks suspend until their data arrives and refetch when a command changes it.
4
4
 
5
+ **[Documentation](https://arley-space--docs.wawesome.app/docs/guides/react/)** · [API reference](https://arley-space--docs.wawesome.app/docs/reference/react/)
6
+
5
7
  ## Install
6
8
 
7
9
  ```sh
@@ -10,38 +12,71 @@ npm install @pkhex-everywhere/engine @pkhex-everywhere/react
10
12
 
11
13
  ## Example
12
14
 
15
+ This app opens a save from a file input, lists the party and downloads the edited save:
16
+
13
17
  ```tsx
14
- import { Suspense } from 'react'
18
+ import { Suspense, type ChangeEvent } from 'react'
15
19
  import { createEngine } from '@pkhex-everywhere/engine'
16
- import { EngineProvider, useParty } from '@pkhex-everywhere/react'
20
+ import { EngineProvider, RequireGame, useEngineStatus, useLoadedGame, useParty } from '@pkhex-everywhere/react'
17
21
 
18
22
  const engine = createEngine()
19
23
 
24
+ function Booting() {
25
+ const { loaded, total } = useEngineStatus()
26
+ return <progress value={loaded} max={total || 1} />
27
+ }
28
+
29
+ function OpenSave() {
30
+ const { load } = useLoadedGame()
31
+ const open = (event: ChangeEvent<HTMLInputElement>) => {
32
+ const file = event.target.files?.[0]
33
+ if (file) void load(file)
34
+ }
35
+ return <input type="file" onChange={open} />
36
+ }
37
+
20
38
  function Party() {
21
39
  const { party } = useParty()
22
40
  return (
23
- <ul>
41
+ <ul id="party">
24
42
  {party.map((pokemon) => (
25
- <li key={pokemon.id}>
26
- {pokemon.species} Lv. {pokemon.level}
27
- </li>
43
+ <li key={pokemon.id}>{pokemon.species}</li>
28
44
  ))}
29
45
  </ul>
30
46
  )
31
47
  }
32
48
 
49
+ function Download() {
50
+ const { export: exportSave } = useLoadedGame()
51
+ const download = async () => {
52
+ const { bytes, fileName } = await exportSave()
53
+ const url = URL.createObjectURL(new Blob([bytes]))
54
+ const link = document.createElement('a')
55
+ link.href = url
56
+ link.download = fileName
57
+ link.click()
58
+ setTimeout(() => URL.revokeObjectURL(url))
59
+ }
60
+ return <button onClick={download}>Download</button>
61
+ }
62
+
33
63
  export function App() {
34
64
  return (
35
65
  <EngineProvider engine={engine}>
36
- <Suspense fallback="Loading…">
37
- <Party />
66
+ <Suspense fallback={<Booting />}>
67
+ <RequireGame fallback={<OpenSave />}>
68
+ <Party />
69
+ <Download />
70
+ </RequireGame>
38
71
  </Suspense>
39
72
  </EngineProvider>
40
73
  )
41
74
  }
42
75
  ```
43
76
 
44
- Load a save with `useLoadedGame().load(base64, fileName)` or `engine.game.load` before rendering hooks that read it.
77
+ The Suspense fallback shows the runtime download with `useEngineStatus()`. `<RequireGame>` renders its fallback until a save is loaded, then its children. If the runtime fails to load, the hooks throw the error to the nearest error boundary.
78
+
79
+ CI builds this app from the packed packages and runs it.
45
80
 
46
81
  ## License
47
82
 
@@ -6,11 +6,15 @@ interface EngineContextValue {
6
6
  cache: QueryCache;
7
7
  }
8
8
  interface EngineProviderProps {
9
+ /** The engine the hooks below read from, usually created once with `createEngine()`. */
9
10
  engine: Engine;
11
+ /** Called with `EngineError` rejections nothing else handled, for example to show a toast. */
10
12
  onUnhandledError?: (error: EngineError) => void;
11
13
  children?: ReactNode;
12
14
  }
15
+ /** Gives the hooks below it an engine and a query cache that refetches when a command changes the save. */
13
16
  export declare function EngineProvider({ engine, onUnhandledError, children }: EngineProviderProps): import("react").JSX.Element;
14
17
  export declare function useEngineContext(): EngineContextValue;
18
+ /** The engine from the nearest `EngineProvider`, for any call the hooks don't cover. */
15
19
  export declare function useEngine(): Engine;
16
20
  export {};
@@ -0,0 +1,6 @@
1
+ import type { ReactNode } from 'react';
2
+ /** Renders `fallback` until a save is loaded, then `children`. Wrap hooks that need a save in it. */
3
+ export declare function RequireGame({ fallback, children }: {
4
+ fallback?: ReactNode;
5
+ children?: ReactNode;
6
+ }): ReactNode;
@@ -1,43 +1,66 @@
1
- import type { Base64, ItemHandle, PokemonHandle, PokemonPatch, TrainerGender } from '@pkhex-everywhere/engine';
1
+ import type { Binary, ItemHandle, PokemonHandle, PokemonPatch, TrainerGender } from '@pkhex-everywhere/engine';
2
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
2
3
  export declare function useBox(): {
4
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
3
5
  addEncounter: (index: number) => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
4
- addFromFile: (bytes: Base64) => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
6
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
7
+ addFromFile: (bytes: Binary) => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
5
8
  box: import("@pkhex-everywhere/engine").PokemonSummary[];
6
9
  };
10
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
7
11
  export declare function useEvents(): {
12
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
8
13
  giveTickets: (includeOldSeaMap: boolean) => Promise<string[]>;
14
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
9
15
  setFlag: (index: number, value: boolean) => Promise<void>;
16
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
10
17
  setWork: (index: number, value: number) => Promise<void>;
11
18
  events: import("@pkhex-everywhere/engine").SaveEvents | null;
12
19
  };
13
20
  export declare function useLoadedGame(): {
14
21
  close: () => Promise<void>;
22
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
15
23
  export: () => Promise<import("@pkhex-everywhere/engine").ExportedSave>;
16
24
  file: () => Promise<import("@pkhex-everywhere/engine").LoadedSave | null>;
17
- load: (data: Base64, fileName: string) => Promise<void>;
25
+ load: (data: Binary, fileName?: string | null) => Promise<void>;
18
26
  loadBlank: (version: number) => Promise<void>;
19
27
  game: import("@pkhex-everywhere/engine").SaveSummary | null;
20
28
  };
29
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
21
30
  export declare function useInventory(): {
31
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
22
32
  setItem: (at: ItemHandle, count: number) => Promise<void>;
23
33
  inventory: import("@pkhex-everywhere/engine").Pouch[];
24
34
  };
35
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
25
36
  export declare function useParty(): {
26
37
  party: import("@pkhex-everywhere/engine").PokemonSummary[];
27
38
  };
39
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
28
40
  export declare function usePokemon(at: PokemonHandle): {
41
+ /** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
29
42
  addToBox: () => Promise<import("@pkhex-everywhere/engine").AddedPokemon>;
43
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
30
44
  clone: () => Promise<void>;
45
+ /** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
31
46
  commit: () => Promise<import("@pkhex-everywhere/engine").PokemonId>;
47
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
32
48
  edit: () => Promise<void>;
49
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
33
50
  setLevel: (level: number) => Promise<void>;
51
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
34
52
  update: (patch: PokemonPatch) => Promise<void>;
35
53
  pokemon: import("@pkhex-everywhere/engine").PokemonSummary;
36
54
  };
55
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
37
56
  export declare function useTrainer(): {
57
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
38
58
  setBattlePoints: (value: number) => Promise<void>;
59
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
39
60
  setGender: (gender: TrainerGender) => Promise<void>;
61
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
40
62
  setMoney: (amount: number) => Promise<void>;
63
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
41
64
  setName: (name: string) => Promise<void>;
42
65
  trainer: import("@pkhex-everywhere/engine").TrainerCard;
43
66
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { EngineProvider, useEngine } from './EngineProvider';
2
2
  export { useQuery, type CallArgs, type CallResult } from './useQuery';
3
+ export { useEngineStatus } from './useEngineStatus';
4
+ export { RequireGame } from './RequireGame';
3
5
  export * from './generated/hooks';
4
6
  export { usePokemonDetails } from './usePokemonDetails';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
- import { EngineError, affects, queryTopics } from "@pkhex-everywhere/engine";
2
+ import { EngineError } from "@pkhex-everywhere/engine";
3
+ import { affects, call, queryTopics } from "@pkhex-everywhere/engine/internal";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  //#region src/cache.ts
5
6
  var QueryCache = class {
@@ -55,6 +56,7 @@ var QueryCache = class {
55
56
  //#region src/EngineProvider.tsx
56
57
  var EngineContext = createContext(null);
57
58
  var reported = /* @__PURE__ */ new WeakSet();
59
+ /** Gives the hooks below it an engine and a query cache that refetches when a command changes the save. */
58
60
  function EngineProvider({ engine, onUnhandledError, children }) {
59
61
  const [cache] = useState(() => new QueryCache());
60
62
  useEffect(() => engine.subscribe(["*"], cache.invalidate), [engine, cache]);
@@ -86,38 +88,70 @@ function useEngineContext() {
86
88
  if (!context) throw new Error("Engine hooks must be used inside an EngineProvider.");
87
89
  return context;
88
90
  }
91
+ /** The engine from the nearest `EngineProvider`, for any call the hooks don't cover. */
89
92
  function useEngine() {
90
93
  return useEngineContext().engine;
91
94
  }
92
95
  //#endregion
93
96
  //#region src/useQuery.ts
94
- function useQuery(call, ...args) {
97
+ var fixes = {
98
+ "no-save": (name) => `${name} needs a loaded save. Load one with useLoadedGame().load(file) and wrap the component in <RequireGame>.`,
99
+ "no-draft": (name) => `${name} needs an open draft. Open one with usePokemon(at).edit() or .clone() first.`
100
+ };
101
+ function explain(name, error) {
102
+ if (!(error instanceof EngineError)) return error;
103
+ const fix = fixes[error.code];
104
+ return fix ? new EngineError(error.code, fix(name)) : error;
105
+ }
106
+ /**
107
+ * Runs a query by name and suspends until it resolves. Refetches when a command changes a topic the query reads,
108
+ * and keeps showing the previous value while it does.
109
+ */
110
+ function useQuery(name, ...args) {
95
111
  const { engine, cache } = useEngineContext();
96
- const key = JSON.stringify([call, args]);
97
- const entry = useSyncExternalStore(cache.subscribe, () => cache.peek(key)) ?? cache.get(key, queryTopics[call], () => engine.call(call, args));
112
+ const key = JSON.stringify([name, args]);
113
+ const load = () => call(engine, name, args).catch((error) => {
114
+ throw explain(name, error);
115
+ });
116
+ const entry = useSyncExternalStore(cache.subscribe, () => cache.peek(key)) ?? cache.get(key, queryTopics[name], load);
98
117
  if (entry.status === "pending") throw entry.promise;
99
118
  if (entry.status === "error") throw entry.error;
100
119
  return entry.value;
101
120
  }
102
121
  //#endregion
122
+ //#region src/useEngineStatus.ts
123
+ /** The engine's boot status. Re-renders as the runtime downloads. Works without a save. */
124
+ function useEngineStatus() {
125
+ const engine = useEngine();
126
+ const read = () => engine.status;
127
+ return useSyncExternalStore(engine.onStatusChange, read, read);
128
+ }
129
+ //#endregion
103
130
  //#region src/generated/hooks.ts
131
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
104
132
  function useBox() {
105
133
  const engine = useEngine();
106
134
  return {
107
135
  box: useQuery("box.get"),
108
136
  ...useMemo(() => ({
137
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
109
138
  addEncounter: (index) => engine.box.addEncounter(index),
139
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
110
140
  addFromFile: (bytes) => engine.box.addFromFile(bytes)
111
141
  }), [engine])
112
142
  };
113
143
  }
144
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
114
145
  function useEvents() {
115
146
  const engine = useEngine();
116
147
  return {
117
148
  events: useQuery("events.get"),
118
149
  ...useMemo(() => ({
150
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
119
151
  giveTickets: (includeOldSeaMap) => engine.events.giveTickets(includeOldSeaMap),
152
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
120
153
  setFlag: (index, value) => engine.events.setFlag(index, value),
154
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
121
155
  setWork: (index, value) => engine.events.setWork(index, value)
122
156
  }), [engine])
123
157
  };
@@ -128,6 +162,7 @@ function useLoadedGame() {
128
162
  game: useQuery("game.get"),
129
163
  ...useMemo(() => ({
130
164
  close: () => engine.game.close(),
165
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
131
166
  export: () => engine.game.export(),
132
167
  file: () => engine.game.file(),
133
168
  load: (data, fileName) => engine.game.load(data, fileName),
@@ -135,16 +170,21 @@ function useLoadedGame() {
135
170
  }), [engine])
136
171
  };
137
172
  }
173
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
138
174
  function useInventory() {
139
175
  const engine = useEngine();
140
176
  return {
141
177
  inventory: useQuery("inventory.get"),
142
- ...useMemo(() => ({ setItem: (at, count) => engine.inventory.setItem(at, count) }), [engine])
178
+ ...useMemo(() => ({
179
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
180
+ setItem: (at, count) => engine.inventory.setItem(at, count) }), [engine])
143
181
  };
144
182
  }
183
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
145
184
  function useParty() {
146
185
  return { party: useQuery("party.get") };
147
186
  }
187
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
148
188
  function usePokemon(at) {
149
189
  const engine = useEngine();
150
190
  const pokemon = useQuery("pokemon.get", at);
@@ -152,29 +192,48 @@ function usePokemon(at) {
152
192
  return {
153
193
  pokemon,
154
194
  ...useMemo(() => ({
195
+ /** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
155
196
  addToBox: () => engine.pokemon.addToBox(),
197
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
156
198
  clone: () => engine.pokemon.clone(at),
199
+ /** Requires a loaded save and an open draft; throws `no-save` or `no-draft` otherwise. Wrap in `<RequireGame>`. */
157
200
  commit: () => engine.pokemon.commit(),
201
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
158
202
  edit: () => engine.pokemon.edit(at),
203
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
159
204
  setLevel: (level) => engine.pokemon.setLevel(at, level),
205
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
160
206
  update: (patch) => engine.pokemon.update(at, patch)
161
207
  }), [engine, key])
162
208
  };
163
209
  }
210
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
164
211
  function useTrainer() {
165
212
  const engine = useEngine();
166
213
  return {
167
214
  trainer: useQuery("trainer.get"),
168
215
  ...useMemo(() => ({
216
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
169
217
  setBattlePoints: (value) => engine.trainer.setBattlePoints(value),
218
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
170
219
  setGender: (gender) => engine.trainer.setGender(gender),
220
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
171
221
  setMoney: (amount) => engine.trainer.setMoney(amount),
222
+ /** Requires a loaded save; throws `no-save` otherwise. Wrap in `<RequireGame>`. */
172
223
  setName: (name) => engine.trainer.setName(name)
173
224
  }), [engine])
174
225
  };
175
226
  }
176
227
  //#endregion
228
+ //#region src/RequireGame.tsx
229
+ /** Renders `fallback` until a save is loaded, then `children`. Wrap hooks that need a save in it. */
230
+ function RequireGame({ fallback = null, children }) {
231
+ const { game } = useLoadedGame();
232
+ return game ? children : fallback;
233
+ }
234
+ //#endregion
177
235
  //#region src/usePokemonDetails.ts
236
+ /** A Pokémon's editable fields and an `update` that applies a patch to it. Requires a loaded save. */
178
237
  function usePokemonDetails(at) {
179
238
  const engine = useEngine();
180
239
  const details = useQuery("pokemon.details", at);
@@ -185,4 +244,4 @@ function usePokemonDetails(at) {
185
244
  };
186
245
  }
187
246
  //#endregion
188
- export { EngineProvider, useBox, useEngine, useEvents, useInventory, useLoadedGame, useParty, usePokemon, usePokemonDetails, useQuery, useTrainer };
247
+ export { EngineProvider, RequireGame, useBox, useEngine, useEngineStatus, useEvents, useInventory, useLoadedGame, useParty, usePokemon, usePokemonDetails, useQuery, useTrainer };
@@ -0,0 +1,3 @@
1
+ import type { EngineStatus } from '@pkhex-everywhere/engine';
2
+ /** The engine's boot status. Re-renders as the runtime downloads. Works without a save. */
3
+ export declare function useEngineStatus(): EngineStatus;
@@ -1,4 +1,5 @@
1
1
  import type { PokemonHandle, PokemonPatch } from '@pkhex-everywhere/engine';
2
+ /** A Pokémon's editable fields and an `update` that applies a patch to it. Requires a loaded save. */
2
3
  export declare function usePokemonDetails(at: PokemonHandle): {
3
4
  details: import("@pkhex-everywhere/engine").EditablePokemon;
4
5
  update: (patch: PokemonPatch) => Promise<void>;
@@ -1,7 +1,13 @@
1
1
  import { type CallName, type EngineClient, type QueryName } from '@pkhex-everywhere/engine';
2
2
  type Lookup<T, Path extends string> = Path extends `${infer Head}.${infer Rest}` ? Head extends keyof T ? Lookup<T[Head], Rest> : never : Path extends keyof T ? T[Path] : never;
3
3
  type CallFunction<Name extends CallName> = Extract<Lookup<EngineClient, Name>, (...args: never[]) => Promise<unknown>>;
4
+ /** The arguments of the call `Name`, such as `[at: PokemonHandle]` for `pokemon.details`. */
4
5
  export type CallArgs<Name extends CallName> = Parameters<CallFunction<Name>>;
6
+ /** What the call `Name` resolves to. */
5
7
  export type CallResult<Name extends CallName> = Awaited<ReturnType<CallFunction<Name>>>;
6
- export declare function useQuery<Name extends QueryName>(call: Name, ...args: CallArgs<Name>): CallResult<Name>;
8
+ /**
9
+ * Runs a query by name and suspends until it resolves. Refetches when a command changes a topic the query reads,
10
+ * and keeps showing the previous value while it does.
11
+ */
12
+ export declare function useQuery<Name extends QueryName>(name: Name, ...args: CallArgs<Name>): CallResult<Name>;
7
13
  export {};
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@pkhex-everywhere/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "React hooks for @pkhex-everywhere/engine.",
5
5
  "license": "GPL-3.0-or-later",
6
+ "homepage": "https://arley-space--docs.wawesome.app/docs/",
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "git+https://github.com/arleypadua/PKHeX.Everywhere.git",
@@ -29,7 +30,7 @@
29
30
  "test": "vitest run"
30
31
  },
31
32
  "peerDependencies": {
32
- "@pkhex-everywhere/engine": "*",
33
+ "@pkhex-everywhere/engine": "^0.2.1",
33
34
  "react": "^19.0.0"
34
35
  },
35
36
  "devDependencies": {