@qlever-llc/trellis 0.10.11 → 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/errors/TrellisError.d.ts +3 -3
- package/esm/errors/TrellisError.js +3 -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 +8 -0
- package/esm/server.d.ts.map +1 -1
- package/esm/server.js +54 -6
- 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.map +1 -1
- package/esm/trellis.js +460 -56
- package/package.json +7 -5
- package/script/errors/TrellisError.d.ts +3 -3
- package/script/errors/TrellisError.js +3 -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 +8 -0
- package/script/server.d.ts.map +1 -1
- package/script/server.js +54 -6
- 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.map +1 -1
- package/script/trellis.js +490 -86
- package/src/errors/TrellisError.ts +4 -4
- package/src/server/internal_jobs/job-manager.ts +35 -5
- package/src/server/runtime.ts +4 -0
- package/src/server/service.ts +27 -0
- package/src/server.ts +66 -11
- 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 +487 -88
- 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
|
@@ -14,7 +14,7 @@ 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";
|
|
@@ -205,6 +205,12 @@ export function annotateHandlerBoundaryError(cause, context) {
|
|
|
205
205
|
error.withTraceId(context.traceId);
|
|
206
206
|
return error;
|
|
207
207
|
}
|
|
208
|
+
function recordRuntimeError(error, attributes) {
|
|
209
|
+
recordTrellisError(error, {
|
|
210
|
+
messagingSystem: "nats",
|
|
211
|
+
...attributes,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
208
214
|
const DurableOperationSignalSchema = Type.Object({
|
|
209
215
|
operationId: Type.String(),
|
|
210
216
|
sequence: Type.Number(),
|
|
@@ -935,14 +941,27 @@ export class Trellis {
|
|
|
935
941
|
const eventName = event;
|
|
936
942
|
const ctx = this.api["events"][eventName];
|
|
937
943
|
if (!ctx) {
|
|
938
|
-
|
|
944
|
+
const error = new UnexpectedError({
|
|
939
945
|
cause: __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_unknownApiError).call(this, "event", event.toString()),
|
|
940
946
|
context: { event: event.toString() },
|
|
941
|
-
})
|
|
947
|
+
});
|
|
948
|
+
recordRuntimeError(error, {
|
|
949
|
+
surface: "event",
|
|
950
|
+
direction: "publisher",
|
|
951
|
+
operation: event,
|
|
952
|
+
phase: "prepare",
|
|
953
|
+
});
|
|
954
|
+
return err(error);
|
|
942
955
|
}
|
|
943
956
|
const subject = this.template(ctx.subject, data).take();
|
|
944
957
|
if (isErr(subject)) {
|
|
945
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
|
+
});
|
|
946
965
|
return subject;
|
|
947
966
|
}
|
|
948
967
|
const header = {
|
|
@@ -956,7 +975,14 @@ export class Trellis {
|
|
|
956
975
|
const msg = encodeSchema(ctx.event, payload).take();
|
|
957
976
|
if (isErr(msg)) {
|
|
958
977
|
logger.error({ err: msg.error }, "Failed to encode event.");
|
|
959
|
-
|
|
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);
|
|
960
986
|
}
|
|
961
987
|
const headers = natsHeaders();
|
|
962
988
|
headers.set("Nats-Msg-Id", header.id);
|
|
@@ -974,7 +1000,17 @@ export class Trellis {
|
|
|
974
1000
|
}));
|
|
975
1001
|
}
|
|
976
1002
|
catch (cause) {
|
|
977
|
-
|
|
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);
|
|
978
1014
|
}
|
|
979
1015
|
}
|
|
980
1016
|
/**
|
|
@@ -993,7 +1029,17 @@ export class Trellis {
|
|
|
993
1029
|
return ok(undefined);
|
|
994
1030
|
}
|
|
995
1031
|
catch (cause) {
|
|
996
|
-
|
|
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);
|
|
997
1043
|
}
|
|
998
1044
|
})());
|
|
999
1045
|
}
|
|
@@ -1229,10 +1275,22 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1229
1275
|
__classPrivateFieldGet(this, _Trellis_log, "f").trace({ method: String(method) }, `Calling ${method.toString()}.`);
|
|
1230
1276
|
const msg = encodeRuntimeSchema(ctx.input, input).take();
|
|
1231
1277
|
if (isErr(msg)) {
|
|
1278
|
+
recordRuntimeError(msg.error, {
|
|
1279
|
+
surface: "rpc",
|
|
1280
|
+
direction: "client",
|
|
1281
|
+
operation: method,
|
|
1282
|
+
phase: "request_encoding",
|
|
1283
|
+
});
|
|
1232
1284
|
return msg;
|
|
1233
1285
|
}
|
|
1234
1286
|
const subject = this.template(ctx.subject, input).take();
|
|
1235
1287
|
if (isErr(subject)) {
|
|
1288
|
+
recordRuntimeError(subject.error, {
|
|
1289
|
+
surface: "rpc",
|
|
1290
|
+
direction: "client",
|
|
1291
|
+
operation: method,
|
|
1292
|
+
phase: "request_encoding",
|
|
1293
|
+
});
|
|
1236
1294
|
return subject;
|
|
1237
1295
|
}
|
|
1238
1296
|
const span = startClientSpan(method, subject);
|
|
@@ -1254,30 +1312,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1254
1312
|
});
|
|
1255
1313
|
const response = msgResult.take();
|
|
1256
1314
|
if (isErr(response)) {
|
|
1315
|
+
recordRuntimeError(response.error, {
|
|
1316
|
+
surface: "rpc",
|
|
1317
|
+
direction: "client",
|
|
1318
|
+
operation: method,
|
|
1319
|
+
phase: "request_send",
|
|
1320
|
+
});
|
|
1257
1321
|
return response;
|
|
1258
1322
|
}
|
|
1259
1323
|
if (response.headers?.get("status") === "error") {
|
|
1260
1324
|
const json = safeJson(response).take();
|
|
1261
1325
|
if (isErr(json)) {
|
|
1262
|
-
|
|
1326
|
+
const error = requestFailedTransportError({
|
|
1263
1327
|
code: "trellis.request.invalid_response",
|
|
1264
1328
|
message: "Trellis returned an invalid response.",
|
|
1265
1329
|
hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
|
|
1266
1330
|
method,
|
|
1267
1331
|
subject,
|
|
1268
1332
|
cause: json.error.cause,
|
|
1269
|
-
})
|
|
1333
|
+
});
|
|
1334
|
+
recordRuntimeError(error, {
|
|
1335
|
+
surface: "rpc",
|
|
1336
|
+
direction: "client",
|
|
1337
|
+
operation: method,
|
|
1338
|
+
phase: "response_decoding",
|
|
1339
|
+
});
|
|
1340
|
+
return err(error);
|
|
1270
1341
|
}
|
|
1271
1342
|
const errorData = parse(TrellisErrorDataSchema, json).take();
|
|
1272
1343
|
if (isErr(errorData)) {
|
|
1273
|
-
|
|
1344
|
+
const error = requestFailedTransportError({
|
|
1274
1345
|
code: "trellis.request.invalid_response",
|
|
1275
1346
|
message: "Trellis returned an invalid response.",
|
|
1276
1347
|
hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
|
|
1277
1348
|
method,
|
|
1278
1349
|
subject,
|
|
1279
1350
|
cause: errorData.error,
|
|
1280
|
-
})
|
|
1351
|
+
});
|
|
1352
|
+
recordRuntimeError(error, {
|
|
1353
|
+
surface: "rpc",
|
|
1354
|
+
direction: "client",
|
|
1355
|
+
operation: method,
|
|
1356
|
+
phase: "response_decoding",
|
|
1357
|
+
});
|
|
1358
|
+
return err(error);
|
|
1281
1359
|
}
|
|
1282
1360
|
const declaredErrorTypes = Array.isArray(ctx.declaredErrorTypes)
|
|
1283
1361
|
? ctx.declaredErrorTypes.filter((value) => typeof value === "string")
|
|
@@ -1288,25 +1366,50 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1288
1366
|
const reconstructed = reconstructDeclaredRpcError(declaredErrorTypes, runtimeErrors, errorData, json);
|
|
1289
1367
|
if (reconstructed) {
|
|
1290
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
|
+
});
|
|
1291
1375
|
return err(reconstructed);
|
|
1292
1376
|
}
|
|
1293
1377
|
const remoteError = new RemoteError({ error: errorData });
|
|
1294
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
|
+
});
|
|
1295
1385
|
return err(remoteError);
|
|
1296
1386
|
}
|
|
1297
1387
|
const json = safeJson(response).take();
|
|
1298
1388
|
if (isErr(json)) {
|
|
1299
|
-
|
|
1389
|
+
const error = requestFailedTransportError({
|
|
1300
1390
|
code: "trellis.request.invalid_response",
|
|
1301
1391
|
message: "Trellis returned an invalid response.",
|
|
1302
1392
|
hint: "Retry the request. If it keeps happening, check the Trellis capability handling this request.",
|
|
1303
1393
|
method,
|
|
1304
1394
|
subject,
|
|
1305
1395
|
cause: json.error.cause,
|
|
1306
|
-
})
|
|
1396
|
+
});
|
|
1397
|
+
recordRuntimeError(error, {
|
|
1398
|
+
surface: "rpc",
|
|
1399
|
+
direction: "client",
|
|
1400
|
+
operation: method,
|
|
1401
|
+
phase: "response_decoding",
|
|
1402
|
+
});
|
|
1403
|
+
return err(error);
|
|
1307
1404
|
}
|
|
1308
1405
|
const outputResult = parseRuntimeSchema(ctx.output, json).take();
|
|
1309
1406
|
if (isErr(outputResult)) {
|
|
1407
|
+
recordRuntimeError(outputResult.error, {
|
|
1408
|
+
surface: "rpc",
|
|
1409
|
+
direction: "client",
|
|
1410
|
+
operation: method,
|
|
1411
|
+
phase: "response_decoding",
|
|
1412
|
+
});
|
|
1310
1413
|
return err(outputResult.error);
|
|
1311
1414
|
}
|
|
1312
1415
|
return ok(outputResult);
|
|
@@ -1335,6 +1438,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1335
1438
|
message: unexpected.message,
|
|
1336
1439
|
});
|
|
1337
1440
|
span.recordException(unexpected);
|
|
1441
|
+
recordRuntimeError(unexpected, {
|
|
1442
|
+
surface: "rpc",
|
|
1443
|
+
direction: "client",
|
|
1444
|
+
operation: method,
|
|
1445
|
+
phase: "unexpected",
|
|
1446
|
+
});
|
|
1338
1447
|
return err(unexpected);
|
|
1339
1448
|
}
|
|
1340
1449
|
finally {
|
|
@@ -1403,11 +1512,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1403
1512
|
}, _Trellis_subscribeFeed = function _Trellis_subscribeFeed(feed, descriptor, input, opts) {
|
|
1404
1513
|
return AsyncResult.from((async () => {
|
|
1405
1514
|
const payload = encodeRuntimeSchema(descriptor.input, input).take();
|
|
1406
|
-
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
|
+
});
|
|
1407
1522
|
return payload;
|
|
1523
|
+
}
|
|
1408
1524
|
const subject = this.template(descriptor.subject, input).take();
|
|
1409
|
-
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
|
+
});
|
|
1410
1532
|
return subject;
|
|
1533
|
+
}
|
|
1411
1534
|
const authHeaders = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_createProof).call(this, subject, payload);
|
|
1412
1535
|
const headers = natsHeaders();
|
|
1413
1536
|
headers.set("session-key", this.auth.sessionKey);
|
|
@@ -1427,13 +1550,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1427
1550
|
catch (cause) {
|
|
1428
1551
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1429
1552
|
sub.unsubscribe();
|
|
1430
|
-
|
|
1553
|
+
const error = createTransportError({
|
|
1431
1554
|
code: "trellis.feed.subscribe_failed",
|
|
1432
1555
|
message: "Trellis could not subscribe to the feed.",
|
|
1433
1556
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
|
|
1434
1557
|
cause,
|
|
1435
1558
|
context: { feed, subject },
|
|
1436
|
-
})
|
|
1559
|
+
});
|
|
1560
|
+
recordRuntimeError(error, {
|
|
1561
|
+
surface: "feed",
|
|
1562
|
+
direction: "client",
|
|
1563
|
+
operation: feed,
|
|
1564
|
+
phase: "request_send",
|
|
1565
|
+
});
|
|
1566
|
+
return err(error);
|
|
1437
1567
|
}
|
|
1438
1568
|
let timeoutId;
|
|
1439
1569
|
let abortHandler;
|
|
@@ -1459,7 +1589,7 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1459
1589
|
if (firstFrame === "timeout" || firstFrame === "aborted") {
|
|
1460
1590
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1461
1591
|
sub.unsubscribe();
|
|
1462
|
-
|
|
1592
|
+
const error = createTransportError({
|
|
1463
1593
|
code: firstFrame === "timeout"
|
|
1464
1594
|
? "trellis.feed.subscribe_timeout"
|
|
1465
1595
|
: "trellis.feed.subscribe_aborted",
|
|
@@ -1470,28 +1600,49 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1470
1600
|
? "Check that the target service is running and has the current deployment digest, then retry."
|
|
1471
1601
|
: "Retry the subscription if the feed is still needed.",
|
|
1472
1602
|
context: { feed, subject },
|
|
1473
|
-
})
|
|
1603
|
+
});
|
|
1604
|
+
recordRuntimeError(error, {
|
|
1605
|
+
surface: "feed",
|
|
1606
|
+
direction: "client",
|
|
1607
|
+
operation: feed,
|
|
1608
|
+
phase: "handshake",
|
|
1609
|
+
});
|
|
1610
|
+
return err(error);
|
|
1474
1611
|
}
|
|
1475
1612
|
if (firstFrame.done) {
|
|
1476
1613
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1477
1614
|
sub.unsubscribe();
|
|
1478
|
-
|
|
1615
|
+
const error = createTransportError({
|
|
1479
1616
|
code: "trellis.feed.subscribe_closed",
|
|
1480
1617
|
message: "Trellis closed the feed before acknowledging it.",
|
|
1481
1618
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
|
|
1482
1619
|
context: { feed, subject },
|
|
1483
|
-
})
|
|
1620
|
+
});
|
|
1621
|
+
recordRuntimeError(error, {
|
|
1622
|
+
surface: "feed",
|
|
1623
|
+
direction: "client",
|
|
1624
|
+
operation: feed,
|
|
1625
|
+
phase: "handshake",
|
|
1626
|
+
});
|
|
1627
|
+
return err(error);
|
|
1484
1628
|
}
|
|
1485
1629
|
const firstMessage = firstFrame.value;
|
|
1486
1630
|
if (firstMessage.headers?.get("status") === "error") {
|
|
1487
1631
|
opts?.signal?.removeEventListener("abort", abort);
|
|
1488
1632
|
sub.unsubscribe();
|
|
1489
|
-
|
|
1633
|
+
const error = createTransportError({
|
|
1490
1634
|
code: "trellis.feed.failed",
|
|
1491
1635
|
message: "Trellis rejected the feed subscription.",
|
|
1492
1636
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health and permissions.",
|
|
1493
1637
|
context: { feed, subject, frame: firstMessage.string() },
|
|
1494
|
-
})
|
|
1638
|
+
});
|
|
1639
|
+
recordRuntimeError(error, {
|
|
1640
|
+
surface: "feed",
|
|
1641
|
+
direction: "client",
|
|
1642
|
+
operation: feed,
|
|
1643
|
+
phase: "remote_error",
|
|
1644
|
+
});
|
|
1645
|
+
return err(error);
|
|
1495
1646
|
}
|
|
1496
1647
|
const firstEvent = firstMessage.headers?.get("feed-status") === "ready"
|
|
1497
1648
|
? undefined
|
|
@@ -1501,19 +1652,40 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1501
1652
|
try {
|
|
1502
1653
|
const parseFeedFrame = (msg) => {
|
|
1503
1654
|
if (msg.headers?.get("status") === "error") {
|
|
1504
|
-
|
|
1655
|
+
const error = createTransportError({
|
|
1505
1656
|
code: "trellis.feed.failed",
|
|
1506
1657
|
message: "Trellis stopped the feed.",
|
|
1507
1658
|
hint: "Retry the subscription. If it keeps failing, check Trellis runtime health.",
|
|
1508
1659
|
context: { feed, subject, frame: msg.string() },
|
|
1509
1660
|
});
|
|
1661
|
+
recordRuntimeError(error, {
|
|
1662
|
+
surface: "feed",
|
|
1663
|
+
direction: "client",
|
|
1664
|
+
operation: feed,
|
|
1665
|
+
phase: "remote_error",
|
|
1666
|
+
});
|
|
1667
|
+
throw error;
|
|
1510
1668
|
}
|
|
1511
1669
|
const json = safeJson(msg).take();
|
|
1512
|
-
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
|
+
});
|
|
1513
1677
|
throw json.error;
|
|
1678
|
+
}
|
|
1514
1679
|
const parsed = parseRuntimeSchema(eventSchema, json).take();
|
|
1515
|
-
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
|
+
});
|
|
1516
1687
|
throw parsed.error;
|
|
1688
|
+
}
|
|
1517
1689
|
return parsed;
|
|
1518
1690
|
};
|
|
1519
1691
|
if (firstEvent)
|
|
@@ -1541,13 +1713,20 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1541
1713
|
await this.nats.flush();
|
|
1542
1714
|
}
|
|
1543
1715
|
catch (cause) {
|
|
1544
|
-
|
|
1716
|
+
const error = createTransportError({
|
|
1545
1717
|
code: "trellis.feed.listen_failed",
|
|
1546
1718
|
message: "Trellis could not listen for feed requests.",
|
|
1547
1719
|
hint: "Check the service deployment digest and runtime permissions, then restart the service.",
|
|
1548
1720
|
cause,
|
|
1549
1721
|
context: { feed, subject },
|
|
1550
1722
|
});
|
|
1723
|
+
recordRuntimeError(error, {
|
|
1724
|
+
surface: "feed",
|
|
1725
|
+
direction: "server",
|
|
1726
|
+
operation: feed,
|
|
1727
|
+
phase: "listen",
|
|
1728
|
+
});
|
|
1729
|
+
throw error;
|
|
1551
1730
|
}
|
|
1552
1731
|
const task = AsyncResult.try(async () => {
|
|
1553
1732
|
for await (const msg of sub) {
|
|
@@ -1568,6 +1747,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1568
1747
|
contractDigest: this.contractDigest,
|
|
1569
1748
|
traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
|
|
1570
1749
|
});
|
|
1750
|
+
recordRuntimeError(error, {
|
|
1751
|
+
surface: "feed",
|
|
1752
|
+
direction: "server",
|
|
1753
|
+
operation: feed,
|
|
1754
|
+
phase: "handler_throw",
|
|
1755
|
+
});
|
|
1571
1756
|
__classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_respondWithError).call(this, msg, error);
|
|
1572
1757
|
}
|
|
1573
1758
|
})();
|
|
@@ -1576,11 +1761,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1576
1761
|
__classPrivateFieldGet(this, _Trellis_tasks, "f").add(`feed:${feed}`, task);
|
|
1577
1762
|
}, _Trellis_processFeedMessage = async function _Trellis_processFeedMessage(feed, descriptor, msg, handler) {
|
|
1578
1763
|
const json = safeJson(msg).take();
|
|
1579
|
-
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
|
+
});
|
|
1580
1771
|
return json;
|
|
1772
|
+
}
|
|
1581
1773
|
const parsed = parseRuntimeSchema(descriptor.input, json).take();
|
|
1582
|
-
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
|
+
});
|
|
1583
1781
|
return parsed;
|
|
1782
|
+
}
|
|
1584
1783
|
const caller = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_authenticateFeedRequest).call(this, {
|
|
1585
1784
|
feed,
|
|
1586
1785
|
subject: msg.subject,
|
|
@@ -1589,12 +1788,26 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1589
1788
|
requiredCapabilities: descriptor.subscribeCapabilities,
|
|
1590
1789
|
});
|
|
1591
1790
|
const callerValue = caller.take();
|
|
1592
|
-
if (isErr(callerValue))
|
|
1791
|
+
if (isErr(callerValue)) {
|
|
1792
|
+
recordRuntimeError(callerValue.error, {
|
|
1793
|
+
surface: "feed",
|
|
1794
|
+
direction: "server",
|
|
1795
|
+
operation: feed,
|
|
1796
|
+
phase: "auth",
|
|
1797
|
+
});
|
|
1593
1798
|
return callerValue;
|
|
1799
|
+
}
|
|
1594
1800
|
if (!msg.reply) {
|
|
1595
|
-
|
|
1801
|
+
const error = new UnexpectedError({
|
|
1596
1802
|
context: { feed, reason: "missing_reply" },
|
|
1597
|
-
})
|
|
1803
|
+
});
|
|
1804
|
+
recordRuntimeError(error, {
|
|
1805
|
+
surface: "feed",
|
|
1806
|
+
direction: "server",
|
|
1807
|
+
operation: feed,
|
|
1808
|
+
phase: "handshake",
|
|
1809
|
+
});
|
|
1810
|
+
return err(error);
|
|
1598
1811
|
}
|
|
1599
1812
|
const readyHeaders = natsHeaders();
|
|
1600
1813
|
readyHeaders.set("feed-status", "ready");
|
|
@@ -1608,15 +1821,44 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1608
1821
|
signal: controller.signal,
|
|
1609
1822
|
emit: (event) => AsyncResult.from((async () => {
|
|
1610
1823
|
const payload = encodeRuntimeSchema(descriptor.event, event).take();
|
|
1611
|
-
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
|
+
});
|
|
1612
1831
|
return payload;
|
|
1832
|
+
}
|
|
1613
1833
|
if (!msg.reply) {
|
|
1614
|
-
|
|
1834
|
+
const error = new UnexpectedError({
|
|
1615
1835
|
context: { feed, reason: "missing_reply" },
|
|
1616
|
-
})
|
|
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);
|
|
1617
1861
|
}
|
|
1618
|
-
this.nats.publish(msg.reply, payload);
|
|
1619
|
-
await this.nats.flush();
|
|
1620
1862
|
return ok(undefined);
|
|
1621
1863
|
})()),
|
|
1622
1864
|
});
|
|
@@ -1624,14 +1866,21 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1624
1866
|
? handlerResult.take()
|
|
1625
1867
|
: handlerResult;
|
|
1626
1868
|
if (isErr(handlerOutcome)) {
|
|
1627
|
-
|
|
1869
|
+
const error = annotateHandlerBoundaryError(handlerOutcome.error, {
|
|
1628
1870
|
feed,
|
|
1629
1871
|
requestId: msg.headers?.get("request-id"),
|
|
1630
1872
|
service: this.name,
|
|
1631
1873
|
contractId: this.contractId,
|
|
1632
1874
|
contractDigest: this.contractDigest,
|
|
1633
1875
|
traceId: traceIdFromTraceparent(msg.headers?.get("traceparent")),
|
|
1634
|
-
})
|
|
1876
|
+
});
|
|
1877
|
+
recordRuntimeError(error, {
|
|
1878
|
+
surface: "feed",
|
|
1879
|
+
direction: "server",
|
|
1880
|
+
operation: feed,
|
|
1881
|
+
phase: "handler_result",
|
|
1882
|
+
});
|
|
1883
|
+
return err(error);
|
|
1635
1884
|
}
|
|
1636
1885
|
return ok(undefined);
|
|
1637
1886
|
}
|
|
@@ -1700,6 +1949,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1700
1949
|
code: SpanStatusCode.ERROR,
|
|
1701
1950
|
message: "Failed to parse JSON",
|
|
1702
1951
|
});
|
|
1952
|
+
recordRuntimeError(jsonData.error, {
|
|
1953
|
+
surface: "rpc",
|
|
1954
|
+
direction: "server",
|
|
1955
|
+
operation: String(method),
|
|
1956
|
+
phase: "parse",
|
|
1957
|
+
});
|
|
1703
1958
|
return jsonData;
|
|
1704
1959
|
}
|
|
1705
1960
|
const parsedInput = parseRuntimeSchema(ctx.input, jsonData).take();
|
|
@@ -1708,6 +1963,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1708
1963
|
code: SpanStatusCode.ERROR,
|
|
1709
1964
|
message: "Input validation failed",
|
|
1710
1965
|
});
|
|
1966
|
+
recordRuntimeError(parsedInput.error, {
|
|
1967
|
+
surface: "rpc",
|
|
1968
|
+
direction: "server",
|
|
1969
|
+
operation: String(method),
|
|
1970
|
+
phase: "input_validation",
|
|
1971
|
+
});
|
|
1711
1972
|
return parsedInput;
|
|
1712
1973
|
}
|
|
1713
1974
|
let caller;
|
|
@@ -1733,7 +1994,14 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1733
1994
|
code: SpanStatusCode.ERROR,
|
|
1734
1995
|
message: "Missing session-key",
|
|
1735
1996
|
});
|
|
1736
|
-
|
|
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);
|
|
1737
2005
|
}
|
|
1738
2006
|
if (!proof) {
|
|
1739
2007
|
__classPrivateFieldGet(this, _Trellis_log, "f").warn({ method }, "Missing proof in request");
|
|
@@ -1741,11 +2009,25 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1741
2009
|
code: SpanStatusCode.ERROR,
|
|
1742
2010
|
message: "Missing proof",
|
|
1743
2011
|
});
|
|
1744
|
-
|
|
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);
|
|
1745
2020
|
}
|
|
1746
2021
|
const iat = Number(iatHeader);
|
|
1747
2022
|
if (!Number.isSafeInteger(iat) || !requestId) {
|
|
1748
|
-
|
|
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);
|
|
1749
2031
|
}
|
|
1750
2032
|
// Verify proof signature locally using the raw request bytes we received.
|
|
1751
2033
|
const payloadBytes = msg.data ?? new Uint8Array();
|
|
@@ -1764,10 +2046,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1764
2046
|
code: SpanStatusCode.ERROR,
|
|
1765
2047
|
message: "Invalid signature",
|
|
1766
2048
|
});
|
|
1767
|
-
|
|
2049
|
+
const error = new AuthError({
|
|
1768
2050
|
reason: "invalid_signature",
|
|
1769
2051
|
context: { sessionKey },
|
|
1770
|
-
})
|
|
2052
|
+
});
|
|
2053
|
+
recordRuntimeError(error, {
|
|
2054
|
+
surface: "rpc",
|
|
2055
|
+
direction: "server",
|
|
2056
|
+
operation: String(method),
|
|
2057
|
+
phase: "auth",
|
|
2058
|
+
});
|
|
2059
|
+
return err(error);
|
|
1771
2060
|
}
|
|
1772
2061
|
let auth;
|
|
1773
2062
|
for (let attempt = 0; attempt < DEFAULT_AUTH_VALIDATE_SESSION_RETRY_ATTEMPTS; attempt++) {
|
|
@@ -1793,9 +2082,16 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1793
2082
|
await sleep(DEFAULT_AUTH_VALIDATE_SESSION_RETRY_MS * (attempt + 1));
|
|
1794
2083
|
}
|
|
1795
2084
|
if (!auth) {
|
|
1796
|
-
|
|
2085
|
+
const error = new UnexpectedError({
|
|
1797
2086
|
context: { reason: "missing_auth_validate_result" },
|
|
1798
|
-
})
|
|
2087
|
+
});
|
|
2088
|
+
recordRuntimeError(error, {
|
|
2089
|
+
surface: "rpc",
|
|
2090
|
+
direction: "server",
|
|
2091
|
+
operation: String(method),
|
|
2092
|
+
phase: "auth",
|
|
2093
|
+
});
|
|
2094
|
+
return err(error);
|
|
1799
2095
|
}
|
|
1800
2096
|
if (auth instanceof Error) {
|
|
1801
2097
|
__classPrivateFieldGet(this, _Trellis_log, "f").warn({
|
|
@@ -1811,22 +2107,42 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1811
2107
|
message: "Auth.Requests.Validate failed",
|
|
1812
2108
|
});
|
|
1813
2109
|
if (auth instanceof BaseError) {
|
|
2110
|
+
recordRuntimeError(auth, {
|
|
2111
|
+
surface: "rpc",
|
|
2112
|
+
direction: "server",
|
|
2113
|
+
operation: String(method),
|
|
2114
|
+
phase: "auth",
|
|
2115
|
+
});
|
|
1814
2116
|
return err(auth);
|
|
1815
2117
|
}
|
|
1816
|
-
|
|
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);
|
|
1817
2126
|
}
|
|
1818
2127
|
if (!auth.allowed) {
|
|
1819
2128
|
span.setStatus({
|
|
1820
2129
|
code: SpanStatusCode.ERROR,
|
|
1821
2130
|
message: "Insufficient permissions",
|
|
1822
2131
|
});
|
|
1823
|
-
|
|
2132
|
+
const error = new AuthError({
|
|
1824
2133
|
reason: "insufficient_permissions",
|
|
1825
2134
|
context: {
|
|
1826
2135
|
requiredCapabilities: ctx.callerCapabilities,
|
|
1827
2136
|
userCapabilities: auth.caller.capabilities,
|
|
1828
2137
|
},
|
|
1829
|
-
})
|
|
2138
|
+
});
|
|
2139
|
+
recordRuntimeError(error, {
|
|
2140
|
+
surface: "rpc",
|
|
2141
|
+
direction: "server",
|
|
2142
|
+
operation: String(method),
|
|
2143
|
+
phase: "auth",
|
|
2144
|
+
});
|
|
2145
|
+
return err(error);
|
|
1830
2146
|
}
|
|
1831
2147
|
if (typeof msg.reply !== "string" ||
|
|
1832
2148
|
!msg.reply.startsWith(`${auth.inboxPrefix}.`)) {
|
|
@@ -1834,10 +2150,17 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1834
2150
|
code: SpanStatusCode.ERROR,
|
|
1835
2151
|
message: "Reply subject mismatch",
|
|
1836
2152
|
});
|
|
1837
|
-
|
|
2153
|
+
const error = new AuthError({
|
|
1838
2154
|
reason: "reply_subject_mismatch",
|
|
1839
2155
|
context: { expected: auth.inboxPrefix, actual: msg.reply },
|
|
1840
|
-
})
|
|
2156
|
+
});
|
|
2157
|
+
recordRuntimeError(error, {
|
|
2158
|
+
surface: "rpc",
|
|
2159
|
+
direction: "server",
|
|
2160
|
+
operation: String(method),
|
|
2161
|
+
phase: "auth",
|
|
2162
|
+
});
|
|
2163
|
+
return err(error);
|
|
1841
2164
|
}
|
|
1842
2165
|
caller = auth.caller;
|
|
1843
2166
|
}
|
|
@@ -1885,6 +2208,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1885
2208
|
message: error.message,
|
|
1886
2209
|
});
|
|
1887
2210
|
span.recordException(error);
|
|
2211
|
+
recordRuntimeError(error, {
|
|
2212
|
+
surface: "rpc",
|
|
2213
|
+
direction: "server",
|
|
2214
|
+
operation: String(method),
|
|
2215
|
+
phase: "handler_throw",
|
|
2216
|
+
});
|
|
1888
2217
|
return err(error);
|
|
1889
2218
|
}
|
|
1890
2219
|
const handlerResult = handlerResultWrapped.take();
|
|
@@ -1910,6 +2239,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1910
2239
|
code: SpanStatusCode.ERROR,
|
|
1911
2240
|
message: error.message,
|
|
1912
2241
|
});
|
|
2242
|
+
recordRuntimeError(error, {
|
|
2243
|
+
surface: "rpc",
|
|
2244
|
+
direction: "server",
|
|
2245
|
+
operation: String(method),
|
|
2246
|
+
phase: "handler_result",
|
|
2247
|
+
});
|
|
1913
2248
|
return err(error);
|
|
1914
2249
|
}
|
|
1915
2250
|
const encoded = encodeSchema(ctx.output, handlerOutcome).take();
|
|
@@ -1918,6 +2253,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1918
2253
|
code: SpanStatusCode.ERROR,
|
|
1919
2254
|
message: "Output encoding failed",
|
|
1920
2255
|
});
|
|
2256
|
+
recordRuntimeError(encoded.error, {
|
|
2257
|
+
surface: "rpc",
|
|
2258
|
+
direction: "server",
|
|
2259
|
+
operation: String(method),
|
|
2260
|
+
phase: "output_encoding",
|
|
2261
|
+
});
|
|
1921
2262
|
return encoded;
|
|
1922
2263
|
}
|
|
1923
2264
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
@@ -1994,6 +2335,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
1994
2335
|
const m = parsedEvent.take();
|
|
1995
2336
|
if (isErr(m)) {
|
|
1996
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
|
+
});
|
|
1997
2344
|
continue;
|
|
1998
2345
|
}
|
|
1999
2346
|
const handlerResult = await __classPrivateFieldGet(this, _Trellis_instances, "m", _Trellis_invokeEventHandler).call(this, {
|
|
@@ -2005,6 +2352,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2005
2352
|
});
|
|
2006
2353
|
const handlerValue = handlerResult.take();
|
|
2007
2354
|
if (isErr(handlerValue)) {
|
|
2355
|
+
recordRuntimeError(handlerValue.error, {
|
|
2356
|
+
surface: "event",
|
|
2357
|
+
direction: "consumer",
|
|
2358
|
+
operation: String(event),
|
|
2359
|
+
phase: "handler_result",
|
|
2360
|
+
});
|
|
2008
2361
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
2009
2362
|
error: handlerValue.error.toSerializable(),
|
|
2010
2363
|
event,
|
|
@@ -2204,6 +2557,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2204
2557
|
const eventPayload = parsedEvent.take();
|
|
2205
2558
|
if (isErr(eventPayload)) {
|
|
2206
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
|
+
});
|
|
2207
2566
|
msg.term();
|
|
2208
2567
|
failed = true;
|
|
2209
2568
|
break;
|
|
@@ -2218,6 +2577,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2218
2577
|
});
|
|
2219
2578
|
const handlerValue = handlerResult.take();
|
|
2220
2579
|
if (isErr(handlerValue)) {
|
|
2580
|
+
recordRuntimeError(handlerValue.error, {
|
|
2581
|
+
surface: "event",
|
|
2582
|
+
direction: "consumer",
|
|
2583
|
+
operation: String(registration.event),
|
|
2584
|
+
phase: "handler_result",
|
|
2585
|
+
});
|
|
2221
2586
|
__classPrivateFieldGet(this, _Trellis_log, "f").error({
|
|
2222
2587
|
error: handlerValue.error.toSerializable(),
|
|
2223
2588
|
event: registration.event,
|
|
@@ -2315,6 +2680,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2315
2680
|
code: SpanStatusCode.ERROR,
|
|
2316
2681
|
message: response.error.message,
|
|
2317
2682
|
});
|
|
2683
|
+
recordRuntimeError(response.error, {
|
|
2684
|
+
surface: "operation",
|
|
2685
|
+
direction: "client",
|
|
2686
|
+
operation: "requestJson",
|
|
2687
|
+
phase: "request_send",
|
|
2688
|
+
});
|
|
2318
2689
|
return response;
|
|
2319
2690
|
}
|
|
2320
2691
|
const json = safeJson(response).take();
|
|
@@ -2330,6 +2701,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2330
2701
|
code: SpanStatusCode.ERROR,
|
|
2331
2702
|
message: error.message,
|
|
2332
2703
|
});
|
|
2704
|
+
recordRuntimeError(error, {
|
|
2705
|
+
surface: "operation",
|
|
2706
|
+
direction: "client",
|
|
2707
|
+
operation: "requestJson",
|
|
2708
|
+
phase: "response_decoding",
|
|
2709
|
+
});
|
|
2333
2710
|
return err(error);
|
|
2334
2711
|
}
|
|
2335
2712
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
@@ -2342,6 +2719,12 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2342
2719
|
message: error.message,
|
|
2343
2720
|
});
|
|
2344
2721
|
span.recordException(error);
|
|
2722
|
+
recordRuntimeError(error, {
|
|
2723
|
+
surface: "operation",
|
|
2724
|
+
direction: "client",
|
|
2725
|
+
operation: "requestJson",
|
|
2726
|
+
phase: "unexpected",
|
|
2727
|
+
});
|
|
2345
2728
|
return err(error);
|
|
2346
2729
|
}
|
|
2347
2730
|
finally {
|
|
@@ -2369,35 +2752,56 @@ _Trellis_log = new WeakMap(), _Trellis_tasks = new WeakMap(), _Trellis_hasExplic
|
|
|
2369
2752
|
}
|
|
2370
2753
|
catch (cause) {
|
|
2371
2754
|
sub.unsubscribe();
|
|
2372
|
-
|
|
2755
|
+
const error = createTransportError({
|
|
2373
2756
|
code: "trellis.watch.failed",
|
|
2374
2757
|
message: "Trellis could not start the operation watch.",
|
|
2375
2758
|
hint: "Retry watching the operation. If it keeps failing, reconnect to Trellis and try again.",
|
|
2376
2759
|
cause,
|
|
2377
2760
|
context: { subject },
|
|
2378
|
-
})
|
|
2761
|
+
});
|
|
2762
|
+
recordRuntimeError(error, {
|
|
2763
|
+
surface: "operation",
|
|
2764
|
+
direction: "client",
|
|
2765
|
+
operation: "watchJson",
|
|
2766
|
+
phase: "request_send",
|
|
2767
|
+
});
|
|
2768
|
+
return err(error);
|
|
2379
2769
|
}
|
|
2380
2770
|
return ok((async function* () {
|
|
2381
2771
|
try {
|
|
2382
2772
|
for await (const msg of sub) {
|
|
2383
2773
|
if (msg.headers?.get("status") === "error") {
|
|
2384
|
-
|
|
2774
|
+
const error = createTransportError({
|
|
2385
2775
|
code: "trellis.watch.failed",
|
|
2386
2776
|
message: "Trellis stopped the operation watch.",
|
|
2387
2777
|
hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
|
|
2388
2778
|
context: { subject, frame: msg.string() },
|
|
2389
|
-
})
|
|
2779
|
+
});
|
|
2780
|
+
recordRuntimeError(error, {
|
|
2781
|
+
surface: "operation",
|
|
2782
|
+
direction: "client",
|
|
2783
|
+
operation: "watchJson",
|
|
2784
|
+
phase: "remote_error",
|
|
2785
|
+
});
|
|
2786
|
+
yield err(error);
|
|
2390
2787
|
continue;
|
|
2391
2788
|
}
|
|
2392
2789
|
const json = safeJson(msg).take();
|
|
2393
2790
|
if (isErr(json)) {
|
|
2394
|
-
|
|
2791
|
+
const error = createTransportError({
|
|
2395
2792
|
code: "trellis.watch.invalid_response",
|
|
2396
2793
|
message: "Trellis returned an invalid watch update.",
|
|
2397
2794
|
hint: "Retry watching the operation. If it keeps happening, reconnect to Trellis and try again.",
|
|
2398
2795
|
cause: json.error.cause,
|
|
2399
2796
|
context: { subject },
|
|
2400
|
-
})
|
|
2797
|
+
});
|
|
2798
|
+
recordRuntimeError(error, {
|
|
2799
|
+
surface: "operation",
|
|
2800
|
+
direction: "client",
|
|
2801
|
+
operation: "watchJson",
|
|
2802
|
+
phase: "response_decoding",
|
|
2803
|
+
});
|
|
2804
|
+
yield err(error);
|
|
2401
2805
|
continue;
|
|
2402
2806
|
}
|
|
2403
2807
|
yield ok(json);
|