@usearete/react 0.1.5 → 0.3.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 +350 -61
- package/dist/index.cjs +2344 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +516 -66
- package/dist/index.esm.js +1800 -9653
- 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 -10033
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,108 +1,397 @@
|
|
|
1
1
|
# Arete React SDK
|
|
2
2
|
|
|
3
|
-
React
|
|
3
|
+
React hooks and provider for consuming Arete stacks in React applications.
|
|
4
4
|
|
|
5
|
-
Built on top of [`@usearete/sdk`](https://www.npmjs.com/package/@usearete/sdk), the
|
|
5
|
+
Built on top of [`@usearete/sdk`](https://www.npmjs.com/package/@usearete/sdk), the framework-agnostic TypeScript core SDK.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @usearete/react
|
|
10
|
+
npm install @usearete/react @usearete/sdk zod
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
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
14
|
|
|
15
|
-
|
|
15
|
+
### Hooks linting
|
|
16
16
|
|
|
17
|
-
|
|
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
|
+
}];
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> Not using React? Use [`@usearete/sdk`](../core/README.md) directly.
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
Keep the generated stack explicit at the provider and consumer boundaries. Equivalent calls share one provider-managed client and store:
|
|
18
39
|
|
|
19
40
|
```tsx
|
|
20
|
-
import { AreteProvider, useArete
|
|
41
|
+
import { AreteProvider, useArete } from '@usearete/react';
|
|
42
|
+
import { ORE_STREAM_STACK } from './generated/ore-stack';
|
|
43
|
+
|
|
44
|
+
const publishableKey = import.meta.env.VITE_ARETE_PUBLISHABLE_KEY;
|
|
45
|
+
if (!publishableKey) throw new Error('VITE_ARETE_PUBLISHABLE_KEY is required');
|
|
46
|
+
|
|
47
|
+
function Dashboard() {
|
|
48
|
+
const arete = useArete(ORE_STREAM_STACK);
|
|
49
|
+
const board = arete.views.OreBoard.state.use({
|
|
50
|
+
address: arete.addresses.board(),
|
|
51
|
+
});
|
|
52
|
+
const roundId = board.data?.state.roundId;
|
|
53
|
+
const round = arete.views.OreRound.state.use(
|
|
54
|
+
roundId == null ? undefined : { roundId },
|
|
55
|
+
);
|
|
21
56
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
57
|
+
if (arete.status === 'error') {
|
|
58
|
+
return <button onClick={() => void arete.retry()}>Reconnect</button>;
|
|
59
|
+
}
|
|
60
|
+
if (board.status !== 'ready' || round.status !== 'ready') {
|
|
61
|
+
return <p>Connecting...</p>;
|
|
62
|
+
}
|
|
63
|
+
|
|
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
|
+
);
|
|
70
|
+
}
|
|
25
71
|
|
|
26
|
-
function App() {
|
|
72
|
+
export default function App() {
|
|
27
73
|
return (
|
|
28
|
-
<AreteProvider
|
|
29
|
-
|
|
74
|
+
<AreteProvider
|
|
75
|
+
autoConnect
|
|
76
|
+
stack={ORE_STREAM_STACK}
|
|
77
|
+
auth={{ publishableKey }}
|
|
78
|
+
>
|
|
79
|
+
<Dashboard />
|
|
30
80
|
</AreteProvider>
|
|
31
81
|
);
|
|
32
82
|
}
|
|
83
|
+
```
|
|
33
84
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|
|
37
110
|
}
|
|
38
111
|
```
|
|
39
112
|
|
|
40
|
-
|
|
113
|
+
Multi-stack apps can keep passing the stack explicitly: `useArete(stack, options)` always wins over the provider default.
|
|
114
|
+
|
|
115
|
+
## Provider
|
|
41
116
|
|
|
42
|
-
|
|
43
|
-
- **React Integration**: Hooks-based API for easy integration with React applications
|
|
44
|
-
- **State Management**: Built-in state management with Zustand
|
|
45
|
-
- **Type Safety**: Full TypeScript support with comprehensive type definitions
|
|
46
|
-
- **View Definitions**: Create state and list views for your data
|
|
47
|
-
- **Transaction Handling**: Define and execute transactions with hooks
|
|
48
|
-
- **Single Item Queries**: Type-safe single item fetching with `take: 1` or `useOne()`
|
|
117
|
+
`AreteProvider` manages connected clients for descendant hooks.
|
|
49
118
|
|
|
50
|
-
|
|
119
|
+
Supported props:
|
|
51
120
|
|
|
52
|
-
|
|
121
|
+
- `stack` — default stack for argument-less `useArete()` calls
|
|
122
|
+
- `stackOptions` — default client lookup options (`url`, `httpUrl`, `transport`, `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
|
|
125
|
+
- `wallet`
|
|
126
|
+
- `auth`
|
|
127
|
+
- `fetch`
|
|
128
|
+
- `validateFrames` — set `false` to suppress rejected-frame warnings; generated schemas still normalize and validate entities
|
|
129
|
+
- `onFrameValidationError` — structured callback for generated-schema rejections
|
|
130
|
+
- `reconnectIntervals`
|
|
131
|
+
- `maxReconnectAttempts`
|
|
132
|
+
- `maxEntriesPerView`
|
|
133
|
+
- `flushIntervalMs`
|
|
53
134
|
|
|
54
|
-
-
|
|
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.
|
|
55
136
|
|
|
56
|
-
|
|
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.
|
|
57
138
|
|
|
58
|
-
|
|
59
|
-
- `useAreteContext` - Access the runtime context directly
|
|
139
|
+
## `useArete(stack?, options?)`
|
|
60
140
|
|
|
61
|
-
|
|
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.
|
|
62
142
|
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
143
|
+
- `views`
|
|
144
|
+
- `programs`
|
|
145
|
+
- `queries`
|
|
146
|
+
- `read` — callable stack reads with `.use(...args, options?)` React hooks
|
|
147
|
+
- `chain`
|
|
148
|
+
- `client`
|
|
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
|
|
152
|
+
- `isConnected`
|
|
153
|
+
- `isLoading`
|
|
154
|
+
- `canRetry` — true when `retry()` can start a new shared connection attempt
|
|
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
|
|
66
158
|
|
|
67
|
-
|
|
159
|
+
Supported hook options:
|
|
68
160
|
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
71
|
-
- `
|
|
72
|
-
- `
|
|
161
|
+
- `url`
|
|
162
|
+
- `httpUrl`
|
|
163
|
+
- `transport`
|
|
164
|
+
- `programs`
|
|
73
165
|
|
|
74
|
-
|
|
166
|
+
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.
|
|
75
167
|
|
|
76
|
-
|
|
168
|
+
Notes:
|
|
77
169
|
|
|
78
|
-
|
|
170
|
+
- `transport: 'http'` disables streaming view subscriptions, but HTTP-backed surfaces like `queries`, `chain`, and program reads still work.
|
|
171
|
+
- Attached `programs` are keyed by value (program name and id): passing a fresh object literal each render still resolves to the same client.
|
|
172
|
+
- 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.
|
|
79
173
|
|
|
80
|
-
|
|
174
|
+
## Views
|
|
81
175
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- `EntityStore` - State management
|
|
85
|
-
- AsyncIterable-based streaming APIs
|
|
176
|
+
View hooks return a discriminated `ViewHookResult<T>`. `status` and `isEmpty`
|
|
177
|
+
narrow the associated data and error fields:
|
|
86
178
|
|
|
87
|
-
|
|
179
|
+
```ts
|
|
180
|
+
if (round.status === 'error') {
|
|
181
|
+
console.error(round.error); // Error
|
|
182
|
+
} else if (round.status === 'ready' && !round.isEmpty) {
|
|
183
|
+
console.log(round.data.id.roundId); // entity, not undefined
|
|
184
|
+
}
|
|
185
|
+
```
|
|
88
186
|
|
|
89
|
-
|
|
90
|
-
- `useArete` - Main hook for accessing stacks
|
|
91
|
-
- `useConnectionState` - Connection monitoring hook
|
|
92
|
-
- `defineStack`, `createStateView`, `createListView` - React-friendly factories
|
|
187
|
+
`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.
|
|
93
188
|
|
|
94
|
-
|
|
189
|
+
Use the connection `status` together with `socketIssue` and `retry()` for recovery UI:
|
|
95
190
|
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
191
|
+
```tsx
|
|
192
|
+
const arete = useArete(ORE_STREAM_STACK);
|
|
193
|
+
|
|
194
|
+
if (arete.canRetry) {
|
|
195
|
+
return <button onClick={() => void arete.retry()}>Reconnect</button>;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return <ConnectionBadge status={arete.status} issue={arete.socketIssue} />;
|
|
100
199
|
```
|
|
101
200
|
|
|
102
|
-
|
|
201
|
+
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:
|
|
103
202
|
|
|
104
|
-
|
|
203
|
+
```ts
|
|
204
|
+
await arete.views.OreRound.state.refresh({ roundId }); // one keyed subscription
|
|
205
|
+
await arete.views.OreRound.state.refresh(); // every active subscription of the view
|
|
206
|
+
await arete.views.OreRound.latest.refresh(); // list views
|
|
207
|
+
|
|
208
|
+
// Handy in mutation reconciliation — the component doesn't need its own
|
|
209
|
+
// subscriptions just to refresh them:
|
|
210
|
+
claim.submit(args, {
|
|
211
|
+
reconcile: { refresh: [arete.views.OreBoard.state, arete.views.OreMiner.state, preview] },
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
When several resources feed one status line, aggregate them with `summarizeStatuses` instead of hand-rolling loading/error lists:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
const streams = summarizeStatuses({ Board: board, Round: round, Miner: authority && miner });
|
|
219
|
+
// streams.loading → names still loading, e.g. ['Board']
|
|
220
|
+
// streams.refreshing → names resynchronizing committed data
|
|
221
|
+
// streams.errors → 'Name: message' strings
|
|
222
|
+
// streams.isLoading / streams.isRefreshing / streams.hasError
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Falsy entries (`authority && miner`) are skipped.
|
|
226
|
+
|
|
227
|
+
List views support:
|
|
228
|
+
|
|
229
|
+
- `.use()`
|
|
230
|
+
- `.use({ take: 1 })`
|
|
231
|
+
- `.useOne()`
|
|
232
|
+
|
|
233
|
+
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.
|
|
234
|
+
|
|
235
|
+
`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.
|
|
236
|
+
|
|
237
|
+
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:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
const firstPage = views.OreRound.latest.use({ take: 10 });
|
|
241
|
+
const secondPage = views.OreRound.latest.use({ take: 10, skip: 10 });
|
|
242
|
+
|
|
243
|
+
if (firstPage.isLoading) return <p>Loading...</p>;
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<>
|
|
247
|
+
{firstPage.isRefreshing && <p>Refreshing...</p>}
|
|
248
|
+
<RoundList rounds={firstPage.data ?? []} />
|
|
249
|
+
<RoundList rounds={secondPage.data ?? []} />
|
|
250
|
+
</>
|
|
251
|
+
);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
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.
|
|
255
|
+
|
|
256
|
+
`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.
|
|
257
|
+
|
|
258
|
+
State views support:
|
|
259
|
+
|
|
260
|
+
- `.use(key)` — subscribe to one keyed entity using the generated key type (for example `{ roundId: bigint }`)
|
|
261
|
+
- `.use(undefined)` — disabled: no subscription, `{ data: undefined, isLoading: false }`. Useful while a wallet is not connected: `views.OreMiner.state.use(authority ? { authority } : undefined)`.
|
|
105
262
|
|
|
106
|
-
|
|
263
|
+
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.
|
|
107
264
|
|
|
108
|
-
|
|
265
|
+
`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.
|
|
266
|
+
|
|
267
|
+
> **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.
|
|
268
|
+
|
|
269
|
+
## Reads
|
|
270
|
+
|
|
271
|
+
Stacks can expose one-shot read functions (e.g. RPC-backed previews). Each one gets a React hook:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
const preview = arete.read.solClaimPreview.use(authority);
|
|
275
|
+
const quote = arete.read.quoteManualDeployment.use(
|
|
276
|
+
input,
|
|
277
|
+
{ debounceMs: 300 },
|
|
278
|
+
);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- 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.
|
|
282
|
+
- The hook is disabled while any required argument is `null`/`undefined`, so no `enabled` flag or non-null assertions are needed.
|
|
283
|
+
- `.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.
|
|
284
|
+
- `status` is `'disabled' | 'connecting' | 'loading' | 'ready' | 'refreshing' | 'error'`. The existing loading and refreshing booleans remain available.
|
|
285
|
+
- `isPending`, `isReady`, and `isEmpty` cover the common UI branches without comparing status strings.
|
|
286
|
+
- `preview.refresh()` re-runs the read on demand, typically after a mutation (see below).
|
|
287
|
+
|
|
288
|
+
The same functions remain callable for imperative access: `await arete.read.solClaimPreview(authority)`. The old `arete.reads` namespace remains as a deprecated alias.
|
|
289
|
+
|
|
290
|
+
## Programs, Queries, and Chain
|
|
291
|
+
|
|
292
|
+
`useArete` mirrors the connected core client surface:
|
|
293
|
+
|
|
294
|
+
- `programs.<program>.raw.<instruction>` for raw typed instructions
|
|
295
|
+
- `programs.<program>.instructions`, `.transactions`, and `.flows` for semantic operations
|
|
296
|
+
- `programs.<program>.accounts` and `programs.<program>.queries` for HTTP reads
|
|
297
|
+
- `queries` for stack-level HTTP queries
|
|
298
|
+
- `chain` for chain helpers
|
|
299
|
+
|
|
300
|
+
Raw instruction hooks preserve `.execute`, `.build`, and `.useMutation()`.
|
|
301
|
+
|
|
302
|
+
Semantic operation hooks preserve:
|
|
303
|
+
|
|
304
|
+
- `.execute`
|
|
305
|
+
- `.prepare`
|
|
306
|
+
- `.useMutation()`
|
|
307
|
+
|
|
308
|
+
Mutation hooks expose `mutate(args, options)` for event handlers. It records
|
|
309
|
+
errors in hook state and does not return a promise. Use the equivalent rejecting
|
|
310
|
+
`mutateAsync(args, options)` or `submit(args, options)` when the caller must
|
|
311
|
+
await or compose the result.
|
|
312
|
+
|
|
313
|
+
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.
|
|
314
|
+
|
|
315
|
+
## Safe amount parsing
|
|
316
|
+
|
|
317
|
+
`safeToRawAmount(input, decimals)` is re-exported by `@usearete/react` for form validation. Unlike `toRawAmount`, it reports invalid input without throwing:
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { safeToRawAmount } from '@usearete/react';
|
|
321
|
+
|
|
322
|
+
const parsed = safeToRawAmount({ ui: amountText }, 9);
|
|
323
|
+
if (!parsed.success) {
|
|
324
|
+
setAmountError(String(parsed.error));
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const rawAmount = parsed.data;
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The return type is `{ success: true, data } | { success: false, error }`.
|
|
332
|
+
|
|
333
|
+
## Default reconciliation
|
|
334
|
+
|
|
335
|
+
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.
|
|
336
|
+
|
|
337
|
+
Per-submit options:
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
// Opt out entirely:
|
|
341
|
+
deploy.submit(args, { reconcile: false });
|
|
342
|
+
|
|
343
|
+
// Keep the default, but also refresh views and one-shot reads afterwards.
|
|
344
|
+
// `refresh` accepts view hook objects, view/read hook results, or plain
|
|
345
|
+
// callbacks:
|
|
346
|
+
claim.submit(args, {
|
|
347
|
+
reconcile: { refresh: [arete.views.OreBoard.state, miner, preview] },
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Custom timeout:
|
|
351
|
+
deploy.submit(args, { reconcile: { timeoutMs: 10_000 } });
|
|
352
|
+
|
|
353
|
+
// Full control — a function replaces the default:
|
|
354
|
+
deploy.submit(args, { reconcile: async (context) => { /* ... */ } });
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
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.
|
|
358
|
+
|
|
359
|
+
## Migration notes (0.3 → next)
|
|
360
|
+
|
|
361
|
+
- `AreteProvider` accepts a default `stack` and `stackOptions`; `useArete()` may then be called with no arguments. Register the stack type via `AreteDefaultStackRegistry` for full inference.
|
|
362
|
+
- View hook results expose a `status` field (`'disabled' | 'connecting' | 'subscribing' | 'ready' | 'error'`).
|
|
363
|
+
- View hook objects (`arete.views.X.state` / `.list`) expose `refresh(key?)` and can be passed directly in `reconcile: { refresh: [...] }`.
|
|
364
|
+
- New `summarizeStatuses(namedResults)` status aggregator; `summarizeViews` remains as a deprecated alias.
|
|
365
|
+
- New `stringifyBigints(value)` display helper (also re-exported from `@usearete/sdk`).
|
|
366
|
+
- `UseAreteResult`'s second type parameter defaults to `undefined`.
|
|
367
|
+
- The duplicate low-level `useView` and `useEntity` hooks were removed. Use generated `arete.views.<Entity>.<view>.use(...)` hooks.
|
|
368
|
+
|
|
369
|
+
## Migration notes (0.2 → 0.3)
|
|
370
|
+
|
|
371
|
+
- **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.
|
|
372
|
+
- **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.
|
|
373
|
+
- **Disabled view hooks report `isLoading: false`.** Previously `enabled: false` still started in a loading state.
|
|
374
|
+
- New additive `UseMutationResult` fields: `isPreparing`, `isAwaitingWallet`, `isReconciling`.
|
|
375
|
+
- New mutation callback: `onConfirmed`, for work that must run after chain confirmation even if stream reconciliation later fails.
|
|
376
|
+
- `useArete` now exposes `socketIssue` and deduplicated `retry()`.
|
|
377
|
+
- Stack reads expose imperative calls and hooks together under `arete.read.<name>`; `arete.reads` remains a deprecated alias.
|
|
378
|
+
- Repeated `useArete` calls now share a provider-managed client when stack and option identities match; remove component-local connection ownership workarounds.
|
|
379
|
+
|
|
380
|
+
## Low-Level Hooks
|
|
381
|
+
|
|
382
|
+
The React SDK also exports:
|
|
383
|
+
|
|
384
|
+
- `useConnectionState`
|
|
385
|
+
- `useAreteContext`
|
|
386
|
+
|
|
387
|
+
These accept the same client lookup overrides when you need to target a non-default client.
|
|
388
|
+
|
|
389
|
+
## Relationship with `@usearete/sdk`
|
|
390
|
+
|
|
391
|
+
`@usearete/react` re-exports selected core APIs and types for convenience, but it is not a complete mirror of the core package.
|
|
392
|
+
|
|
393
|
+
If you need the full low-level surface, import directly from `@usearete/sdk`.
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
MIT
|