@w6w/react 0.6.0 → 0.9.12
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 +214 -44
- package/dist/mod.d.ts +4 -3
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +5 -3
- package/dist/mod.js.map +1 -1
- package/dist/src/W6WProvider.d.ts +89 -39
- package/dist/src/W6WProvider.d.ts.map +1 -1
- package/dist/src/W6WProvider.js +104 -57
- package/dist/src/W6WProvider.js.map +1 -1
- package/dist/src/adapter.d.ts +157 -2
- package/dist/src/adapter.d.ts.map +1 -1
- package/dist/src/adapter.js +97 -1
- package/dist/src/adapter.js.map +1 -1
- package/dist/src/flow-workflow.d.ts +109 -0
- package/dist/src/flow-workflow.d.ts.map +1 -0
- package/dist/src/flow-workflow.js +72 -0
- package/dist/src/flow-workflow.js.map +1 -0
- package/dist/src/hooks.d.ts +90 -11
- package/dist/src/hooks.d.ts.map +1 -1
- package/dist/src/hooks.js +181 -42
- package/dist/src/hooks.js.map +1 -1
- package/dist/src/single-flight.d.ts +33 -0
- package/dist/src/single-flight.d.ts.map +1 -0
- package/dist/src/single-flight.js +71 -0
- package/dist/src/single-flight.js.map +1 -0
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
React bindings for [`@w6w/sdk`](../node) — a `<W6WProvider>` that holds one memoized
|
|
4
4
|
`W6WClient` with per-request token freshness, a small hook set over the SDK's public
|
|
5
|
-
surface (`me`, `documents`, `vars`, `connections`, `workflows`, `run`), and
|
|
5
|
+
surface (`me`, `documents`, `vars`, `connections`, `workflows`, `functions`, `run`), and
|
|
6
6
|
`createW6WUiAdapter`, a structural bridge from a `W6WClient` to
|
|
7
7
|
[`@w6w/ui`](https://github.com/w6w-io/w6w-ui)'s `W6WApi` contract.
|
|
8
8
|
|
|
9
|
-
License: MIT · Version: 0.
|
|
9
|
+
License: MIT · Version: 0.9.12 · Guides: [docs.w6w.io/clients/react](https://docs.w6w.io/clients/react/)
|
|
10
|
+
(with a [hooks reference](https://docs.w6w.io/clients/react/hooks/) and a
|
|
11
|
+
[Next.js walkthrough](https://docs.w6w.io/clients/react/nextjs/))
|
|
10
12
|
|
|
11
13
|
This lane implements no endpoint — it composes `@w6w/sdk`, which is already
|
|
12
14
|
conformant against [`endpoints.json`](../endpoints.json). There is nothing here to
|
|
@@ -18,6 +20,10 @@ re-verify against the wire contract; verify `@w6w/sdk` instead.
|
|
|
18
20
|
npm install @w6w/react react
|
|
19
21
|
```
|
|
20
22
|
|
|
23
|
+
`@w6w/sdk` comes in as a dependency. Add it to your own `package.json` too
|
|
24
|
+
(`npm install @w6w/sdk`) if you import from it directly — `ApiError` for an
|
|
25
|
+
`instanceof` check, or `exchangeToken` from `@w6w/sdk/server` on your backend.
|
|
26
|
+
|
|
21
27
|
`react-dom` is not a dependency of this package — `<W6WProvider>`'s only JSX is a
|
|
22
28
|
context wrapper (`<Ctx.Provider>`), never a DOM element of its own. Your app already
|
|
23
29
|
depends on `react-dom` to mount itself; nothing extra is needed here.
|
|
@@ -38,11 +44,16 @@ function App() {
|
|
|
38
44
|
}
|
|
39
45
|
```
|
|
40
46
|
|
|
41
|
-
`token` accepts a literal string, or a supplier
|
|
42
|
-
request
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
`token` accepts a literal string, or a supplier — sync or **async** — called per
|
|
48
|
+
request (requests in flight at the same time share ONE supplier call; nothing is cached once
|
|
49
|
+
it settles, so memoize an expensive supplier yourself): `() => getCurrentJwt()` or `async () => await getCurrentJwt()` both
|
|
50
|
+
work, and either is awaited before the bearer is attached. Pass a supplier when your
|
|
51
|
+
token rotates (e.g. a short-lived JWT read from an auth SDK, or minted on demand by
|
|
52
|
+
your own backend) and the client picks up the new value on the very next call, with no
|
|
53
|
+
teardown/rebuild of the underlying `W6WClient`. A nullish or blank result (from either
|
|
54
|
+
form) never reaches the wire: every read hook below reports `loading: true` instead of
|
|
55
|
+
sending a request with no credential. See `src/W6WProvider.tsx`'s module header for the
|
|
56
|
+
exact mechanism.
|
|
46
57
|
|
|
47
58
|
Then, anywhere under the provider:
|
|
48
59
|
|
|
@@ -61,6 +72,70 @@ function DocList() {
|
|
|
61
72
|
}
|
|
62
73
|
```
|
|
63
74
|
|
|
75
|
+
## Embedding for enterprise tenants
|
|
76
|
+
|
|
77
|
+
A host juggling more than one signed-in identity — or one that does not have a
|
|
78
|
+
token/identity yet at mount time — needs more than the Quick start's one-shot
|
|
79
|
+
`token` prop. `<W6WProvider>` takes five more props for exactly this:
|
|
80
|
+
|
|
81
|
+
- **`ready`** (default `true`) — set it `false` until you actually have a
|
|
82
|
+
token/identity to embed with. While `false`, every read hook reports
|
|
83
|
+
`loading: true` and none of them calls the SDK at all; flipping it back to
|
|
84
|
+
`true` fetches.
|
|
85
|
+
- **`identityKey`** — a string identifying WHICH signed-in identity this
|
|
86
|
+
client speaks for, e.g. `` `${subject}:${account}` ``. Changing it rebuilds
|
|
87
|
+
the underlying `W6WClient` and resets every read hook's state — the
|
|
88
|
+
supported way to switch accounts without a stale read from the previous
|
|
89
|
+
identity ever rendering under the new one.
|
|
90
|
+
- **`refreshOnUnauthorized`** + **`onUnauthorized`** — opt in to a single,
|
|
91
|
+
one-shot recovery when a request 401s with `{code: "unauthorized"}`: your
|
|
92
|
+
`token` supplier is called once more with `{forceRefresh: true}`, and a
|
|
93
|
+
usable result retries the same request once. `onUnauthorized` receives the
|
|
94
|
+
terminal error — after a failed retry, or immediately when recovery is off
|
|
95
|
+
or not possible — at most once per call.
|
|
96
|
+
- **`headers`** — default headers sent with every request. This is how a
|
|
97
|
+
tenant on the custom-authorizer integration path (no OIDC on your side)
|
|
98
|
+
attaches the tenant header your w6w account's authorizer webhook expects
|
|
99
|
+
(e.g. `headers={{ "X-W6W-Tenant": tenantId }}`) alongside the bearer token.
|
|
100
|
+
Compared by content, not identity — a new-but-equal object on every render
|
|
101
|
+
never rebuilds the client.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
"use client";
|
|
105
|
+
|
|
106
|
+
import { W6WProvider } from "@w6w/react";
|
|
107
|
+
|
|
108
|
+
export default function W6WLayout({ children }: { children: React.ReactNode }) {
|
|
109
|
+
return (
|
|
110
|
+
<W6WProvider
|
|
111
|
+
baseUrl="https://api.example.com"
|
|
112
|
+
ready={true}
|
|
113
|
+
identityKey={`${session.subject}:${session.account}`}
|
|
114
|
+
headers={{ "X-W6W-Tenant": session.tenantId }}
|
|
115
|
+
refreshOnUnauthorized
|
|
116
|
+
onUnauthorized={() => redirectToLogin()}
|
|
117
|
+
token={async () => {
|
|
118
|
+
// Your own backend route, not w6w's — it holds whatever credential
|
|
119
|
+
// mints a w6w token for this signed-in user (a tenant exchange, a
|
|
120
|
+
// cached short-lived token, …). This is a Next.js App Router layout,
|
|
121
|
+
// so it is a Client Component ("use client" above) even though the
|
|
122
|
+
// token-minting route itself runs on your server.
|
|
123
|
+
const res = await fetch("/api/w6w-token");
|
|
124
|
+
if (!res.ok) return null;
|
|
125
|
+
const { token } = await res.json();
|
|
126
|
+
return token;
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
{children}
|
|
130
|
+
</W6WProvider>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The [Next.js walkthrough](https://docs.w6w.io/clients/react/nextjs/) builds the
|
|
136
|
+
`/api/w6w-token` route this example fetches from, with `exchangeToken` and a
|
|
137
|
+
cached, refreshable token supplier.
|
|
138
|
+
|
|
64
139
|
## Using this package with `@w6w/ui`
|
|
65
140
|
|
|
66
141
|
`@w6w/ui`'s components (e.g. `AppPicker`, `ActionTestForm`, the flow editor) take a
|
|
@@ -68,6 +143,7 @@ function DocList() {
|
|
|
68
143
|
`@w6w/react` client with `createW6WUiAdapter`:
|
|
69
144
|
|
|
70
145
|
```tsx
|
|
146
|
+
import { useMemo } from "react";
|
|
71
147
|
import { createW6WUiAdapter, useW6WClient, W6WProvider } from "@w6w/react";
|
|
72
148
|
import { W6WUIProvider, AppPicker } from "@w6w/ui";
|
|
73
149
|
|
|
@@ -88,23 +164,86 @@ function App() {
|
|
|
88
164
|
}
|
|
89
165
|
```
|
|
90
166
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
`
|
|
102
|
-
|
|
103
|
-
**`client.console.*` is documented
|
|
104
|
-
excluded from `endpoints.json`'s
|
|
105
|
-
That means a `console.*`
|
|
106
|
-
|
|
107
|
-
`@w6w/
|
|
167
|
+
`@w6w/ui@0.1.0` is available on npm. Install it with `npm i @w6w/ui`.
|
|
168
|
+
`createW6WUiAdapter` targets its `W6WApi` contract structurally and has no runtime
|
|
169
|
+
dependency on the UI package.
|
|
170
|
+
|
|
171
|
+
`createW6WUiAdapter` is built on `@w6w/sdk/console` for most members — the SAME
|
|
172
|
+
namespace `packages/studio`'s own facade uses for these routes
|
|
173
|
+
(`packages/ui/src/createW6WApi.ts` is the *other* hand-rolled client for them; this
|
|
174
|
+
package is not a third one) — and on the BASE `client.functions.*`/
|
|
175
|
+
`client.workflows.*` surface for `listFunctions`/`getFunction`/`invokeFunction`/
|
|
176
|
+
`listWorkflows`/`getWorkflow`/`runWorkflow`. `listAppsByIds` and `listConnections` go through
|
|
177
|
+
the public `client.apps.byIds` and `client.connections.list`. **The adapter is the supported
|
|
178
|
+
seam:** hand `createW6WUiAdapter(client)` to the UI as it is, and do not reach for the
|
|
179
|
+
console-backed internals or patch members onto it yourself. **`client.console.*` is documented
|
|
180
|
+
"Studio-internal… unstable" and is deliberately excluded from `endpoints.json`'s
|
|
181
|
+
conformance runner** (`node/src/client.ts:114-116`). That means a `console.*`
|
|
182
|
+
signature change ships with no lockstep protection for this bridge beyond
|
|
183
|
+
`@w6w/sdk`'s own version — pin your `@w6w/sdk` version alongside `@w6w/react`'s, and
|
|
184
|
+
re-test the bridge on an upgrade rather than assuming it.
|
|
185
|
+
|
|
186
|
+
### Keeping `createW6WUiAdapter` in sync with the real `@w6w/ui`
|
|
187
|
+
|
|
188
|
+
`package.json` pins `@w6w/ui` to npm version `0.1.0` as a dev dependency.
|
|
189
|
+
Consumers of `@w6w/react` do not install that dependency automatically; it is used
|
|
190
|
+
only by the conformance checks. The release workflow can install it in a clean
|
|
191
|
+
checkout without sibling repositories.
|
|
192
|
+
|
|
193
|
+
`src/__tests__/ui-conformance.check.ts` imports `W6WApi` and `FlowWorkflow` from
|
|
194
|
+
the UI package's public exports. It checks the adapter's return type and the
|
|
195
|
+
workflow converter in both directions. The runtime suite reads the installed
|
|
196
|
+
`dist/provider.d.ts` declaration to enumerate the real interface members.
|
|
197
|
+
|
|
198
|
+
To update the pin, change the npm version in `package.json`, refresh
|
|
199
|
+
`package-lock.json`, and run `npm test` and `npm run build`. These checks report
|
|
200
|
+
any incompatible UI contract changes.
|
|
201
|
+
|
|
202
|
+
**Runtime conformance.** Assignability is checked at compile time only, so it cannot
|
|
203
|
+
see a member that is typed but missing or mis-wired at run time.
|
|
204
|
+
`src/__tests__/adapter-conformance.test.ts` closes that gap. It reads the member names
|
|
205
|
+
(21 required, 8 optional) from the installed `@w6w/ui` `provider.d.ts` declaration — not from
|
|
206
|
+
this package's hand-copied interface — and asserts each is a function on
|
|
207
|
+
`createW6WUiAdapter(client)`. It then drives every member with sentinel arguments
|
|
208
|
+
through a stub transport and asserts the exact wire request produced (forwarding), and
|
|
209
|
+
asserts that a 4xx JSON error body reaches the caller as `err.body`, deep-equal to
|
|
210
|
+
what the server sent. Nothing in it imports `@w6w/ui` at run time; the pinned UI is
|
|
211
|
+
read as text.
|
|
212
|
+
|
|
213
|
+
`toFlowWorkflow` does not validate `steps[].uses`, although the returned type declares it
|
|
214
|
+
required; a step without `uses` converts, so guard before handing it to code that reads
|
|
215
|
+
`step.uses.app`.
|
|
216
|
+
|
|
217
|
+
### Editing workflows with a typed shape
|
|
218
|
+
|
|
219
|
+
`@w6w/sdk` keeps a workflow definition opaque (`WorkflowDefinition`, a
|
|
220
|
+
`Record<string, unknown>`) on purpose, so a newer server's extra fields never break a
|
|
221
|
+
client. The visual editor wants a typed shape, so this package exports a validating
|
|
222
|
+
converter instead of a cast:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { fromFlowWorkflow, toFlowWorkflow, WorkflowShapeError } from "@w6w/react";
|
|
226
|
+
|
|
227
|
+
const { workflow } = await client.workflows.get(id);
|
|
228
|
+
try {
|
|
229
|
+
const flow = toFlowWorkflow(workflow); // FlowWorkflowShape
|
|
230
|
+
// ... hand `flow` to the editor, receive an edited copy back ...
|
|
231
|
+
await client.workflows.update(id, fromFlowWorkflow(edited));
|
|
232
|
+
} catch (err) {
|
|
233
|
+
if (err instanceof WorkflowShapeError) console.error(err.issues); // [{ path, message }, ...]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`toFlowWorkflow` requires string `manifestVersion`/`id`/`name` and a `steps` array of
|
|
238
|
+
objects with a string `id` (plus `edges` an array, `settings` an object, `variables`
|
|
239
|
+
an array, when present); anything else throws `WorkflowShapeError` whose `issues` lists
|
|
240
|
+
every violation. It does not inspect the inside of `steps[].uses` or any other nested
|
|
241
|
+
field. Unknown keys (`retry`, `onError`, `ports`, a host's `x-*` extension) are copied
|
|
242
|
+
through in both directions, so `fromFlowWorkflow(toFlowWorkflow(def))` deep-equals
|
|
243
|
+
`def`. `FlowWorkflowShape` is a structural mirror of `@w6w/ui`'s `FlowWorkflow`, with no
|
|
244
|
+
runtime `@w6w/ui` dependency; `ui-conformance.check.ts` pins it both ways (ui's
|
|
245
|
+
`FlowWorkflow` is accepted by `fromFlowWorkflow`, and the mirror is assignable to ui's
|
|
246
|
+
`FlowWorkflow`).
|
|
108
247
|
|
|
109
248
|
## Hooks catalog
|
|
110
249
|
|
|
@@ -112,6 +251,11 @@ Every read hook returns `{data, error, loading, refetch}`; every mutation hook
|
|
|
112
251
|
returns `{call, data, error, loading}`. None depends on react-query or any other
|
|
113
252
|
data-fetching library.
|
|
114
253
|
|
|
254
|
+
Read hooks asking for the same thing (same hook, same arguments, same provider) while a
|
|
255
|
+
request is in flight share ONE request — two consumers, or React StrictMode's
|
|
256
|
+
mount/unmount/mount, send a single fetch. Nothing is kept once it settles: a hook mounting
|
|
257
|
+
later sends a new request, and `refetch()` starts a fresh one.
|
|
258
|
+
|
|
115
259
|
| Hook | Wraps |
|
|
116
260
|
|---|---|
|
|
117
261
|
| `useMe()` | `client.me()` |
|
|
@@ -128,15 +272,52 @@ data-fetching library.
|
|
|
128
272
|
| `useDeleteVar()` | `client.vars.delete()` |
|
|
129
273
|
| `useConnections()` | `client.connections.list()` |
|
|
130
274
|
| `useWorkflows(options?)` | `client.workflows.list()` |
|
|
275
|
+
| `useWorkflow(id)` | `client.workflows.get()` |
|
|
276
|
+
| `useCreateWorkflow()` | `client.workflows.create()` |
|
|
277
|
+
| `useUpdateWorkflow()` | `client.workflows.update()` |
|
|
278
|
+
| `useArchiveWorkflow()` | `client.workflows.archive()` |
|
|
279
|
+
| `useDeleteWorkflow()` | `client.workflows.delete()` |
|
|
131
280
|
| `useRunWorkflow()` | `client.workflows.run()`, defaults `wait: true` |
|
|
281
|
+
| `useFunctions()` | `client.functions.list()` |
|
|
282
|
+
| `useFunction(id)` | `client.functions.get()` |
|
|
283
|
+
| `useCreateFunction()` | `client.functions.create()` |
|
|
284
|
+
| `useUpdateFunction()` | `client.functions.update()` |
|
|
285
|
+
| `useDeleteFunction()` | `client.functions.delete()` |
|
|
132
286
|
| `useRun()` | `client.run()` (the generic URN dispatch) |
|
|
133
287
|
|
|
288
|
+
Three things the definition hooks inherit from the SDK rather than invent:
|
|
289
|
+
|
|
290
|
+
- **`create` mints the id.** The server requires one in the body and never
|
|
291
|
+
generates it, so `useCreateWorkflow().call({name, steps})` works and the
|
|
292
|
+
`wf_…` / `fn_…` id comes back in the result.
|
|
293
|
+
- **`update` is a full replacement, not a patch.** This is the one place they
|
|
294
|
+
differ from `useUpdateDocument`/`useUpdateVar`, whose second argument is a
|
|
295
|
+
patch of just the fields to change. Read with `useWorkflow`/`useFunction`,
|
|
296
|
+
spread, change, send the whole thing back — passing only the changed fields
|
|
297
|
+
deletes the rest. `useUpdateWorkflow()`'s third argument carries
|
|
298
|
+
`ifUnmodifiedSince`: hand it the `updatedAt` from `useWorkflow` and a save
|
|
299
|
+
that would clobber someone else's edit is refused with `409 workflow_stale`
|
|
300
|
+
rather than winning silently.
|
|
301
|
+
- **Deleting a workflow is two calls**, `useArchiveWorkflow` then
|
|
302
|
+
`useDeleteWorkflow`. Nothing chains them for you: the archive step is the one
|
|
303
|
+
a user can still change their mind after.
|
|
304
|
+
|
|
305
|
+
No mutation hook refetches a list on success — this package carries no cache to
|
|
306
|
+
invalidate, so pair one with the matching read hook's own `refetch`.
|
|
307
|
+
|
|
308
|
+
`client.functions.run()` has **no** hook, and neither does `endpoints.run()` —
|
|
309
|
+
that predates the definition hooks and is unchanged by them. `useRun()` reaches
|
|
310
|
+
a Function by its `fn_…` id today (the server dispatches it through the same
|
|
311
|
+
service the dedicated invoke route uses); calling one by the `key` a user gave
|
|
312
|
+
it still means `useW6WClient().functions.run("send-email", …)`.
|
|
313
|
+
|
|
134
314
|
`useRunWorkflow()`'s `call` defaults `wait: true` when the caller's options omit
|
|
135
315
|
`wait`: `workflows.run` without `wait` (`node/src/workflows.ts`) leaves the caller
|
|
136
316
|
with only a `runId` and no public polling operation to follow it with —
|
|
137
317
|
`console.workflows.getRun` exists, but it is the same studio-internal, unstable
|
|
138
318
|
surface described above, so this hook does not reach for it. Pass `wait: false`
|
|
139
|
-
explicitly if you
|
|
319
|
+
explicitly if you want the queued-and-walk-away behaviour, and follow the run
|
|
320
|
+
with `` useW6WClient().request({ method: "GET", path: path`/runs/${runId}` }) ``.
|
|
140
321
|
|
|
141
322
|
`useW6WClient()` returns the underlying `W6WClient` for anything not covered by a
|
|
142
323
|
hook (e.g. `client.console.*` directly, at your own risk per the caveat above).
|
|
@@ -151,22 +332,11 @@ hook (e.g. `client.console.*` directly, at your own risk per the caveat above).
|
|
|
151
332
|
fine — every extra field is optional — and there is no runtime data loss (the JSON
|
|
152
333
|
payload is unfiltered); a caller typing a variable through this adapter's declared
|
|
153
334
|
return type just gets no autocomplete for those extra fields.
|
|
154
|
-
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
showing the outbound calls the action made — even though this adapter's thrown
|
|
163
|
-
error genuinely carries that data on `.body`/`.raw`. This adapter still aliases
|
|
164
|
-
`@w6w/sdk`'s `ApiError.raw` onto `.body` (`err.body === err.raw`) and sets
|
|
165
|
-
`err.name = "ApiError"`, so it is already correct for any DUCK-TYPING consumer, and
|
|
166
|
-
needs no further change the moment `@w6w/ui` switches its own check to one. The fix
|
|
167
|
-
belongs to `@w6w/ui`, not this package, and is filed there:
|
|
168
|
-
`.ai/projects/backlog/26-08-13-01-ui-error-nominal-check.md`.
|
|
169
|
-
- **No `AbortSignal`/cancellation support in the hook set.** `@w6w/sdk`'s transport
|
|
170
|
-
(`node/src/http.ts`) has nothing to hook an abort into. A read hook only IGNORES a
|
|
171
|
-
result that resolves after its component unmounts (a mounted-ref guard); it cannot
|
|
172
|
-
cancel the in-flight request itself.
|
|
335
|
+
- **Cancellation is per-hook, not exposed to the caller.** Every read hook aborts its
|
|
336
|
+
own superseded or unmounted calls internally (a superseded call, an `identityKey`
|
|
337
|
+
switch, or an unmount all abort the in-flight `AbortSignal` — but a shared request
|
|
338
|
+
aborts only when its LAST subscriber detaches, so StrictMode's remount or a second
|
|
339
|
+
consumer re-attaches instead of aborting it — see `src/hooks.ts`'s
|
|
340
|
+
module header) — but there is no option to pass your OWN `AbortSignal` into a hook
|
|
341
|
+
from outside it. `useW6WClient()` plus the underlying SDK method (which does take a
|
|
342
|
+
`signal`) is the escape hatch if you need that.
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@w6w/react` — the package barrel.
|
|
3
3
|
*
|
|
4
|
-
* A thin React binding over `@w6w/sdk`: `<W6WProvider>` (
|
|
5
|
-
*
|
|
4
|
+
* A thin React binding over `@w6w/sdk`: `<W6WProvider>` (with its native token
|
|
5
|
+
* supplier), a small hook set, and `createW6WUiAdapter` (the
|
|
6
6
|
* `@w6w/ui` `W6WApi` structural bridge, C-1/C-2). See `README.md` for the
|
|
7
7
|
* install + wrap-your-root example, the hooks catalog, and this lane's
|
|
8
8
|
* documented limitations.
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export { VERSION } from "./src/version.ts";
|
|
16
16
|
export { type TokenSource, useW6WClient, W6WProvider, type W6WProviderProps, } from "./src/W6WProvider.tsx";
|
|
17
|
-
export { type MutationResult, type ReadResult, useConnections, useCreateDocument, useCreateVar, useDeleteDocument, useDeleteVar, useDocument, useDocumentByKey, useDocuments, useMe, useRun, useRunWorkflow, useUpdateDocument, useUpdateVar, useVar, useVars, useWorkflows, } from "./src/hooks.ts";
|
|
17
|
+
export { type MutationResult, type ReadResult, useArchiveWorkflow, useConnections, useCreateDocument, useCreateFunction, useCreateVar, useCreateWorkflow, useDeleteDocument, useDeleteFunction, useDeleteVar, useDeleteWorkflow, useDocument, useDocumentByKey, useDocuments, useFunction, useFunctions, useMe, useRun, useRunWorkflow, useUpdateDocument, useUpdateFunction, useUpdateVar, useUpdateWorkflow, useVar, useVars, useWorkflow, useWorkflows, } from "./src/hooks.ts";
|
|
18
18
|
export { createW6WUiAdapter, type W6WApi } from "./src/adapter.ts";
|
|
19
|
+
export { type FlowEdgeShape, type FlowStepShape, type FlowWorkflowShape, fromFlowWorkflow, toFlowWorkflow, WorkflowShapeError, type WorkflowShapeIssue, } from "./src/flow-workflow.ts";
|
|
19
20
|
//# sourceMappingURL=mod.d.ts.map
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EACL,KAAK,WAAW,EAChB,YAAY,EACZ,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,KAAK,EACL,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,MAAM,EACN,OAAO,EACP,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC"}
|
package/dist/mod.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
/**
|
|
2
3
|
* `@w6w/react` — the package barrel.
|
|
3
4
|
*
|
|
4
|
-
* A thin React binding over `@w6w/sdk`: `<W6WProvider>` (
|
|
5
|
-
*
|
|
5
|
+
* A thin React binding over `@w6w/sdk`: `<W6WProvider>` (with its native token
|
|
6
|
+
* supplier), a small hook set, and `createW6WUiAdapter` (the
|
|
6
7
|
* `@w6w/ui` `W6WApi` structural bridge, C-1/C-2). See `README.md` for the
|
|
7
8
|
* install + wrap-your-root example, the hooks catalog, and this lane's
|
|
8
9
|
* documented limitations.
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
*/
|
|
15
16
|
export { VERSION } from "./src/version.js";
|
|
16
17
|
export { useW6WClient, W6WProvider, } from "./src/W6WProvider.js";
|
|
17
|
-
export { useConnections, useCreateDocument, useCreateVar, useDeleteDocument, useDeleteVar, useDocument, useDocumentByKey, useDocuments, useMe, useRun, useRunWorkflow, useUpdateDocument, useUpdateVar, useVar, useVars, useWorkflows, } from "./src/hooks.js";
|
|
18
|
+
export { useArchiveWorkflow, useConnections, useCreateDocument, useCreateFunction, useCreateVar, useCreateWorkflow, useDeleteDocument, useDeleteFunction, useDeleteVar, useDeleteWorkflow, useDocument, useDocumentByKey, useDocuments, useFunction, useFunctions, useMe, useRun, useRunWorkflow, useUpdateDocument, useUpdateFunction, useUpdateVar, useUpdateWorkflow, useVar, useVars, useWorkflow, useWorkflows, } from "./src/hooks.js";
|
|
18
19
|
export { createW6WUiAdapter } from "./src/adapter.js";
|
|
20
|
+
export { fromFlowWorkflow, toFlowWorkflow, WorkflowShapeError, } from "./src/flow-workflow.js";
|
|
19
21
|
//# sourceMappingURL=mod.js.map
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAEL,YAAY,EACZ,WAAW,GAEZ,MAAM,sBAAuB,CAAC;AAE/B,OAAO,EAGL,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,OAAO,EACP,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAe,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAEL,YAAY,EACZ,WAAW,GAEZ,MAAM,sBAAuB,CAAC;AAE/B,OAAO,EAGL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,KAAK,EACL,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,MAAM,EACN,OAAO,EACP,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAe,MAAM,kBAAkB,CAAC;AAEnE,OAAO,EAIL,gBAAgB,EAChB,cAAc,EACd,kBAAkB,GAEnB,MAAM,wBAAwB,CAAC"}
|
|
@@ -1,62 +1,104 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `<W6WProvider>` — one `W6WClient` per `baseUrl
|
|
3
|
-
*
|
|
2
|
+
* `<W6WProvider>` — one `W6WClient` per `{baseUrl, project, identityKey,
|
|
3
|
+
* refreshOnUnauthorized, headers}` combination, with the token resolved per
|
|
4
|
+
* request (concurrent requests share one supplier call).
|
|
4
5
|
*
|
|
5
|
-
* `@w6w/sdk`'s `W6WClientOptions.token` (`node/src/config.ts:41-63`)
|
|
6
|
-
*
|
|
7
|
-
* (
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `@w6w/sdk`'s `W6WClientOptions.token` (`node/src/config.ts:41-63`) accepts
|
|
7
|
+
* either a plain string or a {@linkcode TokenProvider} function — the
|
|
8
|
+
* function form is called (and its result awaited) per request — requests in
|
|
9
|
+
* flight at the same time share ONE call, nothing is cached once it settles,
|
|
10
|
+
* and it is never resolved once at construction. This provider hands the
|
|
11
|
+
* SDK a single STABLE function that reads the CURRENT `token` prop through a
|
|
12
|
+
* ref: a string prop is returned as-is, a function prop is called with
|
|
13
|
+
* whatever `ctx` the SDK forwards (including `{forceRefresh: true}` on its
|
|
14
|
+
* own one-shot 401 recovery retry, `node/src/http.ts`). `fetch` and
|
|
15
|
+
* `onUnauthorized` are the same shape — stable wrappers over "latest" refs —
|
|
16
|
+
* so neither a token rotation, a new `fetch` identity, nor a new
|
|
17
|
+
* `onUnauthorized` closure identity ever rebuilds the client. A nullish or
|
|
18
|
+
* blank token (sync or async) never reaches the wire: the SDK's own
|
|
19
|
+
* `requireToken` raises a local {@linkcode ConfigError} before any `fetch`
|
|
20
|
+
* call, which is the floor `hooks.ts`'s `useAsync` reports as `loading`, not
|
|
21
|
+
* `error`.
|
|
10
22
|
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
* the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* always overwrites it first) and an injected `fetch` that resolves the
|
|
22
|
-
* current `token` prop (calling it, if it is a supplier) on every call.
|
|
23
|
+
* **What DOES rebuild the client** (the `useMemo` below): `baseUrl`,
|
|
24
|
+
* `project`, `identityKey`, `refreshOnUnauthorized`, and a canonical STRING of
|
|
25
|
+
* `headers`' sorted entries — never the `headers` object's own identity (an
|
|
26
|
+
* inline literal would rebuild the client on every render) and never
|
|
27
|
+
* `token`. `identityKey` (e.g. `${subject}:${account}`) is the one supported
|
|
28
|
+
* way to force a rebuild on purpose, for a host juggling more than one
|
|
29
|
+
* signed-in identity: every hook built on `useAsync` (`hooks.ts`) resets its
|
|
30
|
+
* state to `{data: undefined, error: undefined, loading: true}` when the
|
|
31
|
+
* client it reads through changes, aborting whatever it had in flight first —
|
|
32
|
+
* a stale account's data is never shown under a new one.
|
|
23
33
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* function has a stable identity across renders, so keying on the supplier
|
|
28
|
-
* would never rebuild AND never read a fresh value, while keying on the
|
|
29
|
-
* token's resolved value would tear the client down and rebuild it on every
|
|
30
|
-
* rotation — exactly what C-4 says not to do ("without tearing down"). The
|
|
31
|
-
* memo below depends on `[baseUrl, project]` only; `token` and `fetch` are
|
|
32
|
-
* read through "latest" refs, updated every render but never a dependency, so
|
|
33
|
-
* a rotating token is read fresh per request without ever rebuilding the
|
|
34
|
-
* client.
|
|
34
|
+
* `ready` (default `true`) gates every READ hook, never a mutation: while
|
|
35
|
+
* `false`, a read hook reports `loading: true` and never calls the SDK at
|
|
36
|
+
* all. Flipping it to `true` (or changing `identityKey`) fetches.
|
|
35
37
|
*
|
|
36
38
|
* @module
|
|
37
39
|
*/
|
|
38
|
-
import { type FetchLike, W6WClient } from "@w6w/sdk";
|
|
40
|
+
import { type ApiError, type FetchLike, type TokenProvider, W6WClient } from "@w6w/sdk";
|
|
39
41
|
import { type ReactNode } from "react";
|
|
40
|
-
/** A static bearer token, or a supplier
|
|
41
|
-
export type TokenSource = string |
|
|
42
|
+
/** A static bearer token, or a supplier called per request (concurrent requests share one call). */
|
|
43
|
+
export type TokenSource = string | TokenProvider;
|
|
42
44
|
export interface W6WProviderProps {
|
|
43
45
|
/** The w6w server's origin, e.g. `"https://api.example.com"`. */
|
|
44
46
|
baseUrl: string;
|
|
45
|
-
/** A literal bearer token, or a supplier called
|
|
47
|
+
/** A literal bearer token, or a supplier called per request (concurrent requests share one call). */
|
|
46
48
|
token: TokenSource;
|
|
47
49
|
/**
|
|
48
|
-
* The real transport this provider's
|
|
49
|
-
*
|
|
50
|
-
*
|
|
50
|
+
* The real transport this provider's stable `fetch` wrapper delegates to.
|
|
51
|
+
* Defaults to `globalThis.fetch`. Mainly for tests — a caller wanting to
|
|
52
|
+
* record or stub outbound requests.
|
|
51
53
|
*/
|
|
52
54
|
fetch?: FetchLike;
|
|
53
55
|
/** Default project id for the project-scoped operations (`documents.*`, `workflows.list`). */
|
|
54
56
|
project?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Default headers sent with every request — e.g. `{"X-W6W-Tenant": "t1"}`
|
|
59
|
+
* for a Path 3 tenant binding. Compared by CONTENT (sorted entries), not by
|
|
60
|
+
* object identity: a new-but-equal `headers` object on every render never
|
|
61
|
+
* rebuilds the client.
|
|
62
|
+
*/
|
|
63
|
+
headers?: Record<string, string>;
|
|
64
|
+
/**
|
|
65
|
+
* Gates every read hook. `false` (default `true`) means "I don't have a
|
|
66
|
+
* token/identity yet": no read hook calls the SDK, and every one reports
|
|
67
|
+
* `loading: true` until this flips — or forever, if it never does.
|
|
68
|
+
*/
|
|
69
|
+
ready?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Identifies WHICH signed-in identity this client speaks for. Changing it
|
|
72
|
+
* (e.g. `${subject}:${account}`) rebuilds the `W6WClient` and resets every
|
|
73
|
+
* read hook's state — the one supported way to switch accounts without a
|
|
74
|
+
* stale read from the previous identity ever rendering under the new one.
|
|
75
|
+
*/
|
|
76
|
+
identityKey?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Opt in to a single, one-shot recovery when a request 401s with
|
|
79
|
+
* `{code: "unauthorized"}`: the `token` supplier is called once more with
|
|
80
|
+
* `{forceRefresh: true}`, and a usable result retries the same request
|
|
81
|
+
* once. See `@w6w/sdk`'s `W6WClientOptions.refreshOnUnauthorized`.
|
|
82
|
+
*/
|
|
83
|
+
refreshOnUnauthorized?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Called with the terminal `401`/`unauthorized` error — after a failed
|
|
86
|
+
* recovery retry, or immediately when recovery is off or not possible — at
|
|
87
|
+
* most once per call. Always the LATEST `onUnauthorized` prop, read through
|
|
88
|
+
* a ref like `token`/`fetch`.
|
|
89
|
+
*/
|
|
90
|
+
onUnauthorized?: (error: ApiError) => void;
|
|
55
91
|
children: ReactNode;
|
|
56
92
|
}
|
|
93
|
+
interface W6WContextValue {
|
|
94
|
+
client: W6WClient;
|
|
95
|
+
/** Mirrors the `ready` prop. Read by `hooks.ts`'s `useAsync`, never by `useW6WClient`. */
|
|
96
|
+
ready: boolean;
|
|
97
|
+
}
|
|
57
98
|
/**
|
|
58
|
-
* Provides one memoized `W6WClient` to every component under
|
|
59
|
-
* module's header for the
|
|
99
|
+
* Provides one memoized `W6WClient` (plus `ready`) to every component under
|
|
100
|
+
* it. See this module's header for the token-freshness mechanism and the
|
|
101
|
+
* memoization policy.
|
|
60
102
|
*/
|
|
61
103
|
export declare function W6WProvider(props: W6WProviderProps): ReactNode;
|
|
62
104
|
/**
|
|
@@ -65,4 +107,12 @@ export declare function W6WProvider(props: W6WProviderProps): ReactNode;
|
|
|
65
107
|
* `useW6WApi` shape — read-only reference, transcribed here, never imported).
|
|
66
108
|
*/
|
|
67
109
|
export declare function useW6WClient(): W6WClient;
|
|
110
|
+
/**
|
|
111
|
+
* Internal: the context value including `ready`, consumed only by the read
|
|
112
|
+
* hooks in `hooks.ts` (via a relative import — never re-exported from
|
|
113
|
+
* `mod.ts`). `useW6WClient`'s `W6WClient` return type is the public surface;
|
|
114
|
+
* this is the one extra bit every read hook needs to gate on.
|
|
115
|
+
*/
|
|
116
|
+
export declare function useW6WProviderContext(): W6WContextValue;
|
|
117
|
+
export {};
|
|
68
118
|
//# sourceMappingURL=W6WProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"W6WProvider.d.ts","sourceRoot":"","sources":["../../src/W6WProvider.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"W6WProvider.d.ts","sourceRoot":"","sources":["../../src/W6WProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,EAAE,KAAK,SAAS,EAA8C,MAAM,OAAO,CAAC;AAEnF,oGAAoG;AACpG,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,aAAa,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,qGAAqG;IACrG,KAAK,EAAE,WAAW,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC3C,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,SAAS,CAAC;IAClB,0FAA0F;IAC1F,KAAK,EAAE,OAAO,CAAC;CAChB;AAeD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAqE9D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,SAAS,CASxC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CASvD"}
|