@loutrejs/loutre 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/kernel.d.ts +2 -1
- package/dist/core/extension.d.ts +13 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/core/instrumentation.d.ts +36 -0
- package/dist/core/instrumentation.js +1 -0
- package/dist/devtools/capsule.d.ts +51 -0
- package/dist/devtools/capsule.js +245 -0
- package/dist/devtools/index.d.ts +11 -0
- package/dist/devtools/index.js +6 -0
- package/dist/devtools/inspector.d.ts +57 -0
- package/dist/devtools/inspector.js +1 -0
- package/dist/devtools/module.d.ts +18 -0
- package/dist/devtools/module.js +26 -0
- package/dist/devtools/observer.d.ts +77 -0
- package/dist/devtools/observer.js +10 -0
- package/dist/devtools/protocol.d.ts +1 -0
- package/dist/devtools/protocol.js +1 -0
- package/dist/devtools/provider-instrumentation.d.ts +14 -0
- package/dist/devtools/provider-instrumentation.js +117 -0
- package/dist/devtools/runtime.d.ts +20 -0
- package/dist/devtools/runtime.js +519 -0
- package/dist/devtools/values.d.ts +35 -0
- package/dist/devtools/values.js +175 -0
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/http/extension.js +137 -23
- package/dist/message-port/extension.js +20 -4
- package/dist/runtime/di.d.ts +4 -0
- package/dist/runtime/di.js +14 -4
- package/dist/runtime/kernel.d.ts +4 -2
- package/dist/runtime/kernel.js +96 -3
- package/dist/tasks/extension.js +30 -4
- package/dist/websocket/extension.js +14 -5
- package/package.json +5 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const defaultMaxValueBytes = 64 * 1024;
|
|
2
|
+
const defaultMaxDepth = 8;
|
|
3
|
+
export function previewDevtoolsValue(value, options = {}) {
|
|
4
|
+
const maxValueBytes = options.maxValueBytes ?? defaultMaxValueBytes;
|
|
5
|
+
const maxDepth = options.maxDepth ?? defaultMaxDepth;
|
|
6
|
+
const redactions = new Set((options.redact ?? []).map((entry) => entry.trim().toLowerCase()));
|
|
7
|
+
let redacted = false;
|
|
8
|
+
let truncated = false;
|
|
9
|
+
const seen = new WeakSet();
|
|
10
|
+
const encode = (current, depth, path) => {
|
|
11
|
+
if (depth > maxDepth) {
|
|
12
|
+
truncated = true;
|
|
13
|
+
return '[Truncated]';
|
|
14
|
+
}
|
|
15
|
+
if (current === null)
|
|
16
|
+
return null;
|
|
17
|
+
switch (typeof current) {
|
|
18
|
+
case 'boolean':
|
|
19
|
+
case 'number':
|
|
20
|
+
case 'string':
|
|
21
|
+
return current;
|
|
22
|
+
case 'undefined':
|
|
23
|
+
return { $type: 'undefined' };
|
|
24
|
+
case 'bigint':
|
|
25
|
+
return { $type: 'bigint', value: current.toString() };
|
|
26
|
+
case 'function':
|
|
27
|
+
case 'symbol':
|
|
28
|
+
truncated = true;
|
|
29
|
+
return `[${typeof current}]`;
|
|
30
|
+
case 'object':
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
if (current instanceof Date) {
|
|
34
|
+
return { $type: 'date', value: current.toISOString() };
|
|
35
|
+
}
|
|
36
|
+
if (current instanceof Uint8Array) {
|
|
37
|
+
return { $type: 'bytes', base64: bytesToBase64(current) };
|
|
38
|
+
}
|
|
39
|
+
if (seen.has(current)) {
|
|
40
|
+
truncated = true;
|
|
41
|
+
return '[Circular]';
|
|
42
|
+
}
|
|
43
|
+
seen.add(current);
|
|
44
|
+
try {
|
|
45
|
+
if (Array.isArray(current)) {
|
|
46
|
+
const output = [];
|
|
47
|
+
const descriptors = Object.getOwnPropertyDescriptors(current);
|
|
48
|
+
for (let index = 0; index < current.length; index += 1) {
|
|
49
|
+
const descriptor = descriptors[String(index)];
|
|
50
|
+
if (!descriptor) {
|
|
51
|
+
output.length = index + 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!('value' in descriptor)) {
|
|
55
|
+
truncated = true;
|
|
56
|
+
output[index] = '[Accessor]';
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
output[index] = encode(descriptor.value, depth + 1, [
|
|
60
|
+
...path,
|
|
61
|
+
String(index),
|
|
62
|
+
]);
|
|
63
|
+
}
|
|
64
|
+
return output;
|
|
65
|
+
}
|
|
66
|
+
if (current instanceof Map) {
|
|
67
|
+
return {
|
|
68
|
+
$type: 'map',
|
|
69
|
+
entries: [...current.entries()].map(([key, item], index) => [
|
|
70
|
+
encode(key, depth + 1, [...path, String(index), 'key']),
|
|
71
|
+
encode(item, depth + 1, [...path, String(index), 'value']),
|
|
72
|
+
]),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (current instanceof Set) {
|
|
76
|
+
return {
|
|
77
|
+
$type: 'set',
|
|
78
|
+
values: [...current.values()].map((item, index) => encode(item, depth + 1, [...path, String(index)])),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const output = {};
|
|
82
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(current))) {
|
|
83
|
+
if (!descriptor.enumerable)
|
|
84
|
+
continue;
|
|
85
|
+
const nextPath = [...path, key];
|
|
86
|
+
if (shouldRedact(key, nextPath, redactions)) {
|
|
87
|
+
redacted = true;
|
|
88
|
+
output[key] = { $type: 'redacted' };
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!('value' in descriptor)) {
|
|
92
|
+
truncated = true;
|
|
93
|
+
output[key] = '[Accessor]';
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
output[key] = encode(descriptor.value, depth + 1, nextPath);
|
|
97
|
+
}
|
|
98
|
+
return output;
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
seen.delete(current);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
try {
|
|
105
|
+
const encoded = encode(value, 0, []);
|
|
106
|
+
const serialized = JSON.stringify(encoded);
|
|
107
|
+
const bytes = utf8ByteLength(serialized);
|
|
108
|
+
const preview = serialized.length > 512 ? `${serialized.slice(0, 509)}...` : serialized;
|
|
109
|
+
if (bytes > maxValueBytes) {
|
|
110
|
+
return {
|
|
111
|
+
preview,
|
|
112
|
+
truncated: true,
|
|
113
|
+
...(redacted ? { redacted: true } : {}),
|
|
114
|
+
type: valueType(value),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
value: encoded,
|
|
119
|
+
preview,
|
|
120
|
+
...(truncated ? { truncated: true } : {}),
|
|
121
|
+
...(redacted ? { redacted: true } : {}),
|
|
122
|
+
type: valueType(value),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
const type = safeValueType(value);
|
|
127
|
+
return {
|
|
128
|
+
preview: `[${type}]`,
|
|
129
|
+
truncated: true,
|
|
130
|
+
type,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function shouldRedact(key, path, redactions) {
|
|
135
|
+
if (redactions.size === 0)
|
|
136
|
+
return false;
|
|
137
|
+
const normalizedKey = key.toLowerCase();
|
|
138
|
+
const normalizedPath = path.join('.').toLowerCase();
|
|
139
|
+
return redactions.has(normalizedKey) || redactions.has(normalizedPath);
|
|
140
|
+
}
|
|
141
|
+
function valueType(value) {
|
|
142
|
+
if (value === null)
|
|
143
|
+
return 'null';
|
|
144
|
+
if (Array.isArray(value))
|
|
145
|
+
return 'array';
|
|
146
|
+
if (value instanceof Date)
|
|
147
|
+
return 'date';
|
|
148
|
+
if (value instanceof Uint8Array)
|
|
149
|
+
return 'bytes';
|
|
150
|
+
if (value instanceof Map)
|
|
151
|
+
return 'map';
|
|
152
|
+
if (value instanceof Set)
|
|
153
|
+
return 'set';
|
|
154
|
+
return typeof value === 'object' ? 'object' : typeof value;
|
|
155
|
+
}
|
|
156
|
+
function safeValueType(value) {
|
|
157
|
+
try {
|
|
158
|
+
return valueType(value);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return typeof value;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function utf8ByteLength(value) {
|
|
165
|
+
return new TextEncoder().encode(value).byteLength;
|
|
166
|
+
}
|
|
167
|
+
function bytesToBase64(value) {
|
|
168
|
+
let binary = '';
|
|
169
|
+
for (const byte of value)
|
|
170
|
+
binary += String.fromCharCode(byte);
|
|
171
|
+
const encoder = globalThis.btoa;
|
|
172
|
+
if (typeof encoder === 'function')
|
|
173
|
+
return encoder(binary);
|
|
174
|
+
return binary;
|
|
175
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LOUTRE_VERSION = "0.
|
|
1
|
+
export declare const LOUTRE_VERSION = "0.7.0";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// package manifestとのずれを避けるため、scripts/generate-version.mjsがbuild前に更新する。
|
|
2
|
-
export const LOUTRE_VERSION = '0.
|
|
2
|
+
export const LOUTRE_VERSION = '0.7.0';
|
package/dist/http/extension.js
CHANGED
|
@@ -409,7 +409,12 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
409
409
|
}
|
|
410
410
|
let lease;
|
|
411
411
|
try {
|
|
412
|
-
lease = applicationRuntime.beginExecution(
|
|
412
|
+
lease = applicationRuntime.beginExecution({
|
|
413
|
+
executionId: match.executionId,
|
|
414
|
+
executionKind: 'http.request',
|
|
415
|
+
graphNodeId: match.executionId,
|
|
416
|
+
name: `OPTIONS ${match.route.path}`,
|
|
417
|
+
});
|
|
413
418
|
}
|
|
414
419
|
catch (error) {
|
|
415
420
|
completePendingIngress();
|
|
@@ -418,6 +423,7 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
418
423
|
try {
|
|
419
424
|
const headers = await createCorsPreflightResponseHeaders(match.route.middlewares, request, match.route.method);
|
|
420
425
|
if (headers) {
|
|
426
|
+
lease.annotate?.({ 'http.status_code': 204 });
|
|
421
427
|
return new Response(null, { status: 204, headers });
|
|
422
428
|
}
|
|
423
429
|
}
|
|
@@ -445,7 +451,30 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
445
451
|
return Response.json({ error: 'Internal Server Error' }, { status: 500 });
|
|
446
452
|
}
|
|
447
453
|
if (!match) {
|
|
448
|
-
|
|
454
|
+
const completePendingIngress = ingress.enter();
|
|
455
|
+
if (!completePendingIngress) {
|
|
456
|
+
return Response.json({ error: 'Service Unavailable' }, { status: 503 });
|
|
457
|
+
}
|
|
458
|
+
let lease;
|
|
459
|
+
try {
|
|
460
|
+
lease = applicationRuntime.beginExecution({
|
|
461
|
+
executionId: 'http.not-found',
|
|
462
|
+
executionKind: 'http.request',
|
|
463
|
+
name: `${method} ${url.pathname}`,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
completePendingIngress();
|
|
468
|
+
throw error;
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
lease.annotate?.({ 'http.status_code': 404 });
|
|
472
|
+
return Response.json({ error: 'Not Found' }, { status: 404 });
|
|
473
|
+
}
|
|
474
|
+
finally {
|
|
475
|
+
lease.complete();
|
|
476
|
+
completePendingIngress();
|
|
477
|
+
}
|
|
449
478
|
}
|
|
450
479
|
const completePendingIngress = ingress.enter();
|
|
451
480
|
if (!completePendingIngress) {
|
|
@@ -453,7 +482,12 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
453
482
|
}
|
|
454
483
|
let lease;
|
|
455
484
|
try {
|
|
456
|
-
lease = applicationRuntime.beginExecution(
|
|
485
|
+
lease = applicationRuntime.beginExecution({
|
|
486
|
+
executionId: match.executionId,
|
|
487
|
+
executionKind: 'http.request',
|
|
488
|
+
graphNodeId: match.executionId,
|
|
489
|
+
name: `${match.route.method} ${match.route.path}`,
|
|
490
|
+
});
|
|
457
491
|
}
|
|
458
492
|
catch (error) {
|
|
459
493
|
completePendingIngress();
|
|
@@ -462,12 +496,17 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
462
496
|
const abortRequest = () => lease.abort(request.signal.reason);
|
|
463
497
|
let executionOwnedByStream = false;
|
|
464
498
|
let executionFinished = false;
|
|
499
|
+
let executionResult;
|
|
500
|
+
let hasExecutionResult = false;
|
|
465
501
|
const finishExecution = () => {
|
|
466
502
|
if (executionFinished)
|
|
467
503
|
return;
|
|
468
504
|
executionFinished = true;
|
|
469
505
|
request.signal.removeEventListener('abort', abortRequest);
|
|
470
|
-
|
|
506
|
+
if (hasExecutionResult)
|
|
507
|
+
lease.complete(executionResult);
|
|
508
|
+
else
|
|
509
|
+
lease.complete();
|
|
471
510
|
};
|
|
472
511
|
const adoptResponseStream = (stream) => {
|
|
473
512
|
executionOwnedByStream = true;
|
|
@@ -486,9 +525,13 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
486
525
|
request.signal.addEventListener('abort', abortRequest, { once: true });
|
|
487
526
|
if (request.signal.aborted)
|
|
488
527
|
abortRequest();
|
|
528
|
+
const annotateResponse = (response) => {
|
|
529
|
+
lease.annotate?.({ 'http.status_code': response.status });
|
|
530
|
+
return response;
|
|
531
|
+
};
|
|
489
532
|
try {
|
|
490
533
|
const corsHeaders = await createCorsActualResponseHeaders(match.route.middlewares, request);
|
|
491
|
-
const complete = (response) => applyFrameworkHeadersToResponse(response, corsHeaders);
|
|
534
|
+
const complete = (response) => annotateResponse(applyFrameworkHeadersToResponse(response, corsHeaders));
|
|
492
535
|
let context;
|
|
493
536
|
try {
|
|
494
537
|
context = await createHttpContext(request, url, match.params, match.route, lease.signal);
|
|
@@ -509,17 +552,50 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
509
552
|
if (!handler) {
|
|
510
553
|
throw new Error(`LUTRE_HTTP_HANDLER_MISSING: ${match.executionId}.${match.route.name}`);
|
|
511
554
|
}
|
|
512
|
-
const
|
|
555
|
+
const dispatch = () => composeLayers({
|
|
513
556
|
context,
|
|
514
557
|
layers: match.route.middlewares,
|
|
515
558
|
resolve: (token, source) => applicationRuntime.resolve(token, source),
|
|
516
|
-
terminal: async (middlewareContext) =>
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
559
|
+
terminal: async (middlewareContext) => {
|
|
560
|
+
const handlerName = `${match.executionId}.${match.route.name}`;
|
|
561
|
+
const handlerOperation = applicationRuntime.beginOperation?.({
|
|
562
|
+
kind: 'http.handler',
|
|
563
|
+
name: handlerName,
|
|
564
|
+
graphNodeId: match.executionId,
|
|
565
|
+
}, {
|
|
566
|
+
input: middlewareContext.input,
|
|
567
|
+
invoke: (input) => replayHttpHandler(applicationRuntime, handler, match, input, middlewareContext.state),
|
|
568
|
+
});
|
|
569
|
+
const invoke = () => handler({
|
|
570
|
+
input: middlewareContext.input,
|
|
571
|
+
response: middlewareContext.response,
|
|
572
|
+
signal: middlewareContext.signal,
|
|
573
|
+
state: middlewareContext.state,
|
|
574
|
+
});
|
|
575
|
+
let result;
|
|
576
|
+
let hasResult = false;
|
|
577
|
+
try {
|
|
578
|
+
result = await (handlerOperation?.run
|
|
579
|
+
? handlerOperation.run(invoke)
|
|
580
|
+
: invoke());
|
|
581
|
+
hasResult = true;
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
handlerOperation?.fail?.(error);
|
|
586
|
+
throw error;
|
|
587
|
+
}
|
|
588
|
+
finally {
|
|
589
|
+
if (hasResult)
|
|
590
|
+
handlerOperation?.complete(result);
|
|
591
|
+
else
|
|
592
|
+
handlerOperation?.complete();
|
|
593
|
+
}
|
|
594
|
+
},
|
|
522
595
|
});
|
|
596
|
+
const result = await (lease.run ? lease.run(dispatch) : dispatch());
|
|
597
|
+
executionResult = result;
|
|
598
|
+
hasExecutionResult = true;
|
|
523
599
|
const finalized = await finalizeHttpResult(match.route.definition, result, lease.signal, finishExecution);
|
|
524
600
|
if (finalized.streamControl)
|
|
525
601
|
adoptResponseStream(finalized.streamControl);
|
|
@@ -527,11 +603,12 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
527
603
|
}
|
|
528
604
|
catch (error) {
|
|
529
605
|
if (error instanceof HttpInputDecodeError) {
|
|
530
|
-
return applyFrameworkHeadersToResponse(Response.json({ error: 'Invalid request' }, { status: 400 }), await safeCorsHeaders(match.route.middlewares, request));
|
|
606
|
+
return annotateResponse(applyFrameworkHeadersToResponse(Response.json({ error: 'Invalid request' }, { status: 400 }), await safeCorsHeaders(match.route.middlewares, request)));
|
|
531
607
|
}
|
|
532
608
|
if (error instanceof HttpInputValidationError) {
|
|
533
|
-
return applyFrameworkHeadersToResponse(Response.json({ error: 'Validation failed' }, { status: 400 }), await safeCorsHeaders(match.route.middlewares, request));
|
|
609
|
+
return annotateResponse(applyFrameworkHeadersToResponse(Response.json({ error: 'Validation failed' }, { status: 400 }), await safeCorsHeaders(match.route.middlewares, request)));
|
|
534
610
|
}
|
|
611
|
+
lease.fail?.(error);
|
|
535
612
|
try {
|
|
536
613
|
const mapped = await mapDeclaredError(match.route.definition, error);
|
|
537
614
|
if (mapped) {
|
|
@@ -539,13 +616,13 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
539
616
|
if (finalized.streamControl) {
|
|
540
617
|
adoptResponseStream(finalized.streamControl);
|
|
541
618
|
}
|
|
542
|
-
return applyFrameworkHeadersToResponse(finalized.response, await safeCorsHeaders(match.route.middlewares, request));
|
|
619
|
+
return annotateResponse(applyFrameworkHeadersToResponse(finalized.response, await safeCorsHeaders(match.route.middlewares, request)));
|
|
543
620
|
}
|
|
544
621
|
}
|
|
545
622
|
catch {
|
|
546
623
|
// 内部のmapping/finalization errorをclientへ公開しない。
|
|
547
624
|
}
|
|
548
|
-
return applyFrameworkHeadersToResponse(Response.json({ error: 'Internal Server Error' }, { status: 500 }), await safeCorsHeaders(match.route.middlewares, request));
|
|
625
|
+
return annotateResponse(applyFrameworkHeadersToResponse(Response.json({ error: 'Internal Server Error' }, { status: 500 }), await safeCorsHeaders(match.route.middlewares, request)));
|
|
549
626
|
}
|
|
550
627
|
finally {
|
|
551
628
|
if (!executionOwnedByStream)
|
|
@@ -595,7 +672,16 @@ async function createHttpContext(request, url, rawParams, route, signal) {
|
|
|
595
672
|
? await validateRequestHeaders(definition.headers, definition.body !== undefined, request.headers)
|
|
596
673
|
: Object.fromEntries(request.headers.entries());
|
|
597
674
|
const body = definition?.body ? request.body : undefined;
|
|
598
|
-
const response =
|
|
675
|
+
const response = createHttpResponseHelpers(route.definition.responses);
|
|
676
|
+
return {
|
|
677
|
+
request,
|
|
678
|
+
input: { params, query, headers, body },
|
|
679
|
+
response,
|
|
680
|
+
signal,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
function createHttpResponseHelpers(responses) {
|
|
684
|
+
return Object.fromEntries(Object.keys(responses).map((name) => [
|
|
599
685
|
name,
|
|
600
686
|
(value = {}) => ({
|
|
601
687
|
kind: 'http-result',
|
|
@@ -604,12 +690,40 @@ async function createHttpContext(request, url, rawParams, route, signal) {
|
|
|
604
690
|
...(value.headers === undefined ? {} : { headers: value.headers }),
|
|
605
691
|
}),
|
|
606
692
|
]));
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
693
|
+
}
|
|
694
|
+
async function replayHttpHandler(applicationRuntime, handler, match, input, state) {
|
|
695
|
+
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
696
|
+
throw new TypeError('LUTRE_DEVTOOLS_HTTP_HANDLER_INPUT_REQUIRED');
|
|
697
|
+
}
|
|
698
|
+
const lease = applicationRuntime.beginExecution({
|
|
699
|
+
executionId: match.executionId,
|
|
700
|
+
executionKind: 'http.handler',
|
|
701
|
+
graphNodeId: match.executionId,
|
|
702
|
+
name: `${match.executionId}.${match.route.name}`,
|
|
703
|
+
});
|
|
704
|
+
const invoke = () => handler({
|
|
705
|
+
input,
|
|
706
|
+
response: createHttpResponseHelpers(match.route.definition.responses),
|
|
707
|
+
signal: lease.signal,
|
|
708
|
+
state,
|
|
709
|
+
});
|
|
710
|
+
let result;
|
|
711
|
+
let hasResult = false;
|
|
712
|
+
try {
|
|
713
|
+
result = await (lease.run ? lease.run(invoke) : invoke());
|
|
714
|
+
hasResult = true;
|
|
715
|
+
return result;
|
|
716
|
+
}
|
|
717
|
+
catch (error) {
|
|
718
|
+
lease.fail?.(error);
|
|
719
|
+
throw error;
|
|
720
|
+
}
|
|
721
|
+
finally {
|
|
722
|
+
if (hasResult)
|
|
723
|
+
lease.complete(result);
|
|
724
|
+
else
|
|
725
|
+
lease.complete();
|
|
726
|
+
}
|
|
613
727
|
}
|
|
614
728
|
async function finalizeHttpResult(route, result, signal, completeExecution) {
|
|
615
729
|
const response = route.responses[result.response];
|
|
@@ -97,8 +97,13 @@ function createMessagePortRuntime(executions, applicationRuntime) {
|
|
|
97
97
|
}, () => execution.compiled.factory());
|
|
98
98
|
for (const [method, definition] of Object.entries(execution.compiled.routes)) {
|
|
99
99
|
const handler = handlers[method];
|
|
100
|
-
if (handler)
|
|
101
|
-
routes.set(method, {
|
|
100
|
+
if (handler) {
|
|
101
|
+
routes.set(method, {
|
|
102
|
+
executionId: execution.id,
|
|
103
|
+
route: definition,
|
|
104
|
+
handler,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
return {
|
|
@@ -114,7 +119,12 @@ function createMessagePortRuntime(executions, applicationRuntime) {
|
|
|
114
119
|
}
|
|
115
120
|
let lease;
|
|
116
121
|
try {
|
|
117
|
-
lease = applicationRuntime.beginExecution(
|
|
122
|
+
lease = applicationRuntime.beginExecution({
|
|
123
|
+
executionId: route.executionId,
|
|
124
|
+
executionKind: 'message-port.invocation',
|
|
125
|
+
graphNodeId: route.executionId,
|
|
126
|
+
name: method,
|
|
127
|
+
});
|
|
118
128
|
}
|
|
119
129
|
catch (error) {
|
|
120
130
|
completePendingIngress();
|
|
@@ -133,11 +143,12 @@ function createMessagePortRuntime(executions, applicationRuntime) {
|
|
|
133
143
|
value: result,
|
|
134
144
|
}),
|
|
135
145
|
]));
|
|
136
|
-
const
|
|
146
|
+
const invoke = () => route.handler({
|
|
137
147
|
input: value,
|
|
138
148
|
response,
|
|
139
149
|
signal: lease.signal,
|
|
140
150
|
});
|
|
151
|
+
const result = await (lease.run ? lease.run(invoke) : invoke());
|
|
141
152
|
const schema = route.route.responses[result.response];
|
|
142
153
|
if (!schema) {
|
|
143
154
|
throw new Error(`LUTRE_MESSAGE_PORT_RESPONSE_UNDECLARED: ${result.response}`);
|
|
@@ -155,6 +166,10 @@ function createMessagePortRuntime(executions, applicationRuntime) {
|
|
|
155
166
|
value: await validateSchema(schema, result.value),
|
|
156
167
|
};
|
|
157
168
|
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
lease.fail?.(error);
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
158
173
|
finally {
|
|
159
174
|
if (!executionOwnedByStream)
|
|
160
175
|
lease.complete();
|
|
@@ -290,6 +305,7 @@ function createLeasedMessagePortStream(schema, source, lease, activeStreams) {
|
|
|
290
305
|
else
|
|
291
306
|
lease.signal.addEventListener('abort', onAbort, { once: true });
|
|
292
307
|
const closeAfterError = async (error) => {
|
|
308
|
+
lease.fail?.(error);
|
|
293
309
|
let cleanupError;
|
|
294
310
|
try {
|
|
295
311
|
await startIteratorCleanup(error);
|
package/dist/runtime/di.d.ts
CHANGED
|
@@ -3,8 +3,12 @@ import { Logger } from './logger.js';
|
|
|
3
3
|
export declare class DependencyResolutionError extends Error {
|
|
4
4
|
constructor(message: string);
|
|
5
5
|
}
|
|
6
|
+
export interface ContainerHooks {
|
|
7
|
+
created?(provider: ProviderDescriptor, value: unknown): void;
|
|
8
|
+
}
|
|
6
9
|
export interface ContainerOptions {
|
|
7
10
|
readonly logger?: Logger;
|
|
11
|
+
readonly hooks?: ContainerHooks;
|
|
8
12
|
readonly environment?: ReadonlyMap<EnvClass, object>;
|
|
9
13
|
readonly arguments?: ReadonlyMap<ArgsClass, object>;
|
|
10
14
|
}
|
package/dist/runtime/di.js
CHANGED
|
@@ -10,11 +10,13 @@ export class Container {
|
|
|
10
10
|
#providers = new Map();
|
|
11
11
|
#applicationCache = new Map();
|
|
12
12
|
#logger;
|
|
13
|
+
#hooks;
|
|
13
14
|
#environment = new Map();
|
|
14
15
|
#arguments = new Map();
|
|
15
16
|
constructor(providers, options = {}) {
|
|
16
17
|
this.#logger =
|
|
17
18
|
options instanceof Logger ? options : (options.logger ?? new Logger());
|
|
19
|
+
this.#hooks = options instanceof Logger ? {} : (options.hooks ?? {});
|
|
18
20
|
if (!(options instanceof Logger)) {
|
|
19
21
|
for (const [environment, value] of options.environment ?? []) {
|
|
20
22
|
this.#environment.set(environment, value);
|
|
@@ -132,8 +134,10 @@ export class Container {
|
|
|
132
134
|
#create(provider, lineage) {
|
|
133
135
|
try {
|
|
134
136
|
switch (provider.kind) {
|
|
135
|
-
case 'value':
|
|
137
|
+
case 'value': {
|
|
138
|
+
this.#hooks.created?.(provider, provider.useValue);
|
|
136
139
|
return provider.useValue;
|
|
140
|
+
}
|
|
137
141
|
case 'environment': {
|
|
138
142
|
if (this.#environment.has(provider.provide)) {
|
|
139
143
|
return this.#environment.get(provider.provide);
|
|
@@ -146,14 +150,18 @@ export class Container {
|
|
|
146
150
|
}
|
|
147
151
|
throw new DependencyResolutionError(`LUTRE_ARGS_005: Arguments ${provider.provide.name} requires runtime Arguments before Application initialization.`);
|
|
148
152
|
}
|
|
149
|
-
case 'class':
|
|
150
|
-
|
|
153
|
+
case 'class': {
|
|
154
|
+
const value = this.#instantiate(provider.useClass, lineage);
|
|
155
|
+
this.#hooks.created?.(provider, value);
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
151
158
|
case 'factory': {
|
|
152
159
|
const dependencies = provider.inject.map((token) => this.#resolve(token, tokenName(provider.provide), lineage));
|
|
153
160
|
const value = provider.useFactory(...dependencies);
|
|
154
161
|
if (isThenable(value)) {
|
|
155
162
|
throw new DependencyResolutionError('LUTRE_DI_ASYNC_FACTORY: Async factory providers are not supported. Move asynchronous resource initialization to application lifecycle.');
|
|
156
163
|
}
|
|
164
|
+
this.#hooks.created?.(provider, value);
|
|
157
165
|
return value;
|
|
158
166
|
}
|
|
159
167
|
case 'conditional': {
|
|
@@ -163,7 +171,9 @@ export class Container {
|
|
|
163
171
|
if (!implementation) {
|
|
164
172
|
throw new DependencyResolutionError(`No conditional Provider matches ${provider.select.key}=${String(selected)}`);
|
|
165
173
|
}
|
|
166
|
-
|
|
174
|
+
const value = this.#instantiate(implementation, lineage);
|
|
175
|
+
this.#hooks.created?.(provider, value);
|
|
176
|
+
return value;
|
|
167
177
|
}
|
|
168
178
|
}
|
|
169
179
|
}
|
package/dist/runtime/kernel.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RuntimeCapabilityRegistry, type ApplicationModel, type ExecutionExtension, type ExecutionExtensionRuntime, type ExecutionKernelRuntime, type ExecutionLease, type RuntimeCapabilityBinding, type TokenLike } from '../core/index.js';
|
|
1
|
+
import { RuntimeCapabilityRegistry, type ApplicationModel, type ExecutionExtension, type ExecutionExtensionRuntime, type ExecutionKernelRuntime, type ExecutionLease, type ExecutionOperationLease, type RuntimeCapabilityBinding, type RuntimeExecutionMetadata, type RuntimeInstrumentation, type RuntimeInvocationRegistration, type RuntimeOperationMetadata, type TokenLike } from '../core/index.js';
|
|
2
2
|
import { Container } from './di.js';
|
|
3
3
|
import { Logger } from './logger.js';
|
|
4
4
|
export interface ApplicationKernelRuntimeOptions {
|
|
@@ -7,6 +7,7 @@ export interface ApplicationKernelRuntimeOptions {
|
|
|
7
7
|
readonly environmentSource?: unknown;
|
|
8
8
|
readonly argumentsSource?: unknown;
|
|
9
9
|
readonly forceShutdownTimeoutMs?: number;
|
|
10
|
+
readonly instrumentation?: RuntimeInstrumentation;
|
|
10
11
|
}
|
|
11
12
|
export declare class ApplicationKernelRuntime implements ExecutionKernelRuntime {
|
|
12
13
|
#private;
|
|
@@ -18,6 +19,7 @@ export declare class ApplicationKernelRuntime implements ExecutionKernelRuntime
|
|
|
18
19
|
resolve<TValue>(token: TokenLike<TValue>, source?: string): TValue;
|
|
19
20
|
get<TValue>(token: TokenLike<TValue>): TValue;
|
|
20
21
|
extensionRuntime<TRuntime extends ExecutionExtensionRuntime>(extension: ExecutionExtension<any, any, any, any, TRuntime>): TRuntime;
|
|
21
|
-
beginExecution(): ExecutionLease;
|
|
22
|
+
beginExecution(metadata?: RuntimeExecutionMetadata, invocation?: RuntimeInvocationRegistration): ExecutionLease;
|
|
23
|
+
beginOperation(metadata: RuntimeOperationMetadata, invocation?: RuntimeInvocationRegistration): ExecutionOperationLease;
|
|
22
24
|
shutdown(signal?: string): Promise<void>;
|
|
23
25
|
}
|