@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
package/dist/runtime/kernel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertValidApplicationModel, loadArgs, loadEnv, RuntimeCapabilityRegistry, } from '../core/index.js';
|
|
1
|
+
import { assertValidApplicationModel, loadArgs, loadEnv, RuntimeCapabilityRegistry, tokenName, } from '../core/index.js';
|
|
2
2
|
import { Container } from './di.js';
|
|
3
3
|
import { ExecutionTracker } from './execution-tracker.js';
|
|
4
4
|
import { Logger } from './logger.js';
|
|
@@ -9,29 +9,39 @@ export class ApplicationKernelRuntime {
|
|
|
9
9
|
capabilities;
|
|
10
10
|
#extensionRuntimes = new Map();
|
|
11
11
|
#providerInstances = new Map();
|
|
12
|
+
#providerNodes = new Map();
|
|
12
13
|
#initializedModuleIds = new Set();
|
|
13
14
|
#executions = new ExecutionTracker();
|
|
14
15
|
#extensionDrainOperations = new Map();
|
|
15
16
|
#environmentSource;
|
|
16
17
|
#argumentsSource;
|
|
17
18
|
#forceShutdownTimeoutMs;
|
|
19
|
+
#instrumentation;
|
|
18
20
|
#state = 'created';
|
|
19
21
|
#initialization;
|
|
20
22
|
#shutdown;
|
|
21
23
|
constructor(model, options = {}) {
|
|
22
24
|
this.model = assertValidApplicationModel(model);
|
|
25
|
+
for (const node of this.model.nodes) {
|
|
26
|
+
if (node.kind === 'provider')
|
|
27
|
+
this.#providerNodes.set(node.provider, node);
|
|
28
|
+
}
|
|
23
29
|
this.capabilities = new RuntimeCapabilityRegistry(options.capabilities ?? []);
|
|
24
30
|
this.#environmentSource = options.environmentSource;
|
|
25
31
|
this.#argumentsSource =
|
|
26
32
|
'argumentsSource' in options ? options.argumentsSource : Object.freeze({});
|
|
27
33
|
this.#forceShutdownTimeoutMs =
|
|
28
34
|
options.forceShutdownTimeoutMs ?? defaultForceShutdownTimeoutMs;
|
|
35
|
+
this.#instrumentation = options.instrumentation;
|
|
29
36
|
if (!Number.isFinite(this.#forceShutdownTimeoutMs) ||
|
|
30
37
|
this.#forceShutdownTimeoutMs < 0) {
|
|
31
38
|
throw new TypeError('LUTRE_FORCE_SHUTDOWN_TIMEOUT: forceShutdownTimeoutMs must be a non-negative finite number.');
|
|
32
39
|
}
|
|
33
40
|
this.container = new Container(model.providers, {
|
|
34
41
|
logger: options.logger ?? new Logger(),
|
|
42
|
+
hooks: {
|
|
43
|
+
created: (provider, value) => this.#notifyProviderCreated(provider, value),
|
|
44
|
+
},
|
|
35
45
|
});
|
|
36
46
|
}
|
|
37
47
|
initialize() {
|
|
@@ -149,10 +159,48 @@ export class ApplicationKernelRuntime {
|
|
|
149
159
|
}
|
|
150
160
|
return runtime;
|
|
151
161
|
}
|
|
152
|
-
beginExecution() {
|
|
162
|
+
beginExecution(metadata, invocation) {
|
|
153
163
|
if (this.#state !== 'running')
|
|
154
164
|
throw applicationStateError(this.#state);
|
|
155
|
-
|
|
165
|
+
const tracked = this.#executions.begin();
|
|
166
|
+
if (metadata === undefined)
|
|
167
|
+
return tracked;
|
|
168
|
+
const scope = safely(() => this.#instrumentation?.beginExecution?.(metadata, invocation));
|
|
169
|
+
if (!scope)
|
|
170
|
+
return tracked;
|
|
171
|
+
const instrumented = instrumentationLease(scope);
|
|
172
|
+
return {
|
|
173
|
+
signal: tracked.signal,
|
|
174
|
+
run: instrumented.run,
|
|
175
|
+
abort(reason) {
|
|
176
|
+
callInstrumentation(() => scope.abort?.(reason));
|
|
177
|
+
tracked.abort(reason);
|
|
178
|
+
},
|
|
179
|
+
fail: instrumented.fail,
|
|
180
|
+
annotate: instrumented.annotate,
|
|
181
|
+
complete(...result) {
|
|
182
|
+
instrumented.complete(...result);
|
|
183
|
+
tracked.complete();
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
beginOperation(metadata, invocation) {
|
|
188
|
+
return instrumentationLease(safely(() => this.#instrumentation?.beginOperation?.(metadata, invocation)));
|
|
189
|
+
}
|
|
190
|
+
#notifyProviderCreated(provider, value) {
|
|
191
|
+
const instrumentation = this.#instrumentation;
|
|
192
|
+
if (!instrumentation?.providerCreated)
|
|
193
|
+
return;
|
|
194
|
+
const providerNode = this.#providerNodes.get(provider);
|
|
195
|
+
if (!providerNode)
|
|
196
|
+
return;
|
|
197
|
+
callInstrumentation(() => instrumentation.providerCreated({
|
|
198
|
+
providerId: providerNode.id,
|
|
199
|
+
graphNodeId: providerNode.id,
|
|
200
|
+
name: tokenName(providerNode.token),
|
|
201
|
+
scope: provider.scope,
|
|
202
|
+
providerKind: provider.kind,
|
|
203
|
+
}, value));
|
|
156
204
|
}
|
|
157
205
|
shutdown(signal) {
|
|
158
206
|
if (this.#state === 'stopped')
|
|
@@ -171,6 +219,7 @@ export class ApplicationKernelRuntime {
|
|
|
171
219
|
await this.#initialization.catch(() => undefined);
|
|
172
220
|
if (this.#state === 'created') {
|
|
173
221
|
this.#state = 'stopped';
|
|
222
|
+
await closeInstrumentation(this.#instrumentation);
|
|
174
223
|
return;
|
|
175
224
|
}
|
|
176
225
|
if (this.#state === 'stopped')
|
|
@@ -225,6 +274,7 @@ export class ApplicationKernelRuntime {
|
|
|
225
274
|
this.#extensionDrainOperations.clear();
|
|
226
275
|
await this.#cleanupProviders(signal, errors);
|
|
227
276
|
this.#state = 'stopped';
|
|
277
|
+
await closeInstrumentation(this.#instrumentation);
|
|
228
278
|
if (errors.length > 0) {
|
|
229
279
|
throw new AggregateError(errors, 'Application shutdown failed.');
|
|
230
280
|
}
|
|
@@ -281,6 +331,7 @@ export class ApplicationKernelRuntime {
|
|
|
281
331
|
await collectError(() => callLifecycle(instance, 'onModuleDestroy'), errors);
|
|
282
332
|
}
|
|
283
333
|
}
|
|
334
|
+
await closeInstrumentation(this.#instrumentation);
|
|
284
335
|
return errors;
|
|
285
336
|
}
|
|
286
337
|
async #runHook(hook) {
|
|
@@ -306,6 +357,48 @@ function lifecycleHookOf(model, moduleId, phase) {
|
|
|
306
357
|
node.moduleId === moduleId &&
|
|
307
358
|
node.phase === phase)?.hook;
|
|
308
359
|
}
|
|
360
|
+
function instrumentationLease(scope) {
|
|
361
|
+
return {
|
|
362
|
+
run(operation) {
|
|
363
|
+
if (!scope?.run)
|
|
364
|
+
return operation();
|
|
365
|
+
return scope.run(operation);
|
|
366
|
+
},
|
|
367
|
+
fail(reason) {
|
|
368
|
+
callInstrumentation(() => scope?.fail?.(reason));
|
|
369
|
+
},
|
|
370
|
+
annotate(attributes) {
|
|
371
|
+
callInstrumentation(() => scope?.annotate?.(attributes));
|
|
372
|
+
},
|
|
373
|
+
complete(...result) {
|
|
374
|
+
callInstrumentation(() => scope?.complete(...result));
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function safely(operation) {
|
|
379
|
+
try {
|
|
380
|
+
return operation();
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function callInstrumentation(operation) {
|
|
387
|
+
try {
|
|
388
|
+
operation();
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
// instrumentation障害をApplication semanticsへ伝播させてはならない。
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async function closeInstrumentation(instrumentation) {
|
|
395
|
+
try {
|
|
396
|
+
await instrumentation?.close?.();
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// instrumentationのcleanup失敗をshutdown失敗として扱わない。
|
|
400
|
+
}
|
|
401
|
+
}
|
|
309
402
|
async function withTimeout(operation, timeoutMs, createTimeoutError) {
|
|
310
403
|
let timer;
|
|
311
404
|
try {
|
package/dist/tasks/extension.js
CHANGED
|
@@ -238,6 +238,7 @@ export function consume(declaration) {
|
|
|
238
238
|
}
|
|
239
239
|
function createTasksRuntime(executions, applicationRuntime) {
|
|
240
240
|
const runtimes = new Map();
|
|
241
|
+
const runtimeExecutionIds = new Map();
|
|
241
242
|
const triggers = [];
|
|
242
243
|
for (const execution of executions) {
|
|
243
244
|
const compiled = execution.compiled;
|
|
@@ -254,6 +255,7 @@ function createTasksRuntime(executions, applicationRuntime) {
|
|
|
254
255
|
resolve: (dependency) => applicationRuntime.resolve(dependency, execution.id),
|
|
255
256
|
}, () => compiled.factory());
|
|
256
257
|
runtimes.set(compiled.taskExecutionId, runtime);
|
|
258
|
+
runtimeExecutionIds.set(compiled.taskExecutionId, execution.id);
|
|
257
259
|
}
|
|
258
260
|
let triggerHandles = [];
|
|
259
261
|
let triggerStartup;
|
|
@@ -261,7 +263,7 @@ function createTasksRuntime(executions, applicationRuntime) {
|
|
|
261
263
|
let triggersStarted = false;
|
|
262
264
|
let state = 'running';
|
|
263
265
|
const activeInvocations = new Set();
|
|
264
|
-
const
|
|
266
|
+
const invokeTask = (taskExecutionId, taskName, arguments_) => {
|
|
265
267
|
if (state !== 'running') {
|
|
266
268
|
return Promise.reject(new Error(`LUTRE_TASKS_${state.toUpperCase()}`));
|
|
267
269
|
}
|
|
@@ -270,18 +272,42 @@ function createTasksRuntime(executions, applicationRuntime) {
|
|
|
270
272
|
return Promise.reject(new Error(`LUTRE_TASK_NOT_REGISTERED: ${taskName}`));
|
|
271
273
|
}
|
|
272
274
|
const invocation = (async () => {
|
|
273
|
-
const
|
|
275
|
+
const executionId = runtimeExecutionIds.get(taskExecutionId) ?? taskName;
|
|
276
|
+
const hasInput = arguments_.length > 0;
|
|
277
|
+
const input = hasInput ? arguments_[0] : undefined;
|
|
278
|
+
const lease = applicationRuntime.beginExecution({
|
|
279
|
+
executionId,
|
|
280
|
+
executionKind: 'task.invocation',
|
|
281
|
+
graphNodeId: executionId,
|
|
282
|
+
name: taskName,
|
|
283
|
+
}, {
|
|
284
|
+
input,
|
|
285
|
+
invoke: (nextInput) => invokeTask(taskExecutionId, taskName, hasInput ? [nextInput] : []),
|
|
286
|
+
});
|
|
287
|
+
let result;
|
|
288
|
+
let hasResult = false;
|
|
274
289
|
try {
|
|
275
|
-
|
|
290
|
+
const invoke = () => Reflect.apply(runtime, undefined, arguments_);
|
|
291
|
+
result = await (lease.run ? lease.run(invoke) : invoke());
|
|
292
|
+
hasResult = true;
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
lease.fail?.(error);
|
|
297
|
+
throw error;
|
|
276
298
|
}
|
|
277
299
|
finally {
|
|
278
|
-
|
|
300
|
+
if (hasResult)
|
|
301
|
+
lease.complete(result);
|
|
302
|
+
else
|
|
303
|
+
lease.complete();
|
|
279
304
|
}
|
|
280
305
|
})();
|
|
281
306
|
activeInvocations.add(invocation);
|
|
282
307
|
void invocation.then(() => activeInvocations.delete(invocation), () => activeInvocations.delete(invocation));
|
|
283
308
|
return invocation;
|
|
284
309
|
};
|
|
310
|
+
const run = (taskExecutionId, taskName, ...arguments_) => invokeTask(taskExecutionId, taskName, arguments_);
|
|
285
311
|
const stopTriggers = () => {
|
|
286
312
|
if (triggerStop)
|
|
287
313
|
return triggerStop;
|
|
@@ -240,7 +240,12 @@ function createWebSocketRuntime(executions, driver, applicationRuntime) {
|
|
|
240
240
|
}
|
|
241
241
|
let lease;
|
|
242
242
|
try {
|
|
243
|
-
lease = applicationRuntime.beginExecution(
|
|
243
|
+
lease = applicationRuntime.beginExecution({
|
|
244
|
+
executionId: match.executionId,
|
|
245
|
+
executionKind: 'websocket.session',
|
|
246
|
+
graphNodeId: match.executionId,
|
|
247
|
+
name: `WS ${match.route.path}`,
|
|
248
|
+
});
|
|
244
249
|
}
|
|
245
250
|
catch (error) {
|
|
246
251
|
completePendingIngress();
|
|
@@ -251,11 +256,12 @@ function createWebSocketRuntime(executions, driver, applicationRuntime) {
|
|
|
251
256
|
upgraded = await driver.upgrade(request);
|
|
252
257
|
}
|
|
253
258
|
catch (error) {
|
|
259
|
+
lease.fail?.(error);
|
|
254
260
|
lease.complete();
|
|
255
261
|
completePendingIngress();
|
|
256
262
|
throw error;
|
|
257
263
|
}
|
|
258
|
-
const session = createSession(upgraded.connection, match.route, input, lease, handlers.get(match.executionId)?.[match.route.name]);
|
|
264
|
+
const session = createSession(upgraded.connection, match.route, input, lease, handlers.get(match.executionId)?.[match.route.name], lease.run);
|
|
259
265
|
sessions.add(session.active);
|
|
260
266
|
void session.completion.then(() => sessions.delete(session.active), () => sessions.delete(session.active));
|
|
261
267
|
completePendingIngress();
|
|
@@ -341,7 +347,7 @@ function decodeQuery(searchParams) {
|
|
|
341
347
|
}
|
|
342
348
|
return query;
|
|
343
349
|
}
|
|
344
|
-
function createSession(connection, route, input, lease, handler) {
|
|
350
|
+
function createSession(connection, route, input, lease, handler, runInExecution) {
|
|
345
351
|
let state = 'open';
|
|
346
352
|
let transportFailed = false;
|
|
347
353
|
let sendTail = Promise.resolve();
|
|
@@ -352,6 +358,7 @@ function createSession(connection, route, input, lease, handler) {
|
|
|
352
358
|
return normalizeCloseInfo(info);
|
|
353
359
|
}, (error) => {
|
|
354
360
|
state = 'closed';
|
|
361
|
+
lease.fail?.(error);
|
|
355
362
|
lease.abort(error);
|
|
356
363
|
throw error;
|
|
357
364
|
});
|
|
@@ -406,11 +413,13 @@ function createSession(connection, route, input, lease, handler) {
|
|
|
406
413
|
if (!handler) {
|
|
407
414
|
throw new Error(`LUTRE_WEBSOCKET_HANDLER_MISSING: ${route.name}`);
|
|
408
415
|
}
|
|
409
|
-
|
|
416
|
+
const invoke = () => handler(context);
|
|
417
|
+
await (runInExecution ? runInExecution(invoke) : invoke());
|
|
410
418
|
if (state === 'open')
|
|
411
419
|
await close(1000, '');
|
|
412
420
|
}
|
|
413
|
-
catch {
|
|
421
|
+
catch (error) {
|
|
422
|
+
lease.fail?.(error);
|
|
414
423
|
if (state === 'open')
|
|
415
424
|
await close(1011, '');
|
|
416
425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loutrejs/loutre",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"types": "./dist/graph/index.d.ts",
|
|
21
21
|
"import": "./dist/graph/index.js"
|
|
22
22
|
},
|
|
23
|
+
"./devtools": {
|
|
24
|
+
"types": "./dist/devtools/index.d.ts",
|
|
25
|
+
"import": "./dist/devtools/index.js"
|
|
26
|
+
},
|
|
23
27
|
"./runtime": {
|
|
24
28
|
"types": "./dist/runtime/index.d.ts",
|
|
25
29
|
"import": "./dist/runtime/index.js"
|