@loutrejs/loutre 0.5.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.
Files changed (188) hide show
  1. package/README.md +63 -32
  2. package/dist/adapters/aws-lambda.d.ts +5 -3
  3. package/dist/adapters/aws-lambda.js +53 -23
  4. package/dist/adapters/bun.d.ts +7 -4
  5. package/dist/adapters/bun.js +52 -42
  6. package/dist/adapters/cloudflare-workers.d.ts +5 -3
  7. package/dist/adapters/cloudflare-workers.js +23 -28
  8. package/dist/adapters/deno.d.ts +7 -4
  9. package/dist/adapters/deno.js +94 -57
  10. package/dist/adapters/electron.d.ts +13 -5
  11. package/dist/adapters/electron.js +76 -28
  12. package/dist/application/definition.d.ts +31 -0
  13. package/dist/application/definition.js +16 -0
  14. package/dist/application/index.d.ts +4 -63
  15. package/dist/application/index.js +2 -11
  16. package/dist/application/kernel-internal.d.ts +2 -0
  17. package/dist/application/kernel-internal.js +10 -0
  18. package/dist/application/kernel.d.ts +22 -0
  19. package/dist/application/kernel.js +65 -0
  20. package/dist/core/diagnostic.d.ts +9 -0
  21. package/dist/core/diagnostic.js +9 -0
  22. package/dist/core/extension.d.ts +118 -0
  23. package/dist/core/extension.js +73 -0
  24. package/dist/core/generic-layer.d.ts +26 -0
  25. package/dist/core/generic-layer.js +116 -0
  26. package/dist/core/index.d.ts +32 -15
  27. package/dist/core/index.js +15 -15
  28. package/dist/core/injection.d.ts +4 -13
  29. package/dist/core/injection.js +63 -0
  30. package/dist/core/instrumentation.d.ts +36 -0
  31. package/dist/core/model/context.d.ts +18 -0
  32. package/dist/core/model/context.js +34 -0
  33. package/dist/core/model/lifecycle.d.ts +5 -0
  34. package/dist/core/model/lifecycle.js +44 -0
  35. package/dist/core/model/modules.d.ts +2 -0
  36. package/dist/core/model/modules.js +101 -0
  37. package/dist/core/model/providers.d.ts +4 -0
  38. package/dist/core/model/providers.js +74 -0
  39. package/dist/core/model/snapshot.d.ts +6 -0
  40. package/dist/core/model/snapshot.js +31 -0
  41. package/dist/core/model/types.d.ts +78 -0
  42. package/dist/core/model/types.js +1 -0
  43. package/dist/core/model/validation.d.ts +4 -0
  44. package/dist/core/model/validation.js +40 -0
  45. package/dist/core/model/visibility.d.ts +4 -0
  46. package/dist/core/model/visibility.js +20 -0
  47. package/dist/core/model.d.ts +10 -0
  48. package/dist/core/model.js +346 -0
  49. package/dist/core/module.d.ts +8 -13
  50. package/dist/core/source-location.d.ts +8 -0
  51. package/dist/core/source-location.js +31 -0
  52. package/dist/core/token-internal.d.ts +3 -0
  53. package/dist/core/token-internal.js +4 -0
  54. package/dist/core/type.d.ts +7 -0
  55. package/dist/core/type.js +4 -0
  56. package/dist/devtools/capsule.d.ts +51 -0
  57. package/dist/devtools/capsule.js +245 -0
  58. package/dist/devtools/index.d.ts +11 -0
  59. package/dist/devtools/index.js +6 -0
  60. package/dist/devtools/inspector.d.ts +57 -0
  61. package/dist/devtools/inspector.js +1 -0
  62. package/dist/devtools/module.d.ts +18 -0
  63. package/dist/devtools/module.js +26 -0
  64. package/dist/devtools/observer.d.ts +77 -0
  65. package/dist/devtools/observer.js +10 -0
  66. package/dist/devtools/protocol.d.ts +1 -0
  67. package/dist/devtools/protocol.js +1 -0
  68. package/dist/devtools/provider-instrumentation.d.ts +14 -0
  69. package/dist/devtools/provider-instrumentation.js +117 -0
  70. package/dist/devtools/runtime.d.ts +20 -0
  71. package/dist/devtools/runtime.js +519 -0
  72. package/dist/devtools/values.d.ts +35 -0
  73. package/dist/devtools/values.js +175 -0
  74. package/dist/generated/version.d.ts +1 -1
  75. package/dist/generated/version.js +1 -1
  76. package/dist/graph/index.d.ts +2 -3
  77. package/dist/graph/index.js +1 -3
  78. package/dist/graph/model.d.ts +34 -0
  79. package/dist/graph/model.js +127 -0
  80. package/dist/http/auth.d.ts +46 -0
  81. package/dist/http/auth.js +96 -0
  82. package/dist/http/client.d.ts +28 -31
  83. package/dist/http/client.js +58 -44
  84. package/dist/http/cors-internal.d.ts +29 -0
  85. package/dist/http/cors-internal.js +160 -0
  86. package/dist/http/cors.d.ts +5 -8
  87. package/dist/http/cors.js +7 -165
  88. package/dist/http/extension.d.ts +348 -0
  89. package/dist/http/extension.js +1061 -0
  90. package/dist/http/index.d.ts +9 -7
  91. package/dist/http/index.js +5 -7
  92. package/dist/{application → http}/openapi.d.ts +12 -3
  93. package/dist/{application → http}/openapi.js +164 -102
  94. package/dist/http/path.d.ts +1 -42
  95. package/dist/http/path.js +1 -2
  96. package/dist/http/runtime/request.d.ts +17 -0
  97. package/dist/http/runtime/request.js +109 -0
  98. package/dist/http/runtime/response.d.ts +18 -0
  99. package/dist/http/runtime/response.js +108 -0
  100. package/dist/index.d.ts +4 -0
  101. package/dist/index.js +2 -0
  102. package/dist/message-port/extension.d.ts +66 -0
  103. package/dist/message-port/extension.js +411 -0
  104. package/dist/message-port/index.d.ts +4 -73
  105. package/dist/message-port/index.js +2 -189
  106. package/dist/message-port/transport.d.ts +11 -0
  107. package/dist/message-port/transport.js +45 -0
  108. package/dist/runtime/async-iterator-lifecycle.d.ts +12 -0
  109. package/dist/runtime/async-iterator-lifecycle.js +95 -0
  110. package/dist/runtime/capability.d.ts +11 -11
  111. package/dist/runtime/capability.js +3 -3
  112. package/dist/runtime/di.d.ts +5 -21
  113. package/dist/runtime/di.js +22 -289
  114. package/dist/runtime/execution-tracker.d.ts +8 -0
  115. package/dist/runtime/execution-tracker.js +54 -0
  116. package/dist/runtime/index.d.ts +15 -9
  117. package/dist/runtime/index.js +9 -9
  118. package/dist/runtime/ingress-gate.d.ts +7 -0
  119. package/dist/runtime/ingress-gate.js +33 -0
  120. package/dist/runtime/kernel.d.ts +25 -0
  121. package/dist/runtime/kernel.js +447 -0
  122. package/dist/runtime/logger.d.ts +2 -0
  123. package/dist/runtime/logger.js +2 -0
  124. package/dist/runtime/node-capabilities.d.ts +1 -1
  125. package/dist/runtime/node-capabilities.js +1 -1
  126. package/dist/runtime/server-port.d.ts +2 -0
  127. package/dist/runtime/server-port.js +11 -0
  128. package/dist/tasks/extension.d.ts +144 -0
  129. package/dist/tasks/extension.js +525 -0
  130. package/dist/tasks/index.d.ts +2 -0
  131. package/dist/tasks/index.js +1 -0
  132. package/dist/tasks/runtime/cron.d.ts +6 -0
  133. package/dist/tasks/runtime/cron.js +89 -0
  134. package/dist/websocket/errors.d.ts +9 -0
  135. package/dist/websocket/errors.js +18 -0
  136. package/dist/websocket/extension.d.ts +154 -0
  137. package/dist/websocket/extension.js +533 -0
  138. package/dist/websocket/index.d.ts +3 -0
  139. package/dist/websocket/index.js +2 -0
  140. package/package.json +16 -16
  141. package/dist/application/binding.d.ts +0 -33
  142. package/dist/application/binding.js +0 -295
  143. package/dist/application/host.d.ts +0 -8
  144. package/dist/application/host.js +0 -14
  145. package/dist/application/queue.d.ts +0 -3
  146. package/dist/application/queue.js +0 -4
  147. package/dist/application/scheduler.d.ts +0 -2
  148. package/dist/application/scheduler.js +0 -45
  149. package/dist/core/context-key.d.ts +0 -16
  150. package/dist/core/context-key.js +0 -17
  151. package/dist/core/contract-internal.d.ts +0 -22
  152. package/dist/core/contract-internal.js +0 -61
  153. package/dist/core/contract.d.ts +0 -96
  154. package/dist/core/contract.js +0 -88
  155. package/dist/core/execution.d.ts +0 -78
  156. package/dist/core/execution.js +0 -61
  157. package/dist/core/implementation.d.ts +0 -44
  158. package/dist/core/implementation.js +0 -35
  159. package/dist/core/layer.d.ts +0 -137
  160. package/dist/core/layer.js +0 -81
  161. package/dist/graph/compiler.d.ts +0 -4
  162. package/dist/graph/compiler.js +0 -228
  163. package/dist/graph/graph.d.ts +0 -18
  164. package/dist/graph/graph.js +0 -1118
  165. package/dist/graph/ir.d.ts +0 -175
  166. package/dist/http/application.d.ts +0 -18
  167. package/dist/http/application.js +0 -515
  168. package/dist/http/basic-auth.d.ts +0 -37
  169. package/dist/http/basic-auth.js +0 -67
  170. package/dist/http/definitions.d.ts +0 -363
  171. package/dist/http/definitions.js +0 -310
  172. package/dist/http/hosted-protocol.d.ts +0 -4
  173. package/dist/http/hosted-protocol.js +0 -4
  174. package/dist/http/internal.d.ts +0 -8
  175. package/dist/http/internal.js +0 -1
  176. package/dist/http/params.d.ts +0 -2
  177. package/dist/http/params.js +0 -19
  178. package/dist/http/request-body.d.ts +0 -4
  179. package/dist/http/request-body.js +0 -6
  180. package/dist/http/validate.d.ts +0 -20
  181. package/dist/http/validate.js +0 -6
  182. package/dist/message-port/environment.d.ts +0 -2
  183. package/dist/message-port/environment.js +0 -4
  184. package/dist/runtime/application.d.ts +0 -38
  185. package/dist/runtime/application.js +0 -339
  186. package/dist/runtime/pipeline.d.ts +0 -11
  187. package/dist/runtime/pipeline.js +0 -161
  188. /package/dist/{graph/ir.js → core/instrumentation.js} +0 -0
@@ -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;
@@ -0,0 +1,117 @@
1
+ export function discoverProviderMethods(instance) {
2
+ const methods = [];
3
+ const seen = new Set();
4
+ const collect = (target) => {
5
+ for (const key of Reflect.ownKeys(target)) {
6
+ if (key === 'constructor' || seen.has(key))
7
+ continue;
8
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
9
+ if (!descriptor || !('value' in descriptor))
10
+ continue;
11
+ if (typeof descriptor.value !== 'function')
12
+ continue;
13
+ seen.add(key);
14
+ methods.push({
15
+ key,
16
+ name: methodName(key),
17
+ original: descriptor.value,
18
+ });
19
+ }
20
+ };
21
+ collect(instance);
22
+ let prototype = Object.getPrototypeOf(instance);
23
+ while (prototype && prototype !== Object.prototype) {
24
+ collect(prototype);
25
+ prototype = Object.getPrototypeOf(prototype);
26
+ }
27
+ return methods;
28
+ }
29
+ export function instrumentProviderMethods(instance, invoke) {
30
+ const seen = new Set();
31
+ const restore = [];
32
+ for (const key of Reflect.ownKeys(instance)) {
33
+ const descriptor = Object.getOwnPropertyDescriptor(instance, key);
34
+ if (!descriptor || !('value' in descriptor))
35
+ continue;
36
+ if (typeof descriptor.value !== 'function')
37
+ continue;
38
+ if (!descriptor.configurable && descriptor.writable !== true)
39
+ continue;
40
+ seen.add(key);
41
+ const cleanup = defineWrapper(instance, key, descriptor, descriptor.value, invoke);
42
+ if (cleanup)
43
+ restore.push(cleanup);
44
+ }
45
+ if (!Object.isExtensible(instance)) {
46
+ return () => {
47
+ for (const cleanup of restore.toReversed())
48
+ cleanup();
49
+ };
50
+ }
51
+ let prototype = Object.getPrototypeOf(instance);
52
+ while (prototype && prototype !== Object.prototype) {
53
+ for (const key of Reflect.ownKeys(prototype)) {
54
+ if (key === 'constructor' || seen.has(key))
55
+ continue;
56
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
57
+ if (!descriptor || !('value' in descriptor))
58
+ continue;
59
+ if (typeof descriptor.value !== 'function')
60
+ continue;
61
+ seen.add(key);
62
+ const cleanup = defineWrapper(instance, key, {
63
+ configurable: true,
64
+ enumerable: descriptor.enumerable ?? false,
65
+ writable: true,
66
+ value: descriptor.value,
67
+ }, descriptor.value, invoke, true);
68
+ if (cleanup)
69
+ restore.push(cleanup);
70
+ }
71
+ prototype = Object.getPrototypeOf(prototype);
72
+ }
73
+ return () => {
74
+ for (const cleanup of restore.toReversed())
75
+ cleanup();
76
+ };
77
+ }
78
+ function defineWrapper(instance, key, descriptor, original, invoke, shadowsPrototype = false) {
79
+ const wrapper = function (...args) {
80
+ return invoke({
81
+ key,
82
+ name: methodName(key),
83
+ original,
84
+ receiver: this,
85
+ args,
86
+ });
87
+ };
88
+ try {
89
+ Object.defineProperty(instance, key, {
90
+ ...descriptor,
91
+ value: wrapper,
92
+ });
93
+ }
94
+ catch {
95
+ // instrumentationのために変更不能なProviderを壊してはならない。
96
+ return undefined;
97
+ }
98
+ return () => {
99
+ try {
100
+ const current = Object.getOwnPropertyDescriptor(instance, key);
101
+ if (!current || !('value' in current) || current.value !== wrapper)
102
+ return;
103
+ if (shadowsPrototype)
104
+ delete instance[key];
105
+ else
106
+ Object.defineProperty(instance, key, descriptor);
107
+ }
108
+ catch {
109
+ // Application側の変更をcleanupで上書きしてはならない。
110
+ }
111
+ };
112
+ }
113
+ function methodName(key) {
114
+ if (typeof key === 'symbol')
115
+ return key.description ?? key.toString();
116
+ return String(key);
117
+ }
@@ -0,0 +1,20 @@
1
+ import type { RuntimeExecutionMetadata, RuntimeInstrumentation, RuntimeInstrumentationScope, RuntimeInvocationRegistration, RuntimeProviderMetadata, RuntimeOperationMetadata } from '../core/instrumentation.js';
2
+ import type { ProviderMethodInvocationRequest, ProviderPlaygroundDescriptor, ProviderPlaygroundRequest, ReplayCapsuleRequest, RuntimeDevtoolsContextStorage, RuntimeInspector, RuntimeInvocationResult } from './inspector.js';
3
+ import type { DevtoolsModuleOptions } from './module.js';
4
+ import { type RuntimeObserver } from './observer.js';
5
+ export interface RuntimeDevtoolsInstrumentationOptions {
6
+ readonly options: Readonly<DevtoolsModuleOptions>;
7
+ readonly observer: RuntimeObserver;
8
+ readonly context: RuntimeDevtoolsContextStorage;
9
+ }
10
+ export declare class RuntimeDevtoolsInstrumentation implements RuntimeInstrumentation, RuntimeInspector {
11
+ #private;
12
+ constructor(options: RuntimeDevtoolsInstrumentationOptions);
13
+ beginExecution(metadata: RuntimeExecutionMetadata, invocation?: RuntimeInvocationRegistration): RuntimeInstrumentationScope;
14
+ beginOperation(metadata: RuntimeOperationMetadata, invocation?: RuntimeInvocationRegistration): RuntimeInstrumentationScope | undefined;
15
+ providerCreated(metadata: RuntimeProviderMetadata, value: unknown): void;
16
+ providerPlayground(request: ProviderPlaygroundRequest): Promise<ProviderPlaygroundDescriptor>;
17
+ invokeProviderMethod(request: ProviderMethodInvocationRequest): Promise<RuntimeInvocationResult>;
18
+ replayCapsule(request: ReplayCapsuleRequest): Promise<RuntimeInvocationResult>;
19
+ close(): void;
20
+ }