@usearete/react 0.2.0 → 0.4.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/README.md +276 -40
- package/dist/index.cjs +2367 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +461 -70
- package/dist/index.esm.js +1681 -11441
- package/dist/index.esm.js.map +1 -1
- package/eslint-plugin.cjs +5 -0
- package/eslint-rules/arete-hooks.js +171 -0
- package/eslint-rules/package.json +3 -0
- package/package.json +18 -9
- package/dist/index.js +0 -11967
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,43 +7,74 @@ Built on top of [`@usearete/sdk`](https://www.npmjs.com/package/@usearete/sdk),
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @usearete/react
|
|
10
|
+
npm install @usearete/react @usearete/sdk zod
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@usearete/react` installs `zustand` as a normal dependency, so do not install it separately. React remains an application dependency. Generated consumers install the core SDK and Zod because generated stack files import their types and schemas directly.
|
|
14
|
+
|
|
15
|
+
### Hooks linting
|
|
16
|
+
|
|
17
|
+
Arete's fluent `.use()`, `.useOne()`, and `.useMutation()` calls are real React
|
|
18
|
+
hooks, but the standard Hooks rule cannot identify deeply nested member calls.
|
|
19
|
+
The package ships `@usearete/react/eslint-plugin.cjs` with an
|
|
20
|
+
`arete/fluent-hooks` rule for flat ESLint configurations. The ORE template also
|
|
21
|
+
configures the equivalent bundled rule for ESLint 8. Use it together with
|
|
22
|
+
`react-hooks/rules-of-hooks`, not instead of the standard rule.
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
// eslint.config.js
|
|
26
|
+
import areteHooks from '@usearete/react/eslint-plugin.cjs';
|
|
27
|
+
|
|
28
|
+
export default [{
|
|
29
|
+
plugins: { arete: areteHooks },
|
|
30
|
+
rules: { 'arete/fluent-hooks': 'error' },
|
|
31
|
+
}];
|
|
11
32
|
```
|
|
12
33
|
|
|
13
34
|
> Not using React? Use [`@usearete/sdk`](../core/README.md) directly.
|
|
14
35
|
|
|
15
36
|
## Quick Start
|
|
16
37
|
|
|
38
|
+
Keep the generated stack explicit at the provider and consumer boundaries. Equivalent calls share one provider-managed client and store:
|
|
39
|
+
|
|
17
40
|
```tsx
|
|
18
41
|
import { AreteProvider, useArete } from '@usearete/react';
|
|
19
42
|
import { ORE_STREAM_STACK } from './generated/ore-stack';
|
|
20
43
|
|
|
44
|
+
const publishableKey = import.meta.env.VITE_ARETE_PUBLISHABLE_KEY;
|
|
45
|
+
if (!publishableKey) throw new Error('VITE_ARETE_PUBLISHABLE_KEY is required');
|
|
46
|
+
|
|
21
47
|
function Dashboard() {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
48
|
+
const arete = useArete(ORE_STREAM_STACK);
|
|
49
|
+
const board = arete.views.OreBoard.state.use({
|
|
50
|
+
address: arete.addresses.board(),
|
|
25
51
|
});
|
|
52
|
+
const roundId = board.data?.state.roundId;
|
|
53
|
+
const round = arete.views.OreRound.state.use(
|
|
54
|
+
roundId == null ? undefined : { roundId },
|
|
55
|
+
);
|
|
26
56
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (isLoading) {
|
|
30
|
-
return <div>Connecting...</div>;
|
|
57
|
+
if (arete.status === 'error') {
|
|
58
|
+
return <button onClick={() => void arete.retry()}>Reconnect</button>;
|
|
31
59
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return <div>{error.message}</div>;
|
|
60
|
+
if (board.status !== 'ready' || round.status !== 'ready') {
|
|
61
|
+
return <p>Connecting...</p>;
|
|
35
62
|
}
|
|
36
63
|
|
|
37
|
-
return
|
|
64
|
+
return (
|
|
65
|
+
<div>
|
|
66
|
+
{arete.socketIssue && <p>{arete.socketIssue.message}</p>}
|
|
67
|
+
<p>Round {round.data?.id.roundId?.toString() ?? 'unavailable'}</p>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
38
70
|
}
|
|
39
71
|
|
|
40
72
|
export default function App() {
|
|
41
73
|
return (
|
|
42
74
|
<AreteProvider
|
|
43
|
-
autoConnect
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}}
|
|
75
|
+
autoConnect
|
|
76
|
+
stack={ORE_STREAM_STACK}
|
|
77
|
+
auth={{ publishableKey }}
|
|
47
78
|
>
|
|
48
79
|
<Dashboard />
|
|
49
80
|
</AreteProvider>
|
|
@@ -51,102 +82,307 @@ export default function App() {
|
|
|
51
82
|
}
|
|
52
83
|
```
|
|
53
84
|
|
|
85
|
+
The hosted ORE stack requires this publishable key for authentication and rate limiting. Read-only browser viewing does not require a wallet, but it is not unauthenticated.
|
|
86
|
+
|
|
87
|
+
Single-stack applications can remove the repeated constant without ambient types by binding the React API once:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { createAreteReact } from '@usearete/react';
|
|
91
|
+
import { ORE_STREAM_STACK } from './generated/ore-stack';
|
|
92
|
+
|
|
93
|
+
export const {
|
|
94
|
+
Provider: OreProvider,
|
|
95
|
+
useArete: useOre,
|
|
96
|
+
} = createAreteReact(ORE_STREAM_STACK);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`useOre()` remains explicit about the application dependency. Multi-stack apps can keep using `AreteProvider` and `useArete(stack)` directly.
|
|
100
|
+
|
|
101
|
+
Provider-default `useArete()` calls remain available. For full type inference, register the stack type once (e.g. in `src/arete.d.ts`):
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import type { OreStreamStack } from './generated/ore-stack';
|
|
105
|
+
|
|
106
|
+
declare module '@usearete/react' {
|
|
107
|
+
interface AreteDefaultStackRegistry {
|
|
108
|
+
defaultStack: OreStreamStack;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Multi-stack apps can keep passing the stack explicitly: `useArete(stack, options)` always wins over the provider default.
|
|
114
|
+
|
|
54
115
|
## Provider
|
|
55
116
|
|
|
56
117
|
`AreteProvider` manages connected clients for descendant hooks.
|
|
57
118
|
|
|
58
119
|
Supported props:
|
|
59
120
|
|
|
60
|
-
- `
|
|
121
|
+
- `stack` — default stack for argument-less `useArete()` calls
|
|
122
|
+
- `stackOptions` — default client lookup options (`url`, `httpUrl`, `transport`, `transactions`, `programs`) for the provider stack, including explicit `useArete(providerStack)` calls
|
|
123
|
+
- `autoConnect` — defaults to `true`; controls only whether the provider starts the initial connection
|
|
124
|
+
- `autoReconnect` — defaults to `true`; controls only recovery after an established connection is lost
|
|
61
125
|
- `wallet`
|
|
62
126
|
- `auth`
|
|
63
127
|
- `fetch`
|
|
64
|
-
- `validateFrames`
|
|
128
|
+
- `validateFrames` — set `false` to suppress rejected-frame warnings; generated schemas still normalize and validate entities
|
|
129
|
+
- `onFrameValidationError` — structured callback for generated-schema rejections
|
|
65
130
|
- `reconnectIntervals`
|
|
66
131
|
- `maxReconnectAttempts`
|
|
67
132
|
- `maxEntriesPerView`
|
|
68
133
|
- `flushIntervalMs`
|
|
69
134
|
|
|
70
|
-
These are provider-wide defaults.
|
|
135
|
+
These are provider-wide defaults. Per-call overrides stay on the hook call and always win. Connection settings are captured when a shared client is created; changing provider props does not mutate that client. `wallet` updates are synchronized in place. For other changed settings, call `retry()` to replace the shared client using the latest provider configuration.
|
|
71
136
|
|
|
72
|
-
|
|
137
|
+
The lifecycle flags are intentionally independent. `autoConnect={false}` leaves initial connection ownership to the application. It does not disable automatic recovery for a connection the application establishes, but `canRetry` remains false because provider-owned replacement attempts would also stay disconnected; use the client lifecycle directly. `autoReconnect={false}` does not suppress the initial connection; it disables only automatic recovery after that connection is lost.
|
|
73
138
|
|
|
74
|
-
`useArete`
|
|
139
|
+
## `useArete(stack?, options?)`
|
|
140
|
+
|
|
141
|
+
`useArete` returns the connected React surface for a stack. With no arguments it resolves the provider's `stack`/`stackOptions`. Passing the same stack explicitly keeps the provider's `stackOptions`; per-call options override them, while another explicit stack is isolated from the provider stack's options. It throws a descriptive error when no stack is passed and no default is configured.
|
|
75
142
|
|
|
76
143
|
- `views`
|
|
77
144
|
- `programs`
|
|
78
145
|
- `queries`
|
|
146
|
+
- `read` — callable stack reads with `.use(...args, options?)` React hooks
|
|
79
147
|
- `chain`
|
|
80
148
|
- `client`
|
|
81
|
-
- `
|
|
149
|
+
- `reads` — deprecated alias of `read`
|
|
150
|
+
- `status` — preferred connection state for UI; includes the initial client-creation window
|
|
151
|
+
- `connectionState` — raw state from the connected client
|
|
82
152
|
- `isConnected`
|
|
83
153
|
- `isLoading`
|
|
154
|
+
- `canRetry` — true when `retry()` can start a new shared connection attempt
|
|
84
155
|
- `error`
|
|
156
|
+
- `socketIssue` — the latest structured server WebSocket issue, if any
|
|
157
|
+
- `retry()` — replaces the failed shared client; every matching `useArete` consumer observes the connecting window and adopts the same replacement
|
|
85
158
|
|
|
86
159
|
Supported hook options:
|
|
87
160
|
|
|
88
161
|
- `url`
|
|
89
162
|
- `httpUrl`
|
|
90
163
|
- `transport`
|
|
164
|
+
- `transactions` — explicit transaction transport when execution uses a different service from `httpUrl`
|
|
91
165
|
- `programs`
|
|
92
166
|
|
|
167
|
+
All options are optional. By default the client connects to the endpoints embedded in the generated stack definition (`stack.endpoints`), so `useArete(STACK)` with no options is the common case.
|
|
168
|
+
|
|
93
169
|
Notes:
|
|
94
170
|
|
|
95
171
|
- `transport: 'http'` disables streaming view subscriptions, but HTTP-backed surfaces like `queries`, `chain`, and program reads still work.
|
|
96
|
-
-
|
|
172
|
+
- Attached `programs` are keyed by value (program name and id): passing a fresh object literal each render still resolves to the same client.
|
|
173
|
+
- Repeated `useArete` calls with the same stack object and options share one provider-managed client and store. It is safe for sibling components to call the hook independently.
|
|
97
174
|
|
|
98
175
|
## Views
|
|
99
176
|
|
|
100
|
-
View hooks return a `ViewHookResult<T
|
|
177
|
+
View hooks return a discriminated `ViewHookResult<T>`. `status` and `isEmpty`
|
|
178
|
+
narrow the associated data and error fields:
|
|
101
179
|
|
|
102
180
|
```ts
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
181
|
+
if (round.status === 'error') {
|
|
182
|
+
console.error(round.error); // Error
|
|
183
|
+
} else if (round.status === 'ready' && !round.isEmpty) {
|
|
184
|
+
console.log(round.data.id.roundId); // entity, not undefined
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`status` distinguishes "no client yet" (`connecting`) from "subscribed, waiting for the first frame" (`subscribing`). `isPending` covers both states, `isReady` marks a usable ready result, and `isEmpty` distinguishes a ready hook with no entity or list entries from loading. Errors may retain previously committed data, but `isReady` becomes false. `isLoading` remains for compatibility.
|
|
189
|
+
|
|
190
|
+
Use the connection `status` together with `socketIssue` and `retry()` for recovery UI:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
const arete = useArete(ORE_STREAM_STACK);
|
|
194
|
+
|
|
195
|
+
if (arete.canRetry) {
|
|
196
|
+
return <button onClick={() => void arete.retry()}>Reconnect</button>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return <ConnectionBadge status={arete.status} issue={arete.socketIssue} />;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The view hook objects themselves are refresh targets — they refresh that view's *active* subscriptions without you holding a hook result, and are a no-op when nothing is subscribed:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
await arete.views.OreRound.state.refresh({ roundId }); // one keyed subscription
|
|
206
|
+
await arete.views.OreRound.state.refresh(); // every active subscription of the view
|
|
207
|
+
await arete.views.OreRound.latest.refresh(); // list views
|
|
208
|
+
|
|
209
|
+
// Handy in mutation reconciliation — the component doesn't need its own
|
|
210
|
+
// subscriptions just to refresh them:
|
|
211
|
+
claim.submit(args, {
|
|
212
|
+
reconcile: { refresh: [arete.views.OreBoard.state, arete.views.OreMiner.state, preview] },
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
When several resources feed one status line, aggregate them with `summarizeStatuses` instead of hand-rolling loading/error lists:
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
const streams = summarizeStatuses({ Board: board, Round: round, Miner: authority && miner });
|
|
220
|
+
// streams.loading → names still loading, e.g. ['Board']
|
|
221
|
+
// streams.refreshing → names resynchronizing committed data
|
|
222
|
+
// streams.errors → 'Name: message' strings
|
|
223
|
+
// streams.isLoading / streams.isRefreshing / streams.hasError
|
|
109
224
|
```
|
|
110
225
|
|
|
226
|
+
Falsy entries (`authority && miner`) are skipped.
|
|
227
|
+
|
|
111
228
|
List views support:
|
|
112
229
|
|
|
113
230
|
- `.use()`
|
|
114
231
|
- `.use({ take: 1 })`
|
|
115
232
|
- `.useOne()`
|
|
116
233
|
|
|
234
|
+
Put list query configuration (`take`, `skip`, `key`, `partition`, `filters`, `after`, `withSnapshot`, `snapshotLimit`, `schema`, and `onSchemaValidationError`) in the first params object. For compatibility, fields also accepted by view options are honored from the second argument when absent from params; params win when both are supplied. Keep `enabled` and `initialData` in the second argument. State hooks accept one entity as `initialData`, list hooks accept an entity array, and `useOne` accepts one entity.
|
|
235
|
+
|
|
236
|
+
`onSchemaValidationError(diagnostic)` observes entities rejected by a caller-supplied schema. The diagnostic is `{ view, key?, entity, error }`. Accepted entities and existing filtering behavior are unchanged.
|
|
237
|
+
|
|
238
|
+
Protocol v2 gives each parameter set independent ordered membership. Different windows and filters on one view can coexist, while equivalent normalized queries share one reference-counted wire subscription:
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
const firstPage = views.OreRound.latest.use({ take: 10 });
|
|
242
|
+
const secondPage = views.OreRound.latest.use({ take: 10, skip: 10 });
|
|
243
|
+
|
|
244
|
+
if (firstPage.isLoading) return <p>Loading...</p>;
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<>
|
|
248
|
+
{firstPage.isRefreshing && <p>Refreshing...</p>}
|
|
249
|
+
<RoundList rounds={firstPage.data ?? []} />
|
|
250
|
+
<RoundList rounds={secondPage.data ?? []} />
|
|
251
|
+
</>
|
|
252
|
+
);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
During reconnect, hooks preserve their committed data and set `isRefreshing`. A completed authoritative snapshot then atomically replaces membership for that exact query, including an empty result.
|
|
256
|
+
|
|
257
|
+
`await result.refresh()` resolves after the refreshed subscription's next complete snapshot is committed. Registration, send, and subscription failures reject, clear `isRefreshing`, and appear in `result.error`. A view refresh with no active matching subscription remains a no-op.
|
|
258
|
+
|
|
117
259
|
State views support:
|
|
118
260
|
|
|
119
|
-
- `.use(key)`
|
|
261
|
+
- `.use(key)` — subscribe to one keyed entity using the generated key type (for example `{ roundId: bigint }`)
|
|
262
|
+
- `.use(undefined)` — disabled: no subscription, `{ data: undefined, isLoading: false }`. Useful while a wallet is not connected: `views.OreMiner.state.use(authority ? { authority } : undefined)`.
|
|
263
|
+
|
|
264
|
+
Keyed hooks only expose data for the key you passed: when the key changes, `data` becomes `undefined` (and `isLoading` true) until the new key's snapshot arrives. Components never need to re-verify returned data against their inputs.
|
|
265
|
+
|
|
266
|
+
`initialData` is trusted as immediately usable ready data. The first live store update or completed empty snapshot replaces it; an empty list settles as `[]` and an absent state entity settles as `undefined`. Do not seed transaction-authorizing data unless the application is willing to trust that seed.
|
|
267
|
+
|
|
268
|
+
> **BigInt props in development:** entities contain bigints, and React ≤19.2's dev-mode performance track calls `JSON.stringify` on changed props — it crashes on bigint *arrays and objects* (scalar bigints are fine), wedging the app after the first live update. Prefer stack-provided UI fields when they exist (e.g. `deployedPerSquareUi` next to `deployedPerSquare`), use `stringifyBigints(value)` for a typed structural bigint→string conversion of the rest (raw account snapshots are the common case), or upgrade to React 19.3+ where this is fixed.
|
|
269
|
+
|
|
270
|
+
## Reads
|
|
271
|
+
|
|
272
|
+
Stacks can expose one-shot read functions (e.g. RPC-backed previews). Each one gets a React hook:
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
const preview = arete.read.solClaimPreview.use(authority);
|
|
276
|
+
const quote = arete.read.quoteManualDeployment.use(
|
|
277
|
+
input,
|
|
278
|
+
{ debounceMs: 300 },
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- Arguments form the cache key — the read re-runs when they change, and the previous arguments' data is never exposed while the fresh result is loading.
|
|
283
|
+
- The hook is disabled while any required argument is `null`/`undefined`, so no `enabled` flag or non-null assertions are needed.
|
|
284
|
+
- `.use(...args, { debounceMs, initialData })` adds React-only behavior with options last. Debounce applies to automatic argument changes; `refresh()` always runs immediately. When options follow an omitted optional read argument, pass `undefined` in that argument's position.
|
|
285
|
+
- `status` is `'disabled' | 'connecting' | 'loading' | 'ready' | 'refreshing' | 'error'`. The existing loading and refreshing booleans remain available.
|
|
286
|
+
- `isPending`, `isReady`, and `isEmpty` cover the common UI branches without comparing status strings.
|
|
287
|
+
- `preview.refresh()` re-runs the read on demand, typically after a mutation (see below).
|
|
288
|
+
|
|
289
|
+
The same functions remain callable for imperative access: `await arete.read.solClaimPreview(authority)`. The old `arete.reads` namespace remains as a deprecated alias.
|
|
120
290
|
|
|
121
291
|
## Programs, Queries, and Chain
|
|
122
292
|
|
|
123
293
|
`useArete` mirrors the connected core client surface:
|
|
124
294
|
|
|
125
295
|
- `programs.<program>.raw.<instruction>` for raw typed instructions
|
|
126
|
-
- `programs.<program>.
|
|
296
|
+
- `programs.<program>.instructions`, `.transactions`, and `.flows` for semantic operations
|
|
127
297
|
- `programs.<program>.accounts` and `programs.<program>.queries` for HTTP reads
|
|
128
298
|
- `queries` for stack-level HTTP queries
|
|
129
299
|
- `chain` for chain helpers
|
|
130
300
|
|
|
131
301
|
Raw instruction hooks preserve `.execute`, `.build`, and `.useMutation()`.
|
|
132
302
|
|
|
133
|
-
Semantic
|
|
303
|
+
Semantic operation hooks preserve:
|
|
134
304
|
|
|
135
305
|
- `.execute`
|
|
136
|
-
- `.
|
|
137
|
-
- `.resolve`
|
|
138
|
-
- `.plan`
|
|
139
|
-
- `.build`
|
|
140
|
-
- `.stage`
|
|
306
|
+
- `.prepare`
|
|
141
307
|
- `.useMutation()`
|
|
142
308
|
|
|
309
|
+
Mutation hooks expose `mutate(args, options)` for event handlers. It records
|
|
310
|
+
errors in hook state and does not return a promise. Use the equivalent rejecting
|
|
311
|
+
`mutateAsync(args, options)` or `submit(args, options)` when the caller must
|
|
312
|
+
await or compose the result.
|
|
313
|
+
|
|
314
|
+
Program and read hooks are safe to call while the client is still connecting: mutations throw "Arete client is not connected" if submitted early, and reads stay disabled.
|
|
315
|
+
|
|
316
|
+
## Safe amount parsing
|
|
317
|
+
|
|
318
|
+
`safeToRawAmount(input, decimals)` is re-exported by `@usearete/react` for form validation. Unlike `toRawAmount`, it reports invalid input without throwing:
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import { safeToRawAmount } from '@usearete/react';
|
|
322
|
+
|
|
323
|
+
const parsed = safeToRawAmount({ ui: amountText }, 9);
|
|
324
|
+
if (!parsed.success) {
|
|
325
|
+
setAmountError(String(parsed.error));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const rawAmount = parsed.data;
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The return type is `{ success: true, data } | { success: false, error }`.
|
|
333
|
+
|
|
334
|
+
## Default reconciliation
|
|
335
|
+
|
|
336
|
+
Mutations from generated program hooks **reconcile against the stream by default**: after the transaction confirms on-chain, the hook waits until the stack has processed the highest confirmed receipt slot before settling (30s timeout). HTTP-only mutations and results without a receipt slot skip this step. The processed-slot watermark does not promise that a particular entity changed; use `refresh` for the views or one-shot reads that must be re-read.
|
|
337
|
+
|
|
338
|
+
Per-submit options:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
// Opt out entirely:
|
|
342
|
+
deploy.submit(args, { reconcile: false });
|
|
343
|
+
|
|
344
|
+
// Keep the default, but also refresh views and one-shot reads afterwards.
|
|
345
|
+
// `refresh` accepts view hook objects, view/read hook results, or plain
|
|
346
|
+
// callbacks:
|
|
347
|
+
claim.submit(args, {
|
|
348
|
+
reconcile: { refresh: [arete.views.OreBoard.state, miner, preview] },
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Custom timeout:
|
|
352
|
+
deploy.submit(args, { reconcile: { timeoutMs: 10_000 } });
|
|
353
|
+
|
|
354
|
+
// Full control — a function replaces the default:
|
|
355
|
+
deploy.submit(args, { reconcile: async (context) => { /* ... */ } });
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The mutation `phase` field is the discriminated status to branch on for busy labels (`'preparing'`, `'awaiting-wallet'`, `'submitted'`, `'confirmed'`, `'reconciling'`, `'reconciled'`, plus the failure outcomes); `status` remains `'pending'` until the mutation settles. Generated operation hooks publish each completed receipt and signature during `submitted`, before the final operation receipt resolves. `onConfirmed` runs immediately after chain confirmation; `onSuccess` runs only after successful reconciliation (or immediately when reconciliation is disabled/skipped). Reconciliation failure, including a refreshed snapshot timeout, is exposed through `reconciliationError`, does not call `onSuccess`, and does not reject the confirmed `submit()` result. The confirmed transaction remains landed. `retryReconciliation()` repeats only post-confirmation watermark/refresh work using the saved receipts and targets; it never rebuilds, signs, or submits. Convenience booleans (`isPreparing`, `isAwaitingWallet`, `isReconciling`, `canRetryReconciliation`) remain available.
|
|
359
|
+
|
|
360
|
+
## Migration notes (0.3 → next)
|
|
361
|
+
|
|
362
|
+
- `AreteProvider` accepts a default `stack` and `stackOptions`; `useArete()` may then be called with no arguments. Register the stack type via `AreteDefaultStackRegistry` for full inference.
|
|
363
|
+
- View hook results expose a `status` field (`'disabled' | 'connecting' | 'subscribing' | 'ready' | 'error'`).
|
|
364
|
+
- View hook objects (`arete.views.X.state` / `.list`) expose `refresh(key?)` and can be passed directly in `reconcile: { refresh: [...] }`.
|
|
365
|
+
- New `summarizeStatuses(namedResults)` status aggregator; `summarizeViews` remains as a deprecated alias.
|
|
366
|
+
- New `stringifyBigints(value)` display helper (also re-exported from `@usearete/sdk`).
|
|
367
|
+
- `UseAreteResult`'s second type parameter defaults to `undefined`.
|
|
368
|
+
- The duplicate low-level `useView` and `useEntity` hooks were removed. Use generated `arete.views.<Entity>.<view>.use(...)` hooks.
|
|
369
|
+
|
|
370
|
+
## Migration notes (0.2 → 0.3)
|
|
371
|
+
|
|
372
|
+
- **Generated-hook mutations reconcile by default.** `submit()` now resolves after stream reconciliation instead of right after confirmation. Timeouts settle as `confirmed-unreconciled` rather than failing. Pass `reconcile: false` to restore the old behavior.
|
|
373
|
+
- **State view keys are required and generated.** Pass the generated key object, or explicitly pass `undefined`/`null` to disable the hook. Zero-argument state subscriptions are no longer supported, and malformed keys throw the shared core serializer error.
|
|
374
|
+
- **Disabled view hooks report `isLoading: false`.** Previously `enabled: false` still started in a loading state.
|
|
375
|
+
- New additive `UseMutationResult` fields: `isPreparing`, `isAwaitingWallet`, `isReconciling`.
|
|
376
|
+
- New mutation callback: `onConfirmed`, for work that must run after chain confirmation even if stream reconciliation later fails.
|
|
377
|
+
- `useArete` now exposes `socketIssue` and deduplicated `retry()`.
|
|
378
|
+
- Stack reads expose imperative calls and hooks together under `arete.read.<name>`; `arete.reads` remains a deprecated alias.
|
|
379
|
+
- Repeated `useArete` calls now share a provider-managed client when stack and option identities match; remove component-local connection ownership workarounds.
|
|
380
|
+
|
|
143
381
|
## Low-Level Hooks
|
|
144
382
|
|
|
145
383
|
The React SDK also exports:
|
|
146
384
|
|
|
147
385
|
- `useConnectionState`
|
|
148
|
-
- `useView`
|
|
149
|
-
- `useEntity`
|
|
150
386
|
- `useAreteContext`
|
|
151
387
|
|
|
152
388
|
These accept the same client lookup overrides when you need to target a non-default client.
|