@ontrails/http 1.0.0-beta.42 → 1.0.0-beta.43
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/CHANGELOG.md +10 -0
- package/README.md +5 -5
- package/package.json +2 -2
- package/src/blob-output.ts +2 -2
- package/src/build.ts +56 -56
- package/src/bun.ts +6 -6
- package/src/fetch.ts +8 -8
- package/src/index.ts +1 -1
- package/src/method.ts +1 -1
- package/src/openapi.ts +4 -4
- package/src/testing.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @ontrails/http
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.43
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`88a6a62`](https://github.com/outfitter-dev/trails/commit/88a6a62a9e9e230ca6d368fa78dc3ece6c816204): Complete the v1 classification-first cutover from projection/project vocabulary
|
|
8
|
+
to derive/derived for contract-owned fact production and render/rendered for
|
|
9
|
+
surface presentation. Public type, helper, rule, relation, and report names move
|
|
10
|
+
without compatibility aliases; ordinary repository/project nouns remain
|
|
11
|
+
explicit preserves or structured review inventory.
|
|
12
|
+
|
|
3
13
|
## 1.0.0-beta.42
|
|
4
14
|
|
|
5
15
|
## 1.0.0-beta.41
|
package/README.md
CHANGED
|
@@ -32,18 +32,18 @@ await surface(graph, { port: 3000 });
|
|
|
32
32
|
|
|
33
33
|
`@ontrails/http/bun` uses Bun's native `Bun.serve({ routes })` fast path and keeps the shared Web Fetch handler as the fallback. It requires Bun `>=1.2.3` and does not add a third-party runtime dependency.
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Rendering and runtime binding
|
|
36
36
|
|
|
37
37
|
The HTTP package follows the surface API naming split:
|
|
38
38
|
|
|
39
|
-
- `derive*` exports are pure
|
|
39
|
+
- `derive*` exports are pure renderings from the topo. Use `deriveHttpRoutes()` for route definitions and `deriveOpenApiSpec()` for the OpenAPI contract.
|
|
40
40
|
- `create*` exports build runtime objects without opening a network boundary.
|
|
41
41
|
`@ontrails/http/fetch` exports `createRouteHandler()` for one route and
|
|
42
42
|
`createFetchHandler()` for a full topo dispatcher.
|
|
43
43
|
- `surface()` opens the runtime boundary. `@ontrails/hono` opens an adapter
|
|
44
44
|
binding to Hono; `@ontrails/http/bun` opens the native Bun HTTP binding.
|
|
45
45
|
|
|
46
|
-
The shared `@ontrails/http/fetch` kernel owns query/body parsing, content-length validation, public error
|
|
46
|
+
The shared `@ontrails/http/fetch` kernel owns query/body parsing, content-length validation, public error rendering, diagnostics, request IDs, headers, abort propagation, and webhook verification/parsing behavior. Hono and Bun both consume that kernel so route semantics stay aligned.
|
|
47
47
|
|
|
48
48
|
For more control, build the routes yourself:
|
|
49
49
|
|
|
@@ -59,7 +59,7 @@ for (const route of result.value) {
|
|
|
59
59
|
|
|
60
60
|
`deriveHttpRoutes` returns `Result<HttpRouteDefinition[], Error>` rather than a bare array. It returns `Result.err(ValidationError)` if two trails derive the same `(method, path)` pair.
|
|
61
61
|
|
|
62
|
-
OpenAPI is the HTTP surface's persisted client contract
|
|
62
|
+
OpenAPI is the HTTP surface's persisted client contract rendering:
|
|
63
63
|
|
|
64
64
|
```typescript
|
|
65
65
|
import { deriveOpenApiSpec } from '@ontrails/http';
|
|
@@ -93,7 +93,7 @@ import { myHttpAdapter } from './adapter.js';
|
|
|
93
93
|
runConformance(myHttpAdapter, createHttpAdapterConformanceCases());
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
The adapter under test provides a `name` and `createApp(graph, options)` method that returns an object with a Web Fetch-compatible `fetch(request)` handler. The conformance cases cover query and body input
|
|
96
|
+
The adapter under test provides a `name` and `createApp(graph, options)` method that returns an object with a Web Fetch-compatible `fetch(request)` handler. The conformance cases cover query and body input rendering, validation envelopes, public error redaction, request context, abort propagation, and webhook verification/parsing behavior.
|
|
97
97
|
|
|
98
98
|
## Route derivation
|
|
99
99
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/http",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.43",
|
|
4
4
|
"files": [
|
|
5
5
|
"src/**/*.ts",
|
|
6
6
|
"!src/**/__tests__/**",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
28
|
+
"@ontrails/core": "^1.0.0-beta.43"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"zod": "^4.3.5"
|
package/src/blob-output.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared recognition for BlobRef trail output schemas.
|
|
3
3
|
*
|
|
4
|
-
* The runtime handler (`fetch.ts`) and the OpenAPI
|
|
4
|
+
* The runtime handler (`fetch.ts`) and the OpenAPI derivation
|
|
5
5
|
* (`openapi.ts`) must agree on which routes serve raw bytes, so both
|
|
6
6
|
* read the same authored fact: the BlobRef marker meta on the trail's
|
|
7
7
|
* output schema.
|
|
@@ -12,7 +12,7 @@ import { BLOB_REF_SCHEMA_META_KEY } from '@ontrails/core';
|
|
|
12
12
|
/**
|
|
13
13
|
* True when a trail output schema carries the BlobRef marker meta — the
|
|
14
14
|
* authored fact that selects byte streaming over the JSON envelope on
|
|
15
|
-
* the HTTP surface and a binary response body in the OpenAPI
|
|
15
|
+
* the HTTP surface and a binary response body in the OpenAPI derivation.
|
|
16
16
|
*/
|
|
17
17
|
export const isBlobOutputSchema = (output: unknown): boolean => {
|
|
18
18
|
if (typeof output !== 'object' || output === null) {
|
package/src/build.ts
CHANGED
|
@@ -14,14 +14,14 @@ import {
|
|
|
14
14
|
collectAttachedTypedLayers,
|
|
15
15
|
createResources,
|
|
16
16
|
createTrailContext,
|
|
17
|
-
|
|
17
|
+
deriveSurfaceTrailVersionRenderings,
|
|
18
18
|
executeTrail,
|
|
19
19
|
filterSurfaceTrails,
|
|
20
20
|
getActivationWherePredicate,
|
|
21
21
|
getTraceSink,
|
|
22
22
|
LAYER_FIELD_RESERVED_NAMES,
|
|
23
23
|
matchesTrailPattern,
|
|
24
|
-
|
|
24
|
+
renderLayerFieldName,
|
|
25
25
|
TRACE_CONTEXT_KEY,
|
|
26
26
|
traceContextFromRecord,
|
|
27
27
|
validateInput,
|
|
@@ -42,7 +42,7 @@ import type {
|
|
|
42
42
|
BaseSurfaceOptions,
|
|
43
43
|
Layer,
|
|
44
44
|
ResourceOverrideMap,
|
|
45
|
-
|
|
45
|
+
SurfaceTrailVersionRendering,
|
|
46
46
|
TraceContext,
|
|
47
47
|
Topo,
|
|
48
48
|
Trail,
|
|
@@ -99,9 +99,9 @@ export interface HttpRouteDefinition {
|
|
|
99
99
|
readonly trailId: string;
|
|
100
100
|
readonly inputSource: InputSource;
|
|
101
101
|
readonly trail: Trail<unknown, unknown, unknown>;
|
|
102
|
-
readonly versions?: readonly
|
|
102
|
+
readonly versions?: readonly SurfaceTrailVersionRendering[] | undefined;
|
|
103
103
|
/**
|
|
104
|
-
* JSON Schema for the merged request input (trail input +
|
|
104
|
+
* JSON Schema for the merged request input (trail input + rendered layer
|
|
105
105
|
* input fields). Empty/undefined when the trail declares no input and no
|
|
106
106
|
* typed layer is attached. Surface adapters and OpenAPI generators read
|
|
107
107
|
* this to build the published request shape.
|
|
@@ -110,7 +110,7 @@ export interface HttpRouteDefinition {
|
|
|
110
110
|
*/
|
|
111
111
|
readonly inputSchema?: Record<string, unknown> | undefined;
|
|
112
112
|
/**
|
|
113
|
-
* Per-layer
|
|
113
|
+
* Per-layer renderings describing the parameter names the route accepts
|
|
114
114
|
* for typed layers and the routing target back onto each layer's input
|
|
115
115
|
* schema. Empty when the trail has no typed layer attached.
|
|
116
116
|
*
|
|
@@ -121,7 +121,7 @@ export interface HttpRouteDefinition {
|
|
|
121
121
|
*
|
|
122
122
|
* @see TRL-474.
|
|
123
123
|
*/
|
|
124
|
-
readonly
|
|
124
|
+
readonly layerInputRenderings?: readonly HttpLayerInputRendering[];
|
|
125
125
|
readonly parseWebhookInput?:
|
|
126
126
|
| ((rawPayload: unknown) => Result<unknown, Error>)
|
|
127
127
|
| undefined;
|
|
@@ -379,11 +379,11 @@ const withWebhookActivation = (
|
|
|
379
379
|
};
|
|
380
380
|
|
|
381
381
|
// ---------------------------------------------------------------------------
|
|
382
|
-
// Layer input
|
|
382
|
+
// Layer input rendering (TRL-474)
|
|
383
383
|
// ---------------------------------------------------------------------------
|
|
384
384
|
|
|
385
385
|
/**
|
|
386
|
-
* Per-layer
|
|
386
|
+
* Per-layer rendering onto an HTTP route's request input.
|
|
387
387
|
*
|
|
388
388
|
* `routing` maps the parameter name a consumer sees on the request (a query
|
|
389
389
|
* key for `intent: 'read'`, a body field for write/destroy) to the authored
|
|
@@ -391,7 +391,7 @@ const withWebhookActivation = (
|
|
|
391
391
|
* two are the same; on collision the parameter name carries the layer
|
|
392
392
|
* prefix while the routing target preserves the original field.
|
|
393
393
|
*/
|
|
394
|
-
export interface
|
|
394
|
+
export interface HttpLayerInputRendering {
|
|
395
395
|
readonly layerName: string;
|
|
396
396
|
/** parameterName → originalFieldName for this layer. */
|
|
397
397
|
readonly routing: ReadonlyMap<string, string>;
|
|
@@ -429,10 +429,10 @@ const readRequiredFields = (value: unknown): readonly string[] => {
|
|
|
429
429
|
: [];
|
|
430
430
|
};
|
|
431
431
|
|
|
432
|
-
const
|
|
432
|
+
const renderHttpLayerInput = (
|
|
433
433
|
layer: Layer,
|
|
434
434
|
claimedNames: Set<string>
|
|
435
|
-
):
|
|
435
|
+
): HttpLayerInputRendering => {
|
|
436
436
|
if (layer.input === undefined) {
|
|
437
437
|
return {
|
|
438
438
|
layerName: layer.name,
|
|
@@ -464,7 +464,7 @@ const projectHttpLayerInput = (
|
|
|
464
464
|
layerSchema.properties
|
|
465
465
|
)) {
|
|
466
466
|
const renamed = buildHttpRenameTarget(layer.name, fieldName);
|
|
467
|
-
const
|
|
467
|
+
const rendering = renderLayerFieldName(
|
|
468
468
|
layer.name,
|
|
469
469
|
fieldName,
|
|
470
470
|
fieldName,
|
|
@@ -472,28 +472,28 @@ const projectHttpLayerInput = (
|
|
|
472
472
|
claimedNames,
|
|
473
473
|
LAYER_FIELD_RESERVED_NAMES
|
|
474
474
|
);
|
|
475
|
-
properties[
|
|
475
|
+
properties[rendering.claimedName] = fieldSchema;
|
|
476
476
|
if (requiredSet.has(fieldName)) {
|
|
477
|
-
required.push(
|
|
477
|
+
required.push(rendering.claimedName);
|
|
478
478
|
}
|
|
479
|
-
routing.set(
|
|
479
|
+
routing.set(rendering.claimedName, rendering.routingTarget);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
return { layerName: layer.name, properties, required, routing };
|
|
483
483
|
};
|
|
484
484
|
|
|
485
|
-
interface
|
|
485
|
+
interface HttpInputRendering {
|
|
486
486
|
readonly schema: Record<string, unknown> | undefined;
|
|
487
|
-
readonly
|
|
487
|
+
readonly renderings: readonly HttpLayerInputRendering[];
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
const
|
|
490
|
+
const renderHttpInputSchema = (
|
|
491
491
|
trail: Trail<unknown, unknown, unknown>,
|
|
492
492
|
attachedLayers: readonly AttachedTypedLayer[]
|
|
493
|
-
):
|
|
493
|
+
): HttpInputRendering => {
|
|
494
494
|
const baseSchema = zodToJsonSchema(trail.input);
|
|
495
495
|
if (attachedLayers.length === 0) {
|
|
496
|
-
return {
|
|
496
|
+
return { renderings: [], schema: baseSchema };
|
|
497
497
|
}
|
|
498
498
|
|
|
499
499
|
const baseProperties =
|
|
@@ -513,20 +513,20 @@ const projectHttpInputSchema = (
|
|
|
513
513
|
...baseProperties,
|
|
514
514
|
};
|
|
515
515
|
const mergedRequired = [...baseRequired];
|
|
516
|
-
const
|
|
516
|
+
const renderings: HttpLayerInputRendering[] = [];
|
|
517
517
|
|
|
518
518
|
for (const { layer } of attachedLayers) {
|
|
519
|
-
const
|
|
520
|
-
if (
|
|
519
|
+
const rendering = renderHttpLayerInput(layer, claimedNames);
|
|
520
|
+
if (rendering.routing.size === 0) {
|
|
521
521
|
continue;
|
|
522
522
|
}
|
|
523
|
-
Object.assign(mergedProperties,
|
|
524
|
-
mergedRequired.push(...
|
|
525
|
-
|
|
523
|
+
Object.assign(mergedProperties, rendering.properties);
|
|
524
|
+
mergedRequired.push(...rendering.required);
|
|
525
|
+
renderings.push(rendering);
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
-
if (
|
|
529
|
-
return {
|
|
528
|
+
if (renderings.length === 0) {
|
|
529
|
+
return { renderings: [], schema: baseSchema };
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
const mergedSchema: Record<string, unknown> = isJsonObjectSchema(baseSchema)
|
|
@@ -538,7 +538,7 @@ const projectHttpInputSchema = (
|
|
|
538
538
|
delete mergedSchema['required'];
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
-
return {
|
|
541
|
+
return { renderings, schema: mergedSchema };
|
|
542
542
|
};
|
|
543
543
|
|
|
544
544
|
const mergeHttpInputSchemas = (
|
|
@@ -644,28 +644,28 @@ const splitHttpSurfaceVersion = (
|
|
|
644
644
|
* Partition a parsed request input into the trail input plus per-layer
|
|
645
645
|
* inputs, using each layer's routing table.
|
|
646
646
|
*
|
|
647
|
-
* Layer-
|
|
647
|
+
* Layer-rendered parameter names are stripped from the trail input so the
|
|
648
648
|
* trail's schema validation only ever sees its own fields. A layer that
|
|
649
649
|
* received no parameters is omitted from `layerInputs` so consumers can
|
|
650
650
|
* cleanly assert which layers were activated by the request.
|
|
651
651
|
*/
|
|
652
652
|
const partitionHttpInput = (
|
|
653
653
|
input: unknown,
|
|
654
|
-
|
|
654
|
+
renderings: readonly HttpLayerInputRendering[]
|
|
655
655
|
): {
|
|
656
656
|
readonly trailInput: unknown;
|
|
657
657
|
readonly layerInputs: Record<string, unknown>;
|
|
658
658
|
} => {
|
|
659
|
-
if (
|
|
659
|
+
if (renderings.length === 0 || !isJsonObjectSchema(input)) {
|
|
660
660
|
return { layerInputs: {}, trailInput: input };
|
|
661
661
|
}
|
|
662
662
|
const record = input as Record<string, unknown>;
|
|
663
663
|
const claimedKeys = new Set<string>();
|
|
664
664
|
const layerInputs: Record<string, unknown> = {};
|
|
665
|
-
for (const
|
|
665
|
+
for (const rendering of renderings) {
|
|
666
666
|
const layerInput: Record<string, unknown> = {};
|
|
667
667
|
let received = false;
|
|
668
|
-
for (const [paramName, fieldName] of
|
|
668
|
+
for (const [paramName, fieldName] of rendering.routing) {
|
|
669
669
|
claimedKeys.add(paramName);
|
|
670
670
|
const value = record[paramName];
|
|
671
671
|
if (value === undefined) {
|
|
@@ -675,7 +675,7 @@ const partitionHttpInput = (
|
|
|
675
675
|
received = true;
|
|
676
676
|
}
|
|
677
677
|
if (received) {
|
|
678
|
-
layerInputs[
|
|
678
|
+
layerInputs[rendering.layerName] = layerInput;
|
|
679
679
|
}
|
|
680
680
|
}
|
|
681
681
|
const trailInput: Record<string, unknown> = {};
|
|
@@ -704,7 +704,7 @@ const createExecute =
|
|
|
704
704
|
t: Trail<unknown, unknown, unknown>,
|
|
705
705
|
layers: readonly Layer[],
|
|
706
706
|
options: DeriveHttpRoutesOptions,
|
|
707
|
-
|
|
707
|
+
layerRenderings: readonly HttpLayerInputRendering[]
|
|
708
708
|
): HttpRouteDefinition['execute'] =>
|
|
709
709
|
async (input, requestId, abortSignal, request) => {
|
|
710
710
|
const versionedInput = splitHttpSurfaceVersion(
|
|
@@ -714,7 +714,7 @@ const createExecute =
|
|
|
714
714
|
);
|
|
715
715
|
const { trailInput, layerInputs } = partitionHttpInput(
|
|
716
716
|
versionedInput.input,
|
|
717
|
-
|
|
717
|
+
layerRenderings
|
|
718
718
|
);
|
|
719
719
|
const permitResolution = await resolveHttpPermit(
|
|
720
720
|
options,
|
|
@@ -767,7 +767,7 @@ const createWebhookConsumerExecute =
|
|
|
767
767
|
source: WebhookSource,
|
|
768
768
|
layers: readonly Layer[],
|
|
769
769
|
options: DeriveHttpRoutesOptions,
|
|
770
|
-
|
|
770
|
+
layerRenderings: readonly HttpLayerInputRendering[]
|
|
771
771
|
): WebhookConsumerExecute =>
|
|
772
772
|
async (input, requestId, abortSignal, request, activationFireId) => {
|
|
773
773
|
const predicate = getActivationWherePredicate(activationEntry.where);
|
|
@@ -804,7 +804,7 @@ const createWebhookConsumerExecute =
|
|
|
804
804
|
);
|
|
805
805
|
const { trailInput, layerInputs } = partitionHttpInput(
|
|
806
806
|
versionedInput.input,
|
|
807
|
-
|
|
807
|
+
layerRenderings
|
|
808
808
|
);
|
|
809
809
|
const permitResolution = await resolveHttpPermit(
|
|
810
810
|
options,
|
|
@@ -919,22 +919,22 @@ const buildRoute = (
|
|
|
919
919
|
trail,
|
|
920
920
|
options.layers
|
|
921
921
|
);
|
|
922
|
-
const
|
|
923
|
-
const inputSchema = addVersionInputSchema(trail,
|
|
924
|
-
const versions =
|
|
922
|
+
const inputRendering = renderHttpInputSchema(trail, attachedLayers);
|
|
923
|
+
const inputSchema = addVersionInputSchema(trail, inputRendering.schema);
|
|
924
|
+
const versions = deriveSurfaceTrailVersionRenderings(trail);
|
|
925
925
|
return {
|
|
926
926
|
execute: createExecute(
|
|
927
927
|
graph,
|
|
928
928
|
trail,
|
|
929
929
|
layers,
|
|
930
930
|
options,
|
|
931
|
-
|
|
931
|
+
inputRendering.renderings
|
|
932
932
|
),
|
|
933
933
|
...(inputSchema === undefined ? {} : { inputSchema }),
|
|
934
934
|
inputSource: deriveHttpInputSource(method),
|
|
935
|
-
...(
|
|
935
|
+
...(inputRendering.renderings.length === 0
|
|
936
936
|
? {}
|
|
937
|
-
: {
|
|
937
|
+
: { layerInputRenderings: inputRendering.renderings }),
|
|
938
938
|
method,
|
|
939
939
|
path,
|
|
940
940
|
trail,
|
|
@@ -1095,9 +1095,9 @@ const buildWebhookRoute = (
|
|
|
1095
1095
|
trail,
|
|
1096
1096
|
options.layers
|
|
1097
1097
|
);
|
|
1098
|
-
const
|
|
1099
|
-
const inputSchema = addVersionInputSchema(trail,
|
|
1100
|
-
const versions =
|
|
1098
|
+
const inputRendering = renderHttpInputSchema(trail, attachedLayers);
|
|
1099
|
+
const inputSchema = addVersionInputSchema(trail, inputRendering.schema);
|
|
1100
|
+
const versions = deriveSurfaceTrailVersionRenderings(trail);
|
|
1101
1101
|
const consumerExecute = createWebhookConsumerExecute(
|
|
1102
1102
|
graph,
|
|
1103
1103
|
trail,
|
|
@@ -1105,7 +1105,7 @@ const buildWebhookRoute = (
|
|
|
1105
1105
|
source.value,
|
|
1106
1106
|
layers,
|
|
1107
1107
|
options,
|
|
1108
|
-
|
|
1108
|
+
inputRendering.renderings
|
|
1109
1109
|
);
|
|
1110
1110
|
const consumerInvalidRecorder = createWebhookInvalidRecorder(
|
|
1111
1111
|
graph,
|
|
@@ -1118,9 +1118,9 @@ const buildWebhookRoute = (
|
|
|
1118
1118
|
execute: createWebhookExecute(consumerExecute),
|
|
1119
1119
|
...(inputSchema === undefined ? {} : { inputSchema }),
|
|
1120
1120
|
inputSource: 'webhook',
|
|
1121
|
-
...(
|
|
1121
|
+
...(inputRendering.renderings.length === 0
|
|
1122
1122
|
? {}
|
|
1123
|
-
: {
|
|
1123
|
+
: { layerInputRenderings: inputRendering.renderings }),
|
|
1124
1124
|
method: source.value.method,
|
|
1125
1125
|
parseWebhookInput: createWebhookInputParser(source.value),
|
|
1126
1126
|
path: normalizeSourcePath(basePath, source.value.path),
|
|
@@ -1158,7 +1158,7 @@ const isSameWebhookSourceLocation = (
|
|
|
1158
1158
|
|
|
1159
1159
|
/**
|
|
1160
1160
|
* Two webhook source routes can only merge when they declare the same
|
|
1161
|
-
* verifier identity. Reference equality on `verify` matches the
|
|
1161
|
+
* verifier identity. Reference equality on `verify` matches the rendering
|
|
1162
1162
|
* model used elsewhere — a shared source object always passes, while two
|
|
1163
1163
|
* separately-declared verifier functions are treated as distinct policies
|
|
1164
1164
|
* even when their bodies look equivalent.
|
|
@@ -1286,9 +1286,9 @@ const mergeWebhookRoutes = (
|
|
|
1286
1286
|
[WEBHOOK_CONSUMERS]: consumers,
|
|
1287
1287
|
[WEBHOOK_INVALID_RECORDERS]: recorders,
|
|
1288
1288
|
inputSchema: mergeHttpInputSchemas(existing.inputSchema, route.inputSchema),
|
|
1289
|
-
|
|
1290
|
-
...(existing.
|
|
1291
|
-
...(route.
|
|
1289
|
+
layerInputRenderings: [
|
|
1290
|
+
...(existing.layerInputRenderings ?? []),
|
|
1291
|
+
...(route.layerInputRenderings ?? []),
|
|
1292
1292
|
],
|
|
1293
1293
|
...(recordWebhookInvalidFanOut === undefined
|
|
1294
1294
|
? {}
|
package/src/bun.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
InternalError,
|
|
3
3
|
NotFoundError,
|
|
4
4
|
Result,
|
|
5
|
-
|
|
5
|
+
renderPublicSurfaceError,
|
|
6
6
|
trail,
|
|
7
7
|
} from '@ontrails/core';
|
|
8
8
|
import type {
|
|
@@ -61,16 +61,16 @@ const json = (body: Record<string, unknown>, status: number): Response =>
|
|
|
61
61
|
Response.json(body, { status });
|
|
62
62
|
|
|
63
63
|
const mapErrorResponse = (error: Error): Response => {
|
|
64
|
-
const
|
|
64
|
+
const rendering = renderPublicSurfaceError('http', error);
|
|
65
65
|
return json(
|
|
66
66
|
{
|
|
67
67
|
error: {
|
|
68
|
-
category:
|
|
69
|
-
code:
|
|
70
|
-
message:
|
|
68
|
+
category: rendering.category,
|
|
69
|
+
code: rendering.name,
|
|
70
|
+
message: rendering.message,
|
|
71
71
|
},
|
|
72
72
|
},
|
|
73
|
-
|
|
73
|
+
rendering.code
|
|
74
74
|
);
|
|
75
75
|
};
|
|
76
76
|
|
package/src/fetch.ts
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
matchWebhookPath,
|
|
6
6
|
NotFoundError,
|
|
7
7
|
parseWebhookPathParams,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
renderErrorDiagnostics,
|
|
9
|
+
renderPublicSurfaceError,
|
|
10
10
|
ValidationError,
|
|
11
11
|
} from '@ontrails/core';
|
|
12
12
|
import type { BlobRef, Topo } from '@ontrails/core';
|
|
@@ -300,16 +300,16 @@ const json = (body: Record<string, unknown>, status: number): Response =>
|
|
|
300
300
|
Response.json(body, { status });
|
|
301
301
|
|
|
302
302
|
const mapErrorResponse = (error: Error): Response => {
|
|
303
|
-
const
|
|
303
|
+
const rendering = renderPublicSurfaceError('http', error);
|
|
304
304
|
return json(
|
|
305
305
|
{
|
|
306
306
|
error: {
|
|
307
|
-
category:
|
|
308
|
-
code:
|
|
309
|
-
message:
|
|
307
|
+
category: rendering.category,
|
|
308
|
+
code: rendering.name,
|
|
309
|
+
message: rendering.message,
|
|
310
310
|
},
|
|
311
311
|
},
|
|
312
|
-
|
|
312
|
+
rendering.code
|
|
313
313
|
);
|
|
314
314
|
};
|
|
315
315
|
|
|
@@ -332,7 +332,7 @@ const reportInternalDiagnostics = (error: Error, request: Request): void => {
|
|
|
332
332
|
safeRequestId === undefined
|
|
333
333
|
? '[ontrails:http/fetch] Internal error'
|
|
334
334
|
: `[ontrails:http/fetch] Internal error (${safeRequestId})`;
|
|
335
|
-
console.error(label,
|
|
335
|
+
console.error(label, renderErrorDiagnostics(error));
|
|
336
336
|
};
|
|
337
337
|
|
|
338
338
|
interface ResultLike {
|
package/src/index.ts
CHANGED
package/src/method.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type HttpOperationMethod = Lowercase<HttpMethod>;
|
|
|
7
7
|
export type InputSource = 'query' | 'body' | 'webhook';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Owner table for
|
|
10
|
+
* Owner table for rendering trail intent onto HTTP methods.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```ts
|
package/src/openapi.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import {
|
|
9
9
|
ValidationError,
|
|
10
10
|
filterSurfaceTrails,
|
|
11
|
-
|
|
11
|
+
renderErrorClassSurface,
|
|
12
12
|
validateSurfaceTopo,
|
|
13
13
|
zodToJsonSchema,
|
|
14
14
|
} from '@ontrails/core';
|
|
@@ -104,12 +104,12 @@ const errorExampleToEntry = (
|
|
|
104
104
|
errorName: string,
|
|
105
105
|
seen: Set<number>
|
|
106
106
|
): [string, { description: string }] | undefined => {
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
107
|
+
const rendering = renderErrorClassSurface('http', errorName);
|
|
108
|
+
if (rendering === undefined) {
|
|
109
109
|
return undefined;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const { code } =
|
|
112
|
+
const { code } = rendering;
|
|
113
113
|
if (seen.has(code)) {
|
|
114
114
|
return undefined;
|
|
115
115
|
}
|
package/src/testing.ts
CHANGED
|
@@ -317,7 +317,7 @@ export const createHttpAdapterConformanceCases =
|
|
|
317
317
|
},
|
|
318
318
|
{
|
|
319
319
|
check: publicErrorCase,
|
|
320
|
-
name: '
|
|
320
|
+
name: 'renders generic errors as redacted public 500 responses',
|
|
321
321
|
},
|
|
322
322
|
{
|
|
323
323
|
check: requestContextCase,
|