@bluelibs/runner-dev 4.0.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 (215) hide show
  1. package/AI.md +411 -0
  2. package/README.md +1103 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +58 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/client/documentation.d.ts +8 -0
  7. package/dist/client/documentation.js +144 -0
  8. package/dist/client/documentation.js.map +1 -0
  9. package/dist/components/Documentation/Documentation.d.ts +8 -0
  10. package/dist/components/Documentation/Documentation.js +283 -0
  11. package/dist/components/Documentation/Documentation.js.map +1 -0
  12. package/dist/components/Documentation/components/DiagnosticsPanel.d.ts +7 -0
  13. package/dist/components/Documentation/components/DiagnosticsPanel.js +126 -0
  14. package/dist/components/Documentation/components/DiagnosticsPanel.js.map +1 -0
  15. package/dist/components/Documentation/components/EventCard.d.ts +8 -0
  16. package/dist/components/Documentation/components/EventCard.js +164 -0
  17. package/dist/components/Documentation/components/EventCard.js.map +1 -0
  18. package/dist/components/Documentation/components/HookCard.d.ts +8 -0
  19. package/dist/components/Documentation/components/HookCard.js +111 -0
  20. package/dist/components/Documentation/components/HookCard.js.map +1 -0
  21. package/dist/components/Documentation/components/MiddlewareCard.d.ts +8 -0
  22. package/dist/components/Documentation/components/MiddlewareCard.js +159 -0
  23. package/dist/components/Documentation/components/MiddlewareCard.js.map +1 -0
  24. package/dist/components/Documentation/components/ResourceCard.d.ts +8 -0
  25. package/dist/components/Documentation/components/ResourceCard.js +94 -0
  26. package/dist/components/Documentation/components/ResourceCard.js.map +1 -0
  27. package/dist/components/Documentation/components/Sidebar.d.ts +13 -0
  28. package/dist/components/Documentation/components/Sidebar.js +129 -0
  29. package/dist/components/Documentation/components/Sidebar.js.map +1 -0
  30. package/dist/components/Documentation/components/TagCard.d.ts +7 -0
  31. package/dist/components/Documentation/components/TagCard.js +75 -0
  32. package/dist/components/Documentation/components/TagCard.js.map +1 -0
  33. package/dist/components/Documentation/components/TaskCard.d.ts +8 -0
  34. package/dist/components/Documentation/components/TaskCard.js +77 -0
  35. package/dist/components/Documentation/components/TaskCard.js.map +1 -0
  36. package/dist/components/Documentation/index.d.ts +2 -0
  37. package/dist/components/Documentation/index.js +6 -0
  38. package/dist/components/Documentation/index.js.map +1 -0
  39. package/dist/components/Documentation/utils/formatting.d.ts +8 -0
  40. package/dist/components/Documentation/utils/formatting.js +84 -0
  41. package/dist/components/Documentation/utils/formatting.js.map +1 -0
  42. package/dist/components/ExampleComponent.d.ts +10 -0
  43. package/dist/components/ExampleComponent.js +48 -0
  44. package/dist/components/ExampleComponent.js.map +1 -0
  45. package/dist/generated/resolvers-types.d.ts +1523 -0
  46. package/dist/generated/resolvers-types.js +3 -0
  47. package/dist/generated/resolvers-types.js.map +1 -0
  48. package/dist/index.d.ts +61 -0
  49. package/dist/index.js +22 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/main.d.ts +1 -0
  52. package/dist/main.js +3 -0
  53. package/dist/main.js.map +1 -0
  54. package/dist/mcp/env.d.ts +5 -0
  55. package/dist/mcp/env.js +42 -0
  56. package/dist/mcp/env.js.map +1 -0
  57. package/dist/mcp/format.d.ts +50 -0
  58. package/dist/mcp/format.js +249 -0
  59. package/dist/mcp/format.js.map +1 -0
  60. package/dist/mcp/help.d.ts +20 -0
  61. package/dist/mcp/help.js +121 -0
  62. package/dist/mcp/help.js.map +1 -0
  63. package/dist/mcp/http.d.ts +6 -0
  64. package/dist/mcp/http.js +47 -0
  65. package/dist/mcp/http.js.map +1 -0
  66. package/dist/mcp/projectOverview.d.ts +2 -0
  67. package/dist/mcp/projectOverview.js +210 -0
  68. package/dist/mcp/projectOverview.js.map +1 -0
  69. package/dist/mcp/schema.d.ts +1 -0
  70. package/dist/mcp/schema.js +17 -0
  71. package/dist/mcp/schema.js.map +1 -0
  72. package/dist/mcp/tools/graphql.introspect.d.ts +2 -0
  73. package/dist/mcp/tools/graphql.introspect.js +19 -0
  74. package/dist/mcp/tools/graphql.introspect.js.map +1 -0
  75. package/dist/mcp/tools/graphql.mutation.d.ts +2 -0
  76. package/dist/mcp/tools/graphql.mutation.js +43 -0
  77. package/dist/mcp/tools/graphql.mutation.js.map +1 -0
  78. package/dist/mcp/tools/graphql.ping.d.ts +2 -0
  79. package/dist/mcp/tools/graphql.ping.js +23 -0
  80. package/dist/mcp/tools/graphql.ping.js.map +1 -0
  81. package/dist/mcp/tools/graphql.query.d.ts +2 -0
  82. package/dist/mcp/tools/graphql.query.js +42 -0
  83. package/dist/mcp/tools/graphql.query.js.map +1 -0
  84. package/dist/mcp/tools/graphql.schemaSdl.d.ts +2 -0
  85. package/dist/mcp/tools/graphql.schemaSdl.js +15 -0
  86. package/dist/mcp/tools/graphql.schemaSdl.js.map +1 -0
  87. package/dist/mcp/tools/help.read.d.ts +2 -0
  88. package/dist/mcp/tools/help.read.js +67 -0
  89. package/dist/mcp/tools/help.read.js.map +1 -0
  90. package/dist/mcp/tools/help.runner.d.ts +2 -0
  91. package/dist/mcp/tools/help.runner.js +55 -0
  92. package/dist/mcp/tools/help.runner.js.map +1 -0
  93. package/dist/mcp/tools/help.runnerDev.d.ts +2 -0
  94. package/dist/mcp/tools/help.runnerDev.js +56 -0
  95. package/dist/mcp/tools/help.runnerDev.js.map +1 -0
  96. package/dist/mcp.d.ts +1 -0
  97. package/dist/mcp.js +75 -0
  98. package/dist/mcp.js.map +1 -0
  99. package/dist/resources/dev.resource.d.ts +5 -0
  100. package/dist/resources/dev.resource.js +26 -0
  101. package/dist/resources/dev.resource.js.map +1 -0
  102. package/dist/resources/docs.generator.resource.d.ts +17 -0
  103. package/dist/resources/docs.generator.resource.js +230 -0
  104. package/dist/resources/docs.generator.resource.js.map +1 -0
  105. package/dist/resources/graphql-accumulator.resource.d.ts +7 -0
  106. package/dist/resources/graphql-accumulator.resource.js +41 -0
  107. package/dist/resources/graphql-accumulator.resource.js.map +1 -0
  108. package/dist/resources/introspector.resource.d.ts +129 -0
  109. package/dist/resources/introspector.resource.js +266 -0
  110. package/dist/resources/introspector.resource.js.map +1 -0
  111. package/dist/resources/introspector.tools.d.ts +47 -0
  112. package/dist/resources/introspector.tools.js +505 -0
  113. package/dist/resources/introspector.tools.js.map +1 -0
  114. package/dist/resources/live.resource.d.ts +80 -0
  115. package/dist/resources/live.resource.js +231 -0
  116. package/dist/resources/live.resource.js.map +1 -0
  117. package/dist/resources/server.resource.d.ts +38 -0
  118. package/dist/resources/server.resource.js +106 -0
  119. package/dist/resources/server.resource.js.map +1 -0
  120. package/dist/resources/swap.resource.d.ts +43 -0
  121. package/dist/resources/swap.resource.js +251 -0
  122. package/dist/resources/swap.resource.js.map +1 -0
  123. package/dist/resources/swap.tools.d.ts +31 -0
  124. package/dist/resources/swap.tools.js +207 -0
  125. package/dist/resources/swap.tools.js.map +1 -0
  126. package/dist/resources/telemetry.chain.d.ts +13 -0
  127. package/dist/resources/telemetry.chain.js +32 -0
  128. package/dist/resources/telemetry.chain.js.map +1 -0
  129. package/dist/resources/telemetry.resource.d.ts +1 -0
  130. package/dist/resources/telemetry.resource.js +90 -0
  131. package/dist/resources/telemetry.resource.js.map +1 -0
  132. package/dist/schema/context.d.ts +11 -0
  133. package/dist/schema/context.js +3 -0
  134. package/dist/schema/context.js.map +1 -0
  135. package/dist/schema/index.d.ts +7 -0
  136. package/dist/schema/index.js +72 -0
  137. package/dist/schema/index.js.map +1 -0
  138. package/dist/schema/model.d.ts +97 -0
  139. package/dist/schema/model.js +5 -0
  140. package/dist/schema/model.js.map +1 -0
  141. package/dist/schema/mutation.d.ts +3 -0
  142. package/dist/schema/mutation.js +112 -0
  143. package/dist/schema/mutation.js.map +1 -0
  144. package/dist/schema/query.d.ts +3 -0
  145. package/dist/schema/query.js +295 -0
  146. package/dist/schema/query.js.map +1 -0
  147. package/dist/schema/types/AllType.d.ts +3 -0
  148. package/dist/schema/types/AllType.js +149 -0
  149. package/dist/schema/types/AllType.js.map +1 -0
  150. package/dist/schema/types/BaseElementCommon.d.ts +11 -0
  151. package/dist/schema/types/BaseElementCommon.js +61 -0
  152. package/dist/schema/types/BaseElementCommon.js.map +1 -0
  153. package/dist/schema/types/DiagnosticsType.d.ts +2 -0
  154. package/dist/schema/types/DiagnosticsType.js +15 -0
  155. package/dist/schema/types/DiagnosticsType.js.map +1 -0
  156. package/dist/schema/types/EventType.d.ts +4 -0
  157. package/dist/schema/types/EventType.js +97 -0
  158. package/dist/schema/types/EventType.js.map +1 -0
  159. package/dist/schema/types/HookType.d.ts +4 -0
  160. package/dist/schema/types/HookType.js +123 -0
  161. package/dist/schema/types/HookType.js.map +1 -0
  162. package/dist/schema/types/LiveType.d.ts +33 -0
  163. package/dist/schema/types/LiveType.js +553 -0
  164. package/dist/schema/types/LiveType.js.map +1 -0
  165. package/dist/schema/types/MetaType.d.ts +3 -0
  166. package/dist/schema/types/MetaType.js +31 -0
  167. package/dist/schema/types/MetaType.js.map +1 -0
  168. package/dist/schema/types/MiddlewareType.d.ts +4 -0
  169. package/dist/schema/types/MiddlewareType.js +26 -0
  170. package/dist/schema/types/MiddlewareType.js.map +1 -0
  171. package/dist/schema/types/ResourceType.d.ts +2 -0
  172. package/dist/schema/types/ResourceType.js +145 -0
  173. package/dist/schema/types/ResourceType.js.map +1 -0
  174. package/dist/schema/types/RunTypes.d.ts +7 -0
  175. package/dist/schema/types/RunTypes.js +95 -0
  176. package/dist/schema/types/RunTypes.js.map +1 -0
  177. package/dist/schema/types/SwapType.d.ts +5 -0
  178. package/dist/schema/types/SwapType.js +42 -0
  179. package/dist/schema/types/SwapType.js.map +1 -0
  180. package/dist/schema/types/TagType.d.ts +6 -0
  181. package/dist/schema/types/TagType.js +48 -0
  182. package/dist/schema/types/TagType.js.map +1 -0
  183. package/dist/schema/types/TaskLikeCommon.d.ts +7 -0
  184. package/dist/schema/types/TaskLikeCommon.js +86 -0
  185. package/dist/schema/types/TaskLikeCommon.js.map +1 -0
  186. package/dist/schema/types/TaskType.d.ts +11 -0
  187. package/dist/schema/types/TaskType.js +188 -0
  188. package/dist/schema/types/TaskType.js.map +1 -0
  189. package/dist/schema/types/index.d.ts +13 -0
  190. package/dist/schema/types/index.js +44 -0
  191. package/dist/schema/types/index.js.map +1 -0
  192. package/dist/schema/types/middleware/UsageTypes.d.ts +3 -0
  193. package/dist/schema/types/middleware/UsageTypes.js +23 -0
  194. package/dist/schema/types/middleware/UsageTypes.js.map +1 -0
  195. package/dist/schema/types/middleware/common.d.ts +6 -0
  196. package/dist/schema/types/middleware/common.js +156 -0
  197. package/dist/schema/types/middleware/common.js.map +1 -0
  198. package/dist/schema/utils.d.ts +12 -0
  199. package/dist/schema/utils.js +35 -0
  200. package/dist/schema/utils.js.map +1 -0
  201. package/dist/ui/index.html +20 -0
  202. package/dist/ui/static/index-D-NS5aw1.js +40 -0
  203. package/dist/utils/json-schema-to-readable.d.ts +1 -0
  204. package/dist/utils/json-schema-to-readable.js +256 -0
  205. package/dist/utils/json-schema-to-readable.js.map +1 -0
  206. package/dist/utils/path.d.ts +16 -0
  207. package/dist/utils/path.js +101 -0
  208. package/dist/utils/path.js.map +1 -0
  209. package/dist/utils/react-ssr.d.ts +9 -0
  210. package/dist/utils/react-ssr.js +36 -0
  211. package/dist/utils/react-ssr.js.map +1 -0
  212. package/dist/utils/zod.d.ts +6 -0
  213. package/dist/utils/zod.js +39 -0
  214. package/dist/utils/zod.js.map +1 -0
  215. package/package.json +83 -0
package/README.md ADDED
@@ -0,0 +1,1103 @@
1
+ ## Welcome
2
+
3
+ Runner Dev Tools provide introspection, live telemetry, and a GraphQL API to explore and query your running Runner app.
4
+
5
+ The way it works, is that this is a resource that opens a graphql server which opens your application to introspection.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @bluelibs/runner-dev
11
+ ```
12
+
13
+ ```ts
14
+ import { dev } from "@bluelibs/runner-dev";
15
+
16
+ const app = resource({
17
+ register: [
18
+ // your resources,
19
+ dev, // if you are fine with defaults or
20
+ dev.with({
21
+ port: 1337, // default,
22
+ maxEntries: 10000, // how many logs to keep in the store.
23
+ }),
24
+ ],
25
+ });
26
+ ```
27
+
28
+ ## What you get
29
+
30
+ - Introspector: programmatic API to inspect tasks, hooks, resources, events, middleware, and diagnostics (including file paths, contents)
31
+ - Live: in-memory logs and event emissions
32
+ - GraphQL server: deep graph navigation over your app’s topology and live data
33
+ - MCP server: allow your AI to do introspection for you.
34
+
35
+ ## Quickstart
36
+
37
+ Register the Dev resources in your Runner root:
38
+
39
+ ```ts
40
+ import { resource } from "@bluelibs/runner";
41
+ import { dev } from "@bluelibs/runner-dev";
42
+
43
+ const register = [
44
+ // rest of your app elements
45
+ ];
46
+
47
+ if (process.env.DEV_MODE) {
48
+ register.push(dev);
49
+ }
50
+
51
+ export const app = resource({
52
+ id: "app",
53
+ register: [
54
+ // You can omit .with() if you are fine with defaults.
55
+ dev.with({
56
+ port: 1337, // default
57
+ maxEntries: 1000, // default
58
+ }),
59
+ // rest of your app.
60
+ ],
61
+ });
62
+ ```
63
+
64
+ Add it as an MCP Server:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "mcp-graphql": {
70
+ "description": "MCP Server for Active Running Context App",
71
+ "command": "npx",
72
+ "args": ["runner-dev", "mcp"],
73
+ "env": {
74
+ "ENDPOINT": "http://localhost:1337/graphql",
75
+ "ALLOW_MUTATIONS": "true"
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ Then start your app as usual. The Dev GraphQL server will be available at http://localhost:1337/graphql.
83
+
84
+ ### CLI usage (MCP server)
85
+
86
+ After installing, you can start the MCP server from this package via stdio.
87
+
88
+ Using npx:
89
+
90
+ ```bash
91
+ ENDPOINT=http://localhost:1337/graphql npx -y @bluelibs/runner-dev mcp
92
+ ```
93
+
94
+ Optional environment variables:
95
+
96
+ - `ALLOW_MUTATIONS=true` to enable `graphql.mutation`
97
+ - `HEADERS='{"Authorization":"Bearer token"}'` to pass extra headers
98
+
99
+ Available tools once connected:
100
+
101
+ - `graphql.query` — run read-only queries
102
+ - `graphql.mutation` — run mutations (requires `ALLOW_MUTATIONS=true`)
103
+ - `graphql.introspect` — fetch schema
104
+ - `graphql.ping` — reachability check
105
+ - `project.overview` — dynamic Markdown overview aggregated from the API
106
+
107
+ ## Programmatic Introspection (without GraphQL)
108
+
109
+ ```ts
110
+ import { resource } from "@bluelibs/runner";
111
+ import type { Introspector } from "@bluelibs/runner-dev/dist/resources/introspector.resource";
112
+ import { introspector } from "@bluelibs/runner-dev/dist/resources/introspector.resource";
113
+
114
+ export const probe = resource({
115
+ id: "probe",
116
+ dependencies: { introspector },
117
+ async init(_c, { introspector }: { introspector: Introspector }) {
118
+ const tasks = introspector.getTasks(); // Task[]
119
+ const hooks = introspector.getHooks(); // Hook[]
120
+ const resources = introspector.getResources(); // Resource[]
121
+ const events = introspector.getEvents(); // Event[]
122
+ const middlewares = introspector.getMiddlewares(); // Middleware[]
123
+
124
+ const deps = introspector.getDependencies(tasks[0]); // tasks/hooks/resources/emitters
125
+
126
+ // Diagnostics
127
+ const diagnostics = introspector.getDiagnostics();
128
+ // Or granular helpers
129
+ // introspector.getOrphanEvents();
130
+ // introspector.getUnemittedEvents();
131
+ // introspector.getUnusedMiddleware();
132
+ // introspector.getMissingFiles();
133
+ // introspector.getOverrideConflicts();
134
+ },
135
+ });
136
+ ```
137
+
138
+ ## GraphQL API Highlights
139
+
140
+ All arrays are non-null lists with non-null items, and ids are complemented by resolved fields for deep traversal.
141
+
142
+ - Common
143
+
144
+ - `BaseElement`: `id: ID!`, `meta: Meta`, `filePath: String`, `markdownDescription: String!`, `fileContents(startLine: Int, endLine: Int): String`
145
+ - `Meta`: `title: String`, `description: String`, `tags: [MetaTagUsage!]!`
146
+ - `MetaTagUsage`: `id: ID!`, `config: String`
147
+
148
+ - Query root
149
+
150
+ - `all(idIncludes: ID): [BaseElement!]!`
151
+ - `event(id: ID!): Event`, `events(filter: EventFilterInput): [Event!]!`
152
+ - `task(id: ID!): Task`, `tasks(idIncludes: ID): [Task!]!`
153
+ - `hooks(idIncludes: ID): [Hook!]!`
154
+ - `middleware(id: ID!): Middleware`, `middlewares(idIncludes: ID): [Middleware!]!`
155
+ - `taskMiddlewares(idIncludes: ID): [TaskMiddleware!]!`
156
+ - `resourceMiddlewares(idIncludes: ID): [ResourceMiddleware!]!`
157
+ - `resource(id: ID!): Resource`, `resources(idIncludes: ID): [Resource!]!`
158
+ - `root: Resource`
159
+ - `swappedTasks: [SwappedTask!]!`
160
+ - `live: Live!`
161
+ - `diagnostics: [Diagnostic!]!`
162
+
163
+ - All
164
+
165
+ - `id`, `meta`, `filePath`, `fileContents`, `markdownDescription`
166
+
167
+ - Tasks/Hooks
168
+
169
+ - Shared: `emits: [String!]!`, `emitsResolved: [Event!]!`
170
+ - Shared: `dependsOn: [String!]!`
171
+ - Shared: `middleware: [String!]!`, `middlewareResolved: [TaskMiddleware!]!`, `middlewareResolvedDetailed: [TaskMiddlewareUsage!]!`
172
+ - Shared: `overriddenBy: String`, `registeredBy: String`, `registeredByResolved: Resource`
173
+ - Task-specific: `inputSchema: String`, `inputSchemaReadable: String`, `dependsOnResolved { tasks { id } resources { id } emitters { id } }`
174
+ - Hook-specific: `event: String!`, `hookOrder: Int`
175
+
176
+ - Resources
177
+
178
+ - `config: String`, `context: String`, `configSchema: String`, `configSchemaReadable: String`
179
+ - `middleware`, `middlewareResolved: [ResourceMiddleware!]!`, `middlewareResolvedDetailed: [TaskMiddlewareUsage!]!`
180
+ - `overrides`, `overridesResolved`
181
+ - `registers`, `registersResolved`
182
+ - `usedBy: [Task!]!`
183
+ - `emits: [Event!]!` (inferred from task/hook emissions)
184
+ - `dependsOn: [String!]!`, `dependsOnResolved: [Resource!]!`
185
+ - `registeredBy: String`, `registeredByResolved: Resource`
186
+
187
+ - Events
188
+
189
+ - `emittedBy: [String!]!`, `emittedByResolved: [Hook!]!`
190
+ - `listenedToBy: [String!]!`, `listenedToByResolved: [Hook!]!`
191
+ - `payloadSchema: String`, `payloadSchemaReadable: String`
192
+ - `registeredBy: String`, `registeredByResolved: Resource`
193
+
194
+ - TaskMiddleware
195
+
196
+ - `global: GlobalMiddleware`
197
+ - `usedBy: [Task!]!`, `usedByDetailed: [MiddlewareTaskUsage!]!`
198
+ - `emits: [Event!]!`
199
+ - `overriddenBy: String`, `registeredBy: String`, `registeredByResolved: Resource`
200
+
201
+ - ResourceMiddleware
202
+
203
+ - `global: GlobalMiddleware`
204
+ - `usedBy: [Resource!]!`, `usedByDetailed: [MiddlewareResourceUsage!]!`
205
+ - `emits: [Event!]!`
206
+ - `overriddenBy: String`, `registeredBy: String`, `registeredByResolved: Resource`
207
+
208
+ - Live
209
+
210
+ - `logs(afterTimestamp: Float, last: Int, filter: LogFilterInput): [LogEntry!]!`
211
+ - `LogEntry { timestampMs, level, message, data, correlationId }`
212
+ - `LogFilterInput { levels: [LogLevelEnum!], messageIncludes: String, correlationIds: [String!] }`
213
+ - `emissions(afterTimestamp: Float, last: Int, filter: EmissionFilterInput): [EmissionEntry!]!`
214
+ - `EmissionEntry { timestampMs, eventId, emitterId, payload, correlationId }`
215
+ - `EmissionFilterInput { eventIds: [String!], emitterIds: [String!] }`
216
+ - `errors(afterTimestamp: Float, last: Int, filter: ErrorFilterInput): [ErrorEntry!]!`
217
+ - `ErrorEntry { timestampMs, sourceId, sourceKind, message, stack, data, correlationId }`
218
+ - `ErrorFilterInput { sourceKinds: [SourceKindEnum!], sourceIds: [ID!], messageIncludes: String }`
219
+ - `runs(afterTimestamp: Float, last: Int, filter: RunFilterInput): [RunRecord!]!`
220
+ - `RunRecord { timestampMs, nodeId, nodeKind, durationMs, ok, error, parentId, rootId, correlationId }`
221
+ - `RunFilterInput { nodeKinds: [NodeKindEnum!], nodeIds: [String!], ok: Boolean, parentIds: [String!], rootIds: [String!] }`
222
+
223
+ Enums: `LogLevelEnum` = `trace|debug|info|warn|error|fatal|log`, `SourceKindEnum` = `TASK|HOOK|RESOURCE|MIDDLEWARE|INTERNAL`, `NodeKindEnum` = `TASK|HOOK`.
224
+
225
+ - Diagnostics
226
+
227
+ - `Diagnostic { severity: String!, code: String!, message: String!, nodeId: ID, nodeKind: String }`
228
+ - Exposed via `diagnostics: [Diagnostic!]!` on the root query; diagnostics are computed from the in-memory introspected graph with safe filesystem checks.
229
+ - Example codes: `ORPHAN_EVENT`, `UNEMITTED_EVENT`, `UNUSED_MIDDLEWARE`, `MISSING_FILE`, `OVERRIDE_CONFLICT`.
230
+
231
+ ### Example Queries
232
+
233
+ - Explore tasks and dependencies deeply
234
+
235
+ ```graphql
236
+ query {
237
+ tasks {
238
+ id
239
+ filePath
240
+ emits
241
+ emitsResolved {
242
+ id
243
+ }
244
+ dependsOn
245
+ middleware
246
+ middlewareResolved {
247
+ id
248
+ }
249
+ dependsOnResolved {
250
+ tasks {
251
+ id
252
+ }
253
+ resources {
254
+ id
255
+ }
256
+ emitters {
257
+ id
258
+ }
259
+ }
260
+ }
261
+ }
262
+ ```
263
+
264
+ - Diagnostics
265
+
266
+ ```graphql
267
+ query {
268
+ diagnostics {
269
+ severity
270
+ code
271
+ message
272
+ nodeId
273
+ nodeKind
274
+ }
275
+ }
276
+ ```
277
+
278
+ - Traverse from middleware to dependents, then back to their middleware
279
+
280
+ ```graphql
281
+ query {
282
+ middlewares {
283
+ id
284
+ usedByTasksResolved {
285
+ id
286
+ middlewareResolved {
287
+ id
288
+ }
289
+ }
290
+ usedByResourcesResolved {
291
+ id
292
+ }
293
+ emits {
294
+ id
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ - Events and hooks
301
+
302
+ ```graphql
303
+ query {
304
+ events {
305
+ id
306
+ emittedBy
307
+ emittedByResolved {
308
+ id
309
+ }
310
+ listenedToBy
311
+ listenedToByResolved {
312
+ id
313
+ }
314
+ }
315
+ }
316
+ ```
317
+
318
+ ## Live Telemetry
319
+
320
+ The `live` resource records:
321
+
322
+ - Logs emitted via `globals.events.log`
323
+ - All event emissions (via an internal global `on: "*"` hook)
324
+
325
+ GraphQL (basic):
326
+
327
+ ```graphql
328
+ query {
329
+ live {
330
+ logs(afterTimestamp: 0) {
331
+ timestampMs
332
+ level
333
+ message
334
+ data # stringified JSON if object, otherwise null
335
+ }
336
+ emissions(afterTimestamp: 0) {
337
+ timestampMs
338
+ eventId
339
+ emitterId
340
+ payload # stringified JSON if object, otherwise null
341
+ }
342
+ errors(afterTimestamp: 0) {
343
+ timestampMs
344
+ sourceId
345
+ sourceKind
346
+ message
347
+ }
348
+ runs(afterTimestamp: 0) {
349
+ timestampMs
350
+ nodeId
351
+ nodeKind
352
+ durationMs
353
+ ok
354
+ parentId
355
+ rootId
356
+ correlationId
357
+ }
358
+ }
359
+ }
360
+ ```
361
+
362
+ Filter by timestamp (ms) to retrieve only recent entries.
363
+
364
+ GraphQL (with filters and last):
365
+
366
+ ```graphql
367
+ query {
368
+ live {
369
+ logs(
370
+ last: 100
371
+ filter: { levels: [debug, error], messageIncludes: "probe" }
372
+ ) {
373
+ timestampMs
374
+ level
375
+ message
376
+ correlationId
377
+ }
378
+ emissions(
379
+ last: 50
380
+ filter: { eventIds: ["evt.hello"], emitterIds: ["task.id"] }
381
+ ) {
382
+ eventId
383
+ emitterId
384
+ }
385
+ errors(
386
+ last: 10
387
+ filter: { sourceKinds: [TASK, RESOURCE], messageIncludes: "boom" }
388
+ ) {
389
+ sourceKind
390
+ message
391
+ }
392
+ runs(afterTimestamp: 0, last: 5, filter: { ok: true, nodeKinds: [TASK] }) {
393
+ nodeId
394
+ durationMs
395
+ ok
396
+ correlationId
397
+ }
398
+ }
399
+ }
400
+ ```
401
+
402
+ ### Live system health
403
+
404
+ - **memory: `MemoryStats!`**
405
+ - Fields: `heapUsed` (bytes), `heapTotal` (bytes), `rss` (bytes)
406
+ - **cpu: `CpuStats!`**
407
+ - Fields: `usage` (0..1 event loop utilization), `loadAverage` (1‑minute load avg)
408
+ - **eventLoop(reset: Boolean): `EventLoopStats!`**
409
+ - Fields: `lag` (ms, avg delay via `monitorEventLoopDelay`)
410
+ - Args: `reset` optionally clears the histogram after reading
411
+ - **gc(windowMs: Float): `GcStats!`**
412
+ - Fields: `collections` (count), `duration` (ms)
413
+ - Args: `windowMs` returns stats only within the last window; omitted = totals since process start
414
+
415
+ Example query:
416
+
417
+ ```graphql
418
+ query SystemHealth {
419
+ live {
420
+ memory {
421
+ heapUsed
422
+ heapTotal
423
+ rss
424
+ }
425
+ cpu {
426
+ usage
427
+ loadAverage
428
+ }
429
+ eventLoop(reset: true) {
430
+ lag
431
+ }
432
+ gc(windowMs: 10000) {
433
+ collections
434
+ duration
435
+ }
436
+ }
437
+ }
438
+ ```
439
+
440
+ Notes:
441
+
442
+ - `heap*` and `rss` are bytes.
443
+ - `cpu.usage` is a ratio; `loadAverage` is 1‑minute OS load.
444
+ - `eventLoop.lag` may be 0 if `monitorEventLoopDelay` is unavailable.
445
+
446
+ ### Correlation and call chains
447
+
448
+ - What is correlationId? An opaque UUID (via `crypto.randomUUID()`) created for the first task in a run chain.
449
+ - How is it formed?
450
+ - When a task starts, a middleware opens an AsyncLocalStorage scope containing:
451
+ - `correlationId`: a UUID for the chain
452
+ - `chain`: ordered array of node ids representing the call path
453
+ - Nested tasks and listeners reuse the same AsyncLocalStorage scope, so the same `correlationId` flows throughout the chain.
454
+ - What does it contain? Only a UUID string. No payload, no PII.
455
+ - Where is it recorded?
456
+ - `logs.correlationId`
457
+ - `emissions.correlationId`
458
+ - `errors.correlationId`
459
+ - `runs.correlationId` plus `runs.parentId` and `runs.rootId` for chain topology
460
+ - How to use it
461
+ - Read a recent run to discover a correlation id, then filter logs by it:
462
+
463
+ ```graphql
464
+ query TraceByCorrelation($ts: Float, $cid: String!) {
465
+ live {
466
+ runs(afterTimestamp: $ts, last: 10) {
467
+ nodeId
468
+ parentId
469
+ rootId
470
+ correlationId
471
+ }
472
+ logs(last: 100, filter: { correlationIds: [$cid] }) {
473
+ timestampMs
474
+ level
475
+ message
476
+ correlationId
477
+ }
478
+ }
479
+ }
480
+ ```
481
+
482
+ ## Emitting Events (Runner-native)
483
+
484
+ - Define an event:
485
+
486
+ ```ts
487
+ import { event } from "@bluelibs/runner";
488
+
489
+ export const userCreated = event<{ id: string; name: string }>({
490
+ id: "evt.user.created",
491
+ });
492
+ ```
493
+
494
+ - Use it in a task:
495
+
496
+ ```ts
497
+ import { task } from "@bluelibs/runner";
498
+ import { userCreated } from "./events";
499
+
500
+ export const createUser = task({
501
+ id: "task.user.create",
502
+ dependencies: { userCreated },
503
+ async run(input: { name: string }, { userCreated }) {
504
+ const id = crypto.randomUUID();
505
+ await userCreated({ id, name: input.name });
506
+ return { id };
507
+ },
508
+ });
509
+ ```
510
+
511
+ - Emit logs:
512
+
513
+ ```ts
514
+ import { globals, task } from "@bluelibs/runner";
515
+
516
+ export const logSomething = task({
517
+ id: "task.log",
518
+ dependencies: { logger: globals.resources.logger },
519
+ async run(_i, { logger }) {
520
+ logger.info("Hello world!");
521
+ },
522
+ });
523
+ ```
524
+
525
+ ## Notes on Overrides
526
+
527
+ - If a resource overrides another registerable, the overridden node remains discoverable but marked with `overriddenBy`.
528
+ - Only the active definition exists; we do not retain a shadow copy of the original.
529
+
530
+ ## Guarantees and DX
531
+
532
+ - No `any` in APIs; strong types for nodes and relations
533
+ - Non-null lists with non-null items (`[T!]!`) in GraphQL
534
+ - Deep “resolved” fields for easy graph traversal
535
+ - File-aware enhancements:
536
+ - `filePath` everywhere
537
+ - `fileContents` and `markdownDescription` on `all` (computed on demand)
538
+
539
+ ## Development
540
+
541
+ - Library targets `@bluelibs/runner` v4+
542
+ - GraphQL built with Apollo Server 5
543
+ - Tests cover:
544
+ - Node discovery, dependencies, and emissions
545
+ - Overrides, middleware usage, and deep traversal
546
+ - Live logs and emissions (with timestamp filtering)
547
+ - Contributions welcome!
548
+
549
+ ### Type-safe GraphQL resolvers
550
+
551
+ - We generate resolver arg types from the schema using GraphQL Code Generator.
552
+ - Run this after any schema change:
553
+
554
+ ```bash
555
+ npm run codegen
556
+ ```
557
+
558
+ - Generated types are in `src/generated/resolvers-types.ts` and used in schema resolvers (for example `LiveLogsArgs`, `QueryEventArgs`).
559
+
560
+ ## 🔥 Hot-Swapping Debugging System
561
+
562
+ **Revolutionary live debugging feature that allows AI assistants and developers to dynamically replace task run functions in live applications.**
563
+
564
+ ### Overview
565
+
566
+ The hot-swapping system enables:
567
+
568
+ - **Live Function Replacement**: Replace any task's `run` function with new TypeScript/JavaScript code without restarting the application
569
+ - **TypeScript Compilation**: Automatic compilation and validation of swapped code
570
+ - **GraphQL API**: Remote swap operations via GraphQL mutations
571
+ - **Live Telemetry Integration**: Real-time capture of debug logs from swapped functions
572
+ - **Rollback Support**: Easy restoration to original functions
573
+ - **Type Safety**: 100% type-safe implementation with comprehensive error handling
574
+
575
+ ### Quick Setup
576
+
577
+ Add the swap manager to your app:
578
+
579
+ ```ts
580
+ import { resource } from "@bluelibs/runner";
581
+ import { resources as dev } from "@bluelibs/runner-dev";
582
+
583
+ export const app = resource({
584
+ id: "app",
585
+ register: [
586
+ // Core dev resources
587
+ dev.live,
588
+ dev.introspector,
589
+
590
+ // Add the swap manager for hot-swapping
591
+ dev.swapManager,
592
+
593
+ // GraphQL server with swap mutations
594
+ dev.server.with({ port: 1337 }),
595
+ ],
596
+ });
597
+ ```
598
+
599
+ ### GraphQL API
600
+
601
+ #### Queries
602
+
603
+ **Get currently swapped tasks:**
604
+
605
+ ```graphql
606
+ query {
607
+ swappedTasks {
608
+ taskId
609
+ swappedAt
610
+ originalCode
611
+ }
612
+ }
613
+ ```
614
+
615
+ #### Mutations
616
+
617
+ **Swap a task's run function:**
618
+
619
+ ```graphql
620
+ mutation SwapTask($taskId: ID!, $runCode: String!) {
621
+ swapTask(taskId: $taskId, runCode: $runCode) {
622
+ success
623
+ error
624
+ taskId
625
+ }
626
+ }
627
+ ```
628
+
629
+ **Restore original function:**
630
+
631
+ ```graphql
632
+ mutation UnswapTask($taskId: ID!) {
633
+ unswapTask(taskId: $taskId) {
634
+ success
635
+ error
636
+ taskId
637
+ }
638
+ }
639
+ ```
640
+
641
+ **Restore all swapped tasks:**
642
+
643
+ ```graphql
644
+ mutation UnswapAllTasks {
645
+ unswapAllTasks {
646
+ success
647
+ error
648
+ taskId
649
+ }
650
+ }
651
+ ```
652
+
653
+ ### Usage Examples
654
+
655
+ #### Basic Function Swapping
656
+
657
+ Replace a task's logic with enhanced debugging:
658
+
659
+ ```graphql
660
+ mutation {
661
+ swapTask(
662
+ taskId: "user.create"
663
+ runCode: """
664
+ async function run(input, deps) {
665
+ // Add comprehensive logging
666
+ if (deps.emitLog) {
667
+ await deps.emitLog({
668
+ timestamp: new Date(),
669
+ level: "info",
670
+ message: "🔍 DEBUG: Creating user started",
671
+ data: { input }
672
+ });
673
+ }
674
+
675
+ // Enhanced validation
676
+ if (!input.email || !input.email.includes('@')) {
677
+ throw new Error('Invalid email address');
678
+ }
679
+
680
+ // Original logic with debugging
681
+ const result = {
682
+ id: crypto.randomUUID(),
683
+ email: input.email,
684
+ createdAt: new Date().toISOString(),
685
+ debugInfo: {
686
+ swappedAt: Date.now(),
687
+ inputValidated: true
688
+ }
689
+ };
690
+
691
+ if (deps.emitLog) {
692
+ await deps.emitLog({
693
+ timestamp: new Date(),
694
+ level: "info",
695
+ message: "🔍 DEBUG: User created successfully",
696
+ data: { result }
697
+ });
698
+ }
699
+
700
+ return result;
701
+ }
702
+ """
703
+ ) {
704
+ success
705
+ error
706
+ }
707
+ }
708
+ ```
709
+
710
+ #### TypeScript Support
711
+
712
+ The system supports full TypeScript syntax:
713
+
714
+ ```graphql
715
+ mutation {
716
+ swapTask(
717
+ taskId: "data.processor"
718
+ runCode: """
719
+ async function run(input: { items: string[] }, deps: any): Promise<{ processed: number }> {
720
+ const items: string[] = input.items || [];
721
+ let processed: number = 0;
722
+
723
+ for (const item of items) {
724
+ if (typeof item === 'string' && item.length > 0) {
725
+ processed++;
726
+ }
727
+ }
728
+
729
+ return { processed };
730
+ }
731
+ """
732
+ ) {
733
+ success
734
+ error
735
+ }
736
+ }
737
+ ```
738
+
739
+ #### Arrow Functions and Function Bodies
740
+
741
+ Multiple code formats are supported:
742
+
743
+ ```graphql
744
+ # Arrow function
745
+ mutation {
746
+ swapTask(
747
+ taskId: "simple.task"
748
+ runCode: "() => ({ message: 'Hello from arrow function!' })"
749
+ ) {
750
+ success
751
+ }
752
+ }
753
+
754
+ # Function body only
755
+ mutation {
756
+ swapTask(
757
+ taskId: "another.task"
758
+ runCode: """
759
+ const result = { timestamp: Date.now() };
760
+ return result;
761
+ """
762
+ ) {
763
+ success
764
+ }
765
+ }
766
+ ```
767
+
768
+ ### Live Telemetry Integration
769
+
770
+ Swapped functions can emit logs that are captured by the live telemetry system:
771
+
772
+ ```graphql
773
+ # After swapping with debug logging, query the logs
774
+ query RecentDebugLogs {
775
+ live {
776
+ logs(last: 50, filter: { messageIncludes: "🔍 DEBUG" }) {
777
+ timestampMs
778
+ level
779
+ message
780
+ data
781
+ correlationId
782
+ }
783
+ }
784
+ }
785
+ ```
786
+
787
+ ### Programmatic Usage
788
+
789
+ Use the swap manager directly in your code:
790
+
791
+ ```ts
792
+ import { resource } from "@bluelibs/runner";
793
+ import type { SwapManager } from "@bluelibs/runner-dev/dist/resources/swap.resource";
794
+ import { swapManager } from "@bluelibs/runner-dev/dist/resources/swap.resource";
795
+
796
+ export const debugger = resource({
797
+ id: "my.debugger",
798
+ dependencies: { swapManager },
799
+ async init(_c, { swapManager }: { swapManager: SwapManager }) {
800
+ // Swap a task programmatically
801
+ const result = await swapManager.swap("user.create", `
802
+ async function run(input, deps) {
803
+ console.log("Debug: Enhanced user creation");
804
+ return { id: "debug-user", ...input };
805
+ }
806
+ `);
807
+
808
+ if (result.success) {
809
+ console.log(`Task ${result.taskId} swapped successfully`);
810
+ } else {
811
+ console.error(`Swap failed: ${result.error}`);
812
+ }
813
+
814
+ // Check what's currently swapped
815
+ const swappedTasks = swapManager.getSwappedTasks();
816
+ console.log(`Currently swapped: ${swappedTasks.map(t => t.taskId).join(', ')}`);
817
+
818
+ // Restore original function
819
+ await swapManager.unswap("user.create");
820
+ },
821
+ });
822
+ ```
823
+
824
+ ### Error Handling
825
+
826
+ The system provides comprehensive error handling:
827
+
828
+ ```graphql
829
+ mutation {
830
+ swapTask(taskId: "nonexistent.task", runCode: "invalid javascript code {") {
831
+ success # false
832
+ error # "Task 'nonexistent.task' not found" or "Compilation failed: ..."
833
+ taskId # "nonexistent.task"
834
+ }
835
+ }
836
+ ```
837
+
838
+ ### Safety and Best Practices
839
+
840
+ #### Type Safety
841
+
842
+ - No `as any` usage throughout the implementation
843
+ - Full TypeScript type checking and compilation
844
+ - Comprehensive error validation and reporting
845
+
846
+ #### Security Considerations
847
+
848
+ - Code is executed via `eval()` in the Node.js context
849
+ - Intended for development/debugging environments only
850
+ - Swapped functions have access to the same context as original functions
851
+
852
+ #### Best Practices
853
+
854
+ - Use descriptive debug messages in swapped functions
855
+ - Leverage the logging system for telemetry capture
856
+ - Test swapped functions thoroughly before deployment
857
+ - Always restore original functions after debugging
858
+
859
+ #### Error Recovery
860
+
861
+ - Failed swaps don't affect the original function
862
+ - State tracking prevents inconsistencies
863
+ - Easy rollback with `unswapAllTasks` mutation
864
+
865
+ ### AI Assistant Integration
866
+
867
+ This system is specifically designed for AI debugging workflows:
868
+
869
+ 1. **AI analyzes application behavior** via introspection and live telemetry
870
+ 2. **AI identifies issues** in specific tasks or functions
871
+ 3. **AI generates enhanced debug code** with additional logging and validation
872
+ 4. **AI swaps the function remotely** via GraphQL mutations
873
+ 5. **AI monitors enhanced telemetry** to understand the issue
874
+ 6. **AI restores original function** once debugging is complete
875
+
876
+ ### Remote Task Execution
877
+
878
+ The system provides `invokeTask` functionality for remotely executing tasks with JSON input/output serialization, perfect for AI-driven debugging and testing.
879
+
880
+ #### Basic Task Invocation
881
+
882
+ ```graphql
883
+ mutation {
884
+ invokeTask(taskId: "user.create") {
885
+ success
886
+ error
887
+ taskId
888
+ result
889
+ executionTimeMs
890
+ invocationId
891
+ }
892
+ }
893
+ ```
894
+
895
+ #### Task Invocation with Input
896
+
897
+ ```graphql
898
+ mutation {
899
+ invokeTask(
900
+ taskId: "user.create"
901
+ inputJson: "{\"email\": \"test@example.com\", \"name\": \"John Doe\"}"
902
+ ) {
903
+ success
904
+ result
905
+ executionTimeMs
906
+ }
907
+ }
908
+ ```
909
+
910
+ #### JavaScript Input Evaluation
911
+
912
+ For advanced debugging scenarios, use `evalInput: true` to evaluate JavaScript expressions instead of parsing JSON:
913
+
914
+ ```graphql
915
+ mutation {
916
+ invokeTask(
917
+ taskId: "data.processor"
918
+ inputJson: """
919
+ {
920
+ timestamp: new Date("2023-01-01"),
921
+ data: [1, 2, 3].map(x => x * 2),
922
+ config: {
923
+ retries: Math.max(3, process.env.NODE_ENV === 'prod' ? 5 : 1),
924
+ timeout: 30 * 1000
925
+ },
926
+ processData: (items) => items.filter(x => x > 0)
927
+ }
928
+ """
929
+ evalInput: true
930
+ ) {
931
+ success
932
+ result
933
+ executionTimeMs
934
+ }
935
+ }
936
+ ```
937
+
938
+ #### Pure Mode (Bypass Middleware)
939
+
940
+ Pure mode executes tasks with computed dependencies directly from the store, bypassing the middleware pipeline and authentication systems for clean testing:
941
+
942
+ ```graphql
943
+ mutation {
944
+ invokeTask(
945
+ taskId: "user.create"
946
+ inputJson: "{\"email\": \"test@example.com\"}"
947
+ pure: true
948
+ ) {
949
+ success
950
+ result
951
+ executionTimeMs
952
+ }
953
+ }
954
+ ```
955
+
956
+ #### AI Debugging Workflow
957
+
958
+ 1. **Swap task with enhanced debugging**:
959
+
960
+ ```graphql
961
+ mutation {
962
+ swapTask(
963
+ taskId: "user.create"
964
+ runCode: """
965
+ async function run(input, deps) {
966
+ console.log('Input received:', input);
967
+ const result = { id: Math.random(), ...input };
968
+ console.log('Result generated:', result);
969
+ return result;
970
+ }
971
+ """
972
+ ) {
973
+ success
974
+ }
975
+ }
976
+ ```
977
+
978
+ 2. **Invoke task to test behavior**:
979
+
980
+ ```graphql
981
+ mutation {
982
+ invokeTask(
983
+ taskId: "user.create"
984
+ inputJson: """
985
+ {
986
+ email: "debug@test.com",
987
+ createdAt: new Date(),
988
+ metadata: {
989
+ source: "ai-debug",
990
+ sessionId: crypto.randomUUID(),
991
+ testData: [1, 2, 3].map(x => x * 10)
992
+ }
993
+ }
994
+ """
995
+ pure: true
996
+ # Activation of eval() for smarter inputs.
997
+ evalInput: true
998
+ ) {
999
+ success
1000
+ result
1001
+ executionTimeMs
1002
+ }
1003
+ }
1004
+ ```
1005
+
1006
+ 3. **Monitor live telemetry for debug output**:
1007
+
1008
+ ```graphql
1009
+ query {
1010
+ live {
1011
+ logs(last: 10) {
1012
+ level
1013
+ message
1014
+ data
1015
+ timestampMs
1016
+ }
1017
+ }
1018
+ }
1019
+ ```
1020
+
1021
+ #### JSON Serialization
1022
+
1023
+ The system automatically handles complex JavaScript types:
1024
+
1025
+ - **Primitives**: strings, numbers, booleans preserved exactly
1026
+ - **Objects/Arrays**: Deep serialization with proper structure
1027
+ - **Functions**: Converted to `[Function: name]` strings
1028
+ - **Undefined**: Converted to `[undefined]` strings
1029
+ - **Dates**: Serialized as ISO strings
1030
+ - **Circular References**: Safely handled to prevent errors
1031
+
1032
+ #### Input Processing Modes
1033
+
1034
+ **JSON Mode (default)**: `evalInput: false`
1035
+
1036
+ - Input is parsed as JSON using `JSON.parse()`
1037
+ - Safe for structured data
1038
+ - Limited to JSON-compatible types
1039
+
1040
+ **JavaScript Evaluation Mode**: `evalInput: true`
1041
+
1042
+ - Input is evaluated as JavaScript using `eval()`
1043
+ - Supports complex expressions, function calls, Date objects, calculations
1044
+ - Full access to JavaScript runtime and built-in objects
1045
+ - Perfect for AI-driven testing with dynamic inputs
1046
+
1047
+ ### Arbitrary Code Evaluation
1048
+
1049
+ For advanced debugging, the system provides an `eval` mutation to execute arbitrary JavaScript/TypeScript code on the server.
1050
+
1051
+ **⚠️ Security Warning**: This feature is powerful and executes code with the same privileges as the application. It is intended for development environments only and is disabled by default in production. To enable it, set the environment variable `RUNNER_DEV_EVAL=1`.
1052
+
1053
+ #### `eval` Mutation
1054
+
1055
+ **Execute arbitrary code:**
1056
+
1057
+ ```graphql
1058
+ mutation EvalCode($code: String!, $inputJson: String, $evalInput: Boolean) {
1059
+ eval(code: $code, inputJson: $inputJson, evalInput: $evalInput) {
1060
+ success
1061
+ error
1062
+ result # JSON string
1063
+ executionTimeMs
1064
+ }
1065
+ }
1066
+ ```
1067
+
1068
+ - `code`: The JavaScript/TypeScript code to execute.
1069
+ - `inputJson`: Optional input string, parsed as JSON by default.
1070
+ - `evalInput`: If `true`, `inputJson` is evaluated as a JavaScript expression.
1071
+
1072
+ **Example:**
1073
+
1074
+ ```graphql
1075
+ mutation {
1076
+ eval(code: "return { a: 1, b: process.version }") {
1077
+ success
1078
+ result
1079
+ }
1080
+ }
1081
+ ```
1082
+
1083
+ ### Use Cases
1084
+
1085
+ - **Production Debugging**: Add logging to specific functions without restarts
1086
+ - **A/B Testing**: Compare different function implementations live
1087
+ - **Performance Monitoring**: Inject performance measurements
1088
+ - **Error Investigation**: Add error handling and detailed logging
1089
+ - **Feature Development**: Test new logic before permanent implementation
1090
+ - **AI-Driven Debugging**: Enable AI assistants to debug applications autonomously
1091
+
1092
+ ### Architecture
1093
+
1094
+ The hot-swapping system consists of:
1095
+
1096
+ - **SwapManager Resource** (`src/resources/swap.resource.ts`): Core swapping logic
1097
+ - **GraphQL Types** (`src/schema/types/SwapType.ts`): Type definitions for GraphQL API
1098
+ - **GraphQL Mutations** (`src/schema/mutation.ts`): Remote swap operations
1099
+ - **TypeScript Compiler**: Automatic code compilation and validation
1100
+ - **State Management**: Tracking of swapped functions and original code
1101
+ - **Error Handling**: Comprehensive validation and recovery mechanisms
1102
+
1103
+ The implementation maintains 100% type safety and provides extensive test coverage with both unit tests and GraphQL integration tests.