@tscircuit/eval 0.0.287 → 0.0.289

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 (46) 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 +59 -8
  4. package/dist/eval/index.js +2 -3
  5. package/dist/lib/index.d.ts +59 -8
  6. package/dist/lib/index.js +43 -4
  7. package/dist/webworker/entrypoint.js +353 -350
  8. package/dist/worker.d.ts +7 -0
  9. package/dist/worker.js +42 -2
  10. package/lib/shared/types.ts +7 -0
  11. package/lib/worker.ts +47 -1
  12. package/package.json +9 -7
  13. package/scripts/validate-test-matrix.js +148 -0
  14. package/tests/{example1-readme-example.test.tsx → examples/example01-readme-example.test.tsx} +1 -1
  15. package/tests/{example2-multiple-files.test.tsx → examples/example02-multiple-files.test.tsx} +1 -1
  16. package/tests/{example3-encoded-url.test.tsx → examples/example03-encoded-url.test.tsx} +1 -1
  17. package/tests/{example4-root-child-issue.test.tsx → examples/example04-root-child-issue.test.tsx} +1 -1
  18. package/tests/{example5-event-recording.test.tsx → examples/example05-event-recording.test.tsx} +1 -1
  19. package/tests/{example7-import-default-and-namespace.test.tsx → examples/example07-import-default-and-namespace.test.tsx} +2 -2
  20. package/tests/{example8-footprinter-to220.test.tsx → examples/example08-footprinter-to220.test.tsx} +1 -1
  21. package/tests/{example9-not-defined-component.test.tsx → examples/example09-not-defined-component.test.tsx} +1 -1
  22. package/tests/{example13-webworker-without-entrypoint.test.tsx → examples/example13-webworker-without-entrypoint.test.tsx} +1 -1
  23. package/tests/{example16-parts-engine.test.tsx → examples/example16-parts-engine.test.tsx} +1 -1
  24. package/tests/{example17-parse-tscircuit-config.test.tsx → examples/example17-parse-tscircuit-config.test.tsx} +1 -1
  25. package/tests/{circuit-event-forwarding.test.tsx → features/circuit-event-forwarding.test.tsx} +1 -1
  26. package/tests/features/fetch-proxy/fetch-override.test.ts +46 -0
  27. package/tests/features/fetch-proxy/fetch-proxy-validation.test.ts +95 -0
  28. package/tests/{kill.test.ts → features/kill.test.ts} +5 -3
  29. package/tests/{manual-edits.test.tsx → features/manual-edits.test.tsx} +2 -2
  30. package/tests/fixtures/resourcePaths.ts +3 -0
  31. package/webworker/entrypoint.ts +7 -2
  32. package/webworker/fetchProxy.ts +83 -0
  33. package/webworker/import-snippet.ts +2 -4
  34. /package/tests/{example6-dynamic-load-blob-url.test.tsx → examples/example06-dynamic-load-blob-url.test.tsx} +0 -0
  35. /package/tests/{example10-run-tscircuit-code.test.tsx → examples/example10-run-tscircuit-code.test.tsx} +0 -0
  36. /package/tests/{example11-flexible-import-extensions.test.tsx → examples/example11-flexible-import-extensions.test.tsx} +0 -0
  37. /package/tests/{example12-import-from-subdirectory.test.tsx → examples/example12-import-from-subdirectory.test.tsx} +0 -0
  38. /package/tests/{example14-run-tscircuit-module.test.tsx → examples/example14-run-tscircuit-module.test.tsx} +0 -0
  39. /package/tests/{example15-run-tscircuit-module-with-props.test.tsx → examples/example15-run-tscircuit-module-with-props.test.tsx} +0 -0
  40. /package/tests/{parent-directory-import.test.tsx → features/parent-directory-import.test.tsx} +0 -0
  41. /package/tests/{platform-config.test.tsx → features/platform-config.test.tsx} +0 -0
  42. /package/tests/{prioritize-default-export.test.tsx → features/prioritize-default-export.test.tsx} +0 -0
  43. /package/tests/{group-wrapper.test.tsx → repros/group-wrapper.test.tsx} +0 -0
  44. /package/tests/{nine-keyboard-default-export.test.tsx → repros/nine-keyboard-default-export.test.tsx} +0 -0
  45. /package/tests/{get-imports-from-code.test.tsx → util-fns/get-imports-from-code.test.tsx} +0 -0
  46. /package/tests/{getPossibleEntrypointComponentPaths.test.ts → util-fns/getPossibleEntrypointComponentPaths.test.ts} +0 -0
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect } from "bun:test"
2
+ import { createCircuitWebWorker } from "lib"
3
+ import { repoFileUrl } from "tests/fixtures/resourcePaths"
4
+
5
+ describe("fetch proxy validation", () => {
6
+ it("should NOT proxy fetch requests when enableFetchProxy is false (default)", async () => {
7
+ const worker = await createCircuitWebWorker({
8
+ webWorkerUrl: repoFileUrl("dist/webworker/entrypoint.js").href,
9
+ // enableFetchProxy not set, should default to false
10
+ })
11
+
12
+ const rawWorker: Worker = (worker as any).__rawWorker
13
+ const messages: any[] = []
14
+ rawWorker.addEventListener("message", (event) => {
15
+ if (event.data?.type === "worker_fetch") messages.push(event.data)
16
+ })
17
+
18
+ await worker.execute(`
19
+ // Test that fetch calls are NOT proxied when enableFetchProxy is false
20
+ fetch("https://example.com/test")
21
+ .catch(() => {}); // Ignore errors, we just want to see if it's proxied
22
+ `)
23
+
24
+ await new Promise((r) => setTimeout(r, 100))
25
+
26
+ // Should NOT have received any worker_fetch messages
27
+ expect(messages.length).toBe(0)
28
+
29
+ await worker.kill()
30
+ })
31
+
32
+ it("should proxy fetch requests when enableFetchProxy is true", async () => {
33
+ const worker = await createCircuitWebWorker({
34
+ webWorkerUrl: repoFileUrl("dist/webworker/entrypoint.js").href,
35
+ enableFetchProxy: true,
36
+ })
37
+
38
+ const rawWorker: Worker = (worker as any).__rawWorker
39
+ const messages: any[] = []
40
+ rawWorker.addEventListener("message", (event) => {
41
+ if (event.data?.type === "worker_fetch") messages.push(event.data)
42
+ })
43
+
44
+ await worker.execute(`
45
+ // Test that fetch calls are proxied when enableFetchProxy is true
46
+ fetch("https://example.com/test")
47
+ .catch(() => {}); // Ignore errors, we just want to see if it's proxied
48
+ `)
49
+
50
+ await new Promise((r) => setTimeout(r, 100))
51
+
52
+ // Should have received a worker_fetch message indicating the proxy is working
53
+ expect(messages.length).toBeGreaterThan(0)
54
+ expect(messages[0].type).toBe("worker_fetch")
55
+ expect(messages[0].input).toBe("https://example.com/test")
56
+
57
+ await worker.kill()
58
+ })
59
+
60
+ it("should handle fetch errors properly through proxy when enabled", async () => {
61
+ // This test verifies that the fetch proxy correctly handles and forwards errors
62
+ const originalFetch = globalThis.fetch
63
+ const fakeFetch = async () => {
64
+ throw new Error("Network error")
65
+ }
66
+ globalThis.fetch = fakeFetch as any
67
+
68
+ const worker = await createCircuitWebWorker({
69
+ webWorkerUrl: repoFileUrl("dist/webworker/entrypoint.js").href,
70
+ enableFetchProxy: true,
71
+ })
72
+
73
+ const rawWorker: Worker = (worker as any).__rawWorker
74
+ const messages: any[] = []
75
+ rawWorker.addEventListener("message", (event) => {
76
+ if (event.data?.type === "fetch_error") messages.push(event.data)
77
+ })
78
+
79
+ await worker.execute(`
80
+ fetch("https://example.com/test")
81
+ .catch(e => {
82
+ postMessage({ type: "fetch_error", name: e.name, message: e.message });
83
+ });
84
+ `)
85
+
86
+ await new Promise((r) => setTimeout(r, 100))
87
+
88
+ expect(messages).toEqual([
89
+ { type: "fetch_error", name: "Error", message: "Network error" },
90
+ ])
91
+
92
+ await worker.kill()
93
+ globalThis.fetch = originalFetch
94
+ })
95
+ })
@@ -1,11 +1,13 @@
1
1
  import { describe, it, expect } from "bun:test"
2
- import { createCircuitWebWorker } from "../lib"
2
+ import { createCircuitWebWorker } from "lib"
3
3
 
4
4
  describe("kill method", () => {
5
5
  it("should immediately terminate the worker", async () => {
6
6
  const worker = await createCircuitWebWorker({
7
- webWorkerUrl: new URL("../dist/webworker/entrypoint.js", import.meta.url)
8
- .href,
7
+ webWorkerUrl: new URL(
8
+ "../../dist/webworker/entrypoint.js",
9
+ import.meta.url,
10
+ ).href,
9
11
  })
10
12
 
11
13
  // Ensure worker is running
@@ -59,7 +59,7 @@ const example2 = {
59
59
 
60
60
  test("example1: Manual edits in entrypoint.tsx file", async () => {
61
61
  const circuitWebWorker = await createCircuitWebWorker({
62
- webWorkerUrl: new URL("../webworker/index.ts", import.meta.url),
62
+ webWorkerUrl: new URL("../../webworker/index.ts", import.meta.url),
63
63
  })
64
64
 
65
65
  await circuitWebWorker.executeWithFsMap({
@@ -89,7 +89,7 @@ test("example1: Manual edits in entrypoint.tsx file", async () => {
89
89
 
90
90
  test("example2: Manual edits in manual-edits.json file", async () => {
91
91
  const circuitWebWorker = await createCircuitWebWorker({
92
- webWorkerUrl: new URL("../webworker/index.ts", import.meta.url),
92
+ webWorkerUrl: new URL("../../webworker/index.ts", import.meta.url),
93
93
  })
94
94
 
95
95
  await circuitWebWorker.executeWithFsMap({
@@ -0,0 +1,3 @@
1
+ export const repoFileUrl = (pathFromRoot: string) => {
2
+ return new URL(`../../${pathFromRoot}`, import.meta.url)
3
+ }
@@ -11,11 +11,13 @@ import {
11
11
  type ExecutionContext,
12
12
  } from "./execution-context"
13
13
  import { importEvalPath } from "./import-eval-path"
14
- import { normalizeFsMap } from "../lib/runner/normalizeFsMap"
14
+ import { normalizeFsMap } from "lib/runner/normalizeFsMap"
15
15
  import type { RootCircuit } from "@tscircuit/core"
16
16
  import { setupDefaultEntrypointIfNeeded } from "lib/runner/setupDefaultEntrypointIfNeeded"
17
+ import { setupFetchProxy } from "./fetchProxy"
17
18
 
18
19
  globalThis.React = React
20
+ setupFetchProxy()
19
21
 
20
22
  let executionContext: ExecutionContext | null = null
21
23
 
@@ -124,7 +126,10 @@ const webWorkerApi = {
124
126
  for (const event in eventListeners) {
125
127
  for (const listener of eventListeners[event]) {
126
128
  const circuit = executionContext.circuit as unknown as {
127
- removeListener?: (event: string, listener: Function) => void
129
+ removeListener?: (
130
+ event: string,
131
+ listener: (...args: any[]) => void,
132
+ ) => void
128
133
  }
129
134
  if (typeof circuit.removeListener === "function") {
130
135
  circuit.removeListener(event, listener)
@@ -0,0 +1,83 @@
1
+ export const setupFetchProxy = () => {
2
+ const pendingRequests = new Map<
3
+ number,
4
+ { resolve: (value: Response) => void; reject: (reason: any) => void }
5
+ >()
6
+ let requestCounter = 0
7
+
8
+ function fetchProxy(
9
+ input: RequestInfo | URL,
10
+ init?: RequestInit,
11
+ ): Promise<Response> {
12
+ const requestId = ++requestCounter
13
+ return new Promise((resolve, reject) => {
14
+ pendingRequests.set(requestId, { resolve, reject })
15
+ let url: string
16
+ let requestInit: any = init ? { ...init } : {}
17
+
18
+ if (typeof input === "string" || input instanceof URL) {
19
+ url = input.toString()
20
+ } else {
21
+ url = input.url
22
+ requestInit = {
23
+ ...requestInit,
24
+ method: input.method,
25
+ headers: (() => {
26
+ const obj: Record<string, string> = {}
27
+ input.headers.forEach((value, key) => {
28
+ obj[key] = value
29
+ })
30
+ return obj
31
+ })(),
32
+ body: input.bodyUsed ? undefined : (input as any).body,
33
+ }
34
+ }
35
+
36
+ if (requestInit.headers instanceof Headers) {
37
+ const obj: Record<string, string> = {}
38
+ requestInit.headers.forEach((value: string, key: string) => {
39
+ obj[key] = value
40
+ })
41
+ requestInit.headers = obj
42
+ }
43
+ ;(globalThis as any).postMessage({
44
+ type: "worker_fetch",
45
+ requestId,
46
+ input: url,
47
+ init: requestInit,
48
+ })
49
+ })
50
+ }
51
+
52
+ function handleMessage(event: MessageEvent) {
53
+ const data = event.data
54
+ if (!data) return
55
+
56
+ if (data.type === "override_global_fetch") {
57
+ ;(globalThis as any).fetch = fetchProxy
58
+ return
59
+ }
60
+
61
+ if (data.type === "worker_fetch_result") {
62
+ const handlers = pendingRequests.get(data.requestId)
63
+ if (!handlers) return
64
+ pendingRequests.delete(data.requestId)
65
+
66
+ if (data.success) {
67
+ const resp = new Response(data.response.body, {
68
+ status: data.response.status,
69
+ statusText: data.response.statusText,
70
+ headers: data.response.headers,
71
+ })
72
+ handlers.resolve(resp)
73
+ } else {
74
+ const err = new Error(data.error.message)
75
+ if (data.error.name) err.name = data.error.name
76
+ if (data.error.stack) err.stack = data.error.stack
77
+ handlers.reject(err)
78
+ }
79
+ }
80
+ }
81
+
82
+ globalThis.addEventListener("message", handleMessage)
83
+ }
@@ -1,8 +1,5 @@
1
1
  import { evalCompiledJs } from "./eval-compiled-js"
2
2
  import type { ExecutionContext } from "./execution-context"
3
- import * as Babel from "@babel/standalone"
4
- import { importLocalFile } from "./import-local-file"
5
- import { importEvalPath } from "./import-eval-path"
6
3
 
7
4
  export async function importSnippet(
8
5
  importName: string,
@@ -12,7 +9,8 @@ export async function importSnippet(
12
9
  const { preSuppliedImports } = ctx
13
10
  const fullSnippetName = importName.replace("@tsci/", "").replace(".", "/")
14
11
 
15
- const { cjs, error } = await fetch(`${ctx.cjsRegistryUrl}/${fullSnippetName}`)
12
+ const { cjs, error } = await globalThis
13
+ .fetch(`${ctx.cjsRegistryUrl}/${fullSnippetName}`)
16
14
  .then(async (res) => ({ cjs: await res.text(), error: null }))
17
15
  .catch((e) => ({ error: e, cjs: null }))
18
16