@nice-code/action 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +410 -0
- package/build/index.js +21 -1
- package/build/types/ActionRuntimeEnvironment/ActionHandler/ActionHandler.d.ts +1 -1
- package/build/types/NiceAction/NiceAction.d.ts +1 -1
- package/build/types/NiceAction/NiceAction.types.d.ts +1 -1
- package/build/types/index.d.ts +8 -2
- package/build/types/react-query/index.d.ts +2 -2
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# @nice-code/action
|
|
2
|
+
|
|
3
|
+
A fully-typed action-based RPC framework for TypeScript. Define actions once, execute them locally or over HTTP/WebSocket — same code, same types, everywhere.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
Modern apps split logic across server, client, and workers. Typed RPC frameworks help, but most still require separate client/server type definitions, custom serialization glue, and ad-hoc error handling. `@nice-code/action` solves this with:
|
|
8
|
+
|
|
9
|
+
- **One definition, every environment** — same action works locally, over HTTP, or WebSocket
|
|
10
|
+
- **Custom serialization baked in** — `Date`, `Map`, `Buffer` — define it once, it works across the wire automatically
|
|
11
|
+
- **Typed error unions** — declare which errors an action throws; TypeScript enforces handling them
|
|
12
|
+
- **Observable** — attach listeners for logging, analytics, or tracing with zero boilerplate
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bun add @nice-code/action @nice-code/error valibot
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Core Concepts
|
|
25
|
+
|
|
26
|
+
Actions flow through three states:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
NiceAction (definition) → NiceActionPrimed (input attached) → NiceActionResponse (result)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You mostly work with `NiceAction` and let the framework handle the rest.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### 1. Define a domain and its actions
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { createActionRootDomain, action } from "@nice-code/action";
|
|
42
|
+
import * as v from "valibot";
|
|
43
|
+
|
|
44
|
+
const root = createActionRootDomain({ domain: "app" });
|
|
45
|
+
|
|
46
|
+
const orderDomain = root.createChildDomain({
|
|
47
|
+
domain: "order",
|
|
48
|
+
actions: {
|
|
49
|
+
placeOrder: action()
|
|
50
|
+
.input({ schema: v.object({ items: v.array(v.string()), total: v.number() }) })
|
|
51
|
+
.output({ schema: v.object({ orderId: v.string(), estimatedDelivery: v.string() }) }),
|
|
52
|
+
|
|
53
|
+
cancelOrder: action()
|
|
54
|
+
.input({ schema: v.object({ orderId: v.string(), reason: v.string() }) })
|
|
55
|
+
.output({ schema: v.object({ refundAmount: v.number() }) }),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Register handlers
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { ActionHandler, createActionRuntime } from "@nice-code/action";
|
|
64
|
+
|
|
65
|
+
const handler = new ActionHandler()
|
|
66
|
+
.forAction(orderDomain, "placeOrder", {
|
|
67
|
+
execution: async (primed) => {
|
|
68
|
+
const { items, total } = primed.input; // fully typed
|
|
69
|
+
const order = await db.orders.create({ items, total });
|
|
70
|
+
return primed.setResponse({
|
|
71
|
+
orderId: order.id,
|
|
72
|
+
estimatedDelivery: "2-3 business days",
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
.forAction(orderDomain, "cancelOrder", {
|
|
77
|
+
execution: async (primed) => {
|
|
78
|
+
const refund = await payments.refund(primed.input.orderId);
|
|
79
|
+
return primed.setResponse({ refundAmount: refund.amount });
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
root.setRuntimeEnvironment(
|
|
84
|
+
createActionRuntime({ envId: "server" }).addHandlers([handler])
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 3. Execute
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const result = await orderDomain.action("placeOrder").execute({
|
|
92
|
+
items: ["SKU-001", "SKU-002"],
|
|
93
|
+
total: 49.99,
|
|
94
|
+
});
|
|
95
|
+
// result: { orderId: "ord_abc123", estimatedDelivery: "2-3 business days" }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Typed Error Handling
|
|
101
|
+
|
|
102
|
+
Declare errors your action can throw — TypeScript tracks them through `executeSafe()`.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { err_auth, err_payment } from "@nice-code/common-errors";
|
|
106
|
+
|
|
107
|
+
const checkoutDomain = root.createChildDomain({
|
|
108
|
+
domain: "checkout",
|
|
109
|
+
actions: {
|
|
110
|
+
pay: action()
|
|
111
|
+
.input({ schema: v.object({ cartId: v.string(), cardToken: v.string() }) })
|
|
112
|
+
.output({ schema: v.object({ receiptId: v.string() }) })
|
|
113
|
+
.throws(err_auth, ["unauthenticated"]) // only this error from auth domain
|
|
114
|
+
.throws(err_payment), // all payment errors
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const result = await checkoutDomain.action("pay").executeSafe({
|
|
121
|
+
cartId: "cart_xyz",
|
|
122
|
+
cardToken: "tok_...",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!result.ok) {
|
|
126
|
+
// result.error is a fully typed NiceError union
|
|
127
|
+
result.error.handleWithSync([
|
|
128
|
+
forId(err_auth, "unauthenticated", () => res.status(401).json({ error: "Login required" })),
|
|
129
|
+
forId(err_payment, "card_declined", (err) => {
|
|
130
|
+
const { last4 } = err.getContext();
|
|
131
|
+
res.status(402).json({ error: `Card ending in ${last4} was declined` });
|
|
132
|
+
}),
|
|
133
|
+
forDomain(err_payment, () => res.status(402).json({ error: "Payment failed" })),
|
|
134
|
+
]);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// result.output: { receiptId: string }
|
|
139
|
+
res.json({ receiptId: result.output.receiptId });
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Custom Serialization
|
|
145
|
+
|
|
146
|
+
Non-JSON types — `Date`, `Map`, binary — need serialization for transport. Define it once on the schema; the framework handles it on both ends.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const eventDomain = root.createChildDomain({
|
|
150
|
+
domain: "event",
|
|
151
|
+
actions: {
|
|
152
|
+
schedule: action()
|
|
153
|
+
.input(
|
|
154
|
+
{ schema: v.object({ name: v.string(), scheduledAt: v.date() }) },
|
|
155
|
+
// serialize: Date → ISO string for the wire
|
|
156
|
+
({ name, scheduledAt }) => ({ name, iso: scheduledAt.toISOString() }),
|
|
157
|
+
// deserialize: ISO string → Date on the other end
|
|
158
|
+
({ name, iso }) => ({ name, scheduledAt: new Date(iso) })
|
|
159
|
+
)
|
|
160
|
+
.output({ schema: v.object({ eventId: v.string() }) }),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// Client sends this:
|
|
167
|
+
await eventDomain.action("schedule").execute({
|
|
168
|
+
name: "Team meeting",
|
|
169
|
+
scheduledAt: new Date("2025-03-15T14:00:00Z"), // ← native Date
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Wire format (what actually travels over HTTP):
|
|
173
|
+
// { name: "Team meeting", iso: "2025-03-15T14:00:00.000Z" }
|
|
174
|
+
|
|
175
|
+
// Server receives native Date automatically — no manual parsing
|
|
176
|
+
handler.forAction(eventDomain, "schedule", {
|
|
177
|
+
execution: async (primed) => {
|
|
178
|
+
console.log(primed.input.scheduledAt instanceof Date); // true
|
|
179
|
+
await calendar.create(primed.input.scheduledAt);
|
|
180
|
+
return primed.setResponse({ eventId: "evt_..." });
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Same Actions, Different Environments
|
|
188
|
+
|
|
189
|
+
The same domain definition works on both server (with handlers) and client (with remote transport). No code duplication.
|
|
190
|
+
|
|
191
|
+
### Server
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// server.ts
|
|
195
|
+
const handler = new ActionHandler().forDomain(orderDomain, {
|
|
196
|
+
execution: async (primed) => {
|
|
197
|
+
/* ... real DB logic ... */
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
root.setRuntimeEnvironment(
|
|
202
|
+
createActionRuntime({ envId: "server" }).addHandlers([handler])
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// One endpoint handles all actions
|
|
206
|
+
app.post("/actions", async (req, res) => {
|
|
207
|
+
const result = await handler.handleWire(req.body);
|
|
208
|
+
if (!result.handled) return res.status(404).end();
|
|
209
|
+
res.json(result.response.toJsonObject());
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Client
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
// client.ts — exact same domain, different runtime
|
|
217
|
+
import { ActionConnect, ConnectionConfig, createActionRuntime } from "@nice-code/action";
|
|
218
|
+
|
|
219
|
+
const connect = new ActionConnect(
|
|
220
|
+
[new ConnectionConfig({ transports: [{ type: "http", url: "/actions" }] })],
|
|
221
|
+
{ requestTimeout: 30_000 }
|
|
222
|
+
).routeDomain(orderDomain);
|
|
223
|
+
|
|
224
|
+
root.setRuntimeEnvironment(
|
|
225
|
+
createActionRuntime({ envId: "browser" }).addHandlers([connect])
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Works exactly like local execution — goes over HTTP transparently
|
|
229
|
+
const result = await orderDomain.action("placeOrder").execute({
|
|
230
|
+
items: ["SKU-001"],
|
|
231
|
+
total: 29.99,
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### WebSocket for real-time
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
new ConnectionConfig({
|
|
239
|
+
transports: [
|
|
240
|
+
{ type: "ws", url: "wss://api.example.com/ws" }, // persistent connection, no reconnect overhead
|
|
241
|
+
{ type: "http", url: "/actions" }, // fallback
|
|
242
|
+
],
|
|
243
|
+
})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Observability
|
|
249
|
+
|
|
250
|
+
Attach listeners to any domain — fire for every action without modifying handlers.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const unsubscribe = orderDomain.addActionListener({
|
|
254
|
+
execution: (primed, { runtime }) => {
|
|
255
|
+
logger.info(`[${runtime.name}] → ${primed.domain}::${primed.id}`, primed.input);
|
|
256
|
+
},
|
|
257
|
+
response: (response, { runtime }) => {
|
|
258
|
+
const { result } = response;
|
|
259
|
+
if (result.ok) {
|
|
260
|
+
metrics.increment(`action.success`, { action: response.id });
|
|
261
|
+
} else {
|
|
262
|
+
metrics.increment(`action.error`, { action: response.id, error: result.error.id });
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Clean up when done
|
|
268
|
+
unsubscribe();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Pattern Matching
|
|
274
|
+
|
|
275
|
+
When you receive an action and need to branch on its identity:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { matchAction } from "@nice-code/action";
|
|
279
|
+
|
|
280
|
+
await matchAction(incomingAction)
|
|
281
|
+
.with({
|
|
282
|
+
domain: orderDomain,
|
|
283
|
+
id: "placeOrder",
|
|
284
|
+
handler: async (action) => {
|
|
285
|
+
// action narrowed to NiceAction<OrderDomain, "placeOrder">
|
|
286
|
+
await notifyWarehouse(action.input);
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
.with({
|
|
290
|
+
domain: orderDomain,
|
|
291
|
+
id: "cancelOrder",
|
|
292
|
+
handler: async (action) => {
|
|
293
|
+
await notifyCustomer(action.input.orderId);
|
|
294
|
+
},
|
|
295
|
+
})
|
|
296
|
+
.otherwise(async (action) => {
|
|
297
|
+
logger.warn(`Unhandled action: ${action.domain}::${action.id}`);
|
|
298
|
+
})
|
|
299
|
+
.runAsync();
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Wire Format
|
|
305
|
+
|
|
306
|
+
Every action state serializes to JSON — useful for logging, queuing, or replay:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
const primed = orderDomain.action("placeOrder").prime({ items: ["SKU-001"], total: 29.99 });
|
|
310
|
+
|
|
311
|
+
const wire = primed.toJsonObject();
|
|
312
|
+
// {
|
|
313
|
+
// type: "primed",
|
|
314
|
+
// domain: "order",
|
|
315
|
+
// allDomains: ["order", "app"],
|
|
316
|
+
// id: "placeOrder",
|
|
317
|
+
// cuid: "abc123...",
|
|
318
|
+
// timeCreated: 1704067200000,
|
|
319
|
+
// timePrimed: 1704067201000,
|
|
320
|
+
// input: { items: ["SKU-001"], total: 29.99 }
|
|
321
|
+
// }
|
|
322
|
+
|
|
323
|
+
// Reconstruct anywhere
|
|
324
|
+
const rehydrated = orderDomain.hydratePrimed(wire);
|
|
325
|
+
await rehydrated.execute(); // executes with original input
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Store actions in a queue, ship them to a worker, execute on the other side — full round-trip with validation.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Named Handlers (Tags)
|
|
333
|
+
|
|
334
|
+
Route different action types to different handlers — useful for auth tiers, feature flags, or test overrides.
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
const adminHandler = new ActionHandler({ tag: "admin" })
|
|
338
|
+
.forAction(userDomain, "deleteUser", {
|
|
339
|
+
execution: async (primed) => { /* admin-only logic */ },
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const publicHandler = new ActionHandler()
|
|
343
|
+
.forDomain(userDomain, { execution: handlePublicActions });
|
|
344
|
+
|
|
345
|
+
root.setRuntimeEnvironment(
|
|
346
|
+
createActionRuntime({ envId: "server" })
|
|
347
|
+
.addHandlers([adminHandler, publicHandler])
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Use the tagged handler explicitly
|
|
351
|
+
await userDomain.action("deleteUser").execute(input, { tag: "admin" });
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## API Reference
|
|
357
|
+
|
|
358
|
+
### Domain
|
|
359
|
+
|
|
360
|
+
| Method | Description |
|
|
361
|
+
|---|---|
|
|
362
|
+
| `createActionRootDomain({ domain })` | Create root domain |
|
|
363
|
+
| `root.createChildDomain({ domain, actions })` | Create child domain |
|
|
364
|
+
| `domain.action(id)` | Get action definition |
|
|
365
|
+
| `domain.addActionListener({ execution?, response? })` | Observe all actions; returns unsubscribe fn |
|
|
366
|
+
| `domain.hydratePrimed(wire)` | Reconstruct primed action from JSON |
|
|
367
|
+
| `domain.hydrateResponse(wire)` | Reconstruct response from JSON |
|
|
368
|
+
|
|
369
|
+
### Action
|
|
370
|
+
|
|
371
|
+
| Method | Description |
|
|
372
|
+
|---|---|
|
|
373
|
+
| `action.execute(input, meta?)` | Execute; throws on error |
|
|
374
|
+
| `action.executeSafe(input, meta?)` | Execute; returns `{ ok, output }` or `{ ok, error }` |
|
|
375
|
+
| `action.prime(input)` | Attach and validate input |
|
|
376
|
+
| `action.is(other)` | Type guard — check if action matches this definition |
|
|
377
|
+
|
|
378
|
+
### Schema Builder (`action()`)
|
|
379
|
+
|
|
380
|
+
| Method | Description |
|
|
381
|
+
|---|---|
|
|
382
|
+
| `.input({ schema }, serialize?, deserialize?)` | Declare input schema + optional serde |
|
|
383
|
+
| `.output({ schema }, serialize?, deserialize?)` | Declare output schema + optional serde |
|
|
384
|
+
| `.throws(errorDomain, [ids]?)` | Declare throwable errors (all or subset) |
|
|
385
|
+
|
|
386
|
+
### Handlers
|
|
387
|
+
|
|
388
|
+
| Class | Use case |
|
|
389
|
+
|---|---|
|
|
390
|
+
| `ActionHandler` | Local/same-process execution |
|
|
391
|
+
| `ActionConnect` | Remote execution over HTTP or WebSocket |
|
|
392
|
+
|
|
393
|
+
### ActionHandler
|
|
394
|
+
|
|
395
|
+
| Method | Description |
|
|
396
|
+
|---|---|
|
|
397
|
+
| `.forAction(domain, id, { execution, response? })` | Handle one action |
|
|
398
|
+
| `.forActionIds(domain, ids, { execution, response? })` | Handle a subset |
|
|
399
|
+
| `.forDomain(domain, { execution, response? })` | Handle all actions in domain |
|
|
400
|
+
| `.forDomainActionCases(domain, cases)` | Handle actions via case map |
|
|
401
|
+
| `.handleWire(body)` | Parse and dispatch raw JSON (for HTTP endpoints) |
|
|
402
|
+
|
|
403
|
+
### ActionConnect
|
|
404
|
+
|
|
405
|
+
| Method | Description |
|
|
406
|
+
|---|---|
|
|
407
|
+
| `.routeDomain(domain, route?)` | Route all domain actions remotely |
|
|
408
|
+
| `.routeAction(domain, id, route?)` | Route one action remotely |
|
|
409
|
+
| `.routeActionIds(domain, ids, route?)` | Route a subset remotely |
|
|
410
|
+
| `.disconnect()` | Close connections |
|
package/build/index.js
CHANGED
|
@@ -3528,6 +3528,20 @@ class Transport {
|
|
|
3528
3528
|
}
|
|
3529
3529
|
}
|
|
3530
3530
|
|
|
3531
|
+
// src/ActionRuntimeEnvironment/ActionConnect/Transport/Transport.types.ts
|
|
3532
|
+
var ETransportType;
|
|
3533
|
+
((ETransportType2) => {
|
|
3534
|
+
ETransportType2["ws"] = "ws";
|
|
3535
|
+
ETransportType2["http"] = "http";
|
|
3536
|
+
})(ETransportType ||= {});
|
|
3537
|
+
var ETransportStatus;
|
|
3538
|
+
((ETransportStatus2) => {
|
|
3539
|
+
ETransportStatus2["uninitialized"] = "uninitialized";
|
|
3540
|
+
ETransportStatus2["initializing"] = "initializing";
|
|
3541
|
+
ETransportStatus2["ready"] = "ready";
|
|
3542
|
+
ETransportStatus2["failed"] = "failed";
|
|
3543
|
+
})(ETransportStatus ||= {});
|
|
3544
|
+
|
|
3531
3545
|
// src/ActionRuntimeEnvironment/ActionConnect/Transport/TransportHttp.ts
|
|
3532
3546
|
class TransportHttp extends Transport {
|
|
3533
3547
|
abortControllers = new Map;
|
|
@@ -4200,16 +4214,22 @@ export {
|
|
|
4200
4214
|
createActionRuntime,
|
|
4201
4215
|
createActionRootDomain,
|
|
4202
4216
|
action,
|
|
4203
|
-
|
|
4217
|
+
TransportWebSocket,
|
|
4218
|
+
TransportHttp,
|
|
4219
|
+
Transport,
|
|
4204
4220
|
NiceActionSchema,
|
|
4221
|
+
NiceActionRootDomain,
|
|
4205
4222
|
NiceActionResponse,
|
|
4206
4223
|
NiceActionPrimed,
|
|
4207
4224
|
NiceActionDomain,
|
|
4208
4225
|
NiceAction,
|
|
4226
|
+
ETransportType,
|
|
4227
|
+
ETransportStatus,
|
|
4209
4228
|
EErrId_NiceTransport_WebSocket,
|
|
4210
4229
|
EErrId_NiceTransport,
|
|
4211
4230
|
EErrId_NiceAction,
|
|
4212
4231
|
EActionState,
|
|
4232
|
+
ConnectionConfig,
|
|
4213
4233
|
ActionRuntimeEnvironment,
|
|
4214
4234
|
ActionHandler,
|
|
4215
4235
|
ActionConnect
|
|
@@ -15,7 +15,7 @@ export declare class ActionHandler implements IActionHandler {
|
|
|
15
15
|
private getHandlersForAction;
|
|
16
16
|
/**
|
|
17
17
|
* Register a handler for all actions in a domain.
|
|
18
|
-
* Receives the full primed action — use `
|
|
18
|
+
* Receives the full primed action — use `matchAction()` to narrow to a specific action id.
|
|
19
19
|
* Useful for forwarding all domain actions to a remote endpoint.
|
|
20
20
|
* Lower priority than `forAction`.
|
|
21
21
|
*/
|
|
@@ -39,7 +39,7 @@ export declare class NiceAction<DOM extends INiceActionDomain, ID extends keyof
|
|
|
39
39
|
* ```ts
|
|
40
40
|
* const result = await domain.action("getUser").executeSafe({ userId: "123" });
|
|
41
41
|
* if (!result.ok) {
|
|
42
|
-
* result.error.
|
|
42
|
+
* result.error.handleWithSync([
|
|
43
43
|
* forDomain(err_auth, (h) => res.status(401).end()),
|
|
44
44
|
* ]);
|
|
45
45
|
* return;
|
|
@@ -39,7 +39,7 @@ export type INiceActionPrimed_JsonObject<DOM extends INiceActionDomain = INiceAc
|
|
|
39
39
|
* ```ts
|
|
40
40
|
* const result = await domain.action("getUser").executeSafe({ userId: "123" });
|
|
41
41
|
* if (!result.ok) {
|
|
42
|
-
* result.error.
|
|
42
|
+
* result.error.handleWithSync([
|
|
43
43
|
* forDomain(err_auth, (h) => res.status(401).end()),
|
|
44
44
|
* ]);
|
|
45
45
|
* return;
|
package/build/types/index.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
export { createActionRootDomain } from "./ActionDomain/helpers/createRootActionDomain";
|
|
2
2
|
export { NiceActionDomain } from "./ActionDomain/NiceActionDomain";
|
|
3
|
-
export type { INiceActionDomain, INiceActionDomainChildOptions, MaybePromise, TDomainActionId, TInferInputFromSchema, TInferOutputFromSchema, TNiceActionDomainChildDef, TNiceActionDomainSchema, TPossibleDomainId, TPossibleDomainIdList, } from "./ActionDomain/NiceActionDomain.types";
|
|
3
|
+
export type { INiceActionDomain, INiceActionDomainChildOptions, INiceActionRootDomain, MaybePromise, TDomainActionId, TInferInputFromSchema, TInferOutputFromSchema, TNiceActionDomainChildDef, TNiceActionDomainSchema, TPossibleDomainId, TPossibleDomainIdList, } from "./ActionDomain/NiceActionDomain.types";
|
|
4
|
+
export { NiceActionRootDomain } from "./ActionDomain/RootDomain/NiceActionRootDomain";
|
|
4
5
|
export { ActionConnect } from "./ActionRuntimeEnvironment/ActionConnect/ActionConnect";
|
|
5
6
|
export * from "./ActionRuntimeEnvironment/ActionConnect/ActionConnect.types";
|
|
6
|
-
export { ConnectionConfig
|
|
7
|
+
export { ConnectionConfig } from "./ActionRuntimeEnvironment/ActionConnect/ConnectionConfig/ConnectionConfig";
|
|
8
|
+
export * from "./ActionRuntimeEnvironment/ActionConnect/ConnectionConfig/ConnectionConfig.types";
|
|
7
9
|
export * from "./ActionRuntimeEnvironment/ActionConnect/err_nice_connect";
|
|
8
10
|
export * from "./ActionRuntimeEnvironment/ActionConnect/Transport/err_nice_transport";
|
|
9
11
|
export * from "./ActionRuntimeEnvironment/ActionConnect/Transport/err_nice_transport_ws";
|
|
12
|
+
export * from "./ActionRuntimeEnvironment/ActionConnect/Transport/Transport";
|
|
13
|
+
export * from "./ActionRuntimeEnvironment/ActionConnect/Transport/Transport.types";
|
|
14
|
+
export * from "./ActionRuntimeEnvironment/ActionConnect/Transport/TransportHttp";
|
|
15
|
+
export * from "./ActionRuntimeEnvironment/ActionConnect/Transport/TransportWebSocket";
|
|
10
16
|
export { ActionHandler, createHandler, } from "./ActionRuntimeEnvironment/ActionHandler/ActionHandler";
|
|
11
17
|
export type { IActionHandlerInputs, TAtLeastOne, TExecutionAndResponseHandlers, THandleActionExecutionFn, THandleActionResponseFn, THandleActionResult, } from "./ActionRuntimeEnvironment/ActionHandler/ActionHandler.types";
|
|
12
18
|
export { ActionRuntimeEnvironment, createActionRuntime, } from "./ActionRuntimeEnvironment/ActionRuntimeEnvironment";
|
|
@@ -37,7 +37,7 @@ export type TUseNiceMutationOptions<DOM extends INiceActionDomain, ID extends ke
|
|
|
37
37
|
* Passing `null` or `undefined` as `input` disables the query (sets `enabled: false`),
|
|
38
38
|
* which allows conditional execution while respecting React's rules of hooks.
|
|
39
39
|
*
|
|
40
|
-
* The `
|
|
40
|
+
* The `tag` option targets a specific named handler registered on the runtime environment.
|
|
41
41
|
*
|
|
42
42
|
* Supports TanStack Query's `select` option with full type inference — if you pass a
|
|
43
43
|
* `select` transformer, `data` will be typed as the transformer's return type.
|
|
@@ -59,7 +59,7 @@ export declare function useNiceQuery<DOM extends INiceActionDomain, ID extends k
|
|
|
59
59
|
* Ideal for actions that change server state — form submissions, updates, deletes, etc.
|
|
60
60
|
* The input is provided at call time via `mutation.mutate(input)` or `mutation.mutateAsync(input)`.
|
|
61
61
|
*
|
|
62
|
-
* The `
|
|
62
|
+
* The `tag` option targets a specific named handler registered on the runtime environment.
|
|
63
63
|
*
|
|
64
64
|
* @example
|
|
65
65
|
* const mutation = useNiceMutation(domain.action("createUser"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nice-code/action",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"build-types": "tsc --project tsconfig.build.json"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@nice-code/error": "0.1.
|
|
36
|
-
"@nice-code/common-errors": "0.1.
|
|
35
|
+
"@nice-code/error": "0.1.1",
|
|
36
|
+
"@nice-code/common-errors": "0.1.1",
|
|
37
37
|
"@standard-schema/spec": "^1.1.0",
|
|
38
38
|
"http-status-codes": "^2.3.0",
|
|
39
39
|
"nanoid": "^5.1.9",
|