@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,519 @@
1
+ import { ReplayCapsuleStore } from './capsule.js';
2
+ import { serializeDevtoolsError, } from './observer.js';
3
+ import { discoverProviderMethods, instrumentProviderMethods, } from './provider-instrumentation.js';
4
+ import { previewDevtoolsValue } from './values.js';
5
+ export class RuntimeDevtoolsInstrumentation {
6
+ #options;
7
+ #observer;
8
+ #context;
9
+ #captureProviderMethods;
10
+ #captureResults;
11
+ #runId;
12
+ #capsules;
13
+ #providers = new Map();
14
+ #providerMethods = new WeakMap();
15
+ #instrumentedProviderIds = new WeakMap();
16
+ #externalProviderValues = new WeakSet();
17
+ #ambiguousProviders = new WeakSet();
18
+ #providerRestorers = new WeakMap();
19
+ #applicationProviderRestorers = new Map();
20
+ #seq = 0;
21
+ constructor(options) {
22
+ this.#options = options.options;
23
+ this.#observer = options.observer;
24
+ this.#context = options.context;
25
+ this.#captureProviderMethods =
26
+ options.options.capture?.providerMethods !== false;
27
+ const captureResults = options.options.capture?.results;
28
+ this.#captureResults =
29
+ captureResults !== false && captureResults !== 'errors';
30
+ this.#runId = options.observer.runId ?? `run_${crypto.randomUUID()}`;
31
+ const replay = options.options.replay;
32
+ this.#capsules =
33
+ replay?.enabled === false
34
+ ? undefined
35
+ : new ReplayCapsuleStore(this.#runId, {
36
+ ...(replay?.capsuleTtlMs === undefined
37
+ ? {}
38
+ : { ttlMs: replay.capsuleTtlMs }),
39
+ ...(replay?.maxCapsules === undefined
40
+ ? {}
41
+ : { maxCapsules: replay.maxCapsules }),
42
+ ...(options.options.capture === undefined
43
+ ? {}
44
+ : { capture: options.options.capture }),
45
+ ...(options.options.redact === undefined
46
+ ? {}
47
+ : { redact: options.options.redact }),
48
+ });
49
+ }
50
+ beginExecution(metadata, invocation) {
51
+ const traceId = `trace_${crypto.randomUUID()}`;
52
+ const spanId = `span_${crypto.randomUUID()}`;
53
+ const startedAt = performance.now();
54
+ const startedTimestamp = performance.timeOrigin + startedAt;
55
+ const name = metadata.name ?? metadata.executionId;
56
+ const replaySource = this.#context.current()?.replay;
57
+ const capsule = invocation && this.#capsules
58
+ ? this.#capsules.createExecution(traceId, spanId, metadata, {
59
+ input: invocation.input,
60
+ replay: (input, context) => this.#runReplayContext(context, () => invocation.invoke(input)),
61
+ })
62
+ : undefined;
63
+ let completed = false;
64
+ let failed = false;
65
+ let failureReason;
66
+ let cancelled = false;
67
+ const attributes = {};
68
+ this.#notify('executionStarted', {
69
+ type: 'execution.started',
70
+ ...this.#identity(startedTimestamp, traceId, spanId, metadata.graphNodeId),
71
+ executionId: metadata.executionId,
72
+ executionKind: metadata.executionKind,
73
+ name,
74
+ ...(capsule === undefined
75
+ ? {}
76
+ : {
77
+ input: capsule.input,
78
+ capsuleId: capsule.id,
79
+ replayable: true,
80
+ replayModes: capsule.replayModes,
81
+ }),
82
+ ...(replaySource === undefined
83
+ ? {}
84
+ : {
85
+ replayedFromTraceId: replaySource.sourceTraceId,
86
+ replayedFromCapsuleId: replaySource.sourceCapsuleId,
87
+ }),
88
+ });
89
+ this.#notify('spanStarted', {
90
+ type: 'span.started',
91
+ ...this.#identity(startedTimestamp, traceId, spanId, metadata.graphNodeId),
92
+ kind: 'execution',
93
+ name,
94
+ attributes: { executionKind: metadata.executionKind },
95
+ });
96
+ return {
97
+ run: (operation) => this.#context.run({
98
+ trace: { traceId, spanId },
99
+ ...(replaySource === undefined ? {} : { replay: replaySource }),
100
+ }, operation),
101
+ abort() {
102
+ cancelled = true;
103
+ },
104
+ fail(reason) {
105
+ failed = true;
106
+ failureReason = reason;
107
+ },
108
+ annotate(nextAttributes) {
109
+ Object.assign(attributes, nextAttributes);
110
+ },
111
+ complete: (...resultValues) => {
112
+ if (completed)
113
+ return;
114
+ completed = true;
115
+ const status = failed ? 'error' : cancelled ? 'cancelled' : 'ok';
116
+ const error = failed ? serializeDevtoolsError(failureReason) : undefined;
117
+ const result = resultValues.length === 0
118
+ ? undefined
119
+ : this.#previewResult(resultValues[0]);
120
+ const endedAt = performance.now();
121
+ const timestamp = performance.timeOrigin + endedAt;
122
+ this.#notify('spanEnded', {
123
+ type: 'span.ended',
124
+ ...this.#identity(timestamp, traceId, spanId, metadata.graphNodeId),
125
+ durationMs: endedAt - startedAt,
126
+ status,
127
+ ...(Object.keys(attributes).length === 0 ? {} : { attributes }),
128
+ ...(result === undefined ? {} : { result }),
129
+ ...(error === undefined ? {} : { error }),
130
+ });
131
+ this.#notify('executionEnded', {
132
+ type: 'execution.ended',
133
+ ...this.#identity(timestamp, traceId, spanId, metadata.graphNodeId),
134
+ executionId: metadata.executionId,
135
+ durationMs: endedAt - startedAt,
136
+ status,
137
+ ...(Object.keys(attributes).length === 0 ? {} : { attributes }),
138
+ ...(result === undefined ? {} : { result }),
139
+ ...(error === undefined ? {} : { error }),
140
+ });
141
+ },
142
+ };
143
+ }
144
+ beginOperation(metadata, invocation) {
145
+ const current = this.#context.current();
146
+ if (!current?.trace)
147
+ return undefined;
148
+ const spanId = `span_${crypto.randomUUID()}`;
149
+ const startedAt = performance.now();
150
+ const capsule = invocation && this.#capsules
151
+ ? this.#capsules.createExecution(current.trace.traceId, spanId, {
152
+ executionId: `span:${metadata.name}`,
153
+ executionKind: metadata.kind,
154
+ name: metadata.name,
155
+ ...(metadata.graphNodeId === undefined
156
+ ? {}
157
+ : { graphNodeId: metadata.graphNodeId }),
158
+ }, {
159
+ input: invocation.input,
160
+ replay: (input, context) => this.#runReplayContext(context, () => invocation.invoke(input)),
161
+ })
162
+ : undefined;
163
+ let completed = false;
164
+ let failed = false;
165
+ let failureReason;
166
+ const attributes = {};
167
+ this.#notify('spanStarted', {
168
+ type: 'span.started',
169
+ runId: this.#runId,
170
+ seq: ++this.#seq,
171
+ timestamp: performance.timeOrigin + startedAt,
172
+ traceId: current.trace.traceId,
173
+ spanId,
174
+ parentSpanId: current.trace.spanId,
175
+ ...(metadata.graphNodeId === undefined
176
+ ? {}
177
+ : { graphNodeId: metadata.graphNodeId }),
178
+ kind: metadata.kind,
179
+ name: metadata.name,
180
+ ...(capsule === undefined
181
+ ? {}
182
+ : {
183
+ input: capsule.input,
184
+ capsuleId: capsule.id,
185
+ replayable: true,
186
+ replayModes: capsule.replayModes,
187
+ }),
188
+ });
189
+ return {
190
+ run: (operation) => this.#context.run({
191
+ trace: { traceId: current.trace.traceId, spanId },
192
+ ...(current.replay === undefined ? {} : { replay: current.replay }),
193
+ }, operation),
194
+ fail(reason) {
195
+ failed = true;
196
+ failureReason = reason;
197
+ },
198
+ annotate(nextAttributes) {
199
+ Object.assign(attributes, nextAttributes);
200
+ },
201
+ complete: (...resultValues) => {
202
+ if (completed)
203
+ return;
204
+ completed = true;
205
+ const result = resultValues.length === 0
206
+ ? undefined
207
+ : this.#previewResult(resultValues[0]);
208
+ const error = failed ? serializeDevtoolsError(failureReason) : undefined;
209
+ const endedAt = performance.now();
210
+ this.#notify('spanEnded', {
211
+ type: 'span.ended',
212
+ runId: this.#runId,
213
+ seq: ++this.#seq,
214
+ timestamp: performance.timeOrigin + endedAt,
215
+ traceId: current.trace.traceId,
216
+ spanId,
217
+ parentSpanId: current.trace.spanId,
218
+ ...(metadata.graphNodeId === undefined
219
+ ? {}
220
+ : { graphNodeId: metadata.graphNodeId }),
221
+ durationMs: endedAt - startedAt,
222
+ status: failed ? 'error' : 'ok',
223
+ ...(Object.keys(attributes).length === 0 ? {} : { attributes }),
224
+ ...(result === undefined ? {} : { result }),
225
+ ...(error === undefined ? {} : { error }),
226
+ });
227
+ },
228
+ };
229
+ }
230
+ providerCreated(metadata, value) {
231
+ if (typeof value !== 'object' || value === null)
232
+ return;
233
+ let methods = this.#providerMethods.get(value);
234
+ if (!methods) {
235
+ methods = discoverProviderMethods(value);
236
+ this.#providerMethods.set(value, methods);
237
+ }
238
+ if (metadata.scope === 'application') {
239
+ this.#providers.set(metadata.graphNodeId, {
240
+ metadata,
241
+ instance: value,
242
+ methods: new Map(methods.flatMap((method) => typeof method.key === 'string' ? [[method.name, method]] : [])),
243
+ });
244
+ }
245
+ if (!this.#captureProviderMethods)
246
+ return;
247
+ if (metadata.providerKind === 'value' ||
248
+ metadata.providerKind === 'factory') {
249
+ this.#providerRestorers.get(value)?.();
250
+ this.#providerRestorers.delete(value);
251
+ this.#applicationProviderRestorers.delete(value);
252
+ this.#externalProviderValues.add(value);
253
+ return;
254
+ }
255
+ if (this.#externalProviderValues.has(value) ||
256
+ this.#ambiguousProviders.has(value)) {
257
+ return;
258
+ }
259
+ const instrumentedProviderId = this.#instrumentedProviderIds.get(value);
260
+ if (instrumentedProviderId !== undefined) {
261
+ if (instrumentedProviderId !== metadata.graphNodeId) {
262
+ this.#providerRestorers.get(value)?.();
263
+ this.#providerRestorers.delete(value);
264
+ this.#applicationProviderRestorers.delete(value);
265
+ this.#ambiguousProviders.add(value);
266
+ }
267
+ return;
268
+ }
269
+ const restore = instrumentProviderMethods(value, (invocation) => this.#invokeProviderMethod(metadata, value, invocation));
270
+ this.#instrumentedProviderIds.set(value, metadata.graphNodeId);
271
+ this.#providerRestorers.set(value, restore);
272
+ if (metadata.scope === 'application') {
273
+ this.#applicationProviderRestorers.set(value, restore);
274
+ }
275
+ }
276
+ async providerPlayground(request) {
277
+ const provider = this.#providers.get(request.graphNodeId);
278
+ if (!provider) {
279
+ throw new Error(`LUTRE_DEVTOOLS_PROVIDER_UNAVAILABLE: ${request.graphNodeId}`);
280
+ }
281
+ return {
282
+ requestId: request.requestId,
283
+ graphNodeId: request.graphNodeId,
284
+ providerName: provider.metadata.name,
285
+ methods: [...provider.methods.values()]
286
+ .map((method) => ({ name: method.name, arity: method.original.length }))
287
+ .toSorted((left, right) => left.name.localeCompare(right.name)),
288
+ };
289
+ }
290
+ async invokeProviderMethod(request) {
291
+ const provider = this.#providers.get(request.graphNodeId);
292
+ if (!provider) {
293
+ return {
294
+ requestId: request.requestId,
295
+ status: 'error',
296
+ error: {
297
+ name: 'ProviderUnavailableError',
298
+ message: `LUTRE_DEVTOOLS_PROVIDER_UNAVAILABLE: ${request.graphNodeId}`,
299
+ },
300
+ };
301
+ }
302
+ const method = provider.methods.get(request.method);
303
+ if (!method) {
304
+ return {
305
+ requestId: request.requestId,
306
+ status: 'error',
307
+ error: {
308
+ name: 'ProviderMethodUnavailableError',
309
+ message: `LUTRE_DEVTOOLS_PROVIDER_METHOD_UNAVAILABLE: ${request.method}`,
310
+ },
311
+ };
312
+ }
313
+ let traceId;
314
+ try {
315
+ const result = await this.#runProviderMethodExecution(provider.metadata, provider.instance, method.original, method.name, request.args, (nextTraceId) => {
316
+ traceId = nextTraceId;
317
+ });
318
+ return {
319
+ requestId: request.requestId,
320
+ status: 'ok',
321
+ ...(traceId === undefined ? {} : { traceId }),
322
+ result: this.#previewValue(result),
323
+ };
324
+ }
325
+ catch (error) {
326
+ return {
327
+ requestId: request.requestId,
328
+ status: 'error',
329
+ ...(traceId === undefined ? {} : { traceId }),
330
+ error: serializeDevtoolsError(error),
331
+ };
332
+ }
333
+ }
334
+ async replayCapsule(request) {
335
+ if (!this.#capsules) {
336
+ return {
337
+ requestId: request.requestId,
338
+ status: 'error',
339
+ error: {
340
+ name: 'ReplayDisabledError',
341
+ message: 'LUTRE_DEVTOOLS_REPLAY_DISABLED: Replay is not enabled.',
342
+ },
343
+ };
344
+ }
345
+ return this.#capsules.replay(request);
346
+ }
347
+ close() {
348
+ this.#capsules?.clear();
349
+ this.#providers.clear();
350
+ for (const restore of this.#applicationProviderRestorers.values())
351
+ restore();
352
+ this.#applicationProviderRestorers.clear();
353
+ }
354
+ #invokeProviderMethod(metadata, instance, invocation) {
355
+ const current = this.#context.current();
356
+ if (!current?.trace) {
357
+ return Reflect.apply(invocation.original, invocation.receiver, invocation.args);
358
+ }
359
+ const spanId = `span_${crypto.randomUUID()}`;
360
+ const startedAt = performance.now();
361
+ const name = `${metadata.name}.${invocation.name}`;
362
+ const capsule = metadata.scope === 'transient'
363
+ ? undefined
364
+ : this.#capsules?.createExecution(current.trace.traceId, spanId, {
365
+ executionId: `${metadata.providerId}.${invocation.name}`,
366
+ executionKind: 'provider.method',
367
+ graphNodeId: metadata.graphNodeId,
368
+ name,
369
+ }, {
370
+ input: invocation.args,
371
+ replay: (input, context) => this.#runReplayContext(context, () => this.#replayProviderMethod(metadata, instance, invocation.original, invocation.name, input)),
372
+ });
373
+ let result;
374
+ try {
375
+ result = Reflect.apply(invocation.original, invocation.receiver, invocation.args);
376
+ }
377
+ catch (error) {
378
+ this.#recordProviderMethodSpan(metadata, invocation, current.trace, spanId, startedAt, capsule, { status: 'error', error });
379
+ throw error;
380
+ }
381
+ // Promiseのsettlementを観測するとrejection handlerが追加され、Applicationの
382
+ // unhandled-rejection semanticsを変え得る。自動Traceは同期的に完結する呼び出しへ
383
+ // 限定し、明示実行であるPlaygroundとReplayだけがPromiseをawaitする。
384
+ if (result instanceof Promise) {
385
+ if (capsule)
386
+ this.#capsules?.discard(capsule.id);
387
+ return result;
388
+ }
389
+ this.#recordProviderMethodSpan(metadata, invocation, current.trace, spanId, startedAt, capsule, { status: 'ok', result });
390
+ return result;
391
+ }
392
+ #recordProviderMethodSpan(metadata, invocation, parent, spanId, startedAt, capsule, outcome) {
393
+ const name = `${metadata.name}.${invocation.name}`;
394
+ this.#notify('spanStarted', {
395
+ type: 'span.started',
396
+ runId: this.#runId,
397
+ seq: ++this.#seq,
398
+ timestamp: performance.timeOrigin + startedAt,
399
+ traceId: parent.traceId,
400
+ spanId,
401
+ parentSpanId: parent.spanId,
402
+ graphNodeId: metadata.graphNodeId,
403
+ kind: 'provider.method',
404
+ name,
405
+ ...(capsule === undefined
406
+ ? {}
407
+ : {
408
+ input: capsule.input,
409
+ capsuleId: capsule.id,
410
+ replayable: true,
411
+ replayModes: capsule.replayModes,
412
+ }),
413
+ });
414
+ const endedAt = performance.now();
415
+ const result = outcome.status === 'ok' ? this.#previewResult(outcome.result) : undefined;
416
+ this.#notify('spanEnded', {
417
+ type: 'span.ended',
418
+ runId: this.#runId,
419
+ seq: ++this.#seq,
420
+ timestamp: performance.timeOrigin + endedAt,
421
+ traceId: parent.traceId,
422
+ spanId,
423
+ parentSpanId: parent.spanId,
424
+ graphNodeId: metadata.graphNodeId,
425
+ durationMs: endedAt - startedAt,
426
+ status: outcome.status,
427
+ ...(result === undefined ? {} : { result }),
428
+ ...(outcome.status === 'error'
429
+ ? { error: serializeDevtoolsError(outcome.error) }
430
+ : {}),
431
+ });
432
+ }
433
+ async #replayProviderMethod(metadata, instance, original, method, input) {
434
+ return this.#runProviderMethodExecution(metadata, instance, original, method, input);
435
+ }
436
+ async #runProviderMethodExecution(metadata, instance, original, method, input, onTraceId) {
437
+ if (!Array.isArray(input)) {
438
+ throw new TypeError('LUTRE_DEVTOOLS_PROVIDER_ARGS_REQUIRED');
439
+ }
440
+ const scope = this.beginExecution({
441
+ executionId: `${metadata.providerId}.${method}`,
442
+ executionKind: 'provider.method',
443
+ graphNodeId: metadata.graphNodeId,
444
+ name: `${metadata.name}.${method}`,
445
+ }, {
446
+ input,
447
+ invoke: (nextInput) => this.#runProviderMethodExecution(metadata, instance, original, method, nextInput),
448
+ });
449
+ let result;
450
+ let hasResult = false;
451
+ try {
452
+ const invoke = () => {
453
+ const traceId = this.#context.current()?.trace?.traceId;
454
+ if (traceId)
455
+ onTraceId?.(traceId);
456
+ return Reflect.apply(original, instance, input);
457
+ };
458
+ result = await (scope.run ? scope.run(invoke) : invoke());
459
+ hasResult = true;
460
+ return result;
461
+ }
462
+ catch (error) {
463
+ scope.fail?.(error);
464
+ throw error;
465
+ }
466
+ finally {
467
+ if (hasResult)
468
+ scope.complete(result);
469
+ else
470
+ scope.complete();
471
+ }
472
+ }
473
+ #runReplayContext(context, operation) {
474
+ const current = this.#context.current();
475
+ const next = {
476
+ ...(current?.trace === undefined ? {} : { trace: current.trace }),
477
+ replay: context,
478
+ };
479
+ return this.#context.run(next, operation);
480
+ }
481
+ #previewResult(value) {
482
+ if (!this.#captureResults)
483
+ return undefined;
484
+ return this.#previewValue(value);
485
+ }
486
+ #previewValue(value) {
487
+ const capture = this.#options.capture;
488
+ return previewDevtoolsValue(value, {
489
+ ...(capture?.maxValueBytes === undefined
490
+ ? {}
491
+ : { maxValueBytes: capture.maxValueBytes }),
492
+ ...(capture?.maxDepth === undefined
493
+ ? {}
494
+ : { maxDepth: capture.maxDepth }),
495
+ ...(this.#options.redact === undefined
496
+ ? {}
497
+ : { redact: this.#options.redact }),
498
+ });
499
+ }
500
+ #identity(timestamp, traceId, spanId, graphNodeId) {
501
+ return {
502
+ runId: this.#runId,
503
+ seq: ++this.#seq,
504
+ timestamp,
505
+ traceId,
506
+ spanId,
507
+ ...(graphNodeId === undefined ? {} : { graphNodeId }),
508
+ };
509
+ }
510
+ #notify(key, event) {
511
+ try {
512
+ const callback = this.#observer[key];
513
+ callback?.(event);
514
+ }
515
+ catch {
516
+ // observerの失敗をApplicationの挙動へ伝播させてはならない。
517
+ }
518
+ }
519
+ }
@@ -0,0 +1,35 @@
1
+ export type DevtoolsValue = null | boolean | number | string | {
2
+ readonly $type: 'undefined';
3
+ } | {
4
+ readonly $type: 'bigint';
5
+ readonly value: string;
6
+ } | {
7
+ readonly $type: 'date';
8
+ readonly value: string;
9
+ } | {
10
+ readonly $type: 'bytes';
11
+ readonly base64: string;
12
+ } | {
13
+ readonly $type: 'map';
14
+ readonly entries: readonly [DevtoolsValue, DevtoolsValue][];
15
+ } | {
16
+ readonly $type: 'set';
17
+ readonly values: readonly DevtoolsValue[];
18
+ } | {
19
+ readonly $type: 'redacted';
20
+ } | readonly DevtoolsValue[] | {
21
+ readonly [key: string]: DevtoolsValue;
22
+ };
23
+ export interface DevtoolsValuePreview {
24
+ readonly value?: DevtoolsValue;
25
+ readonly preview: string;
26
+ readonly truncated?: boolean;
27
+ readonly redacted?: boolean;
28
+ readonly type?: string;
29
+ }
30
+ export interface DevtoolsValuePreviewOptions {
31
+ readonly maxValueBytes?: number;
32
+ readonly maxDepth?: number;
33
+ readonly redact?: readonly string[];
34
+ }
35
+ export declare function previewDevtoolsValue(value: unknown, options?: DevtoolsValuePreviewOptions): DevtoolsValuePreview;