@qlever-llc/trellis 0.10.10 → 0.10.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/client.d.ts +2 -0
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js +2 -0
- package/esm/client_connect.d.ts +3 -2
- package/esm/client_connect.d.ts.map +1 -1
- package/esm/client_connect.js +4 -1
- package/esm/errors/TrellisError.d.ts +3 -3
- package/esm/errors/TrellisError.js +3 -3
- package/esm/server/health.d.ts.map +1 -1
- package/esm/server/health.js +34 -3
- package/esm/server/internal_jobs/job-manager.d.ts.map +1 -1
- package/esm/server/internal_jobs/job-manager.js +32 -1
- package/esm/server/runtime.d.ts +3 -0
- package/esm/server/runtime.d.ts.map +1 -1
- package/esm/server/service.d.ts +15 -0
- package/esm/server/service.d.ts.map +1 -1
- package/esm/server/service.js +41 -3
- package/esm/server.d.ts.map +1 -1
- package/esm/server.js +99 -10
- package/esm/service/deno.d.ts +1 -1
- package/esm/service/deno.d.ts.map +1 -1
- package/esm/service/mod.d.ts +1 -1
- package/esm/service/mod.d.ts.map +1 -1
- package/esm/service/node.d.ts +1 -1
- package/esm/service/node.d.ts.map +1 -1
- package/esm/service/outbox_inbox.d.ts.map +1 -1
- package/esm/service/outbox_inbox.js +14 -0
- package/esm/telemetry/core.d.ts.map +1 -1
- package/esm/telemetry/core.js +1 -1
- package/esm/telemetry/env.d.ts.map +1 -1
- package/esm/telemetry/env.js +6 -1
- package/esm/telemetry/init.d.ts +3 -0
- package/esm/telemetry/init.d.ts.map +1 -0
- package/esm/telemetry/init.js +7 -0
- package/esm/telemetry/metrics.d.ts +34 -0
- package/esm/telemetry/metrics.d.ts.map +1 -0
- package/esm/telemetry/metrics.js +181 -0
- package/esm/telemetry/mod.d.ts +3 -0
- package/esm/telemetry/mod.d.ts.map +1 -1
- package/esm/telemetry/mod.js +2 -0
- package/esm/telemetry/runtime.d.ts +2 -0
- package/esm/telemetry/runtime.d.ts.map +1 -0
- package/esm/telemetry/runtime.js +134 -0
- package/esm/telemetry.d.ts +3 -0
- package/esm/telemetry.d.ts.map +1 -0
- package/esm/telemetry.js +2 -0
- package/esm/transfer.d.ts.map +1 -1
- package/esm/transfer.js +27 -16
- package/esm/trellis.d.ts +28 -4
- package/esm/trellis.d.ts.map +1 -1
- package/esm/trellis.js +575 -80
- package/package.json +7 -5
- package/script/client.d.ts +2 -0
- package/script/client.d.ts.map +1 -1
- package/script/client.js +2 -0
- package/script/client_connect.d.ts +3 -2
- package/script/client_connect.d.ts.map +1 -1
- package/script/client_connect.js +4 -1
- package/script/errors/TrellisError.d.ts +3 -3
- package/script/errors/TrellisError.js +3 -3
- package/script/server/health.d.ts.map +1 -1
- package/script/server/health.js +34 -3
- package/script/server/internal_jobs/job-manager.d.ts.map +1 -1
- package/script/server/internal_jobs/job-manager.js +32 -1
- package/script/server/runtime.d.ts +3 -0
- package/script/server/runtime.d.ts.map +1 -1
- package/script/server/service.d.ts +15 -0
- package/script/server/service.d.ts.map +1 -1
- package/script/server/service.js +40 -2
- package/script/server.d.ts.map +1 -1
- package/script/server.js +98 -9
- package/script/service/deno.d.ts +1 -1
- package/script/service/deno.d.ts.map +1 -1
- package/script/service/mod.d.ts +1 -1
- package/script/service/mod.d.ts.map +1 -1
- package/script/service/node.d.ts +1 -1
- package/script/service/node.d.ts.map +1 -1
- package/script/service/outbox_inbox.d.ts.map +1 -1
- package/script/service/outbox_inbox.js +14 -0
- package/script/telemetry/core.d.ts.map +1 -1
- package/script/telemetry/core.js +1 -1
- package/script/telemetry/env.d.ts.map +1 -1
- package/script/telemetry/env.js +6 -1
- package/script/telemetry/init.d.ts +3 -0
- package/script/telemetry/init.d.ts.map +1 -0
- package/script/telemetry/init.js +10 -0
- package/script/telemetry/metrics.d.ts +34 -0
- package/script/telemetry/metrics.d.ts.map +1 -0
- package/script/telemetry/metrics.js +186 -0
- package/script/telemetry/mod.d.ts +3 -0
- package/script/telemetry/mod.d.ts.map +1 -1
- package/script/telemetry/mod.js +7 -1
- package/script/telemetry/runtime.d.ts +2 -0
- package/script/telemetry/runtime.d.ts.map +1 -0
- package/script/telemetry/runtime.js +137 -0
- package/script/telemetry.d.ts +3 -0
- package/script/telemetry.d.ts.map +1 -0
- package/script/telemetry.js +18 -0
- package/script/transfer.d.ts.map +1 -1
- package/script/transfer.js +28 -17
- package/script/trellis.d.ts +28 -4
- package/script/trellis.d.ts.map +1 -1
- package/script/trellis.js +606 -110
- package/src/client.ts +4 -0
- package/src/client_connect.ts +11 -9
- package/src/errors/TrellisError.ts +4 -4
- package/src/server/health.ts +41 -3
- package/src/server/internal_jobs/job-manager.ts +35 -5
- package/src/server/runtime.ts +4 -0
- package/src/server/service.ts +75 -3
- package/src/server.ts +124 -14
- package/src/service/deno.ts +1 -0
- package/src/service/mod.ts +1 -0
- package/src/service/node.ts +1 -0
- package/src/service/outbox_inbox.ts +14 -0
- package/src/telemetry/core.ts +1 -1
- package/src/telemetry/env.ts +5 -1
- package/src/telemetry/init.ts +8 -0
- package/src/telemetry/metrics.ts +294 -0
- package/src/telemetry/mod.ts +7 -0
- package/src/telemetry/runtime.ts +218 -0
- package/src/telemetry.ts +2 -0
- package/src/transfer.ts +69 -30
- package/src/trellis.ts +652 -141
- package/esm/tracing.d.ts +0 -5
- package/esm/tracing.d.ts.map +0 -1
- package/esm/tracing.js +0 -8
- package/script/tracing.d.ts +0 -5
- package/script/tracing.d.ts.map +0 -1
- package/script/tracing.js +0 -27
- package/src/tracing.ts +0 -28
package/esm/trellis.js
CHANGED
|
@@ -9,12 +9,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _Trellis_instances, _Trellis_log, _Trellis_tasks, _Trellis_hasExplicitApi, _Trellis_noResponderMaxRetries, _Trellis_noResponderRetryMs, _Trellis_onSessionNotFound, _Trellis_operationStore, _Trellis_eventConsumers, _Trellis_durableEventLoops, _Trellis_createStateFacade, _Trellis_createRpcFacade, _Trellis_createRpcHandleFacade, _Trellis_createEventFacade, _Trellis_createEventPublishFacade, _Trellis_createFeedFacade, _Trellis_createHandlerTrellis, _Trellis_createOperationFacade, _Trellis_unknownApiError, _Trellis_requestBuiltRpcUnknown, _Trellis_requestBuiltRpc, _Trellis_handleBrowserAuthRequired, _Trellis_authenticateFeedRequest, _Trellis_subscribeFeed, _Trellis_handleFeed, _Trellis_processFeedMessage, _Trellis_handleRPC, _Trellis_processRPCMessage, _Trellis_respondWithPayload, _Trellis_respondWithError, _Trellis_startEphemeralEvent, _Trellis_resolveEventConsumerGroup, _Trellis_registerDurableEventHandler, _Trellis_startDurableEventConsumer, _Trellis_durableEventConsumerGroupReady, _Trellis_runDurableEventConsumer, _Trellis_handleDurableEvent, _Trellis_handleDurableEventConsumer, _Trellis_parseEventMessage, _Trellis_escapeSubjectToken, _Trellis_currentIat, _Trellis_createProof, _Trellis_requestMessageWithRetry, _Trellis_requestJson, _Trellis_watchJson;
|
|
12
|
+
var _Trellis_instances, _Trellis_log, _Trellis_tasks, _Trellis_hasExplicitApi, _Trellis_noResponderMaxRetries, _Trellis_noResponderRetryMs, _Trellis_onSessionNotFound, _Trellis_operationStore, _Trellis_eventConsumers, _Trellis_durableEventLoops, _Trellis_createStateFacade, _Trellis_createRpcFacade, _Trellis_createRpcHandleFacade, _Trellis_createEventFacade, _Trellis_createEventPublishFacade, _Trellis_createFeedFacade, _Trellis_createHandlerTrellis, _Trellis_createOperationFacade, _Trellis_unknownApiError, _Trellis_requestBuiltRpcUnknown, _Trellis_requestBuiltRpc, _Trellis_handleBrowserAuthRequired, _Trellis_authenticateFeedRequest, _Trellis_subscribeFeed, _Trellis_handleFeed, _Trellis_processFeedMessage, _Trellis_handleRPC, _Trellis_processRPCMessage, _Trellis_respondWithPayload, _Trellis_respondWithError, _Trellis_startEphemeralEvent, _Trellis_invokeEventHandler, _Trellis_resolveEventConsumerGroup, _Trellis_registerDurableEventHandler, _Trellis_startDurableEventConsumer, _Trellis_durableEventConsumerGroupReady, _Trellis_runDurableEventConsumer, _Trellis_handleDurableEvent, _Trellis_handleDurableEventConsumer, _Trellis_parseEventMessage, _Trellis_escapeSubjectToken, _Trellis_currentIat, _Trellis_createProof, _Trellis_requestMessageWithRetry, _Trellis_requestJson, _Trellis_watchJson;
|
|
13
13
|
import { jetstream, jetstreamManager, } from "@nats-io/jetstream";
|
|
14
14
|
import { createInbox, headers as natsHeaders, } from "@nats-io/nats-core";
|
|
15
15
|
import { CONTRACT_JOBS_METADATA, CONTRACT_KV_METADATA, CONTRACT_STATE_METADATA, } from "./contract_support/mod.js";
|
|
16
16
|
import { AsyncResult, BaseError, err, isErr, ok, Result, } from "@qlever-llc/result";
|
|
17
|
-
import { createNatsHeaderCarrier, extractTraceContext, injectTraceContext, SpanStatusCode, startClientSpan, startServerSpan, withSpanAsync, } from "./
|
|
17
|
+
import { createNatsHeaderCarrier, extractTraceContext, injectTraceContext, recordTrellisError, SpanStatusCode, startClientSpan, startServerSpan, withSpanAsync, } from "./telemetry/mod.js";
|
|
18
18
|
import { Type } from "typebox";
|
|
19
19
|
import { Pointer } from "typebox/value";
|
|
20
20
|
import { ulid } from "ulid";
|
|
@@ -190,6 +190,27 @@ export function isOperationDeferred(value) {
|
|
|
190
190
|
export function isResultLike(value) {
|
|
191
191
|
return value instanceof Result;
|
|
192
192
|
}
|
|
193
|
+
function compactHandlerErrorContext(context) {
|
|
194
|
+
return Object.fromEntries(Object.entries(context).filter(([key, value]) => key !== "traceId" && value !== undefined));
|
|
195
|
+
}
|
|
196
|
+
function sanitizeHandlerErrorContext(error) {
|
|
197
|
+
delete error.getContext().subject;
|
|
198
|
+
}
|
|
199
|
+
export function annotateHandlerBoundaryError(cause, context) {
|
|
200
|
+
const error = cause instanceof BaseError && !(cause instanceof RemoteError)
|
|
201
|
+
? cause
|
|
202
|
+
: new UnexpectedError({ cause });
|
|
203
|
+
sanitizeHandlerErrorContext(error);
|
|
204
|
+
error.withContext(compactHandlerErrorContext(context));
|
|
205
|
+
error.withTraceId(context.traceId);
|
|
206
|
+
return error;
|
|
207
|
+
}
|
|
208
|
+
function recordRuntimeError(error, attributes) {
|
|
209
|
+
recordTrellisError(error, {
|
|
210
|
+
messagingSystem: "nats",
|
|
211
|
+
...attributes,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
193
214
|
const DurableOperationSignalSchema = Type.Object({
|
|
194
215
|
operationId: Type.String(),
|
|
195
216
|
sequence: Type.Number(),
|
|
@@ -220,8 +241,11 @@ const DurableOperationSnapshotSchema = Type.Object({
|
|
|
220
241
|
})),
|
|
221
242
|
output: Type.Optional(Type.Any()),
|
|
222
243
|
error: Type.Optional(Type.Object({
|
|
244
|
+
id: Type.Optional(Type.String()),
|
|
223
245
|
type: Type.String(),
|
|
224
246
|
message: Type.String(),
|
|
247
|
+
context: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
248
|
+
traceId: Type.Optional(Type.String()),
|
|
225
249
|
})),
|
|
226
250
|
});
|
|
227
251
|
export const DurableOperationRecordSchema = Type.Object({
|
|
@@ -727,6 +751,18 @@ export class Trellis {
|
|
|
727
751
|
writable: true,
|
|
728
752
|
value: void 0
|
|
729
753
|
});
|
|
754
|
+
Object.defineProperty(this, "contractId", {
|
|
755
|
+
enumerable: true,
|
|
756
|
+
configurable: true,
|
|
757
|
+
writable: true,
|
|
758
|
+
value: void 0
|
|
759
|
+
});
|
|
760
|
+
Object.defineProperty(this, "contractDigest", {
|
|
761
|
+
enumerable: true,
|
|
762
|
+
configurable: true,
|
|
763
|
+
writable: true,
|
|
764
|
+
value: void 0
|
|
765
|
+
});
|
|
730
766
|
Object.defineProperty(this, "nats", {
|
|
731
767
|
enumerable: true,
|
|
732
768
|
configurable: true,
|
|
@@ -769,6 +805,8 @@ export class Trellis {
|
|
|
769
805
|
__classPrivateFieldSet(this, _Trellis_log, (opts?.log ?? logger).child({ lib: "trellis" }), "f");
|
|
770
806
|
this.timeout = opts?.timeout ?? 3000;
|
|
771
807
|
this.stream = opts?.stream ?? "trellis";
|
|
808
|
+
this.contractId = opts?.contractId;
|
|
809
|
+
this.contractDigest = opts?.contractDigest;
|
|
772
810
|
__classPrivateFieldSet(this, _Trellis_hasExplicitApi, api !== undefined, "f");
|
|
773
811
|
__classPrivateFieldSet(this, _Trellis_noResponderMaxRetries, opts?.noResponderRetry?.maxAttempts ??
|
|
774
812
|
DEFAULT_NO_RESPONDER_MAX_RETRIES, "f");
|
|
@@ -903,14 +941,27 @@ export class Trellis {
|
|
|
903
941
|
const eventName = event;
|
|
904
942
|
const ctx = this.api["events"][eventName];
|
|
905
943
|
if (!ctx) {
|
|
906
|
-
|
|
944
|
+
const error = new UnexpectedError({
|
|
907
945
|
cause: __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_unknownApiError).call(this, "event", event.toString()),
|
|
908
946
|
context: { event: event.toString() },
|
|
909
|
-
})
|
|
947
|
+
});
|
|
948
|
+
recordRuntimeError(error, {
|
|
949
|
+
surface: "event",
|
|
950
|
+
direction: "publisher",
|
|
951
|
+
operation: event,
|
|
952
|
+
phase: "prepare",
|
|
953
|
+
});
|
|
954
|
+
return err(error);
|
|
910
955
|
}
|
|
911
956
|
const subject = this.template(ctx.subject, data).take();
|
|
912
957
|
if (isErr(subject)) {
|
|
913
958
|
logger.error({ err: subject.error }, "Failed to template event.");
|
|
959
|
+
recordRuntimeError(subject.error, {
|
|
960
|
+
surface: "event",
|
|
961
|
+
direction: "publisher",
|
|
962
|
+
operation: event,
|
|
963
|
+
phase: "request_encoding",
|
|
964
|
+
});
|
|
914
965
|
return subject;
|
|
915
966
|
}
|
|
916
967
|
const header = {
|
|
@@ -924,7 +975,14 @@ export class Trellis {
|
|
|
924
975
|
const msg = encodeSchema(ctx.event, payload).take();
|
|
925
976
|
if (isErr(msg)) {
|
|
926
977
|
logger.error({ err: msg.error }, "Failed to encode event.");
|
|
927
|
-
|
|
978
|
+
const error = new UnexpectedError({ cause: msg.error });
|
|
979
|
+
recordRuntimeError(error, {
|
|
980
|
+
surface: "event",
|
|
981
|
+
direction: "publisher",
|
|
982
|
+
operation: event,
|
|
983
|
+
phase: "request_encoding",
|
|
984
|
+
});
|
|
985
|
+
return err(error);
|
|
928
986
|
}
|
|
929
987
|
const headers = natsHeaders();
|
|
930
988
|
headers.set("Nats-Msg-Id", header.id);
|
|
@@ -942,7 +1000,17 @@ export class Trellis {
|
|
|
942
1000
|
}));
|
|
943
1001
|
}
|
|
944
1002
|
catch (cause) {
|
|
945
|
-
|
|
1003
|
+
const error = new UnexpectedError({
|
|
1004
|
+
cause,
|
|
1005
|
+
context: { event: event.toString() },
|
|
1006
|
+
});
|
|
1007
|
+
recordRuntimeError(error, {
|
|
1008
|
+
surface: "event",
|
|
1009
|
+
direction: "publisher",
|
|
1010
|
+
operation: event,
|
|
1011
|
+
phase: "prepare",
|
|
1012
|
+
});
|
|
1013
|
+
return err(error);
|
|
946
1014
|
}
|
|
947
1015
|
}
|
|
948
1016
|
/**
|
|
@@ -961,7 +1029,17 @@ export class Trellis {
|
|
|
961
1029
|
return ok(undefined);
|
|
962
1030
|
}
|
|
963
1031
|
catch (cause) {
|
|
964
|
-
|
|
1032
|
+
const error = new UnexpectedError({
|
|
1033
|
+
cause,
|
|
1034
|
+
context: { event: event.event },
|
|
1035
|
+
});
|
|
1036
|
+
recordRuntimeError(error, {
|
|
1037
|
+
surface: "event",
|
|
1038
|
+
direction: "publisher",
|
|
1039
|
+
operation: event.event,
|
|
1040
|
+
phase: "publish",
|
|
1041
|
+
});
|
|
1042
|
+
return err(error);
|
|
965
1043
|
}
|
|
966
1044
|
})());
|
|
967
1045
|
}
|
|
@@ -1197,10 +1275,22 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1197
1275
|
__classPrivateFieldGet(this, _Trellis_log, "f").trace({ method: String(method) }, `Calling ${method.toString()}.`);
|
|
1198
1276
|
const msg = encodeRuntimeSchema(ctx.input, input).take();
|
|
1199
1277
|
if (isErr(msg)) {
|
|
1278
|
+
recordRuntimeError(msg.error, {
|
|
1279
|
+
surface: "rpc",
|
|
1280
|
+
direction: "client",
|
|
1281
|
+
operation: method,
|
|
1282
|
+
phase: "request_encoding",
|
|
1283
|
+
});
|
|
1200
1284
|
return msg;
|
|
1201
1285
|
}
|
|
1202
1286
|
const subject = this.template(ctx.subject, input).take();
|
|
1203
1287
|
if (isErr(subject)) {
|
|
1288
|
+
recordRuntimeError(subject.error, {
|
|
1289
|
+
surface: "rpc",
|
|
1290
|
+
direction: "client",
|
|
1291
|
+
operation: method,
|
|
1292
|
+
phase: "request_encoding",
|
|
1293
|
+
});
|
|
1204
1294
|
return subject;
|
|
1205
1295
|
}
|
|
1206
1296
|
const span = startClientSpan(method, subject);
|
|
@@ -1222,30 +1312,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1222
1312
|
});
|
|
1223
1313
|
const response = msgResult.take();
|
|
1224
1314
|
if (isErr(response)) {
|
|
1315
|
+
recordRuntimeError(response.error, {
|
|
1316
|
+
surface: "rpc",
|
|
1317
|
+
direction: "client",
|
|
1318
|
+
operation: method,
|
|
1319
|
+
phase: "request_send",
|
|
1320
|
+
});
|
|
1225
1321
|
return response;
|
|
1226
1322
|
}
|
|
1227
1323
|
if (response.headers?.get("status") === "error") {
|
|
1228
1324
|
const json = safeJson(response).take();
|
|
1229
1325
|
if (isErr(json)) {
|
|
1230
|
-
|
|
1326
|
+
const error = requestFailedTransportError({
|
|
1231
1327
|
code: "trellis.request.invalid_response",
|
|
1232
1328
|
message: "Trellis returned an invalid response.",
|
|
1233
1329
|
hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
|
|
1234
1330
|
method,
|
|
1235
1331
|
subject,
|
|
1236
1332
|
cause: json.error.cause,
|
|
1237
|
-
})
|
|
1333
|
+
});
|
|
1334
|
+
recordRuntimeError(error, {
|
|
1335
|
+
surface: "rpc",
|
|
1336
|
+
direction: "client",
|
|
1337
|
+
operation: method,
|
|
1338
|
+
phase: "response_decoding",
|
|
1339
|
+
});
|
|
1340
|
+
return err(error);
|
|
1238
1341
|
}
|
|
1239
1342
|
const errorData = parse(TrellisErrorDataSchema, json).take();
|
|
1240
1343
|
if (isErr(errorData)) {
|
|
1241
|
-
|
|
1344
|
+
const error = requestFailedTransportError({
|
|
1242
1345
|
code: "trellis.request.invalid_response",
|
|
1243
1346
|
message: "Trellis returned an invalid response.",
|
|
1244
1347
|
hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
|
|
1245
1348
|
method,
|
|
1246
1349
|
subject,
|
|
1247
1350
|
cause: errorData.error,
|
|
1248
|
-
})
|
|
1351
|
+
});
|
|
1352
|
+
recordRuntimeError(error, {
|
|
1353
|
+
surface: "rpc",
|
|
1354
|
+
direction: "client",
|
|
1355
|
+
operation: method,
|
|
1356
|
+
phase: "response_decoding",
|
|
1357
|
+
});
|
|
1358
|
+
return err(error);
|
|
1249
1359
|
}
|
|
1250
1360
|
const declaredErrorTypes = Array.isArray(ctx.declaredErrorTypes)
|
|
1251
1361
|
? ctx.declaredErrorTypes.filter((value) => typeof value === "string")
|
|
@@ -1256,25 +1366,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1256
1366
|
const reconstructed = reconstructDeclaredRpcError(declaredErrorTypes, runtimeErrors, errorData, json);
|
|
1257
1367
|
if (reconstructed) {
|
|
1258
1368
|
await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_handleBrowserAuthRequired).call(this, reconstructed);
|
|
1369
|
+
recordRuntimeError(new RemoteError({ error: errorData }), {
|
|
1370
|
+
surface: "rpc",
|
|
1371
|
+
direction: "client",
|
|
1372
|
+
operation: method,
|
|
1373
|
+
phase: "remote_error",
|
|
1374
|
+
});
|
|
1259
1375
|
return err(reconstructed);
|
|
1260
1376
|
}
|
|
1261
1377
|
const remoteError = new RemoteError({ error: errorData });
|
|
1262
1378
|
await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_handleBrowserAuthRequired).call(this, remoteError);
|
|
1379
|
+
recordRuntimeError(remoteError, {
|
|
1380
|
+
surface: "rpc",
|
|
1381
|
+
direction: "client",
|
|
1382
|
+
operation: method,
|
|
1383
|
+
phase: "remote_error",
|
|
1384
|
+
});
|
|
1263
1385
|
return err(remoteError);
|
|
1264
1386
|
}
|
|
1265
1387
|
const json = safeJson(response).take();
|
|
1266
1388
|
if (isErr(json)) {
|
|
1267
|
-
|
|
1389
|
+
const error = requestFailedTransportError({
|
|
1268
1390
|
code: "trellis.request.invalid_response",
|
|
1269
1391
|
message: "Trellis returned an invalid response.",
|
|
1270
1392
|
hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
|
|
1271
1393
|
method,
|
|
1272
1394
|
subject,
|
|
1273
1395
|
cause: json.error.cause,
|
|
1274
|
-
})
|
|
1396
|
+
});
|
|
1397
|
+
recordRuntimeError(error, {
|
|
1398
|
+
surface: "rpc",
|
|
1399
|
+
direction: "client",
|
|
1400
|
+
operation: method,
|
|
1401
|
+
phase: "response_decoding",
|
|
1402
|
+
});
|
|
1403
|
+
return err(error);
|
|
1275
1404
|
}
|
|
1276
1405
|
const outputResult = parseRuntimeSchema(ctx.output, json).take();
|
|
1277
1406
|
if (isErr(outputResult)) {
|
|
1407
|
+
recordRuntimeError(outputResult.error, {
|
|
1408
|
+
surface: "rpc",
|
|
1409
|
+
direction: "client",
|
|
1410
|
+
operation: method,
|
|
1411
|
+
phase: "response_decoding",
|
|
1412
|
+
});
|
|
1278
1413
|
return err(outputResult.error);
|
|
1279
1414
|
}
|
|
1280
1415
|
return ok(outputResult);
|
|
@@ -1303,6 +1438,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1303
1438
|
message: unexpected.message,
|
|
1304
1439
|
});
|
|
1305
1440
|
span.recordException(unexpected);
|
|
1441
|
+
recordRuntimeError(unexpected, {
|
|
1442
|
+
surface: "rpc",
|
|
1443
|
+
direction: "client",
|
|
1444
|
+
operation: method,
|
|
1445
|
+
phase: "unexpected",
|
|
1446
|
+
});
|
|
1306
1447
|
return err(unexpected);
|
|
1307
1448
|
}
|
|
1308
1449
|
finally {
|
|
@@ -1371,11 +1512,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1371
1512
|
}, _Trellis_subscribeFeed = function _Trellis_subscribeFeed(feed, descriptor, input, opts) {
|
|
1372
1513
|
return AsyncResult.from((async () => {
|
|
1373
1514
|
const payload = encodeRuntimeSchema(descriptor.input, input).take();
|
|
1374
|
-
if (isErr(payload))
|
|
1515
|
+
if (isErr(payload)) {
|
|
1516
|
+
recordRuntimeError(payload.error, {
|
|
1517
|
+
surface: "feed",
|
|
1518
|
+
direction: "client",
|
|
1519
|
+
operation: feed,
|
|
1520
|
+
phase: "request_encoding",
|
|
1521
|
+
});
|
|
1375
1522
|
return payload;
|
|
1523
|
+
}
|
|
1376
1524
|
const subject = this.template(descriptor.subject, input).take();
|
|
1377
|
-
if (isErr(subject))
|
|
1525
|
+
if (isErr(subject)) {
|
|
1526
|
+
recordRuntimeError(subject.error, {
|
|
1527
|
+
surface: "feed",
|
|
1528
|
+
direction: "client",
|
|
1529
|
+
operation: feed,
|
|
1530
|
+
phase: "request_template",
|
|
1531
|
+
});
|
|
1378
1532
|
return subject;
|
|
1533
|
+
}
|
|
1379
1534
|
const authHeaders = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_createProof).call(this, subject, payload);
|
|
1380
1535
|
const headers = natsHeaders();
|
|
1381
1536
|
headers.set("session-key", this.auth.sessionKey);
|
|
@@ -1395,13 +1550,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1395
1550
|
catch (cause) {
|
|
1396
1551
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1397
1552
|
sub.unsubscribe();
|
|
1398
|
-
|
|
1553
|
+
const error = createTransportError({
|
|
1399
1554
|
code: "trellis.feed.subscribe_failed",
|
|
1400
1555
|
message: "Trellis could not subscribe to the feed.",
|
|
1401
1556
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
|
|
1402
1557
|
cause,
|
|
1403
1558
|
context: { feed, subject },
|
|
1404
|
-
})
|
|
1559
|
+
});
|
|
1560
|
+
recordRuntimeError(error, {
|
|
1561
|
+
surface: "feed",
|
|
1562
|
+
direction: "client",
|
|
1563
|
+
operation: feed,
|
|
1564
|
+
phase: "request_send",
|
|
1565
|
+
});
|
|
1566
|
+
return err(error);
|
|
1405
1567
|
}
|
|
1406
1568
|
let timeoutId;
|
|
1407
1569
|
let abortHandler;
|
|
@@ -1427,7 +1589,7 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1427
1589
|
if (firstFrame === "timeout" || firstFrame === "aborted") {
|
|
1428
1590
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1429
1591
|
sub.unsubscribe();
|
|
1430
|
-
|
|
1592
|
+
const error = createTransportError({
|
|
1431
1593
|
code: firstFrame === "timeout"
|
|
1432
1594
|
? "trellis.feed.subscribe_timeout"
|
|
1433
1595
|
: "trellis.feed.subscribe_aborted",
|
|
@@ -1438,28 +1600,49 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1438
1600
|
? "Check that the target service is running and has the current deployment digest, then retry."
|
|
1439
1601
|
: "Retry the subscription if the feed is still needed.",
|
|
1440
1602
|
context: { feed, subject },
|
|
1441
|
-
})
|
|
1603
|
+
});
|
|
1604
|
+
recordRuntimeError(error, {
|
|
1605
|
+
surface: "feed",
|
|
1606
|
+
direction: "client",
|
|
1607
|
+
operation: feed,
|
|
1608
|
+
phase: "handshake",
|
|
1609
|
+
});
|
|
1610
|
+
return err(error);
|
|
1442
1611
|
}
|
|
1443
1612
|
if (firstFrame.done) {
|
|
1444
1613
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1445
1614
|
sub.unsubscribe();
|
|
1446
|
-
|
|
1615
|
+
const error = createTransportError({
|
|
1447
1616
|
code: "trellis.feed.subscribe_closed",
|
|
1448
1617
|
message: "Trellis closed the feed before acknowledging it.",
|
|
1449
1618
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
|
|
1450
1619
|
context: { feed, subject },
|
|
1451
|
-
})
|
|
1620
|
+
});
|
|
1621
|
+
recordRuntimeError(error, {
|
|
1622
|
+
surface: "feed",
|
|
1623
|
+
direction: "client",
|
|
1624
|
+
operation: feed,
|
|
1625
|
+
phase: "handshake",
|
|
1626
|
+
});
|
|
1627
|
+
return err(error);
|
|
1452
1628
|
}
|
|
1453
1629
|
const firstMessage = firstFrame.value;
|
|
1454
1630
|
if (firstMessage.headers?.get("status") === "error") {
|
|
1455
1631
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1456
1632
|
sub.unsubscribe();
|
|
1457
|
-
|
|
1633
|
+
const error = createTransportError({
|
|
1458
1634
|
code: "trellis.feed.failed",
|
|
1459
1635
|
message: "Trellis rejected the feed subscription.",
|
|
1460
1636
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health and permissions.",
|
|
1461
1637
|
context: { feed, subject, frame: firstMessage.string() },
|
|
1462
|
-
})
|
|
1638
|
+
});
|
|
1639
|
+
recordRuntimeError(error, {
|
|
1640
|
+
surface: "feed",
|
|
1641
|
+
direction: "client",
|
|
1642
|
+
operation: feed,
|
|
1643
|
+
phase: "remote_error",
|
|
1644
|
+
});
|
|
1645
|
+
return err(error);
|
|
1463
1646
|
}
|
|
1464
1647
|
const firstEvent = firstMessage.headers?.get("feed-status") === "ready"
|
|
1465
1648
|
? undefined
|
|
@@ -1469,19 +1652,40 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1469
1652
|
try {
|
|
1470
1653
|
const parseFeedFrame = (msg) => {
|
|
1471
1654
|
if (msg.headers?.get("status") === "error") {
|
|
1472
|
-
|
|
1655
|
+
const error = createTransportError({
|
|
1473
1656
|
code: "trellis.feed.failed",
|
|
1474
1657
|
message: "Trellis stopped the feed.",
|
|
1475
1658
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
|
|
1476
1659
|
context: { feed, subject, frame: msg.string() },
|
|
1477
1660
|
});
|
|
1661
|
+
recordRuntimeError(error, {
|
|
1662
|
+
surface: "feed",
|
|
1663
|
+
direction: "client",
|
|
1664
|
+
operation: feed,
|
|
1665
|
+
phase: "remote_error",
|
|
1666
|
+
});
|
|
1667
|
+
throw error;
|
|
1478
1668
|
}
|
|
1479
1669
|
const json = safeJson(msg).take();
|
|
1480
|
-
if (isErr(json))
|
|
1670
|
+
if (isErr(json)) {
|
|
1671
|
+
recordRuntimeError(json.error, {
|
|
1672
|
+
surface: "feed",
|
|
1673
|
+
direction: "client",
|
|
1674
|
+
operation: feed,
|
|
1675
|
+
phase: "event_decoding",
|
|
1676
|
+
});
|
|
1481
1677
|
throw json.error;
|
|
1678
|
+
}
|
|
1482
1679
|
const parsed = parseRuntimeSchema(eventSchema, json).take();
|
|
1483
|
-
if (isErr(parsed))
|
|
1680
|
+
if (isErr(parsed)) {
|
|
1681
|
+
recordRuntimeError(parsed.error, {
|
|
1682
|
+
surface: "feed",
|
|
1683
|
+
direction: "client",
|
|
1684
|
+
operation: feed,
|
|
1685
|
+
phase: "event_validation",
|
|
1686
|
+
});
|
|
1484
1687
|
throw parsed.error;
|
|
1688
|
+
}
|
|
1485
1689
|
return parsed;
|
|
1486
1690
|
};
|
|
1487
1691
|
if (firstEvent)
|
|
@@ -1509,13 +1713,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1509
1713
|
await this.nats.flush();
|
|
1510
1714
|
}
|
|
1511
1715
|
catch (cause) {
|
|
1512
|
-
|
|
1716
|
+
const error = createTransportError({
|
|
1513
1717
|
code: "trellis.feed.listen_failed",
|
|
1514
1718
|
message: "Trellis could not listen for feed requests.",
|
|
1515
1719
|
hint: "Check the service deployment digest and runtime permissions, then restart the service.",
|
|
1516
1720
|
cause,
|
|
1517
1721
|
context: { feed, subject },
|
|
1518
1722
|
});
|
|
1723
|
+
recordRuntimeError(error, {
|
|
1724
|
+
surface: "feed",
|
|
1725
|
+
direction: "server",
|
|
1726
|
+
operation: feed,
|
|
1727
|
+
phase: "listen",
|
|
1728
|
+
});
|
|
1729
|
+
throw error;
|
|
1519
1730
|
}
|
|
1520
1731
|
const task = AsyncResult.try(async () => {
|
|
1521
1732
|
for await (const msg of sub) {
|
|
@@ -1528,9 +1739,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1528
1739
|
}
|
|
1529
1740
|
}
|
|
1530
1741
|
catch (cause) {
|
|
1531
|
-
const error = cause
|
|
1532
|
-
|
|
1533
|
-
:
|
|
1742
|
+
const error = annotateHandlerBoundaryError(cause, {
|
|
1743
|
+
feed,
|
|
1744
|
+
requestId: msg.headers?.get("request-id"),
|
|
1745
|
+
service: this.name,
|
|
1746
|
+
contractId: this.contractId,
|
|
1747
|
+
contractDigest: this.contractDigest,
|
|
1748
|
+
traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
|
|
1749
|
+
});
|
|
1750
|
+
recordRuntimeError(error, {
|
|
1751
|
+
surface: "feed",
|
|
1752
|
+
direction: "server",
|
|
1753
|
+
operation: feed,
|
|
1754
|
+
phase: "handler_throw",
|
|
1755
|
+
});
|
|
1534
1756
|
__classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_respondWithError).call(this, msg, error);
|
|
1535
1757
|
}
|
|
1536
1758
|
})();
|
|
@@ -1539,11 +1761,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1539
1761
|
__classPrivateFieldGet(this, _Trellis_tasks, "f").add(`feed:${feed}`, task);
|
|
1540
1762
|
}, _Trellis_processFeedMessage = async function _Trellis_processFeedMessage(feed, descriptor, msg, handler) {
|
|
1541
1763
|
const json = safeJson(msg).take();
|
|
1542
|
-
if (isErr(json))
|
|
1764
|
+
if (isErr(json)) {
|
|
1765
|
+
recordRuntimeError(json.error, {
|
|
1766
|
+
surface: "feed",
|
|
1767
|
+
direction: "server",
|
|
1768
|
+
operation: feed,
|
|
1769
|
+
phase: "request_decoding",
|
|
1770
|
+
});
|
|
1543
1771
|
return json;
|
|
1772
|
+
}
|
|
1544
1773
|
const parsed = parseRuntimeSchema(descriptor.input, json).take();
|
|
1545
|
-
if (isErr(parsed))
|
|
1774
|
+
if (isErr(parsed)) {
|
|
1775
|
+
recordRuntimeError(parsed.error, {
|
|
1776
|
+
surface: "feed",
|
|
1777
|
+
direction: "server",
|
|
1778
|
+
operation: feed,
|
|
1779
|
+
phase: "input_validation",
|
|
1780
|
+
});
|
|
1546
1781
|
return parsed;
|
|
1782
|
+
}
|
|
1547
1783
|
const caller = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_authenticateFeedRequest).call(this, {
|
|
1548
1784
|
feed,
|
|
1549
1785
|
subject: msg.subject,
|
|
@@ -1552,12 +1788,26 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1552
1788
|
requiredCapabilities: descriptor.subscribeCapabilities,
|
|
1553
1789
|
});
|
|
1554
1790
|
const callerValue = caller.take();
|
|
1555
|
-
if (isErr(callerValue))
|
|
1791
|
+
if (isErr(callerValue)) {
|
|
1792
|
+
recordRuntimeError(callerValue.error, {
|
|
1793
|
+
surface: "feed",
|
|
1794
|
+
direction: "server",
|
|
1795
|
+
operation: feed,
|
|
1796
|
+
phase: "auth",
|
|
1797
|
+
});
|
|
1556
1798
|
return callerValue;
|
|
1799
|
+
}
|
|
1557
1800
|
if (!msg.reply) {
|
|
1558
|
-
|
|
1801
|
+
const error = new UnexpectedError({
|
|
1559
1802
|
context: { feed, reason: "missing_reply" },
|
|
1560
|
-
})
|
|
1803
|
+
});
|
|
1804
|
+
recordRuntimeError(error, {
|
|
1805
|
+
surface: "feed",
|
|
1806
|
+
direction: "server",
|
|
1807
|
+
operation: feed,
|
|
1808
|
+
phase: "handshake",
|
|
1809
|
+
});
|
|
1810
|
+
return err(error);
|
|
1561
1811
|
}
|
|
1562
1812
|
const readyHeaders = natsHeaders();
|
|
1563
1813
|
readyHeaders.set("feed-status", "ready");
|
|
@@ -1565,24 +1815,73 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1565
1815
|
await this.nats.flush();
|
|
1566
1816
|
const controller = new AbortController();
|
|
1567
1817
|
try {
|
|
1568
|
-
await handler({
|
|
1818
|
+
const handlerResult = await handler({
|
|
1569
1819
|
input: parsed,
|
|
1570
1820
|
caller: callerValue,
|
|
1571
1821
|
signal: controller.signal,
|
|
1572
1822
|
emit: (event) => AsyncResult.from((async () => {
|
|
1573
1823
|
const payload = encodeRuntimeSchema(descriptor.event, event).take();
|
|
1574
|
-
if (isErr(payload))
|
|
1824
|
+
if (isErr(payload)) {
|
|
1825
|
+
recordRuntimeError(payload.error, {
|
|
1826
|
+
surface: "feed",
|
|
1827
|
+
direction: "server",
|
|
1828
|
+
operation: feed,
|
|
1829
|
+
phase: "event_encoding",
|
|
1830
|
+
});
|
|
1575
1831
|
return payload;
|
|
1832
|
+
}
|
|
1576
1833
|
if (!msg.reply) {
|
|
1577
|
-
|
|
1834
|
+
const error = new UnexpectedError({
|
|
1578
1835
|
context: { feed, reason: "missing_reply" },
|
|
1579
|
-
})
|
|
1836
|
+
});
|
|
1837
|
+
recordRuntimeError(error, {
|
|
1838
|
+
surface: "feed",
|
|
1839
|
+
direction: "server",
|
|
1840
|
+
operation: feed,
|
|
1841
|
+
phase: "event_publish",
|
|
1842
|
+
});
|
|
1843
|
+
return err(error);
|
|
1844
|
+
}
|
|
1845
|
+
try {
|
|
1846
|
+
this.nats.publish(msg.reply, payload);
|
|
1847
|
+
await this.nats.flush();
|
|
1848
|
+
}
|
|
1849
|
+
catch (cause) {
|
|
1850
|
+
const error = new UnexpectedError({
|
|
1851
|
+
cause,
|
|
1852
|
+
context: { feed },
|
|
1853
|
+
});
|
|
1854
|
+
recordRuntimeError(error, {
|
|
1855
|
+
surface: "feed",
|
|
1856
|
+
direction: "server",
|
|
1857
|
+
operation: feed,
|
|
1858
|
+
phase: "event_publish",
|
|
1859
|
+
});
|
|
1860
|
+
return err(error);
|
|
1580
1861
|
}
|
|
1581
|
-
this.nats.publish(msg.reply, payload);
|
|
1582
|
-
await this.nats.flush();
|
|
1583
1862
|
return ok(undefined);
|
|
1584
1863
|
})()),
|
|
1585
1864
|
});
|
|
1865
|
+
const handlerOutcome = isResultLike(handlerResult)
|
|
1866
|
+
? handlerResult.take()
|
|
1867
|
+
: handlerResult;
|
|
1868
|
+
if (isErr(handlerOutcome)) {
|
|
1869
|
+
const error = annotateHandlerBoundaryError(handlerOutcome.error, {
|
|
1870
|
+
feed,
|
|
1871
|
+
requestId: msg.headers?.get("request-id"),
|
|
1872
|
+
service: this.name,
|
|
1873
|
+
contractId: this.contractId,
|
|
1874
|
+
contractDigest: this.contractDigest,
|
|
1875
|
+
traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
|
|
1876
|
+
});
|
|
1877
|
+
recordRuntimeError(error, {
|
|
1878
|
+
surface: "feed",
|
|
1879
|
+
direction: "server",
|
|
1880
|
+
operation: feed,
|
|
1881
|
+
phase: "handler_result",
|
|
1882
|
+
});
|
|
1883
|
+
return err(error);
|
|
1884
|
+
}
|
|
1586
1885
|
return ok(undefined);
|
|
1587
1886
|
}
|
|
1588
1887
|
finally {
|
|
@@ -1650,6 +1949,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1650
1949
|
code: SpanStatusCode.ERROR,
|
|
1651
1950
|
message: "Failed to parse JSON",
|
|
1652
1951
|
});
|
|
1952
|
+
recordRuntimeError(jsonData.error, {
|
|
1953
|
+
surface: "rpc",
|
|
1954
|
+
direction: "server",
|
|
1955
|
+
operation: String(method),
|
|
1956
|
+
phase: "parse",
|
|
1957
|
+
});
|
|
1653
1958
|
return jsonData;
|
|
1654
1959
|
}
|
|
1655
1960
|
const parsedInput = parseRuntimeSchema(ctx.input, jsonData).take();
|
|
@@ -1658,6 +1963,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1658
1963
|
code: SpanStatusCode.ERROR,
|
|
1659
1964
|
message: "Input validation failed",
|
|
1660
1965
|
});
|
|
1966
|
+
recordRuntimeError(parsedInput.error, {
|
|
1967
|
+
surface: "rpc",
|
|
1968
|
+
direction: "server",
|
|
1969
|
+
operation: String(method),
|
|
1970
|
+
phase: "input_validation",
|
|
1971
|
+
});
|
|
1661
1972
|
return parsedInput;
|
|
1662
1973
|
}
|
|
1663
1974
|
let caller;
|
|
@@ -1683,7 +1994,14 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1683
1994
|
code: SpanStatusCode.ERROR,
|
|
1684
1995
|
message: "Missing session-key",
|
|
1685
1996
|
});
|
|
1686
|
-
|
|
1997
|
+
const error = new AuthError({ reason: "missing_session_key" });
|
|
1998
|
+
recordRuntimeError(error, {
|
|
1999
|
+
surface: "rpc",
|
|
2000
|
+
direction: "server",
|
|
2001
|
+
operation: String(method),
|
|
2002
|
+
phase: "auth",
|
|
2003
|
+
});
|
|
2004
|
+
return err(error);
|
|
1687
2005
|
}
|
|
1688
2006
|
if (!proof) {
|
|
1689
2007
|
__classPrivateFieldGet(this, _Trellis_log, "f").warn({ method }, "Missing proof in request");
|
|
@@ -1691,11 +2009,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1691
2009
|
code: SpanStatusCode.ERROR,
|
|
1692
2010
|
message: "Missing proof",
|
|
1693
2011
|
});
|
|
1694
|
-
|
|
2012
|
+
const error = new AuthError({ reason: "missing_proof" });
|
|
2013
|
+
recordRuntimeError(error, {
|
|
2014
|
+
surface: "rpc",
|
|
2015
|
+
direction: "server",
|
|
2016
|
+
operation: String(method),
|
|
2017
|
+
phase: "auth",
|
|
2018
|
+
});
|
|
2019
|
+
return err(error);
|
|
1695
2020
|
}
|
|
1696
2021
|
const iat = Number(iatHeader);
|
|
1697
2022
|
if (!Number.isSafeInteger(iat) || !requestId) {
|
|
1698
|
-
|
|
2023
|
+
const error = new AuthError({ reason: "invalid_signature" });
|
|
2024
|
+
recordRuntimeError(error, {
|
|
2025
|
+
surface: "rpc",
|
|
2026
|
+
direction: "server",
|
|
2027
|
+
operation: String(method),
|
|
2028
|
+
phase: "auth",
|
|
2029
|
+
});
|
|
2030
|
+
return err(error);
|
|
1699
2031
|
}
|
|
1700
2032
|
// Verify proof signature locally using the raw request bytes we received.
|
|
1701
2033
|
const payloadBytes = msg.data ?? new Uint8Array();
|
|
@@ -1714,10 +2046,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1714
2046
|
code: SpanStatusCode.ERROR,
|
|
1715
2047
|
message: "Invalid signature",
|
|
1716
2048
|
});
|
|
1717
|
-
|
|
2049
|
+
const error = new AuthError({
|
|
1718
2050
|
reason: "invalid_signature",
|
|
1719
2051
|
context: { sessionKey },
|
|
1720
|
-
})
|
|
2052
|
+
});
|
|
2053
|
+
recordRuntimeError(error, {
|
|
2054
|
+
surface: "rpc",
|
|
2055
|
+
direction: "server",
|
|
2056
|
+
operation: String(method),
|
|
2057
|
+
phase: "auth",
|
|
2058
|
+
});
|
|
2059
|
+
return err(error);
|
|
1721
2060
|
}
|
|
1722
2061
|
let auth;
|
|
1723
2062
|
for (let attempt = 0; attempt < DEFAULT_AUTH_VALIDATE_SESSION_RETRY_ATTEMPTS; attempt++) {
|
|
@@ -1743,9 +2082,16 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1743
2082
|
await sleep(DEFAULT_AUTH_VALIDATE_SESSION_RETRY_MS * (attempt + 1));
|
|
1744
2083
|
}
|
|
1745
2084
|
if (!auth) {
|
|
1746
|
-
|
|
2085
|
+
const error = new UnexpectedError({
|
|
1747
2086
|
context: { reason: "missing_auth_validate_result" },
|
|
1748
|
-
})
|
|
2087
|
+
});
|
|
2088
|
+
recordRuntimeError(error, {
|
|
2089
|
+
surface: "rpc",
|
|
2090
|
+
direction: "server",
|
|
2091
|
+
operation: String(method),
|
|
2092
|
+
phase: "auth",
|
|
2093
|
+
});
|
|
2094
|
+
return err(error);
|
|
1749
2095
|
}
|
|
1750
2096
|
if (auth instanceof Error) {
|
|
1751
2097
|
__classPrivateFieldGet(this, _Trellis_log, "f").warn({
|
|
@@ -1761,22 +2107,42 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1761
2107
|
message: "Auth.Requests.Validate failed",
|
|
1762
2108
|
});
|
|
1763
2109
|
if (auth instanceof BaseError) {
|
|
2110
|
+
recordRuntimeError(auth, {
|
|
2111
|
+
surface: "rpc",
|
|
2112
|
+
direction: "server",
|
|
2113
|
+
operation: String(method),
|
|
2114
|
+
phase: "auth",
|
|
2115
|
+
});
|
|
1764
2116
|
return err(auth);
|
|
1765
2117
|
}
|
|
1766
|
-
|
|
2118
|
+
const error = new UnexpectedError({ cause: auth });
|
|
2119
|
+
recordRuntimeError(error, {
|
|
2120
|
+
surface: "rpc",
|
|
2121
|
+
direction: "server",
|
|
2122
|
+
operation: String(method),
|
|
2123
|
+
phase: "auth",
|
|
2124
|
+
});
|
|
2125
|
+
return err(error);
|
|
1767
2126
|
}
|
|
1768
2127
|
if (!auth.allowed) {
|
|
1769
2128
|
span.setStatus({
|
|
1770
2129
|
code: SpanStatusCode.ERROR,
|
|
1771
2130
|
message: "Insufficient permissions",
|
|
1772
2131
|
});
|
|
1773
|
-
|
|
2132
|
+
const error = new AuthError({
|
|
1774
2133
|
reason: "insufficient_permissions",
|
|
1775
2134
|
context: {
|
|
1776
2135
|
requiredCapabilities: ctx.callerCapabilities,
|
|
1777
2136
|
userCapabilities: auth.caller.capabilities,
|
|
1778
2137
|
},
|
|
1779
|
-
})
|
|
2138
|
+
});
|
|
2139
|
+
recordRuntimeError(error, {
|
|
2140
|
+
surface: "rpc",
|
|
2141
|
+
direction: "server",
|
|
2142
|
+
operation: String(method),
|
|
2143
|
+
phase: "auth",
|
|
2144
|
+
});
|
|
2145
|
+
return err(error);
|
|
1780
2146
|
}
|
|
1781
2147
|
if (typeof msg.reply !== "string" ||
|
|
1782
2148
|
!msg.reply.startsWith(`${auth.inboxPrefix}.`)) {
|
|
@@ -1784,10 +2150,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1784
2150
|
code: SpanStatusCode.ERROR,
|
|
1785
2151
|
message: "Reply subject mismatch",
|
|
1786
2152
|
});
|
|
1787
|
-
|
|
2153
|
+
const error = new AuthError({
|
|
1788
2154
|
reason: "reply_subject_mismatch",
|
|
1789
2155
|
context: { expected: auth.inboxPrefix, actual: msg.reply },
|
|
1790
|
-
})
|
|
2156
|
+
});
|
|
2157
|
+
recordRuntimeError(error, {
|
|
2158
|
+
surface: "rpc",
|
|
2159
|
+
direction: "server",
|
|
2160
|
+
operation: String(method),
|
|
2161
|
+
phase: "auth",
|
|
2162
|
+
});
|
|
2163
|
+
return err(error);
|
|
1791
2164
|
}
|
|
1792
2165
|
caller = auth.caller;
|
|
1793
2166
|
}
|
|
@@ -1815,7 +2188,14 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1815
2188
|
client: handlerTrellis,
|
|
1816
2189
|
})));
|
|
1817
2190
|
if (handlerResultWrapped.isErr()) {
|
|
1818
|
-
const error = handlerResultWrapped.error
|
|
2191
|
+
const error = annotateHandlerBoundaryError(handlerResultWrapped.error, {
|
|
2192
|
+
method: String(method),
|
|
2193
|
+
requestId: msg.headers?.get("request-id"),
|
|
2194
|
+
service: this.name,
|
|
2195
|
+
contractId: this.contractId,
|
|
2196
|
+
contractDigest: this.contractDigest,
|
|
2197
|
+
traceId: activeTraceId(span) ?? incomingTraceId,
|
|
2198
|
+
});
|
|
1819
2199
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
1820
2200
|
method,
|
|
1821
2201
|
error: error.message,
|
|
@@ -1828,16 +2208,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1828
2208
|
message: error.message,
|
|
1829
2209
|
});
|
|
1830
2210
|
span.recordException(error);
|
|
2211
|
+
recordRuntimeError(error, {
|
|
2212
|
+
surface: "rpc",
|
|
2213
|
+
direction: "server",
|
|
2214
|
+
operation: String(method),
|
|
2215
|
+
phase: "handler_throw",
|
|
2216
|
+
});
|
|
1831
2217
|
return err(error);
|
|
1832
2218
|
}
|
|
1833
2219
|
const handlerResult = handlerResultWrapped.take();
|
|
1834
2220
|
const handlerOutcome = handlerResult.take();
|
|
1835
2221
|
if (isErr(handlerOutcome)) {
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
:
|
|
2222
|
+
const error = annotateHandlerBoundaryError(handlerOutcome.error, {
|
|
2223
|
+
method: String(method),
|
|
2224
|
+
requestId: msg.headers?.get("request-id"),
|
|
2225
|
+
service: this.name,
|
|
2226
|
+
contractId: this.contractId,
|
|
2227
|
+
contractDigest: this.contractDigest,
|
|
2228
|
+
traceId: activeTraceId(span) ?? incomingTraceId,
|
|
2229
|
+
});
|
|
1841
2230
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
1842
2231
|
method,
|
|
1843
2232
|
error: error.message,
|
|
@@ -1850,6 +2239,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1850
2239
|
code: SpanStatusCode.ERROR,
|
|
1851
2240
|
message: error.message,
|
|
1852
2241
|
});
|
|
2242
|
+
recordRuntimeError(error, {
|
|
2243
|
+
surface: "rpc",
|
|
2244
|
+
direction: "server",
|
|
2245
|
+
operation: String(method),
|
|
2246
|
+
phase: "handler_result",
|
|
2247
|
+
});
|
|
1853
2248
|
return err(error);
|
|
1854
2249
|
}
|
|
1855
2250
|
const encoded = encodeSchema(ctx.output, handlerOutcome).take();
|
|
@@ -1858,6 +2253,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1858
2253
|
code: SpanStatusCode.ERROR,
|
|
1859
2254
|
message: "Output encoding failed",
|
|
1860
2255
|
});
|
|
2256
|
+
recordRuntimeError(encoded.error, {
|
|
2257
|
+
surface: "rpc",
|
|
2258
|
+
direction: "server",
|
|
2259
|
+
operation: String(method),
|
|
2260
|
+
phase: "output_encoding",
|
|
2261
|
+
});
|
|
1861
2262
|
return encoded;
|
|
1862
2263
|
}
|
|
1863
2264
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
@@ -1934,17 +2335,31 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1934
2335
|
const m = parsedEvent.take();
|
|
1935
2336
|
if (isErr(m)) {
|
|
1936
2337
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({ error: m.error }, "Event validation failed");
|
|
2338
|
+
recordRuntimeError(m.error, {
|
|
2339
|
+
surface: "event",
|
|
2340
|
+
direction: "consumer",
|
|
2341
|
+
operation: String(event),
|
|
2342
|
+
phase: "input_validation",
|
|
2343
|
+
});
|
|
1937
2344
|
continue;
|
|
1938
2345
|
}
|
|
1939
|
-
const handlerResult = await
|
|
2346
|
+
const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
|
|
2347
|
+
event,
|
|
1940
2348
|
payload: m,
|
|
1941
|
-
subject: msg.subject,
|
|
1942
2349
|
mode: "ephemeral",
|
|
1943
2350
|
message: msg,
|
|
1944
|
-
|
|
1945
|
-
|
|
2351
|
+
fn,
|
|
2352
|
+
});
|
|
2353
|
+
const handlerValue = handlerResult.take();
|
|
2354
|
+
if (isErr(handlerValue)) {
|
|
2355
|
+
recordRuntimeError(handlerValue.error, {
|
|
2356
|
+
surface: "event",
|
|
2357
|
+
direction: "consumer",
|
|
2358
|
+
operation: String(event),
|
|
2359
|
+
phase: "handler_result",
|
|
2360
|
+
});
|
|
1946
2361
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
1947
|
-
error:
|
|
2362
|
+
error: handlerValue.error.toSerializable(),
|
|
1948
2363
|
event,
|
|
1949
2364
|
subject: msg.subject,
|
|
1950
2365
|
}, "Event handler failed");
|
|
@@ -1953,6 +2368,31 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1953
2368
|
});
|
|
1954
2369
|
__classPrivateFieldGet(this, _Trellis_tasks, "f").add(`event:${event}:${ulid()}`, task);
|
|
1955
2370
|
return ok(undefined);
|
|
2371
|
+
}, _Trellis_invokeEventHandler = async function _Trellis_invokeEventHandler(args) {
|
|
2372
|
+
const annotation = {
|
|
2373
|
+
event: String(args.event),
|
|
2374
|
+
service: this.name,
|
|
2375
|
+
contractId: this.contractId,
|
|
2376
|
+
contractDigest: this.contractDigest,
|
|
2377
|
+
traceId: traceIdFromTraceparent(args.message.headers?.get("traceparent")),
|
|
2378
|
+
};
|
|
2379
|
+
try {
|
|
2380
|
+
const result = await Promise.resolve(args.fn(args.payload, createEventListenerContext({
|
|
2381
|
+
payload: args.payload,
|
|
2382
|
+
subject: args.message.subject,
|
|
2383
|
+
mode: args.mode,
|
|
2384
|
+
...(args.group ? { group: args.group } : {}),
|
|
2385
|
+
message: args.message,
|
|
2386
|
+
})));
|
|
2387
|
+
const outcome = isResultLike(result) ? result.take() : result;
|
|
2388
|
+
if (isErr(outcome)) {
|
|
2389
|
+
return err(annotateHandlerBoundaryError(outcome.error, annotation));
|
|
2390
|
+
}
|
|
2391
|
+
return ok(undefined);
|
|
2392
|
+
}
|
|
2393
|
+
catch (cause) {
|
|
2394
|
+
return err(annotateHandlerBoundaryError(cause, annotation));
|
|
2395
|
+
}
|
|
1956
2396
|
}, _Trellis_resolveEventConsumerGroup = function _Trellis_resolveEventConsumerGroup(event, opts) {
|
|
1957
2397
|
const metadata = __classPrivateFieldGet(this, _Trellis_eventConsumers, "f").metadata;
|
|
1958
2398
|
const bindings = __classPrivateFieldGet(this, _Trellis_eventConsumers, "f").bindings ?? {};
|
|
@@ -2078,15 +2518,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2078
2518
|
msg.term();
|
|
2079
2519
|
continue;
|
|
2080
2520
|
}
|
|
2081
|
-
const handlerResult = await
|
|
2521
|
+
const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
|
|
2522
|
+
event,
|
|
2082
2523
|
payload: m,
|
|
2083
|
-
subject: msg.subject,
|
|
2084
2524
|
mode: "durable",
|
|
2085
2525
|
message: msg,
|
|
2086
|
-
|
|
2087
|
-
|
|
2526
|
+
fn,
|
|
2527
|
+
});
|
|
2528
|
+
const handlerValue = handlerResult.take();
|
|
2529
|
+
if (isErr(handlerValue)) {
|
|
2088
2530
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
2089
|
-
error:
|
|
2531
|
+
error: handlerValue.error.toSerializable(),
|
|
2090
2532
|
event,
|
|
2091
2533
|
subject: msg.subject,
|
|
2092
2534
|
}, "Event handler failed");
|
|
@@ -2115,20 +2557,34 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2115
2557
|
const eventPayload = parsedEvent.take();
|
|
2116
2558
|
if (isErr(eventPayload)) {
|
|
2117
2559
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({ error: eventPayload.error }, "Event validation failed");
|
|
2560
|
+
recordRuntimeError(eventPayload.error, {
|
|
2561
|
+
surface: "event",
|
|
2562
|
+
direction: "consumer",
|
|
2563
|
+
operation: String(registration.event),
|
|
2564
|
+
phase: "input_validation",
|
|
2565
|
+
});
|
|
2118
2566
|
msg.term();
|
|
2119
2567
|
failed = true;
|
|
2120
2568
|
break;
|
|
2121
2569
|
}
|
|
2122
|
-
const handlerResult = await
|
|
2570
|
+
const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
|
|
2571
|
+
event: registration.event,
|
|
2123
2572
|
payload: eventPayload,
|
|
2124
|
-
subject: msg.subject,
|
|
2125
2573
|
mode: "durable",
|
|
2126
2574
|
group,
|
|
2127
2575
|
message: msg,
|
|
2128
|
-
|
|
2129
|
-
|
|
2576
|
+
fn: registration.fn,
|
|
2577
|
+
});
|
|
2578
|
+
const handlerValue = handlerResult.take();
|
|
2579
|
+
if (isErr(handlerValue)) {
|
|
2580
|
+
recordRuntimeError(handlerValue.error, {
|
|
2581
|
+
surface: "event",
|
|
2582
|
+
direction: "consumer",
|
|
2583
|
+
operation: String(registration.event),
|
|
2584
|
+
phase: "handler_result",
|
|
2585
|
+
});
|
|
2130
2586
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
2131
|
-
error:
|
|
2587
|
+
error: handlerValue.error.toSerializable(),
|
|
2132
2588
|
event: registration.event,
|
|
2133
2589
|
subject: msg.subject,
|
|
2134
2590
|
}, "Event handler failed");
|
|
@@ -2224,6 +2680,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2224
2680
|
code: SpanStatusCode.ERROR,
|
|
2225
2681
|
message: response.error.message,
|
|
2226
2682
|
});
|
|
2683
|
+
recordRuntimeError(response.error, {
|
|
2684
|
+
surface: "operation",
|
|
2685
|
+
direction: "client",
|
|
2686
|
+
operation: "requestJson",
|
|
2687
|
+
phase: "request_send",
|
|
2688
|
+
});
|
|
2227
2689
|
return response;
|
|
2228
2690
|
}
|
|
2229
2691
|
const json = safeJson(response).take();
|
|
@@ -2239,6 +2701,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2239
2701
|
code: SpanStatusCode.ERROR,
|
|
2240
2702
|
message: error.message,
|
|
2241
2703
|
});
|
|
2704
|
+
recordRuntimeError(error, {
|
|
2705
|
+
surface: "operation",
|
|
2706
|
+
direction: "client",
|
|
2707
|
+
operation: "requestJson",
|
|
2708
|
+
phase: "response_decoding",
|
|
2709
|
+
});
|
|
2242
2710
|
return err(error);
|
|
2243
2711
|
}
|
|
2244
2712
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
@@ -2251,6 +2719,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2251
2719
|
message: error.message,
|
|
2252
2720
|
});
|
|
2253
2721
|
span.recordException(error);
|
|
2722
|
+
recordRuntimeError(error, {
|
|
2723
|
+
surface: "operation",
|
|
2724
|
+
direction: "client",
|
|
2725
|
+
operation: "requestJson",
|
|
2726
|
+
phase: "unexpected",
|
|
2727
|
+
});
|
|
2254
2728
|
return err(error);
|
|
2255
2729
|
}
|
|
2256
2730
|
finally {
|
|
@@ -2278,35 +2752,56 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2278
2752
|
}
|
|
2279
2753
|
catch (cause) {
|
|
2280
2754
|
sub.unsubscribe();
|
|
2281
|
-
|
|
2755
|
+
const error = createTransportError({
|
|
2282
2756
|
code: "trellis.watch.failed",
|
|
2283
2757
|
message: "Trellis could not start the operation watch.",
|
|
2284
2758
|
hint: "Retry watching the operation. If it keeps failing, reconnect to Trellis and try again.",
|
|
2285
2759
|
cause,
|
|
2286
2760
|
context: { subject },
|
|
2287
|
-
})
|
|
2761
|
+
});
|
|
2762
|
+
recordRuntimeError(error, {
|
|
2763
|
+
surface: "operation",
|
|
2764
|
+
direction: "client",
|
|
2765
|
+
operation: "watchJson",
|
|
2766
|
+
phase: "request_send",
|
|
2767
|
+
});
|
|
2768
|
+
return err(error);
|
|
2288
2769
|
}
|
|
2289
2770
|
return ok((async function* () {
|
|
2290
2771
|
try {
|
|
2291
2772
|
for await (const msg of sub) {
|
|
2292
2773
|
if (msg.headers?.get("status") === "error") {
|
|
2293
|
-
|
|
2774
|
+
const error = createTransportError({
|
|
2294
2775
|
code: "trellis.watch.failed",
|
|
2295
2776
|
message: "Trellis stopped the operation watch.",
|
|
2296
2777
|
hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
|
|
2297
2778
|
context: { subject, frame: msg.string() },
|
|
2298
|
-
})
|
|
2779
|
+
});
|
|
2780
|
+
recordRuntimeError(error, {
|
|
2781
|
+
surface: "operation",
|
|
2782
|
+
direction: "client",
|
|
2783
|
+
operation: "watchJson",
|
|
2784
|
+
phase: "remote_error",
|
|
2785
|
+
});
|
|
2786
|
+
yield err(error);
|
|
2299
2787
|
continue;
|
|
2300
2788
|
}
|
|
2301
2789
|
const json = safeJson(msg).take();
|
|
2302
2790
|
if (isErr(json)) {
|
|
2303
|
-
|
|
2791
|
+
const error = createTransportError({
|
|
2304
2792
|
code: "trellis.watch.invalid_response",
|
|
2305
2793
|
message: "Trellis returned an invalid watch update.",
|
|
2306
2794
|
hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
|
|
2307
2795
|
cause: json.error.cause,
|
|
2308
2796
|
context: { subject },
|
|
2309
|
-
})
|
|
2797
|
+
});
|
|
2798
|
+
recordRuntimeError(error, {
|
|
2799
|
+
surface: "operation",
|
|
2800
|
+
direction: "client",
|
|
2801
|
+
operation: "watchJson",
|
|
2802
|
+
phase: "response_decoding",
|
|
2803
|
+
});
|
|
2804
|
+
yield err(error);
|
|
2310
2805
|
continue;
|
|
2311
2806
|
}
|
|
2312
2807
|
yield ok(json);
|