From c72f82b8733a8117c209aca40e7ec745c90938ac Mon Sep 17 00:00:00 2001 From: Caitlin Pinn Date: Thu, 15 Jan 2026 15:09:54 -0800 Subject: [PATCH 1/8] add multiple cloudflare tests and change bundling for the browser build --- .../shared/src/suites/prompt-templating.ts | 15 ++ js/smoke/tests/cloudflare-worker/run-test.mjs | 38 +++-- .../tests/cloudflare-worker/src/browser.ts | 4 + js/smoke/tests/cloudflare-worker/src/index.ts | 136 +----------------- js/smoke/tests/cloudflare-worker/src/node.ts | 4 + .../tests/cloudflare-worker/src/worker.ts | 115 +++++++++++++++ .../cloudflare-worker/wrangler.browser.toml | 3 + .../{wrangler.toml => wrangler.node.toml} | 4 +- js/src/template/nunjucks-env.browser.ts | 11 ++ js/src/template/nunjucks-utils.browser.ts | 5 + js/tsup.config.ts | 42 +++++- 11 files changed, 231 insertions(+), 146 deletions(-) create mode 100644 js/smoke/tests/cloudflare-worker/src/browser.ts create mode 100644 js/smoke/tests/cloudflare-worker/src/node.ts create mode 100644 js/smoke/tests/cloudflare-worker/src/worker.ts create mode 100644 js/smoke/tests/cloudflare-worker/wrangler.browser.toml rename js/smoke/tests/cloudflare-worker/{wrangler.toml => wrangler.node.toml} (57%) create mode 100644 js/src/template/nunjucks-env.browser.ts create mode 100644 js/src/template/nunjucks-utils.browser.ts diff --git a/js/smoke/shared/src/suites/prompt-templating.ts b/js/smoke/shared/src/suites/prompt-templating.ts index c3d44a7a7..05105af10 100644 --- a/js/smoke/shared/src/suites/prompt-templating.ts +++ b/js/smoke/shared/src/suites/prompt-templating.ts @@ -143,6 +143,21 @@ export async function testNunjucksTemplate( const errorMessage = buildError instanceof Error ? buildError.message : String(buildError); + // Some builds/environments intentionally do not ship nunjucks support. + if ( + (environment === "browser" || environment === "cloudflare-worker") && + errorMessage.includes( + "Nunjucks templating is not supported in this build", + ) + ) { + return { + success: true, + testName, + message: + "Nunjucks template test passed - threw expected unsupported error", + }; + } + // Special handling for Cloudflare Workers environment, they disallow code generation if ( environment === "cloudflare-worker" && diff --git a/js/smoke/tests/cloudflare-worker/run-test.mjs b/js/smoke/tests/cloudflare-worker/run-test.mjs index f5cfabc67..7ed26f6d8 100644 --- a/js/smoke/tests/cloudflare-worker/run-test.mjs +++ b/js/smoke/tests/cloudflare-worker/run-test.mjs @@ -25,13 +25,17 @@ async function waitForServer() { return false; } -async function runTest() { +async function runWranglerTest({ config, label }) { killPort(PORT); - const wrangler = spawn("npx", ["wrangler", "dev", "--port", String(PORT)], { - stdio: ["ignore", "pipe", "pipe"], - shell: true, - }); + const wrangler = spawn( + "npx", + ["wrangler", "dev", "--config", config, "--port", String(PORT)], + { + stdio: ["ignore", "pipe", "pipe"], + shell: true, + }, + ); let output = ""; wrangler.stdout.on("data", (d) => (output += d)); @@ -57,7 +61,7 @@ async function runTest() { try { if (!(await waitForServer())) { - console.error("Server failed to start:\n", output); + console.error(`[${label}] Server failed to start:\n`, output); await killWrangler(); return 1; } @@ -65,10 +69,11 @@ async function runTest() { const response = await fetch(`http://localhost:${PORT}/test`); const result = await response.json(); + console.log(`\n=== ${label} ===\n`); console.log(JSON.stringify(result, null, 2)); exitCode = result.success ? 0 : 1; } catch (error) { - console.error("Error:", error.message, "\n", output); + console.error(`[${label}] Error:`, error.message, "\n", output); exitCode = 1; } @@ -76,4 +81,21 @@ async function runTest() { return exitCode; } -runTest().then((code) => process.exit(code)); +async function main() { + const a = await runWranglerTest({ + config: "wrangler.node.toml", + label: "nodejs_compat_v2 + braintrust", + }); + if (a !== 0) process.exit(a); + + const b = await runWranglerTest({ + config: "wrangler.browser.toml", + label: "no compatibility_flags + braintrust/browser", + }); + process.exit(b); +} + +main().catch((err) => { + console.error("Fatal:", err); + process.exit(1); +}); diff --git a/js/smoke/tests/cloudflare-worker/src/browser.ts b/js/smoke/tests/cloudflare-worker/src/browser.ts new file mode 100644 index 000000000..d6f039d99 --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/browser.ts @@ -0,0 +1,4 @@ +import * as braintrust from "braintrust/browser"; +import { createWorker } from "./worker"; + +export default createWorker(braintrust); diff --git a/js/smoke/tests/cloudflare-worker/src/index.ts b/js/smoke/tests/cloudflare-worker/src/index.ts index 22ed4f4a8..5531f9e7a 100644 --- a/js/smoke/tests/cloudflare-worker/src/index.ts +++ b/js/smoke/tests/cloudflare-worker/src/index.ts @@ -1,135 +1 @@ -/** - * Cloudflare Worker smoke test using shared test suites - * This test demonstrates using the shared test package in Cloudflare Workers - */ - -import { - setupTestEnvironment, - cleanupTestEnvironment, - runBasicLoggingTests, - runEvalSmokeTest, - runImportVerificationTests, - runPromptTemplatingTests, - type TestResult, -} from "../../../shared/dist/index.mjs"; - -import * as braintrust from "braintrust"; -const { initLogger, _exportsForTestingOnly } = braintrust; - -// Cloudflare Worker environment bindings (empty for this test) -interface Env {} - -interface TestResponse { - success: boolean; - message: string; - totalTests?: number; - passedTests?: number; - failedTests?: number; - results?: TestResult[]; - failures?: TestResult[]; -} - -/** - * Run the shared test suites in Cloudflare Worker environment - */ -async function runSharedTestSuites(): Promise { - try { - // Setup test environment - const adapters = await setupTestEnvironment({ - initLogger, - testingExports: _exportsForTestingOnly, - canUseFileSystem: false, // No filesystem in Workers - canUseCLI: false, // No CLI in Workers - environment: "cloudflare-worker", - }); - - try { - // Run import verification tests first (forces all exports to be processed) - const importResults = await runImportVerificationTests(braintrust); - - // Run functional tests - const functionalResults = await runBasicLoggingTests(adapters); - - // Run eval smoke test - const evalResult = await runEvalSmokeTest(adapters, braintrust); - - // Run prompt templating tests - const promptTemplatingResults = await runPromptTemplatingTests( - { - Prompt: braintrust.Prompt, - }, - adapters.environment, - ); - - // Combine results - const results = [ - ...importResults, - ...functionalResults, - evalResult, - ...promptTemplatingResults, - ]; - - // Verify all tests passed - const failures = results.filter((r) => !r.success); - - if (failures.length > 0) { - return { - success: false, - message: `${failures.length} test(s) failed`, - totalTests: results.length, - passedTests: results.length - failures.length, - failedTests: failures.length, - results, - failures, - }; - } - - return { - success: true, - message: "All shared test suites passed", - totalTests: results.length, - passedTests: results.length, - failedTests: 0, - results, - }; - } finally { - // Clean up test environment - await cleanupTestEnvironment(adapters); - } - } catch (error) { - return { - success: false, - message: `Error during smoke test: ${error instanceof Error ? error.message : String(error)}`, - totalTests: 0, - passedTests: 0, - failedTests: 0, - }; - } -} - -export default { - async fetch(request: Request, _env: Env): Promise { - const url = new URL(request.url); - - if (url.pathname === "/test") { - const result = await runSharedTestSuites(); - - return new Response(JSON.stringify(result, null, 2), { - headers: { "Content-Type": "application/json" }, - status: result.success ? 200 : 500, - }); - } - - return new Response( - `Braintrust Cloudflare Worker Smoke Test - -GET /test - Run shared test suites - -This worker tests the Braintrust SDK in a Cloudflare Workers environment -using shared test suites for consistency across runtime environments.`, - { - headers: { "Content-Type": "text/plain" }, - }, - ); - }, -}; +export { default } from "./node"; diff --git a/js/smoke/tests/cloudflare-worker/src/node.ts b/js/smoke/tests/cloudflare-worker/src/node.ts new file mode 100644 index 000000000..23e7c00f0 --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/node.ts @@ -0,0 +1,4 @@ +import * as braintrust from "braintrust"; +import { createWorker } from "./worker"; + +export default createWorker(braintrust); diff --git a/js/smoke/tests/cloudflare-worker/src/worker.ts b/js/smoke/tests/cloudflare-worker/src/worker.ts new file mode 100644 index 000000000..58393212a --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/worker.ts @@ -0,0 +1,115 @@ +import { + setupTestEnvironment, + cleanupTestEnvironment, + runBasicLoggingTests, + runEvalSmokeTest, + runImportVerificationTests, + runPromptTemplatingTests, + type TestResult, +} from "../../../shared/dist/index.mjs"; + +// Cloudflare Worker environment bindings (empty for this test) +interface Env {} + +interface TestResponse { + success: boolean; + message: string; + totalTests?: number; + passedTests?: number; + failedTests?: number; + results?: TestResult[]; + failures?: TestResult[]; +} + +export function createWorker( + braintrust: typeof import("braintrust") | typeof import("braintrust/browser"), +): { + fetch(request: Request, _env: Env): Promise; +} { + const { initLogger, _exportsForTestingOnly } = braintrust; + + async function runSharedTestSuites(): Promise { + try { + const adapters = await setupTestEnvironment({ + initLogger, + testingExports: _exportsForTestingOnly, + canUseFileSystem: false, + canUseCLI: false, + environment: "cloudflare-worker", + }); + + try { + const importResults = await runImportVerificationTests(braintrust); + const functionalResults = await runBasicLoggingTests(adapters); + const evalResult = await runEvalSmokeTest(adapters, braintrust); + const promptTemplatingResults = await runPromptTemplatingTests( + { Prompt: braintrust.Prompt }, + adapters.environment, + ); + + const results = [ + ...importResults, + ...functionalResults, + evalResult, + ...promptTemplatingResults, + ]; + + const failures = results.filter((r) => !r.success); + if (failures.length > 0) { + return { + success: false, + message: `${failures.length} test(s) failed`, + totalTests: results.length, + passedTests: results.length - failures.length, + failedTests: failures.length, + results, + failures, + }; + } + + return { + success: true, + message: "All shared test suites passed", + totalTests: results.length, + passedTests: results.length, + failedTests: 0, + results, + }; + } finally { + await cleanupTestEnvironment(adapters); + } + } catch (error) { + return { + success: false, + message: `Error during smoke test: ${error instanceof Error ? error.message : String(error)}`, + totalTests: 0, + passedTests: 0, + failedTests: 0, + }; + } + } + + return { + async fetch(request: Request, _env: Env): Promise { + const url = new URL(request.url); + + if (url.pathname === "/test") { + const result = await runSharedTestSuites(); + return new Response(JSON.stringify(result, null, 2), { + headers: { "Content-Type": "application/json" }, + status: result.success ? 200 : 500, + }); + } + + return new Response( + `Braintrust Cloudflare Worker Smoke Test + +GET /test - Run shared test suites + +This worker tests the Braintrust SDK in a Cloudflare Workers environment +using shared test suites for consistency across runtime environments.`, + { headers: { "Content-Type": "text/plain" } }, + ); + }, + }; +} diff --git a/js/smoke/tests/cloudflare-worker/wrangler.browser.toml b/js/smoke/tests/cloudflare-worker/wrangler.browser.toml new file mode 100644 index 000000000..cbbd2e4f1 --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/wrangler.browser.toml @@ -0,0 +1,3 @@ +name = "braintrust-smoke-test-browser" +main = "src/browser.ts" +compatibility_date = "2024-09-23" diff --git a/js/smoke/tests/cloudflare-worker/wrangler.toml b/js/smoke/tests/cloudflare-worker/wrangler.node.toml similarity index 57% rename from js/smoke/tests/cloudflare-worker/wrangler.toml rename to js/smoke/tests/cloudflare-worker/wrangler.node.toml index 209f954f6..35b9b71cb 100644 --- a/js/smoke/tests/cloudflare-worker/wrangler.toml +++ b/js/smoke/tests/cloudflare-worker/wrangler.node.toml @@ -1,4 +1,4 @@ -name = "braintrust-smoke-test" -main = "src/index.ts" +name = "braintrust-smoke-test-node" +main = "src/node.ts" compatibility_date = "2024-09-23" compatibility_flags = ["nodejs_compat_v2"] diff --git a/js/src/template/nunjucks-env.browser.ts b/js/src/template/nunjucks-env.browser.ts new file mode 100644 index 000000000..000176c04 --- /dev/null +++ b/js/src/template/nunjucks-env.browser.ts @@ -0,0 +1,11 @@ +export function getNunjucksEnv(): never { + throw new Error( + "Nunjucks templating is not supported in this build. Use templateFormat: 'mustache' (or omit templateFormat).", + ); +} + +export function renderNunjucksString(): never { + throw new Error( + "Nunjucks templating is not supported in this build. Use templateFormat: 'mustache' (or omit templateFormat).", + ); +} diff --git a/js/src/template/nunjucks-utils.browser.ts b/js/src/template/nunjucks-utils.browser.ts new file mode 100644 index 000000000..cdf1a5c77 --- /dev/null +++ b/js/src/template/nunjucks-utils.browser.ts @@ -0,0 +1,5 @@ +export function lintTemplate(): never { + throw new Error( + "Nunjucks templating is not supported in this build. Use templateFormat: 'mustache' (or omit templateFormat).", + ); +} diff --git a/js/tsup.config.ts b/js/tsup.config.ts index e7674688e..566f3645b 100644 --- a/js/tsup.config.ts +++ b/js/tsup.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "tsup"; +import path from "node:path"; export default defineConfig([ { @@ -19,7 +20,46 @@ export default defineConfig([ entry: ["src/browser.ts"], format: ["cjs", "esm"], outDir: "dist", - external: ["zod"], + external: ["zod", "nunjucks"], + esbuildPlugins: [ + { + name: "browser-stubs", + setup(build) { + const resolveTo = (p: string) => ({ + path: p, + namespace: "file", + }); + + build.onResolve({ filter: /^\.\/template\/nunjucks-env$/ }, () => + resolveTo( + path.join(process.cwd(), "src/template/nunjucks-env.browser.ts"), + ), + ); + build.onResolve({ filter: /^\.\/template\/nunjucks-utils$/ }, () => + resolveTo( + path.join( + process.cwd(), + "src/template/nunjucks-utils.browser.ts", + ), + ), + ); + + build.onResolve({ filter: /^\.\/nunjucks-env$/ }, (args) => + resolveTo( + path.join(path.dirname(args.importer), "nunjucks-env.browser.ts"), + ), + ); + build.onResolve({ filter: /^\.\/nunjucks-utils$/ }, (args) => + resolveTo( + path.join( + path.dirname(args.importer), + "nunjucks-utils.browser.ts", + ), + ), + ); + }, + }, + ], dts: { // Split DTS generation to reduce memory usage compilerOptions: { From 6606c481675660bd9367c27426d4b68fb47e6f08 Mon Sep 17 00:00:00 2001 From: Caitlin Pinn Date: Thu, 15 Jan 2026 15:55:13 -0800 Subject: [PATCH 2/8] remove support in nextjs --- .github/workflows/js.yaml | 5 +- js/smoke/run-tests.sh | 13 ++++-- .../shared/src/suites/import-verification.ts | 13 ++++++ .../shared/src/suites/prompt-templating.ts | 46 +++++++++---------- js/smoke/tests/cloudflare-worker/package.json | 2 + js/smoke/tests/cloudflare-worker/run-test.mjs | 30 ++++++++++++ js/smoke/tests/deno/shared_suite_test.ts | 9 ++-- js/smoke/tests/deno/span_test.ts | 2 +- .../app/api/smoke-test/edge/route.ts | 9 ++-- 9 files changed, 94 insertions(+), 35 deletions(-) diff --git a/.github/workflows/js.yaml b/.github/workflows/js.yaml index 1dee38552..62015a7b9 100644 --- a/.github/workflows/js.yaml +++ b/.github/workflows/js.yaml @@ -398,6 +398,7 @@ jobs: fail-fast: false matrix: node-version: [20, 22] + mode: [node, browser] steps: - name: Checkout repository @@ -439,6 +440,8 @@ jobs: - name: Run Cloudflare Worker smoke test working-directory: js/smoke shell: bash + env: + CLOUDFLARE_WORKER_MODE: ${{ matrix.mode }} run: | ./run-tests.sh cloudflare-worker @@ -567,6 +570,6 @@ jobs: - name: Run Deno smoke test working-directory: js/smoke env: - BRAINTRUST_BUILD_DIR: ${{ github.workspace }}/js/smoke/tests/deno/build/braintrust/dist/browser.mjs + BRAINTRUST_BUILD_DIR: ${{ github.workspace }}/js/smoke/tests/deno/build/braintrust/dist/index.mjs run: | ./run-tests.sh deno diff --git a/js/smoke/run-tests.sh b/js/smoke/run-tests.sh index ff46415df..70ab82c73 100755 --- a/js/smoke/run-tests.sh +++ b/js/smoke/run-tests.sh @@ -161,11 +161,18 @@ run_test() { cd "$test_dir" - log_info "Running: npm test" + local npm_script="test" + + # Allow CI to run Cloudflare Worker variants as separate pipeline jobs + if [ "$test_name" = "cloudflare-worker" ] && [ -n "${CLOUDFLARE_WORKER_MODE:-}" ]; then + npm_script="test:${CLOUDFLARE_WORKER_MODE}" + fi + + log_info "Running: npm run $npm_script" # Set BRAINTRUST_BUILD_DIR for Deno tests if not already set if [ "$test_name" = "deno" ] && [ -z "${BRAINTRUST_BUILD_DIR:-}" ]; then - local deno_build_file="$TESTS_DIR/deno/build/braintrust/dist/browser.mjs" + local deno_build_file="$TESTS_DIR/deno/build/braintrust/dist/index.mjs" if [ -f "$deno_build_file" ]; then # Convert to absolute path for Deno file:// imports local abs_dir="$(cd "$(dirname "$deno_build_file")" && pwd)" @@ -178,7 +185,7 @@ run_test() { local test_output local test_exit_code - if test_output=$(npm test 2>&1); then + if test_output=$(npm run "$npm_script" 2>&1); then test_exit_code=0 else test_exit_code=$? diff --git a/js/smoke/shared/src/suites/import-verification.ts b/js/smoke/shared/src/suites/import-verification.ts index 23c7b3043..1d1127c8c 100644 --- a/js/smoke/shared/src/suites/import-verification.ts +++ b/js/smoke/shared/src/suites/import-verification.ts @@ -150,6 +150,7 @@ export async function testCoreLoggingExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -183,6 +184,7 @@ export async function testDatasetExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -219,6 +221,7 @@ export async function testPromptExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -263,6 +266,7 @@ export async function testExperimentExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -333,6 +337,7 @@ export async function testEvalExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -375,6 +380,7 @@ export async function testTracingExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -424,6 +430,7 @@ export async function testClientWrapperExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -467,6 +474,7 @@ export async function testUtilityExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -500,6 +508,7 @@ export async function testFunctionExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -536,6 +545,7 @@ export async function testFramework2Exports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -566,6 +576,7 @@ export async function testIDGeneratorExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -599,6 +610,7 @@ export async function testTestingExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } @@ -632,6 +644,7 @@ export async function testStateManagementExports( success: false, testName, error: error as Error, + message: error instanceof Error ? error.message : String(error), }; } } diff --git a/js/smoke/shared/src/suites/prompt-templating.ts b/js/smoke/shared/src/suites/prompt-templating.ts index 05105af10..db4767b49 100644 --- a/js/smoke/shared/src/suites/prompt-templating.ts +++ b/js/smoke/shared/src/suites/prompt-templating.ts @@ -94,6 +94,15 @@ export async function testNunjucksTemplate( ): Promise { const testName = "testNunjucksTemplate"; + if (environment === "cloudflare-worker") { + return { + success: true, + testName, + message: + "Nunjucks template test skipped - not supported in Cloudflare Workers", + }; + } + try { const { Prompt } = module; @@ -143,33 +152,20 @@ export async function testNunjucksTemplate( const errorMessage = buildError instanceof Error ? buildError.message : String(buildError); - // Some builds/environments intentionally do not ship nunjucks support. - if ( - (environment === "browser" || environment === "cloudflare-worker") && - errorMessage.includes( - "Nunjucks templating is not supported in this build", - ) - ) { - return { - success: true, - testName, - message: - "Nunjucks template test passed - threw expected unsupported error", - }; - } + const isUnsupported = errorMessage.includes( + "Nunjucks templating is not supported in this build", + ); - // Special handling for Cloudflare Workers environment, they disallow code generation if ( - environment === "cloudflare-worker" && - errorMessage.includes( - "String template rendering. Disallowed in this environment for security reasons", - ) + (environment === "browser" || + environment === "cloudflare-worker" || + environment === "nextjs-edge-runtime") && + isUnsupported ) { return { success: true, testName, - message: - "Nunjucks template test skipped - Cloudflare Workers does not support string template rendering", + message: "Nunjucks template test skipped - nunjucks unsupported", }; } @@ -182,10 +178,12 @@ export async function testNunjucksTemplate( } try { + const expected = "Items: apple, banana, cherry"; + const actual = nunjucksResult.messages[0]?.content; assertEqual( - nunjucksResult.messages[0]?.content, - "Items: apple, banana, cherry", - "Nunjucks template should render loop correctly", + actual, + expected, + `Nunjucks template should render loop correctly (expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)})`, ); } catch (assertError) { return { diff --git a/js/smoke/tests/cloudflare-worker/package.json b/js/smoke/tests/cloudflare-worker/package.json index 9a58e9af9..a57ccef14 100644 --- a/js/smoke/tests/cloudflare-worker/package.json +++ b/js/smoke/tests/cloudflare-worker/package.json @@ -5,6 +5,8 @@ "private": true, "scripts": { "test": "node run-test.mjs", + "test:node": "node run-test.mjs --config wrangler.node.toml", + "test:browser": "node run-test.mjs --config wrangler.browser.toml", "backup": "npx tsx ../../backup-and-restore.ts backup", "restore": "npx tsx ../../backup-and-restore.ts restore", "install-build": "npx tsx ../../install-build.ts ../../../artifacts braintrust" diff --git a/js/smoke/tests/cloudflare-worker/run-test.mjs b/js/smoke/tests/cloudflare-worker/run-test.mjs index 7ed26f6d8..9a3e6641d 100644 --- a/js/smoke/tests/cloudflare-worker/run-test.mjs +++ b/js/smoke/tests/cloudflare-worker/run-test.mjs @@ -6,6 +6,21 @@ const RETRY_DELAY_MS = 250; const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +function parseArgs(argv) { + const out = {}; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a === "--config") { + const v = argv[i + 1]; + if (!v) throw new Error("Missing value for --config"); + out.config = v; + i++; + continue; + } + } + return out; +} + function killPort(port) { try { execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null || true`, { @@ -82,6 +97,21 @@ async function runWranglerTest({ config, label }) { } async function main() { + const args = parseArgs(process.argv.slice(2)); + if (args.config) { + const label = + args.config === "wrangler.node.toml" + ? "nodejs_compat_v2 + braintrust" + : args.config === "wrangler.browser.toml" + ? "no compatibility_flags + braintrust/browser" + : args.config; + const code = await runWranglerTest({ + config: args.config, + label, + }); + process.exit(code); + } + const a = await runWranglerTest({ config: "wrangler.node.toml", label: "nodejs_compat_v2 + braintrust", diff --git a/js/smoke/tests/deno/shared_suite_test.ts b/js/smoke/tests/deno/shared_suite_test.ts index a5265ca19..8e6a337eb 100644 --- a/js/smoke/tests/deno/shared_suite_test.ts +++ b/js/smoke/tests/deno/shared_suite_test.ts @@ -49,9 +49,12 @@ export async function runSharedTestSuites() { const evalResult = await runEvalSmokeTest(adapters, braintrust); // Run prompt templating tests - const promptTemplatingResults = await runPromptTemplatingTests({ - Prompt: braintrust.Prompt, - }); + const promptTemplatingResults = await runPromptTemplatingTests( + { + Prompt: braintrust.Prompt, + }, + adapters.environment, + ); // Combine results const results = [ diff --git a/js/smoke/tests/deno/span_test.ts b/js/smoke/tests/deno/span_test.ts index 0ebdc885f..c546e2cea 100644 --- a/js/smoke/tests/deno/span_test.ts +++ b/js/smoke/tests/deno/span_test.ts @@ -9,7 +9,7 @@ import { /** * This is a simple test to send a span to the braintrust API * Uses BRAINTRUST_BUILD_DIR environment variable to import the braintrust package - * ie. BRAINTRUST_BUILD_DIR=./package/dist/browser.mjs + * ie. BRAINTRUST_BUILD_DIR=./package/dist/index.mjs */ export async function runBrowserLoggerSmokeTest() { const buildDir = Deno.env.get("BRAINTRUST_BUILD_DIR"); diff --git a/js/smoke/tests/nextjs-instrumentation/app/api/smoke-test/edge/route.ts b/js/smoke/tests/nextjs-instrumentation/app/api/smoke-test/edge/route.ts index 25e3adce0..ecf6b74f1 100644 --- a/js/smoke/tests/nextjs-instrumentation/app/api/smoke-test/edge/route.ts +++ b/js/smoke/tests/nextjs-instrumentation/app/api/smoke-test/edge/route.ts @@ -60,9 +60,12 @@ export async function GET(): Promise> { const evalResult = await runEvalSmokeTest(adapters, braintrust); // Run prompt templating tests - const promptTemplatingResults = await runPromptTemplatingTests({ - Prompt: braintrust.Prompt, - }); + const promptTemplatingResults = await runPromptTemplatingTests( + { + Prompt: braintrust.Prompt, + }, + adapters.environment, + ); // Combine results const results = [ From 25c561c76d78dc70be43b226d79574786bda9115 Mon Sep 17 00:00:00 2001 From: Caitlin Pinn Date: Thu, 15 Jan 2026 16:06:12 -0800 Subject: [PATCH 3/8] updates --- .../shared/src/suites/prompt-templating.ts | 29 +- .../tests/cloudflare-worker/package-lock.json | 1153 ++++++++--------- js/smoke/tests/cloudflare-worker/package.json | 2 +- .../tests/cloudflare-worker/src/browser.ts | 2 +- js/smoke/tests/cloudflare-worker/src/node.ts | 2 +- .../tests/cloudflare-worker/src/worker.ts | 3 +- js/src/template/nunjucks-env.ts | 10 - 7 files changed, 540 insertions(+), 661 deletions(-) diff --git a/js/smoke/shared/src/suites/prompt-templating.ts b/js/smoke/shared/src/suites/prompt-templating.ts index db4767b49..2db349a11 100644 --- a/js/smoke/shared/src/suites/prompt-templating.ts +++ b/js/smoke/shared/src/suites/prompt-templating.ts @@ -94,15 +94,6 @@ export async function testNunjucksTemplate( ): Promise { const testName = "testNunjucksTemplate"; - if (environment === "cloudflare-worker") { - return { - success: true, - testName, - message: - "Nunjucks template test skipped - not supported in Cloudflare Workers", - }; - } - try { const { Prompt } = module; @@ -158,14 +149,30 @@ export async function testNunjucksTemplate( if ( (environment === "browser" || - environment === "cloudflare-worker" || + environment === "cloudflare-worker-browser" || environment === "nextjs-edge-runtime") && isUnsupported ) { return { success: true, testName, - message: "Nunjucks template test skipped - nunjucks unsupported", + message: + "Nunjucks template test passed - threw expected unsupported error", + }; + } + + // In Cloudflare Workers (even with nodejs_compat), string-based template codegen is disallowed. + if ( + environment === "cloudflare-worker-node" && + errorMessage.includes( + "Code generation from strings disallowed for this context", + ) + ) { + return { + success: true, + testName, + message: + "Nunjucks template test passed - threw expected codegen-disallowed error", }; } diff --git a/js/smoke/tests/cloudflare-worker/package-lock.json b/js/smoke/tests/cloudflare-worker/package-lock.json index 84ed25a59..1e11dff58 100644 --- a/js/smoke/tests/cloudflare-worker/package-lock.json +++ b/js/smoke/tests/cloudflare-worker/package-lock.json @@ -8,7 +8,7 @@ "name": "braintrust-cloudflare-worker-smoke-test", "version": "1.0.0", "dependencies": { - "braintrust": "latest", + "braintrust": "file:../../../artifacts/braintrust-2.0.2.tgz", "zod": "^3.25.76" }, "devDependencies": { @@ -164,6 +164,16 @@ "dev": true, "license": "MIT OR Apache-2.0" }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -189,9 +199,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -205,9 +215,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -221,9 +231,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -237,9 +247,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -253,9 +263,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -269,9 +279,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -285,9 +295,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -301,9 +311,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -317,9 +327,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -333,9 +343,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -349,9 +359,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -365,9 +375,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -381,9 +391,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -397,9 +407,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -413,9 +423,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -429,9 +439,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -445,9 +455,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -461,9 +471,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -477,9 +487,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -493,9 +503,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -509,9 +519,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -525,9 +535,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "cpu": [ "arm64" ], @@ -541,9 +551,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -557,9 +567,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -573,9 +583,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -589,9 +599,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -1118,6 +1128,12 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/@types/nunjucks": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.2.6.tgz", + "integrity": "sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==", + "license": "MIT" + }, "node_modules/@vercel/functions": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vercel/functions/-/functions-1.6.0.tgz", @@ -1135,6 +1151,12 @@ } } }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", + "license": "MIT" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1171,6 +1193,15 @@ "node": ">=0.4.0" } }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1207,6 +1238,12 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1244,6 +1281,90 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1254,39 +1375,42 @@ } }, "node_modules/braintrust": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/braintrust/-/braintrust-0.4.10.tgz", - "integrity": "sha512-Dk4aVsHWjgeTyGcM056vleXZGLz3qIHw6rDYP4iyY4DOZeD6JSDGSepGb1dulpcwqf2WC6w9XIFTF7Y/4gfahg==", + "version": "2.0.2", + "resolved": "file:../../../artifacts/braintrust-2.0.2.tgz", + "integrity": "sha512-Vr+KB64EeHXaWWNHLiCHPz01Cc/hx6zdEb5Nlft0W6RSYUZcbWPVttAce6ixyrbs/fBfRCNI4pclGIQOzRPD5w==", "license": "MIT", "dependencies": { "@ai-sdk/provider": "^1.1.3", "@next/env": "^14.2.3", + "@types/nunjucks": "^3.2.6", "@vercel/functions": "^1.0.2", "argparse": "^2.0.1", + "boxen": "^8.0.1", "chalk": "^4.1.2", "cli-progress": "^3.12.0", + "cli-table3": "^0.6.5", "cors": "^2.8.5", "dotenv": "^16.4.5", - "esbuild": "^0.25.10", + "esbuild": "^0.27.0", "eventsource-parser": "^1.1.2", "express": "^4.21.2", "graceful-fs": "^4.2.11", "http-errors": "^2.0.0", "minimatch": "^9.0.3", "mustache": "^4.2.0", + "nunjucks": "^3.2.4", "pluralize": "^8.0.0", "simple-git": "^3.21.0", - "slugify": "^1.6.6", "source-map": "^0.7.4", + "termi-link": "^1.0.1", "uuid": "^9.0.1", - "zod": "^3.25.34", - "zod-to-json-schema": "^3.22.5" + "zod-to-json-schema": "^3.25.0" }, "bin": { "braintrust": "dist/cli.js" }, "peerDependencies": { - "zod": "^3.25.34" + "zod": "^3.25.34 || ^4.0" } }, "node_modules/bytes": { @@ -1327,6 +1451,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1343,6 +1479,18 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-progress": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", @@ -1355,6 +1503,21 @@ "node": ">=4" } }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -1398,6 +1561,15 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1573,9 +1745,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -1585,32 +1757,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escape-html": { @@ -1756,6 +1928,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2088,6 +2272,31 @@ "node": ">= 0.6" } }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "license": "BSD-2-Clause", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2553,15 +2762,6 @@ "is-arrayish": "^0.3.1" } }, - "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -2629,6 +2829,15 @@ "node": ">=8" } }, + "node_modules/termi-link": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/termi-link/-/termi-link-1.1.0.tgz", + "integrity": "sha512-2qSN6TnomHgVLtk+htSWbaYs4Rd2MH/RU7VpHTy6MBstyNyWbM4yKd1DCYpE3fDg8dmGWojXCngNi/MHCzGuAA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2666,575 +2875,168 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=18" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, "engines": { - "node": ">=18" + "node": ">= 0.6" } }, - "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", - "cpu": [ - "arm64" - ], + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=18" + "node": ">=14.17" } }, - "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", - "cpu": [ - "x64" - ], + "node_modules/undici": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz", + "integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": ">=20.18.1" } }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", - "cpu": [ - "arm64" - ], + "node_modules/unenv": { + "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "dependencies": { + "pathe": "^2.0.3" } }, - "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=18" + "node": ">= 0.8" } }, - "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=18" + "node": ">= 0.4.0" } }, - "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", - "cpu": [ - "x64" + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", - "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], "engines": { - "node": ">=18" + "node": ">= 0.8" } }, - "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "string-width": "^7.0.0" + }, "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", - "cpu": [ - "ia32" - ], - "dev": true, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" }, - "node_modules/tsx/node_modules/esbuild": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", - "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "dev": true, - "hasInstallScript": true, + "node_modules/widest-line/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.1", - "@esbuild/android-arm": "0.27.1", - "@esbuild/android-arm64": "0.27.1", - "@esbuild/android-x64": "0.27.1", - "@esbuild/darwin-arm64": "0.27.1", - "@esbuild/darwin-x64": "0.27.1", - "@esbuild/freebsd-arm64": "0.27.1", - "@esbuild/freebsd-x64": "0.27.1", - "@esbuild/linux-arm": "0.27.1", - "@esbuild/linux-arm64": "0.27.1", - "@esbuild/linux-ia32": "0.27.1", - "@esbuild/linux-loong64": "0.27.1", - "@esbuild/linux-mips64el": "0.27.1", - "@esbuild/linux-ppc64": "0.27.1", - "@esbuild/linux-riscv64": "0.27.1", - "@esbuild/linux-s390x": "0.27.1", - "@esbuild/linux-x64": "0.27.1", - "@esbuild/netbsd-arm64": "0.27.1", - "@esbuild/netbsd-x64": "0.27.1", - "@esbuild/openbsd-arm64": "0.27.1", - "@esbuild/openbsd-x64": "0.27.1", - "@esbuild/openharmony-arm64": "0.27.1", - "@esbuild/sunos-x64": "0.27.1", - "@esbuild/win32-arm64": "0.27.1", - "@esbuild/win32-ia32": "0.27.1", - "@esbuild/win32-x64": "0.27.1" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "node": ">=12" }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz", - "integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/unenv": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", - "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pathe": "^2.0.3" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/workerd": { @@ -3784,6 +3586,85 @@ "dev": true, "license": "MIT" }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", diff --git a/js/smoke/tests/cloudflare-worker/package.json b/js/smoke/tests/cloudflare-worker/package.json index a57ccef14..73a4e801f 100644 --- a/js/smoke/tests/cloudflare-worker/package.json +++ b/js/smoke/tests/cloudflare-worker/package.json @@ -12,7 +12,7 @@ "install-build": "npx tsx ../../install-build.ts ../../../artifacts braintrust" }, "dependencies": { - "braintrust": "latest", + "braintrust": "file:../../../artifacts/braintrust-2.0.2.tgz", "zod": "^3.25.76" }, "devDependencies": { diff --git a/js/smoke/tests/cloudflare-worker/src/browser.ts b/js/smoke/tests/cloudflare-worker/src/browser.ts index d6f039d99..8957c1c81 100644 --- a/js/smoke/tests/cloudflare-worker/src/browser.ts +++ b/js/smoke/tests/cloudflare-worker/src/browser.ts @@ -1,4 +1,4 @@ import * as braintrust from "braintrust/browser"; import { createWorker } from "./worker"; -export default createWorker(braintrust); +export default createWorker(braintrust, "cloudflare-worker-browser"); diff --git a/js/smoke/tests/cloudflare-worker/src/node.ts b/js/smoke/tests/cloudflare-worker/src/node.ts index 23e7c00f0..4e9efff06 100644 --- a/js/smoke/tests/cloudflare-worker/src/node.ts +++ b/js/smoke/tests/cloudflare-worker/src/node.ts @@ -1,4 +1,4 @@ import * as braintrust from "braintrust"; import { createWorker } from "./worker"; -export default createWorker(braintrust); +export default createWorker(braintrust, "cloudflare-worker-node"); diff --git a/js/smoke/tests/cloudflare-worker/src/worker.ts b/js/smoke/tests/cloudflare-worker/src/worker.ts index 58393212a..f64fef263 100644 --- a/js/smoke/tests/cloudflare-worker/src/worker.ts +++ b/js/smoke/tests/cloudflare-worker/src/worker.ts @@ -23,6 +23,7 @@ interface TestResponse { export function createWorker( braintrust: typeof import("braintrust") | typeof import("braintrust/browser"), + environment: string, ): { fetch(request: Request, _env: Env): Promise; } { @@ -35,7 +36,7 @@ export function createWorker( testingExports: _exportsForTestingOnly, canUseFileSystem: false, canUseCLI: false, - environment: "cloudflare-worker", + environment, }); try { diff --git a/js/src/template/nunjucks-env.ts b/js/src/template/nunjucks-env.ts index 82a4951fd..b23f1cf44 100644 --- a/js/src/template/nunjucks-env.ts +++ b/js/src/template/nunjucks-env.ts @@ -29,16 +29,6 @@ export function renderNunjucksString( try { return getNunjucksEnv(strict).renderString(template, variables); } catch (error) { - if ( - error instanceof Error && - error.message.includes( - "Code generation from strings disallowed for this context", - ) - ) { - throw new Error( - `String template rendering. Disallowed in this environment for security reasons. Try a different template renderer. Original error: ${error.message}`, - ); - } throw error; } } From 723dad2a3aa055e22e0b177af7006323d5bf8c40 Mon Sep 17 00:00:00 2001 From: Caitlin Pinn Date: Thu, 15 Jan 2026 16:30:18 -0800 Subject: [PATCH 4/8] add full matrix of compatibility --- .github/workflows/js.yaml | 8 +++++++- js/smoke/tests/browser/package-lock.json | 8 ++++---- js/smoke/tests/browser/package.json | 2 +- js/smoke/tests/cloudflare-worker/package.json | 6 ++++-- js/smoke/tests/cloudflare-worker/run-test.mjs | 19 +++++++++++++++++-- .../wrangler.browser-node-compat.toml | 4 ++++ .../wrangler.node-no-compat.toml | 3 +++ 7 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml create mode 100644 js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml diff --git a/.github/workflows/js.yaml b/.github/workflows/js.yaml index 62015a7b9..34a5cf795 100644 --- a/.github/workflows/js.yaml +++ b/.github/workflows/js.yaml @@ -398,7 +398,13 @@ jobs: fail-fast: false matrix: node-version: [20, 22] - mode: [node, browser] + mode: + [ + braintrust-node-compat, + braintrust-browser-node-compat, + braintrust-browser-no-compat, + braintrust-no-compat, + ] steps: - name: Checkout repository diff --git a/js/smoke/tests/browser/package-lock.json b/js/smoke/tests/browser/package-lock.json index bf4a6b3cc..2376d7835 100644 --- a/js/smoke/tests/browser/package-lock.json +++ b/js/smoke/tests/browser/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "braintrust": "latest", + "braintrust": "file:../../../artifacts/braintrust-2.0.2.tgz", "zod": "^4.3.5" }, "devDependencies": { @@ -767,9 +767,9 @@ } }, "node_modules/braintrust": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/braintrust/-/braintrust-2.0.1.tgz", - "integrity": "sha512-YU5IDDWcPtZDfbI4Zm67YD7c+HiXjAeXa+CuQvwmNDpoViU4eKijeP6drbqgzA3chp6fN4+S11xgdvwGrYqteg==", + "version": "2.0.2", + "resolved": "file:../../../artifacts/braintrust-2.0.2.tgz", + "integrity": "sha512-NwEjar0jc/bEalnQuOZmOcUeUY4GhtzE41rjA2KWOotHNF7Nk4ppyXo6GWlJ9QOAGG465l9MTuuekFJzU+Goag==", "license": "MIT", "dependencies": { "@ai-sdk/provider": "^1.1.3", diff --git a/js/smoke/tests/browser/package.json b/js/smoke/tests/browser/package.json index 98f978d3c..0d69d94f8 100644 --- a/js/smoke/tests/browser/package.json +++ b/js/smoke/tests/browser/package.json @@ -16,7 +16,7 @@ "install-build": "npm install --legacy-peer-deps && npx tsx ../../install-build.ts ../../../artifacts braintrust" }, "dependencies": { - "braintrust": "latest", + "braintrust": "file:../../../artifacts/braintrust-2.0.2.tgz", "zod": "^4.3.5" }, "devDependencies": { diff --git a/js/smoke/tests/cloudflare-worker/package.json b/js/smoke/tests/cloudflare-worker/package.json index 73a4e801f..b7f24aac8 100644 --- a/js/smoke/tests/cloudflare-worker/package.json +++ b/js/smoke/tests/cloudflare-worker/package.json @@ -5,8 +5,10 @@ "private": true, "scripts": { "test": "node run-test.mjs", - "test:node": "node run-test.mjs --config wrangler.node.toml", - "test:browser": "node run-test.mjs --config wrangler.browser.toml", + "test:braintrust-node-compat": "node run-test.mjs --config wrangler.node.toml", + "test:braintrust-browser-node-compat": "node run-test.mjs --config wrangler.browser-node-compat.toml", + "test:braintrust-browser-no-compat": "node run-test.mjs --config wrangler.browser.toml", + "test:braintrust-no-compat": "node run-test.mjs --config wrangler.node-no-compat.toml --expect-start-fail", "backup": "npx tsx ../../backup-and-restore.ts backup", "restore": "npx tsx ../../backup-and-restore.ts restore", "install-build": "npx tsx ../../install-build.ts ../../../artifacts braintrust" diff --git a/js/smoke/tests/cloudflare-worker/run-test.mjs b/js/smoke/tests/cloudflare-worker/run-test.mjs index 9a3e6641d..66e3cd7a1 100644 --- a/js/smoke/tests/cloudflare-worker/run-test.mjs +++ b/js/smoke/tests/cloudflare-worker/run-test.mjs @@ -17,6 +17,10 @@ function parseArgs(argv) { i++; continue; } + if (a === "--expect-start-fail") { + out.expectStartFail = true; + continue; + } } return out; } @@ -40,7 +44,7 @@ async function waitForServer() { return false; } -async function runWranglerTest({ config, label }) { +async function runWranglerTest({ config, label, expectStartFail = false }) { killPort(PORT); const wrangler = spawn( @@ -76,6 +80,12 @@ async function runWranglerTest({ config, label }) { try { if (!(await waitForServer())) { + if (expectStartFail) { + console.log(`\n=== ${label} (expected startup failure) ===\n`); + console.log(output.trim() ? output : "(no output)"); + await killWrangler(); + return 0; + } console.error(`[${label}] Server failed to start:\n`, output); await killWrangler(); return 1; @@ -104,10 +114,15 @@ async function main() { ? "nodejs_compat_v2 + braintrust" : args.config === "wrangler.browser.toml" ? "no compatibility_flags + braintrust/browser" - : args.config; + : args.config === "wrangler.browser-node-compat.toml" + ? "nodejs_compat_v2 + braintrust/browser" + : args.config === "wrangler.node-no-compat.toml" + ? "no compatibility_flags + braintrust" + : args.config; const code = await runWranglerTest({ config: args.config, label, + expectStartFail: !!args.expectStartFail, }); process.exit(code); } diff --git a/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml b/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml new file mode 100644 index 000000000..42615b360 --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml @@ -0,0 +1,4 @@ +name = "braintrust-smoke-test-browser-node-compat" +main = "src/browser.ts" +compatibility_date = "2024-09-23" +compatibility_flags = ["nodejs_compat_v2"] diff --git a/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml b/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml new file mode 100644 index 000000000..d807867db --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml @@ -0,0 +1,3 @@ +name = "braintrust-smoke-test-node-no-compat" +main = "src/node.ts" +compatibility_date = "2024-09-23" From 77ad12bac0e4b3c4e84fa94d63ea6498d147bc56 Mon Sep 17 00:00:00 2001 From: Caitlin Pinn Date: Thu, 15 Jan 2026 16:35:41 -0800 Subject: [PATCH 5/8] ensure all 4 environments are checked --- js/smoke/shared/src/suites/prompt-templating.ts | 5 +++-- js/smoke/tests/cloudflare-worker/src/browser-no-compat.ts | 4 ++++ .../tests/cloudflare-worker/src/browser-node-compat.ts | 7 +++++++ js/smoke/tests/cloudflare-worker/src/browser.ts | 2 +- js/smoke/tests/cloudflare-worker/src/node-no-compat.ts | 4 ++++ js/smoke/tests/cloudflare-worker/src/node-node-compat.ts | 4 ++++ js/smoke/tests/cloudflare-worker/src/node.ts | 2 +- .../cloudflare-worker/wrangler.browser-node-compat.toml | 2 +- js/smoke/tests/cloudflare-worker/wrangler.browser.toml | 2 +- .../tests/cloudflare-worker/wrangler.node-no-compat.toml | 2 +- js/smoke/tests/cloudflare-worker/wrangler.node.toml | 2 +- 11 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 js/smoke/tests/cloudflare-worker/src/browser-no-compat.ts create mode 100644 js/smoke/tests/cloudflare-worker/src/browser-node-compat.ts create mode 100644 js/smoke/tests/cloudflare-worker/src/node-no-compat.ts create mode 100644 js/smoke/tests/cloudflare-worker/src/node-node-compat.ts diff --git a/js/smoke/shared/src/suites/prompt-templating.ts b/js/smoke/shared/src/suites/prompt-templating.ts index 2db349a11..50489ee3c 100644 --- a/js/smoke/shared/src/suites/prompt-templating.ts +++ b/js/smoke/shared/src/suites/prompt-templating.ts @@ -149,7 +149,8 @@ export async function testNunjucksTemplate( if ( (environment === "browser" || - environment === "cloudflare-worker-browser" || + environment === "cloudflare-worker-browser-no-compat" || + environment === "cloudflare-worker-browser-node-compat" || environment === "nextjs-edge-runtime") && isUnsupported ) { @@ -163,7 +164,7 @@ export async function testNunjucksTemplate( // In Cloudflare Workers (even with nodejs_compat), string-based template codegen is disallowed. if ( - environment === "cloudflare-worker-node" && + environment === "cloudflare-worker-node-node-compat" && errorMessage.includes( "Code generation from strings disallowed for this context", ) diff --git a/js/smoke/tests/cloudflare-worker/src/browser-no-compat.ts b/js/smoke/tests/cloudflare-worker/src/browser-no-compat.ts new file mode 100644 index 000000000..c594f3fe3 --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/browser-no-compat.ts @@ -0,0 +1,4 @@ +import * as braintrust from "braintrust/browser"; +import { createWorker } from "./worker"; + +export default createWorker(braintrust, "cloudflare-worker-browser-no-compat"); diff --git a/js/smoke/tests/cloudflare-worker/src/browser-node-compat.ts b/js/smoke/tests/cloudflare-worker/src/browser-node-compat.ts new file mode 100644 index 000000000..80f345d3d --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/browser-node-compat.ts @@ -0,0 +1,7 @@ +import * as braintrust from "braintrust/browser"; +import { createWorker } from "./worker"; + +export default createWorker( + braintrust, + "cloudflare-worker-browser-node-compat", +); diff --git a/js/smoke/tests/cloudflare-worker/src/browser.ts b/js/smoke/tests/cloudflare-worker/src/browser.ts index 8957c1c81..c594f3fe3 100644 --- a/js/smoke/tests/cloudflare-worker/src/browser.ts +++ b/js/smoke/tests/cloudflare-worker/src/browser.ts @@ -1,4 +1,4 @@ import * as braintrust from "braintrust/browser"; import { createWorker } from "./worker"; -export default createWorker(braintrust, "cloudflare-worker-browser"); +export default createWorker(braintrust, "cloudflare-worker-browser-no-compat"); diff --git a/js/smoke/tests/cloudflare-worker/src/node-no-compat.ts b/js/smoke/tests/cloudflare-worker/src/node-no-compat.ts new file mode 100644 index 000000000..98a8e9e1d --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/node-no-compat.ts @@ -0,0 +1,4 @@ +import * as braintrust from "braintrust"; +import { createWorker } from "./worker"; + +export default createWorker(braintrust, "cloudflare-worker-node-no-compat"); diff --git a/js/smoke/tests/cloudflare-worker/src/node-node-compat.ts b/js/smoke/tests/cloudflare-worker/src/node-node-compat.ts new file mode 100644 index 000000000..9339aa6a8 --- /dev/null +++ b/js/smoke/tests/cloudflare-worker/src/node-node-compat.ts @@ -0,0 +1,4 @@ +import * as braintrust from "braintrust"; +import { createWorker } from "./worker"; + +export default createWorker(braintrust, "cloudflare-worker-node-node-compat"); diff --git a/js/smoke/tests/cloudflare-worker/src/node.ts b/js/smoke/tests/cloudflare-worker/src/node.ts index 4e9efff06..9339aa6a8 100644 --- a/js/smoke/tests/cloudflare-worker/src/node.ts +++ b/js/smoke/tests/cloudflare-worker/src/node.ts @@ -1,4 +1,4 @@ import * as braintrust from "braintrust"; import { createWorker } from "./worker"; -export default createWorker(braintrust, "cloudflare-worker-node"); +export default createWorker(braintrust, "cloudflare-worker-node-node-compat"); diff --git a/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml b/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml index 42615b360..508e049b1 100644 --- a/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml +++ b/js/smoke/tests/cloudflare-worker/wrangler.browser-node-compat.toml @@ -1,4 +1,4 @@ name = "braintrust-smoke-test-browser-node-compat" -main = "src/browser.ts" +main = "src/browser-node-compat.ts" compatibility_date = "2024-09-23" compatibility_flags = ["nodejs_compat_v2"] diff --git a/js/smoke/tests/cloudflare-worker/wrangler.browser.toml b/js/smoke/tests/cloudflare-worker/wrangler.browser.toml index cbbd2e4f1..6048efa1d 100644 --- a/js/smoke/tests/cloudflare-worker/wrangler.browser.toml +++ b/js/smoke/tests/cloudflare-worker/wrangler.browser.toml @@ -1,3 +1,3 @@ name = "braintrust-smoke-test-browser" -main = "src/browser.ts" +main = "src/browser-no-compat.ts" compatibility_date = "2024-09-23" diff --git a/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml b/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml index d807867db..98528470d 100644 --- a/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml +++ b/js/smoke/tests/cloudflare-worker/wrangler.node-no-compat.toml @@ -1,3 +1,3 @@ name = "braintrust-smoke-test-node-no-compat" -main = "src/node.ts" +main = "src/node-no-compat.ts" compatibility_date = "2024-09-23" diff --git a/js/smoke/tests/cloudflare-worker/wrangler.node.toml b/js/smoke/tests/cloudflare-worker/wrangler.node.toml index 35b9b71cb..6e94cceb6 100644 --- a/js/smoke/tests/cloudflare-worker/wrangler.node.toml +++ b/js/smoke/tests/cloudflare-worker/wrangler.node.toml @@ -1,4 +1,4 @@ name = "braintrust-smoke-test-node" -main = "src/node.ts" +main = "src/node-node-compat.ts" compatibility_date = "2024-09-23" compatibility_flags = ["nodejs_compat_v2"] From 58d2c4d823c1935c4d607a45583fc59c6544118f Mon Sep 17 00:00:00 2001 From: Caitlin Pinn Date: Thu, 15 Jan 2026 16:41:26 -0800 Subject: [PATCH 6/8] add back proper error, undo browser test changes --- js/smoke/shared/src/suites/prompt-templating.ts | 2 +- js/smoke/tests/browser/package-lock.json | 6 +++--- js/smoke/tests/browser/package.json | 2 +- js/smoke/tests/cloudflare-worker/package-lock.json | 2 +- js/src/template/nunjucks-env.ts | 10 ++++++++++ 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/js/smoke/shared/src/suites/prompt-templating.ts b/js/smoke/shared/src/suites/prompt-templating.ts index 50489ee3c..0e82d3a2e 100644 --- a/js/smoke/shared/src/suites/prompt-templating.ts +++ b/js/smoke/shared/src/suites/prompt-templating.ts @@ -166,7 +166,7 @@ export async function testNunjucksTemplate( if ( environment === "cloudflare-worker-node-node-compat" && errorMessage.includes( - "Code generation from strings disallowed for this context", + "String template rendering. Disallowed in this environment for security reasons", ) ) { return { diff --git a/js/smoke/tests/browser/package-lock.json b/js/smoke/tests/browser/package-lock.json index 2376d7835..b665d9b86 100644 --- a/js/smoke/tests/browser/package-lock.json +++ b/js/smoke/tests/browser/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "braintrust": "file:../../../artifacts/braintrust-2.0.2.tgz", + "braintrust": "latest", "zod": "^4.3.5" }, "devDependencies": { @@ -768,8 +768,8 @@ }, "node_modules/braintrust": { "version": "2.0.2", - "resolved": "file:../../../artifacts/braintrust-2.0.2.tgz", - "integrity": "sha512-NwEjar0jc/bEalnQuOZmOcUeUY4GhtzE41rjA2KWOotHNF7Nk4ppyXo6GWlJ9QOAGG465l9MTuuekFJzU+Goag==", + "resolved": "https://registry.npmjs.org/braintrust/-/braintrust-2.0.2.tgz", + "integrity": "sha512-TOfdi2acpDnne3Pcew/shbwda0SKQvjTsGprr0gLWloaeYehhOPyrsWmwKsRNXOJa01D0czTGyetWpUbcH+euw==", "license": "MIT", "dependencies": { "@ai-sdk/provider": "^1.1.3", diff --git a/js/smoke/tests/browser/package.json b/js/smoke/tests/browser/package.json index 0d69d94f8..98f978d3c 100644 --- a/js/smoke/tests/browser/package.json +++ b/js/smoke/tests/browser/package.json @@ -16,7 +16,7 @@ "install-build": "npm install --legacy-peer-deps && npx tsx ../../install-build.ts ../../../artifacts braintrust" }, "dependencies": { - "braintrust": "file:../../../artifacts/braintrust-2.0.2.tgz", + "braintrust": "latest", "zod": "^4.3.5" }, "devDependencies": { diff --git a/js/smoke/tests/cloudflare-worker/package-lock.json b/js/smoke/tests/cloudflare-worker/package-lock.json index 1e11dff58..8e428b02c 100644 --- a/js/smoke/tests/cloudflare-worker/package-lock.json +++ b/js/smoke/tests/cloudflare-worker/package-lock.json @@ -1377,7 +1377,7 @@ "node_modules/braintrust": { "version": "2.0.2", "resolved": "file:../../../artifacts/braintrust-2.0.2.tgz", - "integrity": "sha512-Vr+KB64EeHXaWWNHLiCHPz01Cc/hx6zdEb5Nlft0W6RSYUZcbWPVttAce6ixyrbs/fBfRCNI4pclGIQOzRPD5w==", + "integrity": "sha512-nU5mFGGJEzth094Hs1qiCRZ59s5PJjW35BgFjx2YBJg38HkMBflvf73NBLeYvgoqBfPFs1OyoGpyEyOB16YASw==", "license": "MIT", "dependencies": { "@ai-sdk/provider": "^1.1.3", diff --git a/js/src/template/nunjucks-env.ts b/js/src/template/nunjucks-env.ts index b23f1cf44..82a4951fd 100644 --- a/js/src/template/nunjucks-env.ts +++ b/js/src/template/nunjucks-env.ts @@ -29,6 +29,16 @@ export function renderNunjucksString( try { return getNunjucksEnv(strict).renderString(template, variables); } catch (error) { + if ( + error instanceof Error && + error.message.includes( + "Code generation from strings disallowed for this context", + ) + ) { + throw new Error( + `String template rendering. Disallowed in this environment for security reasons. Try a different template renderer. Original error: ${error.message}`, + ); + } throw error; } } From dc55e8836d67b1b4c6e1eb4492f24094d9f091a7 Mon Sep 17 00:00:00 2001 From: Olmo Maldonado Date: Fri, 16 Jan 2026 15:20:11 -0800 Subject: [PATCH 7/8] fix missing node: prefix, require eslint node: prefix, and update tsup to avoid removing the prefix --- js/eslint.config.ts | 5 +++ js/package.json | 2 + js/src/node.ts | 6 +-- js/tsup.config.ts | 7 ++++ pnpm-lock.yaml | 100 +++++++++++++++++++++++++++++--------------- 5 files changed, 84 insertions(+), 36 deletions(-) diff --git a/js/eslint.config.ts b/js/eslint.config.ts index 2df170c65..31b778ffd 100644 --- a/js/eslint.config.ts +++ b/js/eslint.config.ts @@ -1,5 +1,6 @@ import tseslint from "@typescript-eslint/eslint-plugin"; import tsparser from "@typescript-eslint/parser"; +import nodeImport from "eslint-plugin-node-import"; import tsupConfigImport from "./tsup.config"; // Handle both ESM and CJS module formats @@ -30,6 +31,7 @@ export default [ }, plugins: { "@typescript-eslint": tseslint, + "node-import": nodeImport, }, rules: { // Base TypeScript rules @@ -54,6 +56,9 @@ export default [ ], "no-unused-expressions": ["error", { allowShortCircuit: true }], "@typescript-eslint/no-unused-expressions": "off", + // Require node: protocol for Node.js built-in imports (for Deno compatibility) + // This plugin automatically detects ALL Node.js built-ins - no manual list needed! + "node-import/prefer-node-protocol": "error", }, }, { diff --git a/js/package.json b/js/package.json index 4652bc6c0..e33d0df50 100644 --- a/js/package.json +++ b/js/package.json @@ -99,6 +99,8 @@ "async": "^3.2.5", "autoevals": "^0.0.131", "cross-env": "^7.0.3", + "eslint-plugin-node-import": "^1.0.5", + "jiti": "^2.6.1", "npm-run-all": "^4.1.5", "openapi-zod-client": "^1.18.3", "prettier": "^3.5.3", diff --git a/js/src/node.ts b/js/src/node.ts index 3ec477263..65fd342e7 100644 --- a/js/src/node.ts +++ b/js/src/node.ts @@ -9,8 +9,8 @@ import iso from "./isomorph"; import { getRepoInfo, getPastNAncestors } from "./gitutil"; import { getCallerLocation } from "./stackutil"; import { _internalSetInitialState } from "./logger"; -import { promisify } from "util"; -import * as zlib from "zlib"; +import { promisify } from "node:util"; +import * as zlib from "node:zlib"; export function configureNode() { iso.getRepoInfo = getRepoInfo; @@ -18,7 +18,7 @@ export function configureNode() { iso.getEnv = (name) => process.env[name]; iso.getCallerLocation = getCallerLocation; iso.newAsyncLocalStorage = () => new AsyncLocalStorage(); - iso.processOn = (event: string, handler: (code: any) => void) => { + iso.processOn = (event: string, handler: (code: unknown) => void) => { process.on(event, handler); }; iso.basename = path.basename; diff --git a/js/tsup.config.ts b/js/tsup.config.ts index 566f3645b..4cc04a6a6 100644 --- a/js/tsup.config.ts +++ b/js/tsup.config.ts @@ -1,5 +1,7 @@ import { defineConfig } from "tsup"; import path from "node:path"; +import fs from "node:fs"; +import { builtinModules } from "node:module"; export default defineConfig([ { @@ -7,6 +9,7 @@ export default defineConfig([ format: ["cjs", "esm"], outDir: "dist", external: ["zod"], + removeNodeProtocol: false, dts: { // Split DTS generation to reduce memory usage compilerOptions: { @@ -20,6 +23,7 @@ export default defineConfig([ entry: ["src/browser.ts"], format: ["cjs", "esm"], outDir: "dist", + removeNodeProtocol: false, external: ["zod", "nunjucks"], esbuildPlugins: [ { @@ -72,6 +76,7 @@ export default defineConfig([ { entry: { cli: "src/cli/index.ts" }, format: ["cjs"], + removeNodeProtocol: false, outDir: "dist", external: ["esbuild", "prettier", "typescript", "zod"], // CLI doesn't need DTS @@ -82,6 +87,7 @@ export default defineConfig([ entry: ["dev/index.ts"], format: ["cjs", "esm"], outDir: "dev/dist", + removeNodeProtocol: false, external: ["esbuild", "prettier", "typescript", "zod"], dts: { // Split DTS generation to reduce memory usage @@ -97,6 +103,7 @@ export default defineConfig([ format: ["cjs", "esm"], outDir: "util/dist", external: ["esbuild", "prettier", "typescript", "zod"], + removeNodeProtocol: false, dts: { // Split DTS generation to reduce memory usage compilerOptions: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1cd8ef9c..337ff5f9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,7 +40,7 @@ importers: version: 2.6.6(@types/node@20.10.5)(typescript@5.3.3) tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.5.6)(typescript@5.3.3)(yaml@2.8.2) + version: 8.3.5(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.3.3)(yaml@2.8.2) typedoc: specifier: ^0.25.13 version: 0.25.13(typescript@5.3.3) @@ -70,7 +70,7 @@ importers: version: link:../../js tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2) + version: 8.3.5(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2) tsx: specifier: ^3.14.0 version: 3.14.0 @@ -110,7 +110,7 @@ importers: version: link:../../js tsup: specifier: ^8.5.0 - version: 8.5.1(postcss@8.5.6)(typescript@5.5.4)(yaml@2.8.2) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.5.4)(yaml@2.8.2) typedoc: specifier: ^0.28.15 version: 0.28.15(typescript@5.5.4) @@ -135,7 +135,7 @@ importers: version: 20.10.5 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.5.6)(typescript@5.3.3)(yaml@2.8.2) + version: 8.3.5(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.3.3)(yaml@2.8.2) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -317,10 +317,10 @@ importers: version: 9.0.7 '@typescript-eslint/eslint-plugin': specifier: ^8.49.0 - version: 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.4.4))(eslint@9.39.2)(typescript@5.4.4) + version: 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4))(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4) '@typescript-eslint/parser': specifier: ^8.49.0 - version: 8.50.0(eslint@9.39.2)(typescript@5.4.4) + version: 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4) ai: specifier: ^6.0.0 version: 6.0.27(zod@3.25.76) @@ -333,6 +333,12 @@ importers: cross-env: specifier: ^7.0.3 version: 7.0.3 + eslint-plugin-node-import: + specifier: ^1.0.5 + version: 1.0.5(eslint@9.39.2(jiti@2.6.1)) + jiti: + specifier: ^2.6.1 + version: 2.6.1 npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -353,7 +359,7 @@ importers: version: 29.1.4(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(esbuild@0.27.0)(jest@29.7.0(@types/node@20.10.5))(typescript@5.4.4) tsup: specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2) tsx: specifier: ^3.14.0 version: 3.14.0 @@ -3036,6 +3042,12 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-plugin-node-import@1.0.5: + resolution: {integrity: sha512-razzgbr3EcB5+bm8/gqTqzTJ7Bpiu8PIChiAMRfZCNigr9GZBtnVSI+wPw+RGbWYCCIzWAsK/A7ihoAeSz5j7A==} + engines: {node: ^14.18.0 || ^16.0.0 || >= 18.0.0} + peerDependencies: + eslint: '>=7' + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3761,6 +3773,10 @@ packages: node-notifier: optional: true + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -6517,9 +6533,9 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))': dependencies: - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -7523,15 +7539,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.4.4))(eslint@9.39.2)(typescript@5.4.4)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4))(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.4.4) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4) '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2)(typescript@5.4.4) - '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.4.4) + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4) '@typescript-eslint/visitor-keys': 8.50.0 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.4.4) @@ -7539,14 +7555,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.4.4)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4)': dependencies: '@typescript-eslint/scope-manager': 8.50.0 '@typescript-eslint/types': 8.50.0 '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.4.4) '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) typescript: 5.4.4 transitivePeerDependencies: - supports-color @@ -7569,13 +7585,13 @@ snapshots: dependencies: typescript: 5.4.4 - '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@5.4.4)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4)': dependencies: '@typescript-eslint/types': 8.50.0 '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.4.4) - '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.4.4) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4) debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.4.4) typescript: 5.4.4 transitivePeerDependencies: @@ -7598,13 +7614,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@5.4.4)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.4.4)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.50.0 '@typescript-eslint/types': 8.50.0 '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.4.4) - eslint: 9.39.2 + eslint: 9.39.2(jiti@2.6.1) typescript: 5.4.4 transitivePeerDependencies: - supports-color @@ -8631,6 +8647,10 @@ snapshots: escape-string-regexp@4.0.0: {} + eslint-plugin-node-import@1.0.5(eslint@9.39.2(jiti@2.6.1)): + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -8640,9 +8660,9 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.2: + eslint@9.39.2(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 @@ -8676,6 +8696,8 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -9678,6 +9700,8 @@ snapshots: - supports-color - ts-node + jiti@2.6.1: {} + joycon@3.1.1: {} js-levenshtein@1.1.6: {} @@ -10379,14 +10403,24 @@ snapshots: pluralize@8.0.0: {} - postcss-load-config@6.0.1(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2): dependencies: lilconfig: 3.1.3 optionalDependencies: + jiti: 2.6.1 postcss: 8.5.6 tsx: 3.14.0 yaml: 2.8.2 + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 2.6.1 + postcss: 8.5.6 + tsx: 4.21.0 + yaml: 2.8.2 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -11022,7 +11056,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.3.5(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2): + tsup@8.3.5(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.24.2) cac: 6.7.14 @@ -11032,7 +11066,7 @@ snapshots: esbuild: 0.24.2 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2) resolve-from: 5.0.0 rollup: 4.35.0 source-map: 0.8.0-beta.0 @@ -11049,7 +11083,7 @@ snapshots: - tsx - yaml - tsup@8.3.5(postcss@8.5.6)(typescript@5.3.3)(yaml@2.8.2): + tsup@8.3.5(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.3.3)(yaml@2.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.24.2) cac: 6.7.14 @@ -11059,7 +11093,7 @@ snapshots: esbuild: 0.24.2 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2) resolve-from: 5.0.0 rollup: 4.35.0 source-map: 0.8.0-beta.0 @@ -11076,7 +11110,7 @@ snapshots: - tsx - yaml - tsup@8.5.1(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2): + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(typescript@5.4.4)(yaml@2.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.27.0) cac: 6.7.14 @@ -11087,7 +11121,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2) resolve-from: 5.0.0 rollup: 4.35.0 source-map: 0.7.6 @@ -11104,7 +11138,7 @@ snapshots: - tsx - yaml - tsup@8.5.1(postcss@8.5.6)(typescript@5.5.4)(yaml@2.8.2): + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.5.4)(yaml@2.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.27.0) cac: 6.7.14 @@ -11115,7 +11149,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@3.14.0)(yaml@2.8.2) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2) resolve-from: 5.0.0 rollup: 4.35.0 source-map: 0.7.6 From 028779272e5f474febc5d6d3db6c8bbfad8e8be5 Mon Sep 17 00:00:00 2001 From: Olmo Maldonado Date: Fri, 16 Jan 2026 15:20:39 -0800 Subject: [PATCH 8/8] tweak prepare to use turbo --- js/Makefile | 36 +++----- js/smoke/prepare-tests.sh | 188 ++++++++++++++++++++++++-------------- 2 files changed, 134 insertions(+), 90 deletions(-) diff --git a/js/Makefile b/js/Makefile index 473c7af08..125beef2b 100644 --- a/js/Makefile +++ b/js/Makefile @@ -17,9 +17,15 @@ help: @echo " make test-ai-sdk-v6 - Run AI SDK v6 wrapper tests" @echo " make test-claude-agent-sdk - Run Claude Agent SDK wrapper tests" @echo " make test-api-compat - Run API compatibility tests" - @echo " make test-smoke - Run smoke tests" @echo " make bench - Run queue performance benchmarks" @echo " make test-latest - Run core + latest versions of wrappers" + @echo "" + @echo "Smoke tests (mimics CI workflow):" + @echo " make test-smoke - Run all smoke tests (auto-prepares)" + @echo " make test-smoke deno - Run deno smoke test only" + @echo " make test-smoke span - Run span smoke test only" + @echo "" + @echo "See smoke/README.md for details on smoke test infrastructure" .PHONY: help bench build clean test test-core test-openai test-anthropic test-google-genai test-ai-sdk test-ai-sdk-v5 test-ai-sdk-v6 test-claude-agent-sdk test-latest install-optional-deps publish-beta-local test-smoke @@ -155,33 +161,19 @@ clean: # Smoke tests # ------------------------------------------------------------------------------------------------- -# Marker file to track when smoke tests were last prepared -SMOKE_PREPARE_MARKER := smoke/.prepare-marker SMOKE_DIR := smoke -# Run smoke tests (prepares if source changed) +# Run smoke tests (always prepares - relies on turbo/tsup caching for build efficiency) # Usage: make test-smoke [TEST_NAME...] test-smoke: - @if [ ! -f "$(SMOKE_PREPARE_MARKER)" ]; then \ - echo "⚠ Smoke tests not prepared, preparing..."; \ - cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \ - elif [ -n "$$(find src -type f \( -name '*.ts' -o -name '*.tsx' \) -newer "$(SMOKE_PREPARE_MARKER)" 2>/dev/null | head -1)" ]; then \ - echo "⚠ SDK source files changed, preparing smoke tests..."; \ - cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \ - elif [ -n "$$(find dist -type f -name '*.js' -newer "$(SMOKE_PREPARE_MARKER)" 2>/dev/null | head -1)" ]; then \ - echo "⚠ SDK build artifacts changed, preparing smoke tests..."; \ - cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \ - elif [ ! -d "artifacts" ] || [ -z "$$(ls artifacts/*.tgz 2>/dev/null | head -1)" ]; then \ - echo "⚠ Smoke test artifacts missing, preparing..."; \ - cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \ - else \ - echo "✓ Smoke tests are up to date"; \ - fi - @echo "Running smoke tests..." - @EXIT_CODE=0; \ - cd $(SMOKE_DIR) && ./run-tests.sh $(filter-out test-smoke,$(MAKECMDGOALS)) || EXIT_CODE=$$?; \ + @TESTS="$(filter-out test-smoke,$(MAKECMDGOALS))"; \ + SMOKE_ABS="$$(pwd)/$(SMOKE_DIR)"; \ + cd $(SMOKE_DIR) && ./prepare-tests.sh $$TESTS; \ + EXIT_CODE=0; \ + cd $$SMOKE_ABS && ./run-tests.sh $$TESTS || EXIT_CODE=$$?; \ echo ""; \ echo "Restoring package files..."; \ + cd $$SMOKE_ABS && \ for dir in tests/*/; do \ if [ -f "$$dir/package.json" ] && grep -q '"restore"' "$$dir/package.json" 2>/dev/null; then \ (cd "$$dir" && npm run restore >/dev/null 2>&1 && echo " ✓ Restored $$(basename $$dir)") || true; \ diff --git a/js/smoke/prepare-tests.sh b/js/smoke/prepare-tests.sh index cbfc439eb..55558eac7 100755 --- a/js/smoke/prepare-tests.sh +++ b/js/smoke/prepare-tests.sh @@ -3,15 +3,44 @@ set -euo pipefail # Script to prepare smoke tests by building SDK and installing into test directories # This mimics what CI does +# Usage: ./prepare-tests.sh [test-name...] +# If no test names provided, prepares all tests SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" JS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" SDK_DIR="$(cd "$JS_DIR/.." && pwd)" ARTIFACTS_DIR="$JS_DIR/artifacts" -echo "============================================================" -echo "Preparing Smoke Tests" -echo "============================================================" +# Parse which tests to prepare +REQUESTED_TESTS=("$@") +PREPARE_ALL=false +if [ ${#REQUESTED_TESTS[@]} -eq 0 ]; then + PREPARE_ALL=true +fi + +# Helper to check if we should prepare a specific test +should_prepare() { + local test_name=$1 + if [ "$PREPARE_ALL" = true ]; then + return 0 + fi + for requested in "${REQUESTED_TESTS[@]}"; do + if [ "$requested" = "$test_name" ]; then + return 0 + fi + done + return 1 +} + +if [ "$PREPARE_ALL" = true ]; then + echo "============================================================" + echo "Preparing All Smoke Tests" + echo "============================================================" +else + echo "============================================================" + echo "Preparing Smoke Tests: ${REQUESTED_TESTS[*]}" + echo "============================================================" +fi echo "" # Step 0: Restore test files to clean git state @@ -47,36 +76,46 @@ echo "✓ Packed: $PACKED_TARBALL" echo "" # Step 3: Build and pack @braintrust/otel (needed for otel-v1 and nextjs tests) -echo "Step 3: Building @braintrust/otel..." -OTEL_DIR="$SDK_DIR/integrations/otel-js" -if [ -d "$OTEL_DIR" ]; then - cd "$OTEL_DIR" - - # Install the built braintrust package as a dependency - BRAINTRUST_TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1) - if [ -z "$BRAINTRUST_TARBALL" ]; then - echo "Error: braintrust tarball not found" - exit 1 - fi - - echo "Installing braintrust and OpenTelemetry dependencies..." - npm_config_save=false npm_config_lockfile=false pnpm add \ - "file:$BRAINTRUST_TARBALL" \ - "@opentelemetry/api@^1.9.0" \ - "@opentelemetry/core@^1.9.0" \ - "@opentelemetry/exporter-trace-otlp-http@^0.35.0" \ - "@opentelemetry/sdk-trace-base@^1.9.0" || true +NEEDS_OTEL=false +if should_prepare "otel-v1" || should_prepare "nextjs-instrumentation"; then + NEEDS_OTEL=true +fi - echo "Building @braintrust/otel..." - pnpm run build +if [ "$NEEDS_OTEL" = true ]; then + echo "Step 3: Building @braintrust/otel..." + OTEL_DIR="$SDK_DIR/integrations/otel-js" + if [ -d "$OTEL_DIR" ]; then + cd "$OTEL_DIR" + + # Install the built braintrust package as a dependency + BRAINTRUST_TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1) + if [ -z "$BRAINTRUST_TARBALL" ]; then + echo "Error: braintrust tarball not found" + exit 1 + fi - echo "Packing @braintrust/otel..." - OTEL_TARBALL=$(npm pack --pack-destination "$ARTIFACTS_DIR" 2>&1 | tail -1) - echo "✓ Packed: $OTEL_TARBALL" + echo "Installing braintrust and OpenTelemetry dependencies..." + npm_config_save=false npm_config_lockfile=false pnpm add \ + "file:$BRAINTRUST_TARBALL" \ + "@opentelemetry/api@^1.9.0" \ + "@opentelemetry/core@^1.9.0" \ + "@opentelemetry/exporter-trace-otlp-http@^0.35.0" \ + "@opentelemetry/sdk-trace-base@^1.9.0" || true + + echo "Building @braintrust/otel..." + pnpm run build + + echo "Packing @braintrust/otel..." + OTEL_TARBALL=$(npm pack --pack-destination "$ARTIFACTS_DIR" 2>&1 | tail -1) + echo "✓ Packed: $OTEL_TARBALL" + else + echo "⚠ @braintrust/otel directory not found, skipping" + fi + echo "" else - echo "⚠ @braintrust/otel directory not found, skipping" + echo "Step 3: Skipping @braintrust/otel (not needed for requested tests)" + echo "" fi -echo "" # Step 4: Backup package.json files before modifications echo "Step 4: Creating backups of package.json files..." @@ -89,6 +128,7 @@ TESTS_NEEDING_BRAINTRUST=( "otel-v1" "span" "span-jest" + "browser" ) # Tests that also need @braintrust/otel @@ -97,17 +137,18 @@ TESTS_NEEDING_OTEL=( "nextjs-instrumentation" ) -# Create backups for all tests that will be modified -ALL_TESTS=("${TESTS_NEEDING_BRAINTRUST[@]}" "${TESTS_NEEDING_OTEL[@]}") -for test_name in $(printf '%s\n' "${ALL_TESTS[@]}" | sort -u); do - test_dir="$SCRIPT_DIR/tests/$test_name" - if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then - cd "$test_dir" - # Remove old backups first to ensure fresh backups - rm -f package.json.bak package-lock.json.bak - if grep -q '"backup"' package.json 2>/dev/null; then - npm run backup 2>/dev/null || true - echo " ✓ Backed up $test_name/package.json" +# Create backups only for tests we're preparing +for test_name in "${TESTS_NEEDING_BRAINTRUST[@]}"; do + if should_prepare "$test_name"; then + test_dir="$SCRIPT_DIR/tests/$test_name" + if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then + cd "$test_dir" + # Remove old backups first to ensure fresh backups + rm -f package.json.bak package-lock.json.bak + if grep -q '"backup"' package.json 2>/dev/null; then + npm run backup 2>/dev/null || true + echo " ✓ Backed up $test_name/package.json" + fi fi fi done @@ -118,44 +159,55 @@ echo "Step 5: Installing built packages into test directories..." cd "$SCRIPT_DIR" for test_name in "${TESTS_NEEDING_BRAINTRUST[@]}"; do - test_dir="$SCRIPT_DIR/tests/$test_name" - if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then - echo "Installing braintrust into $test_name..." - cd "$test_dir" - # Remove package-lock.json to avoid version conflicts - rm -f package-lock.json - npm install --legacy-peer-deps 2>/dev/null || npm install - npx tsx ../../install-build.ts ../../../artifacts braintrust - echo " ✓ $test_name" + if should_prepare "$test_name"; then + test_dir="$SCRIPT_DIR/tests/$test_name" + if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then + echo "Installing braintrust into $test_name..." + cd "$test_dir" + # Remove package-lock.json to avoid version conflicts + rm -f package-lock.json + npm install --legacy-peer-deps 2>/dev/null || npm install + npx tsx ../../install-build.ts ../../../artifacts braintrust + echo " ✓ $test_name" + fi fi done for test_name in "${TESTS_NEEDING_OTEL[@]}"; do - test_dir="$SCRIPT_DIR/tests/$test_name" - if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then - echo "Installing @braintrust/otel into $test_name..." - cd "$test_dir" - npx tsx ../../install-build.ts ../../../artifacts otel - echo " ✓ $test_name (otel)" + if should_prepare "$test_name"; then + test_dir="$SCRIPT_DIR/tests/$test_name" + if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then + echo "Installing @braintrust/otel into $test_name..." + cd "$test_dir" + npx tsx ../../install-build.ts ../../../artifacts otel + echo " ✓ $test_name (otel)" + fi fi done # Special handling for Deno - extract tarball for file:// import -echo "Setting up Deno test..." -DENO_DIR="$SCRIPT_DIR/tests/deno" -if [ -d "$DENO_DIR" ]; then - cd "$DENO_DIR" - TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1) - if [ -n "$TARBALL" ]; then - echo "Extracting braintrust tarball for Deno..." - rm -rf build - mkdir -p build - tar -xzf "$TARBALL" -C build - [ -d build/package ] && mv build/package build/braintrust - echo " ✓ Deno build extracted" +if should_prepare "deno"; then + echo "Setting up Deno test..." + DENO_DIR="$SCRIPT_DIR/tests/deno" + if [ -d "$DENO_DIR" ]; then + cd "$DENO_DIR" + TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1) + if [ -n "$TARBALL" ]; then + echo "Extracting braintrust tarball for Deno..." + rm -rf build + mkdir -p build + tar -xzf "$TARBALL" -C build + [ -d build/package ] && mv build/package build/braintrust + echo " ✓ Deno build extracted" + + # Install Deno dependencies (npm packages in deno.json) + echo "Installing Deno dependencies..." + deno install + echo " ✓ Deno dependencies installed" + fi fi + echo "" fi -echo "" # Step 6: Build shared test package echo "Step 6: Building shared test package..."