@tscircuit/eval 0.0.288 → 0.0.290

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 (47) hide show
  1. package/.github/workflows/bun-test.yml +20 -2
  2. package/dist/blob-url.js +1 -1
  3. package/dist/eval/index.d.ts +56 -8
  4. package/dist/eval/index.js +17 -1
  5. package/dist/lib/index.d.ts +57 -8
  6. package/dist/lib/index.js +66 -1
  7. package/dist/webworker/entrypoint.js +352 -349
  8. package/dist/worker.d.ts +1 -0
  9. package/dist/worker.js +50 -1
  10. package/lib/runner/CircuitRunner.ts +19 -0
  11. package/lib/shared/types.ts +2 -0
  12. package/lib/worker.ts +61 -0
  13. package/package.json +9 -7
  14. package/scripts/validate-test-matrix.js +148 -0
  15. package/tests/{example1-readme-example.test.tsx → examples/example01-readme-example.test.tsx} +1 -1
  16. package/tests/{example2-multiple-files.test.tsx → examples/example02-multiple-files.test.tsx} +1 -1
  17. package/tests/{example3-encoded-url.test.tsx → examples/example03-encoded-url.test.tsx} +1 -1
  18. package/tests/{example4-root-child-issue.test.tsx → examples/example04-root-child-issue.test.tsx} +1 -1
  19. package/tests/{example5-event-recording.test.tsx → examples/example05-event-recording.test.tsx} +1 -1
  20. package/tests/{example7-import-default-and-namespace.test.tsx → examples/example07-import-default-and-namespace.test.tsx} +2 -2
  21. package/tests/{example8-footprinter-to220.test.tsx → examples/example08-footprinter-to220.test.tsx} +1 -1
  22. package/tests/{example9-not-defined-component.test.tsx → examples/example09-not-defined-component.test.tsx} +1 -1
  23. package/tests/{example13-webworker-without-entrypoint.test.tsx → examples/example13-webworker-without-entrypoint.test.tsx} +1 -1
  24. package/tests/{example16-parts-engine.test.tsx → examples/example16-parts-engine.test.tsx} +1 -1
  25. package/tests/{example17-parse-tscircuit-config.test.tsx → examples/example17-parse-tscircuit-config.test.tsx} +1 -1
  26. package/tests/{circuit-event-forwarding.test.tsx → features/circuit-event-forwarding.test.tsx} +1 -1
  27. package/tests/features/execute-component-runner.test.tsx +47 -0
  28. package/tests/features/execute-component-worker.test.tsx +25 -0
  29. package/tests/{fetch-override.test.ts → features/fetch-proxy/fetch-override.test.ts} +3 -3
  30. package/tests/{fetch-proxy-validation.test.ts → features/fetch-proxy/fetch-proxy-validation.test.ts} +5 -7
  31. package/tests/{kill.test.ts → features/kill.test.ts} +5 -3
  32. package/tests/{manual-edits.test.tsx → features/manual-edits.test.tsx} +2 -2
  33. package/tests/fixtures/resourcePaths.ts +3 -0
  34. package/webworker/entrypoint.ts +58 -2
  35. /package/tests/{example6-dynamic-load-blob-url.test.tsx → examples/example06-dynamic-load-blob-url.test.tsx} +0 -0
  36. /package/tests/{example10-run-tscircuit-code.test.tsx → examples/example10-run-tscircuit-code.test.tsx} +0 -0
  37. /package/tests/{example11-flexible-import-extensions.test.tsx → examples/example11-flexible-import-extensions.test.tsx} +0 -0
  38. /package/tests/{example12-import-from-subdirectory.test.tsx → examples/example12-import-from-subdirectory.test.tsx} +0 -0
  39. /package/tests/{example14-run-tscircuit-module.test.tsx → examples/example14-run-tscircuit-module.test.tsx} +0 -0
  40. /package/tests/{example15-run-tscircuit-module-with-props.test.tsx → examples/example15-run-tscircuit-module-with-props.test.tsx} +0 -0
  41. /package/tests/{parent-directory-import.test.tsx → features/parent-directory-import.test.tsx} +0 -0
  42. /package/tests/{platform-config.test.tsx → features/platform-config.test.tsx} +0 -0
  43. /package/tests/{prioritize-default-export.test.tsx → features/prioritize-default-export.test.tsx} +0 -0
  44. /package/tests/{group-wrapper.test.tsx → repros/group-wrapper.test.tsx} +0 -0
  45. /package/tests/{nine-keyboard-default-export.test.tsx → repros/nine-keyboard-default-export.test.tsx} +0 -0
  46. /package/tests/{get-imports-from-code.test.tsx → util-fns/get-imports-from-code.test.tsx} +0 -0
  47. /package/tests/{getPossibleEntrypointComponentPaths.test.ts → util-fns/getPossibleEntrypointComponentPaths.test.ts} +0 -0
package/dist/worker.d.ts CHANGED
@@ -27,6 +27,7 @@ interface WebWorkerConfiguration extends CircuitRunnerConfiguration {
27
27
  }
28
28
  type CircuitWebWorker = {
29
29
  execute: (code: string) => Promise<void>;
30
+ executeComponent: (component: any) => Promise<void>;
30
31
  executeWithFsMap: (opts: {
31
32
  entrypoint?: string;
32
33
  mainComponentPath?: string;
package/dist/worker.js CHANGED
@@ -107,6 +107,38 @@ var createCircuitWebWorker = async (configuration) => {
107
107
  }
108
108
  const comlinkWorker = Comlink.wrap(rawWorker);
109
109
  rawWorker.removeEventListener("message", earlyMessageHandler);
110
+ function serializeReactElement(element) {
111
+ if (!element || typeof element !== "object") {
112
+ return element;
113
+ }
114
+ if (element.type && element.props !== void 0) {
115
+ return {
116
+ __isSerializedReactElement: true,
117
+ type: element.type,
118
+ props: serializeProps(element.props),
119
+ key: element.key
120
+ };
121
+ }
122
+ return element;
123
+ }
124
+ function serializeProps(props) {
125
+ if (!props || typeof props !== "object") {
126
+ return props;
127
+ }
128
+ const serialized = {};
129
+ for (const [key, value] of Object.entries(props)) {
130
+ if (key === "children") {
131
+ if (Array.isArray(value)) {
132
+ serialized.children = value.map(serializeReactElement);
133
+ } else {
134
+ serialized.children = serializeReactElement(value);
135
+ }
136
+ } else {
137
+ serialized[key] = value;
138
+ }
139
+ }
140
+ return serialized;
141
+ }
110
142
  if (configuration.enableFetchProxy) {
111
143
  rawWorker.postMessage({ type: "override_global_fetch" });
112
144
  }
@@ -126,6 +158,23 @@ var createCircuitWebWorker = async (configuration) => {
126
158
  }
127
159
  return comlinkWorker.execute.bind(comlinkWorker)(...args);
128
160
  },
161
+ executeComponent: async (component) => {
162
+ if (isTerminated) {
163
+ throw new Error(
164
+ "CircuitWebWorker was terminated, can't executeComponent"
165
+ );
166
+ }
167
+ if (typeof component === "function") {
168
+ return comlinkWorker.executeComponent.bind(comlinkWorker)(component);
169
+ }
170
+ if (component && typeof component === "object" && component.type) {
171
+ const serializedElement = serializeReactElement(component);
172
+ return comlinkWorker.executeComponent.bind(comlinkWorker)(
173
+ serializedElement
174
+ );
175
+ }
176
+ return comlinkWorker.executeComponent.bind(comlinkWorker)(component);
177
+ },
129
178
  executeWithFsMap: async (...args) => {
130
179
  if (isTerminated) {
131
180
  throw new Error(
@@ -157,4 +206,4 @@ export {
157
206
  createCircuitWebWorker,
158
207
  getImportsFromCode
159
208
  };
160
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vbGliL3dvcmtlci50cyIsICIuLi9saWIvdXRpbHMvZ2V0LWltcG9ydHMtZnJvbS1jb2RlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgKiBhcyBDb21saW5rIGZyb20gXCJjb21saW5rXCJcbmV4cG9ydCAqIGZyb20gXCIuL3V0aWxzL2luZGV4XCJcbmltcG9ydCB0eXBlIHtcbiAgSW50ZXJuYWxXZWJXb3JrZXJBcGksXG4gIFdlYldvcmtlckNvbmZpZ3VyYXRpb24sXG4gIENpcmN1aXRXZWJXb3JrZXIsXG59IGZyb20gXCIuL3NoYXJlZC90eXBlc1wiXG5pbXBvcnQgdHlwZSB7IFJvb3RDaXJjdWl0RXZlbnROYW1lIH0gZnJvbSBcIkB0c2NpcmN1aXQvY29yZVwiXG5cbmV4cG9ydCB0eXBlIHsgQ2lyY3VpdFdlYldvcmtlciwgV2ViV29ya2VyQ29uZmlndXJhdGlvbiB9XG5cbmRlY2xhcmUgZ2xvYmFsIHtcbiAgaW50ZXJmYWNlIFdpbmRvdyB7XG4gICAgVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUjogQ2lyY3VpdFdlYldvcmtlciB8IHVuZGVmaW5lZFxuICB9XG4gIHZhciBUU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSOiBDaXJjdWl0V2ViV29ya2VyIHwgdW5kZWZpbmVkXG59XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVDaXJjdWl0V2ViV29ya2VyID0gYXN5bmMgKFxuICBjb25maWd1cmF0aW9uOiBQYXJ0aWFsPFdlYldvcmtlckNvbmZpZ3VyYXRpb24+LFxuKTogUHJvbWlzZTxDaXJjdWl0V2ViV29ya2VyPiA9PiB7XG4gIC8vIEtpbGwgZXhpc3RpbmcgZ2xvYmFsIHdvcmtlciBpbnN0YW5jZSBpZiBwcmVzZW50XG4gIGNvbnN0IGV4aXN0aW5nV29ya2VyID0gZ2xvYmFsVGhpcy5UU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSXG4gIGlmIChleGlzdGluZ1dvcmtlciAmJiB0eXBlb2YgZXhpc3RpbmdXb3JrZXIua2lsbCA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgaWYgKGNvbmZpZ3VyYXRpb24udmVyYm9zZSkge1xuICAgICAgY29uc29sZS5sb2coXCJbV29ya2VyXSBLaWxsaW5nIHByZXZpb3VzIGdsb2JhbCB3b3JrZXIgaW5zdGFuY2UuLi5cIilcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGV4aXN0aW5nV29ya2VyLmtpbGwoKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGlmIChjb25maWd1cmF0aW9uLnZlcmJvc2UpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgIFwiW1dvcmtlcl0gRXJyb3Iga2lsbGluZyBwcmV2aW91cyBnbG9iYWwgd29ya2VyIGluc3RhbmNlOlwiLFxuICAgICAgICAgIGUsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIC8vIEVuc3VyZSB0aGUga2V5IGlzIGNsZWFyZWQgZXZlbiBpZiBraWxsIHRocm93cyBhbiBlcnJvclxuICAgICAgaWYgKGdsb2JhbFRoaXMuVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUiA9PT0gZXhpc3RpbmdXb3JrZXIpIHtcbiAgICAgICAgZ2xvYmFsVGhpcy5UU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSID0gdW5kZWZpbmVkXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaWYgKGNvbmZpZ3VyYXRpb24udmVyYm9zZSkge1xuICAgIGNvbnNvbGUubG9nKFxuICAgICAgXCJbV29ya2VyXSBDcmVhdGluZyBjaXJjdWl0IHdlYiB3b3JrZXIgd2l0aCBjb25maWc6XCIsXG4gICAgICBjb25maWd1cmF0aW9uLFxuICAgIClcbiAgfVxuXG4gIGxldCB3b3JrZXJCbG9iVXJsID1cbiAgICBjb25maWd1cmF0aW9uLndlYldvcmtlckJsb2JVcmwgPz8gY29uZmlndXJhdGlvbi53ZWJXb3JrZXJVcmxcblxuICBpZiAoIXdvcmtlckJsb2JVcmwpIHtcbiAgICBjb25zdCBjZG5VcmwgPSBgaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9AdHNjaXJjdWl0L2V2YWxAJHtjb25maWd1cmF0aW9uLmV2YWxWZXJzaW9uID8/IFwibGF0ZXN0XCJ9L2Rpc3Qvd2Vid29ya2VyL2VudHJ5cG9pbnQuanNgXG5cbiAgICBjb25zdCB3b3JrZXJCbG9iID0gYXdhaXQgZ2xvYmFsVGhpcy5mZXRjaChjZG5VcmwpLnRoZW4oKHJlcykgPT4gcmVzLmJsb2IoKSlcbiAgICB3b3JrZXJCbG9iVXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTCh3b3JrZXJCbG9iKVxuICB9XG5cbiAgY29uc3QgcmF3V29ya2VyID0gbmV3IFdvcmtlcih3b3JrZXJCbG9iVXJsLCB7IHR5cGU6IFwibW9kdWxlXCIgfSlcbiAgbGV0IHdvcmtlckluaXRFcnJvcjogYW55XG4gIHJhd1dvcmtlci5hZGRFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgKGV2ZW50KSA9PiB7XG4gICAgY29uc29sZS5lcnJvcihcIltXb3JrZXJdIEVycm9yIGluIHdvcmtlclwiLCBldmVudClcbiAgICB3b3JrZXJJbml0RXJyb3IgPSBldmVudFxuICB9KVxuICByYXdXb3JrZXIuYWRkRXZlbnRMaXN0ZW5lcihcInVuaGFuZGxlZHJlamVjdGlvblwiLCAoZXZlbnQpID0+IHtcbiAgICBjb25zb2xlLmVycm9yKFwiW1dvcmtlcl0gVW5oYW5kbGVkIHJlamVjdGlvbiBpbiB3b3JrZXJcIiwgZXZlbnQpXG4gIH0pXG4gIHJhd1dvcmtlci5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZWVycm9yXCIsIChldmVudCkgPT4ge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJbV29ya2VyXSBNZXNzYWdlIGVycm9yIGluIHdvcmtlclwiLCBldmVudClcbiAgfSlcbiAgY29uc3QgZWFybHlNZXNzYWdlSGFuZGxlciA9IChldmVudDogTWVzc2FnZUV2ZW50KSA9PiB7XG4gICAgY29uc29sZS5sb2coXCJbV29ya2VyXSBNZXNzYWdlIGluIHdvcmtlclwiLCBldmVudClcbiAgfVxuICByYXdXb3JrZXIuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgZWFybHlNZXNzYWdlSGFuZGxlcilcblxuICAvLyBIYW5kbGUgZmV0Y2ggcmVxdWVzdHMgZnJvbSB0aGUgd29ya2VyXG4gIHJhd1dvcmtlci5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCBhc3luYyAoZXZlbnQ6IE1lc3NhZ2VFdmVudCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPSBldmVudC5kYXRhXG4gICAgaWYgKGRhdGE/LnR5cGUgIT09IFwid29ya2VyX2ZldGNoXCIpIHJldHVyblxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZ2xvYmFsVGhpcy5mZXRjaChkYXRhLmlucHV0LCBkYXRhLmluaXQpXG4gICAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVzcG9uc2UudGV4dCgpXG4gICAgICByYXdXb3JrZXIucG9zdE1lc3NhZ2Uoe1xuICAgICAgICB0eXBlOiBcIndvcmtlcl9mZXRjaF9yZXN1bHRcIixcbiAgICAgICAgcmVxdWVzdElkOiBkYXRhLnJlcXVlc3RJZCxcbiAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICBib2R5LFxuICAgICAgICAgIHN0YXR1czogcmVzcG9uc2Uuc3RhdHVzLFxuICAgICAgICAgIHN0YXR1c1RleHQ6IHJlc3BvbnNlLnN0YXR1c1RleHQsXG4gICAgICAgICAgaGVhZGVyczogKCgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG9iajogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9XG4gICAgICAgICAgICByZXNwb25zZS5oZWFkZXJzLmZvckVhY2goKHZhbHVlLCBrZXkpID0+IHtcbiAgICAgICAgICAgICAgb2JqW2tleV0gPSB2YWx1ZVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHJldHVybiBvYmpcbiAgICAgICAgICB9KSgpLFxuICAgICAgICB9LFxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgcmF3V29ya2VyLnBvc3RNZXNzYWdlKHtcbiAgICAgICAgdHlwZTogXCJ3b3JrZXJfZmV0Y2hfcmVzdWx0XCIsXG4gICAgICAgIHJlcXVlc3RJZDogZGF0YS5yZXF1ZXN0SWQsXG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjoge1xuICAgICAgICAgIG5hbWU6IGVyci5uYW1lLFxuICAgICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICAgIHN0YWNrOiBlcnIuc3RhY2ssXG4gICAgICAgIH0sXG4gICAgICB9KVxuICAgIH1cbiAgfSlcblxuICBpZiAod29ya2VySW5pdEVycm9yKSB7XG4gICAgdGhyb3cgd29ya2VySW5pdEVycm9yXG4gIH1cblxuICBjb25zdCBjb21saW5rV29ya2VyID0gQ29tbGluay53cmFwPEludGVybmFsV2ViV29ya2VyQXBpPihyYXdXb3JrZXIpXG5cbiAgcmF3V29ya2VyLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGVhcmx5TWVzc2FnZUhhbmRsZXIpXG5cbiAgLy8gQ29uZGl0aW9uYWxseSBvdmVycmlkZSBnbG9iYWwgZmV0Y2ggaW5zaWRlIHRoZSB3b3JrZXIgdG8gcm91dGUgdGhyb3VnaCB0aGUgcGFyZW50XG4gIC8vIE9ubHkgZW5hYmxlIHdoZW4gZXhwbGljaXRseSByZXF1ZXN0ZWQgdmlhIGNvbmZpZ3VyYXRpb25cbiAgaWYgKGNvbmZpZ3VyYXRpb24uZW5hYmxlRmV0Y2hQcm94eSkge1xuICAgIHJhd1dvcmtlci5wb3N0TWVzc2FnZSh7IHR5cGU6IFwib3ZlcnJpZGVfZ2xvYmFsX2ZldGNoXCIgfSlcbiAgfVxuXG4gIGlmIChjb25maWd1cmF0aW9uLnNuaXBwZXRzQXBpQmFzZVVybCkge1xuICAgIGF3YWl0IGNvbWxpbmtXb3JrZXIuc2V0U25pcHBldHNBcGlCYXNlVXJsKGNvbmZpZ3VyYXRpb24uc25pcHBldHNBcGlCYXNlVXJsKVxuICB9XG4gIGlmIChjb25maWd1cmF0aW9uLnBsYXRmb3JtKSB7XG4gICAgYXdhaXQgY29tbGlua1dvcmtlci5zZXRQbGF0Zm9ybUNvbmZpZyhjb25maWd1cmF0aW9uLnBsYXRmb3JtKVxuICB9XG5cbiAgbGV0IGlzVGVybWluYXRlZCA9IGZhbHNlXG5cbiAgLy8gQ3JlYXRlIGEgd3JhcHBlciB0aGF0IGhhbmRsZXMgZXZlbnRzIGRpcmVjdGx5IHRocm91Z2ggY2lyY3VpdCBpbnN0YW5jZVxuICBjb25zdCB3cmFwcGVyOiBDaXJjdWl0V2ViV29ya2VyID0ge1xuICAgIGNsZWFyRXZlbnRMaXN0ZW5lcnM6IGNvbWxpbmtXb3JrZXIuY2xlYXJFdmVudExpc3RlbmVycy5iaW5kKGNvbWxpbmtXb3JrZXIpLFxuICAgIHZlcnNpb246IGNvbWxpbmtXb3JrZXIudmVyc2lvbi5iaW5kKGNvbWxpbmtXb3JrZXIpLFxuICAgIGV4ZWN1dGU6IGFzeW5jICguLi5hcmdzKSA9PiB7XG4gICAgICBpZiAoaXNUZXJtaW5hdGVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkNpcmN1aXRXZWJXb3JrZXIgd2FzIHRlcm1pbmF0ZWQsIGNhbid0IGV4ZWN1dGVcIilcbiAgICAgIH1cbiAgICAgIHJldHVybiBjb21saW5rV29ya2VyLmV4ZWN1dGUuYmluZChjb21saW5rV29ya2VyKSguLi5hcmdzKVxuICAgIH0sXG4gICAgZXhlY3V0ZVdpdGhGc01hcDogYXN5bmMgKC4uLmFyZ3MpID0+IHtcbiAgICAgIGlmIChpc1Rlcm1pbmF0ZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIFwiQ2lyY3VpdFdlYldvcmtlciB3YXMgdGVybWluYXRlZCwgY2FuJ3QgZXhlY3V0ZVdpdGhGc01hcFwiLFxuICAgICAgICApXG4gICAgICB9XG4gICAgICByZXR1cm4gY29tbGlua1dvcmtlci5leGVjdXRlV2l0aEZzTWFwLmJpbmQoY29tbGlua1dvcmtlcikoLi4uYXJncylcbiAgICB9LFxuICAgIHJlbmRlclVudGlsU2V0dGxlZDogY29tbGlua1dvcmtlci5yZW5kZXJVbnRpbFNldHRsZWQuYmluZChjb21saW5rV29ya2VyKSxcbiAgICBnZXRDaXJjdWl0SnNvbjogY29tbGlua1dvcmtlci5nZXRDaXJjdWl0SnNvbi5iaW5kKGNvbWxpbmtXb3JrZXIpLFxuICAgIG9uOiAoZXZlbnQ6IHN0cmluZywgY2FsbGJhY2s6ICguLi5hcmdzOiBhbnlbXSkgPT4gdm9pZCkgPT4ge1xuICAgICAgY29uc3QgcHJveGllZENhbGxiYWNrID0gQ29tbGluay5wcm94eShjYWxsYmFjaylcbiAgICAgIGNvbWxpbmtXb3JrZXIub24oZXZlbnQgYXMgUm9vdENpcmN1aXRFdmVudE5hbWUsIHByb3hpZWRDYWxsYmFjaylcbiAgICB9LFxuICAgIGtpbGw6IGFzeW5jICgpID0+IHtcbiAgICAgIGNvbWxpbmtXb3JrZXJbQ29tbGluay5yZWxlYXNlUHJveHldKClcbiAgICAgIHJhd1dvcmtlci50ZXJtaW5hdGUoKVxuICAgICAgaXNUZXJtaW5hdGVkID0gdHJ1ZVxuICAgICAgaWYgKGdsb2JhbFRoaXMuVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUiA9PT0gd3JhcHBlcikge1xuICAgICAgICBnbG9iYWxUaGlzLlRTQ0lSQ1VJVF9HTE9CQUxfQ0lSQ1VJVF9XT1JLRVIgPSB1bmRlZmluZWRcbiAgICAgIH1cbiAgICB9LFxuICB9XG4gIDsod3JhcHBlciBhcyBhbnkpLl9fcmF3V29ya2VyID0gcmF3V29ya2VyXG4gIGdsb2JhbFRoaXMuVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUiA9IHdyYXBwZXJcbiAgcmV0dXJuIHdyYXBwZXJcbn1cbiIsICJleHBvcnQgY29uc3QgZ2V0SW1wb3J0c0Zyb21Db2RlID0gKGNvZGU6IHN0cmluZyk6IHN0cmluZ1tdID0+IHtcbiAgLy8gTWF0Y2ggYmFzaWMgaW1wb3J0IHBhdHRlcm5zIGluY2x1ZGluZyBjb21iaW5lZCBkZWZhdWx0IGFuZCBuYW1lc3BhY2UgaW1wb3J0c1xuICBjb25zdCBpbXBvcnRSZWdleCA9XG4gICAgL15cXHMqaW1wb3J0XFxzKyg/Oig/OltcXHdcXHNdKyxcXHMqKT8oPzpcXCpcXHMrYXNcXHMrW1xcd1xcc10rfFxce1tcXHNcXHcsXStcXH18XFx3KylcXHMrZnJvbVxccyspP1snXCJdKC4rPylbJ1wiXS9nbVxuICBjb25zdCBpbXBvcnRzOiBzdHJpbmdbXSA9IFtdXG4gIGxldCBtYXRjaDogUmVnRXhwRXhlY0FycmF5IHwgbnVsbFxuXG4gIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Bc3NpZ25JbkV4cHJlc3Npb25zOiA8ZXhwbGFuYXRpb24+XG4gIHdoaWxlICgobWF0Y2ggPSBpbXBvcnRSZWdleC5leGVjKGNvZGUpKSAhPT0gbnVsbCkge1xuICAgIGltcG9ydHMucHVzaChtYXRjaFsxXSlcbiAgfVxuXG4gIC8vIE1hdGNoIHJlLWV4cG9ydHNcbiAgY29uc3QgcmVFeHBvcnRSZWdleCA9XG4gICAgL15cXHMqZXhwb3J0XFxzKyg/OlxcKnwoPzpcXHtbXFxzXFx3LF0rXFx9KSlcXHMrZnJvbVxccytbJ1wiXSguKz8pWydcIl0vZ21cbiAgbGV0IHJlRXhwb3J0TWF0Y2g6IFJlZ0V4cEV4ZWNBcnJheSB8IG51bGxcbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0Fzc2lnbkluRXhwcmVzc2lvbnM6IDxleHBsYW5hdGlvbj5cbiAgd2hpbGUgKChyZUV4cG9ydE1hdGNoID0gcmVFeHBvcnRSZWdleC5leGVjKGNvZGUpKSAhPT0gbnVsbCkge1xuICAgIGltcG9ydHMucHVzaChyZUV4cG9ydE1hdGNoWzFdKVxuICB9XG5cbiAgcmV0dXJuIGltcG9ydHNcbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBQSxZQUFZLGFBQWE7OztBQ0FsQixJQUFNLHFCQUFxQixDQUFDLFNBQTJCO0FBRTVELFFBQU0sY0FDSjtBQUNGLFFBQU0sVUFBb0IsQ0FBQztBQUMzQixNQUFJO0FBR0osVUFBUSxRQUFRLFlBQVksS0FBSyxJQUFJLE9BQU8sTUFBTTtBQUNoRCxZQUFRLEtBQUssTUFBTSxDQUFD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
209
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vbGliL3dvcmtlci50cyIsICIuLi9saWIvdXRpbHMvZ2V0LWltcG9ydHMtZnJvbS1jb2RlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgKiBhcyBDb21saW5rIGZyb20gXCJjb21saW5rXCJcbmV4cG9ydCAqIGZyb20gXCIuL3V0aWxzL2luZGV4XCJcbmltcG9ydCB0eXBlIHtcbiAgSW50ZXJuYWxXZWJXb3JrZXJBcGksXG4gIFdlYldvcmtlckNvbmZpZ3VyYXRpb24sXG4gIENpcmN1aXRXZWJXb3JrZXIsXG59IGZyb20gXCIuL3NoYXJlZC90eXBlc1wiXG5pbXBvcnQgdHlwZSB7IFJvb3RDaXJjdWl0RXZlbnROYW1lIH0gZnJvbSBcIkB0c2NpcmN1aXQvY29yZVwiXG5cbmV4cG9ydCB0eXBlIHsgQ2lyY3VpdFdlYldvcmtlciwgV2ViV29ya2VyQ29uZmlndXJhdGlvbiB9XG5cbmRlY2xhcmUgZ2xvYmFsIHtcbiAgaW50ZXJmYWNlIFdpbmRvdyB7XG4gICAgVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUjogQ2lyY3VpdFdlYldvcmtlciB8IHVuZGVmaW5lZFxuICB9XG4gIHZhciBUU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSOiBDaXJjdWl0V2ViV29ya2VyIHwgdW5kZWZpbmVkXG59XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVDaXJjdWl0V2ViV29ya2VyID0gYXN5bmMgKFxuICBjb25maWd1cmF0aW9uOiBQYXJ0aWFsPFdlYldvcmtlckNvbmZpZ3VyYXRpb24+LFxuKTogUHJvbWlzZTxDaXJjdWl0V2ViV29ya2VyPiA9PiB7XG4gIC8vIEtpbGwgZXhpc3RpbmcgZ2xvYmFsIHdvcmtlciBpbnN0YW5jZSBpZiBwcmVzZW50XG4gIGNvbnN0IGV4aXN0aW5nV29ya2VyID0gZ2xvYmFsVGhpcy5UU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSXG4gIGlmIChleGlzdGluZ1dvcmtlciAmJiB0eXBlb2YgZXhpc3RpbmdXb3JrZXIua2lsbCA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgaWYgKGNvbmZpZ3VyYXRpb24udmVyYm9zZSkge1xuICAgICAgY29uc29sZS5sb2coXCJbV29ya2VyXSBLaWxsaW5nIHByZXZpb3VzIGdsb2JhbCB3b3JrZXIgaW5zdGFuY2UuLi5cIilcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGV4aXN0aW5nV29ya2VyLmtpbGwoKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGlmIChjb25maWd1cmF0aW9uLnZlcmJvc2UpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgIFwiW1dvcmtlcl0gRXJyb3Iga2lsbGluZyBwcmV2aW91cyBnbG9iYWwgd29ya2VyIGluc3RhbmNlOlwiLFxuICAgICAgICAgIGUsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIC8vIEVuc3VyZSB0aGUga2V5IGlzIGNsZWFyZWQgZXZlbiBpZiBraWxsIHRocm93cyBhbiBlcnJvclxuICAgICAgaWYgKGdsb2JhbFRoaXMuVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUiA9PT0gZXhpc3RpbmdXb3JrZXIpIHtcbiAgICAgICAgZ2xvYmFsVGhpcy5UU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSID0gdW5kZWZpbmVkXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaWYgKGNvbmZpZ3VyYXRpb24udmVyYm9zZSkge1xuICAgIGNvbnNvbGUubG9nKFxuICAgICAgXCJbV29ya2VyXSBDcmVhdGluZyBjaXJjdWl0IHdlYiB3b3JrZXIgd2l0aCBjb25maWc6XCIsXG4gICAgICBjb25maWd1cmF0aW9uLFxuICAgIClcbiAgfVxuXG4gIGxldCB3b3JrZXJCbG9iVXJsID1cbiAgICBjb25maWd1cmF0aW9uLndlYldvcmtlckJsb2JVcmwgPz8gY29uZmlndXJhdGlvbi53ZWJXb3JrZXJVcmxcblxuICBpZiAoIXdvcmtlckJsb2JVcmwpIHtcbiAgICBjb25zdCBjZG5VcmwgPSBgaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS9AdHNjaXJjdWl0L2V2YWxAJHtjb25maWd1cmF0aW9uLmV2YWxWZXJzaW9uID8/IFwibGF0ZXN0XCJ9L2Rpc3Qvd2Vid29ya2VyL2VudHJ5cG9pbnQuanNgXG5cbiAgICBjb25zdCB3b3JrZXJCbG9iID0gYXdhaXQgZ2xvYmFsVGhpcy5mZXRjaChjZG5VcmwpLnRoZW4oKHJlcykgPT4gcmVzLmJsb2IoKSlcbiAgICB3b3JrZXJCbG9iVXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTCh3b3JrZXJCbG9iKVxuICB9XG5cbiAgY29uc3QgcmF3V29ya2VyID0gbmV3IFdvcmtlcih3b3JrZXJCbG9iVXJsLCB7IHR5cGU6IFwibW9kdWxlXCIgfSlcbiAgbGV0IHdvcmtlckluaXRFcnJvcjogYW55XG4gIHJhd1dvcmtlci5hZGRFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgKGV2ZW50KSA9PiB7XG4gICAgY29uc29sZS5lcnJvcihcIltXb3JrZXJdIEVycm9yIGluIHdvcmtlclwiLCBldmVudClcbiAgICB3b3JrZXJJbml0RXJyb3IgPSBldmVudFxuICB9KVxuICByYXdXb3JrZXIuYWRkRXZlbnRMaXN0ZW5lcihcInVuaGFuZGxlZHJlamVjdGlvblwiLCAoZXZlbnQpID0+IHtcbiAgICBjb25zb2xlLmVycm9yKFwiW1dvcmtlcl0gVW5oYW5kbGVkIHJlamVjdGlvbiBpbiB3b3JrZXJcIiwgZXZlbnQpXG4gIH0pXG4gIHJhd1dvcmtlci5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZWVycm9yXCIsIChldmVudCkgPT4ge1xuICAgIGNvbnNvbGUuZXJyb3IoXCJbV29ya2VyXSBNZXNzYWdlIGVycm9yIGluIHdvcmtlclwiLCBldmVudClcbiAgfSlcbiAgY29uc3QgZWFybHlNZXNzYWdlSGFuZGxlciA9IChldmVudDogTWVzc2FnZUV2ZW50KSA9PiB7XG4gICAgY29uc29sZS5sb2coXCJbV29ya2VyXSBNZXNzYWdlIGluIHdvcmtlclwiLCBldmVudClcbiAgfVxuICByYXdXb3JrZXIuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgZWFybHlNZXNzYWdlSGFuZGxlcilcblxuICAvLyBIYW5kbGUgZmV0Y2ggcmVxdWVzdHMgZnJvbSB0aGUgd29ya2VyXG4gIHJhd1dvcmtlci5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCBhc3luYyAoZXZlbnQ6IE1lc3NhZ2VFdmVudCkgPT4ge1xuICAgIGNvbnN0IGRhdGEgPSBldmVudC5kYXRhXG4gICAgaWYgKGRhdGE/LnR5cGUgIT09IFwid29ya2VyX2ZldGNoXCIpIHJldHVyblxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZ2xvYmFsVGhpcy5mZXRjaChkYXRhLmlucHV0LCBkYXRhLmluaXQpXG4gICAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVzcG9uc2UudGV4dCgpXG4gICAgICByYXdXb3JrZXIucG9zdE1lc3NhZ2Uoe1xuICAgICAgICB0eXBlOiBcIndvcmtlcl9mZXRjaF9yZXN1bHRcIixcbiAgICAgICAgcmVxdWVzdElkOiBkYXRhLnJlcXVlc3RJZCxcbiAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICBib2R5LFxuICAgICAgICAgIHN0YXR1czogcmVzcG9uc2Uuc3RhdHVzLFxuICAgICAgICAgIHN0YXR1c1RleHQ6IHJlc3BvbnNlLnN0YXR1c1RleHQsXG4gICAgICAgICAgaGVhZGVyczogKCgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG9iajogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9XG4gICAgICAgICAgICByZXNwb25zZS5oZWFkZXJzLmZvckVhY2goKHZhbHVlLCBrZXkpID0+IHtcbiAgICAgICAgICAgICAgb2JqW2tleV0gPSB2YWx1ZVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHJldHVybiBvYmpcbiAgICAgICAgICB9KSgpLFxuICAgICAgICB9LFxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgcmF3V29ya2VyLnBvc3RNZXNzYWdlKHtcbiAgICAgICAgdHlwZTogXCJ3b3JrZXJfZmV0Y2hfcmVzdWx0XCIsXG4gICAgICAgIHJlcXVlc3RJZDogZGF0YS5yZXF1ZXN0SWQsXG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBlcnJvcjoge1xuICAgICAgICAgIG5hbWU6IGVyci5uYW1lLFxuICAgICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICAgIHN0YWNrOiBlcnIuc3RhY2ssXG4gICAgICAgIH0sXG4gICAgICB9KVxuICAgIH1cbiAgfSlcblxuICBpZiAod29ya2VySW5pdEVycm9yKSB7XG4gICAgdGhyb3cgd29ya2VySW5pdEVycm9yXG4gIH1cblxuICBjb25zdCBjb21saW5rV29ya2VyID0gQ29tbGluay53cmFwPEludGVybmFsV2ViV29ya2VyQXBpPihyYXdXb3JrZXIpXG5cbiAgcmF3V29ya2VyLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGVhcmx5TWVzc2FnZUhhbmRsZXIpXG5cbiAgLy8gSGVscGVyIHRvIHNlcmlhbGl6ZSBSZWFjdCBlbGVtZW50cyBmb3IgY3Jvc3Mtd29ya2VyIGNvbW11bmljYXRpb25cbiAgZnVuY3Rpb24gc2VyaWFsaXplUmVhY3RFbGVtZW50KGVsZW1lbnQ6IGFueSk6IGFueSB7XG4gICAgaWYgKCFlbGVtZW50IHx8IHR5cGVvZiBlbGVtZW50ICE9PSBcIm9iamVjdFwiKSB7XG4gICAgICByZXR1cm4gZWxlbWVudFxuICAgIH1cblxuICAgIGlmIChlbGVtZW50LnR5cGUgJiYgZWxlbWVudC5wcm9wcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAvLyBUaGlzIGlzIGEgUmVhY3QgZWxlbWVudFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgX19pc1NlcmlhbGl6ZWRSZWFjdEVsZW1lbnQ6IHRydWUsXG4gICAgICAgIHR5cGU6IGVsZW1lbnQudHlwZSxcbiAgICAgICAgcHJvcHM6IHNlcmlhbGl6ZVByb3BzKGVsZW1lbnQucHJvcHMpLFxuICAgICAgICBrZXk6IGVsZW1lbnQua2V5LFxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBlbGVtZW50XG4gIH1cblxuICBmdW5jdGlvbiBzZXJpYWxpemVQcm9wcyhwcm9wczogYW55KTogYW55IHtcbiAgICBpZiAoIXByb3BzIHx8IHR5cGVvZiBwcm9wcyAhPT0gXCJvYmplY3RcIikge1xuICAgICAgcmV0dXJuIHByb3BzXG4gICAgfVxuXG4gICAgY29uc3Qgc2VyaWFsaXplZDogYW55ID0ge31cbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwcm9wcykpIHtcbiAgICAgIGlmIChrZXkgPT09IFwiY2hpbGRyZW5cIikge1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICBzZXJpYWxpemVkLmNoaWxkcmVuID0gdmFsdWUubWFwKHNlcmlhbGl6ZVJlYWN0RWxlbWVudClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzZXJpYWxpemVkLmNoaWxkcmVuID0gc2VyaWFsaXplUmVhY3RFbGVtZW50KHZhbHVlKVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZXJpYWxpemVkW2tleV0gPSB2YWx1ZVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gc2VyaWFsaXplZFxuICB9XG5cbiAgLy8gQ29uZGl0aW9uYWxseSBvdmVycmlkZSBnbG9iYWwgZmV0Y2ggaW5zaWRlIHRoZSB3b3JrZXIgdG8gcm91dGUgdGhyb3VnaCB0aGUgcGFyZW50XG4gIC8vIE9ubHkgZW5hYmxlIHdoZW4gZXhwbGljaXRseSByZXF1ZXN0ZWQgdmlhIGNvbmZpZ3VyYXRpb25cbiAgaWYgKGNvbmZpZ3VyYXRpb24uZW5hYmxlRmV0Y2hQcm94eSkge1xuICAgIHJhd1dvcmtlci5wb3N0TWVzc2FnZSh7IHR5cGU6IFwib3ZlcnJpZGVfZ2xvYmFsX2ZldGNoXCIgfSlcbiAgfVxuXG4gIGlmIChjb25maWd1cmF0aW9uLnNuaXBwZXRzQXBpQmFzZVVybCkge1xuICAgIGF3YWl0IGNvbWxpbmtXb3JrZXIuc2V0U25pcHBldHNBcGlCYXNlVXJsKGNvbmZpZ3VyYXRpb24uc25pcHBldHNBcGlCYXNlVXJsKVxuICB9XG4gIGlmIChjb25maWd1cmF0aW9uLnBsYXRmb3JtKSB7XG4gICAgYXdhaXQgY29tbGlua1dvcmtlci5zZXRQbGF0Zm9ybUNvbmZpZyhjb25maWd1cmF0aW9uLnBsYXRmb3JtKVxuICB9XG5cbiAgbGV0IGlzVGVybWluYXRlZCA9IGZhbHNlXG5cbiAgLy8gQ3JlYXRlIGEgd3JhcHBlciB0aGF0IGhhbmRsZXMgZXZlbnRzIGRpcmVjdGx5IHRocm91Z2ggY2lyY3VpdCBpbnN0YW5jZVxuICBjb25zdCB3cmFwcGVyOiBDaXJjdWl0V2ViV29ya2VyID0ge1xuICAgIGNsZWFyRXZlbnRMaXN0ZW5lcnM6IGNvbWxpbmtXb3JrZXIuY2xlYXJFdmVudExpc3RlbmVycy5iaW5kKGNvbWxpbmtXb3JrZXIpLFxuICAgIHZlcnNpb246IGNvbWxpbmtXb3JrZXIudmVyc2lvbi5iaW5kKGNvbWxpbmtXb3JrZXIpLFxuICAgIGV4ZWN1dGU6IGFzeW5jICguLi5hcmdzKSA9PiB7XG4gICAgICBpZiAoaXNUZXJtaW5hdGVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkNpcmN1aXRXZWJXb3JrZXIgd2FzIHRlcm1pbmF0ZWQsIGNhbid0IGV4ZWN1dGVcIilcbiAgICAgIH1cbiAgICAgIHJldHVybiBjb21saW5rV29ya2VyLmV4ZWN1dGUuYmluZChjb21saW5rV29ya2VyKSguLi5hcmdzKVxuICAgIH0sXG4gICAgZXhlY3V0ZUNvbXBvbmVudDogYXN5bmMgKGNvbXBvbmVudDogYW55KSA9PiB7XG4gICAgICBpZiAoaXNUZXJtaW5hdGVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBcIkNpcmN1aXRXZWJXb3JrZXIgd2FzIHRlcm1pbmF0ZWQsIGNhbid0IGV4ZWN1dGVDb21wb25lbnRcIixcbiAgICAgICAgKVxuICAgICAgfVxuXG4gICAgICAvLyBJZiBpdCdzIGEgZnVuY3Rpb24sIHBhc3MgaXQgYXMtaXMgKHdpbGwgYmUgcHJveGllZCBieSBDb21saW5rKVxuICAgICAgaWYgKHR5cGVvZiBjb21wb25lbnQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICByZXR1cm4gY29tbGlua1dvcmtlci5leGVjdXRlQ29tcG9uZW50LmJpbmQoY29tbGlua1dvcmtlcikoY29tcG9uZW50KVxuICAgICAgfVxuXG4gICAgICAvLyBJZiBpdCdzIGEgUmVhY3QgZWxlbWVudCwgc2VyaWFsaXplIGl0IHRvIGEgcmVjb25zdHJ1Y3RhYmxlIGZvcm1hdFxuICAgICAgaWYgKGNvbXBvbmVudCAmJiB0eXBlb2YgY29tcG9uZW50ID09PSBcIm9iamVjdFwiICYmIGNvbXBvbmVudC50eXBlKSB7XG4gICAgICAgIGNvbnN0IHNlcmlhbGl6ZWRFbGVtZW50ID0gc2VyaWFsaXplUmVhY3RFbGVtZW50KGNvbXBvbmVudClcbiAgICAgICAgcmV0dXJuIGNvbWxpbmtXb3JrZXIuZXhlY3V0ZUNvbXBvbmVudC5iaW5kKGNvbWxpbmtXb3JrZXIpKFxuICAgICAgICAgIHNlcmlhbGl6ZWRFbGVtZW50LFxuICAgICAgICApXG4gICAgICB9XG5cbiAgICAgIHJldHVybiBjb21saW5rV29ya2VyLmV4ZWN1dGVDb21wb25lbnQuYmluZChjb21saW5rV29ya2VyKShjb21wb25lbnQpXG4gICAgfSxcbiAgICBleGVjdXRlV2l0aEZzTWFwOiBhc3luYyAoLi4uYXJncykgPT4ge1xuICAgICAgaWYgKGlzVGVybWluYXRlZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgXCJDaXJjdWl0V2ViV29ya2VyIHdhcyB0ZXJtaW5hdGVkLCBjYW4ndCBleGVjdXRlV2l0aEZzTWFwXCIsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIHJldHVybiBjb21saW5rV29ya2VyLmV4ZWN1dGVXaXRoRnNNYXAuYmluZChjb21saW5rV29ya2VyKSguLi5hcmdzKVxuICAgIH0sXG4gICAgcmVuZGVyVW50aWxTZXR0bGVkOiBjb21saW5rV29ya2VyLnJlbmRlclVudGlsU2V0dGxlZC5iaW5kKGNvbWxpbmtXb3JrZXIpLFxuICAgIGdldENpcmN1aXRKc29uOiBjb21saW5rV29ya2VyLmdldENpcmN1aXRKc29uLmJpbmQoY29tbGlua1dvcmtlciksXG4gICAgb246IChldmVudDogc3RyaW5nLCBjYWxsYmFjazogKC4uLmFyZ3M6IGFueVtdKSA9PiB2b2lkKSA9PiB7XG4gICAgICBjb25zdCBwcm94aWVkQ2FsbGJhY2sgPSBDb21saW5rLnByb3h5KGNhbGxiYWNrKVxuICAgICAgY29tbGlua1dvcmtlci5vbihldmVudCBhcyBSb290Q2lyY3VpdEV2ZW50TmFtZSwgcHJveGllZENhbGxiYWNrKVxuICAgIH0sXG4gICAga2lsbDogYXN5bmMgKCkgPT4ge1xuICAgICAgY29tbGlua1dvcmtlcltDb21saW5rLnJlbGVhc2VQcm94eV0oKVxuICAgICAgcmF3V29ya2VyLnRlcm1pbmF0ZSgpXG4gICAgICBpc1Rlcm1pbmF0ZWQgPSB0cnVlXG4gICAgICBpZiAoZ2xvYmFsVGhpcy5UU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSID09PSB3cmFwcGVyKSB7XG4gICAgICAgIGdsb2JhbFRoaXMuVFNDSVJDVUlUX0dMT0JBTF9DSVJDVUlUX1dPUktFUiA9IHVuZGVmaW5lZFxuICAgICAgfVxuICAgIH0sXG4gIH1cbiAgOyh3cmFwcGVyIGFzIGFueSkuX19yYXdXb3JrZXIgPSByYXdXb3JrZXJcbiAgZ2xvYmFsVGhpcy5UU0NJUkNVSVRfR0xPQkFMX0NJUkNVSVRfV09SS0VSID0gd3JhcHBlclxuICByZXR1cm4gd3JhcHBlclxufVxuIiwgImV4cG9ydCBjb25zdCBnZXRJbXBvcnRzRnJvbUNvZGUgPSAoY29kZTogc3RyaW5nKTogc3RyaW5nW10gPT4ge1xuICAvLyBNYXRjaCBiYXNpYyBpbXBvcnQgcGF0dGVybnMgaW5jbHVkaW5nIGNvbWJpbmVkIGRlZmF1bHQgYW5kIG5hbWVzcGFjZSBpbXBvcnRzXG4gIGNvbnN0IGltcG9ydFJlZ2V4ID1cbiAgICAvXlxccyppbXBvcnRcXHMrKD86KD86W1xcd1xcc10rLFxccyopPyg/OlxcKlxccythc1xccytbXFx3XFxzXSt8XFx7W1xcc1xcdyxdK1xcfXxcXHcrKVxccytmcm9tXFxzKyk/WydcIl0oLis/KVsnXCJdL2dtXG4gIGNvbnN0IGltcG9ydHM6IHN0cmluZ1tdID0gW11cbiAgbGV0IG1hdGNoOiBSZWdFeHBFeGVjQXJyYXkgfCBudWxsXG5cbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0Fzc2lnbkluRXhwcmVzc2lvbnM6IDxleHBsYW5hdGlvbj5cbiAgd2hpbGUgKChtYXRjaCA9IGltcG9ydFJlZ2V4LmV4ZWMoY29kZSkpICE9PSBudWxsKSB7XG4gICAgaW1wb3J0cy5wdXNoKG1hdGNoWzFdKVxuICB9XG5cbiAgLy8gTWF0Y2ggcmUtZXhwb3J0c1xuICBjb25zdCByZUV4cG9ydFJlZ2V4ID1cbiAgICAvXlxccypleHBvcnRcXHMrKD86XFwqfCg/Olxce1tcXHNcXHcsXStcXH0pKVxccytmcm9tXFxzK1snXCJdKC4rPylbJ1wiXS9nbVxuICBsZXQgcmVFeHBvcnRNYXRjaDogUmVnRXhwRXhlY0FycmF5IHwgbnVsbFxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQXNzaWduSW5FeHByZXNzaW9uczogPGV4cGxhbmF0aW9uPlxuICB3aGlsZSAoKHJlRXhwb3J0TWF0Y2ggPSByZUV4cG9ydFJlZ2V4LmV4ZWMoY29kZSkpICE9PSBudWxsKSB7XG4gICAgaW1wb3J0cy5wdXNoKHJlRXhwb3J0TWF0Y2hbMV0pXG4gIH1cblxuICByZXR1cm4gaW1wb3J0c1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFBLFlBQVksYUFBYTs7O0FDQWxCLElBQU0scUJBQXFCLENBQUMsU0FBMkI7QUFFNUQsUUFBTSxjQUNKO0FBQ0YsUUFBTSxVQUFvQixDQUFDO0FBQzNCLE1BQUk7QUFHSixVQUFRLFFBQVEsWUFBWSxLQUFLLElBQUksT0FBTyxNQUFNO0FBQ2hELFlBQVEsS0FBSyxNQUFNLENBQUMsQ0FBQztBQUFBLEVBQ3ZCO0FBR0EsUUFBTSxnQkFDSjtBQUNGLE1BQUk7QUFFSixVQUFRLGdCQUFnQixjQUFjLEtBQUssSUFBSSxPQUFPLE1BQU07QUFDMUQsWUFBUSxLQUFLLGNBQWMsQ0FBQyxDQUFDO0FBQUEsRUFDL0I7QUFFQSxTQUFPO0FBQ1Q7OztBREpPLElBQU0seUJBQXlCLE9BQ3BDLGtCQUM4QjtBQUU5QixRQUFNLGlCQUFpQixXQUFXO0FBQ2xDLE1BQUksa0JBQWtCLE9BQU8sZUFBZSxTQUFTLFlBQVk7QUFDL0QsUUFBSSxjQUFjLFNBQVM7QUFDekIsY0FBUSxJQUFJLHFEQUFxRDtBQUFBLElBQ25FO0FBQ0EsUUFBSTtBQUNGLFlBQU0sZUFBZSxLQUFLO0FBQUEsSUFDNUIsU0FBUyxHQUFHO0FBQ1YsVUFBSSxjQUFjLFNBQVM7QUFDekIsZ0JBQVE7QUFBQSxVQUNOO0FBQUEsVUFDQTtBQUFBLFFBQ0Y7QUFBQSxNQUNGO0FBRUEsVUFBSSxXQUFXLG9DQUFvQyxnQkFBZ0I7QUFDakUsbUJBQVcsa0NBQWtDO0FBQUEsTUFDL0M7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUVBLE1BQUksY0FBYyxTQUFTO0FBQ3pCLFlBQVE7QUFBQSxNQUNOO0FBQUEsTUFDQTtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBRUEsTUFBSSxnQkFDRixjQUFjLG9CQUFvQixjQUFjO0FBRWxELE1BQUksQ0FBQyxlQUFlO0FBQ2xCLFVBQU0sU0FBUyxnREFBZ0QsY0FBYyxlQUFlLFFBQVE7QUFFcEcsVUFBTSxhQUFhLE1BQU0sV0FBVyxNQUFNLE1BQU0sRUFBRSxLQUFLLENBQUMsUUFBUSxJQUFJLEtBQUssQ0FBQztBQUMxRSxvQkFBZ0IsSUFBSSxnQkFBZ0IsVUFBVTtBQUFBLEVBQ2hEO0FBRUEsUUFBTSxZQUFZLElBQUksT0FBTyxlQUFlLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUQsTUFBSTtBQUNKLFlBQVUsaUJBQWlCLFNBQVMsQ0FBQyxVQUFVO0FBQzdDLFlBQVEsTUFBTSw0QkFBNEIsS0FBSztBQUMvQyxzQkFBa0I7QUFBQSxFQUNwQixDQUFDO0FBQ0QsWUFBVSxpQkFBaUIsc0JBQXNCLENBQUMsVUFBVTtBQUMxRCxZQUFRLE1BQU0sMENBQTBDLEtBQUs7QUFBQSxFQUMvRCxDQUFDO0FBQ0QsWUFBVSxpQkFBaUIsZ0JBQWdCLENBQUMsVUFBVTtBQUNwRCxZQUFRLE1BQU0sb0NBQW9DLEtBQUs7QUFBQSxFQUN6RCxDQUFDO0FBQ0QsUUFBTSxzQkFBc0IsQ0FBQyxVQUF3QjtBQUNuRCxZQUFRLElBQUksOEJBQThCLEtBQUs7QUFBQSxFQUNqRDtBQUNBLFlBQVUsaUJBQWlCLFdBQVcsbUJBQW1CO0FBR3pELFlBQVUsaUJBQWlCLFdBQVcsT0FBTyxVQUF3QjtBQUNuRSxVQUFNLE9BQU8sTUFBTTtBQUNuQixRQUFJLE1BQU0sU0FBUyxlQUFnQjtBQUVuQyxRQUFJO0FBQ0YsWUFBTSxXQUFXLE1BQU0sV0FBVyxNQUFNLEtBQUssT0FBTyxLQUFLLElBQUk7QUFDN0QsWUFBTSxPQUFPLE1BQU0sU0FBUyxLQUFLO0FBQ2pDLGdCQUFVLFlBQVk7QUFBQSxRQUNwQixNQUFNO0FBQUEsUUFDTixXQUFXLEtBQUs7QUFBQSxRQUNoQixTQUFTO0FBQUEsUUFDVCxVQUFVO0FBQUEsVUFDUjtBQUFBLFVBQ0EsUUFBUSxTQUFTO0FBQUEsVUFDakIsWUFBWSxTQUFTO0FBQUEsVUFDckIsVUFBVSxNQUFNO0FBQ2Qsa0JBQU0sTUFBOEIsQ0FBQztBQUNyQyxxQkFBUyxRQUFRLFFBQVEsQ0FBQyxPQUFPLFFBQVE7QUFDdkMsa0JBQUksR0FBRyxJQUFJO0FBQUEsWUFDYixDQUFDO0FBQ0QsbUJBQU87QUFBQSxVQUNULEdBQUc7QUFBQSxRQUNMO0FBQUEsTUFDRixDQUFDO0FBQUEsSUFDSCxTQUFTLEtBQVU7QUFDakIsZ0JBQVUsWUFBWTtBQUFBLFFBQ3BCLE1BQU07QUFBQSxRQUNOLFdBQVcsS0FBSztBQUFBLFFBQ2hCLFNBQVM7QUFBQSxRQUNULE9BQU87QUFBQSxVQUNMLE1BQU0sSUFBSTtBQUFBLFVBQ1YsU0FBUyxJQUFJO0FBQUEsVUFDYixPQUFPLElBQUk7QUFBQSxRQUNiO0FBQUEsTUFDRixDQUFDO0FBQUEsSUFDSDtBQUFBLEVBQ0YsQ0FBQztBQUVELE1BQUksaUJBQWlCO0FBQ25CLFVBQU07QUFBQSxFQUNSO0FBRUEsUUFBTSxnQkFBd0IsYUFBMkIsU0FBUztBQUVsRSxZQUFVLG9CQUFvQixXQUFXLG1CQUFtQjtBQUc1RCxXQUFTLHNCQUFzQixTQUFtQjtBQUNoRCxRQUFJLENBQUMsV0FBVyxPQUFPLFlBQVksVUFBVTtBQUMzQyxhQUFPO0FBQUEsSUFDVDtBQUVBLFFBQUksUUFBUSxRQUFRLFFBQVEsVUFBVSxRQUFXO0FBRS9DLGFBQU87QUFBQSxRQUNMLDRCQUE0QjtBQUFBLFFBQzVCLE1BQU0sUUFBUTtBQUFBLFFBQ2QsT0FBTyxlQUFlLFFBQVEsS0FBSztBQUFBLFFBQ25DLEtBQUssUUFBUTtBQUFBLE1BQ2Y7QUFBQSxJQUNGO0FBRUEsV0FBTztBQUFBLEVBQ1Q7QUFFQSxXQUFTLGVBQWUsT0FBaUI7QUFDdkMsUUFBSSxDQUFDLFNBQVMsT0FBTyxVQUFVLFVBQVU7QUFDdkMsYUFBTztBQUFBLElBQ1Q7QUFFQSxVQUFNLGFBQWtCLENBQUM7QUFDekIsZUFBVyxDQUFDLEtBQUssS0FBSyxLQUFLLE9BQU8sUUFBUSxLQUFLLEdBQUc7QUFDaEQsVUFBSSxRQUFRLFlBQVk7QUFDdEIsWUFBSSxNQUFNLFFBQVEsS0FBSyxHQUFHO0FBQ3hCLHFCQUFXLFdBQVcsTUFBTSxJQUFJLHFCQUFxQjtBQUFBLFFBQ3ZELE9BQU87QUFDTCxxQkFBVyxXQUFXLHNCQUFzQixLQUFLO0FBQUEsUUFDbkQ7QUFBQSxNQUNGLE9BQU87QUFDTCxtQkFBVyxHQUFHLElBQUk7QUFBQSxNQUNwQjtBQUFBLElBQ0Y7QUFDQSxXQUFPO0FBQUEsRUFDVDtBQUlBLE1BQUksY0FBYyxrQkFBa0I7QUFDbEMsY0FBVSxZQUFZLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUFBLEVBQ3pEO0FBRUEsTUFBSSxjQUFjLG9CQUFvQjtBQUNwQyxVQUFNLGNBQWMsc0JBQXNCLGNBQWMsa0JBQWtCO0FBQUEsRUFDNUU7QUFDQSxNQUFJLGNBQWMsVUFBVTtBQUMxQixVQUFNLGNBQWMsa0JBQWtCLGNBQWMsUUFBUTtBQUFBLEVBQzlEO0FBRUEsTUFBSSxlQUFlO0FBR25CLFFBQU0sVUFBNEI7QUFBQSxJQUNoQyxxQkFBcUIsY0FBYyxvQkFBb0IsS0FBSyxhQUFhO0FBQUEsSUFDekUsU0FBUyxjQUFjLFFBQVEsS0FBSyxhQUFhO0FBQUEsSUFDakQsU0FBUyxVQUFVLFNBQVM7QUFDMUIsVUFBSSxjQUFjO0FBQ2hCLGNBQU0sSUFBSSxNQUFNLGdEQUFnRDtBQUFBLE1BQ2xFO0FBQ0EsYUFBTyxjQUFjLFFBQVEsS0FBSyxhQUFhLEVBQUUsR0FBRyxJQUFJO0FBQUEsSUFDMUQ7QUFBQSxJQUNBLGtCQUFrQixPQUFPLGNBQW1CO0FBQzFDLFVBQUksY0FBYztBQUNoQixjQUFNLElBQUk7QUFBQSxVQUNSO0FBQUEsUUFDRjtBQUFBLE1BQ0Y7QUFHQSxVQUFJLE9BQU8sY0FBYyxZQUFZO0FBQ25DLGVBQU8sY0FBYyxpQkFBaUIsS0FBSyxhQUFhLEVBQUUsU0FBUztBQUFBLE1BQ3JFO0FBR0EsVUFBSSxhQUFhLE9BQU8sY0FBYyxZQUFZLFVBQVUsTUFBTTtBQUNoRSxjQUFNLG9CQUFvQixzQkFBc0IsU0FBUztBQUN6RCxlQUFPLGNBQWMsaUJBQWlCLEtBQUssYUFBYTtBQUFBLFVBQ3REO0FBQUEsUUFDRjtBQUFBLE1BQ0Y7QUFFQSxhQUFPLGNBQWMsaUJBQWlCLEtBQUssYUFBYSxFQUFFLFNBQVM7QUFBQSxJQUNyRTtBQUFBLElBQ0Esa0JBQWtCLFVBQVUsU0FBUztBQUNuQyxVQUFJLGNBQWM7QUFDaEIsY0FBTSxJQUFJO0FBQUEsVUFDUjtBQUFBLFFBQ0Y7QUFBQSxNQUNGO0FBQ0EsYUFBTyxjQUFjLGlCQUFpQixLQUFLLGFBQWEsRUFBRSxHQUFHLElBQUk7QUFBQSxJQUNuRTtBQUFBLElBQ0Esb0JBQW9CLGNBQWMsbUJBQW1CLEtBQUssYUFBYTtBQUFBLElBQ3ZFLGdCQUFnQixjQUFjLGVBQWUsS0FBSyxhQUFhO0FBQUEsSUFDL0QsSUFBSSxDQUFDLE9BQWUsYUFBdUM7QUFDekQsWUFBTSxrQkFBMEIsY0FBTSxRQUFRO0FBQzlDLG9CQUFjLEdBQUcsT0FBK0IsZUFBZTtBQUFBLElBQ2pFO0FBQUEsSUFDQSxNQUFNLFlBQVk7QUFDaEIsb0JBQXNCLG9CQUFZLEVBQUU7QUFDcEMsZ0JBQVUsVUFBVTtBQUNwQixxQkFBZTtBQUNmLFVBQUksV0FBVyxvQ0FBb0MsU0FBUztBQUMxRCxtQkFBVyxrQ0FBa0M7QUFBQSxNQUMvQztBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQ0MsRUFBQyxRQUFnQixjQUFjO0FBQ2hDLGFBQVcsa0NBQWtDO0FBQzdDLFNBQU87QUFDVDsiLAogICJuYW1lcyI6IFtdCn0K
@@ -102,6 +102,25 @@ export class CircuitRunner implements CircuitRunnerApi {
102
102
  await importEvalPath("./entrypoint.tsx", this._executionContext)
103
103
  }
104
104
 
105
+ async executeComponent(component: any, opts: { name?: string } = {}) {
106
+ if (this._circuitRunnerConfiguration.verbose) {
107
+ console.log("[CircuitRunner] executeComponent called")
108
+ }
109
+
110
+ this._executionContext = createExecutionContext(
111
+ this._circuitRunnerConfiguration,
112
+ {
113
+ ...opts,
114
+ platform: this._circuitRunnerConfiguration.platform,
115
+ },
116
+ )
117
+ this._bindEventListeners(this._executionContext.circuit)
118
+ ;(globalThis as any).__tscircuit_circuit = this._executionContext.circuit
119
+
120
+ const element = typeof component === "function" ? component() : component
121
+ this._executionContext.circuit.add(element as any)
122
+ }
123
+
105
124
  on(event: string, callback: (...args: any[]) => void) {
106
125
  this._eventListeners[event] ??= []
107
126
  this._eventListeners[event].push(callback)
@@ -36,6 +36,7 @@ export interface CircuitRunnerApi {
36
36
  name?: string
37
37
  },
38
38
  ) => Promise<void>
39
+ executeComponent: (component: any) => Promise<void>
39
40
  executeWithFsMap(opts: {
40
41
  entrypoint?: string
41
42
  fsMap: Record<string, string>
@@ -57,6 +58,7 @@ export type InternalWebWorkerApi = CircuitRunnerApi
57
58
 
58
59
  export type CircuitWebWorker = {
59
60
  execute: (code: string) => Promise<void>
61
+ executeComponent: (component: any) => Promise<void>
60
62
  executeWithFsMap: (opts: {
61
63
  entrypoint?: string
62
64
  mainComponentPath?: string
package/lib/worker.ts CHANGED
@@ -122,6 +122,45 @@ export const createCircuitWebWorker = async (
122
122
 
123
123
  rawWorker.removeEventListener("message", earlyMessageHandler)
124
124
 
125
+ // Helper to serialize React elements for cross-worker communication
126
+ function serializeReactElement(element: any): any {
127
+ if (!element || typeof element !== "object") {
128
+ return element
129
+ }
130
+
131
+ if (element.type && element.props !== undefined) {
132
+ // This is a React element
133
+ return {
134
+ __isSerializedReactElement: true,
135
+ type: element.type,
136
+ props: serializeProps(element.props),
137
+ key: element.key,
138
+ }
139
+ }
140
+
141
+ return element
142
+ }
143
+
144
+ function serializeProps(props: any): any {
145
+ if (!props || typeof props !== "object") {
146
+ return props
147
+ }
148
+
149
+ const serialized: any = {}
150
+ for (const [key, value] of Object.entries(props)) {
151
+ if (key === "children") {
152
+ if (Array.isArray(value)) {
153
+ serialized.children = value.map(serializeReactElement)
154
+ } else {
155
+ serialized.children = serializeReactElement(value)
156
+ }
157
+ } else {
158
+ serialized[key] = value
159
+ }
160
+ }
161
+ return serialized
162
+ }
163
+
125
164
  // Conditionally override global fetch inside the worker to route through the parent
126
165
  // Only enable when explicitly requested via configuration
127
166
  if (configuration.enableFetchProxy) {
@@ -147,6 +186,28 @@ export const createCircuitWebWorker = async (
147
186
  }
148
187
  return comlinkWorker.execute.bind(comlinkWorker)(...args)
149
188
  },
189
+ executeComponent: async (component: any) => {
190
+ if (isTerminated) {
191
+ throw new Error(
192
+ "CircuitWebWorker was terminated, can't executeComponent",
193
+ )
194
+ }
195
+
196
+ // If it's a function, pass it as-is (will be proxied by Comlink)
197
+ if (typeof component === "function") {
198
+ return comlinkWorker.executeComponent.bind(comlinkWorker)(component)
199
+ }
200
+
201
+ // If it's a React element, serialize it to a reconstructable format
202
+ if (component && typeof component === "object" && component.type) {
203
+ const serializedElement = serializeReactElement(component)
204
+ return comlinkWorker.executeComponent.bind(comlinkWorker)(
205
+ serializedElement,
206
+ )
207
+ }
208
+
209
+ return comlinkWorker.executeComponent.bind(comlinkWorker)(component)
210
+ },
150
211
  executeWithFsMap: async (...args) => {
151
212
  if (isTerminated) {
152
213
  throw new Error(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/eval",
3
3
  "main": "dist/lib/index.js",
4
- "version": "0.0.288",
4
+ "version": "0.0.290",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "bun run build:lib && bun run build:webworker && bun run build:blob-url && bun run build:runner && bun run build:worker-wrapper",
@@ -16,6 +16,7 @@
16
16
  "format:check": "biome format .",
17
17
  "test:playwright": "playwright test",
18
18
  "test": "bun test tests",
19
+ "test:validate-matrix": "bun run scripts/validate-test-matrix.js",
19
20
  "copy-core-versions": "bun run scripts/copy-core-versions.ts && bun install --ignore-scripts"
20
21
  },
21
22
  "exports": {
@@ -53,20 +54,20 @@
53
54
  "@biomejs/biome": "^1.8.3",
54
55
  "@playwright/test": "^1.50.1",
55
56
  "@tscircuit/capacity-autorouter": "^0.0.100",
56
- "@tscircuit/checks": "^0.0.68",
57
+ "@tscircuit/checks": "^0.0.71",
57
58
  "@tscircuit/circuit-json-flex": "^0.0.3",
58
59
  "@tscircuit/circuit-json-util": "^0.0.65",
59
- "@tscircuit/core": "^0.0.631",
60
+ "@tscircuit/core": "^0.0.641",
60
61
  "@tscircuit/footprinter": "^0.0.208",
61
62
  "@tscircuit/import-snippet": "^0.0.4",
62
63
  "@tscircuit/infgrid-ijump-astar": "^0.0.33",
63
64
  "@tscircuit/layout": "^0.0.28",
64
65
  "@tscircuit/log-soup": "^1.0.2",
65
- "@tscircuit/matchpack": "^0.0.4",
66
+ "@tscircuit/matchpack": "^0.0.9",
66
67
  "@tscircuit/math-utils": "^0.0.18",
67
68
  "@tscircuit/miniflex": "^0.0.4",
68
69
  "@tscircuit/parts-engine": "^0.0.8",
69
- "@tscircuit/props": "^0.0.286",
70
+ "@tscircuit/props": "^0.0.287",
70
71
  "@tscircuit/schematic-autolayout": "^0.0.6",
71
72
  "@tscircuit/schematic-corpus": "^0.0.110",
72
73
  "@tscircuit/schematic-match-adapt": "^0.0.16",
@@ -81,7 +82,7 @@
81
82
  "bun-match-svg": "0.0.12",
82
83
  "calculate-elbow": "^0.0.9",
83
84
  "chokidar-cli": "^3.0.0",
84
- "circuit-json": "^0.0.228",
85
+ "circuit-json": "^0.0.232",
85
86
  "circuit-json-to-bpc": "^0.0.13",
86
87
  "circuit-json-to-connectivity-map": "^0.0.22",
87
88
  "circuit-json-to-simple-3d": "^0.0.6",
@@ -99,7 +100,8 @@
99
100
  "react-dom": "^19.1.0",
100
101
  "schematic-symbols": "^0.0.180",
101
102
  "ts-expect": "^1.3.0",
102
- "tsup": "^8.2.4"
103
+ "tsup": "^8.2.4",
104
+ "minicssgrid": "^0.0.8"
103
105
  },
104
106
  "peerDependencies": {
105
107
  "typescript": "^5.0.0",
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readdirSync, statSync, readFileSync } from "fs"
4
+ import { join } from "path"
5
+ import { fileURLToPath } from "url"
6
+ import { dirname } from "path"
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = dirname(__filename)
10
+
11
+ const testsDir = join(__dirname, "..", "tests")
12
+ const workflowFile = join(
13
+ __dirname,
14
+ "..",
15
+ ".github",
16
+ "workflows",
17
+ "bun-test.yml",
18
+ )
19
+
20
+ // Get all directories in tests/ that contain test files
21
+ function getTestDirectories() {
22
+ const entries = readdirSync(testsDir)
23
+ const testDirs = []
24
+
25
+ for (const entry of entries) {
26
+ const fullPath = join(testsDir, entry)
27
+
28
+ // Skip non-directories
29
+ if (!statSync(fullPath).isDirectory()) continue
30
+
31
+ // Skip hidden directories like .claude
32
+ if (entry.startsWith(".")) continue
33
+
34
+ // Check if directory contains test files
35
+ if (hasTestFiles(fullPath)) {
36
+ testDirs.push(entry)
37
+ }
38
+ }
39
+
40
+ return testDirs.sort()
41
+ }
42
+
43
+ // Check if a directory contains test files (recursively)
44
+ function hasTestFiles(dirPath) {
45
+ try {
46
+ const entries = readdirSync(dirPath)
47
+
48
+ for (const entry of entries) {
49
+ const fullPath = join(dirPath, entry)
50
+ const stat = statSync(fullPath)
51
+
52
+ if (
53
+ stat.isFile() &&
54
+ (entry.endsWith(".test.ts") || entry.endsWith(".test.tsx"))
55
+ ) {
56
+ return true
57
+ }
58
+
59
+ if (stat.isDirectory() && hasTestFiles(fullPath)) {
60
+ return true
61
+ }
62
+ }
63
+
64
+ return false
65
+ } catch (error) {
66
+ console.warn(`Warning: Could not read directory ${dirPath}`)
67
+ return false
68
+ }
69
+ }
70
+
71
+ // Extract test directories from workflow file
72
+ function getWorkflowTestDirectories() {
73
+ const workflowContent = readFileSync(workflowFile, "utf8")
74
+
75
+ // Look for the matrix test-dir configuration
76
+ const matrixMatch = workflowContent.match(/test-dir:\s*\[(.*?)\]/s)
77
+
78
+ if (!matrixMatch) {
79
+ throw new Error(
80
+ "Could not find test-dir matrix configuration in workflow file",
81
+ )
82
+ }
83
+
84
+ const matrixContent = matrixMatch[1]
85
+ const dirs = matrixContent
86
+ .split(",")
87
+ .map((dir) => dir.trim().replace(/['"]/g, ""))
88
+ .filter((dir) => dir.length > 0)
89
+ .sort()
90
+
91
+ return dirs
92
+ }
93
+
94
+ function main() {
95
+ console.log("🔍 Validating test matrix configuration...")
96
+
97
+ const actualTestDirs = getTestDirectories()
98
+ const workflowTestDirs = getWorkflowTestDirectories()
99
+
100
+ console.log(
101
+ `Found ${actualTestDirs.length} test directories:`,
102
+ actualTestDirs,
103
+ )
104
+ console.log(
105
+ `Workflow covers ${workflowTestDirs.length} directories:`,
106
+ workflowTestDirs,
107
+ )
108
+
109
+ const missing = actualTestDirs.filter(
110
+ (dir) => !workflowTestDirs.includes(dir),
111
+ )
112
+ const extra = workflowTestDirs.filter((dir) => !actualTestDirs.includes(dir))
113
+
114
+ if (missing.length > 0) {
115
+ console.error(
116
+ "❌ ERROR: The following test directories are NOT covered by the workflow matrix:",
117
+ )
118
+ for (const dir of missing) {
119
+ console.error(` - ${dir}`)
120
+ }
121
+ console.error(
122
+ "\nPlease add these directories to the test-dir matrix in .github/workflows/bun-test.yml",
123
+ )
124
+ }
125
+
126
+ if (extra.length > 0) {
127
+ console.warn(
128
+ "⚠️ WARNING: The following directories in the workflow matrix do not contain test files:",
129
+ )
130
+ for (const dir of extra) {
131
+ console.warn(` - ${dir}`)
132
+ }
133
+ console.warn(
134
+ "\nConsider removing these from the test-dir matrix in .github/workflows/bun-test.yml",
135
+ )
136
+ }
137
+
138
+ if (missing.length === 0 && extra.length === 0) {
139
+ console.log(
140
+ "✅ All test directories are properly covered by the workflow matrix!",
141
+ )
142
+ process.exit(0)
143
+ } else {
144
+ process.exit(1)
145
+ }
146
+ }
147
+
148
+ main()
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test"
3
3
 
4
4
  test("example1-readme-example", async () => {
5
5
  const circuitWebWorker = await createCircuitWebWorker({
6
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
6
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
7
7
  })
8
8
 
9
9
  await circuitWebWorker.executeWithFsMap({
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test"
3
3
 
4
4
  test("virtual filesystem with components", async () => {
5
5
  const circuitWebWorker = await createCircuitWebWorker({
6
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
6
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
7
7
  })
8
8
 
9
9
  await circuitWebWorker.executeWithFsMap({
@@ -1,7 +1,7 @@
1
1
  import { createCircuitWebWorker } from "lib"
2
2
  import { expect, test } from "bun:test"
3
3
  // @ts-ignore
4
- import blobUrl from "../dist/blob-url"
4
+ import blobUrl from "../../dist/blob-url"
5
5
 
6
6
  test("example3-encoded-worker-url", async () => {
7
7
  const circuitWebWorker = await createCircuitWebWorker({
@@ -15,7 +15,7 @@ test(
15
15
  "example4-root-child-issue",
16
16
  async () => {
17
17
  const circuitWebWorker = await createCircuitWebWorker({
18
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
18
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
19
19
  })
20
20
 
21
21
  await circuitWebWorker.executeWithFsMap({
@@ -4,7 +4,7 @@ import { createCircuitWebWorker } from "lib/index"
4
4
  // Skipped for flakiness, re-enable when flakiness is solved
5
5
  test.skip("example5-event-recording", async () => {
6
6
  const circuitWebWorker = await createCircuitWebWorker({
7
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
7
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
8
8
  })
9
9
 
10
10
  let eventCount = 0
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test"
3
3
 
4
4
  test("namespace import syntax", async () => {
5
5
  const circuitWebWorker = await createCircuitWebWorker({
6
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
6
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
7
7
  })
8
8
 
9
9
  await circuitWebWorker.executeWithFsMap({
@@ -39,7 +39,7 @@ test("namespace import syntax", async () => {
39
39
 
40
40
  test("combined default and namespace import with fallback", async () => {
41
41
  const circuitWebWorker = await createCircuitWebWorker({
42
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
42
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
43
43
  })
44
44
 
45
45
  await circuitWebWorker.executeWithFsMap({
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test"
3
3
 
4
4
  test("example8-footprinter-to220", async () => {
5
5
  const circuitWebWorker = await createCircuitWebWorker({
6
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
6
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
7
7
  })
8
8
 
9
9
  await circuitWebWorker.execute(`
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test"
3
3
 
4
4
  test("example9-not-defined-component", async () => {
5
5
  const circuitWebWorker = await createCircuitWebWorker({
6
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
6
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
7
7
  })
8
8
 
9
9
  expect(async () => {
@@ -4,7 +4,7 @@ import type { SourceSimpleResistor } from "circuit-json"
4
4
 
5
5
  test("example13-webworker-without-entrypoint", async () => {
6
6
  const circuitWebWorker = await createCircuitWebWorker({
7
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
7
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
8
8
  })
9
9
 
10
10
  try {
@@ -4,7 +4,7 @@ import type { SourceComponentBase } from "circuit-json"
4
4
 
5
5
  test("example16-jlc-parts-engine with entrypoint", async () => {
6
6
  const circuitWebWorker = await createCircuitWebWorker({
7
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
7
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
8
8
  verbose: true,
9
9
  })
10
10
 
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test"
3
3
 
4
4
  test("parse tscircuit.config.js with mainEntrypoint", async () => {
5
5
  const circuitWebWorker = await createCircuitWebWorker({
6
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
6
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
7
7
  })
8
8
 
9
9
  await circuitWebWorker.executeWithFsMap({
@@ -6,7 +6,7 @@ test("circuit-web-worker-events", async () => {
6
6
  const capturedEvents: string[] = []
7
7
 
8
8
  const circuitWebWorker = await createCircuitWebWorker({
9
- webWorkerUrl: new URL("../webworker/entrypoint.ts", import.meta.url),
9
+ webWorkerUrl: new URL("../../webworker/entrypoint.ts", import.meta.url),
10
10
  })
11
11
 
12
12
  await circuitWebWorker.execute(`
@@ -0,0 +1,47 @@
1
+ import { expect, test } from "bun:test"
2
+ import * as React from "react"
3
+ import { CircuitRunner } from "lib/runner/CircuitRunner"
4
+
5
+ test("CircuitRunner.executeComponent with React element", async () => {
6
+ const runner = new CircuitRunner()
7
+ const element = React.createElement(
8
+ "board",
9
+ { width: "10mm", height: "10mm" },
10
+ React.createElement("resistor", {
11
+ name: "R1",
12
+ resistance: "1k",
13
+ footprint: "0402",
14
+ }),
15
+ )
16
+
17
+ await runner.executeComponent(element)
18
+ await runner.renderUntilSettled()
19
+ const circuitJson = await runner.getCircuitJson()
20
+ const r1 = circuitJson.find(
21
+ (el: any) => el.type === "source_component" && el.name === "R1",
22
+ )
23
+ expect(r1).toBeDefined()
24
+ await runner.kill()
25
+ })
26
+
27
+ test("CircuitRunner.executeComponent with factory function", async () => {
28
+ const runner = new CircuitRunner()
29
+ await runner.executeComponent(() =>
30
+ React.createElement(
31
+ "board",
32
+ { width: "10mm", height: "10mm" },
33
+ React.createElement("resistor", {
34
+ name: "R2",
35
+ resistance: "2k",
36
+ footprint: "0402",
37
+ }),
38
+ ),
39
+ )
40
+ await runner.renderUntilSettled()
41
+ const circuitJson = await runner.getCircuitJson()
42
+ const r2 = circuitJson.find(
43
+ (el: any) => el.type === "source_component" && el.name === "R2",
44
+ )
45
+ expect(r2).toBeDefined()
46
+ await runner.kill()
47
+ })