@pihanga2/core 0.3.7 → 0.3.9
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/REST-USAGE.md +613 -0
- package/dist/reducer.d.ts.map +1 -1
- package/dist/reducer.js +77 -5
- package/dist/reducer.js.map +1 -1
- package/dist/types.d.ts +28 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
- package/src/reducer.ts +105 -5
- package/src/types.ts +65 -9
package/REST-USAGE.md
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD013 -->
|
|
2
|
+
|
|
3
|
+
# REST Usage (PiRegister.{GET|POST|PUT|PATCH|DELETE})
|
|
4
|
+
|
|
5
|
+
This document is split into two parts:
|
|
6
|
+
|
|
7
|
+
* **Usage (what most library users need day-to-day)**
|
|
8
|
+
* **Debugging / internals (useful when something goes wrong)**
|
|
9
|
+
|
|
10
|
+
## Table of contents
|
|
11
|
+
|
|
12
|
+
* [Usage](#usage)
|
|
13
|
+
* [Mental model](#mental-model)
|
|
14
|
+
* [Common registration properties](#common-registration-properties)
|
|
15
|
+
* [URL templates and bindings](#url-templates-and-bindings)
|
|
16
|
+
* [Usage: GET (start here)](#usage-get-start-here)
|
|
17
|
+
* [Minimal typed GET example (no auth/context)](#minimal-typed-get-example-no-authcontext)
|
|
18
|
+
* [Advanced GET example (auth via context + origin + headers)](#advanced-get-example-auth-via-context--origin--headers)
|
|
19
|
+
* [Usage: Request context + auth (common pattern)](#usage-request-context--auth-common-pattern)
|
|
20
|
+
* [Usage: Progress (submitted/result/error) actions](#usage-progress-submittedresulterror-actions)
|
|
21
|
+
* [Usage: POST / PUT / PATCH (request bodies)](#usage-post--put--patch-request-bodies)
|
|
22
|
+
* [Common request body shape](#common-request-body-shape)
|
|
23
|
+
* [POST](#post)
|
|
24
|
+
* [PUT](#put)
|
|
25
|
+
* [PATCH](#patch)
|
|
26
|
+
* [Usage: DELETE](#usage-delete)
|
|
27
|
+
* [Usage: Error handling](#usage-error-handling)
|
|
28
|
+
* [Debugging / internals](#debugging--internals)
|
|
29
|
+
* [Where the code lives](#where-the-code-lives)
|
|
30
|
+
* [How it hooks into Redux](#how-it-hooks-into-redux)
|
|
31
|
+
* [Internal action types](#internal-action-types)
|
|
32
|
+
* [Response parsing](#response-parsing)
|
|
33
|
+
* [Notes / gotchas](#notes--gotchas)
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Mental model
|
|
38
|
+
|
|
39
|
+
1. You register a REST handler, e.g. `register.GET({ ... })`.
|
|
40
|
+
2. It **listens** for a Redux action type (`trigger`).
|
|
41
|
+
3. When that action is dispatched, it builds a URL from your `url` template plus bindings from `request(...)`, optionally loads `context(...)` and applies `headers(...)`, then calls `fetch`.
|
|
42
|
+
4. On success, your `reply(...)` runs and typically dispatches your domain actions.
|
|
43
|
+
|
|
44
|
+
### Common registration properties
|
|
45
|
+
|
|
46
|
+
All verbs share the properties from `RegisterGenericProps` (`packages/core/src/rest/types.ts`):
|
|
47
|
+
|
|
48
|
+
| property | type | purpose |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| `name` | `string` | Logical call name (used for internal bookkeeping/debugging). |
|
|
51
|
+
| `trigger` | `string` | Redux action type that starts the call. |
|
|
52
|
+
| `url` | `string` | URL template supporting bindings like `:id` and optional bindings like `?page`. |
|
|
53
|
+
| `origin?` | `string \| (action, state, ctxt) => string \| URL` | Base origin. Default is `window.location.href`. |
|
|
54
|
+
| `context?` | `(action, state) => Promise<C> \| null` | Async context (e.g. auth token, base URL). |
|
|
55
|
+
| `guard?` | `(action, state, dispatch, ctxt) => boolean` | Return `false` to skip the request. |
|
|
56
|
+
| `headers?` | `(action, state, ctxt) => Record<string,string>` | Request headers (auth, correlation IDs, etc.). |
|
|
57
|
+
| `reply` | `(state, content, dispatch, resultAction) => void` | Called on success (HTTP < 300). Dispatch domain actions here. |
|
|
58
|
+
| `error?` | `(state, errorAction, requestAction, dispatch) => S` | Called on non-2xx responses. Dispatch domain error actions here. |
|
|
59
|
+
|
|
60
|
+
### URL templates and bindings
|
|
61
|
+
|
|
62
|
+
Bindings are substituted into the `url` **path segments** and **query string**:
|
|
63
|
+
|
|
64
|
+
* `:name` = required binding. Missing it triggers an internal error.
|
|
65
|
+
* `?name` = optional binding. Missing it omits that query parameter.
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
|
|
69
|
+
* `/1/artifacts/:id` requires `{ id: "..." }`
|
|
70
|
+
* `/1/orders?limit=?limit&page=?page` omits `limit` and/or `page` if not provided
|
|
71
|
+
|
|
72
|
+
Path bindings are URL-encoded.
|
|
73
|
+
|
|
74
|
+
## Usage: GET (start here)
|
|
75
|
+
|
|
76
|
+
GET registrations provide optional `request(...)` bindings (no request body).
|
|
77
|
+
|
|
78
|
+
### Minimal typed GET example (no auth/context)
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import type {
|
|
82
|
+
Bindings,
|
|
83
|
+
DispatchF,
|
|
84
|
+
ErrorAction,
|
|
85
|
+
PiRegister,
|
|
86
|
+
ResultAction,
|
|
87
|
+
ReduxAction,
|
|
88
|
+
ReduxState,
|
|
89
|
+
register,
|
|
90
|
+
} from "@pihanga2/core"
|
|
91
|
+
|
|
92
|
+
type MyState = ReduxState & {}
|
|
93
|
+
|
|
94
|
+
type LoadThingAction = ReduxAction & {
|
|
95
|
+
id: string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type Thing = {
|
|
99
|
+
id: string
|
|
100
|
+
name: string
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
register((r: PiRegister) => {
|
|
104
|
+
r.GET<MyState, LoadThingAction, Thing>({
|
|
105
|
+
name: "loadThing",
|
|
106
|
+
trigger: "THING/LOAD",
|
|
107
|
+
url: "/v1/things/:id",
|
|
108
|
+
|
|
109
|
+
request: (action: LoadThingAction, _state: MyState): Bindings => ({
|
|
110
|
+
id: action.id,
|
|
111
|
+
}),
|
|
112
|
+
|
|
113
|
+
reply: (
|
|
114
|
+
_state: MyState,
|
|
115
|
+
content: Thing,
|
|
116
|
+
dispatch: DispatchF,
|
|
117
|
+
_result: ResultAction<LoadThingAction>,
|
|
118
|
+
): void => {
|
|
119
|
+
dispatch({ type: "THING/LOADED", thing: content })
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
error: (
|
|
123
|
+
state: MyState,
|
|
124
|
+
err: ErrorAction<LoadThingAction>,
|
|
125
|
+
_requestAction: LoadThingAction,
|
|
126
|
+
dispatch: DispatchF,
|
|
127
|
+
): MyState => {
|
|
128
|
+
dispatch({ type: "THING/LOAD_FAILED", cause: err })
|
|
129
|
+
return state
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Advanced GET example (auth via context + origin + headers)
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import type {
|
|
139
|
+
Bindings,
|
|
140
|
+
DispatchF,
|
|
141
|
+
ErrorAction,
|
|
142
|
+
PiRegister,
|
|
143
|
+
ResultAction,
|
|
144
|
+
ReduxAction,
|
|
145
|
+
ReduxState,
|
|
146
|
+
register,
|
|
147
|
+
} from "@pihanga2/core"
|
|
148
|
+
|
|
149
|
+
type MyState = ReduxState
|
|
150
|
+
|
|
151
|
+
type LoadThingAction = ReduxAction & {
|
|
152
|
+
id: string
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
type Thing = {
|
|
156
|
+
id: string
|
|
157
|
+
name: string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type MyAuthContext = {
|
|
161
|
+
apiOrigin: string
|
|
162
|
+
token: string
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
register((r: PiRegister) => {
|
|
166
|
+
r.GET<MyState, LoadThingAction, Thing, MyAuthContext>({
|
|
167
|
+
name: "loadThing",
|
|
168
|
+
trigger: "THING/LOAD",
|
|
169
|
+
url: "/v1/things/:id",
|
|
170
|
+
|
|
171
|
+
context: async (
|
|
172
|
+
_action: LoadThingAction,
|
|
173
|
+
_state: MyState,
|
|
174
|
+
): Promise<MyAuthContext> => ({
|
|
175
|
+
apiOrigin: "https://api.example.com",
|
|
176
|
+
token: "...",
|
|
177
|
+
}),
|
|
178
|
+
|
|
179
|
+
origin: (
|
|
180
|
+
_action: LoadThingAction,
|
|
181
|
+
_state: MyState,
|
|
182
|
+
ctxt: MyAuthContext,
|
|
183
|
+
): string => ctxt.apiOrigin,
|
|
184
|
+
|
|
185
|
+
headers: (
|
|
186
|
+
_action: LoadThingAction,
|
|
187
|
+
_state: MyState,
|
|
188
|
+
ctxt: MyAuthContext,
|
|
189
|
+
): Record<string, string> => ({
|
|
190
|
+
Authorization: `Bearer ${ctxt.token}`,
|
|
191
|
+
}),
|
|
192
|
+
|
|
193
|
+
request: (action: LoadThingAction, _state: MyState): Bindings => ({
|
|
194
|
+
id: action.id,
|
|
195
|
+
}),
|
|
196
|
+
|
|
197
|
+
reply: (
|
|
198
|
+
_state: MyState,
|
|
199
|
+
content: Thing,
|
|
200
|
+
dispatch: DispatchF,
|
|
201
|
+
_result: ResultAction<LoadThingAction>,
|
|
202
|
+
): void => {
|
|
203
|
+
dispatch({ type: "THING/LOADED", thing: content })
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
error: (
|
|
207
|
+
state: MyState,
|
|
208
|
+
err: ErrorAction<LoadThingAction>,
|
|
209
|
+
_requestAction: LoadThingAction,
|
|
210
|
+
dispatch: DispatchF,
|
|
211
|
+
): MyState => {
|
|
212
|
+
dispatch({ type: "THING/LOAD_FAILED", cause: err })
|
|
213
|
+
return state
|
|
214
|
+
},
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Worked example (GET with path binding) from `packages/ivcap`
|
|
220
|
+
|
|
221
|
+
From `packages/ivcap/src/artifact/artifact.get.ts`:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import type {
|
|
225
|
+
Bindings,
|
|
226
|
+
DispatchF,
|
|
227
|
+
PiRegister,
|
|
228
|
+
ResultAction,
|
|
229
|
+
ReduxAction,
|
|
230
|
+
ReduxState,
|
|
231
|
+
register,
|
|
232
|
+
} from "@pihanga2/core"
|
|
233
|
+
|
|
234
|
+
register((r: PiRegister) => {
|
|
235
|
+
r.GET<ReduxState, ReduxAction & LoadArtifactRecordEvent, any>({
|
|
236
|
+
...CommonProps("getArtifactRecord"),
|
|
237
|
+
url: "/1/artifacts/:id",
|
|
238
|
+
trigger: ACTION_TYPES.LOAD_RECORD,
|
|
239
|
+
request: (
|
|
240
|
+
action: ReduxAction & LoadArtifactRecordEvent,
|
|
241
|
+
_state: ReduxState,
|
|
242
|
+
): Bindings => ({ id: action.id }),
|
|
243
|
+
reply: (
|
|
244
|
+
_state: ReduxState,
|
|
245
|
+
content: any,
|
|
246
|
+
dispatch: DispatchF,
|
|
247
|
+
result: ResultAction<ReduxAction & LoadArtifactRecordEvent>,
|
|
248
|
+
): void => {
|
|
249
|
+
const ev: ArtifactRecordEvent = { artifact: toArtifactRecord(content) }
|
|
250
|
+
dispatchEvent(ev, ACTION_TYPES.RECORD, dispatch, result.request)
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Usage: Request context + auth (common pattern)
|
|
257
|
+
|
|
258
|
+
For authenticated APIs, a common pattern is:
|
|
259
|
+
|
|
260
|
+
* `context()` loads auth/base-url asynchronously (token, API URL)
|
|
261
|
+
* `origin()` sets the base URL from that context
|
|
262
|
+
* `headers()` adds auth headers (e.g. `Authorization: Bearer ...`)
|
|
263
|
+
|
|
264
|
+
The `packages/ivcap` module uses a reusable helper (`packages/ivcap/src/common.ts`):
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
export const CommonProps = (name: string) => ({
|
|
268
|
+
name,
|
|
269
|
+
context: () => GetOAuthContext(),
|
|
270
|
+
origin: (_a: any, _s: any, ctxt: OAuthContextT) => ctxt.ivcapURL,
|
|
271
|
+
headers: (_a: any, _s: any, ctxt: OAuthContextT) => ({
|
|
272
|
+
Authorization: `Bearer ${ctxt.token}`,
|
|
273
|
+
}),
|
|
274
|
+
error: restErrorHandling(`ivcap-api:${name}`),
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Usage: Progress (submitted/result/error) actions
|
|
279
|
+
|
|
280
|
+
Every REST registration reports lifecycle/progress via **additional Redux actions**. This is useful for:
|
|
281
|
+
|
|
282
|
+
* showing spinners (request submitted)
|
|
283
|
+
* logging / debugging
|
|
284
|
+
* building a generic request-tracker in state
|
|
285
|
+
|
|
286
|
+
The base action namespaces are defined in:
|
|
287
|
+
|
|
288
|
+
* `packages/core/src/rest/types.ts` for **POST/PUT/PATCH/DELETE** (`Domain = "pi/rest"`)
|
|
289
|
+
* `packages/core/src/rest/get.ts` for **GET** (`Domain = "pi/rest"` but `pi/rest/get` subdomain)
|
|
290
|
+
|
|
291
|
+
### Base action types (POST/PUT/PATCH/DELETE)
|
|
292
|
+
|
|
293
|
+
In `packages/core/src/rest/types.ts`:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
export const Domain = "pi/rest"
|
|
297
|
+
export const ACTION_TYPES = registerActions(Domain, [
|
|
298
|
+
"POST_SUBMITTED",
|
|
299
|
+
"POST_RESULT",
|
|
300
|
+
"POST_ERROR",
|
|
301
|
+
"POST_INTERNAL_ERROR",
|
|
302
|
+
"PUT_SUBMITTED",
|
|
303
|
+
"PUT_RESULT",
|
|
304
|
+
"PUT_ERROR",
|
|
305
|
+
"PUT_INTERNAL_ERROR",
|
|
306
|
+
"PATCH_SUBMITTED",
|
|
307
|
+
"PATCH_RESULT",
|
|
308
|
+
"PATCH_ERROR",
|
|
309
|
+
"PATCH_INTERNAL_ERROR",
|
|
310
|
+
"DELETE_SUBMITTED",
|
|
311
|
+
"DELETE_RESULT",
|
|
312
|
+
"DELETE_ERROR",
|
|
313
|
+
"DELETE_INTERNAL_ERROR",
|
|
314
|
+
"UNAUTHORISED_ERROR",
|
|
315
|
+
"PERMISSION_DENIED_ERROR",
|
|
316
|
+
"NOT_FOUND_ERROR",
|
|
317
|
+
"ERROR",
|
|
318
|
+
"CONTEXT_ERROR",
|
|
319
|
+
])
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
For a specific handler registration, the REST worker **specialises** these by appending the handler’s `name`.
|
|
323
|
+
|
|
324
|
+
For POST/PUT/PATCH/DELETE, the current implementation appends using `:${name}`, e.g.:
|
|
325
|
+
|
|
326
|
+
* `pi/rest/POST_SUBMITTED:createOrder`
|
|
327
|
+
* `pi/rest/POST_RESULT:createOrder`
|
|
328
|
+
* `pi/rest/POST_ERROR:createOrder`
|
|
329
|
+
|
|
330
|
+
### Base action types (GET)
|
|
331
|
+
|
|
332
|
+
GET uses a different action namespace in `packages/core/src/rest/get.ts`:
|
|
333
|
+
|
|
334
|
+
* `pi/rest/get/submitted`
|
|
335
|
+
* `pi/rest/get/result`
|
|
336
|
+
* `pi/rest/get/error`
|
|
337
|
+
* `pi/rest/get/internal_error`
|
|
338
|
+
|
|
339
|
+
and specialises them by appending `/${name}`.
|
|
340
|
+
|
|
341
|
+
For example, from `packages/core/src/rest/get.ts`:
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
const submitType = `${ACTION_TYPES.SUBMITTED}/${name}`
|
|
345
|
+
const resultType = `${ACTION_TYPES.RESULT}/${name}`
|
|
346
|
+
const errorType = `${ACTION_TYPES.ERROR}/${name}`
|
|
347
|
+
const intErrorType = `${ACTION_TYPES.INTERNAL_ERROR}/${name}`
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Payload shapes
|
|
351
|
+
|
|
352
|
+
These lifecycle actions include useful payload:
|
|
353
|
+
|
|
354
|
+
* **submitted** actions use `SubmitAction` (includes `requestID`, `url`, `bindings`)
|
|
355
|
+
* **result** actions use `ResultAction<A>` (includes `statusCode`, `content`, `contentType`, `mimeType`, `size`, `headers`, plus `url` and original `request` action)
|
|
356
|
+
* **error** actions use `ErrorAction<A>` (similar to result, plus an `ErrorKind` classification)
|
|
357
|
+
|
|
358
|
+
If you want to track progress in your app state, these are the actions to listen for.
|
|
359
|
+
|
|
360
|
+
## Usage: POST / PUT / PATCH (request bodies)
|
|
361
|
+
|
|
362
|
+
POST/PUT/PATCH all have a request body.
|
|
363
|
+
|
|
364
|
+
They share the same `request(...)` return type:
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
type PoPuPaRequest = {
|
|
368
|
+
body: any
|
|
369
|
+
contentType?: string
|
|
370
|
+
bindings?: Bindings
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Common request body shape
|
|
375
|
+
|
|
376
|
+
Common patterns:
|
|
377
|
+
|
|
378
|
+
* Use `bindings` when your `url` contains `:id` or query bindings.
|
|
379
|
+
* For JSON bodies: set `contentType: "application/json"` (or omit it and return an object body).
|
|
380
|
+
* For binary bodies: set `contentType` appropriately and pass an `ArrayBuffer`/`Blob`.
|
|
381
|
+
|
|
382
|
+
### POST
|
|
383
|
+
|
|
384
|
+
POST creates a new server-side resource.
|
|
385
|
+
|
|
386
|
+
Minimal typed pattern:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
import type {
|
|
390
|
+
DispatchF,
|
|
391
|
+
PiRegister,
|
|
392
|
+
ReduxAction,
|
|
393
|
+
ReduxState,
|
|
394
|
+
register,
|
|
395
|
+
} from "@pihanga2/core"
|
|
396
|
+
|
|
397
|
+
type MyState = ReduxState
|
|
398
|
+
|
|
399
|
+
type Thing = { id: string; name: string }
|
|
400
|
+
type ThingCreatePayload = { name: string }
|
|
401
|
+
|
|
402
|
+
type CreateThingAction = ReduxAction & {
|
|
403
|
+
payload: ThingCreatePayload
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
register((r: PiRegister) => {
|
|
407
|
+
r.POST<MyState, CreateThingAction, Thing>({
|
|
408
|
+
name: "createThing",
|
|
409
|
+
trigger: "THING/CREATE",
|
|
410
|
+
url: "/v1/things",
|
|
411
|
+
request: (action: CreateThingAction, _state: MyState) => ({
|
|
412
|
+
body: action.payload,
|
|
413
|
+
contentType: "application/json",
|
|
414
|
+
}),
|
|
415
|
+
reply: (_state: MyState, content: Thing, dispatch: DispatchF) => {
|
|
416
|
+
dispatch({ type: "THING/CREATED", thing: content })
|
|
417
|
+
},
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Real example (POST JSON) from `packages/ivcap`:
|
|
423
|
+
|
|
424
|
+
### Example (POST JSON) from `packages/ivcap`
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
import type {
|
|
428
|
+
DispatchF,
|
|
429
|
+
PiRegister,
|
|
430
|
+
ResultAction,
|
|
431
|
+
ReduxAction,
|
|
432
|
+
ReduxState,
|
|
433
|
+
register,
|
|
434
|
+
} from "@pihanga2/core"
|
|
435
|
+
|
|
436
|
+
register((r: PiRegister) => {
|
|
437
|
+
r.POST<ReduxState, ReduxAction & CreateOrderEvent, any>({
|
|
438
|
+
...CommonProps("createOrder"),
|
|
439
|
+
url: "/1/orders",
|
|
440
|
+
trigger: ORDER_ACTION.CREATE,
|
|
441
|
+
request: (
|
|
442
|
+
action: ReduxAction & CreateOrderEvent,
|
|
443
|
+
_state: ReduxState,
|
|
444
|
+
) => ({
|
|
445
|
+
body: {
|
|
446
|
+
name: action.name,
|
|
447
|
+
service: action.serviceID,
|
|
448
|
+
parameters: action.parameters,
|
|
449
|
+
},
|
|
450
|
+
contentType: "application/json",
|
|
451
|
+
}),
|
|
452
|
+
reply: (
|
|
453
|
+
_state: ReduxState,
|
|
454
|
+
content: any,
|
|
455
|
+
dispatch: DispatchF,
|
|
456
|
+
result: ResultAction<ReduxAction & CreateOrderEvent>,
|
|
457
|
+
): void => {
|
|
458
|
+
const ev: OrderCreatedEvent = {
|
|
459
|
+
refID: result.request.refID,
|
|
460
|
+
order: toOrderRecord(content),
|
|
461
|
+
}
|
|
462
|
+
dispatchEvent(ev, ORDER_ACTION.CREATED, dispatch, result.request)
|
|
463
|
+
},
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### PUT
|
|
469
|
+
|
|
470
|
+
PUT typically replaces a resource at a known URL.
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
register.PUT<MyState, UpdateThingAction, Thing>({
|
|
474
|
+
name: "updateThing",
|
|
475
|
+
trigger: "THING/UPDATE",
|
|
476
|
+
url: "/v1/things/:id",
|
|
477
|
+
request: (action: UpdateThingAction, _state: MyState) => ({
|
|
478
|
+
bindings: { id: action.id },
|
|
479
|
+
body: action.payload,
|
|
480
|
+
contentType: "application/json",
|
|
481
|
+
}),
|
|
482
|
+
reply: (_state: MyState, content: Thing, dispatch: DispatchF) => {
|
|
483
|
+
dispatch({ type: "THING/UPDATED", thing: content })
|
|
484
|
+
},
|
|
485
|
+
})
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### PATCH
|
|
489
|
+
|
|
490
|
+
PATCH typically applies a partial update.
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
register((r: PiRegister) => {
|
|
494
|
+
r.PATCH<MyState, PatchThingAction, Thing>({
|
|
495
|
+
name: "patchThing",
|
|
496
|
+
trigger: "THING/PATCH",
|
|
497
|
+
url: "/v1/things/:id",
|
|
498
|
+
request: (action: PatchThingAction, _state: MyState) => ({
|
|
499
|
+
bindings: { id: action.id },
|
|
500
|
+
body: action.patch,
|
|
501
|
+
contentType: "application/json",
|
|
502
|
+
}),
|
|
503
|
+
reply: (_state: MyState, content: Thing, dispatch: DispatchF) => {
|
|
504
|
+
dispatch({ type: "THING/PATCHED", thing: content })
|
|
505
|
+
},
|
|
506
|
+
})
|
|
507
|
+
})
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## Usage: DELETE
|
|
511
|
+
|
|
512
|
+
DELETE is bindings-only (like GET) but uses method `DELETE`.
|
|
513
|
+
|
|
514
|
+
```ts
|
|
515
|
+
import type {
|
|
516
|
+
Bindings,
|
|
517
|
+
DispatchF,
|
|
518
|
+
PiRegister,
|
|
519
|
+
ResultAction,
|
|
520
|
+
ReduxAction,
|
|
521
|
+
ReduxState,
|
|
522
|
+
register,
|
|
523
|
+
} from "@pihanga2/core"
|
|
524
|
+
|
|
525
|
+
type MyState = ReduxState
|
|
526
|
+
type DeleteThingAction = ReduxAction & { id: string }
|
|
527
|
+
|
|
528
|
+
register((r: PiRegister) => {
|
|
529
|
+
r.DELETE<MyState, DeleteThingAction, unknown>({
|
|
530
|
+
name: "deleteThing",
|
|
531
|
+
trigger: "THING/DELETE",
|
|
532
|
+
url: "/v1/things/:id",
|
|
533
|
+
request: (action: DeleteThingAction, _state: MyState): Bindings => ({
|
|
534
|
+
id: action.id,
|
|
535
|
+
}),
|
|
536
|
+
reply: (
|
|
537
|
+
_state: MyState,
|
|
538
|
+
_content: unknown,
|
|
539
|
+
dispatch: DispatchF,
|
|
540
|
+
result: ResultAction<DeleteThingAction>,
|
|
541
|
+
): void => {
|
|
542
|
+
dispatch({ type: "THING/DELETED", id: result.request.id })
|
|
543
|
+
},
|
|
544
|
+
})
|
|
545
|
+
})
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Usage: Error handling
|
|
549
|
+
|
|
550
|
+
On non-2xx responses, the REST module dispatches an `ErrorAction` containing:
|
|
551
|
+
|
|
552
|
+
* `statusCode`
|
|
553
|
+
* `content` (parsed body)
|
|
554
|
+
* `error: ErrorKind` (401/403/404 mapped; else `Other`)
|
|
555
|
+
* `url`
|
|
556
|
+
* `request` (the original trigger action)
|
|
557
|
+
|
|
558
|
+
You can attach an `error(...)` handler per call, or centralize handling.
|
|
559
|
+
|
|
560
|
+
If you want a reusable auth+error strategy, see [Usage: Request context + auth (common pattern)](#usage-request-context--auth-common-pattern).
|
|
561
|
+
|
|
562
|
+
## Debugging / internals
|
|
563
|
+
|
|
564
|
+
### Where the code lives
|
|
565
|
+
|
|
566
|
+
* `packages/core/src/rest/get.ts`
|
|
567
|
+
* `packages/core/src/rest/postPutPatch.ts`
|
|
568
|
+
* `packages/core/src/rest/delete.ts`
|
|
569
|
+
* shared plumbing: `packages/core/src/rest/utils.ts`
|
|
570
|
+
* types: `packages/core/src/rest/types.ts`
|
|
571
|
+
|
|
572
|
+
### How it hooks into Redux
|
|
573
|
+
|
|
574
|
+
Pihanga’s `PiReducer` is a small registration layer on top of Redux Toolkit’s store (`packages/core/src/reducer.ts`).
|
|
575
|
+
|
|
576
|
+
When you call `register.GET({...})`, internally it registers reducers for:
|
|
577
|
+
|
|
578
|
+
* the trigger action (`trigger`)
|
|
579
|
+
* the internal success action (which calls your `reply(...)`)
|
|
580
|
+
* optionally the internal error action (which calls your `error(...)`)
|
|
581
|
+
|
|
582
|
+
So the REST system is effectively “middleware implemented as reducers”: it reacts to actions and dispatches more actions.
|
|
583
|
+
|
|
584
|
+
### Internal action types
|
|
585
|
+
|
|
586
|
+
You typically don’t dispatch these directly, but they’re helpful to know when debugging Redux logs.
|
|
587
|
+
|
|
588
|
+
GET (`packages/core/src/rest/get.ts`) creates types like:
|
|
589
|
+
|
|
590
|
+
* `pi/rest/get/submitted/${name}`
|
|
591
|
+
* `pi/rest/get/result/${name}`
|
|
592
|
+
* `pi/rest/get/error/${name}`
|
|
593
|
+
* `pi/rest/get/internal_error/${name}`
|
|
594
|
+
|
|
595
|
+
POST/PUT/PATCH/DELETE currently create types like:
|
|
596
|
+
|
|
597
|
+
* `pi/rest/post_submitted:${name}`
|
|
598
|
+
* `pi/rest/put_result:${name}`
|
|
599
|
+
* `pi/rest/delete_error:${name}`
|
|
600
|
+
|
|
601
|
+
### Response parsing
|
|
602
|
+
|
|
603
|
+
Response parsing is in `packages/core/src/rest/utils.ts`:
|
|
604
|
+
|
|
605
|
+
* `application/json` => `response.json()` => `RestContentType.Object`
|
|
606
|
+
* `application/jose` or `text/*` => `response.text()` => `RestContentType.Text`
|
|
607
|
+
* otherwise => `response.blob()` => `RestContentType.Blob`
|
|
608
|
+
|
|
609
|
+
### Notes / gotchas
|
|
610
|
+
|
|
611
|
+
* `reply(...)` runs in response to an internal action; keep it fast and dispatch domain actions.
|
|
612
|
+
* If `context(...)` is async, the current implementation calls `handleEvent(null as S, ...)` (see `registerCommon`), so don’t rely on the `state` parameter inside `guard/headers/origin` when using `context`.
|
|
613
|
+
* Prefer `?name` bindings for optional query parameters so they get omitted cleanly.
|
package/dist/reducer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAE,OAAO,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAE,OAAO,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAGL,SAAS,EAQT,UAAU,EACX,MAAM,SAAS,CAAC;AAKjB,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAqB/B,wBAAgB,aAAa,CAC3B,YAAY,EAAE,UAAU,EACxB,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,GACxB,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,CAsN1C"}
|
package/dist/reducer.js
CHANGED
|
@@ -14,6 +14,77 @@ export function createReducer(initialState, dispatcher) {
|
|
|
14
14
|
const delayedDispatcher = (a) => {
|
|
15
15
|
setTimeout(() => dispatcher(a), 0);
|
|
16
16
|
};
|
|
17
|
+
const DISPATCH_P_REDUCE_TIMEOUT_TYPE = "pi/dispatchPReduce/timeout";
|
|
18
|
+
const dispatchPReduce = (request, pOpts, onReply, onError, onTimeout) => {
|
|
19
|
+
const { replyType, timeoutMs = 10000, matchReply, matchError } = pOpts;
|
|
20
|
+
// Use a token so we can route timeout actions.
|
|
21
|
+
const token = `${Date.now()}:${Math.random()}`;
|
|
22
|
+
// Use `register` (not registerOneShot) so we can cancel explicitly.
|
|
23
|
+
const keyReply = `dispatchPReduce:reply:${replyType}:${token}`;
|
|
24
|
+
const keyTimeout = `dispatchPReduce:timeout:${replyType}:${token}`;
|
|
25
|
+
let settled = false;
|
|
26
|
+
let cancelReply = () => { };
|
|
27
|
+
let cancelTimeout = () => { };
|
|
28
|
+
let timer;
|
|
29
|
+
const cleanup = () => {
|
|
30
|
+
cancelReply();
|
|
31
|
+
cancelTimeout();
|
|
32
|
+
if (timer) {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
// Reply handler: decides whether to call onReply or onError.
|
|
37
|
+
cancelReply = registerReducer(replyType, (s2, a2, d2, o2) => {
|
|
38
|
+
if (settled)
|
|
39
|
+
return;
|
|
40
|
+
if (matchReply && !matchReply(a2))
|
|
41
|
+
return;
|
|
42
|
+
settled = true;
|
|
43
|
+
cleanup();
|
|
44
|
+
// If matchError is provided and the action matches, treat as error.
|
|
45
|
+
if (matchError && matchError(a2)) {
|
|
46
|
+
if (onError) {
|
|
47
|
+
onError(s2, a2, d2, o2);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Fall back to onReply if no onError was provided.
|
|
51
|
+
onReply(s2, a2, d2, o2);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
onReply(s2, a2, d2, o2);
|
|
56
|
+
}, 0, keyReply);
|
|
57
|
+
// Timeout handler: triggered by a dispatched internal timeout action.
|
|
58
|
+
if (onTimeout) {
|
|
59
|
+
cancelTimeout = registerReducer(DISPATCH_P_REDUCE_TIMEOUT_TYPE, (s2, a2, d2, o2) => {
|
|
60
|
+
if (settled)
|
|
61
|
+
return;
|
|
62
|
+
if (!a2 || a2.token !== token)
|
|
63
|
+
return;
|
|
64
|
+
settled = true;
|
|
65
|
+
cleanup();
|
|
66
|
+
onTimeout(s2, a2, d2, o2);
|
|
67
|
+
}, 0, keyTimeout);
|
|
68
|
+
}
|
|
69
|
+
timer = setTimeout(() => {
|
|
70
|
+
if (settled)
|
|
71
|
+
return;
|
|
72
|
+
if (!onTimeout) {
|
|
73
|
+
// no timeout handler requested
|
|
74
|
+
cleanup();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const timeoutAction = {
|
|
78
|
+
type: DISPATCH_P_REDUCE_TIMEOUT_TYPE,
|
|
79
|
+
cause: "timeout",
|
|
80
|
+
token,
|
|
81
|
+
replyType,
|
|
82
|
+
};
|
|
83
|
+
delayedDispatcher(timeoutAction);
|
|
84
|
+
}, timeoutMs);
|
|
85
|
+
// Must dispatch after the current reducer tick.
|
|
86
|
+
delayedDispatcher(request);
|
|
87
|
+
};
|
|
17
88
|
const reducer = (state, action) => {
|
|
18
89
|
var _a;
|
|
19
90
|
const s = state || initialState;
|
|
@@ -31,16 +102,17 @@ export function createReducer(initialState, dispatcher) {
|
|
|
31
102
|
return s;
|
|
32
103
|
}
|
|
33
104
|
const nextState = produce(s, (draft) => {
|
|
105
|
+
const opts = { rawState: s, dispatchPReduce };
|
|
34
106
|
if (!draft.pihanga) {
|
|
35
107
|
draft.pihanga = {};
|
|
36
108
|
}
|
|
37
109
|
draft.pihanga.reducers = [];
|
|
38
110
|
if (ra) {
|
|
39
|
-
const rout = _reduce(ra, draft, action, delayedDispatcher);
|
|
111
|
+
const rout = _reduce(ra, draft, action, delayedDispatcher, opts);
|
|
40
112
|
mappings[action.type] = rout;
|
|
41
113
|
}
|
|
42
114
|
if (rany) {
|
|
43
|
-
const rout2 = _reduce(rany, draft, action, delayedDispatcher);
|
|
115
|
+
const rout2 = _reduce(rany, draft, action, delayedDispatcher, opts);
|
|
44
116
|
mappings["*"] = rout2;
|
|
45
117
|
}
|
|
46
118
|
return;
|
|
@@ -101,7 +173,7 @@ function removeReducer(key, m) {
|
|
|
101
173
|
return m;
|
|
102
174
|
}
|
|
103
175
|
}
|
|
104
|
-
function _reduce(ra, draft, action, delayedDispatcher) {
|
|
176
|
+
function _reduce(ra, draft, action, delayedDispatcher, opts) {
|
|
105
177
|
const rout = [];
|
|
106
178
|
ra.forEach((m) => {
|
|
107
179
|
var _a, _b, _c, _d;
|
|
@@ -111,12 +183,12 @@ function _reduce(ra, draft, action, delayedDispatcher) {
|
|
|
111
183
|
// }
|
|
112
184
|
if (m.mapperMulti) {
|
|
113
185
|
(_b = (_a = draft.pihanga) === null || _a === void 0 ? void 0 : _a.reducers) === null || _b === void 0 ? void 0 : _b.push(m.definedIn || m.key || "unknown");
|
|
114
|
-
m.mapperMulti(draft, action, delayedDispatcher);
|
|
186
|
+
m.mapperMulti(draft, action, delayedDispatcher, opts);
|
|
115
187
|
rout.push(m);
|
|
116
188
|
}
|
|
117
189
|
else if (m.mapperOnce) {
|
|
118
190
|
(_d = (_c = draft.pihanga) === null || _c === void 0 ? void 0 : _c.reducers) === null || _d === void 0 ? void 0 : _d.push(m.definedIn || m.key || "unknown");
|
|
119
|
-
const flag = m.mapperOnce(draft, action, delayedDispatcher);
|
|
191
|
+
const flag = m.mapperOnce(draft, action, delayedDispatcher, opts);
|
|
120
192
|
if (!flag) {
|
|
121
193
|
rout.push(m);
|
|
122
194
|
}
|
package/dist/reducer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.js","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reducer.js","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -26,9 +26,35 @@ export type PiRegisterComponent = {
|
|
|
26
26
|
[key: string]: string;
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
|
-
export type
|
|
30
|
-
|
|
29
|
+
export type DispatchPReduceTimeoutAction = ReduxAction & {
|
|
30
|
+
cause: "timeout";
|
|
31
|
+
/** Correlation token to route to the correct handler */
|
|
32
|
+
token: string;
|
|
33
|
+
/** The awaited reply action type */
|
|
34
|
+
replyType: string;
|
|
35
|
+
};
|
|
36
|
+
export type ReduceF<S extends ReduxState, A extends ReduxAction> = (state: S, action: A, dispatch: DispatchF, opts?: ReduceOpts<S>) => void;
|
|
37
|
+
export type ReduceOnceF<S extends ReduxState, A extends ReduxAction> = (state: S, action: A, dispatch: DispatchF, opts?: ReduceOpts<S>) => boolean;
|
|
31
38
|
export type DispatchF = <T extends ReduxAction>(a: T) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Options passed to reducer mappers.
|
|
41
|
+
*/
|
|
42
|
+
export interface ReduceOpts<S extends ReduxState> {
|
|
43
|
+
/**
|
|
44
|
+
* The current redux state **before** immer's draft wrapping.
|
|
45
|
+
*/
|
|
46
|
+
rawState: Readonly<S>;
|
|
47
|
+
/**
|
|
48
|
+
* Dispatch a request action (after the current reducer has finished) and then
|
|
49
|
+
* handle the next matching reply.
|
|
50
|
+
*/
|
|
51
|
+
dispatchPReduce: <Req extends ReduxAction, Rep extends ReduxAction, Err extends Rep = never>(request: Req, opts: {
|
|
52
|
+
replyType: string;
|
|
53
|
+
timeoutMs?: number;
|
|
54
|
+
matchReply?: (reply: Rep) => boolean;
|
|
55
|
+
matchError?: (reply: Rep) => reply is Err;
|
|
56
|
+
}, onReply: (state: S, action: Rep, dispatch: DispatchF, opts?: ReduceOpts<S>) => void, onError?: (state: S, action: Err, dispatch: DispatchF, opts?: ReduceOpts<S>) => void, onTimeout?: (state: S, action: DispatchPReduceTimeoutAction, dispatch: DispatchF, opts?: ReduceOpts<S>) => void) => void;
|
|
57
|
+
}
|
|
32
58
|
export interface PiReducer {
|
|
33
59
|
register: PiRegisterReducerF;
|
|
34
60
|
registerOneShot: PiRegisterOneShotReducerF;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IAEb,OAAO,CAAC,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AACF,MAAM,MAAM,SAAS,GAAG;IAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAC,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,CAAC,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAC;CAElC,CAAC;AAEF,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,WAAW,IAAI,CACjE,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0GA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pihanga2/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "max.ott@data61.csiro.au",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
|
-
"src"
|
|
10
|
+
"src",
|
|
11
|
+
"REST-USAGE.md"
|
|
11
12
|
],
|
|
12
13
|
"main": "./dist/index.js",
|
|
13
14
|
"keywords": [
|
package/src/reducer.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {Action, Reducer} from "@reduxjs/toolkit";
|
|
2
2
|
import {
|
|
3
3
|
DispatchF,
|
|
4
|
+
DispatchPReduceTimeoutAction,
|
|
4
5
|
PiReducer,
|
|
5
6
|
PiReducerCancelF,
|
|
6
7
|
PiRegisterOneShotReducerF,
|
|
7
8
|
PiRegisterReducerF,
|
|
9
|
+
ReduceOpts,
|
|
8
10
|
ReduceF,
|
|
9
11
|
ReduceOnceF,
|
|
10
12
|
ReduxAction,
|
|
@@ -50,6 +52,99 @@ export function createReducer(
|
|
|
50
52
|
const delayedDispatcher = (a: any): void => {
|
|
51
53
|
setTimeout(() => dispatcher(a), 0);
|
|
52
54
|
};
|
|
55
|
+
|
|
56
|
+
const DISPATCH_P_REDUCE_TIMEOUT_TYPE = "pi/dispatchPReduce/timeout";
|
|
57
|
+
|
|
58
|
+
const dispatchPReduce: ReduceOpts<ReduxState>["dispatchP"] = (
|
|
59
|
+
request,
|
|
60
|
+
pOpts,
|
|
61
|
+
onReply,
|
|
62
|
+
onError,
|
|
63
|
+
onTimeout,
|
|
64
|
+
) => {
|
|
65
|
+
const {replyType, timeoutMs = 10000, matchReply, matchError} = pOpts;
|
|
66
|
+
|
|
67
|
+
// Use a token so we can route timeout actions.
|
|
68
|
+
const token = `${Date.now()}:${Math.random()}`;
|
|
69
|
+
|
|
70
|
+
// Use `register` (not registerOneShot) so we can cancel explicitly.
|
|
71
|
+
const keyReply = `dispatchPReduce:reply:${replyType}:${token}`;
|
|
72
|
+
const keyTimeout = `dispatchPReduce:timeout:${replyType}:${token}`;
|
|
73
|
+
|
|
74
|
+
let settled = false;
|
|
75
|
+
|
|
76
|
+
let cancelReply: PiReducerCancelF = () => {};
|
|
77
|
+
let cancelTimeout: PiReducerCancelF = () => {};
|
|
78
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
79
|
+
|
|
80
|
+
const cleanup = () => {
|
|
81
|
+
cancelReply();
|
|
82
|
+
cancelTimeout();
|
|
83
|
+
if (timer) {
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Reply handler: decides whether to call onReply or onError.
|
|
89
|
+
cancelReply = registerReducer(
|
|
90
|
+
replyType,
|
|
91
|
+
(s2: any, a2: any, d2: any, o2: any) => {
|
|
92
|
+
if (settled) return;
|
|
93
|
+
if (matchReply && !matchReply(a2)) return;
|
|
94
|
+
settled = true;
|
|
95
|
+
cleanup();
|
|
96
|
+
|
|
97
|
+
// If matchError is provided and the action matches, treat as error.
|
|
98
|
+
if (matchError && matchError(a2)) {
|
|
99
|
+
if (onError) {
|
|
100
|
+
onError(s2, a2, d2, o2);
|
|
101
|
+
} else {
|
|
102
|
+
// Fall back to onReply if no onError was provided.
|
|
103
|
+
onReply(s2, a2, d2, o2);
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
onReply(s2, a2, d2, o2);
|
|
108
|
+
},
|
|
109
|
+
0,
|
|
110
|
+
keyReply,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Timeout handler: triggered by a dispatched internal timeout action.
|
|
114
|
+
if (onTimeout) {
|
|
115
|
+
cancelTimeout = registerReducer(
|
|
116
|
+
DISPATCH_P_REDUCE_TIMEOUT_TYPE,
|
|
117
|
+
(s2: any, a2: any, d2: any, o2: any) => {
|
|
118
|
+
if (settled) return;
|
|
119
|
+
if (!a2 || a2.token !== token) return;
|
|
120
|
+
settled = true;
|
|
121
|
+
cleanup();
|
|
122
|
+
onTimeout(s2, a2, d2, o2);
|
|
123
|
+
},
|
|
124
|
+
0,
|
|
125
|
+
keyTimeout,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
timer = setTimeout(() => {
|
|
130
|
+
if (settled) return;
|
|
131
|
+
if (!onTimeout) {
|
|
132
|
+
// no timeout handler requested
|
|
133
|
+
cleanup();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const timeoutAction: DispatchPReduceTimeoutAction = {
|
|
137
|
+
type: DISPATCH_P_REDUCE_TIMEOUT_TYPE,
|
|
138
|
+
cause: "timeout",
|
|
139
|
+
token,
|
|
140
|
+
replyType,
|
|
141
|
+
};
|
|
142
|
+
delayedDispatcher(timeoutAction);
|
|
143
|
+
}, timeoutMs);
|
|
144
|
+
|
|
145
|
+
// Must dispatch after the current reducer tick.
|
|
146
|
+
delayedDispatcher(request);
|
|
147
|
+
};
|
|
53
148
|
const reducer = (
|
|
54
149
|
state: ReduxState | undefined,
|
|
55
150
|
action: Action,
|
|
@@ -70,16 +165,20 @@ export function createReducer(
|
|
|
70
165
|
}
|
|
71
166
|
|
|
72
167
|
const nextState = produce<ReduxState, ReduxState>(s, (draft) => {
|
|
168
|
+
const opts: ReduceOpts<ReduxState> = {
|
|
169
|
+
rawState: s,
|
|
170
|
+
dispatchP: dispatchPReduce,
|
|
171
|
+
};
|
|
73
172
|
if (!draft.pihanga) {
|
|
74
173
|
draft.pihanga = {};
|
|
75
174
|
}
|
|
76
175
|
draft.pihanga.reducers = [];
|
|
77
176
|
if (ra) {
|
|
78
|
-
const rout = _reduce(ra, draft, action, delayedDispatcher);
|
|
177
|
+
const rout = _reduce(ra, draft, action, delayedDispatcher, opts);
|
|
79
178
|
mappings[action.type] = rout;
|
|
80
179
|
}
|
|
81
180
|
if (rany) {
|
|
82
|
-
const rout2 = _reduce(rany, draft, action, delayedDispatcher);
|
|
181
|
+
const rout2 = _reduce(rany, draft, action, delayedDispatcher, opts);
|
|
83
182
|
mappings["*"] = rout2;
|
|
84
183
|
}
|
|
85
184
|
return;
|
|
@@ -110,7 +209,7 @@ export function createReducer(
|
|
|
110
209
|
A extends ReduxAction,
|
|
111
210
|
>(
|
|
112
211
|
eventType: string,
|
|
113
|
-
mapper:
|
|
212
|
+
mapper: ReduceOnceF<S, A>,
|
|
114
213
|
priority: number = 0,
|
|
115
214
|
key: string | undefined = undefined,
|
|
116
215
|
): PiReducerCancelF => {
|
|
@@ -176,6 +275,7 @@ function _reduce(
|
|
|
176
275
|
draft: ReduxState,
|
|
177
276
|
action: Action,
|
|
178
277
|
delayedDispatcher: (a: any) => void,
|
|
278
|
+
opts: ReduceOpts<ReduxState>,
|
|
179
279
|
): ReducerDef<ReduxState, Action<any>>[] {
|
|
180
280
|
const rout: ReducerDef<ReduxState, Action<any>>[] = [];
|
|
181
281
|
ra.forEach((m) => {
|
|
@@ -185,11 +285,11 @@ function _reduce(
|
|
|
185
285
|
// }
|
|
186
286
|
if (m.mapperMulti) {
|
|
187
287
|
draft.pihanga?.reducers?.push(m.definedIn || m.key || "unknown");
|
|
188
|
-
m.mapperMulti(draft, action, delayedDispatcher);
|
|
288
|
+
m.mapperMulti(draft, action, delayedDispatcher, opts);
|
|
189
289
|
rout.push(m);
|
|
190
290
|
} else if (m.mapperOnce) {
|
|
191
291
|
draft.pihanga?.reducers?.push(m.definedIn || m.key || "unknown");
|
|
192
|
-
const flag = m.mapperOnce(draft, action, delayedDispatcher);
|
|
292
|
+
const flag = m.mapperOnce(draft, action, delayedDispatcher, opts);
|
|
193
293
|
if (!flag) {
|
|
194
294
|
rout.push(m);
|
|
195
295
|
}
|
package/src/types.ts
CHANGED
|
@@ -27,20 +27,76 @@ export type PiRegisterComponent = {
|
|
|
27
27
|
// defaults?: { [key: string]: any }
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
export type DispatchPReduceTimeoutAction = ReduxAction & {
|
|
31
|
+
cause: "timeout";
|
|
32
|
+
/** Correlation token to route to the correct handler */
|
|
33
|
+
token: string;
|
|
34
|
+
/** The awaited reply action type */
|
|
35
|
+
replyType: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
30
38
|
export type ReduceF<S extends ReduxState, A extends ReduxAction> = (
|
|
31
39
|
state: S,
|
|
32
40
|
action: A,
|
|
33
|
-
dispatch: DispatchF
|
|
41
|
+
dispatch: DispatchF,
|
|
42
|
+
opts?: ReduceOpts<S>,
|
|
34
43
|
) => void; // S
|
|
35
44
|
|
|
36
45
|
export type ReduceOnceF<S extends ReduxState, A extends ReduxAction> = (
|
|
37
46
|
state: S,
|
|
38
47
|
action: A,
|
|
39
|
-
dispatch: DispatchF
|
|
48
|
+
dispatch: DispatchF,
|
|
49
|
+
opts?: ReduceOpts<S>,
|
|
40
50
|
) => boolean; // [S, boolean]
|
|
41
51
|
|
|
42
52
|
export type DispatchF = <T extends ReduxAction>(a: T) => void;
|
|
43
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Options passed to reducer mappers.
|
|
56
|
+
*/
|
|
57
|
+
export interface ReduceOpts<S extends ReduxState> {
|
|
58
|
+
/**
|
|
59
|
+
* The current redux state **before** immer's draft wrapping.
|
|
60
|
+
*/
|
|
61
|
+
rawState: Readonly<S>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Dispatch a request action (after the current reducer has finished) and then
|
|
65
|
+
* handle the next matching reply.
|
|
66
|
+
*/
|
|
67
|
+
dispatchP: <
|
|
68
|
+
Req extends ReduxAction,
|
|
69
|
+
Rep extends ReduxAction,
|
|
70
|
+
Err extends Rep = never,
|
|
71
|
+
>(
|
|
72
|
+
request: Req,
|
|
73
|
+
opts: {
|
|
74
|
+
replyType: string;
|
|
75
|
+
timeoutMs?: number;
|
|
76
|
+
matchReply?: (reply: Rep) => boolean;
|
|
77
|
+
matchError?: (reply: Rep) => reply is Err;
|
|
78
|
+
},
|
|
79
|
+
onReply: (
|
|
80
|
+
state: S,
|
|
81
|
+
action: Rep,
|
|
82
|
+
dispatch: DispatchF,
|
|
83
|
+
opts?: ReduceOpts<S>,
|
|
84
|
+
) => void,
|
|
85
|
+
onError?: (
|
|
86
|
+
state: S,
|
|
87
|
+
action: Err,
|
|
88
|
+
dispatch: DispatchF,
|
|
89
|
+
opts?: ReduceOpts<S>,
|
|
90
|
+
) => void,
|
|
91
|
+
onTimeout?: (
|
|
92
|
+
state: S,
|
|
93
|
+
action: DispatchPReduceTimeoutAction,
|
|
94
|
+
dispatch: DispatchF,
|
|
95
|
+
opts?: ReduceOpts<S>,
|
|
96
|
+
) => void,
|
|
97
|
+
) => void;
|
|
98
|
+
}
|
|
99
|
+
|
|
44
100
|
export interface PiReducer {
|
|
45
101
|
register: PiRegisterReducerF;
|
|
46
102
|
registerOneShot: PiRegisterOneShotReducerF;
|
|
@@ -55,18 +111,18 @@ export type PiRegisterReducerF = <S extends ReduxState, A extends ReduxAction>(
|
|
|
55
111
|
mapper: ReduceF<S, A>, // (state: S, action: A, dispatch: DispatchF) => S,
|
|
56
112
|
priority?: number,
|
|
57
113
|
key?: string,
|
|
58
|
-
targetMapper?: ReduceF<S, A
|
|
114
|
+
targetMapper?: ReduceF<S, A>,
|
|
59
115
|
) => PiReducerCancelF;
|
|
60
116
|
|
|
61
117
|
export type PiReducerCancelF = () => void;
|
|
62
118
|
|
|
63
119
|
export type PiRegisterOneShotReducerF = <
|
|
64
120
|
S extends ReduxState,
|
|
65
|
-
A extends ReduxAction
|
|
121
|
+
A extends ReduxAction,
|
|
66
122
|
>(
|
|
67
123
|
eventType: string,
|
|
68
124
|
mapper: ReduceOnceF<S, A>,
|
|
69
|
-
priority?: number
|
|
125
|
+
priority?: number,
|
|
70
126
|
) => void;
|
|
71
127
|
|
|
72
128
|
// CARDS
|
|
@@ -105,7 +161,7 @@ export type PiCardRef = string | PiCardDef;
|
|
|
105
161
|
export type RefF = any;
|
|
106
162
|
export type StateMapper<T, S extends ReduxState, C = PiDefCtxtProps> = (
|
|
107
163
|
state: S,
|
|
108
|
-
context: StateMapperContext<C
|
|
164
|
+
context: StateMapperContext<C>,
|
|
109
165
|
) => T;
|
|
110
166
|
|
|
111
167
|
export type StateMapperContext<C> = {
|
|
@@ -119,7 +175,7 @@ export type PiMapProps<
|
|
|
119
175
|
CType,
|
|
120
176
|
S extends ReduxState,
|
|
121
177
|
EType = {},
|
|
122
|
-
C = PiDefCtxtProps
|
|
178
|
+
C = PiDefCtxtProps,
|
|
123
179
|
> = {
|
|
124
180
|
[Property in keyof CType]:
|
|
125
181
|
| CType[Property]
|
|
@@ -134,7 +190,7 @@ export type EventHandler<T, S extends ReduxState> = {
|
|
|
134
190
|
export type EventMapper<T, C = PiDefCtxtProps> = {
|
|
135
191
|
[Key in keyof T as `${Key & string}Mapper`]?: (
|
|
136
192
|
ev: T[Key],
|
|
137
|
-
ctxt: C
|
|
193
|
+
ctxt: C,
|
|
138
194
|
) => ReduxAction | null;
|
|
139
195
|
};
|
|
140
196
|
|
|
@@ -160,5 +216,5 @@ export type RegisterCardF = (name: string, parameters: PiCardDef) => PiCardRef;
|
|
|
160
216
|
export type MetaCardMapperF = (
|
|
161
217
|
name: string,
|
|
162
218
|
props: any,
|
|
163
|
-
registerCard: RegisterCardF
|
|
219
|
+
registerCard: RegisterCardF,
|
|
164
220
|
) => PiCardDef;
|