@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.
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.disposable" preserve="true" />
2
- import type { RuntimeCapabilityBinding, TokenLike, TokenValue } from '../core/index.js';
2
+ import type { RuntimeCapabilityBinding, RuntimeInstrumentation, TokenLike, TokenValue } from '../core/index.js';
3
3
  import { type ApplicationModelGraphIR } from '../graph/index.js';
4
4
  import { type Logger } from '../runtime/index.js';
5
5
  import type { ApplicationDefinition, ApplicationExtensionHostApis, BootstrapArguments } from './definition.js';
@@ -16,6 +16,7 @@ export type KernelApplicationOptions<TDefinition extends ApplicationDefinition>
16
16
  readonly environment?: unknown;
17
17
  readonly logger?: Logger;
18
18
  readonly forceShutdownTimeoutMs?: number;
19
+ readonly instrumentation?: RuntimeInstrumentation;
19
20
  } & BootstrapArguments<TDefinition>;
20
21
  export declare function createKernelApplication<const TDefinition extends ApplicationDefinition>(options: KernelApplicationOptions<TDefinition>): KernelHostedApplication<TDefinition>;
21
22
  export declare function bootstrapApplication<const TDefinition extends ApplicationDefinition>(options: KernelApplicationOptions<TDefinition>): Promise<KernelHostedApplication<TDefinition>>;
@@ -1,4 +1,5 @@
1
1
  import type { Diagnostic } from './diagnostic.js';
2
+ import type { RuntimeExecutionMetadata, RuntimeInvocationRegistration, RuntimeOperationMetadata } from './instrumentation.js';
2
3
  import type { TokenLike } from './token.js';
3
4
  declare const runtimeCapabilityValue: unique symbol;
4
5
  declare const executionExtensionTypeInfo: unique symbol;
@@ -27,11 +28,21 @@ export declare class RuntimeCapabilityRegistry implements RuntimeCapabilityBindi
27
28
  }
28
29
  export interface ExecutionLease {
29
30
  readonly signal: AbortSignal;
31
+ run?<T>(operation: () => T): T;
30
32
  abort(reason?: unknown): void;
31
- complete(): void;
33
+ fail?(reason: unknown): void;
34
+ annotate?(attributes: Readonly<Record<string, unknown>>): void;
35
+ complete(...result: [] | [unknown]): void;
36
+ }
37
+ export interface ExecutionOperationLease {
38
+ run?<T>(operation: () => T): T;
39
+ fail?(reason: unknown): void;
40
+ annotate?(attributes: Readonly<Record<string, unknown>>): void;
41
+ complete(...result: [] | [unknown]): void;
32
42
  }
33
43
  export interface ExecutionKernelRuntime {
34
- beginExecution(): ExecutionLease;
44
+ beginExecution(metadata?: RuntimeExecutionMetadata, invocation?: RuntimeInvocationRegistration): ExecutionLease;
45
+ beginOperation?(metadata: RuntimeOperationMetadata, invocation?: RuntimeInvocationRegistration): ExecutionOperationLease;
35
46
  resolve<TValue>(token: TokenLike<TValue>, source?: string): TValue;
36
47
  }
37
48
  export interface ExecutionExtensionDrainContext {
@@ -7,7 +7,8 @@ export type { EnvClass, EnvKey, EnvSchema } from './env.js';
7
7
  export { defineError, DomainError } from './error.js';
8
8
  export type { ErrorDefinition } from './error.js';
9
9
  export { bindRuntimeCapability, defineExecution, defineExecutionExtension, executionDefinitionBrand, isExecutionDefinition, runtimeCapability, RuntimeCapabilityRegistry, } from './extension.js';
10
- export type { AnyExecutionExtension, ExecutionCompileContext, ExecutionContribution, ExecutionDefinition, ExecutionExtension, ExecutionExtensionDrainContext, ExecutionExtensionRuntime, ExecutionExtensionRuntimeContext, ExecutionExtensionValidationContext, ExecutionKernelRuntime, ExecutionLease, ExecutionProjectionContext, ExtensionOfDefinition, HostApiOfExtension, HostExtension, HostExtensionContext, RuntimeCapability, RuntimeCapabilityBinding, RuntimeCapabilityBindings, RuntimeCapabilityValue, } from './extension.js';
10
+ export type { AnyExecutionExtension, ExecutionCompileContext, ExecutionContribution, ExecutionDefinition, ExecutionExtension, ExecutionExtensionDrainContext, ExecutionExtensionRuntime, ExecutionExtensionRuntimeContext, ExecutionExtensionValidationContext, ExecutionKernelRuntime, ExecutionLease, ExecutionProjectionContext, ExecutionOperationLease, ExtensionOfDefinition, HostApiOfExtension, HostExtension, HostExtensionContext, RuntimeCapability, RuntimeCapabilityBinding, RuntimeCapabilityBindings, RuntimeCapabilityValue, } from './extension.js';
11
+ export type { RuntimeExecutionMetadata, RuntimeInstrumentation, RuntimeInstrumentationScope, RuntimeInvocationRegistration, RuntimeProviderMetadata, RuntimeOperationMetadata, } from './instrumentation.js';
11
12
  export { composeLayers, defineLayer } from './generic-layer.js';
12
13
  export type { GenericLayer, GenericLayerContext, GenericLayerNext, } from './generic-layer.js';
13
14
  export { collectInjectedDependencies, inject, InjectionContextError, runInInjectionContext, } from './injection.js';
@@ -0,0 +1,36 @@
1
+ import type { Scope } from './provider.js';
2
+ export interface RuntimeInvocationRegistration {
3
+ readonly input: unknown;
4
+ invoke(input: unknown): unknown | Promise<unknown>;
5
+ }
6
+ export interface RuntimeExecutionMetadata {
7
+ readonly executionId: string;
8
+ readonly executionKind: string;
9
+ readonly name?: string;
10
+ readonly graphNodeId?: string;
11
+ }
12
+ export interface RuntimeOperationMetadata {
13
+ readonly kind: string;
14
+ readonly name: string;
15
+ readonly graphNodeId?: string;
16
+ }
17
+ export interface RuntimeProviderMetadata {
18
+ readonly providerId: string;
19
+ readonly name: string;
20
+ readonly graphNodeId: string;
21
+ readonly scope: Scope;
22
+ readonly providerKind: 'value' | 'class' | 'factory' | 'conditional' | 'environment' | 'arguments';
23
+ }
24
+ export interface RuntimeInstrumentationScope {
25
+ run?<T>(operation: () => T): T;
26
+ abort?(reason?: unknown): void;
27
+ fail?(reason: unknown): void;
28
+ annotate?(attributes: Readonly<Record<string, unknown>>): void;
29
+ complete(...result: [] | [unknown]): void;
30
+ }
31
+ export interface RuntimeInstrumentation {
32
+ beginExecution?(metadata: RuntimeExecutionMetadata, invocation?: RuntimeInvocationRegistration): RuntimeInstrumentationScope | undefined;
33
+ beginOperation?(metadata: RuntimeOperationMetadata, invocation?: RuntimeInvocationRegistration): RuntimeInstrumentationScope | undefined;
34
+ providerCreated?(metadata: RuntimeProviderMetadata, value: unknown): void;
35
+ close?(): void | Promise<void>;
36
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ import type { RuntimeExecutionMetadata } from '../core/instrumentation.js';
2
+ import type { DevtoolsModuleOptions } from './module.js';
3
+ import type { ReplayCapsuleRequest, ReplayExecutionContext, RuntimeInvocationResult } from './inspector.js';
4
+ import { type DevtoolsValuePreview } from './values.js';
5
+ export interface ReplayRegistration {
6
+ readonly input: unknown;
7
+ replay(input: unknown, context: ReplayExecutionContext): unknown | Promise<unknown>;
8
+ }
9
+ export type CapturedValue = {
10
+ readonly mode: 'snapshot';
11
+ readonly value: unknown;
12
+ } | {
13
+ readonly mode: 'reference';
14
+ readonly handleId: string;
15
+ } | {
16
+ readonly mode: 'unavailable';
17
+ readonly reason: string;
18
+ };
19
+ export interface ExecutionReplayCapsulePreview {
20
+ readonly id: string;
21
+ readonly kind: 'execution';
22
+ readonly runId: string;
23
+ readonly traceId: string;
24
+ readonly spanId: string;
25
+ readonly createdAt: number;
26
+ readonly expiresAt: number;
27
+ readonly execution: {
28
+ readonly executionId: string;
29
+ readonly executionKind: string;
30
+ readonly graphNodeId?: string;
31
+ readonly name: string;
32
+ };
33
+ readonly input: DevtoolsValuePreview;
34
+ readonly fidelity: 'snapshot' | 'reference' | 'unavailable';
35
+ readonly replayModes: readonly ['direct'];
36
+ }
37
+ export interface ReplayCapsuleStoreOptions {
38
+ readonly ttlMs?: number;
39
+ readonly maxCapsules?: number;
40
+ readonly capture?: DevtoolsModuleOptions['capture'];
41
+ readonly redact?: readonly string[];
42
+ }
43
+ export declare class ReplayCapsuleStore {
44
+ #private;
45
+ constructor(runId: string, options?: ReplayCapsuleStoreOptions);
46
+ createExecution(traceId: string, spanId: string, observation: RuntimeExecutionMetadata, registration: ReplayRegistration): ExecutionReplayCapsulePreview;
47
+ preview(capsuleId: string): ExecutionReplayCapsulePreview | undefined;
48
+ replay(request: ReplayCapsuleRequest): Promise<RuntimeInvocationResult>;
49
+ discard(capsuleId: string): void;
50
+ clear(): void;
51
+ }
@@ -0,0 +1,245 @@
1
+ import { serializeDevtoolsError } from './observer.js';
2
+ import { previewDevtoolsValue } from './values.js';
3
+ const defaultCapsuleTtlMs = 5 * 60_000;
4
+ const defaultMaxCapsules = 1_000;
5
+ export class ReplayCapsuleStore {
6
+ #runId;
7
+ #ttlMs;
8
+ #maxCapsules;
9
+ #capture;
10
+ #redact;
11
+ #capsules = new Map();
12
+ #references = new Map();
13
+ constructor(runId, options = {}) {
14
+ this.#runId = runId;
15
+ this.#ttlMs = options.ttlMs ?? defaultCapsuleTtlMs;
16
+ this.#maxCapsules = options.maxCapsules ?? defaultMaxCapsules;
17
+ this.#capture = options.capture;
18
+ this.#redact = options.redact ?? [];
19
+ if (!Number.isFinite(this.#ttlMs) || this.#ttlMs <= 0) {
20
+ throw new TypeError('capsuleTtlMs must be a positive finite number.');
21
+ }
22
+ if (!Number.isInteger(this.#maxCapsules) || this.#maxCapsules < 1) {
23
+ throw new TypeError('maxCapsules must be a positive integer.');
24
+ }
25
+ }
26
+ createExecution(traceId, spanId, observation, registration) {
27
+ this.#purgeExpired();
28
+ const id = `capsule_${crypto.randomUUID()}`;
29
+ const createdAt = Date.now();
30
+ const captured = this.#captureValue(registration.input);
31
+ const preview = {
32
+ id,
33
+ kind: 'execution',
34
+ runId: this.#runId,
35
+ traceId,
36
+ spanId,
37
+ createdAt,
38
+ expiresAt: createdAt + this.#ttlMs,
39
+ execution: {
40
+ executionId: observation.executionId,
41
+ executionKind: observation.executionKind,
42
+ name: observation.name ?? observation.executionId,
43
+ ...(observation.graphNodeId === undefined
44
+ ? {}
45
+ : { graphNodeId: observation.graphNodeId }),
46
+ },
47
+ input: previewDevtoolsValue(registration.input, {
48
+ ...(this.#capture?.maxValueBytes === undefined
49
+ ? {}
50
+ : { maxValueBytes: this.#capture.maxValueBytes }),
51
+ ...(this.#capture?.maxDepth === undefined
52
+ ? {}
53
+ : { maxDepth: this.#capture.maxDepth }),
54
+ redact: this.#redact,
55
+ }),
56
+ fidelity: captured.mode,
57
+ replayModes: ['direct'],
58
+ };
59
+ this.#capsules.set(id, { preview, captured, replay: registration.replay });
60
+ while (this.#capsules.size > this.#maxCapsules) {
61
+ const oldest = this.#capsules.keys().next().value;
62
+ if (!oldest)
63
+ break;
64
+ this.#delete(oldest);
65
+ }
66
+ return preview;
67
+ }
68
+ preview(capsuleId) {
69
+ this.#purgeExpired();
70
+ return this.#capsules.get(capsuleId)?.preview;
71
+ }
72
+ async replay(request) {
73
+ this.#purgeExpired();
74
+ const capsule = this.#capsules.get(request.capsuleId);
75
+ if (!capsule) {
76
+ return {
77
+ requestId: request.requestId,
78
+ status: 'error',
79
+ error: {
80
+ name: 'ReplayCapsuleUnavailableError',
81
+ message: `LUTRE_DEVTOOLS_CAPSULE_UNAVAILABLE: ${request.capsuleId}`,
82
+ },
83
+ };
84
+ }
85
+ let input;
86
+ try {
87
+ input =
88
+ 'inputOverride' in request
89
+ ? request.inputOverride
90
+ : this.#restore(capsule.captured);
91
+ }
92
+ catch (error) {
93
+ return {
94
+ requestId: request.requestId,
95
+ status: 'error',
96
+ error: serializeDevtoolsError(error),
97
+ };
98
+ }
99
+ const context = {
100
+ sourceTraceId: capsule.preview.traceId,
101
+ sourceCapsuleId: capsule.preview.id,
102
+ ...(request.parentTraceId === undefined
103
+ ? {}
104
+ : { parentTraceId: request.parentTraceId }),
105
+ };
106
+ try {
107
+ const result = await capsule.replay(input, context);
108
+ return {
109
+ requestId: request.requestId,
110
+ status: 'ok',
111
+ result: previewDevtoolsValue(result, {
112
+ ...(this.#capture?.maxValueBytes === undefined
113
+ ? {}
114
+ : { maxValueBytes: this.#capture.maxValueBytes }),
115
+ ...(this.#capture?.maxDepth === undefined
116
+ ? {}
117
+ : { maxDepth: this.#capture.maxDepth }),
118
+ redact: this.#redact,
119
+ }),
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ requestId: request.requestId,
125
+ status: 'error',
126
+ error: serializeDevtoolsError(error),
127
+ };
128
+ }
129
+ }
130
+ discard(capsuleId) {
131
+ this.#delete(capsuleId);
132
+ }
133
+ clear() {
134
+ this.#capsules.clear();
135
+ this.#references.clear();
136
+ }
137
+ #captureValue(value) {
138
+ try {
139
+ if (isSnapshotCandidate(value)) {
140
+ return { mode: 'snapshot', value: structuredClone(value) };
141
+ }
142
+ }
143
+ catch {
144
+ // cloneできない値を失うより、同一process内だけで有効な参照として保持する。
145
+ }
146
+ if (value !== undefined) {
147
+ const handleId = `ref_${crypto.randomUUID()}`;
148
+ this.#references.set(handleId, value);
149
+ return { mode: 'reference', handleId };
150
+ }
151
+ return { mode: 'snapshot', value: undefined };
152
+ }
153
+ #restore(captured) {
154
+ switch (captured.mode) {
155
+ case 'snapshot':
156
+ return structuredClone(captured.value);
157
+ case 'reference':
158
+ if (this.#references.has(captured.handleId)) {
159
+ return this.#references.get(captured.handleId);
160
+ }
161
+ throw new Error(`LUTRE_DEVTOOLS_REFERENCE_UNAVAILABLE: ${captured.handleId}`);
162
+ case 'unavailable':
163
+ throw new Error(`LUTRE_DEVTOOLS_CAPTURE_UNAVAILABLE: ${captured.reason}`);
164
+ }
165
+ }
166
+ #purgeExpired() {
167
+ const now = Date.now();
168
+ for (const [id, capsule] of this.#capsules) {
169
+ if (capsule.preview.expiresAt > now)
170
+ continue;
171
+ this.#delete(id);
172
+ }
173
+ }
174
+ #delete(id) {
175
+ const capsule = this.#capsules.get(id);
176
+ if (!capsule)
177
+ return;
178
+ if (capsule.captured.mode === 'reference') {
179
+ this.#references.delete(capsule.captured.handleId);
180
+ }
181
+ this.#capsules.delete(id);
182
+ }
183
+ }
184
+ function isSnapshotCandidate(value, seen = new WeakSet()) {
185
+ if (value === null)
186
+ return true;
187
+ if (typeof value === 'undefined' ||
188
+ typeof value === 'string' ||
189
+ typeof value === 'number' ||
190
+ typeof value === 'boolean' ||
191
+ typeof value === 'bigint') {
192
+ return true;
193
+ }
194
+ if (typeof value !== 'object')
195
+ return false;
196
+ if (seen.has(value))
197
+ return true;
198
+ seen.add(value);
199
+ try {
200
+ if (value instanceof Date ||
201
+ value instanceof ArrayBuffer ||
202
+ ArrayBuffer.isView(value)) {
203
+ return true;
204
+ }
205
+ if (Array.isArray(value)) {
206
+ return dataPropertiesAreSnapshotCandidates(value, seen, 'length');
207
+ }
208
+ if (value instanceof Map) {
209
+ for (const [key, item] of Map.prototype.entries.call(value)) {
210
+ if (!isSnapshotCandidate(key, seen) ||
211
+ !isSnapshotCandidate(item, seen)) {
212
+ return false;
213
+ }
214
+ }
215
+ return true;
216
+ }
217
+ if (value instanceof Set) {
218
+ for (const item of Set.prototype.values.call(value)) {
219
+ if (!isSnapshotCandidate(item, seen))
220
+ return false;
221
+ }
222
+ return true;
223
+ }
224
+ const prototype = Object.getPrototypeOf(value);
225
+ if (prototype !== Object.prototype && prototype !== null)
226
+ return false;
227
+ return dataPropertiesAreSnapshotCandidates(value, seen);
228
+ }
229
+ finally {
230
+ seen.delete(value);
231
+ }
232
+ }
233
+ function dataPropertiesAreSnapshotCandidates(value, seen, ignoredKey) {
234
+ const descriptors = Object.getOwnPropertyDescriptors(value);
235
+ for (const key of Reflect.ownKeys(descriptors)) {
236
+ if (key === ignoredKey)
237
+ continue;
238
+ const descriptor = descriptors[key];
239
+ if (!descriptor || !('value' in descriptor))
240
+ return false;
241
+ if (!isSnapshotCandidate(descriptor.value, seen))
242
+ return false;
243
+ }
244
+ return true;
245
+ }
@@ -0,0 +1,11 @@
1
+ export { DEVTOOLS_PROTOCOL_VERSION } from './protocol.js';
2
+ export { DevtoolsModule, devtoolsOptionsOf } from './module.js';
3
+ export type { DevtoolsModuleOptions } from './module.js';
4
+ export { ReplayCapsuleStore, type CapturedValue, type ExecutionReplayCapsulePreview, type ReplayCapsuleStoreOptions, } from './capsule.js';
5
+ export type { ProviderMethodInvocationRequest, ProviderPlaygroundDescriptor, ProviderPlaygroundMethod, ProviderPlaygroundRequest, ReplayCapsuleRequest, ReplayExecutionContext, RuntimeDevtoolsContext, RuntimeDevtoolsContextStorage, RuntimeInspector, RuntimeInvocationResult, RuntimeTraceContext, } from './inspector.js';
6
+ export { RuntimeDevtoolsInstrumentation } from './runtime.js';
7
+ export type { RuntimeDevtoolsInstrumentationOptions } from './runtime.js';
8
+ export type { ExecutionEndedEvent, ExecutionStartedEvent, RuntimeEvent, RuntimeEventBase, RuntimeObserver, RuntimeSpanKind, SerializedDevtoolsError, SpanEndedEvent, SpanStartedEvent, } from './observer.js';
9
+ export { serializeDevtoolsError } from './observer.js';
10
+ export type { DevtoolsValue, DevtoolsValuePreview, DevtoolsValuePreviewOptions, } from './values.js';
11
+ export { previewDevtoolsValue } from './values.js';
@@ -0,0 +1,6 @@
1
+ export { DEVTOOLS_PROTOCOL_VERSION } from './protocol.js';
2
+ export { DevtoolsModule, devtoolsOptionsOf } from './module.js';
3
+ export { ReplayCapsuleStore, } from './capsule.js';
4
+ export { RuntimeDevtoolsInstrumentation } from './runtime.js';
5
+ export { serializeDevtoolsError } from './observer.js';
6
+ export { previewDevtoolsValue } from './values.js';
@@ -0,0 +1,57 @@
1
+ import type { SerializedDevtoolsError } from './observer.js';
2
+ import type { DevtoolsValuePreview } from './values.js';
3
+ export interface ReplayExecutionContext {
4
+ readonly sourceTraceId: string;
5
+ readonly sourceCapsuleId: string;
6
+ readonly parentTraceId?: string;
7
+ }
8
+ export interface ReplayCapsuleRequest {
9
+ readonly requestId: string;
10
+ readonly capsuleId: string;
11
+ readonly inputOverride?: unknown;
12
+ readonly parentTraceId?: string;
13
+ }
14
+ export interface ProviderPlaygroundRequest {
15
+ readonly requestId: string;
16
+ readonly graphNodeId: string;
17
+ }
18
+ export interface ProviderMethodInvocationRequest {
19
+ readonly requestId: string;
20
+ readonly graphNodeId: string;
21
+ readonly method: string;
22
+ readonly args: readonly unknown[];
23
+ }
24
+ export interface ProviderPlaygroundMethod {
25
+ readonly name: string;
26
+ readonly arity: number;
27
+ }
28
+ export interface ProviderPlaygroundDescriptor {
29
+ readonly requestId: string;
30
+ readonly graphNodeId: string;
31
+ readonly providerName: string;
32
+ readonly methods: readonly ProviderPlaygroundMethod[];
33
+ }
34
+ export interface RuntimeInvocationResult {
35
+ readonly requestId: string;
36
+ readonly status: 'ok' | 'error';
37
+ readonly traceId?: string;
38
+ readonly result?: DevtoolsValuePreview;
39
+ readonly error?: SerializedDevtoolsError;
40
+ }
41
+ export interface RuntimeInspector {
42
+ replayCapsule(request: ReplayCapsuleRequest): Promise<RuntimeInvocationResult>;
43
+ providerPlayground(request: ProviderPlaygroundRequest): Promise<ProviderPlaygroundDescriptor>;
44
+ invokeProviderMethod(request: ProviderMethodInvocationRequest): Promise<RuntimeInvocationResult>;
45
+ }
46
+ export interface RuntimeTraceContext {
47
+ readonly traceId: string;
48
+ readonly spanId: string;
49
+ }
50
+ export interface RuntimeDevtoolsContext {
51
+ readonly trace?: RuntimeTraceContext;
52
+ readonly replay?: ReplayExecutionContext;
53
+ }
54
+ export interface RuntimeDevtoolsContextStorage {
55
+ run<T>(context: RuntimeDevtoolsContext, operation: () => T): T;
56
+ current(): RuntimeDevtoolsContext | undefined;
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { ApplicationModel } from '../core/model/types.js';
2
+ export interface DevtoolsModuleOptions {
3
+ readonly enabled?: boolean;
4
+ readonly capture?: {
5
+ readonly providerMethods?: boolean;
6
+ readonly results?: false | 'errors' | 'all';
7
+ readonly maxValueBytes?: number;
8
+ readonly maxDepth?: number;
9
+ };
10
+ readonly replay?: {
11
+ readonly enabled?: boolean;
12
+ readonly capsuleTtlMs?: number;
13
+ readonly maxCapsules?: number;
14
+ };
15
+ readonly redact?: readonly string[];
16
+ }
17
+ export declare function DevtoolsModule(options?: DevtoolsModuleOptions): import("../core/module.js").ModuleInstance<import("../core/module.js").ModuleDefinition>;
18
+ export declare function devtoolsOptionsOf(model: ApplicationModel): Readonly<DevtoolsModuleOptions> | undefined;
@@ -0,0 +1,26 @@
1
+ import { defineModule } from '../core/module.js';
2
+ import { provide } from '../core/provider.js';
3
+ import { token } from '../core/token.js';
4
+ const devtoolsOptionsToken = token('loutre.devtools.options');
5
+ const devtoolsModuleTemplate = defineModule((options = {}) => ({
6
+ name: 'LoutreDevtoolsModule',
7
+ providers: [
8
+ provide(devtoolsOptionsToken).useValue(Object.freeze({ ...options })),
9
+ ],
10
+ }));
11
+ export function DevtoolsModule(options) {
12
+ return devtoolsModuleTemplate(options);
13
+ }
14
+ export function devtoolsOptionsOf(model) {
15
+ const provider = model.providers.find((candidate) => {
16
+ if (candidate.kind !== 'value')
17
+ return false;
18
+ const provided = candidate.provide;
19
+ return (typeof provided !== 'function' &&
20
+ provided.kind === 'token' &&
21
+ provided.id === devtoolsOptionsToken.id);
22
+ });
23
+ return provider?.kind === 'value'
24
+ ? provider.useValue
25
+ : undefined;
26
+ }
@@ -0,0 +1,77 @@
1
+ import type { RuntimeExecutionMetadata } from '../core/instrumentation.js';
2
+ import type { DevtoolsValuePreview } from './values.js';
3
+ export type RuntimeSpanKind = 'execution' | 'http.request' | 'http.middleware' | 'http.handler' | 'task.handler' | 'message-port.handler' | 'websocket.handler' | 'di.resolve' | 'provider.construct' | 'provider.method' | 'response' | 'dependency' | 'custom';
4
+ export interface SerializedDevtoolsError {
5
+ readonly name?: string;
6
+ readonly message: string;
7
+ readonly stack?: string;
8
+ }
9
+ export interface RuntimeEventBase {
10
+ readonly runId: string;
11
+ readonly seq: number;
12
+ readonly timestamp: number;
13
+ readonly traceId?: string;
14
+ readonly spanId?: string;
15
+ readonly parentSpanId?: string;
16
+ readonly graphNodeId?: string;
17
+ }
18
+ export interface ExecutionStartedEvent extends RuntimeEventBase {
19
+ readonly type: 'execution.started';
20
+ readonly traceId: string;
21
+ readonly spanId: string;
22
+ readonly executionId: string;
23
+ readonly executionKind: string;
24
+ readonly name: string;
25
+ readonly input?: DevtoolsValuePreview;
26
+ readonly capsuleId?: string;
27
+ readonly replayable?: boolean;
28
+ readonly replayModes?: readonly ('direct' | 'transport')[];
29
+ readonly replayedFromTraceId?: string;
30
+ readonly replayedFromCapsuleId?: string;
31
+ }
32
+ export interface ExecutionEndedEvent extends RuntimeEventBase {
33
+ readonly type: 'execution.ended';
34
+ readonly traceId: string;
35
+ readonly spanId: string;
36
+ readonly executionId: string;
37
+ readonly durationMs: number;
38
+ readonly status: 'ok' | 'error' | 'cancelled';
39
+ readonly attributes?: Readonly<Record<string, unknown>>;
40
+ readonly result?: DevtoolsValuePreview;
41
+ readonly error?: SerializedDevtoolsError;
42
+ }
43
+ export interface SpanStartedEvent extends RuntimeEventBase {
44
+ readonly type: 'span.started';
45
+ readonly spanId: string;
46
+ readonly traceId: string;
47
+ readonly kind: RuntimeSpanKind;
48
+ readonly name: string;
49
+ readonly attributes?: Readonly<Record<string, unknown>>;
50
+ readonly input?: DevtoolsValuePreview;
51
+ readonly capsuleId?: string;
52
+ readonly replayable?: boolean;
53
+ readonly replayModes?: readonly ('direct' | 'transport')[];
54
+ }
55
+ export interface SpanEndedEvent extends RuntimeEventBase {
56
+ readonly type: 'span.ended';
57
+ readonly spanId: string;
58
+ readonly traceId: string;
59
+ readonly durationMs: number;
60
+ readonly status: 'ok' | 'error' | 'cancelled';
61
+ readonly attributes?: Readonly<Record<string, unknown>>;
62
+ readonly result?: DevtoolsValuePreview;
63
+ readonly error?: SerializedDevtoolsError;
64
+ }
65
+ export type RuntimeEvent = ExecutionStartedEvent | ExecutionEndedEvent | SpanStartedEvent | SpanEndedEvent;
66
+ export interface RuntimeObserver {
67
+ readonly runId?: string;
68
+ executionStarted?(event: ExecutionStartedEvent): void;
69
+ executionEnded?(event: ExecutionEndedEvent): void;
70
+ spanStarted?(event: SpanStartedEvent): void;
71
+ spanEnded?(event: SpanEndedEvent): void;
72
+ }
73
+ export interface RuntimeExecutionContext extends RuntimeExecutionMetadata {
74
+ readonly traceId: string;
75
+ readonly spanId: string;
76
+ }
77
+ export declare function serializeDevtoolsError(error: unknown): SerializedDevtoolsError;
@@ -0,0 +1,10 @@
1
+ export function serializeDevtoolsError(error) {
2
+ if (error instanceof Error) {
3
+ return {
4
+ name: error.name,
5
+ message: error.message,
6
+ ...(error.stack === undefined ? {} : { stack: error.stack }),
7
+ };
8
+ }
9
+ return { message: String(error) };
10
+ }
@@ -0,0 +1 @@
1
+ export declare const DEVTOOLS_PROTOCOL_VERSION = 1;
@@ -0,0 +1 @@
1
+ export const DEVTOOLS_PROTOCOL_VERSION = 1;
@@ -0,0 +1,14 @@
1
+ export interface ProviderMethodInvocation {
2
+ readonly key: PropertyKey;
3
+ readonly name: string;
4
+ readonly original: Function;
5
+ readonly receiver: unknown;
6
+ readonly args: readonly unknown[];
7
+ }
8
+ export interface ProviderMethodDefinition {
9
+ readonly key: PropertyKey;
10
+ readonly name: string;
11
+ readonly original: Function;
12
+ }
13
+ export declare function discoverProviderMethods(instance: object): readonly ProviderMethodDefinition[];
14
+ export declare function instrumentProviderMethods(instance: object, invoke: (invocation: ProviderMethodInvocation) => unknown): () => void;