@ultimat3/action 19.1.2 → 19.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/client.ts +27 -1
- package/src/errors.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/action",
|
|
3
|
-
"version": "19.
|
|
3
|
+
"version": "19.2.0",
|
|
4
4
|
"description": "The action primitive: one declaration projected to route, OpenAPI, client, MCP tool, job handle, tests",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"test": "bun test"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@ultimat3/cache": "19.
|
|
38
|
-
"@ultimat3/core": "19.
|
|
39
|
-
"@ultimat3/http": "19.
|
|
40
|
-
"@ultimat3/policy": "19.
|
|
41
|
-
"@ultimat3/schema": "19.
|
|
37
|
+
"@ultimat3/cache": "19.2.0",
|
|
38
|
+
"@ultimat3/core": "19.2.0",
|
|
39
|
+
"@ultimat3/http": "19.2.0",
|
|
40
|
+
"@ultimat3/policy": "19.2.0",
|
|
41
|
+
"@ultimat3/schema": "19.2.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/client.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* and the way that is guaranteed is that `keyFor` is never called here.
|
|
11
11
|
*/
|
|
12
12
|
import type { ClientFlight, ClientRetry, UltimateError, WireAnswer } from '@ultimat3/core';
|
|
13
|
-
import { FRAMEWORK_CODE, problemOf, traceHeaders } from '@ultimat3/core';
|
|
13
|
+
import { FRAMEWORK_CODE, isJsonObject, problemOf, traceHeaders } from '@ultimat3/core';
|
|
14
14
|
import type { InferInput, InferOutput, StandardSchemaV1 } from '@ultimat3/schema';
|
|
15
15
|
import type { Action } from './action';
|
|
16
16
|
import { ContractDriftError, RemoteActionError, RpcFailedError } from './errors';
|
|
@@ -216,6 +216,7 @@ function toUltimateError(text: string, status: number, name: string): UltimateEr
|
|
|
216
216
|
// Parsed, never taken: `body` is whatever answered the request. A list this build cannot read
|
|
217
217
|
// is dropped rather than repaired, and `cause` below still carries every rejection in it.
|
|
218
218
|
issues: issuesFromWire(body['issues']),
|
|
219
|
+
meta: metaFromWire(body['meta']),
|
|
219
220
|
cause: stringOr(body['cause'] ?? body['detail'], `${name} failed with ${status}`),
|
|
220
221
|
fix: stringOr(body['fix'], `x actions describe ${name} --json`),
|
|
221
222
|
// RFC-9457's `type` IS a documentation URI, so a server that sends no `docs` extension has
|
|
@@ -226,6 +227,31 @@ function toUltimateError(text: string, status: number, name: string): UltimateEr
|
|
|
226
227
|
});
|
|
227
228
|
}
|
|
228
229
|
|
|
230
|
+
/**
|
|
231
|
+
* The document's `meta` member, or nothing. Parsed JSON is JSON-safe by construction, so the only
|
|
232
|
+
* work is shape: an object, copied own key by own key onto a fresh record. `issues` is skipped
|
|
233
|
+
* because it has its own reader and its own bound one line up, and `__proto__` because
|
|
234
|
+
* `JSON.parse` mints it as a real own key — copied by assignment it would replace the prototype
|
|
235
|
+
* of the error's `meta`, which the reporter and the overlay walk. EMPTY is `undefined`: an
|
|
236
|
+
* `{}` under `meta` would say the server declared keys and sent none.
|
|
237
|
+
*/
|
|
238
|
+
function metaFromWire(value: unknown): Readonly<Record<string, unknown>> | undefined {
|
|
239
|
+
if (!isJsonObject(value)) return undefined;
|
|
240
|
+
const out: Record<string, unknown> = {};
|
|
241
|
+
let carried = 0;
|
|
242
|
+
for (const [key, member] of Object.entries(value)) {
|
|
243
|
+
if (key === 'issues' || key === '__proto__') continue;
|
|
244
|
+
Object.defineProperty(out, key, {
|
|
245
|
+
value: member,
|
|
246
|
+
writable: true,
|
|
247
|
+
enumerable: true,
|
|
248
|
+
configurable: true,
|
|
249
|
+
});
|
|
250
|
+
carried += 1;
|
|
251
|
+
}
|
|
252
|
+
return carried === 0 ? undefined : out;
|
|
253
|
+
}
|
|
254
|
+
|
|
229
255
|
function nonEmpty(value: unknown): string | undefined {
|
|
230
256
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
231
257
|
}
|
package/src/errors.ts
CHANGED
|
@@ -260,6 +260,12 @@ export interface RemoteFailure {
|
|
|
260
260
|
* refuses to read, and in both cases `cause` still holds every rejection.
|
|
261
261
|
*/
|
|
262
262
|
readonly issues?: readonly ValidationIssue[] | undefined;
|
|
263
|
+
/**
|
|
264
|
+
* The document's `meta` member — the keys the server's `registerProblemMeta` declared for the
|
|
265
|
+
* code, already read as a plain JSON object by `client.ts`. Carried onto the error's own `meta`
|
|
266
|
+
* so an island reads `error.meta.sessionId` where it used to run a regex over `cause`.
|
|
267
|
+
*/
|
|
268
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
/** A link, not a string the server happened to put in a field the overlay renders as an href. */
|
|
@@ -314,6 +320,10 @@ export class RemoteActionError extends UltimateError {
|
|
|
314
320
|
// `terminal` on the one field the framework promises a client never has to infer.
|
|
315
321
|
retry: retryForStatus(failure.code, failure.status),
|
|
316
322
|
meta: {
|
|
323
|
+
// The server's declared keys FIRST, so the four this class owns win a collision: a server
|
|
324
|
+
// meta naming `status` would otherwise overwrite the one a report reads the HTTP status
|
|
325
|
+
// from, and `origin: 'remote'` is what marks the code as one this bundle never declared.
|
|
326
|
+
...failure.meta,
|
|
317
327
|
origin: 'remote',
|
|
318
328
|
action: failure.action,
|
|
319
329
|
status: failure.status,
|