diff --git a/.buildkite/ci.mjs b/.buildkite/ci.mjs index 9f39a72a3d..3fb1e643f3 100755 --- a/.buildkite/ci.mjs +++ b/.buildkite/ci.mjs @@ -543,9 +543,8 @@ function getTestBunStep(platform, options, testOptions = {}) { label: `${getPlatformLabel(platform)} - test-bun`, depends_on: depends, agents: getTestAgent(platform, options), - cancel_on_build_failing: isMergeQueue(), retry: getRetry(), - soft_fail: isMainBranch() ? true : [{ exit_status: 2 }], + cancel_on_build_failing: isMergeQueue(), parallelism: unifiedTests ? undefined : os === "darwin" ? 2 : 10, command: os === "windows" @@ -590,6 +589,7 @@ function getBuildImageStep(platform, options) { DEBUG: "1", }, retry: getRetry(), + cancel_on_build_failing: isMergeQueue(), command: command.filter(Boolean).join(" "), timeout_in_minutes: 3 * 60, }; diff --git a/.clangd b/.clangd index f736d521d0..b0ceeaa684 100644 --- a/.clangd +++ b/.clangd @@ -3,3 +3,6 @@ Index: CompileFlags: CompilationDatabase: build/debug + +Diagnostics: + UnusedIncludes: None diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000000..e56b21e52f --- /dev/null +++ b/.cursorignore @@ -0,0 +1,7 @@ +# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) +bench +vendor +*-fixture.{js,ts} +zig-cache +packages/bun-uws/fuzzing +build \ No newline at end of file diff --git a/.lldbinit b/.lldbinit index b54a4195c3..a2357365bb 100644 --- a/.lldbinit +++ b/.lldbinit @@ -1,4 +1,4 @@ -command script import vendor/zig/tools/lldb_pretty_printers.py +# command script import vendor/zig/tools/lldb_pretty_printers.py command script import vendor/WebKit/Tools/lldb/lldb_webkit.py # type summary add --summary-string "${var} | inner=${var[0-30]}, source=${var[33-64]}, tag=${var[31-32]}" "unsigned long" diff --git a/.vscode/launch.json b/.vscode/launch.json index 161553d45e..817b7533d3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,7 +22,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -38,7 +38,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -60,7 +60,23 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], + }, + { + "type": "lldb", + "request": "launch", + "name": "bun test [file] (verbose)", + "program": "${workspaceFolder}/build/debug/bun-debug", + "args": ["test", "${file}"], + "cwd": "${workspaceFolder}", + "env": { + "BUN_DEBUG_QUIET_LOGS": "0", + "BUN_DEBUG_jest": "1", + "BUN_GARBAGE_COLLECTOR_LEVEL": "2", + }, + "console": "internalConsole", + // Don't pause when the GC runs while the debugger is open. + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -76,7 +92,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -92,7 +108,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -109,7 +125,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -131,7 +147,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -153,7 +169,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -172,7 +188,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -187,23 +203,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], - }, - { - "type": "lldb", - "request": "launch", - "name": "bun --expose-internals run [file]", - "program": "${workspaceFolder}/build/debug/bun-debug", - "args": ["--expose-internals", "run", "${file}"], - "cwd": "${workspaceFolder}", - "env": { - "BUN_DEBUG_QUIET_LOGS": "1", - "BUN_DEBUG_jest": "1", - "BUN_GARBAGE_COLLECTOR_LEVEL": "0", - }, - "console": "internalConsole", - // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -221,7 +221,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -236,7 +236,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -253,7 +253,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -275,7 +275,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -297,7 +297,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -313,7 +313,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -329,7 +329,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -345,7 +345,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -361,7 +361,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -378,7 +378,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -400,7 +400,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -421,7 +421,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, // bun test [*] { @@ -437,7 +437,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -452,7 +452,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -468,7 +468,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], "serverReadyAction": { "pattern": "https://debug.bun.sh/#localhost:([0-9]+)/", "uriFormat": "https://debug.bun.sh/#ws://localhost:%s/", @@ -488,7 +488,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, { "type": "lldb", @@ -503,7 +503,7 @@ }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, // Windows: bun test [file] { @@ -1125,7 +1125,7 @@ ], "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. - "postRunCommands": ["process handle -p true -s false -n false SIGUSR1"], + "postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"], }, ], "inputs": [ diff --git a/.vscode/settings.json b/.vscode/settings.json index deead7e531..eca14849b6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -63,7 +63,6 @@ "editor.tabSize": 4, "editor.defaultFormatter": "xaver.clang-format", }, - "clangd.arguments": ["-header-insertion=never", "-no-unused-includes"], // JavaScript "prettier.enable": true, diff --git a/LATEST b/LATEST index c99926d330..2c6bb72b8c 100644 --- a/LATEST +++ b/LATEST @@ -1 +1 @@ -1.1.38 \ No newline at end of file +1.1.41 \ No newline at end of file diff --git a/bench/snippets/byteLength.mjs b/bench/snippets/byteLength.mjs new file mode 100644 index 0000000000..810bf487fd --- /dev/null +++ b/bench/snippets/byteLength.mjs @@ -0,0 +1,27 @@ +import { Buffer } from "node:buffer"; +import { bench, run } from "../runner.mjs"; + +const variations = [ + ["latin1", "hello world"], + ["utf16", "hello emoji 🤔"], +]; + +for (const [label, string] of variations) { + const big = Buffer.alloc(1000000, string).toString(); + const small = Buffer.from(string).toString(); + const substring = big.slice(0, big.length - 2); + + bench(`${substring.length}`, () => { + return Buffer.byteLength(substring, "utf8"); + }); + + bench(`${small.length}`, () => { + return Buffer.byteLength(small); + }); + + bench(`${big.length}`, () => { + return Buffer.byteLength(big); + }); +} + +await run(); diff --git a/bench/snippets/zlib.mjs b/bench/snippets/zlib.mjs new file mode 100644 index 0000000000..8bfc87e308 --- /dev/null +++ b/bench/snippets/zlib.mjs @@ -0,0 +1,62 @@ +import { bench, run } from "../runner.mjs"; +import zlib from "node:zlib"; +import { promisify } from "node:util"; + +const deflate = promisify(zlib.deflate); +const inflate = promisify(zlib.inflate); + +const short = "Hello World!"; +const long = "Hello World!".repeat(1024); +const veryLong = "Hello World!".repeat(10240); + +// Pre-compress some data for decompression tests +const shortBuf = Buffer.from(short); +const longBuf = Buffer.from(long); +const veryLongBuf = Buffer.from(veryLong); + +let [shortCompressed, longCompressed, veryLongCompressed] = await Promise.all([ + deflate(shortBuf, { level: 6 }), + deflate(longBuf, { level: 6 }), + deflate(veryLongBuf, { level: 6 }), +]); + +const format = new Intl.NumberFormat("en-US", { notation: "compact", unit: "byte" }); +// Compression tests at different levels +bench(`deflate ${format.format(short.length)}B (level 1)`, async () => { + await deflate(shortBuf, { level: 1 }); +}); + +bench(`deflate ${format.format(short.length)} (level 6)`, async () => { + await deflate(shortBuf, { level: 6 }); +}); + +bench(`deflate ${format.format(long.length)} (level 1)`, async () => { + await deflate(longBuf, { level: 1 }); +}); + +bench(`deflate ${format.format(long.length)} (level 6)`, async () => { + await deflate(longBuf, { level: 6 }); +}); + +bench(`deflate ${format.format(veryLong.length)} (level 1)`, async () => { + await deflate(veryLongBuf, { level: 1 }); +}); + +bench(`deflate ${format.format(veryLong.length)} (level 6)`, async () => { + await deflate(veryLongBuf, { level: 6 }); +}); + +// Decompression tests +bench(`inflate ${format.format(short.length)}`, async () => { + await inflate(shortCompressed); +}); + +bench(`inflate ${format.format(long.length)}`, async () => { + await inflate(longCompressed); +}); + +bench(`inflate ${format.format(veryLong.length)}`, async () => { + await inflate(veryLongCompressed); +}); + +await run(); diff --git a/build.zig b/build.zig index 9a1e3b25a7..0fc377326f 100644 --- a/build.zig +++ b/build.zig @@ -328,6 +328,12 @@ pub fn build(b: *Build) !void { }); } + // zig build translate-c-headers + { + const step = b.step("translate-c", "Copy generated translated-c-headers.zig to zig-out"); + step.dependOn(&b.addInstallFile(getTranslateC(b, b.host, .Debug).getOutput(), "translated-c-headers.zig").step); + } + // zig build enum-extractor { // const step = b.step("enum-extractor", "Extract enum definitions (invoked by a code generator)"); @@ -380,6 +386,25 @@ pub fn addMultiCheck( } } +fn getTranslateC(b: *Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *Step.TranslateC { + const translate_c = b.addTranslateC(.{ + .root_source_file = b.path("src/c-headers-for-zig.h"), + .target = target, + .optimize = optimize, + .link_libc = true, + }); + inline for ([_](struct { []const u8, bool }){ + .{ "WINDOWS", translate_c.target.result.os.tag == .windows }, + .{ "POSIX", translate_c.target.result.os.tag != .windows }, + .{ "LINUX", translate_c.target.result.os.tag == .linux }, + .{ "DARWIN", translate_c.target.result.os.tag.isDarwin() }, + }) |entry| { + const str, const value = entry; + translate_c.defineCMacroRaw(b.fmt("{s}={d}", .{ str, @intFromBool(value) })); + } + return translate_c; +} + pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { const obj = b.addObject(.{ .name = if (opts.optimize == .Debug) "bun-debug" else "bun", @@ -428,13 +453,8 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { addInternalPackages(b, obj, opts); obj.root_module.addImport("build_options", opts.buildOptionsModule(b)); - const translate_plugin_api = b.addTranslateC(.{ - .root_source_file = b.path("./packages/bun-native-bundler-plugin-api/bundler_plugin.h"), - .target = opts.target, - .optimize = opts.optimize, - .link_libc = true, - }); - obj.root_module.addImport("bun-native-bundler-plugin-api", translate_plugin_api.createModule()); + const translate_c = getTranslateC(b, opts.target, opts.optimize); + obj.root_module.addImport("translated-c-headers", translate_c.createModule()); return obj; } diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index 84a99eaa58..bcbb8d8c5b 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -600,7 +600,8 @@ file(GLOB BUN_C_SOURCES ${CONFIGURE_DEPENDS} ) if(WIN32) - list(APPEND BUN_C_SOURCES ${CWD}/src/bun.js/bindings/windows/musl-memmem.c) + list(APPEND BUN_CXX_SOURCES ${CWD}/src/bun.js/bindings/windows/rescle.cpp) + list(APPEND BUN_CXX_SOURCES ${CWD}/src/bun.js/bindings/windows/rescle-binding.cpp) endif() register_repository( @@ -650,11 +651,19 @@ if(WIN32) set(Bun_VERSION_WITH_TAG ${VERSION}) endif() set(BUN_ICO_PATH ${CWD}/src/bun.ico) + configure_file(${CWD}/src/bun.ico ${CODEGEN_PATH}/bun.ico COPYONLY) configure_file( ${CWD}/src/windows-app-info.rc ${CODEGEN_PATH}/windows-app-info.rc + @ONLY ) - list(APPEND BUN_CPP_SOURCES ${CODEGEN_PATH}/windows-app-info.rc) + add_custom_command( + OUTPUT ${CODEGEN_PATH}/windows-app-info.res + COMMAND rc.exe /fo ${CODEGEN_PATH}/windows-app-info.res ${CODEGEN_PATH}/windows-app-info.rc + DEPENDS ${CODEGEN_PATH}/windows-app-info.rc ${CODEGEN_PATH}/bun.ico + COMMENT "Adding Windows resource file ${CODEGEN_PATH}/windows-app-info.res with ico in ${CODEGEN_PATH}/bun.ico" + ) + set(WINDOWS_RESOURCES ${CODEGEN_PATH}/windows-app-info.res) endif() # --- Executable --- @@ -662,7 +671,7 @@ endif() set(BUN_CPP_OUTPUT ${BUILD_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${bun}${CMAKE_STATIC_LIBRARY_SUFFIX}) if(BUN_LINK_ONLY) - add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT}) + add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT} ${WINDOWS_RESOURCES}) set_target_properties(${bun} PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(${bun} PRIVATE ${BUN_CPP_OUTPUT}) elseif(BUN_CPP_ONLY) @@ -680,7 +689,7 @@ elseif(BUN_CPP_ONLY) ${BUN_CPP_OUTPUT} ) else() - add_executable(${bun} ${BUN_CPP_SOURCES}) + add_executable(${bun} ${BUN_CPP_SOURCES} ${WINDOWS_RESOURCES}) target_link_libraries(${bun} PRIVATE ${BUN_ZIG_OUTPUT}) endif() @@ -850,7 +859,7 @@ endif() if(WIN32) target_link_options(${bun} PUBLIC - /STACK:0x1200000,0x100000 + /STACK:0x1200000,0x200000 /errorlimit:0 ) if(RELEASE) diff --git a/cmake/targets/BuildCares.cmake b/cmake/targets/BuildCares.cmake index 373369b543..761f6d1998 100644 --- a/cmake/targets/BuildCares.cmake +++ b/cmake/targets/BuildCares.cmake @@ -4,7 +4,7 @@ register_repository( REPOSITORY c-ares/c-ares COMMIT - 41ee334af3e3d0027dca5e477855d0244936bd49 + 4f4912bce7374f787b10576851b687935f018e17 ) register_cmake_command( diff --git a/cmake/targets/BuildLibDeflate.cmake b/cmake/targets/BuildLibDeflate.cmake index f629d52fe5..f2820e3e79 100644 --- a/cmake/targets/BuildLibDeflate.cmake +++ b/cmake/targets/BuildLibDeflate.cmake @@ -4,7 +4,7 @@ register_repository( REPOSITORY ebiggers/libdeflate COMMIT - 9d624d1d8ba82c690d6d6be1d0a961fc5a983ea4 + 733848901289eca058804ca0737f8796875204c8 ) register_cmake_command( diff --git a/cmake/tools/SetupWebKit.cmake b/cmake/tools/SetupWebKit.cmake index 47ad1b9e34..7ff6ffca0d 100644 --- a/cmake/tools/SetupWebKit.cmake +++ b/cmake/tools/SetupWebKit.cmake @@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use") option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading") if(NOT WEBKIT_VERSION) - set(WEBKIT_VERSION 58549ddc4d9e7164823fe9d4e86c46c003e46a19) + set(WEBKIT_VERSION 30046aef5ec6590c74c6a696e4f01683f962a6a2) endif() if(WEBKIT_LOCAL) diff --git a/docs/api/cc.md b/docs/api/cc.md index 212b928df5..0cdf0b0a75 100644 --- a/docs/api/cc.md +++ b/docs/api/cc.md @@ -179,16 +179,16 @@ type Flags = string | string[]; These are flags like `-I` for include directories and `-D` for preprocessor definitions. -#### `defines: Record` +#### `define: Record` -The `defines` is an optional object that should be passed to the TinyCC compiler. +The `define` is an optional object that should be passed to the TinyCC compiler. ```ts type Defines = Record; cc({ source: "hello.c", - defines: { + define: { "NDEBUG": "1", }, }); diff --git a/docs/api/fetch.md b/docs/api/fetch.md index afbf53c5c1..5cb4068c9c 100644 --- a/docs/api/fetch.md +++ b/docs/api/fetch.md @@ -234,7 +234,7 @@ To prefetch a DNS entry, you can use the `dns.prefetch` API. This API is useful ```ts import { dns } from "bun"; -dns.prefetch("bun.sh", 443); +dns.prefetch("bun.sh"); ``` #### DNS caching diff --git a/docs/api/utils.md b/docs/api/utils.md index 3b87922106..8c96472f01 100644 --- a/docs/api/utils.md +++ b/docs/api/utils.md @@ -771,3 +771,28 @@ console.log(obj); // => { foo: "bar" } ``` Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. This exposes the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as an ArrayBuffer. + +## `estimateShallowMemoryUsageOf` in `bun:jsc` + +The `estimateShallowMemoryUsageOf` function returns a best-effort estimate of the memory usage of an object in bytes, excluding the memory usage of properties or other objects it references. For accurate per-object memory usage, use `Bun.generateHeapSnapshot`. + +```js +import { estimateShallowMemoryUsageOf } from "bun:jsc"; + +const obj = { foo: "bar" }; +const usage = estimateShallowMemoryUsageOf(obj); +console.log(usage); // => 16 + +const buffer = Buffer.alloc(1024 * 1024); +estimateShallowMemoryUsageOf(buffer); +// => 1048624 + +const req = new Request("https://bun.sh"); +estimateShallowMemoryUsageOf(req); +// => 167 + +const array = Array(1024).fill({ a: 1 }); +// Arrays are usually not stored contiguously in memory, so this will not return a useful value (which isn't a bug). +estimateShallowMemoryUsageOf(array); +// => 16 +``` diff --git a/docs/bundler/executables.md b/docs/bundler/executables.md index 6ae39a574c..c477e6a82c 100644 --- a/docs/bundler/executables.md +++ b/docs/bundler/executables.md @@ -279,6 +279,19 @@ $ bun build --compile --asset-naming="[name].[ext]" ./index.ts To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller. +## Windows-specific flags + +When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file: + +- `--windows-icon=path/to/icon.ico` to customize the executable file icon. +- `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY. + +{% callout %} + +These flags currently cannot be used when cross-compiling because they depend on Windows APIs. + +{% /callout %} + ## Unsupported CLI arguments Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags: diff --git a/docs/bundler/html.md b/docs/bundler/html.md new file mode 100644 index 0000000000..59c9cd2583 --- /dev/null +++ b/docs/bundler/html.md @@ -0,0 +1,110 @@ +As of Bun v1.1.43, Bun's bundler now has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else. + +```html#index.html + + + + + + + + + + +``` + +One command is all you need (won't be experimental after Bun v1.2): + +{% codetabs %} + +```bash#CLI +$ bun build --experimental-html --experimental-css ./index.html --outdir=dist +``` + +```ts#API +Bun.build({ + entrypoints: ["./index.html"], + outdir: "./dist", + + // On by default in Bun v1.2+ + html: true, + experimentalCss: true, +}); +``` + +{% /codetabs %} + +Bun automatically: + +- Bundles, tree-shakes, and optimizes your JavaScript, JSX and TypeScript +- Bundles and optimizes your CSS +- Copies & hashes images and other assets +- Updates all references to local files or packages in your HTML + +## Zero Config, Maximum Performance + +The HTML bundler is enabled by default after Bun v1.2+. Drop in your existing HTML files and Bun will handle: + +- **TypeScript & JSX** - Write modern JavaScript for browsers without the setup +- **CSS** - Bundle CSS stylesheets directly from `` or `@import` +- **Images & Assets** - Automatic copying & hashing & rewriting of assets in JavaScript, CSS, and HTML + +## Watch mode + +You can run `bun build --watch` to watch for changes and rebuild automatically. + +You've never seen a watch mode this fast. + +## Plugin API + +Need more control? Configure the bundler through the JavaScript API and use Bun's builtin `HTMLRewriter` to preprocess HTML. + +```ts +await Bun.build({ + entrypoints: ["./index.html"], + outdir: "./dist", + html: true, + experimentalCss: true, + minify: true, + + plugins: [ + { + // A plugin that makes every HTML tag lowercase + name: "lowercase-html-plugin", + setup({ onLoad }) { + const rewriter = new HTMLRewriter().on("*", { + element(element) { + element.tagName = element.tagName.toLowerCase(); + }, + text(element) { + element.replace(element.text.toLowerCase()); + }, + }); + + onLoad({ filter: /\.html$/ }, async args => { + const html = await Bun.file(args.path).text(); + + return { + // Bun's bundler will scan the HTML for + + +``` + +{% /codetabs %} + +It will output a new HTML file with the bundled assets: + +{% codetabs %} + +```html#dist/output.html + + + + Local image + External image + + + +``` + +{% /codetabs %} + +Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external. + +Currently, the list of selectors is: + +- `audio[src]` +- `iframe[src]` +- `img[src]` +- `img[srcset]` +- `link:not([rel~='stylesheet']):not([rel~='modulepreload']):not([rel~='manifest']):not([rel~='icon']):not([rel~='apple-touch-icon'])[href]` +- `link[as='font'][href], link[type^='font/'][href]` +- `link[as='image'][href]` +- `link[as='style'][href]` +- `link[as='video'][href], link[as='audio'][href]` +- `link[as='worker'][href]` +- `link[rel='icon'][href], link[rel='apple-touch-icon'][href]` +- `link[rel='manifest'][href]` +- `link[rel='stylesheet'][href]` +- `script[src]` +- `source[src]` +- `source[srcset]` +- `video[poster]` +- `video[src]` + ### `sh` loader **Bun Shell loader**. Default for `.sh` files diff --git a/docs/bundler/plugins.md b/docs/bundler/plugins.md index 6ac9654f0f..8e6b79c0e7 100644 --- a/docs/bundler/plugins.md +++ b/docs/bundler/plugins.md @@ -2,11 +2,47 @@ Bun provides a universal plugin API that can be used to extend both the _runtime Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location. -For more complete documentation of the Plugin API, see [Runtime > Plugins](https://bun.sh/docs/runtime/plugins). +## Lifecycle hooks + +Plugins can register callbacks to be run at various points in the lifecycle of a bundle: + +- [`onStart()`](#onstart): Run once the bundler has started a bundle +- [`onResolve()`](#onresolve): Run before a module is resolved +- [`onLoad()`](#onload): Run before a module is loaded. +- [`onBeforeParse()`](#onbeforeparse): Run zero-copy native addons in the parser thread before a file is parsed. + +### Reference + +A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions): + +```ts +type PluginBuilder = { + onStart(callback: () => void): void; + onResolve: ( + args: { filter: RegExp; namespace?: string }, + callback: (args: { path: string; importer: string }) => { + path: string; + namespace?: string; + } | void, + ) => void; + onLoad: ( + args: { filter: RegExp; namespace?: string }, + defer: () => Promise, + callback: (args: { path: string }) => { + loader?: Loader; + contents?: string; + exports?: Record; + }, + ) => void; + config: BuildConfig; +}; + +type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml"; +``` ## Usage -A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function. Register a plugin with Bun using the `plugin` function. +A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function. ```tsx#myPlugin.ts import type { BunPlugin } from "bun"; @@ -22,9 +58,343 @@ const myPlugin: BunPlugin = { This plugin can be passed into the `plugins` array when calling `Bun.build`. ```ts -Bun.build({ +await Bun.build({ entrypoints: ["./app.ts"], outdir: "./out", plugins: [myPlugin], }); ``` + +## Plugin lifecycle + +### Namespaces + +`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespaace? + +Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`. + +The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`. + +Other common namespaces are: + +- `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`) +- `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`) + +### `onStart` + +```ts +onStart(callback: () => void): Promise | void; +``` + +Registers a callback to be run when the bundler starts a new bundle. + +```ts +import { plugin } from "bun"; + +plugin({ + name: "onStart example", + + setup(build) { + build.onStart(() => { + console.log("Bundle started!"); + }); + }, +}); +``` + +The callback can return a `Promise`. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing. + +For example: + +```ts +const result = await Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./dist", + sourcemap: "external", + plugins: [ + { + name: "Sleep for 10 seconds", + setup(build) { + build.onStart(async () => { + await Bunlog.sleep(10_000); + }); + }, + }, + { + name: "Log bundle time to a file", + setup(build) { + build.onStart(async () => { + const now = Date.now(); + await Bun.$`echo ${now} > bundle-time.txt`; + }); + }, + }, + ], +}); +``` + +In the above example, Bun will wait until the first `onStart()` (sleeping for 10 seconds) has completed, _as well as_ the second `onStart()` (writing the bundle time to a file). + +Note that `onStart()` callbacks (like every other lifecycle callback) do not have the ability to modify the `build.config` object. If you want to mutate `build.config`, you must do so directly in the `setup()` function. + +### `onResolve` + +```ts +onResolve( + args: { filter: RegExp; namespace?: string }, + callback: (args: { path: string; importer: string }) => { + path: string; + namespace?: string; + } | void, +): void; +``` + +To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun actually has to find and read that module. The "finding" part is known as "resolving" a module. + +The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved. + +The first argument to `onResolve()` is an object with a `filter` and [`namespace`](#what-is-a-namespace) property. The filter is a regular expression which is run on the import string. Effectively, these allow you to filter which modules your custom resolution logic will apply to. + +The second argument to `onResolve()` is a callback which is run for each module import Bun finds that matches the `filter` and `namespace` defined in the first argument. + +The callback receives as input the _path_ to the matching module. The callback can return a _new path_ for the module. Bun will read the contents of the _new path_ and parse it as a module. + +For example, redirecting all imports to `images/` to `./public/images/`: + +```ts +import { plugin } from "bun"; + +plugin({ + name: "onResolve example", + setup(build) { + build.onResolve({ filter: /.*/, namespace: "file" }, args => { + if (args.path.startsWith("images/")) { + return { + path: args.path.replace("images/", "./public/images/"), + }; + } + }); + }, +}); +``` + +### `onLoad` + +```ts +onLoad( + args: { filter: RegExp; namespace?: string }, + defer: () => Promise, + callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => { + loader?: Loader; + contents?: string; + exports?: Record; + }, +): void; +``` + +After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it. + +The `onLoad()` plugin lifecycle callback allows you to modify the _contents_ of a module before it is read and parsed by Bun. + +Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to. + +The second argument to `onLoad()` is a callback which is run for each matching module _before_ Bun loads the contents of the module into memory. + +This callback receives as input the _path_ to the matching module, the _importer_ of the module (the module that imported the module), the _namespace_ of the module, and the _kind_ of the module. + +The callback can return a new `contents` string for the module as well as a new `loader`. + +For example: + +```ts +import { plugin } from "bun"; + +const envPlugin: BunPlugin = { + name: "env plugin", + setup(build) { + build.onLoad({ filter: /env/, namespace: "file" }, args => { + return { + contents: `export default ${JSON.stringify(process.env)}`, + loader: "js", + }; + }); + }, +}); + +Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./dist", + plugins: [envPlugin], +}); + +// import env from "env" +// env.FOO === "bar" +``` + +This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables. + +#### `.defer()` + +One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a `Promise` that is resolved when all _other_ modules have been loaded. + +This allows you to delay execution of the `onLoad` callback until all other modules have been loaded. + +This is useful for returning contens of a module that depends on other modules. + +##### Example: tracking and reporting unused exports + +```ts +import { plugin } from "bun"; + +plugin({ + name: "track imports", + setup(build) { + const transpiler = new Bun.Transpiler(); + + let trackedImports: Record = {}; + + // Each module that goes through this onLoad callback + // will record its imports in `trackedImports` + build.onLoad({ filter: /\.ts/ }, async ({ path }) => { + const contents = await Bun.file(path).arrayBuffer(); + + const imports = transpiler.scanImports(contents); + + for (const i of imports) { + trackedImports[i.path] = (trackedImports[i.path] || 0) + 1; + } + + return undefined; + }); + + build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => { + // Wait for all files to be loaded, ensuring + // that every file goes through the above `onLoad()` function + // and their imports tracked + await defer(); + + // Emit JSON containing the stats of each import + return { + contents: `export default ${JSON.stringify(trackedImports)}`, + loader: "json", + }; + }); + }, +}); +``` + +Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback. + +## Native plugins + +One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel. + +However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded. + +Native plugins are written as [NAPI](/docs/node-api) modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins. + +In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript. + +These are the following lifecycle hooks which are available to native plugins: + +- [`onBeforeParse()`](#onbeforeparse): Called on any thread before a file is parsed by Bun's bundler. + +Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. + +To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement. + +### Creating a native plugin in Rust + +Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. + +To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement. + +```bash +bun add -g @napi-rs/cli +napi new +``` + +Then install this crate: + +```bash +cargo add bun-native-plugin +``` + +Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which +will implement our native plugin. + +Here's an example implementing the `onBeforeParse` hook: + +```rs +use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader}; +use napi_derive::napi; + +/// Define the plugin and its name +define_bun_plugin!("replace-foo-with-bar"); + +/// Here we'll implement `onBeforeParse` with code that replaces all occurrences of +/// `foo` with `bar`. +/// +/// We use the #[bun] macro to generate some of the boilerplate code. +/// +/// The argument of the function (`handle: &mut OnBeforeParse`) tells +/// the macro that this function implements the `onBeforeParse` hook. +#[bun] +pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> { + // Fetch the input source code. + let input_source_code = handle.input_source_code()?; + + // Get the Loader for the file + let loader = handle.output_loader(); + + + let output_source_code = input_source_code.replace("foo", "bar"); + + handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX); + + Ok(()) +} +``` + +And to use it in Bun.build(): + +```typescript +import myNativeAddon from "./my-native-addon"; +Bun.build({ + entrypoints: ["./app.tsx"], + plugins: [ + { + name: "my-plugin", + + setup(build) { + build.onBeforeParse( + { + namespace: "file", + filter: "**/*.tsx", + }, + { + napiModule: myNativeAddon, + symbol: "replace_foo_with_bar", + // external: myNativeAddon.getSharedState() + }, + ); + }, + }, + ], +}); +``` + +### `onBeforeParse` + +```ts +onBeforeParse( + args: { filter: RegExp; namespace?: string }, + callback: { napiModule: NapiModule; symbol: string; external?: unknown }, +): void; +``` + +This lifecycle callback is run immediately before a file is parsed by Bun's bundler. + +As input, it receives the file's contents and can optionally return new source code. + +This callback can be called from any thread and so the napi module implementation must be thread-safe. diff --git a/docs/bundler/vs-esbuild.md b/docs/bundler/vs-esbuild.md index 1266914c05..35bb62f6f7 100644 --- a/docs/bundler/vs-esbuild.md +++ b/docs/bundler/vs-esbuild.md @@ -695,7 +695,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - In Bun, `minify` can be a boolean or an object. ```ts - Bun.build({ + await Bun.build({ entrypoints: ['./index.tsx'], // enable all minification minify: true diff --git a/docs/cli/bun-install.md b/docs/cli/bun-install.md index 20832cc53e..a2eb2ee196 100644 --- a/docs/cli/bun-install.md +++ b/docs/cli/bun-install.md @@ -47,6 +47,9 @@ registry = "https://registry.yarnpkg.com/" # Install for production? This is the equivalent to the "--production" CLI argument production = false +# Save a text-based lockfile? This is equivalent to the "--save-text-lockfile" CLI argument +saveTextLockfile = false + # Disallow changes to lockfile? This is the equivalent to the "--frozen-lockfile" CLI argument frozenLockfile = false @@ -54,12 +57,15 @@ frozenLockfile = false dryRun = true # Install optionalDependencies (default: true) +# Setting this to false is equivalent to the `--omit=optional` CLI argument optional = true # Install local devDependencies (default: true) +# Setting this to false is equivalent to the `--omit=dev` CLI argument dev = true # Install peerDependencies (default: true) +# Setting this to false is equivalent to the `--omit=peer` CLI argument peer = true # Max number of concurrent lifecycle scripts (default: (cpu count or GOMAXPROCS) x2) @@ -108,6 +114,7 @@ export interface Install { scopes: Scopes; registry: Registry; production: boolean; + saveTextLockfile: boolean; frozenLockfile: boolean; dryRun: boolean; optional: boolean; diff --git a/docs/cli/install.md b/docs/cli/install.md index 88de3d1759..25d46c98a1 100644 --- a/docs/cli/install.md +++ b/docs/cli/install.md @@ -130,6 +130,20 @@ $ bun install --frozen-lockfile For more information on Bun's binary lockfile `bun.lockb`, refer to [Package manager > Lockfile](https://bun.sh/docs/install/lockfile). +## Omitting dependencies + +To omit dev, peer, or optional dependencies use the `--omit` flag. + +```bash +# Exclude "devDependencies" from the installation. This will apply to the +# root package and workspaces if they exist. Transitive dependencies will +# not have "devDependencies". +$ bun install --omit dev + +# Install only dependencies from "dependencies" +$ bun install --omit=dev --omit=peer --omit=optional +``` + ## Dry run To perform a dry run (i.e. don't actually install anything): @@ -149,7 +163,8 @@ Bun supports installing dependencies from Git, GitHub, and local or remotely-hos "lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21", "moment": "git@github.com:moment/moment.git", "zod": "github:colinhacks/zod", - "react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz" + "react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "bun-types": "npm:@types/bun" } } ``` @@ -173,6 +188,9 @@ peer = true # equivalent to `--production` flag production = false +# equivalent to `--save-text-lockfile` flag +saveTextLockfile = false + # equivalent to `--frozen-lockfile` flag frozenLockfile = false diff --git a/docs/guides/ecosystem/nextjs.md b/docs/guides/ecosystem/nextjs.md index c3147d703b..1183b430e0 100644 --- a/docs/guides/ecosystem/nextjs.md +++ b/docs/guides/ecosystem/nextjs.md @@ -15,6 +15,14 @@ $ bun create next-app Creating a new Next.js app in /path/to/my-app. ``` +You can specify a starter template using the `--example` flag. + +```sh +$ bun create next-app --example with-supabase +✔ What is your project named? … my-app +... +``` + --- To start the dev server with Bun, run `bun --bun run dev` from the project root. diff --git a/docs/guides/install/registry-scope.md b/docs/guides/install/registry-scope.md index a36cde6fb0..0eeb973dee 100644 --- a/docs/guides/install/registry-scope.md +++ b/docs/guides/install/registry-scope.md @@ -2,7 +2,9 @@ name: Configure a private registry for an organization scope with bun install --- -Bun does not read `.npmrc` files; instead private registries are configured via `bunfig.toml`. To configure a registry for a particular npm scope: +Private registries can be configured using either [`.npmrc`](https://bun.sh/docs/install/npmrc) or [`bunfig.toml`](https://bun.sh/docs/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options. + +To configure a registry for a particular npm scope: ```toml#bunfig.toml [install.scopes] diff --git a/docs/guides/test/svelte-test.md b/docs/guides/test/svelte-test.md new file mode 100644 index 0000000000..062cd3312d --- /dev/null +++ b/docs/guides/test/svelte-test.md @@ -0,0 +1,120 @@ +--- +name: "import, require, and test Svelte components with bun test" +--- + +Bun's [Plugin API](/docs/runtime/plugins) lets you add custom loaders to your project. The `test.preload` option in `bunfig.toml` lets you configure your loader to start before your tests run. + +Firstly, install `@testing-library/svelte`, `svelte`, and `@happy-dom/global-registrator`. + +```bash +$ bun add @testing-library/svelte svelte@4 @happy-dom/global-registrator +``` + +Then, save this plugin in your project. + +```ts#svelte-loader.js +import { plugin } from "bun"; +import { compile } from "svelte/compiler"; +import { readFileSync } from "fs"; +import { beforeEach, afterEach } from "bun:test"; +import { GlobalRegistrator } from "@happy-dom/global-registrator"; + +beforeEach(async () => { + await GlobalRegistrator.register(); +}); + +afterEach(async () => { + await GlobalRegistrator.unregister(); +}); + +plugin({ + name: "svelte loader", + setup(builder) { + builder.onLoad({ filter: /\.svelte(\?[^.]+)?$/ }, ({ path }) => { + try { + const source = readFileSync( + path.substring( + 0, + path.includes("?") ? path.indexOf("?") : path.length + ), + "utf-8" + ); + + const result = compile(source, { + filename: path, + generate: "client", + dev: false, + }); + + return { + contents: result.js.code, + loader: "js", + }; + } catch (err) { + throw new Error(`Failed to compile Svelte component: ${err.message}`); + } + }); + }, +}); + +``` + +--- + +Add this to `bunfig.toml` to tell Bun to preload the plugin, so it loads before your tests run. + +```toml#bunfig.toml +[test] +# Tell Bun to load this plugin before your tests run +preload = ["./svelte-loader.js"] + +# This also works: +# test.preload = ["./svelte-loader.js"] +``` + +--- + +Add an example `.svelte` file in your project. + +```html#Counter.svelte + + + +``` + +--- + +Now you can `import` or `require` `*.svelte` files in your tests, and it will load the Svelte component as a JavaScript module. + +```ts#hello-svelte.test.ts +import { test, expect } from "bun:test"; +import { render, fireEvent } from "@testing-library/svelte"; +import Counter from "./Counter.svelte"; + +test("Counter increments when clicked", async () => { + const { getByText, component } = render(Counter); + const button = getByText("+1"); + + // Initial state + expect(component.$$.ctx[0]).toBe(0); // initialCount is the first prop + + // Click the increment button + await fireEvent.click(button); + + // Check the new state + expect(component.$$.ctx[0]).toBe(1); +}); +``` + +--- + +Use `bun test` to run your tests. + +```bash +$ bun test +``` + +--- diff --git a/docs/install/index.md b/docs/install/index.md index cdf41b9311..8f412a05e9 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -55,6 +55,13 @@ To install dependencies without allowing changes to lockfile (useful on CI): $ bun install --frozen-lockfile ``` +To exclude dependency types from installing, use `--omit` with `dev`, `optional`, or `peer`: + +```bash +# Disable devDependencies and optionalDependencies +$ bun install --omit=dev --omit=optional +``` + To perform a dry run (i.e. don't actually install anything): ```bash @@ -86,6 +93,9 @@ peer = true # equivalent to `--production` flag production = false +# equivalent to `--save-text-lockfile` flag +saveTextLockfile = false + # equivalent to `--frozen-lockfile` flag frozenLockfile = false diff --git a/docs/install/lockfile.md b/docs/install/lockfile.md index 66fb28e2b2..01df57fe0f 100644 --- a/docs/install/lockfile.md +++ b/docs/install/lockfile.md @@ -74,6 +74,24 @@ print = "yarn" {% /codetabs %} +### Text-based lockfile + +Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without configuration, at [no cost to performance](https://bun.sh/blog/bun-lock-text-lockfile#cached-bun-install-gets-30-faster). + +To generate the lockfile, use `--save-text-lockfile` with `bun install`. You can do this for new projects and existing projects already using `bun.lockb` (resolutions will be preserved). + +```bash +$ bun install --save-text-lockfile +$ head -n3 bun.lock +{ + "lockfileVersion": 0, + "workspaces": { +``` + +Once `bun.lock` is generated, Bun will use it for all subsequent installs and updates through commands that read and modify the lockfile. If both lockfiles exist, `bun.lock` will be choosen over `bun.lockb`. + +Bun v1.2.0 will switch the default lockfile format to `bun.lock`. + {% details summary="Configuring lockfile" %} ```toml diff --git a/docs/install/npmrc.md b/docs/install/npmrc.md index ae3c074892..3153dd6feb 100644 --- a/docs/install/npmrc.md +++ b/docs/install/npmrc.md @@ -6,7 +6,7 @@ Bun supports loading configuration options from [`.npmrc`](https://docs.npmjs.co {% /callout %} -# Supported options +## Supported options ### `registry`: Set the default registry diff --git a/docs/nav.ts b/docs/nav.ts index 6dd5a06dca..900cfdcb24 100644 --- a/docs/nav.ts +++ b/docs/nav.ts @@ -214,9 +214,9 @@ export default { page("bundler", "`Bun.build`", { description: "Bundle code for consumption in the browser with Bun's native bundler.", }), - // page("bundler/intro", "How bundlers work", { - // description: "A visual introduction to bundling", - // }), + page("bundler/html", "HTML", { + description: `Bundle html files with Bun's native bundler.`, + }), page("bundler/loaders", "Loaders", { description: "Bun's built-in loaders for the bundler and runtime", }), @@ -226,6 +226,7 @@ export default { page("bundler/macros", "Macros", { description: `Run JavaScript functions at bundle-time and inline the results into your bundle`, }), + page("bundler/vs-esbuild", "vs esbuild", { description: `Guides for migrating from other bundlers to Bun.`, }), diff --git a/docs/project/bindgen.md b/docs/project/bindgen.md index 3144d7f57f..83ee48d63a 100644 --- a/docs/project/bindgen.md +++ b/docs/project/bindgen.md @@ -61,7 +61,10 @@ This function declaration is equivalent to: declare function add(a: number, b: number = 1): number; ``` -The code generator will provide `bun.gen.math.jsAdd`, which is the native function implementation. To pass to JavaScript, use `bun.gen.math.createAddCallback(global)` +The code generator will provide `bun.gen.math.jsAdd`, which is the native +function implementation. To pass to JavaScript, use +`bun.gen.math.createAddCallback(global)`. JS files in `src/js/` may use +`$bindgenFn("math.bind.ts", "add")` to get a handle to the implementation. ## Strings @@ -104,7 +107,7 @@ export const action = fn({ In Zig, each variant gets a number, based on the order the schema defines. -``` +```zig fn action1(a: i32) i32 { return a; } @@ -180,9 +183,9 @@ export const add = fn({ // enforce in i32 range a: t.i32.enforceRange(), // clamp to u16 range - c: t.u16, + b: t.u16, // enforce in arbitrary range, with a default if not provided - b: t.i32.enforceRange(0, 1000).default(5), + c: t.i32.enforceRange(0, 1000).default(5), // clamp to arbitrary range, or null d: t.u16.clamp(0, 10).optional, }, @@ -190,6 +193,29 @@ export const add = fn({ }); ``` +Various Node.js validator functions such as `validateInteger`, `validateNumber`, and more are available. Use these when implementing Node.js APIs, so the error messages match 1:1 what Node would do. + +Unlike `enforceRange`, which is taken from WebIDL, `validate*` functions are much more strict on the input they accept. For example, Node's numerical validator check `typeof value === 'number'`, while WebIDL uses `ToNumber` for lossy conversion. + +```ts +import { t, fn } from "bindgen"; + +export const add = fn({ + args: { + global: t.globalObject, + // throw if not given a number + a: t.f64.validateNumber(), + // valid in i32 range + a: t.i32.validateInt32(), + // f64 within safe integer range + b: t.f64.validateInteger(), + // f64 in given range + c: t.f64.validateNumber(-10000, 10000), + }, + ret: t.i32, +}); +``` + ## Callbacks TODO diff --git a/docs/runtime/bunfig.md b/docs/runtime/bunfig.md index 1bfcd540e5..8a19be1ce5 100644 --- a/docs/runtime/bunfig.md +++ b/docs/runtime/bunfig.md @@ -238,6 +238,17 @@ By default Bun uses caret ranges; if the `latest` version of a package is `2.4.1 exact = false ``` +### `install.saveTextLockfile` + +Generate `bun.lock`, a human-readable text-based lockfile. Once generated, Bun will use this file instead of `bun.lockb`, choosing it over the binary lockfile if both are present. + +Default `false`. In Bun v1.2.0 the default lockfile format will change to `bun.lock`. + +```toml +[install] +saveTextLockfile = true +``` + "; + + int offset = (trimmedStr.length() + padding.length()) % 4; + // multiple X by the number in offset + pos = 0u; + for (int posCount = 0; posCount < offset; posCount = posCount + 1) { + if ((pos = padding.find(L"X", pos)) != std::string::npos) { + padding.replace(pos, 1, L"XX"); + pos += executionLevel_.length(); + } + } + + // convert the wchar back into char, so that it encodes correctly for Windows to read the XML. + std::wstring stringSectionW = trimmedStr + padding; + std::wstring_convert, wchar_t> converter; + std::string stringSection = converter.to_bytes(stringSectionW); + + if (!UpdateResourceW(ru.Get(), RT_MANIFEST, MAKEINTRESOURCEW(1), + kLangEnUs, // this is hardcoded at 1033, ie, en-us, as that is what RT_MANIFEST default uses + &stringSection.at(0), sizeof(char) * stringSection.size())) { + return false; + } + } + + // load file contents and replace the manifest + if (!applicationManifestPath_.empty()) { + std::wstring fileContents = ReadFileToString(applicationManifestPath_.c_str()); + + // clean old padding and add new padding, ensuring that the size is a multiple of 4 + std::wstring::size_type padPos = fileContents.find(L""); + // trim anything after the , 11 being the length of (ie, remove old padding) + std::wstring trimmedStr = fileContents.substr(0, padPos + 11); + std::wstring padding = L"\n"; + + int offset = (trimmedStr.length() + padding.length()) % 4; + // multiple X by the number in offset + std::wstring::size_type pos = 0u; + for (int posCount = 0; posCount < offset; posCount = posCount + 1) { + if ((pos = padding.find(L"X", pos)) != std::string::npos) { + padding.replace(pos, 1, L"XX"); + pos += executionLevel_.length(); + } + } + + // convert the wchar back into char, so that it encodes correctly for Windows to read the XML. + std::wstring stringSectionW = fileContents + padding; + std::wstring_convert, wchar_t> converter; + std::string stringSection = converter.to_bytes(stringSectionW); + + if (!UpdateResourceW(ru.Get(), RT_MANIFEST, MAKEINTRESOURCEW(1), + kLangEnUs, // this is hardcoded at 1033, ie, en-us, as that is what RT_MANIFEST default uses + &stringSection.at(0), sizeof(char) * stringSection.size())) { + return false; + } + } + + // update string table. + for (const auto& i : stringTableMap_) { + for (const auto& j : i.second) { + std::vector stringTableBuffer; + if (!SerializeStringTable(j.second, j.first, &stringTableBuffer)) { + return false; + } + + if (!UpdateResourceW(ru.Get(), RT_STRING, MAKEINTRESOURCEW(j.first + 1), i.first, + &stringTableBuffer[0], static_cast(stringTableBuffer.size()))) { + return false; + } + } + } + + for (const auto& rcDataLangPair : rcDataLngMap_) { + for (const auto& rcDataMap : rcDataLangPair.second) { + if (!UpdateResourceW(ru.Get(), RT_RCDATA, reinterpret_cast(rcDataMap.first), + rcDataLangPair.first, (LPVOID)rcDataMap.second.data(), rcDataMap.second.size())) { + return false; + } + } + } + + for (const auto& iLangIconInfoPair : iconBundleMap_) { + auto langId = iLangIconInfoPair.first; + auto maxIconId = iLangIconInfoPair.second.maxIconId; + for (const auto& iNameBundlePair : iLangIconInfoPair.second.iconBundles) { + UINT bundleId = iNameBundlePair.first; + const std::unique_ptr& pIcon = iNameBundlePair.second; + if (!pIcon) + continue; + + auto& icon = *pIcon; + // update icon. + if (icon.grpHeader.size() > 0) { + if (!UpdateResourceW(ru.Get(), RT_GROUP_ICON, MAKEINTRESOURCEW(bundleId), + langId, icon.grpHeader.data(), icon.grpHeader.size())) { + return false; + } + + for (size_t i = 0; i < icon.header.count; ++i) { + if (!UpdateResourceW(ru.Get(), RT_ICON, MAKEINTRESOURCEW(i + 1), + langId, icon.images[i].data(), icon.images[i].size())) { + + return false; + } + } + + for (size_t i = icon.header.count; i < maxIconId; ++i) { + if (!UpdateResourceW(ru.Get(), RT_ICON, MAKEINTRESOURCEW(i + 1), + langId, nullptr, 0)) { + return false; + } + } + } + } + } + + return ru.Commit(); +} + +bool ResourceUpdater::SerializeStringTable(const StringValues& values, UINT blockId, std::vector* out) +{ + // calc total size. + // string table is pascal string list. + size_t size = 0; + for (size_t i = 0; i < 16; i++) { + size += sizeof(WORD); + size += values[i].length() * sizeof(WCHAR); + } + + out->resize(size); + + // write. + char* pDst = &(*out)[0]; + for (size_t i = 0; i < 16; i++) { + WORD length = static_cast(values[i].length()); + memcpy(pDst, &length, sizeof(length)); + pDst += sizeof(WORD); + + if (length > 0) { + WORD bytes = length * sizeof(WCHAR); + memcpy(pDst, values[i].c_str(), bytes); + pDst += bytes; + } + } + + return true; +} + +// static +BOOL CALLBACK ResourceUpdater::OnEnumResourceLanguage(HANDLE hModule, LPCWSTR lpszType, LPCWSTR lpszName, WORD wIDLanguage, LONG_PTR lParam) +{ + ResourceUpdater* instance = reinterpret_cast(lParam); + if (IS_INTRESOURCE(lpszName) && IS_INTRESOURCE(lpszType)) { + // case reinterpret_cast(RT_VERSION): { + switch (reinterpret_cast(lpszType)) { + case 16: { + try { + instance->versionStampMap_[wIDLanguage] = VersionInfo(instance->module_, wIDLanguage); + } catch (const std::system_error& e) { + return false; + } + break; + } + case 6: { + // case reinterpret_cast(RT_STRING): { + UINT id = reinterpret_cast(lpszName) - 1; + auto& vector = instance->stringTableMap_[wIDLanguage][id]; + for (size_t k = 0; k < 16; k++) { + CStringW buf; + + buf.LoadStringW(instance->module_, id * 16 + k, wIDLanguage); + vector.push_back(buf.GetBuffer()); + } + break; + } + // case reinterpret_cast(RT_ICON): { + case 3: { + UINT iconId = reinterpret_cast(lpszName); + UINT maxIconId = instance->iconBundleMap_[wIDLanguage].maxIconId; + if (iconId > maxIconId) + maxIconId = iconId; + break; + } + // case reinterpret_cast(RT_GROUP_ICON): { + case 14: { + UINT iconId = reinterpret_cast(lpszName); + instance->iconBundleMap_[wIDLanguage].iconBundles[iconId] = nullptr; + break; + } + // case reinterpret_cast(RT_RCDATA): { + case 10: { + const auto moduleHandle = HMODULE(hModule); + HRSRC hResInfo = FindResource(moduleHandle, lpszName, lpszType); + DWORD cbResource = SizeofResource(moduleHandle, hResInfo); + HGLOBAL hResData = LoadResource(moduleHandle, hResInfo); + + const auto* pResource = (const BYTE*)LockResource(hResData); + const auto resId = reinterpret_cast(lpszName); + instance->rcDataLngMap_[wIDLanguage][resId] = std::vector(pResource, pResource + cbResource); + + UnlockResource(hResData); + FreeResource(hResData); + } + default: + break; + } + } + return TRUE; +} + +// static +BOOL CALLBACK ResourceUpdater::OnEnumResourceName(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam) +{ + EnumResourceLanguagesW(hModule, lpszType, lpszName, (ENUMRESLANGPROCW)OnEnumResourceLanguage, lParam); + return TRUE; +} + +// static +// courtesy of http://stackoverflow.com/questions/420852/reading-an-applications-manifest-file +BOOL CALLBACK ResourceUpdater::OnEnumResourceManifest(HMODULE hModule, LPCTSTR lpType, LPWSTR lpName, LONG_PTR lParam) +{ + ResourceUpdater* instance = reinterpret_cast(lParam); + HRSRC hResInfo = FindResource(hModule, lpName, lpType); + DWORD cbResource = SizeofResource(hModule, hResInfo); + + HGLOBAL hResData = LoadResource(hModule, hResInfo); + const BYTE* pResource = (const BYTE*)LockResource(hResData); + + // FIXME(zcbenz): Do a real UTF string convertion. + int len = strlen(reinterpret_cast(pResource)); + std::wstring manifestStringLocal(pResource, pResource + len); + + // FIXME(zcbenz): Strip the BOM instead of doing string search. + size_t start = manifestStringLocal.find(L" 0) { + manifestStringLocal = manifestStringLocal.substr(start); + } + + // Support alternative formatting, such as using " vs ' and level="..." on another line + size_t found = manifestStringLocal.find(L"requestedExecutionLevel"); + size_t level = manifestStringLocal.find(L"level=\"", found); + size_t end = manifestStringLocal.find(L"\"", level + 7); + if (level < 0) { + level = manifestStringLocal.find(L"level=\'", found); + end = manifestStringLocal.find(L"\'", level + 7); + } + + instance->originalExecutionLevel_ = manifestStringLocal.substr(level + 7, end - level - 7); + + // also store original manifestString + instance->manifestString_ = manifestStringLocal; + + UnlockResource(hResData); + FreeResource(hResData); + + return TRUE; // Keep going +} + +ScopedResourceUpdater::ScopedResourceUpdater(const WCHAR* filename, bool deleteOld) + : handle_(BeginUpdateResourceW(filename, deleteOld)) +{ +} + +ScopedResourceUpdater::~ScopedResourceUpdater() +{ + if (!commited_) { + EndUpdate(false); + } +} + +HANDLE ScopedResourceUpdater::Get() const +{ + return handle_; +} + +bool ScopedResourceUpdater::Commit() +{ + commited_ = true; + return EndUpdate(true); +} + +bool ScopedResourceUpdater::EndUpdate(bool doesCommit) +{ + BOOL fDiscard = doesCommit ? FALSE : TRUE; + BOOL bResult = EndUpdateResourceW(handle_, fDiscard); + DWORD e = GetLastError(); + return bResult ? true : false; +} + +} // namespace rescle diff --git a/src/bun.js/bindings/windows/rescle.h b/src/bun.js/bindings/windows/rescle.h new file mode 100644 index 0000000000..417de1dc0d --- /dev/null +++ b/src/bun.js/bindings/windows/rescle.h @@ -0,0 +1,211 @@ +// This file is from Electron's fork of rescle +// https://github.com/electron/rcedit/blob/e36b688b42df0e236922019ce14e0ea165dc176d/src/rescle.h +// 'bun build --compile' uses this on Windows to allow +// patching the icon of the generated executable. +// +// Copyright (c) 2013 GitHub Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// Use of this source code is governed by MIT license that can be found in the +// LICENSE file. +// +// This file is modified from Rescle written by yoshio.okumura@gmail.com: +// http://code.google.com/p/rescle/ + +#ifndef VERSION_INFO_UPDATER +#define VERSION_INFO_UPDATER + +#ifndef _UNICODE +#define _UNICODE +#endif + +#ifndef UNICODE +#define UNICODE +#endif + +#include +#include +#include + +#include +#include // unique_ptr + +#define RU_VS_COMMENTS L"Comments" +#define RU_VS_COMPANY_NAME L"CompanyName" +#define RU_VS_FILE_DESCRIPTION L"FileDescription" +#define RU_VS_FILE_VERSION L"FileVersion" +#define RU_VS_INTERNAL_NAME L"InternalName" +#define RU_VS_LEGAL_COPYRIGHT L"LegalCopyright" +#define RU_VS_LEGAL_TRADEMARKS L"LegalTrademarks" +#define RU_VS_ORIGINAL_FILENAME L"OriginalFilename" +#define RU_VS_PRIVATE_BUILD L"PrivateBuild" +#define RU_VS_PRODUCT_NAME L"ProductName" +#define RU_VS_PRODUCT_VERSION L"ProductVersion" +#define RU_VS_SPECIAL_BUILD L"SpecialBuild" + +namespace rescle { + +struct IconsValue { + typedef struct _ICONENTRY { + BYTE width; + BYTE height; + BYTE colorCount; + BYTE reserved; + WORD planes; + WORD bitCount; + DWORD bytesInRes; + DWORD imageOffset; + } ICONENTRY; + + typedef struct _ICONHEADER { + WORD reserved; + WORD type; + WORD count; + std::vector entries; + } ICONHEADER; + + ICONHEADER header; + std::vector> images; + std::vector grpHeader; +}; + +struct Translate { + LANGID wLanguage; + WORD wCodePage; +}; + +typedef std::pair VersionString; +typedef std::pair OffsetLengthPair; + +struct VersionStringTable { + Translate encoding; + std::vector strings; +}; + +class VersionInfo { +public: + VersionInfo(); + VersionInfo(HMODULE hModule, WORD languageId); + + std::vector Serialize() const; + + bool HasFixedFileInfo() const; + VS_FIXEDFILEINFO& GetFixedFileInfo(); + const VS_FIXEDFILEINFO& GetFixedFileInfo() const; + void SetFixedFileInfo(const VS_FIXEDFILEINFO& value); + + std::vector stringTables; + std::vector supportedTranslations; + +private: + VS_FIXEDFILEINFO fixedFileInfo_; + + void FillDefaultData(); + void DeserializeVersionInfo(const BYTE* pData, size_t size); + + VersionStringTable DeserializeVersionStringTable(const BYTE* tableData); + void DeserializeVersionStringFileInfo(const BYTE* offset, size_t length, std::vector& stringTables); + void DeserializeVarFileInfo(const unsigned char* offset, std::vector& translations); + OffsetLengthPair GetChildrenData(const BYTE* entryData); +}; + +class ResourceUpdater { +public: + typedef std::vector StringValues; + typedef std::map StringTable; + typedef std::map StringTableMap; + typedef std::map VersionStampMap; + typedef std::map> IconTable; + typedef std::vector RcDataValue; + typedef std::map RcDataMap; + typedef std::map RcDataLangMap; + + struct IconResInfo { + UINT maxIconId = 0; + IconTable iconBundles; + }; + + typedef std::map IconTableMap; + + ResourceUpdater(); + ~ResourceUpdater(); + + bool Load(const WCHAR* filename); + bool SetVersionString(WORD languageId, const WCHAR* name, const WCHAR* value); + bool SetVersionString(const WCHAR* name, const WCHAR* value); + const WCHAR* GetVersionString(WORD languageId, const WCHAR* name); + const WCHAR* GetVersionString(const WCHAR* name); + bool SetProductVersion(WORD languageId, UINT id, unsigned short v1, unsigned short v2, unsigned short v3, unsigned short v4); + bool SetProductVersion(unsigned short v1, unsigned short v2, unsigned short v3, unsigned short v4); + bool SetFileVersion(WORD languageId, UINT id, unsigned short v1, unsigned short v2, unsigned short v3, unsigned short v4); + bool SetFileVersion(unsigned short v1, unsigned short v2, unsigned short v3, unsigned short v4); + bool ChangeString(WORD languageId, UINT id, const WCHAR* value); + bool ChangeString(UINT id, const WCHAR* value); + bool ChangeRcData(UINT id, const WCHAR* pathToResource); + const WCHAR* GetString(WORD languageId, UINT id); + const WCHAR* GetString(UINT id); + bool SetIcon(const WCHAR* path, const LANGID& langId, UINT iconBundle); + bool SetIcon(const WCHAR* path, const LANGID& langId); + bool SetIcon(const WCHAR* path); + bool SetExecutionLevel(const WCHAR* value); + bool IsExecutionLevelSet(); + bool SetApplicationManifest(const WCHAR* value); + bool IsApplicationManifestSet(); + bool Commit(); + +private: + bool SerializeStringTable(const StringValues& values, UINT blockId, std::vector* out); + + static BOOL CALLBACK OnEnumResourceName(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam); + static BOOL CALLBACK OnEnumResourceManifest(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam); + static BOOL CALLBACK OnEnumResourceLanguage(HANDLE hModule, LPCWSTR lpszType, LPCWSTR lpszName, WORD wIDLanguage, LONG_PTR lParam); + + HMODULE module_; + std::wstring filename_; + std::wstring executionLevel_; + std::wstring originalExecutionLevel_; + std::wstring applicationManifestPath_; + std::wstring manifestString_; + VersionStampMap versionStampMap_; + StringTableMap stringTableMap_; + IconTableMap iconBundleMap_; + RcDataLangMap rcDataLngMap_; +}; + +class ScopedResourceUpdater { +public: + ScopedResourceUpdater(const WCHAR* filename, bool deleteOld); + ~ScopedResourceUpdater(); + + HANDLE Get() const; + bool Commit(); + +private: + bool EndUpdate(bool doesCommit); + + HANDLE handle_; + bool commited_ = false; +}; + +} // namespace rescle + +#endif // VERSION_INFO_UPDATER diff --git a/src/bun.js/bindings/wtf-bindings.cpp b/src/bun.js/bindings/wtf-bindings.cpp index 52de7d5a7c..d33725bbae 100644 --- a/src/bun.js/bindings/wtf-bindings.cpp +++ b/src/bun.js/bindings/wtf-bindings.cpp @@ -1,6 +1,7 @@ #include "root.h" #include "wtf-bindings.h" - +#include +#include #include #include #include @@ -237,4 +238,16 @@ size_t toISOString(JSC::VM& vm, double date, char in[64]) return charactersWritten; } +static thread_local WTF::StackBounds stackBoundsForCurrentThread = WTF::StackBounds::emptyBounds(); + +extern "C" void Bun__StackCheck__initialize() +{ + stackBoundsForCurrentThread = WTF::StackBounds::currentThreadStackBounds(); +} + +extern "C" void* Bun__StackCheck__getMaxStack() +{ + return stackBoundsForCurrentThread.end(); +} + } diff --git a/src/bun.js/config.zig b/src/bun.js/config.zig index d82c3467d2..c8fbeddbd4 100644 --- a/src/bun.js/config.zig +++ b/src/bun.js/config.zig @@ -16,7 +16,7 @@ const ast = @import("../import_record.zig"); const logger = bun.logger; const Api = @import("../api/schema.zig").Api; const options = @import("../options.zig"); -const Bundler = bun.bundler.ServeBundler; +const Transpiler = bun.transpiler.ServeBundler; const js_printer = bun.js_printer; pub const DefaultBunDefines = struct { diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 514e8489d0..21e6393b89 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -18,6 +18,10 @@ const ReadFileTask = WebCore.Blob.ReadFile.ReadFileTask; const WriteFileTask = WebCore.Blob.WriteFile.WriteFileTask; const napi_async_work = JSC.napi.napi_async_work; const FetchTasklet = Fetch.FetchTasklet; +const AWS = @import("../s3.zig").AWSCredentials; +const S3HttpSimpleTask = AWS.S3HttpSimpleTask; +const S3HttpDownloadStreamingTask = AWS.S3HttpDownloadStreamingTask; + const JSValue = JSC.JSValue; const js = JSC.C; const Waker = bun.Async.Waker; @@ -407,6 +411,9 @@ const ServerAllConnectionsClosedTask = @import("./api/server.zig").ServerAllConn // Task.get(ReadFileTask) -> ?ReadFileTask pub const Task = TaggedPointerUnion(.{ FetchTasklet, + S3HttpSimpleTask, + S3HttpDownloadStreamingTask, + PosixSignalTask, AsyncGlobWalkTask, AsyncTransformTask, ReadFileTask, @@ -555,7 +562,7 @@ pub const GarbageCollectionController = struct { } var gc_timer_interval: i32 = 1000; - if (vm.bundler.env.get("BUN_GC_TIMER_INTERVAL")) |timer| { + if (vm.transpiler.env.get("BUN_GC_TIMER_INTERVAL")) |timer| { if (std.fmt.parseInt(i32, timer, 10)) |parsed| { if (parsed > 0) { gc_timer_interval = parsed; @@ -564,7 +571,7 @@ pub const GarbageCollectionController = struct { } this.gc_timer_interval = gc_timer_interval; - this.disabled = vm.bundler.env.has("BUN_GC_TIMER_DISABLE"); + this.disabled = vm.transpiler.env.has("BUN_GC_TIMER_DISABLE"); if (!this.disabled) this.gc_repeating_timer.set(this, onGCRepeatingTimer, gc_timer_interval, gc_timer_interval); @@ -782,6 +789,20 @@ pub const EventLoop = struct { entered_event_loop_count: isize = 0, concurrent_ref: std.atomic.Value(i32) = std.atomic.Value(i32).init(0), + signal_handler: if (Environment.isPosix) ?*PosixSignalHandle else void = if (Environment.isPosix) null else {}, + + pub export fn Bun__ensureSignalHandler() void { + if (Environment.isPosix) { + if (JSC.VirtualMachine.getMainThreadVM()) |vm| { + const this = vm.eventLoop(); + if (this.signal_handler == null) { + this.signal_handler = PosixSignalHandle.new(.{}); + @memset(&this.signal_handler.?.signals, 0); + } + } + } + } + pub const Debug = if (Environment.isDebug) struct { is_inside_tick_queue: bool = false, js_call_count_outside_tick_queue: usize = 0, @@ -991,6 +1012,15 @@ pub const EventLoop = struct { var fetch_task: *Fetch.FetchTasklet = task.get(Fetch.FetchTasklet).?; fetch_task.onProgressUpdate(); }, + .S3HttpSimpleTask => { + var s3_task: *S3HttpSimpleTask = task.get(S3HttpSimpleTask).?; + s3_task.onResponse(); + }, + .S3HttpDownloadStreamingTask => { + var s3_task: *S3HttpDownloadStreamingTask = task.get(S3HttpDownloadStreamingTask).?; + s3_task.onResponse(); + }, + @field(Task.Tag, @typeName(AsyncGlobWalkTask)) => { var globWalkTask: *AsyncGlobWalkTask = task.get(AsyncGlobWalkTask).?; globWalkTask.*.runFromJS(); @@ -1256,6 +1286,9 @@ pub const EventLoop = struct { var any: *bun.bundle_v2.DeferredBatchTask = task.get(bun.bundle_v2.DeferredBatchTask).?; any.runOnJSThread(); }, + @field(Task.Tag, typeBaseName(@typeName(PosixSignalTask))) => { + PosixSignalTask.runFromJSThread(@intCast(task.asUintptr()), global); + }, else => { bun.Output.panic("Unexpected tag: {s}", .{@tagName(task.tag())}); @@ -1310,6 +1343,12 @@ pub const EventLoop = struct { pub fn tickConcurrentWithCount(this: *EventLoop) usize { this.updateCounts(); + if (comptime Environment.isPosix) { + if (this.signal_handler) |signal_handler| { + signal_handler.drain(this); + } + } + var concurrent = this.concurrent_tasks.popBatch(); const count = concurrent.count; if (count == 0) @@ -2027,6 +2066,13 @@ pub const AnyEventLoop = union(enum) { pub const Task = AnyTaskWithExtraContext; + pub fn iterationNumber(this: *const AnyEventLoop) u64 { + return switch (this.*) { + .js => this.js.usocketsLoop().iterationNumber(), + .mini => this.mini.loop.iterationNumber(), + }; + } + pub fn wakeup(this: *AnyEventLoop) void { this.loop().wakeup(); } @@ -2242,7 +2288,7 @@ pub const EventLoopHandle = union(enum) { pub inline fn createNullDelimitedEnvMap(this: @This(), alloc: Allocator) ![:null]?[*:0]u8 { return switch (this) { - .js => this.js.virtual_machine.bundler.env.map.createNullDelimitedEnvMap(alloc), + .js => this.js.virtual_machine.transpiler.env.map.createNullDelimitedEnvMap(alloc), .mini => this.mini.env.?.map.createNullDelimitedEnvMap(alloc), }; } @@ -2256,14 +2302,14 @@ pub const EventLoopHandle = union(enum) { pub inline fn topLevelDir(this: EventLoopHandle) []const u8 { return switch (this) { - .js => this.js.virtual_machine.bundler.fs.top_level_dir, + .js => this.js.virtual_machine.transpiler.fs.top_level_dir, .mini => this.mini.top_level_dir, }; } pub inline fn env(this: EventLoopHandle) *bun.DotEnv.Loader { return switch (this) { - .js => this.js.virtual_machine.bundler.env, + .js => this.js.virtual_machine.transpiler.env, .mini => this.mini.env.?, }; } @@ -2293,3 +2339,99 @@ pub const EventLoopTaskPtr = union { js: *ConcurrentTask, mini: *JSC.AnyTaskWithExtraContext, }; + +pub const PosixSignalHandle = struct { + const buffer_size = 8192; + + signals: [buffer_size]u8 = undefined, + + // Producer index (signal handler writes). + tail: std.atomic.Value(u16) = std.atomic.Value(u16).init(0), + // Consumer index (main thread reads). + head: std.atomic.Value(u16) = std.atomic.Value(u16).init(0), + + const log = bun.Output.scoped(.PosixSignalHandle, true); + + pub usingnamespace bun.New(@This()); + + /// Called by the signal handler (single producer). + /// Returns `true` if enqueued successfully, or `false` if the ring is full. + pub fn enqueue(this: *PosixSignalHandle, signal: u8) bool { + // Read the current tail and head (Acquire to ensure we have up‐to‐date values). + const old_tail = this.tail.load(.acquire); + const head_val = this.head.load(.acquire); + + // Compute the next tail (wrapping around buffer_size). + const next_tail = (old_tail +% 1) % buffer_size; + + // Check if the ring is full. + if (next_tail == (head_val % buffer_size)) { + // The ring buffer is full. + // We cannot block or wait here (since we're in a signal handler). + // So we just drop the signal or log if desired. + log("signal queue is full; dropping", .{}); + return false; + } + + // Store the signal into the ring buffer slot (Release to ensure data is visible). + @atomicStore(u8, &this.signals[old_tail % buffer_size], signal, .release); + + // Publish the new tail (Release so that the consumer sees the updated tail). + this.tail.store(old_tail +% 1, .release); + + JSC.VirtualMachine.getMainThreadVM().?.eventLoop().wakeup(); + + return true; + } + + /// This is the signal handler entry point. Calls enqueue on the ring buffer. + /// Note: Must be minimal logic here. Only do atomics & signal‐safe calls. + export fn Bun__onPosixSignal(number: i32) void { + const vm = JSC.VirtualMachine.getMainThreadVM().?; + _ = vm.eventLoop().signal_handler.?.enqueue(@intCast(number)); + } + + /// Called by the main thread (single consumer). + /// Returns `null` if the ring is empty, or the next signal otherwise. + pub fn dequeue(this: *PosixSignalHandle) ?u8 { + // Read the current head and tail. + const old_head = this.head.load(.acquire); + const tail_val = this.tail.load(.acquire); + + // If head == tail, the ring is empty. + if (old_head == tail_val) { + return null; // No available items + } + + const slot_index = old_head % buffer_size; + // Acquire load of the stored signal to get the item. + const signal = @atomicRmw(u8, &this.signals[slot_index], .Xchg, 0, .acq_rel); + + // Publish the updated head (Release). + this.head.store(old_head +% 1, .release); + + return signal; + } + + /// Drain as many signals as possible and enqueue them as tasks in the event loop. + /// Called by the main thread. + pub fn drain(this: *PosixSignalHandle, event_loop: *JSC.EventLoop) void { + while (this.dequeue()) |signal| { + // Example: wrap the signal into a Task structure + var posix_signal_task: PosixSignalTask = undefined; + var task = JSC.Task.init(&posix_signal_task); + task.setUintptr(signal); + event_loop.enqueueTask(task); + } + } +}; + +pub const PosixSignalTask = struct { + number: u8, + extern "C" fn Bun__onSignalForJS(number: i32, globalObject: *JSC.JSGlobalObject) void; + + pub usingnamespace bun.New(@This()); + pub fn runFromJSThread(number: u8, globalObject: *JSC.JSGlobalObject) void { + Bun__onSignalForJS(number, globalObject); + } +}; diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index f3888bff60..3fe65eb8c5 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -20,14 +20,14 @@ const IdentityContext = @import("../identity_context.zig").IdentityContext; const Fs = @import("../fs.zig"); const Resolver = @import("../resolver/resolver.zig"); const ast = @import("../import_record.zig"); -const MacroEntryPoint = bun.bundler.MacroEntryPoint; -const ParseResult = bun.bundler.ParseResult; +const MacroEntryPoint = bun.transpiler.MacroEntryPoint; +const ParseResult = bun.transpiler.ParseResult; const logger = bun.logger; const Api = @import("../api/schema.zig").Api; const options = @import("../options.zig"); -const Bundler = bun.Bundler; -const PluginRunner = bun.bundler.PluginRunner; -const ServerEntryPoint = bun.bundler.ServerEntryPoint; +const Transpiler = bun.Transpiler; +const PluginRunner = bun.transpiler.PluginRunner; +const ServerEntryPoint = bun.transpiler.ServerEntryPoint; const js_printer = bun.js_printer; const js_parser = bun.js_parser; const js_ast = bun.JSAst; @@ -567,7 +567,7 @@ pub export fn Bun__onDidAppendPlugin(jsc_vm: *VirtualMachine, globalObject: *JSG .global_object = globalObject, .allocator = jsc_vm.allocator, }; - jsc_vm.bundler.linker.plugin_runner = &jsc_vm.plugin_runner.?; + jsc_vm.transpiler.linker.plugin_runner = &jsc_vm.plugin_runner.?; } const WindowsOnly = struct { @@ -772,7 +772,7 @@ pub const VirtualMachine = struct { global: *JSGlobalObject, allocator: std.mem.Allocator, has_loaded_constructors: bool = false, - bundler: Bundler, + transpiler: Transpiler, bun_watcher: ImportWatcher = .{ .none = {} }, console: *ConsoleObject, log: *logger.Log, @@ -942,7 +942,7 @@ pub const VirtualMachine = struct { } pub fn getTLSRejectUnauthorized(this: *const VirtualMachine) bool { - return this.default_tls_reject_unauthorized orelse this.bundler.env.getTLSRejectUnauthorized(); + return this.default_tls_reject_unauthorized orelse this.transpiler.env.getTLSRejectUnauthorized(); } pub fn onSubprocessSpawn(this: *VirtualMachine, process: *bun.spawn.Process) void { @@ -955,7 +955,7 @@ pub const VirtualMachine = struct { pub fn getVerboseFetch(this: *VirtualMachine) bun.http.HTTPVerboseLevel { return this.default_verbose_fetch orelse { - if (this.bundler.env.get("BUN_CONFIG_VERBOSE_FETCH")) |verbose_fetch| { + if (this.transpiler.env.get("BUN_CONFIG_VERBOSE_FETCH")) |verbose_fetch| { if (strings.eqlComptime(verbose_fetch, "true") or strings.eqlComptime(verbose_fetch, "1")) { this.default_verbose_fetch = .headers; return .headers; @@ -972,9 +972,15 @@ pub const VirtualMachine = struct { pub const VMHolder = struct { pub threadlocal var vm: ?*VirtualMachine = null; pub threadlocal var cached_global_object: ?*JSGlobalObject = null; + pub var main_thread_vm: ?*VirtualMachine = null; pub export fn Bun__setDefaultGlobalObject(global: *JSGlobalObject) void { if (vm) |vm_instance| { vm_instance.global = global; + + // Ensure this is always set when it should be. + if (vm_instance.is_main_thread) { + VMHolder.main_thread_vm = vm_instance; + } } cached_global_object = global; @@ -994,6 +1000,10 @@ pub const VirtualMachine = struct { return VMHolder.vm.?; } + pub fn getMainThreadVM() ?*VirtualMachine { + return VMHolder.main_thread_vm; + } + pub fn mimeType(this: *VirtualMachine, str: []const u8) ?bun.http.MimeType { return this.rareData().mimeTypeFromString(this.allocator, str); } @@ -1026,7 +1036,7 @@ pub const VirtualMachine = struct { printer: *js_printer.BufferPrinter, pub fn get(this: *SourceMapHandlerGetter) js_printer.SourceMapHandler { - if (this.vm.debugger == null) { + if (this.vm.debugger == null or this.vm.debugger.?.mode == .connect) { return SavedSourceMap.SourceMapHandler.init(&this.vm.source_mappings); } @@ -1117,7 +1127,7 @@ pub const VirtualMachine = struct { } pub fn loadExtraEnvAndSourceCodePrinter(this: *VirtualMachine) void { - var map = this.bundler.env.map; + var map = this.transpiler.env.map; ensureSourceCodePrinter(this); @@ -1241,7 +1251,7 @@ pub const VirtualMachine = struct { } pub inline fn packageManager(this: *VirtualMachine) *PackageManager { - return this.bundler.getPackageManager(); + return this.transpiler.getPackageManager(); } pub fn garbageCollect(this: *const VirtualMachine, sync: bool) usize { @@ -1262,7 +1272,7 @@ pub const VirtualMachine = struct { pub fn reload(this: *VirtualMachine, _: *HotReloader.HotReloadTask) void { Output.debug("Reloading...", .{}); - const should_clear_terminal = !this.bundler.env.hasSetNoClearTerminalOnReload(!Output.enable_ansi_colors); + const should_clear_terminal = !this.transpiler.env.hasSetNoClearTerminalOnReload(!Output.enable_ansi_colors); if (this.hot_reload == .watch) { Output.flush(); bun.reloadProcess( @@ -1700,7 +1710,7 @@ pub const VirtualMachine = struct { vm.allocator = arena.allocator(); vm.arena = &arena; - vm.bundler.configureDefines() catch @panic("Failed to configure defines"); + vm.transpiler.configureDefines() catch @panic("Failed to configure defines"); vm.is_main_thread = false; vm.eventLoop().ensureWaker(); @@ -1818,8 +1828,8 @@ pub const VirtualMachine = struct { ensureSourceCodePrinter(this); } - this.bundler.options.target = .bun_macro; - this.bundler.resolver.caches.fs.use_alternate_source_cache = true; + this.transpiler.options.target = .bun_macro; + this.transpiler.resolver.caches.fs.use_alternate_source_cache = true; this.macro_mode = true; this.event_loop = &this.macro_event_loop; Analytics.Features.macros += 1; @@ -1827,8 +1837,8 @@ pub const VirtualMachine = struct { } pub fn disableMacroMode(this: *VirtualMachine) void { - this.bundler.options.target = .bun; - this.bundler.resolver.caches.fs.use_alternate_source_cache = false; + this.transpiler.options.target = .bun; + this.transpiler.resolver.caches.fs.use_alternate_source_cache = false; this.macro_mode = false; this.event_loop = &this.regular_event_loop; this.transpiler_store.enabled = true; @@ -1868,7 +1878,7 @@ pub const VirtualMachine = struct { const console = try allocator.create(ConsoleObject); console.* = ConsoleObject.init(Output.errorWriter(), Output.writer()); const log = opts.log.?; - const bundler = try Bundler.init( + const transpiler = try Transpiler.init( allocator, log, opts.args, @@ -1881,10 +1891,10 @@ pub const VirtualMachine = struct { .transpiler_store = RuntimeTranspilerStore.init(), .allocator = allocator, .entry_point = ServerEntryPoint{}, - .bundler = bundler, + .transpiler = transpiler, .console = console, .log = log, - .origin = bundler.options.origin, + .origin = transpiler.options.origin, .saved_source_map_table = SavedSourceMap.HashTable.init(bun.default_allocator), .source_mappings = undefined, .macros = MacroMap.init(allocator), @@ -1910,23 +1920,25 @@ pub const VirtualMachine = struct { vm.regular_event_loop.concurrent_tasks = .{}; vm.event_loop = &vm.regular_event_loop; - vm.bundler.macro_context = null; - vm.bundler.resolver.store_fd = false; - vm.bundler.resolver.prefer_module_field = false; + vm.transpiler.macro_context = null; + vm.transpiler.resolver.store_fd = false; + vm.transpiler.resolver.prefer_module_field = false; - vm.bundler.resolver.onWakePackageManager = .{ + vm.transpiler.resolver.onWakePackageManager = .{ .context = &vm.modules, .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, }; - vm.bundler.resolver.standalone_module_graph = opts.graph.?; + vm.transpiler.resolver.standalone_module_graph = opts.graph.?; // Avoid reading from tsconfig.json & package.json when we're in standalone mode - vm.bundler.configureLinkerWithAutoJSX(false); - - vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); + vm.transpiler.configureLinkerWithAutoJSX(false); + vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler); + if (opts.is_main_thread) { + VMHolder.main_thread_vm = vm; + } vm.global = ZigGlobalObject.create( vm.console, -1, @@ -1944,6 +1956,10 @@ pub const VirtualMachine = struct { return vm; } + export fn Bun__isMainThreadVM() callconv(.C) bool { + return get().is_main_thread; + } + pub const Options = struct { allocator: std.mem.Allocator, args: Api.TransformOptions, @@ -1957,6 +1973,7 @@ pub const VirtualMachine = struct { graph: ?*bun.StandaloneModuleGraph = null, debugger: bun.CLI.Command.Debugger = .{ .unspecified = {} }, + is_main_thread: bool = false, }; pub var is_smol_mode = false; @@ -1975,23 +1992,25 @@ pub const VirtualMachine = struct { VMHolder.vm = try allocator.create(VirtualMachine); const console = try allocator.create(ConsoleObject); console.* = ConsoleObject.init(Output.errorWriter(), Output.writer()); - const bundler = try Bundler.init( + const transpiler = try Transpiler.init( allocator, log, try Config.configureTransformOptionsForBunVM(allocator, opts.args), opts.env_loader, ); var vm = VMHolder.vm.?; - + if (opts.is_main_thread) { + VMHolder.main_thread_vm = vm; + } vm.* = VirtualMachine{ .global = undefined, .transpiler_store = RuntimeTranspilerStore.init(), .allocator = allocator, .entry_point = ServerEntryPoint{}, - .bundler = bundler, + .transpiler = transpiler, .console = console, .log = log, - .origin = bundler.options.origin, + .origin = transpiler.options.origin, .saved_source_map_table = SavedSourceMap.HashTable.init(bun.default_allocator), .source_mappings = undefined, .macros = MacroMap.init(allocator), @@ -2016,19 +2035,19 @@ pub const VirtualMachine = struct { vm.regular_event_loop.concurrent_tasks = .{}; vm.event_loop = &vm.regular_event_loop; - vm.bundler.macro_context = null; - vm.bundler.resolver.store_fd = opts.store_fd; - vm.bundler.resolver.prefer_module_field = false; + vm.transpiler.macro_context = null; + vm.transpiler.resolver.store_fd = opts.store_fd; + vm.transpiler.resolver.prefer_module_field = false; - vm.bundler.resolver.onWakePackageManager = .{ + vm.transpiler.resolver.onWakePackageManager = .{ .context = &vm.modules, .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, }; - vm.bundler.configureLinker(); + vm.transpiler.configureLinker(); - vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); + vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler); vm.global = ZigGlobalObject.create( vm.console, @@ -2065,16 +2084,12 @@ pub const VirtualMachine = struct { } const unix = bun.getenvZ("BUN_INSPECT") orelse ""; - const notify = bun.getenvZ("BUN_INSPECT_NOTIFY") orelse ""; const connect_to = bun.getenvZ("BUN_INSPECT_CONNECT_TO") orelse ""; const set_breakpoint_on_first_line = unix.len > 0 and strings.endsWith(unix, "?break=1"); // If we should set a breakpoint on the first line const wait_for_debugger = unix.len > 0 and strings.endsWith(unix, "?wait=1"); // If we should wait for the debugger to connect before starting the event loop - const wait_for_connection: Debugger.Wait = switch (set_breakpoint_on_first_line or wait_for_debugger) { - true => if (notify.len > 0 or connect_to.len > 0) .shortly else .forever, - false => .off, - }; + const wait_for_connection: Debugger.Wait = if (set_breakpoint_on_first_line or wait_for_debugger) .forever else .off; switch (cli_flag) { .unspecified => { @@ -2085,22 +2100,14 @@ pub const VirtualMachine = struct { .wait_for_connection = wait_for_connection, .set_breakpoint_on_first_line = set_breakpoint_on_first_line, }; - } else if (notify.len > 0) { - this.debugger = Debugger{ - .path_or_port = null, - .from_environment_variable = notify, - .wait_for_connection = wait_for_connection, - .set_breakpoint_on_first_line = set_breakpoint_on_first_line, - .mode = .connect, - }; } else if (connect_to.len > 0) { // This works in the vscode debug terminal because that relies on unix or notify being set, which they // are in the debug terminal. This branch doesn't reach this.debugger = Debugger{ .path_or_port = null, .from_environment_variable = connect_to, - .wait_for_connection = wait_for_connection, - .set_breakpoint_on_first_line = set_breakpoint_on_first_line, + .wait_for_connection = .off, + .set_breakpoint_on_first_line = false, .mode = .connect, }; } @@ -2115,11 +2122,11 @@ pub const VirtualMachine = struct { }, } - if (this.debugger != null) { - this.bundler.options.minify_identifiers = false; - this.bundler.options.minify_syntax = false; - this.bundler.options.minify_whitespace = false; - this.bundler.options.debugger = true; + if (this.isInspectorEnabled() and this.debugger.?.mode != .connect) { + this.transpiler.options.minify_identifiers = false; + this.transpiler.options.minify_syntax = false; + this.transpiler.options.minify_whitespace = false; + this.transpiler.options.debugger = true; } } @@ -2140,7 +2147,7 @@ pub const VirtualMachine = struct { VMHolder.vm = try allocator.create(VirtualMachine); const console = try allocator.create(ConsoleObject); console.* = ConsoleObject.init(Output.errorWriter(), Output.writer()); - const bundler = try Bundler.init( + const transpiler = try Transpiler.init( allocator, log, try Config.configureTransformOptionsForBunVM(allocator, opts.args), @@ -2153,10 +2160,10 @@ pub const VirtualMachine = struct { .allocator = allocator, .transpiler_store = RuntimeTranspilerStore.init(), .entry_point = ServerEntryPoint{}, - .bundler = bundler, + .transpiler = transpiler, .console = console, .log = log, - .origin = bundler.options.origin, + .origin = transpiler.options.origin, .saved_source_map_table = SavedSourceMap.HashTable.init(bun.default_allocator), .source_mappings = undefined, .macros = MacroMap.init(allocator), @@ -2183,24 +2190,24 @@ pub const VirtualMachine = struct { vm.regular_event_loop.concurrent_tasks = .{}; vm.event_loop = &vm.regular_event_loop; vm.hot_reload = worker.parent.hot_reload; - vm.bundler.macro_context = null; - vm.bundler.resolver.store_fd = opts.store_fd; - vm.bundler.resolver.prefer_module_field = false; - vm.bundler.resolver.onWakePackageManager = .{ + vm.transpiler.macro_context = null; + vm.transpiler.resolver.store_fd = opts.store_fd; + vm.transpiler.resolver.prefer_module_field = false; + vm.transpiler.resolver.onWakePackageManager = .{ .context = &vm.modules, .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, }; - vm.bundler.resolver.standalone_module_graph = opts.graph; + vm.transpiler.resolver.standalone_module_graph = opts.graph; if (opts.graph == null) { - vm.bundler.configureLinker(); + vm.transpiler.configureLinker(); } else { - vm.bundler.configureLinkerWithAutoJSX(false); + vm.transpiler.configureLinkerWithAutoJSX(false); } vm.smol = opts.smol; - vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); + vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler); vm.global = ZigGlobalObject.create( vm.console, @@ -2212,7 +2219,7 @@ pub const VirtualMachine = struct { vm.regular_event_loop.global = vm.global; vm.regular_event_loop.virtual_machine = vm; vm.jsc = vm.global.vm(); - vm.bundler.setAllocator(allocator); + vm.transpiler.setAllocator(allocator); vm.body_value_hive_allocator = BodyValueHiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value)); return vm; @@ -2232,7 +2239,7 @@ pub const VirtualMachine = struct { VMHolder.vm = try allocator.create(VirtualMachine); const console = try allocator.create(ConsoleObject); console.* = ConsoleObject.init(Output.errorWriter(), Output.writer()); - const bundler = try Bundler.init( + const transpiler = try Transpiler.init( allocator, log, try Config.configureTransformOptionsForBunVM(allocator, opts.args), @@ -2245,10 +2252,10 @@ pub const VirtualMachine = struct { .transpiler_store = RuntimeTranspilerStore.init(), .allocator = allocator, .entry_point = ServerEntryPoint{}, - .bundler = bundler, + .transpiler = transpiler, .console = console, .log = log, - .origin = bundler.options.origin, + .origin = transpiler.options.origin, .saved_source_map_table = SavedSourceMap.HashTable.init(bun.default_allocator), .source_mappings = undefined, .macros = MacroMap.init(allocator), @@ -2273,19 +2280,19 @@ pub const VirtualMachine = struct { vm.regular_event_loop.concurrent_tasks = .{}; vm.event_loop = &vm.regular_event_loop; - vm.bundler.macro_context = null; - vm.bundler.resolver.store_fd = opts.store_fd; - vm.bundler.resolver.prefer_module_field = false; + vm.transpiler.macro_context = null; + vm.transpiler.resolver.store_fd = opts.store_fd; + vm.transpiler.resolver.prefer_module_field = false; - vm.bundler.resolver.onWakePackageManager = .{ + vm.transpiler.resolver.onWakePackageManager = .{ .context = &vm.modules, .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, }; - vm.bundler.configureLinker(); + vm.transpiler.configureLinker(); - vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); + vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler); vm.regular_event_loop.virtual_machine = vm; vm.smol = opts.smol; @@ -2466,7 +2473,7 @@ pub const VirtualMachine = struct { } break :brk .{ - jsc_vm.bundler.options.loaders.get(ext_for_loader) orelse brk2: { + jsc_vm.transpiler.options.loaders.get(ext_for_loader) orelse brk2: { if (strings.eqlLong(specifier, jsc_vm.main, true)) { break :brk2 options.Loader.js; } @@ -2581,7 +2588,7 @@ pub const VirtualMachine = struct { else source else - jsc_vm.bundler.fs.top_level_dir; + jsc_vm.transpiler.fs.top_level_dir; const result: Resolver.Result = try brk: { // TODO: We only want to retry on not found only when the directories we searched for were cached. @@ -2591,7 +2598,7 @@ pub const VirtualMachine = struct { // This cache-bust is disabled when the filesystem is not being used to resolve. var retry_on_not_found = std.fs.path.isAbsolute(source_to_use); while (true) { - break :brk switch (jsc_vm.bundler.resolver.resolveAndAutoInstall( + break :brk switch (jsc_vm.transpiler.resolver.resolveAndAutoInstall( source_to_use, normalized_specifier, if (is_esm) .stmt else .require, @@ -2619,7 +2626,7 @@ pub const VirtualMachine = struct { }; break :name bun.path.joinAbsStringBufZ( - jsc_vm.bundler.fs.top_level_dir, + jsc_vm.transpiler.fs.top_level_dir, &specifier_cache_resolver_buf, &parts, .auto, @@ -2627,7 +2634,7 @@ pub const VirtualMachine = struct { }; // Only re-query if we previously had something cached. - if (jsc_vm.bundler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) { + if (jsc_vm.transpiler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) { continue; } @@ -2638,7 +2645,7 @@ pub const VirtualMachine = struct { }; if (!jsc_vm.macro_mode) { - jsc_vm.has_any_macro_remappings = jsc_vm.has_any_macro_remappings or jsc_vm.bundler.options.macro_remap.count() > 0; + jsc_vm.has_any_macro_remappings = jsc_vm.has_any_macro_remappings or jsc_vm.transpiler.options.macro_remap.count() > 0; } ret.result = result; ret.query_string = query_string; @@ -2737,7 +2744,7 @@ pub const VirtualMachine = struct { } } - if (JSC.HardcodedModule.Aliases.getWithEql(specifier, bun.String.eqlComptime, jsc_vm.bundler.options.target)) |hardcoded| { + if (JSC.HardcodedModule.Aliases.getWithEql(specifier, bun.String.eqlComptime, jsc_vm.transpiler.options.target)) |hardcoded| { // if (hardcoded.tag == .none) { // resolveMaybeNeedsTrailingSlash( // res, @@ -2760,12 +2767,12 @@ pub const VirtualMachine = struct { var log = logger.Log.init(bun.default_allocator); defer log.deinit(); jsc_vm.log = &log; - jsc_vm.bundler.resolver.log = &log; - jsc_vm.bundler.linker.log = &log; + jsc_vm.transpiler.resolver.log = &log; + jsc_vm.transpiler.linker.log = &log; defer { jsc_vm.log = old_log; - jsc_vm.bundler.linker.log = old_log; - jsc_vm.bundler.resolver.log = old_log; + jsc_vm.transpiler.linker.log = old_log; + jsc_vm.transpiler.resolver.log = old_log; } _resolve(&result, specifier_utf8.slice(), normalizeSource(source_utf8.slice()), is_esm, is_a_file_path) catch |err_| { var err = err_; @@ -2915,10 +2922,17 @@ pub const VirtualMachine = struct { writer: Writer, comptime allow_side_effects: bool, ) void { + var formatter = ConsoleObject.Formatter{ + .globalThis = this.global, + .quote_strings = false, + .single_line = false, + .stack_check = bun.StackCheck.init(), + }; + defer formatter.deinit(); if (Output.enable_ansi_colors) { - this.printErrorlikeObject(exception.value(), exception, exception_list, Writer, writer, true, allow_side_effects); + this.printErrorlikeObject(exception.value(), exception, exception_list, &formatter, Writer, writer, true, allow_side_effects); } else { - this.printErrorlikeObject(exception.value(), exception, exception_list, Writer, writer, false, allow_side_effects); + this.printErrorlikeObject(exception.value(), exception, exception_list, &formatter, Writer, writer, false, allow_side_effects); } } @@ -2948,7 +2962,6 @@ pub const VirtualMachine = struct { if (result.isException(this.global.vm())) { const exception = @as(*Exception, @ptrCast(result.asVoid())); - this.printException( exception, exception_list, @@ -2956,10 +2969,17 @@ pub const VirtualMachine = struct { writer, true, ); - } else if (Output.enable_ansi_colors) { - this.printErrorlikeObject(result, null, exception_list, @TypeOf(writer), writer, true, true); } else { - this.printErrorlikeObject(result, null, exception_list, @TypeOf(writer), writer, false, true); + var formatter = ConsoleObject.Formatter{ + .globalThis = this.global, + .quote_strings = false, + .single_line = false, + .stack_check = bun.StackCheck.init(), + }; + defer formatter.deinit(); + switch (Output.enable_ansi_colors) { + inline else => |enable_colors| this.printErrorlikeObject(result, null, exception_list, &formatter, @TypeOf(writer), writer, enable_colors, true), + } } } @@ -2983,8 +3003,8 @@ pub const VirtualMachine = struct { defer this.is_in_preload = false; for (this.preload) |preload| { - var result = switch (this.bundler.resolver.resolveAndAutoInstall( - this.bundler.fs.top_level_dir, + var result = switch (this.transpiler.resolver.resolveAndAutoInstall( + this.transpiler.fs.top_level_dir, normalizeSource(preload), .stmt, if (this.standalone_module_graph == null) .read_only else .disable, @@ -2998,7 +3018,7 @@ pub const VirtualMachine = struct { "{s} resolving preload {}", .{ @errorName(e), - bun.fmt.formatJSONString(preload), + bun.fmt.formatJSONStringLatin1(preload), }, ) catch unreachable; return e; @@ -3010,7 +3030,7 @@ pub const VirtualMachine = struct { this.allocator, "preload not found {}", .{ - bun.fmt.formatJSONString(preload), + bun.fmt.formatJSONStringLatin1(preload), }, ) catch unreachable; return error.ModuleNotFound; @@ -3079,7 +3099,7 @@ pub const VirtualMachine = struct { ); this.eventLoop().ensureWaker(); - if (!this.bundler.options.disable_transpilation) { + if (!this.transpiler.options.disable_transpilation) { if (try this.loadPreloads()) |promise| { JSC.JSValue.fromCell(promise).ensureStillAlive(); JSC.JSValue.fromCell(promise).protect(); @@ -3109,7 +3129,7 @@ pub const VirtualMachine = struct { try this.ensureDebugger(true); - if (!this.bundler.options.disable_transpilation) { + if (!this.transpiler.options.disable_transpilation) { if (try this.loadPreloads()) |promise| { JSC.JSValue.fromCell(promise).ensureStillAlive(); this.pending_internal_promise = promise; @@ -3224,7 +3244,7 @@ pub const VirtualMachine = struct { if (!entry_point_entry.found_existing) { var macro_entry_pointer: *MacroEntryPoint = this.allocator.create(MacroEntryPoint) catch unreachable; entry_point_entry.value_ptr.* = macro_entry_pointer; - try macro_entry_pointer.generate(&this.bundler, Fs.PathName.init(entry_path), function_name, hash, specifier); + try macro_entry_pointer.generate(&this.transpiler, Fs.PathName.init(entry_path), function_name, hash, specifier); } const entry_point = entry_point_entry.value_ptr.*; @@ -3264,14 +3284,8 @@ pub const VirtualMachine = struct { return promise; } - pub fn printErrorLikeObjectSimple(this: *VirtualMachine, value: JSValue, writer: anytype, comptime escape_codes: bool) void { - this.printErrorlikeObject(value, null, null, @TypeOf(writer), writer, escape_codes, false); - } - pub fn printErrorLikeObjectToConsole(this: *VirtualMachine, value: JSValue) void { - switch (Output.enable_ansi_colors_stderr) { - inline else => |colors| this.printErrorLikeObjectSimple(value, Output.errorWriter(), colors), - } + this.runErrorHandler(value, null); } // When the Error-like object is one of our own, it's best to rely on the object directly instead of serializing it to a ZigException. @@ -3286,6 +3300,7 @@ pub const VirtualMachine = struct { value: JSValue, exception: ?*Exception, exception_list: ?*ExceptionList, + formatter: *ConsoleObject.Formatter, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool, @@ -3313,7 +3328,7 @@ pub const VirtualMachine = struct { } if (exception_list) |list| { - zig_exception.addToErrorList(list, this.bundler.fs.top_level_dir, &this.origin) catch {}; + zig_exception.addToErrorList(list, this.transpiler.fs.top_level_dir, &this.origin) catch {}; } } } @@ -3323,6 +3338,7 @@ pub const VirtualMachine = struct { const AggregateErrorIterator = struct { writer: Writer, current_exception_list: ?*ExceptionList = null, + formatter: *ConsoleObject.Formatter, pub fn iteratorWithColor(_vm: [*c]VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { iterator(_vm, globalObject, nextValue, ctx.?, true); @@ -3332,10 +3348,10 @@ pub const VirtualMachine = struct { } inline fn iterator(_: [*c]VM, _: *JSGlobalObject, nextValue: JSValue, ctx: ?*anyopaque, comptime color: bool) void { const this_ = @as(*@This(), @ptrFromInt(@intFromPtr(ctx))); - VirtualMachine.get().printErrorlikeObject(nextValue, null, this_.current_exception_list, Writer, this_.writer, color, allow_side_effects); + VirtualMachine.get().printErrorlikeObject(nextValue, null, this_.current_exception_list, this_.formatter, Writer, this_.writer, color, allow_side_effects); } }; - var iter = AggregateErrorIterator{ .writer = writer, .current_exception_list = exception_list }; + var iter = AggregateErrorIterator{ .writer = writer, .current_exception_list = exception_list, .formatter = formatter }; if (comptime allow_ansi_color) { value.getErrorsProperty(this.global).forEach(this.global, &iter, AggregateErrorIterator.iteratorWithColor); } else { @@ -3347,6 +3363,7 @@ pub const VirtualMachine = struct { was_internal = this.printErrorFromMaybePrivateData( value, exception_list, + formatter, Writer, writer, allow_ansi_color, @@ -3354,10 +3371,11 @@ pub const VirtualMachine = struct { ); } - pub fn printErrorFromMaybePrivateData( + fn printErrorFromMaybePrivateData( this: *VirtualMachine, value: JSC.JSValue, exception_list: ?*ExceptionList, + formatter: *ConsoleObject.Formatter, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool, @@ -3406,6 +3424,7 @@ pub const VirtualMachine = struct { this.printErrorInstance( value, exception_list, + formatter, Writer, writer, allow_ansi_color, @@ -3432,7 +3451,7 @@ pub const VirtualMachine = struct { if (stack.len > 0) { var vm = VirtualMachine.get(); const origin: ?*const URL = if (vm.is_from_devserver) &vm.origin else null; - const dir = vm.bundler.fs.top_level_dir; + const dir = vm.transpiler.fs.top_level_dir; for (stack) |frame| { const file_slice = frame.source_url.toUTF8(bun.default_allocator); @@ -3558,7 +3577,7 @@ pub const VirtualMachine = struct { // defer this so that it copies correctly defer { if (exception_list) |list| { - exception.addToErrorList(list, this.bundler.fs.top_level_dir, &this.origin) catch unreachable; + exception.addToErrorList(list, this.transpiler.fs.top_level_dir, &this.origin) catch unreachable; } } @@ -3619,7 +3638,8 @@ pub const VirtualMachine = struct { if (frame.source_url.hasPrefixComptime("bun:") or frame.source_url.hasPrefixComptime("node:") or frame.source_url.isEmpty() or - frame.source_url.eqlComptime("native")) + frame.source_url.eqlComptime("native") or + frame.source_url.eqlComptime("unknown")) { top_frame_is_builtin = true; continue; @@ -3746,7 +3766,7 @@ pub const VirtualMachine = struct { } } - pub fn printErrorInstance(this: *VirtualMachine, error_instance: JSValue, exception_list: ?*ExceptionList, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool, comptime allow_side_effects: bool) anyerror!void { + fn printErrorInstance(this: *VirtualMachine, error_instance: JSValue, exception_list: ?*ExceptionList, formatter: *ConsoleObject.Formatter, comptime Writer: type, writer: Writer, comptime allow_ansi_color: bool, comptime allow_side_effects: bool) anyerror!void { var exception_holder = ZigException.Holder.init(); var exception = exception_holder.zigException(); defer exception_holder.deinit(this); @@ -3918,32 +3938,6 @@ pub const VirtualMachine = struct { try this.printErrorNameAndMessage(name, message, Writer, writer, allow_ansi_color); } - var add_extra_line = false; - - const Show = struct { - system_code: bool = false, - syscall: bool = false, - errno: bool = false, - path: bool = false, - fd: bool = false, - }; - - const show = Show{ - .system_code = !exception.system_code.eql(name) and !exception.system_code.isEmpty(), - .syscall = !exception.syscall.isEmpty(), - .errno = exception.errno != 0, - .path = !exception.path.isEmpty(), - .fd = exception.fd != -1, - }; - - const extra_fields = .{ - "url", - "info", - "pkg", - "errors", - "cause", - }; - // This is usually unsafe to do, but we are protecting them each time first var errors_to_append = std.ArrayList(JSC.JSValue).init(this.allocator); defer { @@ -3953,82 +3947,147 @@ pub const VirtualMachine = struct { errors_to_append.deinit(); } - if (error_instance != .zero and error_instance.isCell() and error_instance.jsType().canGet()) { - inline for (extra_fields) |field| { - if (try error_instance.getTruthyComptime(this.global, field)) |value| { - const kind = value.jsType(); - if (kind.isStringLike()) { - if (value.toStringOrNull(this.global)) |str| { - var zig_str = str.toSlice(this.global, bun.default_allocator); - defer zig_str.deinit(); - try writer.print(comptime Output.prettyFmt(" {s}: \"{s}\"\n", allow_ansi_color), .{ field, zig_str.slice() }); - add_extra_line = true; + var saw_cause = false; + if (error_instance != .zero) { + const error_instance_type = error_instance.jsType(); + if (error_instance_type == .ErrorInstance) { + const Iterator = JSC.JSPropertyIterator(.{ + .include_value = true, + .skip_empty_name = true, + .own_properties_only = true, + .observable = false, + .only_non_index_properties = true, + }); + var iterator = Iterator.init(this.global, error_instance); + defer iterator.deinit(); + const longest_name = @min(iterator.getLongestPropertyName(), 10); + var is_first_property = true; + while (iterator.next() orelse iterator.getCodeProperty()) |field| { + const value = iterator.value; + if (field.eqlComptime("message") or field.eqlComptime("name") or field.eqlComptime("stack")) { + continue; + } + + // We special-case the code property. Let's avoid printing it twice. + if (field.eqlComptime("code")) { + if (value.isString()) { + const str = value.toBunString(this.global); + defer str.deref(); + if (!str.isEmpty()) { + if (str.eql(name)) { + continue; + } + } } - } else if (kind == .ErrorInstance and + } + + const kind = value.jsType(); + if (kind == .ErrorInstance and // avoid infinite recursion !prev_had_errors) { + if (field.eqlComptime("cause")) { + saw_cause = true; + } value.protect(); try errors_to_append.append(value); - } else if (kind.isObject() or kind.isArray()) { + } else if (kind.isObject() or kind.isArray() or value.isPrimitive() or kind.isStringLike()) { var bun_str = bun.String.empty; defer bun_str.deref(); - value.jsonStringify(this.global, 2, &bun_str); //2 - try writer.print(comptime Output.prettyFmt(" {s}: {}\n", allow_ansi_color), .{ field, bun_str }); - add_extra_line = true; + const prev_disable_inspect_custom = formatter.disable_inspect_custom; + const prev_quote_strings = formatter.quote_strings; + const prev_max_depth = formatter.max_depth; + formatter.depth += 1; + defer { + formatter.depth -= 1; + formatter.max_depth = prev_max_depth; + formatter.quote_strings = prev_quote_strings; + formatter.disable_inspect_custom = prev_disable_inspect_custom; + } + formatter.max_depth = 1; + formatter.quote_strings = true; + formatter.disable_inspect_custom = true; + + const pad_left = longest_name -| field.length(); + is_first_property = false; + try writer.writeByteNTimes(' ', pad_left); + + try writer.print(comptime Output.prettyFmt(" {}: ", allow_ansi_color), .{field}); + + // When we're printing errors for a top-level uncaught eception / rejection, suppress further errors here. + if (allow_side_effects) { + if (this.global.hasException()) { + this.global.clearException(); + } + } + + formatter.format( + JSC.Formatter.Tag.getAdvanced( + value, + this.global, + .{ .disable_inspect_custom = true, .hide_global = true }, + ), + Writer, + writer, + value, + this.global, + allow_ansi_color, + ) catch {}; + + if (allow_side_effects) { + // When we're printing errors for a top-level uncaught eception / rejection, suppress further errors here. + if (this.global.hasException()) { + this.global.clearException(); + } + } else if (this.global.hasException() or formatter.failed) { + return; + } + + try writer.writeAll(comptime Output.prettyFmt(",\n", allow_ansi_color)); + } + } + + if (!is_first_property) { + try writer.writeAll("\n"); + } + } else { + // If you do reportError([1,2,3]] we should still show something at least. + const tag = JSC.Formatter.Tag.getAdvanced( + error_instance, + this.global, + .{ .disable_inspect_custom = true, .hide_global = true }, + ); + if (tag.tag != .NativeCode) { + try formatter.format( + tag, + Writer, + writer, + error_instance, + this.global, + allow_ansi_color, + ); + + // Always include a newline in this case + try writer.writeAll("\n"); + } + } + + // "cause" is not enumerable, so the above loop won't see it. + if (!saw_cause and error_instance_type == .ErrorInstance) { + if (error_instance.getOwn(this.global, "cause")) |cause| { + if (cause.jsType() == .ErrorInstance) { + cause.protect(); + try errors_to_append.append(cause); } } } } - if (show.errno) { - if (show.syscall) { - try writer.writeAll(" "); - } - try writer.print(comptime Output.prettyFmt(" errno: {d}\n", allow_ansi_color), .{exception.errno}); - add_extra_line = true; - } - - if (show.system_code) { - if (show.syscall) { - try writer.writeAll(" "); - } else if (show.errno) { - try writer.writeAll(" "); - } - try writer.print(comptime Output.prettyFmt(" code: \"{}\"\n", allow_ansi_color), .{exception.system_code}); - add_extra_line = true; - } - - if (show.syscall) { - try writer.print(comptime Output.prettyFmt(" syscall: \"{}\"\n", allow_ansi_color), .{exception.syscall}); - add_extra_line = true; - } - - if (show.path) { - if (show.syscall) { - try writer.writeAll(" "); - } else if (show.errno) { - try writer.writeAll(" "); - } - try writer.print(comptime Output.prettyFmt(" path: \"{}\"\n", allow_ansi_color), .{exception.path}); - } - - if (show.fd) { - if (show.syscall) { - try writer.writeAll(" "); - } else if (show.errno) { - try writer.writeAll(" "); - } - try writer.print(comptime Output.prettyFmt(" fd: {d}\n", allow_ansi_color), .{exception.fd}); - } - - if (add_extra_line) try writer.writeAll("\n"); - try printStackTrace(@TypeOf(writer), writer, exception.stack, allow_ansi_color); for (errors_to_append.items) |err| { try writer.writeAll("\n"); - try this.printErrorInstance(err, exception_list, Writer, writer, allow_ansi_color, allow_side_effects); + try this.printErrorInstance(err, exception_list, formatter, Writer, writer, allow_ansi_color, allow_side_effects); } } @@ -4364,12 +4423,12 @@ pub const VirtualMachine = struct { /// To satisfy the interface from NewHotReloader() pub fn getLoaders(vm: *VirtualMachine) *bun.options.Loader.HashTable { - return &vm.bundler.options.loaders; + return &vm.transpiler.options.loaders; } /// To satisfy the interface from NewHotReloader() pub fn bustDirCache(vm: *VirtualMachine, path: []const u8) bool { - return vm.bundler.resolver.bustDirCache(path); + return vm.transpiler.resolver.bustDirCache(path); } comptime { @@ -4531,7 +4590,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime .{ .watch = Watcher.init( Reloader, reloader, - this.bundler.fs, + this.transpiler.fs, bun.default_allocator, ) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); @@ -4541,7 +4600,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime .{ .hot = Watcher.init( Reloader, reloader, - this.bundler.fs, + this.transpiler.fs, bun.default_allocator, ) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); @@ -4549,24 +4608,24 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime } }; if (reload_immediately) { - this.bundler.resolver.watcher = Resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.watch); + this.transpiler.resolver.watcher = Resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.watch); } else { - this.bundler.resolver.watcher = Resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.hot); + this.transpiler.resolver.watcher = Resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.hot); } } else { this.bun_watcher = Watcher.init( Reloader, reloader, - this.bundler.fs, + this.transpiler.fs, bun.default_allocator, ) catch |err| { bun.handleErrorReturnTrace(err, @errorReturnTrace()); Output.panic("Failed to enable File Watcher: {s}", .{@errorName(err)}); }; - this.bundler.resolver.watcher = Resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.?); + this.transpiler.resolver.watcher = Resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.?); } - clear_screen = !this.bundler.env.hasSetNoClearTerminalOnReload(!Output.enable_ansi_colors); + clear_screen = !this.transpiler.env.hasSetNoClearTerminalOnReload(!Output.enable_ansi_colors); reloader.getContext().start() catch @panic("Failed to start File Watcher"); } diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 260339d0fc..6779141c44 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -19,14 +19,14 @@ const IdentityContext = @import("../identity_context.zig").IdentityContext; const Fs = @import("../fs.zig"); const Resolver = @import("../resolver/resolver.zig"); const ast = @import("../import_record.zig"); -const MacroEntryPoint = bun.bundler.MacroEntryPoint; -const ParseResult = bun.bundler.ParseResult; +const MacroEntryPoint = bun.transpiler.MacroEntryPoint; +const ParseResult = bun.transpiler.ParseResult; const logger = bun.logger; const Api = @import("../api/schema.zig").Api; const options = @import("../options.zig"); -const Bundler = bun.Bundler; -const PluginRunner = bun.bundler.PluginRunner; -const ServerEntryPoint = bun.bundler.ServerEntryPoint; +const Transpiler = bun.Transpiler; +const PluginRunner = bun.transpiler.PluginRunner; +const ServerEntryPoint = bun.transpiler.ServerEntryPoint; const js_printer = bun.js_printer; const js_parser = bun.js_parser; const js_ast = bun.JSAst; @@ -87,6 +87,7 @@ const Async = bun.Async; const String = bun.String; const debug = Output.scoped(.ModuleLoader, true); +const panic = std.debug.panic; inline fn jsSyntheticModule(name: ResolvedSource.Tag, specifier: String) ResolvedSource { return ResolvedSource{ @@ -261,7 +262,7 @@ pub const RuntimeTranspilerStore = struct { .referrer = bun.default_allocator.dupe(u8, referrer) catch unreachable, .vm = vm, .log = logger.Log.init(bun.default_allocator), - .loader = vm.bundler.options.loader(owned_path.name.ext), + .loader = vm.transpiler.options.loader(owned_path.name.ext), .promise = JSC.Strong.create(JSC.JSValue.fromCell(promise), globalObject), .poll_ref = .{}, .fetcher = TranspilerJob.Fetcher{ @@ -346,7 +347,7 @@ pub const RuntimeTranspilerStore = struct { if (resolved_source.is_commonjs_module) { const actual_package_json: *PackageJSON = brk2: { // this should already be cached virtually always so it's fine to do this - const dir_info = (vm.bundler.resolver.readDirInfo(this.path.name.dir) catch null) orelse + const dir_info = (vm.transpiler.resolver.readDirInfo(this.path.name.dir) catch null) orelse break :brk .javascript; break :brk2 dir_info.package_json orelse dir_info.enclosing_package_json; @@ -412,13 +413,13 @@ pub const RuntimeTranspilerStore = struct { }; var vm = this.vm; - var bundler: bun.Bundler = undefined; - bundler = vm.bundler; - bundler.setAllocator(allocator); - bundler.setLog(&this.log); - bundler.resolver.opts = bundler.options; - bundler.macro_context = null; - bundler.linker.resolver = &bundler.resolver; + var transpiler: bun.Transpiler = undefined; + transpiler = vm.transpiler; + transpiler.setAllocator(allocator); + transpiler.setLog(&this.log); + transpiler.resolver.opts = transpiler.options; + transpiler.macro_context = null; + transpiler.linker.resolver = &transpiler.resolver; var fd: ?StoredFileDescriptorType = null; var package_json: ?*PackageJSON = null; @@ -441,7 +442,7 @@ pub const RuntimeTranspilerStore = struct { const macro_remappings = if (vm.macro_mode or !vm.has_any_macro_remappings or is_node_override) MacroRemap{} else - bundler.options.macro_remap; + transpiler.options.macro_remap; var fallback_source: logger.Source = undefined; @@ -458,7 +459,7 @@ pub const RuntimeTranspilerStore = struct { vm.main_hash == hash and strings.eqlLong(vm.main, path.text, false); - var parse_options = Bundler.ParseOptions{ + var parse_options = Transpiler.ParseOptions{ .allocator = allocator, .path = path, .loader = loader, @@ -467,12 +468,12 @@ pub const RuntimeTranspilerStore = struct { .file_fd_ptr = &input_file_fd, .file_hash = hash, .macro_remappings = macro_remappings, - .jsx = bundler.options.jsx, - .emit_decorator_metadata = bundler.options.emit_decorator_metadata, + .jsx = transpiler.options.jsx, + .emit_decorator_metadata = transpiler.options.emit_decorator_metadata, .virtual_source = null, .dont_bundle_twice = true, .allow_commonjs = true, - .inject_jest_globals = bundler.options.rewrite_jest_for_tests and is_main, + .inject_jest_globals = transpiler.options.rewrite_jest_for_tests and is_main, .set_breakpoint_on_first_line = vm.debugger != null and vm.debugger.?.set_breakpoint_on_first_line and is_main and @@ -497,7 +498,7 @@ pub const RuntimeTranspilerStore = struct { } } - var parse_result: bun.bundler.ParseResult = bundler.parseMaybeReturnFileOnlyAllowSharedBuffer( + var parse_result: bun.transpiler.ParseResult = transpiler.parseMaybeReturnFileOnlyAllowSharedBuffer( parse_options, null, false, @@ -595,14 +596,14 @@ pub const RuntimeTranspilerStore = struct { for (parse_result.ast.import_records.slice()) |*import_record_| { var import_record: *bun.ImportRecord = import_record_; - if (JSC.HardcodedModule.Aliases.get(import_record.path.text, bundler.options.target)) |replacement| { + if (JSC.HardcodedModule.Aliases.get(import_record.path.text, transpiler.options.target)) |replacement| { import_record.path.text = replacement.path; import_record.tag = replacement.tag; import_record.is_external_without_side_effects = true; continue; } - if (bundler.options.rewrite_jest_for_tests) { + if (transpiler.options.rewrite_jest_for_tests) { if (strings.eqlComptime( import_record.path.text, "@jest/globals", @@ -642,7 +643,7 @@ pub const RuntimeTranspilerStore = struct { { var mapper = vm.sourceMapHandler(&printer); defer source_code_printer.?.* = printer; - _ = bundler.printWithSourceMap( + _ = transpiler.printWithSourceMap( parse_result, @TypeOf(&printer), &printer, @@ -1380,20 +1381,20 @@ pub const ModuleLoader = struct { const specifier = this.specifier; const old_log = jsc_vm.log; - jsc_vm.bundler.linker.log = log; - jsc_vm.bundler.log = log; - jsc_vm.bundler.resolver.log = log; + jsc_vm.transpiler.linker.log = log; + jsc_vm.transpiler.log = log; + jsc_vm.transpiler.resolver.log = log; jsc_vm.packageManager().log = log; defer { - jsc_vm.bundler.linker.log = old_log; - jsc_vm.bundler.log = old_log; - jsc_vm.bundler.resolver.log = old_log; + jsc_vm.transpiler.linker.log = old_log; + jsc_vm.transpiler.log = old_log; + jsc_vm.transpiler.resolver.log = old_log; jsc_vm.packageManager().log = old_log; } // We _must_ link because: // - node_modules bundle won't be properly - try jsc_vm.bundler.linker.link( + try jsc_vm.transpiler.linker.link( path, &parse_result, jsc_vm.origin, @@ -1409,7 +1410,7 @@ pub const ModuleLoader = struct { { var mapper = jsc_vm.sourceMapHandler(&printer); defer VirtualMachine.source_code_printer.?.* = printer; - _ = try jsc_vm.bundler.printWithSourceMap( + _ = try jsc_vm.transpiler.printWithSourceMap( parse_result, @TypeOf(&printer), &printer, @@ -1479,7 +1480,7 @@ pub const ModuleLoader = struct { var jsc_vm = global.bunVM(); const filename = str.toUTF8(jsc_vm.allocator); defer filename.deinit(); - const loader = jsc_vm.bundler.options.loader(Fs.PathName.init(filename.slice()).ext).toAPI(); + const loader = jsc_vm.transpiler.options.loader(Fs.PathName.init(filename.slice()).ext).toAPI(); if (loader == .file) { return Api.Loader.js; } @@ -1504,10 +1505,23 @@ pub const ModuleLoader = struct { ) !ResolvedSource { const disable_transpilying = comptime flags.disableTranspiling(); + if (comptime disable_transpilying) { + if (!(loader.isJavaScriptLike() or loader == .toml or loader == .text or loader == .json)) { + // Don't print "export default " + return ResolvedSource{ + .allocator = null, + .source_code = bun.String.empty, + .specifier = input_specifier, + .source_url = input_specifier.createIfDifferent(path.text), + .hash = 0, + }; + } + } + switch (loader) { .js, .jsx, .ts, .tsx, .json, .toml, .text => { jsc_vm.transpiled_count += 1; - jsc_vm.bundler.resetStore(); + jsc_vm.transpiler.resetStore(); const hash = JSC.GenericWatcher.getHash(path.text); const is_main = jsc_vm.main.len == path.text.len and jsc_vm.main_hash == hash and @@ -1569,19 +1583,19 @@ pub const ModuleLoader = struct { .sourcemap_allocator = bun.default_allocator, }; - const old = jsc_vm.bundler.log; - jsc_vm.bundler.log = log; - jsc_vm.bundler.linker.log = log; - jsc_vm.bundler.resolver.log = log; - if (jsc_vm.bundler.resolver.package_manager) |pm| { + const old = jsc_vm.transpiler.log; + jsc_vm.transpiler.log = log; + jsc_vm.transpiler.linker.log = log; + jsc_vm.transpiler.resolver.log = log; + if (jsc_vm.transpiler.resolver.package_manager) |pm| { pm.log = log; } defer { - jsc_vm.bundler.log = old; - jsc_vm.bundler.linker.log = old; - jsc_vm.bundler.resolver.log = old; - if (jsc_vm.bundler.resolver.package_manager) |pm| { + jsc_vm.transpiler.log = old; + jsc_vm.transpiler.linker.log = old; + jsc_vm.transpiler.resolver.log = old; + if (jsc_vm.transpiler.resolver.package_manager) |pm| { pm.log = old; } } @@ -1592,7 +1606,7 @@ pub const ModuleLoader = struct { const macro_remappings = if (jsc_vm.macro_mode or !jsc_vm.has_any_macro_remappings or is_node_override) MacroRemap{} else - jsc_vm.bundler.options.macro_remap; + jsc_vm.transpiler.options.macro_remap; var fallback_source: logger.Source = undefined; @@ -1604,7 +1618,7 @@ pub const ModuleLoader = struct { var should_close_input_file_fd = fd == null; var input_file_fd: StoredFileDescriptorType = bun.invalid_fd; - var parse_options = Bundler.ParseOptions{ + var parse_options = Transpiler.ParseOptions{ .allocator = allocator, .path = path, .loader = loader, @@ -1613,12 +1627,12 @@ pub const ModuleLoader = struct { .file_fd_ptr = &input_file_fd, .file_hash = hash, .macro_remappings = macro_remappings, - .jsx = jsc_vm.bundler.options.jsx, - .emit_decorator_metadata = jsc_vm.bundler.options.emit_decorator_metadata, + .jsx = jsc_vm.transpiler.options.jsx, + .emit_decorator_metadata = jsc_vm.transpiler.options.emit_decorator_metadata, .virtual_source = virtual_source, .dont_bundle_twice = true, .allow_commonjs = true, - .inject_jest_globals = jsc_vm.bundler.options.rewrite_jest_for_tests and is_main, + .inject_jest_globals = jsc_vm.transpiler.options.rewrite_jest_for_tests and is_main, .keep_json_and_toml_as_one_statement = true, .allow_bytecode_cache = true, .set_breakpoint_on_first_line = is_main and @@ -1652,7 +1666,7 @@ pub const ModuleLoader = struct { JSC.VM.ReleaseHeapAccess{ .vm = jsc_vm.jsc, .needs_to_release = false }; defer heap_access.acquire(); - break :brk jsc_vm.bundler.parseMaybeReturnFileOnly( + break :brk jsc_vm.transpiler.parseMaybeReturnFileOnly( parse_options, null, return_file_only, @@ -1719,7 +1733,7 @@ pub const ModuleLoader = struct { } } - if (jsc_vm.bundler.log.errors > 0) { + if (jsc_vm.transpiler.log.errors > 0) { give_back_arena = false; return error.ParseError; } @@ -1767,7 +1781,7 @@ pub const ModuleLoader = struct { .specifier = input_specifier, .source_url = input_specifier.createIfDifferent(path.text), .hash = 0, - .jsvalue_for_export = parse_result.ast.parts.@"[0]"().stmts[0].data.s_expr.value.toJS(allocator, globalObject orelse jsc_vm.global) catch @panic("Unexpected JS error"), + .jsvalue_for_export = parse_result.ast.parts.@"[0]"().stmts[0].data.s_expr.value.toJS(allocator, globalObject orelse jsc_vm.global) catch |e| panic("Unexpected JS error: {s}", .{@errorName(e)}), .tag = .exports_object, }; } @@ -1816,7 +1830,7 @@ pub const ModuleLoader = struct { if (entry.metadata.module_type == .cjs and parse_result.source.path.isFile()) { const actual_package_json: *PackageJSON = package_json orelse brk2: { // this should already be cached virtually always so it's fine to do this - const dir_info = (jsc_vm.bundler.resolver.readDirInfo(parse_result.source.path.name.dir) catch null) orelse + const dir_info = (jsc_vm.transpiler.resolver.readDirInfo(parse_result.source.path.name.dir) catch null) orelse break :brk .javascript; break :brk2 dir_info.package_json orelse dir_info.enclosing_package_json; @@ -1832,11 +1846,11 @@ pub const ModuleLoader = struct { }; } - const start_count = jsc_vm.bundler.linker.import_counter; + const start_count = jsc_vm.transpiler.linker.import_counter; // We _must_ link because: // - node_modules bundle won't be properly - try jsc_vm.bundler.linker.link( + try jsc_vm.transpiler.linker.link( path, &parse_result, jsc_vm.origin, @@ -1852,8 +1866,8 @@ pub const ModuleLoader = struct { if (parse_result.source.contents_is_recycled) { // this shared buffer is about to become owned by the AsyncModule struct - jsc_vm.bundler.resolver.caches.fs.resetSharedBuffer( - jsc_vm.bundler.resolver.caches.fs.sharedBuffer(), + jsc_vm.transpiler.resolver.caches.fs.resetSharedBuffer( + jsc_vm.transpiler.resolver.caches.fs.sharedBuffer(), ); } @@ -1877,8 +1891,8 @@ pub const ModuleLoader = struct { } if (!jsc_vm.macro_mode) - jsc_vm.resolved_count += jsc_vm.bundler.linker.import_counter - start_count; - jsc_vm.bundler.linker.import_counter = 0; + jsc_vm.resolved_count += jsc_vm.transpiler.linker.import_counter - start_count; + jsc_vm.transpiler.linker.import_counter = 0; var printer = source_code_printer.*; printer.ctx.reset(); @@ -1886,7 +1900,7 @@ pub const ModuleLoader = struct { _ = brk: { var mapper = jsc_vm.sourceMapHandler(&printer); - break :brk try jsc_vm.bundler.printWithSourceMap( + break :brk try jsc_vm.transpiler.printWithSourceMap( parse_result, @TypeOf(&printer), &printer, @@ -1916,7 +1930,7 @@ pub const ModuleLoader = struct { if (parse_result.ast.exports_kind == .cjs and parse_result.source.path.isFile()) { const actual_package_json: *PackageJSON = package_json orelse brk2: { // this should already be cached virtually always so it's fine to do this - const dir_info = (jsc_vm.bundler.resolver.readDirInfo(parse_result.source.path.name.dirOrDot()) catch null) orelse + const dir_info = (jsc_vm.transpiler.resolver.readDirInfo(parse_result.source.path.name.dirOrDot()) catch null) orelse break :brk .javascript; break :brk2 dir_info.package_json orelse dir_info.enclosing_package_json; @@ -1965,7 +1979,7 @@ pub const ModuleLoader = struct { // } // } - // var parse_options = Bundler.ParseOptions{ + // var parse_options = Transpiler.ParseOptions{ // .allocator = allocator, // .path = path, // .loader = loader, @@ -1973,10 +1987,10 @@ pub const ModuleLoader = struct { // .file_descriptor = fd, // .file_hash = hash, // .macro_remappings = MacroRemap{}, - // .jsx = jsc_vm.bundler.options.jsx, + // .jsx = jsc_vm.transpiler.options.jsx, // }; - // var parse_result = jsc_vm.bundler.parse( + // var parse_result = jsc_vm.transpiler.parse( // parse_options, // null, // ) orelse { @@ -2192,7 +2206,7 @@ pub const ModuleLoader = struct { ret: *ErrorableResolvedSource, ) bool { JSC.markBinding(@src()); - var log = logger.Log.init(jsc_vm.bundler.allocator); + var log = logger.Log.init(jsc_vm.transpiler.allocator); defer log.deinit(); if (ModuleLoader.fetchBuiltinModule( @@ -2223,7 +2237,7 @@ pub const ModuleLoader = struct { allow_promise: bool, ) ?*anyopaque { JSC.markBinding(@src()); - var log = logger.Log.init(jsc_vm.bundler.allocator); + var log = logger.Log.init(jsc_vm.transpiler.allocator); defer log.deinit(); var _specifier = specifier_ptr.toUTF8(jsc_vm.allocator); @@ -2249,7 +2263,7 @@ pub const ModuleLoader = struct { // Deliberately optional. // The concurrent one only handles javascript-like loaders right now. - var loader: ?options.Loader = jsc_vm.bundler.options.loaders.get(path.name.ext); + var loader: ?options.Loader = jsc_vm.transpiler.options.loaders.get(path.name.ext); if (jsc_vm.module_loader.eval_source) |eval_source| { if (strings.endsWithComptime(specifier, bun.pathLiteral("/[eval]"))) { @@ -2276,7 +2290,7 @@ pub const ModuleLoader = struct { path = current_path; } - loader = jsc_vm.bundler.options.loaders.get(current_path.name.ext) orelse .tsx; + loader = jsc_vm.transpiler.options.loaders.get(current_path.name.ext) orelse .tsx; } else { loader = .tsx; } @@ -2611,7 +2625,7 @@ pub const ModuleLoader = struct { const loader = if (loader_ != ._none) options.Loader.fromAPI(loader_) else - jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: { + jsc_vm.transpiler.options.loaders.get(path.name.ext) orelse brk: { if (strings.eqlLong(specifier, jsc_vm.main, true)) { break :brk options.Loader.js; } diff --git a/src/bun.js/modules/BunJSCModule.h b/src/bun.js/modules/BunJSCModule.h index 353e09fac9..562d795cfe 100644 --- a/src/bun.js/modules/BunJSCModule.h +++ b/src/bun.js/modules/BunJSCModule.h @@ -1,6 +1,7 @@ #include "_NativeModule.h" #include "ExceptionOr.h" +#include "JavaScriptCore/CallData.h" #include "JavaScriptCore/ArgList.h" #include "JavaScriptCore/ExceptionScope.h" #include "JavaScriptCore/JSCJSValue.h" @@ -693,7 +694,7 @@ JSC_DEFINE_HOST_FUNCTION(functionRunProfiler, (JSGlobalObject * globalObject, Ca samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(); samplingProfiler.start(); - JSValue returnValue = JSC::call(globalObject, function, callData, JSC::jsUndefined(), args); + JSValue returnValue = JSC::profiledCall(globalObject, ProfilingReason::API, function, callData, JSC::jsUndefined(), args); if (returnValue.isEmpty() || throwScope.exception()) { return JSValue::encode(reportFailure(vm)); @@ -889,6 +890,37 @@ JSC_DEFINE_HOST_FUNCTION(functionCodeCoverageForFile, basicBlocks.size(), functionStartOffset, ignoreSourceMap); } +JSC_DEFINE_HOST_FUNCTION(functionEstimateDirectMemoryUsageOf, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + JSValue value = callFrame->argument(0); + if (value.isCell()) { + auto& vm = globalObject->vm(); + EnsureStillAliveScope alive = value; + return JSValue::encode(jsDoubleNumber(alive.value().asCell()->estimatedSizeInBytes(vm))); + } + + return JSValue::encode(jsNumber(0)); +} + +#if USE(BMALLOC_MEMORY_FOOTPRINT_API) + +#include + +JSC_DEFINE_HOST_FUNCTION(functionPercentAvailableMemoryInUse, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsDoubleNumber(bmalloc::api::percentAvailableMemoryInUse())); +} + +#else + +JSC_DEFINE_HOST_FUNCTION(functionPercentAvailableMemoryInUse, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsNull()); +} + +#endif + // clang-format off /* Source for BunJSCModuleTable.lut.h @begin BunJSCModuleTable @@ -918,17 +950,19 @@ JSC_DEFINE_HOST_FUNCTION(functionCodeCoverageForFile, totalCompileTime functionTotalCompileTime Function 0 getProtectedObjects functionGetProtectedObjects Function 0 generateHeapSnapshotForDebugging functionGenerateHeapSnapshotForDebugging Function 0 - profile functionRunProfiler Function 0 + profile functionRunProfiler Function 0 setTimeZone functionSetTimeZone Function 0 serialize functionSerialize Function 0 - deserialize functionDeserialize Function 0 + deserialize functionDeserialize Function 0 + estimateShallowMemoryUsageOf functionEstimateDirectMemoryUsageOf Function 1 + percentAvailableMemoryInUse functionPercentAvailableMemoryInUse Function 0 @end */ namespace Zig { DEFINE_NATIVE_MODULE(BunJSC) { - INIT_NATIVE_MODULE(34); + INIT_NATIVE_MODULE(36); putNativeFn(Identifier::fromString(vm, "callerSourceOrigin"_s), functionCallerSourceOrigin); putNativeFn(Identifier::fromString(vm, "jscDescribe"_s), functionDescribe); @@ -957,11 +991,13 @@ DEFINE_NATIVE_MODULE(BunJSC) putNativeFn(Identifier::fromString(vm, "getProtectedObjects"_s), functionGetProtectedObjects); putNativeFn(Identifier::fromString(vm, "generateHeapSnapshotForDebugging"_s), functionGenerateHeapSnapshotForDebugging); putNativeFn(Identifier::fromString(vm, "profile"_s), functionRunProfiler); - putNativeFn(Identifier::fromString(vm, "codeCoverageForFile"_s), functionCodeCoverageForFile); + putNativeFn(Identifier::fromString(vm, "codeCoverageForFile"_s), functionCodeCoverageForFile); putNativeFn(Identifier::fromString(vm, "setTimeZone"_s), functionSetTimeZone); putNativeFn(Identifier::fromString(vm, "serialize"_s), functionSerialize); putNativeFn(Identifier::fromString(vm, "deserialize"_s), functionDeserialize); - + putNativeFn(Identifier::fromString(vm, "estimateShallowMemoryUsageOf"_s), functionEstimateDirectMemoryUsageOf); + putNativeFn(Identifier::fromString(vm, "percentAvailableMemoryInUse"_s), functionPercentAvailableMemoryInUse); + // Deprecated putNativeFn(Identifier::fromString(vm, "describe"_s), functionDescribe); putNativeFn(Identifier::fromString(vm, "describeArray"_s), functionDescribeArray); diff --git a/src/bun.js/modules/NodeUtilTypesModule.h b/src/bun.js/modules/NodeUtilTypesModule.h index cc4117701e..219665c958 100644 --- a/src/bun.js/modules/NodeUtilTypesModule.h +++ b/src/bun.js/modules/NodeUtilTypesModule.h @@ -1,6 +1,8 @@ #pragma once #include "BunClientData.h" +#include "JSDOMWrapper.h" +#include "JSEventTarget.h" #include "JavaScriptCore/CatchScope.h" #include "_NativeModule.h" @@ -452,89 +454,67 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionIsCryptoKey, GET_FIRST_CELL return JSValue::encode(jsBoolean(cell->inherits())); } +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsEventTarget, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->inherits())); +} namespace Zig { + +// Hardcoded module "node:util/types" DEFINE_NATIVE_MODULE(NodeUtilTypes) { - INIT_NATIVE_MODULE(43); + INIT_NATIVE_MODULE(44); putNativeFn(Identifier::fromString(vm, "isExternal"_s), jsFunctionIsExternal); putNativeFn(Identifier::fromString(vm, "isDate"_s), jsFunctionIsDate); - putNativeFn(Identifier::fromString(vm, "isArgumentsObject"_s), - jsFunctionIsArgumentsObject); - putNativeFn(Identifier::fromString(vm, "isBigIntObject"_s), - jsFunctionIsBigIntObject); - putNativeFn(Identifier::fromString(vm, "isBooleanObject"_s), - jsFunctionIsBooleanObject); - putNativeFn(Identifier::fromString(vm, "isNumberObject"_s), - jsFunctionIsNumberObject); - putNativeFn(Identifier::fromString(vm, "isStringObject"_s), - jsFunctionIsStringObject); - putNativeFn(Identifier::fromString(vm, "isSymbolObject"_s), - jsFunctionIsSymbolObject); - putNativeFn(Identifier::fromString(vm, "isNativeError"_s), - jsFunctionIsNativeError); + putNativeFn(Identifier::fromString(vm, "isArgumentsObject"_s), jsFunctionIsArgumentsObject); + putNativeFn(Identifier::fromString(vm, "isBigIntObject"_s), jsFunctionIsBigIntObject); + putNativeFn(Identifier::fromString(vm, "isBooleanObject"_s), jsFunctionIsBooleanObject); + putNativeFn(Identifier::fromString(vm, "isNumberObject"_s), jsFunctionIsNumberObject); + putNativeFn(Identifier::fromString(vm, "isStringObject"_s), jsFunctionIsStringObject); + putNativeFn(Identifier::fromString(vm, "isSymbolObject"_s), jsFunctionIsSymbolObject); + putNativeFn(Identifier::fromString(vm, "isNativeError"_s), jsFunctionIsNativeError); putNativeFn(Identifier::fromString(vm, "isRegExp"_s), jsFunctionIsRegExp); - putNativeFn(Identifier::fromString(vm, "isAsyncFunction"_s), - jsFunctionIsAsyncFunction); - putNativeFn(Identifier::fromString(vm, "isGeneratorFunction"_s), - jsFunctionIsGeneratorFunction); - putNativeFn(Identifier::fromString(vm, "isGeneratorObject"_s), - jsFunctionIsGeneratorObject); + putNativeFn(Identifier::fromString(vm, "isAsyncFunction"_s), jsFunctionIsAsyncFunction); + putNativeFn(Identifier::fromString(vm, "isGeneratorFunction"_s), jsFunctionIsGeneratorFunction); + putNativeFn(Identifier::fromString(vm, "isGeneratorObject"_s), jsFunctionIsGeneratorObject); putNativeFn(Identifier::fromString(vm, "isPromise"_s), jsFunctionIsPromise); putNativeFn(Identifier::fromString(vm, "isMap"_s), jsFunctionIsMap); putNativeFn(Identifier::fromString(vm, "isSet"_s), jsFunctionIsSet); - putNativeFn(Identifier::fromString(vm, "isMapIterator"_s), - jsFunctionIsMapIterator); - putNativeFn(Identifier::fromString(vm, "isSetIterator"_s), - jsFunctionIsSetIterator); + putNativeFn(Identifier::fromString(vm, "isMapIterator"_s), jsFunctionIsMapIterator); + putNativeFn(Identifier::fromString(vm, "isSetIterator"_s), jsFunctionIsSetIterator); putNativeFn(Identifier::fromString(vm, "isWeakMap"_s), jsFunctionIsWeakMap); putNativeFn(Identifier::fromString(vm, "isWeakSet"_s), jsFunctionIsWeakSet); - putNativeFn(Identifier::fromString(vm, "isArrayBuffer"_s), - jsFunctionIsArrayBuffer); + putNativeFn(Identifier::fromString(vm, "isArrayBuffer"_s), jsFunctionIsArrayBuffer); putNativeFn(Identifier::fromString(vm, "isDataView"_s), jsFunctionIsDataView); - putNativeFn(Identifier::fromString(vm, "isSharedArrayBuffer"_s), - jsFunctionIsSharedArrayBuffer); + putNativeFn(Identifier::fromString(vm, "isSharedArrayBuffer"_s), jsFunctionIsSharedArrayBuffer); putNativeFn(Identifier::fromString(vm, "isProxy"_s), jsFunctionIsProxy); - putNativeFn(Identifier::fromString(vm, "isModuleNamespaceObject"_s), - jsFunctionIsModuleNamespaceObject); - putNativeFn(Identifier::fromString(vm, "isAnyArrayBuffer"_s), - jsFunctionIsAnyArrayBuffer); - putNativeFn(Identifier::fromString(vm, "isBoxedPrimitive"_s), - jsFunctionIsBoxedPrimitive); - putNativeFn(Identifier::fromString(vm, "isArrayBufferView"_s), - jsFunctionIsArrayBufferView); - putNativeFn(Identifier::fromString(vm, "isTypedArray"_s), - jsFunctionIsTypedArray); - putNativeFn(Identifier::fromString(vm, "isUint8Array"_s), - jsFunctionIsUint8Array); - putNativeFn(Identifier::fromString(vm, "isUint8ClampedArray"_s), - jsFunctionIsUint8ClampedArray); - putNativeFn(Identifier::fromString(vm, "isUint16Array"_s), - jsFunctionIsUint16Array); - putNativeFn(Identifier::fromString(vm, "isUint32Array"_s), - jsFunctionIsUint32Array); - putNativeFn(Identifier::fromString(vm, "isInt8Array"_s), - jsFunctionIsInt8Array); - putNativeFn(Identifier::fromString(vm, "isInt16Array"_s), - jsFunctionIsInt16Array); - putNativeFn(Identifier::fromString(vm, "isInt32Array"_s), - jsFunctionIsInt32Array); - putNativeFn(Identifier::fromString(vm, "isFloat16Array"_s), - jsFunctionIsFloat16Array); - putNativeFn(Identifier::fromString(vm, "isFloat32Array"_s), - jsFunctionIsFloat32Array); - putNativeFn(Identifier::fromString(vm, "isFloat64Array"_s), - jsFunctionIsFloat64Array); - putNativeFn(Identifier::fromString(vm, "isBigInt64Array"_s), - jsFunctionIsBigInt64Array); - putNativeFn(Identifier::fromString(vm, "isBigUint64Array"_s), - jsFunctionIsBigUint64Array); - putNativeFn(Identifier::fromString(vm, "isKeyObject"_s), - jsFunctionIsKeyObject); - putNativeFn(Identifier::fromString(vm, "isCryptoKey"_s), - jsFunctionIsCryptoKey); + putNativeFn(Identifier::fromString(vm, "isModuleNamespaceObject"_s), jsFunctionIsModuleNamespaceObject); + putNativeFn(Identifier::fromString(vm, "isAnyArrayBuffer"_s), jsFunctionIsAnyArrayBuffer); + putNativeFn(Identifier::fromString(vm, "isBoxedPrimitive"_s), jsFunctionIsBoxedPrimitive); + putNativeFn(Identifier::fromString(vm, "isArrayBufferView"_s), jsFunctionIsArrayBufferView); + putNativeFn(Identifier::fromString(vm, "isTypedArray"_s), jsFunctionIsTypedArray); + putNativeFn(Identifier::fromString(vm, "isUint8Array"_s), jsFunctionIsUint8Array); + putNativeFn(Identifier::fromString(vm, "isUint8ClampedArray"_s), jsFunctionIsUint8ClampedArray); + putNativeFn(Identifier::fromString(vm, "isUint16Array"_s), jsFunctionIsUint16Array); + putNativeFn(Identifier::fromString(vm, "isUint32Array"_s), jsFunctionIsUint32Array); + putNativeFn(Identifier::fromString(vm, "isInt8Array"_s), jsFunctionIsInt8Array); + putNativeFn(Identifier::fromString(vm, "isInt16Array"_s), jsFunctionIsInt16Array); + putNativeFn(Identifier::fromString(vm, "isInt32Array"_s), jsFunctionIsInt32Array); + putNativeFn(Identifier::fromString(vm, "isFloat16Array"_s), jsFunctionIsFloat16Array); + putNativeFn(Identifier::fromString(vm, "isFloat32Array"_s), jsFunctionIsFloat32Array); + putNativeFn(Identifier::fromString(vm, "isFloat64Array"_s), jsFunctionIsFloat64Array); + putNativeFn(Identifier::fromString(vm, "isBigInt64Array"_s), jsFunctionIsBigInt64Array); + putNativeFn(Identifier::fromString(vm, "isBigUint64Array"_s), jsFunctionIsBigUint64Array); + putNativeFn(Identifier::fromString(vm, "isKeyObject"_s), jsFunctionIsKeyObject); + putNativeFn(Identifier::fromString(vm, "isCryptoKey"_s), jsFunctionIsCryptoKey); + putNativeFn(Identifier::fromString(vm, "isEventTarget"_s), jsFunctionIsEventTarget); RETURN_NATIVE_MODULE(); } + } // namespace Zig diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index dab9d40b98..8086084209 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -3557,10 +3557,12 @@ pub const NodeFS = struct { return Maybe(Return.CopyFile).todo(); } - var src_buf: bun.WPathBuffer = undefined; - var dest_buf: bun.WPathBuffer = undefined; - const src = strings.toWPathNormalizeAutoExtend(&src_buf, args.src.sliceZ(&this.sync_error_buf)); - const dest = strings.toWPathNormalizeAutoExtend(&dest_buf, args.dest.sliceZ(&this.sync_error_buf)); + const src_buf = bun.OSPathBufferPool.get(); + defer bun.OSPathBufferPool.put(src_buf); + const dest_buf = bun.OSPathBufferPool.get(); + defer bun.OSPathBufferPool.put(dest_buf); + const src = strings.toWPathNormalizeAutoExtend(src_buf, args.src.sliceZ(&this.sync_error_buf)); + const dest = strings.toWPathNormalizeAutoExtend(dest_buf, args.dest.sliceZ(&this.sync_error_buf)); if (windows.CopyFileW(src.ptr, dest.ptr, if (args.mode.shouldntOverwrite()) 1 else 0) == windows.FALSE) { if (ret.errnoSysP(0, .copyfile, args.src.slice())) |rest| { return shouldIgnoreEbusy(args.src, args.dest, rest); @@ -3766,11 +3768,12 @@ pub const NodeFS = struct { // TODO: verify this works correctly with unicode codepoints pub fn mkdirRecursiveImpl(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor, comptime Ctx: type, ctx: Ctx) Maybe(Return.Mkdir) { _ = flavor; - var buf: bun.OSPathBuffer = undefined; + const buf = bun.OSPathBufferPool.get(); + defer bun.OSPathBufferPool.put(buf); const path: bun.OSPathSliceZ = if (Environment.isWindows) - strings.toNTPath(&buf, args.path.slice()) + strings.toNTPath(buf, args.path.slice()) else - args.path.osPath(&buf); + args.path.osPath(buf); // TODO: remove and make it always a comptime argument return switch (args.always_return_none) { @@ -5673,7 +5676,8 @@ pub const NodeFS = struct { pub fn osPathIntoSyncErrorBufOverlap(this: *NodeFS, slice: anytype) []const u8 { if (Environment.isWindows) { - var tmp: bun.WPathBuffer = undefined; + const tmp = bun.OSPathBufferPool.get(); + defer bun.OSPathBufferPool.put(tmp); @memcpy(tmp[0..slice.len], slice); return bun.strings.fromWPath(&this.sync_error_buf, tmp[0..slice.len]); } else {} @@ -6184,8 +6188,9 @@ pub const NodeFS = struct { .err => |err| return .{ .err = err }, .result => |src_fd| src_fd, }; - var wbuf: bun.WPathBuffer = undefined; - const len = bun.windows.GetFinalPathNameByHandleW(handle.cast(), &wbuf, wbuf.len, 0); + const wbuf = bun.OSPathBufferPool.get(); + defer bun.OSPathBufferPool.put(wbuf); + const len = bun.windows.GetFinalPathNameByHandleW(handle.cast(), wbuf, wbuf.len, 0); if (len == 0) { return ret.errnoSysP(0, .copyfile, this.osPathIntoSyncErrorBuf(dest)) orelse dst_enoent_maybe; } diff --git a/src/bun.js/node/node_os.bind.ts b/src/bun.js/node/node_os.bind.ts new file mode 100644 index 0000000000..db3082c4b7 --- /dev/null +++ b/src/bun.js/node/node_os.bind.ts @@ -0,0 +1,94 @@ +// Internal bindings for node:os +// The entrypoint for node:os is `src/js/node/os.ts` +import { fn, t } from "bindgen"; + +export const cpus = fn({ + args: { + global: t.globalObject, + }, + ret: t.any, +}); +export const freemem = fn({ + args: {}, + ret: t.u64, +}); +export const getPriority = fn({ + args: { + global: t.globalObject, + pid: t.i32.validateInt32().default(0).nonNull, + }, + ret: t.i32, +}); +export const homedir = fn({ + args: { + global: t.globalObject, + }, + ret: t.DOMString, +}); +export const hostname = fn({ + args: { + global: t.globalObject, + }, + ret: t.any, +}); +export const loadavg = fn({ + args: { + global: t.globalObject, + }, + ret: t.any, +}); +export const networkInterfaces = fn({ + args: { + global: t.globalObject, + }, + ret: t.any, +}); +export const release = fn({ + args: {}, + ret: t.DOMString, +}); +export const totalmem = fn({ + args: {}, + ret: t.u64, +}); +export const uptime = fn({ + args: { + global: t.globalObject, + }, + ret: t.f64, +}); +export const UserInfoOptions = t.dictionary({ + encoding: t.DOMString.default(""), +}); +export const userInfo = fn({ + args: { + global: t.globalObject, + options: UserInfoOptions.default({}), + }, + ret: t.any, +}); +export const version = fn({ + args: {}, + ret: t.DOMString, +}); +const PRI_MIN = -20; +const PRI_MAX = 19; +export const setPriority = fn({ + variants: [ + { + args: { + global: t.globalObject, + pid: t.i32.validateInt32(), + priority: t.i32.validateInt32(PRI_MIN, PRI_MAX), + }, + ret: t.undefined, + }, + { + args: { + global: t.globalObject, + priority: t.i32.validateInt32(PRI_MIN, PRI_MAX), + }, + ret: t.undefined, + }, + ], +}); diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index a7c4da6691..fc60648fee 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -7,856 +7,825 @@ const strings = bun.strings; const JSC = bun.JSC; const Environment = bun.Environment; const Global = bun.Global; -const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false; - const libuv = bun.windows.libuv; -pub const OS = struct { - pub fn create(globalObject: *JSC.JSGlobalObject) JSC.JSValue { - const module = JSC.JSValue.createEmptyObject(globalObject, 16); +const gen = bun.gen.node_os; - module.put(globalObject, JSC.ZigString.static("cpus"), JSC.NewFunction(globalObject, JSC.ZigString.static("cpus"), 0, cpus, false)); - module.put(globalObject, JSC.ZigString.static("freemem"), JSC.NewFunction(globalObject, JSC.ZigString.static("freemem"), 0, freemem, false)); - module.put(globalObject, JSC.ZigString.static("getPriority"), JSC.NewFunction(globalObject, JSC.ZigString.static("getPriority"), 1, getPriority, false)); - module.put(globalObject, JSC.ZigString.static("homedir"), JSC.NewFunction(globalObject, JSC.ZigString.static("homedir"), 0, homedir, false)); - module.put(globalObject, JSC.ZigString.static("hostname"), JSC.NewFunction(globalObject, JSC.ZigString.static("hostname"), 0, hostname, false)); - module.put(globalObject, JSC.ZigString.static("loadavg"), JSC.NewFunction(globalObject, JSC.ZigString.static("loadavg"), 0, loadavg, false)); - module.put(globalObject, JSC.ZigString.static("machine"), JSC.NewFunction(globalObject, JSC.ZigString.static("machine"), 0, machine, false)); - module.put(globalObject, JSC.ZigString.static("networkInterfaces"), JSC.NewFunction(globalObject, JSC.ZigString.static("networkInterfaces"), 0, networkInterfaces, false)); - module.put(globalObject, JSC.ZigString.static("release"), JSC.NewFunction(globalObject, JSC.ZigString.static("release"), 0, release, false)); - module.put(globalObject, JSC.ZigString.static("setPriority"), JSC.NewFunction(globalObject, JSC.ZigString.static("setPriority"), 2, setPriority, false)); - module.put(globalObject, JSC.ZigString.static("totalmem"), JSC.NewFunction(globalObject, JSC.ZigString.static("totalmem"), 0, totalmem, false)); - module.put(globalObject, JSC.ZigString.static("type"), JSC.NewFunction(globalObject, JSC.ZigString.static("type"), 0, OS.type, false)); - module.put(globalObject, JSC.ZigString.static("uptime"), JSC.NewFunction(globalObject, JSC.ZigString.static("uptime"), 0, uptime, false)); - module.put(globalObject, JSC.ZigString.static("userInfo"), JSC.NewFunction(globalObject, JSC.ZigString.static("userInfo"), 0, userInfo, false)); - module.put(globalObject, JSC.ZigString.static("version"), JSC.NewFunction(globalObject, JSC.ZigString.static("version"), 0, version, false)); - module.put(globalObject, JSC.ZigString.static("machine"), JSC.NewFunction(globalObject, JSC.ZigString.static("machine"), 0, machine, false)); +pub fn createNodeOsBinding(global: *JSC.JSGlobalObject) JSC.JSValue { + return JSC.JSObject.create(.{ + .cpus = gen.createCpusCallback(global), + .freemem = gen.createFreememCallback(global), + .getPriority = gen.createGetPriorityCallback(global), + .homedir = gen.createHomedirCallback(global), + .hostname = gen.createHostnameCallback(global), + .loadavg = gen.createLoadavgCallback(global), + .networkInterfaces = gen.createNetworkInterfacesCallback(global), + .release = gen.createReleaseCallback(global), + .totalmem = gen.createTotalmemCallback(global), + .uptime = gen.createUptimeCallback(global), + .userInfo = gen.createUserInfoCallback(global), + .version = gen.createVersionCallback(global), + .setPriority = gen.createSetPriorityCallback(global), + }, global).toJS(); +} - return module; +const CPUTimes = struct { + user: u64 = 0, + nice: u64 = 0, + sys: u64 = 0, + idle: u64 = 0, + irq: u64 = 0, + + pub fn toValue(self: CPUTimes, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + const fields = comptime std.meta.fieldNames(CPUTimes); + const ret = JSC.JSValue.createEmptyObject(globalThis, fields.len); + inline for (fields) |fieldName| { + ret.put(globalThis, JSC.ZigString.static(fieldName), JSC.JSValue.jsNumberFromUint64(@field(self, fieldName))); + } + return ret; + } +}; + +pub fn cpus(global: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { + const cpusImpl = switch (Environment.os) { + .linux => cpusImplLinux, + .mac => cpusImplDarwin, + .windows => cpusImplWindows, + else => @compileError("Unsupported OS"), + }; + + return cpusImpl(global) catch { + const err = JSC.SystemError{ + .message = bun.String.static("Failed to get CPU information"), + .code = bun.String.static(@tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR)), + }; + return global.throwValue(err.toErrorInstance(global)); + }; +} + +fn cpusImplLinux(globalThis: *JSC.JSGlobalObject) !JSC.JSValue { + // Create the return array + const values = JSC.JSValue.createEmptyArray(globalThis, 0); + var num_cpus: u32 = 0; + + var stack_fallback = std.heap.stackFallback(1024 * 8, bun.default_allocator); + var file_buf = std.ArrayList(u8).init(stack_fallback.get()); + defer file_buf.deinit(); + + // Read /proc/stat to get number of CPUs and times + { + const file = try std.fs.openFileAbsolute("/proc/stat", .{}); + defer file.close(); + + const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap(); + defer file_buf.clearRetainingCapacity(); + const contents = file_buf.items[0..read]; + + var line_iter = std.mem.tokenizeScalar(u8, contents, '\n'); + + // Skip the first line (aggregate of all CPUs) + _ = line_iter.next(); + + // Read each CPU line + while (line_iter.next()) |line| { + // CPU lines are formatted as `cpu0 user nice sys idle iowait irq softirq` + var toks = std.mem.tokenize(u8, line, " \t"); + const cpu_name = toks.next(); + if (cpu_name == null or !std.mem.startsWith(u8, cpu_name.?, "cpu")) break; // done with CPUs + + //NOTE: libuv assumes this is fixed on Linux, not sure that's actually the case + const scale = 10; + + var times = CPUTimes{}; + times.user = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); + times.nice = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); + times.sys = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); + times.idle = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); + _ = try (toks.next() orelse error.eol); // skip iowait + times.irq = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); + + // Actually create the JS object representing the CPU + const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); + cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); + values.putIndex(globalThis, num_cpus, cpu); + + num_cpus += 1; + } } - const CPUTimes = struct { - user: u64 = 0, - nice: u64 = 0, - sys: u64 = 0, - idle: u64 = 0, - irq: u64 = 0, + // Read /proc/cpuinfo to get model information (optional) + if (std.fs.openFileAbsolute("/proc/cpuinfo", .{})) |file| { + defer file.close(); - pub fn toValue(self: CPUTimes, globalThis: *JSC.JSGlobalObject) JSC.JSValue { - const fields = comptime std.meta.fieldNames(CPUTimes); - const ret = JSC.JSValue.createEmptyObject(globalThis, fields.len); - inline for (fields) |fieldName| { - ret.put(globalThis, JSC.ZigString.static(fieldName), JSC.JSValue.jsNumberFromUint64(@field(self, fieldName))); + const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap(); + defer file_buf.clearRetainingCapacity(); + const contents = file_buf.items[0..read]; + + var line_iter = std.mem.tokenizeScalar(u8, contents, '\n'); + + const key_processor = "processor\t: "; + const key_model_name = "model name\t: "; + + var cpu_index: u32 = 0; + var has_model_name = true; + while (line_iter.next()) |line| { + if (strings.hasPrefixComptime(line, key_processor)) { + if (!has_model_name) { + const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); + } + // If this line starts a new processor, parse the index from the line + const digits = std.mem.trim(u8, line[key_processor.len..], " \t\n"); + cpu_index = try std.fmt.parseInt(u32, digits, 10); + if (cpu_index >= num_cpus) return error.too_may_cpus; + has_model_name = false; + } else if (strings.hasPrefixComptime(line, key_model_name)) { + // If this is the model name, extract it and store on the current cpu + const model_name = line[key_model_name.len..]; + const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(model_name).withEncoding().toJS(globalThis)); + has_model_name = true; } - return ret; + } + if (!has_model_name) { + const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); + } + } else |_| { + // Initialize model name to "unknown" + var it = values.arrayIterator(globalThis); + while (it.next()) |cpu| { + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); + } + } + + // Read /sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq to get current frequency (optional) + for (0..num_cpus) |cpu_index| { + const cpu = JSC.JSObject.getIndex(values, globalThis, @truncate(cpu_index)); + + var path_buf: [128]u8 = undefined; + const path = try std.fmt.bufPrint(&path_buf, "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", .{cpu_index}); + if (std.fs.openFileAbsolute(path, .{})) |file| { + defer file.close(); + + const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap(); + defer file_buf.clearRetainingCapacity(); + const contents = file_buf.items[0..read]; + + const digits = std.mem.trim(u8, contents, " \n"); + const speed = (std.fmt.parseInt(u64, digits, 10) catch 0) / 1000; + + cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(speed)); + } else |_| { + // Initialize CPU speed to 0 + cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(0)); + } + } + + return values; +} + +extern fn bun_sysconf__SC_CLK_TCK() isize; +fn cpusImplDarwin(globalThis: *JSC.JSGlobalObject) !JSC.JSValue { + const local_bindings = @import("../../darwin_c.zig"); + const c = std.c; + + // Fetch the CPU info structure + var num_cpus: c.natural_t = 0; + var info: [*]local_bindings.processor_cpu_load_info = undefined; + var info_size: std.c.mach_msg_type_number_t = 0; + if (local_bindings.host_processor_info(std.c.mach_host_self(), local_bindings.PROCESSOR_CPU_LOAD_INFO, &num_cpus, @as(*local_bindings.processor_info_array_t, @ptrCast(&info)), &info_size) != .SUCCESS) { + return error.no_processor_info; + } + defer _ = std.c.vm_deallocate(std.c.mach_task_self(), @intFromPtr(info), info_size); + + // Ensure we got the amount of data we expected to guard against buffer overruns + if (info_size != C.PROCESSOR_CPU_LOAD_INFO_COUNT * num_cpus) { + return error.broken_process_info; + } + + // Get CPU model name + var model_name_buf: [512]u8 = undefined; + var len: usize = model_name_buf.len; + // Try brand_string first and if it fails try hw.model + if (!(std.c.sysctlbyname("machdep.cpu.brand_string", &model_name_buf, &len, null, 0) == 0 or + std.c.sysctlbyname("hw.model", &model_name_buf, &len, null, 0) == 0)) + { + return error.no_processor_info; + } + // NOTE: sysctlbyname doesn't update len if it was large enough, so we + // still have to find the null terminator. All cpus can share the same + // model name. + const model_name = JSC.ZigString.init(std.mem.sliceTo(&model_name_buf, 0)).withEncoding().toJS(globalThis); + + // Get CPU speed + var speed: u64 = 0; + len = @sizeOf(@TypeOf(speed)); + _ = std.c.sysctlbyname("hw.cpufrequency", &speed, &len, null, 0); + if (speed == 0) { + // Suggested by Node implementation: + // If sysctl hw.cputype == CPU_TYPE_ARM64, the correct value is unavailable + // from Apple, but we can hard-code it here to a plausible value. + speed = 2_400_000_000; + } + + // Get the multiplier; this is the number of ms/tick + const ticks: i64 = bun_sysconf__SC_CLK_TCK(); + const multiplier = 1000 / @as(u64, @intCast(ticks)); + + // Set up each CPU value in the return + const values = JSC.JSValue.createEmptyArray(globalThis, @as(u32, @intCast(num_cpus))); + var cpu_index: u32 = 0; + while (cpu_index < num_cpus) : (cpu_index += 1) { + const times = CPUTimes{ + .user = info[cpu_index].cpu_ticks[0] * multiplier, + .nice = info[cpu_index].cpu_ticks[3] * multiplier, + .sys = info[cpu_index].cpu_ticks[1] * multiplier, + .idle = info[cpu_index].cpu_ticks[2] * multiplier, + .irq = 0, // not available + }; + + const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); + cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(speed / 1_000_000)); + cpu.put(globalThis, JSC.ZigString.static("model"), model_name); + cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); + + values.putIndex(globalThis, cpu_index, cpu); + } + return values; +} + +pub fn cpusImplWindows(globalThis: *JSC.JSGlobalObject) !JSC.JSValue { + var cpu_infos: [*]libuv.uv_cpu_info_t = undefined; + var count: c_int = undefined; + const err = libuv.uv_cpu_info(&cpu_infos, &count); + if (err != 0) { + return error.NoProcessorInfo; + } + defer libuv.uv_free_cpu_info(cpu_infos, count); + + const values = JSC.JSValue.createEmptyArray(globalThis, @intCast(count)); + + for (cpu_infos[0..@intCast(count)], 0..@intCast(count)) |cpu_info, i| { + const times = CPUTimes{ + .user = cpu_info.cpu_times.user, + .nice = cpu_info.cpu_times.nice, + .sys = cpu_info.cpu_times.sys, + .idle = cpu_info.cpu_times.idle, + .irq = cpu_info.cpu_times.irq, + }; + + const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); + cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(bun.span(cpu_info.model)).withEncoding().toJS(globalThis)); + cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(cpu_info.speed)); + cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); + + values.putIndex(globalThis, @intCast(i), cpu); + } + + return values; +} + +pub fn freemem() u64 { + return C.getFreeMemory(); +} + +pub fn getPriority(global: *JSC.JSGlobalObject, pid: i32) bun.JSError!i32 { + return C.getProcessPriority(pid) orelse { + const err = JSC.SystemError{ + .message = bun.String.static("no such process"), + .code = bun.String.static("ESRCH"), + .errno = comptime switch (bun.Environment.os) { + else => -@as(c_int, @intFromEnum(std.posix.E.SRCH)), + .windows => libuv.UV_ESRCH, + }, + .syscall = bun.String.static("uv_os_getpriority"), + }; + return global.throwValue(err.toErrorInstanceWithInfoObject(global)); + }; +} + +pub fn homedir(global: *JSC.JSGlobalObject) !bun.String { + // In Node.js, this is a wrapper around uv_os_homedir. + if (Environment.isWindows) { + var out: bun.PathBuffer = undefined; + var size: usize = out.len; + if (libuv.uv_os_homedir(&out, &size).toError(.uv_os_homedir)) |err| { + return global.throwValue(err.toJSC(global)); + } + return bun.String.createUTF8(out[0..size]); + } else { + + // The posix implementation of uv_os_homedir first checks the HOME + // environment variable, then falls back to reading the passwd entry. + if (bun.getenvZ("HOME")) |home| { + if (home.len > 0) + return bun.String.init(home); + } + + // From libuv: + // > Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it + // > is frequently 1024 or 4096, so we can just use that directly. The pwent + // > will not usually be large. + // Instead of always using an allocation, first try a stack allocation + // of 4096, then fallback to heap. + var stack_string_bytes: [4096]u8 = undefined; + var string_bytes: []u8 = &stack_string_bytes; + defer if (string_bytes.ptr != &stack_string_bytes) + bun.default_allocator.free(string_bytes); + + var pw: bun.C.passwd = undefined; + var result: ?*bun.C.passwd = null; + + const ret = while (true) { + const ret = bun.C.getpwuid_r( + bun.C.geteuid(), + &pw, + string_bytes.ptr, + string_bytes.len, + &result, + ); + + if (ret == @intFromEnum(bun.C.E.INTR)) + continue; + + // If the system call wants more memory, double it. + if (ret == @intFromEnum(bun.C.E.RANGE)) { + const len = string_bytes.len; + bun.default_allocator.free(string_bytes); + string_bytes = ""; + string_bytes = try bun.default_allocator.alloc(u8, len * 2); + continue; + } + + break ret; + }; + + if (ret != 0) { + return global.throwValue(bun.sys.Error.fromCode( + @enumFromInt(ret), + .uv_os_homedir, + ).toJSC(global)); + } + + if (result == null) { + // in uv__getpwuid_r, null result throws UV_ENOENT. + return global.throwValue(bun.sys.Error.fromCode( + .NOENT, + .uv_os_homedir, + ).toJSC(global)); + } + + return if (pw.pw_dir) |dir| + bun.String.createUTF8(bun.span(dir)) + else + bun.String.empty; + } +} + +pub fn hostname(global: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { + if (Environment.isWindows) { + var name_buffer: [129:0]u16 = undefined; + if (bun.windows.GetHostNameW(&name_buffer, name_buffer.len) == 0) { + const str = bun.String.createUTF16(bun.sliceTo(&name_buffer, 0)); + defer str.deref(); + return str.toJS(global); + } + + var result: std.os.windows.ws2_32.WSADATA = undefined; + if (std.os.windows.ws2_32.WSAStartup(0x202, &result) == 0) { + if (bun.windows.GetHostNameW(&name_buffer, name_buffer.len) == 0) { + var y = bun.String.createUTF16(bun.sliceTo(&name_buffer, 0)); + defer y.deref(); + return y.toJS(global); + } + } + + return JSC.ZigString.init("unknown").withEncoding().toJS(global); + } else { + var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; + return JSC.ZigString.init(std.posix.gethostname(&name_buffer) catch "unknown").withEncoding().toJS(global); + } +} + +pub fn loadavg(global: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { + const result = C.getSystemLoadavg(); + return JSC.JSArray.create(global, &.{ + JSC.JSValue.jsNumber(result[0]), + JSC.JSValue.jsNumber(result[1]), + JSC.JSValue.jsNumber(result[2]), + }); +} + +pub const networkInterfaces = switch (Environment.os) { + .linux, .mac => networkInterfacesPosix, + .windows => networkInterfacesWindows, + else => @compileError("Unsupported OS"), +}; + +fn networkInterfacesPosix(globalThis: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { + // getifaddrs sets a pointer to a linked list + var interface_start: ?*C.ifaddrs = null; + const rc = C.getifaddrs(&interface_start); + if (rc != 0) { + const err = JSC.SystemError{ + .message = bun.String.static("A system error occurred: getifaddrs returned an error"), + .code = bun.String.static("ERR_SYSTEM_ERROR"), + .errno = @intFromEnum(std.posix.errno(rc)), + .syscall = bun.String.static("getifaddrs"), + }; + + return globalThis.throwValue(err.toErrorInstance(globalThis)); + } + defer C.freeifaddrs(interface_start); + + const helpers = struct { + // We'll skip interfaces that aren't actually available + pub fn skip(iface: *C.ifaddrs) bool { + // Skip interfaces that aren't actually available + if (iface.ifa_flags & C.IFF_RUNNING == 0) return true; + if (iface.ifa_flags & C.IFF_UP == 0) return true; + if (iface.ifa_addr == null) return true; + + return false; + } + + // We won't actually return link-layer interfaces but we need them for + // extracting the MAC address + pub fn isLinkLayer(iface: *C.ifaddrs) bool { + if (iface.ifa_addr == null) return false; + return if (comptime Environment.isLinux) + return iface.ifa_addr.*.sa_family == std.posix.AF.PACKET + else if (comptime Environment.isMac) + return iface.ifa_addr.?.*.family == std.posix.AF.LINK + else + unreachable; + } + + pub fn isLoopback(iface: *C.ifaddrs) bool { + return iface.ifa_flags & C.IFF_LOOPBACK == C.IFF_LOOPBACK; } }; - pub fn cpus(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - return switch (Environment.os) { - .linux => cpusImplLinux(globalThis), - .mac => cpusImplDarwin(globalThis), - .windows => cpusImplWindows(globalThis), - else => @compileError("unsupported OS"), - } catch { - const err = JSC.SystemError{ - .message = bun.String.static("Failed to get cpu information"), - .code = bun.String.static(@tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR)), - }; - - return globalThis.throwValue(err.toErrorInstance(globalThis)); - }; + // The list currently contains entries for link-layer interfaces + // and the IPv4, IPv6 interfaces. We only want to return the latter two + // but need the link-layer entries to determine MAC address. + // So, on our first pass through the linked list we'll count the number of + // INET interfaces only. + var num_inet_interfaces: usize = 0; + var it = interface_start; + while (it) |iface| : (it = iface.ifa_next) { + if (helpers.skip(iface) or helpers.isLinkLayer(iface)) continue; + num_inet_interfaces += 1; } - fn cpusImplLinux(globalThis: *JSC.JSGlobalObject) !JSC.JSValue { - // Create the return array - const values = JSC.JSValue.createEmptyArray(globalThis, 0); - var num_cpus: u32 = 0; + var ret = JSC.JSValue.createEmptyObject(globalThis, 8); - var stack_fallback = std.heap.stackFallback(1024 * 8, bun.default_allocator); - var file_buf = std.ArrayList(u8).init(stack_fallback.get()); - defer file_buf.deinit(); + // Second pass through, populate each interface object + it = interface_start; + while (it) |iface| : (it = iface.ifa_next) { + if (helpers.skip(iface) or helpers.isLinkLayer(iface)) continue; - // Read /proc/stat to get number of CPUs and times - if (std.fs.openFileAbsolute("/proc/stat", .{})) |file| { - defer file.close(); + const interface_name = std.mem.sliceTo(iface.ifa_name, 0); + const addr = std.net.Address.initPosix(@alignCast(@as(*std.posix.sockaddr, @ptrCast(iface.ifa_addr)))); + const netmask = std.net.Address.initPosix(@alignCast(@as(*std.posix.sockaddr, @ptrCast(iface.ifa_netmask)))); - const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf).unwrap(); - defer file_buf.clearRetainingCapacity(); - const contents = file_buf.items[0..read]; + var interface = JSC.JSValue.createEmptyObject(globalThis, 7); - var line_iter = std.mem.tokenizeScalar(u8, contents, '\n'); - - // Skip the first line (aggregate of all CPUs) - _ = line_iter.next(); - - // Read each CPU line - while (line_iter.next()) |line| { - // CPU lines are formatted as `cpu0 user nice sys idle iowait irq softirq` - var toks = std.mem.tokenize(u8, line, " \t"); - const cpu_name = toks.next(); - if (cpu_name == null or !std.mem.startsWith(u8, cpu_name.?, "cpu")) break; // done with CPUs - - //NOTE: libuv assumes this is fixed on Linux, not sure that's actually the case - const scale = 10; - - var times = CPUTimes{}; - times.user = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); - times.nice = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); - times.sys = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); - times.idle = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); - _ = try (toks.next() orelse error.eol); // skip iowait - times.irq = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10); - - // Actually create the JS object representing the CPU - const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); - cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); - values.putIndex(globalThis, num_cpus, cpu); - - num_cpus += 1; - } - } else |_| { - return error.no_proc_stat; - } - - // Read /proc/cpuinfo to get model information (optional) - if (std.fs.openFileAbsolute("/proc/cpuinfo", .{})) |file| { - defer file.close(); - - const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf).unwrap(); - defer file_buf.clearRetainingCapacity(); - const contents = file_buf.items[0..read]; - - var line_iter = std.mem.tokenizeScalar(u8, contents, '\n'); - - const key_processor = "processor\t: "; - const key_model_name = "model name\t: "; - - var cpu_index: u32 = 0; - var has_model_name = true; - while (line_iter.next()) |line| { - if (strings.hasPrefixComptime(line, key_processor)) { - if (!has_model_name) { - const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); - } - // If this line starts a new processor, parse the index from the line - const digits = std.mem.trim(u8, line[key_processor.len..], " \t\n"); - cpu_index = try std.fmt.parseInt(u32, digits, 10); - if (cpu_index >= num_cpus) return error.too_may_cpus; - has_model_name = false; - } else if (strings.hasPrefixComptime(line, key_model_name)) { - // If this is the model name, extract it and store on the current cpu - const model_name = line[key_model_name.len..]; - const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(model_name).withEncoding().toJS(globalThis)); - has_model_name = true; - } - } - if (!has_model_name) { - const cpu = JSC.JSObject.getIndex(values, globalThis, cpu_index); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); - } - } else |_| { - // Initialize model name to "unknown" - var it = values.arrayIterator(globalThis); - while (it.next()) |cpu| { - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.static("unknown").withEncoding().toJS(globalThis)); - } - } - - // Read /sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq to get current frequency (optional) - for (0..num_cpus) |cpu_index| { - const cpu = JSC.JSObject.getIndex(values, globalThis, @truncate(cpu_index)); - - var path_buf: [128]u8 = undefined; - const path = try std.fmt.bufPrint(&path_buf, "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", .{cpu_index}); - if (std.fs.openFileAbsolute(path, .{})) |file| { - defer file.close(); - - const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf).unwrap(); - defer file_buf.clearRetainingCapacity(); - const contents = file_buf.items[0..read]; - - const digits = std.mem.trim(u8, contents, " \n"); - const speed = (std.fmt.parseInt(u64, digits, 10) catch 0) / 1000; - - cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(speed)); - } else |_| { - // Initialize CPU speed to 0 - cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(0)); - } - } - - return values; - } - - extern fn bun_sysconf__SC_CLK_TCK() isize; - fn cpusImplDarwin(globalThis: *JSC.JSGlobalObject) !JSC.JSValue { - const local_bindings = @import("../../darwin_c.zig"); - const c = std.c; - - // Fetch the CPU info structure - var num_cpus: c.natural_t = 0; - var info: [*]local_bindings.processor_cpu_load_info = undefined; - var info_size: std.c.mach_msg_type_number_t = 0; - if (local_bindings.host_processor_info(std.c.mach_host_self(), local_bindings.PROCESSOR_CPU_LOAD_INFO, &num_cpus, @as(*local_bindings.processor_info_array_t, @ptrCast(&info)), &info_size) != .SUCCESS) { - return error.no_processor_info; - } - defer _ = std.c.vm_deallocate(std.c.mach_task_self(), @intFromPtr(info), info_size); - - // Ensure we got the amount of data we expected to guard against buffer overruns - if (info_size != C.PROCESSOR_CPU_LOAD_INFO_COUNT * num_cpus) { - return error.broken_process_info; - } - - // Get CPU model name - var model_name_buf: [512]u8 = undefined; - var len: usize = model_name_buf.len; - // Try brand_string first and if it fails try hw.model - if (!(std.c.sysctlbyname("machdep.cpu.brand_string", &model_name_buf, &len, null, 0) == 0 or - std.c.sysctlbyname("hw.model", &model_name_buf, &len, null, 0) == 0)) + // address The assigned IPv4 or IPv6 address + // cidr The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. { - return error.no_processor_info; - } - //NOTE: sysctlbyname doesn't update len if it was large enough, so we - // still have to find the null terminator. All cpus can share the same - // model name. - const model_name = JSC.ZigString.init(std.mem.sliceTo(&model_name_buf, 0)).withEncoding().toJS(globalThis); - - // Get CPU speed - var speed: u64 = 0; - len = @sizeOf(@TypeOf(speed)); - _ = std.c.sysctlbyname("hw.cpufrequency", &speed, &len, null, 0); - if (speed == 0) { - // Suggested by Node implementation: - // If sysctl hw.cputype == CPU_TYPE_ARM64, the correct value is unavailable - // from Apple, but we can hard-code it here to a plausible value. - speed = 2_400_000_000; - } - - // Get the multiplier; this is the number of ms/tick - const ticks: i64 = bun_sysconf__SC_CLK_TCK(); - const multiplier = 1000 / @as(u64, @intCast(ticks)); - - // Set up each CPU value in the return - const values = JSC.JSValue.createEmptyArray(globalThis, @as(u32, @intCast(num_cpus))); - var cpu_index: u32 = 0; - while (cpu_index < num_cpus) : (cpu_index += 1) { - const times = CPUTimes{ - .user = info[cpu_index].cpu_ticks[0] * multiplier, - .nice = info[cpu_index].cpu_ticks[3] * multiplier, - .sys = info[cpu_index].cpu_ticks[1] * multiplier, - .idle = info[cpu_index].cpu_ticks[2] * multiplier, - .irq = 0, // not available + // Compute the CIDR suffix; returns null if the netmask cannot + // be converted to a CIDR suffix + const maybe_suffix: ?u8 = switch (addr.any.family) { + std.posix.AF.INET => netmaskToCIDRSuffix(netmask.in.sa.addr), + std.posix.AF.INET6 => netmaskToCIDRSuffix(@as(u128, @bitCast(netmask.in6.sa.addr))), + else => null, }; - const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); - cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(speed / 1_000_000)); - cpu.put(globalThis, JSC.ZigString.static("model"), model_name); - cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); - - values.putIndex(globalThis, cpu_index, cpu); - } - return values; - } - - pub fn cpusImplWindows(globalThis: *JSC.JSGlobalObject) !JSC.JSValue { - var cpu_infos: [*]libuv.uv_cpu_info_t = undefined; - var count: c_int = undefined; - const err = libuv.uv_cpu_info(&cpu_infos, &count); - if (err != 0) { - return error.no_processor_info; - } - defer libuv.uv_free_cpu_info(cpu_infos, count); - - const values = JSC.JSValue.createEmptyArray(globalThis, 0); - - for (cpu_infos[0..@intCast(count)], 0..@intCast(count)) |cpu_info, i| { - const times = CPUTimes{ - .user = cpu_info.cpu_times.user, - .nice = cpu_info.cpu_times.nice, - .sys = cpu_info.cpu_times.sys, - .idle = cpu_info.cpu_times.idle, - .irq = cpu_info.cpu_times.irq, - }; - - const cpu = JSC.JSValue.createEmptyObject(globalThis, 3); - cpu.put(globalThis, JSC.ZigString.static("model"), JSC.ZigString.init(bun.span(cpu_info.model)).withEncoding().toJS(globalThis)); - cpu.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumber(cpu_info.speed)); - cpu.put(globalThis, JSC.ZigString.static("times"), times.toValue(globalThis)); - - values.putIndex(globalThis, @intCast(i), cpu); - } - - return values; - } - - pub fn endianness(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - return JSC.ZigString.init("LE").withEncoding().toJS(globalThis); - } - - pub fn freemem(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - return JSC.JSValue.jsNumberFromUint64(C.getFreeMemory()); - } - - pub fn getPriority(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - var args_ = callframe.arguments_old(1); - const arguments: []const JSC.JSValue = args_.ptr[0..args_.len]; - - if (arguments.len > 0 and !arguments[0].isNumber()) { - return globalThis.ERR_INVALID_ARG_TYPE("getPriority() expects a number", .{}).throw(); - } - - const pid = if (arguments.len > 0) arguments[0].asInt32() else 0; - - const priority = C.getProcessPriority(pid); - if (priority == -1) { - //const info = JSC.JSValue.createEmptyObject(globalThis, 4); - //info.put(globalThis, JSC.ZigString.static("errno"), JSC.JSValue.jsNumberFromInt32(-3)); - //info.put(globalThis, JSC.ZigString.static("code"), JSC.ZigString.init("ESRCH").withEncoding().toValueGC(globalThis)); - //info.put(globalThis, JSC.ZigString.static("message"), JSC.ZigString.init("no such process").withEncoding().toValueGC(globalThis)); - //info.put(globalThis, JSC.ZigString.static("syscall"), JSC.ZigString.init("uv_os_getpriority").withEncoding().toValueGC(globalThis)); - - const err = JSC.SystemError{ - .message = bun.String.static("A system error occurred: uv_os_getpriority returned ESRCH (no such process)"), - .code = bun.String.static("ERR_SYSTEM_ERROR"), - //.info = info, - .errno = -3, - .syscall = bun.String.static("uv_os_getpriority"), - }; - - return globalThis.throwValue(err.toErrorInstance(globalThis)); - } - - return JSC.JSValue.jsNumberFromInt32(priority); - } - - pub fn homedir(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - const dir: []const u8 = brk: { - if (comptime Environment.isWindows) { - if (bun.getenvZ("USERPROFILE")) |env| - break :brk bun.asByteSlice(env); - } else { - if (bun.getenvZ("HOME")) |env| - break :brk bun.asByteSlice(env); + // Format the address and then, if valid, the CIDR suffix; both + // the address and cidr values can be slices into this same buffer + // e.g. addr_str = "192.168.88.254", cidr_str = "192.168.88.254/24" + var buf: [64]u8 = undefined; + const addr_str = bun.fmt.formatIp(addr, &buf) catch unreachable; + var cidr = JSC.JSValue.null; + if (maybe_suffix) |suffix| { + //NOTE addr_str might not start at buf[0] due to slicing in formatIp + const start = @intFromPtr(addr_str.ptr) - @intFromPtr(&buf[0]); + // Start writing the suffix immediately after the address + const suffix_str = std.fmt.bufPrint(buf[start + addr_str.len ..], "/{}", .{suffix}) catch unreachable; + // The full cidr value is the address + the suffix + const cidr_str = buf[start .. start + addr_str.len + suffix_str.len]; + cidr = JSC.ZigString.init(cidr_str).withEncoding().toJS(globalThis); } - break :brk "unknown"; - }; - - return JSC.ZigString.init(dir).withEncoding().toJS(globalThis); - } - - pub fn hostname(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - if (comptime Environment.isWindows) { - var name_buffer: [129:0]u16 = undefined; - if (bun.windows.GetHostNameW(&name_buffer, name_buffer.len) == 0) { - const str = bun.String.createUTF16(bun.sliceTo(&name_buffer, 0)); - defer str.deref(); - return str.toJS(globalThis); - } - - var result: std.os.windows.ws2_32.WSADATA = undefined; - if (std.os.windows.ws2_32.WSAStartup(0x202, &result) == 0) { - if (bun.windows.GetHostNameW(&name_buffer, name_buffer.len) == 0) { - const str = bun.String.createUTF16(bun.sliceTo(&name_buffer, 0)); - defer str.deref(); - return str.toJS(globalThis); - } - } - - return JSC.ZigString.init("unknown").withEncoding().toJS(globalThis); + interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toJS(globalThis)); + interface.put(globalThis, JSC.ZigString.static("cidr"), cidr); } - var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; - - return JSC.ZigString.init(std.posix.gethostname(&name_buffer) catch "unknown").withEncoding().toJS(globalThis); - } - - pub fn loadavg(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - const result = C.getSystemLoadavg(); - return JSC.JSArray.create(globalThis, &.{ - JSC.JSValue.jsNumber(result[0]), - JSC.JSValue.jsNumber(result[1]), - JSC.JSValue.jsNumber(result[2]), - }); - } - - pub fn networkInterfaces(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - return switch (Environment.os) { - .windows => networkInterfacesWindows(globalThis), - else => networkInterfacesPosix(globalThis), - }; - } - - fn networkInterfacesPosix(globalThis: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { - // getifaddrs sets a pointer to a linked list - var interface_start: ?*C.ifaddrs = null; - const rc = C.getifaddrs(&interface_start); - if (rc != 0) { - const err = JSC.SystemError{ - .message = bun.String.static("A system error occurred: getifaddrs returned an error"), - .code = bun.String.static("ERR_SYSTEM_ERROR"), - .errno = @intFromEnum(std.posix.errno(rc)), - .syscall = bun.String.static("getifaddrs"), - }; - - return globalThis.throwValue(err.toErrorInstance(globalThis)); - } - defer C.freeifaddrs(interface_start); - - const helpers = struct { - // We'll skip interfaces that aren't actually available - pub fn skip(iface: *C.ifaddrs) bool { - // Skip interfaces that aren't actually available - if (iface.ifa_flags & C.IFF_RUNNING == 0) return true; - if (iface.ifa_flags & C.IFF_UP == 0) return true; - if (iface.ifa_addr == null) return true; - - return false; - } - - // We won't actually return link-layer interfaces but we need them for - // extracting the MAC address - pub fn isLinkLayer(iface: *C.ifaddrs) bool { - if (iface.ifa_addr == null) return false; - return if (comptime Environment.isLinux) - return iface.ifa_addr.*.sa_family == std.posix.AF.PACKET - else if (comptime Environment.isMac) - return iface.ifa_addr.?.*.family == std.posix.AF.LINK - else - unreachable; - } - - pub fn isLoopback(iface: *C.ifaddrs) bool { - return iface.ifa_flags & C.IFF_LOOPBACK == C.IFF_LOOPBACK; - } - }; - - // The list currently contains entries for link-layer interfaces - // and the IPv4, IPv6 interfaces. We only want to return the latter two - // but need the link-layer entries to determine MAC address. - // So, on our first pass through the linked list we'll count the number of - // INET interfaces only. - var num_inet_interfaces: usize = 0; - var it = interface_start; - while (it) |iface| : (it = iface.ifa_next) { - if (helpers.skip(iface) or helpers.isLinkLayer(iface)) continue; - num_inet_interfaces += 1; + // netmask The IPv4 or IPv6 network mask + { + var buf: [64]u8 = undefined; + const str = bun.fmt.formatIp(netmask, &buf) catch unreachable; + interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toJS(globalThis)); } - var ret = JSC.JSValue.createEmptyObject(globalThis, 8); + // family Either IPv4 or IPv6 + interface.put(globalThis, JSC.ZigString.static("family"), (switch (addr.any.family) { + std.posix.AF.INET => JSC.ZigString.static("IPv4"), + std.posix.AF.INET6 => JSC.ZigString.static("IPv6"), + else => JSC.ZigString.static("unknown"), + }).toJS(globalThis)); - // Second pass through, populate each interface object - it = interface_start; - while (it) |iface| : (it = iface.ifa_next) { - if (helpers.skip(iface) or helpers.isLinkLayer(iface)) continue; + // mac The MAC address of the network interface + { + // We need to search for the link-layer interface whose name matches this one + var ll_it = interface_start; + const maybe_ll_addr = while (ll_it) |ll_iface| : (ll_it = ll_iface.ifa_next) { + if (helpers.skip(ll_iface) or !helpers.isLinkLayer(ll_iface)) continue; - const interface_name = std.mem.sliceTo(iface.ifa_name, 0); - const addr = std.net.Address.initPosix(@alignCast(@as(*std.posix.sockaddr, @ptrCast(iface.ifa_addr)))); - const netmask = std.net.Address.initPosix(@alignCast(@as(*std.posix.sockaddr, @ptrCast(iface.ifa_netmask)))); + const ll_name = bun.sliceTo(ll_iface.ifa_name, 0); + if (!strings.hasPrefix(ll_name, interface_name)) continue; + if (ll_name.len > interface_name.len and ll_name[interface_name.len] != ':') continue; - var interface = JSC.JSValue.createEmptyObject(globalThis, 7); - - // address The assigned IPv4 or IPv6 address - // cidr The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. - { - // Compute the CIDR suffix; returns null if the netmask cannot - // be converted to a CIDR suffix - const maybe_suffix: ?u8 = switch (addr.any.family) { - std.posix.AF.INET => netmaskToCIDRSuffix(netmask.in.sa.addr), - std.posix.AF.INET6 => netmaskToCIDRSuffix(@as(u128, @bitCast(netmask.in6.sa.addr))), - else => null, - }; - - // Format the address and then, if valid, the CIDR suffix; both - // the address and cidr values can be slices into this same buffer - // e.g. addr_str = "192.168.88.254", cidr_str = "192.168.88.254/24" - var buf: [64]u8 = undefined; - const addr_str = bun.fmt.formatIp(addr, &buf) catch unreachable; - var cidr = JSC.JSValue.null; - if (maybe_suffix) |suffix| { - //NOTE addr_str might not start at buf[0] due to slicing in formatIp - const start = @intFromPtr(addr_str.ptr) - @intFromPtr(&buf[0]); - // Start writing the suffix immediately after the address - const suffix_str = std.fmt.bufPrint(buf[start + addr_str.len ..], "/{}", .{suffix}) catch unreachable; - // The full cidr value is the address + the suffix - const cidr_str = buf[start .. start + addr_str.len + suffix_str.len]; - cidr = JSC.ZigString.init(cidr_str).withEncoding().toJS(globalThis); - } - - interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toJS(globalThis)); - interface.put(globalThis, JSC.ZigString.static("cidr"), cidr); - } - - // netmask The IPv4 or IPv6 network mask - { - var buf: [64]u8 = undefined; - const str = bun.fmt.formatIp(netmask, &buf) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toJS(globalThis)); - } - - // family Either IPv4 or IPv6 - interface.put(globalThis, JSC.ZigString.static("family"), (switch (addr.any.family) { - std.posix.AF.INET => JSC.ZigString.static("IPv4"), - std.posix.AF.INET6 => JSC.ZigString.static("IPv6"), - else => JSC.ZigString.static("unknown"), - }).toJS(globalThis)); - - // mac The MAC address of the network interface - { - // We need to search for the link-layer interface whose name matches this one - var ll_it = interface_start; - const maybe_ll_addr = while (ll_it) |ll_iface| : (ll_it = ll_iface.ifa_next) { - if (helpers.skip(ll_iface) or !helpers.isLinkLayer(ll_iface)) continue; - - const ll_name = bun.sliceTo(ll_iface.ifa_name, 0); - if (!strings.hasPrefix(ll_name, interface_name)) continue; - if (ll_name.len > interface_name.len and ll_name[interface_name.len] != ':') continue; - - // This is the correct link-layer interface entry for the current interface, - // cast to a link-layer socket address - if (comptime Environment.isLinux) { - break @as(?*std.posix.sockaddr.ll, @ptrCast(@alignCast(ll_iface.ifa_addr))); - } else if (comptime Environment.isMac) { - break @as(?*C.sockaddr_dl, @ptrCast(@alignCast(ll_iface.ifa_addr))); - } else { - @compileError("unreachable"); - } - } else null; - - if (maybe_ll_addr) |ll_addr| { - // Encode its link-layer address. We need 2*6 bytes for the - // hex characters and 5 for the colon separators - var mac_buf: [17]u8 = undefined; - const addr_data = if (comptime Environment.isLinux) ll_addr.addr else if (comptime Environment.isMac) ll_addr.sdl_data[ll_addr.sdl_nlen..] else @compileError("unreachable"); - if (addr_data.len < 6) { - const mac = "00:00:00:00:00:00"; - interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); - } else { - const mac = std.fmt.bufPrint(&mac_buf, "{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}", .{ - addr_data[0], addr_data[1], addr_data[2], - addr_data[3], addr_data[4], addr_data[5], - }) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); - } + // This is the correct link-layer interface entry for the current interface, + // cast to a link-layer socket address + if (comptime Environment.isLinux) { + break @as(?*std.posix.sockaddr.ll, @ptrCast(@alignCast(ll_iface.ifa_addr))); + } else if (comptime Environment.isMac) { + break @as(?*C.sockaddr_dl, @ptrCast(@alignCast(ll_iface.ifa_addr))); } else { + @compileError("unreachable"); + } + } else null; + + if (maybe_ll_addr) |ll_addr| { + // Encode its link-layer address. We need 2*6 bytes for the + // hex characters and 5 for the colon separators + var mac_buf: [17]u8 = undefined; + const addr_data = if (comptime Environment.isLinux) ll_addr.addr else if (comptime Environment.isMac) ll_addr.sdl_data[ll_addr.sdl_nlen..] else @compileError("unreachable"); + if (addr_data.len < 6) { const mac = "00:00:00:00:00:00"; interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); + } else { + const mac = std.fmt.bufPrint(&mac_buf, "{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}", .{ + addr_data[0], addr_data[1], addr_data[2], + addr_data[3], addr_data[4], addr_data[5], + }) catch unreachable; + interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); } - } - - // internal true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false - interface.put(globalThis, JSC.ZigString.static("internal"), JSC.JSValue.jsBoolean(helpers.isLoopback(iface))); - - // scopeid The numeric IPv6 scope ID (only specified when family is IPv6) - if (addr.any.family == std.posix.AF.INET6) { - interface.put(globalThis, JSC.ZigString.static("scope_id"), JSC.JSValue.jsNumber(addr.in6.sa.scope_id)); - } - - // Does this entry already exist? - if (ret.get_unsafe(globalThis, interface_name)) |array| { - // Add this interface entry to the existing array - const next_index = @as(u32, @intCast(array.getLength(globalThis))); - array.putIndex(globalThis, next_index, interface); } else { - // Add it as an array with this interface as an element - const member_name = JSC.ZigString.init(interface_name); - var array = JSC.JSValue.createEmptyArray(globalThis, 1); - array.putIndex(globalThis, 0, interface); - ret.put(globalThis, &member_name, array); - } - } - - return ret; - } - - fn networkInterfacesWindows(globalThis: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { - var ifaces: [*]libuv.uv_interface_address_t = undefined; - var count: c_int = undefined; - const err = libuv.uv_interface_addresses(&ifaces, &count); - if (err != 0) { - const sys_err = JSC.SystemError{ - .message = bun.String.static("uv_interface_addresses failed"), - .code = bun.String.static("ERR_SYSTEM_ERROR"), - //.info = info, - .errno = err, - .syscall = bun.String.static("uv_interface_addresses"), - }; - return globalThis.throwValue(sys_err.toErrorInstance(globalThis)); - } - defer libuv.uv_free_interface_addresses(ifaces, count); - - var ret = JSC.JSValue.createEmptyObject(globalThis, 8); - - // 65 comes from: https://stackoverflow.com/questions/39443413/why-is-inet6-addrstrlen-defined-as-46-in-c - var ip_buf: [65]u8 = undefined; - var mac_buf: [17]u8 = undefined; - - for (ifaces[0..@intCast(count)]) |iface| { - var interface = JSC.JSValue.createEmptyObject(globalThis, 7); - - // address The assigned IPv4 or IPv6 address - // cidr The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. - var cidr = JSC.JSValue.null; - { - // Compute the CIDR suffix; returns null if the netmask cannot - // be converted to a CIDR suffix - const maybe_suffix: ?u8 = switch (iface.address.address4.family) { - std.posix.AF.INET => netmaskToCIDRSuffix(iface.netmask.netmask4.addr), - std.posix.AF.INET6 => netmaskToCIDRSuffix(@as(u128, @bitCast(iface.netmask.netmask6.addr))), - else => null, - }; - - // Format the address and then, if valid, the CIDR suffix; both - // the address and cidr values can be slices into this same buffer - // e.g. addr_str = "192.168.88.254", cidr_str = "192.168.88.254/24" - const addr_str = bun.fmt.formatIp( - // std.net.Address will do ptrCast depending on the family so this is ok - std.net.Address.initPosix(@ptrCast(&iface.address.address4)), - &ip_buf, - ) catch unreachable; - if (maybe_suffix) |suffix| { - //NOTE addr_str might not start at buf[0] due to slicing in formatIp - const start = @intFromPtr(addr_str.ptr) - @intFromPtr(&ip_buf[0]); - // Start writing the suffix immediately after the address - const suffix_str = std.fmt.bufPrint(ip_buf[start + addr_str.len ..], "/{}", .{suffix}) catch unreachable; - // The full cidr value is the address + the suffix - const cidr_str = ip_buf[start .. start + addr_str.len + suffix_str.len]; - cidr = JSC.ZigString.init(cidr_str).withEncoding().toJS(globalThis); - } - - interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toJS(globalThis)); - } - - // netmask - { - const str = bun.fmt.formatIp( - // std.net.Address will do ptrCast depending on the family so this is ok - std.net.Address.initPosix(@ptrCast(&iface.netmask.netmask4)), - &ip_buf, - ) catch unreachable; - interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toJS(globalThis)); - } - // family - interface.put(globalThis, JSC.ZigString.static("family"), (switch (iface.address.address4.family) { - std.posix.AF.INET => JSC.ZigString.static("IPv4"), - std.posix.AF.INET6 => JSC.ZigString.static("IPv6"), - else => JSC.ZigString.static("unknown"), - }).toJS(globalThis)); - - // mac - { - const phys = iface.phys_addr; - const mac = std.fmt.bufPrint(&mac_buf, "{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}", .{ - phys[0], phys[1], phys[2], phys[3], phys[4], phys[5], - }) catch unreachable; + const mac = "00:00:00:00:00:00"; interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); } - - // internal - { - interface.put(globalThis, JSC.ZigString.static("internal"), JSC.JSValue.jsBoolean(iface.is_internal != 0)); - } - - // cidr. this is here to keep ordering consistent with the node implementation - interface.put(globalThis, JSC.ZigString.static("cidr"), cidr); - - // scopeid - if (iface.address.address4.family == std.posix.AF.INET6) { - interface.put(globalThis, JSC.ZigString.static("scopeid"), JSC.JSValue.jsNumber(iface.address.address6.scope_id)); - } - - // Does this entry already exist? - const interface_name = bun.span(iface.name); - if (ret.get_unsafe(globalThis, interface_name)) |array| { - // Add this interface entry to the existing array - const next_index = @as(u32, @intCast(array.getLength(globalThis))); - array.putIndex(globalThis, next_index, interface); - } else { - // Add it as an array with this interface as an element - const member_name = JSC.ZigString.init(interface_name); - var array = JSC.JSValue.createEmptyArray(globalThis, 1); - array.putIndex(globalThis, 0, interface); - ret.put(globalThis, &member_name, array); - } } - return ret; - } + // internal true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false + interface.put(globalThis, JSC.ZigString.static("internal"), JSC.JSValue.jsBoolean(helpers.isLoopback(iface))); - pub fn platform(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - return JSC.ZigString.init(Global.os_name).withEncoding().toJS(globalThis); - } - - pub fn release(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; - return JSC.ZigString.init(C.getRelease(&name_buffer)).withEncoding().toJS(globalThis); - } - - pub fn setPriority(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - var args_ = callframe.arguments_old(2); - var arguments: []const JSC.JSValue = args_.ptr[0..args_.len]; - - if (arguments.len == 0) { - const err = JSC.toTypeError( - .ERR_INVALID_ARG_TYPE, - "The \"priority\" argument must be of type number. Received undefined", - .{}, - globalThis, - ); - return globalThis.throwValue(err); + // scopeid The numeric IPv6 scope ID (only specified when family is IPv6) + if (addr.any.family == std.posix.AF.INET6) { + interface.put(globalThis, JSC.ZigString.static("scope_id"), JSC.JSValue.jsNumber(addr.in6.sa.scope_id)); } - const pid = if (arguments.len == 2) arguments[0].coerce(i32, globalThis) else 0; - const priority = if (arguments.len == 2) arguments[1].coerce(i32, globalThis) else arguments[0].coerce(i32, globalThis); - - if (priority < -20 or priority > 19) { - const err = JSC.toTypeError( - .ERR_OUT_OF_RANGE, - "The value of \"priority\" is out of range. It must be >= -20 && <= 19", - .{}, - globalThis, - ); - return globalThis.throwValue(err); - } - - const errcode = C.setProcessPriority(pid, priority); - switch (errcode) { - .SRCH => { - const err = JSC.SystemError{ - .message = bun.String.static("A system error occurred: uv_os_setpriority returned ESRCH (no such process)"), - .code = bun.String.static(@tagName(.ERR_SYSTEM_ERROR)), - //.info = info, - .errno = -3, - .syscall = bun.String.static("uv_os_setpriority"), - }; - - return globalThis.throwValue(err.toErrorInstance(globalThis)); - }, - .ACCES => { - const err = JSC.SystemError{ - .message = bun.String.static("A system error occurred: uv_os_setpriority returned EACCESS (permission denied)"), - .code = bun.String.static(@tagName(.ERR_SYSTEM_ERROR)), - //.info = info, - .errno = -13, - .syscall = bun.String.static("uv_os_setpriority"), - }; - - return globalThis.throwValue(err.toErrorInstance(globalThis)); - }, - else => {}, - } - - return .undefined; - } - - pub fn totalmem(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - return JSC.JSValue.jsNumberFromUint64(C.getTotalMemory()); - } - - pub fn @"type"(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - - if (comptime Environment.isWindows) - return bun.String.static("Windows_NT").toJS(globalThis) - else if (comptime Environment.isMac) - return bun.String.static("Darwin").toJS(globalThis) - else if (comptime Environment.isLinux) - return bun.String.static("Linux").toJS(globalThis); - - return JSC.ZigString.init(Global.os_name).withEncoding().toJS(globalThis); - } - - pub fn uptime(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - if (Environment.isWindows) { - var uptime_value: f64 = undefined; - const err = libuv.uv_uptime(&uptime_value); - if (err != 0) { - const sys_err = JSC.SystemError{ - .message = bun.String.static("failed to get system uptime"), - .code = bun.String.static("ERR_SYSTEM_ERROR"), - .errno = err, - .syscall = bun.String.static("uv_uptime"), - }; - return globalThis.throwValue(sys_err.toErrorInstance(globalThis)); - } - return JSC.JSValue.jsNumber(uptime_value); - } - - return JSC.JSValue.jsNumberFromUint64(C.getSystemUptime()); - } - - pub fn userInfo(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - const result = JSC.JSValue.createEmptyObject(globalThis, 5); - - result.put(globalThis, JSC.ZigString.static("homedir"), try homedir(globalThis, callframe)); - - if (comptime Environment.isWindows) { - result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(bun.getenvZ("USERNAME") orelse "unknown").withEncoding().toJS(globalThis)); - result.put(globalThis, JSC.ZigString.static("uid"), JSC.JSValue.jsNumber(-1)); - result.put(globalThis, JSC.ZigString.static("gid"), JSC.JSValue.jsNumber(-1)); - result.put(globalThis, JSC.ZigString.static("shell"), JSC.JSValue.jsNull()); + // Does this entry already exist? + if (ret.get_unsafe(globalThis, interface_name)) |array| { + // Add this interface entry to the existing array + const next_index = @as(u32, @intCast(array.getLength(globalThis))); + array.putIndex(globalThis, next_index, interface); } else { - const username = bun.getenvZ("USER") orelse "unknown"; + // Add it as an array with this interface as an element + const member_name = JSC.ZigString.init(interface_name); + var array = JSC.JSValue.createEmptyArray(globalThis, 1); + array.putIndex(globalThis, 0, interface); + ret.put(globalThis, &member_name, array); + } + } - result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(username).withEncoding().toJS(globalThis)); - result.put(globalThis, JSC.ZigString.static("shell"), JSC.ZigString.init(bun.getenvZ("SHELL") orelse "unknown").withEncoding().toJS(globalThis)); + return ret; +} - result.put(globalThis, JSC.ZigString.static("uid"), JSC.JSValue.jsNumber(C.getuid())); - result.put(globalThis, JSC.ZigString.static("gid"), JSC.JSValue.jsNumber(C.getgid())); +fn networkInterfacesWindows(globalThis: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { + var ifaces: [*]libuv.uv_interface_address_t = undefined; + var count: c_int = undefined; + const err = libuv.uv_interface_addresses(&ifaces, &count); + if (err != 0) { + const sys_err = JSC.SystemError{ + .message = bun.String.static("uv_interface_addresses failed"), + .code = bun.String.static("ERR_SYSTEM_ERROR"), + //.info = info, + .errno = err, + .syscall = bun.String.static("uv_interface_addresses"), + }; + return globalThis.throwValue(sys_err.toErrorInstance(globalThis)); + } + defer libuv.uv_free_interface_addresses(ifaces, count); + + var ret = JSC.JSValue.createEmptyObject(globalThis, 8); + + // 65 comes from: https://stackoverflow.com/questions/39443413/why-is-inet6-addrstrlen-defined-as-46-in-c + var ip_buf: [65]u8 = undefined; + var mac_buf: [17]u8 = undefined; + + for (ifaces[0..@intCast(count)]) |iface| { + var interface = JSC.JSValue.createEmptyObject(globalThis, 7); + + // address The assigned IPv4 or IPv6 address + // cidr The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. + var cidr = JSC.JSValue.null; + { + // Compute the CIDR suffix; returns null if the netmask cannot + // be converted to a CIDR suffix + const maybe_suffix: ?u8 = switch (iface.address.address4.family) { + std.posix.AF.INET => netmaskToCIDRSuffix(iface.netmask.netmask4.addr), + std.posix.AF.INET6 => netmaskToCIDRSuffix(@as(u128, @bitCast(iface.netmask.netmask6.addr))), + else => null, + }; + + // Format the address and then, if valid, the CIDR suffix; both + // the address and cidr values can be slices into this same buffer + // e.g. addr_str = "192.168.88.254", cidr_str = "192.168.88.254/24" + const addr_str = bun.fmt.formatIp( + // std.net.Address will do ptrCast depending on the family so this is ok + std.net.Address.initPosix(@ptrCast(&iface.address.address4)), + &ip_buf, + ) catch unreachable; + if (maybe_suffix) |suffix| { + //NOTE addr_str might not start at buf[0] due to slicing in formatIp + const start = @intFromPtr(addr_str.ptr) - @intFromPtr(&ip_buf[0]); + // Start writing the suffix immediately after the address + const suffix_str = std.fmt.bufPrint(ip_buf[start + addr_str.len ..], "/{}", .{suffix}) catch unreachable; + // The full cidr value is the address + the suffix + const cidr_str = ip_buf[start .. start + addr_str.len + suffix_str.len]; + cidr = JSC.ZigString.init(cidr_str).withEncoding().toJS(globalThis); + } + + interface.put(globalThis, JSC.ZigString.static("address"), JSC.ZigString.init(addr_str).withEncoding().toJS(globalThis)); } - return result; + // netmask + { + const str = bun.fmt.formatIp( + // std.net.Address will do ptrCast depending on the family so this is ok + std.net.Address.initPosix(@ptrCast(&iface.netmask.netmask4)), + &ip_buf, + ) catch unreachable; + interface.put(globalThis, JSC.ZigString.static("netmask"), JSC.ZigString.init(str).withEncoding().toJS(globalThis)); + } + // family + interface.put(globalThis, JSC.ZigString.static("family"), (switch (iface.address.address4.family) { + std.posix.AF.INET => JSC.ZigString.static("IPv4"), + std.posix.AF.INET6 => JSC.ZigString.static("IPv6"), + else => JSC.ZigString.static("unknown"), + }).toJS(globalThis)); + + // mac + { + const phys = iface.phys_addr; + const mac = std.fmt.bufPrint(&mac_buf, "{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}:{x:0>2}", .{ + phys[0], phys[1], phys[2], phys[3], phys[4], phys[5], + }) catch unreachable; + interface.put(globalThis, JSC.ZigString.static("mac"), JSC.ZigString.init(mac).withEncoding().toJS(globalThis)); + } + + // internal + { + interface.put(globalThis, JSC.ZigString.static("internal"), JSC.JSValue.jsBoolean(iface.is_internal != 0)); + } + + // cidr. this is here to keep ordering consistent with the node implementation + interface.put(globalThis, JSC.ZigString.static("cidr"), cidr); + + // scopeid + if (iface.address.address4.family == std.posix.AF.INET6) { + interface.put(globalThis, JSC.ZigString.static("scopeid"), JSC.JSValue.jsNumber(iface.address.address6.scope_id)); + } + + // Does this entry already exist? + const interface_name = bun.span(iface.name); + if (ret.get_unsafe(globalThis, interface_name)) |array| { + // Add this interface entry to the existing array + const next_index = @as(u32, @intCast(array.getLength(globalThis))); + array.putIndex(globalThis, next_index, interface); + } else { + // Add it as an array with this interface as an element + const member_name = JSC.ZigString.init(interface_name); + var array = JSC.JSValue.createEmptyArray(globalThis, 1); + array.putIndex(globalThis, 0, interface); + ret.put(globalThis, &member_name, array); + } } - pub fn version(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; - return JSC.ZigString.init(C.getVersion(&name_buffer)).withEncoding().toJS(globalThis); + return ret; +} + +pub fn release() bun.String { + var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; + return bun.String.createUTF8(C.getRelease(&name_buffer)); +} + +pub fn setPriority1(global: *JSC.JSGlobalObject, pid: i32, priority: i32) !void { + const errcode = C.setProcessPriority(pid, priority); + switch (errcode) { + .SRCH => { + const err = JSC.SystemError{ + .message = bun.String.static("no such process"), + .code = bun.String.static("ESRCH"), + .errno = comptime switch (bun.Environment.os) { + else => -@as(c_int, @intFromEnum(std.posix.E.SRCH)), + .windows => libuv.UV_ESRCH, + }, + .syscall = bun.String.static("uv_os_getpriority"), + }; + return global.throwValue(err.toErrorInstanceWithInfoObject(global)); + }, + .ACCES => { + const err = JSC.SystemError{ + .message = bun.String.static("permission denied"), + .code = bun.String.static("EACCES"), + .errno = comptime switch (bun.Environment.os) { + else => -@as(c_int, @intFromEnum(std.posix.E.ACCES)), + .windows => libuv.UV_EACCES, + }, + .syscall = bun.String.static("uv_os_getpriority"), + }; + return global.throwValue(err.toErrorInstanceWithInfoObject(global)); + }, + .PERM => { + const err = JSC.SystemError{ + .message = bun.String.static("operation not permitted"), + .code = bun.String.static("EPERM"), + .errno = comptime switch (bun.Environment.os) { + else => -@as(c_int, @intFromEnum(std.posix.E.SRCH)), + .windows => libuv.UV_ESRCH, + }, + .syscall = bun.String.static("uv_os_getpriority"), + }; + return global.throwValue(err.toErrorInstanceWithInfoObject(global)); + }, + else => { + // no other error codes can be emitted + }, + } +} + +pub fn setPriority2(global: *JSC.JSGlobalObject, priority: i32) !void { + return setPriority1(global, 0, priority); +} + +pub fn totalmem() u64 { + return C.getTotalMemory(); +} + +pub fn uptime(global: *JSC.JSGlobalObject) bun.JSError!f64 { + if (Environment.isWindows) { + var uptime_value: f64 = undefined; + const err = libuv.uv_uptime(&uptime_value); + if (err != 0) { + const sys_err = JSC.SystemError{ + .message = bun.String.static("failed to get system uptime"), + .code = bun.String.static("ERR_SYSTEM_ERROR"), + .errno = err, + .syscall = bun.String.static("uv_uptime"), + }; + return global.throwValue(sys_err.toErrorInstance(global)); + } + return uptime_value; } - inline fn getMachineName() [:0]const u8 { - return switch (@import("builtin").target.cpu.arch) { - .arm => "arm", - .aarch64 => "arm64", - .mips => "mips", - .mips64 => "mips64", - .powerpc64 => "ppc64", - .powerpc64le => "ppc64le", - .s390x => "s390x", - .x86 => "i386", - .x86_64 => "x86_64", - else => "unknown", - }; + return @floatFromInt(C.getSystemUptime()); +} + +pub fn userInfo(globalThis: *JSC.JSGlobalObject, options: gen.UserInfoOptions) bun.JSError!JSC.JSValue { + _ = options; // TODO: + + const result = JSC.JSValue.createEmptyObject(globalThis, 5); + + const home = try homedir(globalThis); + defer home.deref(); + + result.put(globalThis, JSC.ZigString.static("homedir"), home.toJS(globalThis)); + + if (comptime Environment.isWindows) { + result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(bun.getenvZ("USERNAME") orelse "unknown").withEncoding().toJS(globalThis)); + result.put(globalThis, JSC.ZigString.static("uid"), JSC.JSValue.jsNumber(-1)); + result.put(globalThis, JSC.ZigString.static("gid"), JSC.JSValue.jsNumber(-1)); + result.put(globalThis, JSC.ZigString.static("shell"), JSC.JSValue.jsNull()); + } else { + const username = bun.getenvZ("USER") orelse "unknown"; + + result.put(globalThis, JSC.ZigString.static("username"), JSC.ZigString.init(username).withEncoding().toJS(globalThis)); + result.put(globalThis, JSC.ZigString.static("shell"), JSC.ZigString.init(bun.getenvZ("SHELL") orelse "unknown").withEncoding().toJS(globalThis)); + result.put(globalThis, JSC.ZigString.static("uid"), JSC.JSValue.jsNumber(C.getuid())); + result.put(globalThis, JSC.ZigString.static("gid"), JSC.JSValue.jsNumber(C.getgid())); } - pub fn machine(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - JSC.markBinding(@src()); - return JSC.ZigString.static(comptime getMachineName()).toJS(globalThis); - } -}; + return result; +} + +pub fn version() bun.JSError!bun.String { + var name_buffer: [bun.HOST_NAME_MAX]u8 = undefined; + return bun.String.createUTF8(C.getVersion(&name_buffer)); +} /// Given a netmask returns a CIDR suffix. Returns null if the mask is not valid. /// `@TypeOf(mask)` must be one of u32 (IPv4) or u128 (IPv6) diff --git a/src/bun.js/node/node_util_binding.zig b/src/bun.js/node/node_util_binding.zig index 2f886cb0fa..cbcf25f09b 100644 --- a/src/bun.js/node/node_util_binding.zig +++ b/src/bun.js/node/node_util_binding.zig @@ -1,5 +1,6 @@ const std = @import("std"); const bun = @import("root").bun; +const Allocator = std.mem.Allocator; const Environment = bun.Environment; const JSC = bun.JSC; const string = bun.string; @@ -105,3 +106,91 @@ pub fn internalErrorName(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFr var fmtstring = bun.String.createFormat("Unknown system error {d}", .{err_int}) catch bun.outOfMemory(); return fmtstring.transferToJS(globalThis); } + +/// `extractedSplitNewLines` for ASCII/Latin1 strings. Panics if passed a non-string. +/// Returns `undefined` if param is utf8 or utf16 and not fully ascii. +/// +/// ```js +/// // util.js +/// const extractedNewLineRe = new RegExp("(?<=\\n)"); +/// extractedSplitNewLines = value => RegExpPrototypeSymbolSplit(extractedNewLineRe, value); +/// ``` +pub fn extractedSplitNewLinesFastPathStringsOnly(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + bun.assert(callframe.argumentsCount() == 1); + const value = callframe.argument(0); + bun.assert(value.isString()); + + const str = try value.toBunString2(globalThis); + defer str.deref(); + + return switch (str.encoding()) { + inline .utf16, .latin1 => |encoding| split(encoding, globalThis, bun.default_allocator, &str), + .utf8 => if (bun.strings.isAllASCII(str.byteSlice())) + return split(.utf8, globalThis, bun.default_allocator, &str) + else + return JSC.JSValue.jsUndefined(), + }; +} + +fn split( + comptime encoding: bun.strings.EncodingNonAscii, + globalThis: *JSC.JSGlobalObject, + allocator: Allocator, + str: *const bun.String, +) bun.JSError!JSC.JSValue { + var fallback = std.heap.stackFallback(1024, allocator); + const alloc = fallback.get(); + const Char = switch (encoding) { + .utf8, .latin1 => u8, + .utf16 => u16, + }; + + var lines: std.ArrayListUnmanaged(bun.String) = .{}; + defer { + for (lines.items) |out| { + out.deref(); + } + lines.deinit(alloc); + } + + const buffer: []const Char = if (encoding == .utf16) + str.utf16() + else + str.byteSlice(); + var it: SplitNewlineIterator(Char) = .{ .buffer = buffer, .index = 0 }; + while (it.next()) |line| { + const encoded_line = switch (encoding) { + inline .utf8 => bun.String.fromUTF8(line), + inline .latin1 => bun.String.createLatin1(line), + inline .utf16 => bun.String.fromUTF16(line), + }; + errdefer encoded_line.deref(); + try lines.append(alloc, encoded_line); + } + + return bun.String.toJSArray(globalThis, lines.items); +} + +pub fn SplitNewlineIterator(comptime T: type) type { + return struct { + buffer: []const T, + index: ?usize, + + const Self = @This(); + + /// Returns a slice of the next field, or null if splitting is complete. + pub fn next(self: *Self) ?[]const T { + const start = self.index orelse return null; + + if (std.mem.indexOfScalarPos(T, self.buffer, start, '\n')) |delim_start| { + const end = delim_start + 1; + const slice = self.buffer[start..end]; + self.index = end; + return slice; + } else { + self.index = null; + return self.buffer[start..]; + } + } + }; +} diff --git a/src/bun.js/node/node_zlib_binding.zig b/src/bun.js/node/node_zlib_binding.zig index 8531c209d5..cd233554c7 100644 --- a/src/bun.js/node/node_zlib_binding.zig +++ b/src/bun.js/node/node_zlib_binding.zig @@ -6,6 +6,7 @@ const string = bun.string; const Output = bun.Output; const ZigString = JSC.ZigString; const validators = @import("./util/validators.zig"); +const debug = bun.Output.scoped(.zlib, true); pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.arguments_old(2).ptr; @@ -71,6 +72,8 @@ pub fn CompressionStream(comptime T: type) type { var in: ?[]const u8 = null; var out: ?[]u8 = null; + const this_value = callframe.this(); + bun.assert(!arguments[0].isUndefined()); // must provide flush value flush = arguments[0].toU32(); _ = std.meta.intToEnum(bun.zlib.FlushValue, flush) catch bun.assert(false); // Invalid flush value @@ -102,7 +105,9 @@ pub fn CompressionStream(comptime T: type) type { this.stream.setBuffers(in, out); this.stream.setFlush(@intCast(flush)); - // + // Only create the strong handle when we have a pending write + // And make sure to clear it when we are done. + this.this_value.set(globalThis, this_value); const vm = globalThis.bunVM(); this.task = .{ .callback = &AsyncJob.runTask }; @@ -136,13 +141,23 @@ pub fn CompressionStream(comptime T: type) type { this.write_in_progress = false; - if (!(this.checkError(globalThis) catch return globalThis.reportActiveExceptionAsUnhandled(error.JSError))) { + // Clear the strong handle before we call any callbacks. + const this_value = this.this_value.trySwap() orelse { + debug("this_value is null in runFromJSThread", .{}); + return; + }; + + this_value.ensureStillAlive(); + + if (!(this.checkError(globalThis, this_value) catch return globalThis.reportActiveExceptionAsUnhandled(error.JSError))) { return; } this.stream.updateWriteResult(&this.write_result.?[1], &this.write_result.?[0]); + this_value.ensureStillAlive(); - _ = this.write_callback.get().?.call(globalThis, this.this_value.get().?, &.{}) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); + const write_callback: JSC.JSValue = T.writeCallbackGetCached(this_value).?; + _ = write_callback.call(globalThis, this_value, &.{}) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); if (this.pending_close) _ = this._close(); } @@ -192,11 +207,10 @@ pub fn CompressionStream(comptime T: type) type { this.stream.setBuffers(in, out); this.stream.setFlush(@intCast(flush)); - - // + const this_value = callframe.this(); this.stream.doWork(); - if (try this.checkError(globalThis)) { + if (try this.checkError(globalThis, this_value)) { this.stream.updateWriteResult(&this.write_result.?[1], &this.write_result.?[0]); this.write_in_progress = false; } @@ -206,11 +220,9 @@ pub fn CompressionStream(comptime T: type) type { } pub fn reset(this: *T, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - _ = callframe; - const err = this.stream.reset(); if (err.isError()) { - try this.emitError(globalThis, err); + try this.emitError(globalThis, callframe.this(), err); } return .undefined; } @@ -233,34 +245,34 @@ pub fn CompressionStream(comptime T: type) type { this.stream.close(); } - pub fn setOnError(this: *T, globalThis: *JSC.JSGlobalObject, value: JSC.JSValue) bool { + pub fn setOnError(_: *T, this_value: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) bool { if (value.isFunction()) { - this.onerror_value.set(globalThis, value); + T.errorCallbackSetCached(this_value, globalObject, value); } return true; } - pub fn getOnError(this: *T, globalThis: *JSC.JSGlobalObject) JSC.JSValue { - _ = globalThis; - return this.onerror_value.get() orelse .undefined; + pub fn getOnError(_: *T, this_value: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { + return T.errorCallbackGetCached(this_value) orelse .undefined; } /// returns true if no error was detected/emitted - fn checkError(this: *T, globalThis: *JSC.JSGlobalObject) !bool { + fn checkError(this: *T, globalThis: *JSC.JSGlobalObject, this_value: JSC.JSValue) !bool { const err = this.stream.getErrorInfo(); if (!err.isError()) return true; - try this.emitError(globalThis, err); + try this.emitError(globalThis, this_value, err); return false; } - fn emitError(this: *T, globalThis: *JSC.JSGlobalObject, err_: Error) !void { + fn emitError(this: *T, globalThis: *JSC.JSGlobalObject, this_value: JSC.JSValue, err_: Error) !void { var msg_str = bun.String.createFormat("{s}", .{std.mem.sliceTo(err_.msg, 0) orelse ""}) catch bun.outOfMemory(); const msg_value = msg_str.transferToJS(globalThis); const err_value = JSC.jsNumber(err_.err); var code_str = bun.String.createFormat("{s}", .{std.mem.sliceTo(err_.code, 0) orelse ""}) catch bun.outOfMemory(); const code_value = code_str.transferToJS(globalThis); - _ = try this.onerror_value.get().?.call(globalThis, this.this_value.get().?, &.{ msg_value, err_value, code_value }); + const callback: JSC.JSValue = T.errorCallbackGetCached(this_value) orelse Output.panic("Assertion failure: cachedErrorCallback is null in node:zlib binding", .{}); + _ = try callback.call(globalThis, this_value, &.{ msg_value, err_value, code_value }); this.write_in_progress = false; if (this.pending_close) _ = this._close(); @@ -307,8 +319,6 @@ pub const SNativeZlib = struct { globalThis: *JSC.JSGlobalObject, stream: ZlibContext = .{}, write_result: ?[*]u32 = null, - write_callback: JSC.Strong = .{}, - onerror_value: JSC.Strong = .{}, poll_ref: CountedKeepAlive = .{}, this_value: JSC.Strong = .{}, write_in_progress: bool = false, @@ -341,14 +351,14 @@ pub const SNativeZlib = struct { } //// adding this didnt help much but leaving it here to compare the number with later - // pub fn estimatedSize(this: *const SNativeZlib) usize { - // _ = this; - // const internal_state_size = 3309; // @sizeOf(@cImport(@cInclude("deflate.h")).internal_state) @ cloudflare/zlib @ 92530568d2c128b4432467b76a3b54d93d6350bd - // return @sizeOf(SNativeZlib) + internal_state_size; - // } + pub fn estimatedSize(_: *const SNativeZlib) usize { + const internal_state_size = 3309; // @sizeOf(@cImport(@cInclude("deflate.h")).internal_state) @ cloudflare/zlib @ 92530568d2c128b4432467b76a3b54d93d6350bd + return @sizeOf(SNativeZlib) + internal_state_size; + } pub fn init(this: *SNativeZlib, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.argumentsUndef(7).slice(); + const this_value = callframe.this(); if (arguments.len != 7) { return globalThis.ERR_MISSING_ARGS("init(windowBits, level, memLevel, strategy, writeResult, writeCallback, dictionary)", .{}).throw(); @@ -364,7 +374,12 @@ pub const SNativeZlib = struct { const dictionary = if (arguments[6].isUndefined()) null else arguments[6].asArrayBuffer(globalThis).?.byteSlice(); this.write_result = writeResult; - this.write_callback.set(globalThis, writeCallback); + SNativeZlib.writeCallbackSetCached(this_value, globalThis, writeCallback); + + // Keep the dictionary alive by keeping a reference to it in the JS object. + if (dictionary != null) { + SNativeZlib.dictionarySetCached(this_value, globalThis, arguments[6]); + } this.stream.init(level, windowBits, memLevel, strategy, dictionary); @@ -383,15 +398,15 @@ pub const SNativeZlib = struct { const err = this.stream.setParams(level, strategy); if (err.isError()) { - try this.emitError(globalThis, err); + try this.emitError(globalThis, callframe.this(), err); } return .undefined; } pub fn deinit(this: *@This()) void { - this.write_callback.deinit(); - this.onerror_value.deinit(); + this.this_value.deinit(); this.poll_ref.deinit(); + this.stream.close(); this.destroy(); } }; @@ -497,7 +512,17 @@ const ZlibContext = struct { return .{ .msg = message, .err = @intFromEnum(this.err), - .code = @tagName(this.err), + .code = switch (this.err) { + .Ok => "Z_OK", + .StreamEnd => "Z_STREAM_END", + .NeedDict => "Z_NEED_DICT", + .ErrNo => "Z_ERRNO", + .StreamError => "Z_STREAM_ERROR", + .DataError => "Z_DATA_ERROR", + .MemError => "Z_MEM_ERROR", + .BufError => "Z_BUF_ERROR", + .VersionError => "Z_VERSION_ERROR", + }, }; } @@ -652,7 +677,7 @@ pub const NativeBrotli = JSC.Codegen.JSNativeBrotli.getConstructor; pub const SNativeBrotli = struct { pub usingnamespace bun.NewRefCounted(@This(), deinit); - pub usingnamespace JSC.Codegen.JSNativeZlib; + pub usingnamespace JSC.Codegen.JSNativeBrotli; pub usingnamespace CompressionStream(@This()); ref_count: u32 = 1, @@ -660,8 +685,6 @@ pub const SNativeBrotli = struct { globalThis: *JSC.JSGlobalObject, stream: BrotliContext = .{}, write_result: ?[*]u32 = null, - write_callback: JSC.Strong = .{}, - onerror_value: JSC.Strong = .{}, poll_ref: CountedKeepAlive = .{}, this_value: JSC.Strong = .{}, write_in_progress: bool = false, @@ -706,8 +729,9 @@ pub const SNativeBrotli = struct { }; } - pub fn init(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + pub fn init(this: *SNativeBrotli, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.argumentsUndef(3).slice(); + const this_value = callframe.this(); if (arguments.len != 3) { return globalThis.ERR_MISSING_ARGS("init(params, writeResult, writeCallback)", .{}).throw(); } @@ -715,12 +739,14 @@ pub const SNativeBrotli = struct { // this does not get gc'd because it is stored in the JS object's `this._writeState`. and the JS object is tied to the native handle as `_handle[owner_symbol]`. const writeResult = arguments[1].asArrayBuffer(globalThis).?.asU32().ptr; const writeCallback = try validators.validateFunction(globalThis, arguments[2], "writeCallback", .{}); + this.write_result = writeResult; - this.write_callback.set(globalThis, writeCallback); + + SNativeBrotli.writeCallbackSetCached(this_value, globalThis, writeCallback); var err = this.stream.init(); if (err.isError()) { - try this.emitError(globalThis, err); + try this.emitError(globalThis, this_value, err); return JSC.jsBoolean(false); } @@ -749,9 +775,12 @@ pub const SNativeBrotli = struct { } pub fn deinit(this: *@This()) void { - this.write_callback.deinit(); - this.onerror_value.deinit(); + this.this_value.deinit(); this.poll_ref.deinit(); + switch (this.stream.mode) { + .BROTLI_ENCODE, .BROTLI_DECODE => this.stream.close(), + else => {}, + } this.destroy(); } }; diff --git a/src/bun.js/node/path.zig b/src/bun.js/node/path.zig index c0ff1d10be..3545f4418d 100644 --- a/src/bun.js/node/path.zig +++ b/src/bun.js/node/path.zig @@ -1237,7 +1237,7 @@ pub inline fn joinWindowsJS_T(comptime T: type, globalObject: *JSC.JSGlobalObjec pub fn joinJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, paths: []const []const T) JSC.JSValue { // Adding 8 bytes when Windows for the possible UNC root. var bufLen: usize = if (isWindows) 8 else 0; - for (paths) |path| bufLen += if (bufLen > 0 and path.len > 0) path.len + 1 else path.len; + for (paths) |path| bufLen += if (path.len > 0) path.len + 1 else path.len; bufLen = @max(bufLen, PATH_SIZE(T)); const buf = allocator.alloc(T, bufLen) catch bun.outOfMemory(); defer allocator.free(buf); diff --git a/src/bun.js/node/path_watcher.zig b/src/bun.js/node/path_watcher.zig index 4d1455fdc7..c5045fc542 100644 --- a/src/bun.js/node/path_watcher.zig +++ b/src/bun.js/node/path_watcher.zig @@ -151,7 +151,7 @@ pub const PathWatcherManager = struct { .main_watcher = try Watcher.init( PathWatcherManager, this, - vm.bundler.fs, + vm.transpiler.fs, bun.default_allocator, ), .vm = vm, diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index c664c48954..72232be196 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1123,7 +1123,7 @@ pub const ArgumentsSlice = struct { arena: bun.ArenaAllocator = bun.ArenaAllocator.init(bun.default_allocator), all: []const JSC.JSValue, threw: bool = false, - protected: std.bit_set.IntegerBitSet(32) = std.bit_set.IntegerBitSet(32).initEmpty(), + protected: bun.bit_set.IntegerBitSet(32) = bun.bit_set.IntegerBitSet(32).initEmpty(), will_be_async: bool = false, pub fn unprotect(this: *ArgumentsSlice) void { @@ -1132,7 +1132,7 @@ pub const ArgumentsSlice = struct { while (iter.next()) |i| { JSC.C.JSValueUnprotect(ctx, this.all[i].asObjectRef()); } - this.protected = std.bit_set.IntegerBitSet(32).initEmpty(); + this.protected = bun.bit_set.IntegerBitSet(32).initEmpty(); } pub fn deinit(this: *ArgumentsSlice) void { @@ -1726,7 +1726,7 @@ pub fn StatType(comptime Big: bool) type { } // dev, mode, nlink, uid, gid, rdev, blksize, ino, size, blocks, atimeMs, mtimeMs, ctimeMs, birthtimeMs - var args = callFrame.argumentsPtr()[0..@min(callFrame.argumentsCount(), 14)]; + var args = callFrame.arguments(); const atime_ms: f64 = if (args.len > 10 and args[10].isNumber()) args[10].asNumber() else 0; const mtime_ms: f64 = if (args.len > 11 and args[11].isNumber()) args[11].asNumber() else 0; @@ -2092,7 +2092,7 @@ pub const Process = struct { // When we update the cwd from JS, we have to update the bundler's version as well // However, this might be called many times in a row, so we use a pre-allocated buffer // that way we don't have to worry about garbage collector - const fs = JSC.VirtualMachine.get().bundler.fs; + const fs = JSC.VirtualMachine.get().transpiler.fs; const into_cwd_buf = switch (bun.sys.getcwd(&buf)) { .result => |r| r, .err => |err| { @@ -2133,6 +2133,46 @@ pub const Process = struct { vm.globalExit(); } + // TODO: switch this to using *bun.wtf.String when it is added + pub fn Bun__Process__editWindowsEnvVar(k: bun.String, v: bun.String) callconv(.C) void { + if (k.tag == .Empty) return; + const wtf1 = k.value.WTFStringImpl; + var fixed_stack_allocator = std.heap.stackFallback(1025, bun.default_allocator); + const allocator = fixed_stack_allocator.get(); + var buf1 = allocator.alloc(u16, k.utf16ByteLength() + 1) catch bun.outOfMemory(); + defer allocator.free(buf1); + var buf2 = allocator.alloc(u16, v.utf16ByteLength() + 1) catch bun.outOfMemory(); + defer allocator.free(buf2); + const len1: usize = switch (wtf1.is8Bit()) { + true => bun.strings.copyLatin1IntoUTF16([]u16, buf1, []const u8, wtf1.latin1Slice()).written, + false => b: { + @memcpy(buf1[0..wtf1.length()], wtf1.utf16Slice()); + break :b wtf1.length(); + }, + }; + buf1[len1] = 0; + const str2: ?[*:0]const u16 = if (v.tag != .Dead) str: { + if (v.tag == .Empty) break :str (&[_]u16{0})[0..0 :0]; + const wtf2 = v.value.WTFStringImpl; + const len2: usize = switch (wtf2.is8Bit()) { + true => bun.strings.copyLatin1IntoUTF16([]u16, buf2, []const u8, wtf2.latin1Slice()).written, + false => b: { + @memcpy(buf2[0..wtf2.length()], wtf2.utf16Slice()); + break :b wtf2.length(); + }, + }; + buf2[len2] = 0; + break :str buf2[0..len2 :0].ptr; + } else null; + _ = bun.windows.SetEnvironmentVariableW(buf1[0..len1 :0].ptr, str2); + } + + comptime { + if (Environment.export_cpp_apis and Environment.isWindows) { + @export(Bun__Process__editWindowsEnvVar, .{ .name = "Bun__Process__editWindowsEnvVar" }); + } + } + pub export const Bun__version: [*:0]const u8 = "v" ++ bun.Global.package_json_version; pub export const Bun__version_with_sha: [*:0]const u8 = "v" ++ bun.Global.package_json_version_with_sha; pub export const Bun__versions_boringssl: [*:0]const u8 = bun.Global.versions.boringssl; diff --git a/src/bun.js/rare_data.zig b/src/bun.js/rare_data.zig index 45ebac3282..5bad28c278 100644 --- a/src/bun.js/rare_data.zig +++ b/src/bun.js/rare_data.zig @@ -48,7 +48,58 @@ listening_sockets_for_watch_mode_lock: bun.Lock = .{}, temp_pipe_read_buffer: ?*PipeReadBuffer = null, +aws_signature_cache: AWSSignatureCache = .{}, + const PipeReadBuffer = [256 * 1024]u8; +const DIGESTED_HMAC_256_LEN = 32; +pub const AWSSignatureCache = struct { + cache: bun.StringArrayHashMap([DIGESTED_HMAC_256_LEN]u8) = bun.StringArrayHashMap([DIGESTED_HMAC_256_LEN]u8).init(bun.default_allocator), + date: u64 = 0, + lock: bun.Lock = .{}, + + pub fn clean(this: *@This()) void { + for (this.cache.keys()) |cached_key| { + bun.default_allocator.free(cached_key); + } + this.cache.clearRetainingCapacity(); + } + + pub fn get(this: *@This(), numeric_day: u64, key: []const u8) ?[]const u8 { + this.lock.lock(); + defer this.lock.unlock(); + if (this.date == 0) { + return null; + } + if (this.date == numeric_day) { + if (this.cache.getKey(key)) |cached| { + return cached; + } + } + return null; + } + + pub fn set(this: *@This(), numeric_day: u64, key: []const u8, value: [DIGESTED_HMAC_256_LEN]u8) void { + this.lock.lock(); + defer this.lock.unlock(); + if (this.date == 0) { + this.cache = bun.StringArrayHashMap([DIGESTED_HMAC_256_LEN]u8).init(bun.default_allocator); + } else if (this.date != numeric_day) { + // day changed so we clean the old cache + this.clean(); + } + this.date = numeric_day; + this.cache.put(bun.default_allocator.dupe(u8, key) catch bun.outOfMemory(), value) catch bun.outOfMemory(); + } + pub fn deinit(this: *@This()) void { + this.date = 0; + this.clean(); + this.cache.deinit(); + } +}; + +pub fn awsCache(this: *RareData) *AWSSignatureCache { + return &this.aws_signature_cache; +} pub fn pipeReadBuffer(this: *RareData) *PipeReadBuffer { return this.temp_pipe_read_buffer orelse { @@ -389,6 +440,8 @@ pub fn deinit(this: *RareData) void { bun.default_allocator.destroy(pipe); } + this.aws_signature_cache.deinit(); + if (this.boring_ssl_engine) |engine| { _ = bun.BoringSSL.ENGINE_free(engine); } diff --git a/src/bun.js/script_execution_context.zig b/src/bun.js/script_execution_context.zig deleted file mode 100644 index 3753952363..0000000000 --- a/src/bun.js/script_execution_context.zig +++ /dev/null @@ -1,7 +0,0 @@ -const JSC = bun.JSC; - -pub const ScriptExecutionContext = extern struct { - main_file_path: JSC.ZigString, - is_macro: bool = false, - js_global_object: bool = false, -}; diff --git a/src/bun.js/test/diff_format.zig b/src/bun.js/test/diff_format.zig index fc04a74e33..e198e474cd 100644 --- a/src/bun.js/test/diff_format.zig +++ b/src/bun.js/test/diff_format.zig @@ -111,7 +111,7 @@ pub const DiffFormatter = struct { Writer, buf_writer, fmt_options, - ); + ) catch {}; // TODO: buffered_writer.flush() catch unreachable; buffered_writer_.context = &expected_buf; @@ -125,7 +125,7 @@ pub const DiffFormatter = struct { Writer, buf_writer, fmt_options, - ); + ) catch {}; // TODO: buffered_writer.flush() catch unreachable; } diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index e09000c7b0..a9ef928354 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -4060,6 +4060,37 @@ pub const Expect = struct { return this.throw(globalThis, signature, "\n\n" ++ "Expected number of calls: \\>= 1\n" ++ "Received number of calls: {any}\n", .{calls.getLength(globalThis)}); } + pub fn toHaveBeenCalledOnce(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { + JSC.markBinding(@src()); + + const thisValue = callframe.this(); + defer this.postMatch(globalThis); + const value: JSValue = try this.getValue(globalThis, thisValue, "toHaveBeenCalledOnce", "expected"); + + incrementExpectCallCounter(); + + const calls = JSMockFunction__getCalls(value); + + if (calls == .zero or !calls.jsType().isArray()) { + return globalThis.throw("Expected value must be a mock function: {}", .{value}); + } + + var pass = @as(i32, @intCast(calls.getLength(globalThis))) == 1; + + const not = this.flags.not; + if (not) pass = !pass; + if (pass) return .undefined; + + // handle failure + if (not) { + const signature = comptime getSignature("toHaveBeenCalledOnce", "expected", true); + return this.throw(globalThis, signature, "\n\n" ++ "Expected number of calls: not 1\n" ++ "Received number of calls: {d}\n", .{calls.getLength(globalThis)}); + } + + const signature = comptime getSignature("toHaveBeenCalledOnce", "expected", false); + return this.throw(globalThis, signature, "\n\n" ++ "Expected number of calls: 1\n" ++ "Received number of calls: {d}\n", .{calls.getLength(globalThis)}); + } + pub fn toHaveBeenCalledTimes(this: *Expect, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); @@ -4161,7 +4192,7 @@ pub const Expect = struct { JSC.markBinding(@src()); const thisValue = callframe.this(); - const arguments = callframe.argumentsPtr()[0..callframe.argumentsCount()]; + const arguments = callframe.arguments(); defer this.postMatch(globalThis); const value: JSValue = try this.getValue(globalThis, thisValue, "toHaveBeenCalledWith", "expected"); @@ -4220,7 +4251,7 @@ pub const Expect = struct { JSC.markBinding(@src()); const thisValue = callframe.this(); - const arguments = callframe.argumentsPtr()[0..callframe.argumentsCount()]; + const arguments = callframe.arguments(); defer this.postMatch(globalThis); const value: JSValue = try this.getValue(globalThis, thisValue, "toHaveBeenLastCalledWith", "expected"); @@ -4278,7 +4309,7 @@ pub const Expect = struct { JSC.markBinding(@src()); const thisValue = callframe.this(); - const arguments = callframe.argumentsPtr()[0..callframe.argumentsCount()]; + const arguments = callframe.arguments(); defer this.postMatch(globalThis); const value: JSValue = try this.getValue(globalThis, thisValue, "toHaveBeenNthCalledWith", "expected"); @@ -4721,12 +4752,11 @@ pub const Expect = struct { incrementExpectCallCounter(); // prepare the args array - const args_ptr = callFrame.argumentsPtr(); - const args_count = callFrame.argumentsCount(); + const args = callFrame.arguments(); var allocator = std.heap.stackFallback(8 * @sizeOf(JSValue), globalThis.allocator()); - var matcher_args = try std.ArrayList(JSValue).initCapacity(allocator.get(), args_count + 1); + var matcher_args = try std.ArrayList(JSValue).initCapacity(allocator.get(), args.len + 1); matcher_args.appendAssumeCapacity(value); - for (0..args_count) |i| matcher_args.appendAssumeCapacity(args_ptr[i]); + for (args) |arg| matcher_args.appendAssumeCapacity(arg); _ = try executeCustomMatcher(globalThis, matcher_name, matcher_fn, matcher_args.items, expect.flags, false); @@ -5202,14 +5232,13 @@ pub const ExpectCustomAsymmetricMatcher = struct { ExpectCustomAsymmetricMatcher.matcherFnSetCached(instance_jsvalue, globalThis, matcher_fn); // capture the args as a JS array saved in the instance, so the matcher can be executed later on with them - const args_ptr = callFrame.argumentsPtr(); - const args_count: usize = callFrame.argumentsCount(); - var args = JSValue.createEmptyArray(globalThis, args_count); - for (0..args_count) |i| { - args.putIndex(globalThis, @truncate(i), args_ptr[i]); + const args = callFrame.arguments(); + const array = JSValue.createEmptyArray(globalThis, args.len); + for (args, 0..) |arg, i| { + array.putIndex(globalThis, @truncate(i), arg); } - args.ensureStillAlive(); - ExpectCustomAsymmetricMatcher.capturedArgsSetCached(instance_jsvalue, globalThis, args); + ExpectCustomAsymmetricMatcher.capturedArgsSetCached(instance_jsvalue, globalThis, array); + array.ensureStillAlive(); // return the same instance, now fully initialized including the captured args (previously it was incomplete) return instance_jsvalue; diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts index 38c0397a03..57b4273aba 100644 --- a/src/bun.js/test/jest.classes.ts +++ b/src/bun.js/test/jest.classes.ts @@ -297,6 +297,10 @@ export default [ fn: "toHaveBeenCalled", length: 0, }, + toHaveBeenCalledOnce: { + fn: "toHaveBeenCalledOnce", + length: 0, + }, toHaveBeenCalledTimes: { fn: "toHaveBeenCalledTimes", length: 1, diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index dd119384c7..2934577d49 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -574,10 +574,10 @@ pub const JestPrettyFormat = struct { const next_value = this.remaining_values[0]; this.remaining_values = this.remaining_values[1..]; switch (token) { - Tag.String => this.printAs(Tag.String, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), - Tag.Double => this.printAs(Tag.Double, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), - Tag.Object => this.printAs(Tag.Object, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), - Tag.Integer => this.printAs(Tag.Integer, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors), + Tag.String => this.printAs(Tag.String, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: + Tag.Double => this.printAs(Tag.Double, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: + Tag.Object => this.printAs(Tag.Object, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: + Tag.Integer => this.printAs(Tag.Integer, Writer, writer_, next_value, next_value.jsType(), enable_ansi_colors) catch {}, // TODO: // undefined is overloaded to mean the '%o" field Tag.Undefined => this.format(Tag.get(next_value, globalThis), Writer, writer_, next_value, globalThis, enable_ansi_colors), @@ -861,7 +861,7 @@ pub const JestPrettyFormat = struct { writer.print( comptime Output.prettyFmt("{s}: ", enable_ansi_colors), - .{bun.fmt.formatJSONString(key.slice())}, + .{bun.fmt.formatJSONStringLatin1(key.slice())}, ); } } else { @@ -899,7 +899,7 @@ pub const JestPrettyFormat = struct { value: JSValue, jsType: JSValue.JSType, comptime enable_ansi_colors: bool, - ) void { + ) error{}!void { if (this.failed) return; var writer = WrappedWriter(Writer){ .ctx = writer_, .estimated_line_length = &this.estimated_line_length }; @@ -1060,7 +1060,7 @@ pub const JestPrettyFormat = struct { }, .Double => { if (value.isCell()) { - this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); + try this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); return; } @@ -1240,12 +1240,11 @@ pub const JestPrettyFormat = struct { this.addForNewLine("FormData (entries) ".len); writer.writeAll(comptime Output.prettyFmt("FormData (entries) ", enable_ansi_colors)); - return this.printAs( + return try this.printAs( .Object, Writer, writer_, - toJSONFunction.call(this.globalThis, value, &.{}) catch |err| - this.globalThis.takeException(err), + toJSONFunction.call(this.globalThis, value, &.{}) catch |err| this.globalThis.takeException(err), .Object, enable_ansi_colors, ); @@ -1273,12 +1272,12 @@ pub const JestPrettyFormat = struct { return; } else if (jsType != .DOMWrapper) { if (value.isCallable(this.globalThis.vm())) { - return this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors); + return try this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors); } - return this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, jsType, enable_ansi_colors); } - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); }, .NativeCode => { this.addForNewLine("[native code]".len); @@ -1293,7 +1292,7 @@ pub const JestPrettyFormat = struct { }, .Boolean => { if (value.isCell()) { - this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); + try this.printAs(.Object, Writer, writer_, value, .Object, enable_ansi_colors); return; } if (value.toBoolean()) { @@ -1401,7 +1400,7 @@ pub const JestPrettyFormat = struct { const event_type = switch (EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { .MessageEvent, .ErrorEvent => |evt| evt, else => { - return this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); + return try this.printAs(.Object, Writer, writer_, value, .Event, enable_ansi_colors); }, }; @@ -1962,7 +1961,7 @@ pub const JestPrettyFormat = struct { // comptime var so we have to repeat it here. The rationale there is // it _should_ limit the stack usage because each version of the // function will be relatively small - return switch (result.tag) { + return try switch (result.tag) { .StringPossiblyFormatted => this.printAs(.StringPossiblyFormatted, Writer, writer, value, result.cell, enable_ansi_colors), .String => this.printAs(.String, Writer, writer, value, result.cell, enable_ansi_colors), .Undefined => this.printAs(.Undefined, Writer, writer, value, result.cell, enable_ansi_colors), @@ -2076,7 +2075,7 @@ pub const JestPrettyFormat = struct { this.addForNewLine("ObjectContaining ".len); writer.writeAll("ObjectContaining "); } - this.printAs(.Object, @TypeOf(writer_), writer_, object_value, .Object, enable_ansi_colors); + this.printAs(.Object, @TypeOf(writer_), writer_, object_value, .Object, enable_ansi_colors) catch {}; // TODO: } else if (value.as(expect.ExpectStringContaining)) |matcher| { const substring_value = expect.ExpectStringContaining.stringValueGetCached(value) orelse return true; @@ -2088,7 +2087,7 @@ pub const JestPrettyFormat = struct { this.addForNewLine("StringContaining ".len); writer.writeAll("StringContaining "); } - this.printAs(.String, @TypeOf(writer_), writer_, substring_value, .String, enable_ansi_colors); + this.printAs(.String, @TypeOf(writer_), writer_, substring_value, .String, enable_ansi_colors) catch {}; // TODO: } else if (value.as(expect.ExpectStringMatching)) |matcher| { const test_value = expect.ExpectStringMatching.testValueGetCached(value) orelse return true; @@ -2103,7 +2102,7 @@ pub const JestPrettyFormat = struct { const original_quote_strings = this.quote_strings; if (test_value.isRegExp()) this.quote_strings = false; - this.printAs(.String, @TypeOf(writer_), writer_, test_value, .String, enable_ansi_colors); + this.printAs(.String, @TypeOf(writer_), writer_, test_value, .String, enable_ansi_colors) catch {}; // TODO: this.quote_strings = original_quote_strings; } else if (value.as(expect.ExpectCustomAsymmetricMatcher)) |instance| { const printed = instance.customPrint(value, this.globalThis, writer_, true) catch unreachable; @@ -2121,7 +2120,7 @@ pub const JestPrettyFormat = struct { this.addForNewLine(matcher_name.length() + 1); writer.print("{s}", .{matcher_name}); writer.writeAll(" "); - this.printAs(.Array, @TypeOf(writer_), writer_, args_value, .Array, enable_ansi_colors); + this.printAs(.Array, @TypeOf(writer_), writer_, args_value, .Array, enable_ansi_colors) catch {}; // TODO: } } else { return false; diff --git a/src/bun.js/test/snapshot.zig b/src/bun.js/test/snapshot.zig index 99eb561d40..b9380d4f68 100644 --- a/src/bun.js/test/snapshot.zig +++ b/src/bun.js/test/snapshot.zig @@ -115,7 +115,7 @@ pub const Snapshots = struct { if (this.file_buf.items.len == 0) return; const vm = VirtualMachine.get(); - const opts = js_parser.Parser.Options.init(vm.bundler.options.jsx, .js); + const opts = js_parser.Parser.Options.init(vm.transpiler.options.jsx, .js); var temp_log = logger.Log.init(this.allocator); const test_file = Jest.runner.?.files.get(file.id); @@ -141,7 +141,7 @@ pub const Snapshots = struct { opts, &temp_log, &source, - vm.bundler.options.define, + vm.transpiler.options.define, this.allocator, ); @@ -224,7 +224,7 @@ pub const Snapshots = struct { var success = true; const vm = VirtualMachine.get(); - const opts = js_parser.Parser.Options.init(vm.bundler.options.jsx, .js); + const opts = js_parser.Parser.Options.init(vm.transpiler.options.jsx, .js); for (this.inline_snapshots_to_write.keys(), this.inline_snapshots_to_write.values()) |file_id, *ils_info| { _ = arena_backing.reset(.retain_capacity); @@ -311,7 +311,7 @@ pub const Snapshots = struct { } try lexer.next(); var parser: bun.js_parser.TSXParser = undefined; - try bun.js_parser.TSXParser.init(arena, &log, &source, vm.bundler.options.define, lexer, opts, &parser); + try bun.js_parser.TSXParser.init(arena, &log, &source, vm.transpiler.options.define, lexer, opts, &parser); try parser.lexer.expect(.t_open_paren); const after_open_paren_loc = parser.lexer.loc().start; diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index b91b917496..c8ae517d90 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -152,7 +152,7 @@ pub const WebWorker = struct { } } - var resolved_entry_point: bun.resolver.Result = parent.bundler.resolveEntryPoint(str) catch { + var resolved_entry_point: bun.resolver.Result = parent.transpiler.resolveEntryPoint(str) catch { const out = logger.toJS(parent.global, bun.default_allocator, "Error resolving Worker entry point").toBunString(parent.global); error_message.* = out; return null; @@ -186,10 +186,10 @@ pub const WebWorker = struct { log("[{d}] WebWorker.create", .{this_context_id}); var spec_slice = specifier_str.toUTF8(bun.default_allocator); defer spec_slice.deinit(); - const prev_log = parent.bundler.log; + const prev_log = parent.transpiler.log; var temp_log = bun.logger.Log.init(bun.default_allocator); - parent.bundler.setLog(&temp_log); - defer parent.bundler.setLog(prev_log); + parent.transpiler.setLog(&temp_log); + defer parent.transpiler.setLog(prev_log); defer temp_log.deinit(); const preload_modules = if (preload_modules_ptr) |ptr| @@ -226,7 +226,7 @@ pub const WebWorker = struct { .execution_context_id = this_context_id, .mini = mini, .specifier = bun.default_allocator.dupe(u8, path) catch bun.outOfMemory(), - .store_fd = parent.bundler.resolver.store_fd, + .store_fd = parent.transpiler.resolver.store_fd, .name = brk: { if (!name_str.isEmpty()) { break :brk std.fmt.allocPrintZ(bun.default_allocator, "{}", .{name_str}) catch bun.outOfMemory(); @@ -274,14 +274,14 @@ pub const WebWorker = struct { this.arena = try bun.MimallocArena.init(); var vm = try JSC.VirtualMachine.initWorker(this, .{ .allocator = this.arena.?.allocator(), - .args = this.parent.bundler.options.transform_options, + .args = this.parent.transpiler.options.transform_options, .store_fd = this.store_fd, .graph = this.parent.standalone_module_graph, }); vm.allocator = this.arena.?.allocator(); vm.arena = &this.arena.?; - var b = &vm.bundler; + var b = &vm.transpiler; b.configureDefines() catch { this.flushLogs(); @@ -292,12 +292,12 @@ pub const WebWorker = struct { // TODO: we may have to clone other parts of vm state. this will be more // important when implementing vm.deinit() const map = try vm.allocator.create(bun.DotEnv.Map); - map.* = try vm.bundler.env.map.cloneWithAllocator(vm.allocator); + map.* = try vm.transpiler.env.map.cloneWithAllocator(vm.allocator); const loader = try vm.allocator.create(bun.DotEnv.Loader); loader.* = bun.DotEnv.Loader.init(map, vm.allocator); - vm.bundler.env = loader; + vm.transpiler.env = loader; vm.loadExtraEnvAndSourceCodePrinter(); vm.is_main_thread = false; @@ -337,7 +337,7 @@ pub const WebWorker = struct { // Prevent recursion vm.onUnhandledRejection = &JSC.VirtualMachine.onQuietUnhandledRejectionHandlerCaptureValue; - const error_instance = error_instance_or_exception.toError() orelse error_instance_or_exception; + var error_instance = error_instance_or_exception.toError() orelse error_instance_or_exception; var array = bun.MutableString.init(bun.default_allocator, 0) catch unreachable; defer array.deinit(); @@ -364,7 +364,13 @@ pub const WebWorker = struct { .flush = false, .max_depth = 32, }, - ); + ) catch |err| { + switch (err) { + error.JSError => {}, + error.OutOfMemory => globalObject.throwOutOfMemory() catch {}, + } + error_instance = globalObject.tryTakeException().?; + }; buffered_writer.flush() catch { bun.outOfMemory(); }; @@ -513,15 +519,18 @@ pub const WebWorker = struct { if (loop) |loop_| { loop_.internal_loop_data.jsc_vm = null; } + bun.uws.onThreadExit(); this.deinit(); if (vm_to_deinit) |vm| { vm.deinit(); // NOTE: deinit here isn't implemented, so freeing workers will leak the vm. } + bun.deleteAllPoolsForThreadExit(); if (arena) |*arena_| { arena_.deinit(); } + bun.exitThread(); } diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 9c1467bf5d..a154a8cb75 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -19,7 +19,6 @@ const default_allocator = bun.default_allocator; const FeatureFlags = bun.FeatureFlags; const ArrayBuffer = @import("../base.zig").ArrayBuffer; const Properties = @import("../base.zig").Properties; - const getAllocator = @import("../base.zig").getAllocator; const Environment = @import("../../env.zig"); @@ -44,6 +43,10 @@ const Request = JSC.WebCore.Request; const libuv = bun.windows.libuv; +const AWSCredentials = @import("../../s3.zig").AWSCredentials; +const S3MultiPartUpload = @import("../../s3.zig").MultiPartUpload; +const AWS = AWSCredentials; + const PathOrBlob = union(enum) { path: JSC.Node.PathOrFileDescriptor, blob: Blob, @@ -134,7 +137,25 @@ pub const Blob = struct { } pub fn hasContentTypeFromUser(this: *const Blob) bool { - return this.content_type_was_set or (this.store != null and this.store.?.data == .file); + return this.content_type_was_set or (this.store != null and (this.store.?.data == .file or this.store.?.data == .s3)); + } + + pub fn contentTypeOrMimeType(this: *const Blob) ?[]const u8 { + if (this.content_type.len > 0) { + return this.content_type; + } + if (this.store) |store| { + switch (store.data) { + .file => |file| { + return file.mime_type.value; + }, + .s3 => |s3| { + return s3.mime_type.value; + }, + else => return null, + } + } + return null; } pub fn isBunFile(this: *const Blob) bool { @@ -144,7 +165,16 @@ pub const Blob = struct { } const ReadFileUV = @import("./blob/ReadFile.zig").ReadFileUV; + pub fn doReadFromS3(this: *Blob, comptime Function: anytype, global: *JSGlobalObject) JSValue { + bloblog("doReadFromS3", .{}); + const WrappedFn = struct { + pub fn wrapped(b: *Blob, g: *JSGlobalObject, by: []u8) JSC.JSValue { + return JSC.toJSHostValue(g, Function(b, g, by, .clone)); + } + }; + return S3BlobDownloadTask.init(global, this, WrappedFn.wrapped); + } pub fn doReadFile(this: *Blob, comptime Function: anytype, global: *JSGlobalObject) JSValue { bloblog("doReadFile", .{}); @@ -263,6 +293,10 @@ pub const Blob = struct { blob.resolveSize(); } switch (store.data) { + .s3 => |_| { + // TODO: s3 + // we need to make this async and use s3Download/s3DownloadSlice + }, .file => |file| { // TODO: make this async + lazy @@ -464,6 +498,7 @@ pub const Blob = struct { const blob = Blob.new(Blob.findOrCreateFileFromPath( &path_or_fd, globalThis, + true, )); break :file blob; @@ -480,6 +515,7 @@ pub const Blob = struct { const blob = Blob.new(Blob.findOrCreateFileFromPath( &dest, globalThis, + true, )); break :file blob; @@ -682,6 +718,15 @@ pub const Blob = struct { { const store = this.store.?; switch (store.data) { + .s3 => |s3| { + try writer.writeAll(comptime Output.prettyFmt("S3Ref", enable_ansi_colors)); + try writer.print( + comptime Output.prettyFmt(" (\"{s}\")", enable_ansi_colors), + .{ + s3.pathlike.slice(), + }, + ); + }, .file => |file| { try writer.writeAll(comptime Output.prettyFmt("FileRef", enable_ansi_colors)); switch (file.pathlike) { @@ -839,7 +884,7 @@ pub const Blob = struct { ctx: JSC.C.JSContextRef, source_blob: *Blob, destination_blob: *Blob, - mkdirp_if_not_exists: bool, + options: WriteFileOptions, ) JSC.JSValue { const destination_type = std.meta.activeTag(destination_blob.store.?.data); @@ -867,6 +912,44 @@ pub const Blob = struct { return JSC.JSPromise.rejectedPromiseValue(ctx, result.toJS(ctx)); } + } else if (destination_type == .s3) { + + // create empty file + const s3 = &destination_blob.store.?.data.s3; + var aws_options = s3.getCredentialsWithOptions(options.extra_options, ctx) catch |err| { + return JSC.JSPromise.rejectedPromiseValue(ctx, ctx.takeException(err)); + }; + defer aws_options.deinit(); + + const Wrapper = struct { + promise: JSC.JSPromise.Strong, + pub usingnamespace bun.New(@This()); + + pub fn resolve(result: AWS.S3UploadResult, this: *@This()) void { + if (this.promise.globalObject()) |globalObject| { + switch (result) { + .success => this.promise.resolve(globalObject, JSC.jsNumber(0)), + .failure => |err| { + this.promise.rejectOnNextTick(globalObject, err.toJS(globalObject)); + }, + } + } + this.deinit(); + } + + fn deinit(this: *@This()) void { + this.promise.deinit(); + } + }; + + const promise = JSC.JSPromise.Strong.init(ctx); + const promise_value = promise.value(); + const proxy = ctx.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + aws_options.credentials.s3Upload(s3.path(), "", destination_blob.contentTypeOrMimeType(), proxy_url, @ptrCast(&Wrapper.resolve), Wrapper.new(.{ + .promise = promise, + })); + return promise_value; } return JSC.JSPromise.resolvedPromiseValue(ctx, JSC.JSValue.jsNumber(0)); @@ -891,7 +974,7 @@ pub const Blob = struct { *WriteFilePromise, write_file_promise, &WriteFilePromise.run, - mkdirp_if_not_exists, + options.mkdirp_if_not_exists orelse true, ); return promise_value; } @@ -902,7 +985,7 @@ pub const Blob = struct { *WriteFilePromise, write_file_promise, WriteFilePromise.run, - mkdirp_if_not_exists, + options.mkdirp_if_not_exists orelse true, ) catch unreachable; var task = WriteFile.WriteFileTask.createOnJSThread(bun.default_allocator, ctx, file_copier) catch bun.outOfMemory(); // Defer promise creation until we're just about to schedule the task @@ -920,7 +1003,7 @@ pub const Blob = struct { destination_blob.store.?, source_blob.store.?, ctx.bunVM().eventLoop(), - mkdirp_if_not_exists, + options.mkdirp_if_not_exists orelse true, destination_blob.size, ); } @@ -932,10 +1015,21 @@ pub const Blob = struct { destination_blob.offset, destination_blob.size, ctx, - mkdirp_if_not_exists, + options.mkdirp_if_not_exists orelse true, ) catch unreachable; file_copier.schedule(); return file_copier.promise.value(); + } else if (destination_type == .file and source_type == .s3) { + const s3 = &source_blob.store.?.data.s3; + if (JSC.WebCore.ReadableStream.fromJS(JSC.WebCore.ReadableStream.fromBlob( + ctx, + source_blob, + @truncate(s3.options.partSize * S3MultiPartUpload.OneMiB), + ), ctx)) |stream| { + return destination_blob.pipeReadableStreamToBlob(ctx, stream, options.extra_options); + } else { + return JSC.JSPromise.rejectedPromiseValue(ctx, ctx.createErrorInstance("Failed to stream bytes from s3 bucket", .{})); + } } else if (destination_type == .bytes and source_type == .bytes) { // If this is bytes <> bytes, we can just duplicate it // this is an edgecase @@ -946,41 +1040,95 @@ pub const Blob = struct { const cloned = Blob.new(clone); cloned.allocator = bun.default_allocator; return JSPromise.resolvedPromiseValue(ctx, cloned.toJS(ctx)); - } else if (destination_type == .bytes and source_type == .file) { - var fake_call_frame: [8]JSC.JSValue = undefined; - @memset(@as([*]u8, @ptrCast(&fake_call_frame))[0..@sizeOf(@TypeOf(fake_call_frame))], 0); - const blob_value = source_blob.getSlice(ctx, @as(*JSC.CallFrame, @ptrCast(&fake_call_frame))) catch .zero; // TODO: + } else if (destination_type == .bytes and (source_type == .file or source_type == .s3)) { + const blob_value = source_blob.getSliceFrom(ctx, 0, 0, "", false); return JSPromise.resolvedPromiseValue( ctx, blob_value, ); + } else if (destination_type == .s3) { + const s3 = &destination_blob.store.?.data.s3; + var aws_options = s3.getCredentialsWithOptions(options.extra_options, ctx) catch |err| { + return JSC.JSPromise.rejectedPromiseValue(ctx, ctx.takeException(err)); + }; + defer aws_options.deinit(); + const store = source_blob.store.?; + const proxy = ctx.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + switch (store.data) { + .bytes => |bytes| { + if (bytes.len > S3MultiPartUpload.MAX_SINGLE_UPLOAD_SIZE) { + if (JSC.WebCore.ReadableStream.fromJS(JSC.WebCore.ReadableStream.fromBlob( + ctx, + source_blob, + @truncate(s3.options.partSize * S3MultiPartUpload.OneMiB), + ), ctx)) |stream| { + return (if (options.extra_options != null) aws_options.credentials.dupe() else s3.getCredentials()).s3UploadStream(s3.path(), stream, ctx, aws_options.options, destination_blob.contentTypeOrMimeType(), proxy_url, null, undefined); + } else { + return JSC.JSPromise.rejectedPromiseValue(ctx, ctx.createErrorInstance("Failed to stream bytes to s3 bucket", .{})); + } + } else { + const Wrapper = struct { + store: *Store, + promise: JSC.JSPromise.Strong, + pub usingnamespace bun.New(@This()); + + pub fn resolve(result: AWS.S3UploadResult, this: *@This()) void { + if (this.promise.globalObject()) |globalObject| { + switch (result) { + .success => this.promise.resolve(globalObject, JSC.jsNumber(this.store.data.bytes.len)), + .failure => |err| { + this.promise.rejectOnNextTick(globalObject, err.toJS(globalObject)); + }, + } + } + this.deinit(); + } + + fn deinit(this: *@This()) void { + this.promise.deinit(); + this.store.deref(); + } + }; + store.ref(); + const promise = JSC.JSPromise.Strong.init(ctx); + const promise_value = promise.value(); + + aws_options.credentials.s3Upload(s3.path(), bytes.slice(), destination_blob.contentTypeOrMimeType(), proxy_url, @ptrCast(&Wrapper.resolve), Wrapper.new(.{ + .store = store, + .promise = promise, + })); + return promise_value; + } + }, + .file, .s3 => { + // stream + if (JSC.WebCore.ReadableStream.fromJS(JSC.WebCore.ReadableStream.fromBlob( + ctx, + source_blob, + @truncate(s3.options.partSize * S3MultiPartUpload.OneMiB), + ), ctx)) |stream| { + return (if (options.extra_options != null) aws_options.credentials.dupe() else s3.getCredentials()).s3UploadStream(s3.path(), stream, ctx, s3.options, destination_blob.contentTypeOrMimeType(), proxy_url, null, undefined); + } else { + return JSC.JSPromise.rejectedPromiseValue(ctx, ctx.createErrorInstance("Failed to stream bytes to s3 bucket", .{})); + } + }, + } } unreachable; } - pub fn writeFile(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - const arguments = callframe.arguments_old(3).slice(); - var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); - defer args.deinit(); - - // accept a path or a blob - var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); - defer { - if (path_or_blob == .path) { - path_or_blob.path.deinit(); - } - } - - var data = args.nextEat() orelse { - return globalThis.throwInvalidArguments("Bun.write(pathOrFdOrBlob, blob) expects a Blob-y thing to write", .{}); - }; - + const WriteFileOptions = struct { + mkdirp_if_not_exists: ?bool = null, + extra_options: ?JSValue = null, + }; + pub fn writeFileInternal(globalThis: *JSC.JSGlobalObject, path_or_blob_: *PathOrBlob, data: JSC.JSValue, options: WriteFileOptions) bun.JSError!JSC.JSValue { if (data.isEmptyOrUndefinedOrNull()) { return globalThis.throwInvalidArguments("Bun.write(pathOrFdOrBlob, blob) expects a Blob-y thing to write", .{}); } - + var path_or_blob = path_or_blob_.*; if (path_or_blob == .blob) { if (path_or_blob.blob.store == null) { return globalThis.throwInvalidArguments("Blob is detached", .{}); @@ -1000,22 +1148,7 @@ pub const Blob = struct { var needs_async = false; - var mkdirp_if_not_exists: ?bool = null; - - if (args.nextEat()) |options_object| { - if (options_object.isObject()) { - if (try options_object.getTruthy(globalThis, "createPath")) |create_directory| { - if (!create_directory.isBoolean()) { - return globalThis.throwInvalidArgumentType("write", "options.createPath", "boolean"); - } - mkdirp_if_not_exists = create_directory.toBoolean(); - } - } else if (!options_object.isEmptyOrUndefinedOrNull()) { - return globalThis.throwInvalidArgumentType("write", "options", "object"); - } - } - - if (mkdirp_if_not_exists) |mkdir| { + if (options.mkdirp_if_not_exists) |mkdir| { if (mkdir and path_or_blob == .blob and path_or_blob.blob.store != null and @@ -1033,7 +1166,7 @@ pub const Blob = struct { if (comptime !Environment.isWindows) { if (path_or_blob == .path or // If they try to set an offset, its a little more complicated so let's avoid that - (path_or_blob.blob.offset == 0 and + (path_or_blob.blob.offset == 0 and !path_or_blob.blob.isS3() and // Is this a file that is known to be a pipe? Let's avoid blocking the main thread on it. !(path_or_blob.blob.store != null and path_or_blob.blob.store.?.data == .file and @@ -1115,7 +1248,7 @@ pub const Blob = struct { // if path_or_blob is a path, convert it into a file blob var destination_blob: Blob = if (path_or_blob == .path) brk: { - break :brk Blob.findOrCreateFileFromPath(&path_or_blob.path, globalThis); + break :brk Blob.findOrCreateFileFromPath(&path_or_blob_.path, globalThis, true); } else path_or_blob.blob.dupe(); if (destination_blob.store == null) { @@ -1140,12 +1273,30 @@ pub const Blob = struct { _ = response.body.value.use(); return JSC.JSPromise.rejectedPromiseValue(globalThis, err_ref.toJS(globalThis)); }, - .Locked => { + .Locked => |*locked| { + if (destination_blob.isS3()) { + const s3 = &destination_blob.store.?.data.s3; + var aws_options = try s3.getCredentialsWithOptions(options.extra_options, globalThis); + defer aws_options.deinit(); + _ = response.body.value.toReadableStream(globalThis); + if (locked.readable.get()) |readable| { + if (readable.isDisturbed(globalThis)) { + destination_blob.detach(); + return globalThis.throwInvalidArguments("ReadableStream has already been used", .{}); + } + const proxy = globalThis.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + + return (if (options.extra_options != null) aws_options.credentials.dupe() else s3.getCredentials()).s3UploadStream(s3.path(), readable, globalThis, aws_options.options, destination_blob.contentTypeOrMimeType(), proxy_url, null, undefined); + } + destination_blob.detach(); + return globalThis.throwInvalidArguments("ReadableStream has already been used", .{}); + } var task = bun.new(WriteFileWaitFromLockedValueTask, .{ .globalThis = globalThis, .file_blob = destination_blob, .promise = JSC.JSPromise.Strong.init(globalThis), - .mkdirp_if_not_exists = mkdirp_if_not_exists orelse true, + .mkdirp_if_not_exists = options.mkdirp_if_not_exists orelse true, }); response.body.value.Locked.task = task; @@ -1171,12 +1322,29 @@ pub const Blob = struct { _ = request.body.value.use(); return JSC.JSPromise.rejectedPromiseValue(globalThis, err_ref.toJS(globalThis)); }, - .Locked => { + .Locked => |locked| { + if (destination_blob.isS3()) { + const s3 = &destination_blob.store.?.data.s3; + var aws_options = try s3.getCredentialsWithOptions(options.extra_options, globalThis); + defer aws_options.deinit(); + _ = request.body.value.toReadableStream(globalThis); + if (locked.readable.get()) |readable| { + if (readable.isDisturbed(globalThis)) { + destination_blob.detach(); + return globalThis.throwInvalidArguments("ReadableStream has already been used", .{}); + } + const proxy = globalThis.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + return (if (options.extra_options != null) aws_options.credentials.dupe() else s3.getCredentials()).s3UploadStream(s3.path(), readable, globalThis, aws_options.options, destination_blob.contentTypeOrMimeType(), proxy_url, null, undefined); + } + destination_blob.detach(); + return globalThis.throwInvalidArguments("ReadableStream has already been used", .{}); + } var task = bun.new(WriteFileWaitFromLockedValueTask, .{ .globalThis = globalThis, .file_blob = destination_blob, .promise = JSC.JSPromise.Strong.init(globalThis), - .mkdirp_if_not_exists = mkdirp_if_not_exists orelse true, + .mkdirp_if_not_exists = options.mkdirp_if_not_exists orelse true, }); request.body.value.Locked.task = task; @@ -1212,7 +1380,42 @@ pub const Blob = struct { } } - return writeFileWithSourceDestination(globalThis, &source_blob, &destination_blob, mkdirp_if_not_exists orelse true); + return writeFileWithSourceDestination(globalThis, &source_blob, &destination_blob, options); + } + pub fn writeFile(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + // accept a path or a blob + var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); + defer { + if (path_or_blob == .path) { + path_or_blob.path.deinit(); + } + } + + const data = args.nextEat() orelse { + return globalThis.throwInvalidArguments("Bun.write(pathOrFdOrBlob, blob) expects a Blob-y thing to write", .{}); + }; + var mkdirp_if_not_exists: ?bool = null; + const options = args.nextEat(); + if (options) |options_object| { + if (options_object.isObject()) { + if (try options_object.getTruthy(globalThis, "createPath")) |create_directory| { + if (!create_directory.isBoolean()) { + return globalThis.throwInvalidArgumentType("write", "options.createPath", "boolean"); + } + mkdirp_if_not_exists = create_directory.toBoolean(); + } + } else if (!options_object.isEmptyOrUndefinedOrNull()) { + return globalThis.throwInvalidArgumentType("write", "options", "object"); + } + } + return writeFileInternal(globalThis, &path_or_blob, data, .{ + .mkdirp_if_not_exists = mkdirp_if_not_exists, + .extra_options = options, + }); } const write_permissions = 0o664; @@ -1391,12 +1594,268 @@ pub const Blob = struct { return JSC.JSPromise.resolvedPromiseValue(globalThis, JSC.JSValue.jsNumber(written)); } + pub fn JSS3File_upload_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + // accept a path or a blob + var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); + errdefer { + if (path_or_blob == .path) { + path_or_blob.path.deinit(); + } + } + + if (path_or_blob == .blob and (path_or_blob.blob.store == null or path_or_blob.blob.store.?.data != .s3)) { + return globalThis.throwInvalidArguments("S3.upload(pathOrS3, blob) expects a S3 or path to upload", .{}); + } + + const data = args.nextEat() orelse { + return globalThis.throwInvalidArguments("S3.upload(pathOrS3, blob) expects a Blob-y thing to upload", .{}); + }; + + switch (path_or_blob) { + .path => |path| { + const options = args.nextEat(); + if (path == .fd) { + return globalThis.throwInvalidArguments("S3.upload(pathOrS3, blob) expects a S3 or path to upload", .{}); + } + var blob = try constructS3FileInternalStore(globalThis, path.path, options); + defer blob.deinit(); + + var blob_internal: PathOrBlob = .{ .blob = blob }; + return try writeFileInternal(globalThis, &blob_internal, data, .{ + .mkdirp_if_not_exists = false, + .extra_options = options, + }); + }, + .blob => return try writeFileInternal(globalThis, &path_or_blob, data, .{ + .mkdirp_if_not_exists = false, + .extra_options = args.nextEat(), + }), + } + } + + pub fn JSS3File_size_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + // accept a path or a blob + var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); + errdefer { + if (path_or_blob == .path) { + path_or_blob.path.deinit(); + } + } + + if (path_or_blob == .blob and (path_or_blob.blob.store == null or path_or_blob.blob.store.?.data != .s3)) { + return globalThis.throwInvalidArguments("S3.size(pathOrS3) expects a S3 or path to get size", .{}); + } + + switch (path_or_blob) { + .path => |path| { + const options = args.nextEat(); + if (path == .fd) { + return globalThis.throwInvalidArguments("S3.size(pathOrS3) expects a S3 or path to get size", .{}); + } + var blob = try constructS3FileInternalStore(globalThis, path.path, options); + defer blob.deinit(); + + return S3BlobStatTask.size(globalThis, &blob); + }, + .blob => |*blob| { + return getSize(blob, globalThis); + }, + } + } + pub fn JSS3File_exists_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + // accept a path or a blob + var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); + errdefer { + if (path_or_blob == .path) { + path_or_blob.path.deinit(); + } + } + + if (path_or_blob == .blob and (path_or_blob.blob.store == null or path_or_blob.blob.store.?.data != .s3)) { + return globalThis.throwInvalidArguments("S3.exists(pathOrS3) expects a S3 or path to check if it exists", .{}); + } + + switch (path_or_blob) { + .path => |path| { + const options = args.nextEat(); + if (path == .fd) { + return globalThis.throwInvalidArguments("S3.exists(pathOrS3) expects a S3 or path to check if it exists", .{}); + } + var blob = try constructS3FileInternalStore(globalThis, path.path, options); + defer blob.deinit(); + + return S3BlobStatTask.exists(globalThis, &blob); + }, + .blob => |*blob| { + return getExists(blob, globalThis, callframe); + }, + } + } + + pub export fn JSS3File__exists(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + return JSS3File_exists_(globalThis, callframe) catch |err| switch (err) { + error.JSError => .zero, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return .zero; + }, + }; + } + pub export fn JSS3File__size(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + return JSS3File_size_(globalThis, callframe) catch |err| switch (err) { + error.JSError => .zero, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return .zero; + }, + }; + } + pub export fn JSS3File__upload(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + return JSS3File_upload_(globalThis, callframe) catch |err| switch (err) { + error.JSError => .zero, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return .zero; + }, + }; + } + pub fn JSS3File_presign_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + // accept a path or a blob + var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); + errdefer { + if (path_or_blob == .path) { + path_or_blob.path.deinit(); + } + } + + if (path_or_blob == .blob and (path_or_blob.blob.store == null or path_or_blob.blob.store.?.data != .s3)) { + return globalThis.throwInvalidArguments("S3.presign(pathOrS3, options) expects a S3 or path to presign", .{}); + } + + switch (path_or_blob) { + .path => |path| { + if (path == .fd) { + return globalThis.throwInvalidArguments("S3.presign(pathOrS3, options) expects a S3 or path to presign", .{}); + } + const options = args.nextEat(); + var blob = try constructS3FileInternalStore(globalThis, path.path, options); + defer blob.deinit(); + return try getPresignUrlFrom(&blob, globalThis, options); + }, + .blob => return try getPresignUrlFrom(&path_or_blob.blob, globalThis, args.nextEat()), + } + } + + pub export fn JSS3File__presign(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + return JSS3File_presign_(globalThis, callframe) catch |err| switch (err) { + error.JSError => .zero, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return .zero; + }, + }; + } + pub fn JSS3File_unlink_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + // accept a path or a blob + var path_or_blob = try PathOrBlob.fromJSNoCopy(globalThis, &args); + errdefer { + if (path_or_blob == .path) { + path_or_blob.path.deinit(); + } + } + if (path_or_blob == .blob and (path_or_blob.blob.store == null or path_or_blob.blob.store.?.data != .s3)) { + return globalThis.throwInvalidArguments("S3.unlink(pathOrS3) expects a S3 or path to delete", .{}); + } + + switch (path_or_blob) { + .path => |path| { + if (path == .fd) { + return globalThis.throwInvalidArguments("S3.unlink(pathOrS3) expects a S3 or path to delete", .{}); + } + const options = args.nextEat(); + var blob = try constructS3FileInternalStore(globalThis, path.path, options); + defer blob.deinit(); + return try blob.store.?.data.s3.unlink(globalThis, options); + }, + .blob => |blob| { + return try blob.store.?.data.s3.unlink(globalThis, args.nextEat()); + }, + } + } + + pub export fn JSS3File__unlink(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { + return JSS3File_unlink_(globalThis, callframe) catch |err| switch (err) { + error.JSError => .zero, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return .zero; + }, + }; + } + pub export fn JSS3File__hasInstance(_: JSC.JSValue, _: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(JSC.conv) bool { + JSC.markBinding(@src()); + const blob = value.as(Blob) orelse return false; + return blob.isS3(); + } + pub export fn JSDOMFile__hasInstance(_: JSC.JSValue, _: *JSC.JSGlobalObject, value: JSC.JSValue) callconv(JSC.conv) bool { JSC.markBinding(@src()); const blob = value.as(Blob) orelse return false; return blob.is_jsdom_file; } + extern fn BUN__createJSS3FileConstructor(*JSC.JSGlobalObject) JSValue; + pub fn getJSS3FileConstructor( + globalObject: *JSC.JSGlobalObject, + _: *JSC.JSObject, + ) callconv(JSC.conv) JSValue { + return BUN__createJSS3FileConstructor(globalObject); + } + export fn JSS3File__construct(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) ?*Blob { + const vm = globalThis.bunVM(); + const arguments = callframe.arguments_old(2).slice(); + var args = JSC.Node.ArgumentsSlice.init(vm, arguments); + defer args.deinit(); + + const path_or_fd = (JSC.Node.PathLike.fromJS(globalThis, &args)) catch |err| switch (err) { + error.JSError => null, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return null; + }, + }; + if (path_or_fd == null) { + globalThis.throwInvalidArguments("Expected file path string", .{}) catch return null; + return null; + } + return constructS3FileInternal(globalThis, path_or_fd.?, args.nextEat()) catch |err| switch (err) { + error.JSError => null, + error.OutOfMemory => { + globalThis.throwOutOfMemory() catch {}; + return null; + }, + }; + } export fn JSDOMFile__construct(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) ?*Blob { return JSDOMFile__construct_(globalThis, callframe) catch |err| switch (err) { error.JSError => null, @@ -1431,7 +1890,6 @@ pub const Blob = struct { return globalThis.throwInvalidArguments("new Blob() expects an Array", .{}); }, }; - if (blob.store) |store_| { switch (store_.data) { .bytes => |*bytes| { @@ -1439,7 +1897,7 @@ pub const Blob = struct { (name_value_str.toUTF8WithoutRef(bun.default_allocator).clone(bun.default_allocator) catch unreachable).slice(), ); }, - .file => { + .s3, .file => { blob.name = name_value_str.dupeRef(); }, } @@ -1516,6 +1974,7 @@ pub const Blob = struct { store.data.bytes.len; }, .file => size += store.data.file.pathlike.estimatedSize(), + .s3 => size += store.data.s3.estimatedSize(), } } @@ -1532,6 +1991,63 @@ pub const Blob = struct { } } + fn constructS3FileInternalStore( + globalObject: *JSC.JSGlobalObject, + path: JSC.Node.PathLike, + options: ?JSC.JSValue, + ) bun.JSError!Blob { + + // get ENV config + var aws_options = try AWS.getCredentialsWithOptions(globalObject.bunVM().transpiler.env.getAWSCredentials(), options, globalObject); + defer aws_options.deinit(); + const store = Blob.Store.initS3(path, null, aws_options.credentials, bun.default_allocator) catch bun.outOfMemory(); + errdefer store.deinit(); + store.data.s3.options = aws_options.options; + + var blob = Blob.initWithStore(store, globalObject); + if (options) |opts| { + if (try opts.getTruthy(globalObject, "type")) |file_type| { + inner: { + if (file_type.isString()) { + var allocator = bun.default_allocator; + var str = file_type.toSlice(globalObject, bun.default_allocator); + defer str.deinit(); + const slice = str.slice(); + if (!strings.isAllASCII(slice)) { + break :inner; + } + blob.content_type_was_set = true; + if (globalObject.bunVM().mimeType(str.slice())) |entry| { + blob.content_type = entry.value; + break :inner; + } + const content_type_buf = allocator.alloc(u8, slice.len) catch bun.outOfMemory(); + blob.content_type = strings.copyLowercase(slice, content_type_buf); + blob.content_type_allocated = true; + } + } + } + } + return blob; + } + fn constructS3FileInternal( + globalObject: *JSC.JSGlobalObject, + path: JSC.Node.PathLike, + options: ?JSC.JSValue, + ) bun.JSError!*Blob { + var ptr = Blob.new(try constructS3FileInternalStore(globalObject, path, options)); + ptr.allocator = bun.default_allocator; + return ptr; + } + fn constructS3FileInternalJS( + globalObject: *JSC.JSGlobalObject, + path: JSC.Node.PathLike, + options: ?JSC.JSValue, + ) bun.JSError!JSC.JSValue { + var ptr = try constructS3FileInternal(globalObject, path, options); + return ptr.toJS(globalObject); + } + pub fn constructBunFile( globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, @@ -1544,13 +2060,18 @@ pub const Blob = struct { var path = (try JSC.Node.PathOrFileDescriptor.fromJS(globalObject, &args, bun.default_allocator)) orelse { return globalObject.throwInvalidArguments("Expected file path string or file descriptor", .{}); }; + const options = if (arguments.len >= 2) arguments[1] else null; + + if (path == .path) { + if (strings.startsWith(path.path.slice(), "s3://")) { + return try constructS3FileInternalJS(globalObject, path.path, options); + } + } defer path.deinitAndUnprotect(); - var blob = Blob.findOrCreateFileFromPath(&path, globalObject); - - if (arguments.len >= 2) { - const opts = arguments[1]; + var blob = Blob.findOrCreateFileFromPath(&path, globalObject, false); + if (options) |opts| { if (opts.isObject()) { if (try opts.getTruthy(globalObject, "type")) |file_type| { inner: { @@ -1584,10 +2105,34 @@ pub const Blob = struct { return ptr.toJS(globalObject); } - pub fn findOrCreateFileFromPath(path_or_fd: *JSC.Node.PathOrFileDescriptor, globalThis: *JSGlobalObject) Blob { + pub fn constructS3File( + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) bun.JSError!JSC.JSValue { + const vm = globalObject.bunVM(); + const arguments = callframe.arguments_old(2).slice(); + var args = JSC.Node.ArgumentsSlice.init(vm, arguments); + defer args.deinit(); + + const path = (try JSC.Node.PathLike.fromJS(globalObject, &args)) orelse { + return globalObject.throwInvalidArguments("Expected file path string", .{}); + }; + return constructS3FileInternalJS(globalObject, path, args.nextEat()); + } + + pub fn findOrCreateFileFromPath(path_or_fd: *JSC.Node.PathOrFileDescriptor, globalThis: *JSGlobalObject, comptime check_s3: bool) Blob { var vm = globalThis.bunVM(); const allocator = bun.default_allocator; - + if (check_s3) { + if (path_or_fd.* == .path) { + if (strings.startsWith(path_or_fd.path.slice(), "s3://")) { + const credentials = globalThis.bunVM().transpiler.env.getAWSCredentials(); + const copy = path_or_fd.*; + path_or_fd.* = .{ .path = .{ .string = bun.PathString.empty } }; + return Blob.initWithStore(Blob.Store.initS3(copy.path, null, credentials, allocator) catch bun.outOfMemory(), globalThis); + } + } + } const path: JSC.Node.PathOrFileDescriptor = brk: { switch (path_or_fd.*) { .path => { @@ -1655,10 +2200,18 @@ pub const Blob = struct { pub usingnamespace bun.New(@This()); + pub fn memoryCost(this: *const Store) usize { + return if (this.hasOneRef()) @sizeOf(@This()) + switch (this.data) { + .bytes => this.data.bytes.len, + .file => 0, + .s3 => |s3| s3.estimatedSize(), + } else 0; + } + pub fn size(this: *const Store) SizeType { return switch (this.data) { .bytes => this.data.bytes.len, - .file => Blob.max_size, + .s3, .file => Blob.max_size, }; } @@ -1667,6 +2220,7 @@ pub const Blob = struct { pub const Data = union(enum) { bytes: ByteStore, file: FileStore, + s3: S3Store, }; pub fn ref(this: *Store) void { @@ -1695,6 +2249,34 @@ pub const Blob = struct { this.deref(); } + pub fn initS3(pathlike: JSC.Node.PathLike, mime_type: ?http.MimeType, credentials: AWSCredentials, allocator: std.mem.Allocator) !*Store { + var path = pathlike; + // this actually protects/refs the pathlike + path.toThreadSafe(); + + const store = Blob.Store.new(.{ + .data = .{ + .s3 = S3Store.init( + path, + mime_type orelse brk: { + const sliced = path.slice(); + if (sliced.len > 0) { + var extname = std.fs.path.extension(sliced); + extname = std.mem.trim(u8, extname, "."); + if (http.MimeType.byExtensionNoDefault(extname)) |mime| { + break :brk mime; + } + } + break :brk null; + }, + credentials, + ), + }, + .allocator = allocator, + .ref_count = std.atomic.Value(u32).init(1), + }); + return store; + } pub fn initFile(pathlike: JSC.Node.PathOrFileDescriptor, mime_type: ?http.MimeType, allocator: std.mem.Allocator) !*Store { const store = Blob.Store.new(.{ .data = .{ @@ -1764,6 +2346,9 @@ pub const Blob = struct { } } }, + .s3 => |*s3| { + s3.deinit(allocator); + }, } this.destroy(); @@ -1792,6 +2377,14 @@ pub const Blob = struct { }, } }, + .s3 => |s3| { + const pathlike_tag: JSC.Node.PathOrFileDescriptor.SerializeTag = .path; + try writer.writeInt(u8, @intFromEnum(pathlike_tag), .little); + + const path_slice = s3.pathlike.slice(); + try writer.writeInt(u32, @as(u32, @truncate(path_slice.len)), .little); + try writer.writeAll(path_slice); + }, .bytes => |bytes| { const slice = bytes.slice(); try writer.writeInt(u32, @truncate(slice.len), .little); @@ -3146,6 +3739,17 @@ pub const Blob = struct { // milliseconds since ECMAScript epoch last_modified: JSC.JSTimeType = JSC.init_timestamp, + pub fn unlink(this: *const FileStore, globalThis: *JSC.JSGlobalObject) JSValue { + return switch (this.pathlike) { + .path => switch (globalThis.bunVM().nodeFS().unlink(.{ + .path = this.pathlike.path, + }, .sync)) { + .err => |err| JSC.JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)), + else => JSC.JSPromise.resolvedPromiseValue(globalThis, .true), + }, + .fd => JSC.JSPromise.resolvedPromiseValue(globalThis, globalThis.createInvalidArgs("Is not possible to unlink a file descriptor", .{})), + }; + } pub fn isSeekable(this: *const FileStore) ?bool { if (this.seekable) |seekable| { return seekable; @@ -3163,6 +3767,105 @@ pub const Blob = struct { } }; + pub const S3Store = struct { + pathlike: JSC.Node.PathLike, + mime_type: http.MimeType = http.MimeType.other, + credentials: ?*AWSCredentials, + options: S3MultiPartUpload.MultiPartUploadOptions = .{}, + pub fn isSeekable(_: *const @This()) ?bool { + return true; + } + + pub fn getCredentials(this: *const @This()) *AWSCredentials { + bun.assert(this.credentials != null); + return this.credentials.?; + } + + pub fn getCredentialsWithOptions(this: *const @This(), options: ?JSValue, globalObject: *JSC.JSGlobalObject) bun.JSError!AWS.AWSCredentialsWithOptions { + return AWS.getCredentialsWithOptions(this.getCredentials().*, options, globalObject); + } + + pub fn path(this: *@This()) []const u8 { + var path_name = bun.URL.parse(this.pathlike.slice()).s3Path(); + // normalize start and ending + if (strings.endsWith(path_name, "/")) { + path_name = path_name[0..path_name.len]; + } + if (strings.startsWith(path_name, "/")) { + path_name = path_name[1..]; + } + return path_name; + } + + pub fn unlink(this: *@This(), globalThis: *JSC.JSGlobalObject, extra_options: ?JSValue) bun.JSError!JSValue { + const Wrapper = struct { + promise: JSC.JSPromise.Strong, + + pub usingnamespace bun.New(@This()); + + pub fn resolve(result: AWS.S3DeleteResult, self: *@This()) void { + defer self.deinit(); + const globalObject = self.promise.globalObject().?; + switch (result) { + .success => { + self.promise.resolve(globalObject, .true); + }, + .not_found => { + const js_err = globalObject.createErrorInstance("File not found", .{}); + js_err.put(globalObject, ZigString.static("code"), ZigString.init("FileNotFound").toJS(globalObject)); + self.promise.reject(globalObject, js_err); + }, + .failure => |err| { + self.promise.rejectOnNextTick(globalObject, err.toJS(globalObject)); + }, + } + } + + fn deinit(self: *@This()) void { + self.promise.deinit(); + self.destroy(); + } + }; + const promise = JSC.JSPromise.Strong.init(globalThis); + const value = promise.value(); + const proxy_url = globalThis.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy = if (proxy_url) |url| url.href else null; + var aws_options = try this.getCredentialsWithOptions(extra_options, globalThis); + defer aws_options.deinit(); + aws_options.credentials.s3Delete(this.path(), @ptrCast(&Wrapper.resolve), Wrapper.new(.{ + .promise = promise, + }), proxy); + + return value; + } + + pub fn init(pathlike: JSC.Node.PathLike, mime_type: ?http.MimeType, credentials: AWSCredentials) S3Store { + return .{ + .credentials = credentials.dupe(), + .pathlike = pathlike, + .mime_type = mime_type orelse http.MimeType.other, + }; + } + pub fn estimatedSize(this: *const @This()) usize { + return this.pathlike.estimatedSize() + if (this.credentials) |credentials| credentials.estimatedSize() else 0; + } + + pub fn deinit(this: *@This(), allocator: std.mem.Allocator) void { + if (this.pathlike == .string) { + allocator.free(@constCast(this.pathlike.slice())); + } else { + this.pathlike.deinit(); + } + this.pathlike = .{ + .string = bun.PathString.empty, + }; + if (this.credentials) |credentials| { + credentials.deref(); + this.credentials = null; + } + } + }; + pub const ByteStore = struct { ptr: [*]u8 = undefined, len: SizeType = 0, @@ -3423,15 +4126,531 @@ pub const Blob = struct { return JSValue.jsBoolean(bun.isRegularFile(store.data.file.mode) or bun.C.S.ISFIFO(store.data.file.mode)); } + pub fn isS3(this: *const Blob) bool { + if (this.store) |store| { + return store.data == .s3; + } + return false; + } + + const S3BlobDownloadTask = struct { + blob: Blob, + globalThis: *JSC.JSGlobalObject, + promise: JSC.JSPromise.Strong, + poll_ref: bun.Async.KeepAlive = .{}, + + handler: S3ReadHandler, + usingnamespace bun.New(S3BlobDownloadTask); + pub const S3ReadHandler = *const fn (this: *Blob, globalthis: *JSGlobalObject, raw_bytes: []u8) JSValue; + + pub fn callHandler(this: *S3BlobDownloadTask, raw_bytes: []u8) JSValue { + return this.handler(&this.blob, this.globalThis, raw_bytes); + } + pub fn onS3DownloadResolved(result: AWS.S3DownloadResult, this: *S3BlobDownloadTask) void { + defer this.deinit(); + switch (result) { + .success => |response| { + const bytes = response.body.list.items; + if (this.blob.size == Blob.max_size) { + this.blob.size = @truncate(bytes.len); + } + JSC.AnyPromise.wrap(.{ .normal = this.promise.get() }, this.globalThis, S3BlobDownloadTask.callHandler, .{ this, bytes }); + }, + .not_found => { + const js_err = this.globalThis.createErrorInstance("File not found", .{}); + js_err.put(this.globalThis, ZigString.static("code"), ZigString.init("FileNotFound").toJS(this.globalThis)); + this.promise.reject(this.globalThis, js_err); + }, + .failure => |err| { + this.promise.rejectOnNextTick(this.globalThis, err.toJS(this.globalThis)); + }, + } + } + + pub fn init(globalThis: *JSC.JSGlobalObject, blob: *Blob, handler: S3BlobDownloadTask.S3ReadHandler) JSValue { + blob.store.?.ref(); + + const this = S3BlobDownloadTask.new(.{ + .globalThis = globalThis, + .blob = blob.*, + .promise = JSC.JSPromise.Strong.init(globalThis), + .handler = handler, + }); + const promise = this.promise.value(); + const env = this.globalThis.bunVM().transpiler.env; + const credentials = this.blob.store.?.data.s3.getCredentials(); + const path = this.blob.store.?.data.s3.path(); + + this.poll_ref.ref(globalThis.bunVM()); + if (blob.offset > 0) { + const len: ?usize = if (blob.size != Blob.max_size) @intCast(blob.size) else null; + const offset: usize = @intCast(blob.offset); + credentials.s3DownloadSlice(path, offset, len, @ptrCast(&S3BlobDownloadTask.onS3DownloadResolved), this, if (env.getHttpProxy(true, null)) |proxy| proxy.href else null); + } else if (blob.size == Blob.max_size) { + credentials.s3Download(path, @ptrCast(&S3BlobDownloadTask.onS3DownloadResolved), this, if (env.getHttpProxy(true, null)) |proxy| proxy.href else null); + } else { + const len: usize = @intCast(blob.size); + const offset: usize = @intCast(blob.offset); + credentials.s3DownloadSlice(path, offset, len, @ptrCast(&S3BlobDownloadTask.onS3DownloadResolved), this, if (env.getHttpProxy(true, null)) |proxy| proxy.href else null); + } + return promise; + } + + pub fn deinit(this: *S3BlobDownloadTask) void { + this.blob.store.?.deref(); + this.poll_ref.unrefOnNextTick(this.globalThis.bunVM()); + this.promise.deinit(); + this.destroy(); + } + }; + + const S3BlobStatTask = struct { + promise: JSC.JSPromise.Strong, + usingnamespace bun.New(S3BlobStatTask); + + pub fn onS3ExistsResolved(result: AWS.S3StatResult, this: *S3BlobStatTask) void { + defer this.deinit(); + const globalThis = this.promise.globalObject().?; + switch (result) { + .not_found => { + this.promise.resolve(globalThis, .false); + }, + .success => |_| { + // calling .exists() should not prevent it to download a bigger file + // this would make it download a slice of the actual value, if the file changes before we download it + // if (this.blob.size == Blob.max_size) { + // this.blob.size = @truncate(stat.size); + // } + this.promise.resolve(globalThis, .true); + }, + .failure => |err| { + this.promise.rejectOnNextTick(globalThis, err.toJS(globalThis)); + }, + } + } + + pub fn onS3SizeResolved(result: AWS.S3StatResult, this: *S3BlobStatTask) void { + defer this.deinit(); + const globalThis = this.promise.globalObject().?; + + switch (result) { + .not_found => { + const js_err = globalThis.createErrorInstance("File not Found", .{}); + js_err.put(globalThis, ZigString.static("code"), ZigString.static("FileNotFound").toJS(globalThis)); + this.promise.rejectOnNextTick(globalThis, js_err); + }, + .success => |stat| { + this.promise.resolve(globalThis, JSValue.jsNumber(stat.size)); + }, + .failure => |err| { + this.promise.rejectOnNextTick(globalThis, err.toJS(globalThis)); + }, + } + } + + pub fn exists(globalThis: *JSC.JSGlobalObject, blob: *Blob) JSValue { + const this = S3BlobStatTask.new(.{ + .promise = JSC.JSPromise.Strong.init(globalThis), + }); + const promise = this.promise.value(); + const credentials = blob.store.?.data.s3.getCredentials(); + const path = blob.store.?.data.s3.path(); + const env = globalThis.bunVM().transpiler.env; + + credentials.s3Stat(path, @ptrCast(&S3BlobStatTask.onS3ExistsResolved), this, if (env.getHttpProxy(true, null)) |proxy| proxy.href else null); + return promise; + } + + pub fn size(globalThis: *JSC.JSGlobalObject, blob: *Blob) JSValue { + const this = S3BlobStatTask.new(.{ + .promise = JSC.JSPromise.Strong.init(globalThis), + }); + const promise = this.promise.value(); + const credentials = blob.store.?.data.s3.getCredentials(); + const path = blob.store.?.data.s3.path(); + const env = globalThis.bunVM().transpiler.env; + + credentials.s3Stat(path, @ptrCast(&S3BlobStatTask.onS3SizeResolved), this, if (env.getHttpProxy(true, null)) |proxy| proxy.href else null); + return promise; + } + + pub fn deinit(this: *S3BlobStatTask) void { + this.promise.deinit(); + this.destroy(); + } + }; + + pub fn doWrite(this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(3).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + + const data = args.nextEat() orelse { + return globalThis.throwInvalidArguments("blob.write(pathOrFdOrBlob, blob) expects a Blob-y thing to write", .{}); + }; + if (data.isEmptyOrUndefinedOrNull()) { + return globalThis.throwInvalidArguments("blob.write(pathOrFdOrBlob, blob) expects a Blob-y thing to write", .{}); + } + var mkdirp_if_not_exists: ?bool = null; + const options = args.nextEat(); + if (options) |options_object| { + if (options_object.isObject()) { + if (try options_object.getTruthy(globalThis, "createPath")) |create_directory| { + if (!create_directory.isBoolean()) { + return globalThis.throwInvalidArgumentType("write", "options.createPath", "boolean"); + } + mkdirp_if_not_exists = create_directory.toBoolean(); + } + if (try options_object.getTruthy(globalThis, "type")) |content_type| { + //override the content type + if (!content_type.isString()) { + return globalThis.throwInvalidArgumentType("write", "options.type", "string"); + } + var content_type_str = content_type.toSlice(globalThis, bun.default_allocator); + defer content_type_str.deinit(); + const slice = content_type_str.slice(); + if (strings.isAllASCII(slice)) { + if (this.content_type_allocated) { + bun.default_allocator.free(this.content_type); + } + this.content_type_was_set = true; + + if (globalThis.bunVM().mimeType(slice)) |mime| { + this.content_type = mime.value; + } else { + const content_type_buf = bun.default_allocator.alloc(u8, slice.len) catch bun.outOfMemory(); + this.content_type = strings.copyLowercase(slice, content_type_buf); + this.content_type_allocated = true; + } + } + } + } else if (!options_object.isEmptyOrUndefinedOrNull()) { + return globalThis.throwInvalidArgumentType("write", "options", "object"); + } + } + var blob_internal: PathOrBlob = .{ .blob = this.* }; + return writeFileInternal(globalThis, &blob_internal, data, .{ .mkdirp_if_not_exists = mkdirp_if_not_exists, .extra_options = options }); + } + + pub fn doUnlink(this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const arguments = callframe.arguments_old(1).slice(); + var args = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments); + defer args.deinit(); + const store = this.store orelse { + return JSC.JSPromise.resolvedPromiseValue(globalThis, globalThis.createInvalidArgs("Blob is detached", .{})); + }; + return switch (store.data) { + .s3 => |*s3| try s3.unlink(globalThis, args.nextEat()), + .file => |file| file.unlink(globalThis), + else => JSC.JSPromise.resolvedPromiseValue(globalThis, globalThis.createInvalidArgs("Blob is read-only", .{})), + }; + } + // This mostly means 'can it be read?' pub fn getExists( this: *Blob, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame, ) bun.JSError!JSValue { + if (this.isS3()) { + return S3BlobStatTask.exists(globalThis, this); + } return JSC.JSPromise.resolvedPromiseValue(globalThis, this.getExistsSync()); } + pub fn getPresignUrlFrom(this: *Blob, globalThis: *JSC.JSGlobalObject, extra_options: ?JSValue) bun.JSError!JSValue { + if (this.isS3()) { + var method: bun.http.Method = .GET; + var expires: usize = 86400; // 1 day default + + var credentialsWithOptions: AWS.AWSCredentialsWithOptions = .{ + .credentials = this.store.?.data.s3.getCredentials().*, + }; + defer { + credentialsWithOptions.deinit(); + } + if (extra_options) |options| { + if (options.isObject()) { + if (try options.getTruthyComptime(globalThis, "method")) |method_| { + method = Method.fromJS(globalThis, method_) orelse { + return globalThis.throwInvalidArguments("method must be GET, PUT, DELETE or HEAD when using s3 protocol", .{}); + }; + } + if (try options.getOptional(globalThis, "expiresIn", i32)) |expires_| { + if (expires_ <= 0) return globalThis.throwInvalidArguments("expiresIn must be greather than 0", .{}); + expires = @intCast(expires_); + } + } + credentialsWithOptions = try this.store.?.data.s3.getCredentialsWithOptions(options, globalThis); + } + const path = this.store.?.data.s3.path(); + + const result = credentialsWithOptions.credentials.signRequest(.{ + .path = path, + .method = method, + }, .{ .expires = expires }) catch |sign_err| { + return AWS.throwSignError(sign_err, globalThis); + }; + defer result.deinit(); + var str = bun.String.fromUTF8(result.url); + return str.transferToJS(this.globalThis); + } + + return globalThis.throwError(error.NotSupported, "is only possible to presign s3:// files"); + } + + pub fn getPresignUrl(this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { + const args = callframe.arguments_old(1); + return getPresignUrlFrom(this, globalThis, if (args.len > 0) args.ptr[0] else null); + } + + pub const FileStreamWrapper = struct { + promise: JSC.JSPromise.Strong, + readable_stream_ref: JSC.WebCore.ReadableStream.Strong, + sink: *JSC.WebCore.FileSink, + + pub usingnamespace bun.New(@This()); + + pub fn deinit(this: *@This()) void { + this.promise.deinit(); + this.readable_stream_ref.deinit(); + this.sink.deref(); + this.destroy(); + } + }; + + pub const shim = JSC.Shimmer("Bun", "FileStreamWrapper", @This()); + pub fn onFileStreamResolveRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + var args = callframe.arguments_old(2); + var this = args.ptr[args.len - 1].asPromisePtr(FileStreamWrapper); + defer this.deinit(); + if (this.readable_stream_ref.get()) |stream| { + stream.done(globalThis); + } + this.readable_stream_ref.deinit(); + this.promise.resolve(globalThis, JSC.JSValue.jsNumber(0)); + return .undefined; + } + + pub fn onFileStreamRejectRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const args = callframe.arguments_old(2); + var this = args.ptr[args.len - 1].asPromisePtr(FileStreamWrapper); + defer this.sink.deinit(); + const err = args.ptr[0]; + + this.promise.rejectOnNextTick(globalThis, err); + + if (this.readable_stream_ref.get()) |stream| { + stream.cancel(globalThis); + this.readable_stream_ref.deinit(); + } + return .undefined; + } + pub const Export = shim.exportFunctions(.{ + .onResolveRequestStream = onFileStreamResolveRequestStream, + .onRejectRequestStream = onFileStreamRejectRequestStream, + }); + comptime { + const jsonResolveRequestStream = JSC.toJSHostFunction(onFileStreamResolveRequestStream); + @export(jsonResolveRequestStream, .{ .name = Export[0].symbol_name }); + const jsonRejectRequestStream = JSC.toJSHostFunction(onFileStreamRejectRequestStream); + @export(jsonRejectRequestStream, .{ .name = Export[1].symbol_name }); + } + pub fn pipeReadableStreamToBlob(this: *Blob, globalThis: *JSC.JSGlobalObject, readable_stream: JSC.WebCore.ReadableStream, extra_options: ?JSValue) JSC.JSValue { + var store = this.store orelse { + return JSC.JSPromise.rejectedPromiseValue(globalThis, globalThis.createErrorInstance("Blob is detached", .{})); + }; + + if (this.isS3()) { + const s3 = &this.store.?.data.s3; + var aws_options = s3.getCredentialsWithOptions(extra_options, globalThis) catch |err| { + return JSC.JSPromise.rejectedPromiseValue(globalThis, globalThis.takeException(err)); + }; + defer aws_options.deinit(); + + const path = s3.path(); + const proxy = globalThis.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + + return (if (extra_options != null) aws_options.credentials.dupe() else s3.getCredentials()).s3UploadStream(path, readable_stream, globalThis, aws_options.options, this.contentTypeOrMimeType(), proxy_url, null, undefined); + } + + if (store.data != .file) { + return JSC.JSPromise.rejectedPromiseValue(globalThis, globalThis.createErrorInstance("Blob is read-only", .{})); + } + + const file_sink = brk_sink: { + if (Environment.isWindows) { + const pathlike = store.data.file.pathlike; + const fd: bun.FileDescriptor = if (pathlike == .fd) pathlike.fd else brk: { + var file_path: bun.PathBuffer = undefined; + const path = pathlike.path.sliceZ(&file_path); + switch (bun.sys.open( + path, + bun.O.WRONLY | bun.O.CREAT | bun.O.NONBLOCK, + write_permissions, + )) { + .result => |result| { + break :brk result; + }, + .err => |err| { + return JSC.JSPromise.rejectedPromiseValue(globalThis, err.withPath(path).toJSC(globalThis)); + }, + } + unreachable; + }; + + const is_stdout_or_stderr = brk: { + if (pathlike != .fd) { + break :brk false; + } + + if (globalThis.bunVM().rare_data) |rare| { + if (store == rare.stdout_store) { + break :brk true; + } + + if (store == rare.stderr_store) { + break :brk true; + } + } + + break :brk switch (bun.FDTag.get(fd)) { + .stdout, .stderr => true, + else => false, + }; + }; + var sink = JSC.WebCore.FileSink.init(fd, this.globalThis.bunVM().eventLoop()); + sink.writer.owns_fd = pathlike != .fd; + + if (is_stdout_or_stderr) { + switch (sink.writer.startSync(fd, false)) { + .err => |err| { + sink.deref(); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)); + }, + else => {}, + } + } else { + switch (sink.writer.start(fd, true)) { + .err => |err| { + sink.deref(); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)); + }, + else => {}, + } + } + + break :brk_sink sink; + } + + var sink = JSC.WebCore.FileSink.init(bun.invalid_fd, this.globalThis.bunVM().eventLoop()); + + const input_path: JSC.WebCore.PathOrFileDescriptor = brk: { + if (store.data.file.pathlike == .fd) { + break :brk .{ .fd = store.data.file.pathlike.fd }; + } else { + break :brk .{ + .path = ZigString.Slice.fromUTF8NeverFree( + store.data.file.pathlike.path.slice(), + ).clone( + bun.default_allocator, + ) catch bun.outOfMemory(), + }; + } + }; + defer input_path.deinit(); + + const stream_start: JSC.WebCore.StreamStart = .{ + .FileSink = .{ + .input_path = input_path, + }, + }; + + switch (sink.start(stream_start)) { + .err => |err| { + sink.deref(); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)); + }, + else => {}, + } + break :brk_sink sink; + }; + var signal = &file_sink.signal; + + signal.* = JSC.WebCore.FileSink.JSSink.SinkSignal.init(.zero); + + // explicitly set it to a dead pointer + // we use this memory address to disable signals being sent + signal.clear(); + bun.assert(signal.isDead()); + + const assignment_result: JSC.JSValue = JSC.WebCore.FileSink.JSSink.assignToStream( + globalThis, + readable_stream.value, + file_sink, + @as(**anyopaque, @ptrCast(&signal.ptr)), + ); + + assignment_result.ensureStillAlive(); + + // assert that it was updated + bun.assert(!signal.isDead()); + + if (assignment_result.toError()) |err| { + file_sink.deref(); + return JSC.JSPromise.rejectedPromiseValue(globalThis, err); + } + + if (!assignment_result.isEmptyOrUndefinedOrNull()) { + globalThis.bunVM().drainMicrotasks(); + + assignment_result.ensureStillAlive(); + // it returns a Promise when it goes through ReadableStreamDefaultReader + if (assignment_result.asAnyPromise()) |promise| { + switch (promise.status(globalThis.vm())) { + .pending => { + const wrapper = FileStreamWrapper.new(.{ + .promise = JSC.JSPromise.Strong.init(globalThis), + .readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(readable_stream, globalThis), + .sink = file_sink, + }); + const promise_value = wrapper.promise.value(); + + assignment_result.then( + globalThis, + wrapper, + onFileStreamResolveRequestStream, + onFileStreamRejectRequestStream, + ); + return promise_value; + }, + .fulfilled => { + file_sink.deref(); + readable_stream.done(globalThis); + return JSC.JSPromise.resolvedPromiseValue(globalThis, JSC.JSValue.jsNumber(0)); + }, + .rejected => { + file_sink.deref(); + + readable_stream.cancel(globalThis); + + return JSC.JSPromise.rejectedPromiseValue(globalThis, promise.result(globalThis.vm())); + }, + } + } else { + file_sink.deref(); + + readable_stream.cancel(globalThis); + + return JSC.JSPromise.rejectedPromiseValue(globalThis, assignment_result); + } + } + file_sink.deref(); + + return JSC.JSPromise.resolvedPromiseValue(globalThis, JSC.JSValue.jsNumber(0)); + } + pub fn getWriter( this: *Blob, globalThis: *JSC.JSGlobalObject, @@ -3447,7 +4666,43 @@ pub const Blob = struct { var store = this.store orelse { return globalThis.throwInvalidArguments("Blob is detached", .{}); }; + if (this.isS3()) { + const s3 = &this.store.?.data.s3; + const path = s3.path(); + const proxy = globalThis.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + if (arguments.len > 0) { + const options = arguments.ptr[0]; + if (options.isObject()) { + if (try options.getTruthy(globalThis, "type")) |content_type| { + //override the content type + if (!content_type.isString()) { + return globalThis.throwInvalidArgumentType("write", "options.type", "string"); + } + var content_type_str = content_type.toSlice(globalThis, bun.default_allocator); + defer content_type_str.deinit(); + const slice = content_type_str.slice(); + if (strings.isAllASCII(slice)) { + if (this.content_type_allocated) { + bun.default_allocator.free(this.content_type); + } + this.content_type_was_set = true; + if (globalThis.bunVM().mimeType(slice)) |mime| { + this.content_type = mime.value; + } else { + const content_type_buf = bun.default_allocator.alloc(u8, slice.len) catch bun.outOfMemory(); + this.content_type = strings.copyLowercase(slice, content_type_buf); + this.content_type_allocated = true; + } + } + } + const credentialsWithOptions = try s3.getCredentialsWithOptions(options, globalThis); + return try credentialsWithOptions.credentials.dupe().s3WritableStream(path, globalThis, credentialsWithOptions.options, this.contentTypeOrMimeType(), proxy_url); + } + } + return try s3.getCredentials().s3WritableStream(path, globalThis, .{}, this.contentTypeOrMimeType(), proxy_url); + } if (store.data != .file) { return globalThis.throwInvalidArguments("Blob is read-only", .{}); } @@ -3556,6 +4811,30 @@ pub const Blob = struct { return sink.toJS(globalThis); } + pub fn getSliceFrom(this: *Blob, globalThis: *JSC.JSGlobalObject, relativeStart: i64, relativeEnd: i64, content_type: []const u8, content_type_was_allocated: bool) JSValue { + const offset = this.offset +| @as(SizeType, @intCast(relativeStart)); + const len = @as(SizeType, @intCast(@max(relativeEnd -| relativeStart, 0))); + + // This copies over the is_all_ascii flag + // which is okay because this will only be a <= slice + var blob = this.dupe(); + blob.offset = offset; + blob.size = len; + + // infer the content type if it was not specified + if (content_type.len == 0 and this.content_type.len > 0 and !this.content_type_allocated) { + blob.content_type = this.content_type; + } else { + blob.content_type = content_type; + } + blob.content_type_allocated = content_type_was_allocated; + blob.content_type_was_set = this.content_type_was_set or content_type_was_allocated; + + var blob_ = Blob.new(blob); + blob_.allocator = bun.default_allocator; + return blob_.toJS(globalThis); + } + /// https://w3c.github.io/FileAPI/#slice-method-algo /// The slice() method returns a new Blob object with bytes ranging from the /// optional start parameter up to but not including the optional end @@ -3647,26 +4926,7 @@ pub const Blob = struct { } } - const offset = this.offset +| @as(SizeType, @intCast(relativeStart)); - const len = @as(SizeType, @intCast(@max(relativeEnd -| relativeStart, 0))); - - // This copies over the is_all_ascii flag - // which is okay because this will only be a <= slice - var blob = this.dupe(); - blob.offset = offset; - blob.size = len; - - // infer the content type if it was not specified - if (content_type.len == 0 and this.content_type.len > 0 and !this.content_type_allocated) - content_type = this.content_type; - - blob.content_type = content_type; - blob.content_type_allocated = content_type_was_allocated; - blob.content_type_was_set = this.content_type_was_set or content_type_was_allocated; - - var blob_ = Blob.new(blob); - blob_.allocator = allocator; - return blob_.toJS(globalThis); + return this.getSliceFrom(globalThis, relativeStart, relativeEnd, content_type, content_type_was_allocated); } pub fn getMimeType(this: *const Blob) ?bun.http.MimeType { @@ -3727,6 +4987,17 @@ pub const Blob = struct { return if (this.getNameString()) |name| name.toJS(globalThis) else .undefined; } + pub fn getBucket( + this: *Blob, + globalThis: *JSC.JSGlobalObject, + ) JSValue { + if (this.getBucketName()) |name| { + var str = bun.String.createUTF8(name); + return str.transferToJS(globalThis); + } + return .undefined; + } + pub fn setName( this: *Blob, jsThis: JSC.JSValue, @@ -3769,12 +5040,38 @@ pub const Blob = struct { } else if (store.data == .bytes) { if (store.data.bytes.stored_name.slice().len > 0) return store.data.bytes.stored_name.slice(); + } else if (store.data == .s3) { + return store.data.s3.path(); } } return null; } + pub fn getBucketName( + this: *const Blob, + ) ?[]const u8 { + const store = this.store orelse return null; + if (store.data != .s3) return null; + const credentials = store.data.s3.getCredentials(); + var full_path = store.data.s3.path(); + if (strings.startsWith(full_path, "/")) { + full_path = full_path[1..]; + } + var bucket: []const u8 = credentials.bucket; + + if (bucket.len == 0) { + if (strings.indexOf(full_path, "/")) |end| { + bucket = full_path[0..end]; + if (bucket.len > 0) { + return bucket; + } + } + return null; + } + return bucket; + } + // TODO: Move this to a separate `File` object or BunFile pub fn getLastModified( this: *Blob, @@ -3783,7 +5080,7 @@ pub const Blob = struct { if (this.store) |store| { if (store.data == .file) { // last_modified can be already set during read. - if (store.data.file.last_modified == JSC.init_timestamp) { + if (store.data.file.last_modified == JSC.init_timestamp and !this.isS3()) { resolveFileStat(store); } return JSValue.jsNumber(store.data.file.last_modified); @@ -3826,8 +5123,11 @@ pub const Blob = struct { } } - pub fn getSize(this: *Blob, _: *JSC.JSGlobalObject) JSValue { + pub fn getSize(this: *Blob, globalThis: *JSC.JSGlobalObject) JSValue { if (this.size == Blob.max_size) { + if (this.isS3()) { + return S3BlobStatTask.size(globalThis, this); + } this.resolveSize(); if (this.size == Blob.max_size and this.store != null) { return JSC.jsNumber(std.math.inf(f64)); @@ -4182,7 +5482,7 @@ pub const Blob = struct { } pub fn needsToReadFile(this: *const Blob) bool { - return this.store != null and this.store.?.data == .file; + return this.store != null and (this.store.?.data == .file); } pub fn toStringWithBytes(this: *Blob, global: *JSGlobalObject, raw_bytes: []const u8, comptime lifetime: Lifetime) bun.JSError!JSValue { @@ -4274,6 +5574,9 @@ pub const Blob = struct { if (this.needsToReadFile()) { return this.doReadFile(toStringWithBytes, global); } + if (this.isS3()) { + return this.doReadFromS3(toStringWithBytes, global); + } const view_: []u8 = @constCast(this.sharedView()); @@ -4288,6 +5591,9 @@ pub const Blob = struct { if (this.needsToReadFile()) { return this.doReadFile(toJSONWithBytes, global); } + if (this.isS3()) { + return this.doReadFromS3(toJSONWithBytes, global); + } const view_ = this.sharedView(); @@ -4450,6 +5756,10 @@ pub const Blob = struct { return this.doReadFile(WithBytesFn, global); } + if (this.isS3()) { + return this.doReadFromS3(WithBytesFn, global); + } + const view_ = this.sharedView(); if (view_.len == 0) return JSC.ArrayBuffer.create(global, "", TypedArrayView); @@ -4461,6 +5771,9 @@ pub const Blob = struct { if (this.needsToReadFile()) { return this.doReadFile(toFormDataWithBytes, global); } + if (this.isS3()) { + return this.doReadFromS3(toFormDataWithBytes, global); + } const view_ = this.sharedView(); @@ -4770,6 +6083,15 @@ pub const AnyBlob = union(enum) { InternalBlob: InternalBlob, WTFStringImpl: bun.WTF.StringImpl, + /// Assumed that AnyBlob itself is covered by the caller. + pub fn memoryCost(this: *const AnyBlob) usize { + return switch (this.*) { + .Blob => |*blob| if (blob.store) |blob_store| blob_store.memoryCost() else 0, + .WTFStringImpl => |str| if (str.refCount() == 1) str.memoryCost() else 0, + .InternalBlob => |*internal_blob| internal_blob.memoryCost(), + }; + } + pub fn hasOneRef(this: *const AnyBlob) bool { if (this.store()) |s| { return s.hasOneRef(); @@ -5090,6 +6412,13 @@ pub const AnyBlob = union(enum) { }; } + pub fn isS3(self: *const @This()) bool { + return switch (self.*) { + .Blob => self.Blob.isS3(), + .WTFStringImpl, .InternalBlob => false, + }; + } + pub fn detach(self: *@This()) void { return switch (self.*) { .Blob => { @@ -5122,6 +6451,10 @@ pub const InternalBlob = struct { bytes: std.ArrayList(u8), was_string: bool = false, + pub fn memoryCost(this: *const @This()) usize { + return this.bytes.capacity; + } + pub fn toStringOwned(this: *@This(), globalThis: *JSC.JSGlobalObject) JSValue { const bytes_without_bom = strings.withoutUTF8BOM(this.bytes.items); if (strings.toUTF16Alloc(globalThis.allocator(), bytes_without_bom, false, false) catch &[_]u16{}) |out| { diff --git a/src/bun.js/webcore/blob/WriteFile.zig b/src/bun.js/webcore/blob/WriteFile.zig index 13804680ae..9560fc4cd2 100644 --- a/src/bun.js/webcore/blob/WriteFile.zig +++ b/src/bun.js/webcore/blob/WriteFile.zig @@ -709,7 +709,7 @@ pub const WriteFileWaitFromLockedValueTask = struct { => { var blob = value.use(); // TODO: this should be one promise not two! - const new_promise = Blob.writeFileWithSourceDestination(globalThis, &blob, &file_blob, this.mkdirp_if_not_exists); + const new_promise = Blob.writeFileWithSourceDestination(globalThis, &blob, &file_blob, .{ .mkdirp_if_not_exists = this.mkdirp_if_not_exists }); if (new_promise.asAnyPromise()) |p| { switch (p.unwrap(globalThis.vm(), .mark_handled)) { // Fulfill the new promise using the pending promise diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 8505ee8789..abdfa55d87 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -72,7 +72,7 @@ pub const Body = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("bodyUsed: ", enable_ansi_colors)); - formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.value == .Used), .BooleanObject, enable_ansi_colors); + try formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.value == .Used), .BooleanObject, enable_ansi_colors); if (this.value == .Blob) { try formatter.printComma(Writer, writer, enable_ansi_colors); @@ -89,7 +89,7 @@ pub const Body = struct { try formatter.printComma(Writer, writer, enable_ansi_colors); try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); - formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); + try formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); } } } @@ -414,6 +414,16 @@ pub const Body = struct { }; } + pub fn memoryCost(this: *const Value) usize { + return switch (this.*) { + .InternalBlob => this.InternalBlob.bytes.items.len, + .WTFStringImpl => this.WTFStringImpl.memoryCost(), + .Locked => this.Locked.sizeHint(), + // .InlineBlob => this.InlineBlob.sliceConst().len, + else => 0, + }; + } + pub fn estimatedSize(this: *const Value) usize { return switch (this.*) { .InternalBlob => this.InternalBlob.sliceConst().len, @@ -1101,9 +1111,9 @@ pub const Body = struct { var body = Body{ .value = Value{ .Null = {} } }; body.value = try Value.fromJS(globalThis, value); - if (body.value == .Blob) + if (body.value == .Blob) { assert(body.value.Blob.allocator == null); // owned by Body - + } return body; } }; diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index 6d745017ec..7274373be0 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -77,6 +77,10 @@ pub const Request = struct { pub const getBlobWithoutCallFrame = RequestMixin.getBlobWithoutCallFrame; pub const WeakRef = bun.WeakPtr(Request, .weak_ptr_data); + pub fn memoryCost(this: *const Request) usize { + return @sizeOf(Request) + this.request_context.memoryCost() + this.url.byteSlice().len + this.body.value.memoryCost(); + } + pub export fn Request__getUWSRequest( this: *Request, ) ?*uws.Request { @@ -223,7 +227,7 @@ pub const Request = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("headers: ", enable_ansi_colors)); - formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); + try formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); if (this.body.value == .Blob) { try writer.writeAll("\n"); @@ -243,7 +247,7 @@ pub const Request = struct { if (this.body.value.Locked.readable.get()) |stream| { try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); - formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); + try formatter.printAs(.Object, Writer, writer, stream.value, stream.value.jsType(), enable_ansi_colors); } } } diff --git a/src/bun.js/webcore/response.classes.ts b/src/bun.js/webcore/response.classes.ts index 157f0abc38..ba7e022fa3 100644 --- a/src/bun.js/webcore/response.classes.ts +++ b/src/bun.js/webcore/response.classes.ts @@ -10,6 +10,7 @@ export default [ estimatedSize: true, configurable: false, overridesToJS: true, + memoryCost: true, proto: { text: { fn: "getText" }, json: { fn: "getJSON" }, @@ -164,6 +165,16 @@ export default [ getter: "getLastModified", }, + // Non-standard, s3 + BunFile support + unlink: { fn: "doUnlink", length: 0 }, + write: { fn: "doWrite", length: 2 }, + // Non-standard, s3 support + bucket: { + cache: true, + getter: "getBucket", + }, + presign: { fn: "getPresignUrl", length: 1 }, + size: { getter: "getSize", }, diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index 644c853f4c..38e737ea52 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -55,6 +55,7 @@ const Async = bun.Async; const BoringSSL = bun.BoringSSL; const X509 = @import("../api/bun/x509.zig"); const PosixToWinNormalizer = bun.path.PosixToWinNormalizer; +const s3 = @import("../../s3.zig"); pub const Response = struct { const ResponseMixin = BodyMixin(@This()); @@ -143,7 +144,7 @@ pub const Response = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("ok: ", enable_ansi_colors)); - formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.isOK()), .BooleanObject, enable_ansi_colors); + try formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.isOK()), .BooleanObject, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); @@ -156,7 +157,7 @@ pub const Response = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("status: ", enable_ansi_colors)); - formatter.printAs(.Double, Writer, writer, JSC.JSValue.jsNumber(this.init.status_code), .NumberObject, enable_ansi_colors); + try formatter.printAs(.Double, Writer, writer, JSC.JSValue.jsNumber(this.init.status_code), .NumberObject, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); @@ -168,13 +169,13 @@ pub const Response = struct { try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("headers: ", enable_ansi_colors)); - formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); + try formatter.printAs(.Private, Writer, writer, this.getHeaders(formatter.globalThis), .DOMWrapper, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); try writer.writeAll(comptime Output.prettyFmt("redirected: ", enable_ansi_colors)); - formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.redirected), .BooleanObject, enable_ansi_colors); + try formatter.printAs(.Boolean, Writer, writer, JSC.JSValue.jsBoolean(this.redirected), .BooleanObject, enable_ansi_colors); formatter.printComma(Writer, writer, enable_ansi_colors) catch bun.outOfMemory(); try writer.writeAll("\n"); @@ -512,6 +513,37 @@ pub const Response = struct { pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*Response { const arguments = callframe.argumentsAsArray(2); + if (!arguments[0].isUndefinedOrNull() and arguments[0].isObject()) { + if (arguments[0].as(Blob)) |blob| { + if (blob.isS3()) { + if (!arguments[1].isEmptyOrUndefinedOrNull()) { + return globalThis.throwInvalidArguments("new Response(s3File) do not support ResponseInit options", .{}); + } + var response: Response = .{ + .init = Response.Init{ + .status_code = 302, + }, + .body = Body{ + .value = .{ .Empty = {} }, + }, + .url = bun.String.empty, + }; + + const result = blob.store.?.data.s3.getCredentials().signRequest(.{ + .path = blob.store.?.data.s3.path(), + .method = .GET, + }, .{ .expires = 15 * 60 }) catch |sign_err| { + return s3.AWSCredentials.throwSignError(sign_err, globalThis); + }; + defer result.deinit(); + response.init.headers = response.getOrCreateHeaders(globalThis); + response.redirected = true; + var headers_ref = response.init.headers.?; + headers_ref.put(.Location, result.url, globalThis); + return bun.new(Response, response); + } + } + } var init: Init = (brk: { if (arguments[1].isUndefinedOrNull()) { break :brk Init{ @@ -697,7 +729,14 @@ pub const Response = struct { }; const null_fd = bun.invalid_fd; +fn setHeaders(headers: *?Headers, new_headers: []const picohttp.Header, allocator: std.mem.Allocator) void { + var old = headers.*; + headers.* = Headers.fromPicoHttpHeaders(new_headers, allocator) catch bun.outOfMemory(); + if (old) |*headers_| { + headers_.deinit(); + } +} pub const Fetch = struct { const headers_string = "headers"; const method_string = "method"; @@ -911,6 +950,13 @@ pub const Fetch = struct { }; } + pub fn isS3(this: *const HTTPRequestBody) bool { + return switch (this.*) { + .AnyBlob => |*blob| blob.isS3(), + else => false, + }; + } + pub fn hasContentTypeFromUser(this: *HTTPRequestBody) bool { return switch (this.*) { .AnyBlob => |blob| blob.hasContentTypeFromUser(), @@ -1092,7 +1138,7 @@ pub const Fetch = struct { const globalThis = this.global_this; var response_stream = FetchTaskletStream.new(.{ - .task = this, + .task = .{ .fetch = this }, .buffer = .{}, .globalThis = globalThis, }).toSink(); @@ -1889,10 +1935,10 @@ pub const Fetch = struct { var proxy: ?ZigURL = null; if (fetch_options.proxy) |proxy_opt| { if (!proxy_opt.isEmpty()) { //if is empty just ignore proxy - proxy = fetch_options.proxy orelse jsc_vm.bundler.env.getHttpProxy(fetch_options.url); + proxy = fetch_options.proxy orelse jsc_vm.transpiler.env.getHttpProxyFor(fetch_options.url); } } else { - proxy = jsc_vm.bundler.env.getHttpProxy(fetch_options.url); + proxy = jsc_vm.transpiler.env.getHttpProxyFor(fetch_options.url); } if (fetch_tasklet.check_server_identity.has() and fetch_tasklet.reject_unauthorized) { @@ -2182,7 +2228,7 @@ pub const Fetch = struct { } const url = ZigURL.parse(url_str.toOwnedSlice(bun.default_allocator) catch bun.outOfMemory()); - if (!url.isHTTP() and !url.isHTTPS()) { + if (!url.isHTTP() and !url.isHTTPS() and !url.isS3()) { bun.default_allocator.free(url.href); return globalObject.throwInvalidArguments("URL must be HTTP or HTTPS", .{}); } @@ -2273,6 +2319,7 @@ pub const Fetch = struct { var signal: ?*JSC.WebCore.AbortSignal = null; // Custom Hostname var hostname: ?[]u8 = null; + var range: ?[]u8 = null; var unix_socket_path: ZigString.Slice = ZigString.Slice.empty; var url_proxy_buffer: []const u8 = ""; @@ -2311,6 +2358,10 @@ pub const Fetch = struct { bun.default_allocator.free(hn); hostname = null; } + if (range) |range_| { + bun.default_allocator.free(range_); + range = null; + } if (ssl_config) |conf| { ssl_config = null; @@ -2928,6 +2979,15 @@ pub const Fetch = struct { } hostname = _hostname.toOwnedSliceZ(allocator) catch bun.outOfMemory(); } + if (url.isS3()) { + if (headers_.fastGet(JSC.FetchHeaders.HTTPHeaderName.Range)) |_range| { + if (range) |range_| { + range = null; + allocator.free(range_); + } + range = _range.toOwnedSliceZ(allocator) catch bun.outOfMemory(); + } + } break :extract_headers Headers.from(headers_, allocator, .{ .body = body.getAnyBlob() }) catch bun.outOfMemory(); } @@ -3008,7 +3068,7 @@ pub const Fetch = struct { var cwd_buf: bun.PathBuffer = undefined; const cwd = if (Environment.isWindows) (bun.getcwd(&cwd_buf) catch |err| { return globalThis.throwError(err, "Failed to resolve file url"); - }) else globalThis.bunVM().bundler.fs.top_level_dir; + }) else globalThis.bunVM().transpiler.fs.top_level_dir; const fullpath = bun.path.joinAbsStringBuf( cwd, @@ -3039,6 +3099,7 @@ pub const Fetch = struct { break :blob Blob.findOrCreateFileFromPath( &pathlike, globalThis, + true, ); }; @@ -3056,8 +3117,8 @@ pub const Fetch = struct { } if (url.protocol.len > 0) { - if (!(url.isHTTP() or url.isHTTPS())) { - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "protocol must be http: or https:", .{}, ctx); + if (!(url.isHTTP() or url.isHTTPS() or url.isS3())) { + const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "protocol must be http:, https: or s3:", .{}, ctx); is_error = true; return JSPromise.rejectedPromiseValue(globalThis, err); } @@ -3078,7 +3139,22 @@ pub const Fetch = struct { } var http_body = body; + if (body.isS3()) { + prepare_body: { + // is a S3 file we can use chunked here + if (JSC.WebCore.ReadableStream.fromJS(JSC.WebCore.ReadableStream.fromBlob(globalThis, &body.AnyBlob.Blob, s3.MultiPartUpload.DefaultPartSize), globalThis)) |stream| { + var old = body; + defer old.detach(); + body = .{ .ReadableStream = JSC.WebCore.ReadableStream.Strong.init(stream, globalThis) }; + break :prepare_body; + } + const rejected_value = JSPromise.rejectedPromiseValue(globalThis, globalThis.createErrorInstance("Failed to start s3 stream", .{})); + body.detach(); + + return rejected_value; + } + } if (body.needsToReadFile()) { prepare_body: { const opened_fd_res: JSC.Maybe(bun.FileDescriptor) = switch (body.store().?.data.file.pathlike) { @@ -3174,6 +3250,179 @@ pub const Fetch = struct { } } + if (url.isS3()) { + // get ENV config + var credentialsWithOptions: s3.AWSCredentials.AWSCredentialsWithOptions = .{ + .credentials = globalThis.bunVM().transpiler.env.getAWSCredentials(), + .options = .{}, + }; + defer { + credentialsWithOptions.deinit(); + } + + if (options_object) |options| { + if (try options.getTruthyComptime(globalThis, "s3")) |s3_options| { + if (s3_options.isObject()) { + s3_options.ensureStillAlive(); + credentialsWithOptions = try s3.AWSCredentials.getCredentialsWithOptions(credentialsWithOptions.credentials, s3_options, globalThis); + } + } + } + + if (body == .ReadableStream) { + // we cannot direct stream to s3 we need to use multi part upload + defer body.ReadableStream.deinit(); + const Wrapper = struct { + promise: JSC.JSPromise.Strong, + url: ZigURL, + url_proxy_buffer: []const u8, + pub usingnamespace bun.New(@This()); + + pub fn resolve(result: s3.AWSCredentials.S3UploadResult, self: *@This()) void { + if (self.promise.globalObject()) |global| { + switch (result) { + .success => { + const response = bun.new(Response, Response{ + .body = .{ .value = .Empty }, + .redirected = false, + .init = .{ .method = .PUT, .status_code = 200 }, + .url = bun.String.createAtomIfPossible(self.url.href), + }); + const response_js = Response.makeMaybePooled(@as(js.JSContextRef, global), response); + response_js.ensureStillAlive(); + self.promise.resolve(global, response_js); + }, + .failure => |err| { + const response = bun.new(Response, Response{ + .body = .{ + .value = .{ + .InternalBlob = .{ + .bytes = std.ArrayList(u8).fromOwnedSlice(bun.default_allocator, bun.default_allocator.dupe(u8, err.message) catch bun.outOfMemory()), + .was_string = true, + }, + }, + }, + .redirected = false, + .init = .{ + .method = .PUT, + .status_code = 500, + .status_text = bun.String.createAtomIfPossible(err.code), + }, + .url = bun.String.createAtomIfPossible(self.url.href), + }); + const response_js = Response.makeMaybePooled(@as(js.JSContextRef, global), response); + response_js.ensureStillAlive(); + self.promise.resolve(global, response_js); + }, + } + } + bun.default_allocator.free(self.url_proxy_buffer); + self.destroy(); + } + }; + if (method != .PUT and method != .POST) { + return JSC.JSPromise.rejectedPromiseValue(globalThis, globalThis.createErrorInstance("Only POST and PUT do support body when using S3", .{})); + } + const promise = JSC.JSPromise.Strong.init(globalThis); + + const s3_stream = Wrapper.new(.{ + .url = url, + .url_proxy_buffer = url_proxy_buffer, + .promise = promise, + }); + + const promise_value = promise.value(); + const proxy_url = if (proxy) |p| p.href else ""; + _ = credentialsWithOptions.credentials.dupe().s3UploadStream( + url.s3Path(), + body.ReadableStream.get().?, + globalThis, + credentialsWithOptions.options, + if (headers) |h| h.getContentType() else null, + proxy_url, + @ptrCast(&Wrapper.resolve), + s3_stream, + ); + url = .{}; + url_proxy_buffer = ""; + return promise_value; + } + if (method == .POST) { + method = .PUT; + } + + var result = credentialsWithOptions.credentials.signRequest(.{ + .path = url.s3Path(), + .method = method, + }, null) catch |sign_err| { + is_error = true; + return JSPromise.rejectedPromiseValue(globalThis, s3.AWSCredentials.getJSSignError(sign_err, globalThis)); + }; + defer result.deinit(); + if (proxy) |proxy_| { + // proxy and url are in the same buffer lets replace it + const old_buffer = url_proxy_buffer; + defer allocator.free(old_buffer); + var buffer = allocator.alloc(u8, result.url.len + proxy_.href.len) catch bun.outOfMemory(); + bun.copy(u8, buffer[0..result.url.len], result.url); + bun.copy(u8, buffer[proxy_.href.len..], proxy_.href); + url_proxy_buffer = buffer; + + url = ZigURL.parse(url_proxy_buffer[0..result.url.len]); + proxy = ZigURL.parse(url_proxy_buffer[result.url.len..]); + } else { + // replace headers and url of the request + allocator.free(url_proxy_buffer); + url_proxy_buffer = result.url; + url = ZigURL.parse(result.url); + result.url = ""; // fetch now owns this + } + + const content_type = if (headers) |h| h.getContentType() else null; + + if (range) |range_| { + const _headers = result.headers(); + var headersWithRange: [5]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + .{ .name = "range", .value = range_ }, + }; + + setHeaders(&headers, &headersWithRange, allocator); + } else if (content_type) |ct| { + if (ct.len > 0) { + const _headers = result.headers(); + if (_headers.len > 4) { + var headersWithContentType: [6]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + _headers[4], + .{ .name = "Content-Type", .value = ct }, + }; + setHeaders(&headers, &headersWithContentType, allocator); + } else { + var headersWithContentType: [5]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + .{ .name = "Content-Type", .value = ct }, + }; + + setHeaders(&headers, &headersWithContentType, allocator); + } + } else { + setHeaders(&headers, result.headers(), allocator); + } + } else { + setHeaders(&headers, result.headers(), allocator); + } + } + // Only create this after we have validated all the input. // or else we will leak it var promise = JSPromise.Strong.init(globalThis); @@ -3262,7 +3511,21 @@ pub const Headers = struct { this.entries.deinit(this.allocator); this.buf.clearAndFree(this.allocator); } + pub fn getContentType(this: *const Headers) ?[]const u8 { + if (this.entries.len == 0 or this.buf.items.len == 0) { + return null; + } + const header_entries = this.entries.slice(); + const header_names = header_entries.items(.name); + const header_values = header_entries.items(.value); + for (header_names, 0..header_names.len) |name, i| { + if (bun.strings.eqlCaseInsensitiveASCII(this.asStr(name), "content-type", true)) { + return this.asStr(header_values[i]); + } + } + return null; + } pub fn asStr(this: *const Headers, ptr: Api.StringPointer) []const u8 { return if (ptr.offset + ptr.length <= this.buf.items.len) this.buf.items[ptr.offset..][0..ptr.length] @@ -3274,6 +3537,45 @@ pub const Headers = struct { body: ?*const AnyBlob = null, }; + pub fn fromPicoHttpHeaders(headers: []const picohttp.Header, allocator: std.mem.Allocator) !Headers { + const header_count = headers.len; + var result = Headers{ + .entries = .{}, + .buf = .{}, + .allocator = allocator, + }; + + var buf_len: usize = 0; + for (headers) |header| { + buf_len += header.name.len + header.value.len; + } + result.entries.ensureTotalCapacity(allocator, header_count) catch bun.outOfMemory(); + result.entries.len = headers.len; + result.buf.ensureTotalCapacityPrecise(allocator, buf_len) catch bun.outOfMemory(); + result.buf.items.len = buf_len; + var offset: u32 = 0; + for (headers, 0..headers.len) |header, i| { + const name_offset = offset; + bun.copy(u8, result.buf.items[offset..][0..header.name.len], header.name); + offset += @truncate(header.name.len); + const value_offset = offset; + bun.copy(u8, result.buf.items[offset..][0..header.value.len], header.value); + offset += @truncate(header.value.len); + + result.entries.set(i, .{ + .name = .{ + .offset = name_offset, + .length = @truncate(header.name.len), + }, + .value = .{ + .offset = value_offset, + .length = @truncate(header.value.len), + }, + }); + } + return result; + } + pub fn from(fetch_headers_ref: ?*FetchHeaders, allocator: std.mem.Allocator, options: Options) !Headers { var header_count: u32 = 0; var buf_len: u32 = 0; diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 346296cb34..0d437358ea 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -43,6 +43,7 @@ const Request = JSC.WebCore.Request; const assert = bun.assert; const Syscall = bun.sys; const uv = bun.windows.libuv; +const S3MultiPartUpload = @import("../../s3.zig").MultiPartUpload; const AnyBlob = JSC.WebCore.AnyBlob; pub const ReadableStream = struct { @@ -360,6 +361,9 @@ pub const ReadableStream = struct { .globalThis = globalThis, .context = .{ .event_loop = JSC.EventLoopHandle.init(globalThis.bunVM().eventLoop()), + .start_offset = blob.offset, + .max_size = if (blob.size != Blob.max_size) blob.size else null, + .lazy = .{ .blob = store, }, @@ -369,6 +373,14 @@ pub const ReadableStream = struct { return reader.toReadableStream(globalThis); }, + .s3 => |*s3| { + const credentials = s3.getCredentials(); + const path = s3.path(); + const proxy = globalThis.bunVM().transpiler.env.getHttpProxy(true, null); + const proxy_url = if (proxy) |p| p.href else null; + + return credentials.s3ReadableStream(path, blob.offset, if (blob.size != Blob.max_size) blob.size else null, proxy_url, globalThis); + }, } } @@ -1544,6 +1556,11 @@ pub const ArrayBufferSink = struct { return Sink.init(this); } + pub fn memoryCost(this: *const ArrayBufferSink) usize { + // Since this is a JSSink, the NewJSSink function does @sizeOf(JSSink) which includes @sizeOf(ArrayBufferSink). + return this.bytes.cap; + } + pub const JSSink = NewJSSink(@This(), "ArrayBufferSink"); }; @@ -1637,6 +1654,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { pub fn start(_: *@This()) void {} }; + pub fn memoryCost(this: *ThisSink) callconv(.C) usize { + return @sizeOf(ThisSink) + SinkType.memoryCost(&this.sink); + } + pub fn onClose(ptr: JSValue, reason: JSValue) callconv(.C) void { JSC.markBinding(@src()); @@ -1769,16 +1790,23 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return globalThis.throwValue(JSC.toTypeError(.ERR_INVALID_ARG_TYPE, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis)); } - const str = arg.getZigString(globalThis); - if (str.len == 0) { + const str = arg.toString(globalThis); + if (globalThis.hasException()) { + return .zero; + } + + const view = str.view(globalThis); + + if (view.isEmpty()) { return JSC.JSValue.jsNumber(0); } - if (str.is16Bit()) { - return this.sink.writeUTF16(.{ .temporary = bun.ByteList.initConst(std.mem.sliceAsBytes(str.utf16SliceAligned())) }).toJS(globalThis); + defer str.ensureStillAlive(); + if (view.is16Bit()) { + return this.sink.writeUTF16(.{ .temporary = bun.ByteList.initConst(std.mem.sliceAsBytes(view.utf16SliceAligned())) }).toJS(globalThis); } - return this.sink.writeLatin1(.{ .temporary = bun.ByteList.initConst(str.slice()) }).toJS(globalThis); + return this.sink.writeLatin1(.{ .temporary = bun.ByteList.initConst(view.slice()) }).toJS(globalThis); } pub fn writeUTF8(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -1806,16 +1834,22 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { const arg = args[0]; - const str = arg.getZigString(globalThis); - if (str.len == 0) { + const str = arg.toString(globalThis); + if (globalThis.hasException()) { + return .zero; + } + + const view = str.view(globalThis); + if (view.isEmpty()) { return JSC.JSValue.jsNumber(0); } + defer str.ensureStillAlive(); if (str.is16Bit()) { - return this.sink.writeUTF16(.{ .temporary = str.utf16SliceAligned() }).toJS(globalThis); + return this.sink.writeUTF16(.{ .temporary = view.utf16SliceAligned() }).toJS(globalThis); } - return this.sink.writeLatin1(.{ .temporary = str.slice() }).toJS(globalThis); + return this.sink.writeLatin1(.{ .temporary = view.slice() }).toJS(globalThis); } pub fn close(globalThis: *JSGlobalObject, sink_ptr: ?*anyopaque) callconv(.C) JSValue { @@ -1951,6 +1985,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { @export(jsConstruct, .{ .name = shim.symbolName("construct") }); @export(endWithSink, .{ .name = shim.symbolName("endWithSink") }); @export(updateRef, .{ .name = shim.symbolName("updateRef") }); + @export(memoryCost, .{ .name = shim.symbolName("memoryCost") }); shim.assertJSFunction(.{ write, @@ -1986,7 +2021,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { // TODO: make this JSGlobalObject local // for better security -const ByteListPool = ObjectPool( +pub const ByteListPool = ObjectPool( bun.ByteList, null, true, @@ -2029,6 +2064,14 @@ pub fn HTTPServerWritable(comptime ssl: bool) type { this.signal = signal; } + // Don't include @sizeOf(This) because it's already included in the memoryCost of the sink + pub fn memoryCost(this: *@This()) usize { + // TODO: include Socket send buffer size. We can't here because we + // don't track if it's still accessible. + // Since this is a JSSink, the NewJSSink function does @sizeOf(JSSink) which includes @sizeOf(ArrayBufferSink). + return this.buffer.cap; + } + fn handleWrote(this: *@This(), amount1: usize) void { defer log("handleWrote: {d} offset: {d}, {d}", .{ amount1, this.offset, this.buffer.len }); const amount = @as(Blob.SizeType, @truncate(amount1)); @@ -2570,7 +2613,12 @@ pub fn HTTPServerWritable(comptime ssl: bool) type { if (this.pooled_buffer) |pooled| { this.buffer.len = 0; + if (this.buffer.cap > 64 * 1024) { + this.buffer.deinitWithAllocator(bun.default_allocator); + this.buffer = bun.ByteList.init(""); + } pooled.data = this.buffer; + this.buffer = bun.ByteList.init(""); this.pooled_buffer = null; pooled.release(); @@ -2603,17 +2651,34 @@ pub fn HTTPServerWritable(comptime ssl: bool) type { pub const HTTPSResponseSink = HTTPServerWritable(true); pub const HTTPResponseSink = HTTPServerWritable(false); pub const FetchTaskletChunkedRequestSink = struct { - task: ?*JSC.WebCore.Fetch.FetchTasklet = null, + task: ?HTTPWritableStream = null, signal: Signal = .{}, globalThis: *JSGlobalObject = undefined, highWaterMark: Blob.SizeType = 2048, buffer: bun.io.StreamBuffer, ended: bool = false, done: bool = false, + encoded: bool = true, + + endPromise: JSC.JSPromise.Strong = .{}, + auto_flusher: AutoFlusher = AutoFlusher{}, pub usingnamespace bun.New(FetchTaskletChunkedRequestSink); + const HTTPWritableStream = union(enum) { + fetch: *JSC.WebCore.Fetch.FetchTasklet, + s3_upload: *S3MultiPartUpload, + }; + fn getHighWaterMark(this: *@This()) Blob.SizeType { + if (this.task) |task| { + return switch (task) { + .s3_upload => |s3| @truncate(s3.partSizeInBytes()), + else => this.highWaterMark, + }; + } + return this.highWaterMark; + } fn unregisterAutoFlusher(this: *@This()) void { if (this.auto_flusher.registered) AutoFlusher.unregisterDeferredMicrotaskWithTypeUnchecked(@This(), this, this.globalThis.bunVM()); @@ -2642,6 +2707,7 @@ pub const FetchTaskletChunkedRequestSink = struct { if (this.ended) { return .{ .result = {} }; } + switch (stream_start) { .chunk_size => |chunk_size| { if (chunk_size > 0) { @@ -2671,9 +2737,23 @@ pub const FetchTaskletChunkedRequestSink = struct { this.buffer = .{}; buffer.deinit(); + this.detachWritable(); + } + + fn detachWritable(this: *@This()) void { if (this.task) |task| { this.task = null; - task.deref(); + switch (task) { + inline .fetch, .s3_upload => |writable| { + writable.deref(); + }, + } + } + } + + fn sendRequestData(writable: HTTPWritableStream, data: []const u8, is_last: bool) void { + switch (writable) { + inline .fetch, .s3_upload => |task| task.sendRequestData(data, is_last), } } @@ -2682,19 +2762,22 @@ pub const FetchTaskletChunkedRequestSink = struct { if (this.task) |task| { if (is_last) this.done = true; + if (this.encoded) { + if (data.len == 0) { + sendRequestData(task, bun.http.end_of_chunked_http1_1_encoding_response_body, true); + return; + } - if (data.len == 0) { - task.sendRequestData(bun.http.end_of_chunked_http1_1_encoding_response_body, true); - return; - } - - // chunk encoding is really simple - if (is_last) { - const chunk = std.fmt.allocPrint(bun.default_allocator, "{x}\r\n{s}\r\n0\r\n\r\n", .{ data.len, data }) catch return error.OOM; - task.sendRequestData(chunk, true); + // chunk encoding is really simple + if (is_last) { + const chunk = std.fmt.allocPrint(bun.default_allocator, "{x}\r\n{s}\r\n0\r\n\r\n", .{ data.len, data }) catch return error.OOM; + sendRequestData(task, chunk, true); + } else { + const chunk = std.fmt.allocPrint(bun.default_allocator, "{x}\r\n{s}\r\n", .{ data.len, data }) catch return error.OOM; + sendRequestData(task, chunk, false); + } } else { - const chunk = std.fmt.allocPrint(bun.default_allocator, "{x}\r\n{s}\r\n", .{ data.len, data }) catch return error.OOM; - task.sendRequestData(chunk, false); + sendRequestData(task, data, is_last); } } } @@ -2746,14 +2829,14 @@ pub const FetchTaskletChunkedRequestSink = struct { const bytes = data.slice(); const len = @as(Blob.SizeType, @truncate(bytes.len)); - if (this.buffer.size() == 0 and len >= this.highWaterMark) { + if (this.buffer.size() == 0 and len >= this.getHighWaterMark()) { // fast path: // - large-ish chunk this.send(bytes, false) catch { return .{ .err = Syscall.Error.fromCode(.NOMEM, .write) }; }; return .{ .owned = len }; - } else if (this.buffer.size() + len >= this.highWaterMark) { + } else if (this.buffer.size() + len >= this.getHighWaterMark()) { _ = this.buffer.write(bytes) catch { return .{ .err = Syscall.Error.fromCode(.NOMEM, .write) }; }; @@ -2780,7 +2863,7 @@ pub const FetchTaskletChunkedRequestSink = struct { const bytes = data.slice(); const len = @as(Blob.SizeType, @truncate(bytes.len)); - if (this.buffer.size() == 0 and len >= this.highWaterMark) { + if (this.buffer.size() == 0 and len >= this.getHighWaterMark()) { // common case if (strings.isAllASCII(bytes)) { // fast path: @@ -2799,7 +2882,7 @@ pub const FetchTaskletChunkedRequestSink = struct { return .{ .err = Syscall.Error.fromCode(.NOMEM, .write) }; }; return .{ .owned = len }; - } else if (this.buffer.size() + len >= this.highWaterMark) { + } else if (this.buffer.size() + len >= this.getHighWaterMark()) { // kinda fast path: // - combined chunk is large enough to flush automatically this.buffer.writeLatin1(bytes) catch { @@ -2831,7 +2914,7 @@ pub const FetchTaskletChunkedRequestSink = struct { }; const readable = this.buffer.slice(); - if (readable.len >= this.highWaterMark) { + if (readable.len >= this.getHighWaterMark()) { _ = this.internalFlush() catch { return .{ .err = Syscall.Error.fromCode(.NOMEM, .write) }; }; @@ -2856,19 +2939,30 @@ pub const FetchTaskletChunkedRequestSink = struct { return .{ .result = {} }; } pub fn endFromJS(this: *@This(), _: *JSGlobalObject) JSC.Maybe(JSValue) { - if (this.ended) { + if (!this.ended) { + if (this.done) { + this.ended = true; + this.signal.close(null); + this.finalize(); + } else { + _ = this.end(null); + } + } + const promise = this.endPromise.valueOrEmpty(); + if (promise.isEmptyOrUndefinedOrNull()) { return .{ .result = JSC.JSValue.jsNumber(0) }; } - - if (this.done) { - this.ended = true; - this.signal.close(null); - this.finalize(); - return .{ .result = JSC.JSValue.jsNumber(0) }; - } - _ = this.end(null); - return .{ .result = JSC.JSValue.jsNumber(0) }; + return .{ .result = promise }; } + pub fn toJS(this: *@This(), globalThis: *JSGlobalObject) JSValue { + return JSSink.createObject(globalThis, this, 0); + } + + pub fn memoryCost(this: *const @This()) usize { + // Since this is a JSSink, the NewJSSink function does @sizeOf(JSSink) which includes @sizeOf(ArrayBufferSink). + return this.buffer.memoryCost(); + } + const name = "FetchTaskletChunkedRequestSink"; pub const JSSink = NewJSSink(@This(), name); }; @@ -2889,6 +2983,7 @@ pub fn ReadableStreamSource( comptime deinit_fn: fn (this: *Context) void, comptime setRefUnrefFn: ?fn (this: *Context, enable: bool) void, comptime drainInternalBuffer: ?fn (this: *Context) bun.ByteList, + comptime memoryCostFn: ?fn (this: *const Context) usize, comptime toBufferedValue: ?fn (this: *Context, globalThis: *JSC.JSGlobalObject, action: BufferedReadableStreamAction) bun.JSError!JSC.JSValue, ) type { return struct { @@ -3053,6 +3148,14 @@ pub fn ReadableStreamSource( pub const arrayBufferFromJS = JSReadableStreamSource.arrayBuffer; pub const blobFromJS = JSReadableStreamSource.blob; pub const bytesFromJS = JSReadableStreamSource.bytes; + + pub fn memoryCost(this: *const ReadableStreamSourceType) usize { + if (memoryCostFn) |function| { + return function(&this.context) + @sizeOf(@This()); + } + return @sizeOf(@This()); + } + pub const JSReadableStreamSource = struct { pub fn pull(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue { JSC.markBinding(@src()); @@ -3310,6 +3413,11 @@ pub const FileSink = struct { pub const IOWriter = bun.io.StreamingWriter(@This(), onWrite, onError, onReady, onClose); pub const Poll = IOWriter; + pub fn memoryCost(this: *const FileSink) usize { + // Since this is a JSSink, the NewJSSink function does @sizeOf(JSSink) which includes @sizeOf(FileSink). + return this.writer.memoryCost(); + } + fn Bun__ForceFileSinkToBeSynchronousOnWindows(globalObject: *JSC.JSGlobalObject, jsvalue: JSC.JSValue) callconv(.C) void { comptime bun.assert(Environment.isWindows); @@ -3813,6 +3921,8 @@ pub const FileReader = struct { pending_view: []u8 = &.{}, fd: bun.FileDescriptor = bun.invalid_fd, start_offset: ?usize = null, + max_size: ?usize = null, + total_readed: usize = 0, started: bool = false, waiting_for_onReaderDone: bool = false, event_loop: JSC.EventLoopHandle, @@ -3968,7 +4078,7 @@ pub const FileReader = struct { var file_type: bun.io.FileType = .file; if (this.lazy == .blob) { switch (this.lazy.blob.data) { - .bytes => @panic("Invalid state in FileReader: expected file "), + .s3, .bytes => @panic("Invalid state in FileReader: expected file "), .file => |*file| { defer { this.lazy.blob.deref(); @@ -4091,15 +4201,32 @@ pub const FileReader = struct { } pub fn onReadChunk(this: *@This(), init_buf: []const u8, state: bun.io.ReadState) bool { - const buf = init_buf; + var buf = init_buf; log("onReadChunk() = {d} ({s})", .{ buf.len, @tagName(state) }); if (this.done) { this.reader.close(); return false; } + var close = false; + defer if (close) this.reader.close(); + var hasMore = state != .eof; - const hasMore = state != .eof; + if (buf.len > 0) { + if (this.max_size) |max_size| { + if (this.total_readed >= max_size) return false; + const len = @min(max_size - this.total_readed, buf.len); + if (buf.len > len) { + buf = buf[0..len]; + } + this.total_readed += len; + + if (buf.len == 0) { + close = true; + hasMore = false; + } + } + } if (this.read_inside_on_pull != .none) { switch (this.read_inside_on_pull) { @@ -4420,6 +4547,11 @@ pub const FileReader = struct { return this.reader.setRawMode(flag); } + pub fn memoryCost(this: *const FileReader) usize { + // ReadableStreamSource covers @sizeOf(FileReader) + return this.reader.memoryCost(); + } + pub const Source = ReadableStreamSource( @This(), "File", @@ -4429,6 +4561,7 @@ pub const FileReader = struct { deinit, setRefOrUnref, drain, + memoryCost, null, ); }; @@ -4570,6 +4703,14 @@ pub const ByteBlobLoader = struct { return .zero; } + pub fn memoryCost(this: *const ByteBlobLoader) usize { + // ReadableStreamSource covers @sizeOf(FileReader) + if (this.store) |store| { + return store.memoryCost(); + } + return 0; + } + pub const Source = ReadableStreamSource( @This(), "Blob", @@ -4579,6 +4720,7 @@ pub const ByteBlobLoader = struct { deinit, null, drain, + memoryCost, toBufferedValue, ); }; @@ -4633,6 +4775,8 @@ pub const ByteStream = struct { size_hint: Blob.SizeType = 0, buffer_action: ?BufferAction = null, + const log = Output.scoped(.ByteStream, false); + const BufferAction = union(BufferedReadableStreamAction) { text: JSC.JSPromise.Strong, arrayBuffer: JSC.JSPromise.Strong, @@ -4737,6 +4881,9 @@ pub const ByteStream = struct { if (stream == .owned) allocator.free(stream.owned.slice()); if (stream == .owned_and_done) allocator.free(stream.owned_and_done.slice()); } + this.has_received_last_chunk = stream.isDone(); + + log("ByteStream.onData already done... do nothing", .{}); return; } @@ -4760,6 +4907,8 @@ pub const ByteStream = struct { this.buffer_action = null; } + log("ByteStream.onData err action.reject()", .{}); + action.reject(stream.err); return; } @@ -4769,7 +4918,16 @@ pub const ByteStream = struct { this.buffer_action = null; } + if (this.buffer.capacity == 0 and stream == .done) { + log("ByteStream.onData done and action.fulfill()", .{}); + + var blob = this.toAnyBlob().?; + action.fulfill(&blob); + return; + } if (this.buffer.capacity == 0 and stream == .owned_and_done) { + log("ByteStream.onData owned_and_done and action.fulfill()", .{}); + this.buffer = std.ArrayList(u8).fromOwnedSlice(bun.default_allocator, @constCast(chunk)); var blob = this.toAnyBlob().?; action.fulfill(&blob); @@ -4780,10 +4938,12 @@ pub const ByteStream = struct { allocator.free(stream.slice()); } } + log("ByteStream.onData appendSlice and action.fulfill()", .{}); this.buffer.appendSlice(chunk) catch bun.outOfMemory(); var blob = this.toAnyBlob().?; action.fulfill(&blob); + return; } else { this.buffer.appendSlice(chunk) catch bun.outOfMemory(); @@ -4837,13 +4997,18 @@ pub const ByteStream = struct { } const remaining = chunk[to_copy.len..]; - if (remaining.len > 0) + if (remaining.len > 0 and chunk.len > 0) this.append(stream, to_copy.len, chunk, allocator) catch @panic("Out of memory while copying request body"); + log("ByteStream.onData pending.run()", .{}); + this.pending.run(); + return; } + log("ByteStream.onData no action just append", .{}); + this.append(stream, 0, chunk, allocator) catch @panic("Out of memory while copying request body"); } @@ -4873,6 +5038,7 @@ pub const ByteStream = struct { .err => { this.pending.result = .{ .err = stream.err }; }, + .done => {}, else => unreachable, } return; @@ -4893,6 +5059,7 @@ pub const ByteStream = struct { this.pending.result = .{ .err = stream.err }; }, + .done => {}, // We don't support the rest of these yet else => unreachable, } @@ -4985,6 +5152,11 @@ pub const ByteStream = struct { } } + pub fn memoryCost(this: *const @This()) usize { + // ReadableStreamSource covers @sizeOf(ByteStream) + return this.buffer.capacity; + } + pub fn deinit(this: *@This()) void { JSC.markBinding(@src()); if (this.buffer.capacity > 0) this.buffer.clearAndFree(); @@ -5080,6 +5252,7 @@ pub const ByteStream = struct { deinit, null, drain, + memoryCost, toBufferedValue, ); }; diff --git a/src/bun.zig b/src/bun.zig index efac52b91b..77d75c1f0d 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -1331,8 +1331,8 @@ pub const PackageManager = install.PackageManager; pub const RunCommand = @import("./cli/run_command.zig").RunCommand; pub const fs = @import("./fs.zig"); -pub const Bundler = bundler.Bundler; -pub const bundler = @import("./bundler.zig"); +pub const Transpiler = transpiler.Transpiler; +pub const transpiler = @import("./transpiler.zig"); pub const which = @import("./which.zig").which; pub const js_parser = @import("./js_parser.zig"); pub const js_printer = @import("./js_printer.zig"); @@ -1871,6 +1871,14 @@ pub const StringSet = struct { } } + pub fn contains(self: *StringSet, key: []const u8) bool { + return self.map.contains(key); + } + + pub fn swapRemove(self: *StringSet, key: []const u8) bool { + return self.map.swapRemove(key); + } + pub fn deinit(self: *StringSet) void { for (self.map.keys()) |key| { self.map.allocator.free(key); @@ -3229,6 +3237,19 @@ pub fn exitThread() noreturn { } } +pub fn deleteAllPoolsForThreadExit() void { + const pools_to_delete = .{ + JSC.WebCore.ByteListPool, + bun.WPathBufferPool, + bun.PathBufferPool, + bun.JSC.ConsoleObject.Formatter.Visited.Pool, + bun.js_parser.StringVoidMap.Pool, + }; + inline for (pools_to_delete) |pool| { + pool.deleteAll(); + } +} + pub const Tmpfile = @import("./tmp.zig").Tmpfile; pub const io = @import("./io/io.zig"); @@ -4122,3 +4143,81 @@ pub inline fn isComptimeKnown(x: anytype) bool { pub inline fn itemOrNull(comptime T: type, slice: []const T, index: usize) ?T { return if (index < slice.len) slice[index] else null; } + +/// To handle stack overflows: +/// 1. StackCheck.init() +/// 2. .isSafeToRecurse() +pub const StackCheck = struct { + cached_stack_end: usize = 0, + + extern fn Bun__StackCheck__initialize() void; + pub fn configureThread() void { + Bun__StackCheck__initialize(); + } + + extern "C" fn Bun__StackCheck__getMaxStack() usize; + fn getStackEnd() usize { + return Bun__StackCheck__getMaxStack(); + } + + pub fn init() StackCheck { + return StackCheck{ .cached_stack_end = getStackEnd() }; + } + + pub fn update(this: *StackCheck) void { + this.cached_stack_end = getStackEnd(); + } + + /// Is there at least 128 KB of stack space available? + pub fn isSafeToRecurse(this: StackCheck) bool { + const stack_ptr: usize = @frameAddress(); + const remaining_stack = stack_ptr -| this.cached_stack_end; + return remaining_stack > 1024 * if (Environment.isWindows) 256 else 128; + } +}; + +// Workaround for lack of branch hints. +pub noinline fn throwStackOverflow() StackOverflow!void { + @setCold(true); + return error.StackOverflow; +} +const StackOverflow = error{StackOverflow}; + +// This pool exists because on Windows, each path buffer costs 64 KB. +// This makes the stack memory usage very unpredictable, which means we can't really know how much stack space we have left. +// This pool is a workaround to make the stack memory usage more predictable. +// We keep up to 4 path buffers alive per thread at a time. +pub fn PathBufferPoolT(comptime T: type) type { + return struct { + const Pool = ObjectPool(PathBuf, null, true, 4); + pub const PathBuf = struct { + bytes: T, + + pub fn deinit(this: *PathBuf) void { + var node: *Pool.Node = @alignCast(@fieldParentPtr("data", this)); + node.release(); + } + }; + + pub fn get() *T { + // use a threadlocal allocator so mimalloc deletes it on thread deinit. + return &Pool.get(bun.threadlocalAllocator()).data.bytes; + } + + pub fn put(buffer: *T) void { + var path_buf: *PathBuf = @alignCast(@fieldParentPtr("bytes", buffer)); + path_buf.deinit(); + } + + pub fn deleteAll() void { + Pool.deleteAll(); + } + }; +} + +pub const PathBufferPool = PathBufferPoolT(bun.PathBuffer); +pub const WPathBufferPool = if (Environment.isWindows) PathBufferPoolT(bun.WPathBuffer) else struct { + // So it can be used in code that deletes all the pools. + pub fn deleteAll() void {} +}; +pub const OSPathBufferPool = if (Environment.isWindows) WPathBufferPool else PathBufferPool; diff --git a/src/bun_js.zig b/src/bun_js.zig index 29198e5a73..4ec6a1e9d6 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -24,7 +24,7 @@ const Api = @import("api/schema.zig").Api; const resolve_path = @import("./resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("./bun.js/config.zig").configureTransformOptionsForBun; const Command = @import("cli.zig").Command; -const bundler = bun.bundler; +const transpiler = bun.transpiler; const DotEnv = @import("env_loader.zig"); const which = @import("which.zig").which; const JSC = bun.JSC; @@ -64,6 +64,7 @@ pub const Run = struct { .log = ctx.log, .args = ctx.args, .graph = graph_ptr, + .is_main_thread = true, }), .arena = arena, .ctx = ctx, @@ -71,7 +72,7 @@ pub const Run = struct { }; var vm = run.vm; - var b = &vm.bundler; + var b = &vm.transpiler; vm.preload = ctx.preloads; vm.argv = ctx.passthrough; vm.arena = &run.arena; @@ -93,7 +94,7 @@ pub const Run = struct { b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers; b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace; - b.options.experimental_css = ctx.bundler_options.experimental_css; + b.options.experimental = ctx.bundler_options.experimental; // b.options.minify_syntax = ctx.bundler_options.minify_syntax; @@ -155,7 +156,7 @@ pub const Run = struct { @setCold(true); // this is a hack: make dummy bundler so we can use its `.runEnvLoader()` function to populate environment variables probably should split out the functionality - var bundle = try bun.Bundler.init( + var bundle = try bun.Transpiler.init( ctx.allocator, ctx.log, try @import("./bun.js/config.zig").configureTransformOptionsForBunVM(ctx.allocator, ctx.args), @@ -198,6 +199,7 @@ pub const Run = struct { .smol = ctx.runtime_options.smol, .eval = ctx.runtime_options.eval.eval_and_print, .debugger = ctx.runtime_options.debugger, + .is_main_thread = true, }, ), .arena = arena, @@ -206,7 +208,7 @@ pub const Run = struct { }; var vm = run.vm; - var b = &vm.bundler; + var b = &vm.transpiler; vm.preload = ctx.preloads; vm.argv = ctx.passthrough; vm.arena = &run.arena; @@ -262,13 +264,13 @@ pub const Run = struct { JSC.VirtualMachine.is_main_thread_vm = true; // Allow setting a custom timezone - if (vm.bundler.env.get("TZ")) |tz| { + if (vm.transpiler.env.get("TZ")) |tz| { if (tz.len > 0) { _ = vm.global.setTimeZone(&JSC.ZigString.init(tz)); } } - vm.bundler.env.loadTracy(); + vm.transpiler.env.loadTracy(); doPreconnect(ctx.runtime_options.preconnect); @@ -298,8 +300,8 @@ pub const Run = struct { else => {}, } - if (strings.eqlComptime(this.entry_path, ".") and vm.bundler.fs.top_level_dir.len > 0) { - this.entry_path = vm.bundler.fs.top_level_dir; + if (strings.eqlComptime(this.entry_path, ".") and vm.transpiler.fs.top_level_dir.len > 0) { + this.entry_path = vm.transpiler.fs.top_level_dir; } if (vm.loadEntryPoint(this.entry_path)) |promise| { diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 66c4018634..a9d2345bf1 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1,4 +1,4 @@ -// This is Bun's JavaScript/TypeScript bundler +// This is Bun's JavaScript/TypeScript transpiler // // A lot of the implementation is based on the Go implementation of esbuild. Thank you Evan Wallace. // @@ -41,7 +41,7 @@ // // make mimalloc-debug // -const Bundler = bun.Bundler; +const Transpiler = bun.Transpiler; const bun = @import("root").bun; const string = bun.string; const Output = bun.Output; @@ -59,6 +59,7 @@ const lex = @import("../js_lexer.zig"); const Logger = @import("../logger.zig"); const options = @import("../options.zig"); const js_parser = bun.js_parser; +const Part = js_ast.Part; const json_parser = @import("../json_parser.zig"); const js_printer = @import("../js_printer.zig"); const js_ast = @import("../js_ast.zig"); @@ -89,6 +90,7 @@ const MacroRemap = @import("../resolver/package_json.zig").MacroMap; const DebugLogs = _resolver.DebugLogs; const OOM = bun.OOM; +const HTMLScanner = @import("../HTMLScanner.zig"); const Router = @import("../router.zig"); const isPackagePath = _resolver.isPackagePath; const Lock = @import("../lock.zig").Lock; @@ -100,7 +102,6 @@ const Linker = linker.Linker; const Resolver = _resolver.Resolver; const TOML = @import("../toml/toml_parser.zig").TOML; const EntryPoints = @import("./entry_points.zig"); -const ThisBundler = @import("../bundler.zig").Bundler; const Dependency = js_ast.Dependency; const JSAst = js_ast.BundledAst; const Loader = options.Loader; @@ -128,13 +129,13 @@ const BitSet = bun.bit_set.DynamicBitSetUnmanaged; const Async = bun.Async; const Loc = Logger.Loc; const bake = bun.bake; - +const lol = bun.LOLHTML; const debug_deferred = bun.Output.scoped(.BUNDLER_DEFERRED, true); const logPartDependencyTree = Output.scoped(.part_dep_tree, false); fn tracer(comptime src: std.builtin.SourceLocation, comptime name: [:0]const u8) bun.tracy.Ctx { - return bun.tracy.traceNamed(src, "Bundler." ++ name); + return bun.tracy.traceNamed(src, "Transpiler." ++ name); } pub const ThreadPool = struct { @@ -264,7 +265,7 @@ pub const ThreadPool = struct { log: *Logger.Log, estimated_input_lines_of_code: usize = 0, macro_context: js_ast.Macro.MacroContext, - bundler: Bundler = undefined, + transpiler: Transpiler = undefined, }; pub fn init(worker: *Worker, v2: *BundleV2) void { @@ -292,18 +293,18 @@ pub const ThreadPool = struct { }; this.data.log.* = Logger.Log.init(allocator); this.ctx = ctx; - this.data.bundler = ctx.bundler.*; - this.data.bundler.setLog(this.data.log); - this.data.bundler.setAllocator(allocator); - this.data.bundler.linker.resolver = &this.data.bundler.resolver; - this.data.bundler.macro_context = js_ast.Macro.MacroContext.init(&this.data.bundler); - this.data.macro_context = this.data.bundler.macro_context.?; + this.data.transpiler = ctx.transpiler.*; + this.data.transpiler.setLog(this.data.log); + this.data.transpiler.setAllocator(allocator); + this.data.transpiler.linker.resolver = &this.data.transpiler.resolver; + this.data.transpiler.macro_context = js_ast.Macro.MacroContext.init(&this.data.transpiler); + this.data.macro_context = this.data.transpiler.macro_context.?; this.temporary_arena = bun.ArenaAllocator.init(this.allocator); this.stmt_list = LinkerContext.StmtList.init(this.allocator); const CacheSet = @import("../cache.zig"); - this.data.bundler.resolver.caches = CacheSet.Set.init(this.allocator); + this.data.transpiler.resolver.caches = CacheSet.Set.init(this.allocator); debug("Worker.create()", .{}); } @@ -367,12 +368,12 @@ fn fmtEscapedNamespace(slice: []const u8, comptime fmt: []const u8, _: std.fmt.F } pub const BundleV2 = struct { - bundler: *Bundler, + transpiler: *Transpiler, /// When Server Component is enabled, this is used for the client bundles - /// and `bundler` is used for the server bundles. - client_bundler: *Bundler, + /// and `transpiler` is used for the server bundles. + client_bundler: *Transpiler, /// See bake.Framework.ServerComponents.separate_ssr_graph - ssr_bundler: *Bundler, + ssr_bundler: *Transpiler, /// When Bun Bake is used, the resolved framework is passed here framework: ?bake.Framework, graph: Graph, @@ -397,7 +398,7 @@ pub const BundleV2 = struct { drain_defer_task: DeferredBatchTask = .{}, - /// Set true by DevServer. Currently every usage of the bundler (Bun.build + /// Set true by DevServer. Currently every usage of the transpiler (Bun.build /// and `bun build` cli) runs at the top of an event loop. When this is /// true, a callback is executed after all work is complete. asynchronous: bool = false, @@ -405,8 +406,8 @@ pub const BundleV2 = struct { const BakeOptions = struct { framework: bake.Framework, - client_bundler: *Bundler, - ssr_bundler: *Bundler, + client_bundler: *Transpiler, + ssr_bundler: *Transpiler, plugins: ?*JSC.API.JSBundler.Plugin, }; @@ -427,20 +428,20 @@ pub const BundleV2 = struct { // running the plugins. .js => |jsc_event_loop| return jsc_event_loop, // The CLI currently has no JSC event loop; for now, no plugin support - .mini => @panic("No JavaScript event loop for bundler plugins to run on"), + .mini => @panic("No JavaScript event loop for transpiler plugins to run on"), } } - /// Most of the time, accessing .bundler directly is OK. This is only + /// Most of the time, accessing .transpiler directly is OK. This is only /// needed when it is important to distinct between client and server /// /// Note that .log, .allocator, and other things are shared - /// between the three bundler configurations - pub inline fn bundlerForTarget(this: *BundleV2, target: options.Target) *Bundler { - return if (!this.bundler.options.server_components) - this.bundler + /// between the three transpiler configurations + pub inline fn bundlerForTarget(this: *BundleV2, target: options.Target) *Transpiler { + return if (!this.transpiler.options.server_components) + this.transpiler else switch (target) { - else => this.bundler, + else => this.transpiler, .browser => this.client_bundler, .bake_server_components_ssr => this.ssr_bundler, }; @@ -452,7 +453,7 @@ pub const BundleV2 = struct { /// Same semantics as bundlerForTarget for `path_to_source_index_map` pub inline fn pathToSourceIndexMap(this: *BundleV2, target: options.Target) *PathToSourceIndexMap { - return if (!this.bundler.options.server_components) + return if (!this.transpiler.options.server_components) &this.graph.path_to_source_index_map else switch (target) { else => &this.graph.path_to_source_index_map, @@ -583,7 +584,7 @@ pub const BundleV2 = struct { // imported and weird things will happen visitor.visit(Index.runtime, false, false); - switch (this.bundler.options.code_splitting) { + switch (this.transpiler.options.code_splitting) { inline else => |check_dynamic_imports| { for (this.graph.entry_points.items) |entry_point| { visitor.visit(entry_point, false, comptime check_dynamic_imports); @@ -636,19 +637,19 @@ pub const BundleV2 = struct { import_record: bun.JSC.API.JSBundler.Resolve.MiniImportRecord, target: options.Target, ) void { - const bundler = this.bundlerForTarget(target); + const transpiler = this.bundlerForTarget(target); var had_busted_dir_cache: bool = false; - var resolve_result = while (true) break bundler.resolver.resolve( + var resolve_result = while (true) break transpiler.resolver.resolve( Fs.PathName.init(import_record.source_file).dirWithTrailingSlash(), import_record.specifier, import_record.kind, ) catch |err| { // Only perform directory busting when hot-reloading is enabled if (err == error.ModuleNotFound) { - if (this.bundler.options.dev_server) |dev| { + if (this.transpiler.options.dev_server) |dev| { if (!had_busted_dir_cache) { // Only re-query if we previously had something cached. - if (bundler.resolver.bustDirCacheFromSpecifier(import_record.source_file, import_record.specifier)) { + if (transpiler.resolver.bustDirCacheFromSpecifier(import_record.source_file, import_record.specifier)) { had_busted_dir_cache = true; continue; } @@ -751,7 +752,7 @@ pub const BundleV2 = struct { if (path.pretty.ptr == path.text.ptr) { // TODO: outbase - const rel = bun.path.relativePlatform(bundler.fs.top_level_dir, path.text, .loose, false); + const rel = bun.path.relativePlatform(transpiler.fs.top_level_dir, path.text, .loose, false); path.pretty = this.graph.allocator.dupe(u8, rel) catch bun.outOfMemory(); } path.assertPrettyIsValid(); @@ -769,7 +770,7 @@ pub const BundleV2 = struct { const entry = this.pathToSourceIndexMap(target).getOrPut(this.graph.allocator, path.hashKey()) catch bun.outOfMemory(); if (!entry.found_existing) { path.* = this.pathWithPrettyInitialized(path.*, target) catch bun.outOfMemory(); - const loader = brk: { + const loader: Loader = (brk: { if (import_record.importer_source_index) |importer| { var record: *ImportRecord = &this.graph.ast.items(.import_records)[importer].slice()[import_record.import_record_index]; if (record.loader()) |out_loader| { @@ -777,8 +778,9 @@ pub const BundleV2 = struct { } } - break :brk path.loader(&bundler.options.loaders) orelse options.Loader.file; - }; + break :brk path.loader(&transpiler.options.loaders) orelse options.Loader.file; + // HTML is only allowed at the entry point. + }).disableHTML(); const idx = this.enqueueParseTask( &resolve_result, .{ @@ -794,7 +796,7 @@ pub const BundleV2 = struct { // For non-javascript files, make all of these files share indices. // For example, it is silly to bundle index.css depended on by client+server twice. // It makes sense to separate these for JS because the target affects DCE - if (this.bundler.options.server_components and !loader.isJavaScriptLike()) { + if (this.transpiler.options.server_components and !loader.isJavaScriptLike()) { const a, const b = switch (target) { else => .{ &this.graph.client_path_to_source_index_map, &this.graph.ssr_path_to_source_index_map }, .browser => .{ &this.graph.path_to_source_index_map, &this.graph.ssr_path_to_source_index_map }, @@ -833,7 +835,15 @@ pub const BundleV2 = struct { } this.incrementScanCounter(); const source_index = Index.source(this.graph.input_files.len); - const loader = this.bundler.options.loaders.get(path.name.ext) orelse .file; + const loader = brk: { + const default = path.loader(&this.transpiler.options.loaders) orelse .file; + + if (!this.transpiler.options.experimental.html) { + break :brk default.disableHTML(); + } + + break :brk default; + }; path.* = this.pathWithPrettyInitialized(path.*, target) catch bun.outOfMemory(); path.assertPrettyIsValid(); @@ -860,7 +870,7 @@ pub const BundleV2 = struct { // Handle onLoad plugins as entry points if (!this.enqueueOnLoadPluginIfNeeded(task)) { - if (loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (loader.shouldCopyForBundling(this.transpiler.options.experimental)) { var additional_files: *BabyList(AdditionalFile) = &this.graph.input_files.items(.additional_files)[source_index.get()]; additional_files.push(this.graph.allocator, .{ .source_index = task.source_index.get() }) catch unreachable; this.graph.input_files.items(.side_effects)[source_index.get()] = _resolver.SideEffects.no_side_effects__pure_data; @@ -876,7 +886,7 @@ pub const BundleV2 = struct { } pub fn init( - bundler: *ThisBundler, + transpiler: *Transpiler, bake_options: ?BakeOptions, allocator: std.mem.Allocator, event_loop: EventLoop, @@ -884,16 +894,16 @@ pub const BundleV2 = struct { thread_pool: ?*ThreadPoolLib, heap: ?ThreadlocalArena, ) !*BundleV2 { - bundler.env.loadTracy(); + transpiler.env.loadTracy(); const this = try allocator.create(BundleV2); - bundler.options.mark_builtins_as_external = bundler.options.target.isBun() or bundler.options.target == .node; - bundler.resolver.opts.mark_builtins_as_external = bundler.options.target.isBun() or bundler.options.target == .node; + transpiler.options.mark_builtins_as_external = transpiler.options.target.isBun() or transpiler.options.target == .node; + transpiler.resolver.opts.mark_builtins_as_external = transpiler.options.target.isBun() or transpiler.options.target == .node; this.* = .{ - .bundler = bundler, - .client_bundler = bundler, - .ssr_bundler = bundler, + .transpiler = transpiler, + .client_bundler = transpiler, + .ssr_bundler = transpiler, .framework = null, .graph = .{ .pool = undefined, @@ -920,50 +930,50 @@ pub const BundleV2 = struct { this.framework = bo.framework; this.linker.framework = &this.framework.?; this.plugins = bo.plugins; - bun.assert(bundler.options.server_components); + bun.assert(transpiler.options.server_components); bun.assert(this.client_bundler.options.server_components); if (bo.framework.server_components.?.separate_ssr_graph) bun.assert(this.ssr_bundler.options.server_components); } this.linker.graph.allocator = this.graph.heap.allocator(); this.graph.allocator = this.linker.graph.allocator; - this.bundler.allocator = this.graph.allocator; - this.bundler.resolver.allocator = this.graph.allocator; - this.bundler.linker.allocator = this.graph.allocator; - this.bundler.log.msgs.allocator = this.graph.allocator; - this.bundler.log.clone_line_text = true; + this.transpiler.allocator = this.graph.allocator; + this.transpiler.resolver.allocator = this.graph.allocator; + this.transpiler.linker.allocator = this.graph.allocator; + this.transpiler.log.msgs.allocator = this.graph.allocator; + this.transpiler.log.clone_line_text = true; // We don't expose an option to disable this. Bake forbids tree-shaking // since every export must is always exist in case a future module // starts depending on it. - if (this.bundler.options.output_format == .internal_bake_dev) { - this.bundler.options.tree_shaking = false; - this.bundler.resolver.opts.tree_shaking = false; + if (this.transpiler.options.output_format == .internal_bake_dev) { + this.transpiler.options.tree_shaking = false; + this.transpiler.resolver.opts.tree_shaking = false; } else { - this.bundler.options.tree_shaking = true; - this.bundler.resolver.opts.tree_shaking = true; + this.transpiler.options.tree_shaking = true; + this.transpiler.resolver.opts.tree_shaking = true; } - this.linker.resolver = &this.bundler.resolver; - this.linker.graph.code_splitting = bundler.options.code_splitting; + this.linker.resolver = &this.transpiler.resolver; + this.linker.graph.code_splitting = transpiler.options.code_splitting; - this.linker.options.minify_syntax = bundler.options.minify_syntax; - this.linker.options.minify_identifiers = bundler.options.minify_identifiers; - this.linker.options.minify_whitespace = bundler.options.minify_whitespace; - this.linker.options.emit_dce_annotations = bundler.options.emit_dce_annotations; - this.linker.options.ignore_dce_annotations = bundler.options.ignore_dce_annotations; - this.linker.options.banner = bundler.options.banner; - this.linker.options.footer = bundler.options.footer; - this.linker.options.experimental_css = bundler.options.experimental_css; - this.linker.options.css_chunking = bundler.options.css_chunking; - this.linker.options.source_maps = bundler.options.source_map; - this.linker.options.tree_shaking = bundler.options.tree_shaking; - this.linker.options.public_path = bundler.options.public_path; - this.linker.options.target = bundler.options.target; - this.linker.options.output_format = bundler.options.output_format; - this.linker.options.generate_bytecode_cache = bundler.options.bytecode; + this.linker.options.minify_syntax = transpiler.options.minify_syntax; + this.linker.options.minify_identifiers = transpiler.options.minify_identifiers; + this.linker.options.minify_whitespace = transpiler.options.minify_whitespace; + this.linker.options.emit_dce_annotations = transpiler.options.emit_dce_annotations; + this.linker.options.ignore_dce_annotations = transpiler.options.ignore_dce_annotations; + this.linker.options.banner = transpiler.options.banner; + this.linker.options.footer = transpiler.options.footer; + this.linker.options.experimental = transpiler.options.experimental; + this.linker.options.css_chunking = transpiler.options.css_chunking; + this.linker.options.source_maps = transpiler.options.source_map; + this.linker.options.tree_shaking = transpiler.options.tree_shaking; + this.linker.options.public_path = transpiler.options.public_path; + this.linker.options.target = transpiler.options.target; + this.linker.options.output_format = transpiler.options.output_format; + this.linker.options.generate_bytecode_cache = transpiler.options.bytecode; - this.linker.dev_server = bundler.options.dev_server; + this.linker.dev_server = transpiler.options.dev_server; var pool = try this.graph.allocator.create(ThreadPool); if (cli_watch_flag) { @@ -999,7 +1009,7 @@ pub const BundleV2 = struct { pub fn onAfterDecrementScanCounter(this: *BundleV2) void { if (this.asynchronous and this.isDone()) { - this.finishFromBakeDevServer(this.bundler.options.dev_server orelse + this.finishFromBakeDevServer(this.transpiler.options.dev_server orelse @panic("No dev server attached in asynchronous bundle job")) catch bun.outOfMemory(); } @@ -1021,7 +1031,7 @@ pub const BundleV2 = struct { { // Add the runtime - const rt = ParseTask.getRuntimeSource(this.bundler.options.target); + const rt = ParseTask.getRuntimeSource(this.transpiler.options.target); try this.graph.input_files.append(bun.default_allocator, Graph.InputFile{ .source = rt.source, .loader = .js, @@ -1045,7 +1055,7 @@ pub const BundleV2 = struct { // gets its content set after the scan+parse phase, but before linking. // // The dev server does not use these, as it is implement in the HMR runtime. - if (this.bundler.options.dev_server == null) { + if (this.transpiler.options.dev_server == null) { try this.reserveSourceIndexesForBake(); } @@ -1064,14 +1074,15 @@ pub const BundleV2 = struct { switch (variant) { .normal => { for (data) |entry_point| { - const resolved = this.bundler.resolveEntryPoint(entry_point) catch + const resolved = this.transpiler.resolveEntryPoint(entry_point) catch continue; - _ = try this.enqueueEntryItem(null, &batch, resolved, true, this.bundler.options.target); + + _ = try this.enqueueEntryItem(null, &batch, resolved, true, this.transpiler.options.target); } }, .dev_server => { for (data.files.set.keys(), data.files.set.values()) |abs_path, flags| { - const resolved = this.bundler.resolveEntryPoint(abs_path) catch + const resolved = this.transpiler.resolveEntryPoint(abs_path) catch continue; if (flags.client) brk: { @@ -1080,19 +1091,19 @@ pub const BundleV2 = struct { try data.css_data.putNoClobber(this.graph.allocator, Index.init(source_index), .{ .imported_on_server = false }); } } - if (flags.server) _ = try this.enqueueEntryItem(null, &batch, resolved, true, this.bundler.options.target); + if (flags.server) _ = try this.enqueueEntryItem(null, &batch, resolved, true, this.transpiler.options.target); if (flags.ssr) _ = try this.enqueueEntryItem(null, &batch, resolved, true, .bake_server_components_ssr); } }, .bake_production => { for (data.files.keys()) |key| { - const resolved = this.bundler.resolveEntryPoint(key.absPath()) catch + const resolved = this.transpiler.resolveEntryPoint(key.absPath()) catch continue; // TODO: wrap client files so the exports arent preserved. _ = try this.enqueueEntryItem(null, &batch, resolved, true, switch (key.side) { .client => .browser, - .server => this.bundler.options.target, + .server => this.transpiler.options.target, }) orelse continue; } }, @@ -1105,8 +1116,8 @@ pub const BundleV2 = struct { fn cloneAST(this: *BundleV2) !void { const trace = tracer(@src(), "cloneAST"); defer trace.end(); - this.linker.allocator = this.bundler.allocator; - this.linker.graph.allocator = this.bundler.allocator; + this.linker.allocator = this.transpiler.allocator; + this.linker.graph.allocator = this.transpiler.allocator; this.linker.graph.ast = try this.graph.ast.clone(this.linker.allocator); var ast = this.linker.graph.ast.slice(); for (ast.items(.module_scope)) |*module_scope| { @@ -1135,8 +1146,8 @@ pub const BundleV2 = struct { const alloc = this.graph.allocator; - var server = try AstBuilder.init(this.graph.allocator, &bake.server_virtual_source, this.bundler.options.hot_module_reloading); - var client = try AstBuilder.init(this.graph.allocator, &bake.client_virtual_source, this.bundler.options.hot_module_reloading); + var server = try AstBuilder.init(this.graph.allocator, &bake.server_virtual_source, this.transpiler.options.hot_module_reloading); + var client = try AstBuilder.init(this.graph.allocator, &bake.client_virtual_source, this.transpiler.options.hot_module_reloading); var server_manifest_props: std.ArrayListUnmanaged(G.Property) = .{}; var client_manifest_props: std.ArrayListUnmanaged(G.Property) = .{}; @@ -1261,11 +1272,13 @@ pub const BundleV2 = struct { this: *BundleV2, resolve_result: *const _resolver.Result, source: Logger.Source, - loader: Loader, + loader_: Loader, known_target: options.Target, ) OOM!Index.Int { const source_index = Index.init(@as(u32, @intCast(this.graph.ast.len))); this.graph.ast.append(bun.default_allocator, JSAst.empty) catch unreachable; + // Only enable HTML loader when it's an entry point. + const loader = loader_.disableHTML(); this.graph.input_files.append(bun.default_allocator, .{ .source = source, @@ -1287,7 +1300,7 @@ pub const BundleV2 = struct { // Handle onLoad plugins if (!this.enqueueOnLoadPluginIfNeeded(task)) { - if (loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (loader.shouldCopyForBundling(this.transpiler.options.experimental)) { var additional_files: *BabyList(AdditionalFile) = &this.graph.input_files.items(.additional_files)[source_index.get()]; additional_files.push(this.graph.allocator, .{ .source_index = task.source_index.get() }) catch unreachable; this.graph.input_files.items(.side_effects)[source_index.get()] = _resolver.SideEffects.no_side_effects__pure_data; @@ -1326,7 +1339,7 @@ pub const BundleV2 = struct { }, .side_effects = .has_side_effects, .jsx = if (known_target == .bake_server_components_ssr and !this.framework.?.server_components.?.separate_ssr_graph) - this.bundler.options.jsx + this.transpiler.options.jsx else this.bundlerForTarget(known_target).options.jsx, .source_index = source_index, @@ -1343,7 +1356,7 @@ pub const BundleV2 = struct { // Handle onLoad plugins if (!this.enqueueOnLoadPluginIfNeeded(task)) { - if (loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (loader.shouldCopyForBundling(this.transpiler.options.experimental)) { var additional_files: *BabyList(AdditionalFile) = &this.graph.input_files.items(.additional_files)[source_index.get()]; additional_files.push(this.graph.allocator, .{ .source_index = task.source_index.get() }) catch unreachable; this.graph.input_files.items(.side_effects)[source_index.get()] = _resolver.SideEffects.no_side_effects__pure_data; @@ -1386,7 +1399,7 @@ pub const BundleV2 = struct { } pub fn generateFromCLI( - bundler: *ThisBundler, + transpiler: *Transpiler, allocator: std.mem.Allocator, event_loop: EventLoop, enable_reloading: bool, @@ -1394,16 +1407,16 @@ pub const BundleV2 = struct { minify_duration: *u64, source_code_size: *u64, ) !std.ArrayList(options.OutputFile) { - var this = try BundleV2.init(bundler, null, allocator, event_loop, enable_reloading, null, null); + var this = try BundleV2.init(transpiler, null, allocator, event_loop, enable_reloading, null, null); this.unique_key = generateUniqueKey(); - if (this.bundler.log.hasErrors()) { + if (this.transpiler.log.hasErrors()) { return error.BuildFailed; } - this.graph.pool.pool.schedule(try this.enqueueEntryPoints(.normal, this.bundler.options.entry_points)); + this.graph.pool.pool.schedule(try this.enqueueEntryPoints(.normal, this.transpiler.options.entry_points)); - if (this.bundler.log.hasErrors()) { + if (this.transpiler.log.hasErrors()) { return error.BuildFailed; } @@ -1412,7 +1425,7 @@ pub const BundleV2 = struct { minify_duration.* = @as(u64, @intCast(@divTrunc(@as(i64, @truncate(std.time.nanoTimestamp())) - @as(i64, @truncate(bun.CLI.start_time)), @as(i64, std.time.ns_per_ms)))); source_code_size.* = this.source_code_length; - if (this.bundler.log.hasErrors()) { + if (this.transpiler.log.hasErrors()) { return error.BuildFailed; } @@ -1439,7 +1452,7 @@ pub const BundleV2 = struct { pub fn generateFromBakeProductionCLI( entry_points: bake.production.EntryPointMap, - server_bundler: *ThisBundler, + server_bundler: *Transpiler, kit_options: BakeOptions, allocator: std.mem.Allocator, event_loop: EventLoop, @@ -1447,19 +1460,19 @@ pub const BundleV2 = struct { var this = try BundleV2.init(server_bundler, kit_options, allocator, event_loop, false, null, null); this.unique_key = generateUniqueKey(); - if (this.bundler.log.hasErrors()) { + if (this.transpiler.log.hasErrors()) { return error.BuildFailed; } this.graph.pool.pool.schedule(try this.enqueueEntryPoints(.bake_production, entry_points)); - if (this.bundler.log.hasErrors()) { + if (this.transpiler.log.hasErrors()) { return error.BuildFailed; } this.waitForParse(); - if (this.bundler.log.hasErrors()) { + if (this.transpiler.log.hasErrors()) { return error.BuildFailed; } @@ -1507,7 +1520,7 @@ pub const BundleV2 = struct { const unique_key_for_additional_files = this.graph.input_files.items(.unique_key_for_additional_file); const content_hashes_for_additional_files = this.graph.input_files.items(.content_hash_for_additional_file); const sources = this.graph.input_files.items(.source); - var additional_output_files = std.ArrayList(options.OutputFile).init(this.bundler.allocator); + var additional_output_files = std.ArrayList(options.OutputFile).init(this.transpiler.allocator); const additional_files: []BabyList(AdditionalFile) = this.graph.input_files.items(.additional_files); const loaders = this.graph.input_files.items(.loader); @@ -1517,13 +1530,13 @@ pub const BundleV2 = struct { const key = unique_key_for_additional_files[index]; if (key.len > 0) { var template = PathTemplate.asset; - if (this.bundler.options.asset_naming.len > 0) - template.data = this.bundler.options.asset_naming; + if (this.transpiler.options.asset_naming.len > 0) + template.data = this.transpiler.options.asset_naming; const source = &sources[index]; var pathname = source.path.name; // TODO: outbase - pathname = Fs.PathName.init(bun.path.relativePlatform(this.bundler.options.root_dir, source.path.text, .loose, false)); + pathname = Fs.PathName.init(bun.path.relativePlatform(this.transpiler.options.root_dir, source.path.text, .loose, false)); template.placeholder.name = pathname.base; template.placeholder.dir = pathname.dir; @@ -1579,7 +1592,7 @@ pub const BundleV2 = struct { .promise = JSC.JSPromise.Strong.init(globalThis), .globalThis = globalThis, .poll_ref = Async.KeepAlive.init(), - .env = globalThis.bunVM().bundler.env, + .env = globalThis.bunVM().transpiler.env, .plugins = plugins, .log = Logger.Log.init(bun.default_allocator), .task = JSBundleCompletionTask.TaskCompletion.init(completion), @@ -1623,18 +1636,18 @@ pub const BundleV2 = struct { result: Result = .{ .pending = {} }, next: ?*JSBundleCompletionTask = null, - bundler: *BundleV2 = undefined, + transpiler: *BundleV2 = undefined, plugins: ?*bun.JSC.API.JSBundler.Plugin = null, ref_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(1), pub fn configureBundler( completion: *JSBundleCompletionTask, - bundler: *Bundler, + transpiler: *Transpiler, allocator: std.mem.Allocator, ) !void { const config = &completion.config; - bundler.* = try bun.Bundler.init( + transpiler.* = try bun.Transpiler.init( allocator, &completion.log, Api.TransformOptions{ @@ -1651,46 +1664,52 @@ pub const BundleV2 = struct { .extension_order = &.{}, .env_files = &.{}, .conditions = config.conditions.map.keys(), - .ignore_dce_annotations = bundler.options.ignore_dce_annotations, + .ignore_dce_annotations = transpiler.options.ignore_dce_annotations, .drop = config.drop.map.keys(), }, completion.env, ); - bundler.options.env.behavior = config.env_behavior; - bundler.options.env.prefix = config.env_prefix.slice(); + transpiler.options.env.behavior = config.env_behavior; + transpiler.options.env.prefix = config.env_prefix.slice(); - bundler.options.entry_points = config.entry_points.keys(); - bundler.options.jsx = config.jsx; - bundler.options.no_macros = config.no_macros; - bundler.options.loaders = try options.loadersFromTransformOptions(allocator, config.loaders, config.target); - bundler.options.entry_naming = config.names.entry_point.data; - bundler.options.chunk_naming = config.names.chunk.data; - bundler.options.asset_naming = config.names.asset.data; + transpiler.options.entry_points = config.entry_points.keys(); + transpiler.options.jsx = config.jsx; + transpiler.options.no_macros = config.no_macros; + transpiler.options.loaders = try options.loadersFromTransformOptions(allocator, config.loaders, config.target); + transpiler.options.entry_naming = config.names.entry_point.data; + transpiler.options.chunk_naming = config.names.chunk.data; + transpiler.options.asset_naming = config.names.asset.data; - bundler.options.public_path = config.public_path.list.items; - bundler.options.output_format = config.format; - bundler.options.bytecode = config.bytecode; + transpiler.options.public_path = config.public_path.list.items; + transpiler.options.output_format = config.format; + transpiler.options.bytecode = config.bytecode; - bundler.options.output_dir = config.outdir.slice(); - bundler.options.root_dir = config.rootdir.slice(); - bundler.options.minify_syntax = config.minify.syntax; - bundler.options.minify_whitespace = config.minify.whitespace; - bundler.options.minify_identifiers = config.minify.identifiers; - bundler.options.inlining = config.minify.syntax; - bundler.options.source_map = config.source_map; - bundler.options.packages = config.packages; - bundler.options.code_splitting = config.code_splitting; - bundler.options.emit_dce_annotations = config.emit_dce_annotations orelse !config.minify.whitespace; - bundler.options.ignore_dce_annotations = config.ignore_dce_annotations; - bundler.options.experimental_css = config.experimental_css; - bundler.options.css_chunking = config.css_chunking; - bundler.options.banner = config.banner.slice(); - bundler.options.footer = config.footer.slice(); + transpiler.options.output_dir = config.outdir.slice(); + transpiler.options.root_dir = config.rootdir.slice(); + transpiler.options.minify_syntax = config.minify.syntax; + transpiler.options.minify_whitespace = config.minify.whitespace; + transpiler.options.minify_identifiers = config.minify.identifiers; + transpiler.options.inlining = config.minify.syntax; + transpiler.options.source_map = config.source_map; + transpiler.options.packages = config.packages; + transpiler.options.code_splitting = config.code_splitting; + transpiler.options.emit_dce_annotations = config.emit_dce_annotations orelse !config.minify.whitespace; + transpiler.options.ignore_dce_annotations = config.ignore_dce_annotations; + transpiler.options.experimental = config.experimental; + transpiler.options.css_chunking = config.css_chunking; + transpiler.options.banner = config.banner.slice(); + transpiler.options.footer = config.footer.slice(); - bundler.configureLinker(); - try bundler.configureDefines(); + transpiler.configureLinker(); + try transpiler.configureDefines(); - bundler.resolver.opts = bundler.options; + if (bun.FeatureFlags.breaking_changes_1_2) { + if (!transpiler.options.production) { + try transpiler.options.conditions.appendSlice(&.{"development"}); + } + } + + transpiler.resolver.opts = transpiler.options; } pub fn completeOnBundleThread(completion: *JSBundleCompletionTask) void { @@ -1717,11 +1736,16 @@ pub const BundleV2 = struct { this.poll_ref.unref(globalThis.bunVM()); const promise = this.promise.swap(); - const root_obj = JSC.JSValue.createEmptyObject(globalThis, 2); switch (this.result) { .pending => unreachable, - .err => { + .err => brk: { + if (this.config.throw_on_error) { + promise.reject(globalThis, this.log.toJSAggregateError(globalThis, bun.String.static("Bundle failed"))); + break :brk; + } + + const root_obj = JSC.JSValue.createEmptyObject(globalThis, 3); root_obj.put(globalThis, JSC.ZigString.static("outputs"), JSC.JSValue.createEmptyArray(globalThis, 0)); root_obj.put( globalThis, @@ -1733,8 +1757,10 @@ pub const BundleV2 = struct { JSC.ZigString.static("logs"), this.log.toJSArray(globalThis, bun.default_allocator), ); + promise.resolve(globalThis, root_obj); }, .value => |*build| { + const root_obj = JSC.JSValue.createEmptyObject(globalThis, 3); const output_files: []options.OutputFile = build.output_files.items; const output_files_js = JSC.JSValue.createEmptyArray(globalThis, output_files.len); if (output_files_js == .zero) { @@ -1799,10 +1825,13 @@ pub const BundleV2 = struct { JSC.ZigString.static("logs"), this.log.toJSArray(globalThis, bun.default_allocator), ); + promise.resolve(globalThis, root_obj); }, } - promise.resolve(globalThis, root_obj); + if (Environment.isDebug) { + bun.assert(promise.status(globalThis.vm()) != .pending); + } } }; @@ -1852,7 +1881,7 @@ pub const BundleV2 = struct { this.graph.heap.gc(true); } } - const log = this.bundler.log; + const log = this.transpiler.log; // TODO: watcher @@ -1877,7 +1906,7 @@ pub const BundleV2 = struct { this.decrementScanCounter(); }, .success => |code| { - const should_copy_for_bundling = load.parse_task.defer_copy_for_bundling and code.loader.shouldCopyForBundling(this.bundler.options.experimental_css); + const should_copy_for_bundling = load.parse_task.defer_copy_for_bundling and code.loader.shouldCopyForBundling(this.transpiler.options.experimental); if (should_copy_for_bundling) { const source_index = load.source_index; var additional_files: *BabyList(AdditionalFile) = &this.graph.input_files.items(.additional_files)[source_index.get()]; @@ -1896,7 +1925,7 @@ pub const BundleV2 = struct { this.graph.pool.pool.schedule(ThreadPoolLib.Batch.from(&parse_task.task)); }, .err => |msg| { - if (this.bundler.options.dev_server) |dev| { + if (this.transpiler.options.dev_server) |dev| { const source = &this.graph.input_files.items(.source)[load.source_index.get()]; // A stack-allocated Log object containing the singular message var msg_mut = msg; @@ -1939,7 +1968,7 @@ pub const BundleV2 = struct { this.graph.heap.gc(true); } } - const log = this.bundler.log; + const log = this.transpiler.log; switch (resolve.value.consume()) { .no_match => { @@ -1994,7 +2023,7 @@ pub const BundleV2 = struct { existing.value_ptr.* = source_index.get(); out_source_index = source_index; this.graph.ast.append(bun.default_allocator, JSAst.empty) catch unreachable; - const loader = path.loader(&this.bundler.options.loaders) orelse options.Loader.file; + const loader = path.loader(&this.transpiler.options.loaders) orelse options.Loader.file; this.graph.input_files.append(bun.default_allocator, .{ .source = .{ @@ -2030,7 +2059,7 @@ pub const BundleV2 = struct { // Handle onLoad plugins if (!this.enqueueOnLoadPluginIfNeeded(task)) { - if (loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (loader.shouldCopyForBundling(this.transpiler.options.experimental)) { var additional_files: *BabyList(AdditionalFile) = &this.graph.input_files.items(.additional_files)[source_index.get()]; additional_files.push(this.graph.allocator, .{ .source_index = task.source_index.get() }) catch unreachable; this.graph.input_files.items(.side_effects)[source_index.get()] = _resolver.SideEffects.no_side_effects__pure_data; @@ -2119,7 +2148,7 @@ pub const BundleV2 = struct { ) !std.ArrayList(options.OutputFile) { this.unique_key = generateUniqueKey(); - if (this.bundler.log.errors > 0) { + if (this.transpiler.log.errors > 0) { return error.BuildFailed; } @@ -2132,7 +2161,7 @@ pub const BundleV2 = struct { this.graph.heap.helpCatchMemoryIssues(); - if (this.bundler.log.errors > 0) { + if (this.transpiler.log.errors > 0) { return error.BuildFailed; } @@ -2157,14 +2186,14 @@ pub const BundleV2 = struct { reachable_files, ); - if (this.bundler.log.errors > 0) { + if (this.transpiler.log.errors > 0) { return error.BuildFailed; } return try this.linker.generateChunksInParallel(chunks, false); } - /// Dev Server uses this instead to run a subset of the bundler, and to run it asynchronously. + /// Dev Server uses this instead to run a subset of the transpiler, and to run it asynchronously. pub fn startFromBakeDevServer(this: *BundleV2, bake_entry_points: bake.DevServer.EntryPointList) !BakeBundleStart { this.unique_key = generateUniqueKey(); @@ -2367,7 +2396,7 @@ pub const BundleV2 = struct { pub fn enqueueOnLoadPluginIfNeeded(this: *BundleV2, parse: *ParseTask) bool { if (this.plugins) |plugins| { if (plugins.hasAnyMatches(&parse.path, true)) { - if (parse.is_entry_point and parse.loader != null and parse.loader.?.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (parse.is_entry_point and parse.loader != null and parse.loader.?.shouldCopyForBundling(this.transpiler.options.experimental)) { parse.defer_copy_for_bundling = true; } // This is where onLoad plugins are enqueued @@ -2386,7 +2415,7 @@ pub const BundleV2 = struct { } fn pathWithPrettyInitialized(this: *BundleV2, path: Fs.Path, target: options.Target) !Fs.Path { - return genericPathWithPrettyInitialized(path, target, this.bundler.fs.top_level_dir, this.graph.allocator); + return genericPathWithPrettyInitialized(path, target, this.transpiler.fs.top_level_dir, this.graph.allocator); } fn reserveSourceIndexesForBake(this: *BundleV2) !void { @@ -2468,7 +2497,7 @@ pub const BundleV2 = struct { inline else => |is_server| { const src = if (is_server) bake.server_virtual_source else bake.client_virtual_source; if (strings.eqlComptime(import_record.path.text, src.path.pretty)) { - if (this.bundler.options.dev_server != null) { + if (this.transpiler.options.dev_server != null) { import_record.is_external_without_side_effects = true; import_record.source_index = Index.invalid; } else { @@ -2495,7 +2524,7 @@ pub const BundleV2 = struct { continue; } - if (this.bundler.options.rewrite_jest_for_tests) { + if (this.transpiler.options.rewrite_jest_for_tests) { if (strings.eqlComptime( import_record.path.text, "@jest/globals", @@ -2540,13 +2569,13 @@ pub const BundleV2 = struct { continue; } - const bundler, const renderer: bake.Graph, const target = + const transpiler, const renderer: bake.Graph, const target = if (import_record.tag == .bake_resolve_to_ssr_graph) brk: { // TODO: consider moving this error into js_parser so it is caught more reliably // Then we can assert(this.framework != null) if (this.framework == null) { - this.bundler.log.addErrorFmt( + this.transpiler.log.addErrorFmt( source, import_record.range.loc, this.graph.allocator, @@ -2559,7 +2588,7 @@ pub const BundleV2 = struct { const is_supported = this.framework.?.server_components != null and this.framework.?.server_components.?.separate_ssr_graph; if (!is_supported) { - this.bundler.log.addErrorFmt( + this.transpiler.log.addErrorFmt( source, import_record.range.loc, this.graph.allocator, @@ -2581,17 +2610,17 @@ pub const BundleV2 = struct { }; var had_busted_dir_cache = false; - var resolve_result = inner: while (true) break bundler.resolver.resolveWithFramework( + var resolve_result = inner: while (true) break transpiler.resolver.resolveWithFramework( source_dir, import_record.path.text, import_record.kind, ) catch |err| { // Only perform directory busting when hot-reloading is enabled if (err == error.ModuleNotFound) { - if (this.bundler.options.dev_server) |dev| { + if (this.transpiler.options.dev_server) |dev| { if (!had_busted_dir_cache) { // Only re-query if we previously had something cached. - if (bundler.resolver.bustDirCacheFromSpecifier( + if (transpiler.resolver.bustDirCacheFromSpecifier( source.path.text, import_record.path.text, )) { @@ -2624,7 +2653,7 @@ pub const BundleV2 = struct { if (isPackagePath(import_record.path.text)) { if (ast.target == .browser and options.ExternalModules.isNodeBuiltin(import_record.path.text)) { addError( - this.bundler.log, + this.transpiler.log, source, import_record.range, this.graph.allocator, @@ -2634,7 +2663,7 @@ pub const BundleV2 = struct { ) catch @panic("unexpected log error"); } else { addError( - this.bundler.log, + this.transpiler.log, source, import_record.range, this.graph.allocator, @@ -2645,7 +2674,7 @@ pub const BundleV2 = struct { } } else { addError( - this.bundler.log, + this.transpiler.log, source, import_record.range, this.graph.allocator, @@ -2683,12 +2712,12 @@ pub const BundleV2 = struct { continue; } - if (this.bundler.options.dev_server) |dev_server| { + if (this.transpiler.options.dev_server) |dev_server| { import_record.source_index = Index.invalid; import_record.is_external_without_side_effects = true; if (dev_server.isFileCached(path.text, renderer)) |entry| { - const rel = bun.path.relativePlatform(this.bundler.fs.top_level_dir, path.text, .loose, false); + const rel = bun.path.relativePlatform(this.transpiler.fs.top_level_dir, path.text, .loose, false); import_record.path.text = rel; import_record.path.pretty = rel; import_record.path = this.pathWithPrettyInitialized(path.*, target) catch bun.outOfMemory(); @@ -2702,7 +2731,7 @@ pub const BundleV2 = struct { const hash_key = path.hashKey(); if (this.pathToSourceIndexMap(target).get(hash_key)) |id| { - if (this.bundler.options.dev_server != null) { + if (this.transpiler.options.dev_server != null) { import_record.path = this.graph.input_files.items(.source)[id].path; } else { import_record.source_index = Index.init(id); @@ -2738,13 +2767,21 @@ pub const BundleV2 = struct { resolve_task.jsx = resolve_result.jsx; resolve_task.jsx.development = this.bundlerForTarget(target).options.jsx.development; - if (import_record.tag.loader()) |loader| { - resolve_task.loader = loader; - } + // Figure out the loader. + { + if (import_record.tag.loader()) |loader| { + resolve_task.loader = loader; + } - if (resolve_task.loader == null) { - resolve_task.loader = path.loader(&this.bundler.options.loaders); - resolve_task.tree_shaking = this.bundler.options.tree_shaking; + if (resolve_task.loader == null) { + resolve_task.loader = path.loader(&this.transpiler.options.loaders); + resolve_task.tree_shaking = this.transpiler.options.tree_shaking; + } + + // HTML must be an entry point. + if (resolve_task.loader) |*loader| { + loader.* = loader.disableHTML(); + } } resolve_entry.value_ptr.* = resolve_task; @@ -2788,7 +2825,7 @@ pub const BundleV2 = struct { .success => |val| val.source.index.get(), }; const loader: Loader = this.graph.input_files.items(.loader)[source]; - if (!loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (!loader.shouldCopyForBundling(this.transpiler.options.experimental)) { this.finalizers.append(bun.default_allocator, parse_result.external) catch bun.outOfMemory(); } else { this.graph.input_files.items(.allocator)[source] = ExternalFreeFunctionAllocator.create(@ptrCast(parse_result.external.function.?), parse_result.external.ctx.?); @@ -2818,7 +2855,7 @@ pub const BundleV2 = struct { } } - // To minimize contention, watchers are appended by the bundler thread. + // To minimize contention, watchers are appended by the transpiler thread. if (this.bun_watcher) |watcher| { if (parse_result.watcher_data.fd != bun.invalid_fd and parse_result.watcher_data.fd != .zero) { const source = switch (parse_result.value) { @@ -2850,7 +2887,7 @@ pub const BundleV2 = struct { } }, .success => |*result| { - result.log.cloneToWithRecycled(this.bundler.log, true) catch unreachable; + result.log.cloneToWithRecycled(this.transpiler.log, true) catch unreachable; // Warning: `input_files` and `ast` arrays may resize in this function call // It is not safe to cache slices from them. @@ -2862,6 +2899,9 @@ pub const BundleV2 = struct { graph.input_files.items(.unique_key_for_additional_file)[result.source.index.get()] = result.unique_key_for_additional_file; graph.input_files.items(.content_hash_for_additional_file)[result.source.index.get()] = result.content_hash_for_additional_file; + // Record which loader we used for this file + graph.input_files.items(.loader)[result.source.index.get()] = result.loader; + debug("onParse({d}, {s}) = {d} imports, {d} exports", .{ result.source.index.get(), result.source.path.text, @@ -2895,7 +2935,7 @@ pub const BundleV2 = struct { .side_effects = value.side_effects, }; - const loader = new_task.loader orelse new_input_file.source.path.loader(&this.bundler.options.loaders) orelse options.Loader.file; + const loader = new_task.loader orelse new_input_file.source.path.loader(&this.transpiler.options.loaders) orelse options.Loader.file; new_input_file.source.index = Index.source(graph.input_files.len); new_input_file.source.path = new_task.path; @@ -2915,7 +2955,7 @@ pub const BundleV2 = struct { continue; } - if (loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + if (loader.shouldCopyForBundling(this.transpiler.options.experimental)) { var additional_files: *BabyList(AdditionalFile) = &graph.input_files.items(.additional_files)[result.source.index.get()]; additional_files.push(this.graph.allocator, .{ .source_index = new_task.source_index.get() }) catch unreachable; new_input_file.side_effects = _resolver.SideEffects.no_side_effects__pure_data; @@ -2925,8 +2965,11 @@ pub const BundleV2 = struct { // schedule as early as possible graph.pool.pool.schedule(ThreadPoolLib.Batch.from(&new_task.task)); } else { - const loader = value.loader orelse graph.input_files.items(.source)[existing.value_ptr.*].path.loader(&this.bundler.options.loaders) orelse options.Loader.file; - if (loader.shouldCopyForBundling(this.bundler.options.experimental_css)) { + const loader = value.loader orelse + graph.input_files.items(.source)[existing.value_ptr.*].path.loader(&this.transpiler.options.loaders) orelse + options.Loader.file; + + if (loader.shouldCopyForBundling(this.transpiler.options.experimental)) { var additional_files: *BabyList(AdditionalFile) = &graph.input_files.items(.additional_files)[result.source.index.get()]; additional_files.push(this.graph.allocator, .{ .source_index = existing.value_ptr.* }) catch unreachable; graph.estimated_file_loader_count += 1; @@ -2942,7 +2985,7 @@ pub const BundleV2 = struct { if (this.resolve_tasks_waiting_for_import_source_index.fetchSwapRemove(result.source.index.get())) |pending_entry| { for (pending_entry.value.slice()) |to_assign| { - if (this.bundler.options.dev_server == null or + if (this.transpiler.options.dev_server == null or input_file_loaders[to_assign.to_source_index.get()] == .css) { import_records.slice()[to_assign.import_record_index].source_index = to_assign.to_source_index; @@ -2959,7 +3002,7 @@ pub const BundleV2 = struct { for (import_records.slice(), 0..) |*record, i| { if (path_to_source_index_map.get(record.path.hashKey())) |source_index| { - if (this.bundler.options.dev_server == null or + if (this.transpiler.options.dev_server == null or input_file_loaders[source_index] == .css) record.source_index.value = source_index; @@ -3011,7 +3054,7 @@ pub const BundleV2 = struct { // Enqueue only one file var server_source = result.source; server_source.path.pretty = server_source.path.text; - server_source.path = this.pathWithPrettyInitialized(server_source.path, this.bundler.options.target) catch bun.outOfMemory(); + server_source.path = this.pathWithPrettyInitialized(server_source.path, this.transpiler.options.target) catch bun.outOfMemory(); const server_index = this.enqueueParseTask2( server_source, this.graph.input_files.items(.loader)[result.source.index.get()], @@ -3042,7 +3085,7 @@ pub const BundleV2 = struct { } if (process_log) { - if (this.bundler.options.dev_server) |dev_server| { + if (this.transpiler.options.dev_server) |dev_server| { dev_server.handleParseTaskFailure( err.err, err.target.bakeGraph(), @@ -3050,19 +3093,19 @@ pub const BundleV2 = struct { &err.log, ) catch bun.outOfMemory(); } else if (err.log.msgs.items.len > 0) { - err.log.cloneToWithRecycled(this.bundler.log, true) catch unreachable; + err.log.cloneToWithRecycled(this.transpiler.log, true) catch unreachable; } else { - this.bundler.log.addErrorFmt( + this.transpiler.log.addErrorFmt( null, Logger.Loc.Empty, - this.bundler.log.msgs.allocator, + this.transpiler.log.msgs.allocator, "{s} while {s}", .{ @errorName(err.err), @tagName(err.step) }, ) catch unreachable; } } - if (Environment.allow_assert and this.bundler.options.dev_server != null) { + if (Environment.allow_assert and this.transpiler.options.dev_server != null) { bun.assert(this.graph.ast.items(.parts)[err.source_index.get()].len == 0); } }, @@ -3071,12 +3114,12 @@ pub const BundleV2 = struct { /// To satisfy the interface from NewHotReloader() pub fn getLoaders(vm: *BundleV2) *bun.options.Loader.HashTable { - return &vm.bundler.options.loaders; + return &vm.transpiler.options.loaders; } /// To satisfy the interface from NewHotReloader() pub fn bustDirCache(vm: *BundleV2, path: []const u8) bool { - return vm.bundler.resolver.bustDirCache(path); + return vm.transpiler.resolver.bustDirCache(path); } }; @@ -3193,14 +3236,14 @@ pub fn BundleThread(CompletionStruct: type) type { ast_memory_allocator.reset(); ast_memory_allocator.push(); - const bundler = try allocator.create(bun.Bundler); + const transpiler = try allocator.create(bun.Transpiler); - try completion.configureBundler(bundler, allocator); + try completion.configureBundler(transpiler, allocator); - bundler.resolver.generation = generation; + transpiler.resolver.generation = generation; const this = try BundleV2.init( - bundler, + transpiler, null, // TODO: Kit allocator, JSC.AnyEventLoop.init(allocator), @@ -3214,7 +3257,7 @@ pub fn BundleThread(CompletionStruct: type) type { BundleV2.JSBundleCompletionTask => completion, else => @compileError("Unknown completion struct: " ++ CompletionStruct), }; - completion.bundler = this; + completion.transpiler = this; defer { if (this.graph.pool.pool.threadpool_context == @as(?*anyopaque, @ptrCast(this.graph.pool))) { @@ -3231,16 +3274,16 @@ pub fn BundleThread(CompletionStruct: type) type { this.linker.source_maps.quoted_contents_wait_group.wait(); var out_log = Logger.Log.init(bun.default_allocator); - this.bundler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); + this.transpiler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); completion.log = out_log; } completion.result = .{ .value = .{ - .output_files = try this.runFromJSInNewThread(bundler.options.entry_points), + .output_files = try this.runFromJSInNewThread(transpiler.options.entry_points), } }; var out_log = Logger.Log.init(bun.default_allocator); - this.bundler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); + this.transpiler.log.appendToWithRecycled(&out_log, true) catch bun.outOfMemory(); completion.log = out_log; completion.completeOnBundleThread(); } @@ -3268,8 +3311,8 @@ pub const DeferredBatchTask = struct { } pub fn getCompletion(this: *DeferredBatchTask) ?*bun.BundleV2.JSBundleCompletionTask { - const bundler: *BundleV2 = @alignCast(@fieldParentPtr("drain_defer_task", this)); - return bundler.completion; + const transpiler: *BundleV2 = @alignCast(@fieldParentPtr("drain_defer_task", this)); + return transpiler.completion; } pub fn schedule(this: *DeferredBatchTask) void { @@ -3292,7 +3335,7 @@ pub const DeferredBatchTask = struct { return; }; - completion.bundler.plugins.?.drainDeferred(completion.result == .err); + completion.transpiler.plugins.?.drainDeferred(completion.result == .err); } }; @@ -3369,6 +3412,8 @@ pub const ParseTask = struct { unique_key_for_additional_file: []const u8 = "", /// Used by "file" loader files. content_hash_for_additional_file: u64 = 0, + + loader: Loader, }; pub const Error = struct { @@ -3405,7 +3450,7 @@ pub const ParseTask = struct { .module_type = resolve_result.module_type, .emit_decorator_metadata = resolve_result.emit_decorator_metadata, .package_version = if (resolve_result.package_json) |package_json| package_json.version else "", - .known_target = ctx.bundler.options.target, + .known_target = ctx.transpiler.options.target, }; } @@ -3560,25 +3605,25 @@ pub const ParseTask = struct { fn getEmptyCSSAST( log: *Logger.Log, - bundler: *Bundler, + transpiler: *Transpiler, opts: js_parser.Parser.Options, allocator: std.mem.Allocator, source: Logger.Source, ) !JSAst { const root = Expr.init(E.Object, E.Object{}, Logger.Loc{ .start = 0 }); - var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); ast.css = bun.create(allocator, bun.css.BundlerStyleSheet, bun.css.BundlerStyleSheet.empty(allocator)); return ast; } - fn getEmptyAST(log: *Logger.Log, bundler: *Bundler, opts: js_parser.Parser.Options, allocator: std.mem.Allocator, source: Logger.Source, comptime RootType: type) !JSAst { + fn getEmptyAST(log: *Logger.Log, transpiler: *Transpiler, opts: js_parser.Parser.Options, allocator: std.mem.Allocator, source: Logger.Source, comptime RootType: type) !JSAst { const root = Expr.init(RootType, RootType{}, Logger.Loc.Empty); - return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + return JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); } fn getAST( log: *Logger.Log, - bundler: *Bundler, + transpiler: *Transpiler, opts: js_parser.Parser.Options, allocator: std.mem.Allocator, resolver: *Resolver, @@ -3592,9 +3637,9 @@ pub const ParseTask = struct { const trace = tracer(@src(), "ParseJS"); defer trace.end(); return if (try resolver.caches.js.parse( - bundler.allocator, + transpiler.allocator, opts, - bundler.options.define, + transpiler.options.define, log, &source, )) |res| @@ -3602,7 +3647,7 @@ pub const ParseTask = struct { else switch (opts.module_type == .esm) { inline else => |as_undefined| try getEmptyAST( log, - bundler, + transpiler, opts, allocator, source, @@ -3614,25 +3659,25 @@ pub const ParseTask = struct { const trace = tracer(@src(), "ParseJSON"); defer trace.end(); const root = (try resolver.caches.json.parsePackageJSON(log, source, allocator, false)) orelse Expr.init(E.Object, E.Object{}, Logger.Loc.Empty); - return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + return JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); }, .toml => { const trace = tracer(@src(), "ParseTOML"); defer trace.end(); const root = try TOML.parse(&source, log, allocator, false); - return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + return JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); }, .text => { const root = Expr.init(E.String, E.String{ .data = source.contents, }, Logger.Loc{ .start = 0 }); - var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); - ast.addUrlForCss(allocator, bundler.options.experimental_css, &source, "text/plain", null); + var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); + ast.addUrlForCss(allocator, transpiler.options.experimental, &source, "text/plain", null); return ast; }, .sqlite_embedded, .sqlite => { - if (!bundler.options.target.isBun()) { + if (!transpiler.options.target.isBun()) { log.addError( null, Logger.Loc.Empty, @@ -3692,11 +3737,11 @@ pub const ParseTask = struct { .name = "db", }, Logger.Loc{ .start = 0 }); - return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + return JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); }, .napi => { // (dap-eval-cb "source.contents.ptr") - if (bundler.options.target == .browser) { + if (transpiler.options.target == .browser) { log.addError( null, Logger.Loc.Empty, @@ -3723,18 +3768,68 @@ pub const ParseTask = struct { }, Logger.Loc{ .start = 0 }); unique_key_for_additional_file.* = unique_key; - return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + return JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); + }, + .html => { + if (transpiler.options.experimental.html) { + var scanner = HTMLScanner.init(allocator, log, &source); + try scanner.scan(source.contents); + + // Reuse existing code for creating the AST + // because it handles the various Ref and other structs we + // need in order to print code later. + var ast = (try js_parser.newLazyExportAST( + allocator, + transpiler.options.define, + opts, + log, + Expr.init(E.Missing, E.Missing{}, Logger.Loc.Empty), + &source, + "", + )).?; + ast.import_records = scanner.import_records; + + // We're banning import default of html loader files for now. + // + // TLDR: it kept including: + // + // var name_default = ...; + // + // in the bundle because of the exports AST, and + // gave up on figuring out how to fix it so that + // this feature could ship. + ast.has_lazy_export = false; + ast.parts.ptr[1] = .{ + .stmts = &.{}, + .is_live = true, + .import_record_indices = brk2: { + // Generate a single part that depends on all the import records. + // This is to ensure that we generate a JavaScript bundle containing all the user's code. + var import_record_indices = try Part.ImportRecordIndices.initCapacity(allocator, scanner.import_records.len); + import_record_indices.len = @truncate(scanner.import_records.len); + for (import_record_indices.slice(), 0..) |*import_record, index| { + import_record.* = @intCast(index); + } + break :brk2 import_record_indices; + }, + }; + + // Try to avoid generating unnecessary ESM <> CJS wrapper code. + if (opts.output_format == .esm or opts.output_format == .iife) { + ast.exports_kind = .esm; + } + + return JSAst.init(ast); + } }, .css => { - if (bundler.options.experimental_css) { - // const unique_key = std.fmt.allocPrint(allocator, "{any}A{d:0>8}", .{ bun.fmt.hexIntLower(unique_key_prefix), source.index.get() }) catch unreachable; - // unique_key_for_additional_file.* = unique_key; + if (transpiler.options.experimental.css) { var import_records = BabyList(ImportRecord){}; const source_code = source.contents; var css_ast = switch (bun.css.BundlerStyleSheet.parseBundler( allocator, source_code, - bun.css.ParserOptions.default(allocator, bundler.log), + bun.css.ParserOptions.default(allocator, transpiler.log), &import_records, )) { .result => |v| v, @@ -3744,7 +3839,7 @@ pub const ParseTask = struct { }, }; if (css_ast.minify(allocator, bun.css.MinifyOptions{ - .targets = .{}, + .targets = bun.css.Targets.forBundlerTarget(transpiler.options.target), .unused_symbols = .{}, }).asErr()) |e| { try e.addToLogger(log, &source); @@ -3752,7 +3847,7 @@ pub const ParseTask = struct { } const root = Expr.init(E.Object, E.Object{}, Logger.Loc{ .start = 0 }); const css_ast_heap = bun.create(allocator, bun.css.BundlerStyleSheet, css_ast); - var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); ast.css = css_ast_heap; ast.import_records = import_records; return ast; @@ -3765,21 +3860,21 @@ pub const ParseTask = struct { .data = unique_key, }, Logger.Loc{ .start = 0 }); unique_key_for_additional_file.* = unique_key; - var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, transpiler.options.define, opts, log, root, &source, "")).?); ast.url_for_css = unique_key; - ast.addUrlForCss(allocator, bundler.options.experimental_css, &source, null, unique_key); + ast.addUrlForCss(allocator, transpiler.options.experimental, &source, null, unique_key); return ast; } fn getCodeForParseTaskWithoutPlugins( task: *ParseTask, log: *Logger.Log, - bundler: *Bundler, + transpiler: *Transpiler, resolver: *Resolver, allocator: std.mem.Allocator, file_path: *Fs.Path, loader: Loader, - experimental_css: bool, + experimental: Loader.Experimental, ) !CacheEntry { return switch (task.contents_or_fd) { .fd => |contents| brk: { @@ -3805,12 +3900,12 @@ pub const ParseTask = struct { } break :brk resolver.caches.fs.readFileWithAllocator( - if (loader.shouldCopyForBundling(experimental_css)) + if (loader.shouldCopyForBundling(experimental)) // The OutputFile will own the memory for the contents bun.default_allocator else allocator, - bundler.fs, + transpiler.fs, file_path.text, task.contents_or_fd.fd.dir, false, @@ -3854,12 +3949,12 @@ pub const ParseTask = struct { fn getCodeForParseTask( task: *ParseTask, log: *Logger.Log, - bundler: *Bundler, + transpiler: *Transpiler, resolver: *Resolver, allocator: std.mem.Allocator, file_path: *Fs.Path, loader: *Loader, - experimental_css: bool, + experimental: Loader.Experimental, from_plugin: *bool, ) !CacheEntry { const might_have_on_parse_plugins = brk: { @@ -3874,7 +3969,7 @@ pub const ParseTask = struct { }; if (!might_have_on_parse_plugins) { - return getCodeForParseTaskWithoutPlugins(task, log, bundler, resolver, allocator, file_path, loader.*, experimental_css); + return getCodeForParseTaskWithoutPlugins(task, log, transpiler, resolver, allocator, file_path, loader.*, experimental); } var should_continue_running: i32 = 1; @@ -3882,12 +3977,12 @@ pub const ParseTask = struct { var ctx = OnBeforeParsePlugin{ .task = task, .log = log, - .bundler = bundler, + .transpiler = transpiler, .resolver = resolver, .allocator = allocator, .file_path = file_path, .loader = loader, - .experimental_css = experimental_css, + .experimental = experimental, .deferred_error = null, .should_continue_running = &should_continue_running, }; @@ -3898,18 +3993,18 @@ pub const ParseTask = struct { const OnBeforeParsePlugin = struct { task: *ParseTask, log: *Logger.Log, - bundler: *Bundler, + transpiler: *Transpiler, resolver: *Resolver, allocator: std.mem.Allocator, file_path: *Fs.Path, loader: *Loader, - experimental_css: bool, + experimental: Loader.Experimental, deferred_error: ?anyerror = null, should_continue_running: *i32, result: ?*OnBeforeParseResult = null, - const headers = @import("bun-native-bundler-plugin-api"); + const headers = bun.C.translated; comptime { bun.assert(@sizeOf(OnBeforeParseArguments) == @sizeOf(headers.OnBeforeParseArguments)); @@ -4059,14 +4154,14 @@ pub const ParseTask = struct { const entry = getCodeForParseTaskWithoutPlugins( this.task, this.log, - this.bundler, + this.transpiler, this.resolver, this.allocator, this.file_path, result.loader, - this.experimental_css, + this.experimental, ) catch |err| { this.deferred_error = err; this.should_continue_running.* = 0; @@ -4192,7 +4287,7 @@ pub const ParseTask = struct { } } - return try getCodeForParseTaskWithoutPlugins(this.task, this.log, this.bundler, this.resolver, this.allocator, this.file_path, this.loader.*, this.experimental_css); + return try getCodeForParseTaskWithoutPlugins(this.task, this.log, this.transpiler, this.resolver, this.allocator, this.file_path, this.loader.*, this.experimental); } }; @@ -4205,15 +4300,26 @@ pub const ParseTask = struct { const allocator = this.allocator; var data = this.data; - var bundler = &data.bundler; - errdefer bundler.resetStore(); - var resolver: *Resolver = &bundler.resolver; + var transpiler = &data.transpiler; + errdefer transpiler.resetStore(); + var resolver: *Resolver = &transpiler.resolver; var file_path = task.path; step.* = .read_file; - var loader = task.loader orelse file_path.loader(&bundler.options.loaders) orelse options.Loader.file; + var loader = task.loader orelse file_path.loader(&transpiler.options.loaders) orelse options.Loader.file; + + // Do not process files as HTML if any of the following are true: + // - building for node or bun.js + // - the experimental.html flag is not enabled. + // + // We allow non-entrypoints to import HTML so that people could + // potentially use an onLoad plugin that returns HTML. + if (!transpiler.options.experimental.html or task.known_target != .browser) { + loader = loader.disableHTML(); + task.loader = loader; + } var contents_came_from_plugin: bool = false; - var entry = try getCodeForParseTask(task, log, bundler, resolver, allocator, &file_path, &loader, this.ctx.bundler.options.experimental_css, &contents_came_from_plugin); + var entry = try getCodeForParseTask(task, log, transpiler, resolver, allocator, &file_path, &loader, this.ctx.transpiler.options.experimental, &contents_came_from_plugin); // WARNING: Do not change the variant of `task.contents_or_fd` from // `.fd` to `.contents` (or back) after this point! @@ -4257,7 +4363,7 @@ pub const ParseTask = struct { const is_empty = strings.isAllWhitespace(entry.contents); - const use_directive: UseDirective = if (!is_empty and bundler.options.server_components) + const use_directive: UseDirective = if (!is_empty and transpiler.options.server_components) if (UseDirective.parse(entry.contents)) |use| use else @@ -4272,11 +4378,11 @@ pub const ParseTask = struct { task.known_target != .bake_server_components_ssr and this.ctx.framework.?.server_components.?.separate_ssr_graph) or // set the target to the client when bundling client-side files - (bundler.options.server_components and task.known_target == .browser)) + (transpiler.options.server_components and task.known_target == .browser)) { - bundler = this.ctx.client_bundler; - resolver = &bundler.resolver; - bun.assert(bundler.options.target == .browser); + transpiler = this.ctx.client_bundler; + resolver = &transpiler.resolver; + bun.assert(transpiler.options.target == .browser); } var source = Logger.Source{ @@ -4287,12 +4393,12 @@ pub const ParseTask = struct { }; const target = (if (task.source_index.get() == 1) targetFromHashbang(entry.contents) else null) orelse - if (task.known_target == .bake_server_components_ssr and bundler.options.framework.?.server_components.?.separate_ssr_graph) + if (task.known_target == .bake_server_components_ssr and transpiler.options.framework.?.server_components.?.separate_ssr_graph) .bake_server_components_ssr else - bundler.options.target; + transpiler.options.target; - const output_format = bundler.options.output_format; + const output_format = transpiler.options.output_format; var opts = js_parser.Parser.Options.init(task.jsx, loader); opts.bundle = true; @@ -4305,25 +4411,25 @@ pub const ParseTask = struct { opts.features.unwrap_commonjs_to_esm = output_format == .esm and FeatureFlags.unwrap_commonjs_to_esm; opts.features.use_import_meta_require = target.isBun(); opts.features.top_level_await = output_format == .esm or output_format == .internal_bake_dev; - opts.features.auto_import_jsx = task.jsx.parse and bundler.options.auto_import_jsx; - opts.features.trim_unused_imports = loader.isTypeScript() or (bundler.options.trim_unused_imports orelse false); - opts.features.inlining = bundler.options.minify_syntax; + opts.features.auto_import_jsx = task.jsx.parse and transpiler.options.auto_import_jsx; + opts.features.trim_unused_imports = loader.isTypeScript() or (transpiler.options.trim_unused_imports orelse false); + opts.features.inlining = transpiler.options.minify_syntax; opts.output_format = output_format; - opts.features.minify_syntax = bundler.options.minify_syntax; - opts.features.minify_identifiers = bundler.options.minify_identifiers; - opts.features.emit_decorator_metadata = bundler.options.emit_decorator_metadata; - opts.features.unwrap_commonjs_packages = bundler.options.unwrap_commonjs_packages; + opts.features.minify_syntax = transpiler.options.minify_syntax; + opts.features.minify_identifiers = transpiler.options.minify_identifiers; + opts.features.emit_decorator_metadata = transpiler.options.emit_decorator_metadata; + opts.features.unwrap_commonjs_packages = transpiler.options.unwrap_commonjs_packages; opts.features.hot_module_reloading = output_format == .internal_bake_dev and !source.index.isRuntime(); opts.features.react_fast_refresh = target == .browser and - bundler.options.react_fast_refresh and + transpiler.options.react_fast_refresh and loader.isJSX() and !source.path.isNodeModule(); - opts.features.server_components = if (bundler.options.server_components) switch (target) { + opts.features.server_components = if (transpiler.options.server_components) switch (target) { .browser => .client_side, else => switch (use_directive) { .none => .wrap_anon_server_functions, - .client => if (bundler.options.framework.?.server_components.?.separate_ssr_graph) + .client => if (transpiler.options.framework.?.server_components.?.separate_ssr_graph) .client_side else .wrap_exports_for_client_reference, @@ -4331,37 +4437,37 @@ pub const ParseTask = struct { }, } else .none; - opts.framework = bundler.options.framework; + opts.framework = transpiler.options.framework; - opts.ignore_dce_annotations = bundler.options.ignore_dce_annotations and !source.index.isRuntime(); + opts.ignore_dce_annotations = transpiler.options.ignore_dce_annotations and !source.index.isRuntime(); // For files that are not user-specified entrypoints, set `import.meta.main` to `false`. // Entrypoints will have `import.meta.main` set as "unknown", unless we use `--compile`, // in which we inline `true`. - if (bundler.options.inline_entrypoint_import_meta_main or !task.is_entry_point) { + if (transpiler.options.inline_entrypoint_import_meta_main or !task.is_entry_point) { opts.import_meta_main_value = task.is_entry_point; - } else if (bundler.options.target == .node) { + } else if (transpiler.options.target == .node) { opts.lower_import_meta_main_for_node_js = true; } - opts.tree_shaking = if (source.index.isRuntime()) true else bundler.options.tree_shaking; + opts.tree_shaking = if (source.index.isRuntime()) true else transpiler.options.tree_shaking; opts.module_type = task.module_type; task.jsx.parse = loader.isJSX(); var unique_key_for_additional_file: []const u8 = ""; var ast: JSAst = if (!is_empty) - try getAST(log, bundler, opts, allocator, resolver, source, loader, task.ctx.unique_key, &unique_key_for_additional_file) + try getAST(log, transpiler, opts, allocator, resolver, source, loader, task.ctx.unique_key, &unique_key_for_additional_file) else switch (opts.module_type == .esm) { - inline else => |as_undefined| if (loader == .css and this.ctx.bundler.options.experimental_css) try getEmptyCSSAST( + inline else => |as_undefined| if (loader == .css and this.ctx.transpiler.options.experimental.css) try getEmptyCSSAST( log, - bundler, + transpiler, opts, allocator, source, ) else try getEmptyAST( log, - bundler, + transpiler, opts, allocator, source, @@ -4370,7 +4476,7 @@ pub const ParseTask = struct { }; ast.target = target; - if (ast.parts.len <= 1 and ast.css == null) { + if (ast.parts.len <= 1 and ast.css == null and (task.loader == null or task.loader.? != .html)) { task.side_effects = .no_side_effects__empty_ast; } @@ -4383,9 +4489,10 @@ pub const ParseTask = struct { .use_directive = use_directive, .unique_key_for_additional_file = unique_key_for_additional_file, .side_effects = task.side_effects, + .loader = loader, // Hash the files in here so that we do it in parallel. - .content_hash_for_additional_file = if (loader.shouldCopyForBundling(this.ctx.bundler.options.experimental_css)) + .content_hash_for_additional_file = if (loader.shouldCopyForBundling(this.ctx.transpiler.options.experimental)) ContentHasher.run(source.contents) else 0, @@ -4406,7 +4513,7 @@ pub const ParseTask = struct { const value: ParseTask.Result.Value = if (run(this, worker, &step, &log)) |ast| value: { // When using HMR, always flag asts with errors as parse failures. // Not done outside of the dev server out of fear of breaking existing code. - if (this.ctx.bundler.options.dev_server != null and ast.log.hasErrors()) { + if (this.ctx.transpiler.options.dev_server != null and ast.log.hasErrors()) { break :value .{ .err = .{ .err = error.SyntaxError, @@ -4538,7 +4645,7 @@ pub const ServerComponentParseTask = struct { log: *Logger.Log, allocator: std.mem.Allocator, ) bun.OOM!ParseTask.Result.Success { - var ab = try AstBuilder.init(allocator, &task.source, task.ctx.bundler.options.hot_module_reloading); + var ab = try AstBuilder.init(allocator, &task.source, task.ctx.transpiler.options.hot_module_reloading); switch (task.data) { .client_reference_proxy => |data| try task.generateClientReferenceProxy(data, &ab), @@ -4548,11 +4655,12 @@ pub const ServerComponentParseTask = struct { return .{ .ast = try ab.toBundledAst(switch (task.data) { // Server-side - .client_reference_proxy => task.ctx.bundler.options.target, + .client_reference_proxy => task.ctx.transpiler.options.target, // Client-side, .client_entry_wrapper => .browser, }), .source = task.source, + .loader = .js, .log = log.*, .use_directive = .none, .side_effects = .no_side_effects__pure_data, @@ -4588,7 +4696,7 @@ pub const ServerComponentParseTask = struct { // In production, the path here must be the final chunk path, but // that information is not yet available since chunks are not // computed. The unique_key replacement system is used here. - .data = if (task.ctx.bundler.options.dev_server != null) + .data = if (task.ctx.transpiler.options.dev_server != null) data.other_source.path.pretty else try std.fmt.allocPrint(b.allocator, "{}S{d:0>8}", .{ @@ -4928,15 +5036,15 @@ pub const Graph = struct { /// each `.defer()` called in an onLoad plugin. /// /// Returns true if there were more tasks queued. - pub fn drainDeferredTasks(this: *@This(), bundler: *BundleV2) bool { - bundler.thread_lock.assertLocked(); + pub fn drainDeferredTasks(this: *@This(), transpiler: *BundleV2) bool { + transpiler.thread_lock.assertLocked(); if (this.deferred_pending > 0) { this.pending_items += this.deferred_pending; this.deferred_pending = 0; - bundler.drain_defer_task.init(); - bundler.drain_defer_task.schedule(); + transpiler.drain_defer_task.init(); + transpiler.drain_defer_task.schedule(); return true; } @@ -4974,6 +5082,7 @@ const EntryPoint = struct { none, user_specified, dynamic_import, + html, pub fn outputKind(this: Kind) JSC.API.BuildArtifact.OutputKind { return switch (this) { @@ -5096,9 +5205,9 @@ const LinkerGraph = struct { pub fn addPartToFile( graph: *LinkerGraph, id: u32, - part: js_ast.Part, + part: Part, ) !u32 { - var parts: *js_ast.Part.List = &graph.ast.items(.parts)[id]; + var parts: *Part.List = &graph.ast.items(.parts)[id]; const part_id = @as(u32, @truncate(parts.len)); try parts.push(graph.allocator, part); var top_level_symbol_to_parts_overlay: ?*TopLevelSymbolToParts = null; @@ -5168,7 +5277,7 @@ const LinkerGraph = struct { if (use_count == 0) return; var parts_list = g.ast.items(.parts)[source_index].slice(); - var part: *js_ast.Part = &parts_list[part_index]; + var part: *Part = &parts_list[part_index]; // Mark this symbol as used by this part @@ -5508,7 +5617,7 @@ pub const LinkerContext = struct { minify_identifiers: bool = false, banner: []const u8 = "", footer: []const u8 = "", - experimental_css: bool = false, + experimental: Loader.Experimental = .{}, css_chunking: bool = false, source_maps: options.SourceMapOption = .none, target: options.Target = .browser, @@ -5606,7 +5715,7 @@ pub const LinkerContext = struct { return format != .cjs; } - pub fn shouldIncludePart(c: *LinkerContext, source_index: Index.Int, part: js_ast.Part) bool { + pub fn shouldIncludePart(c: *LinkerContext, source_index: Index.Int, part: Part) bool { // As an optimization, ignore parts containing a single import statement to // an internal non-wrapped file. These will be ignored anyway and it's a // performance hit to spin up a goroutine only to discover this later. @@ -5633,10 +5742,10 @@ pub const LinkerContext = struct { defer trace.end(); this.parse_graph = &bundle.graph; - this.graph.code_splitting = bundle.bundler.options.code_splitting; - this.log = bundle.bundler.log; + this.graph.code_splitting = bundle.transpiler.options.code_splitting; + this.log = bundle.transpiler.log; - this.resolver = &bundle.bundler.resolver; + this.resolver = &bundle.transpiler.resolver; this.cycle_detector = std.ArrayList(ImportTracker).init(this.allocator); this.graph.reachable_files = reachable; @@ -5790,6 +5899,15 @@ pub const LinkerContext = struct { this.parse_graph.heap.gc(true); } + const JSChunkKeyFormatter = struct { + has_html: bool, + entry_bits: []const u8, + + pub fn format(this: @This(), comptime _: []const u8, _: anytype, writer: anytype) !void { + try writer.writeAll(&[_]u8{@intFromBool(!this.has_html)}); + try writer.writeAll(this.entry_bits); + } + }; pub noinline fn computeChunks( this: *LinkerContext, unique_key: u64, @@ -5814,6 +5932,13 @@ pub const LinkerContext = struct { const entry_source_indices = this.graph.entry_points.items(.source_index); const css_asts = this.graph.ast.items(.css); + const experimental_css = this.options.experimental.css; + const experimental_html = this.options.experimental.html; + const css_chunking = this.options.css_chunking; + var html_chunks = bun.StringArrayHashMap(Chunk).init(temp_allocator); + const loaders = this.parse_graph.input_files.items(.loader); + + const code_splitting = this.graph.code_splitting; // Create chunks for entry points for (entry_source_indices, 0..) |source_index, entry_id_| { @@ -5822,7 +5947,41 @@ pub const LinkerContext = struct { var entry_bits = &this.graph.files.items(.entry_bits)[source_index]; entry_bits.set(entry_bit); - if (this.options.experimental_css and css_asts[source_index] != null) { + const has_html_chunk = experimental_html and loaders[source_index] == .html; + const js_chunk_key = brk: { + if (code_splitting) { + break :brk try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len)); + } else { + // Force HTML chunks to always be generated, even if there's an identical JS file. + break :brk try std.fmt.allocPrint(temp_allocator, "{}", .{JSChunkKeyFormatter{ + .has_html = has_html_chunk, + .entry_bits = entry_bits.bytes(this.graph.entry_points.len), + }}); + } + }; + + // Put this early on in this loop so that CSS-only entry points work. + if (has_html_chunk) { + const html_chunk_entry = try html_chunks.getOrPut( + js_chunk_key, + ); + if (!html_chunk_entry.found_existing) { + html_chunk_entry.value_ptr.* = .{ + .entry_point = .{ + .entry_point_id = entry_bit, + .source_index = source_index, + .is_entry_point = true, + }, + .entry_bits = entry_bits.*, + .content = .{ + .html = .{}, + }, + .output_source_map = sourcemap.SourceMapPieces.init(this.allocator), + }; + } + } + + if (experimental_css and css_asts[source_index] != null) { const order = this.findImportedFilesInCSSOrder(temp_allocator, &.{Index.init(source_index)}); // Create a chunk for the entry point here to ensure that the chunk is // always generated even if the resulting file is empty @@ -5851,14 +6010,16 @@ pub const LinkerContext = struct { }, }, .output_source_map = sourcemap.SourceMapPieces.init(this.allocator), + .has_html_chunk = has_html_chunk, }; } continue; } + // Create a chunk for the entry point here to ensure that the chunk is // always generated even if the resulting file is empty - const js_chunk_entry = try js_chunks.getOrPut(try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len))); + const js_chunk_entry = try js_chunks.getOrPut(js_chunk_key); js_chunk_entry.value_ptr.* = .{ .entry_point = .{ .entry_point_id = entry_bit, @@ -5869,10 +6030,11 @@ pub const LinkerContext = struct { .content = .{ .javascript = .{}, }, + .has_html_chunk = has_html_chunk, .output_source_map = sourcemap.SourceMapPieces.init(this.allocator), }; - if (this.options.experimental_css) { + if (experimental_css) { // If this JS entry point has an associated CSS entry point, generate it // now. This is essentially done by generating a virtual CSS file that // only contains "@import" statements in the order that the files were @@ -5883,7 +6045,7 @@ pub const LinkerContext = struct { if (css_source_indices.len > 0) { const order = this.findImportedFilesInCSSOrder(temp_allocator, css_source_indices.slice()); - const hash_to_use = if (!this.options.css_chunking) + const hash_to_use = if (!css_chunking) bun.hash(try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len))) else brk: { var hasher = std.hash.Wyhash.init(5); @@ -5921,6 +6083,7 @@ pub const LinkerContext = struct { }, .files_with_parts_in_chunk = css_files_with_parts_in_chunk, .output_source_map = sourcemap.SourceMapPieces.init(this.allocator), + .has_html_chunk = has_html_chunk, }; } } @@ -5949,9 +6112,8 @@ pub const LinkerContext = struct { if (css_reprs[source_index.get()] != null) continue; if (this.graph.code_splitting) { - var js_chunk_entry = try js_chunks.getOrPut( - try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len)), - ); + const js_chunk_key = try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len)); + var js_chunk_entry = try js_chunks.getOrPut(js_chunk_key); if (!js_chunk_entry.found_existing) { js_chunk_entry.value_ptr.* = .{ @@ -5983,7 +6145,7 @@ pub const LinkerContext = struct { // Sort the chunks for determinism. This matters because we use chunk indices // as sorting keys in a few places. const chunks: []Chunk = sort_chunks: { - var sorted_chunks = try BabyList(Chunk).initCapacity(this.allocator, js_chunks.count() + css_chunks.count()); + var sorted_chunks = try BabyList(Chunk).initCapacity(this.allocator, js_chunks.count() + css_chunks.count() + html_chunks.count()); var sorted_keys = try BabyList(string).initCapacity(temp_allocator, js_chunks.count()); @@ -5991,12 +6153,20 @@ pub const LinkerContext = struct { sorted_keys.appendSliceAssumeCapacity(js_chunks.keys()); sorted_keys.sortAsc(); var js_chunk_indices_with_css = try BabyList(u32).initCapacity(temp_allocator, js_chunks_with_css); - for (sorted_keys.slice(), 0..) |key, i| { + for (sorted_keys.slice()) |key| { const chunk = js_chunks.get(key) orelse unreachable; - sorted_chunks.appendAssumeCapacity(chunk); if (chunk.content.javascript.css_chunks.len > 0) - js_chunk_indices_with_css.appendAssumeCapacity(@intCast(i)); + js_chunk_indices_with_css.appendAssumeCapacity(sorted_chunks.len); + + sorted_chunks.appendAssumeCapacity(chunk); + + // Attempt to order the JS HTML chunk immediately after the non-html one. + if (chunk.has_html_chunk and experimental_html) { + if (html_chunks.fetchSwapRemove(key)) |html_chunk| { + sorted_chunks.appendAssumeCapacity(html_chunk.value); + } + } } if (css_chunks.count() > 0) { @@ -6021,6 +6191,12 @@ pub const LinkerContext = struct { } } + // We don't care about the order of the HTML chunks that have + // no JS chunks. + if (experimental_html) { + try sorted_chunks.append(this.allocator, html_chunks.values()); + } + break :sort_chunks sorted_chunks.slice(); }; @@ -6030,7 +6206,7 @@ pub const LinkerContext = struct { // to look up the path for this chunk to use with the import. for (chunks, 0..) |*chunk, chunk_id| { if (chunk.entry_point.is_entry_point) { - entry_point_chunk_indices[chunk.entry_point.source_index] = @as(u32, @truncate(chunk_id)); + entry_point_chunk_indices[chunk.entry_point.source_index] = @intCast(chunk_id); } } @@ -6057,7 +6233,7 @@ pub const LinkerContext = struct { this.unique_key_prefix = chunk.unique_key[0..std.fmt.count("{}", .{bun.fmt.hexIntLower(unique_key)})]; if (chunk.entry_point.is_entry_point and - kinds[chunk.entry_point.source_index] == .user_specified) + (chunk.content == .html or (kinds[chunk.entry_point.source_index] == .user_specified and !chunk.has_html_chunk))) { chunk.template = PathTemplate.file; if (this.resolver.opts.entry_naming.len > 0) @@ -6110,6 +6286,7 @@ pub const LinkerContext = struct { ); }, .css => {}, // handled in `findImportedCSSFilesInJSOrder` + .html => {}, } } } @@ -6139,7 +6316,7 @@ pub const LinkerContext = struct { const FindImportedPartsVisitor = struct { entry_bits: *const AutoBitSet, flags: []const JSMeta.Flags, - parts: []BabyList(js_ast.Part), + parts: []BabyList(Part), import_records: []BabyList(ImportRecord), files: std.ArrayList(Index.Int), part_ranges: std.ArrayList(PartRange), @@ -6789,7 +6966,7 @@ pub const LinkerContext = struct { @panic("Internal error: expected at least one part for lazy export"); } - var part: *js_ast.Part = &parts.ptr[1]; + var part: *Part = &parts.ptr[1]; if (part.stmts.len == 0) { @panic("Internal error: expected at least one statement in the lazy export"); @@ -6801,7 +6978,7 @@ pub const LinkerContext = struct { } const expr = Expr{ - .data = stmt.data.s_lazy_export, + .data = stmt.data.s_lazy_export.*, .loc = stmt.loc, }; const module_ref = this.graph.ast.items(.module_ref)[source_index]; @@ -6933,7 +7110,7 @@ pub const LinkerContext = struct { { var import_records_list: []ImportRecord.List = this.graph.ast.items(.import_records); - // var parts_list: [][]js_ast.Part = this.graph.ast.items(.parts); + // var parts_list: [][]Part = this.graph.ast.items(.parts); var exports_kind: []js_ast.ExportsKind = this.graph.ast.items(.exports_kind); var entry_point_kinds: []EntryPoint.Kind = this.graph.files.items(.entry_point_kind); var named_imports: []js_ast.Ast.NamedImports = this.graph.ast.items(.named_imports); @@ -6966,15 +7143,14 @@ pub const LinkerContext = struct { const import_records: []ImportRecord = import_records_list[id].slice(); // Is it CSS? - if (css_asts[id]) |css| { - _ = css; // autofix + if (css_asts[id] != null) { // Inline URLs for non-CSS files into the CSS file - for (import_records, 0..) |*record, import_record_idx| { - _ = import_record_idx; // autofix + for (import_records) |*record| { if (record.source_index.isValid()) { // Other file is not CSS if (css_asts[record.source_index.get()] == null) { - if (urls_for_css[record.source_index.get()]) |url| { + const url = urls_for_css[record.source_index.get()]; + if (url.len > 0) { record.path.text = url; } } @@ -7424,9 +7600,9 @@ pub const LinkerContext = struct { ) catch unreachable; } var imports_to_bind_list: []RefImportData = this.graph.meta.items(.imports_to_bind); - var parts_list: []js_ast.Part.List = ast_fields.items(.parts); + var parts_list: []Part.List = ast_fields.items(.parts); - var parts: []js_ast.Part = parts_list[id].slice(); + var parts: []Part = parts_list[id].slice(); const imports_to_bind = &imports_to_bind_list[id]; for (imports_to_bind.keys(), imports_to_bind.values()) |ref_untyped, import_untyped| { @@ -7437,7 +7613,7 @@ pub const LinkerContext = struct { if (named_imports[id].get(ref)) |named_import| { for (named_import.local_parts_with_uses.slice()) |part_index| { - var part: *js_ast.Part = &parts[part_index]; + var part: *Part = &parts[part_index]; const parts_declaring_symbol: []const u32 = this.graph.topLevelSymbolToParts(import_source_index, import.data.import_ref); const total_len = parts_declaring_symbol.len + @as(usize, import.re_exports.len) + @as(usize, part.dependencies.len); @@ -7656,7 +7832,7 @@ pub const LinkerContext = struct { // module may be simultaneously imported and required, and the // importing code should not see "__esModule" while the requiring // code should see "__esModule". This is an extremely complex - // and subtle set of bundler interop issues. See for example + // and subtle set of transpiler interop issues. See for example // https://github.com/evanw/esbuild/issues/1591. if (kind == .require) { record.wrap_with_to_commonjs = true; @@ -7789,7 +7965,7 @@ pub const LinkerContext = struct { var properties = std.ArrayList(js_ast.G.Property) .initCapacity(allocator, export_aliases.len) catch bun.outOfMemory(); - var ns_export_symbol_uses = js_ast.Part.SymbolUseMap{}; + var ns_export_symbol_uses = Part.SymbolUseMap{}; ns_export_symbol_uses.ensureTotalCapacity(allocator, export_aliases.len) catch bun.outOfMemory(); const initial_flags = c.graph.meta.items(.flags)[id]; @@ -8111,7 +8287,7 @@ pub const LinkerContext = struct { var local_dependencies = std.AutoHashMap(u32, u32).init(allocator); defer local_dependencies.deinit(); - const parts_slice: []js_ast.Part = c.graph.ast.items(.parts)[id].slice(); + const parts_slice: []Part = c.graph.ast.items(.parts)[id].slice(); const named_imports: *js_ast.Ast.NamedImports = &c.graph.ast.items(.named_imports)[id]; const our_imports_to_bind = imports_to_bind[id]; @@ -8347,7 +8523,7 @@ pub const LinkerContext = struct { const CrossChunkDependencies = struct { chunk_meta: []ChunkMeta, chunks: []Chunk, - parts: []BabyList(js_ast.Part), + parts: []BabyList(Part), import_records: []BabyList(bun.ImportRecord), flags: []const JSMeta.Flags, entry_point_chunk_indices: []Index.Int, @@ -8778,6 +8954,7 @@ pub const LinkerContext = struct { switch (chunk.content) { .javascript => postProcessJSChunk(ctx, worker, chunk, chunk_index) catch |err| Output.panic("TODO: handle error: {s}", .{@errorName(err)}), .css => postProcessCSSChunk(ctx, worker, chunk) catch |err| Output.panic("TODO: handle error: {s}", .{@errorName(err)}), + .html => postProcessHTMLChunk(ctx, worker, chunk) catch |err| Output.panic("TODO: handle error: {s}", .{@errorName(err)}), } } @@ -8793,7 +8970,7 @@ pub const LinkerContext = struct { defer trace.end(); const all_module_scopes = c.graph.ast.items(.module_scope); const all_flags: []const JSMeta.Flags = c.graph.meta.items(.flags); - const all_parts: []const js_ast.Part.List = c.graph.ast.items(.parts); + const all_parts: []const Part.List = c.graph.ast.items(.parts); const all_wrapper_refs: []const Ref = c.graph.ast.items(.wrapper_ref); const all_import_records: []const ImportRecord.List = c.graph.ast.items(.import_records); @@ -8902,8 +9079,8 @@ pub const LinkerContext = struct { } top_level_symbols.clearRetainingCapacity(); - for (sorted_imports_from_other_chunks.items) |stable| { - try minify_renamer.accumulateSymbolUseCount(&top_level_symbols, stable.ref, 1, stable_source_indices); + for (sorted_imports_from_other_chunks.items) |stable_ref| { + try minify_renamer.accumulateSymbolUseCount(&top_level_symbols, stable_ref.ref, 1, stable_source_indices); } top_level_symbols_all.appendSlice(top_level_symbols.items) catch unreachable; try minify_renamer.allocateTopLevelSymbolSlots(top_level_symbols_all); @@ -8930,7 +9107,7 @@ pub const LinkerContext = struct { for (files_in_order) |source_index| { const wrap = all_flags[source_index].wrap; - const parts: []const js_ast.Part = all_parts[source_index].slice(); + const parts: []const Part = all_parts[source_index].slice(); switch (wrap) { // Modules wrapped in a CommonJS closure look like this: @@ -9042,6 +9219,7 @@ pub const LinkerContext = struct { switch (chunk.content) { .javascript => generateJSRenamer_(ctx, worker, chunk, chunk_index), .css => {}, + .html => {}, } } @@ -9074,6 +9252,16 @@ pub const LinkerContext = struct { ctx.chunk.compile_results_for_chunk[part_range.i] = generateCompileResultForCssChunkImpl(worker, ctx.c, ctx.chunk, part_range.i); } + fn generateCompileResultForHtmlChunk(task: *ThreadPoolLib.Task) void { + const part_range: *const PendingPartRange = @fieldParentPtr("task", task); + const ctx = part_range.ctx; + defer ctx.wg.finish(); + var worker = ThreadPool.Worker.get(@fieldParentPtr("linker", ctx.c)); + defer worker.unget(); + + ctx.chunk.compile_results_for_chunk[part_range.i] = generateCompileResultForHTMLChunkImpl(worker, ctx.c, ctx.chunk, ctx.chunks); + } + fn generateCompileResultForCssChunkImpl(worker: *ThreadPool.Worker, c: *LinkerContext, chunk: *Chunk, imports_in_chunk_index: u32) CompileResult { const trace = tracer(@src(), "generateCodeForFileInChunkCss"); defer trace.end(); @@ -9106,13 +9294,15 @@ pub const LinkerContext = struct { }; var import_records = BabyList(ImportRecord).init(&import_records_); const css: *const bun.css.BundlerStyleSheet = &chunk.content.css.asts[imports_in_chunk_index]; + const printer_options = bun.css.PrinterOptions{ + // TODO: make this more configurable + .minify = c.options.minify_whitespace, + .targets = bun.css.Targets.forBundlerTarget(c.options.target), + }; _ = css.toCssWithWriter( worker.allocator, &buffer_writer, - bun.css.PrinterOptions{ - // TODO: make this more configurable - .minify = c.options.minify_whitespace, - }, + printer_options, &import_records, ) catch { @panic("TODO: HANDLE THIS ERROR!"); @@ -9126,13 +9316,15 @@ pub const LinkerContext = struct { }, .source_index => |idx| { const css: *const bun.css.BundlerStyleSheet = &chunk.content.css.asts[imports_in_chunk_index]; + const printer_options = bun.css.PrinterOptions{ + .targets = bun.css.Targets.forBundlerTarget(c.options.target), + // TODO: make this more configurable + .minify = c.options.minify_whitespace or c.options.minify_syntax or c.options.minify_identifiers, + }; _ = css.toCssWithWriter( worker.allocator, &buffer_writer, - bun.css.PrinterOptions{ - // TODO: make this more configurable - .minify = c.options.minify_whitespace or c.options.minify_syntax or c.options.minify_identifiers, - }, + printer_options, &c.graph.ast.items(.import_records)[idx.get()], ) catch { @panic("TODO: HANDLE THIS ERROR!"); @@ -9370,6 +9562,161 @@ pub const LinkerContext = struct { } } + fn generateCompileResultForHTMLChunkImpl(worker: *ThreadPool.Worker, c: *LinkerContext, chunk: *Chunk, chunks: []Chunk) CompileResult { + const parse_graph = c.parse_graph; + const input_files = parse_graph.input_files.slice(); + const sources = input_files.items(.source); + const import_records = c.graph.ast.items(.import_records); + + // We want to rewrite the HTML with the following transforms: + // 1. Remove all ", .{js_chunk.unique_key}) catch bun.outOfMemory(); + defer allocator.free(script); + element.append(script, true) catch bun.outOfMemory(); + } + } + + const processor = HTMLScanner.HTMLProcessor(@This(), true); + + pub fn run(this: *@This(), input: []const u8) !void { + processor.run(this, input) catch bun.outOfMemory(); + } + }; + + var html_loader = HTMLLoader{ + .linker = c, + .source_index = chunk.entry_point.source_index, + .import_records = import_records[chunk.entry_point.source_index].slice(), + .log = c.log, + .allocator = worker.allocator, + .minify_whitespace = c.options.minify_whitespace, + .chunk = chunk, + .chunks = chunks, + .output = std.ArrayList(u8).init(worker.allocator), + .current_import_record_index = 0, + }; + + html_loader.run(sources[chunk.entry_point.source_index].contents) catch bun.outOfMemory(); + + return .{ + .html = .{ + .code = html_loader.output.items, + .source_index = chunk.entry_point.source_index, + }, + }; + } + + fn postProcessHTMLChunk(ctx: GenerateChunkCtx, worker: *ThreadPool.Worker, chunk: *Chunk) !void { + + // This is where we split output into pieces + + const c = ctx.c; + var j = StringJoiner{ + .allocator = worker.allocator, + .watcher = .{ + .input = chunk.unique_key, + }, + }; + + const compile_results = chunk.compile_results_for_chunk; + + for (compile_results) |compile_result| { + j.push(compile_result.code(), bun.default_allocator); + } + + j.ensureNewlineAtEnd(); + + chunk.intermediate_output = c.breakOutputIntoPieces( + worker.allocator, + &j, + @as(u32, @truncate(ctx.chunks.len)), + ) catch bun.outOfMemory(); + + chunk.isolated_hash = c.generateIsolatedHash(chunk); + } + // This runs after we've already populated the compile results fn postProcessCSSChunk(ctx: GenerateChunkCtx, worker: *ThreadPool.Worker, chunk: *Chunk) !void { const c = ctx.c; @@ -9546,7 +9893,7 @@ pub const LinkerContext = struct { c.source_(chunk.entry_point.source_index), print_options, cross_chunk_import_records.slice(), - &[_]js_ast.Part{ + &[_]Part{ .{ .stmts = chunk.content.javascript.cross_chunk_prefix_stmts.slice() }, }, chunk.renamer, @@ -9559,7 +9906,7 @@ pub const LinkerContext = struct { c.source_(chunk.entry_point.source_index), print_options, &.{}, - &[_]js_ast.Part{ + &[_]Part{ .{ .stmts = chunk.content.javascript.cross_chunk_suffix_stmts.slice() }, }, chunk.renamer, @@ -10669,7 +11016,7 @@ pub const LinkerContext = struct { c.source_(source_index), print_options, ast.import_records.slice(), - &[_]js_ast.Part{ + &[_]Part{ .{ .stmts = stmts.items, }, @@ -11454,7 +11801,7 @@ pub const LinkerContext = struct { // hmr-runtime.ts defines `module.importSync` to be // a synchronous import. this is different from // require in that esm <-> cjs is handled - // automatically, instead of with bundler-added + // automatically, instead of with transpiler-added // annotations like '__commonJS'. // // this cannot be done in the parse step because the final @@ -11564,7 +11911,7 @@ pub const LinkerContext = struct { allocator: std.mem.Allocator, temp_allocator: std.mem.Allocator, ) js_printer.PrintResult { - const parts: []js_ast.Part = c.graph.ast.items(.parts)[part_range.source_index.get()].slice()[part_range.part_index_begin..part_range.part_index_end]; + const parts: []Part = c.graph.ast.items(.parts)[part_range.source_index.get()].slice()[part_range.part_index_begin..part_range.part_index_end]; const all_flags: []const JSMeta.Flags = c.graph.meta.items(.flags); const flags = all_flags[part_range.source_index.get()]; const wrapper_part_index = if (flags.wrap != .none) @@ -12161,8 +12508,8 @@ pub const LinkerContext = struct { runtime_require_ref: ?Ref, source_index: Index, ) js_printer.PrintResult { - const parts_to_print = &[_]js_ast.Part{ - js_ast.Part{ .stmts = out_stmts }, + const parts_to_print = &[_]Part{ + Part{ .stmts = out_stmts }, }; const print_options = js_printer.Options{ @@ -12264,7 +12611,7 @@ pub const LinkerContext = struct { var has_js_chunk = false; var has_css_chunk = false; - + var has_html_chunk = false; bun.assert(chunks.len > 0); { @@ -12290,7 +12637,7 @@ pub const LinkerContext = struct { c.source_maps.line_offset_tasks.len = 0; } - if (c.options.experimental_css) { + if (c.options.experimental.css) { // Per CSS chunk: // Remove duplicate rules across files. This must be done in serial, not // in parallel, and must be done from the last rule to the first rule. @@ -12346,6 +12693,7 @@ pub const LinkerContext = struct { defer c.allocator.free(chunk_contexts); var wait_group = try c.allocator.create(sync.WaitGroup); wait_group.init(); + defer { wait_group.deinit(); c.allocator.destroy(wait_group); @@ -12367,6 +12715,13 @@ pub const LinkerContext = struct { total_count += chunk.content.css.imports_in_chunk_in_order.len; chunk.compile_results_for_chunk = c.allocator.alloc(CompileResult, chunk.content.css.imports_in_chunk_in_order.len) catch bun.outOfMemory(); }, + .html => { + has_html_chunk = true; + // HTML gets only one chunk. + chunk_ctx.* = .{ .wg = wait_group, .c = c, .chunks = chunks, .chunk = chunk }; + total_count += 1; + chunk.compile_results_for_chunk = c.allocator.alloc(CompileResult, 1) catch bun.outOfMemory(); + }, } } @@ -12406,8 +12761,7 @@ pub const LinkerContext = struct { } }, .css => { - for (chunk.content.css.imports_in_chunk_in_order.slice(), 0..) |css_import, i| { - _ = css_import; // autofix + for (0..chunk.content.css.imports_in_chunk_in_order.len) |i| { remaining_part_ranges[0] = .{ .part_range = .{}, .i = @as(u32, @truncate(i)), @@ -12421,6 +12775,19 @@ pub const LinkerContext = struct { remaining_part_ranges = remaining_part_ranges[1..]; } }, + .html => { + remaining_part_ranges[0] = .{ + .part_range = .{}, + .i = 0, + .task = ThreadPoolLib.Task{ + .callback = &generateCompileResultForHtmlChunk, + }, + .ctx = chunk_ctx, + }; + + batch.push(ThreadPoolLib.Batch.from(&remaining_part_ranges[0].task)); + remaining_part_ranges = remaining_part_ranges[1..]; + }, } } wait_group.counter = @as(u32, @truncate(total_count)); @@ -12462,7 +12829,7 @@ pub const LinkerContext = struct { // - Reuse unchanged parts to assemble the full bundle if Cmd+R is used in the browser // - Send only the newly changed code through a socket. // - // When this isnt the initial bundle, concatenation as usual would produce a + // When this isn't the initial bundle, concatenation as usual would produce a // broken module. It is DevServer's job to create and send HMR patches. if (is_dev_server) return; @@ -12572,7 +12939,7 @@ pub const LinkerContext = struct { ) catch unreachable; const root_path = c.resolver.opts.output_dir; - const more_than_one_output = c.parse_graph.additional_output_files.items.len > 0 or c.options.generate_bytecode_cache or (has_css_chunk and has_js_chunk); + const more_than_one_output = c.parse_graph.additional_output_files.items.len > 0 or c.options.generate_bytecode_cache or (has_css_chunk and has_js_chunk) or (has_html_chunk and (has_js_chunk or has_css_chunk)); if (!c.resolver.opts.compile and more_than_one_output and !c.resolver.opts.supports_multiple_outputs) { try c.log.addError(null, Logger.Loc.Empty, "cannot write multiple output files without an output directory"); @@ -12773,6 +13140,7 @@ pub const LinkerContext = struct { .referenced_css_files = switch (chunk.content) { .javascript => |js| @ptrCast(try bun.default_allocator.dupe(u32, js.css_chunks)), .css => &.{}, + .html => &.{}, }, })); if (sourcemap_output_file) |sourcemap_file| { @@ -13184,6 +13552,7 @@ pub const LinkerContext = struct { .referenced_css_files = switch (chunk.content) { .javascript => |js| @ptrCast(try bun.default_allocator.dupe(u32, js.css_chunks)), .css => &.{}, + .html => &.{}, }, })); @@ -13228,7 +13597,6 @@ pub const LinkerContext = struct { .buffer = JSC.Buffer{ .buffer = .{ .ptr = @constCast(bytes.ptr), - // TODO: handle > 4 GB files .len = @as(u32, @truncate(bytes.len)), .byte_len = @as(u32, @truncate(bytes.len)), }, @@ -13295,7 +13663,7 @@ pub const LinkerContext = struct { entry_points_count: usize, distances: []u32, distance: u32, - parts: []bun.BabyList(js_ast.Part), + parts: []bun.BabyList(Part), import_records: []bun.BabyList(bun.ImportRecord), file_entry_bits: []AutoBitSet, css_reprs: []?*bun.css.BundlerStyleSheet, @@ -13385,7 +13753,7 @@ pub const LinkerContext = struct { c: *LinkerContext, source_index: Index.Int, side_effects: []_resolver.SideEffects, - parts: []bun.BabyList(js_ast.Part), + parts: []bun.BabyList(Part), import_records: []bun.BabyList(bun.ImportRecord), entry_point_kinds: []EntryPoint.Kind, css_reprs: []?*bun.css.BundlerStyleSheet, @@ -13501,12 +13869,12 @@ pub const LinkerContext = struct { part_index: Index.Int, source_index: Index.Int, side_effects: []_resolver.SideEffects, - parts: []bun.BabyList(js_ast.Part), + parts: []bun.BabyList(Part), import_records: []bun.BabyList(bun.ImportRecord), entry_point_kinds: []EntryPoint.Kind, css_reprs: []?*bun.css.BundlerStyleSheet, ) void { - const part: *js_ast.Part = &parts[source_index].slice()[part_index]; + const part: *Part = &parts[source_index].slice()[part_index]; // only once if (part.is_live) { @@ -13715,7 +14083,7 @@ pub const LinkerContext = struct { next_source.path.pretty, named_import.alias.?, }, - "Bun's bundler defaults to browser builds instead of node or bun builds. If you want to use node or bun builds, you can set the target to \"node\" or \"bun\" in the bundler options.", + "Bun's bundler defaults to browser builds instead of node or bun builds. If you want to use node or bun builds, you can set the target to \"node\" or \"bun\" in the transpiler options.", .{}, r, ) catch unreachable; @@ -13741,7 +14109,7 @@ pub const LinkerContext = struct { next_source.path.pretty, named_import.alias.?, }, - "Bun's bundler defaults to browser builds instead of node or bun builds. If you want to use node or bun builds, you can set the target to \"node\" or \"bun\" in the bundler options.", + "Bun's bundler defaults to browser builds instead of node or bun builds. If you want to use node or bun builds, you can set the target to \"node\" or \"bun\" in the transpiler options.", .{}, r, ) catch unreachable; @@ -13881,7 +14249,7 @@ pub const LinkerContext = struct { for (common_js_parts) |part_id| { const runtime_parts = c.graph.ast.items(.parts)[Index.runtime.get()].slice(); - const part: *js_ast.Part = &runtime_parts[part_id]; + const part: *Part = &runtime_parts[part_id]; const symbol_refs = part.symbol_uses.keys(); for (symbol_refs) |ref| { if (ref.eql(c.cjs_runtime_ref)) continue; @@ -13904,7 +14272,7 @@ pub const LinkerContext = struct { .{ .stmts = &.{}, .symbol_uses = bun.from( - js_ast.Part.SymbolUseMap, + Part.SymbolUseMap, c.allocator, .{ .{ wrapper_ref, .{ .count_estimate = 1 } }, @@ -13965,7 +14333,7 @@ pub const LinkerContext = struct { source_index, .{ .symbol_uses = bun.from( - js_ast.Part.SymbolUseMap, + Part.SymbolUseMap, c.allocator, .{ .{ wrapper_ref, .{ .count_estimate = 1 } }, @@ -14597,6 +14965,7 @@ pub const Chunk = struct { entry_point: Chunk.EntryPoint = .{}, is_executable: bool = false, + has_html_chunk: bool = false, output_source_map: sourcemap.SourceMapPieces, @@ -14611,6 +14980,30 @@ pub const Chunk = struct { return this.entry_point.is_entry_point; } + pub fn getJSChunkForHTML(this: *const Chunk, chunks: []Chunk) ?*Chunk { + const entry_point_id = this.entry_point.entry_point_id; + for (chunks) |*other| { + if (other.content == .javascript) { + if (other.entry_point.entry_point_id == entry_point_id) { + return other; + } + } + } + return null; + } + + pub fn getCSSChunkForHTML(this: *const Chunk, chunks: []Chunk) ?*Chunk { + const entry_point_id = this.entry_point.entry_point_id; + for (chunks) |*other| { + if (other.content == .css) { + if (other.entry_point.entry_point_id == entry_point_id) { + return other; + } + } + } + return null; + } + pub inline fn entryBits(this: *const Chunk) *const AutoBitSet { return &this.entry_bits; } @@ -14962,11 +15355,12 @@ pub const Chunk = struct { pub const EntryPoint = packed struct(u64) { /// Index into `Graph.input_files` source_index: u32 = 0, - entry_point_id: u31 = 0, + entry_point_id: ID = 0, is_entry_point: bool = false, + is_html: bool = false, /// so `EntryPoint` can be a u64 - pub const ID = u31; + pub const ID = u30; }; pub const JavaScriptChunk = struct { @@ -15060,16 +15454,20 @@ pub const Chunk = struct { pub const ContentKind = enum { javascript, css, + html, }; + pub const HtmlChunk = struct {}; + pub const Content = union(ContentKind) { javascript: JavaScriptChunk, css: CssChunk, - + html: HtmlChunk, pub fn loader(this: *const Content) Loader { return switch (this.*) { .javascript => .js, .css => .css, + .html => .html, }; } @@ -15077,6 +15475,7 @@ pub const Chunk = struct { return switch (this.*) { .javascript => "js", .css => "css", + .html => "html", }; } }; @@ -15156,6 +15555,10 @@ pub const CompileResult = union(enum) { // TODO: we need to do this source_map: ?bun.sourcemap.Chunk = null, }, + html: struct { + source_index: Index.Int, + code: []const u8, + }, pub const empty = CompileResult{ .javascript = .{ @@ -15174,7 +15577,7 @@ pub const CompileResult = union(enum) { .result => |r2| r2.code, else => "", }, - .css => |*c| c.code, + inline .html, .css => |*c| c.code, }; } @@ -15185,14 +15588,13 @@ pub const CompileResult = union(enum) { else => null, }, .css => |*c| c.source_map, + .html => null, }; } pub fn sourceIndex(this: *const CompileResult) Index.Int { return switch (this.*) { - .javascript => |r| r.source_index, - .css => |*c| c.source_index, - // else => 0, + inline else => |*r| r.source_index, }; } }; @@ -15485,7 +15887,7 @@ pub const AstBuilder = struct { bun.assert(p.scopes.items.len == 0); const module_scope = p.current_scope; - var parts = try js_ast.Part.List.initCapacity(p.allocator, 2); + var parts = try Part.List.initCapacity(p.allocator, 2); parts.len = 2; parts.mut(0).* = .{}; parts.mut(1).* = .{ @@ -15494,7 +15896,7 @@ pub const AstBuilder = struct { // pretend that every symbol was used .symbol_uses = uses: { - var map: js_ast.Part.SymbolUseMap = .{}; + var map: Part.SymbolUseMap = .{}; try map.ensureTotalCapacity(p.allocator, p.symbols.items.len); for (0..p.symbols.items.len) |i| { map.putAssumeCapacity(Ref{ @@ -15538,7 +15940,8 @@ pub const AstBuilder = struct { _ = try js_parser.ImportScanner.scan(AstBuilder, p, p.stmts.items, false, true, &hmr_transform_ctx); - const new_parts = try hmr_transform_ctx.finalize(p, parts.slice()); + try hmr_transform_ctx.finalize(p, parts.slice()); + const new_parts = parts.slice(); // preserve original capacity parts.len = @intCast(new_parts.len); bun.assert(new_parts.ptr == parts.ptr); @@ -15633,12 +16036,12 @@ pub const CssEntryPointMeta = struct { imported_on_server: bool, }; -/// The lifetime of this structure is tied to the bundler's arena +/// The lifetime of this structure is tied to the transpiler's arena pub const BakeBundleStart = struct { css_entry_points: std.AutoArrayHashMapUnmanaged(Index, CssEntryPointMeta), }; -/// The lifetime of this structure is tied to the bundler's arena +/// The lifetime of this structure is tied to the transpiler's arena pub const BakeBundleOutput = struct { chunks: []Chunk, css_file_list: std.AutoArrayHashMapUnmanaged(Index, CssEntryPointMeta), diff --git a/src/bundler/entry_points.zig b/src/bundler/entry_points.zig index 306116742f..2636cc7960 100644 --- a/src/bundler/entry_points.zig +++ b/src/bundler/entry_points.zig @@ -4,7 +4,7 @@ const bun = @import("root").bun; const string = bun.string; const Fs = @import("../fs.zig"); const js_ast = bun.JSAst; -const Bundler = bun.Bundler; +const Transpiler = bun.Transpiler; const strings = bun.strings; pub const FallbackEntryPoint = struct { @@ -16,8 +16,8 @@ pub const FallbackEntryPoint = struct { pub fn generate( entry: *FallbackEntryPoint, input_path: string, - comptime BundlerType: type, - bundler: *BundlerType, + comptime TranspilerType: type, + transpiler: *TranspilerType, ) !void { // This is *extremely* naive. // The basic idea here is this: @@ -29,7 +29,7 @@ pub const FallbackEntryPoint = struct { // We go through the steps of printing the code -- only to then parse/transpile it because // we want it to go through the linker and the rest of the transpilation process - const disable_css_imports = bundler.options.framework.?.client_css_in_js != .auto_onimportcss; + const disable_css_imports = transpiler.options.framework.?.client_css_in_js != .auto_onimportcss; var code: string = undefined; @@ -48,7 +48,7 @@ pub const FallbackEntryPoint = struct { if (count < entry.code_buffer.len) { code = try std.fmt.bufPrint(&entry.code_buffer, fmt, args); } else { - code = try std.fmt.allocPrint(bundler.allocator, fmt, args); + code = try std.fmt.allocPrint(transpiler.allocator, fmt, args); } } else { const fmt = @@ -64,7 +64,7 @@ pub const FallbackEntryPoint = struct { if (count < entry.code_buffer.len) { code = try std.fmt.bufPrint(&entry.code_buffer, fmt, args); } else { - code = try std.fmt.allocPrint(bundler.allocator, fmt, args); + code = try std.fmt.allocPrint(transpiler.allocator, fmt, args); } } @@ -105,7 +105,7 @@ pub const ClientEntryPoint = struct { return outbuffer[0 .. generated_path.len + original_ext.len]; } - pub fn generate(entry: *ClientEntryPoint, comptime BundlerType: type, bundler: *BundlerType, original_path: Fs.PathName, client: string) !void { + pub fn generate(entry: *ClientEntryPoint, comptime TranspilerType: type, transpiler: *TranspilerType, original_path: Fs.PathName, client: string) !void { // This is *extremely* naive. // The basic idea here is this: @@ -118,7 +118,7 @@ pub const ClientEntryPoint = struct { // we want it to go through the linker and the rest of the transpilation process const dir_to_use: string = original_path.dirWithTrailingSlash(); - const disable_css_imports = bundler.options.framework.?.client_css_in_js != .auto_onimportcss; + const disable_css_imports = transpiler.options.framework.?.client_css_in_js != .auto_onimportcss; var code: string = undefined; @@ -266,7 +266,7 @@ pub const MacroEntryPoint = struct { pub fn generate( entry: *MacroEntryPoint, - _: *Bundler, + _: *Transpiler, import_path: Fs.PathName, function_name: string, macro_id: i32, diff --git a/src/bunfig.zig b/src/bunfig.zig index f5e41c434d..8ca56ec0ee 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -469,6 +469,12 @@ pub const Bunfig = struct { } } + if (install_obj.get("saveTextLockfile")) |save_text_lockfile| { + if (save_text_lockfile.asBool()) |value| { + install.save_text_lockfile = value; + } + } + if (install_obj.get("concurrentScripts")) |jobs| { if (jobs.data == .e_number) { install.concurrent_scripts = jobs.data.e_number.toU32(); @@ -591,6 +597,14 @@ pub const Bunfig = struct { } } + if (run_expr.get("elide-lines")) |elide_lines| { + if (elide_lines.data == .e_number) { + this.ctx.bundler_options.elide_lines = @intFromFloat(elide_lines.data.e_number.value); + } else { + try this.addError(elide_lines.loc, "Expected number"); + } + } + if (run_expr.get("shell")) |shell| { if (shell.asString(allocator)) |value| { if (strings.eqlComptime(value, "bun")) { diff --git a/src/c-headers-for-zig.h b/src/c-headers-for-zig.h new file mode 100644 index 0000000000..42c03aca64 --- /dev/null +++ b/src/c-headers-for-zig.h @@ -0,0 +1,21 @@ +// This file is run through translate-c and exposed to Zig code +// under the namespace bun.C.translated. Prefer adding includes +// to this file instead of manually porting struct definitions +// into Zig code. By using automatic translation, differences +// in platforms can be avoided. +// +// When Zig is translating this file, it will define these macros: +// - WINDOWS +// - DARWIN +// - LINUX +// - POSIX + +// OnBeforeParseResult, etc... +#include "../packages/bun-native-bundler-plugin-api/bundler_plugin.h" + +#if POSIX +// passwd, getpwuid_r +#include "pwd.h" +// geteuid +#include +#endif diff --git a/src/c.zig b/src/c.zig index e0b8238fca..03ee097e08 100644 --- a/src/c.zig +++ b/src/c.zig @@ -2,6 +2,8 @@ const std = @import("std"); const bun = @import("root").bun; const Environment = @import("./env.zig"); +pub const translated = @import("translated-c-headers"); + const PlatformSpecific = switch (Environment.os) { .mac => @import("./darwin_c.zig"), .linux => @import("./linux_c.zig"), @@ -44,7 +46,7 @@ pub extern "c" fn strchr(str: [*]const u8, char: u8) ?[*]const u8; pub fn lstat_absolute(path: [:0]const u8) !Stat { if (builtin.os.tag == .windows) { - @compileError("Not implemented yet, conside using bun.sys.lstat()"); + @compileError("Not implemented yet, consider using bun.sys.lstat()"); } var st = zeroes(libc_stat); @@ -341,8 +343,8 @@ pub fn getSelfExeSharedLibPaths(allocator: std.mem.Allocator) error{OutOfMemory} /// Same as MADV_DONTNEED but used with posix_madvise() sys-tem system /// tem call. /// -/// MADV_FREE Indicates that the application will not need the infor-mation information -/// mation contained in this address range, so the pages may +/// MADV_FREE Indicates that the application will not need the information +/// contained in this address range, so the pages may /// be reused right away. The address range will remain /// valid. This is used with madvise() system call. /// @@ -350,16 +352,8 @@ pub fn getSelfExeSharedLibPaths(allocator: std.mem.Allocator) error{OutOfMemory} /// with POSIX_ prefix for the advice system call argument. pub extern "c" fn posix_madvise(ptr: *anyopaque, len: usize, advice: i32) c_int; -pub fn getProcessPriority(pid_: i32) i32 { - const pid = @as(c_uint, @intCast(pid_)); - return get_process_priority(pid); -} - -pub fn setProcessPriority(pid_: i32, priority_: i32) std.c.E { - if (pid_ < 0) return .SRCH; - - const pid = @as(c_uint, @intCast(pid_)); - const priority = @as(c_int, @intCast(priority_)); +pub fn setProcessPriority(pid: i32, priority: i32) std.c.E { + if (pid < 0) return .SRCH; const code: i32 = set_process_priority(pid, priority); @@ -404,7 +398,6 @@ pub fn getRelease(buf: []u8) []const u8 { } } -pub extern fn memmem(haystack: [*]const u8, haystacklen: usize, needle: [*]const u8, needlelen: usize) ?[*]const u8; pub extern fn cfmakeraw(*std.posix.termios) void; const LazyStatus = enum { @@ -468,9 +461,18 @@ pub fn dlsym(comptime Type: type, comptime name: [:0]const u8) ?Type { return dlsymWithHandle(Type, name, handle_getter); } +/// Error condition is encoded as null +/// The only error in this function is ESRCH (no process found) +pub fn getProcessPriority(pid: i32) ?i32 { + return switch (get_process_priority(pid)) { + std.math.maxInt(i32) => null, + else => |prio| prio, + }; +} + // set in c-bindings.cpp -pub extern fn get_process_priority(pid: c_uint) i32; -pub extern fn set_process_priority(pid: c_uint, priority: c_int) i32; +extern fn get_process_priority(pid: i32) i32; +pub extern fn set_process_priority(pid: i32, priority: i32) i32; pub extern fn strncasecmp(s1: [*]const u8, s2: [*]const u8, n: usize) i32; pub extern fn memmove(dest: [*]u8, src: [*]const u8, n: usize) void; @@ -493,3 +495,7 @@ pub extern "C" fn bun_restore_stdio() void; pub extern "C" fn open_as_nonblocking_tty(i32, i32) i32; pub extern fn strlen(ptr: [*c]const u8) usize; + +pub const passwd = translated.passwd; +pub const geteuid = translated.geteuid; +pub const getpwuid_r = translated.getpwuid_r; diff --git a/src/cli.zig b/src/cli.zig index 49a9124216..54037e2965 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -32,7 +32,7 @@ const configureTransformOptionsForBun = @import("./bun.js/config.zig").configure const clap = bun.clap; const BunJS = @import("./bun_js.zig"); const Install = @import("./install/install.zig"); -const bundler = bun.bundler; +const transpiler = bun.transpiler; const DotEnv = @import("./env_loader.zig"); const RunCommand_ = @import("./cli/run_command.zig").RunCommand; const CreateCommand_ = @import("./cli/create_command.zig").CreateCommand; @@ -243,6 +243,7 @@ pub const Arguments = struct { const auto_only_params = [_]ParamType{ // clap.parseParam("--all") catch unreachable, clap.parseParam("--silent Don't print the script command") catch unreachable, + clap.parseParam("--elide-lines Number of lines of script output shown when using --filter (default: 10). Set to 0 to show all lines.") catch unreachable, clap.parseParam("-v, --version Print version and exit") catch unreachable, clap.parseParam("--revision Print version with revision and exit") catch unreachable, } ++ auto_or_run_params; @@ -250,6 +251,7 @@ pub const Arguments = struct { const run_only_params = [_]ParamType{ clap.parseParam("--silent Don't print the script command") catch unreachable, + clap.parseParam("--elide-lines Number of lines of script output shown when using --filter (default: 10). Set to 0 to show all lines.") catch unreachable, } ++ auto_or_run_params; pub const run_params = run_only_params ++ runtime_params_ ++ transpiler_params_ ++ base_params_; @@ -284,13 +286,16 @@ pub const Arguments = struct { clap.parseParam("--minify-syntax Minify syntax and inline data") catch unreachable, clap.parseParam("--minify-whitespace Minify whitespace") catch unreachable, clap.parseParam("--minify-identifiers Minify identifiers") catch unreachable, - clap.parseParam("--experimental-css Enabled experimental CSS bundling") catch unreachable, + clap.parseParam("--experimental-css Enable experimental CSS bundling") catch unreachable, clap.parseParam("--experimental-css-chunking Chunk CSS files together to reduce duplicated CSS loaded in a browser. Only has an affect when multiple entrypoints import CSS") catch unreachable, + clap.parseParam("--experimental-html Use .html files as entry points for JavaScript & CSS") catch unreachable, clap.parseParam("--dump-environment-variables") catch unreachable, clap.parseParam("--conditions ... Pass custom conditions to resolve") catch unreachable, clap.parseParam("--app (EXPERIMENTAL) Build a web app for production using Bun Bake.") catch unreachable, clap.parseParam("--server-components (EXPERIMENTAL) Enable server components") catch unreachable, - clap.parseParam("--env Inline environment variables into the bundle as process.env.${name}. Defaults to 'inline'. To inline environment variables matching a prefix, use my prefix like 'FOO_PUBLIC_*'. To disable, use 'disable'. In Bun v1.2+, the default is 'disable'.") catch unreachable, + clap.parseParam("--env Inline environment variables into the bundle as process.env.${name}. Defaults to 'inline'. To inline environment variables matching a prefix, use my prefix like 'FOO_PUBLIC_*'. To disable, use 'disable'. In Bun v1.2+, the default is 'disable'.") catch unreachable, + clap.parseParam("--windows-hide-console When using --compile targeting Windows, prevent a Command prompt from opening alongside the executable") catch unreachable, + clap.parseParam("--windows-icon When using --compile targeting Windows, assign an executable icon") catch unreachable, } ++ if (FeatureFlags.bake_debugging_features) [_]ParamType{ clap.parseParam("--debug-dump-server-files When --app is set, dump all server files to disk even when building statically") catch unreachable, clap.parseParam("--debug-no-minify When --app is set, do not minify anything") catch unreachable, @@ -499,6 +504,15 @@ pub const Arguments = struct { if (cmd == .RunCommand or cmd == .AutoCommand) { ctx.filters = args.options("--filter"); + + if (args.option("--elide-lines")) |elide_lines| { + if (elide_lines.len > 0) { + ctx.bundler_options.elide_lines = std.fmt.parseInt(usize, elide_lines, 10) catch { + Output.prettyErrorln("error: Invalid elide-lines: \"{s}\"", .{elide_lines}); + Global.exit(1); + }; + } + } } if (cmd == .TestCommand) { @@ -828,7 +842,9 @@ pub const Arguments = struct { } const experimental_css = args.flag("--experimental-css"); - ctx.bundler_options.experimental_css = experimental_css; + const experimental_html = args.flag("--experimental-html"); + ctx.bundler_options.experimental.css = experimental_css; + ctx.bundler_options.experimental.html = experimental_html; ctx.bundler_options.css_chunking = args.flag("--experimental-css-chunking"); const minify_flag = args.flag("--minify"); @@ -928,6 +944,31 @@ pub const Arguments = struct { ctx.bundler_options.inline_entrypoint_import_meta_main = true; } + if (args.flag("--windows-hide-console")) { + // --windows-hide-console technically doesnt depend on WinAPI, but since since --windows-icon + // does, all of these customization options have been gated to windows-only + if (!Environment.isWindows) { + Output.errGeneric("Using --windows-hide-console is only available when compiling on Windows", .{}); + Global.crash(); + } + if (!ctx.bundler_options.compile) { + Output.errGeneric("--windows-hide-console requires --compile", .{}); + Global.crash(); + } + ctx.bundler_options.windows_hide_console = true; + } + if (args.option("--windows-icon")) |path| { + if (!Environment.isWindows) { + Output.errGeneric("Using --windows-icon is only available when compiling on Windows", .{}); + Global.crash(); + } + if (!ctx.bundler_options.compile) { + Output.errGeneric("--windows-icon requires --compile", .{}); + Global.crash(); + } + ctx.bundler_options.windows_icon = path; + } + if (args.option("--outdir")) |outdir| { if (outdir.len > 0) { ctx.bundler_options.outdir = outdir; @@ -1079,6 +1120,15 @@ pub const Arguments = struct { ctx.debug.silent = true; } + if (args.option("--elide-lines")) |elide_lines| { + if (elide_lines.len > 0) { + ctx.bundler_options.elide_lines = std.fmt.parseInt(usize, elide_lines, 10) catch { + Output.prettyErrorln("error: Invalid elide-lines: \"{s}\"", .{elide_lines}); + Global.exit(1); + }; + } + } + if (opts.define) |define| { if (define.keys.len > 0) bun.JSC.RuntimeTranspilerCache.is_disabled = true; @@ -1456,9 +1506,6 @@ pub const Command = struct { has_loaded_global_config: bool = false, pub const BundlerOptions = struct { - compile: bool = false, - compile_target: Cli.CompileTarget = .{}, - outdir: []const u8 = "", outfile: []const u8 = "", root_dir: []const u8 = "", @@ -1480,7 +1527,7 @@ pub const Command = struct { bytecode: bool = false, banner: []const u8 = "", footer: []const u8 = "", - experimental_css: bool = false, + experimental: options.Loader.Experimental = .{}, css_chunking: bool = false, bake: bool = false, @@ -1489,6 +1536,12 @@ pub const Command = struct { env_behavior: Api.DotEnvBehavior = .disable, env_prefix: []const u8 = "", + elide_lines: ?usize = null, + // Compile options + compile: bool = false, + compile_target: Cli.CompileTarget = .{}, + windows_hide_console: bool = false, + windows_icon: ?[]const u8 = null, }; pub fn create(allocator: std.mem.Allocator, log: *logger.Log, comptime command: Command.Tag) anyerror!Context { @@ -1888,7 +1941,6 @@ pub const Command = struct { completions = try RunCommand.completions(ctx, null, &reject_list, .script_and_descriptions); } else if (strings.eqlComptime(filter[0], "a")) { const FirstLetter = AddCompletions.FirstLetter; - const index = AddCompletions.index; outer: { if (filter.len > 1 and filter[1].len > 0) { @@ -1921,7 +1973,8 @@ pub const Command = struct { 'z' => FirstLetter.z, else => break :outer, }; - const results = index.get(first_letter); + AddCompletions.init(bun.default_allocator) catch bun.outOfMemory(); + const results = AddCompletions.getPackages(first_letter); var prefilled_i: usize = 0; for (results) |cur| { diff --git a/src/cli/add_completions.txt b/src/cli/add_completions.txt index 7f2670cf7f..b7c7b6a1f6 100644 --- a/src/cli/add_completions.txt +++ b/src/cli/add_completions.txt @@ -2218,6 +2218,7 @@ elm-hot elm-hot-webpack-loader elm-test elm-webpack-loader +elysia email-validator emailjs-base64 emailjs-imap-client @@ -3700,6 +3701,7 @@ homebridge-config-ui-x homedir homedir-polyfill honeybadger-js +hono hook-std hookable hooks diff --git a/src/cli/add_completions.zig b/src/cli/add_completions.zig index b96f1c1e81..70d7e497d9 100644 --- a/src/cli/add_completions.zig +++ b/src/cli/add_completions.zig @@ -1,6 +1,16 @@ -pub const add_completions: []const u8 = @embedFile("add_completions.txt"); +// Auto-generated file. Do not edit. +// To regenerate this file, run: +// +// bun misctools/generate-add-completions.ts +// +// If you update add_completions.txt, then you should run this script again. +// +// This used to be a comptime block, but it made the build too slow. +// Compressing the completions list saves about 100 KB of binary size. const std = @import("std"); -const Environment = @import("../env.zig"); +const bun = @import("root").bun; +const zstd = bun.zstd; +const Environment = bun.Environment; pub const FirstLetter = enum(u8) { a = 'a', @@ -31,58 +41,64 @@ pub const FirstLetter = enum(u8) { z = 'z', }; -pub const Index = std.EnumArray(FirstLetter, []const []const u8); -pub const index: Index = if (Environment.isDebug) Index.initFill(&.{"OOMWorkAround"}) else brk: { - var array: Index = Index.initFill(&[_][]const u8{}); +const compressed_data = [_]u8{ 40, 181, 47, 253, 164, 156, 121, 2, 0, 148, 101, 6, 158, 128, 11, 130, 29, 50, 176, 110, 136, 168, 13, 197, 255, 138, 8, 111, 46, 215, 144, 108, 221, 73, 168, 182, 179, 179, 179, 179, 179, 179, 51, 237, 133, 67, 129, 22, 40, 149, 155, 13, 77, 238, 238, 175, 34, 93, 183, 136, 45, 12, 248, 199, 52, 72, 79, 1, 55, 29, 153, 29, 166, 29, 235, 37, 92, 124, 219, 151, 239, 224, 193, 181, 73, 119, 101, 17, 191, 237, 69, 28, 40, 90, 23, 246, 210, 75, 200, 80, 249, 38, 68, 112, 232, 47, 107, 117, 80, 218, 229, 37, 108, 104, 236, 17, 254, 137, 39, 108, 176, 212, 110, 10, 196, 195, 108, 211, 246, 97, 118, 226, 9, 27, 38, 158, 208, 49, 241, 132, 10, 152, 126, 158, 112, 225, 61, 161, 130, 79, 135, 226, 158, 112, 225, 158, 176, 193, 216, 61, 225, 2, 187, 39, 84, 240, 134, 111, 218, 233, 144, 178, 116, 7, 148, 108, 39, 125, 178, 111, 202, 94, 211, 137, 94, 27, 180, 246, 223, 2, 14, 11, 126, 189, 100, 84, 49, 80, 154, 90, 78, 84, 54, 224, 150, 205, 61, 225, 162, 45, 115, 127, 45, 35, 238, 9, 25, 223, 152, 113, 79, 216, 144, 176, 177, 48, 78, 238, 9, 23, 205, 139, 147, 123, 122, 169, 139, 123, 194, 7, 4, 247, 132, 142, 79, 135, 178, 214, 50, 79, 99, 135, 188, 35, 222, 202, 73, 175, 126, 173, 191, 237, 233, 100, 63, 27, 106, 204, 180, 116, 190, 217, 241, 214, 138, 109, 42, 225, 111, 245, 78, 55, 209, 76, 241, 73, 151, 186, 39, 108, 124, 82, 197, 173, 85, 146, 210, 77, 128, 56, 32, 97, 118, 180, 57, 33, 135, 239, 130, 144, 164, 246, 130, 180, 237, 242, 8, 114, 223, 95, 72, 4, 181, 69, 238, 9, 31, 216, 113, 152, 120, 167, 89, 165, 56, 88, 79, 3, 39, 189, 246, 63, 208, 138, 117, 244, 141, 153, 111, 204, 32, 247, 132, 12, 215, 109, 125, 19, 144, 123, 194, 137, 214, 111, 118, 148, 43, 169, 66, 238, 9, 25, 184, 178, 108, 14, 174, 154, 41, 143, 74, 60, 161, 4, 114, 79, 216, 224, 158, 80, 193, 90, 234, 60, 161, 68, 115, 5, 156, 123, 66, 5, 247, 132, 141, 102, 104, 42, 225, 111, 218, 170, 150, 77, 225, 120, 58, 15, 136, 43, 226, 164, 138, 19, 106, 165, 18, 69, 48, 131, 235, 218, 198, 187, 167, 19, 46, 124, 211, 73, 4, 136, 219, 10, 228, 158, 147, 134, 219, 138, 246, 38, 34, 220, 185, 31, 147, 166, 13, 119, 238, 132, 18, 30, 105, 180, 58, 199, 157, 112, 209, 236, 160, 13, 221, 9, 31, 141, 59, 161, 130, 183, 254, 202, 180, 191, 157, 84, 103, 66, 191, 74, 59, 33, 4, 51, 52, 119, 107, 83, 231, 221, 115, 37, 9, 231, 18, 125, 74, 109, 136, 37, 61, 247, 143, 41, 156, 75, 244, 21, 109, 15, 109, 43, 203, 32, 41, 153, 243, 112, 60, 29, 101, 217, 76, 171, 163, 5, 141, 247, 153, 57, 56, 42, 41, 210, 79, 237, 132, 143, 8, 18, 82, 39, 132, 68, 144, 128, 82, 39, 100, 72, 157, 80, 65, 42, 150, 157, 144, 161, 65, 118, 66, 200, 227, 18, 42, 168, 184, 132, 10, 158, 75, 8, 105, 151, 80, 65, 196, 181, 132, 24, 151, 80, 66, 215, 50, 200, 37, 92, 248, 90, 141, 245, 150, 150, 239, 139, 208, 193, 243, 155, 144, 126, 190, 55, 64, 68, 64, 252, 72, 2, 98, 95, 244, 72, 120, 227, 180, 168, 201, 197, 160, 167, 243, 83, 161, 7, 162, 56, 161, 167, 191, 61, 174, 80, 182, 143, 84, 75, 228, 186, 253, 237, 113, 194, 6, 232, 219, 83, 201, 14, 44, 115, 78, 156, 144, 193, 155, 19, 42, 168, 69, 141, 155, 19, 58, 168, 236, 198, 61, 7, 84, 170, 147, 234, 158, 71, 30, 209, 230, 135, 246, 115, 16, 228, 17, 164, 141, 84, 155, 19, 62, 164, 125, 231, 214, 228, 42, 8, 14, 193, 117, 13, 160, 77, 254, 174, 9, 206, 64, 39, 173, 222, 180, 237, 116, 252, 129, 206, 197, 223, 231, 187, 3, 138, 238, 100, 81, 10, 106, 32, 109, 227, 165, 136, 208, 218, 212, 49, 210, 181, 12, 3, 11, 156, 75, 180, 192, 178, 203, 121, 31, 78, 122, 101, 82, 219, 65, 173, 102, 10, 226, 181, 212, 65, 206, 53, 120, 200, 9, 171, 230, 132, 11, 215, 181, 204, 4, 226, 71, 95, 97, 208, 171, 80, 132, 196, 243, 27, 143, 123, 80, 133, 30, 199, 9, 33, 30, 199, 9, 21, 124, 161, 107, 25, 199, 55, 245, 198, 233, 43, 175, 161, 5, 16, 14, 13, 157, 17, 132, 14, 231, 151, 130, 62, 237, 167, 93, 172, 245, 28, 84, 169, 168, 131, 190, 117, 194, 30, 65, 252, 104, 105, 170, 232, 2, 247, 132, 190, 37, 244, 215, 50, 236, 203, 24, 63, 56, 192, 183, 117, 252, 219, 46, 119, 160, 117, 97, 218, 128, 54, 39, 111, 64, 82, 87, 0, 2, 236, 74, 11, 48, 126, 6, 58, 215, 107, 90, 162, 7, 208, 202, 198, 251, 77, 39, 141, 19, 50, 200, 102, 120, 94, 174, 2, 253, 184, 241, 254, 2, 186, 150, 124, 109, 89, 106, 127, 131, 36, 229, 187, 51, 90, 159, 92, 218, 188, 30, 211, 120, 63, 187, 89, 138, 111, 202, 22, 172, 199, 68, 180, 123, 30, 130, 158, 231, 183, 95, 82, 23, 128, 175, 66, 19, 184, 112, 62, 29, 242, 6, 102, 218, 162, 118, 240, 85, 188, 173, 14, 41, 109, 155, 255, 192, 63, 189, 126, 103, 60, 204, 58, 133, 46, 199, 109, 191, 229, 59, 168, 103, 66, 223, 242, 35, 152, 224, 147, 23, 131, 152, 182, 14, 37, 92, 233, 167, 218, 9, 253, 84, 187, 216, 91, 62, 132, 134, 125, 142, 214, 111, 219, 14, 233, 90, 242, 23, 52, 188, 34, 28, 45, 238, 6, 185, 216, 91, 167, 67, 237, 49, 3, 165, 16, 255, 88, 217, 190, 86, 236, 210, 55, 65, 253, 65, 69, 155, 4, 81, 60, 173, 111, 194, 114, 108, 29, 53, 46, 66, 253, 165, 83, 204, 36, 24, 180, 46, 209, 211, 252, 190, 78, 3, 142, 231, 98, 26, 180, 159, 55, 165, 104, 88, 229, 167, 122, 112, 192, 133, 175, 92, 120, 53, 8, 53, 109, 253, 49, 141, 19, 62, 200, 212, 44, 211, 222, 147, 161, 102, 153, 150, 78, 66, 16, 100, 96, 26, 39, 84, 152, 112, 109, 187, 40, 218, 144, 122, 83, 213, 56, 97, 132, 166, 150, 170, 249, 209, 146, 233, 68, 63, 155, 62, 220, 37, 115, 126, 41, 12, 26, 92, 50, 228, 64, 6, 138, 238, 4, 241, 183, 213, 127, 14, 156, 173, 66, 250, 47, 226, 174, 189, 64, 227, 135, 174, 101, 221, 181, 33, 77, 62, 114, 215, 118, 215, 134, 248, 164, 145, 229, 57, 82, 237, 65, 48, 97, 150, 131, 154, 29, 124, 83, 135, 180, 101, 42, 81, 171, 75, 168, 175, 126, 14, 77, 149, 242, 142, 219, 61, 252, 182, 58, 223, 116, 18, 225, 164, 84, 213, 118, 65, 21, 93, 206, 225, 109, 43, 203, 144, 123, 58, 136, 198, 235, 173, 147, 7, 95, 253, 90, 39, 76, 221, 97, 45, 77, 72, 233, 2, 196, 96, 45, 77, 16, 93, 77, 75, 132, 13, 173, 173, 170, 105, 35, 73, 39, 123, 208, 202, 232, 85, 141, 202, 50, 218, 14, 57, 159, 78, 162, 225, 133, 26, 55, 180, 93, 80, 227, 236, 141, 48, 248, 182, 171, 105, 197, 190, 227, 125, 25, 227, 132, 139, 138, 54, 218, 13, 14, 241, 77, 25, 227, 132, 144, 197, 252, 72, 219, 174, 8, 127, 223, 138, 34, 213, 28, 92, 223, 202, 9, 82, 154, 140, 113, 66, 6, 17, 32, 212, 131, 75, 38, 225, 109, 23, 198, 9, 29, 30, 102, 27, 123, 139, 19, 54, 250, 49, 186, 206, 59, 218, 186, 100, 12, 213, 249, 26, 197, 32, 137, 176, 104, 126, 228, 210, 52, 106, 246, 78, 247, 120, 223, 133, 148, 101, 123, 80, 150, 13, 194, 48, 171, 28, 223, 58, 105, 156, 80, 243, 226, 132, 13, 191, 190, 19, 8, 151, 12, 53, 94, 7, 143, 64, 112, 104, 147, 173, 19, 78, 104, 2, 19, 244, 73, 215, 74, 244, 73, 215, 226, 9, 253, 117, 40, 72, 221, 229, 90, 156, 144, 162, 45, 39, 116, 52, 156, 80, 193, 195, 108, 163, 65, 149, 97, 56, 33, 131, 219, 95, 204, 143, 42, 238, 96, 237, 63, 84, 225, 69, 181, 116, 85, 156, 208, 1, 109, 210, 49, 188, 181, 81, 120, 29, 127, 141, 207, 134, 230, 110, 157, 64, 170, 56, 97, 132, 175, 205, 70, 141, 57, 113, 66, 200, 210, 253, 148, 36, 78, 216, 120, 146, 235, 74, 78, 200, 248, 77, 10, 226, 132, 11, 245, 135, 56, 33, 67, 246, 87, 20, 57, 131, 56, 225, 131, 113, 51, 136, 19, 58, 180, 50, 170, 16, 39, 100, 32, 78, 216, 120, 20, 47, 136, 19, 46, 56, 225, 163, 178, 204, 179, 131, 216, 58, 142, 20, 75, 248, 160, 88, 194, 134, 214, 247, 183, 18, 58, 236, 91, 9, 21, 42, 190, 18, 42, 180, 166, 236, 111, 228, 43, 225, 67, 169, 132, 217, 65, 190, 18, 58, 144, 175, 132, 144, 167, 179, 45, 107, 102, 114, 49, 14, 255, 166, 19, 125, 80, 120, 169, 199, 43, 225, 195, 90, 6, 189, 171, 179, 58, 175, 132, 11, 103, 240, 207, 247, 135, 122, 99, 231, 14, 78, 85, 65, 180, 241, 74, 184, 144, 120, 37, 84, 216, 183, 43, 225, 194, 3, 121, 28, 39, 228, 160, 58, 121, 113, 187, 18, 54, 24, 102, 87, 194, 5, 111, 188, 238, 234, 232, 155, 62, 56, 175, 150, 210, 164, 191, 134, 126, 166, 246, 250, 95, 62, 36, 172, 132, 14, 230, 219, 182, 163, 232, 78, 180, 29, 74, 40, 209, 250, 204, 12, 154, 112, 215, 110, 16, 178, 75, 39, 191, 11, 163, 41, 36, 41, 255, 241, 234, 13, 72, 179, 220, 57, 24, 109, 244, 219, 52, 172, 126, 45, 117, 37, 116, 240, 8, 67, 196, 90, 143, 65, 174, 15, 141, 194, 10, 81, 228, 167, 210, 230, 132, 90, 173, 132, 13, 87, 150, 205, 225, 17, 8, 239, 187, 124, 151, 196, 103, 130, 208, 216, 35, 104, 129, 7, 95, 251, 239, 193, 64, 107, 196, 251, 46, 135, 47, 67, 53, 253, 138, 34, 196, 229, 74, 168, 32, 193, 167, 212, 126, 240, 198, 251, 217, 14, 68, 208, 104, 254, 85, 42, 73, 200, 104, 236, 145, 132, 11, 173, 21, 93, 200, 35, 9, 33, 252, 173, 202, 110, 253, 166, 137, 65, 154, 70, 81, 51, 131, 26, 123, 100, 193, 211, 185, 94, 234, 77, 184, 128, 122, 46, 8, 31, 223, 54, 161, 130, 171, 77, 168, 160, 54, 161, 195, 59, 39, 8, 109, 66, 136, 133, 35, 104, 19, 54, 76, 154, 162, 77, 200, 160, 24, 36, 237, 57, 9, 25, 240, 8, 210, 37, 73, 235, 144, 4, 123, 203, 128, 52, 120, 223, 133, 92, 233, 231, 122, 76, 27, 240, 197, 252, 19, 85, 14, 82, 45, 44, 23, 114, 74, 34, 141, 240, 8, 74, 198, 237, 36, 108, 116, 163, 176, 147, 144, 97, 87, 66, 157, 106, 37, 236, 36, 132, 184, 166, 101, 39, 97, 68, 43, 102, 78, 66, 134, 10, 52, 124, 69, 213, 90, 39, 97, 68, 219, 124, 238, 137, 58, 9, 29, 188, 173, 234, 36, 92, 184, 183, 96, 143, 65, 161, 205, 239, 223, 116, 87, 138, 240, 102, 214, 220, 173, 72, 226, 11, 225, 186, 146, 164, 78, 66, 9, 39, 125, 82, 144, 75, 117, 18, 62, 36, 105, 37, 13, 198, 92, 49, 225, 75, 151, 92, 249, 169, 184, 181, 68, 30, 121, 164, 138, 219, 68, 243, 226, 212, 240, 77, 25, 51, 6, 30, 249, 247, 253, 78, 39, 33, 227, 213, 59, 157, 132, 11, 214, 233, 36, 92, 80, 187, 114, 210, 22, 105, 106, 218, 202, 104, 51, 0, 241, 164, 108, 116, 61, 136, 96, 177, 119, 144, 3, 215, 253, 253, 166, 168, 245, 83, 118, 58, 9, 37, 58, 157, 132, 14, 136, 138, 54, 218, 107, 25, 212, 240, 114, 240, 160, 158, 9, 105, 211, 82, 75, 39, 33, 67, 75, 39, 161, 194, 174, 212, 234, 144, 172, 116, 18, 46, 176, 108, 73, 58, 9, 23, 148, 244, 214, 28, 212, 190, 231, 186, 206, 29, 74, 191, 217, 157, 170, 66, 20, 94, 43, 29, 111, 160, 146, 93, 141, 198, 30, 137, 168, 122, 43, 85, 20, 162, 236, 169, 104, 202, 152, 161, 108, 95, 63, 85, 67, 91, 22, 190, 233, 36, 124, 72, 154, 153, 132, 11, 254, 141, 153, 132, 12, 223, 118, 161, 198, 36, 100, 76, 154, 50, 9, 23, 22, 38, 161, 2, 143, 64, 88, 120, 45, 136, 73, 232, 104, 205, 174, 68, 173, 223, 52, 33, 164, 241, 126, 126, 211, 109, 70, 107, 105, 66, 8, 253, 236, 69, 151, 38, 108, 248, 4, 36, 42, 225, 194, 87, 82, 66, 5, 100, 104, 154, 151, 132, 11, 188, 36, 108, 52, 47, 9, 21, 40, 153, 80, 129, 175, 119, 58, 159, 9, 27, 43, 159, 103, 66, 6, 198, 10, 57, 158, 9, 25, 206, 231, 122, 83, 71, 234, 153, 208, 0, 234, 153, 80, 161, 237, 226, 153, 144, 161, 117, 187, 51, 225, 130, 87, 103, 194, 133, 149, 109, 85, 103, 66, 134, 234, 165, 19, 119, 240, 41, 181, 81, 3, 255, 166, 173, 241, 210, 153, 48, 226, 183, 162, 72, 251, 61, 47, 228, 109, 243, 93, 38, 132, 172, 108, 118, 153, 112, 145, 191, 46, 19, 66, 154, 73, 203, 132, 11, 191, 12, 114, 120, 78, 24, 180, 98, 87, 166, 109, 252, 187, 32, 139, 114, 172, 75, 164, 254, 150, 177, 66, 234, 79, 210, 118, 177, 199, 81, 203, 132, 143, 39, 151, 227, 127, 137, 90, 38, 132, 104, 197, 50, 212, 51, 53, 212, 51, 161, 165, 44, 19, 58, 84, 115, 210, 70, 250, 235, 15, 106, 81, 171, 254, 186, 182, 174, 149, 9, 25, 240, 193, 1, 162, 218, 46, 104, 173, 76, 216, 112, 139, 82, 154, 137, 67, 154, 230, 26, 117, 145, 6, 102, 27, 4, 241, 86, 183, 43, 19, 50, 180, 151, 93, 153, 112, 129, 14, 250, 169, 86, 38, 92, 52, 10, 55, 116, 178, 159, 75, 81, 68, 184, 39, 97, 94, 72, 215, 146, 16, 46, 41, 238, 206, 167, 243, 85, 218, 15, 124, 149, 54, 175, 227, 233, 32, 103, 208, 130, 214, 38, 91, 178, 134, 166, 223, 56, 73, 168, 231, 130, 142, 9, 36, 12, 128, 66, 16, 33, 161, 130, 250, 82, 60, 194, 82, 251, 33, 142, 47, 35, 233, 34, 215, 127, 30, 92, 255, 89, 112, 65, 255, 89, 208, 161, 21, 235, 13, 231, 155, 166, 11, 233, 63, 11, 62, 244, 159, 5, 21, 180, 237, 122, 22, 100, 240, 8, 114, 75, 89, 112, 241, 240, 226, 111, 65, 6, 254, 237, 126, 11, 46, 52, 101, 191, 66, 220, 80, 69, 49, 190, 224, 249, 215, 118, 46, 61, 114, 60, 223, 27, 94, 19, 45, 159, 124, 218, 91, 144, 193, 73, 159, 148, 199, 82, 237, 45, 216, 208, 222, 130, 10, 146, 173, 183, 224, 66, 54, 237, 68, 107, 189, 5, 29, 107, 189, 5, 21, 156, 225, 170, 56, 33, 127, 223, 246, 215, 38, 107, 172, 253, 71, 209, 90, 255, 211, 225, 3, 165, 153, 183, 224, 194, 218, 127, 190, 144, 164, 116, 146, 100, 30, 97, 52, 74, 58, 71, 74, 29, 154, 43, 79, 219, 201, 3, 255, 2, 103, 235, 56, 68, 189, 117, 162, 253, 244, 77, 128, 168, 91, 167, 234, 36, 214, 90, 230, 49, 129, 59, 188, 65, 217, 62, 250, 150, 43, 223, 4, 93, 64, 29, 148, 73, 111, 15, 77, 36, 94, 43, 81, 43, 195, 188, 5, 33, 15, 179, 186, 150, 183, 32, 67, 127, 23, 74, 254, 5, 27, 201, 191, 160, 66, 99, 95, 80, 65, 146, 212, 73, 19, 144, 192, 23, 108, 76, 64, 2, 95, 80, 161, 213, 57, 125, 71, 190, 160, 163, 249, 147, 70, 218, 162, 87, 231, 182, 57, 242, 5, 39, 92, 221, 38, 109, 228, 11, 58, 124, 193, 134, 36, 229, 251, 174, 132, 220, 181, 221, 147, 144, 3, 73, 202, 247, 86, 167, 64, 190, 105, 147, 173, 254, 208, 251, 46, 111, 117, 138, 239, 122, 44, 187, 42, 30, 102, 23, 180, 102, 253, 33, 189, 27, 123, 4, 105, 147, 146, 148, 174, 193, 202, 135, 217, 137, 149, 223, 148, 105, 167, 182, 232, 97, 214, 25, 212, 224, 170, 208, 195, 108, 171, 227, 54, 161, 135, 89, 71, 241, 48, 235, 144, 164, 116, 237, 73, 18, 47, 228, 75, 126, 196, 62, 148, 218, 227, 186, 235, 119, 57, 111, 88, 196, 143, 88, 106, 191, 235, 90, 82, 165, 118, 3, 202, 246, 219, 65, 12, 36, 41, 29, 68, 21, 55, 212, 44, 65, 173, 132, 183, 170, 84, 167, 93, 107, 211, 150, 153, 152, 52, 125, 124, 210, 87, 229, 224, 173, 78, 225, 109, 202, 195, 195, 236, 106, 222, 59, 133, 75, 214, 234, 173, 255, 174, 204, 182, 100, 72, 215, 243, 222, 128, 162, 239, 13, 142, 167, 131, 182, 93, 181, 31, 81, 120, 185, 75, 134, 220, 211, 221, 91, 139, 187, 105, 235, 200, 61, 189, 179, 251, 166, 11, 48, 211, 22, 169, 226, 22, 209, 218, 94, 2, 90, 43, 118, 37, 107, 110, 104, 1, 237, 164, 232, 82, 106, 153, 174, 101, 22, 52, 111, 81, 229, 208, 224, 51, 53, 78, 15, 238, 9, 181, 212, 149, 90, 215, 58, 111, 217, 255, 64, 127, 155, 194, 96, 226, 18, 75, 91, 123, 12, 82, 44, 209, 46, 78, 137, 123, 193, 9, 103, 122, 65, 5, 213, 76, 105, 184, 167, 163, 111, 203, 78, 146, 218, 11, 62, 56, 175, 82, 123, 65, 135, 247, 76, 189, 32, 131, 68, 57, 114, 82, 203, 94, 208, 177, 40, 5, 169, 246, 183, 205, 94, 176, 193, 25, 26, 175, 183, 236, 5, 31, 116, 41, 197, 12, 53, 230, 22, 124, 52, 188, 239, 114, 110, 65, 6, 111, 156, 182, 194, 11, 50, 154, 86, 120, 193, 5, 137, 170, 240, 130, 144, 79, 234, 21, 94, 176, 161, 149, 226, 17, 135, 82, 120, 65, 136, 182, 126, 188, 32, 195, 167, 54, 4, 105, 188, 244, 227, 5, 23, 106, 223, 163, 240, 71, 37, 232, 151, 49, 242, 181, 255, 30, 52, 222, 207, 102, 39, 217, 208, 163, 18, 119, 93, 15, 60, 157, 170, 61, 110, 14, 143, 232, 250, 231, 5, 25, 142, 55, 47, 184, 224, 142, 23, 84, 112, 210, 39, 197, 81, 225, 229, 125, 215, 202, 167, 80, 4, 12, 244, 77, 26, 116, 53, 13, 127, 171, 218, 229, 233, 14, 15, 179, 207, 13, 194, 27, 167, 125, 208, 79, 69, 241, 172, 198, 200, 93, 162, 85, 146, 214, 53, 47, 78, 104, 129, 203, 126, 111, 84, 120, 121, 230, 5, 23, 76, 225, 97, 22, 23, 186, 120, 65, 5, 73, 226, 5, 177, 88, 5, 61, 60, 138, 23, 124, 60, 138, 23, 84, 72, 170, 120, 193, 133, 102, 196, 11, 62, 218, 130, 1, 240, 8, 114, 108, 65, 6, 199, 211, 233, 183, 22, 100, 44, 199, 30, 34, 129, 98, 143, 133, 215, 130, 248, 145, 231, 164, 101, 66, 142, 173, 190, 181, 32, 131, 167, 95, 27, 169, 222, 69, 223, 90, 208, 225, 169, 124, 34, 135, 210, 69, 132, 6, 8, 58, 172, 253, 215, 248, 69, 236, 224, 216, 163, 133, 215, 99, 225, 181, 96, 3, 195, 172, 66, 146, 182, 107, 65, 199, 178, 253, 141, 19, 82, 234, 174, 107, 193, 7, 143, 168, 36, 215, 130, 139, 247, 93, 146, 148, 107, 193, 198, 90, 48, 0, 191, 11, 54, 210, 54, 102, 218, 24, 196, 178, 11, 105, 187, 99, 212, 150, 211, 239, 130, 15, 93, 74, 45, 229, 209, 239, 130, 143, 9, 38, 112, 187, 224, 2, 5, 36, 184, 93, 112, 33, 2, 9, 64, 224, 118, 65, 6, 151, 77, 89, 184, 106, 191, 99, 47, 210, 240, 166, 173, 82, 236, 188, 50, 20, 89, 24, 194, 97, 173, 68, 170, 237, 130, 140, 221, 228, 217, 118, 65, 198, 68, 226, 208, 208, 44, 211, 56, 161, 230, 238, 101, 20, 17, 234, 153, 208, 227, 233, 108, 187, 32, 67, 196, 163, 245, 215, 50, 200, 155, 92, 12, 154, 192, 4, 181, 93, 112, 66, 146, 210, 161, 182, 11, 50, 60, 2, 161, 217, 5, 33, 202, 246, 63, 27, 53, 187, 224, 162, 217, 5, 29, 141, 61, 130, 154, 93, 144, 209, 218, 232, 183, 69, 205, 46, 248, 240, 8, 90, 181, 11, 46, 244, 233, 155, 0, 113, 62, 221, 68, 115, 236, 248, 85, 47, 17, 115, 13, 194, 19, 43, 212, 250, 175, 11, 70, 184, 173, 120, 215, 5, 23, 75, 221, 117, 65, 6, 109, 90, 54, 146, 32, 129, 211, 5, 31, 223, 116, 65, 5, 73, 39, 115, 82, 211, 5, 27, 173, 191, 116, 65, 134, 86, 167, 186, 32, 131, 46, 216, 88, 118, 33, 180, 28, 66, 234, 185, 32, 4, 193, 4, 69, 0, 69, 192, 17, 66, 78, 66, 42, 73, 186, 160, 2, 34, 72, 112, 146, 46, 200, 208, 57, 65, 79, 69, 46, 164, 212, 130, 16, 143, 48, 60, 162, 138, 27, 106, 120, 247, 116, 90, 144, 193, 35, 170, 56, 45, 11, 50, 36, 112, 69, 206, 231, 130, 140, 198, 251, 235, 12, 209, 180, 85, 42, 221, 67, 219, 166, 147, 57, 252, 227, 39, 151, 218, 110, 144, 123, 114, 111, 94, 28, 90, 179, 43, 145, 123, 114, 79, 238, 185, 224, 194, 61, 211, 248, 151, 40, 173, 231, 130, 18, 11, 175, 6, 57, 158, 11, 54, 86, 191, 8, 38, 104, 168, 141, 96, 130, 8, 38, 136, 96, 2, 138, 134, 45, 138, 96, 130, 8, 38, 136, 48, 129, 58, 132, 70, 211, 8, 18, 42, 12, 18, 207, 215, 230, 119, 135, 55, 184, 254, 227, 78, 233, 92, 89, 54, 7, 247, 73, 83, 134, 68, 179, 76, 155, 80, 103, 199, 24, 242, 224, 174, 137, 219, 161, 93, 9, 169, 51, 38, 77, 103, 35, 130, 9, 144, 122, 46, 184, 72, 219, 223, 52, 61, 157, 11, 46, 236, 74, 40, 155, 174, 92, 208, 161, 169, 229, 202, 5, 29, 212, 173, 107, 114, 193, 133, 54, 217, 52, 185, 160, 195, 251, 46, 228, 170, 153, 162, 114, 193, 134, 182, 141, 202, 5, 25, 60, 226, 248, 29, 142, 231, 114, 73, 202, 5, 27, 30, 169, 52, 98, 24, 8, 47, 56, 151, 232, 153, 108, 165, 255, 218, 120, 59, 25, 132, 255, 245, 103, 201, 5, 194, 4, 171, 246, 26, 188, 162, 205, 21, 49, 208, 198, 139, 36, 229, 130, 12, 39, 229, 242, 44, 185, 96, 163, 241, 122, 150, 92, 112, 241, 48, 203, 144, 36, 117, 210, 90, 153, 220, 246, 4, 78, 250, 164, 60, 36, 43, 41, 234, 158, 71, 227, 149, 170, 229, 59, 68, 131, 195, 155, 188, 9, 3, 17, 32, 64, 218, 228, 59, 133, 115, 57, 65, 225, 229, 145, 133, 153, 52, 237, 126, 175, 174, 189, 150, 121, 22, 59, 120, 238, 202, 244, 214, 201, 182, 167, 150, 39, 136, 31, 73, 146, 58, 9, 41, 237, 92, 73, 85, 123, 15, 180, 190, 111, 163, 29, 65, 115, 107, 182, 162, 223, 56, 33, 237, 125, 19, 18, 144, 203, 105, 55, 64, 89, 54, 109, 213, 158, 2, 18, 224, 174, 162, 141, 118, 3, 180, 249, 81, 63, 167, 169, 31, 51, 75, 29, 85, 60, 242, 233, 24, 39, 3, 44, 128, 1, 147, 166, 72, 21, 183, 150, 186, 152, 151, 4, 52, 109, 215, 178, 14, 1, 2, 9, 174, 84, 34, 157, 236, 191, 164, 222, 56, 185, 237, 119, 68, 225, 197, 40, 96, 1, 16, 36, 60, 64, 130, 4, 170, 56, 109, 123, 12, 171, 123, 222, 1, 16, 168, 104, 163, 141, 30, 73, 218, 247, 6, 44, 160, 213, 61, 87, 76, 89, 54, 196, 48, 106, 102, 30, 149, 32, 253, 76, 187, 220, 83, 77, 219, 126, 170, 28, 160, 244, 159, 99, 12, 129, 171, 123, 174, 212, 180, 117, 128, 82, 199, 201, 145, 36, 241, 90, 34, 144, 0, 130, 8, 68, 112, 69, 8, 64, 0, 1, 20, 128, 90, 157, 203, 166, 32, 166, 239, 143, 35, 143, 36, 197, 239, 17, 104, 125, 215, 247, 100, 140, 83, 211, 118, 109, 246, 63, 192, 181, 19, 80, 241, 136, 106, 187, 32, 2, 80, 120, 49, 223, 8, 112, 188, 21, 117, 64, 1, 248, 1, 254, 244, 250, 189, 113, 66, 238, 185, 146, 163, 74, 126, 123, 12, 224, 146, 33, 10, 230, 118, 160, 189, 20, 109, 93, 236, 141, 83, 46, 109, 137, 102, 226, 186, 235, 219, 54, 109, 18, 43, 203, 134, 82, 177, 108, 137, 214, 172, 243, 115, 187, 231, 91, 29, 183, 139, 48, 223, 118, 20, 202, 178, 33, 73, 123, 78, 66, 206, 167, 107, 125, 101, 217, 233, 6, 160, 44, 219, 186, 178, 108, 72, 87, 211, 146, 13, 224, 249, 109, 203, 180, 85, 170, 58, 95, 211, 216, 183, 221, 46, 166, 241, 126, 182, 101, 187, 84, 165, 42, 6, 28, 192, 219, 254, 218, 183, 160, 135, 217, 182, 204, 153, 2, 28, 79, 197, 107, 98, 169, 98, 134, 38, 168, 226, 17, 181, 249, 169, 144, 78, 152, 105, 31, 192, 91, 151, 89, 197, 12, 85, 60, 210, 76, 220, 117, 45, 185, 157, 19, 244, 77, 89, 227, 222, 73, 83, 6, 76, 154, 62, 171, 111, 118, 108, 162, 162, 22, 66, 181, 29, 210, 182, 171, 45, 242, 206, 101, 177, 119, 154, 124, 228, 192, 35, 13, 172, 156, 68, 52, 222, 231, 92, 235, 24, 39, 180, 46, 91, 23, 166, 10, 104, 230, 160, 156, 168, 137, 42, 6, 154, 183, 232, 3, 111, 12, 210, 79, 181, 192, 221, 83, 211, 86, 45, 102, 208, 183, 100, 175, 41, 122, 111, 12, 180, 145, 178, 108, 79, 46, 231, 171, 10, 224, 74, 37, 119, 74, 214, 170, 146, 33, 239, 137, 248, 209, 202, 214, 223, 69, 241, 174, 17, 111, 112, 199, 237, 144, 54, 173, 12, 85, 60, 130, 244, 77, 6, 176, 28, 63, 154, 236, 155, 160, 200, 151, 129, 231, 183, 179, 113, 183, 139, 113, 192, 61, 87, 106, 188, 170, 120, 185, 74, 3, 242, 173, 156, 168, 75, 134, 180, 45, 163, 170, 129, 126, 54, 186, 84, 161, 181, 50, 45, 80, 241, 8, 250, 148, 218, 15, 120, 243, 103, 115, 82, 133, 30, 102, 85, 219, 5, 9, 224, 225, 219, 46, 87, 136, 182, 67, 21, 67, 236, 74, 168, 177, 116, 201, 5, 23, 150, 46, 207, 178, 228, 130, 140, 206, 37, 23, 92, 208, 223, 133, 94, 4, 19, 44, 216, 136, 96, 130, 5, 21, 52, 44, 207, 129, 252, 59, 232, 121, 39, 32, 212, 115, 65, 110, 2, 75, 244, 188, 72, 251, 85, 184, 227, 137, 170, 5, 141, 25, 227, 4, 97, 157, 72, 251, 85, 36, 74, 60, 173, 111, 2, 170, 36, 58, 252, 178, 93, 72, 63, 187, 178, 12, 122, 42, 220, 142, 173, 111, 14, 73, 98, 38, 209, 218, 52, 188, 32, 46, 97, 118, 30, 16, 13, 47, 212, 188, 69, 213, 67, 103, 68, 87, 194, 220, 96, 194, 121, 43, 137, 11, 173, 90, 209, 74, 34, 195, 121, 43, 137, 34, 112, 74, 162, 35, 2, 167, 52, 239, 236, 22, 172, 9, 254, 48, 119, 109, 87, 81, 10, 109, 78, 72, 162, 28, 61, 217, 21, 72, 107, 214, 37, 137, 25, 68, 146, 152, 73, 148, 35, 167, 36, 78, 104, 243, 59, 98, 217, 233, 144, 83, 18, 31, 36, 202, 145, 166, 157, 168, 66, 207, 18, 204, 180, 69, 78, 73, 100, 52, 127, 210, 214, 56, 33, 167, 36, 50, 240, 157, 144, 83, 18, 23, 143, 79, 169, 141, 156, 146, 24, 81, 217, 181, 148, 68, 6, 109, 236, 184, 137, 247, 92, 200, 117, 178, 216, 181, 209, 179, 26, 23, 96, 178, 159, 173, 142, 237, 2, 169, 61, 94, 84, 33, 101, 236, 74, 73, 116, 224, 84, 151, 107, 179, 227, 146, 169, 63, 188, 247, 50, 191, 73, 73, 124, 36, 159, 36, 42, 84, 242, 115, 146, 200, 224, 106, 201, 181, 12, 126, 219, 162, 156, 36, 50, 114, 146, 168, 224, 157, 102, 151, 211, 172, 131, 222, 181, 105, 222, 2, 173, 88, 127, 215, 230, 127, 137, 140, 137, 214, 202, 50, 218, 142, 255, 37, 66, 248, 95, 226, 163, 85, 210, 118, 57, 154, 70, 145, 82, 11, 250, 169, 237, 17, 8, 250, 186, 28, 227, 135, 198, 173, 101, 226, 127, 137, 142, 134, 151, 54, 45, 157, 228, 160, 218, 46, 136, 255, 37, 54, 92, 55, 237, 98, 135, 84, 51, 197, 61, 93, 131, 93, 9, 61, 188, 156, 42, 20, 1, 231, 95, 34, 132, 243, 47, 241, 177, 9, 253, 170, 151, 200, 240, 198, 235, 188, 234, 37, 66, 160, 254, 79, 124, 56, 213, 229, 144, 5, 165, 101, 252, 137, 14, 78, 209, 157, 160, 166, 14, 105, 74, 231, 136, 241, 39, 82, 232, 239, 66, 11, 127, 98, 99, 225, 79, 84, 72, 254, 68, 133, 117, 217, 212, 113, 196, 83, 34, 168, 229, 186, 231, 250, 196, 6, 110, 143, 44, 244, 115, 149, 50, 182, 175, 222, 128, 101, 167, 123, 215, 158, 240, 71, 37, 16, 214, 254, 131, 240, 251, 168, 68, 37, 187, 86, 34, 181, 199, 83, 251, 121, 251, 101, 187, 24, 118, 82, 42, 3, 39, 165, 62, 217, 157, 16, 214, 254, 163, 208, 167, 111, 2, 132, 199, 47, 219, 181, 116, 173, 236, 116, 16, 214, 126, 123, 232, 87, 223, 202, 201, 132, 190, 149, 19, 93, 255, 220, 206, 59, 40, 99, 95, 255, 234, 144, 166, 161, 167, 95, 251, 233, 215, 126, 248, 42, 109, 125, 250, 38, 160, 137, 138, 254, 99, 188, 28, 90, 127, 217, 174, 134, 21, 65, 18, 230, 182, 201, 88, 33, 9, 115, 132, 55, 72, 152, 29, 244, 91, 121, 104, 253, 95, 166, 223, 116, 34, 225, 164, 212, 198, 14, 117, 82, 38, 36, 241, 85, 16, 19, 79, 168, 145, 218, 227, 137, 11, 148, 247, 68, 5, 10, 47, 6, 185, 186, 199, 65, 189, 117, 2, 97, 205, 204, 123, 98, 131, 122, 235, 36, 251, 209, 163, 146, 54, 225, 235, 41, 134, 104, 109, 101, 218, 162, 252, 84, 206, 28, 190, 120, 97, 143, 163, 214, 172, 123, 162, 195, 2, 127, 117, 127, 208, 120, 191, 27, 239, 59, 118, 80, 197, 13, 77, 124, 171, 59, 131, 40, 116, 169, 150, 206, 131, 51, 72, 215, 194, 212, 61, 241, 209, 180, 109, 85, 77, 91, 219, 161, 215, 230, 60, 113, 34, 2, 19, 136, 32, 65, 98, 26, 121, 4, 2, 4, 205, 249, 221, 211, 137, 121, 34, 196, 19, 27, 118, 165, 221, 228, 203, 90, 153, 60, 209, 209, 233, 137, 10, 46, 61, 81, 33, 91, 122, 226, 130, 174, 37, 31, 121, 34, 195, 19, 27, 79, 118, 39, 242, 68, 134, 242, 207, 119, 212, 224, 105, 93, 107, 191, 19, 27, 223, 137, 16, 95, 57, 217, 238, 68, 198, 195, 108, 119, 34, 195, 164, 233, 78, 92, 248, 148, 142, 113, 66, 79, 191, 35, 102, 209, 195, 48, 235, 146, 77, 56, 41, 181, 185, 233, 187, 100, 159, 171, 65, 69, 85, 91, 213, 30, 64, 80, 247, 60, 63, 183, 131, 192, 96, 194, 182, 151, 154, 126, 83, 143, 120, 7, 77, 253, 248, 155, 221, 161, 105, 151, 47, 134, 121, 78, 131, 235, 182, 122, 122, 74, 91, 155, 150, 221, 169, 15, 218, 180, 236, 135, 78, 152, 185, 100, 141, 247, 92, 14, 253, 108, 253, 198, 251, 73, 189, 245, 37, 86, 238, 111, 91, 134, 43, 170, 218, 54, 253, 134, 207, 53, 81, 81, 213, 22, 65, 168, 182, 140, 163, 245, 215, 50, 14, 79, 235, 100, 229, 155, 160, 143, 86, 134, 89, 5, 145, 164, 92, 208, 147, 221, 137, 142, 198, 219, 94, 195, 37, 197, 245, 243, 185, 81, 119, 162, 131, 97, 86, 161, 238, 68, 70, 67, 207, 111, 39, 46, 158, 214, 55, 1, 229, 118, 162, 67, 169, 106, 250, 21, 69, 254, 77, 217, 107, 58, 81, 226, 201, 238, 244, 166, 19, 27, 222, 116, 162, 130, 122, 167, 107, 58, 145, 17, 161, 113, 211, 137, 12, 78, 211, 137, 10, 77, 39, 42, 80, 170, 147, 117, 79, 135, 180, 19, 27, 240, 181, 255, 144, 67, 51, 212, 61, 143, 116, 178, 78, 234, 68, 72, 46, 220, 46, 145, 193, 223, 105, 214, 219, 78, 56, 159, 14, 53, 203, 52, 78, 104, 57, 151, 216, 96, 31, 63, 14, 84, 113, 170, 64, 40, 172, 218, 126, 211, 38, 23, 165, 44, 104, 254, 100, 252, 47, 247, 45, 128, 154, 86, 236, 127, 179, 115, 207, 29, 90, 155, 230, 45, 170, 144, 62, 51, 109, 6, 17, 82, 50, 137, 148, 204, 153, 194, 57, 98, 81, 235, 144, 36, 35, 193, 238, 218, 206, 149, 129, 50, 110, 39, 77, 168, 231, 34, 209, 58, 217, 86, 11, 22, 149, 136, 189, 117, 46, 209, 161, 238, 121, 176, 102, 198, 91, 246, 67, 60, 173, 111, 2, 164, 149, 97, 222, 130, 84, 211, 6, 213, 212, 185, 196, 71, 195, 148, 115, 137, 12, 30, 241, 94, 229, 92, 98, 195, 145, 164, 237, 98, 160, 138, 155, 174, 101, 8, 224, 234, 120, 118, 196, 174, 212, 128, 154, 38, 231, 18, 27, 18, 229, 188, 56, 151, 232, 224, 239, 179, 211, 185, 196, 134, 243, 233, 26, 122, 21, 122, 230, 70, 206, 37, 66, 218, 46, 168, 85, 173, 125, 11, 114, 46, 241, 65, 130, 238, 239, 218, 127, 16, 77, 219, 148, 253, 147, 166, 77, 91, 95, 165, 221, 58, 105, 245, 7, 146, 182, 11, 181, 54, 13, 39, 83, 39, 185, 68, 115, 183, 106, 115, 66, 146, 182, 11, 194, 73, 159, 11, 146, 180, 93, 146, 182, 235, 225, 205, 221, 218, 192, 188, 133, 159, 219, 161, 166, 237, 55, 59, 109, 164, 109, 155, 166, 249, 169, 34, 212, 61, 63, 128, 39, 101, 163, 11, 81, 120, 49, 104, 129, 87, 215, 118, 232, 155, 238, 218, 213, 240, 190, 222, 234, 218, 46, 64, 20, 234, 164, 76, 42, 121, 185, 135, 38, 222, 153, 23, 8, 175, 197, 46, 209, 161, 185, 68, 5, 154, 94, 250, 186, 68, 70, 98, 181, 46, 145, 65, 157, 148, 105, 93, 34, 67, 127, 21, 183, 227, 18, 27, 21, 117, 137, 14, 9, 20, 115, 188, 123, 58, 81, 184, 59, 145, 58, 41, 93, 34, 196, 37, 6, 192, 223, 245, 209, 115, 226, 196, 55, 251, 211, 18, 23, 78, 250, 100, 79, 75, 100, 172, 165, 142, 163, 167, 37, 66, 168, 111, 75, 124, 248, 51, 34, 232, 245, 45, 145, 65, 215, 46, 204, 30, 142, 59, 161, 134, 109, 131, 71, 158, 84, 72, 125, 75, 124, 160, 190, 37, 42, 232, 155, 160, 214, 172, 171, 66, 223, 18, 29, 24, 131, 123, 21, 88, 111, 173, 170, 61, 135, 79, 169, 141, 248, 23, 123, 7, 241, 67, 88, 235, 235, 122, 222, 157, 230, 113, 59, 175, 146, 237, 119, 137, 87, 215, 118, 84, 180, 209, 214, 102, 248, 55, 101, 77, 54, 227, 174, 169, 233, 155, 160, 142, 212, 101, 114, 93, 192, 86, 49, 90, 62, 169, 225, 205, 139, 83, 243, 226, 228, 80, 139, 245, 173, 134, 36, 229, 162, 240, 90, 250, 52, 232, 175, 67, 81, 218, 142, 252, 166, 77, 50, 135, 166, 218, 13, 164, 229, 147, 244, 61, 36, 41, 215, 210, 44, 104, 189, 37, 66, 214, 91, 162, 66, 171, 183, 196, 5, 101, 217, 12, 106, 188, 14, 248, 145, 146, 112, 79, 16, 240, 209, 182, 225, 133, 86, 110, 98, 71, 149, 5, 170, 56, 245, 4, 84, 105, 73, 90, 247, 185, 24, 84, 145, 136, 224, 105, 223, 211, 254, 67, 237, 123, 40, 66, 67, 235, 123, 242, 163, 8, 18, 79, 134, 190, 162, 143, 34, 72, 168, 116, 218, 239, 179, 53, 235, 40, 66, 163, 176, 114, 220, 10, 125, 210, 165, 190, 191, 30, 67, 17, 28, 208, 165, 22, 181, 186, 106, 63, 59, 218, 247, 184, 209, 190, 8, 181, 221, 160, 125, 46, 251, 253, 153, 23, 180, 143, 226, 155, 54, 118, 140, 19, 82, 237, 31, 128, 235, 90, 6, 1, 250, 185, 188, 101, 71, 56, 233, 241, 55, 252, 46, 231, 107, 113, 39, 7, 173, 216, 223, 150, 14, 161, 255, 184, 181, 117, 45, 89, 177, 174, 237, 18, 74, 4, 57, 72, 106, 25, 69, 112, 254, 37, 79, 184, 36, 49, 115, 136, 149, 219, 250, 201, 22, 212, 180, 85, 201, 107, 93, 162, 69, 41, 232, 105, 102, 202, 80, 123, 32, 241, 100, 13, 109, 2, 16, 44, 100, 177, 119, 38, 156, 102, 157, 8, 173, 239, 106, 59, 194, 117, 53, 204, 77, 243, 80, 214, 31, 10, 202, 149, 220, 12, 131, 214, 73, 58, 210, 181, 140, 246, 187, 46, 72, 251, 158, 75, 117, 101, 226, 197, 64, 215, 146, 206, 113, 43, 212, 52, 217, 12, 184, 174, 37, 63, 63, 149, 132, 235, 74, 152, 31, 60, 2, 225, 59, 157, 78, 213, 116, 101, 130, 200, 109, 136, 126, 178, 109, 249, 128, 86, 38, 94, 40, 2, 106, 128, 11, 71, 8, 101, 205, 78, 106, 234, 160, 70, 97, 37, 233, 100, 136, 65, 179, 147, 90, 34, 131, 42, 110, 14, 142, 16, 203, 46, 55, 129, 37, 46, 220, 4, 150, 168, 224, 17, 164, 234, 61, 228, 249, 77, 188, 116, 34, 73, 204, 34, 214, 254, 251, 231, 214, 254, 99, 160, 177, 243, 143, 107, 188, 234, 241, 74, 78, 73, 212, 170, 118, 82, 0, 127, 199, 184, 221, 3, 3, 60, 162, 138, 219, 2, 20, 144, 147, 140, 36, 192, 93, 151, 74, 117, 22, 80, 247, 188, 0, 60, 177, 66, 107, 130, 227, 178, 155, 7, 16, 192, 141, 166, 100, 90, 191, 45, 34, 192, 187, 237, 228, 57, 201, 8, 3, 206, 75, 209, 250, 255, 22, 163, 150, 91, 181, 68, 170, 233, 55, 110, 198, 193, 162, 152, 101, 123, 22, 248, 92, 142, 233, 51, 160, 85, 237, 132, 219, 33, 101, 251, 237, 53, 122, 109, 207, 106, 1, 173, 109, 243, 27, 63, 208, 197, 222, 26, 28, 160, 105, 163, 138, 31, 90, 155, 134, 151, 132, 54, 39, 180, 11, 162, 43, 19, 171, 180, 93, 3, 222, 120, 63, 211, 50, 251, 220, 30, 104, 253, 182, 14, 52, 109, 223, 217, 113, 192, 218, 80, 52, 222, 79, 237, 49, 158, 246, 159, 149, 45, 209, 211, 220, 232, 103, 90, 160, 249, 115, 61, 182, 142, 82, 123, 252, 129, 115, 137, 30, 102, 151, 99, 137, 143, 230, 79, 181, 43, 39, 104, 177, 119, 28, 75, 140, 104, 188, 222, 58, 65, 170, 165, 183, 58, 150, 24, 161, 77, 186, 43, 67, 142, 37, 62, 28, 75, 84, 160, 109, 25, 85, 76, 177, 68, 135, 71, 208, 243, 251, 77, 213, 34, 254, 109, 13, 39, 212, 184, 34, 89, 226, 2, 79, 106, 170, 154, 58, 85, 40, 209, 34, 150, 24, 177, 54, 31, 249, 147, 148, 149, 248, 240, 136, 182, 117, 43, 145, 161, 46, 29, 127, 84, 116, 57, 198, 9, 165, 227, 86, 98, 195, 202, 55, 65, 209, 59, 179, 149, 8, 81, 141, 217, 74, 100, 240, 8, 132, 103, 173, 196, 133, 182, 93, 170, 37, 99, 156, 144, 246, 91, 43, 81, 226, 43, 250, 168, 53, 187, 18, 29, 238, 140, 118, 37, 46, 90, 221, 82, 7, 81, 56, 169, 83, 61, 119, 113, 179, 18, 27, 60, 242, 48, 251, 173, 43, 209, 161, 180, 211, 113, 164, 141, 43, 86, 166, 149, 8, 33, 73, 233, 24, 155, 76, 1, 8, 34, 16, 225, 153, 108, 133, 22, 248, 39, 107, 102, 208, 36, 146, 248, 240, 230, 110, 69, 147, 72, 162, 99, 87, 66, 147, 72, 226, 226, 93, 223, 4, 109, 84, 201, 72, 34, 68, 215, 146, 223, 155, 200, 240, 8, 132, 135, 157, 196, 133, 75, 152, 157, 68, 134, 166, 18, 126, 132, 178, 185, 73, 148, 208, 201, 174, 116, 170, 18, 7, 245, 92, 208, 55, 109, 18, 29, 169, 154, 54, 137, 12, 106, 187, 65, 77, 226, 226, 249, 109, 143, 49, 78, 200, 191, 211, 73, 168, 73, 156, 144, 96, 2, 8, 80, 107, 203, 36, 54, 214, 122, 14, 210, 182, 12, 147, 248, 240, 8, 114, 77, 100, 104, 125, 247, 116, 82, 133, 92, 19, 27, 82, 123, 156, 105, 226, 162, 249, 179, 181, 105, 131, 150, 93, 223, 116, 87, 66, 76, 19, 27, 184, 173, 64, 76, 37, 46, 34, 72, 64, 173, 75, 37, 50, 90, 85, 242, 66, 46, 153, 166, 126, 236, 232, 253, 69, 84, 98, 3, 119, 109, 165, 18, 27, 88, 106, 191, 35, 165, 18, 29, 186, 158, 119, 196, 143, 42, 14, 41, 149, 248, 224, 173, 78, 250, 100, 72, 169, 68, 136, 183, 46, 236, 53, 164, 109, 23, 82, 42, 17, 162, 181, 233, 251, 46, 244, 77, 39, 138, 24, 43, 164, 84, 34, 3, 73, 74, 183, 40, 5, 45, 246, 14, 122, 117, 79, 164, 84, 162, 3, 74, 37, 42, 232, 92, 175, 105, 137, 86, 170, 68, 136, 54, 217, 252, 222, 208, 252, 153, 252, 49, 11, 163, 202, 68, 235, 187, 84, 137, 150, 42, 102, 72, 39, 20, 191, 146, 197, 252, 205, 159, 218, 9, 249, 131, 174, 37, 83, 227, 253, 148, 248, 200, 79, 137, 10, 174, 253, 62, 149, 73, 251, 249, 206, 128, 4, 109, 242, 93, 177, 243, 202, 22, 248, 55, 101, 186, 84, 63, 39, 219, 174, 1, 149, 218, 206, 209, 39, 141, 44, 60, 209, 252, 217, 233, 212, 81, 218, 86, 246, 154, 42, 110, 142, 111, 202, 90, 179, 238, 137, 86, 166, 109, 120, 57, 144, 172, 164, 160, 138, 71, 28, 242, 149, 69, 120, 227, 253, 86, 247, 100, 219, 64, 165, 58, 173, 140, 165, 195, 184, 25, 228, 13, 125, 179, 171, 104, 219, 230, 45, 135, 36, 224, 10, 241, 205, 142, 45, 74, 203, 178, 61, 138, 194, 213, 210, 101, 144, 123, 174, 36, 241, 109, 155, 93, 80, 107, 78, 137, 16, 143, 160, 166, 237, 183, 199, 139, 249, 81, 171, 227, 165, 19, 85, 232, 249, 93, 187, 172, 3, 238, 154, 82, 98, 131, 47, 90, 189, 211, 33, 109, 242, 53, 45, 23, 98, 240, 78, 83, 74, 100, 104, 20, 86, 75, 39, 186, 208, 167, 236, 247, 37, 81, 194, 249, 116, 72, 255, 45, 246, 37, 177, 193, 151, 68, 5, 107, 45, 243, 44, 118, 168, 117, 45, 238, 111, 156, 190, 121, 73, 116, 176, 216, 17, 63, 218, 37, 209, 209, 170, 56, 169, 66, 187, 36, 62, 150, 92, 18, 21, 214, 74, 166, 245, 147, 130, 82, 123, 28, 37, 66, 232, 106, 90, 110, 63, 106, 118, 65, 137, 14, 136, 10, 120, 8, 205, 58, 232, 120, 22, 164, 180, 149, 194, 14, 169, 90, 7, 31, 34, 72, 80, 207, 5, 173, 131, 141, 71, 29, 116, 60, 21, 117, 80, 65, 45, 87, 212, 65, 134, 181, 255, 26, 13, 47, 103, 22, 187, 18, 226, 138, 58, 200, 200, 165, 233, 8, 21, 117, 144, 145, 84, 69, 168, 168, 131, 139, 92, 154, 70, 252, 168, 162, 14, 62, 150, 166, 81, 69, 29, 92, 84, 212, 193, 7, 173, 40, 196, 55, 109, 88, 9, 61, 9, 183, 58, 220, 110, 1, 228, 202, 90, 29, 108, 208, 86, 7, 21, 84, 3, 31, 148, 33, 183, 212, 193, 133, 115, 71, 219, 100, 172, 220, 185, 19, 146, 120, 101, 75, 29, 100, 144, 40, 111, 224, 181, 212, 65, 137, 214, 166, 225, 21, 225, 145, 181, 82, 177, 85, 7, 33, 158, 150, 17, 210, 170, 131, 140, 103, 146, 234, 224, 130, 99, 79, 185, 84, 7, 25, 78, 193, 35, 16, 24, 115, 55, 52, 3, 245, 76, 8, 31, 234, 96, 0, 34, 11, 35, 215, 181, 14, 240, 251, 58, 168, 149, 131, 16, 143, 74, 148, 131, 11, 190, 56, 168, 64, 66, 73, 7, 21, 180, 173, 183, 78, 210, 193, 198, 167, 131, 10, 86, 63, 79, 39, 251, 121, 58, 232, 120, 79, 7, 29, 19, 239, 169, 24, 226, 95, 236, 29, 196, 31, 65, 201, 110, 144, 180, 93, 72, 194, 232, 81, 188, 32, 223, 7, 207, 167, 195, 64, 169, 82, 186, 48, 69, 158, 14, 74, 52, 109, 219, 241, 116, 144, 225, 146, 45, 28, 223, 166, 237, 163, 241, 50, 248, 93, 56, 85, 168, 153, 65, 142, 167, 131, 144, 182, 139, 167, 131, 12, 173, 243, 209, 183, 114, 210, 80, 197, 105, 219, 107, 242, 249, 43, 168, 201, 71, 252, 200, 211, 193, 137, 10, 242, 116, 144, 194, 169, 42, 212, 40, 140, 60, 29, 132, 60, 171, 49, 242, 9, 59, 200, 211, 193, 198, 99, 225, 213, 52, 59, 52, 185, 24, 79, 7, 37, 220, 211, 193, 135, 75, 152, 29, 148, 218, 227, 15, 235, 45, 145, 167, 131, 12, 79, 7, 21, 124, 210, 197, 104, 163, 167, 49, 235, 116, 240, 129, 226, 211, 81, 157, 14, 46, 180, 145, 234, 116, 208, 33, 29, 108, 232, 116, 208, 33, 73, 233, 30, 231, 210, 193, 6, 47, 4, 118, 46, 29, 100, 64, 22, 52, 149, 240, 163, 214, 78, 93, 214, 165, 131, 12, 214, 165, 131, 10, 30, 81, 197, 13, 57, 93, 75, 126, 99, 95, 251, 15, 226, 93, 35, 15, 46, 161, 44, 155, 121, 192, 59, 23, 71, 211, 214, 121, 132, 16, 127, 203, 85, 224, 89, 58, 216, 104, 218, 170, 78, 199, 209, 179, 116, 176, 193, 35, 173, 140, 165, 131, 12, 143, 44, 150, 14, 50, 120, 107, 211, 111, 97, 233, 160, 195, 35, 72, 130, 131, 11, 18, 28, 12, 128, 171, 123, 26, 138, 42, 17, 28, 132, 68, 224, 8, 14, 42, 180, 58, 183, 21, 13, 46, 182, 162, 193, 134, 46, 79, 50, 228, 182, 162, 189, 6, 33, 13, 175, 133, 243, 203, 92, 127, 219, 57, 4, 183, 21, 155, 148, 253, 231, 138, 28, 34, 48, 12, 83, 34, 48, 255, 200, 173, 231, 175, 239, 242, 25, 218, 252, 222, 76, 188, 153, 248, 55, 102, 154, 137, 71, 240, 198, 105, 23, 48, 255, 17, 154, 9, 37, 157, 86, 172, 55, 214, 82, 199, 191, 237, 66, 238, 233, 238, 233, 175, 31, 205, 115, 220, 220, 212, 113, 91, 214, 202, 24, 226, 161, 42, 35, 13, 234, 60, 90, 159, 153, 129, 232, 90, 166, 194, 32, 191, 225, 192, 45, 155, 123, 91, 230, 238, 158, 208, 103, 106, 252, 160, 141, 151, 225, 208, 82, 151, 58, 115, 132, 210, 166, 165, 3, 81, 75, 151, 92, 28, 78, 73, 228, 214, 107, 240, 193, 173, 215, 160, 194, 187, 237, 164, 246, 53, 248, 224, 174, 175, 65, 72, 83, 9, 127, 131, 12, 206, 248, 138, 190, 68, 91, 214, 76, 252, 219, 46, 93, 207, 63, 52, 74, 58, 95, 75, 29, 127, 180, 215, 13, 15, 179, 207, 16, 109, 90, 68, 43, 69, 155, 147, 152, 245, 42, 9, 91, 150, 65, 195, 11, 61, 41, 27, 93, 201, 58, 37, 143, 196, 202, 35, 73, 43, 234, 152, 52, 117, 210, 39, 165, 121, 238, 233, 237, 77, 152, 112, 215, 229, 145, 136, 111, 235, 120, 164, 45, 211, 38, 217, 46, 91, 236, 23, 120, 231, 226, 160, 45, 115, 127, 28, 39, 244, 153, 26, 5, 131, 150, 186, 18, 90, 182, 127, 162, 113, 218, 149, 26, 15, 179, 16, 75, 151, 92, 30, 18, 254, 237, 117, 131, 13, 22, 165, 184, 212, 48, 242, 83, 229, 43, 67, 201, 117, 131, 20, 201, 117, 131, 10, 15, 195, 162, 112, 55, 184, 160, 79, 223, 4, 164, 180, 193, 155, 78, 228, 30, 119, 131, 14, 103, 144, 132, 36, 241, 106, 170, 221, 96, 67, 155, 252, 166, 169, 213, 161, 39, 63, 39, 140, 138, 157, 112, 167, 100, 16, 26, 156, 130, 214, 132, 214, 38, 31, 158, 60, 40, 117, 224, 65, 91, 70, 149, 106, 104, 26, 47, 247, 104, 183, 170, 82, 6, 53, 32, 105, 102, 144, 47, 230, 119, 221, 95, 230, 71, 107, 2, 3, 73, 122, 203, 161, 38, 31, 173, 9, 77, 254, 163, 245, 219, 174, 9, 223, 244, 245, 147, 171, 33, 23, 2, 188, 241, 46, 143, 129, 86, 102, 218, 34, 245, 78, 183, 128, 111, 91, 214, 180, 117, 46, 153, 251, 62, 32, 128, 2, 56, 120, 84, 210, 32, 21, 71, 126, 35, 15, 253, 84, 136, 125, 33, 220, 191, 83, 217, 165, 78, 131, 43, 132, 171, 165, 203, 72, 16, 64, 39, 251, 73, 55, 251, 189, 113, 3, 237, 1, 7, 245, 53, 65, 59, 39, 90, 157, 127, 14, 180, 213, 161, 60, 45, 17, 196, 154, 176, 120, 26, 94, 236, 88, 118, 49, 214, 132, 117, 144, 174, 125, 104, 253, 223, 229, 28, 41, 211, 136, 197, 156, 28, 151, 221, 224, 2, 143, 184, 186, 236, 6, 25, 201, 18, 212, 248, 150, 221, 224, 130, 227, 119, 149, 221, 32, 163, 105, 171, 171, 178, 27, 124, 88, 143, 209, 148, 221, 32, 195, 215, 229, 171, 58, 206, 207, 237, 36, 90, 93, 118, 114, 244, 228, 114, 159, 82, 59, 162, 194, 139, 75, 182, 46, 145, 187, 100, 46, 217, 99, 217, 181, 160, 146, 141, 195, 191, 49, 51, 193, 192, 5, 80, 113, 102, 160, 242, 218, 46, 7, 124, 129, 156, 100, 4, 53, 208, 186, 214, 57, 175, 212, 224, 233, 236, 151, 72, 87, 51, 161, 21, 170, 111, 130, 55, 232, 98, 111, 156, 144, 98, 110, 16, 162, 152, 27, 84, 88, 142, 28, 212, 33, 215, 149, 21, 141, 27, 124, 244, 91, 13, 42, 180, 215, 232, 91, 13, 46, 220, 169, 66, 144, 135, 217, 229, 171, 193, 69, 83, 9, 127, 3, 134, 209, 63, 142, 80, 95, 110, 14, 215, 182, 140, 42, 52, 161, 190, 220, 80, 203, 109, 20, 238, 5, 173, 252, 148, 205, 160, 229, 171, 65, 6, 154, 134, 87, 131, 11, 190, 80, 209, 230, 133, 182, 28, 121, 22, 134, 214, 138, 103, 157, 119, 120, 98, 133, 124, 161, 21, 187, 178, 105, 104, 105, 107, 239, 39, 86, 58, 48, 86, 30, 65, 43, 189, 87, 161, 149, 189, 10, 173, 84, 207, 37, 194, 194, 171, 193, 134, 227, 185, 28, 45, 188, 26, 7, 228, 17, 177, 86, 162, 133, 87, 131, 140, 133, 87, 131, 10, 254, 184, 209, 204, 114, 53, 200, 192, 114, 53, 168, 16, 33, 233, 4, 173, 6, 23, 107, 255, 105, 191, 175, 232, 87, 244, 83, 50, 253, 124, 121, 207, 82, 251, 65, 63, 159, 161, 36, 168, 8, 174, 146, 117, 142, 86, 131, 15, 136, 10, 47, 104, 53, 216, 160, 36, 180, 254, 103, 114, 180, 26, 132, 124, 190, 9, 104, 53, 184, 80, 17, 212, 227, 149, 190, 45, 59, 180, 26, 124, 80, 18, 158, 151, 123, 94, 206, 113, 173, 108, 55, 34, 144, 16, 225, 147, 161, 213, 224, 98, 87, 4, 151, 236, 83, 29, 180, 26, 132, 180, 174, 117, 13, 58, 224, 133, 65, 171, 65, 6, 37, 97, 53, 232, 80, 17, 250, 241, 67, 148, 4, 109, 156, 208, 106, 176, 161, 36, 184, 86, 18, 173, 6, 27, 171, 193, 0, 172, 126, 191, 13, 50, 52, 188, 126, 155, 77, 200, 1, 25, 152, 105, 251, 219, 224, 162, 17, 130, 130, 157, 54, 114, 224, 183, 233, 231, 158, 16, 55, 51, 161, 214, 190, 5, 178, 46, 145, 3, 25, 26, 133, 213, 111, 131, 11, 230, 229, 183, 213, 95, 171, 63, 165, 141, 146, 78, 2, 130, 116, 24, 222, 228, 163, 245, 157, 48, 0, 161, 44, 27, 51, 109, 17, 187, 134, 213, 15, 181, 109, 182, 25, 233, 51, 91, 160, 109, 23, 122, 137, 67, 42, 181, 35, 252, 151, 49, 78, 136, 146, 173, 222, 13, 109, 164, 116, 27, 164, 120, 223, 37, 73, 137, 126, 27, 116, 208, 229, 183, 65, 72, 5, 131, 243, 233, 208, 111, 131, 15, 46, 155, 194, 108, 131, 11, 143, 160, 79, 143, 217, 6, 27, 186, 210, 58, 179, 13, 50, 152, 109, 240, 209, 208, 120, 127, 91, 179, 232, 157, 157, 6, 29, 190, 237, 66, 144, 182, 146, 86, 167, 193, 133, 243, 47, 157, 6, 25, 92, 155, 116, 200, 105, 144, 241, 220, 32, 167, 65, 6, 7, 125, 19, 180, 160, 25, 125, 69, 255, 181, 193, 136, 215, 6, 27, 206, 175, 13, 46, 164, 100, 175, 13, 50, 168, 69, 186, 188, 127, 252, 248, 107, 131, 19, 254, 218, 160, 131, 131, 214, 101, 155, 60, 93, 254, 86, 191, 237, 183, 240, 166, 19, 49, 117, 30, 61, 227, 102, 208, 226, 133, 61, 4, 104, 171, 190, 145, 218, 209, 107, 131, 139, 215, 6, 35, 42, 203, 104, 59, 199, 55, 59, 118, 218, 168, 145, 64, 124, 105, 101, 157, 101, 59, 152, 105, 139, 94, 27, 132, 240, 231, 91, 93, 43, 123, 77, 81, 163, 48, 122, 109, 208, 0, 236, 180, 185, 49, 67, 175, 13, 46, 208, 6, 25, 191, 12, 105, 189, 109, 127, 91, 231, 211, 33, 213, 94, 126, 42, 164, 13, 54, 228, 167, 66, 238, 144, 199, 87, 60, 39, 77, 98, 173, 76, 232, 181, 193, 7, 215, 6, 29, 48, 204, 62, 107, 131, 11, 215, 148, 204, 43, 99, 109, 112, 98, 217, 197, 218, 32, 195, 79, 28, 170, 45, 69, 219, 161, 61, 60, 191, 16, 169, 157, 136, 181, 193, 133, 254, 46, 228, 150, 181, 65, 7, 243, 22, 214, 6, 25, 42, 30, 177, 54, 200, 144, 218, 227, 77, 27, 92, 52, 109, 240, 209, 30, 211, 180, 65, 134, 197, 236, 32, 105, 27, 244, 115, 125, 46, 215, 186, 150, 151, 230, 5, 60, 124, 229, 253, 121, 225, 150, 80, 205, 20, 247, 116, 207, 59, 96, 143, 52, 128, 17, 27, 97, 166, 13, 50, 180, 78, 182, 21, 75, 146, 54, 248, 88, 146, 61, 215, 70, 158, 26, 164, 112, 77, 13, 58, 90, 174, 251, 203, 6, 25, 218, 188, 108, 112, 33, 194, 4, 45, 64, 130, 67, 124, 229, 145, 63, 47, 236, 160, 85, 210, 76, 209, 180, 147, 86, 247, 164, 77, 63, 199, 224, 65, 187, 35, 255, 166, 141, 23, 2, 92, 29, 207, 229, 217, 143, 52, 53, 51, 7, 160, 240, 9, 92, 81, 1, 60, 119, 37, 3, 170, 184, 77, 236, 74, 232, 89, 252, 179, 65, 71, 131, 13, 201, 88, 54, 184, 224, 239, 83, 174, 108, 144, 161, 109, 151, 186, 117, 168, 65, 0, 65, 131, 13, 254, 144, 225, 104, 81, 131, 11, 13, 2, 0, 65, 158, 138, 138, 199, 224, 194, 90, 185, 84, 84, 60, 6, 25, 254, 150, 67, 15, 109, 91, 173, 172, 168, 120, 12, 58, 24, 37, 113, 80, 91, 254, 199, 224, 3, 25, 182, 31, 131, 10, 79, 133, 219, 37, 107, 117, 237, 49, 216, 224, 147, 116, 144, 83, 168, 246, 35, 165, 141, 86, 62, 80, 150, 13, 130, 178, 108, 11, 28, 243, 82, 109, 23, 132, 13, 202, 178, 173, 245, 24, 132, 188, 180, 104, 173, 199, 32, 195, 221, 51, 168, 224, 158, 193, 9, 247, 12, 42, 104, 75, 113, 6, 23, 152, 51, 8, 105, 170, 157, 156, 193, 69, 4, 103, 80, 129, 243, 233, 86, 106, 67, 112, 6, 31, 16, 156, 193, 70, 246, 87, 20, 130, 51, 200, 80, 95, 166, 191, 77, 65, 206, 32, 133, 99, 250, 200, 25, 92, 184, 51, 216, 96, 208, 225, 111, 185, 197, 163, 146, 198, 174, 87, 73, 24, 114, 6, 33, 90, 155, 86, 134, 156, 193, 134, 36, 165, 123, 141, 32, 103, 208, 1, 145, 253, 140, 212, 30, 79, 153, 28, 69, 232, 90, 6, 61, 14, 57, 131, 16, 175, 88, 139, 59, 57, 82, 77, 77, 84, 211, 5, 180, 254, 39, 67, 223, 184, 25, 132, 60, 34, 48, 110, 6, 23, 24, 55, 131, 10, 207, 226, 219, 12, 50, 56, 174, 205, 36, 114, 248, 102, 167, 218, 12, 50, 180, 25, 116, 164, 150, 45, 105, 166, 60, 164, 246, 120, 3, 117, 131, 234, 124, 141, 178, 108, 6, 29, 30, 129, 208, 12, 62, 82, 54, 131, 10, 13, 254, 200, 49, 184, 208, 140, 84, 75, 228, 24, 108, 184, 254, 58, 132, 175, 77, 166, 17, 170, 61, 102, 112, 65, 169, 234, 164, 52, 51, 248, 224, 17, 102, 236, 67, 43, 183, 245, 147, 33, 201, 74, 72, 86, 215, 146, 106, 29, 249, 169, 84, 113, 67, 249, 169, 88, 51, 131, 13, 107, 255, 61, 90, 235, 127, 58, 244, 44, 200, 193, 147, 146, 37, 131, 90, 253, 30, 28, 150, 45, 207, 27, 240, 236, 175, 232, 4, 202, 79, 21, 177, 137, 221, 19, 177, 102, 6, 31, 60, 192, 13, 249, 43, 160, 153, 193, 71, 243, 111, 99, 246, 240, 77, 155, 68, 13, 173, 147, 69, 75, 166, 218, 227, 110, 180, 153, 129, 56, 41, 181, 153, 193, 133, 83, 85, 136, 193, 243, 173, 168, 153, 193, 8, 126, 94, 184, 209, 55, 102, 48, 226, 87, 242, 141, 25, 92, 160, 111, 204, 32, 196, 23, 51, 8, 161, 43, 95, 178, 152, 65, 198, 195, 108, 163, 85, 45, 102, 240, 225, 178, 31, 249, 75, 220, 118, 253, 91, 224, 141, 247, 29, 51, 112, 143, 187, 65, 254, 18, 51, 248, 128, 208, 24, 92, 40, 203, 134, 26, 131, 139, 202, 91, 12, 82, 84, 222, 98, 80, 65, 215, 146, 191, 77, 105, 144, 112, 199, 223, 98, 208, 193, 213, 190, 197, 32, 131, 82, 247, 188, 227, 157, 102, 151, 99, 7, 213, 114, 33, 8, 136, 171, 123, 30, 81, 120, 49, 15, 138, 182, 11, 162, 240, 98, 144, 241, 188, 24, 84, 248, 149, 44, 230, 71, 110, 23, 131, 144, 231, 87, 146, 118, 49, 200, 104, 101, 152, 231, 136, 160, 182, 155, 10, 47, 21, 93, 12, 66, 186, 117, 146, 139, 193, 69, 126, 42, 84, 209, 111, 157, 160, 137, 166, 244, 251, 253, 115, 49, 184, 240, 207, 197, 224, 67, 157, 148, 201, 145, 139, 65, 135, 207, 197, 160, 130, 55, 185, 24, 92, 112, 79, 8, 27, 214, 90, 9, 131, 11, 251, 208, 83, 81, 177, 12, 58, 68, 60, 204, 246, 251, 202, 50, 216, 136, 128, 36, 116, 45, 249, 40, 63, 21, 82, 90, 89, 6, 33, 158, 255, 101, 112, 225, 105, 117, 124, 25, 92, 188, 247, 50, 40, 193, 154, 41, 12, 95, 204, 223, 160, 45, 83, 218, 142, 111, 91, 6, 25, 17, 60, 130, 14, 74, 219, 50, 184, 208, 218, 150, 65, 238, 61, 34, 252, 174, 106, 203, 32, 67, 132, 103, 110, 73, 51, 5, 181, 206, 231, 129, 243, 75, 209, 183, 114, 226, 112, 207, 117, 45, 185, 233, 11, 184, 178, 108, 232, 93, 255, 153, 27, 158, 122, 241, 204, 141, 32, 141, 132, 209, 171, 208, 55, 237, 135, 198, 30, 65, 17, 79, 82, 208, 195, 148, 177, 131, 114, 146, 17, 135, 86, 143, 168, 168, 114, 120, 131, 235, 178, 165, 11, 84, 91, 6, 31, 170, 45, 131, 10, 206, 80, 234, 164, 199, 143, 94, 189, 1, 127, 137, 25, 8, 253, 92, 47, 173, 76, 20, 30, 193, 154, 25, 180, 178, 45, 131, 142, 200, 194, 14, 198, 10, 173, 3, 117, 104, 253, 181, 12, 66, 214, 90, 6, 173, 181, 204, 106, 187, 100, 66, 77, 219, 181, 12, 229, 83, 106, 63, 120, 68, 43, 214, 25, 175, 78, 209, 157, 104, 59, 164, 77, 178, 117, 64, 221, 243, 90, 177, 142, 148, 157, 174, 101, 176, 0, 171, 31, 114, 120, 152, 101, 217, 148, 78, 90, 189, 129, 174, 101, 112, 129, 247, 93, 12, 213, 30, 59, 30, 129, 208, 150, 211, 99, 245, 115, 62, 29, 226, 71, 219, 15, 66, 221, 243, 19, 159, 175, 21, 219, 120, 134, 147, 222, 209, 144, 135, 71, 22, 79, 103, 219, 5, 249, 54, 94, 111, 250, 14, 146, 102, 166, 249, 179, 109, 69, 42, 228, 239, 91, 121, 240, 244, 55, 133, 240, 111, 186, 150, 193, 6, 93, 203, 160, 130, 107, 191, 102, 25, 92, 188, 174, 102, 25, 100, 152, 192, 164, 2, 170, 184, 85, 192, 91, 151, 101, 208, 193, 61, 169, 228, 133, 26, 6, 29, 16, 141, 58, 198, 48, 200, 160, 159, 139, 97, 144, 97, 93, 54, 180, 12, 131, 140, 167, 95, 155, 194, 65, 127, 21, 55, 9, 73, 98, 230, 112, 141, 61, 242, 192, 7, 199, 15, 219, 217, 52, 101, 112, 241, 252, 115, 114, 228, 30, 82, 134, 148, 42, 131, 17, 74, 149, 65, 133, 116, 146, 50, 184, 48, 129, 43, 66, 9, 151, 48, 59, 200, 23, 180, 58, 159, 202, 96, 67, 175, 98, 80, 65, 49, 24, 0, 109, 188, 18, 131, 12, 191, 48, 168, 192, 33, 200, 35, 11, 131, 13, 30, 89, 24, 84, 88, 24, 108, 180, 100, 11, 131, 143, 133, 193, 0, 232, 147, 12, 58, 232, 147, 12, 42, 120, 227, 180, 147, 100, 176, 193, 132, 5, 245, 78, 183, 116, 173, 236, 116, 139, 82, 154, 182, 173, 147, 210, 52, 73, 113, 93, 203, 122, 83, 71, 15, 179, 207, 114, 218, 232, 97, 246, 97, 246, 41, 17, 101, 75, 157, 71, 241, 194, 154, 153, 214, 149, 176, 187, 54, 179, 254, 48, 251, 176, 86, 50, 253, 21, 253, 119, 93, 43, 153, 19, 224, 93, 35, 212, 129, 78, 72, 210, 174, 244, 204, 203, 51, 47, 12, 86, 97, 169, 253, 254, 249, 200, 37, 91, 249, 38, 168, 68, 186, 50, 233, 232, 125, 151, 123, 117, 164, 244, 61, 25, 202, 35, 109, 77, 189, 23, 104, 197, 50, 83, 6, 113, 199, 188, 222, 147, 153, 120, 152, 93, 75, 190, 47, 224, 164, 143, 164, 109, 101, 175, 249, 58, 44, 188, 154, 6, 51, 203, 248, 202, 55, 65, 31, 12, 179, 234, 225, 193, 196, 25, 41, 130, 132, 212, 233, 33, 73, 234, 36, 213, 118, 105, 188, 39, 131, 14, 199, 211, 97, 157, 12, 50, 52, 117, 50, 184, 208, 56, 237, 123, 126, 75, 6, 31, 239, 250, 142, 134, 87, 195, 115, 210, 50, 161, 39, 181, 100, 16, 242, 168, 175, 100, 144, 97, 41, 147, 43, 25, 92, 180, 74, 146, 193, 133, 38, 159, 177, 40, 165, 21, 73, 50, 232, 224, 15, 193, 177, 122, 27, 244, 52, 127, 74, 6, 33, 13, 105, 123, 73, 6, 25, 92, 31, 69, 5, 230, 219, 182, 243, 175, 40, 58, 148, 66, 184, 173, 248, 138, 62, 3, 146, 148, 239, 239, 24, 226, 186, 150, 105, 112, 199, 199, 194, 171, 249, 138, 226, 162, 249, 87, 185, 250, 67, 43, 163, 185, 27, 34, 194, 2, 25, 86, 78, 20, 53, 222, 135, 198, 239, 109, 41, 205, 17, 141, 119, 245, 147, 112, 215, 213, 94, 55, 112, 91, 209, 160, 180, 159, 239, 15, 179, 203, 221, 225, 161, 165, 46, 230, 165, 245, 31, 55, 242, 149, 251, 157, 174, 249, 129, 179, 236, 116, 136, 1, 141, 215, 93, 29, 173, 9, 254, 176, 5, 77, 219, 166, 225, 229, 223, 150, 157, 106, 138, 222, 119, 49, 120, 74, 164, 241, 126, 114, 132, 65, 235, 175, 101, 220, 66, 184, 255, 6, 36, 73, 157, 228, 252, 74, 117, 144, 187, 246, 242, 240, 206, 229, 193, 241, 92, 140, 166, 157, 168, 210, 127, 221, 224, 252, 75, 137, 5, 20, 184, 88, 57, 81, 212, 174, 162, 216, 120, 152, 101, 168, 251, 247, 196, 19, 226, 111, 112, 93, 203, 32, 132, 97, 88, 146, 114, 65, 43, 183, 162, 56, 225, 250, 169, 220, 131, 72, 148, 163, 214, 73, 171, 63, 146, 226, 220, 161, 177, 71, 30, 118, 37, 70, 191, 181, 52, 38, 139, 87, 119, 199, 174, 166, 173, 75, 196, 174, 162, 8, 161, 116, 129, 71, 32, 240, 74, 180, 234, 171, 115, 15, 226, 157, 102, 155, 242, 228, 114, 17, 218, 204, 30, 159, 16, 54, 58, 245, 121, 168, 90, 11, 193, 7, 108, 52, 179, 21, 197, 5, 143, 32, 102, 43, 138, 11, 95, 215, 70, 43, 138, 26, 175, 131, 99, 173, 116, 124, 1, 105, 101, 152, 183, 160, 164, 21, 197, 7, 66, 146, 81, 20, 21, 30, 149, 112, 5, 220, 68, 209, 97, 121, 237, 56, 106, 170, 221, 180, 78, 20, 31, 52, 245, 227, 71, 107, 211, 247, 93, 15, 239, 52, 185, 218, 69, 4, 126, 182, 142, 163, 215, 166, 243, 35, 150, 231, 203, 115, 16, 255, 124, 8, 199, 188, 36, 154, 92, 200, 49, 47, 254, 164, 206, 219, 34, 199, 76, 161, 191, 203, 241, 43, 105, 56, 96, 195, 210, 116, 92, 54, 210, 137, 226, 67, 39, 138, 10, 114, 162, 216, 64, 19, 69, 199, 218, 255, 109, 207, 225, 188, 98, 195, 25, 94, 215, 74, 10, 242, 200, 218, 127, 15, 201, 245, 187, 254, 183, 199, 41, 73, 236, 146, 200, 35, 14, 186, 24, 38, 41, 139, 19, 63, 183, 139, 96, 79, 91, 157, 87, 124, 168, 252, 218, 127, 168, 89, 135, 56, 175, 232, 224, 188, 98, 99, 217, 133, 156, 87, 92, 52, 240, 228, 26, 53, 246, 8, 114, 94, 113, 194, 179, 65, 206, 43, 66, 56, 233, 21, 21, 154, 124, 69, 133, 124, 69, 5, 173, 146, 149, 20, 164, 138, 248, 145, 190, 149, 19, 101, 217, 124, 31, 168, 120, 4, 169, 181, 111, 89, 224, 164, 252, 79, 180, 246, 31, 3, 139, 80, 98, 97, 80, 131, 43, 58, 152, 237, 187, 34, 131, 175, 220, 111, 234, 174, 232, 120, 93, 76, 234, 66, 219, 174, 8, 105, 157, 104, 187, 226, 194, 21, 27, 79, 235, 155, 224, 138, 139, 133, 113, 56, 72, 182, 237, 220, 185, 34, 195, 162, 176, 115, 197, 5, 195, 108, 115, 69, 6, 87, 148, 96, 174, 216, 96, 174, 168, 64, 85, 128, 225, 164, 79, 202, 132, 175, 253, 215, 192, 193, 63, 95, 29, 39, 103, 239, 129, 39, 86, 72, 215, 107, 109, 78, 222, 170, 218, 67, 74, 181, 161, 111, 172, 208, 99, 180, 216, 19, 225, 94, 225, 85, 20, 137, 135, 217, 215, 55, 81, 234, 175, 61, 78, 219, 158, 63, 42, 65, 223, 236, 168, 0, 137, 149, 79, 84, 44, 135, 88, 241, 211, 217, 203, 46, 212, 180, 125, 215, 119, 64, 153, 76, 239, 250, 18, 244, 151, 78, 22, 48, 144, 36, 117, 210, 90, 234, 252, 155, 189, 117, 97, 202, 80, 179, 15, 192, 35, 157, 141, 253, 53, 242, 72, 203, 228, 11, 120, 164, 210, 11, 244, 42, 215, 245, 58, 162, 189, 111, 66, 131, 39, 87, 147, 77, 58, 170, 60, 229, 160, 242, 148, 182, 78, 246, 179, 185, 132, 217, 121, 160, 111, 162, 82, 219, 121, 43, 163, 237, 10, 80, 177, 108, 223, 113, 235, 164, 160, 103, 53, 174, 188, 166, 138, 155, 210, 138, 253, 109, 233, 237, 33, 110, 254, 218, 73, 29, 113, 115, 224, 142, 121, 33, 110, 218, 118, 181, 69, 220, 30, 168, 116, 211, 28, 120, 86, 99, 196, 13, 165, 147, 152, 165, 147, 152, 193, 239, 122, 32, 128, 32, 81, 197, 35, 12, 82, 99, 143, 52, 109, 93, 35, 174, 17, 9, 9, 44, 35, 190, 60, 231, 183, 97, 32, 61, 250, 182, 203, 129, 79, 6, 18, 184, 250, 2, 117, 82, 54, 141, 86, 248, 85, 185, 39, 117, 173, 255, 186, 180, 92, 34, 42, 203, 240, 191, 212, 30, 211, 185, 94, 211, 18, 45, 203, 182, 134, 137, 138, 98, 13, 15, 16, 64, 21, 183, 119, 141, 48, 96, 0, 207, 93, 137, 186, 245, 159, 165, 154, 110, 51, 79, 39, 99, 207, 151, 92, 191, 13, 106, 253, 244, 234, 208, 148, 206, 17, 87, 184, 226, 152, 87, 211, 184, 223, 29, 56, 191, 148, 4, 124, 74, 237, 6, 42, 30, 65, 11, 48, 88, 166, 190, 128, 115, 217, 224, 83, 106, 11, 128, 1, 179, 43, 241, 196, 87, 180, 81, 120, 100, 97, 36, 154, 70, 35, 22, 32, 0, 245, 92, 208, 210, 150, 222, 68, 15, 60, 17, 66, 180, 87, 0, 93, 102, 149, 182, 119, 128, 182, 72, 221, 243, 12, 116, 170, 150, 239, 11, 216, 59, 200, 241, 93, 203, 115, 15, 218, 174, 136, 119, 125, 6, 94, 75, 134, 212, 129, 149, 173, 245, 43, 233, 236, 184, 29, 74, 150, 116, 0, 11, 84, 203, 94, 36, 154, 221, 53, 165, 68, 137, 149, 43, 46, 80, 199, 201, 21, 25, 30, 64, 149, 252, 246, 248, 0, 210, 246, 43, 67, 174, 40, 128, 103, 201, 181, 146, 130, 34, 154, 54, 170, 56, 185, 98, 35, 159, 76, 174, 200, 160, 138, 211, 182, 231, 224, 40, 42, 250, 185, 32, 139, 189, 243, 248, 228, 6, 169, 107, 114, 187, 51, 33, 124, 112, 210, 39, 91, 15, 85, 185, 50, 185, 34, 35, 130, 4, 228, 204, 147, 2, 137, 96, 2, 228, 220, 158, 183, 70, 48, 65, 83, 199, 18, 175, 77, 87, 4, 19, 36, 231, 116, 69, 48, 129, 187, 101, 12, 34, 152, 128, 117, 78, 176, 206, 8, 18, 80, 229, 181, 93, 16, 17, 76, 128, 148, 47, 170, 205, 72, 68, 48, 1, 235, 164, 136, 96, 2, 212, 150, 210, 169, 34, 34, 152, 128, 117, 162, 5, 106, 237, 91, 92, 113, 225, 190, 120, 97, 143, 131, 173, 227, 104, 2, 87, 100, 188, 58, 154, 128, 98, 2, 87, 124, 76, 224, 104, 2, 87, 92, 160, 9, 92, 17, 130, 38, 112, 197, 6, 182, 142, 35, 9, 92, 145, 225, 107, 89, 182, 72, 2, 87, 124, 240, 21, 69, 18, 184, 4, 87, 100, 120, 58, 39, 200, 21, 23, 58, 217, 95, 182, 237, 16, 191, 179, 117, 188, 181, 162, 11, 185, 226, 131, 227, 218, 12, 114, 69, 198, 179, 180, 194, 11, 82, 236, 188, 50, 87, 124, 224, 179, 209, 110, 154, 68, 174, 40, 225, 221, 138, 10, 46, 73, 249, 254, 144, 164, 124, 151, 200, 213, 58, 225, 6, 62, 105, 4, 33, 142, 87, 183, 34, 67, 183, 162, 2, 151, 12, 233, 202, 201, 190, 9, 138, 18, 141, 155, 116, 167, 184, 240, 136, 179, 226, 66, 171, 36, 173, 107, 169, 60, 27, 43, 18, 160, 177, 71, 16, 191, 131, 247, 195, 4, 16, 160, 6, 202, 158, 138, 166, 200, 192, 76, 219, 73, 83, 92, 60, 204, 78, 154, 78, 154, 226, 130, 105, 156, 38, 77, 113, 161, 118, 229, 162, 105, 219, 63, 105, 138, 141, 73, 83, 116, 108, 66, 147, 166, 200, 240, 48, 203, 94, 83, 92, 176, 215, 20, 21, 144, 241, 100, 119, 34, 68, 155, 223, 155, 226, 162, 25, 30, 129, 160, 189, 111, 66, 83, 116, 232, 211, 55, 161, 41, 46, 92, 178, 166, 184, 160, 188, 249, 215, 119, 130, 52, 181, 116, 120, 33, 64, 248, 218, 127, 15, 100, 112, 126, 41, 200, 85, 211, 70, 45, 87, 53, 197, 134, 111, 170, 154, 34, 3, 195, 72, 53, 197, 134, 254, 79, 164, 154, 162, 67, 43, 78, 77, 247, 185, 209, 54, 161, 174, 45, 93, 70, 34, 26, 133, 21, 106, 218, 42, 78, 77, 177, 33, 53, 197, 134, 90, 32, 3, 115, 45, 162, 87, 161, 198, 243, 142, 41, 46, 104, 115, 66, 144, 111, 117, 79, 10, 59, 166, 184, 208, 138, 245, 116, 28, 83, 108, 52, 109, 153, 58, 255, 208, 58, 105, 117, 135, 136, 198, 251, 204, 20, 23, 239, 187, 144, 63, 180, 49, 99, 217, 149, 24, 42, 218, 104, 59, 168, 231, 210, 216, 8, 51, 230, 240, 8, 99, 35, 204, 20, 33, 30, 134, 234, 164, 232, 59, 106, 253, 214, 247, 95, 166, 184, 240, 203, 20, 27, 158, 78, 247, 101, 138, 140, 165, 254, 184, 51, 161, 92, 11, 83, 116, 208, 197, 14, 107, 166, 72, 18, 179, 8, 151, 76, 146, 74, 241, 126, 158, 206, 235, 59, 122, 215, 247, 76, 218, 11, 50, 130, 154, 137, 55, 232, 122, 222, 87, 63, 228, 64, 83, 63, 246, 198, 109, 61, 2, 181, 254, 227, 254, 199, 253, 32, 63, 21, 154, 64, 2, 16, 36, 166, 155, 201, 158, 248, 109, 111, 2, 106, 165, 176, 107, 117, 142, 29, 228, 73, 149, 146, 117, 186, 126, 31, 105, 147, 14, 130, 157, 177, 114, 160, 148, 101, 99, 160, 233, 242, 36, 243, 214, 138, 85, 201, 171, 65, 227, 253, 148, 72, 37, 60, 105, 187, 190, 45, 211, 150, 162, 59, 65, 109, 181, 27, 224, 71, 140, 182, 243, 218, 56, 233, 31, 222, 228, 98, 208, 4, 203, 138, 186, 182, 109, 154, 54, 120, 227, 245, 85, 175, 14, 238, 16, 249, 169, 156, 227, 73, 141, 35, 24, 187, 167, 86, 101, 217, 144, 218, 149, 19, 102, 218, 183, 93, 200, 87, 194, 217, 58, 238, 240, 219, 222, 4, 180, 43, 61, 173, 237, 208, 174, 228, 128, 123, 122, 103, 135, 118, 37, 231, 211, 81, 236, 242, 87, 119, 182, 142, 163, 93, 73, 155, 236, 101, 104, 33, 118, 165, 2, 80, 120, 49, 146, 102, 10, 146, 224, 15, 212, 218, 183, 60, 188, 32, 26, 208, 6, 169, 226, 137, 72, 66, 171, 214, 62, 7, 252, 1, 180, 12, 154, 116, 232, 113, 144, 214, 61, 87, 106, 32, 128, 135, 180, 158, 11, 5, 255, 139, 104, 118, 129, 75, 36, 158, 180, 69, 175, 146, 148, 189, 160, 214, 133, 41, 42, 64, 2, 204, 63, 136, 75, 47, 65, 145, 108, 39, 144, 71, 28, 154, 241, 33, 215, 159, 165, 200, 192, 150, 162, 196, 187, 62, 98, 75, 25, 60, 59, 136, 45, 197, 5, 91, 138, 142, 180, 207, 208, 138, 117, 183, 107, 41, 54, 60, 34, 89, 138, 11, 44, 19, 98, 150, 226, 66, 181, 255, 161, 244, 247, 165, 222, 132, 158, 206, 2, 236, 155, 40, 69, 197, 122, 3, 7, 127, 203, 65, 92, 157, 244, 201, 30, 28, 184, 231, 74, 8, 27, 90, 91, 91, 231, 232, 91, 54, 186, 20, 31, 40, 19, 250, 249, 142, 40, 172, 218, 82, 216, 91, 254, 142, 208, 138, 237, 124, 110, 19, 85, 15, 75, 83, 74, 228, 13, 90, 238, 162, 170, 49, 99, 156, 208, 3, 39, 189, 50, 14, 17, 235, 209, 186, 22, 3, 175, 41, 67, 11, 26, 247, 39, 93, 138, 12, 199, 158, 122, 168, 226, 6, 193, 92, 34, 108, 80, 225, 133, 37, 18, 197, 134, 86, 149, 236, 248, 182, 11, 121, 68, 145, 81, 209, 70, 27, 169, 122, 43, 181, 69, 30, 81, 140, 240, 136, 162, 130, 103, 133, 84, 190, 171, 165, 17, 70, 181, 157, 172, 226, 132, 4, 10, 64, 0, 129, 91, 69, 136, 86, 39, 125, 82, 80, 211, 40, 106, 26, 69, 198, 55, 164, 253, 124, 119, 93, 79, 53, 138, 144, 166, 173, 243, 233, 26, 127, 103, 131, 36, 49, 107, 254, 124, 110, 135, 166, 209, 166, 81, 124, 208, 102, 60, 54, 161, 166, 81, 100, 104, 187, 160, 166, 81, 92, 124, 227, 102, 20, 25, 154, 63, 41, 195, 40, 50, 26, 94, 72, 21, 25, 222, 123, 25, 164, 138, 140, 214, 253, 215, 118, 136, 31, 57, 64, 136, 55, 222, 95, 165, 200, 104, 218, 42, 197, 199, 251, 46, 244, 164, 166, 41, 35, 42, 17, 63, 114, 109, 148, 98, 131, 86, 39, 181, 151, 11, 105, 163, 20, 27, 116, 210, 47, 69, 80, 138, 12, 157, 244, 75, 138, 12, 174, 75, 146, 148, 157, 164, 200, 96, 6, 41, 46, 218, 226, 17, 148, 146, 34, 131, 105, 139, 226, 99, 177, 69, 209, 177, 216, 162, 168, 240, 84, 164, 226, 67, 21, 123, 60, 245, 29, 146, 148, 239, 46, 217, 35, 30, 79, 117, 142, 214, 199, 83, 177, 193, 121, 181, 148, 150, 235, 249, 158, 138, 14, 238, 9, 121, 211, 214, 83, 241, 241, 77, 27, 167, 162, 195, 191, 212, 169, 200, 208, 78, 74, 197, 5, 119, 220, 14, 57, 41, 21, 29, 234, 220, 168, 240, 178, 152, 31, 226, 157, 29, 137, 149, 137, 23, 90, 20, 55, 80, 247, 148, 42, 148, 150, 101, 123, 20, 58, 232, 239, 202, 246, 40, 100, 180, 50, 182, 40, 39, 10, 27, 109, 149, 74, 218, 174, 7, 71, 30, 121, 127, 10, 29, 188, 209, 120, 63, 251, 41, 100, 180, 74, 146, 58, 105, 1, 15, 179, 18, 34, 52, 90, 25, 168, 162, 203, 61, 133, 20, 238, 41, 84, 32, 73, 79, 161, 130, 186, 231, 21, 46, 32, 127, 133, 13, 175, 174, 80, 65, 33, 67, 27, 87, 56, 110, 133, 12, 143, 64, 192, 134, 187, 174, 103, 182, 21, 54, 144, 174, 181, 148, 108, 133, 14, 38, 142, 40, 217, 10, 27, 180, 105, 217, 10, 25, 90, 37, 41, 251, 81, 147, 173, 16, 210, 240, 98, 120, 228, 193, 35, 16, 90, 29, 200, 89, 3, 182, 139, 189, 131, 190, 217, 85, 148, 194, 35, 16, 76, 182, 194, 6, 37, 129, 83, 182, 194, 5, 83, 78, 161, 194, 51, 235, 120, 195, 63, 223, 145, 74, 94, 207, 220, 72, 85, 118, 211, 40, 172, 208, 225, 201, 238, 244, 223, 5, 233, 90, 30, 43, 124, 232, 254, 243, 239, 114, 254, 172, 240, 241, 219, 60, 43, 132, 40, 147, 254, 120, 80, 45, 93, 69, 27, 118, 37, 228, 64, 6, 159, 128, 158, 21, 50, 232, 90, 242, 31, 99, 139, 28, 183, 132, 227, 118, 254, 157, 174, 209, 216, 35, 172, 144, 241, 172, 80, 129, 50, 78, 75, 41, 104, 201, 197, 188, 48, 43, 140, 104, 253, 77, 10, 99, 165, 82, 155, 177, 66, 198, 195, 108, 63, 199, 88, 97, 131, 219, 50, 86, 200, 208, 140, 157, 166, 138, 50, 86, 8, 81, 247, 60, 99, 133, 14, 18, 207, 127, 160, 131, 4, 140, 21, 42, 68, 192, 88, 161, 130, 175, 126, 136, 177, 66, 134, 54, 201, 88, 33, 228, 209, 90, 177, 43, 183, 213, 17, 99, 133, 14, 218, 114, 185, 198, 178, 139, 177, 194, 7, 247, 5, 99, 133, 13, 201, 249, 31, 68, 107, 118, 37, 98, 172, 176, 161, 254, 150, 21, 50, 172, 203, 134, 21, 50, 248, 218, 127, 14, 109, 78, 168, 34, 177, 194, 70, 69, 98, 133, 10, 93, 73, 172, 176, 161, 146, 88, 161, 66, 107, 214, 21, 181, 102, 253, 37, 86, 232, 208, 120, 63, 27, 190, 41, 123, 137, 21, 62, 112, 117, 207, 39, 86, 200, 144, 52, 51, 232, 233, 108, 118, 76, 31, 105, 219, 254, 196, 10, 27, 36, 254, 196, 10, 25, 118, 37, 244, 48, 251, 240, 8, 4, 39, 117, 170, 39, 86, 8, 121, 199, 137, 21, 50, 168, 226, 6, 83, 197, 12, 178, 150, 145, 80, 103, 149, 36, 86, 232, 160, 144, 241, 149, 71, 222, 20, 46, 94, 37, 137, 29, 121, 83, 232, 112, 210, 43, 163, 237, 144, 55, 133, 20, 221, 58, 97, 228, 77, 33, 3, 213, 84, 146, 114, 65, 251, 235, 49, 213, 20, 50, 20, 83, 168, 96, 178, 20, 42, 104, 109, 205, 58, 98, 43, 81, 248, 152, 68, 20, 42, 72, 237, 113, 165, 77, 235, 100, 21, 50, 214, 37, 170, 108, 127, 50, 180, 10, 39, 124, 109, 50, 69, 186, 150, 68, 7, 249, 186, 32, 143, 64, 120, 88, 151, 72, 53, 39, 95, 133, 17, 155, 78, 66, 207, 247, 42, 116, 60, 15, 52, 35, 131, 31, 245, 42, 108, 232, 85, 248, 224, 71, 159, 75, 211, 86, 161, 132, 71, 32, 112, 107, 91, 133, 141, 119, 109, 86, 97, 67, 106, 143, 89, 245, 26, 203, 190, 46, 134, 89, 133, 139, 150, 108, 45, 117, 144, 46, 195, 172, 66, 135, 181, 36, 195, 172, 194, 134, 197, 222, 137, 192, 48, 171, 176, 225, 164, 79, 135, 24, 102, 21, 66, 48, 204, 42, 84, 104, 169, 236, 241, 163, 86, 161, 99, 213, 42, 71, 195, 43, 87, 225, 67, 211, 246, 87, 41, 250, 164, 189, 10, 61, 240, 8, 98, 217, 77, 174, 66, 135, 63, 150, 182, 150, 78, 66, 40, 225, 146, 33, 7, 119, 228, 42, 108, 172, 194, 0, 176, 236, 70, 225, 66, 50, 10, 21, 232, 86, 84, 225, 2, 50, 60, 169, 115, 136, 71, 144, 166, 157, 168, 66, 137, 70, 97, 69, 97, 103, 194, 211, 254, 130, 181, 246, 77, 64, 202, 180, 255, 191, 101, 131, 38, 170, 16, 66, 157, 148, 201, 35, 90, 58, 122, 124, 107, 154, 168, 194, 133, 190, 149, 19, 85, 184, 88, 235, 53, 52, 81, 133, 12, 183, 104, 162, 10, 25, 42, 252, 170, 112, 129, 98, 2, 87, 164, 147, 109, 112, 210, 227, 159, 248, 245, 240, 205, 110, 65, 90, 118, 61, 154, 182, 173, 85, 242, 13, 165, 107, 229, 123, 91, 136, 231, 26, 135, 174, 101, 30, 20, 94, 76, 235, 132, 27, 36, 125, 85, 232, 176, 54, 95, 21, 58, 88, 242, 85, 225, 66, 191, 171, 114, 229, 198, 7, 30, 129, 224, 170, 144, 65, 21, 54, 86, 190, 9, 170, 144, 225, 155, 29, 185, 115, 167, 10, 29, 141, 247, 65, 209, 157, 188, 239, 66, 58, 217, 207, 54, 0, 110, 135, 40, 148, 126, 38, 109, 167, 10, 29, 170, 253, 15, 142, 203, 110, 246, 181, 196, 131, 16, 202, 169, 114, 170, 144, 225, 84, 97, 163, 105, 139, 158, 95, 111, 170, 80, 226, 97, 86, 21, 55, 85, 216, 208, 135, 95, 182, 75, 21, 58, 120, 67, 63, 27, 93, 170, 144, 161, 159, 235, 33, 197, 174, 132, 150, 42, 92, 44, 85, 168, 96, 225, 213, 168, 66, 134, 230, 207, 214, 166, 141, 42, 124, 60, 70, 21, 42, 56, 163, 234, 115, 49, 170, 208, 241, 48, 140, 42, 92, 104, 20, 86, 74, 187, 85, 155, 193, 143, 90, 25, 85, 8, 241, 77, 155, 68, 173, 254, 212, 190, 183, 216, 181, 125, 129, 39, 41, 250, 217, 60, 168, 69, 40, 161, 77, 43, 107, 56, 158, 234, 211, 246, 71, 124, 179, 183, 250, 179, 178, 37, 82, 199, 201, 39, 252, 81, 9, 82, 150, 77, 27, 226, 143, 74, 144, 75, 214, 224, 146, 249, 46, 62, 109, 191, 35, 137, 9, 92, 145, 106, 51, 170, 233, 87, 30, 173, 170, 25, 61, 45, 81, 107, 0, 177, 242, 57, 214, 4, 101, 48, 204, 42, 228, 84, 21, 54, 220, 86, 56, 184, 173, 104, 175, 162, 129, 136, 80, 177, 68, 116, 190, 131, 42, 108, 108, 63, 10, 134, 217, 127, 168, 226, 164, 10, 7, 224, 17, 8, 138, 142, 213, 64, 144, 36, 85, 184, 120, 167, 217, 133, 32, 60, 2, 97, 37, 219, 164, 10, 29, 254, 168, 4, 61, 168, 82, 133, 139, 134, 23, 195, 31, 175, 183, 36, 131, 28, 154, 183, 168, 194, 5, 132, 72, 237, 241, 84, 133, 11, 151, 170, 80, 65, 210, 118, 169, 84, 133, 140, 111, 170, 218, 34, 85, 216, 104, 222, 162, 80, 65, 105, 167, 227, 17, 141, 107, 88, 19, 252, 97, 72, 23, 133, 16, 190, 40, 108, 40, 173, 120, 82, 33, 67, 250, 7, 225, 145, 39, 21, 46, 248, 218, 127, 74, 251, 33, 103, 137, 63, 21, 58, 224, 17, 253, 84, 200, 224, 17, 8, 250, 169, 28, 202, 246, 27, 20, 110, 12, 127, 82, 237, 98, 71, 189, 211, 121, 99, 34, 148, 182, 190, 167, 62, 212, 181, 173, 147, 32, 74, 125, 39, 218, 104, 87, 54, 77, 23, 138, 180, 175, 204, 249, 237, 7, 253, 84, 216, 176, 253, 242, 83, 225, 34, 2, 95, 208, 39, 93, 234, 11, 90, 85, 179, 54, 133, 251, 129, 123, 220, 13, 133, 91, 53, 16, 129, 47, 200, 1, 157, 236, 3, 173, 42, 213, 81, 90, 89, 230, 85, 196, 171, 8, 22, 240, 5, 61, 240, 198, 255, 143, 65, 4, 18, 190, 160, 215, 6, 33, 2, 95, 16, 3, 107, 45, 195, 255, 18, 53, 120, 109, 16, 28, 92, 178, 252, 84, 200, 240, 180, 253, 202, 80, 195, 202, 35, 8, 35, 124, 245, 107, 104, 243, 187, 99, 93, 162, 252, 84, 248, 176, 175, 129, 240, 169, 112, 241, 60, 21, 42, 120, 196, 241, 84, 200, 240, 48, 235, 120, 42, 92, 104, 197, 182, 58, 158, 10, 27, 239, 137, 28, 79, 133, 140, 78, 133, 13, 111, 90, 210, 82, 33, 163, 181, 105, 169, 144, 161, 173, 190, 227, 77, 39, 186, 208, 203, 86, 150, 10, 39, 60, 178, 144, 160, 16, 67, 187, 73, 133, 11, 46, 25, 106, 82, 225, 34, 130, 9, 20, 42, 88, 110, 220, 60, 65, 18, 20, 58, 28, 67, 51, 28, 148, 237, 187, 116, 210, 62, 146, 208, 149, 201, 145, 4, 133, 140, 71, 37, 45, 221, 245, 77, 112, 52, 218, 72, 130, 194, 134, 134, 62, 125, 19, 84, 251, 85, 190, 9, 72, 130, 194, 71, 227, 230, 89, 236, 144, 4, 133, 14, 231, 211, 125, 46, 118, 18, 148, 163, 237, 82, 81, 7, 73, 80, 216, 248, 100, 13, 77, 205, 78, 122, 101, 144, 4, 133, 12, 245, 84, 36, 65, 225, 98, 226, 206, 247, 201, 246, 114, 169, 66, 18, 20, 62, 250, 241, 55, 135, 55, 222, 79, 26, 225, 118, 72, 127, 157, 29, 115, 208, 188, 24, 73, 80, 216, 160, 154, 157, 132, 36, 40, 100, 248, 86, 18, 73, 80, 184, 208, 181, 36, 170, 72, 248, 80, 145, 240, 129, 186, 76, 206, 255, 84, 154, 124, 136, 36, 229, 242, 126, 73, 219, 215, 68, 243, 195, 146, 239, 14, 124, 127, 189, 6, 78, 226, 6, 250, 165, 243, 218, 100, 17, 43, 19, 47, 7, 222, 53, 242, 236, 184, 203, 230, 72, 155, 100, 203, 192, 105, 55, 74, 37, 222, 147, 65, 107, 147, 137, 208, 181, 140, 74, 94, 141, 66, 225, 224, 16, 90, 121, 252, 170, 151, 168, 1, 23, 186, 210, 147, 112, 1, 210, 200, 65, 125, 41, 9, 23, 218, 46, 200, 45, 74, 73, 216, 200, 78, 72, 43, 182, 161, 233, 228, 155, 78, 32, 154, 90, 190, 183, 69, 140, 223, 95, 194, 5, 30, 65, 20, 128, 64, 2, 9, 34, 232, 164, 95, 194, 6, 165, 75, 181, 116, 218, 67, 207, 154, 64, 191, 13, 122, 9, 143, 64, 104, 125, 10, 143, 64, 120, 120, 106, 218, 253, 18, 58, 124, 235, 36, 190, 14, 226, 155, 23, 236, 146, 72, 21, 55, 100, 168, 226, 134, 10, 146, 196, 13, 21, 212, 73, 153, 184, 225, 130, 27, 54, 184, 33, 67, 61, 19, 226, 134, 11, 213, 126, 214, 144, 193, 35, 16, 252, 81, 73, 67, 134, 250, 54, 84, 72, 198, 182, 225, 2, 58, 150, 54, 219, 112, 225, 233, 71, 154, 182, 33, 99, 217, 134, 10, 154, 74, 248, 17, 4, 131, 69, 41, 173, 43, 223, 247, 78, 40, 156, 244, 75, 241, 215, 224, 254, 29, 180, 191, 158, 0, 14, 208, 239, 201, 175, 110, 251, 29, 85, 154, 8, 187, 36, 237, 242, 215, 165, 18, 159, 88, 107, 223, 132, 111, 219, 234, 214, 53, 188, 241, 186, 47, 107, 117, 185, 124, 82, 166, 213, 53, 94, 70, 20, 118, 235, 80, 171, 106, 102, 251, 232, 23, 72, 156, 1, 209, 253, 95, 139, 59, 57, 106, 19, 13, 91, 231, 184, 19, 170, 52, 13, 35, 60, 242, 78, 179, 203, 105, 214, 65, 149, 166, 225, 3, 163, 28, 106, 0, 105, 20, 86, 223, 30, 75, 232, 74, 216, 209, 188, 24, 162, 121, 113, 131, 187, 117, 26, 168, 210, 52, 124, 248, 91, 174, 210, 52, 116, 208, 138, 117, 254, 151, 168, 210, 52, 132, 52, 13, 27, 149, 110, 26, 54, 144, 112, 55, 13, 23, 62, 165, 118, 211, 112, 209, 52, 124, 52, 13, 31, 18, 52, 13, 29, 173, 20, 221, 9, 242, 8, 163, 28, 138, 208, 180, 125, 220, 80, 132, 166, 161, 195, 130, 92, 175, 201, 86, 18, 79, 22, 225, 186, 18, 126, 180, 190, 46, 199, 184, 1, 69, 104, 26, 58, 188, 105, 208, 133, 140, 166, 225, 35, 157, 244, 46, 147, 54, 106, 26, 66, 36, 105, 29, 106, 26, 66, 184, 36, 49, 67, 77, 67, 134, 54, 201, 158, 138, 92, 168, 105, 24, 209, 52, 164, 104, 26, 62, 188, 174, 245, 22, 212, 52, 212, 52, 108, 64, 137, 134, 215, 130, 148, 169, 243, 40, 59, 251, 43, 218, 144, 193, 155, 54, 14, 173, 207, 164, 243, 75, 129, 240, 181, 142, 129, 126, 46, 111, 217, 14, 174, 76, 157, 127, 184, 100, 250, 235, 218, 47, 169, 79, 184, 254, 227, 150, 208, 181, 100, 229, 41, 237, 164, 8, 192, 93, 23, 82, 223, 18, 185, 210, 138, 101, 251, 142, 23, 104, 219, 197, 48, 92, 209, 246, 240, 111, 204, 56, 92, 87, 194, 16, 231, 95, 99, 245, 67, 250, 111, 177, 47, 137, 190, 162, 13, 35, 190, 162, 13, 21, 252, 145, 186, 231, 41, 218, 208, 145, 253, 21, 93, 236, 29, 244, 160, 37, 163, 104, 67, 134, 213, 15, 233, 115, 95, 138, 71, 144, 106, 203, 32, 138, 54, 68, 209, 134, 12, 119, 109, 165, 18, 73, 154, 41, 175, 13, 31, 82, 191, 54, 92, 144, 120, 178, 215, 134, 11, 6, 218, 168, 165, 32, 103, 15, 236, 74, 232, 97, 118, 189, 54, 124, 188, 54, 84, 192, 154, 213, 61, 223, 192, 35, 109, 2, 16, 44, 242, 136, 46, 246, 119, 109, 232, 240, 60, 40, 125, 215, 134, 20, 141, 223, 219, 82, 32, 173, 255, 248, 209, 116, 146, 141, 222, 181, 161, 227, 59, 21, 98, 90, 27, 54, 18, 43, 214, 22, 177, 120, 65, 13, 68, 34, 96, 109, 8, 97, 109, 248, 96, 109, 216, 64, 135, 46, 165, 218, 144, 97, 229, 155, 160, 159, 218, 176, 49, 241, 132, 62, 181, 33, 227, 209, 213, 169, 13, 23, 105, 21, 111, 171, 99, 240, 174, 157, 218, 176, 161, 177, 71, 84, 67, 6, 215, 73, 249, 102, 135, 44, 240, 56, 46, 187, 97, 198, 14, 146, 120, 178, 59, 85, 195, 133, 75, 13, 21, 40, 203, 150, 26, 50, 112, 179, 147, 108, 200, 160, 191, 11, 81, 104, 63, 224, 194, 23, 171, 95, 123, 14, 7, 213, 187, 232, 91, 11, 106, 150, 112, 210, 43, 131, 16, 199, 179, 161, 130, 115, 167, 100, 104, 93, 54, 124, 168, 181, 111, 65, 235, 178, 237, 59, 90, 91, 91, 231, 13, 142, 221, 39, 83, 203, 45, 27, 50, 44, 246, 14, 195, 191, 233, 68, 189, 121, 113, 114, 104, 227, 149, 252, 45, 156, 212, 116, 225, 150, 13, 27, 223, 234, 158, 220, 178, 161, 99, 121, 190, 60, 135, 184, 101, 75, 109, 224, 158, 16, 183, 108, 200, 224, 150, 13, 21, 90, 54, 84, 208, 204, 178, 225, 2, 194, 8, 164, 148, 101, 67, 137, 181, 18, 41, 203, 134, 13, 42, 25, 201, 134, 11, 174, 109, 151, 74, 109, 212, 240, 193, 178, 27, 212, 144, 161, 33, 0, 42, 24, 6, 32, 2, 195, 154, 224, 15, 195, 133, 135, 217, 138, 254, 99, 200, 96, 235, 56, 90, 240, 128, 3, 111, 218, 190, 235, 63, 96, 235, 120, 107, 139, 8, 240, 24, 70, 60, 134, 16, 239, 143, 161, 130, 71, 36, 52, 56, 158, 78, 63, 214, 143, 161, 163, 85, 151, 0, 212, 173, 251, 100, 13, 39, 125, 50, 151, 140, 161, 109, 151, 55, 45, 215, 2, 77, 91, 137, 71, 82, 197, 201, 155, 66, 239, 153, 26, 212, 115, 217, 126, 136, 31, 181, 82, 9, 209, 74, 229, 126, 167, 67, 251, 235, 49, 116, 72, 112, 212, 250, 235, 49, 108, 56, 110, 135, 36, 143, 33, 3, 237, 99, 232, 240, 12, 27, 20, 103, 168, 96, 77, 112, 103, 184, 192, 239, 158, 78, 237, 12, 27, 45, 1, 67, 127, 23, 196, 35, 72, 146, 210, 53, 206, 208, 241, 174, 141, 156, 225, 194, 25, 54, 148, 45, 107, 134, 12, 218, 156, 16, 107, 134, 140, 109, 134, 10, 36, 43, 41, 200, 169, 66, 205, 240, 241, 63, 129, 225, 195, 255, 4, 134, 10, 19, 24, 50, 188, 57, 134, 10, 142, 161, 35, 173, 231, 226, 24, 46, 224, 194, 121, 43, 204, 144, 225, 31, 51, 84, 208, 166, 165, 147, 144, 250, 99, 134, 144, 111, 203, 14, 125, 123, 204, 240, 177, 15, 53, 222, 103, 134, 13, 183, 142, 183, 58, 207, 12, 29, 239, 204, 80, 193, 35, 16, 124, 187, 153, 97, 131, 177, 114, 92, 51, 123, 180, 108, 154, 25, 50, 248, 174, 108, 102, 184, 80, 17, 144, 54, 126, 110, 204, 176, 193, 147, 75, 21, 114, 204, 11, 161, 131, 8, 19, 164, 159, 109, 155, 116, 20, 77, 227, 6, 173, 88, 117, 254, 228, 197, 56, 232, 235, 82, 133, 30, 212, 153, 225, 145, 237, 247, 208, 230, 132, 22, 64, 203, 162, 86, 63, 212, 26, 59, 208, 120, 209, 55, 221, 149, 208, 131, 101, 225, 120, 170, 6, 55, 141, 25, 66, 30, 109, 204, 112, 97, 177, 124, 16, 167, 108, 140, 7, 245, 78, 39, 177, 109, 217, 195, 35, 2, 96, 224, 79, 42, 68, 225, 174, 109, 253, 45, 232, 225, 155, 50, 102, 200, 208, 186, 94, 210, 197, 12, 27, 139, 25, 62, 180, 199, 12, 122, 110, 12, 20, 75, 228, 158, 132, 121, 81, 68, 48, 232, 202, 228, 157, 235, 41, 102, 8, 65, 15, 179, 138, 25, 62, 116, 197, 182, 110, 240, 82, 197, 12, 25, 22, 171, 32, 135, 46, 165, 152, 33, 131, 63, 104, 169, 43, 41, 102, 200, 88, 148, 130, 82, 49, 67, 6, 238, 223, 35, 146, 196, 12, 25, 158, 214, 197, 164, 41, 82, 170, 159, 170, 173, 122, 119, 184, 54, 67, 162, 162, 77, 226, 18, 74, 164, 177, 128, 66, 130, 35, 167, 60, 144, 36, 49, 67, 137, 230, 6, 34, 148, 177, 43, 69, 146, 210, 53, 26, 220, 233, 251, 195, 73, 159, 148, 119, 154, 109, 16, 173, 223, 236, 38, 40, 188, 173, 164, 105, 132, 65, 58, 137, 25, 46, 240, 181, 255, 84, 251, 81, 101, 97, 54, 81, 89, 152, 161, 163, 178, 48, 67, 133, 134, 192, 223, 67, 218, 9, 197, 218, 133, 25, 54, 188, 99, 3, 45, 138, 49, 100, 40, 198, 208, 241, 172, 197, 80, 161, 109, 37, 25, 122, 178, 59, 209, 98, 8, 233, 85, 142, 189, 132, 33, 195, 49, 125, 9, 67, 6, 9, 67, 71, 132, 69, 83, 231, 31, 18, 232, 208, 168, 139, 48, 132, 56, 124, 69, 95, 215, 146, 159, 210, 249, 99, 229, 38, 246, 79, 169, 77, 201, 70, 139, 69, 24, 196, 98, 21, 244, 208, 185, 76, 178, 65, 180, 73, 182, 220, 13, 93, 191, 175, 235, 121, 127, 136, 80, 169, 206, 131, 111, 204, 32, 149, 234, 168, 84, 135, 66, 165, 58, 238, 113, 55, 142, 86, 181, 147, 85, 16, 109, 78, 236, 45, 78, 187, 26, 26, 255, 191, 199, 163, 120, 105, 52, 94, 213, 156, 82, 162, 197, 34, 12, 29, 22, 139, 48, 84, 112, 126, 41, 149, 101, 184, 224, 175, 44, 195, 5, 136, 165, 45, 93, 101, 25, 50, 148, 85, 150, 225, 194, 86, 150, 161, 99, 157, 91, 247, 117, 95, 134, 15, 143, 32, 163, 161, 180, 98, 61, 194, 241, 183, 156, 244, 202, 56, 32, 8, 247, 116, 90, 177, 140, 161, 244, 125, 153, 47, 196, 251, 178, 109, 203, 88, 227, 181, 121, 152, 109, 203, 144, 97, 251, 53, 147, 8, 109, 126, 72, 175, 197, 182, 101, 232, 128, 155, 150, 218, 50, 100, 248, 166, 170, 45, 106, 203, 208, 193, 22, 109, 25, 66, 156, 217, 50, 92, 104, 12, 74, 203, 150, 33, 163, 149, 45, 83, 182, 12, 27, 203, 46, 101, 203, 112, 225, 108, 25, 115, 40, 91, 134, 15, 101, 203, 80, 97, 215, 90, 134, 143, 182, 75, 54, 106, 93, 107, 25, 66, 112, 241, 40, 181, 12, 23, 148, 237, 55, 180, 98, 87, 254, 186, 76, 232, 25, 118, 87, 141, 147, 58, 196, 73, 239, 112, 117, 207, 83, 44, 207, 183, 31, 66, 107, 255, 173, 149, 104, 129, 214, 101, 144, 1, 148, 101, 67, 173, 57, 61, 208, 116, 129, 54, 106, 101, 175, 33, 85, 156, 84, 65, 224, 4, 120, 152, 117, 207, 165, 84, 210, 30, 3, 93, 191, 223, 64, 83, 58, 215, 37, 87, 227, 253, 108, 251, 174, 10, 85, 60, 194, 64, 1, 156, 79, 198, 244, 87, 226, 9, 104, 8, 160, 107, 201, 119, 192, 105, 30, 183, 243, 42, 97, 26, 4, 12, 96, 33, 73, 219, 197, 255, 18, 45, 128, 49, 77, 131, 214, 255, 100, 223, 184, 5, 208, 128, 164, 147, 117, 178, 7, 254, 16, 190, 233, 68, 85, 23, 187, 18, 99, 117, 66, 15, 15, 111, 58, 27, 173, 159, 180, 155, 147, 58, 119, 192, 157, 150, 97, 195, 118, 90, 134, 11, 156, 150, 33, 196, 245, 51, 237, 226, 133, 61, 17, 16, 202, 246, 219, 107, 104, 238, 63, 128, 0, 38, 158, 10, 183, 67, 189, 64, 155, 29, 143, 74, 208, 131, 58, 41, 211, 67, 2, 6, 104, 109, 192, 253, 202, 144, 226, 53, 101, 190, 175, 12, 27, 248, 224, 188, 50, 231, 149, 33, 195, 193, 151, 247, 205, 250, 202, 48, 98, 66, 215, 239, 107, 147, 108, 35, 26, 111, 131, 124, 101, 248, 112, 126, 153, 175, 124, 101, 232, 64, 10, 110, 101, 184, 160, 218, 143, 114, 211, 246, 167, 163, 88, 118, 33, 93, 146, 86, 134, 14, 39, 159, 109, 101, 184, 248, 102, 143, 192, 160, 111, 229, 196, 85, 251, 31, 46, 25, 130, 112, 70, 219, 225, 141, 61, 178, 64, 211, 128, 248, 3, 215, 237, 108, 188, 159, 86, 194, 93, 89, 54, 228, 158, 43, 69, 52, 109, 157, 127, 90, 34, 7, 109, 90, 25, 46, 80, 88, 25, 42, 100, 5, 171, 166, 12, 23, 218, 252, 254, 180, 59, 180, 54, 233, 208, 211, 206, 160, 167, 27, 105, 153, 237, 212, 30, 79, 136, 54, 126, 96, 132, 71, 152, 50, 92, 208, 138, 101, 202, 144, 129, 31, 185, 100, 136, 145, 51, 76, 25, 22, 192, 25, 166, 140, 41, 195, 69, 131, 51, 136, 41, 67, 6, 198, 92, 129, 216, 99, 111, 149, 50, 108, 120, 90, 223, 4, 71, 171, 123, 254, 129, 144, 111, 169, 12, 23, 154, 98, 216, 224, 138, 97, 163, 41, 134, 13, 62, 165, 54, 90, 197, 176, 177, 15, 161, 85, 12, 31, 18, 63, 5, 63, 115, 69, 30, 169, 40, 70, 81, 241, 8, 170, 40, 86, 81, 236, 65, 69, 49, 231, 147, 61, 68, 232, 90, 242, 25, 211, 160, 181, 255, 24, 211, 56, 32, 198, 237, 0, 169, 150, 72, 181, 101, 24, 240, 198, 203, 224, 83, 238, 63, 9, 119, 92, 118, 147, 157, 14, 105, 133, 234, 155, 224, 14, 40, 242, 83, 249, 46, 164, 84, 162, 247, 84, 12, 27, 164, 253, 100, 136, 145, 4, 138, 225, 163, 162, 14, 146, 64, 49, 92, 232, 90, 6, 73, 160, 24, 50, 92, 145, 4, 138, 33, 3, 243, 164, 160, 149, 216, 123, 37, 25, 66, 168, 69, 91, 86, 73, 134, 12, 30, 113, 62, 25, 164, 146, 145, 213, 64, 112, 62, 25, 46, 30, 20, 205, 159, 207, 249, 100, 248, 192, 61, 33, 173, 216, 125, 104, 223, 196, 62, 111, 156, 214, 65, 127, 23, 197, 55, 102, 26, 167, 69, 15, 120, 227, 180, 168, 53, 235, 170, 22, 44, 74, 65, 222, 192, 155, 23, 167, 135, 92, 165, 12, 162, 117, 225, 84, 169, 206, 164, 214, 82, 208, 234, 39, 145, 171, 148, 57, 62, 145, 241, 116, 99, 79, 134, 36, 158, 12, 27, 43, 29, 239, 100, 184, 120, 22, 30, 127, 203, 161, 86, 212, 186, 64, 169, 82, 73, 39, 67, 6, 93, 175, 31, 236, 74, 8, 226, 219, 174, 4, 169, 120, 4, 241, 63, 52, 144, 180, 93, 13, 232, 151, 130, 97, 164, 107, 201, 119, 192, 37, 109, 25, 240, 167, 34, 151, 131, 138, 126, 74, 244, 185, 61, 51, 67, 241, 219, 255, 137, 126, 155, 157, 212, 18, 73, 252, 62, 38, 28, 194, 35, 26, 133, 149, 164, 147, 33, 195, 35, 16, 36, 157, 12, 23, 139, 202, 237, 100, 149, 166, 173, 147, 58, 25, 50, 112, 126, 153, 183, 58, 151, 12, 31, 171, 65, 21, 94, 26, 191, 190, 147, 206, 181, 108, 29, 181, 67, 100, 97, 228, 146, 225, 226, 151, 237, 66, 46, 153, 75, 134, 142, 182, 146, 108, 201, 180, 201, 150, 12, 31, 206, 51, 99, 201, 112, 193, 157, 146, 37, 195, 197, 174, 100, 168, 160, 76, 58, 114, 137, 6, 119, 220, 14, 162, 87, 33, 149, 36, 147, 36, 67, 7, 71, 146, 161, 194, 239, 114, 142, 13, 143, 160, 246, 154, 181, 201, 208, 1, 145, 48, 65, 235, 18, 57, 182, 78, 90, 155, 12, 33, 220, 115, 236, 80, 90, 102, 91, 147, 12, 29, 182, 31, 122, 109, 77, 50, 116, 236, 74, 219, 36, 75, 219, 36, 195, 198, 51, 38, 25, 46, 32, 231, 148, 12, 27, 124, 245, 67, 146, 148, 14, 66, 179, 173, 147, 146, 77, 224, 73, 96, 232, 152, 192, 147, 192, 240, 209, 250, 109, 23, 122, 18, 24, 58, 38, 192, 252, 171, 88, 200, 144, 172, 98, 161, 66, 197, 194, 70, 165, 61, 11, 21, 210, 126, 190, 35, 157, 35, 167, 44, 92, 232, 111, 83, 180, 145, 83, 22, 62, 42, 170, 218, 34, 167, 80, 22, 66, 156, 178, 80, 65, 227, 202, 91, 184, 144, 201, 223, 194, 133, 110, 111, 161, 130, 4, 19, 151, 36, 246, 10, 176, 183, 240, 225, 159, 239, 168, 2, 236, 45, 140, 168, 0, 123, 11, 29, 149, 95, 168, 224, 155, 95, 168, 224, 146, 85, 96, 161, 79, 223, 132, 70, 175, 23, 66, 94, 47, 108, 188, 211, 236, 162, 240, 66, 134, 103, 114, 128, 64, 199, 162, 40, 188, 112, 225, 241, 66, 7, 52, 241, 41, 181, 33, 206, 139, 116, 255, 121, 161, 195, 117, 185, 243, 194, 6, 199, 11, 21, 56, 206, 193, 33, 190, 45, 59, 214, 6, 169, 123, 62, 63, 213, 106, 154, 23, 65, 15, 22, 197, 77, 99, 182, 64, 75, 124, 179, 115, 79, 135, 24, 120, 230, 5, 53, 72, 82, 46, 142, 121, 33, 3, 23, 40, 203, 133, 142, 230, 112, 164, 105, 188, 144, 33, 121, 215, 166, 241, 66, 135, 127, 220, 120, 33, 164, 105, 188, 80, 161, 162, 139, 23, 46, 120, 100, 37, 188, 144, 97, 37, 188, 80, 129, 1, 82, 218, 188, 56, 33, 7, 150, 78, 116, 53, 188, 144, 241, 52, 13, 47, 92, 104, 26, 94, 248, 192, 154, 25, 198, 52, 188, 26, 94, 184, 120, 94, 217, 240, 194, 133, 71, 34, 104, 120, 33, 195, 130, 134, 151, 68, 195, 11, 29, 13, 47, 84, 224, 220, 115, 238, 213, 219, 115, 126, 41, 19, 182, 189, 213, 55, 97, 219, 155, 176, 237, 53, 76, 216, 246, 156, 95, 74, 227, 109, 15, 226, 89, 26, 111, 123, 141, 183, 189, 135, 239, 124, 103, 219, 175, 108, 251, 29, 216, 246, 59, 247, 184, 109, 46, 199, 79, 193, 239, 192, 207, 239, 224, 202, 246, 215, 63, 183, 243, 203, 55, 59, 99, 87, 73, 54, 215, 138, 245, 87, 87, 95, 142, 165, 175, 231, 239, 218, 234, 116, 194, 172, 55, 29, 228, 59, 223, 85, 186, 166, 42, 157, 74, 231, 120, 150, 86, 213, 156, 26, 63, 203, 47, 227, 95, 198, 20, 110, 14, 199, 220, 180, 98, 153, 41, 115, 120, 150, 74, 178, 74, 50, 135, 75, 6, 225, 143, 74, 92, 178, 247, 94, 166, 149, 194, 43, 66, 61, 83, 43, 133, 151, 231, 122, 84, 242, 120, 150, 71, 37, 234, 153, 150, 92, 84, 2, 209, 222, 195, 90, 175, 173, 109, 16, 39, 125, 178, 166, 233, 242, 8, 253, 109, 203, 248, 82, 36, 86, 190, 9, 207, 59, 166, 238, 160, 159, 202, 221, 215, 37, 83, 9, 79, 218, 66, 124, 235, 164, 173, 131, 191, 74, 117, 30, 109, 61, 29, 79, 199, 209, 244, 63, 23, 227, 240, 158, 75, 181, 101, 84, 162, 218, 50, 14, 204, 67, 39, 234, 42, 218, 104, 55, 56, 191, 20, 87, 77, 157, 95, 202, 146, 154, 122, 191, 69, 213, 195, 211, 253, 18, 167, 7, 87, 207, 180, 148, 101, 130, 0, 161, 218, 46, 16, 62, 33, 56, 191, 20, 231, 151, 210, 246, 233, 116, 56, 56, 159, 142, 255, 53, 237, 106, 240, 71, 37, 108, 29, 87, 109, 151, 87, 103, 86, 237, 53, 240, 86, 181, 216, 209, 201, 126, 54, 109, 136, 39, 187, 83, 61, 117, 105, 235, 231, 98, 215, 250, 109, 181, 23, 60, 217, 157, 175, 77, 167, 147, 58, 157, 180, 216, 59, 175, 222, 170, 218, 99, 199, 184, 25, 56, 41, 19, 133, 36, 229, 178, 86, 38, 213, 214, 245, 173, 156, 72, 76, 216, 246, 92, 41, 156, 244, 201, 86, 190, 9, 234, 19, 175, 77, 243, 131, 122, 46, 21, 85, 109, 85, 91, 70, 61, 23, 136, 9, 39, 165, 62, 142, 211, 147, 221, 89, 1, 8, 17, 15, 181, 174, 168, 225, 120, 42, 94, 200, 208, 146, 196, 75, 146, 120, 33, 67, 146, 120, 161, 194, 63, 154, 182, 21, 27, 161, 77, 54, 51, 143, 141, 48, 83, 164, 139, 82, 32, 150, 251, 67, 35, 113, 104, 101, 226, 118, 79, 106, 88, 153, 120, 33, 196, 35, 12, 238, 9, 130, 183, 122, 4, 66, 163, 181, 181, 181, 215, 13, 206, 160, 86, 199, 83, 37, 47, 92, 248, 131, 174, 117, 120, 64, 92, 226, 249, 16, 205, 50, 17, 20, 13, 147, 144, 74, 94, 200, 88, 20, 91, 232, 240, 166, 173, 106, 187, 32, 54, 89, 11, 27, 84, 242, 210, 214, 201, 46, 116, 80, 252, 239, 194, 134, 86, 73, 90, 167, 191, 11, 29, 173, 191, 11, 31, 187, 144, 177, 80, 129, 126, 42, 247, 90, 91, 85, 211, 70, 223, 116, 173, 47, 248, 231, 218, 162, 71, 47, 60, 204, 54, 117, 34, 16, 222, 119, 225, 194, 125, 23, 66, 48, 253, 69, 239, 106, 223, 132, 93, 248, 96, 166, 45, 98, 205, 204, 231, 114, 168, 242, 218, 46, 156, 80, 218, 228, 59, 250, 246, 48, 43, 105, 187, 208, 129, 36, 109, 23, 70, 248, 251, 190, 166, 237, 66, 137, 138, 5, 73, 106, 47, 205, 159, 116, 130, 58, 181, 237, 66, 10, 109, 219, 118, 53, 246, 22, 39, 8, 109, 187, 208, 179, 32, 109, 187, 16, 210, 186, 104, 148, 116, 254, 248, 69, 191, 108, 23, 50, 214, 46, 108, 168, 125, 15, 173, 93, 200, 216, 215, 160, 181, 11, 23, 18, 110, 29, 111, 180, 54, 13, 47, 164, 254, 216, 81, 127, 236, 56, 158, 214, 181, 178, 129, 180, 70, 68, 168, 240, 210, 120, 24, 122, 167, 217, 133, 16, 238, 61, 179, 11, 25, 24, 134, 159, 217, 133, 11, 221, 133, 10, 24, 168, 55, 77, 187, 240, 33, 66, 195, 35, 139, 85, 150, 93, 200, 112, 249, 203, 46, 100, 112, 9, 246, 86, 177, 47, 187, 176, 49, 193, 45, 187, 26, 120, 4, 97, 68, 3, 115, 109, 217, 133, 144, 101, 23, 54, 212, 113, 114, 180, 50, 109, 69, 23, 54, 180, 46, 116, 84, 116, 161, 130, 54, 209, 133, 10, 232, 87, 41, 122, 208, 86, 223, 241, 246, 116, 54, 70, 115, 111, 209, 40, 172, 36, 220, 225, 157, 37, 170, 142, 118, 112, 217, 20, 212, 120, 223, 113, 4, 83, 214, 168, 44, 243, 240, 153, 218, 130, 164, 138, 19, 122, 44, 157, 232, 194, 133, 5, 12, 36, 216, 215, 212, 49, 68, 23, 62, 220, 117, 161, 66, 182, 214, 133, 11, 237, 165, 147, 180, 157, 46, 116, 168, 116, 232, 25, 93, 200, 144, 168, 46, 84, 104, 125, 127, 43, 181, 74, 58, 217, 130, 111, 249, 143, 65, 104, 125, 114, 169, 237, 6, 194, 3, 172, 77, 4, 37, 91, 33, 132, 168, 212, 133, 10, 30, 113, 169, 22, 50, 184, 84, 43, 45, 116, 88, 207, 181, 37, 38, 156, 22, 58, 212, 166, 133, 10, 174, 21, 235, 13, 119, 109, 30, 129, 176, 88, 57, 153, 52, 125, 86, 104, 193, 4, 117, 70, 69, 46, 116, 60, 21, 185, 80, 161, 129, 198, 140, 73, 148, 163, 200, 2, 207, 146, 116, 42, 116, 160, 21, 219, 246, 115, 33, 163, 245, 115, 225, 66, 123, 205, 208, 182, 11, 98, 173, 76, 232, 61, 23, 58, 112, 104, 242, 151, 231, 8, 29, 120, 211, 114, 225, 130, 51, 140, 134, 23, 58, 30, 9, 66, 30, 9, 6, 96, 35, 204, 184, 159, 4, 25, 173, 235, 73, 112, 65, 159, 4, 21, 184, 190, 182, 214, 229, 73, 48, 162, 242, 18, 108, 168, 125, 46, 193, 5, 119, 9, 42, 104, 171, 239, 94, 75, 144, 145, 147, 198, 18, 92, 104, 217, 88, 130, 11, 239, 219, 188, 88, 130, 12, 207, 149, 44, 65, 134, 207, 118, 240, 230, 132, 34, 36, 75, 176, 241, 112, 138, 238, 196, 73, 153, 16, 196, 67, 53, 245, 118, 232, 63, 238, 134, 54, 45, 123, 2, 67, 243, 156, 170, 237, 214, 9, 163, 100, 9, 66, 50, 249, 107, 80, 34, 139, 198, 30, 241, 8, 242, 125, 180, 9, 64, 176, 76, 123, 98, 120, 115, 250, 214, 201, 107, 179, 170, 77, 168, 51, 75, 240, 241, 40, 126, 20, 51, 22, 36, 75, 208, 33, 89, 130, 10, 156, 234, 114, 196, 18, 100, 40, 85, 170, 142, 116, 49, 204, 115, 28, 57, 255, 154, 4, 41, 42, 220, 36, 168, 224, 186, 77, 130, 20, 255, 88, 146, 184, 73, 144, 145, 141, 151, 4, 23, 250, 233, 146, 224, 130, 182, 101, 152, 68, 106, 73, 208, 145, 218, 227, 136, 146, 75, 130, 13, 134, 145, 106, 43, 65, 107, 173, 4, 27, 201, 184, 29, 9, 50, 32, 109, 178, 249, 93, 31, 83, 134, 38, 143, 74, 176, 97, 242, 168, 4, 21, 188, 55, 221, 147, 110, 209, 220, 189, 143, 74, 176, 225, 182, 155, 71, 37, 184, 240, 8, 82, 197, 233, 81, 9, 58, 36, 201, 43, 61, 42, 65, 8, 196, 184, 25, 213, 201, 203, 163, 18, 84, 128, 78, 250, 37, 69, 234, 185, 60, 42, 65, 7, 138, 74, 80, 129, 115, 235, 210, 42, 65, 70, 47, 163, 18, 167, 42, 209, 201, 174, 68, 78, 85, 130, 16, 168, 169, 74, 240, 49, 129, 4, 17, 26, 146, 76, 73, 37, 200, 128, 14, 111, 78, 18, 92, 112, 141, 91, 36, 184, 80, 209, 79, 9, 46, 56, 56, 47, 173, 19, 69, 186, 188, 9, 157, 141, 157, 219, 122, 227, 182, 143, 5, 233, 165, 83, 108, 39, 147, 190, 192, 189, 181, 184, 127, 219, 155, 240, 104, 154, 3, 233, 36, 102, 143, 134, 141, 208, 181, 76, 3, 143, 68, 88, 96, 98, 210, 148, 129, 15, 224, 193, 174, 134, 50, 238, 5, 155, 78, 114, 48, 240, 48, 11, 209, 188, 116, 201, 5, 37, 235, 148, 160, 3, 166, 218, 41, 65, 6, 93, 207, 93, 74, 112, 209, 250, 190, 202, 214, 82, 151, 18, 108, 240, 72, 74, 152, 148, 188, 5, 73, 144, 81, 137, 32, 67, 151, 99, 235, 250, 148, 8, 58, 152, 58, 196, 143, 36, 148, 8, 58, 94, 253, 181, 65, 202, 164, 55, 168, 103, 66, 75, 46, 42, 105, 240, 120, 150, 115, 137, 30, 24, 102, 27, 191, 16, 191, 232, 233, 126, 9, 189, 191, 8, 54, 244, 139, 224, 194, 195, 109, 133, 71, 112, 161, 105, 235, 168, 226, 17, 108, 248, 156, 120, 4, 23, 38, 30, 65, 5, 219, 207, 35, 184, 80, 241, 30, 65, 5, 151, 172, 161, 218, 223, 224, 17, 124, 44, 86, 225, 247, 8, 46, 60, 130, 16, 159, 239, 17, 92, 112, 143, 32, 164, 155, 85, 211, 201, 2, 19, 186, 150, 28, 192, 122, 253, 192, 55, 59, 114, 199, 237, 148, 190, 107, 139, 144, 208, 143, 136, 104, 32, 177, 64, 63, 23, 251, 234, 187, 71, 208, 241, 219, 30, 114, 143, 32, 196, 67, 89, 38, 228, 30, 65, 7, 247, 8, 42, 80, 154, 158, 210, 70, 17, 173, 89, 79, 111, 143, 96, 163, 129, 185, 198, 96, 206, 35, 248, 96, 148, 243, 8, 46, 56, 159, 206, 35, 139, 82, 126, 23, 74, 174, 55, 157, 182, 227, 225, 188, 122, 167, 115, 141, 61, 130, 11, 138, 89, 219, 133, 190, 237, 114, 248, 182, 75, 219, 54, 212, 74, 219, 216, 35, 200, 224, 112, 210, 167, 99, 152, 85, 144, 180, 88, 62, 141, 197, 124, 85, 34, 48, 30, 156, 227, 34, 76, 60, 69, 96, 60, 204, 126, 179, 67, 88, 44, 31, 244, 48, 233, 154, 182, 165, 125, 95, 85, 168, 129, 135, 87, 73, 98, 138, 119, 219, 201, 159, 223, 213, 249, 4, 195, 107, 101, 131, 5, 173, 146, 100, 24, 102, 25, 109, 85, 207, 239, 218, 101, 255, 129, 118, 58, 198, 105, 189, 8, 181, 201, 117, 99, 111, 203, 16, 255, 215, 5, 53, 157, 168, 3, 78, 250, 116, 16, 18, 79, 230, 17, 148, 182, 225, 134, 133, 26, 87, 116, 49, 165, 15, 205, 42, 65, 238, 41, 6, 186, 210, 147, 42, 143, 169, 66, 18, 173, 12, 243, 216, 107, 143, 94, 151, 168, 177, 71, 112, 98, 226, 9, 69, 64, 141, 61, 130, 10, 202, 216, 35, 184, 16, 129, 161, 45, 243, 8, 46, 124, 219, 229, 17, 100, 144, 36, 75, 237, 143, 252, 87, 103, 136, 36, 89, 74, 135, 118, 61, 130, 143, 93, 143, 160, 194, 183, 117, 60, 130, 12, 174, 142, 167, 227, 17, 100, 168, 237, 198, 35, 200, 224, 158, 241, 8, 46, 104, 106, 102, 60, 130, 139, 228, 234, 17, 92, 112, 234, 17, 84, 104, 234, 17, 84, 128, 176, 5, 125, 147, 207, 118, 72, 221, 211, 128, 63, 162, 162, 141, 182, 122, 167, 91, 186, 228, 242, 88, 151, 168, 145, 88, 121, 4, 23, 242, 83, 161, 166, 18, 234, 201, 46, 85, 30, 193, 136, 92, 165, 108, 37, 75, 65, 175, 174, 237, 22, 36, 86, 30, 65, 158, 60, 130, 14, 229, 146, 71, 112, 225, 153, 117, 92, 21, 39, 143, 224, 227, 85, 117, 58, 201, 35, 216, 104, 139, 71, 80, 97, 23, 143, 160, 66, 75, 143, 160, 194, 98, 149, 8, 30, 65, 136, 195, 98, 249, 32, 143, 224, 226, 97, 22, 162, 46, 147, 43, 68, 4, 8, 36, 144, 48, 129, 4, 17, 210, 129, 32, 157, 197, 39, 93, 30, 193, 134, 71, 144, 225, 30, 65, 136, 71, 112, 194, 35, 232, 144, 64, 49, 228, 17, 100, 64, 79, 103, 47, 187, 144, 71, 112, 34, 21, 71, 144, 71, 112, 225, 204, 249, 109, 110, 86, 34, 156, 136, 32, 132, 167, 109, 254, 108, 187, 48, 78, 200, 35, 8, 17, 193, 6, 200, 35, 248, 136, 96, 99, 101, 91, 214, 250, 252, 16, 190, 217, 121, 203, 254, 6, 77, 91, 79, 213, 149, 48, 39, 8, 13, 175, 157, 9, 130, 78, 214, 95, 255, 194, 237, 33, 7, 26, 175, 147, 218, 115, 251, 22, 232, 82, 237, 45, 104, 223, 190, 198, 90, 203, 124, 69, 95, 219, 46, 73, 167, 131, 96, 187, 252, 105, 125, 19, 88, 118, 58, 200, 82, 117, 40, 234, 143, 89, 132, 126, 118, 179, 236, 157, 102, 213, 65, 78, 250, 137, 165, 233, 146, 53, 176, 102, 230, 193, 178, 211, 57, 40, 237, 86, 70, 213, 131, 47, 249, 168, 105, 187, 22, 119, 114, 1, 32, 3, 219, 126, 52, 33, 130, 139, 182, 11, 106, 217, 223, 168, 139, 32, 196, 35, 168, 81, 23, 193, 197, 230, 115, 4, 23, 212, 57, 130, 10, 217, 28, 65, 5, 118, 28, 90, 142, 32, 196, 114, 4, 31, 203, 17, 84, 240, 171, 56, 130, 11, 20, 128, 64, 2, 9, 34, 168, 85, 28, 97, 231, 150, 68, 240, 241, 237, 117, 131, 126, 35, 216, 104, 156, 161, 95, 150, 78, 219, 53, 104, 118, 200, 222, 8, 62, 28, 219, 8, 42, 44, 86, 209, 38, 130, 12, 171, 162, 17, 84, 80, 139, 208, 209, 42, 105, 126, 184, 147, 62, 41, 77, 155, 108, 245, 39, 73, 204, 16, 255, 75, 228, 17, 139, 229, 227, 158, 144, 196, 111, 98, 169, 223, 76, 40, 252, 155, 54, 165, 235, 121, 237, 124, 71, 90, 102, 147, 58, 103, 208, 224, 250, 136, 194, 171, 193, 98, 239, 160, 86, 182, 15, 113, 247, 184, 27, 137, 214, 214, 94, 63, 208, 201, 98, 215, 118, 208, 220, 173, 238, 83, 186, 134, 251, 119, 112, 222, 74, 34, 73, 98, 150, 171, 148, 57, 233, 147, 181, 126, 183, 23, 153, 248, 109, 47, 18, 65, 61, 19, 106, 235, 173, 223, 236, 244, 61, 157, 6, 36, 137, 153, 46, 118, 180, 54, 25, 165, 73, 93, 101, 98, 1, 85, 220, 22, 52, 213, 175, 172, 59, 208, 84, 191, 31, 11, 160, 169, 62, 179, 12, 122, 128, 41, 107, 220, 59, 105, 12, 32, 140, 213, 68, 31, 36, 137, 25, 210, 79, 229, 208, 224, 218, 168, 92, 144, 54, 217, 58, 161, 104, 150, 105, 252, 216, 149, 252, 165, 222, 212, 54, 157, 228, 208, 10, 67, 251, 173, 149, 232, 93, 35, 248, 120, 215, 8, 42, 52, 78, 251, 77, 85, 4, 27, 220, 36, 82, 182, 63, 65, 161, 109, 157, 148, 204, 65, 243, 103, 242, 199, 44, 188, 224, 155, 29, 27, 76, 50, 130, 143, 8, 138, 226, 239, 72, 234, 124, 129, 42, 110, 174, 107, 233, 100, 15, 173, 20, 238, 134, 193, 39, 117, 21, 7, 134, 89, 165, 205, 169, 146, 145, 68, 16, 109, 203, 60, 35, 184, 112, 156, 240, 8, 218, 149, 185, 50, 130, 14, 149, 100, 4, 21, 214, 102, 4, 31, 173, 141, 102, 4, 27, 36, 169, 206, 37, 41, 35, 216, 120, 118, 146, 148, 17, 92, 188, 39, 255, 86, 108, 131, 67, 51, 210, 135, 88, 107, 193, 255, 139, 18, 191, 232, 112, 166, 191, 184, 208, 12, 42, 201, 71, 191, 184, 104, 197, 134, 174, 37, 37, 170, 14, 61, 250, 69, 134, 247, 92, 232, 23, 23, 139, 139, 247, 102, 252, 98, 163, 109, 182, 66, 191, 200, 248, 92, 108, 252, 34, 99, 173, 68, 191, 200, 176, 250, 101, 219, 165, 249, 43, 250, 190, 232, 208, 170, 22, 59, 174, 237, 252, 2, 14, 161, 77, 43, 115, 152, 144, 48, 59, 17, 203, 174, 5, 236, 245, 123, 91, 200, 227, 185, 175, 47, 50, 254, 241, 147, 11, 169, 227, 103, 95, 116, 240, 197, 198, 111, 227, 64, 225, 164, 87, 6, 34, 173, 227, 154, 25, 251, 98, 4, 4, 17, 152, 96, 2, 236, 139, 16, 254, 45, 217, 23, 27, 36, 52, 156, 10, 4, 1, 162, 128, 132, 9, 16, 251, 34, 36, 2, 130, 0, 77, 48, 1, 4, 18, 16, 251, 162, 68, 218, 214, 134, 47, 50, 60, 162, 107, 23, 102, 221, 234, 139, 11, 95, 108, 60, 47, 190, 184, 160, 107, 25, 95, 95, 92, 68, 240, 69, 5, 110, 43, 156, 65, 53, 83, 144, 47, 70, 52, 248, 116, 22, 175, 238, 146, 73, 82, 41, 200, 215, 249, 198, 204, 65, 83, 203, 247, 22, 49, 177, 240, 106, 26, 208, 226, 195, 210, 234, 158, 163, 213, 175, 97, 251, 57, 247, 144, 47, 54, 44, 82, 56, 159, 14, 165, 246, 26, 28, 24, 70, 142, 253, 138, 126, 62, 195, 226, 133, 61, 159, 14, 53, 19, 71, 190, 248, 104, 114, 49, 200, 57, 232, 85, 203, 147, 180, 92, 203, 115, 67, 170, 37, 74, 237, 111, 56, 254, 12, 77, 91, 231, 149, 16, 179, 30, 94, 59, 169, 55, 154, 182, 173, 189, 166, 240, 90, 137, 158, 126, 237, 37, 125, 157, 79, 135, 32, 114, 189, 7, 165, 173, 239, 219, 224, 42, 218, 52, 180, 145, 175, 179, 8, 105, 120, 190, 213, 61, 124, 209, 161, 107, 73, 167, 217, 33, 111, 155, 11, 162, 106, 221, 65, 81, 18, 106, 139, 156, 79, 167, 34, 11, 35, 95, 100, 80, 201, 46, 180, 232, 192, 92, 67, 190, 203, 173, 21, 72, 39, 11, 241, 217, 104, 67, 190, 248, 176, 178, 95, 68, 54, 228, 139, 12, 106, 87, 78, 30, 8, 93, 79, 233, 218, 133, 25, 242, 197, 198, 90, 114, 113, 194, 37, 67, 190, 200, 64, 225, 165, 56, 33, 95, 132, 104, 188, 136, 32, 95, 116, 68, 22, 142, 32, 95, 108, 104, 92, 4, 249, 34, 131, 51, 110, 225, 22, 249, 226, 130, 78, 246, 83, 182, 190, 195, 179, 22, 52, 104, 147, 108, 155, 151, 171, 232, 34, 95, 132, 44, 246, 14, 242, 197, 197, 163, 146, 103, 7, 249, 98, 35, 29, 84, 69, 29, 228, 139, 15, 139, 139, 213, 207, 241, 52, 255, 58, 59, 237, 136, 231, 123, 213, 47, 179, 13, 242, 69, 6, 117, 244, 15, 144, 198, 160, 166, 13, 239, 167, 246, 30, 223, 150, 29, 82, 197, 9, 57, 175, 150, 178, 214, 107, 12, 242, 197, 133, 167, 27, 239, 103, 197, 174, 108, 13, 152, 171, 55, 52, 157, 40, 242, 197, 133, 106, 166, 60, 42, 65, 19, 64, 16, 129, 8, 30, 209, 220, 237, 139, 16, 207, 59, 166, 140, 181, 201, 84, 146, 88, 33, 135, 244, 50, 40, 109, 203, 32, 95, 116, 52, 43, 180, 40, 225, 248, 221, 23, 27, 220, 115, 49, 58, 215, 75, 189, 173, 54, 33, 95, 92, 232, 90, 114, 173, 76, 200, 23, 31, 109, 127, 237, 91, 144, 47, 70, 248, 34, 133, 234, 93, 244, 61, 36, 45, 215, 130, 124, 145, 209, 217, 28, 190, 109, 179, 17, 76, 160, 158, 11, 242, 197, 70, 99, 80, 106, 143, 63, 28, 223, 39, 187, 23, 25, 173, 20, 111, 40, 253, 100, 219, 18, 249, 226, 99, 101, 196, 202, 180, 175, 75, 58, 110, 37, 242, 197, 7, 95, 12, 128, 107, 247, 98, 196, 162, 163, 113, 47, 42, 76, 48, 113, 139, 14, 148, 108, 198, 226, 30, 205, 75, 66, 77, 170, 134, 229, 22, 29, 190, 246, 159, 227, 155, 238, 130, 240, 181, 255, 24, 183, 248, 208, 74, 229, 4, 110, 113, 49, 129, 91, 84, 208, 110, 177, 81, 209, 70, 27, 185, 69, 134, 91, 116, 112, 139, 1, 96, 235, 200, 121, 113, 145, 182, 241, 226, 66, 171, 218, 229, 141, 23, 31, 90, 215, 226, 142, 64, 104, 149, 112, 47, 188, 200, 192, 155, 182, 255, 234, 16, 216, 91, 231, 18, 2, 47, 74, 184, 186, 231, 31, 254, 136, 23, 31, 26, 60, 204, 122, 235, 36, 183, 115, 210, 22, 27, 206, 191, 100, 244, 251, 190, 45, 66, 60, 2, 129, 251, 219, 34, 131, 165, 182, 197, 199, 63, 215, 22, 45, 125, 180, 190, 245, 202, 126, 203, 65, 60, 144, 80, 234, 235, 219, 162, 130, 78, 82, 41, 14, 6, 90, 125, 193, 107, 59, 151, 172, 241, 188, 28, 53, 172, 108, 12, 77, 201, 60, 252, 99, 245, 142, 222, 105, 130, 216, 135, 90, 191, 161, 93, 12, 150, 126, 91, 92, 104, 109, 244, 219, 226, 66, 130, 4, 223, 22, 23, 220, 19, 132, 111, 17, 136, 113, 51, 232, 83, 50, 251, 157, 104, 173, 6, 214, 229, 123, 91, 92, 208, 252, 160, 218, 74, 48, 226, 157, 102, 189, 45, 46, 220, 19, 58, 34, 52, 208, 3, 231, 211, 77, 188, 99, 186, 28, 227, 196, 255, 18, 53, 208, 218, 251, 38, 180, 69, 134, 54, 39, 228, 32, 241, 100, 141, 207, 231, 218, 34, 100, 129, 187, 3, 31, 30, 73, 91, 84, 144, 180, 197, 135, 203, 126, 79, 172, 144, 164, 45, 66, 42, 188, 32, 73, 91, 92, 72, 90, 227, 133, 188, 170, 131, 17, 200, 216, 182, 168, 192, 25, 191, 173, 78, 91, 92, 48, 211, 22, 31, 141, 23, 29, 76, 91, 108, 124, 211, 38, 17, 58, 120, 147, 107, 50, 161, 180, 211, 241, 207, 86, 143, 243, 224, 217, 95, 81, 6, 238, 185, 18, 197, 167, 99, 236, 160, 138, 219, 4, 77, 112, 91, 180, 54, 10, 163, 86, 166, 45, 62, 190, 181, 45, 46, 84, 84, 91, 84, 88, 249, 212, 78, 86, 181, 69, 71, 197, 193, 48, 114, 173, 32, 165, 21, 85, 109, 209, 129, 23, 139, 12, 207, 154, 180, 250, 194, 104, 129, 254, 46, 58, 214, 122, 14, 170, 232, 164, 113, 66, 216, 160, 213, 249, 116, 11, 90, 213, 98, 166, 45, 58, 160, 182, 232, 72, 206, 22, 21, 126, 213, 75, 157, 112, 59, 182, 56, 225, 174, 235, 41, 118, 200, 87, 54, 81, 241, 136, 243, 168, 45, 69, 3, 227, 135, 227, 218, 12, 68, 81, 18, 114, 32, 131, 46, 247, 254, 193, 23, 243, 79, 84, 33, 9, 93, 141, 231, 119, 45, 78, 232, 231, 42, 101, 108, 177, 225, 108, 177, 209, 202, 22, 29, 30, 65, 12, 179, 10, 53, 108, 241, 161, 203, 177, 117, 228, 146, 45, 62, 92, 178, 69, 5, 143, 180, 166, 236, 111, 80, 170, 146, 151, 55, 88, 214, 192, 205, 196, 81, 91, 246, 45, 223, 132, 166, 185, 136, 166, 185, 125, 199, 168, 45, 131, 104, 212, 69, 42, 152, 161, 182, 108, 87, 66, 144, 182, 140, 65, 161, 238, 121, 182, 94, 177, 238, 208, 127, 93, 233, 42, 150, 109, 219, 7, 242, 149, 57, 32, 180, 223, 187, 76, 72, 155, 100, 139, 13, 75, 243, 175, 69, 6, 119, 112, 134, 209, 176, 76, 23, 64, 207, 130, 34, 180, 185, 53, 139, 12, 20, 173, 21, 235, 205, 44, 58, 116, 43, 179, 184, 48, 105, 138, 152, 69, 134, 166, 142, 185, 233, 34, 35, 2, 9, 36, 160, 170, 139, 139, 138, 90, 84, 224, 106, 81, 129, 71, 144, 46, 181, 184, 88, 57, 121, 84, 130, 22, 181, 232, 88, 212, 162, 2, 79, 139, 14, 79, 139, 10, 244, 119, 33, 199, 164, 113, 90, 92, 144, 164, 117, 154, 22, 23, 16, 165, 105, 209, 225, 50, 33, 77, 139, 11, 143, 64, 88, 153, 22, 23, 173, 104, 101, 90, 108, 104, 102, 203, 226, 2, 245, 120, 37, 196, 150, 69, 9, 95, 204, 223, 114, 89, 108, 180, 92, 22, 21, 28, 84, 113, 107, 80, 177, 137, 23, 131, 6, 94, 218, 135, 251, 119, 84, 83, 73, 162, 112, 70, 194, 73, 13, 154, 43, 218, 38, 0, 193, 226, 0, 28, 211, 111, 56, 36, 2, 106, 19, 128, 96, 145, 161, 77, 0, 130, 69, 5, 101, 217, 208, 39, 69, 83, 199, 61, 14, 50, 60, 223, 234, 8, 240, 174, 111, 130, 163, 85, 210, 118, 61, 212, 46, 90, 221, 227, 32, 164, 105, 243, 56, 184, 240, 72, 252, 57, 184, 208, 218, 158, 131, 11, 86, 63, 136, 191, 229, 30, 156, 33, 173, 88, 173, 88, 127, 68, 129, 180, 98, 35, 180, 98, 149, 237, 59, 124, 101, 107, 214, 27, 86, 78, 90, 191, 57, 168, 69, 45, 217, 115, 144, 209, 118, 65, 220, 60, 7, 25, 207, 206, 23, 146, 148, 203, 127, 221, 58, 14, 105, 170, 238, 13, 174, 235, 117, 219, 236, 69, 89, 243, 156, 67, 63, 151, 183, 236, 103, 224, 19, 142, 34, 158, 151, 231, 224, 194, 55, 109, 210, 29, 194, 221, 191, 131, 11, 186, 17, 34, 72, 168, 226, 132, 30, 141, 194, 234, 159, 227, 252, 19, 79, 133, 123, 193, 59, 59, 143, 32, 135, 166, 45, 55, 255, 88, 210, 118, 65, 52, 109, 25, 167, 182, 16, 119, 63, 89, 77, 170, 26, 214, 202, 228, 105, 191, 37, 83, 71, 234, 164, 76, 20, 105, 61, 23, 135, 43, 114, 74, 132, 243, 175, 241, 253, 239, 224, 195, 253, 59, 168, 240, 216, 249, 165, 160, 8, 15, 7, 183, 108, 223, 118, 49, 84, 113, 170, 0, 132, 90, 20, 65, 4, 2, 163, 92, 132, 135, 231, 164, 101, 138, 224, 202, 178, 161, 38, 31, 233, 100, 223, 0, 238, 86, 226, 2, 152, 232, 199, 4, 144, 248, 42, 213, 233, 52, 224, 106, 121, 210, 236, 160, 13, 95, 209, 71, 252, 42, 73, 198, 40, 135, 248, 189, 209, 228, 51, 92, 215, 243, 159, 116, 169, 63, 24, 136, 136, 240, 48, 75, 209, 186, 204, 182, 101, 239, 26, 65, 173, 46, 147, 55, 180, 50, 204, 54, 95, 224, 146, 33, 166, 75, 33, 140, 149, 132, 235, 191, 101, 251, 63, 209, 98, 239, 224, 195, 98, 239, 160, 194, 228, 45, 119, 112, 33, 221, 65, 199, 114, 7, 27, 223, 24, 109, 7, 33, 19, 156, 131, 10, 156, 115, 80, 129, 147, 56, 73, 74, 231, 32, 163, 117, 81, 216, 65, 6, 143, 32, 127, 84, 242, 236, 160, 195, 159, 29, 71, 207, 14, 54, 114, 61, 118, 80, 66, 215, 190, 179, 131, 139, 199, 49, 59, 184, 160, 180, 178, 12, 122, 248, 130, 174, 37, 213, 59, 146, 48, 59, 200, 224, 198, 14, 42, 120, 4, 181, 54, 42, 151, 71, 27, 59, 232, 0, 209, 171, 148, 177, 131, 139, 10, 218, 199, 64, 27, 181, 189, 40, 99, 7, 29, 206, 167, 67, 202, 216, 193, 134, 99, 143, 154, 145, 50, 118, 212, 98, 7, 25, 206, 13, 189, 47, 59, 216, 192, 35, 16, 90, 54, 236, 32, 67, 162, 236, 160, 2, 4, 233, 120, 74, 230, 32, 163, 89, 28, 108, 120, 4, 130, 47, 7, 25, 26, 123, 196, 193, 133, 165, 32, 9, 92, 248, 42, 237, 117, 112, 209, 186, 233, 36, 183, 14, 66, 60, 191, 106, 23, 59, 242, 72, 4, 2, 3, 107, 194, 132, 54, 39, 228, 170, 153, 66, 225, 138, 222, 105, 118, 65, 84, 203, 229, 52, 235, 32, 67, 223, 196, 193, 129, 132, 127, 167, 147, 156, 75, 29, 27, 124, 210, 165, 142, 12, 170, 173, 106, 169, 35, 131, 23, 33, 126, 189, 100, 26, 117, 132, 248, 199, 18, 85, 199, 133, 219, 138, 5, 73, 84, 29, 27, 74, 213, 209, 161, 84, 29, 31, 13, 190, 26, 253, 146, 58, 52, 59, 66, 64, 32, 153, 64, 130, 8, 46, 169, 99, 195, 89, 118, 186, 6, 127, 167, 89, 111, 139, 240, 193, 129, 187, 46, 150, 234, 40, 225, 148, 163, 2, 245, 92, 144, 68, 57, 50, 90, 43, 214, 61, 57, 58, 60, 206, 240, 72, 195, 61, 157, 28, 33, 144, 180, 77, 178, 135, 243, 106, 41, 168, 115, 105, 80, 170, 84, 27, 98, 22, 45, 64, 209, 157, 32, 254, 182, 14, 154, 119, 146, 142, 46, 232, 161, 105, 139, 12, 13, 21, 69, 248, 128, 187, 133, 227, 151, 184, 42, 244, 236, 184, 196, 90, 143, 65, 135, 227, 228, 232, 112, 93, 184, 238, 111, 218, 167, 240, 181, 255, 16, 210, 200, 72, 142, 143, 214, 79, 142, 144, 165, 173, 165, 147, 80, 114, 116, 60, 204, 58, 66, 22, 199, 0, 120, 211, 74, 58, 46, 84, 210, 81, 193, 99, 162, 10, 233, 36, 29, 25, 58, 73, 71, 5, 164, 233, 165, 35, 131, 111, 187, 26, 159, 60, 29, 33, 238, 233, 168, 208, 234, 92, 58, 50, 56, 233, 211, 61, 238, 9, 2, 100, 98, 177, 119, 26, 48, 162, 177, 71, 216, 241, 35, 109, 233, 216, 208, 170, 90, 58, 50, 120, 235, 50, 235, 158, 95, 128, 194, 142, 163, 167, 243, 125, 106, 233, 232, 192, 210, 89, 58, 50, 60, 204, 46, 150, 142, 11, 150, 142, 143, 247, 85, 200, 35, 16, 120, 33, 232, 90, 6, 130, 53, 35, 9, 179, 131, 28, 36, 190, 10, 249, 218, 116, 92, 88, 57, 81, 212, 142, 134, 248, 2, 157, 44, 246, 8, 21, 109, 180, 43, 218, 104, 79, 168, 247, 67, 91, 142, 124, 46, 166, 1, 81, 38, 29, 31, 173, 78, 106, 47, 215, 67, 101, 25, 180, 168, 69, 17, 64, 128, 182, 147, 73, 199, 8, 85, 233, 168, 32, 81, 142, 210, 37, 56, 50, 40, 109, 205, 186, 4, 71, 70, 235, 164, 37, 56, 50, 72, 112, 132, 120, 120, 57, 85, 72, 130, 99, 35, 130, 227, 35, 226, 219, 46, 255, 135, 243, 75, 65, 46, 217, 251, 75, 167, 226, 64, 122, 69, 30, 169, 52, 50, 60, 82, 105, 84, 112, 9, 253, 93, 168, 210, 184, 168, 52, 6, 128, 210, 24, 0, 87, 247, 124, 123, 141, 140, 246, 26, 27, 77, 155, 246, 26, 25, 92, 219, 107, 92, 104, 175, 189, 198, 7, 18, 168, 189, 70, 200, 107, 100, 52, 78, 168, 189, 198, 69, 106, 143, 39, 106, 175, 177, 225, 158, 214, 55, 50, 172, 111, 116, 184, 137, 55, 42, 232, 90, 242, 17, 68, 127, 159, 55, 50, 184, 55, 42, 112, 224, 234, 158, 111, 208, 181, 36, 133, 23, 211, 8, 153, 192, 188, 241, 49, 129, 121, 163, 130, 171, 40, 82, 204, 152, 58, 148, 158, 222, 232, 208, 245, 186, 113, 193, 147, 107, 116, 240, 8, 132, 165, 19, 110, 100, 184, 91, 212, 205, 208, 90, 177, 173, 225, 164, 13, 1, 81, 197, 205, 161, 255, 184, 241, 193, 143, 84, 123, 220, 200, 80, 199, 141, 13, 206, 167, 123, 230, 70, 198, 51, 55, 58, 92, 194, 220, 184, 224, 107, 255, 73, 232, 155, 60, 223, 137, 131, 135, 217, 213, 188, 8, 175, 238, 169, 147, 86, 159, 112, 79, 174, 138, 27, 242, 6, 92, 21, 55, 87, 197, 173, 177, 71, 36, 156, 79, 183, 128, 86, 172, 235, 90, 70, 173, 125, 139, 171, 127, 4, 197, 179, 26, 35, 119, 143, 184, 215, 82, 87, 90, 160, 21, 235, 72, 151, 106, 239, 193, 210, 37, 151, 9, 254, 199, 64, 107, 197, 179, 178, 37, 50, 24, 64, 231, 178, 22, 235, 226, 133, 101, 226, 198, 135, 79, 234, 14, 92, 63, 211, 42, 109, 23, 161, 147, 197, 62, 241, 232, 249, 91, 182, 101, 173, 175, 74, 194, 93, 251, 45, 220, 248, 128, 176, 161, 59, 185, 113, 129, 169, 214, 168, 240, 206, 14, 106, 141, 14, 11, 175, 165, 53, 54, 52, 195, 26, 21, 18, 107, 124, 240, 163, 181, 158, 67, 137, 53, 78, 160, 196, 26, 35, 18, 107, 132, 96, 141, 142, 196, 26, 33, 110, 53, 42, 104, 203, 80, 67, 4, 38, 128, 64, 49, 117, 219, 216, 80, 233, 40, 220, 26, 239, 52, 219, 248, 96, 204, 118, 170, 211, 8, 73, 117, 26, 21, 154, 23, 167, 165, 105, 100, 84, 212, 137, 176, 52, 141, 139, 138, 54, 218, 104, 105, 26, 27, 21, 117, 208, 210, 52, 46, 150, 166, 81, 65, 82, 76, 163, 66, 132, 8, 80, 96, 130, 196, 52, 46, 38, 16, 193, 4, 17, 36, 166, 113, 241, 235, 59, 129, 48, 144, 52, 83, 180, 145, 65, 146, 152, 57, 180, 151, 162, 141, 12, 205, 15, 109, 52, 209, 70, 198, 195, 172, 63, 237, 68, 79, 191, 54, 46, 104, 239, 179, 125, 109, 92, 80, 184, 157, 215, 198, 197, 162, 36, 109, 157, 215, 198, 6, 132, 127, 75, 247, 124, 39, 115, 137, 182, 159, 11, 226, 240, 21, 253, 70, 5, 173, 236, 69, 188, 185, 91, 29, 252, 225, 97, 246, 93, 27, 23, 170, 50, 37, 131, 222, 181, 241, 241, 174, 141, 10, 147, 141, 80, 201, 46, 136, 55, 118, 174, 141, 13, 254, 205, 174, 162, 16, 86, 63, 137, 213, 207, 213, 115, 87, 34, 127, 9, 230, 45, 252, 220, 78, 89, 54, 231, 33, 38, 139, 31, 24, 183, 235, 90, 6, 210, 171, 82, 83, 213, 212, 169, 106, 160, 40, 233, 209, 220, 74, 132, 144, 132, 13, 7, 165, 147, 126, 87, 74, 51, 232, 170, 168, 208, 138, 109, 104, 239, 130, 138, 170, 182, 13, 99, 101, 227, 125, 199, 21, 112, 82, 166, 246, 32, 84, 251, 117, 178, 216, 145, 246, 42, 93, 235, 148, 101, 163, 80, 150, 45, 153, 74, 118, 53, 195, 189, 234, 193, 157, 146, 65, 88, 240, 14, 111, 126, 215, 212, 18, 97, 196, 211, 175, 157, 239, 15, 47, 185, 161, 201, 87, 4, 121, 215, 199, 205, 48, 20, 37, 173, 149, 104, 130, 54, 46, 152, 111, 187, 92, 190, 217, 105, 163, 163, 97, 173, 116, 60, 160, 107, 25, 212, 246, 1, 105, 202, 229, 208, 3, 23, 13, 169, 78, 27, 29, 212, 74, 176, 161, 191, 77, 105, 157, 240, 195, 215, 202, 132, 88, 54, 109, 92, 224, 154, 210, 121, 174, 236, 116, 234, 168, 105, 187, 54, 251, 31, 60, 48, 94, 144, 78, 144, 97, 162, 106, 98, 245, 67, 109, 155, 109, 70, 250, 204, 158, 153, 54, 58, 52, 78, 91, 89, 166, 141, 141, 84, 202, 180, 145, 161, 178, 253, 201, 150, 244, 5, 137, 95, 63, 151, 35, 73, 39, 123, 156, 212, 86, 170, 180, 85, 237, 107, 188, 211, 69, 56, 159, 236, 209, 30, 107, 205, 174, 68, 234, 214, 53, 109, 149, 74, 135, 214, 210, 244, 64, 211, 184, 223, 31, 232, 122, 205, 64, 223, 202, 137, 239, 66, 128, 43, 245, 71, 0, 87, 237, 25, 192, 39, 84, 91, 164, 107, 201, 31, 128, 164, 237, 74, 128, 54, 201, 22, 253, 75, 223, 172, 0, 239, 92, 139, 21, 226, 134, 190, 237, 106, 96, 177, 119, 24, 211, 56, 36, 92, 159, 219, 51, 51, 16, 46, 105, 187, 22, 108, 132, 153, 66, 42, 18, 171, 135, 75, 82, 123, 105, 64, 7, 46, 153, 54, 58, 112, 179, 178, 2, 161, 3, 226, 113, 9, 179, 243, 120, 10, 240, 228, 82, 165, 149, 92, 218, 8, 81, 182, 169, 101, 91, 246, 150, 132, 126, 42, 108, 88, 57, 105, 253, 182, 218, 184, 224, 12, 244, 223, 178, 205, 13, 60, 72, 58, 25, 132, 146, 237, 168, 72, 172, 30, 222, 180, 85, 169, 142, 186, 199, 209, 70, 7, 140, 151, 115, 168, 8, 173, 255, 153, 220, 65, 69, 208, 182, 235, 161, 34, 52, 119, 43, 90, 141, 54, 66, 86, 163, 141, 10, 30, 105, 180, 241, 209, 84, 194, 255, 244, 4, 54, 208, 230, 132, 32, 207, 185, 202, 130, 101, 167, 171, 120, 147, 139, 129, 0, 225, 159, 106, 63, 114, 149, 135, 175, 63, 43, 91, 162, 247, 68, 174, 210, 180, 97, 180, 122, 167, 171, 104, 163, 141, 144, 5, 19, 205, 15, 215, 253, 86, 197, 105, 219, 67, 14, 168, 54, 58, 214, 254, 107, 248, 125, 206, 144, 224, 200, 241, 93, 203, 115, 11, 158, 70, 27, 29, 108, 131, 154, 54, 143, 74, 80, 163, 141, 11, 248, 98, 126, 70, 27, 25, 143, 202, 91, 140, 54, 50, 84, 120, 169, 52, 252, 81, 73, 163, 240, 3, 195, 175, 13, 4, 6, 73, 23, 163, 141, 16, 79, 218, 197, 14, 53, 62, 36, 163, 141, 10, 171, 80, 3, 163, 141, 142, 117, 217, 16, 163, 141, 12, 70, 27, 21, 104, 122, 233, 149, 167, 218, 235, 111, 234, 45, 151, 203, 38, 221, 181, 109, 147, 158, 77, 186, 163, 242, 212, 122, 236, 81, 121, 202, 185, 93, 12, 170, 184, 43, 83, 231, 23, 52, 188, 22, 112, 208, 252, 219, 152, 33, 73, 98, 133, 38, 42, 139, 57, 57, 26, 167, 84, 136, 46, 13, 148, 58, 233, 115, 89, 224, 252, 147, 88, 151, 168, 242, 41, 81, 163, 242, 148, 54, 46, 52, 123, 67, 52, 84, 86, 110, 218, 229, 216, 250, 131, 181, 50, 33, 138, 166, 255, 185, 24, 135, 180, 50, 33, 247, 132, 15, 157, 239, 74, 27, 23, 187, 18, 106, 36, 86, 218, 184, 32, 73, 188, 148, 54, 46, 36, 137, 153, 36, 241, 98, 166, 173, 195, 215, 161, 223, 223, 74, 200, 119, 149, 54, 46, 40, 125, 84, 105, 227, 226, 83, 106, 163, 10, 47, 74, 27, 33, 254, 218, 73, 29, 125, 74, 165, 141, 14, 20, 221, 201, 251, 174, 110, 132, 112, 210, 39, 229, 33, 181, 199, 17, 3, 132, 17, 169, 61, 142, 22, 37, 59, 105, 35, 164, 105, 218, 200, 96, 45, 39, 109, 100, 232, 101, 39, 105, 227, 66, 87, 74, 116, 72, 152, 160, 199, 139, 54, 46, 220, 115, 209, 70, 134, 214, 73, 106, 35, 195, 171, 58, 142, 24, 255, 243, 130, 176, 129, 243, 233, 86, 106, 227, 3, 91, 11, 77, 219, 214, 73, 65, 191, 18, 200, 175, 100, 127, 31, 210, 182, 75, 219, 46, 213, 118, 45, 100, 225, 213, 160, 94, 165, 82, 27, 33, 46, 187, 129, 31, 45, 74, 73, 237, 127, 96, 249, 56, 60, 26, 137, 91, 141, 149, 90, 154, 42, 58, 105, 138, 84, 106, 67, 72, 218, 115, 212, 73, 153, 28, 238, 161, 212, 75, 151, 92, 214, 74, 164, 20, 92, 246, 123, 229, 169, 201, 126, 182, 93, 141, 251, 217, 211, 178, 75, 226, 170, 154, 86, 52, 232, 242, 254, 241, 227, 252, 82, 156, 95, 138, 55, 117, 52, 97, 231, 247, 189, 19, 138, 238, 100, 223, 99, 180, 245, 243, 93, 253, 69, 44, 74, 153, 240, 23, 161, 254, 40, 212, 223, 254, 122, 175, 141, 90, 10, 90, 224, 160, 58, 117, 81, 109, 230, 32, 9, 183, 191, 152, 127, 241, 227, 97, 246, 61, 249, 159, 182, 223, 128, 237, 71, 176, 85, 166, 206, 68, 69, 223, 245, 39, 26, 59, 78, 151, 122, 147, 255, 208, 228, 47, 249, 159, 223, 30, 95, 251, 79, 155, 19, 162, 232, 78, 208, 90, 82, 221, 82, 220, 82, 118, 37, 52, 241, 230, 132, 154, 137, 55, 19, 111, 188, 175, 218, 215, 232, 122, 222, 117, 61, 207, 251, 227, 182, 253, 18, 102, 7, 105, 147, 205, 19, 170, 184, 45, 229, 5, 65, 60, 146, 180, 239, 21, 75, 65, 78, 63, 237, 231, 123, 231, 123, 91, 231, 218, 118, 73, 58, 157, 71, 84, 83, 8, 2, 105, 20, 86, 170, 118, 57, 211, 10, 8, 211, 229, 152, 42, 78, 57, 105, 153, 144, 246, 251, 108, 205, 122, 46, 170, 214, 27, 154, 25, 162, 176, 227, 20, 218, 233, 56, 3, 10, 171, 116, 24, 43, 244, 190, 171, 61, 158, 12, 124, 210, 165, 238, 168, 115, 105, 240, 247, 92, 72, 210, 118, 33, 199, 211, 97, 172, 254, 113, 55, 212, 91, 39, 20, 43, 117, 178, 111, 165, 98, 198, 224, 129, 89, 164, 180, 247, 77, 208, 70, 107, 49, 179, 6, 79, 46, 109, 70, 59, 155, 169, 232, 98, 180, 223, 147, 95, 162, 242, 148, 246, 218, 212, 56, 105, 107, 127, 182, 123, 220, 13, 69, 79, 208, 213, 52, 252, 200, 113, 171, 179, 99, 229, 55, 63, 120, 4, 194, 179, 158, 151, 131, 72, 218, 46, 164, 54, 151, 139, 216, 181, 218, 164, 115, 247, 77, 219, 86, 213, 236, 208, 212, 161, 160, 103, 53, 110, 93, 216, 155, 80, 197, 238, 136, 155, 47, 249, 13, 22, 165, 60, 59, 142, 40, 186, 19, 6, 172, 106, 39, 252, 73, 39, 15, 212, 190, 215, 120, 155, 182, 238, 121, 7, 158, 212, 180, 211, 181, 149, 164, 236, 47, 64, 3, 173, 110, 41, 110, 41, 205, 196, 189, 153, 56, 243, 242, 52, 191, 3, 157, 139, 106, 143, 37, 92, 182, 5, 90, 255, 243, 129, 118, 58, 190, 228, 163, 197, 223, 148, 53, 90, 0, 167, 232, 78, 82, 59, 145, 50, 233, 223, 249, 78, 161, 147, 93, 236, 29, 7, 142, 167, 210, 86, 154, 173, 255, 44, 205, 4, 53, 51, 120, 77, 25, 114, 207, 49, 163, 156, 75, 167, 77, 58, 94, 168, 233, 54, 55, 80, 204, 86, 42, 102, 18, 95, 209, 71, 186, 96, 65, 219, 116, 210, 163, 18, 138, 238, 4, 105, 39, 37, 145, 2, 84, 146, 148, 68, 78, 85, 210, 252, 153, 86, 181, 109, 205, 139, 54, 114, 46, 219, 34, 231, 18, 181, 93, 128, 49, 87, 48, 230, 10, 212, 210, 33, 109, 146, 49, 224, 184, 147, 203, 165, 232, 78, 22, 123, 39, 229, 162, 84, 25, 148, 246, 93, 58, 13, 124, 58, 9, 41, 125, 102, 27, 244, 218, 60, 160, 105, 39, 173, 142, 120, 177, 54, 238, 120, 46, 102, 91, 41, 186, 235, 18, 57, 233, 149, 249, 138, 54, 164, 124, 97, 36, 36, 109, 23, 90, 155, 140, 146, 61, 105, 250, 60, 193, 76, 91, 164, 105, 39, 234, 224, 83, 53, 240, 8, 226, 119, 79, 127, 202, 87, 197, 201, 250, 0, 205, 50, 109, 34, 109, 123, 140, 113, 98, 64, 49, 106, 169, 43, 45, 240, 43, 65, 223, 233, 164, 212, 178, 151, 9, 199, 115, 57, 127, 230, 229, 89, 140, 43, 71, 15, 47, 206, 191, 68, 206, 249, 139, 40, 85, 199, 1, 179, 191, 94, 196, 132, 39, 204, 170, 118, 18, 177, 82, 49, 219, 202, 178, 166, 140, 43, 26, 228, 42, 101, 253, 60, 149, 53, 168, 98, 3, 216, 231, 73, 169, 196, 147, 173, 230, 201, 229, 144, 238, 63, 47, 222, 207, 138, 93, 153, 120, 53, 126, 93, 42, 113, 6, 75, 243, 114, 36, 193, 4, 53, 86, 42, 81, 187, 35, 93, 75, 170, 100, 3, 168, 100, 199, 195, 172, 164, 237, 90, 119, 187, 150, 34, 215, 182, 203, 117, 165, 39, 41, 179, 139, 129, 215, 197, 164, 54, 14, 79, 69, 46, 136, 156, 52, 150, 120, 196, 61, 194, 154, 153, 198, 30, 65, 141, 61, 194, 141, 61, 194, 58, 146, 168, 249, 87, 194, 236, 32, 165, 223, 30, 51, 228, 187, 64, 233, 103, 210, 118, 170, 144, 239, 50, 214, 183, 149, 48, 59, 136, 1, 198, 169, 245, 219, 62, 215, 154, 137, 75, 152, 29, 180, 64, 147, 173, 80, 182, 92, 213, 94, 101, 155, 135, 196, 155, 182, 206, 192, 181, 73, 86, 225, 229, 157, 102, 87, 235, 90, 217, 180, 54, 201, 214, 113, 39, 164, 77, 50, 8, 109, 146, 65, 152, 237, 165, 118, 213, 115, 65, 141, 211, 62, 124, 69, 219, 4, 32, 88, 9, 179, 131, 26, 222, 147, 31, 61, 59, 235, 18, 61, 59, 18, 102, 167, 213, 85, 51, 5, 61, 56, 205, 58, 175, 108, 169, 3, 97, 233, 176, 87, 219, 13, 63, 185, 144, 218, 110, 36, 76, 144, 54, 3, 215, 166, 121, 206, 218, 64, 68, 144, 77, 54, 140, 167, 162, 226, 49, 77, 181, 147, 163, 181, 94, 4, 99, 133, 24, 119, 133, 106, 51, 16, 150, 205, 154, 153, 198, 200, 95, 98, 7, 73, 226, 133, 252, 37, 94, 220, 46, 9, 157, 48, 67, 110, 151, 218, 110, 80, 133, 151, 138, 46, 137, 95, 9, 106, 114, 253, 50, 141, 125, 234, 185, 104, 47, 227, 236, 116, 45, 227, 160, 107, 25, 231, 83, 25, 125, 19, 58, 25, 122, 79, 70, 226, 93, 98, 105, 101, 93, 211, 166, 147, 39, 87, 235, 132, 39, 158, 78, 213, 30, 47, 168, 185, 91, 155, 230, 17, 196, 143, 30, 79, 133, 112, 220, 9, 61, 158, 218, 64, 27, 87, 56, 110, 181, 150, 108, 154, 108, 215, 109, 254, 148, 12, 98, 204, 152, 73, 18, 43, 199, 3, 13, 92, 221, 243, 142, 219, 41, 160, 91, 231, 251, 148, 168, 105, 147, 88, 53, 109, 18, 67, 156, 244, 184, 193, 187, 126, 83, 12, 180, 113, 5, 133, 4, 139, 189, 131, 84, 83, 13, 84, 83, 206, 36, 2, 17, 128, 0, 77, 246, 65, 83, 46, 231, 203, 236, 68, 41, 158, 5, 202, 246, 245, 83, 233, 167, 250, 84, 249, 169, 22, 80, 219, 13, 202, 79, 21, 1, 117, 46, 13, 124, 149, 191, 102, 239, 55, 241, 4, 49, 241, 212, 224, 170, 51, 161, 95, 165, 205, 104, 35, 253, 212, 134, 112, 239, 219, 107, 110, 188, 141, 182, 3, 77, 46, 6, 61, 253, 237, 49, 133, 27, 154, 104, 20, 86, 223, 158, 51, 243, 9, 103, 230, 252, 232, 113, 156, 26, 168, 90, 255, 199, 146, 180, 43, 53, 70, 139, 57, 249, 55, 245, 198, 9, 173, 132, 33, 175, 41, 251, 138, 126, 171, 240, 178, 152, 31, 125, 227, 244, 208, 244, 155, 55, 94, 111, 202, 86, 161, 214, 182, 249, 141, 211, 2, 172, 151, 105, 19, 251, 175, 76, 123, 20, 173, 206, 142, 121, 35, 7, 235, 49, 205, 139, 155, 221, 229, 90, 17, 84, 113, 82, 30, 129, 240, 73, 25, 70, 145, 170, 245, 111, 186, 171, 129, 107, 219, 133, 28, 203, 149, 58, 23, 212, 180, 213, 90, 7, 17, 144, 71, 190, 211, 113, 187, 24, 164, 250, 225, 89, 218, 118, 41, 219, 71, 13, 28, 94, 83, 134, 90, 155, 126, 75, 115, 132, 1, 124, 69, 27, 210, 181, 228, 127, 235, 202, 87, 165, 184, 55, 78, 219, 180, 108, 253, 84, 205, 170, 228, 213, 158, 54, 51, 186, 72, 52, 109, 215, 243, 142, 38, 128, 64, 61, 23, 85, 156, 58, 151, 6, 173, 180, 170, 181, 111, 97, 160, 243, 115, 2, 73, 237, 252, 138, 62, 210, 138, 245, 6, 15, 82, 123, 252, 241, 253, 255, 150, 215, 148, 161, 220, 94, 144, 219, 74, 123, 41, 218, 17, 206, 73, 120, 227, 133, 208, 79, 182, 20, 206, 51, 51, 71, 146, 182, 75, 0, 223, 186, 30, 19, 13, 180, 173, 44, 67, 173, 170, 41, 242, 198, 74, 61, 160, 41, 45, 240, 64, 0, 65, 162, 227, 83, 106, 59, 41, 181, 177, 17, 65, 27, 21, 232, 90, 82, 27, 33, 141, 14, 143, 32, 70, 53, 50, 104, 197, 54, 110, 153, 16, 163, 26, 33, 109, 153, 212, 200, 248, 85, 154, 26, 25, 222, 119, 165, 198, 135, 212, 216, 80, 109, 213, 70, 202, 246, 81, 106, 116, 240, 37, 31, 165, 198, 197, 63, 86, 183, 20, 71, 169, 241, 225, 78, 223, 81, 106, 100, 164, 198, 6, 210, 138, 117, 109, 148, 26, 37, 26, 47, 74, 141, 13, 169, 241, 145, 26, 31, 94, 83, 134, 82, 227, 66, 251, 189, 107, 131, 82, 163, 131, 53, 51, 40, 117, 106, 74, 182, 66, 169, 177, 209, 44, 211, 32, 212, 93, 174, 197, 170, 50, 121, 167, 106, 186, 50, 161, 212, 248, 208, 79, 182, 45, 81, 106, 116, 164, 198, 0, 56, 254, 150, 198, 5, 74, 54, 58, 30, 197, 11, 162, 100, 35, 195, 35, 168, 233, 36, 27, 25, 173, 248, 208, 181, 108, 69, 23, 131, 90, 23, 232, 103, 35, 68, 210, 9, 250, 108, 92, 164, 227, 217, 184, 176, 52, 29, 151, 141, 11, 39, 61, 126, 199, 55, 187, 227, 207, 61, 180, 105, 101, 207, 114, 46, 187, 105, 217, 8, 129, 84, 212, 89, 107, 25, 198, 142, 180, 159, 140, 253, 43, 160, 207, 111, 66, 5, 30, 92, 53, 83, 32, 26, 60, 210, 96, 250, 251, 128, 48, 86, 45, 27, 25, 92, 69, 85, 203, 198, 133, 106, 217, 232, 200, 198, 135, 166, 151, 238, 248, 212, 94, 68, 37, 106, 72, 197, 178, 113, 33, 213, 194, 178, 145, 193, 35, 175, 177, 116, 35, 132, 171, 228, 94, 155, 141, 17, 21, 76, 54, 42, 44, 38, 27, 21, 148, 147, 124, 49, 59, 196, 100, 35, 131, 201, 70, 5, 188, 42, 27, 33, 204, 147, 130, 218, 46, 217, 232, 136, 208, 24, 128, 198, 30, 65, 141, 14, 200, 144, 175, 76, 117, 45, 169, 32, 244, 249, 77, 104, 175, 129, 223, 4, 132, 188, 9, 216, 240, 200, 226, 183, 189, 9, 184, 144, 191, 84, 77, 240, 90, 16, 197, 90, 251, 38, 224, 194, 227, 245, 66, 146, 180, 111, 2, 54, 36, 105, 223, 33, 241, 124, 238, 180, 12, 233, 123, 208, 230, 119, 117, 82, 186, 190, 9, 232, 240, 180, 190, 9, 16, 198, 10, 81, 88, 165, 227, 168, 245, 77, 192, 7, 157, 158, 111, 2, 50, 184, 79, 64, 5, 15, 179, 62, 1, 33, 250, 38, 16, 28, 15, 61, 1, 25, 60, 50, 97, 2, 42, 180, 46, 11, 175, 9, 184, 240, 133, 35, 19, 112, 33, 50, 1, 3, 192, 139, 38, 160, 194, 162, 9, 168, 96, 2, 2, 224, 211, 235, 111, 88, 75, 29, 71, 141, 157, 127, 28, 66, 124, 85, 28, 42, 248, 218, 127, 19, 247, 207, 33, 195, 35, 168, 213, 241, 231, 176, 209, 150, 211, 239, 130, 242, 83, 57, 254, 28, 66, 120, 4, 130, 227, 207, 225, 194, 31, 61, 204, 246, 115, 232, 104, 176, 216, 59, 136, 113, 51, 72, 2, 25, 239, 218, 207, 33, 67, 191, 70, 218, 207, 97, 195, 249, 149, 250, 142, 178, 191, 162, 15, 104, 115, 66, 14, 216, 72, 247, 28, 42, 168, 226, 244, 180, 231, 144, 65, 97, 213, 150, 242, 208, 181, 164, 227, 207, 161, 181, 94, 99, 233, 68, 87, 43, 163, 202, 241, 153, 208, 90, 207, 33, 35, 37, 207, 161, 130, 100, 37, 197, 109, 191, 163, 230, 185, 205, 5, 225, 80, 201, 207, 104, 149, 180, 93, 168, 121, 14, 29, 254, 104, 238, 214, 166, 77, 243, 28, 66, 188, 255, 19, 53, 207, 97, 163, 121, 14, 21, 120, 4, 97, 99, 121, 109, 121, 14, 33, 239, 250, 72, 169, 227, 228, 237, 53, 224, 182, 145, 118, 45, 207, 161, 195, 195, 180, 33, 168, 246, 187, 195, 135, 75, 214, 208, 149, 137, 23, 66, 84, 251, 145, 187, 195, 197, 251, 46, 228, 14, 23, 221, 14, 21, 104, 239, 155, 224, 104, 218, 66, 76, 154, 67, 67, 219, 116, 210, 227, 233, 215, 134, 224, 217, 241, 166, 218, 201, 21, 128, 34, 16, 64, 37, 185, 24, 8, 116, 192, 27, 13, 220, 23, 167, 5, 60, 194, 188, 133, 159, 159, 219, 161, 131, 162, 59, 129, 240, 93, 13, 28, 44, 85, 28, 225, 146, 61, 42, 89, 175, 255, 185, 182, 79, 243, 251, 58, 19, 217, 44, 104, 246, 126, 201, 113, 43, 6, 174, 90, 50, 70, 177, 228, 90, 156, 80, 115, 247, 50, 117, 9, 199, 237, 156, 179, 227, 118, 184, 104, 87, 209, 6, 111, 126, 36, 34, 16, 62, 160, 166, 45, 133, 126, 54, 233, 184, 29, 130, 64, 135, 234, 172, 96, 248, 187, 103, 185, 68, 239, 222, 49, 110, 135, 12, 15, 179, 107, 101, 66, 15, 179, 14, 79, 75, 244, 48, 235, 44, 120, 145, 234, 92, 148, 36, 229, 2, 1, 194, 3, 8, 142, 167, 3, 65, 125, 159, 150, 16, 212, 23, 241, 79, 252, 99, 101, 251, 235, 95, 125, 229, 190, 196, 211, 255, 244, 107, 47, 240, 85, 218, 79, 223, 132, 6, 157, 172, 247, 75, 218, 200, 29, 183, 107, 254, 151, 180, 162, 104, 241, 130, 135, 89, 85, 220, 36, 222, 105, 118, 49, 109, 157, 148, 10, 81, 72, 125, 89, 219, 116, 28, 92, 146, 114, 137, 88, 43, 147, 68, 171, 36, 181, 151, 7, 253, 92, 104, 173, 68, 13, 112, 225, 159, 239, 40, 53, 200, 159, 58, 175, 140, 219, 225, 4, 227, 118, 248, 104, 26, 225, 118, 200, 160, 44, 27, 250, 164, 17, 110, 247, 208, 185, 32, 6, 22, 164, 233, 41, 110, 135, 15, 14, 26, 67, 120, 211, 137, 36, 105, 191, 162, 141, 194, 223, 183, 226, 48, 129, 43, 122, 224, 162, 225, 133, 18, 183, 67, 198, 243, 194, 237, 144, 97, 53, 16, 116, 225, 118, 200, 208, 207, 201, 194, 35, 159, 82, 59, 39, 25, 65, 105, 31, 24, 43, 148, 246, 145, 76, 157, 228, 72, 130, 249, 182, 67, 136, 71, 244, 233, 155, 240, 252, 54, 111, 59, 124, 136, 48, 56, 133, 174, 37, 27, 190, 206, 163, 111, 219, 140, 79, 169, 221, 180, 133, 55, 78, 235, 168, 232, 98, 148, 109, 59, 108, 112, 167, 100, 16, 86, 78, 180, 98, 221, 177, 43, 61, 36, 105, 223, 4, 103, 232, 81, 182, 223, 78, 67, 115, 183, 230, 167, 122, 84, 120, 121, 30, 30, 72, 165, 33, 17, 161, 191, 116, 226, 12, 188, 98, 91, 54, 205, 3, 200, 73, 70, 154, 29, 49, 80, 81, 151, 20, 223, 118, 185, 62, 168, 78, 213, 242, 29, 53, 203, 52, 118, 32, 73, 234, 36, 215, 127, 22, 212, 186, 255, 218, 14, 29, 190, 169, 107, 59, 92, 168, 226, 166, 237, 208, 1, 169, 166, 78, 219, 33, 68, 37, 47, 109, 135, 12, 22, 29, 152, 67, 74, 27, 33, 218, 174, 29, 50, 124, 106, 155, 180, 29, 50, 156, 38, 109, 135, 14, 10, 33, 220, 227, 110, 80, 203, 254, 6, 95, 209, 111, 14, 13, 159, 82, 219, 33, 131, 227, 207, 33, 109, 135, 12, 143, 52, 109, 42, 239, 28, 54, 158, 206, 94, 118, 245, 59, 135, 20, 12, 183, 115, 184, 224, 75, 21, 179, 93, 28, 116, 45, 195, 178, 155, 92, 229, 64, 99, 95, 245, 92, 4, 192, 96, 165, 39, 234, 1, 124, 229, 1, 109, 78, 232, 65, 155, 86, 38, 129, 98, 143, 6, 141, 61, 2, 81, 109, 25, 39, 189, 50, 13, 14, 218, 16, 111, 188, 159, 116, 66, 63, 215, 226, 7, 95, 251, 111, 193, 154, 224, 139, 82, 144, 42, 110, 12, 48, 64, 173, 239, 82, 53, 224, 146, 45, 176, 40, 39, 61, 118, 184, 208, 148, 253, 236, 112, 33, 73, 233, 158, 29, 46, 116, 217, 78, 46, 233, 100, 239, 172, 153, 225, 109, 201, 212, 218, 183, 188, 179, 91, 236, 157, 5, 139, 189, 131, 26, 118, 60, 130, 26, 118, 28, 169, 255, 44, 218, 141, 118, 243, 238, 184, 236, 6, 82, 81, 213, 182, 98, 173, 165, 76, 180, 101, 147, 149, 19, 138, 213, 175, 146, 223, 30, 7, 110, 38, 77, 219, 111, 246, 135, 239, 100, 222, 57, 65, 186, 28, 219, 103, 79, 87, 38, 125, 98, 125, 251, 131, 244, 148, 54, 211, 230, 175, 184, 7, 73, 219, 133, 158, 150, 200, 61, 87, 66, 172, 158, 186, 123, 26, 76, 154, 162, 87, 215, 174, 44, 163, 237, 26, 104, 202, 229, 24, 72, 82, 123, 65, 218, 164, 83, 45, 157, 111, 118, 90, 39, 188, 0, 131, 231, 55, 165, 227, 6, 86, 191, 5, 170, 56, 109, 123, 222, 180, 101, 251, 5, 96, 224, 201, 229, 124, 21, 210, 138, 109, 15, 164, 90, 251, 169, 61, 78, 177, 50, 241, 26, 128, 11, 128, 101, 167, 91, 224, 160, 49, 131, 116, 189, 78, 167, 105, 188, 190, 169, 90, 7, 149, 156, 188, 38, 150, 92, 30, 113, 52, 147, 182, 221, 236, 22, 120, 245, 180, 202, 178, 153, 71, 37, 200, 61, 45, 131, 197, 222, 201, 165, 233, 138, 58, 15, 222, 234, 150, 58, 217, 48, 104, 102, 246, 33, 166, 239, 143, 163, 111, 14, 180, 117, 126, 41, 20, 186, 150, 245, 157, 32, 127, 137, 153, 202, 50, 200, 155, 183, 84, 116, 57, 3, 93, 175, 145, 106, 250, 21, 135, 73, 211, 199, 83, 39, 28, 33, 93, 203, 99, 2, 40, 245, 215, 158, 170, 231, 29, 211, 207, 180, 203, 61, 181, 128, 3, 213, 153, 124, 213, 62, 200, 85, 202, 16, 227, 247, 71, 193, 238, 9, 65, 190, 162, 205, 61, 33, 247, 180, 160, 117, 210, 24, 180, 101, 16, 75, 211, 186, 92, 63, 151, 41, 233, 162, 205, 128, 98, 182, 192, 66, 89, 30, 145, 142, 91, 137, 42, 234, 52, 120, 178, 27, 212, 188, 56, 237, 74, 15, 7, 76, 23, 137, 103, 238, 247, 87, 119, 135, 103, 57, 215, 144, 90, 78, 84, 162, 5, 220, 211, 59, 175, 124, 103, 135, 15, 77, 219, 198, 251, 235, 236, 212, 217, 97, 195, 217, 161, 130, 214, 214, 50, 61, 252, 155, 29, 46, 180, 170, 182, 204, 55, 59, 116, 232, 155, 160, 149, 223, 172, 12, 114, 232, 160, 212, 31, 250, 102, 135, 141, 86, 127, 19, 237, 84, 16, 131, 135, 167, 95, 219, 129, 162, 105, 171, 248, 129, 174, 180, 238, 216, 33, 163, 117, 153, 85, 204, 144, 86, 172, 163, 74, 50, 212, 234, 28, 59, 116, 160, 206, 14, 237, 51, 115, 236, 176, 161, 236, 212, 241, 63, 139, 29, 50, 96, 235, 248, 179, 216, 33, 195, 181, 237, 250, 101, 8, 39, 154, 182, 206, 237, 74, 8, 33, 148, 58, 190, 139, 29, 50, 116, 45, 131, 142, 86, 138, 238, 4, 49, 220, 184, 89, 8, 135, 111, 203, 14, 27, 116, 45, 195, 14, 25, 34, 17, 216, 185, 230, 80, 162, 237, 130, 16, 178, 236, 170, 224, 6, 121, 120, 164, 105, 14, 23, 154, 195, 0, 84, 152, 67, 5, 191, 141, 153, 63, 142, 34, 48, 101, 17, 152, 67, 7, 253, 124, 238, 134, 23, 138, 192, 28, 54, 68, 96, 14, 29, 141, 43, 137, 34, 48, 135, 12, 108, 52, 188, 28, 205, 221, 138, 16, 199, 203, 161, 194, 251, 46, 135, 11, 173, 21, 93, 14, 25, 42, 204, 104, 47, 35, 161, 205, 15, 236, 45, 227, 118, 18, 154, 64, 7, 99, 238, 39, 151, 67, 70, 179, 247, 45, 151, 67, 8, 109, 187, 158, 197, 14, 241, 163, 206, 150, 203, 161, 3, 181, 185, 92, 83, 46, 135, 13, 143, 172, 196, 225, 2, 51, 109, 27, 137, 195, 133, 123, 168, 145, 56, 100, 104, 36, 14, 27, 205, 58, 152, 198, 9, 53, 18, 135, 12, 9, 120, 146, 56, 92, 240, 8, 4, 16, 120, 146, 56, 100, 48, 237, 202, 58, 100, 240, 8, 132, 93, 56, 191, 204, 25, 102, 21, 90, 90, 89, 135, 142, 108, 155, 124, 29, 46, 92, 63, 123, 29, 46, 250, 233, 191, 186, 117, 216, 224, 69, 234, 214, 225, 226, 189, 173, 195, 5, 87, 239, 20, 251, 235, 177, 8, 247, 239, 52, 82, 229, 226, 244, 248, 102, 247, 41, 251, 125, 73, 196, 109, 29, 54, 224, 182, 14, 21, 28, 116, 37, 109, 29, 50, 228, 182, 117, 184, 208, 180, 117, 168, 224, 17, 102, 43, 186, 14, 25, 218, 164, 63, 181, 14, 25, 170, 253, 106, 29, 46, 214, 90, 70, 251, 125, 90, 199, 56, 173, 195, 6, 167, 117, 232, 120, 22, 36, 73, 235, 176, 33, 211, 58, 84, 112, 28, 58, 90, 255, 83, 33, 231, 56, 148, 104, 118, 156, 179, 227, 208, 65, 27, 59, 14, 23, 42, 219, 148, 29, 135, 139, 95, 251, 22, 109, 135, 36, 60, 194, 248, 91, 58, 14, 27, 238, 233, 26, 135, 12, 174, 113, 216, 136, 176, 38, 56, 106, 28, 46, 22, 167, 198, 161, 3, 53, 14, 33, 26, 135, 14, 212, 56, 108, 160, 198, 97, 99, 2, 223, 22, 53, 14, 23, 141, 195, 134, 195, 134, 123, 104, 28, 82, 52, 14, 3, 80, 161, 14, 21, 72, 90, 54, 196, 243, 219, 233, 254, 233, 231, 98, 84, 33, 137, 180, 142, 147, 111, 4, 130, 178, 71, 225, 134, 52, 181, 100, 205, 76, 171, 195, 135, 206, 37, 66, 181, 116, 84, 155, 65, 159, 20, 194, 37, 204, 78, 5, 92, 210, 201, 16, 211, 234, 208, 241, 164, 86, 135, 11, 30, 113, 234, 112, 65, 253, 177, 243, 77, 29, 54, 90, 91, 83, 135, 12, 170, 169, 67, 5, 166, 14, 21, 120, 164, 81, 135, 11, 106, 81, 171, 58, 92, 168, 58, 124, 48, 202, 97, 227, 125, 21, 114, 120, 132, 81, 14, 23, 222, 147, 81, 14, 25, 150, 93, 136, 81, 14, 23, 159, 28, 58, 24, 43, 20, 225, 118, 149, 197, 225, 227, 27, 34, 48, 74, 178, 56, 100, 144, 40, 71, 239, 218, 104, 165, 123, 210, 61, 233, 112, 225, 47, 29, 66, 86, 78, 156, 119, 38, 130, 243, 233, 112, 193, 35, 16, 22, 94, 13, 226, 167, 208, 212, 143, 189, 194, 237, 168, 172, 115, 67, 59, 207, 200, 254, 138, 42, 203, 246, 21, 69, 11, 212, 51, 53, 156, 79, 135, 14, 231, 211, 161, 130, 147, 62, 29, 54, 168, 102, 202, 67, 143, 19, 114, 79, 135, 19, 173, 255, 75, 121, 136, 166, 18, 254, 167, 209, 207, 126, 23, 64, 132, 149, 137, 151, 131, 243, 47, 93, 170, 245, 148, 72, 167, 46, 200, 253, 59, 20, 17, 104, 16, 1, 2, 6, 206, 191, 228, 5, 17, 32, 36, 204, 139, 166, 100, 34, 24, 191, 191, 132, 240, 65, 61, 211, 202, 228, 43, 147, 167, 195, 135, 71, 30, 93, 140, 109, 99, 109, 63, 198, 178, 171, 37, 139, 249, 81, 183, 254, 39, 67, 61, 97, 241, 232, 98, 52, 246, 136, 59, 191, 20, 36, 65, 58, 146, 32, 66, 181, 31, 69, 16, 64, 211, 182, 1, 9, 24, 58, 245, 249, 109, 79, 187, 249, 211, 126, 62, 197, 171, 167, 110, 125, 151, 42, 145, 51, 168, 129, 55, 205, 161, 40, 238, 233, 148, 101, 67, 141, 61, 194, 64, 89, 54, 148, 182, 149, 189, 230, 140, 17, 209, 246, 115, 61, 42, 105, 52, 246, 200, 183, 6, 196, 45, 117, 22, 94, 142, 213, 15, 45, 207, 89, 253, 16, 59, 237, 5, 202, 178, 161, 181, 30, 243, 224, 224, 248, 174, 166, 169, 67, 219, 100, 172, 24, 102, 29, 219, 15, 229, 167, 114, 72, 187, 152, 198, 205, 235, 241, 21, 85, 156, 22, 171, 64, 76, 124, 99, 134, 45, 18, 205, 206, 63, 8, 54, 248, 194, 215, 213, 121, 124, 83, 135, 95, 228, 82, 61, 19, 98, 30, 113, 64, 160, 196, 42, 168, 119, 186, 116, 233, 176, 241, 154, 150, 14, 23, 88, 146, 14, 21, 254, 39, 48, 196, 78, 163, 249, 19, 29, 84, 211, 86, 155, 116, 216, 224, 208, 241, 78, 179, 141, 84, 58, 108, 168, 116, 168, 64, 61, 130, 86, 63, 8, 15, 179, 13, 156, 207, 8, 199, 211, 65, 16, 92, 120, 4, 105, 74, 135, 139, 148, 14, 33, 41, 29, 62, 104, 37, 33, 38, 54, 161, 7, 2, 8, 210, 33, 99, 173, 100, 90, 63, 41, 200, 225, 163, 153, 65, 191, 73, 65, 14, 29, 79, 63, 114, 184, 192, 154, 25, 124, 244, 3, 57, 116, 40, 99, 127, 109, 228, 112, 225, 112, 225, 112, 161, 110, 29, 114, 200, 224, 146, 33, 231, 112, 225, 169, 200, 133, 28, 50, 252, 34, 135, 28, 66, 158, 181, 178, 45, 131, 28, 54, 152, 89, 6, 57, 100, 120, 218, 225, 194, 117, 91, 29, 57, 41, 21, 57, 108, 104, 101, 84, 33, 135, 11, 228, 150, 166, 150, 9, 185, 212, 30, 79, 228, 176, 225, 105, 137, 28, 46, 32, 68, 235, 132, 81, 33, 181, 199, 209, 115, 255, 24, 33, 90, 159, 92, 255, 24, 25, 223, 84, 253, 99, 92, 188, 63, 70, 5, 39, 61, 70, 133, 101, 151, 123, 70, 134, 138, 51, 62, 156, 79, 231, 140, 12, 46, 155, 51, 46, 48, 46, 212, 25, 27, 207, 90, 150, 12, 114, 198, 134, 51, 6, 96, 101, 219, 102, 155, 145, 81, 209, 70, 27, 53, 205, 216, 88, 217, 86, 53, 227, 194, 83, 219, 29, 183, 91, 128, 148, 182, 170, 102, 164, 200, 86, 150, 9, 253, 46, 205, 216, 96, 81, 186, 82, 54, 35, 35, 155, 81, 129, 191, 99, 84, 240, 8, 90, 9, 65, 134, 2, 48, 71, 70, 235, 59, 198, 133, 199, 157, 99, 92, 104, 221, 126, 237, 136, 213, 207, 35, 16, 212, 61, 255, 104, 218, 166, 236, 151, 240, 35, 36, 225, 127, 32, 225, 119, 119, 141, 107, 234, 24, 23, 106, 213, 54, 117, 140, 12, 58, 217, 166, 142, 209, 193, 31, 73, 120, 104, 39, 165, 185, 129, 166, 141, 42, 78, 104, 1, 87, 247, 252, 154, 192, 143, 214, 101, 63, 191, 253, 146, 58, 240, 100, 234, 36, 7, 141, 119, 57, 150, 254, 176, 192, 131, 214, 212, 202, 9, 53, 216, 149, 144, 54, 173, 76, 155, 86, 214, 208, 216, 35, 14, 72, 154, 41, 202, 178, 153, 8, 134, 89, 72, 235, 191, 58, 98, 128, 15, 77, 29, 163, 68, 83, 199, 168, 176, 210, 49, 66, 84, 251, 209, 110, 242, 68, 204, 216, 176, 175, 169, 55, 198, 5, 55, 70, 5, 223, 30, 179, 198, 184, 88, 141, 209, 241, 172, 198, 168, 160, 108, 191, 237, 210, 198, 184, 80, 182, 143, 180, 49, 50, 180, 49, 163, 163, 49, 50, 52, 53, 70, 133, 166, 239, 187, 208, 107, 67, 141, 177, 161, 49, 58, 84, 155, 65, 146, 212, 133, 126, 43, 138, 26, 99, 132, 71, 32, 252, 50, 198, 197, 243, 142, 169, 195, 241, 84, 188, 16, 132, 83, 85, 232, 151, 49, 54, 152, 109, 26, 198, 200, 160, 255, 186, 148, 49, 50, 42, 188, 184, 100, 104, 49, 62, 44, 198, 0, 36, 181, 140, 15, 39, 189, 50, 15, 115, 24, 27, 12, 180, 57, 161, 180, 189, 36, 131, 246, 45, 80, 42, 145, 166, 101, 24, 70, 136, 214, 87, 198, 138, 145, 241, 180, 190, 9, 143, 79, 197, 8, 113, 212, 120, 23, 248, 149, 240, 39, 198, 197, 202, 79, 140, 11, 204, 174, 196, 184, 144, 24, 29, 204, 91, 24, 21, 188, 49, 8, 29, 148, 23, 70, 133, 137, 39, 196, 143, 220, 225, 201, 238, 108, 230, 109, 52, 52, 10, 123, 58, 222, 172, 26, 133, 95, 155, 1, 48, 110, 70, 162, 129, 66, 128, 240, 235, 11, 209, 10, 213, 102, 218, 123, 120, 152, 85, 237, 247, 166, 19, 241, 83, 48, 172, 21, 235, 254, 168, 196, 193, 37, 67, 207, 79, 56, 158, 78, 4, 131, 6, 165, 15, 143, 123, 32, 132, 59, 110, 135, 34, 11, 99, 67, 61, 19, 138, 44, 140, 12, 8, 22, 70, 5, 90, 177, 205, 17, 30, 206, 191, 100, 92, 144, 172, 164, 32, 79, 198, 136, 214, 137, 39, 35, 196, 147, 177, 193, 147, 209, 209, 120, 37, 200, 147, 209, 193, 147, 81, 193, 164, 233, 100, 92, 72, 79, 67, 5, 149, 134, 1, 88, 151, 78, 105, 200, 208, 20, 109, 250, 53, 104, 104, 19, 128, 96, 209, 87, 94, 195, 198, 87, 94, 67, 133, 149, 173, 236, 53, 92, 112, 47, 236, 53, 100, 80, 202, 104, 11, 226, 111, 8, 113, 10, 183, 243, 143, 181, 94, 67, 71, 74, 94, 195, 134, 229, 53, 84, 208, 36, 197, 27, 46, 120, 67, 134, 243, 134, 10, 180, 115, 37, 85, 222, 176, 209, 19, 26, 42, 112, 215, 176, 209, 176, 129, 11, 39, 173, 67, 65, 204, 173, 149, 168, 189, 126, 180, 215, 238, 124, 186, 134, 243, 233, 190, 45, 82, 237, 71, 43, 153, 107, 8, 193, 92, 195, 135, 183, 42, 115, 13, 39, 188, 241, 254, 75, 205, 12, 98, 174, 225, 2, 166, 49, 163, 109, 195, 11, 237, 106, 112, 253, 84, 238, 161, 8, 230, 26, 54, 126, 213, 75, 196, 28, 115, 13, 23, 204, 53, 84, 224, 26, 6, 160, 245, 115, 33, 213, 18, 81, 184, 97, 3, 58, 158, 231, 134, 10, 238, 243, 125, 159, 27, 54, 120, 33, 72, 82, 58, 125, 110, 248, 64, 134, 71, 32, 56, 213, 229, 220, 208, 225, 139, 110, 184, 39, 8, 75, 213, 161, 52, 147, 228, 14, 207, 232, 66, 15, 141, 196, 97, 195, 195, 236, 59, 110, 184, 208, 38, 253, 161, 119, 220, 208, 49, 65, 4, 199, 220, 112, 225, 209, 24, 228, 116, 37, 204, 13, 23, 206, 184, 161, 2, 227, 198, 184, 33, 3, 55, 100, 124, 107, 15, 141, 89, 163, 112, 147, 172, 165, 20, 138, 229, 253, 131, 197, 236, 144, 123, 166, 180, 211, 241, 111, 118, 234, 14, 212, 118, 163, 154, 186, 3, 13, 124, 179, 99, 160, 117, 109, 50, 39, 165, 238, 82, 197, 13, 31, 44, 74, 65, 30, 129, 200, 69, 55, 242, 219, 44, 40, 188, 24, 8, 77, 191, 162, 16, 156, 170, 132, 185, 194, 109, 5, 99, 177, 124, 244, 119, 161, 197, 42, 13, 21, 95, 149, 197, 236, 144, 171, 212, 118, 30, 181, 74, 154, 29, 68, 48, 65, 5, 181, 165, 188, 54, 148, 150, 189, 44, 162, 120, 47, 2, 164, 19, 163, 79, 217, 234, 174, 191, 46, 209, 243, 130, 88, 118, 163, 75, 127, 129, 147, 94, 153, 86, 247, 220, 86, 32, 87, 197, 9, 69, 248, 83, 254, 249, 15, 199, 93, 146, 114, 57, 32, 72, 167, 21, 169, 90, 251, 14, 144, 123, 186, 123, 122, 35, 61, 2, 225, 207, 130, 26, 76, 208, 246, 103, 85, 39, 47, 142, 167, 146, 104, 203, 84, 34, 165, 237, 144, 18, 38, 37, 170, 159, 119, 140, 231, 213, 60, 139, 93, 227, 84, 177, 3, 129, 243, 47, 185, 105, 235, 159, 109, 66, 146, 216, 73, 255, 26, 89, 11, 81, 139, 180, 173, 147, 146, 61, 72, 214, 91, 208, 179, 154, 134, 87, 4, 19, 84, 80, 229, 181, 93, 14, 234, 28, 73, 142, 17, 193, 4, 223, 86, 25, 104, 218, 137, 170, 141, 224, 254, 29, 135, 90, 199, 109, 69, 195, 61, 13, 132, 169, 96, 124, 241, 244, 107, 71, 112, 156, 244, 202, 56, 62, 93, 197, 31, 86, 78, 212, 155, 92, 12, 3, 93, 203, 160, 73, 211, 103, 57, 109, 180, 54, 153, 34, 85, 220, 156, 166, 138, 178, 8, 229, 159, 239, 13, 59, 244, 102, 191, 75, 34, 171, 26, 38, 170, 186, 161, 88, 114, 72, 2, 167, 13, 168, 228, 138, 136, 3, 132, 68, 242, 76, 162, 70, 73, 142, 41, 197, 148, 162, 162, 218, 0, 51, 18, 200, 97, 35, 113, 56, 26, 11, 3, 73, 22, 237, 1, 20, 128, 2, 11, 10, 197, 57, 237, 74, 18, 131, 36, 133, 138, 4, 4, 32, 3, 32, 1, 160, 0, 202, 5, 25, 88, 80, 108, 229, 116, 212, 199, 83, 154, 70, 218, 47, 23, 43, 66, 24, 212, 49, 246, 133, 246, 114, 219, 107, 207, 51, 13, 240, 61, 228, 153, 1, 201, 127, 238, 54, 60, 224, 38, 190, 105, 108, 181, 160, 198, 73, 159, 69, 4, 184, 13, 22, 57, 183, 4, 88, 110, 186, 46, 161, 202, 138, 254, 93, 79, 10, 7, 232, 252, 70, 80, 41, 74, 232, 241, 175, 54, 129, 81, 18, 103, 140, 64, 127, 208, 134, 126, 79, 65, 178, 6, 37, 173, 21, 60, 246, 46, 57, 234, 16, 109, 117, 139, 26, 251, 201, 9, 76, 180, 223, 14, 158, 143, 253, 103, 209, 7, 81, 21, 74, 161, 112, 245, 40, 202, 206, 142, 201, 43, 183, 186, 247, 75, 203, 227, 214, 192, 173, 26, 16, 118, 125, 130, 107, 130, 20, 210, 59, 137, 204, 5, 134, 71, 170, 34, 28, 77, 187, 65, 134, 153, 221, 102, 22, 137, 19, 146, 89, 121, 160, 123, 167, 193, 172, 104, 92, 112, 141, 195, 191, 248, 131, 125, 114, 117, 97, 15, 10, 79, 84, 50, 156, 242, 96, 129, 63, 17, 224, 192, 61, 90, 16, 74, 207, 236, 187, 120, 3, 89, 243, 53, 36, 135, 46, 160, 156, 126, 167, 238, 51, 156, 104, 60, 170, 181, 187, 24, 28, 99, 112, 9, 69, 163, 207, 157, 149, 60, 15, 176, 126, 109, 103, 59, 22, 163, 85, 122, 34, 169, 85, 146, 246, 67, 135, 73, 2, 13, 28, 166, 225, 80, 19, 172, 193, 210, 143, 66, 250, 129, 197, 103, 45, 249, 137, 180, 225, 3, 152, 208, 23, 7, 94, 198, 29, 226, 233, 10, 128, 67, 233, 213, 88, 106, 214, 76, 124, 235, 88, 80, 66, 124, 175, 72, 184, 233, 113, 232, 161, 22, 166, 134, 14, 92, 19, 152, 227, 22, 37, 49, 139, 110, 207, 254, 74, 86, 195, 129, 49, 58, 106, 71, 67, 137, 213, 67, 168, 111, 132, 253, 62, 134, 220, 185, 86, 83, 76, 215, 64, 164, 171, 42, 10, 217, 20, 211, 129, 219, 133, 126, 63, 37, 216, 182, 5, 184, 214, 178, 138, 69, 61, 180, 112, 181, 248, 172, 153, 181, 66, 159, 193, 83, 250, 231, 17, 80, 138, 142, 161, 163, 200, 220, 62, 178, 111, 182, 188, 43, 208, 174, 87, 60, 136, 95, 236, 46, 37, 2, 153, 30, 49, 107, 33, 69, 182, 88, 138, 67, 125, 179, 61, 23, 126, 27, 246, 111, 181, 94, 31, 241, 173, 1, 245, 32, 177, 247, 218, 85, 17, 6, 15, 158, 121, 150, 149, 55, 134, 141, 26, 93, 32, 101, 97, 62, 148, 237, 148, 191, 204, 8, 79, 6, 45, 246, 134, 59, 78, 188, 81, 109, 30, 102, 185, 92, 26, 31, 100, 85, 33, 89, 183, 64, 40, 72, 159, 102, 187, 225, 197, 209, 137, 183, 207, 71, 196, 252, 18, 8, 181, 171, 62, 2, 230, 27, 49, 241, 129, 160, 217, 204, 236, 134, 131, 184, 141, 132, 58, 34, 132, 5, 153, 234, 166, 4, 95, 196, 151, 115, 221, 72, 80, 4, 100, 212, 127, 158, 47, 26, 48, 129, 232, 9, 237, 71, 2, 139, 56, 0, 198, 96, 194, 110, 99, 56, 209, 203, 16, 178, 201, 192, 203, 206, 54, 199, 254, 135, 43, 83, 153, 187, 135, 218, 167, 74, 8, 214, 95, 44, 163, 211, 150, 81, 32, 238, 28, 240, 177, 82, 154, 0, 243, 129, 85, 224, 157, 187, 187, 216, 7, 197, 95, 21, 29, 92, 126, 75, 82, 65, 114, 98, 241, 2, 110, 25, 12, 137, 96, 156, 174, 102, 155, 41, 170, 190, 97, 59, 239, 0, 225, 22, 9, 32, 235, 26, 146, 190, 35, 45, 113, 0, 94, 132, 34, 170, 175, 22, 24, 136, 202, 193, 16, 93, 157, 170, 209, 233, 71, 153, 209, 45, 6, 217, 116, 35, 162, 52, 42, 232, 179, 232, 32, 47, 36, 227, 159, 99, 113, 95, 118, 159, 216, 203, 141, 136, 179, 248, 111, 187, 69, 198, 104, 127, 228, 217, 83, 14, 164, 121, 190, 182, 191, 114, 70, 71, 236, 180, 244, 240, 239, 87, 235, 243, 38, 163, 91, 216, 182, 125, 207, 79, 63, 27, 87, 133, 95, 64, 113, 150, 70, 195, 180, 117, 236, 242, 187, 74, 27, 236, 165, 241, 124, 175, 221, 214, 115, 133, 249, 148, 67, 4, 174, 218, 7, 207, 142, 13, 67, 43, 100, 48, 136, 206, 127, 23, 137, 1, 133, 165, 77, 75, 153, 102, 71, 127, 208, 78, 30, 248, 61, 23, 51, 235, 173, 25, 38, 129, 160, 178, 48, 249, 75, 175, 134, 45, 245, 200, 11, 218, 89, 243, 248, 86, 198, 99, 69, 122, 244, 41, 148, 71, 143, 210, 25, 68, 135, 149, 44, 48, 195, 90, 26, 65, 158, 226, 40, 56, 143, 59, 250, 64, 38, 79, 59, 93, 218, 84, 183, 57, 93, 243, 176, 187, 163, 8, 144, 147, 106, 112, 171, 37, 129, 151, 85, 49, 140, 95, 28, 185, 160, 118, 174, 241, 179, 238, 12, 84, 33, 135, 17, 187, 168, 197, 234, 35, 12, 133, 224, 157, 60, 237, 115, 84, 62, 162, 91, 157, 130, 71, 129, 122, 179, 72, 208, 176, 247, 77, 115, 169, 71, 37, 243, 26, 250, 178, 50, 106, 193, 219, 159, 80, 35, 88, 19, 137, 20, 18, 230, 78, 219, 10, 127, 197, 51, 127, 132, 30, 33, 13, 210, 194, 71, 231, 181, 118, 137, 136, 86, 252, 183, 57, 26, 108, 95, 140, 120, 1, 70, 232, 242, 26, 105, 70, 34, 65, 40, 210, 111, 74, 188, 210, 225, 81, 222, 64, 240, 126, 148, 147, 198, 11, 254, 49, 42, 14, 80, 226, 24, 33, 114, 33, 26, 130, 8, 29, 133, 98, 83, 183, 148, 90, 156, 121, 98, 182, 172, 147, 66, 126, 76, 56, 39, 181, 102, 227, 163, 225, 127, 164, 118, 233, 53, 202, 183, 252, 122, 77, 85, 179, 252, 24, 202, 28, 64, 64, 162, 244, 117, 202, 57, 181, 104, 17, 247, 167, 91, 240, 188, 26, 27, 109, 37, 113, 54, 205, 48, 83, 146, 89, 82, 244, 46, 244, 56, 133, 52, 246, 226, 28, 72, 57, 213, 180, 6, 232, 196, 27, 146, 252, 144, 215, 87, 149, 15, 31, 182, 155, 81, 96, 173, 162, 16, 207, 103, 152, 83, 8, 233, 188, 92, 117, 180, 129, 158, 136, 65, 111, 196, 11, 80, 8, 217, 142, 33, 244, 123, 234, 131, 226, 153, 34, 201, 99, 86, 101, 73, 219, 41, 171, 82, 110, 30, 24, 80, 218, 161, 57, 118, 132, 254, 161, 205, 70, 139, 114, 145, 181, 138, 36, 166, 229, 216, 124, 113, 67, 139, 139, 42, 210, 34, 28, 19, 83, 60, 115, 97, 48, 152, 99, 213, 85, 59, 66, 54, 146, 189, 194, 100, 65, 112, 186, 5, 45, 119, 128, 213, 130, 141, 187, 211, 80, 175, 146, 192, 168, 137, 11, 83, 90, 85, 97, 156, 129, 116, 247, 246, 62, 67, 246, 149, 75, 183, 134, 114, 209, 200, 156, 85, 27, 196, 163, 218, 151, 177, 75, 204, 11, 133, 187, 48, 183, 244, 239, 178, 85, 91, 207, 160, 35, 185, 112, 156, 174, 113, 139, 215, 50, 175, 84, 58, 145, 185, 31, 70, 1, 242, 22, 7, 232, 63, 31, 208, 117, 3, 231, 100, 131, 120, 64, 108, 184, 216, 120, 76, 153, 248, 0, 140, 4, 81, 23, 178, 109, 89, 188, 128, 104, 182, 190, 237, 240, 63, 102, 247, 62, 0, 77, 100, 61, 72, 130, 145, 111, 222, 236, 16, 105, 203, 133, 160, 255, 96, 45, 113, 170, 201, 43, 31, 229, 26, 148, 168, 201, 251, 176, 247, 164, 178, 181, 245, 152, 62, 37, 14, 156, 99, 234, 30, 136, 232, 249, 179, 51, 67, 140, 150, 134, 21, 156, 120, 201, 89, 141, 254, 62, 105, 243, 216, 231, 225, 190, 14, 40, 198, 229, 47, 100, 217, 228, 239, 142, 211, 234, 235, 199, 110, 76, 154, 105, 132, 76, 35, 180, 162, 147, 151, 203, 238, 50, 61, 97, 249, 205, 87, 217, 39, 42, 148, 88, 155, 6, 52, 248, 130, 238, 214, 57, 24, 127, 153, 16, 233, 94, 172, 42, 232, 209, 2, 172, 201, 181, 110, 11, 208, 108, 72, 150, 122, 13, 55, 95, 179, 245, 154, 250, 196, 59, 101, 67, 236, 179, 214, 62, 110, 80, 55, 7, 94, 8, 65, 200, 140, 180, 189, 17, 25, 80, 74, 177, 183, 142, 51, 171, 61, 236, 108, 124, 193, 137, 74, 41, 252, 255, 224, 0, 143, 238, 14, 143, 62, 124, 16, 185, 20, 22, 119, 13, 61, 4, 18, 245, 123, 141, 197, 35, 224, 233, 132, 38, 64, 50, 254, 201, 114, 99, 174, 35, 173, 227, 238, 158, 66, 161, 113, 222, 159, 0, 164, 69, 27, 136, 28, 142, 204, 215, 137, 32, 115, 54, 66, 41, 62, 108, 200, 15, 221, 73, 229, 119, 83, 27, 238, 110, 251, 107, 6, 112, 156, 22, 0, 164, 239, 236, 148, 88, 1, 125, 246, 48, 156, 103, 16, 97, 88, 221, 93, 133, 133, 204, 50, 191, 86, 234, 111, 172, 110, 55, 53, 56, 154, 29, 150, 175, 174, 200, 142, 104, 85, 246, 208, 238, 46, 210, 55, 118, 49, 188, 243, 131, 90, 252, 120, 156, 234, 136, 0, 49, 22, 193, 107, 106, 226, 169, 246, 33, 74, 182, 200, 25, 223, 69, 142, 221, 148, 177, 144, 17, 171, 49, 158, 220, 255, 66, 156, 252, 56, 41, 64, 40, 156, 75, 84, 67, 50, 226, 97, 246, 189, 4, 133, 173, 21, 26, 240, 155, 19, 48, 56, 93, 82, 133, 233, 49, 86, 82, 73, 3, 30, 108, 163, 177, 173, 195, 106, 124, 149, 249, 75, 107, 107, 144, 43, 91, 205, 78, 102, 156, 58, 167, 78, 246, 233, 68, 79, 152, 254, 129, 203, 232, 139, 227, 59, 84, 159, 110, 92, 229, 23, 87, 29, 201, 215, 233, 161, 84, 210, 16, 58, 35, 123, 166, 209, 32, 79, 3, 168, 243, 195, 112, 152, 178, 26, 118, 1, 115, 76, 222, 8, 225, 161, 178, 123, 249, 95, 104, 229, 115, 155, 52, 68, 234, 1, 229, 144, 201, 119, 179, 94, 18, 180, 151, 5, 80, 162, 22, 199, 5, 96, 30, 197, 247, 27, 238, 59, 171, 145, 11, 240, 6, 248, 246, 150, 236, 8, 223, 178, 159, 1, 164, 233, 240, 118, 55, 35, 88, 89, 114, 124, 112, 243, 38, 155, 74, 57, 132, 201, 69, 227, 170, 233, 28, 232, 227, 19, 231, 217, 252, 205, 5, 85, 206, 18, 188, 175, 57, 85, 81, 224, 186, 112, 146, 16, 48, 72, 197, 245, 124, 9, 131, 197, 240, 75, 227, 235, 242, 225, 184, 10, 151, 128, 124, 182, 65, 96, 101, 109, 102, 103, 47, 79, 46, 130, 203, 190, 205, 114, 228, 131, 170, 182, 175, 178, 196, 195, 199, 188, 195, 151, 130, 106, 98, 14, 165, 6, 82, 116, 164, 152, 156, 24, 3, 49, 85, 136, 12, 68, 95, 35, 212, 168, 35, 52, 71, 19, 91, 142, 184, 95, 195, 252, 224, 182, 173, 198, 204, 137, 13, 254, 34, 70, 140, 14, 16, 48, 160, 251, 148, 53, 73, 13, 152, 82, 226, 194, 44, 137, 60, 15, 71, 208, 199, 67, 75, 55, 58, 55, 9, 247, 179, 48, 56, 42, 209, 127, 6, 61, 192, 59, 8, 170, 64, 219, 250, 7, 173, 219, 68, 161, 113, 244, 101, 80, 39, 170, 175, 58, 175, 86, 129, 145, 40, 164, 234, 20, 66, 39, 102, 111, 16, 123, 10, 192, 27, 28, 80, 26, 87, 184, 186, 202, 126, 124, 141, 7, 35, 21, 235, 3, 81, 129, 79, 146, 19, 154, 190, 2, 175, 177, 0, 231, 28, 151, 73, 147, 239, 144, 128, 220, 180, 167, 167, 82, 221, 99, 57, 168, 246, 251, 35, 203, 180, 84, 102, 68, 249, 138, 37, 17, 229, 65, 217, 99, 21, 82, 37, 161, 253, 12, 34, 106, 17, 229, 1, 213, 70, 84, 105, 154, 252, 102, 128, 24, 19, 132, 116, 214, 240, 6, 110, 7, 59, 51, 83, 32, 133, 222, 175, 69, 134, 191, 64, 62, 176, 195, 3, 64, 113, 58, 188, 63, 68, 60, 35, 189, 198, 58, 75, 114, 132, 252, 179, 192, 181, 128, 93, 189, 255, 233, 78, 219, 176, 108, 60, 143, 48, 53, 63, 213, 167, 98, 75, 141, 73, 189, 59, 190, 147, 106, 69, 125, 206, 230, 251, 49, 147, 171, 252, 26, 47, 159, 230, 72, 26, 26, 145, 40, 64, 33, 172, 124, 166, 119, 90, 122, 137, 104, 254, 60, 25, 69, 224, 149, 111, 138, 191, 244, 81, 108, 205, 168, 48, 97, 15, 4, 98, 39, 156, 188, 162, 45, 29, 176, 254, 62, 121, 81, 106, 76, 238, 248, 113, 3, 13, 152, 253, 160, 145, 156, 86, 173, 8, 197, 252, 140, 86, 45, 39, 67, 54, 97, 56, 191, 161, 220, 69, 216, 215, 84, 171, 128, 2, 95, 181, 207, 238, 45, 221, 53, 147, 208, 68, 110, 18, 58, 255, 57, 143, 78, 52, 207, 148, 174, 10, 18, 196, 40, 213, 253, 101, 4, 7, 3, 223, 92, 125, 36, 183, 229, 71, 248, 120, 154, 33, 58, 211, 231, 17, 232, 151, 216, 255, 74, 201, 61, 253, 156, 209, 255, 167, 44, 202, 161, 56, 77, 180, 117, 5, 180, 52, 24, 8, 33, 72, 74, 30, 119, 13, 179, 187, 181, 115, 68, 197, 239, 10, 69, 79, 81, 200, 145, 209, 198, 237, 12, 152, 124, 200, 203, 170, 99, 151, 22, 62, 89, 198, 68, 116, 237, 139, 217, 107, 209, 109, 9, 96, 35, 250, 54, 149, 111, 53, 192, 225, 79, 26, 97, 60, 243, 162, 104, 147, 24, 175, 84, 2, 49, 174, 202, 38, 53, 34, 160, 82, 158, 101, 180, 144, 18, 238, 65, 63, 165, 108, 15, 108, 140, 170, 32, 122, 73, 76, 64, 188, 103, 225, 239, 100, 240, 12, 25, 39, 9, 187, 164, 112, 162, 100, 185, 27, 72, 122, 218, 28, 2, 20, 247, 7, 190, 131, 66, 116, 242, 180, 95, 189, 79, 158, 86, 246, 22, 14, 0, 104, 154, 105, 100, 39, 227, 139, 56, 221, 153, 88, 164, 163, 19, 248, 164, 72, 149, 49, 161, 247, 6, 100, 233, 26, 245, 44, 43, 115, 214, 91, 164, 205, 224, 59, 5, 48, 170, 187, 210, 184, 4, 83, 123, 170, 24, 194, 169, 181, 127, 47, 225, 198, 59, 145, 144, 8, 106, 166, 8, 121, 198, 120, 198, 155, 26, 143, 137, 243, 213, 27, 235, 203, 195, 28, 143, 144, 33, 123, 252, 32, 18, 248, 224, 59, 193, 210, 78, 21, 149, 16, 232, 130, 47, 93, 161, 37, 98, 80, 219, 199, 46, 45, 177, 231, 28, 127, 238, 28, 144, 0, 39, 54, 5, 215, 8, 70, 41, 196, 118, 117, 233, 11, 252, 41, 217, 123, 250, 53, 74, 20, 228, 84, 231, 254, 19, 211, 177, 231, 41, 16, 38, 47, 21, 158, 64, 255, 107, 29, 83, 142, 37, 37, 153, 116, 81, 210, 58, 227, 49, 184, 188, 219, 170, 168, 29, 19, 228, 8, 78, 45, 162, 75, 40, 157, 32, 224, 130, 72, 105, 24, 87, 20, 250, 9, 123, 187, 110, 77, 232, 243, 110, 45, 175, 67, 117, 168, 169, 57, 142, 12, 225, 19, 132, 2, 113, 42, 233, 47, 132, 241, 11, 189, 145, 11, 227, 175, 254, 14, 168, 95, 4, 241, 224, 97, 0, 196, 105, 146, 205, 254, 212, 1, 252, 81, 109, 177, 226, 171, 172, 44, 153, 216, 153, 245, 78, 167, 181, 63, 105, 184, 114, 92, 13, 107, 5, 21, 150, 22, 208, 65, 169, 159, 237, 9, 48, 206, 61, 24, 121, 172, 137, 75, 105, 105, 85, 149, 162, 190, 212, 129, 15, 74, 239, 185, 44, 162, 12, 192, 218, 84, 23, 132, 130, 120, 242, 243, 202, 192, 150, 177, 158, 242, 191, 184, 97, 16, 204, 35, 173, 159, 99, 33, 109, 204, 111, 3, 249, 253, 75, 113, 207, 98, 66, 218, 7, 81, 164, 106, 108, 77, 225, 64, 109, 33, 143, 88, 87, 76, 52, 48, 200, 172, 169, 190, 246, 172, 170, 230, 2, 43, 138, 226, 155, 225, 150, 250, 3, 234, 86, 75, 20, 18, 62, 163, 10, 172, 69, 38, 122, 35, 48, 48, 234, 21, 189, 158, 78, 153, 72, 62, 77, 157, 102, 248, 38, 151, 164, 15, 230, 232, 230, 230, 146, 195, 221, 244, 233, 250, 93, 118, 64, 249, 223, 158, 216, 33, 138, 44, 50, 225, 112, 56, 94, 44, 98, 154, 44, 31, 190, 150, 194, 223, 110, 165, 225, 157, 146, 115, 174, 157, 44, 76, 168, 155, 77, 222, 205, 138, 49, 160, 195, 193, 154, 228, 1, 60, 37, 239, 9, 18, 10, 238, 72, 220, 33, 88, 93, 40, 58, 88, 251, 117, 183, 246, 20, 109, 188, 79, 127, 251, 206, 90, 125, 178, 113, 34, 218, 135, 250, 57, 242, 163, 23, 203, 191, 231, 174, 34, 16, 210, 238, 64, 175, 248, 180, 16, 182, 177, 98, 231, 121, 51, 52, 88, 91, 236, 10, 11, 179, 145, 3, 66, 177, 33, 92, 244, 102, 231, 182, 106, 10, 237, 184, 148, 178, 39, 56, 14, 45, 223, 14, 14, 204, 22, 49, 75, 1, 241, 117, 246, 7, 29, 24, 122, 120, 132, 15, 171, 69, 223, 192, 162, 79, 44, 136, 9, 174, 170, 129, 118, 1, 74, 30, 218, 247, 39, 55, 99, 150, 72, 199, 214, 184, 4, 129, 72, 67, 121, 183, 235, 143, 183, 228, 130, 92, 160, 3, 194, 145, 242, 95, 85, 45, 170, 174, 157, 173, 73, 121, 213, 168, 95, 101, 58, 207, 57, 237, 51, 156, 67, 35, 207, 85, 161, 33, 129, 138, 8, 241, 58, 58, 218, 43, 97, 220, 177, 200, 65, 164, 152, 238, 19, 59, 174, 149, 178, 12, 116, 134, 0, 72, 43, 107, 23, 254, 57, 7, 86, 85, 22, 185, 194, 226, 25, 46, 23, 81, 132, 78, 65, 99, 41, 37, 91, 202, 168, 4, 103, 176, 70, 247, 112, 34, 178, 26, 38, 234, 249, 169, 232, 213, 141, 182, 155, 255, 2, 79, 139, 67, 182, 27, 8, 131, 32, 114, 21, 192, 243, 151, 58, 13, 202, 255, 52, 6, 209, 44, 79, 60, 83, 180, 39, 94, 235, 226, 156, 32, 152, 44, 75, 50, 22, 249, 148, 230, 69, 98, 136, 120, 252, 39, 11, 85, 109, 117, 181, 106, 249, 82, 163, 207, 240, 192, 56, 34, 98, 6, 11, 36, 89, 64, 181, 52, 148, 28, 174, 129, 74, 125, 2, 118, 246, 6, 140, 76, 196, 122, 215, 85, 51, 192, 137, 93, 192, 37, 30, 68, 155, 100, 224, 59, 192, 218, 1, 33, 57, 247, 155, 123, 126, 79, 101, 74, 8, 19, 169, 9, 41, 232, 229, 24, 102, 75, 78, 210, 239, 226, 219, 215, 170, 229, 116, 116, 190, 121, 212, 59, 20, 55, 80, 94, 246, 251, 114, 1, 252, 136, 224, 51, 243, 18, 200, 101, 13, 102, 133, 25, 157, 53, 245, 48, 241, 153, 68, 160, 148, 126, 93, 251, 113, 245, 174, 211, 43, 242, 95, 62, 43, 198, 25, 136, 140, 219, 98, 19, 42, 63, 93, 130, 63, 120, 203, 45, 113, 5, 110, 148, 163, 223, 73, 8, 202, 136, 41, 122, 54, 247, 148, 76, 211, 44, 33, 86, 36, 182, 163, 204, 160, 22, 126, 202, 172, 52, 174, 18, 132, 159, 92, 76, 90, 114, 170, 105, 4, 154, 131, 248, 128, 228, 101, 160, 212, 203, 39, 171, 149, 239, 57, 123, 168, 81, 5, 74, 147, 83, 42, 173, 196, 87, 198, 143, 248, 5, 165, 138, 198, 34, 196, 25, 1, 203, 100, 203, 238, 217, 141, 27, 158, 38, 149, 145, 45, 104, 34, 27, 237, 64, 74, 35, 68, 226, 142, 114, 174, 122, 142, 239, 104, 153, 247, 155, 19, 0, 113, 104, 228, 155, 119, 147, 216, 215, 90, 53, 47, 92, 112, 75, 201, 85, 95, 45, 29, 232, 100, 73, 61, 145, 220, 235, 94, 68, 16, 170, 210, 162, 205, 5, 118, 225, 147, 229, 142, 31, 91, 98, 235, 125, 214, 111, 222, 45, 79, 180, 40, 29, 126, 112, 149, 52, 52, 158, 53, 59, 209, 240, 231, 220, 239, 9, 17, 78, 58, 227, 80, 167, 87, 133, 110, 67, 101, 39, 147, 188, 60, 15, 243, 6, 162, 34, 162, 125, 184, 237, 10, 56, 81, 154, 213, 251, 47, 54, 245, 10, 115, 127, 166, 254, 237, 197, 167, 153, 118, 105, 92, 96, 254, 188, 140, 228, 183, 106, 153, 82, 122, 162, 242, 31, 233, 215, 186, 110, 200, 169, 178, 107, 83, 113, 130, 168, 158, 111, 20, 150, 140, 162, 125, 53, 200, 205, 128, 219, 38, 242, 196, 139, 150, 239, 78, 224, 157, 25, 104, 63, 159, 50, 48, 186, 33, 219, 82, 157, 194, 209, 13, 65, 120, 100, 254, 44, 9, 120, 31, 103, 89, 94, 79, 150, 142, 167, 115, 117, 146, 214, 96, 152, 213, 142, 228, 72, 127, 85, 102, 237, 129, 12, 220, 220, 105, 45, 43, 38, 6, 128, 133, 87, 48, 105, 136, 177, 240, 42, 185, 72, 0, 107, 230, 26, 172, 122, 251, 110, 2, 32, 170, 103, 101, 69, 49, 107, 46, 36, 226, 208, 68, 31, 178, 81, 21, 31, 30, 236, 191, 244, 50, 30, 54, 143, 67, 207, 180, 19, 116, 96, 16, 123, 210, 82, 168, 221, 199, 147, 189, 133, 60, 91, 41, 205, 62, 61, 134, 36, 46, 18, 78, 133, 139, 190, 114, 11, 16, 28, 71, 53, 175, 35, 164, 173, 144, 111, 33, 184, 196, 125, 36, 101, 243, 29, 80, 169, 81, 186, 41, 196, 124, 120, 63, 118, 15, 61, 0, 60, 177, 247, 142, 158, 220, 202, 24, 204, 68, 10, 180, 34, 54, 16, 114, 151, 107, 6, 32, 220, 210, 191, 129, 80, 190, 124, 140, 214, 61, 1, 34, 193, 136, 141, 251, 157, 193, 46, 232, 84, 160, 249, 138, 207, 67, 2, 2, 7, 8, 244, 179, 136, 166, 131, 1, 145, 14, 5, 47, 81, 190, 198, 188, 245, 5, 50, 60, 55, 70, 217, 89, 28, 3, 41, 47, 251, 77, 248, 33, 68, 6, 116, 121, 191, 24, 67, 197, 16, 215, 111, 229, 132, 37, 212, 180, 190, 21, 69, 211, 154, 114, 141, 69, 69, 16, 0, 60, 110, 57, 13, 184, 190, 241, 198, 7, 19, 89, 150, 66, 157, 141, 175, 0, 136, 156, 163, 218, 215, 127, 82, 255, 152, 215, 37, 251, 186, 223, 111, 242, 253, 233, 175, 46, 179, 1, 247, 225, 30, 19, 192, 85, 130, 137, 155, 219, 41, 33, 185, 33, 126, 131, 237, 0, 45, 246, 54, 86, 176, 133, 211, 6, 141, 230, 90, 73, 65, 75, 120, 47, 179, 96, 28, 193, 30, 17, 22, 43, 98, 93, 196, 105, 88, 201, 40, 82, 5, 186, 29, 165, 158, 119, 102, 58, 14, 85, 89, 47, 85, 29, 140, 17, 27, 113, 21, 217, 143, 195, 91, 38, 192, 181, 229, 0, 150, 248, 97, 36, 158, 62, 103, 179, 116, 191, 114, 121, 95, 124, 249, 112, 228, 21, 127, 248, 123, 5, 192, 167, 229, 174, 2, 242, 151, 132, 55, 183, 139, 51, 103, 138, 69, 210, 136, 33, 104, 169, 119, 127, 45, 152, 138, 226, 157, 201, 29, 80, 106, 224, 12, 30, 1, 132, 36, 248, 62, 0, 147, 132, 226, 104, 67, 211, 87, 38, 35, 99, 160, 131, 195, 32, 144, 10, 32, 252, 197, 133, 128, 180, 123, 104, 143, 82, 64, 74, 70, 188, 208, 2, 59, 77, 212, 247, 109, 161, 52, 248, 78, 230, 221, 102, 142, 40, 136, 142, 172, 69, 135, 25, 92, 126, 241, 174, 200, 85, 205, 44, 175, 144, 83, 54, 250, 11, 206, 204, 162, 120, 232, 215, 8, 198, 110, 71, 48, 136, 146, 12, 184, 0, 18, 30, 55, 48, 216, 77, 109, 17, 9, 249, 51, 71, 255, 170, 218, 62, 243, 195, 71, 45, 149, 217, 142, 83, 181, 38, 44, 9, 9, 66, 57, 175, 69, 15, 60, 170, 97, 112, 61, 249, 182, 34, 100, 11, 216, 137, 101, 195, 80, 14, 35, 111, 223, 11, 61, 106, 208, 91, 81, 142, 145, 96, 145, 226, 159, 88, 22, 18, 211, 192, 154, 185, 67, 93, 73, 144, 206, 221, 201, 179, 138, 223, 88, 151, 122, 128, 150, 6, 102, 9, 91, 68, 130, 217, 147, 125, 1, 225, 102, 245, 212, 7, 178, 203, 91, 139, 175, 43, 102, 221, 172, 214, 188, 107, 116, 54, 78, 92, 225, 4, 6, 241, 194, 148, 237, 44, 171, 149, 17, 180, 104, 33, 144, 86, 133, 205, 70, 236, 87, 40, 195, 147, 67, 8, 34, 112, 64, 22, 111, 196, 96, 246, 129, 131, 226, 36, 181, 250, 68, 98, 86, 159, 77, 5, 182, 147, 224, 21, 65, 18, 180, 36, 85, 224, 100, 229, 166, 85, 59, 255, 113, 41, 130, 120, 232, 2, 113, 114, 211, 53, 200, 45, 183, 238, 240, 174, 1, 68, 82, 167, 6, 198, 172, 55, 103, 200, 2, 8, 30, 53, 196, 99, 40, 138, 239, 185, 86, 191, 230, 174, 97, 61, 7, 120, 85, 142, 6, 202, 107, 114, 20, 202, 13, 209, 82, 46, 122, 14, 21, 27, 44, 152, 132, 217, 40, 254, 113, 170, 186, 229, 228, 81, 15, 148, 77, 26, 43, 113, 17, 100, 156, 33, 90, 38, 88, 129, 171, 199, 148, 30, 16, 94, 95, 182, 24, 159, 4, 96, 106, 98, 219, 36, 73, 218, 79, 152, 152, 176, 159, 34, 13, 80, 54, 176, 157, 191, 128, 46, 230, 117, 199, 127, 8, 36, 208, 7, 237, 41, 241, 160, 116, 172, 181, 90, 94, 233, 130, 233, 225, 35, 53, 155, 106, 115, 118, 192, 220, 118, 190, 248, 170, 110, 113, 23, 171, 120, 176, 247, 181, 130, 172, 197, 44, 111, 239, 3, 89, 217, 108, 182, 120, 190, 62, 138, 226, 232, 31, 233, 3, 77, 114, 53, 228, 157, 129, 209, 233, 26, 245, 254, 235, 164, 195, 194, 50, 73, 225, 14, 225, 251, 100, 59, 93, 167, 134, 171, 186, 46, 101, 169, 205, 33, 87, 23, 97, 138, 116, 226, 51, 2, 229, 221, 188, 16, 219, 175, 168, 202, 198, 113, 51, 183, 116, 137, 139, 224, 233, 20, 110, 121, 47, 206, 155, 201, 166, 114, 226, 30, 186, 157, 124, 241, 5, 119, 99, 101, 106, 151, 49, 60, 206, 210, 16, 66, 2, 247, 168, 159, 166, 192, 198, 55, 217, 22, 126, 235, 14, 203, 81, 72, 142, 194, 187, 42, 118, 189, 167, 4, 199, 59, 110, 8, 88, 121, 247, 138, 57, 180, 45, 96, 77, 227, 228, 29, 187, 65, 173, 142, 96, 100, 26, 22, 160, 88, 108, 210, 83, 138, 196, 35, 9, 58, 176, 71, 59, 21, 128, 59, 166, 47, 114, 158, 226, 145, 144, 199, 35, 25, 219, 22, 38, 104, 119, 65, 4, 222, 170, 224, 89, 206, 26, 96, 161, 59, 62, 37, 201, 27, 92, 91, 201, 64, 220, 109, 103, 222, 7, 100, 225, 114, 221, 19, 47, 192, 117, 243, 236, 23, 100, 113, 251, 111, 69, 196, 144, 237, 22, 0, 243, 251, 138, 240, 123, 95, 168, 224, 167, 223, 133, 197, 143, 144, 126, 100, 139, 36, 120, 185, 19, 175, 240, 251, 66, 46, 56, 197, 169, 168, 17, 168, 218, 183, 9, 186, 123, 140, 168, 226, 74, 117, 140, 191, 210, 161, 96, 53, 229, 192, 250, 224, 73, 189, 235, 153, 225, 28, 2, 12, 95, 25, 130, 167, 139, 189, 24, 71, 2, 102, 46, 149, 137, 237, 194, 61, 67, 138, 20, 249, 40, 35, 24, 176, 188, 60, 122, 73, 231, 156, 204, 253, 245, 41, 229, 144, 192, 152, 102, 71, 6, 241, 0, 37, 176, 223, 91, 213, 236, 216, 245, 235, 127, 83, 130, 229, 102, 244, 219, 40, 236, 135, 5, 153, 88, 186, 213, 62, 120, 187, 115, 9, 181, 44, 8, 212, 111, 78, 255, 237, 244, 101, 133, 113, 169, 31, 28, 202, 147, 120, 190, 147, 133, 123, 14, 223, 120, 41, 71, 114, 51, 165, 6, 90, 90, 113, 207, 246, 150, 40, 143, 124, 73, 170, 193, 232, 107, 43, 98, 59, 25, 123, 59, 250, 108, 7, 21, 105, 79, 72, 113, 27, 1, 88, 37, 20, 218, 254, 183, 233, 14, 124, 16, 59, 17, 164, 162, 242, 247, 9, 80, 7, 183, 151, 68, 41, 48, 22, 53, 134, 88, 223, 113, 154, 95, 205, 178, 170, 125, 216, 45, 152, 141, 76, 33, 184, 43, 186, 41, 151, 115, 223, 224, 97, 100, 154, 30, 74, 20, 148, 113, 180, 100, 226, 221, 19, 143, 119, 182, 45, 90, 99, 157, 90, 57, 80, 58, 227, 107, 36, 177, 138, 144, 245, 78, 4, 218, 205, 154, 147, 59, 62, 163, 67, 7, 174, 32, 91, 254, 145, 126, 187, 221, 208, 92, 111, 73, 170, 173, 107, 59, 69, 78, 7, 171, 135, 241, 148, 138, 50, 40, 228, 138, 10, 229, 228, 145, 95, 223, 13, 161, 183, 245, 41, 150, 149, 199, 7, 255, 138, 221, 142, 172, 65, 53, 31, 230, 47, 229, 126, 42, 131, 66, 18, 77, 203, 6, 206, 228, 215, 192, 209, 175, 98, 184, 159, 241, 106, 95, 255, 111, 55, 45, 234, 212, 4, 32, 208, 111, 37, 212, 53, 129, 70, 168, 156, 151, 188, 2, 121, 154, 140, 84, 155, 164, 51, 114, 142, 45, 24, 51, 99, 137, 224, 147, 212, 26, 220, 163, 19, 39, 180, 215, 53, 165, 162, 182, 233, 59, 221, 11, 90, 237, 112, 78, 36, 117, 233, 23, 229, 192, 94, 99, 96, 118, 171, 57, 42, 16, 98, 175, 43, 171, 196, 248, 18, 57, 19, 98, 181, 237, 235, 184, 177, 155, 6, 83, 179, 70, 162, 221, 121, 216, 116, 116, 200, 193, 164, 252, 59, 216, 48, 104, 112, 42, 207, 152, 246, 91, 109, 141, 68, 104, 49, 225, 210, 8, 79, 179, 62, 10, 31, 156, 189, 216, 179, 81, 102, 220, 79, 187, 167, 207, 11, 119, 83, 130, 24, 100, 215, 115, 143, 96, 213, 132, 109, 77, 35, 168, 40, 247, 133, 60, 53, 179, 26, 159, 70, 149, 59, 249, 130, 175, 113, 97, 166, 123, 156, 54, 129, 126, 197, 171, 18, 105, 91, 254, 211, 78, 233, 206, 145, 166, 101, 97, 32, 157, 236, 137, 252, 183, 59, 234, 219, 181, 236, 190, 76, 56, 145, 170, 245, 59, 208, 29, 16, 116, 45, 138, 135, 248, 118, 228, 3, 251, 244, 177, 85, 107, 13, 57, 60, 35, 237, 73, 5, 58, 140, 120, 165, 47, 223, 127, 98, 136, 84, 16, 18, 225, 126, 38, 100, 120, 35, 194, 204, 170, 144, 253, 217, 18, 86, 133, 225, 191, 220, 178, 177, 11, 51, 146, 205, 132, 128, 33, 90, 195, 234, 116, 68, 20, 73, 194, 135, 137, 0, 16, 169, 243, 123, 5, 130, 172, 162, 48, 240, 232, 208, 158, 32, 49, 140, 245, 51, 37, 131, 92, 67, 126, 44, 13, 31, 62, 240, 80, 131, 136, 187, 222, 199, 19, 28, 229, 240, 122, 208, 244, 180, 58, 232, 46, 1, 83, 171, 236, 220, 69, 6, 43, 152, 203, 3, 16, 246, 17, 69, 116, 54, 64, 9, 240, 109, 189, 32, 237, 189, 153, 148, 107, 2, 7, 224, 144, 242, 114, 76, 46, 66, 91, 31, 228, 112, 115, 124, 108, 39, 108, 225, 232, 245, 175, 122, 57, 47, 175, 12, 179, 39, 27, 172, 167, 249, 76, 135, 106, 133, 82, 162, 27, 220, 11, 94, 144, 228, 95, 114, 158, 217, 58, 24, 211, 24, 42, 118, 88, 178, 81, 163, 27, 41, 116, 193, 231, 88, 164, 166, 21, 95, 207, 245, 47, 245, 173, 56, 11, 144, 87, 124, 231, 79, 214, 60, 160, 185, 39, 102, 146, 204, 70, 202, 222, 203, 101, 183, 165, 180, 33, 216, 127, 96, 249, 7, 143, 237, 96, 186, 72, 40, 64, 213, 23, 251, 103, 123, 251, 135, 182, 237, 169, 43, 46, 30, 140, 133, 53, 127, 17, 18, 37, 31, 123, 86, 187, 5, 75, 64, 27, 13, 175, 180, 152, 249, 135, 82, 16, 139, 136, 204, 175, 27, 135, 255, 204, 39, 223, 160, 78, 6, 151, 95, 65, 13, 41, 64, 93, 64, 1, 218, 99, 109, 134, 201, 25, 62, 0, 191, 212, 140, 170, 8, 19, 28, 119, 45, 2, 53, 228, 169, 150, 252, 218, 233, 121, 176, 198, 208, 137, 58, 62, 37, 8, 150, 83, 247, 152, 133, 121, 85, 85, 2, 54, 179, 179, 105, 251, 99, 239, 223, 90, 42, 76, 49, 187, 250, 187, 197, 150, 141, 139, 36, 178, 143, 241, 206, 44, 80, 255, 14, 198, 156, 112, 154, 232, 11, 75, 53, 213, 83, 123, 94, 141, 155, 201, 113, 241, 56, 70, 252, 64, 158, 166, 165, 96, 152, 186, 2, 92, 3, 114, 52, 156, 134, 7, 222, 31, 90, 137, 52, 48, 12, 201, 84, 147, 254, 138, 19, 184, 114, 245, 138, 7, 90, 49, 167, 145, 107, 182, 83, 253, 40, 52, 160, 187, 243, 67, 151, 81, 3, 218, 68, 56, 202, 178, 144, 175, 238, 77, 145, 191, 171, 245, 128, 4, 208, 195, 102, 119, 57, 240, 6, 66, 254, 211, 45, 130, 58, 225, 51, 99, 73, 13, 56, 58, 28, 35, 110, 33, 180, 51, 26, 140, 70, 26, 229, 180, 229, 176, 138, 226, 9, 134, 129, 185, 187, 239, 223, 2, 229, 233, 150, 121, 138, 51, 41, 143, 68, 57, 118, 56, 31, 87, 195, 1, 75, 87, 156, 253, 225, 48, 243, 60, 94, 212, 205, 205, 151, 180, 100, 135, 245, 23, 108, 18, 5, 132, 244, 135, 177, 10, 37, 12, 28, 10, 127, 93, 103, 207, 148, 27, 227, 93, 148, 246, 204, 17, 88, 121, 29, 100, 198, 40, 185, 62, 104, 157, 5, 56, 71, 213, 100, 127, 249, 128, 244, 137, 235, 154, 144, 197, 8, 208, 124, 114, 242, 13, 251, 50, 254, 34, 91, 56, 10, 193, 195, 147, 85, 84, 21, 114, 112, 185, 50, 108, 118, 235, 123, 103, 247, 179, 202, 50, 17, 200, 66, 193, 58, 63, 43, 104, 163, 165, 140, 252, 54, 59, 102, 13, 117, 138, 175, 183, 48, 220, 215, 173, 86, 24, 135, 190, 226, 209, 212, 8, 111, 15, 152, 3, 126, 226, 149, 168, 204, 178, 165, 4, 230, 32, 34, 8, 79, 242, 94, 100, 141, 5, 125, 159, 109, 205, 161, 61, 95, 14, 59, 3, 132, 196, 133, 176, 198, 172, 25, 243, 70, 148, 96, 196, 241, 24, 65, 116, 30, 177, 162, 251, 228, 156, 147, 77, 244, 65, 138, 223, 212, 22, 40, 122, 1, 202, 219, 51, 101, 78, 118, 96, 101, 146, 112, 10, 172, 36, 211, 254, 136, 208, 48, 88, 82, 217, 33, 32, 148, 185, 234, 217, 100, 29, 141, 88, 247, 168, 34, 215, 165, 136, 149, 42, 203, 97, 227, 69, 216, 67, 65, 14, 27, 19, 84, 33, 141, 58, 75, 72, 178, 44, 16, 160, 251, 174, 94, 248, 52, 184, 125, 190, 77, 251, 88, 72, 11, 153, 160, 127, 77, 22, 127, 219, 245, 89, 76, 136, 192, 202, 239, 219, 248, 248, 171, 164, 198, 78, 148, 85, 159, 72, 218, 223, 99, 53, 38, 201, 186, 121, 68, 137, 194, 174, 136, 239, 224, 160, 67, 111, 220, 229, 93, 245, 31, 250, 193, 91, 110, 252, 12, 7, 131, 47, 62, 73, 10, 100, 129, 88, 40, 230, 245, 208, 146, 14, 132, 120, 204, 222, 135, 68, 47, 222, 19, 60, 161, 7, 67, 64, 68, 70, 29, 152, 64, 7, 183, 251, 225, 148, 61, 155, 127, 233, 89, 112, 169, 73, 157, 76, 150, 85, 219, 41, 71, 220, 234, 15, 173, 98, 124, 209, 206, 71, 56, 201, 187, 64, 16, 103, 159, 78, 9, 84, 187, 250, 154, 196, 49, 125, 168, 154, 127, 40, 15, 139, 198, 155, 71, 7, 80, 98, 219, 105, 202, 183, 96, 32, 115, 28, 168, 81, 69, 23, 103, 33, 219, 47, 154, 131, 125, 7, 229, 140, 163, 248, 62, 156, 231, 8, 141, 34, 232, 193, 150, 171, 224, 190, 62, 76, 47, 203, 161, 1, 186, 3, 67, 55, 0, 117, 153, 152, 165, 90, 146, 124, 169, 150, 90, 241, 163, 67, 146, 64, 13, 165, 218, 131, 135, 166, 193, 186, 200, 149, 8, 55, 9, 227, 66, 157, 212, 226, 97, 91, 172, 199, 196, 59, 131, 75, 170, 88, 8, 242, 30, 148, 211, 214, 208, 102, 225, 102, 165, 119, 155, 69, 208, 18, 168, 81, 84, 153, 79, 240, 92, 216, 36, 99, 75, 125, 249, 93, 182, 54, 232, 169, 235, 184, 210, 169, 104, 11, 163, 110, 116, 174, 218, 205, 75, 188, 68, 8, 194, 219, 137, 103, 93, 23, 10, 64, 49, 217, 198, 215, 166, 151, 189, 209, 148, 92, 61, 14, 11, 101, 129, 79, 230, 112, 234, 42, 125, 185, 107, 178, 135, 186, 78, 220, 33, 145, 91, 144, 176, 224, 134, 93, 16, 202, 31, 211, 10, 221, 21, 202, 245, 141, 245, 51, 147, 27, 192, 146, 205, 175, 255, 21, 186, 205, 62, 177, 127, 20, 231, 2, 81, 3, 161, 67, 225, 24, 66, 9, 102, 150, 218, 178, 3, 255, 65, 4, 141, 81, 0, 197, 134, 168, 72, 163, 246, 235, 224, 14, 62, 70, 205, 42, 161, 183, 151, 76, 86, 78, 101, 168, 253, 154, 46, 206, 132, 135, 104, 60, 217, 8, 128, 64, 60, 88, 6, 152, 85, 189, 49, 186, 230, 62, 156, 64, 47, 172, 148, 77, 114, 199, 27, 222, 122, 225, 213, 230, 57, 233, 175, 32, 113, 1, 36, 30, 146, 82, 127, 199, 75, 195, 196, 117, 250, 190, 80, 4, 229, 168, 125, 109, 55, 135, 0, 189, 75, 14, 58, 177, 239, 93, 131, 234, 227, 168, 209, 224, 140, 99, 240, 36, 225, 141, 184, 71, 253, 235, 142, 103, 83, 120, 97, 74, 94, 20, 13, 176, 36, 55, 204, 132, 64, 162, 157, 251, 233, 127, 119, 51, 143, 194, 94, 249, 60, 10, 141, 252, 138, 129, 160, 193, 213, 122, 197, 216, 74, 4, 20, 59, 47, 197, 124, 199, 186, 87, 30, 88, 120, 70, 15, 171, 134, 218, 144, 167, 229, 168, 35, 179, 203, 208, 151, 121, 47, 192, 83, 179, 102, 112, 147, 20, 118, 169, 148, 69, 103, 200, 134, 204, 254, 162, 24, 3, 120, 7, 194, 166, 163, 175, 153, 35, 252, 209, 15, 164, 29, 133, 23, 74, 177, 174, 196, 118, 97, 220, 139, 245, 22, 58, 125, 146, 250, 198, 163, 228, 225, 101, 103, 214, 180, 18, 111, 105, 127, 33, 249, 2, 217, 144, 86, 167, 107, 236, 76, 40, 72, 64, 24, 94, 2, 238, 173, 42, 26, 117, 41, 124, 44, 108, 0, 7, 49, 228, 158, 225, 78, 88, 247, 141, 142, 242, 207, 128, 58, 142, 40, 141, 36, 93, 17, 54, 9, 210, 175, 248, 170, 142, 121, 4, 221, 108, 1, 184, 162, 113, 221, 64, 181, 30, 9, 32, 13, 69, 14, 154, 11, 237, 136, 237, 188, 146, 233, 171, 57, 159, 80, 235, 12, 80, 151, 11, 42, 157, 137, 44, 104, 53, 241, 188, 62, 99, 253, 118, 220, 242, 169, 182, 146, 31, 113, 19, 16, 61, 6, 75, 128, 18, 5, 52, 168, 187, 7, 139, 94, 14, 12, 107, 223, 20, 134, 162, 13, 12, 142, 51, 39, 158, 99, 191, 124, 233, 4, 96, 172, 172, 84, 138, 84, 49, 91, 8, 133, 190, 160, 59, 122, 114, 72, 82, 57, 6, 93, 108, 9, 88, 252, 240, 247, 185, 170, 28, 163, 243, 228, 81, 212, 118, 86, 5, 30, 20, 80, 46, 173, 177, 119, 216, 111, 84, 139, 132, 97, 122, 67, 108, 176, 171, 31, 192, 71, 108, 22, 65, 42, 83, 221, 166, 15, 32, 207, 141, 92, 177, 170, 65, 169, 83, 65, 54, 118, 184, 156, 7, 197, 130, 29, 9, 157, 87, 71, 235, 66, 54, 225, 220, 48, 160, 193, 88, 185, 231, 251, 229, 157, 118, 66, 206, 143, 242, 42, 72, 52, 178, 243, 198, 193, 43, 12, 81, 121, 178, 65, 82, 39, 113, 135, 191, 197, 25, 142, 148, 52, 46, 123, 192, 188, 246, 95, 87, 173, 225, 162, 39, 182, 51, 169, 185, 228, 25, 115, 30, 210, 225, 47, 168, 93, 139, 76, 121, 224, 61, 30, 24, 45, 34, 37, 181, 180, 128, 113, 155, 43, 70, 224, 60, 218, 30, 252, 249, 131, 154, 168, 9, 191, 85, 234, 233, 1, 44, 212, 224, 137, 138, 217, 13, 22, 242, 242, 53, 56, 105, 189, 221, 117, 62, 23, 174, 5, 224, 90, 248, 165, 13, 60, 67, 82, 189, 152, 105, 3, 139, 183, 12, 172, 166, 171, 255, 217, 124, 209, 99, 54, 24, 190, 163, 255, 2, 240, 164, 227, 85, 33, 161, 145, 116, 165, 236, 167, 159, 144, 242, 3, 146, 19, 50, 41, 147, 107, 91, 175, 196, 255, 199, 237, 77, 251, 219, 134, 189, 244, 68, 254, 6, 75, 127, 198, 102, 251, 159, 11, 184, 93, 83, 138, 212, 140, 140, 6, 36, 173, 187, 180, 108, 151, 133, 118, 66, 79, 12, 118, 208, 46, 125, 226, 42, 57, 213, 210, 197, 108, 152, 71, 173, 149, 135, 237, 62, 232, 173, 29, 172, 126, 113, 71, 106, 218, 86, 188, 1, 33, 158, 245, 155, 87, 41, 192, 89, 211, 249, 179, 104, 18, 33, 85, 24, 147, 141, 184, 80, 179, 223, 73, 21, 49, 7, 51, 100, 176, 0, 147, 244, 197, 172, 193, 132, 53, 85, 13, 245, 234, 12, 200, 81, 229, 103, 54, 92, 103, 85, 26, 128, 20, 35, 187, 4, 27, 213, 225, 155, 33, 131, 173, 198, 30, 7, 9, 53, 196, 10, 94, 237, 163, 5, 172, 28, 113, 80, 194, 43, 145, 4, 236, 3, 72, 133, 153, 203, 227, 3, 246, 104, 97, 142, 157, 88, 45, 227, 179, 186, 37, 91, 31, 179, 62, 102, 144, 76, 199, 215, 113, 163, 177, 129, 129, 96, 215, 157, 4, 245, 192, 127, 80, 107, 219, 227, 216, 148, 203, 227, 212, 164, 165, 165, 83, 55, 18, 157, 178, 38, 72, 132, 180, 238, 70, 3, 12, 5, 97, 58, 155, 168, 184, 115, 175, 123, 17, 168, 187, 243, 74, 109, 199, 96, 126, 228, 98, 132, 15, 144, 164, 246, 57, 19, 83, 230, 184, 222, 50, 245, 22, 162, 245, 162, 146, 89, 186, 89, 5, 177, 128, 97, 29, 86, 147, 172, 67, 139, 249, 165, 137, 110, 149, 113, 229, 162, 164, 59, 218, 41, 17, 124, 43, 130, 13, 113, 199, 215, 165, 33, 146, 8, 220, 14, 151, 124, 1, 70, 231, 102, 246, 217, 176, 97, 79, 191, 243, 204, 172, 78, 71, 4, 239, 151, 227, 221, 255, 133, 178, 140, 248, 91, 235, 79, 111, 48, 181, 151, 236, 142, 165, 198, 189, 28, 53, 136, 250, 153, 75, 195, 82, 131, 204, 217, 175, 219, 137, 72, 87, 207, 28, 73, 112, 14, 44, 55, 68, 97, 88, 27, 154, 179, 9, 200, 145, 146, 232, 191, 19, 150, 223, 15, 205, 21, 161, 118, 30, 188, 186, 171, 208, 8, 63, 88, 180, 56, 192, 72, 228, 135, 104, 243, 190, 238, 39, 179, 179, 122, 84, 54, 120, 48, 154, 174, 253, 192, 59, 249, 111, 119, 73, 17, 139, 121, 27, 98, 57, 161, 222, 147, 146, 37, 54, 159, 66, 191, 158, 89, 60, 147, 6, 126, 84, 165, 19, 22, 147, 43, 148, 87, 63, 174, 214, 178, 22, 233, 112, 25, 157, 123, 118, 219, 179, 249, 182, 114, 217, 218, 80, 33, 248, 103, 89, 0, 87, 200, 154, 249, 23, 157, 67, 106, 97, 249, 183, 20, 67, 56, 75, 84, 176, 40, 0, 50, 144, 58, 38, 89, 93, 107, 197, 123, 90, 164, 99, 48, 17, 13, 212, 121, 152, 96, 84, 166, 118, 106, 128, 230, 107, 95, 26, 107, 4, 161, 48, 191, 181, 197, 150, 66, 18, 252, 11, 235, 79, 238, 66, 233, 26, 137, 89, 16, 5, 249, 174, 96, 250, 242, 12, 16, 241, 94, 73, 232, 36, 244, 54, 140, 38, 43, 213, 89, 53, 115, 212, 223, 21, 84, 167, 119, 246, 0, 5, 218, 87, 141, 124, 140, 206, 146, 171, 26, 171, 128, 9, 221, 33, 116, 110, 98, 87, 46, 11, 34, 41, 60, 123, 115, 8, 168, 183, 63, 117, 171, 129, 157, 216, 92, 75, 128, 46, 59, 169, 53, 33, 115, 65, 162, 155, 178, 128, 25, 109, 237, 41, 89, 107, 48, 23, 79, 191, 16, 205, 172, 148, 171, 201, 246, 104, 166, 6, 134, 23, 233, 136, 106, 157, 246, 136, 102, 57, 76, 34, 221, 231, 144, 129, 9, 26, 234, 97, 208, 33, 94, 134, 86, 25, 206, 224, 129, 138, 240, 108, 64, 17, 97, 248, 188, 16, 16, 184, 9, 123, 43, 18, 135, 33, 252, 11, 100, 46, 42, 20, 209, 144, 173, 136, 115, 65, 129, 236, 137, 53, 9, 5, 109, 211, 100, 75, 199, 57, 141, 40, 42, 32, 10, 66, 177, 93, 91, 215, 170, 24, 79, 41, 9, 230, 65, 104, 193, 139, 75, 233, 180, 90, 204, 152, 170, 244, 35, 149, 77, 41, 120, 50, 106, 72, 8, 103, 152, 32, 198, 161, 133, 129, 250, 38, 148, 229, 225, 133, 5, 81, 134, 16, 45, 17, 145, 114, 193, 12, 41, 18, 161, 219, 118, 197, 208, 151, 77, 227, 115, 137, 121, 225, 156, 42, 251, 13, 233, 206, 235, 129, 94, 197, 158, 104, 19, 26, 117, 164, 6, 236, 54, 97, 246, 84, 239, 138, 233, 18, 151, 202, 152, 137, 218, 168, 73, 128, 167, 7, 162, 193, 86, 129, 128, 95, 5, 237, 98, 57, 8, 155, 11, 199, 247, 109, 71, 90, 58, 206, 232, 80, 164, 210, 115, 126, 230, 26, 251, 216, 109, 3, 37, 203, 62, 194, 123, 27, 87, 107, 146, 154, 64, 194, 130, 148, 138, 89, 40, 242, 253, 235, 64, 128, 99, 74, 225, 162, 211, 246, 170, 219, 168, 158, 47, 173, 210, 184, 231, 53, 225, 124, 18, 57, 137, 223, 144, 48, 164, 114, 106, 209, 216, 70, 29, 69, 114, 131, 174, 112, 40, 234, 73, 138, 176, 194, 110, 119, 204, 79, 241, 81, 1, 18, 72, 164, 80, 97, 203, 114, 152, 4, 27, 118, 67, 177, 81, 49, 58, 15, 106, 163, 227, 43, 14, 91, 251, 72, 129, 99, 15, 173, 239, 36, 41, 54, 107, 131, 4, 66, 9, 42, 152, 125, 34, 68, 170, 80, 188, 250, 63, 62, 198, 231, 62, 246, 254, 127, 11, 248, 242, 76, 15, 173, 32, 230, 106, 145, 228, 85, 175, 156, 71, 40, 30, 78, 202, 15, 233, 254, 63, 68, 191, 238, 126, 87, 79, 31, 78, 135, 113, 199, 114, 193, 181, 215, 192, 108, 128, 120, 62, 34, 247, 87, 5, 111, 105, 244, 85, 56, 77, 112, 206, 109, 50, 74, 1, 163, 18, 158, 27, 21, 65, 122, 11, 232, 11, 85, 145, 177, 4, 246, 1, 133, 84, 70, 68, 61, 155, 51, 232, 150, 77, 34, 2, 64, 205, 110, 1, 179, 229, 251, 168, 238, 45, 9, 246, 52, 91, 13, 93, 109, 252, 70, 184, 105, 79, 40, 199, 91, 65, 31, 2, 163, 248, 150, 245, 2, 208, 223, 162, 18, 170, 125, 201, 16, 195, 242, 148, 17, 197, 153, 149, 234, 129, 134, 71, 30, 146, 2, 227, 248, 213, 121, 212, 153, 29, 101, 7, 246, 119, 92, 30, 63, 55, 73, 129, 27, 128, 162, 96, 10, 248, 250, 55, 99, 115, 129, 101, 230, 163, 14, 8, 158, 92, 159, 97, 2, 192, 149, 22, 3, 196, 9, 176, 114, 137, 2, 104, 182, 5, 134, 58, 60, 97, 146, 148, 6, 54, 33, 203, 162, 53, 23, 50, 45, 30, 228, 13, 233, 55, 200, 65, 150, 39, 104, 168, 200, 140, 65, 178, 143, 114, 40, 64, 225, 60, 43, 248, 40, 191, 0, 138, 172, 254, 185, 91, 2, 67, 184, 237, 169, 95, 69, 57, 162, 110, 78, 143, 92, 24, 7, 4, 63, 151, 107, 144, 82, 60, 19, 170, 227, 90, 163, 221, 81, 180, 224, 102, 136, 144, 152, 237, 77, 247, 168, 212, 87, 160, 135, 76, 58, 139, 227, 103, 212, 184, 10, 77, 150, 189, 39, 90, 149, 197, 204, 253, 99, 171, 79, 66, 202, 131, 89, 1, 15, 10, 85, 202, 30, 107, 110, 156, 181, 213, 87, 68, 44, 0, 13, 19, 69, 235, 242, 150, 69, 138, 65, 200, 55, 48, 178, 69, 211, 139, 121, 53, 182, 165, 252, 203, 117, 40, 100, 248, 16, 110, 70, 94, 39, 211, 236, 178, 194, 163, 191, 249, 233, 188, 244, 124, 122, 192, 107, 230, 78, 27, 175, 65, 222, 1, 217, 198, 37, 181, 19, 162, 241, 159, 133, 55, 110, 82, 134, 43, 192, 149, 126, 201, 214, 254, 223, 73, 62, 227, 220, 138, 83, 117, 190, 169, 25, 161, 135, 38, 83, 133, 204, 56, 212, 115, 38, 88, 46, 237, 27, 228, 40, 68, 24, 169, 116, 28, 119, 95, 36, 205, 134, 129, 220, 194, 44, 198, 222, 121, 227, 126, 192, 26, 175, 204, 149, 162, 71, 159, 158, 238, 116, 123, 249, 201, 74, 206, 30, 11, 40, 16, 72, 162, 42, 127, 197, 252, 159, 6, 9, 3, 110, 70, 213, 133, 181, 153, 92, 140, 105, 86, 185, 71, 115, 231, 85, 241, 218, 137, 34, 32, 52, 195, 234, 49, 220, 123, 165, 126, 247, 177, 10, 69, 93, 145, 71, 89, 211, 206, 234, 148, 198, 199, 206, 211, 35, 19, 96, 130, 231, 137, 108, 217, 123, 17, 167, 78, 141, 242, 196, 17, 129, 9, 159, 237, 183, 133, 1, 95, 26, 231, 8, 204, 152, 184, 96, 56, 144, 136, 128, 42, 236, 247, 18, 217, 172, 141, 57, 187, 21, 23, 198, 26, 109, 218, 180, 199, 199, 52, 118, 164, 120, 51, 3, 19, 152, 144, 249, 152, 74, 129, 82, 55, 204, 102, 66, 211, 97, 180, 104, 113, 209, 27, 229, 238, 44, 123, 251, 114, 201, 0, 8, 11, 126, 113, 102, 100, 1, 233, 218, 52, 166, 231, 69, 75, 10, 166, 179, 78, 191, 117, 254, 97, 201, 153, 1, 179, 188, 140, 137, 132, 34, 108, 132, 26, 216, 175, 234, 36, 196, 161, 45, 149, 180, 217, 174, 75, 129, 84, 205, 21, 110, 248, 108, 53, 154, 47, 4, 152, 72, 146, 46, 208, 106, 168, 37, 201, 232, 44, 211, 129, 57, 134, 251, 150, 29, 46, 60, 104, 20, 67, 56, 116, 61, 181, 52, 191, 143, 128, 5, 214, 81, 68, 73, 182, 179, 32, 23, 84, 124, 205, 49, 41, 137, 195, 167, 189, 38, 89, 22, 118, 242, 76, 52, 158, 92, 160, 224, 36, 154, 168, 212, 60, 182, 143, 12, 51, 123, 142, 80, 60, 112, 0, 213, 34, 3, 27, 161, 9, 108, 141, 28, 97, 102, 130, 29, 95, 191, 220, 148, 138, 190, 64, 49, 170, 159, 245, 227, 255, 3, 116, 128, 174, 124, 214, 165, 98, 78, 9, 216, 65, 206, 0, 240, 109, 119, 67, 165, 0, 22, 168, 170, 130, 2, 94, 14, 206, 158, 149, 157, 26, 44, 241, 21, 176, 84, 200, 91, 59, 14, 3, 138, 69, 84, 146, 150, 138, 226, 172, 18, 96, 49, 156, 163, 18, 212, 26, 169, 99, 98, 8, 205, 231, 18, 191, 36, 118, 189, 185, 60, 128, 83, 107, 88, 253, 42, 205, 163, 48, 163, 18, 221, 148, 128, 219, 103, 135, 36, 168, 232, 116, 68, 122, 38, 10, 62, 71, 41, 169, 34, 131, 174, 122, 58, 59, 82, 24, 59, 79, 184, 118, 242, 27, 214, 199, 140, 39, 105, 78, 148, 153, 43, 176, 126, 83, 217, 240, 118, 100, 76, 234, 70, 92, 22, 48, 46, 216, 75, 133, 119, 92, 108, 32, 99, 211, 25, 142, 175, 226, 156, 53, 178, 36, 117, 218, 218, 130, 5, 49, 215, 164, 191, 34, 234, 90, 1, 60, 151, 105, 226, 255, 94, 126, 182, 52, 164, 105, 139, 126, 178, 244, 160, 252, 170, 140, 160, 99, 186, 139, 43, 143, 92, 129, 250, 33, 139, 91, 252, 207, 17, 154, 169, 53, 140, 170, 64, 216, 181, 181, 69, 231, 198, 224, 170, 71, 21, 82, 187, 224, 216, 164, 51, 161, 2, 68, 131, 137, 107, 206, 196, 203, 20, 77, 51, 160, 234, 153, 61, 217, 109, 184, 164, 61, 249, 210, 156, 67, 254, 94, 254, 35, 139, 189, 123, 49, 111, 194, 101, 82, 135, 234, 205, 215, 109, 63, 84, 79, 221, 20, 132, 133, 79, 208, 84, 170, 123, 187, 24, 159, 243, 26, 124, 217, 103, 225, 238, 200, 80, 47, 245, 48, 17, 219, 60, 162, 146, 241, 182, 133, 72, 132, 4, 206, 197, 20, 181, 1, 135, 121, 121, 102, 202, 98, 211, 29, 88, 61, 100, 149, 6, 24, 103, 138, 194, 101, 107, 177, 206, 250, 179, 154, 47, 29, 144, 246, 120, 115, 27, 188, 252, 18, 150, 214, 198, 144, 182, 149, 178, 71, 12, 252, 147, 243, 126, 143, 187, 181, 21, 250, 253, 120, 103, 240, 28, 73, 36, 139, 61, 124, 6, 180, 161, 218, 142, 82, 245, 168, 60, 7, 197, 252, 83, 83, 68, 102, 64, 138, 25, 132, 194, 88, 236, 135, 232, 53, 70, 19, 212, 62, 102, 170, 32, 22, 5, 208, 231, 207, 89, 208, 157, 87, 96, 148, 67, 164, 172, 97, 249, 238, 116, 233, 217, 10, 225, 246, 202, 56, 222, 205, 23, 245, 76, 19, 201, 146, 68, 28, 166, 127, 67, 168, 145, 135, 194, 8, 129, 29, 180, 70, 38, 80, 142, 94, 9, 254, 58, 249, 113, 90, 121, 11, 27, 55, 110, 80, 43, 62, 116, 209, 48, 7, 133, 100, 72, 227, 79, 69, 25, 253, 109, 6, 127, 214, 182, 234, 26, 231, 79, 201, 180, 42, 209, 200, 185, 110, 118, 71, 103, 233, 178, 98, 56, 70, 193, 27, 30, 133, 115, 89, 37, 192, 103, 138, 49, 222, 9, 166, 94, 84, 214, 119, 147, 161, 190, 93, 253, 110, 110, 208, 89, 174, 143, 83, 164, 147, 39, 228, 244, 128, 66, 8, 46, 42, 225, 45, 150, 234, 138, 17, 139, 48, 33, 36, 88, 43, 219, 159, 3, 28, 230, 122, 23, 4, 116, 204, 220, 129, 173, 42, 0, 176, 17, 23, 221, 184, 218, 68, 210, 178, 163, 102, 122, 148, 105, 208, 188, 158, 52, 134, 84, 62, 90, 242, 66, 174, 54, 14, 51, 96, 205, 239, 250, 121, 155, 33, 163, 224, 42, 57, 199, 183, 224, 242, 114, 96, 160, 253, 199, 53, 253, 61, 158, 9, 32, 103, 93, 232, 0, 125, 247, 146, 32, 217, 137, 214, 186, 110, 15, 91, 127, 246, 127, 33, 135, 7, 44, 2, 162, 224, 205, 152, 69, 128, 162, 87, 10, 160, 38, 36, 59, 72, 118, 204, 166, 236, 102, 149, 44, 13, 60, 187, 63, 83, 19, 49, 253, 110, 147, 55, 20, 172, 83, 120, 0, 177, 96, 25, 45, 154, 37, 135, 8, 145, 124, 182, 7, 12, 47, 170, 118, 175, 60, 131, 98, 0, 128, 43, 58, 251, 84, 47, 10, 77, 140, 175, 223, 52, 132, 221, 193, 62, 191, 80, 150, 188, 62, 194, 149, 9, 145, 250, 79, 199, 24, 233, 21, 13, 55, 35, 212, 59, 6, 124, 64, 93, 67, 61, 98, 219, 129, 3, 163, 178, 107, 8, 173, 253, 110, 79, 167, 255, 253, 215, 170, 129, 49, 148, 31, 58, 203, 79, 101, 113, 18, 123, 212, 29, 194, 173, 8, 1, 14, 232, 64, 95, 205, 2, 18, 64, 181, 240, 112, 39, 155, 254, 68, 182, 53, 203, 108, 109, 215, 168, 12, 186, 184, 4, 45, 21, 222, 54, 155, 212, 64, 96, 186, 246, 158, 161, 32, 68, 205, 3, 240, 2, 207, 218, 168, 147, 50, 219, 208, 252, 74, 112, 131, 200, 28, 86, 42, 228, 62, 6, 128, 202, 234, 205, 32, 102, 143, 154, 128, 165, 77, 208, 44, 20, 134, 200, 75, 221, 112, 218, 178, 194, 170, 245, 151, 185, 16, 201, 98, 209, 37, 129, 168, 175, 23, 99, 72, 251, 40, 217, 139, 209, 131, 185, 222, 232, 197, 152, 198, 147, 33, 226, 225, 66, 211, 51, 150, 105, 103, 241, 27, 44, 236, 24, 170, 51, 142, 222, 8, 157, 106, 232, 145, 236, 170, 1, 17, 89, 91, 216, 40, 238, 187, 81, 30, 53, 51, 146, 206, 216, 142, 62, 189, 71, 145, 64, 40, 244, 96, 92, 51, 186, 135, 165, 172, 17, 180, 165, 204, 104, 232, 84, 147, 125, 49, 217, 183, 133, 14, 208, 208, 207, 195, 139, 234, 193, 139, 16, 25, 22, 131, 233, 222, 232, 96, 177, 245, 73, 223, 134, 164, 24, 121, 190, 150, 229, 85, 177, 233, 82, 164, 96, 175, 62, 105, 247, 93, 3, 84, 254, 101, 226, 65, 71, 70, 161, 190, 127, 111, 31, 239, 186, 185, 119, 191, 66, 238, 32, 58, 187, 136, 8, 3, 210, 127, 197, 17, 70, 225, 84, 248, 206, 18, 94, 163, 26, 93, 105, 96, 217, 210, 142, 12, 14, 91, 182, 61, 62, 208, 0, 91, 118, 195, 221, 38, 127, 81, 166, 106, 206, 141, 43, 191, 27, 205, 130, 156, 33, 135, 135, 38, 245, 222, 64, 39, 162, 70, 207, 152, 249, 105, 249, 4, 200, 130, 77, 65, 132, 230, 174, 163, 227, 57, 235, 106, 70, 186, 163, 244, 173, 248, 45, 238, 86, 108, 215, 68, 243, 114, 185, 124, 203, 135, 227, 122, 201, 215, 100, 19, 206, 13, 5, 29, 116, 117, 80, 19, 19, 112, 63, 63, 235, 42, 57, 7, 19, 253, 206, 106, 4, 143, 78, 61, 244, 217, 71, 252, 226, 229, 130, 205, 45, 254, 194, 233, 40, 250, 208, 32, 163, 106, 241, 161, 218, 23, 238, 94, 126, 208, 142, 223, 231, 43, 103, 178, 149, 172, 62, 33, 0, 198, 45, 197, 208, 157, 106, 21, 40, 88, 9, 211, 112, 8, 130, 246, 234, 102, 193, 185, 158, 176, 61, 40, 85, 16, 39, 238, 25, 205, 29, 100, 166, 5, 109, 34, 165, 187, 210, 36, 190, 170, 57, 243, 144, 157, 243, 24, 251, 95, 239, 23, 44, 149, 164, 179, 147, 231, 141, 111, 223, 3, 48, 167, 187, 172, 174, 225, 98, 187, 98, 180, 154, 99, 207, 38, 79, 233, 142, 163, 169, 59, 63, 151, 235, 70, 63, 32, 110, 112, 210, 11, 31, 221, 250, 245, 142, 90, 123, 8, 207, 96, 97, 29, 1, 178, 16, 151, 21, 4, 34, 19, 170, 74, 136, 70, 75, 164, 117, 97, 7, 209, 49, 64, 39, 67, 211, 233, 201, 255, 147, 85, 141, 238, 60, 253, 3, 26, 62, 66, 50, 69, 64, 4, 218, 85, 154, 254, 54, 24, 27, 136, 127, 254, 10, 77, 5, 60, 74, 116, 36, 4, 98, 129, 167, 228, 134, 187, 106, 250, 136, 174, 45, 180, 102, 38, 209, 246, 192, 231, 164, 95, 56, 74, 228, 22, 67, 141, 36, 124, 53, 192, 99, 254, 21, 110, 116, 72, 112, 231, 100, 195, 39, 222, 90, 212, 189, 174, 78, 220, 228, 36, 54, 54, 138, 230, 117, 22, 228, 24, 146, 90, 182, 180, 37, 142, 173, 194, 115, 146, 236, 191, 82, 132, 141, 5, 126, 82, 203, 70, 33, 147, 197, 234, 90, 201, 200, 180, 161, 34, 90, 156, 99, 108, 181, 185, 244, 105, 182, 12, 152, 53, 171, 149, 41, 243, 158, 153, 243, 14, 38, 161, 177, 181, 33, 27, 15, 194, 135, 20, 31, 156, 61, 53, 84, 209, 114, 179, 83, 178, 160, 170, 46, 15, 101, 178, 94, 60, 43, 22, 149, 141, 49, 199, 172, 178, 186, 23, 138, 173, 41, 244, 95, 129, 232, 165, 232, 32, 172, 45, 78, 227, 148, 56, 20, 163, 104, 35, 236, 99, 73, 132, 19, 138, 182, 171, 73, 185, 246, 99, 11, 62, 190, 255, 149, 83, 20, 37, 221, 202, 233, 83, 224, 78, 153, 149, 224, 4, 209, 138, 204, 161, 38, 50, 79, 187, 219, 29, 201, 189, 191, 107, 171, 0, 6, 23, 119, 144, 217, 146, 27, 249, 243, 234, 22, 146, 198, 248, 99, 200, 164, 120, 12, 147, 174, 101, 88, 248, 180, 111, 219, 38, 236, 203, 74, 76, 14, 73, 204, 131, 220, 92, 255, 176, 63, 133, 157, 99, 165, 237, 108, 195, 247, 104, 180, 228, 0, 128, 47, 135, 74, 184, 226, 134, 106, 118, 36, 34, 95, 80, 243, 17, 191, 4, 68, 48, 19, 28, 227, 232, 85, 147, 24, 173, 54, 20, 138, 240, 105, 203, 133, 145, 20, 134, 169, 85, 21, 168, 214, 136, 206, 46, 18, 85, 191, 167, 15, 255, 182, 60, 63, 93, 42, 61, 47, 22, 18, 82, 120, 223, 24, 50, 136, 141, 218, 227, 224, 120, 171, 49, 221, 134, 68, 222, 222, 123, 72, 17, 112, 23, 183, 227, 200, 170, 121, 13, 251, 113, 0, 184, 12, 128, 123, 75, 103, 100, 200, 161, 51, 255, 63, 83, 237, 198, 191, 117, 144, 109, 212, 34, 220, 180, 68, 69, 137, 55, 96, 216, 89, 42, 186, 104, 144, 227, 5, 102, 170, 118, 161, 133, 41, 227, 25, 244, 127, 249, 252, 132, 81, 115, 107, 59, 151, 158, 71, 69, 36, 52, 220, 254, 120, 107, 3, 100, 8, 25, 64, 174, 6, 71, 203, 11, 43, 41, 79, 75, 55, 50, 190, 208, 109, 48, 185, 253, 105, 126, 25, 1, 9, 136, 131, 150, 92, 200, 64, 154, 187, 74, 55, 167, 21, 193, 217, 202, 181, 65, 28, 213, 40, 105, 129, 212, 54, 141, 205, 106, 110, 7, 68, 109, 21, 73, 201, 173, 56, 193, 154, 144, 174, 152, 3, 81, 195, 27, 100, 231, 16, 28, 217, 11, 186, 123, 79, 149, 180, 167, 126, 93, 206, 180, 33, 45, 178, 250, 52, 96, 242, 26, 96, 128, 119, 128, 5, 24, 142, 156, 173, 159, 47, 97, 153, 16, 102, 86, 217, 161, 236, 31, 99, 33, 255, 206, 150, 165, 247, 136, 45, 175, 175, 211, 130, 174, 3, 21, 95, 41, 216, 230, 109, 168, 48, 10, 48, 97, 87, 201, 3, 221, 206, 15, 59, 6, 33, 88, 107, 115, 33, 0, 90, 170, 162, 68, 98, 150, 102, 85, 114, 79, 125, 72, 153, 218, 188, 221, 145, 206, 165, 157, 136, 96, 46, 175, 41, 99, 190, 46, 249, 224, 131, 74, 125, 229, 129, 156, 152, 248, 100, 215, 64, 40, 64, 98, 75, 249, 120, 171, 74, 65, 160, 210, 0, 215, 248, 207, 129, 148, 171, 192, 96, 79, 35, 196, 165, 232, 4, 48, 107, 145, 31, 1, 64, 145, 143, 235, 75, 43, 122, 197, 193, 97, 73, 13, 142, 36, 62, 34, 109, 47, 161, 122, 207, 184, 192, 76, 238, 209, 142, 195, 205, 178, 136, 233, 41, 184, 135, 53, 251, 69, 167, 190, 247, 16, 95, 116, 70, 225, 177, 113, 197, 94, 21, 252, 105, 245, 235, 93, 100, 226, 203, 253, 224, 214, 107, 187, 133, 152, 220, 200, 234, 237, 64, 252, 25, 76, 96, 58, 80, 201, 68, 154, 131, 141, 202, 221, 199, 26, 186, 87, 110, 199, 201, 251, 60, 170, 68, 240, 6, 15, 122, 149, 213, 110, 94, 246, 11, 50, 31, 170, 9, 3, 4, 84, 132, 9, 73, 147, 39, 120, 106, 52, 35, 73, 2, 226, 214, 68, 214, 228, 91, 4, 31, 196, 39, 129, 78, 61, 170, 247, 156, 136, 233, 67, 80, 154, 115, 140, 149, 159, 250, 206, 22, 26, 152, 199, 195, 240, 163, 80, 78, 92, 97, 238, 106, 145, 105, 18, 191, 108, 86, 197, 194, 227, 157, 51, 127, 25, 178, 11, 56, 114, 15, 102, 78, 165, 59, 53, 200, 28, 206, 141, 235, 10, 61, 154, 192, 58, 228, 132, 135, 241, 19, 116, 114, 185, 103, 210, 146, 117, 162, 19, 163, 244, 251, 123, 194, 148, 19, 209, 218, 105, 158, 87, 242, 132, 219, 4, 188, 104, 0, 219, 95, 47, 151, 15, 115, 87, 217, 154, 124, 220, 237, 40, 45, 188, 241, 81, 110, 249, 17, 69, 70, 13, 230, 76, 68, 26, 118, 147, 250, 254, 76, 195, 46, 65, 79, 181, 24, 143, 125, 55, 49, 109, 57, 171, 53, 45, 93, 198, 137, 254, 229, 160, 116, 221, 4, 20, 22, 202, 4, 87, 64, 235, 68, 180, 214, 192, 169, 224, 96, 104, 250, 56, 49, 87, 70, 38, 239, 58, 124, 27, 69, 83, 232, 127, 154, 42, 186, 53, 91, 76, 129, 202, 72, 209, 197, 182, 57, 64, 2, 34, 82, 214, 42, 126, 198, 170, 222, 241, 87, 230, 6, 162, 158, 199, 39, 215, 119, 83, 72, 172, 176, 208, 204, 67, 174, 212, 153, 70, 2, 150, 162, 175, 242, 13, 170, 143, 4, 45, 96, 44, 190, 62, 148, 184, 239, 234, 134, 116, 239, 63, 195, 15, 136, 12, 238, 69, 18, 46, 22, 187, 103, 212, 202, 66, 185, 44, 68, 55, 85, 29, 82, 181, 184, 183, 61, 200, 152, 103, 13, 82, 236, 12, 213, 158, 205, 33, 177, 139, 117, 206, 105, 78, 80, 153, 27, 222, 169, 37, 112, 79, 99, 182, 217, 35, 9, 210, 28, 241, 196, 236, 51, 104, 204, 247, 74, 118, 216, 247, 107, 182, 56, 105, 185, 131, 225, 2, 58, 78, 59, 75, 218, 52, 166, 17, 52, 0, 149, 191, 153, 67, 56, 36, 10, 169, 247, 143, 175, 203, 160, 41, 77, 251, 166, 119, 153, 146, 101, 149, 114, 22, 23, 25, 95, 128, 254, 130, 30, 100, 28, 164, 202, 224, 231, 159, 170, 9, 101, 210, 211, 144, 16, 83, 146, 204, 101, 168, 56, 133, 169, 168, 7, 65, 46, 224, 177, 28, 233, 43, 7, 27, 93, 58, 239, 75, 237, 14, 197, 213, 232, 57, 30, 39, 211, 248, 243, 192, 246, 85, 190, 64, 215, 7, 136, 60, 10, 67, 168, 185, 128, 60, 77, 210, 235, 211, 123, 24, 55, 66, 163, 201, 141, 30, 212, 130, 125, 171, 104, 55, 254, 60, 106, 25, 41, 109, 172, 195, 46, 50, 183, 222, 15, 55, 171, 47, 79, 1, 40, 32, 241, 140, 103, 13, 70, 187, 151, 50, 192, 125, 54, 108, 98, 39, 134, 10, 211, 94, 62, 198, 206, 100, 58, 220, 107, 30, 237, 113, 236, 157, 202, 244, 132, 239, 230, 147, 248, 166, 249, 79, 251, 123, 198, 203, 207, 46, 147, 37, 200, 158, 18, 237, 175, 91, 12, 225, 179, 140, 145, 84, 137, 5, 34, 56, 36, 102, 199, 89, 101, 225, 185, 38, 200, 1, 251, 73, 98, 211, 137, 135, 22, 32, 241, 144, 245, 140, 106, 22, 158, 105, 45, 192, 109, 25, 249, 251, 42, 206, 58, 20, 71, 169, 175, 96, 37, 147, 146, 34, 56, 220, 19, 137, 142, 151, 49, 174, 177, 103, 143, 251, 245, 165, 93, 105, 122, 52, 78, 57, 65, 25, 21, 241, 1, 221, 30, 228, 250, 89, 49, 51, 171, 21, 155, 92, 201, 1, 143, 132, 91, 124, 192, 40, 59, 79, 10, 185, 189, 76, 188, 123, 120, 181, 78, 208, 139, 217, 222, 121, 208, 224, 212, 175, 25, 202, 29, 122, 175, 168, 211, 52, 146, 3, 4, 50, 37, 161, 159, 183, 223, 7, 181, 180, 190, 147, 118, 119, 120, 81, 27, 132, 2, 111, 182, 81, 192, 95, 145, 229, 95, 61, 226, 51, 208, 197, 151, 207, 221, 165, 180, 219, 71, 50, 248, 72, 250, 38, 118, 202, 190, 235, 60, 246, 243, 163, 217, 101, 132, 31, 163, 89, 1, 1, 248, 13, 148, 100, 176, 29, 186, 10, 154, 87, 253, 143, 209, 162, 216, 130, 91, 229, 29, 187, 250, 150, 193, 67, 80, 165, 162, 148, 91, 54, 17, 187, 179, 154, 233, 234, 41, 185, 102, 176, 132, 48, 233, 133, 97, 225, 68, 142, 131, 90, 239, 63, 224, 150, 164, 91, 24, 251, 33, 2, 135, 117, 188, 154, 104, 59, 129, 88, 186, 141, 130, 165, 198, 149, 66, 110, 201, 20, 104, 195, 30, 7, 14, 66, 9, 32, 73, 120, 168, 111, 28, 58, 171, 103, 164, 57, 173, 10, 182, 125, 27, 169, 15, 97, 200, 167, 41, 187, 184, 141, 180, 176, 77, 43, 242, 232, 131, 252, 98, 76, 67, 254, 210, 239, 183, 232, 132, 16, 253, 129, 155, 236, 170, 13, 42, 244, 88, 86, 150, 60, 103, 59, 214, 240, 75, 20, 46, 9, 212, 71, 63, 64, 64, 135, 240, 67, 72, 253, 112, 73, 152, 250, 231, 142, 192, 149, 23, 137, 14, 115, 60, 135, 29, 134, 12, 4, 138, 86, 47, 7, 122, 51, 134, 249, 28, 222, 227, 191, 96, 107, 151, 157, 4, 200, 161, 131, 50, 248, 55, 222, 99, 97, 171, 84, 209, 146, 75, 78, 33, 37, 129, 172, 194, 235, 181, 248, 146, 127, 66, 72, 189, 59, 77, 211, 157, 5, 241, 113, 111, 47, 185, 24, 142, 240, 212, 125, 96, 166, 132, 16, 57, 30, 83, 164, 63, 243, 225, 56, 62, 87, 111, 75, 102, 154, 175, 213, 41, 186, 116, 42, 51, 144, 94, 22, 179, 252, 103, 231, 125, 45, 235, 218, 57, 232, 207, 132, 2, 221, 118, 176, 232, 181, 222, 247, 46, 2, 49, 120, 172, 47, 206, 38, 148, 117, 106, 135, 201, 193, 118, 63, 253, 24, 133, 68, 133, 95, 1, 152, 35, 6, 109, 234, 25, 183, 59, 113, 192, 238, 0, 112, 90, 30, 61, 39, 240, 75, 65, 75, 158, 162, 45, 38, 104, 227, 112, 194, 159, 141, 220, 187, 60, 121, 227, 233, 192, 197, 207, 249, 183, 20, 57, 3, 235, 71, 88, 146, 178, 101, 78, 32, 20, 255, 17, 95, 139, 112, 195, 73, 69, 86, 222, 21, 157, 86, 30, 42, 152, 188, 77, 191, 35, 212, 40, 88, 107, 162, 186, 102, 98, 40, 110, 179, 174, 198, 123, 69, 177, 169, 179, 107, 182, 249, 232, 28, 37, 193, 162, 99, 36, 171, 32, 27, 152, 97, 167, 55, 141, 248, 67, 132, 152, 125, 113, 60, 183, 139, 232, 96, 196, 91, 47, 104, 109, 249, 128, 171, 19, 153, 23, 112, 142, 191, 90, 146, 188, 161, 112, 31, 110, 31, 50, 65, 137, 55, 6, 44, 234, 57, 112, 190, 39, 71, 112, 115, 56, 177, 158, 35, 190, 153, 119, 97, 128, 72, 38, 252, 67, 27, 130, 18, 68, 149, 0, 246, 236, 9, 194, 140, 219, 18, 131, 227, 224, 116, 68, 154, 140, 79, 135, 223, 154, 73, 141, 84, 177, 6, 149, 233, 48, 234, 166, 74, 8, 9, 228, 241, 33, 84, 205, 226, 241, 43, 63, 9, 70, 163, 184, 58, 204, 53, 160, 46, 248, 38, 67, 155, 157, 207, 182, 112, 171, 65, 140, 50, 174, 80, 17, 226, 170, 243, 169, 80, 130, 101, 242, 89, 137, 5, 158, 52, 16, 139, 119, 175, 210, 112, 63, 50, 53, 173, 35, 64, 13, 40, 133, 102, 197, 242, 170, 152, 13, 102, 105, 138, 61, 100, 1, 109, 248, 107, 190, 169, 15, 45, 182, 36, 236, 34, 188, 125, 44, 119, 140, 63, 87, 74, 197, 201, 85, 193, 36, 71, 152, 130, 186, 122, 234, 175, 99, 73, 241, 207, 131, 134, 210, 136, 211, 25, 61, 202, 242, 167, 189, 163, 112, 162, 132, 55, 64, 94, 113, 111, 60, 51, 100, 136, 83, 238, 135, 190, 14, 147, 201, 36, 240, 173, 37, 45, 25, 153, 71, 15, 36, 200, 0, 241, 149, 194, 180, 141, 54, 37, 17, 72, 88, 157, 107, 137, 145, 203, 178, 208, 5, 190, 112, 19, 200, 90, 152, 8, 122, 212, 172, 130, 0, 156, 83, 106, 199, 126, 53, 126, 118, 78, 142, 164, 110, 2, 51, 206, 229, 115, 164, 191, 188, 96, 242, 143, 76, 210, 19, 63, 160, 210, 53, 167, 191, 74, 137, 36, 107, 94, 236, 129, 8, 128, 42, 156, 72, 104, 220, 101, 196, 198, 141, 191, 235, 202, 210, 187, 143, 3, 222, 146, 109, 142, 94, 19, 176, 150, 161, 130, 14, 191, 77, 100, 101, 96, 10, 249, 193, 34, 40, 141, 99, 39, 170, 25, 64, 196, 228, 160, 7, 219, 209, 252, 9, 114, 137, 126, 157, 36, 124, 50, 106, 130, 138, 182, 166, 54, 247, 227, 157, 200, 78, 225, 82, 14, 233, 140, 69, 26, 10, 67, 15, 41, 109, 11, 178, 125, 76, 165, 155, 68, 205, 194, 3, 146, 142, 85, 112, 3, 191, 97, 46, 210, 36, 135, 212, 160, 164, 87, 223, 8, 242, 110, 0, 138, 113, 115, 3, 255, 95, 124, 195, 237, 171, 192, 244, 168, 74, 174, 184, 181, 149, 17, 63, 110, 106, 173, 219, 66, 203, 120, 137, 222, 69, 236, 124, 226, 216, 137, 75, 217, 128, 79, 121, 189, 237, 116, 142, 247, 17, 9, 193, 66, 160, 232, 27, 85, 174, 153, 198, 4, 238, 28, 199, 138, 28, 204, 64, 178, 151, 43, 24, 208, 179, 88, 149, 94, 59, 255, 108, 237, 122, 86, 199, 172, 246, 24, 3, 13, 142, 84, 140, 207, 186, 6, 112, 201, 69, 44, 94, 160, 76, 155, 125, 146, 24, 254, 41, 204, 125, 175, 32, 233, 112, 251, 221, 52, 2, 96, 153, 128, 81, 191, 165, 233, 101, 20, 22, 49, 15, 235, 228, 193, 200, 48, 165, 179, 192, 154, 192, 52, 56, 235, 8, 216, 21, 72, 50, 4, 173, 34, 50, 135, 19, 114, 142, 54, 97, 247, 23, 218, 64, 223, 120, 196, 97, 37, 123, 214, 227, 245, 42, 110, 7, 225, 122, 146, 39, 159, 151, 108, 229, 130, 237, 169, 6, 61, 178, 237, 105, 121, 90, 22, 228, 249, 12, 29, 143, 207, 179, 52, 17, 163, 169, 120, 149, 242, 51, 228, 235, 95, 194, 26, 35, 226, 224, 47, 203, 138, 159, 183, 208, 41, 20, 130, 86, 9, 99, 131, 192, 175, 123, 54, 43, 120, 190, 0, 203, 178, 80, 119, 181, 39, 159, 210, 140, 147, 69, 24, 220, 168, 50, 255, 51, 34, 123, 137, 167, 160, 138, 67, 189, 206, 207, 230, 193, 95, 190, 161, 33, 231, 80, 77, 15, 134, 216, 33, 208, 235, 88, 131, 41, 146, 214, 30, 229, 220, 248, 114, 132, 10, 175, 38, 124, 55, 160, 172, 152, 37, 64, 124, 21, 233, 63, 1, 36, 239, 231, 54, 4, 79, 48, 109, 203, 128, 130, 25, 64, 83, 151, 227, 15, 226, 78, 8, 14, 113, 100, 58, 192, 243, 65, 6, 69, 216, 195, 180, 11, 140, 127, 182, 70, 187, 24, 156, 129, 20, 88, 212, 145, 235, 158, 107, 37, 39, 141, 1, 234, 111, 244, 25, 209, 37, 187, 30, 130, 177, 141, 161, 170, 129, 255, 139, 111, 40, 20, 61, 23, 60, 17, 104, 35, 134, 120, 241, 132, 25, 171, 119, 184, 22, 54, 66, 79, 153, 9, 78, 173, 9, 104, 93, 75, 177, 0, 250, 15, 254, 84, 67, 40, 99, 19, 171, 46, 3, 229, 22, 164, 144, 224, 219, 23, 98, 161, 224, 118, 52, 129, 201, 15, 164, 180, 156, 217, 139, 213, 108, 43, 61, 36, 218, 200, 85, 54, 157, 11, 47, 121, 6, 249, 158, 231, 22, 143, 28, 5, 82, 88, 188, 160, 250, 244, 237, 15, 63, 139, 235, 244, 224, 12, 62, 238, 177, 75, 197, 192, 225, 203, 38, 43, 197, 17, 178, 92, 224, 234, 100, 204, 83, 28, 209, 46, 156, 213, 31, 48, 32, 94, 118, 226, 27, 36, 212, 223, 0, 153, 208, 193, 156, 145, 100, 148, 119, 213, 20, 106, 96, 6, 105, 229, 182, 199, 33, 153, 118, 148, 36, 54, 80, 199, 254, 116, 202, 39, 201, 245, 163, 177, 236, 75, 159, 233, 37, 27, 37, 31, 25, 237, 65, 19, 190, 222, 251, 179, 199, 245, 147, 197, 238, 205, 16, 169, 250, 233, 174, 212, 235, 9, 115, 105, 161, 145, 199, 19, 202, 208, 98, 213, 47, 27, 53, 57, 124, 248, 12, 245, 2, 234, 64, 107, 213, 182, 5, 243, 29, 238, 192, 110, 132, 174, 114, 243, 123, 102, 119, 241, 100, 86, 27, 13, 210, 46, 7, 204, 144, 180, 223, 55, 47, 37, 126, 104, 16, 62, 77, 75, 245, 4, 128, 241, 149, 0, 93, 154, 82, 0, 146, 255, 99, 57, 57, 247, 120, 185, 201, 183, 102, 10, 66, 221, 252, 235, 33, 64, 60, 69, 250, 119, 211, 223, 118, 189, 253, 229, 194, 13, 230, 100, 111, 209, 71, 90, 217, 141, 40, 196, 171, 168, 118, 252, 16, 192, 124, 148, 41, 109, 126, 234, 85, 219, 95, 122, 167, 217, 196, 199, 231, 219, 77, 100, 190, 106, 253, 225, 180, 190, 193, 0, 76, 181, 254, 210, 241, 196, 76, 50, 158, 234, 120, 116, 136, 75, 52, 192, 192, 6, 16, 9, 0, 137, 110, 112, 246, 55, 14, 204, 135, 140, 93, 226, 121, 120, 0, 112, 2, 165, 151, 57, 96, 215, 201, 178, 237, 229, 193, 76, 145, 83, 87, 17, 121, 130, 192, 21, 116, 6, 252, 97, 219, 72, 173, 174, 227, 26, 128, 206, 197, 51, 31, 122, 29, 187, 60, 132, 168, 148, 90, 39, 219, 87, 117, 178, 210, 99, 121, 43, 116, 82, 21, 209, 134, 11, 3, 24, 117, 15, 222, 207, 206, 41, 242, 2, 150, 44, 174, 28, 218, 155, 181, 71, 196, 137, 193, 135, 105, 16, 172, 54, 79, 115, 79, 192, 207, 130, 21, 195, 213, 37, 244, 50, 82, 36, 62, 236, 31, 79, 193, 244, 166, 96, 239, 250, 238, 181, 65, 254, 193, 246, 135, 204, 94, 71, 6, 198, 168, 141, 48, 0, 228, 153, 70, 86, 4, 28, 87, 208, 182, 82, 12, 93, 32, 70, 176, 196, 176, 106, 36, 30, 166, 151, 128, 33, 89, 28, 171, 26, 120, 184, 227, 229, 5, 106, 165, 100, 57, 82, 248, 165, 3, 172, 98, 114, 235, 68, 178, 80, 228, 66, 139, 50, 107, 235, 68, 148, 115, 127, 111, 199, 8, 95, 64, 190, 239, 90, 232, 64, 0, 6, 234, 225, 143, 45, 171, 9, 242, 88, 109, 135, 246, 54, 31, 158, 46, 196, 2, 189, 147, 152, 70, 147, 170, 150, 134, 77, 87, 165, 64, 134, 254, 202, 193, 190, 200, 89, 21, 217, 241, 229, 110, 154, 16, 15, 20, 111, 112, 183, 208, 75, 130, 111, 201, 84, 41, 9, 133, 8, 7, 224, 162, 168, 20, 118, 191, 48, 134, 215, 129, 68, 223, 44, 193, 132, 2, 94, 177, 33, 166, 248, 249, 106, 16, 36, 231, 134, 245, 48, 213, 23, 155, 109, 206, 95, 77, 211, 49, 193, 95, 6, 63, 113, 52, 75, 163, 225, 78, 242, 245, 34, 153, 240, 53, 187, 224, 205, 223, 173, 78, 196, 129, 100, 150, 139, 157, 111, 145, 145, 176, 203, 100, 190, 115, 50, 94, 43, 137, 96, 175, 169, 91, 204, 155, 101, 35, 121, 68, 254, 116, 65, 85, 63, 75, 187, 28, 104, 224, 7, 117, 149, 129, 209, 50, 89, 60, 199, 215, 117, 147, 205, 221, 36, 39, 103, 69, 5, 91, 43, 115, 26, 131, 98, 153, 144, 123, 149, 82, 120, 45, 177, 229, 37, 3, 134, 3, 25, 25, 184, 6, 41, 142, 180, 82, 218, 18, 152, 89, 155, 83, 237, 55, 171, 31, 193, 37, 232, 246, 138, 105, 215, 81, 37, 35, 15, 80, 198, 146, 21, 41, 18, 128, 242, 250, 32, 56, 47, 150, 116, 193, 46, 178, 84, 7, 39, 124, 147, 132, 29, 94, 255, 48, 164, 66, 255, 9, 228, 33, 195, 122, 6, 125, 64, 113, 90, 186, 179, 51, 52, 90, 224, 112, 204, 14, 92, 37, 179, 184, 62, 169, 189, 97, 143, 199, 179, 212, 55, 196, 106, 26, 201, 36, 192, 253, 157, 170, 209, 155, 153, 2, 232, 152, 154, 107, 233, 138, 245, 110, 208, 41, 38, 7, 216, 79, 185, 21, 17, 47, 80, 0, 116, 242, 11, 247, 93, 85, 116, 81, 108, 129, 246, 103, 41, 56, 161, 167, 108, 143, 107, 93, 165, 115, 215, 33, 66, 113, 122, 49, 89, 38, 225, 162, 94, 221, 169, 44, 81, 225, 195, 233, 14, 65, 188, 17, 86, 245, 52, 185, 69, 2, 179, 210, 185, 45, 211, 134, 178, 166, 115, 53, 239, 161, 187, 176, 206, 216, 31, 191, 63, 65, 193, 192, 150, 237, 9, 132, 13, 147, 243, 87, 148, 116, 213, 205, 43, 243, 242, 188, 76, 28, 49, 49, 23, 25, 240, 67, 227, 120, 46, 124, 153, 165, 127, 13, 91, 250, 140, 65, 79, 67, 59, 49, 37, 124, 125, 245, 226, 224, 150, 193, 165, 6, 92, 189, 189, 114, 199, 239, 13, 11, 78, 42, 4, 195, 24, 115, 23, 231, 32, 232, 201, 24, 166, 12, 113, 53, 46, 6, 126, 74, 245, 144, 37, 22, 63, 36, 29, 149, 11, 229, 83, 109, 7, 78, 199, 22, 129, 204, 162, 118, 44, 133, 11, 234, 253, 12, 40, 110, 150, 187, 80, 66, 126, 16, 121, 32, 36, 237, 19, 235, 217, 50, 94, 253, 46, 25, 54, 124, 191, 67, 128, 143, 42, 184, 117, 108, 218, 112, 116, 123, 163, 187, 1, 209, 135, 24, 107, 156, 168, 193, 71, 247, 36, 117, 246, 4, 6, 51, 32, 147, 42, 170, 142, 101, 142, 216, 148, 9, 170, 154, 108, 165, 208, 106, 179, 236, 100, 17, 214, 232, 158, 145, 58, 210, 148, 100, 65, 255, 69, 232, 172, 194, 200, 56, 80, 66, 244, 90, 42, 3, 244, 123, 171, 22, 102, 1, 134, 9, 165, 208, 173, 136, 221, 156, 4, 57, 185, 34, 149, 106, 140, 21, 227, 60, 35, 1, 107, 176, 74, 109, 81, 66, 147, 172, 91, 32, 251, 21, 168, 167, 188, 93, 142, 159, 188, 16, 134, 67, 51, 137, 234, 158, 123, 112, 31, 186, 28, 160, 137, 8, 7, 202, 94, 190, 250, 24, 169, 229, 130, 232, 112, 166, 225, 153, 46, 29, 148, 108, 241, 111, 97, 103, 125, 32, 21, 119, 25, 185, 241, 35, 116, 211, 138, 35, 11, 140, 1, 50, 178, 91, 134, 31, 89, 69, 22, 53, 119, 150, 81, 48, 39, 29, 224, 77, 102, 117, 139, 139, 40, 32, 17, 153, 24, 17, 228, 3, 142, 9, 43, 147, 22, 88, 113, 105, 213, 11, 252, 157, 187, 174, 53, 201, 193, 239, 146, 244, 163, 122, 203, 41, 99, 32, 211, 22, 29, 32, 147, 13, 142, 239, 67, 44, 69, 185, 0, 18, 242, 222, 11, 86, 211, 59, 104, 196, 181, 152, 143, 248, 62, 42, 193, 1, 88, 157, 77, 37, 227, 81, 165, 213, 176, 198, 92, 178, 15, 200, 176, 67, 190, 21, 168, 146, 219, 164, 0, 168, 147, 110, 132, 1, 72, 131, 228, 128, 52, 183, 124, 198, 223, 172, 123, 135, 244, 217, 13, 246, 241, 207, 7, 149, 50, 180, 21, 206, 254, 34, 64, 200, 235, 210, 217, 43, 28, 171, 8, 111, 66, 96, 122, 29, 178, 7, 31, 150, 156, 42, 26, 16, 42, 117, 8, 141, 189, 104, 175, 27, 159, 237, 255, 232, 62, 221, 210, 6, 138, 159, 14, 199, 115, 146, 70, 143, 242, 80, 233, 9, 182, 197, 191, 70, 246, 98, 66, 216, 11, 123, 31, 236, 123, 39, 144, 67, 118, 202, 86, 217, 24, 86, 20, 21, 245, 34, 247, 186, 201, 134, 65, 169, 196, 231, 87, 152, 62, 97, 122, 38, 69, 180, 26, 81, 100, 88, 175, 203, 26, 107, 1, 209, 180, 202, 141, 4, 254, 108, 129, 190, 124, 23, 214, 130, 213, 6, 154, 17, 9, 204, 169, 47, 142, 113, 161, 125, 89, 56, 230, 112, 129, 212, 189, 164, 89, 145, 95, 107, 253, 37, 251, 80, 174, 134, 57, 230, 71, 96, 211, 74, 226, 191, 103, 45, 9, 49, 20, 141, 205, 246, 134, 236, 194, 62, 102, 23, 155, 67, 50, 74, 18, 164, 52, 20, 149, 193, 121, 75, 253, 217, 224, 74, 157, 179, 202, 11, 73, 63, 224, 175, 9, 228, 182, 51, 193, 218, 108, 140, 217, 141, 107, 243, 41, 170, 215, 184, 109, 9, 250, 187, 123, 28, 254, 94, 4, 116, 24, 193, 28, 106, 54, 71, 147, 229, 246, 248, 241, 143, 163, 219, 146, 137, 12, 6, 240, 226, 7, 207, 24, 234, 243, 108, 0, 120, 104, 189, 104, 203, 153, 201, 109, 142, 89, 214, 89, 59, 244, 210, 187, 164, 135, 156, 122, 42, 206, 6, 108, 97, 197, 73, 145, 78, 24, 204, 168, 7, 224, 221, 209, 48, 254, 29, 67, 34, 1, 144, 220, 143, 43, 109, 52, 56, 200, 150, 169, 17, 236, 11, 252, 209, 36, 155, 147, 36, 4, 48, 13, 172, 29, 178, 65, 206, 245, 57, 7, 228, 239, 210, 132, 79, 68, 219, 165, 174, 169, 132, 212, 11, 236, 243, 130, 195, 118, 57, 109, 137, 186, 208, 62, 202, 14, 138, 60, 6, 86, 116, 129, 172, 242, 27, 124, 14, 184, 68, 81, 195, 59, 124, 98, 214, 22, 50, 47, 218, 125, 181, 77, 125, 0, 170, 53, 202, 202, 10, 227, 195, 167, 32, 230, 102, 193, 187, 80, 246, 42, 143, 48, 167, 17, 129, 246, 5, 195, 134, 79, 251, 148, 55, 84, 215, 101, 73, 52, 134, 60, 65, 211, 14, 226, 131, 243, 136, 83, 3, 246, 249, 130, 137, 71, 141, 250, 152, 45, 20, 92, 98, 248, 223, 50, 149, 86, 182, 191, 107, 178, 14, 96, 1, 120, 83, 109, 65, 165, 174, 171, 166, 91, 6, 51, 72, 123, 128, 169, 104, 194, 53, 154, 59, 122, 16, 111, 220, 253, 113, 207, 131, 236, 170, 244, 16, 176, 188, 9, 234, 126, 78, 210, 169, 162, 105, 165, 138, 179, 56, 233, 48, 51, 170, 188, 215, 233, 186, 145, 18, 54, 209, 203, 75, 92, 158, 198, 58, 10, 120, 51, 52, 244, 55, 117, 188, 177, 145, 41, 173, 98, 110, 28, 74, 244, 141, 60, 198, 80, 174, 246, 95, 120, 176, 247, 149, 91, 22, 218, 94, 113, 108, 250, 0, 200, 17, 127, 144, 206, 107, 9, 139, 229, 206, 233, 44, 253, 100, 155, 142, 72, 20, 118, 157, 254, 144, 236, 79, 219, 33, 174, 104, 14, 217, 193, 249, 59, 28, 238, 207, 106, 38, 25, 6, 75, 32, 88, 124, 158, 30, 98, 60, 34, 172, 35, 163, 216, 134, 111, 198, 166, 120, 54, 109, 118, 250, 86, 31, 6, 27, 5, 81, 182, 187, 226, 162, 43, 80, 61, 35, 235, 127, 28, 113, 172, 157, 225, 69, 2, 9, 94, 11, 117, 103, 40, 189, 236, 131, 75, 136, 12, 60, 112, 66, 159, 208, 178, 200, 175, 37, 217, 225, 97, 130, 113, 221, 120, 69, 149, 8, 155, 10, 107, 183, 247, 75, 108, 165, 115, 121, 133, 46, 35, 11, 18, 59, 5, 5, 31, 102, 199, 163, 159, 112, 58, 19, 229, 72, 185, 188, 120, 198, 252, 213, 139, 7, 68, 33, 139, 255, 154, 14, 229, 156, 23, 2, 129, 169, 220, 13, 222, 23, 70, 251, 249, 106, 81, 145, 11, 38, 185, 108, 97, 133, 15, 27, 180, 3, 179, 138, 129, 132, 234, 199, 28, 42, 194, 246, 31, 201, 124, 189, 160, 217, 209, 19, 53, 204, 199, 181, 194, 149, 166, 142, 223, 241, 81, 154, 26, 68, 151, 41, 188, 90, 32, 208, 162, 247, 39, 43, 139, 64, 145, 170, 245, 56, 210, 102, 166, 86, 205, 54, 225, 128, 151, 208, 125, 131, 204, 124, 175, 67, 44, 11, 198, 180, 230, 51, 96, 122, 30, 212, 104, 225, 105, 213, 104, 166, 37, 42, 72, 184, 90, 67, 184, 20, 62, 83, 127, 162, 165, 72, 147, 219, 30, 23, 12, 215, 100, 101, 172, 87, 108, 46, 154, 157, 227, 1, 164, 46, 254, 50, 176, 155, 39, 230, 243, 36, 169, 17, 141, 100, 223, 68, 196, 200, 144, 39, 37, 154, 252, 70, 211, 122, 69, 94, 74, 12, 36, 224, 193, 39, 66, 245, 15, 17, 157, 254, 38, 162, 51, 11, 213, 83, 116, 216, 206, 23, 14, 4, 204, 128, 247, 192, 199, 4, 33, 169, 186, 117, 231, 31, 18, 195, 225, 185, 58, 22, 196, 57, 23, 197, 112, 17, 115, 45, 120, 250, 109, 147, 116, 133, 129, 68, 170, 73, 60, 59, 58, 182, 18, 194, 225, 75, 250, 59, 172, 151, 228, 238, 198, 181, 73, 27, 154, 238, 125, 222, 1, 9, 211, 17, 28, 144, 53, 8, 102, 12, 121, 73, 180, 146, 61, 226, 64, 77, 22, 154, 93, 141, 56, 68, 61, 65, 61, 26, 41, 32, 18, 19, 12, 237, 175, 159, 130, 7, 80, 17, 176, 104, 146, 7, 178, 13, 131, 14, 17, 110, 41, 100, 171, 214, 92, 131, 187, 184, 45, 39, 17, 46, 24, 143, 4, 142, 146, 225, 225, 143, 131, 95, 65, 127, 64, 58, 87, 205, 141, 120, 246, 189, 17, 0, 252, 254, 231, 37, 76, 71, 7, 171, 92, 227, 247, 196, 133, 212, 211, 20, 103, 109, 4, 77, 56, 62, 60, 227, 229, 55, 65, 143, 126, 163, 198, 85, 187, 241, 95, 116, 132, 34, 20, 234, 153, 211, 106, 192, 144, 104, 112, 18, 131, 232, 80, 180, 202, 178, 238, 64, 228, 35, 250, 236, 188, 178, 67, 140, 158, 187, 164, 18, 188, 27, 249, 67, 94, 91, 71, 3, 196, 103, 222, 127, 153, 23, 112, 252, 55, 113, 197, 22, 31, 84, 186, 253, 218, 242, 39, 232, 116, 246, 142, 219, 140, 107, 85, 10, 226, 137, 151, 115, 242, 6, 166, 179, 68, 47, 80, 100, 148, 160, 101, 219, 89, 28, 125, 180, 28, 232, 26, 227, 234, 141, 225, 18, 223, 106, 109, 0, 7, 225, 103, 41, 193, 96, 43, 242, 206, 192, 50, 110, 39, 187, 177, 241, 184, 80, 40, 163, 147, 130, 55, 99, 218, 9, 192, 169, 157, 123, 22, 129, 220, 254, 30, 118, 32, 49, 76, 232, 255, 194, 113, 153, 79, 137, 156, 100, 13, 162, 96, 155, 105, 176, 159, 81, 21, 120, 210, 200, 53, 145, 169, 55, 212, 244, 24, 26, 113, 70, 57, 9, 203, 202, 143, 148, 190, 118, 22, 76, 210, 58, 92, 23, 33, 110, 6, 250, 95, 3, 103, 223, 131, 219, 102, 135, 43, 20, 124, 179, 65, 100, 236, 109, 117, 39, 199, 101, 238, 202, 5, 103, 167, 137, 30, 164, 154, 214, 200, 89, 146, 147, 158, 76, 170, 105, 179, 73, 208, 65, 188, 47, 85, 213, 232, 30, 207, 144, 53, 18, 106, 3, 54, 209, 3, 209, 104, 173, 123, 183, 62, 46, 117, 90, 172, 157, 181, 117, 252, 125, 72, 11, 42, 234, 98, 214, 80, 46, 143, 44, 122, 31, 193, 78, 109, 225, 129, 148, 217, 190, 20, 206, 141, 129, 149, 225, 204, 69, 171, 33, 195, 123, 252, 54, 114, 155, 71, 159, 44, 209, 10, 220, 246, 70, 192, 122, 105, 15, 113, 212, 224, 80, 144, 33, 215, 60, 214, 119, 84, 6, 168, 24, 201, 202, 51, 52, 8, 199, 123, 153, 253, 213, 152, 186, 167, 206, 92, 32, 169, 53, 219, 212, 166, 121, 198, 131, 95, 146, 229, 11, 146, 43, 112, 224, 134, 189, 110, 216, 90, 32, 228, 85, 83, 91, 184, 229, 244, 177, 178, 247, 168, 28, 196, 178, 243, 100, 34, 128, 106, 232, 218, 219, 215, 216, 87, 186, 192, 46, 111, 102, 111, 27, 6, 229, 233, 246, 144, 64, 119, 189, 95, 211, 215, 109, 202, 86, 223, 33, 160, 104, 17, 224, 223, 126, 96, 0, 213, 44, 186, 217, 236, 100, 93, 104, 176, 212, 210, 56, 32, 101, 205, 28, 11, 6, 95, 251, 209, 232, 12, 107, 182, 231, 64, 129, 68, 65, 110, 67, 137, 5, 100, 167, 145, 36, 133, 187, 35, 43, 77, 150, 129, 198, 82, 234, 220, 170, 168, 205, 15, 132, 186, 88, 5, 78, 80, 20, 44, 103, 202, 194, 36, 149, 217, 148, 212, 103, 64, 114, 46, 214, 32, 18, 36, 93, 129, 48, 177, 201, 168, 0, 137, 192, 55, 156, 172, 226, 87, 45, 63, 240, 88, 65, 12, 121, 22, 124, 6, 8, 87, 182, 161, 180, 87, 215, 226, 144, 190, 229, 12, 123, 62, 249, 14, 177, 133, 196, 193, 37, 50, 161, 83, 156, 233, 128, 162, 167, 26, 114, 112, 95, 252, 115, 64, 243, 140, 176, 41, 220, 155, 105, 105, 47, 7, 118, 96, 244, 98, 67, 34, 145, 166, 175, 166, 223, 195, 3, 194, 35, 243, 59, 68, 24, 245, 166, 161, 234, 14, 121, 172, 1, 142, 157, 105, 246, 173, 212, 14, 11, 184, 212, 187, 133, 140, 122, 190, 165, 233, 148, 156, 126, 175, 58, 64, 91, 67, 124, 199, 38, 132, 191, 150, 93, 53, 135, 147, 224, 134, 4, 20, 135, 62, 17, 247, 251, 18, 251, 190, 75, 177, 68, 105, 131, 38, 176, 194, 169, 197, 20, 103, 75, 112, 0, 35, 34, 166, 130, 115, 151, 220, 190, 6, 169, 185, 164, 56, 14, 121, 13, 78, 166, 131, 99, 94, 57, 217, 200, 235, 86, 46, 76, 77, 20, 40, 73, 52, 72, 50, 245, 196, 59, 193, 6, 25, 120, 42, 75, 17, 229, 190, 218, 25, 28, 20, 137, 21, 126, 76, 244, 82, 205, 135, 86, 127, 119, 248, 147, 16, 168, 178, 127, 3, 192, 138, 57, 139, 1, 249, 9, 63, 10, 119, 83, 83, 3, 230, 194, 13, 249, 235, 246, 34, 92, 165, 92, 55, 251, 48, 177, 251, 236, 227, 34, 124, 60, 222, 117, 76, 163, 190, 192, 54, 200, 62, 51, 136, 114, 206, 70, 16, 187, 107, 140, 105, 142, 9, 251, 53, 236, 30, 143, 236, 169, 96, 196, 90, 0, 228, 52, 33, 87, 67, 112, 54, 27, 42, 204, 164, 113, 163, 186, 217, 107, 197, 47, 76, 21, 49, 98, 176, 227, 144, 236, 110, 43, 57, 243, 104, 97, 59, 58, 48, 172, 84, 193, 103, 140, 45, 254, 226, 179, 79, 26, 27, 16, 208, 62, 222, 105, 22, 56, 4, 219, 67, 42, 186, 142, 113, 162, 162, 224, 178, 115, 245, 128, 221, 194, 5, 13, 52, 108, 95, 54, 12, 226, 37, 130, 184, 38, 14, 136, 32, 28, 43, 132, 25, 126, 69, 79, 113, 70, 14, 153, 45, 3, 82, 233, 253, 19, 198, 10, 233, 213, 133, 52, 152, 217, 146, 2, 147, 13, 102, 45, 80, 119, 254, 163, 164, 113, 161, 125, 79, 199, 185, 88, 144, 72, 233, 21, 6, 125, 231, 23, 8, 151, 49, 152, 154, 119, 37, 130, 196, 194, 200, 151, 24, 223, 184, 25, 215, 215, 193, 224, 8, 73, 73, 244, 201, 117, 209, 217, 196, 21, 239, 114, 161, 138, 250, 248, 240, 190, 225, 219, 50, 191, 58, 232, 218, 126, 243, 50, 28, 232, 161, 156, 192, 60, 97, 161, 23, 4, 77, 54, 148, 232, 42, 140, 80, 224, 103, 9, 166, 101, 20, 14, 148, 138, 81, 64, 129, 70, 78, 235, 132, 67, 48, 227, 194, 205, 246, 154, 169, 100, 127, 12, 231, 111, 88, 23, 191, 63, 251, 4, 111, 236, 137, 55, 122, 240, 209, 166, 184, 132, 186, 10, 138, 42, 194, 197, 48, 243, 206, 219, 79, 53, 145, 24, 100, 119, 116, 128, 99, 12, 17, 173, 79, 74, 222, 170, 193, 228, 22, 108, 137, 218, 186, 133, 160, 193, 190, 131, 202, 160, 182, 236, 87, 222, 197, 25, 89, 59, 3, 59, 224, 228, 72, 83, 187, 12, 38, 156, 224, 171, 23, 35, 234, 206, 56, 34, 132, 9, 25, 224, 28, 15, 27, 31, 122, 206, 89, 66, 46, 184, 212, 69, 112, 93, 153, 12, 68, 44, 132, 116, 115, 128, 127, 45, 128, 248, 30, 123, 214, 86, 25, 214, 46, 40, 31, 180, 238, 34, 71, 78, 85, 178, 19, 28, 237, 133, 16, 183, 58, 220, 88, 117, 171, 40, 50, 57, 229, 39, 28, 162, 89, 224, 32, 48, 42, 9, 70, 69, 200, 114, 164, 18, 80, 139, 255, 8, 245, 214, 198, 186, 112, 109, 51, 40, 154, 173, 3, 248, 41, 252, 186, 21, 152, 99, 162, 138, 72, 74, 98, 234, 124, 57, 135, 125, 75, 184, 107, 91, 35, 89, 149, 170, 101, 45, 255, 52, 194, 231, 8, 71, 53, 151, 232, 147, 106, 193, 32, 171, 218, 107, 175, 130, 147, 58, 199, 14, 166, 56, 84, 245, 182, 131, 210, 77, 140, 145, 67, 39, 36, 81, 74, 50, 59, 175, 65, 108, 69, 176, 119, 111, 5, 220, 228, 215, 249, 151, 25, 193, 88, 104, 245, 3, 166, 241, 56, 24, 85, 116, 252, 110, 31, 45, 31, 53, 135, 25, 184, 240, 2, 165, 102, 56, 14, 203, 151, 32, 183, 137, 174, 112, 232, 137, 49, 192, 76, 194, 48, 51, 3, 18, 202, 128, 171, 244, 35, 28, 200, 1, 21, 28, 132, 83, 66, 47, 117, 147, 113, 37, 106, 80, 118, 217, 100, 229, 126, 182, 183, 183, 81, 65, 33, 181, 235, 223, 130, 118, 54, 177, 90, 10, 187, 200, 6, 102, 189, 93, 76, 254, 55, 69, 103, 130, 201, 57, 98, 207, 5, 152, 231, 64, 101, 0, 222, 78, 244, 13, 38, 248, 63, 163, 31, 175, 155, 203, 77, 249, 24, 139, 52, 224, 68, 199, 157, 103, 40, 131, 224, 242, 137, 225, 181, 131, 29, 128, 229, 242, 92, 42, 58, 85, 101, 69, 120, 167, 86, 99, 57, 220, 94, 231, 162, 99, 132, 221, 55, 37, 174, 194, 195, 194, 178, 232, 61, 38, 66, 42, 168, 182, 23, 106, 241, 198, 192, 217, 215, 50, 59, 225, 217, 8, 24, 147, 240, 54, 209, 158, 154, 47, 7, 212, 225, 38, 19, 4, 241, 170, 42, 2, 107, 13, 240, 77, 158, 228, 103, 237, 64, 173, 177, 143, 48, 208, 217, 140, 31, 108, 229, 140, 70, 10, 51, 122, 64, 75, 12, 145, 211, 165, 169, 145, 240, 228, 255, 115, 84, 244, 84, 125, 231, 86, 8, 52, 248, 112, 145, 221, 45, 53, 9, 92, 53, 66, 180, 172, 242, 22, 113, 106, 211, 138, 42, 63, 66, 178, 2, 160, 86, 191, 169, 237, 86, 87, 70, 78, 62, 1, 213, 97, 167, 104, 63, 60, 222, 61, 77, 55, 70, 198, 23, 8, 135, 250, 188, 93, 66, 134, 227, 192, 20, 182, 81, 129, 108, 60, 88, 141, 93, 205, 132, 52, 179, 95, 168, 181, 39, 38, 7, 206, 107, 214, 181, 101, 190, 56, 89, 60, 178, 166, 107, 99, 154, 3, 129, 39, 242, 99, 206, 124, 171, 178, 132, 138, 177, 200, 26, 11, 83, 208, 126, 142, 130, 38, 169, 82, 225, 242, 213, 101, 234, 176, 14, 229, 79, 19, 58, 72, 4, 201, 238, 77, 94, 151, 153, 10, 128, 254, 183, 134, 62, 42, 170, 39, 226, 231, 213, 223, 31, 74, 61, 94, 199, 136, 181, 153, 45, 48, 157, 50, 0, 60, 251, 155, 235, 37, 42, 168, 203, 8, 246, 116, 56, 66, 99, 34, 92, 170, 73, 29, 32, 185, 65, 23, 178, 124, 2, 254, 140, 196, 162, 33, 208, 190, 145, 237, 96, 137, 104, 94, 114, 2, 37, 83, 2, 59, 241, 115, 1, 208, 157, 193, 165, 118, 17, 156, 91, 135, 158, 192, 108, 229, 212, 236, 9, 28, 232, 75, 152, 109, 156, 152, 207, 180, 242, 34, 100, 12, 112, 243, 72, 166, 219, 31, 150, 89, 73, 243, 4, 219, 157, 46, 75, 247, 234, 143, 10, 88, 150, 67, 124, 219, 10, 6, 26, 15, 78, 177, 77, 114, 221, 121, 214, 54, 107, 254, 196, 239, 216, 168, 230, 230, 28, 94, 69, 11, 67, 161, 37, 39, 191, 18, 246, 116, 211, 14, 236, 250, 100, 23, 87, 174, 118, 164, 193, 229, 87, 38, 151, 122, 79, 217, 111, 231, 135, 82, 222, 183, 14, 208, 217, 233, 1, 121, 21, 121, 251, 162, 112, 186, 24, 65, 154, 7, 50, 186, 246, 24, 173, 198, 186, 89, 38, 117, 87, 26, 10, 94, 50, 193, 16, 135, 68, 167, 148, 251, 144, 115, 60, 48, 28, 187, 104, 161, 255, 192, 223, 214, 21, 143, 212, 214, 194, 44, 128, 162, 174, 96, 166, 91, 17, 247, 83, 230, 57, 106, 16, 104, 142, 57, 78, 235, 132, 164, 183, 174, 244, 56, 103, 113, 118, 96, 65, 40, 163, 7, 30, 134, 159, 76, 247, 31, 213, 30, 211, 203, 74, 188, 201, 38, 148, 123, 234, 157, 17, 192, 247, 26, 71, 201, 37, 226, 94, 89, 80, 180, 133, 94, 22, 94, 64, 39, 236, 227, 143, 163, 140, 126, 160, 125, 29, 99, 25, 56, 107, 89, 224, 93, 213, 48, 6, 174, 250, 127, 228, 165, 247, 193, 169, 53, 114, 176, 103, 191, 116, 118, 23, 97, 219, 73, 32, 245, 48, 81, 200, 42, 64, 190, 25, 126, 59, 198, 254, 136, 198, 30, 154, 159, 115, 133, 150, 85, 84, 81, 198, 220, 150, 67, 242, 28, 203, 189, 175, 18, 152, 74, 40, 37, 157, 101, 110, 50, 144, 94, 53, 103, 188, 60, 29, 26, 46, 38, 83, 193, 5, 41, 184, 184, 43, 65, 187, 143, 212, 123, 83, 233, 166, 99, 246, 45, 86, 5, 154, 252, 76, 189, 21, 225, 45, 46, 102, 104, 8, 225, 163, 7, 24, 22, 178, 179, 22, 211, 176, 8, 21, 171, 190, 105, 171, 201, 153, 254, 143, 184, 143, 79, 44, 99, 68, 83, 14, 221, 163, 40, 137, 30, 178, 150, 183, 168, 122, 148, 165, 126, 56, 195, 175, 162, 202, 69, 219, 175, 193, 166, 186, 252, 212, 135, 62, 240, 153, 66, 35, 240, 111, 129, 19, 45, 140, 227, 201, 185, 173, 12, 198, 97, 228, 10, 41, 101, 107, 165, 106, 140, 26, 165, 166, 212, 16, 53, 231, 73, 16, 53, 1, 76, 73, 68, 160, 75, 156, 36, 124, 36, 235, 158, 89, 98, 139, 130, 113, 19, 7, 232, 95, 112, 121, 125, 246, 130, 143, 5, 190, 216, 150, 246, 149, 254, 80, 102, 35, 174, 82, 169, 90, 189, 234, 242, 87, 91, 255, 78, 98, 197, 208, 42, 141, 31, 17, 233, 217, 151, 135, 31, 87, 71, 132, 255, 79, 98, 62, 168, 221, 172, 247, 225, 46, 155, 228, 196, 76, 95, 161, 68, 233, 203, 192, 236, 146, 149, 43, 85, 98, 110, 35, 242, 116, 216, 226, 78, 157, 114, 40, 157, 241, 33, 12, 14, 205, 192, 201, 12, 219, 255, 249, 6, 105, 86, 80, 101, 184, 26, 56, 101, 227, 25, 69, 140, 68, 35, 245, 46, 110, 221, 50, 51, 237, 20, 23, 174, 4, 137, 44, 207, 0, 201, 52, 147, 202, 111, 190, 72, 177, 228, 97, 210, 163, 150, 154, 2, 241, 97, 151, 93, 210, 199, 127, 218, 57, 45, 91, 96, 194, 160, 131, 205, 120, 102, 209, 51, 28, 68, 48, 199, 130, 77, 209, 13, 204, 141, 240, 217, 165, 87, 56, 119, 167, 53, 207, 74, 106, 163, 27, 100, 6, 252, 238, 114, 63, 40, 162, 86, 5, 153, 91, 98, 106, 124, 126, 91, 105, 173, 98, 74, 222, 85, 125, 64, 213, 102, 107, 3, 158, 243, 8, 167, 224, 50, 213, 167, 203, 19, 16, 235, 4, 43, 104, 115, 28, 1, 168, 41, 94, 241, 67, 161, 140, 45, 89, 118, 172, 162, 117, 224, 95, 245, 214, 214, 220, 117, 32, 163, 250, 84, 55, 174, 20, 7, 238, 213, 19, 177, 36, 105, 6, 75, 15, 72, 154, 18, 64, 85, 108, 213, 56, 86, 240, 124, 142, 134, 82, 100, 199, 198, 243, 12, 146, 80, 148, 251, 223, 79, 153, 181, 188, 100, 167, 228, 175, 227, 241, 164, 28, 120, 133, 170, 177, 202, 232, 101, 171, 207, 2, 32, 212, 173, 147, 117, 171, 218, 83, 216, 241, 94, 3, 103, 187, 230, 26, 168, 180, 159, 4, 173, 108, 61, 76, 182, 54, 115, 135, 144, 2, 39, 81, 126, 253, 172, 101, 144, 19, 3, 202, 12, 234, 87, 11, 222, 188, 82, 204, 27, 81, 216, 250, 163, 94, 103, 73, 149, 233, 251, 140, 172, 85, 138, 51, 203, 121, 30, 64, 184, 120, 46, 233, 199, 59, 106, 115, 123, 102, 145, 129, 213, 216, 74, 92, 11, 166, 50, 79, 3, 13, 80, 245, 241, 248, 193, 208, 78, 77, 229, 21, 67, 229, 192, 61, 115, 213, 93, 52, 225, 217, 147, 236, 135, 88, 184, 240, 49, 182, 109, 95, 67, 184, 204, 181, 176, 213, 34, 36, 149, 98, 143, 193, 225, 48, 180, 219, 69, 244, 64, 96, 234, 83, 5, 113, 122, 23, 115, 55, 87, 212, 130, 154, 96, 152, 92, 29, 136, 202, 217, 58, 236, 195, 153, 17, 14, 13, 213, 145, 36, 159, 163, 58, 191, 3, 11, 124, 34, 155, 237, 58, 57, 3, 144, 222, 25, 234, 107, 15, 183, 234, 160, 27, 44, 77, 149, 128, 57, 87, 142, 236, 113, 192, 44, 95, 140, 168, 65, 170, 167, 167, 38, 131, 33, 140, 27, 10, 248, 16, 71, 99, 115, 210, 177, 65, 198, 48, 11, 13, 1, 247, 24, 222, 248, 24, 73, 117, 11, 249, 29, 226, 114, 41, 64, 147, 58, 10, 52, 76, 19, 128, 25, 96, 78, 56, 108, 40, 200, 163, 225, 39, 179, 7, 84, 210, 59, 167, 133, 135, 35, 177, 111, 9, 96, 25, 142, 26, 71, 203, 43, 18, 176, 75, 224, 206, 24, 157, 10, 0, 133, 216, 218, 121, 210, 52, 164, 86, 148, 80, 18, 156, 150, 18, 197, 44, 74, 110, 158, 202, 194, 40, 132, 234, 166, 98, 208, 166, 193, 25, 225, 231, 118, 0, 136, 194, 251, 212, 237, 49, 94, 92, 250, 222, 143, 52, 229, 213, 147, 96, 189, 214, 182, 11, 149, 41, 202, 15, 64, 33, 216, 207, 177, 186, 8, 104, 161, 240, 145, 252, 200, 39, 45, 17, 161, 97, 92, 19, 73, 212, 7, 244, 26, 121, 77, 143, 178, 161, 48, 158, 113, 14, 2, 65, 152, 91, 51, 229, 240, 117, 52, 69, 236, 248, 75, 195, 136, 161, 56, 0, 23, 112, 252, 156, 183, 148, 116, 31, 215, 235, 78, 32, 227, 131, 219, 238, 212, 252, 43, 42, 128, 55, 12, 182, 80, 113, 168, 229, 158, 225, 54, 105, 162, 106, 41, 139, 28, 74, 213, 152, 209, 193, 3, 150, 77, 235, 193, 20, 105, 99, 66, 169, 242, 111, 229, 169, 14, 6, 26, 138, 220, 112, 172, 16, 75, 232, 233, 186, 153, 185, 253, 168, 242, 85, 202, 128, 167, 112, 84, 210, 132, 250, 61, 132, 1, 125, 51, 232, 128, 164, 2, 53, 141, 55, 237, 54, 114, 104, 0, 129, 0, 193, 109, 124, 129, 83, 48, 234, 47, 210, 47, 50, 95, 62, 156, 180, 113, 73, 81, 202, 149, 63, 161, 102, 201, 28, 165, 21, 108, 118, 205, 67, 107, 1, 242, 249, 196, 248, 179, 171, 198, 129, 145, 136, 228, 121, 198, 99, 87, 83, 133, 192, 17, 176, 12, 81, 54, 19, 66, 108, 58, 203, 110, 50, 112, 189, 12, 124, 32, 108, 70, 3, 184, 2, 11, 57, 206, 188, 255, 33, 137, 78, 63, 170, 63, 236, 212, 38, 102, 226, 116, 148, 26, 22, 30, 220, 80, 0, 244, 42, 166, 19, 192, 1, 151, 213, 35, 69, 244, 40, 88, 47, 251, 77, 95, 95, 77, 88, 40, 37, 119, 122, 50, 235, 233, 158, 215, 48, 199, 214, 84, 232, 211, 139, 193, 220, 144, 193, 94, 223, 110, 90, 146, 145, 98, 176, 143, 66, 215, 70, 157, 227, 114, 24, 187, 64, 249, 185, 134, 81, 219, 11, 187, 148, 239, 34, 232, 15, 29, 132, 225, 157, 46, 100, 93, 168, 237, 161, 86, 129, 132, 59, 67, 32, 58, 231, 132, 191, 42, 194, 2, 45, 50, 22, 180, 120, 142, 49, 191, 153, 215, 200, 224, 118, 235, 211, 196, 116, 27, 45, 116, 240, 68, 85, 168, 162, 100, 58, 10, 82, 102, 116, 177, 52, 16, 153, 61, 221, 111, 245, 128, 163, 191, 101, 108, 13, 58, 102, 171, 160, 161, 170, 129, 12, 197, 172, 228, 29, 225, 174, 214, 114, 97, 65, 160, 66, 90, 250, 229, 36, 204, 142, 163, 106, 182, 120, 67, 199, 184, 80, 147, 5, 167, 59, 117, 16, 135, 89, 69, 171, 210, 17, 226, 147, 117, 58, 33, 60, 80, 78, 103, 65, 79, 221, 48, 40, 148, 165, 149, 169, 106, 174, 50, 174, 186, 228, 122, 103, 65, 100, 3, 252, 57, 16, 176, 193, 199, 52, 103, 50, 146, 89, 87, 218, 84, 108, 175, 13, 195, 161, 111, 69, 128, 6, 191, 152, 120, 143, 177, 211, 94, 130, 41, 47, 136, 158, 165, 217, 25, 65, 234, 139, 212, 146, 51, 216, 59, 224, 4, 97, 77, 223, 128, 248, 95, 81, 39, 225, 183, 242, 69, 191, 48, 37, 177, 24, 119, 11, 227, 80, 254, 109, 164, 229, 55, 101, 88, 89, 168, 113, 244, 176, 100, 126, 79, 164, 212, 180, 241, 82, 196, 39, 81, 185, 106, 20, 3, 132, 98, 158, 244, 6, 125, 8, 77, 234, 34, 142, 170, 146, 224, 189, 127, 215, 124, 34, 24, 17, 243, 7, 74, 146, 166, 128, 179, 6, 98, 114, 190, 79, 67, 219, 171, 197, 129, 213, 239, 132, 144, 44, 234, 52, 230, 16, 94, 220, 92, 136, 0, 186, 244, 229, 53, 148, 233, 73, 249, 152, 217, 100, 6, 196, 77, 97, 138, 6, 5, 41, 196, 19, 142, 92, 46, 110, 54, 151, 156, 156, 177, 129, 2, 62, 200, 189, 193, 181, 93, 68, 60, 90, 149, 84, 203, 83, 201, 26, 18, 52, 156, 65, 179, 59, 3, 125, 19, 204, 236, 61, 163, 85, 36, 237, 47, 144, 140, 87, 154, 95, 60, 0, 22, 37, 155, 6, 7, 0, 176, 46, 247, 159, 37, 173, 224, 210, 12, 217, 248, 79, 132, 93, 138, 135, 78, 17, 227, 18, 45, 252, 48, 58, 254, 85, 36, 176, 69, 114, 1, 92, 207, 85, 193, 254, 112, 204, 78, 157, 146, 103, 123, 196, 100, 10, 46, 184, 161, 162, 78, 144, 141, 50, 86, 252, 50, 235, 184, 151, 55, 73, 187, 10, 37, 50, 198, 0, 207, 155, 104, 37, 137, 196, 54, 132, 15, 22, 93, 114, 237, 90, 250, 96, 61, 18, 152, 194, 63, 239, 16, 92, 110, 63, 209, 229, 27, 61, 154, 245, 208, 226, 145, 184, 219, 48, 187, 2, 193, 78, 24, 51, 81, 34, 172, 109, 33, 8, 23, 232, 115, 57, 152, 233, 140, 200, 196, 108, 8, 65, 251, 72, 68, 76, 181, 220, 62, 34, 119, 9, 126, 13, 186, 100, 214, 191, 149, 154, 131, 254, 200, 126, 152, 142, 103, 127, 57, 81, 255, 99, 129, 250, 33, 25, 183, 235, 134, 117, 115, 108, 95, 28, 86, 141, 217, 180, 116, 61, 201, 51, 90, 59, 160, 254, 213, 93, 21, 55, 204, 97, 62, 113, 100, 6, 107, 57, 64, 201, 157, 228, 173, 229, 35, 61, 235, 102, 161, 14, 189, 164, 112, 76, 75, 127, 18, 231, 145, 130, 79, 66, 150, 63, 30, 164, 181, 68, 176, 81, 217, 135, 54, 172, 66, 60, 232, 8, 206, 224, 245, 102, 177, 243, 64, 87, 233, 193, 244, 95, 68, 78, 255, 14, 144, 223, 147, 32, 36, 230, 212, 218, 185, 120, 243, 3, 64, 139, 233, 70, 90, 136, 5, 171, 221, 87, 98, 15, 43, 200, 131, 182, 75, 81, 139, 243, 233, 218, 246, 61, 246, 189, 204, 39, 168, 208, 131, 142, 200, 109, 158, 159, 54, 35, 91, 244, 135, 204, 241, 171, 25, 55, 255, 101, 15, 169, 221, 208, 223, 109, 244, 74, 78, 126, 25, 199, 151, 191, 110, 7, 74, 60, 169, 101, 212, 84, 228, 234, 187, 204, 110, 158, 131, 28, 21, 186, 158, 193, 129, 10, 199, 206, 160, 140, 85, 209, 196, 9, 167, 6, 157, 187, 234, 179, 190, 131, 250, 100, 34, 13, 98, 40, 94, 92, 80, 253, 7, 188, 233, 45, 45, 253, 101, 65, 250, 138, 64, 231, 109, 103, 163, 213, 33, 183, 99, 129, 181, 119, 96, 30, 234, 220, 121, 7, 109, 255, 191, 243, 188, 128, 33, 88, 16, 143, 190, 7, 175, 181, 7, 136, 203, 156, 61, 136, 2, 69, 232, 15, 168, 44, 87, 109, 95, 162, 79, 8, 203, 107, 170, 255, 6, 142, 43, 24, 69, 126, 184, 121, 213, 43, 132, 164, 198, 90, 213, 28, 144, 14, 208, 129, 24, 87, 170, 144, 6, 210, 3, 76, 220, 223, 12, 223, 191, 33, 42, 214, 218, 4, 236, 194, 78, 55, 215, 50, 24, 189, 64, 174, 166, 176, 110, 190, 23, 1, 240, 177, 109, 233, 48, 246, 116, 99, 168, 128, 246, 212, 113, 10, 168, 40, 72, 17, 184, 129, 191, 38, 158, 23, 192, 155, 21, 229, 218, 137, 168, 21, 5, 65, 28, 231, 85, 4, 162, 146, 77, 99, 228, 4, 175, 0, 27, 51, 79, 18, 111, 97, 103, 172, 198, 6, 91, 139, 38, 66, 197, 242, 219, 5, 14, 8, 127, 229, 156, 139, 219, 109, 240, 174, 20, 236, 133, 43, 9, 16, 154, 83, 150, 171, 193, 155, 217, 243, 153, 236, 53, 153, 145, 224, 243, 157, 117, 96, 254, 115, 134, 149, 101, 59, 101, 15, 198, 164, 71, 7, 151, 178, 173, 211, 235, 229, 9, 9, 216, 99, 178, 234, 127, 134, 80, 226, 219, 50, 22, 218, 153, 150, 38, 209, 194, 32, 66, 149, 30, 149, 124, 77, 60, 85, 118, 172, 161, 20, 91, 231, 157, 210, 37, 69, 134, 33, 41, 44, 94, 151, 67, 239, 251, 3, 113, 169, 83, 73, 192, 111, 144, 160, 228, 150, 146, 175, 125, 222, 155, 123, 120, 75, 181, 150, 133, 17, 143, 51, 247, 178, 66, 113, 249, 92, 118, 95, 180, 201, 132, 159, 86, 91, 191, 124, 12, 73, 198, 125, 183, 26, 228, 56, 235, 78, 33, 22, 9, 155, 187, 49, 250, 236, 170, 144, 254, 195, 101, 129, 204, 91, 228, 232, 83, 34, 133, 250, 46, 165, 21, 160, 235, 156, 158, 172, 126, 180, 15, 106, 81, 212, 178, 143, 108, 132, 145, 237, 39, 123, 72, 143, 134, 212, 135, 27, 181, 113, 35, 239, 62, 130, 93, 164, 198, 100, 252, 46, 187, 226, 50, 86, 186, 179, 196, 12, 114, 191, 240, 54, 83, 30, 201, 49, 217, 127, 202, 74, 23, 109, 216, 68, 15, 233, 13, 104, 156, 230, 200, 214, 101, 233, 175, 45, 5, 172, 142, 11, 77, 59, 228, 0, 66, 239, 70, 115, 148, 7, 67, 156, 0, 219, 132, 98, 27, 9, 144, 144, 30, 53, 134, 115, 149, 61, 197, 154, 111, 239, 224, 80, 8, 112, 195, 96, 237, 218, 127, 219, 175, 24, 53, 17, 216, 165, 180, 141, 129, 29, 101, 54, 56, 100, 229, 159, 192, 145, 52, 2, 153, 108, 127, 155, 32, 254, 200, 17, 46, 107, 205, 54, 29, 73, 214, 178, 101, 9, 180, 115, 31, 88, 253, 161, 26, 155, 120, 10, 213, 21, 61, 170, 228, 187, 227, 229, 20, 13, 78, 254, 11, 124, 55, 50, 212, 53, 166, 57, 186, 207, 11, 213, 242, 19, 215, 140, 210, 48, 136, 185, 131, 193, 183, 132, 145, 191, 195, 179, 86, 138, 83, 78, 219, 176, 27, 23, 80, 193, 113, 109, 148, 119, 97, 141, 92, 177, 70, 159, 13, 162, 139, 226, 84, 187, 120, 201, 21, 43, 242, 165, 195, 138, 70, 31, 24, 41, 58, 171, 25, 33, 30, 148, 108, 195, 238, 74, 70, 117, 66, 159, 139, 32, 185, 29, 159, 14, 174, 35, 224, 103, 147, 56, 1, 125, 46, 177, 13, 15, 3, 210, 81, 64, 203, 182, 202, 93, 111, 154, 2, 2, 86, 44, 152, 8, 167, 132, 60, 182, 67, 198, 182, 59, 84, 227, 96, 138, 251, 2, 21, 239, 58, 230, 57, 146, 193, 221, 12, 196, 61, 207, 231, 98, 11, 4, 177, 246, 216, 157, 89, 172, 178, 170, 175, 209, 162, 131, 47, 188, 103, 192, 165, 115, 97, 209, 252, 98, 231, 65, 187, 46, 16, 110, 192, 45, 167, 91, 57, 155, 96, 143, 39, 54, 55, 180, 107, 144, 184, 96, 68, 85, 87, 192, 17, 40, 90, 175, 225, 73, 82, 213, 27, 132, 105, 207, 130, 108, 238, 253, 31, 9, 225, 127, 214, 181, 236, 100, 148, 115, 164, 35, 73, 14, 16, 234, 43, 172, 86, 134, 180, 41, 169, 247, 54, 159, 144, 53, 44, 127, 207, 23, 253, 167, 50, 123, 106, 3, 74, 109, 111, 6, 105, 58, 228, 41, 2, 35, 131, 31, 117, 123, 73, 250, 146, 154, 175, 173, 76, 76, 196, 238, 132, 163, 150, 146, 219, 219, 162, 17, 80, 195, 1, 218, 225, 161, 23, 203, 47, 33, 130, 142, 104, 196, 107, 96, 40, 22, 79, 138, 143, 242, 139, 17, 13, 26, 49, 232, 163, 53, 24, 253, 191, 27, 53, 110, 165, 218, 243, 64, 82, 172, 181, 41, 86, 38, 50, 91, 32, 139, 109, 78, 246, 77, 178, 120, 213, 175, 168, 119, 85, 63, 220, 60, 161, 201, 134, 51, 147, 129, 22, 230, 17, 123, 201, 180, 236, 130, 33, 64, 141, 142, 12, 201, 155, 226, 224, 151, 19, 253, 200, 162, 85, 156, 199, 249, 221, 157, 217, 185, 123, 36, 209, 173, 203, 130, 132, 217, 6, 18, 248, 105, 46, 201, 184, 214, 35, 26, 85, 104, 33, 158, 236, 154, 152, 193, 82, 146, 188, 134, 42, 198, 199, 161, 4, 182, 101, 172, 233, 217, 160, 119, 161, 2, 90, 189, 238, 44, 85, 141, 207, 17, 249, 162, 228, 140, 189, 155, 68, 199, 162, 0, 132, 149, 176, 161, 112, 217, 249, 30, 64, 124, 242, 238, 62, 5, 87, 48, 254, 192, 199, 77, 152, 131, 116, 175, 242, 42, 211, 29, 157, 15, 182, 203, 230, 173, 217, 218, 88, 177, 68, 11, 33, 110, 198, 0, 105, 207, 231, 126, 70, 112, 244, 48, 89, 31, 101, 149, 200, 80, 177, 197, 120, 228, 183, 140, 107, 113, 148, 66, 159, 249, 136, 175, 156, 119, 43, 167, 238, 171, 11, 20, 101, 203, 192, 18, 167, 199, 29, 251, 102, 76, 232, 240, 168, 229, 75, 93, 95, 228, 194, 163, 91, 45, 178, 60, 161, 107, 85, 229, 36, 214, 247, 122, 140, 102, 109, 158, 125, 93, 248, 250, 188, 104, 44, 4, 222, 138, 241, 141, 40, 203, 30, 217, 206, 190, 170, 131, 112, 1, 69, 65, 14, 85, 36, 244, 2, 205, 80, 101, 222, 204, 48, 231, 92, 211, 65, 7, 227, 18, 31, 115, 153, 150, 78, 41, 226, 101, 210, 13, 110, 156, 132, 29, 245, 109, 49, 141, 108, 77, 166, 30, 11, 52, 79, 198, 110, 30, 15, 49, 55, 48, 13, 23, 185, 127, 78, 123, 135, 67, 15, 112, 114, 38, 222, 58, 98, 160, 206, 51, 76, 172, 101, 138, 116, 230, 163, 165, 233, 145, 62, 14, 124, 232, 137, 116, 177, 16, 22, 226, 104, 163, 35, 74, 61, 203, 245, 215, 72, 251, 49, 65, 7, 77, 7, 6, 138, 248, 242, 40, 136, 207, 167, 248, 67, 30, 30, 136, 55, 159, 249, 37, 174, 247, 63, 39, 253, 106, 206, 229, 75, 69, 189, 151, 181, 237, 72, 64, 125, 125, 232, 145, 140, 5, 99, 41, 42, 34, 117, 161, 73, 35, 72, 40, 188, 200, 69, 218, 45, 5, 149, 3, 137, 122, 109, 28, 57, 220, 58, 248, 252, 221, 14, 6, 21, 190, 98, 192, 220, 163, 225, 169, 23, 1, 133, 25, 85, 34, 126, 242, 240, 22, 158, 123, 116, 121, 129, 90, 72, 137, 80, 197, 206, 210, 141, 232, 107, 126, 235, 147, 34, 192, 217, 34, 110, 226, 16, 91, 2, 36, 156, 26, 187, 34, 68, 135, 197, 228, 157, 229, 166, 149, 134, 41, 163, 123, 119, 92, 136, 33, 73, 41, 246, 241, 122, 172, 240, 245, 6, 233, 221, 207, 2, 65, 153, 58, 47, 74, 210, 112, 19, 215, 194, 139, 148, 53, 188, 199, 134, 143, 188, 37, 27, 27, 0, 106, 213, 176, 59, 56, 180, 90, 175, 191, 202, 177, 181, 124, 101, 249, 87, 12, 177, 136, 127, 99, 141, 23, 131, 241, 241, 51, 40, 251, 127, 172, 55, 185, 112, 200, 105, 211, 95, 230, 55, 128, 199, 59, 150, 209, 84, 43, 66, 147, 102, 177, 240, 2, 51, 21, 170, 129, 145, 154, 228, 251, 62, 199, 21, 217, 126, 2, 9, 72, 23, 123, 67, 77, 145, 188, 116, 239, 164, 9, 180, 17, 229, 93, 167, 200, 35, 53, 20, 148, 11, 90, 7, 128, 38, 149, 96, 76, 211, 162, 86, 247, 47, 219, 153, 228, 208, 174, 1, 77, 130, 206, 8, 56, 77, 178, 126, 103, 64, 66, 173, 130, 178, 15, 157, 82, 151, 6, 31, 32, 24, 254, 116, 177, 125, 94, 103, 50, 23, 88, 167, 177, 178, 127, 154, 65, 221, 58, 221, 59, 226, 100, 212, 90, 195, 98, 94, 191, 30, 232, 150, 153, 106, 253, 109, 235, 75, 64, 207, 222, 250, 116, 71, 33, 65, 213, 195, 126, 156, 152, 66, 192, 144, 40, 227, 151, 193, 175, 99, 224, 91, 60, 164, 255, 6, 122, 7, 67, 247, 68, 73, 212, 29, 46, 88, 182, 8, 140, 159, 12, 53, 10, 134, 178, 137, 239, 153, 185, 79, 82, 248, 233, 246, 212, 236, 69, 141, 78, 223, 143, 137, 36, 152, 203, 115, 15, 73, 227, 247, 216, 141, 74, 85, 92, 252, 219, 11, 86, 24, 36, 132, 246, 133, 77, 210, 94, 247, 210, 48, 169, 100, 17, 141, 75, 195, 158, 42, 251, 39, 47, 60, 157, 65, 57, 125, 28, 71, 111, 42, 158, 47, 14, 42, 28, 117, 50, 96, 232, 48, 42, 244, 34, 57, 12, 138, 203, 106, 250, 25, 67, 23, 176, 220, 128, 5, 19, 7, 102, 171, 23, 9, 30, 96, 230, 35, 15, 34, 132, 122, 139, 178, 231, 226, 252, 122, 142, 51, 52, 11, 84, 228, 93, 72, 237, 102, 129, 75, 238, 112, 182, 84, 130, 158, 130, 41, 139, 174, 56, 166, 174, 139, 242, 44, 145, 148, 133, 123, 42, 62, 163, 116, 84, 154, 57, 28, 58, 32, 154, 34, 151, 21, 127, 143, 122, 136, 139, 252, 101, 165, 227, 105, 4, 244, 58, 116, 125, 45, 98, 76, 103, 94, 178, 138, 217, 234, 42, 40, 152, 61, 28, 185, 62, 43, 88, 177, 3, 57, 129, 36, 0, 238, 63, 194, 253, 71, 206, 114, 243, 137, 92, 79, 175, 23, 62, 120, 193, 114, 13, 142, 244, 4, 131, 205, 14, 186, 248, 29, 212, 164, 52, 12, 111, 31, 124, 143, 212, 208, 173, 96, 133, 27, 194, 239, 242, 62, 141, 119, 203, 52, 113, 243, 31, 84, 235, 5, 5, 64, 191, 252, 81, 217, 200, 232, 68, 61, 213, 171, 145, 221, 224, 102, 138, 6, 53, 167, 130, 6, 206, 196, 226, 4, 174, 108, 86, 154, 134, 37, 59, 110, 9, 191, 10, 117, 191, 133, 114, 72, 86, 93, 113, 19, 75, 34, 46, 2, 185, 169, 224, 240, 13, 200, 128, 24, 118, 36, 233, 120, 115, 6, 192, 92, 12, 228, 224, 111, 192, 137, 188, 16, 155, 62, 198, 60, 154, 52, 196, 125, 70, 58, 33, 43, 11, 168, 35, 190, 161, 124, 108, 153, 90, 186, 113, 57, 176, 154, 172, 202, 179, 55, 179, 115, 45, 183, 90, 4, 216, 112, 48, 228, 230, 219, 58, 70, 142, 78, 74, 9, 135, 22, 40, 70, 146, 224, 171, 128, 68, 8, 228, 90, 35, 220, 41, 184, 91, 212, 34, 57, 143, 189, 6, 215, 17, 90, 143, 19, 92, 97, 0, 32, 3, 71, 160, 179, 249, 172, 83, 31, 79, 228, 235, 186, 237, 88, 60, 223, 16, 150, 62, 200, 199, 30, 185, 44, 207, 192, 77, 194, 159, 154, 169, 139, 30, 66, 92, 141, 190, 237, 210, 202, 206, 242, 79, 195, 207, 22, 106, 63, 29, 195, 112, 209, 66, 25, 0, 132, 115, 0, 202, 40, 194, 111, 241, 33, 135, 53, 223, 250, 204, 154, 93, 179, 199, 141, 86, 243, 91, 194, 109, 181, 226, 69, 138, 254, 216, 121, 193, 63, 120, 2, 134, 176, 156, 166, 74, 11, 81, 70, 173, 20, 22, 53, 20, 94, 78, 223, 122, 51, 83, 235, 222, 40, 243, 124, 92, 42, 236, 168, 151, 228, 224, 219, 96, 235, 105, 253, 68, 151, 63, 159, 213, 61, 96, 8, 224, 24, 28, 90, 129, 156, 22, 25, 66, 66, 143, 239, 196, 171, 29, 13, 95, 163, 56, 87, 48, 212, 215, 72, 227, 81, 104, 53, 139, 236, 188, 128, 188, 39, 63, 223, 221, 87, 39, 250, 188, 147, 17, 199, 187, 191, 223, 149, 255, 172, 2, 174, 180, 30, 3, 20, 9, 179, 209, 14, 135, 177, 230, 230, 197, 6, 214, 127, 184, 220, 182, 147, 196, 1, 233, 166, 208, 117, 53, 27, 239, 167, 250, 146, 188, 191, 44, 63, 126, 25, 131, 251, 155, 209, 124, 244, 61, 128, 33, 161, 254, 124, 17, 223, 157, 9, 174, 72, 136, 130, 99, 2, 218, 76, 248, 115, 52, 140, 75, 220, 169, 145, 119, 90, 154, 55, 179, 3, 247, 245, 224, 97, 120, 11, 63, 95, 248, 227, 205, 148, 9, 156, 121, 111, 198, 183, 194, 240, 224, 127, 172, 161, 40, 176, 174, 198, 115, 39, 21, 135, 93, 120, 52, 77, 227, 203, 226, 47, 228, 208, 254, 86, 42, 178, 168, 118, 24, 219, 101, 28, 233, 205, 124, 126, 162, 249, 141, 40, 110, 247, 247, 167, 36, 186, 206, 2, 218, 68, 198, 144, 175, 3, 72, 97, 133, 245, 21, 59, 121, 75, 124, 96, 81, 44, 114, 14, 155, 78, 83, 4, 100, 18, 134, 220, 4, 88, 1, 176, 47, 235, 54, 199, 40, 55, 162, 55, 29, 186, 218, 192, 34, 234, 96, 241, 128, 217, 64, 204, 95, 238, 170, 235, 230, 242, 14, 87, 111, 159, 173, 38, 146, 75, 197, 5, 115, 239, 112, 251, 19, 178, 192, 186, 135, 123, 127, 45, 136, 135, 4, 78, 178, 164, 252, 8, 36, 171, 74, 21, 243, 236, 22, 204, 242, 123, 6, 114, 138, 102, 239, 74, 238, 255, 233, 3, 164, 62, 58, 143, 197, 248, 147, 172, 165, 4, 14, 1, 147, 251, 79, 85, 50, 48, 118, 108, 30, 77, 81, 140, 29, 143, 198, 121, 197, 19, 225, 49, 93, 53, 73, 33, 122, 100, 234, 165, 67, 183, 100, 195, 168, 123, 37, 235, 86, 147, 164, 71, 20, 232, 206, 20, 228, 22, 17, 177, 2, 116, 163, 136, 187, 43, 160, 176, 83, 52, 39, 224, 140, 250, 66, 232, 255, 36, 166, 132, 97, 252, 44, 246, 106, 21, 113, 16, 166, 70, 54, 222, 228, 10, 51, 19, 106, 141, 31, 102, 129, 132, 219, 84, 27, 192, 66, 145, 133, 208, 111, 137, 81, 145, 121, 176, 155, 133, 222, 27, 156, 93, 19, 244, 230, 154, 5, 113, 177, 247, 70, 125, 202, 138, 60, 3, 29, 182, 33, 211, 112, 136, 107, 223, 175, 131, 92, 13, 23, 133, 238, 136, 130, 231, 215, 211, 199, 47, 70, 75, 104, 253, 172, 61, 246, 99, 80, 154, 169, 205, 208, 105, 43, 2, 110, 203, 246, 16, 176, 11, 152, 190, 185, 100, 102, 6, 125, 63, 101, 184, 157, 6, 52, 237, 85, 121, 193, 4, 245, 227, 178, 121, 91, 208, 31, 149, 32, 198, 6, 44, 160, 127, 164, 97, 113, 61, 8, 129, 126, 48, 10, 29, 100, 71, 211, 225, 40, 9, 0, 3, 68, 93, 210, 80, 229, 218, 36, 186, 188, 107, 182, 149, 10, 6, 152, 149, 89, 117, 111, 83, 95, 195, 187, 170, 69, 248, 36, 46, 55, 27, 32, 176, 49, 156, 89, 40, 187, 234, 208, 124, 4, 208, 147, 55, 234, 237, 170, 226, 161, 111, 205, 227, 202, 198, 15, 149, 253, 102, 56, 203, 135, 155, 57, 133, 7, 109, 38, 45, 84, 8, 201, 120, 98, 237, 208, 163, 213, 53, 176, 121, 188, 246, 40, 141, 178, 27, 12, 74, 154, 55, 231, 250, 214, 249, 165, 18, 187, 143, 85, 131, 175, 192, 203, 220, 173, 219, 162, 181, 130, 100, 203, 31, 68, 146, 157, 10, 1, 49, 123, 56, 175, 34, 92, 31, 205, 201, 39, 97, 89, 162, 176, 166, 198, 242, 192, 212, 230, 208, 253, 110, 118, 141, 136, 68, 241, 174, 93, 119, 157, 164, 70, 52, 222, 125, 170, 235, 170, 115, 129, 254, 58, 191, 19, 12, 9, 76, 159, 116, 183, 185, 166, 169, 247, 179, 96, 43, 63, 202, 50, 91, 14, 150, 45, 142, 89, 161, 224, 94, 7, 25, 127, 143, 190, 148, 251, 243, 246, 157, 36, 116, 199, 185, 196, 245, 6, 4, 255, 135, 86, 236, 255, 223, 240, 191, 8, 186, 178, 34, 247, 77, 40, 193, 224, 191, 235, 56, 14, 246, 154, 127, 15, 214, 232, 95, 120, 83, 143, 147, 159, 97, 207, 96, 9, 174, 129, 106, 21, 250, 164, 65, 103, 111, 23, 160, 242, 127, 5, 172, 168, 68, 41, 119, 189, 125, 79, 82, 22, 58, 113, 205, 193, 107, 168, 144, 220, 34, 198, 164, 235, 153, 160, 200, 39, 196, 87, 81, 195, 247, 81, 215, 98, 5, 153, 67, 60, 255, 1, 229, 149, 1, 43, 195, 242, 114, 27, 7, 17, 7, 48, 7, 15, 179, 77, 91, 7, 173, 203, 172, 82, 199, 183, 45, 3, 129, 165, 46, 118, 88, 51, 37, 119, 226, 88, 128, 248, 41, 126, 17, 63, 63, 4, 255, 115, 59, 255, 240, 220, 172, 123, 102, 29, 135, 40, 107, 120, 53, 184, 127, 231, 193, 184, 153, 137, 247, 151, 78, 69, 91, 191, 61, 101, 207, 63, 105, 107, 14, 17, 32, 90, 219, 46, 140, 31, 62, 162, 109, 58, 9, 130, 12, 43, 19, 47, 212, 0, 90, 155, 76, 81, 171, 218, 9, 247, 3, 109, 2, 16, 172, 132, 3, 110, 169, 67, 209, 224, 73, 187, 152, 1, 69, 219, 82, 197, 108, 65, 235, 90, 151, 104, 66, 213, 115, 201, 79, 245, 192, 44, 117, 92, 194, 188, 160, 230, 5, 218, 50, 149, 8, 178, 46, 251, 29, 25, 152, 183, 68, 232, 119, 92, 180, 46, 74, 54, 196, 101, 98, 232, 155, 48, 80, 197, 12, 73, 58, 21, 146, 48, 187, 167, 6, 244, 119, 130, 90, 213, 218, 183, 48, 80, 166, 206, 163, 4, 184, 126, 190, 35, 86, 79, 221, 49, 160, 109, 215, 63, 102, 96, 125, 91, 10, 111, 205, 186, 170, 6, 107, 177, 75, 228, 128, 132, 185, 17, 65, 18, 94, 203, 211, 252, 238, 28, 191, 99, 67, 146, 56, 57, 126, 71, 134, 71, 28, 227, 119, 92, 120, 100, 173, 101, 30, 73, 218, 119, 132, 56, 125, 71, 5, 181, 139, 117, 185, 40, 5, 226, 158, 223, 241, 42, 73, 251, 206, 37, 98, 169, 239, 248, 240, 88, 189, 99, 3, 175, 122, 199, 133, 73, 83, 164, 222, 113, 161, 222, 81, 129, 114, 143, 64, 96, 60, 133, 174, 253, 166, 169, 213, 33, 255, 223, 133, 252, 243, 29, 29, 250, 123, 0, 213, 244, 43, 218, 0, 161, 152, 29, 213, 213, 52, 252, 144, 149, 137, 23, 62, 84, 49, 67, 159, 239, 216, 112, 126, 41, 200, 31, 217, 249, 142, 11, 26, 137, 131, 52, 246, 72, 131, 182, 109, 154, 162, 7, 213, 140, 116, 126, 0, 100, 192, 175, 99, 249, 142, 11, 213, 210, 209, 38, 223, 177, 17, 225, 174, 65, 154, 178, 223, 161, 37, 99, 40, 203, 102, 28, 30, 65, 190, 192, 115, 210, 50, 161, 126, 107, 129, 208, 148, 253, 15, 72, 179, 36, 229, 35, 195, 59, 6, 192, 19, 45, 119, 132, 120, 4, 2, 91, 119, 92, 64, 24, 71, 17, 28, 119, 132, 136, 224, 184, 163, 194, 51, 211, 70, 142, 59, 54, 112, 220, 209, 193, 184, 163, 2, 117, 71, 5, 12, 147, 201, 29, 25, 182, 29, 31, 175, 158, 218, 145, 33, 181, 163, 163, 181, 149, 61, 111, 156, 144, 79, 112, 132, 52, 213, 158, 224, 200, 176, 38, 56, 54, 244, 119, 77, 112, 100, 240, 53, 193, 145, 34, 237, 251, 50, 52, 193, 49, 98, 77, 112, 108, 224, 164, 104, 130, 35, 228, 155, 54, 118, 236, 113, 142, 16, 206, 47, 115, 125, 253, 92, 142, 233, 163, 87, 117, 92, 127, 219, 57, 46, 248, 149, 56, 152, 111, 219, 206, 113, 193, 76, 91, 180, 32, 182, 31, 122, 152, 125, 118, 142, 143, 166, 177, 115, 124, 72, 218, 115, 112, 109, 233, 188, 233, 108, 52, 109, 251, 33, 110, 37, 238, 120, 239, 101, 80, 99, 135, 76, 140, 93, 206, 81, 226, 119, 57, 127, 114, 57, 199, 134, 235, 182, 182, 206, 145, 225, 17, 8, 109, 157, 227, 162, 162, 206, 81, 65, 251, 121, 42, 67, 74, 213, 57, 54, 208, 42, 73, 235, 154, 23, 39, 196, 30, 69, 227, 254, 164, 206, 145, 241, 252, 166, 101, 54, 169, 115, 124, 60, 41, 169, 115, 100, 208, 246, 42, 40, 169, 243, 71, 57, 71, 135, 71, 57, 71, 5, 215, 166, 101, 63, 92, 45, 132, 58, 80, 119, 215, 118, 11, 252, 225, 147, 118, 87, 148, 115, 132, 84, 148, 115, 84, 80, 12, 46, 155, 226, 72, 237, 241, 181, 212, 241, 71, 165, 25, 173, 170, 147, 210, 204, 160, 230, 110, 93, 176, 214, 74, 26, 52, 120, 211, 137, 26, 197, 161, 150, 46, 243, 187, 32, 117, 132, 120, 36, 123, 209, 102, 229, 154, 208, 233, 28, 31, 92, 23, 67, 2, 197, 144, 164, 149, 237, 55, 117, 72, 83, 58, 199, 70, 82, 197, 9, 185, 100, 15, 171, 159, 131, 75, 22, 225, 148, 54, 120, 68, 215, 146, 174, 177, 71, 208, 183, 106, 246, 71, 37, 159, 52, 178, 240, 67, 194, 249, 166, 233, 66, 223, 30, 105, 228, 42, 101, 200, 61, 49, 72, 206, 120, 178, 59, 209, 55, 59, 58, 24, 243, 130, 210, 54, 118, 116, 120, 218, 126, 86, 211, 216, 209, 161, 203, 155, 16, 209, 217, 216, 177, 161, 117, 162, 140, 29, 23, 189, 216, 241, 161, 77, 182, 78, 34, 154, 159, 95, 181, 139, 29, 25, 96, 111, 21, 59, 54, 248, 132, 4, 18, 200, 61, 34, 73, 236, 200, 192, 203, 75, 225, 171, 180, 33, 90, 223, 165, 74, 20, 193, 11, 161, 129, 15, 141, 211, 38, 118, 92, 64, 44, 254, 240, 164, 93, 236, 30, 206, 113, 173, 146, 116, 17, 107, 25, 73, 82, 220, 24, 104, 147, 7, 93, 79, 49, 123, 180, 109, 222, 226, 198, 65, 66, 242, 52, 199, 135, 228, 105, 142, 10, 238, 155, 163, 2, 242, 230, 216, 32, 73, 233, 40, 220, 28, 25, 46, 155, 194, 220, 28, 25, 78, 122, 101, 36, 218, 28, 27, 79, 69, 69, 54, 71, 6, 143, 36, 244, 104, 38, 11, 134, 89, 133, 176, 97, 237, 63, 212, 184, 201, 86, 46, 155, 163, 67, 155, 223, 31, 126, 33, 218, 156, 208, 179, 227, 200, 101, 115, 116, 144, 100, 115, 116, 172, 75, 247, 204, 113, 241, 104, 188, 15, 174, 173, 84, 34, 247, 204, 113, 194, 57, 230, 168, 176, 128, 0, 13, 15, 146, 148, 206, 113, 102, 230, 232, 104, 91, 89, 182, 28, 23, 106, 155, 183, 28, 25, 154, 183, 28, 29, 154, 183, 28, 21, 36, 219, 120, 57, 50, 36, 94, 142, 10, 232, 160, 24, 214, 91, 34, 77, 187, 28, 29, 13, 78, 117, 57, 46, 60, 32, 54, 151, 99, 176, 216, 59, 13, 223, 148, 53, 238, 157, 52, 69, 19, 85, 11, 90, 81, 3, 253, 92, 142, 11, 174, 139, 8, 12, 142, 231, 114, 92, 104, 90, 46, 199, 133, 135, 217, 8, 9, 179, 179, 40, 165, 225, 97, 246, 29, 232, 208, 120, 127, 21, 90, 114, 57, 74, 120, 75, 101, 18, 199, 69, 47, 92, 169, 115, 43, 113, 108, 240, 168, 88, 71, 199, 122, 203, 214, 247, 117, 116, 76, 122, 29, 21, 244, 189, 75, 56, 108, 58, 9, 57, 26, 183, 117, 92, 208, 182, 139, 162, 173, 1, 74, 231, 8, 33, 208, 195, 236, 2, 143, 160, 135, 217, 5, 84, 49, 230, 73, 65, 41, 53, 232, 155, 160, 206, 37, 151, 135, 206, 69, 87, 69, 197, 82, 28, 60, 237, 251, 126, 190, 71, 64, 144, 142, 63, 240, 190, 203, 169, 123, 68, 112, 254, 37, 43, 91, 246, 104, 187, 34, 188, 225, 141, 211, 66, 22, 172, 156, 60, 154, 33, 24, 43, 7, 54, 172, 79, 108, 29, 25, 154, 187, 21, 33, 236, 181, 102, 29, 25, 32, 223, 84, 186, 10, 104, 191, 111, 104, 93, 217, 154, 117, 92, 180, 102, 29, 29, 173, 207, 172, 35, 195, 51, 235, 168, 160, 106, 29, 33, 107, 242, 28, 199, 133, 181, 255, 30, 13, 43, 223, 4, 69, 16, 181, 30, 59, 142, 12, 207, 142, 163, 130, 227, 233, 52, 154, 29, 71, 135, 42, 110, 104, 93, 162, 135, 89, 199, 142, 35, 68, 69, 151, 227, 184, 240, 168, 4, 125, 211, 137, 178, 117, 28, 27, 172, 100, 29, 199, 133, 231, 153, 117, 28, 25, 60, 2, 65, 181, 58, 46, 105, 69, 33, 120, 68, 146, 212, 73, 207, 111, 194, 67, 93, 38, 135, 76, 154, 162, 181, 212, 113, 108, 56, 52, 45, 23, 117, 28, 31, 80, 3, 123, 158, 142, 35, 131, 235, 116, 28, 23, 28, 199, 134, 174, 101, 212, 53, 86, 190, 9, 234, 43, 223, 4, 117, 88, 249, 38, 232, 227, 24, 194, 214, 113, 36, 161, 141, 23, 54, 180, 62, 185, 28, 74, 73, 58, 142, 144, 206, 8, 122, 240, 200, 74, 248, 144, 144, 142, 163, 130, 174, 101, 208, 4, 18, 68, 72, 199, 241, 161, 107, 25, 4, 65, 58, 142, 13, 8, 210, 113, 84, 240, 8, 132, 116, 28, 25, 118, 37, 84, 81, 140, 35, 195, 209, 97, 93, 54, 20, 241, 234, 234, 188, 58, 54, 216, 58, 142, 36, 208, 171, 99, 195, 202, 73, 218, 198, 172, 211, 65, 175, 142, 11, 90, 255, 181, 65, 175, 142, 14, 85, 173, 190, 18, 198, 136, 166, 173, 36, 241, 90, 208, 171, 35, 228, 97, 22, 186, 147, 86, 71, 6, 36, 73, 249, 254, 77, 89, 99, 231, 159, 7, 88, 61, 245, 246, 186, 129, 123, 172, 147, 210, 180, 109, 154, 124, 164, 93, 0, 215, 77, 128, 250, 82, 102, 27, 86, 249, 169, 156, 146, 200, 39, 56, 98, 32, 225, 173, 79, 46, 148, 106, 97, 185, 80, 218, 165, 77, 209, 214, 234, 144, 235, 226, 70, 44, 151, 0, 18, 175, 146, 244, 22, 244, 77, 153, 46, 213, 222, 226, 148, 6, 152, 164, 32, 253, 220, 196, 238, 14, 30, 80, 154, 117, 137, 26, 184, 235, 106, 7, 36, 98, 89, 18, 72, 0, 65, 84, 84, 181, 125, 52, 78, 59, 73, 6, 233, 164, 213, 177, 161, 27, 221, 15, 143, 32, 126, 6, 141, 61, 130, 15, 15, 179, 169, 57, 40, 22, 179, 147, 36, 102, 14, 187, 18, 106, 208, 138, 93, 250, 38, 168, 99, 99, 130, 58, 42, 248, 149, 188, 235, 59, 176, 171, 40, 122, 250, 219, 46, 231, 17, 234, 153, 144, 166, 166, 223, 212, 177, 97, 109, 50, 213, 212, 81, 131, 218, 110, 84, 239, 162, 154, 58, 50, 124, 237, 63, 200, 35, 241, 208, 0, 161, 205, 50, 190, 159, 142, 222, 245, 83, 187, 17, 0, 195, 172, 66, 173, 19, 166, 142, 15, 213, 11, 113, 215, 133, 246, 121, 66, 251, 30, 188, 0, 254, 203, 24, 35, 0, 33, 188, 201, 71, 24, 209, 58, 217, 86, 139, 82, 36, 38, 58, 35, 72, 27, 47, 246, 182, 233, 82, 199, 7, 218, 66, 43, 182, 161, 76, 157, 8, 159, 52, 218, 32, 228, 143, 13, 175, 236, 144, 63, 46, 92, 115, 200, 31, 25, 252, 209, 241, 184, 240, 71, 37, 13, 249, 35, 131, 127, 37, 137, 37, 137, 153, 147, 184, 194, 136, 112, 9, 253, 236, 102, 29, 18, 40, 151, 12, 249, 35, 67, 195, 240, 216, 144, 36, 102, 40, 178, 248, 166, 174, 237, 144, 47, 242, 199, 6, 127, 132, 184, 63, 58, 214, 4, 199, 101, 55, 200, 31, 35, 252, 17, 242, 73, 93, 69, 145, 63, 54, 190, 169, 106, 248, 227, 227, 241, 33, 157, 63, 82, 52, 63, 253, 184, 240, 251, 244, 227, 2, 63, 122, 250, 17, 162, 189, 157, 208, 211, 143, 13, 100, 188, 58, 246, 250, 113, 161, 139, 97, 94, 63, 46, 156, 127, 24, 210, 215, 143, 13, 167, 121, 220, 143, 12, 19, 190, 246, 159, 196, 87, 244, 81, 74, 150, 251, 49, 98, 229, 78, 182, 31, 33, 88, 234, 130, 116, 185, 237, 199, 135, 107, 219, 53, 241, 78, 179, 173, 180, 193, 25, 134, 35, 92, 219, 46, 182, 253, 40, 193, 182, 31, 21, 60, 130, 13, 10, 93, 94, 106, 117, 207, 107, 90, 110, 63, 46, 120, 152, 93, 164, 246, 248, 82, 197, 204, 177, 214, 74, 26, 174, 141, 246, 35, 164, 109, 58, 9, 57, 168, 183, 78, 178, 127, 33, 30, 65, 14, 42, 219, 159, 108, 1, 132, 71, 36, 254, 234, 72, 53, 165, 146, 148, 253, 184, 160, 223, 162, 25, 190, 105, 167, 211, 148, 253, 248, 152, 32, 130, 126, 84, 112, 154, 199, 143, 16, 39, 61, 126, 92, 232, 236, 230, 71, 9, 247, 92, 14, 223, 236, 223, 236, 104, 101, 218, 134, 23, 59, 126, 132, 120, 152, 93, 142, 109, 196, 3, 50, 216, 241, 163, 66, 231, 226, 248, 145, 193, 35, 16, 208, 17, 1, 132, 8, 68, 208, 195, 236, 98, 252, 8, 89, 140, 31, 21, 186, 98, 25, 77, 37, 252, 8, 105, 93, 102, 191, 169, 90, 126, 132, 52, 94, 132, 224, 71, 134, 71, 16, 62, 116, 45, 51, 65, 252, 200, 152, 0, 243, 15, 241, 227, 98, 2, 223, 246, 33, 126, 126, 92, 32, 126, 116, 240, 35, 131, 223, 129, 31, 25, 21, 135, 24, 241, 35, 126, 116, 64, 252, 252, 232, 128, 24, 211, 32, 126, 140, 120, 68, 144, 240, 201, 235, 113, 225, 18, 15, 63, 58, 92, 159, 223, 4, 126, 132, 248, 84, 136, 31, 33, 174, 105, 217, 73, 136, 31, 29, 252, 216, 128, 141, 119, 6, 135, 7, 126, 132, 136, 128, 4, 16, 64, 208, 30, 25, 28, 123, 124, 56, 233, 147, 61, 50, 120, 68, 173, 199, 133, 245, 251, 232, 208, 245, 251, 16, 87, 45, 243, 221, 190, 115, 251, 200, 88, 253, 22, 250, 244, 77, 208, 196, 158, 182, 143, 11, 253, 84, 140, 230, 197, 73, 169, 123, 254, 233, 155, 224, 112, 82, 43, 131, 120, 12, 73, 51, 131, 122, 65, 220, 191, 131, 26, 123, 196, 65, 63, 23, 211, 22, 61, 109, 191, 225, 164, 212, 167, 73, 86, 82, 208, 47, 99, 156, 36, 22, 115, 162, 167, 237, 227, 195, 55, 59, 219, 199, 5, 99, 251, 168, 0, 33, 73, 204, 144, 54, 174, 104, 240, 8, 210, 198, 21, 200, 112, 124, 69, 145, 247, 195, 108, 132, 55, 67, 171, 36, 173, 67, 7, 143, 86, 167, 42, 241, 8, 4, 110, 210, 161, 141, 43, 32, 149, 101, 30, 24, 102, 21, 82, 182, 143, 141, 135, 89, 181, 246, 113, 241, 72, 210, 62, 46, 60, 46, 105, 207, 121, 100, 144, 164, 92, 40, 220, 206, 99, 195, 37, 131, 60, 124, 211, 238, 151, 26, 39, 52, 17, 193, 105, 30, 183, 243, 200, 208, 245, 31, 209, 180, 79, 110, 231, 145, 129, 210, 140, 103, 182, 157, 71, 6, 136, 101, 151, 122, 135, 56, 133, 51, 89, 144, 227, 202, 144, 131, 106, 59, 143, 11, 190, 80, 169, 237, 60, 54, 48, 229, 209, 243, 189, 206, 163, 163, 173, 243, 8, 73, 172, 180, 213, 249, 135, 217, 166, 206, 227, 227, 89, 154, 58, 143, 12, 206, 160, 166, 206, 227, 162, 105, 203, 212, 121, 92, 236, 67, 202, 212, 121, 100, 72, 218, 99, 30, 23, 214, 254, 107, 160, 235, 53, 162, 248, 182, 235, 43, 250, 200, 120, 237, 76, 232, 208, 120, 63, 35, 38, 128, 152, 127, 19, 12, 26, 24, 102, 21, 250, 138, 254, 87, 244, 209, 209, 234, 188, 62, 50, 172, 75, 180, 54, 219, 127, 219, 139, 52, 88, 236, 29, 228, 139, 249, 25, 32, 67, 31, 29, 30, 186, 216, 245, 17, 242, 174, 143, 10, 218, 156, 80, 132, 71, 32, 32, 100, 65, 171, 183, 62, 50, 72, 40, 136, 65, 118, 235, 227, 163, 249, 179, 109, 147, 78, 226, 87, 2, 105, 109, 45, 23, 180, 240, 65, 43, 214, 17, 107, 125, 108, 120, 68, 21, 55, 247, 239, 32, 119, 93, 77, 83, 211, 199, 6, 143, 99, 250, 184, 16, 1, 122, 109, 25, 43, 127, 142, 233, 227, 131, 167, 179, 185, 161, 244, 201, 181, 250, 65, 28, 211, 71, 134, 254, 54, 197, 23, 76, 31, 27, 142, 233, 163, 194, 107, 91, 250, 216, 240, 205, 110, 162, 177, 71, 152, 236, 70, 151, 62, 50, 92, 126, 227, 93, 155, 234, 99, 67, 5, 154, 234, 35, 196, 241, 129, 82, 127, 46, 61, 54, 60, 50, 26, 239, 194, 56, 61, 66, 188, 239, 90, 107, 37, 18, 207, 71, 70, 119, 62, 42, 56, 131, 58, 31, 25, 212, 229, 187, 124, 92, 36, 117, 142, 92, 62, 50, 254, 49, 114, 249, 184, 144, 172, 124, 149, 228, 35, 195, 61, 169, 182, 148, 181, 249, 248, 88, 155, 143, 10, 139, 122, 24, 38, 31, 23, 222, 248, 113, 82, 62, 50, 80, 243, 231, 122, 75, 62, 66, 44, 249, 75, 62, 50, 124, 179, 55, 180, 98, 31, 32, 186, 150, 124, 92, 208, 181, 228, 178, 228, 35, 195, 113, 235, 183, 117, 208, 10, 225, 17, 166, 239, 143, 35, 227, 87, 146, 218, 227, 184, 72, 142, 1, 113, 210, 43, 227, 72, 90, 81, 148, 218, 227, 216, 120, 28, 39, 212, 140, 240, 65, 181, 255, 17, 129, 12, 173, 110, 41, 142, 12, 232, 72, 20, 71, 199, 147, 20, 71, 5, 143, 32, 214, 73, 113, 151, 20, 199, 70, 4, 8, 137, 247, 85, 232, 97, 159, 195, 23, 243, 251, 231, 55, 172, 220, 180, 12, 195, 13, 18, 43, 143, 188, 197, 232, 164, 113, 196, 55, 117, 104, 146, 226, 248, 160, 56, 58, 22, 80, 39, 101, 210, 118, 136, 226, 248, 80, 127, 149, 102, 226, 184, 64, 70, 99, 111, 38, 142, 12, 220, 76, 28, 21, 16, 226, 81, 188, 60, 173, 19, 141, 160, 153, 56, 54, 48, 255, 17, 22, 30, 129, 240, 73, 151, 186, 227, 154, 153, 243, 233, 32, 52, 19, 199, 9, 143, 64, 104, 38, 173, 191, 150, 129, 208, 76, 154, 182, 158, 10, 161, 153, 56, 54, 56, 80, 232, 155, 32, 126, 111, 242, 81, 51, 113, 100, 52, 192, 40, 135, 56, 63, 1, 225, 205, 196, 145, 129, 55, 185, 24, 140, 104, 149, 228, 130, 154, 137, 99, 163, 117, 43, 116, 65, 105, 151, 151, 16, 63, 74, 157, 168, 153, 56, 54, 248, 214, 137, 227, 163, 153, 227, 152, 168, 138, 144, 36, 133, 56, 232, 47, 157, 56, 50, 60, 58, 233, 231, 8, 89, 185, 221, 207, 113, 225, 175, 13, 105, 74, 166, 159, 35, 196, 45, 74, 105, 117, 143, 51, 225, 223, 236, 28, 154, 125, 39, 239, 218, 112, 249, 140, 79, 175, 211, 126, 254, 227, 43, 250, 8, 242, 84, 32, 36, 36, 137, 25, 106, 117, 207, 209, 161, 207, 173, 149, 177, 231, 232, 104, 149, 36, 117, 18, 123, 139, 15, 247, 69, 103, 243, 28, 23, 42, 203, 48, 207, 145, 225, 29, 227, 118, 15, 86, 207, 209, 161, 158, 157, 158, 227, 194, 93, 83, 122, 142, 14, 95, 209, 207, 95, 158, 35, 131, 174, 231, 125, 121, 142, 12, 73, 91, 95, 158, 227, 194, 41, 200, 27, 239, 187, 5, 246, 33, 148, 152, 240, 229, 57, 62, 26, 133, 21, 4, 45, 157, 232, 90, 158, 35, 132, 247, 170, 229, 57, 46, 154, 206, 229, 57, 50, 184, 39, 225, 231, 79, 215, 235, 245, 141, 124, 25, 190, 237, 162, 113, 255, 167, 76, 203, 248, 19, 53, 238, 111, 224, 192, 61, 185, 67, 51, 2, 84, 243, 131, 107, 191, 79, 101, 144, 210, 246, 16, 55, 6, 180, 190, 211, 172, 115, 0, 245, 167, 180, 185, 53, 139, 184, 161, 92, 73, 21, 4, 205, 206, 128, 47, 148, 47, 128, 123, 114, 244, 192, 3, 215, 182, 75, 213, 190, 6, 185, 167, 15, 192, 61, 57, 34, 128, 187, 38, 117, 160, 85, 53, 183, 0, 220, 83, 50, 71, 173, 170, 25, 57, 96, 177, 119, 208, 130, 7, 2, 8, 18, 53, 92, 146, 210, 209, 94, 166, 81, 129, 134, 149, 138, 217, 35, 81, 241, 8, 82, 14, 111, 156, 22, 169, 183, 114, 162, 235, 249, 199, 39, 141, 180, 237, 255, 68, 135, 194, 8, 9, 18, 254, 126, 199, 133, 63, 203, 129, 166, 237, 51, 211, 126, 104, 101, 205, 12, 47, 6, 72, 161, 12, 206, 191, 68, 171, 159, 254, 99, 230, 145, 9, 255, 166, 158, 144, 115, 44, 181, 223, 25, 215, 192, 120, 65, 110, 189, 37, 114, 21, 11, 150, 218, 15, 121, 117, 151, 172, 225, 159, 188, 152, 135, 89, 9, 92, 209, 195, 172, 235, 62, 204, 62, 204, 110, 211, 86, 113, 82, 85, 1, 135, 239, 124, 119, 154, 109, 182, 253, 11, 60, 163, 139, 109, 191, 131, 181, 146, 129, 239, 124, 127, 160, 154, 170, 116, 16, 32, 168, 165, 60, 171, 49, 98, 219, 207, 191, 216, 59, 200, 109, 255, 179, 60, 60, 191, 157, 107, 217, 186, 3, 11, 52, 229, 114, 74, 65, 105, 72, 231, 34, 161, 157, 253, 62, 27, 131, 6, 207, 232, 138, 96, 2, 228, 120, 58, 14, 142, 167, 19, 89, 24, 69, 52, 40, 125, 104, 188, 14, 11, 21, 64, 63, 27, 238, 126, 142, 86, 246, 123, 34, 95, 116, 128, 73, 211, 137, 30, 200, 240, 191, 76, 63, 109, 191, 227, 2, 146, 182, 222, 186, 22, 55, 68, 159, 190, 9, 1, 19, 108, 200, 196, 19, 84, 72, 60, 65, 5, 157, 17, 244, 218, 4, 23, 254, 142, 219, 4, 25, 38, 172, 9, 42, 104, 188, 21, 59, 65, 6, 247, 180, 170, 45, 133, 34, 130, 190, 73, 55, 19, 135, 72, 138, 171, 40, 182, 19, 132, 208, 223, 9, 42, 52, 94, 223, 9, 50, 120, 4, 33, 234, 59, 65, 133, 181, 255, 144, 239, 4, 25, 218, 118, 41, 219, 71, 190, 19, 132, 32, 223, 9, 62, 24, 184, 100, 15, 19, 238, 124, 58, 165, 44, 219, 131, 233, 175, 59, 36, 90, 179, 238, 137, 32, 235, 105, 59, 193, 133, 230, 207, 212, 202, 240, 129, 71, 144, 225, 17, 8, 255, 19, 7, 134, 226, 200, 35, 143, 9, 232, 34, 143, 60, 56, 158, 139, 105, 240, 165, 19, 93, 232, 249, 93, 249, 38, 60, 224, 154, 250, 177, 59, 224, 203, 65, 203, 246, 79, 120, 4, 2, 227, 83, 106, 63, 84, 237, 4, 33, 24, 115, 5, 131, 7, 15, 146, 148, 239, 218, 233, 248, 2, 6, 90, 255, 51, 249, 131, 111, 37, 81, 231, 98, 39, 161, 235, 116, 178, 159, 205, 65, 219, 46, 213, 180, 209, 197, 239, 235, 68, 184, 199, 221, 116, 46, 205, 221, 10, 209, 218, 54, 181, 147, 3, 109, 78, 223, 233, 52, 120, 78, 28, 36, 105, 223, 117, 169, 150, 105, 66, 219, 46, 137, 215, 13, 158, 151, 67, 21, 134, 232, 92, 30, 190, 21, 4, 121, 154, 63, 237, 4, 23, 239, 236, 76, 112, 1, 226, 155, 221, 209, 234, 224, 30, 82, 109, 25, 164, 107, 153, 201, 4, 23, 154, 187, 117, 130, 12, 138, 226, 158, 238, 29, 137, 95, 245, 18, 61, 240, 166, 148, 170, 117, 130, 141, 165, 169, 117, 130, 12, 30, 129, 208, 154, 85, 211, 9, 46, 60, 226, 128, 158, 223, 196, 75, 39, 8, 161, 30, 175, 164, 154, 41, 14, 203, 141, 155, 39, 206, 154, 25, 101, 219, 240, 198, 139, 146, 169, 195, 192, 113, 217, 141, 54, 77, 254, 160, 43, 147, 43, 219, 111, 175, 33, 141, 118, 227, 93, 186, 228, 114, 180, 190, 182, 107, 253, 182, 143, 147, 58, 91, 215, 113, 96, 109, 220, 65, 83, 51, 227, 104, 188, 15, 147, 137, 135, 59, 55, 196, 174, 132, 178, 209, 9, 50, 178, 209, 9, 42, 168, 78, 16, 50, 65, 134, 87, 192, 101, 154, 96, 195, 211, 57, 65, 133, 206, 9, 54, 190, 109, 231, 4, 25, 24, 102, 21, 194, 134, 111, 57, 65, 133, 136, 86, 137, 39, 115, 80, 153, 107, 109, 203, 9, 66, 40, 4, 71, 172, 124, 19, 60, 242, 88, 8, 42, 217, 229, 64, 37, 187, 80, 3, 7, 30, 76, 80, 180, 50, 204, 91, 36, 84, 178, 11, 49, 88, 8, 17, 173, 234, 22, 96, 174, 53, 160, 159, 171, 31, 82, 16, 171, 159, 218, 149, 19, 116, 208, 79, 133, 118, 229, 4, 27, 57, 153, 192, 4, 23, 19, 152, 32, 196, 4, 25, 18, 79, 134, 38, 200, 160, 58, 121, 113, 143, 10, 82, 181, 190, 43, 161, 9, 70, 76, 208, 225, 182, 226, 161, 66, 4, 19, 181, 239, 33, 3, 4, 106, 223, 195, 133, 134, 8, 38, 11, 245, 78, 231, 120, 28, 246, 61, 132, 232, 123, 168, 64, 183, 255, 225, 66, 69, 27, 237, 6, 77, 255, 115, 49, 200, 149, 99, 195, 195, 44, 132, 181, 15, 81, 77, 255, 81, 81, 180, 246, 31, 46, 214, 254, 67, 5, 85, 156, 154, 62, 220, 161, 255, 44, 104, 2, 204, 63, 132, 152, 0, 243, 15, 21, 36, 192, 252, 67, 5, 157, 0, 98, 254, 161, 67, 106, 143, 163, 6, 101, 217, 24, 223, 180, 201, 124, 101, 168, 25, 59, 13, 127, 52, 185, 32, 116, 104, 245, 135, 10, 219, 239, 104, 247, 133, 106, 143, 189, 51, 169, 90, 71, 234, 15, 31, 173, 16, 74, 25, 115, 5, 82, 45, 81, 75, 7, 161, 141, 17, 51, 101, 234, 91, 82, 228, 42, 101, 141, 125, 253, 225, 4, 164, 149, 177, 69, 154, 154, 153, 164, 12, 163, 72, 219, 169, 42, 164, 254, 176, 65, 243, 39, 93, 20, 144, 199, 113, 114, 187, 36, 24, 109, 36, 73, 185, 60, 132, 112, 222, 74, 196, 171, 123, 34, 245, 135, 13, 245, 135, 10, 22, 127, 168, 96, 101, 122, 63, 108, 240, 126, 168, 96, 77, 232, 135, 10, 17, 76, 160, 158, 139, 101, 215, 234, 135, 12, 109, 57, 178, 250, 225, 194, 241, 116, 86, 63, 92, 96, 131, 29, 32, 205, 91, 254, 180, 190, 9, 232, 213, 27, 22, 112, 150, 157, 46, 194, 85, 180, 209, 94, 253, 176, 1, 105, 173, 255, 15, 79, 212, 15, 41, 208, 209, 150, 35, 104, 245, 67, 198, 234, 135, 10, 42, 219, 15, 21, 182, 31, 62, 60, 178, 253, 112, 193, 27, 244, 119, 109, 63, 140, 240, 8, 218, 126, 200, 128, 90, 157, 107, 154, 123, 184, 120, 94, 238, 225, 194, 107, 163, 150, 130, 180, 51, 97, 132, 67, 235, 131, 243, 106, 41, 232, 159, 107, 43, 193, 48, 171, 30, 146, 164, 78, 66, 16, 253, 84, 238, 33, 67, 195, 11, 41, 247, 30, 46, 180, 58, 6, 68, 29, 36, 160, 74, 39, 244, 112, 91, 209, 30, 46, 168, 165, 251, 180, 135, 11, 124, 104, 15, 27, 95, 209, 134, 148, 126, 190, 183, 135, 13, 175, 222, 30, 62, 40, 217, 237, 225, 66, 123, 200, 80, 183, 174, 61, 100, 96, 205, 237, 225, 130, 227, 169, 171, 61, 116, 120, 152, 173, 108, 123, 184, 112, 44, 81, 101, 187, 81, 14, 143, 32, 245, 168, 178, 237, 33, 196, 195, 44, 100, 81, 202, 2, 171, 95, 195, 83, 34, 200, 185, 68, 201, 18, 10, 7, 43, 189, 117, 178, 237, 33, 132, 71, 32, 252, 182, 135, 11, 119, 176, 190, 147, 7, 15, 179, 14, 44, 32, 0, 151, 204, 225, 147, 58, 111, 43, 177, 214, 131, 214, 103, 18, 169, 182, 192, 183, 61, 140, 120, 215, 222, 246, 112, 209, 171, 144, 99, 250, 15, 19, 140, 85, 203, 110, 64, 6, 62, 97, 219, 195, 5, 181, 19, 182, 61, 100, 152, 176, 237, 161, 66, 235, 183, 101, 183, 237, 161, 67, 223, 202, 9, 106, 188, 237, 225, 67, 233, 55, 59, 71, 3, 3, 80, 201, 4, 208, 148, 76, 227, 7, 72, 215, 14, 64, 83, 50, 156, 146, 41, 128, 166, 100, 190, 229, 123, 227, 7, 24, 208, 148, 204, 1, 92, 34, 223, 246, 112, 65, 132, 182, 109, 154, 58, 120, 211, 137, 148, 46, 179, 237, 33, 132, 97, 86, 109, 123, 170, 182, 61, 108, 232, 246, 67, 150, 231, 17, 143, 1, 92, 87, 66, 1, 79, 250, 100, 4, 72, 150, 20, 185, 178, 108, 232, 249, 214, 212, 160, 46, 147, 107, 123, 200, 104, 149, 228, 79, 180, 74, 242, 33, 68, 180, 170, 228, 5, 252, 45, 247, 224, 124, 186, 71, 37, 203, 74, 104, 115, 114, 135, 246, 115, 215, 118, 168, 85, 181, 135, 14, 202, 164, 51, 170, 218, 195, 70, 123, 200, 192, 61, 181, 135, 11, 21, 109, 180, 145, 243, 233, 80, 106, 15, 29, 34, 184, 173, 88, 218, 67, 7, 157, 236, 47, 219, 118, 168, 194, 75, 123, 248, 208, 30, 50, 86, 78, 20, 181, 135, 139, 230, 85, 176, 135, 11, 203, 30, 62, 150, 61, 84, 160, 216, 67, 5, 18, 79, 246, 112, 161, 113, 255, 122, 200, 160, 118, 61, 84, 88, 15, 3, 48, 217, 135, 10, 126, 37, 251, 112, 33, 130, 218, 135, 10, 202, 164, 35, 181, 15, 25, 15, 179, 13, 247, 196, 79, 161, 147, 244, 9, 143, 184, 34, 108, 188, 247, 50, 233, 225, 130, 159, 219, 181, 46, 15, 27, 30, 65, 249, 42, 30, 25, 94, 157, 97, 24, 85, 60, 54, 30, 231, 158, 199, 5, 143, 32, 118, 207, 227, 2, 53, 51, 15, 52, 13, 226, 77, 123, 152, 117, 207, 99, 195, 202, 180, 238, 121, 92, 52, 88, 251, 143, 226, 129, 55, 102, 140, 19, 90, 192, 85, 194, 19, 121, 164, 177, 227, 158, 199, 137, 214, 223, 166, 168, 123, 30, 29, 234, 75, 209, 245, 124, 163, 177, 67, 52, 118, 247, 32, 244, 33, 164, 51, 239, 186, 0, 114, 165, 18, 102, 7, 249, 74, 104, 37, 107, 30, 64, 211, 86, 169, 58, 98, 205, 186, 13, 177, 126, 192, 154, 17, 195, 19, 152, 45, 52, 53, 51, 220, 14, 121, 75, 71, 154, 146, 113, 224, 45, 29, 53, 117, 140, 52, 37, 147, 221, 234, 223, 152, 33, 77, 201, 168, 123, 30, 39, 84, 161, 196, 12, 150, 225, 193, 95, 161, 39, 187, 83, 121, 4, 2, 195, 145, 122, 215, 86, 17, 223, 234, 158, 8, 121, 180, 77, 39, 49, 188, 80, 150, 205, 61, 76, 67, 221, 243, 18, 13, 223, 180, 177, 123, 30, 29, 212, 61, 143, 10, 139, 85, 30, 21, 34, 84, 30, 21, 176, 231, 136, 41, 143, 139, 103, 202, 227, 227, 151, 242, 168, 160, 109, 41, 143, 11, 220, 41, 25, 132, 182, 148, 71, 7, 171, 218, 9, 132, 34, 177, 136, 6, 222, 181, 148, 71, 6, 95, 233, 94, 43, 212, 44, 229, 113, 194, 97, 35, 204, 148, 242, 184, 240, 8, 132, 214, 10, 85, 202, 163, 227, 177, 246, 31, 114, 88, 41, 219, 171, 82, 30, 27, 222, 84, 41, 143, 15, 173, 171, 223, 35, 3, 254, 30, 65, 76, 191, 199, 134, 75, 218, 46, 135, 55, 47, 253, 30, 25, 56, 238, 200, 177, 180, 181, 247, 15, 237, 123, 132, 120, 254, 177, 241, 252, 163, 130, 8, 13, 134, 97, 10, 227, 89, 35, 48, 255, 184, 208, 166, 109, 63, 254, 200, 72, 175, 200, 31, 23, 238, 249, 163, 130, 235, 74, 251, 157, 206, 31, 31, 201, 254, 168, 176, 152, 63, 42, 52, 188, 224, 223, 217, 216, 83, 106, 67, 26, 210, 171, 131, 193, 210, 150, 222, 164, 112, 64, 134, 101, 151, 63, 46, 232, 146, 164, 245, 199, 69, 69, 249, 163, 130, 242, 71, 5, 238, 124, 186, 228, 143, 12, 7, 220, 249, 116, 200, 193, 159, 226, 186, 150, 65, 201, 31, 27, 201, 31, 21, 40, 75, 127, 92, 104, 236, 17, 8, 249, 169, 32, 248, 35, 164, 185, 130, 252, 145, 193, 191, 217, 85, 20, 41, 245, 215, 30, 242, 199, 8, 117, 207, 47, 245, 199, 7, 242, 199, 136, 198, 141, 161, 238, 121, 135, 247, 68, 234, 143, 13, 254, 8, 209, 2, 109, 153, 164, 74, 5, 50, 84, 42, 48, 0, 170, 229, 251, 171, 192, 69, 107, 5, 54, 90, 85, 211, 174, 192, 5, 99, 174, 64, 135, 71, 32, 104, 227, 10, 92, 84, 224, 194, 35, 203, 174, 10, 100, 72, 237, 241, 198, 195, 236, 178, 171, 2, 31, 150, 93, 21, 168, 224, 254, 29, 180, 128, 189, 126, 111, 251, 128, 175, 132, 36, 218, 74, 220, 86, 32, 131, 171, 226, 228, 182, 2, 25, 110, 43, 208, 209, 171, 144, 219, 10, 92, 76, 96, 220, 90, 129, 12, 222, 180, 229, 214, 10, 116, 224, 119, 90, 129, 11, 90, 129, 140, 181, 184, 147, 55, 173, 192, 134, 227, 207, 161, 166, 21, 200, 240, 71, 77, 43, 208, 193, 149, 101, 5, 46, 168, 111, 179, 178, 2, 27, 222, 119, 101, 235, 247, 96, 195, 189, 7, 21, 120, 36, 177, 218, 247, 32, 99, 223, 131, 10, 84, 115, 242, 85, 40, 66, 155, 19, 90, 234, 159, 127, 176, 209, 238, 104, 125, 114, 57, 132, 14, 78, 122, 252, 224, 194, 147, 206, 15, 46, 240, 131, 1, 104, 15, 6, 224, 65, 199, 235, 245, 160, 130, 71, 30, 84, 208, 158, 132, 171, 165, 251, 32, 132, 155, 235, 131, 11, 239, 250, 15, 136, 230, 79, 172, 80, 75, 198, 250, 96, 195, 123, 234, 131, 11, 157, 250, 160, 66, 91, 230, 158, 30, 92, 172, 138, 124, 80, 97, 85, 188, 124, 112, 65, 194, 236, 180, 206, 7, 25, 204, 53, 212, 252, 208, 207, 180, 203, 61, 165, 10, 162, 117, 62, 248, 160, 61, 7, 136, 47, 252, 203, 198, 77, 231, 131, 141, 197, 236, 242, 65, 6, 143, 160, 202, 99, 249, 32, 163, 85, 37, 63, 190, 237, 106, 120, 100, 209, 152, 132, 22, 203, 135, 241, 251, 75, 136, 129, 126, 79, 180, 88, 62, 248, 192, 242, 65, 5, 15, 46, 210, 202, 7, 21, 158, 36, 31, 84, 216, 215, 124, 112, 161, 241, 230, 131, 143, 54, 1, 8, 30, 84, 64, 15, 62, 252, 193, 199, 162, 176, 225, 17, 199, 211, 169, 84, 144, 177, 48, 147, 10, 42, 32, 164, 130, 13, 137, 95, 6, 177, 214, 175, 224, 3, 107, 253, 10, 46, 184, 123, 5, 31, 238, 21, 84, 224, 17, 8, 206, 167, 67, 206, 167, 171, 84, 112, 33, 185, 94, 172, 130, 12, 203, 29, 185, 127, 103, 177, 10, 74, 104, 59, 114, 176, 196, 106, 177, 10, 50, 44, 86, 193, 134, 119, 58, 105, 177, 10, 50, 22, 171, 68, 88, 172, 194, 82, 251, 181, 185, 130, 42, 184, 192, 85, 113, 163, 153, 85, 80, 194, 35, 104, 177, 10, 50, 176, 10, 54, 240, 187, 167, 19, 90, 172, 130, 142, 183, 160, 197, 42, 8, 177, 88, 5, 21, 248, 170, 60, 199, 14, 85, 124, 85, 176, 161, 226, 171, 130, 10, 30, 241, 85, 193, 5, 134, 217, 85, 193, 133, 8, 237, 84, 80, 161, 157, 10, 62, 36, 137, 25, 106, 167, 130, 140, 118, 42, 168, 64, 21, 55, 196, 173, 21, 108, 44, 85, 65, 5, 189, 202, 241, 236, 71, 141, 170, 32, 164, 87, 169, 231, 130, 26, 85, 193, 135, 126, 170, 74, 165, 130, 11, 30, 137, 80, 65, 133, 9, 36, 136, 80, 65, 133, 231, 118, 200, 189, 130, 42, 157, 28, 61, 217, 168, 130, 13, 141, 247, 83, 123, 76, 131, 47, 170, 160, 195, 35, 13, 91, 84, 193, 6, 93, 75, 254, 42, 69, 21, 116, 60, 21, 21, 143, 130, 11, 19, 64, 204, 83, 112, 193, 41, 24, 128, 197, 242, 89, 44, 31, 72, 35, 113, 232, 161, 177, 71, 28, 107, 25, 244, 64, 127, 155, 130, 11, 78, 122, 109, 10, 50, 56, 169, 105, 83, 144, 65, 215, 190, 203, 166, 32, 131, 129, 194, 197, 226, 160, 39, 101, 163, 107, 0, 217, 20, 108, 180, 174, 166, 101, 83, 144, 177, 46, 29, 133, 130, 12, 201, 53, 106, 117, 52, 176, 78, 6, 181, 82, 88, 181, 117, 160, 56, 156, 165, 74, 52, 109, 255, 177, 186, 77, 250, 224, 12, 154, 182, 78, 202, 164, 237, 156, 3, 9, 109, 134, 52, 181, 228, 2, 235, 42, 170, 214, 190, 6, 7, 239, 153, 84, 114, 63, 144, 218, 227, 72, 181, 199, 221, 72, 82, 46, 168, 194, 139, 164, 153, 130, 13, 60, 226, 178, 41, 46, 155, 162, 154, 41, 200, 112, 217, 148, 134, 106, 166, 160, 35, 37, 141, 41, 184, 32, 129, 9, 60, 204, 98, 10, 82, 44, 236, 53, 10, 46, 84, 36, 20, 84, 192, 40, 199, 191, 216, 59, 18, 74, 83, 199, 14, 206, 47, 5, 27, 36, 212, 73, 153, 212, 45, 5, 31, 34, 26, 94, 254, 248, 55, 59, 1, 172, 205, 254, 86, 151, 110, 81, 220, 52, 102, 15, 188, 21, 14, 142, 231, 59, 132, 100, 189, 5, 61, 171, 105, 120, 77, 164, 125, 207, 165, 11, 116, 45, 131, 208, 179, 54, 213, 3, 39, 125, 82, 212, 227, 149, 144, 68, 227, 247, 7, 95, 204, 143, 212, 196, 83, 225, 118, 200, 217, 61, 88, 201, 16, 188, 16, 133, 85, 91, 10, 50, 104, 233, 164, 182, 20, 92, 244, 127, 182, 165, 224, 66, 61, 117, 105, 163, 182, 20, 124, 72, 154, 25, 212, 150, 130, 12, 181, 150, 130, 14, 181, 150, 130, 15, 102, 41, 168, 64, 155, 90, 10, 62, 190, 181, 45, 106, 210, 82, 208, 209, 180, 109, 237, 53, 90, 244, 56, 20, 156, 120, 178, 59, 35, 188, 189, 70, 13, 204, 49, 116, 61, 197, 12, 162, 238, 121, 212, 246, 215, 62, 9, 165, 21, 94, 208, 2, 39, 173, 67, 193, 133, 87, 135, 130, 143, 71, 165, 54, 98, 172, 80, 83, 135, 130, 14, 77, 29, 10, 42, 44, 85, 135, 130, 11, 104, 129, 213, 207, 161, 238, 121, 101, 203, 156, 244, 202, 64, 26, 202, 167, 67, 193, 7, 231, 27, 210, 161, 160, 35, 130, 9, 120, 67, 193, 5, 165, 19, 110, 199, 22, 189, 54, 20, 108, 120, 109, 40, 168, 128, 13, 71, 107, 181, 85, 10, 50, 124, 231, 187, 68, 149, 130, 13, 137, 230, 135, 89, 73, 74, 202, 162, 20, 116, 172, 9, 190, 40, 5, 25, 36, 64, 240, 80, 202, 178, 25, 4, 129, 141, 244, 173, 77, 107, 231, 226, 120, 248, 138, 182, 7, 232, 233, 215, 22, 128, 196, 87, 177, 108, 212, 252, 153, 252, 49, 11, 175, 108, 173, 175, 77, 43, 115, 60, 29, 36, 129, 98, 16, 13, 175, 199, 194, 171, 65, 223, 236, 216, 46, 240, 236, 176, 227, 159, 172, 185, 129, 75, 218, 115, 18, 114, 252, 45, 6, 73, 82, 186, 237, 111, 138, 158, 213, 184, 178, 204, 3, 18, 19, 101, 128, 156, 234, 131, 136, 207, 212, 120, 193, 62, 218, 166, 147, 26, 90, 151, 106, 90, 177, 222, 64, 171, 90, 219, 100, 243, 230, 45, 239, 202, 108, 75, 134, 252, 213, 189, 129, 59, 224, 194, 27, 175, 47, 181, 139, 82, 80, 98, 53, 139, 82, 36, 190, 49, 179, 40, 5, 27, 22, 165, 224, 2, 222, 71, 196, 162, 20, 92, 96, 146, 106, 81, 10, 50, 104, 115, 66, 252, 238, 233, 180, 40, 101, 81, 10, 46, 180, 109, 211, 36, 44, 74, 65, 10, 239, 69, 41, 72, 225, 154, 210, 45, 74, 65, 135, 175, 44, 22, 165, 32, 100, 81, 10, 50, 240, 8, 242, 198, 187, 40, 5, 35, 42, 234, 82, 41, 144, 100, 146, 84, 10, 66, 188, 94, 72, 146, 74, 65, 135, 212, 158, 198, 0, 210, 73, 204, 34, 18, 154, 214, 41, 3, 7, 90, 63, 98, 162, 1, 175, 162, 32, 133, 162, 96, 0, 222, 179, 201, 68, 193, 6, 255, 182, 236, 158, 164, 96, 163, 105, 249, 36, 133, 233, 231, 58, 73, 10, 54, 168, 199, 188, 116, 162, 10, 53, 246, 79, 10, 50, 240, 181, 255, 30, 146, 169, 147, 28, 34, 183, 245, 183, 19, 146, 208, 182, 171, 245, 147, 130, 141, 8, 19, 146, 148, 174, 241, 48, 235, 164, 79, 10, 46, 160, 131, 118, 82, 80, 161, 105, 73, 65, 135, 171, 100, 37, 5, 25, 122, 149, 100, 37, 5, 23, 206, 167, 67, 238, 65, 36, 41, 23, 36, 89, 73, 193, 134, 100, 37, 5, 21, 26, 163, 223, 164, 224, 98, 177, 10, 74, 18, 40, 24, 161, 44, 219, 163, 18, 148, 36, 80, 240, 177, 240, 90, 80, 146, 64, 65, 70, 99, 143, 32, 10, 50, 84, 42, 19, 84, 224, 21, 125, 19, 84, 209, 55, 153, 112, 181, 116, 25, 228, 146, 61, 184, 231, 74, 168, 162, 111, 130, 14, 7, 92, 100, 235, 155, 224, 66, 3, 238, 218, 78, 223, 4, 25, 250, 38, 216, 80, 7, 143, 56, 233, 113, 4, 3, 119, 201, 32, 28, 18, 65, 146, 214, 233, 167, 66, 17, 52, 37, 138, 160, 111, 130, 15, 173, 43, 223, 247, 78, 146, 255, 162, 31, 255, 42, 125, 79, 254, 199, 227, 220, 190, 3, 145, 0, 130, 8, 68, 112, 109, 160, 23, 44, 253, 8, 105, 155, 137, 67, 107, 235, 210, 113, 180, 114, 19, 75, 60, 42, 249, 102, 167, 181, 85, 53, 109, 254, 181, 184, 147, 35, 125, 147, 201, 126, 210, 94, 69, 241, 248, 54, 243, 109, 55, 225, 34, 160, 198, 187, 191, 94, 190, 50, 164, 47, 45, 122, 186, 177, 39, 115, 60, 21, 47, 71, 171, 199, 65, 79, 69, 174, 198, 255, 111, 65, 30, 161, 88, 169, 152, 33, 143, 52, 19, 215, 223, 182, 76, 91, 138, 79, 217, 95, 81, 212, 38, 0, 193, 182, 9, 64, 176, 140, 38, 191, 209, 213, 48, 55, 13, 116, 45, 249, 72, 49, 55, 218, 180, 178, 149, 15, 190, 41, 107, 178, 25, 214, 204, 60, 154, 54, 137, 213, 50, 85, 250, 169, 60, 2, 193, 143, 190, 61, 214, 149, 48, 59, 154, 23, 55, 20, 51, 85, 172, 56, 130, 195, 179, 26, 163, 135, 39, 41, 41, 73, 187, 82, 68, 2, 30, 102, 95, 223, 132, 129, 54, 210, 245, 32, 192, 193, 174, 5, 30, 249, 78, 231, 241, 38, 8, 225, 127, 15, 74, 255, 57, 198, 9, 165, 246, 184, 4, 175, 197, 206, 225, 105, 255, 89, 217, 18, 233, 155, 96, 3, 179, 56, 244, 19, 100, 208, 55, 217, 126, 14, 2, 232, 39, 251, 138, 254, 67, 243, 156, 175, 230, 248, 138, 126, 1, 22, 63, 240, 77, 119, 165, 5, 201, 117, 99, 143, 56, 48, 192, 222, 97, 224, 233, 87, 166, 75, 31, 185, 39, 164, 138, 147, 79, 112, 1, 77, 62, 65, 5, 215, 245, 186, 39, 184, 192, 41, 60, 65, 5, 125, 250, 38, 32, 10, 79, 176, 225, 12, 162, 240, 4, 25, 86, 186, 230, 9, 50, 168, 229, 137, 145, 168, 100, 171, 73, 227, 131, 196, 233, 68, 30, 6, 57, 144, 67, 202, 49, 165, 142, 202, 6, 147, 17, 40, 210, 3, 97, 44, 20, 137, 2, 73, 21, 133, 15, 20, 0, 3, 14, 64, 173, 25, 205, 76, 19, 164, 76, 169, 170, 13, 64, 16, 0, 4, 109, 75, 111, 237, 120, 170, 176, 201, 1, 78, 65, 242, 207, 98, 231, 248, 155, 37, 98, 142, 118, 116, 122, 6, 69, 240, 26, 157, 196, 61, 207, 229, 171, 211, 118, 68, 198, 142, 40, 103, 168, 102, 48, 188, 43, 26, 45, 249, 235, 140, 168, 165, 124, 54, 255, 142, 34, 25, 28, 15, 15, 7, 51, 179, 7, 109, 171, 165, 175, 67, 121, 9, 25, 190, 128, 198, 47, 71, 166, 61, 171, 12, 194, 125, 81, 62, 133, 61, 176, 25, 172, 66, 207, 46, 240, 250, 136, 21, 95, 93, 21, 109, 2, 45, 170, 136, 127, 78, 167, 63, 71, 118, 198, 110, 67, 167, 243, 24, 31, 119, 14, 61, 110, 109, 68, 17, 154, 243, 229, 149, 199, 135, 120, 58, 46, 81, 50, 71, 174, 122, 85, 31, 36, 158, 218, 166, 121, 26, 120, 230, 52, 172, 117, 155, 193, 237, 43, 141, 84, 148, 247, 191, 123, 49, 197, 218, 15, 73, 29, 124, 250, 198, 47, 7, 129, 50, 172, 194, 193, 55, 97, 35, 146, 73, 103, 73, 25, 137, 248, 242, 119, 10, 222, 11, 114, 8, 161, 29, 217, 2, 31, 195, 156, 139, 31, 144, 19, 49, 77, 243, 250, 250, 158, 29, 91, 131, 133, 11, 173, 87, 119, 15, 209, 216, 4, 111, 201, 119, 204, 170, 12, 229, 220, 185, 218, 97, 208, 149, 132, 246, 107, 162, 130, 91, 222, 97, 123, 201, 73, 22, 157, 3, 139, 213, 122, 136, 16, 211, 171, 145, 253, 126, 175, 213, 63, 36, 80, 40, 74, 46, 229, 152, 185, 30, 29, 213, 127, 236, 167, 160, 71, 141, 178, 173, 45, 2, 4, 65, 100, 96, 16, 109, 18, 223, 179, 37, 88, 62, 97, 198, 128, 89, 31, 28, 171, 155, 67, 234, 24, 166, 229, 210, 1, 36, 125, 123, 33, 32, 159, 235, 21, 154, 114, 49, 161, 209, 38, 248, 131, 160, 239, 52, 206, 165, 78, 153, 191, 179, 16, 42, 160, 128, 112, 38, 251, 192, 90, 206, 130, 88, 119, 70, 252, 18, 11, 179, 240, 5, 100, 198, 75, 115, 52, 66, 150, 128, 138, 172, 98, 221, 8, 158, 17, 246, 14, 141, 120, 235, 229, 96, 229, 38, 166, 254, 35, 123, 81, 186, 64, 22, 66, 16, 101, 234, 75, 141, 194, 154, 204, 160, 33, 197, 1, 164, 244, 196, 56, 52, 1, 230, 17, 165, 50, 203, 106, 231, 107, 255, 71, 126, 255, 83, 70, 32, 14, 208, 139, 229, 226, 138, 222, 163, 27, 134, 240, 219, 246, 188, 154, 62, 151, 167, 106, 0, 111, 96, 182, 220, 50, 223, 221, 34, 135, 240, 83, 156, 114, 29, 53, 107, 46, 148, 169, 111, 250, 153, 57, 135, 197, 216, 102, 163, 127, 53, 245, 203, 58, 142, 97, 151, 166, 77, 26, 90, 165, 113, 133, 3, 211, 169, 168, 233, 135, 37, 226, 23, 139, 175, 179, 198, 136, 20, 22, 109, 224, 185, 222, 51, 2, 77, 1, 21, 91, 58, 141, 1, 254, 39, 222, 220, 161, 150, 232, 214, 206, 133, 116, 175, 101, 131, 104, 229, 86, 114, 173, 94, 26, 84, 77, 121, 235, 211, 159, 120, 165, 227, 125, 45, 11, 6, 120, 215, 203, 97, 132, 167, 107, 114, 2, 63, 167, 80, 100, 118, 8, 168, 20, 10, 88, 130, 247, 36, 104, 167, 86, 49, 242, 147, 141, 55, 121, 206, 224, 73, 188, 54, 218, 224, 30, 234, 52, 66, 117, 125, 196, 8, 206, 95, 111, 5, 137, 155, 199, 14, 204, 128, 209, 175, 40, 9, 170, 39, 57, 63, 34, 25, 41, 144, 20, 52, 191, 143, 140, 47, 200, 15, 254, 180, 209, 74, 88, 18, 237, 88, 104, 188, 136, 146, 188, 32, 113, 74, 111, 82, 169, 247, 76, 73, 222, 208, 129, 181, 51, 131, 210, 3, 142, 9, 254, 221, 46, 51, 186, 85, 24, 193, 137, 43, 2, 177, 133, 141, 182, 235, 30, 173, 240, 78, 104, 184, 120, 239, 75, 106, 178, 243, 105, 169, 57, 11, 252, 172, 139, 247, 36, 4, 3, 60, 142, 188, 214, 138, 149, 137, 212, 43, 30, 208, 251, 181, 41, 81, 153, 107, 139, 70, 60, 202, 192, 178, 31, 69, 153, 26, 178, 170, 5, 27, 159, 164, 135, 6, 75, 231, 231, 210, 179, 22, 92, 107, 192, 110, 86, 174, 53, 125, 52, 210, 243, 11, 243, 206, 53, 185, 58, 34, 50, 117, 222, 30, 26, 97, 122, 209, 129, 141, 246, 76, 101, 208, 79, 111, 243, 217, 105, 178, 227, 88, 219, 40, 54, 211, 46, 12, 238, 22, 201, 176, 24, 246, 117, 145, 177, 7, 158, 173, 6, 203, 30, 67, 205, 203, 18, 44, 171, 177, 238, 84, 167, 123, 168, 52, 65, 199, 170, 77, 117, 50, 201, 0, 42, 105, 27, 193, 91, 60, 152, 238, 83, 218, 246, 151, 33, 138, 16, 172, 65, 228, 30, 38, 200, 137, 214, 31, 140, 12, 227, 197, 164, 80, 251, 215, 153, 59, 87, 118, 248, 74, 117, 152, 44, 112, 169, 155, 66, 63, 219, 82, 190, 113, 213, 217, 15, 80, 28, 133, 75, 234, 156, 56, 109, 240, 28, 229, 19, 244, 25, 106, 11, 28, 210, 191, 115, 178, 226, 128, 7, 130, 213, 90, 188, 33, 63, 184, 31, 99, 153, 36, 128, 28, 123, 36, 197, 40, 166, 51, 83, 234, 195, 42, 92, 172, 162, 110, 231, 8, 171, 250, 65, 99, 198, 137, 235, 224, 73, 81, 207, 4, 250, 204, 78, 72, 102, 82, 141, 85, 202, 92, 226, 36, 230, 22, 137, 67, 234, 142, 136, 96, 14, 83, 162, 53, 9, 7, 165, 66, 241, 199, 74, 141, 188, 63, 253, 107, 148, 2, 248, 65, 138, 123, 227, 220, 48, 249, 213, 32, 2, 252, 42, 190, 107, 99, 205, 110, 239, 3, 119, 218, 246, 52, 167, 46, 50, 232, 66, 164, 80, 68, 64, 194, 41, 8, 0, 25, 216, 162, 206, 222, 137, 107, 149, 114, 205, 198, 16, 67, 146, 212, 141, 181, 149, 247, 252, 14, 66, 173, 223, 125, 254, 126, 152, 34, 75, 107, 91, 204, 195, 185, 206, 199, 232, 6, 226, 128, 104, 142, 209, 160, 21, 238, 81, 44, 113, 3, 230, 250, 236, 161, 142, 124, 40, 106, 132, 206, 233, 89, 99, 100, 226, 246, 177, 31, 127, 158, 221, 200, 67, 35, 143, 140, 9, 22, 99, 102, 143, 127, 120, 255, 225, 234, 103, 88, 205, 128, 128, 156, 166, 127, 60, 20, 180, 231, 246, 237, 66, 13, 97, 121, 166, 188, 236, 4, 62, 227, 174, 89, 64, 34, 176, 95, 159, 49, 41, 137, 69, 199, 48, 188, 93, 219, 103, 140, 33, 11, 199, 65, 241, 100, 133, 160, 3, 18, 26, 119, 136, 229, 38, 129, 136, 121, 34, 168, 144, 85, 241, 96, 72, 29, 93, 16, 4, 136, 97, 53, 59, 183, 36, 44, 141, 140, 136, 131, 91, 74, 19, 4, 207, 189, 0, 227, 93, 81, 151, 91, 67, 216, 122, 118, 153, 22, 246, 242, 231, 69, 102, 95, 173, 97, 72, 66, 136, 12, 217, 206, 79, 140, 24, 11, 79, 251, 209, 203, 35, 198, 121, 231, 229, 6, 214, 88, 132, 224, 126, 255, 176, 129, 36, 204, 94, 130, 235, 86, 226, 14, 197, 149, 44, 136, 165, 139, 195, 229, 180, 195, 241, 164, 112, 219, 37, 62, 195, 245, 100, 77, 180, 229, 144, 60, 222, 135, 160, 63, 106, 78, 34, 17, 216, 152, 173, 81, 27, 1, 44, 232, 36, 81, 104, 58, 47, 32, 100, 164, 211, 188, 125, 238, 219, 179, 131, 97, 199, 137, 25, 205, 162, 200, 145, 197, 38, 220, 24, 102, 152, 150, 158, 43, 234, 238, 66, 49, 90, 131, 174, 97, 219, 26, 117, 38, 36, 82, 247, 130, 156, 205, 161, 248, 80, 56, 181, 70, 137, 146, 17, 103, 30, 168, 35, 99, 3, 7, 215, 81, 169, 118, 55, 162, 131, 132, 124, 156, 193, 241, 85, 147, 187, 47, 54, 51, 125, 20, 241, 20, 2, 41, 229, 62, 1, 73, 240, 126, 190, 178, 71, 69, 141, 94, 180, 131, 67, 102, 8, 233, 80, 73, 0, 137, 161, 142, 175, 0, 181, 86, 206, 144, 205, 103, 78, 53, 100, 139, 158, 99, 187, 111, 238, 109, 201, 241, 130, 246, 174, 163, 51, 123, 115, 225, 33, 255, 12, 41, 84, 69, 209, 109, 151, 96, 244, 151, 3, 201, 255, 96, 125, 221, 123, 239, 163, 182, 138, 138, 211, 200, 82, 228, 110, 239, 131, 44, 191, 123, 159, 156, 94, 133, 218, 142, 212, 20, 204, 53, 39, 71, 138, 197, 147, 130, 27, 240, 124, 116, 113, 175, 215, 58, 106, 133, 224, 9, 122, 175, 33, 23, 20, 105, 244, 166, 1, 167, 118, 19, 156, 50, 192, 195, 128, 104, 11, 14, 245, 88, 3, 174, 184, 169, 6, 74, 156, 128, 16, 68, 121, 198, 229, 50, 42, 176, 176, 144, 44, 60, 112, 243, 55, 105, 196, 184, 102, 91, 98, 202, 57, 77, 55, 3, 38, 102, 130, 51, 111, 14, 71, 16, 118, 208, 180, 36, 59, 37, 237, 59, 13, 253, 193, 65, 175, 92, 79, 64, 11, 31, 18, 22, 69, 86, 156, 188, 165, 63, 50, 105, 49, 18, 243, 114, 12, 222, 78, 3, 228, 175, 206, 231, 72, 59, 227, 42, 144, 198, 225, 19, 67, 27, 120, 192, 164, 5, 65, 42, 224, 33, 69, 218, 64, 176, 6, 217, 104, 169, 68, 229, 218, 167, 153, 63, 26, 41, 17, 202, 243, 12, 158, 44, 126, 84, 25, 123, 196, 72, 197, 135, 197, 101, 122, 191, 150, 226, 170, 135, 18, 107, 234, 63, 237, 94, 236, 227, 111, 120, 82, 193, 182, 52, 206, 104, 46, 234, 81, 23, 88, 116, 248, 108, 48, 1, 48, 224, 78, 162, 208, 197, 208, 223, 130, 202, 206, 60, 16, 56, 168, 240, 8, 236, 142, 216, 107, 200, 76, 111, 113, 208, 32, 13, 125, 137, 223, 163, 172, 14, 38, 32, 22, 177, 74, 204, 99, 133, 115, 241, 136, 232, 253, 159, 254, 10, 251, 114, 134, 179, 0, 161, 120, 211, 89, 35, 171, 183, 203, 244, 93, 1, 68, 226, 185, 162, 79, 90, 158, 189, 50, 138, 140, 213, 157, 193, 44, 153, 98, 228, 165, 31, 125, 231, 218, 227, 247, 27, 144, 177, 87, 202, 76, 177, 177, 53, 109, 142, 205, 233, 240, 33, 1, 99, 37, 93, 6, 131, 91, 90, 96, 193, 79, 93, 148, 73, 192, 197, 20, 52, 223, 108, 236, 179, 19, 132, 66, 186, 56, 102, 179, 253, 89, 73, 2, 235, 83, 198, 40, 96, 44, 102, 146, 46, 169, 83, 199, 47, 246, 150, 46, 109, 108, 22, 254, 126, 150, 9, 178, 41, 89, 64, 250, 213, 59, 79, 1, 244, 241, 234, 115, 98, 254, 65, 255, 57, 170, 52, 107, 66, 220, 186, 201, 89, 168, 10, 172, 179, 35, 64, 249, 77, 10, 247, 2, 18, 172, 205, 249, 222, 139, 12, 100, 170, 19, 227, 128, 16, 6, 114, 119, 238, 186, 239, 137, 248, 167, 92, 234, 55, 185, 107, 53, 153, 93, 155, 192, 110, 179, 205, 8, 153, 194, 237, 243, 203, 164, 98, 16, 217, 19, 56, 31, 248, 92, 68, 233, 200, 21, 182, 81, 250, 9, 20, 50, 144, 187, 251, 15, 112, 203, 42, 187, 23, 72, 60, 64, 191, 61, 86, 64, 158, 58, 196, 8, 154, 174, 30, 134, 63, 47, 75, 34, 37, 64, 70, 28, 71, 45, 126, 101, 5, 53, 224, 55, 131, 48, 193, 101, 47, 134, 186, 42, 33, 139, 111, 155, 224, 118, 149, 39, 113, 5, 115, 233, 10, 234, 171, 166, 229, 119, 156, 46, 142, 251, 136, 104, 141, 89, 204, 70, 186, 163, 201, 194, 100, 91, 202, 104, 186, 138, 59, 146, 178, 205, 30, 65, 180, 84, 252, 209, 3, 104, 240, 87, 70, 186, 159, 42, 177, 146, 202, 224, 12, 212, 201, 213, 153, 210, 57, 210, 248, 98, 254, 29, 32, 97, 185, 161, 108, 163, 129, 30, 7, 194, 181, 208, 198, 166, 212, 199, 224, 41, 38, 39, 135, 165, 0, 170, 159, 11, 89, 254, 21, 62, 205, 234, 106, 0, 34, 211, 99, 251, 21, 19, 96, 2, 87, 230, 112, 58, 22, 118, 90, 88, 169, 121, 193, 135, 88, 231, 199, 126, 92, 130, 166, 96, 54, 94, 15, 242, 32, 154, 89, 104, 68, 205, 96, 62, 26, 208, 50, 54, 157, 233, 33, 81, 119, 9, 160, 106, 227, 212, 41, 212, 67, 184, 103, 48, 192, 238, 84, 165, 216, 147, 210, 115, 111, 204, 27, 228, 166, 65, 203, 22, 59, 95, 190, 99, 11, 75, 78, 247, 225, 101, 151, 132, 170, 49, 180, 158, 246, 37, 201, 39, 123, 135, 220, 27, 11, 167, 112, 80, 230, 106, 222, 6, 247, 225, 31, 194, 71, 200, 131, 231, 45, 90, 156, 154, 232, 167, 170, 177, 237, 227, 119, 28, 55, 228, 236, 238, 39, 217, 50, 119, 196, 118, 58, 251, 215, 242, 158, 99, 185, 144, 92, 100, 54, 70, 46, 55, 75, 34, 7, 229, 14, 230, 142, 197, 187, 70, 205, 184, 84, 165, 174, 106, 88, 14, 34, 61, 31, 182, 23, 253, 12, 162, 15, 75, 97, 250, 12, 121, 46, 20, 228, 134, 253, 99, 234, 77, 215, 60, 79, 136, 31, 237, 138, 29, 140, 41, 23, 161, 248, 114, 31, 204, 57, 68, 251, 57, 129, 254, 238, 80, 121, 3, 36, 131, 95, 224, 238, 82, 171, 251, 120, 36, 54, 132, 51, 252, 54, 51, 224, 92, 147, 71, 59, 163, 0, 220, 235, 240, 63, 9, 64, 53, 216, 243, 45, 197, 172, 8, 128, 90, 142, 111, 253, 211, 246, 132, 135, 69, 238, 108, 176, 0, 27, 170, 29, 128, 185, 79, 0, 28, 20, 203, 187, 210, 42, 103, 117, 37, 146, 173, 238, 50, 71, 89, 168, 56, 153, 195, 240, 73, 243, 54, 132, 77, 86, 4, 67, 144, 18, 43, 22, 201, 42, 47, 112, 148, 26, 36, 248, 106, 102, 149, 109, 22, 239, 49, 125, 40, 207, 63, 170, 109, 250, 137, 124, 138, 162, 59, 181, 59, 104, 92, 84, 204, 41, 243, 11, 238, 140, 251, 124, 96, 34, 103, 205, 15, 103, 28, 31, 135, 135, 169, 109, 156, 15, 146, 88, 56, 32, 145, 202, 98, 26, 220, 183, 103, 206, 155, 153, 12, 229, 242, 159, 2, 27, 76, 236, 62, 111, 155, 67, 193, 137, 153, 4, 113, 139, 114, 34, 3, 174, 131, 14, 248, 85, 206, 93, 53, 27, 60, 145, 134, 206, 139, 234, 95, 197, 100, 141, 251, 244, 126, 224, 138, 66, 147, 145, 126, 144, 176, 161, 253, 52, 211, 112, 130, 203, 246, 230, 28, 196, 111, 224, 187, 29, 169, 176, 54, 64, 148, 77, 105, 80, 11, 172, 27, 133, 66, 74, 239, 14, 248, 40, 15, 40, 51, 180, 174, 93, 198, 216, 116, 169, 66, 249, 86, 118, 41, 141, 203, 30, 46, 196, 104, 50, 116, 140, 148, 22, 236, 95, 123, 212, 133, 132, 226, 136, 121, 83, 160, 218, 8, 155, 241, 141, 216, 16, 103, 212, 72, 246, 184, 233, 174, 42, 10, 148, 249, 214, 70, 76, 108, 133, 117, 170, 234, 183, 183, 58, 174, 67, 204, 100, 227, 57, 44, 236, 152, 74, 15, 225, 72, 92, 196, 93, 40, 1, 68, 53, 254, 128, 78, 178, 93, 193, 234, 133, 86, 160, 31, 139, 64, 123, 190, 94, 76, 221, 66, 83, 115, 217, 144, 195, 72, 109, 213, 158, 3, 33, 247, 113, 166, 29, 144, 125, 218, 141, 165, 149, 54, 227, 59, 230, 115, 153, 148, 226, 141, 61, 180, 247, 171, 103, 219, 88, 37, 1, 74, 64, 37, 55, 162, 27, 236, 34, 45, 24, 136, 71, 228, 211, 55, 5, 134, 137, 162, 166, 175, 75, 13, 230, 120, 212, 54, 133, 213, 13, 212, 4, 75, 141, 17, 248, 142, 201, 250, 5, 221, 163, 237, 12, 161, 28, 238, 54, 158, 171, 228, 241, 92, 6, 0, 81, 232, 157, 213, 166, 240, 149, 3, 113, 11, 68, 217, 104, 163, 44, 113, 158, 213, 129, 142, 169, 83, 128, 174, 159, 118, 192, 38, 166, 163, 1, 198, 235, 155, 12, 16, 156, 12, 21, 244, 216, 229, 30, 161, 210, 22, 35, 22, 117, 126, 237, 94, 7, 217, 143, 145, 188, 52, 104, 98, 103, 53, 225, 105, 49, 246, 72, 3, 53, 240, 210, 195, 27, 186, 43, 247, 24, 204, 81, 229, 242, 32, 97, 84, 239, 144, 247, 4, 84, 158, 212, 187, 14, 18, 4, 12, 88, 248, 37, 10, 225, 196, 28, 170, 41, 138, 32, 149, 30, 134, 240, 43, 63, 144, 55, 220, 192, 49, 179, 137, 88, 163, 118, 187, 115, 167, 230, 99, 71, 225, 136, 190, 250, 55, 29, 186, 209, 106, 249, 128, 137, 153, 173, 182, 206, 68, 108, 38, 17, 165, 211, 69, 183, 74, 75, 43, 189, 59, 81, 189, 108, 108, 161, 183, 4, 24, 213, 237, 145, 162, 209, 80, 110, 35, 125, 172, 242, 209, 135, 3, 47, 212, 183, 137, 131, 95, 148, 95, 62, 211, 138, 17, 206, 113, 108, 150, 15, 132, 147, 227, 65, 39, 108, 224, 222, 148, 167, 68, 47, 10, 35, 104, 77, 79, 57, 240, 199, 184, 236, 76, 184, 130, 167, 150, 221, 17, 116, 132, 135, 128, 90, 63, 149, 158, 32, 27, 228, 82, 235, 61, 29, 137, 135, 245, 134, 183, 172, 78, 150, 234, 243, 196, 68, 162, 80, 79, 63, 108, 251, 114, 40, 250, 180, 84, 215, 162, 255, 38, 126, 85, 181, 5, 122, 4, 46, 93, 4, 68, 237, 84, 160, 191, 255, 46, 160, 103, 118, 242, 126, 40, 214, 157, 129, 245, 101, 13, 236, 36, 243, 11, 0, 91, 229, 253, 187, 217, 117, 133, 3, 22, 84, 25, 68, 237, 53, 127, 36, 59, 0, 69, 6, 33, 119, 202, 100, 131, 170, 240, 138, 178, 10, 104, 63, 129, 237, 145, 53, 80, 160, 152, 191, 62, 87, 0, 46, 14, 44, 15, 181, 64, 114, 89, 156, 238, 183, 153, 199, 9, 4, 31, 41, 91, 240, 109, 188, 209, 54, 117, 214, 86, 210, 150, 231, 193, 192, 70, 103, 109, 220, 148, 144, 50, 100, 184, 197, 35, 4, 22, 39, 246, 65, 168, 134, 144, 151, 125, 65, 42, 9, 167, 46, 35, 220, 208, 188, 211, 115, 84, 28, 221, 254, 21, 54, 67, 63, 235, 214, 42, 108, 118, 91, 172, 249, 252, 172, 208, 154, 238, 152, 19, 3, 16, 227, 124, 68, 72, 176, 240, 36, 48, 13, 194, 216, 227, 147, 231, 16, 242, 32, 42, 23, 93, 204, 3, 206, 10, 141, 255, 94, 43, 37, 107, 174, 61, 165, 221, 27, 52, 144, 8, 138, 50, 46, 64, 184, 168, 144, 13, 240, 20, 72, 174, 105, 84, 5, 79, 112, 55, 230, 170, 37, 229, 197, 61, 209, 24, 171, 3, 35, 170, 168, 96, 254, 84, 149, 170, 114, 252, 101, 78, 60, 21, 88, 69, 214, 79, 140, 244, 150, 181, 197, 17, 69, 184, 194, 144, 32, 18, 202, 36, 146, 124, 28, 46, 218, 237, 137, 139, 169, 242, 0, 122, 218, 44, 162, 77, 23, 221, 129, 111, 172, 145, 215, 86, 43, 197, 176, 242, 197, 38, 224, 189, 169, 229, 0, 20, 96, 50, 139, 4, 13, 213, 152, 45, 231, 24, 122, 11, 106, 2, 244, 114, 179, 184, 10, 12, 244, 131, 160, 22, 145, 48, 136, 195, 101, 65, 8, 210, 237, 182, 111, 4, 42, 50, 190, 43, 134, 121, 52, 197, 165, 89, 188, 30, 226, 161, 50, 182, 104, 200, 166, 254, 251, 178, 9, 14, 69, 204, 74, 147, 182, 204, 18, 97, 247, 255, 10, 191, 221, 122, 72, 106, 173, 224, 247, 239, 23, 93, 79, 108, 207, 58, 202, 143, 217, 194, 55, 99, 147, 192, 171, 139, 19, 49, 204, 51, 137, 63, 238, 115, 140, 41, 12, 27, 194, 16, 50, 168, 155, 208, 27, 33, 229, 87, 219, 56, 37, 100, 62, 97, 215, 183, 41, 134, 54, 211, 199, 33, 109, 83, 184, 155, 13, 156, 140, 180, 220, 142, 220, 57, 232, 80, 151, 241, 250, 170, 52, 224, 59, 38, 10, 214, 33, 219, 21, 82, 10, 227, 243, 215, 241, 165, 244, 248, 115, 103, 1, 28, 111, 6, 135, 178, 161, 167, 193, 5, 39, 62, 100, 216, 217, 155, 211, 203, 128, 81, 161, 77, 44, 82, 85, 255, 30, 80, 161, 236, 128, 216, 9, 229, 56, 174, 224, 68, 69, 25, 119, 169, 240, 7, 229, 5, 149, 141, 47, 195, 102, 190, 163, 179, 195, 64, 207, 61, 154, 71, 100, 111, 192, 98, 205, 22, 243, 108, 217, 14, 228, 118, 44, 87, 247, 34, 171, 180, 164, 124, 187, 194, 161, 39, 140, 22, 63, 147, 114, 190, 241, 248, 33, 44, 226, 29, 195, 99, 36, 213, 180, 66, 3, 7, 91, 49, 150, 140, 233, 99, 57, 0, 197, 106, 6, 93, 147, 230, 140, 51, 124, 154, 97, 181, 225, 247, 163, 35, 64, 5, 127, 110, 225, 62, 121, 23, 180, 214, 110, 230, 61, 127, 57, 36, 180, 102, 130, 75, 102, 155, 195, 154, 204, 254, 189, 198, 20, 142, 55, 155, 172, 181, 221, 78, 98, 177, 117, 165, 40, 52, 57, 72, 1, 246, 213, 159, 72, 190, 31, 1, 130, 136, 177, 107, 25, 127, 211, 228, 27, 115, 158, 59, 21, 83, 96, 82, 83, 78, 151, 73, 224, 226, 200, 79, 24, 11, 139, 201, 166, 188, 99, 228, 10, 203, 185, 161, 36, 167, 119, 158, 107, 165, 221, 161, 174, 87, 61, 239, 20, 255, 96, 122, 181, 239, 138, 8, 155, 82, 37, 24, 168, 227, 21, 177, 28, 252, 190, 236, 89, 243, 60, 232, 103, 12, 126, 129, 246, 143, 27, 220, 252, 141, 96, 57, 123, 250, 121, 43, 212, 247, 125, 148, 33, 204, 137, 190, 145, 57, 101, 52, 101, 78, 219, 199, 194, 126, 106, 250, 86, 44, 141, 40, 54, 67, 131, 80, 228, 105, 117, 39, 240, 128, 208, 17, 215, 11, 32, 205, 2, 75, 200, 89, 219, 28, 205, 141, 117, 3, 36, 58, 156, 18, 95, 7, 220, 252, 7, 146, 100, 221, 69, 120, 42, 233, 247, 2, 143, 214, 188, 58, 214, 99, 212, 91, 176, 9, 117, 105, 22, 0, 54, 26, 131, 171, 61, 54, 73, 51, 100, 178, 32, 183, 148, 180, 44, 211, 42, 251, 138, 157, 218, 244, 165, 0, 136, 40, 235, 192, 96, 65, 41, 8, 116, 114, 238, 41, 69, 200, 36, 38, 75, 46, 49, 233, 130, 9, 192, 57, 3, 132, 31, 129, 229, 96, 211, 88, 150, 88, 135, 115, 51, 242, 53, 239, 34, 181, 206, 166, 191, 126, 161, 78, 134, 189, 234, 187, 73, 54, 177, 160, 242, 183, 54, 231, 36, 57, 127, 88, 252, 167, 112, 141, 34, 51, 11, 9, 242, 221, 44, 252, 147, 250, 150, 130, 253, 149, 211, 14, 128, 75, 230, 68, 63, 2, 101, 233, 1, 58, 32, 24, 141, 146, 26, 21, 234, 83, 111, 16, 230, 187, 229, 152, 84, 156, 173, 80, 81, 249, 76, 162, 41, 213, 27, 116, 151, 13, 3, 101, 21, 10, 86, 12, 80, 192, 118, 104, 133, 14, 0, 19, 94, 45, 238, 229, 92, 229, 32, 159, 80, 84, 178, 110, 14, 102, 97, 127, 136, 172, 5, 180, 38, 114, 82, 196, 67, 140, 218, 180, 253, 26, 217, 160, 0, 125, 2, 211, 3, 173, 198, 92, 217, 167, 195, 177, 114, 214, 11, 169, 243, 69, 112, 238, 2, 121, 54, 163, 138, 43, 144, 39, 96, 145, 201, 251, 165, 166, 228, 248, 51, 55, 230, 24, 194, 83, 112, 125, 245, 213, 36, 79, 51, 79, 168, 198, 164, 236, 143, 176, 131, 243, 161, 201, 46, 54, 132, 9, 14, 154, 182, 31, 122, 35, 238, 233, 115, 224, 83, 44, 176, 26, 15, 31, 1, 160, 104, 18, 235, 103, 107, 1, 35, 221, 74, 206, 8, 207, 168, 166, 147, 51, 189, 213, 6, 244, 172, 145, 114, 56, 83, 121, 231, 215, 136, 174, 58, 174, 7, 7, 197, 1, 1, 125, 219, 115, 221, 25, 96, 85, 3, 84, 218, 47, 191, 148, 134, 97, 114, 195, 228, 121, 5, 246, 10, 236, 83, 85, 149, 179, 42, 154, 148, 51, 43, 192, 175, 226, 7, 128, 38, 31, 224, 253, 76, 199, 37, 214, 163, 56, 83, 74, 238, 47, 117, 139, 224, 68, 75, 114, 0, 161, 88, 29, 134, 54, 0, 209, 128, 171, 150, 147, 177, 37, 136, 69, 253, 89, 179, 182, 182, 129, 43, 66, 66, 166, 122, 229, 14, 203, 70, 224, 38, 57, 147, 253, 178, 93, 201, 209, 3, 61, 29, 201, 12, 224, 69, 215, 123, 221, 204, 40, 198, 0, 60, 131, 98, 92, 63, 215, 92, 84, 230, 186, 64, 76, 239, 206, 243, 182, 224, 248, 52, 68, 151, 193, 126, 238, 86, 66, 89, 164, 231, 3, 68, 92, 5, 133, 241, 173, 96, 244, 149, 127, 102, 188, 225, 225, 199, 181, 57, 58, 236, 245, 151, 65, 26, 41, 47, 84, 34, 18, 220, 240, 249, 129, 83, 195, 163, 216, 167, 229, 159, 139, 254, 230, 24, 95, 17, 3, 212, 112, 22, 132, 55, 235, 139, 43, 52, 5, 180, 64, 156, 237, 225, 44, 76, 230, 113, 77, 39, 249, 183, 75, 245, 23, 229, 33, 3, 255, 236, 41, 64, 197, 104, 98, 86, 113, 10, 97, 25, 232, 124, 28, 28, 13, 28, 5, 0, 76, 223, 89, 151, 78, 120, 139, 143, 23, 80, 70, 17, 185, 14, 76, 220, 4, 140, 132, 111, 71, 40, 67, 166, 146, 48, 10, 199, 51, 72, 10, 207, 66, 236, 30, 131, 16, 199, 104, 159, 95, 112, 223, 199, 155, 158, 231, 212, 162, 86, 80, 72, 203, 7, 71, 69, 143, 120, 84, 39, 69, 20, 19, 129, 61, 83, 135, 140, 137, 135, 203, 237, 204, 128, 97, 253, 214, 98, 104, 214, 57, 104, 31, 185, 67, 108, 147, 133, 178, 191, 153, 253, 17, 91, 149, 88, 215, 128, 103, 204, 95, 44, 137, 163, 117, 134, 129, 225, 185, 62, 180, 16, 71, 75, 123, 94, 10, 140, 37, 137, 217, 1, 204, 195, 70, 85, 57, 15, 47, 16, 22, 62, 22, 152, 207, 75, 135, 103, 142, 1, 10, 166, 177, 174, 152, 62, 125, 202, 98, 92, 153, 103, 19, 164, 53, 181, 76, 113, 146, 65, 17, 185, 94, 193, 156, 254, 185, 59, 100, 173, 227, 33, 17, 202, 189, 127, 255, 246, 97, 167, 94, 48, 168, 119, 119, 141, 122, 205, 213, 26, 16, 231, 11, 86, 94, 38, 116, 105, 177, 52, 147, 41, 211, 150, 137, 95, 237, 18, 245, 10, 209, 100, 209, 3, 84, 180, 213, 47, 251, 48, 51, 81, 101, 188, 67, 111, 243, 231, 65, 95, 163, 138, 2, 156, 129, 245, 98, 146, 246, 212, 166, 160, 147, 75, 176, 2, 193, 65, 230, 199, 117, 21, 56, 134, 163, 186, 115, 148, 207, 251, 145, 153, 232, 227, 97, 195, 93, 243, 90, 112, 35, 168, 151, 232, 225, 196, 156, 207, 188, 208, 79, 109, 82, 148, 83, 159, 63, 96, 211, 132, 178, 248, 83, 106, 231, 202, 1, 7, 57, 62, 177, 220, 133, 160, 136, 150, 242, 53, 20, 186, 201, 19, 90, 9, 216, 62, 193, 83, 199, 46, 14, 248, 154, 58, 3, 168, 45, 5, 40, 209, 223, 233, 69, 232, 67, 167, 163, 56, 59, 160, 53, 125, 231, 88, 116, 243, 171, 250, 109, 195, 85, 109, 125, 29, 163, 168, 124, 240, 24, 154, 38, 51, 221, 131, 223, 34, 172, 147, 3, 222, 255, 25, 202, 143, 78, 171, 22, 185, 198, 161, 51, 41, 108, 112, 84, 23, 98, 76, 54, 251, 149, 141, 211, 163, 249, 138, 135, 107, 206, 114, 177, 202, 1, 181, 2, 200, 131, 83, 148, 69, 170, 248, 81, 208, 38, 194, 73, 128, 147, 121, 166, 212, 43, 71, 243, 66, 197, 248, 75, 128, 23, 175, 106, 130, 202, 207, 162, 252, 11, 28, 152, 130, 64, 238, 200, 95, 112, 141, 48, 51, 201, 198, 145, 66, 173, 213, 142, 28, 159, 241, 183, 190, 244, 33, 236, 73, 234, 210, 67, 128, 185, 62, 249, 26, 192, 136, 142, 49, 17, 23, 71, 95, 254, 136, 134, 169, 83, 138, 250, 134, 239, 52, 180, 27, 199, 12, 33, 45, 121, 247, 201, 26, 151, 111, 26, 185, 154, 195, 200, 32, 233, 88, 215, 70, 140, 137, 124, 98, 40, 137, 70, 94, 2, 129, 6, 69, 219, 11, 11, 233, 157, 3, 191, 98, 89, 118, 17, 185, 8, 218, 170, 105, 36, 64, 202, 56, 13, 248, 35, 219, 17, 234, 34, 182, 212, 127, 153, 2, 20, 139, 136, 15, 160, 250, 152, 212, 24, 100, 193, 202, 238, 233, 111, 141, 233, 246, 127, 82, 31, 77, 32, 129, 81, 34, 227, 203, 50, 1, 179, 148, 50, 159, 28, 148, 197, 236, 248, 181, 16, 232, 45, 6, 212, 121, 53, 231, 179, 97, 176, 66, 68, 178, 176, 64, 206, 122, 146, 46, 86, 100, 43, 125, 98, 238, 201, 108, 167, 87, 246, 20, 70, 167, 9, 98, 115, 253, 11, 122, 70, 219, 71, 20, 11, 184, 196, 181, 104, 159, 124, 33, 228, 107, 190, 30, 77, 220, 233, 77, 37, 8, 32, 61, 168, 19, 112, 200, 59, 212, 26, 93, 215, 190, 221, 58, 10, 159, 81, 98, 31, 5, 64, 55, 137, 23, 19, 47, 105, 61, 92, 73, 37, 192, 86, 225, 7, 69, 208, 234, 164, 138, 196, 241, 187, 83, 240, 59, 239, 135, 10, 9, 157, 63, 26, 207, 125, 54, 146, 102, 164, 92, 105, 98, 234, 147, 100, 146, 131, 164, 153, 222, 32, 242, 137, 137, 6, 93, 194, 25, 4, 85, 69, 116, 155, 46, 107, 181, 14, 170, 101, 166, 210, 11, 124, 190, 214, 186, 48, 124, 38, 153, 128, 95, 244, 227, 65, 191, 230, 171, 15, 192, 252, 21, 253, 26, 222, 25, 201, 58, 59, 2, 65, 191, 108, 219, 99, 75, 201, 251, 137, 57, 123, 47, 114, 85, 163, 249, 196, 216, 235, 49, 27, 141, 95, 18, 62, 102, 174, 81, 173, 56, 102, 34, 47, 156, 77, 3, 232, 149, 155, 227, 226, 254, 41, 105, 252, 134, 212, 44, 64, 9, 34, 166, 234, 46, 3, 50, 132, 105, 178, 110, 38, 86, 139, 8, 239, 218, 122, 32, 241, 79, 43, 149, 192, 90, 108, 28, 35, 47, 26, 227, 169, 92, 150, 131, 155, 226, 250, 239, 73, 67, 151, 84, 92, 173, 115, 7, 36, 103, 184, 10, 160, 237, 247, 189, 188, 145, 114, 181, 151, 28, 71, 251, 238, 90, 0, 26, 208, 123, 104, 136, 144, 214, 242, 137, 178, 119, 23, 84, 98, 7, 239, 17, 195, 130, 185, 135, 27, 23, 111, 205, 19, 52, 140, 121, 134, 18, 50, 32, 141, 79, 134, 177, 59, 5, 190, 232, 112, 128, 96, 165, 99, 88, 167, 240, 186, 170, 216, 176, 130, 24, 214, 56, 223, 20, 188, 0, 30, 31, 66, 71, 110, 3, 233, 80, 220, 147, 135, 90, 24, 160, 137, 158, 58, 134, 240, 221, 75, 243, 151, 214, 28, 4, 150, 8, 15, 202, 178, 240, 122, 231, 219, 197, 46, 240, 70, 92, 232, 184, 178, 56, 64, 249, 10, 207, 230, 172, 181, 0, 23, 112, 50, 205, 183, 16, 58, 140, 50, 242, 197, 108, 158, 39, 225, 184, 211, 246, 21, 255, 56, 232, 67, 5, 130, 161, 104, 6, 176, 142, 184, 21, 42, 246, 172, 246, 236, 28, 255, 231, 35, 19, 126, 17, 176, 37, 121, 72, 202, 113, 71, 188, 184, 196, 105, 45, 64, 221, 191, 53, 81, 32, 160, 112, 0, 36, 243, 56, 127, 157, 117, 150, 212, 134, 150, 112, 210, 162, 214, 122, 87, 222, 74, 153, 116, 238, 170, 81, 103, 63, 230, 46, 69, 31, 223, 12, 32, 81, 157, 2, 65, 40, 142, 77, 92, 190, 214, 70, 94, 248, 87, 30, 148, 92, 243, 200, 146, 220, 185, 135, 174, 232, 81, 8, 126, 130, 15, 250, 76, 19, 112, 155, 218, 8, 166, 137, 248, 134, 156, 22, 173, 131, 44, 96, 115, 211, 148, 140, 93, 14, 189, 54, 32, 240, 150, 75, 99, 191, 74, 189, 29, 52, 209, 168, 229, 100, 80, 228, 100, 220, 176, 104, 115, 202, 29, 34, 136, 103, 39, 79, 14, 221, 51, 130, 152, 229, 68, 132, 22, 33, 21 }; - var i: u8 = 'a'; - var tokenizer = std.mem.tokenize(u8, add_completions, "\n"); +const uncompressed_size: usize = 162204; - while (i <= 'z') { - var init_tokenizer = tokenizer; - var count: usize = 0; - @setEvalBranchQuota(9999999); - while (init_tokenizer.next()) |pkg| { - if (pkg.len == 0) continue; - if (pkg[0] == i) { - count += 1; - } else { - break; - } - } - - var record: [count][]const u8 = undefined; - var record_i: usize = 0; - var next_i = i + 1; - - while (tokenizer.next()) |pkg| { - if (pkg.len == 0) continue; - - if (pkg[0] == i) { - record[record_i] = pkg; - record_i += 1; - } else { - next_i = pkg[0]; - break; - } - } - - const cloned = record; - array.set(@as(FirstLetter, @enumFromInt(i)), &cloned); - - @setEvalBranchQuota(999999); - i = next_i; - } - break :brk array; -}; -pub const biggest_list: usize = brk: { - var a = index; - var iter = a.iterator(); - var max: usize = 0; - while (iter.next()) |list| { - max = @max(list.value.len, max); - } - break :brk max; +pub const IndexEntry = struct { + offset: usize, + length: usize, }; -const index_blob = "add_completions.index.blob"; +pub const Index = std.EnumArray(FirstLetter, IndexEntry); + +pub const index = Index.init(.{ .a = .{ .offset = 0, .length = 540 }, .b = .{ .offset = 540, .length = 513 }, .c = .{ .offset = 1053, .length = 691 }, .d = .{ .offset = 1744, .length = 377 }, .e = .{ .offset = 2121, .length = 660 }, .f = .{ .offset = 2781, .length = 344 }, .g = .{ .offset = 3125, .length = 434 }, .h = .{ .offset = 3559, .length = 275 }, .i = .{ .offset = 3834, .length = 391 }, .j = .{ .offset = 4225, .length = 335 }, .k = .{ .offset = 4560, .length = 117 }, .l = .{ .offset = 4677, .length = 398 }, .m = .{ .offset = 5075, .length = 497 }, .n = .{ .offset = 5572, .length = 386 }, .o = .{ .offset = 5958, .length = 149 }, .p = .{ .offset = 6107, .length = 709 }, .q = .{ .offset = 6816, .length = 33 }, .r = .{ .offset = 6849, .length = 1034 }, .s = .{ .offset = 7883, .length = 817 }, .t = .{ .offset = 8700, .length = 417 }, .u = .{ .offset = 9117, .length = 217 }, .v = .{ .offset = 9334, .length = 302 }, .w = .{ .offset = 9636, .length = 220 }, .x = .{ .offset = 9856, .length = 60 }, .y = .{ .offset = 9916, .length = 53 }, .z = .{ .offset = 9969, .length = 30 } }); + +var decompressed_data: ?[]u8 = null; +var packages_list: ?[][]const u8 = null; + +pub fn init(allocator: std.mem.Allocator) !void { + // Decompress data + var data = try allocator.alloc(u8, uncompressed_size); + errdefer allocator.free(data); + + const result = zstd.decompress(data, &compressed_data); + decompressed_data = data[0..result.success]; + + // Parse package list + const total_count = std.mem.readInt(u32, data[0..4], .little); + var packages = try allocator.alloc([]const u8, total_count); + errdefer allocator.free(packages); + + var pos: usize = 4; + var i: usize = 0; + while (i < total_count) : (i += 1) { + const len = std.mem.readInt(u16, data[pos..][0..2], .little); + pos += 2; + packages[i] = data[pos .. pos + len]; + pos += len; + } + + packages_list = packages; +} + +pub fn deinit(allocator: std.mem.Allocator) void { + if (packages_list) |pkgs| { + allocator.free(pkgs); + packages_list = null; + } + + if (decompressed_data) |data| { + allocator.free(data); + decompressed_data = null; + } +} + +pub fn getPackages(letter: FirstLetter) []const []const u8 { + const entry = index.get(letter); + if (entry.length == 0) return &[_][]const u8{}; + + return packages_list.?[entry.offset .. entry.offset + entry.length]; +} + +pub const biggest_list: usize = 1034; diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 3a975221f6..073bceb129 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -25,7 +25,7 @@ const sync = @import("../sync.zig"); const Api = @import("../api/schema.zig").Api; const resolve_path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; -const bundler = bun.bundler; +const transpiler = bun.transpiler; const DotEnv = @import("../env_loader.zig"); @@ -74,13 +74,13 @@ pub const BuildCommand = struct { } } - var this_bundler = try bundler.Bundler.init(allocator, log, ctx.args, null); + var this_transpiler = try transpiler.Transpiler.init(allocator, log, ctx.args, null); - this_bundler.options.source_map = options.SourceMapOption.fromApi(ctx.args.source_map); + this_transpiler.options.source_map = options.SourceMapOption.fromApi(ctx.args.source_map); - this_bundler.options.compile = ctx.bundler_options.compile; + this_transpiler.options.compile = ctx.bundler_options.compile; - if (this_bundler.options.source_map == .external and ctx.bundler_options.outdir.len == 0 and !ctx.bundler_options.compile) { + if (this_transpiler.options.source_map == .external and ctx.bundler_options.outdir.len == 0 and !ctx.bundler_options.compile) { Output.prettyErrorln("error: cannot use an external source map without --outdir", .{}); Global.exit(1); return; @@ -89,37 +89,37 @@ pub const BuildCommand = struct { var outfile = ctx.bundler_options.outfile; const output_to_stdout = !ctx.bundler_options.compile and outfile.len == 0 and ctx.bundler_options.outdir.len == 0; - this_bundler.options.supports_multiple_outputs = !(output_to_stdout or outfile.len > 0); + this_transpiler.options.supports_multiple_outputs = !(output_to_stdout or outfile.len > 0); - this_bundler.options.public_path = ctx.bundler_options.public_path; - this_bundler.options.entry_naming = ctx.bundler_options.entry_naming; - this_bundler.options.chunk_naming = ctx.bundler_options.chunk_naming; - this_bundler.options.asset_naming = ctx.bundler_options.asset_naming; - this_bundler.options.server_components = ctx.bundler_options.server_components; - this_bundler.options.react_fast_refresh = ctx.bundler_options.react_fast_refresh; - this_bundler.options.inline_entrypoint_import_meta_main = ctx.bundler_options.inline_entrypoint_import_meta_main; - this_bundler.options.code_splitting = ctx.bundler_options.code_splitting; - this_bundler.options.minify_syntax = ctx.bundler_options.minify_syntax; - this_bundler.options.minify_whitespace = ctx.bundler_options.minify_whitespace; - this_bundler.options.minify_identifiers = ctx.bundler_options.minify_identifiers; - this_bundler.options.emit_dce_annotations = ctx.bundler_options.emit_dce_annotations; - this_bundler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations; + this_transpiler.options.public_path = ctx.bundler_options.public_path; + this_transpiler.options.entry_naming = ctx.bundler_options.entry_naming; + this_transpiler.options.chunk_naming = ctx.bundler_options.chunk_naming; + this_transpiler.options.asset_naming = ctx.bundler_options.asset_naming; + this_transpiler.options.server_components = ctx.bundler_options.server_components; + this_transpiler.options.react_fast_refresh = ctx.bundler_options.react_fast_refresh; + this_transpiler.options.inline_entrypoint_import_meta_main = ctx.bundler_options.inline_entrypoint_import_meta_main; + this_transpiler.options.code_splitting = ctx.bundler_options.code_splitting; + this_transpiler.options.minify_syntax = ctx.bundler_options.minify_syntax; + this_transpiler.options.minify_whitespace = ctx.bundler_options.minify_whitespace; + this_transpiler.options.minify_identifiers = ctx.bundler_options.minify_identifiers; + this_transpiler.options.emit_dce_annotations = ctx.bundler_options.emit_dce_annotations; + this_transpiler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations; - this_bundler.options.banner = ctx.bundler_options.banner; - this_bundler.options.footer = ctx.bundler_options.footer; - this_bundler.options.drop = ctx.args.drop; + this_transpiler.options.banner = ctx.bundler_options.banner; + this_transpiler.options.footer = ctx.bundler_options.footer; + this_transpiler.options.drop = ctx.args.drop; - this_bundler.options.experimental_css = ctx.bundler_options.experimental_css; - this_bundler.options.css_chunking = ctx.bundler_options.css_chunking; + this_transpiler.options.experimental = ctx.bundler_options.experimental; + this_transpiler.options.css_chunking = ctx.bundler_options.css_chunking; - this_bundler.options.output_dir = ctx.bundler_options.outdir; - this_bundler.options.output_format = ctx.bundler_options.output_format; + this_transpiler.options.output_dir = ctx.bundler_options.outdir; + this_transpiler.options.output_format = ctx.bundler_options.output_format; if (ctx.bundler_options.output_format == .internal_bake_dev) { - this_bundler.options.tree_shaking = false; + this_transpiler.options.tree_shaking = false; } - this_bundler.options.bytecode = ctx.bundler_options.bytecode; + this_transpiler.options.bytecode = ctx.bundler_options.bytecode; if (ctx.bundler_options.compile) { if (ctx.bundler_options.code_splitting) { @@ -136,21 +136,21 @@ pub const BuildCommand = struct { const base_public_path = bun.StandaloneModuleGraph.targetBasePublicPath(compile_target.os, "root/"); - this_bundler.options.public_path = base_public_path; + this_transpiler.options.public_path = base_public_path; if (outfile.len == 0) { - outfile = std.fs.path.basename(this_bundler.options.entry_points[0]); + outfile = std.fs.path.basename(this_transpiler.options.entry_points[0]); const ext = std.fs.path.extension(outfile); if (ext.len > 0) { outfile = outfile[0 .. outfile.len - ext.len]; } if (strings.eqlComptime(outfile, "index")) { - outfile = std.fs.path.basename(std.fs.path.dirname(this_bundler.options.entry_points[0]) orelse "index"); + outfile = std.fs.path.basename(std.fs.path.dirname(this_transpiler.options.entry_points[0]) orelse "index"); } if (strings.eqlComptime(outfile, "bun")) { - outfile = std.fs.path.basename(std.fs.path.dirname(this_bundler.options.entry_points[0]) orelse "bun"); + outfile = std.fs.path.basename(std.fs.path.dirname(this_transpiler.options.entry_points[0]) orelse "bun"); } } @@ -169,12 +169,12 @@ pub const BuildCommand = struct { } if (ctx.bundler_options.outdir.len == 0 and !ctx.bundler_options.compile) { - if (this_bundler.options.entry_points.len > 1) { + if (this_transpiler.options.entry_points.len > 1) { Output.prettyErrorln("error: Must use --outdir when specifying more than one entry point.", .{}); Global.exit(1); return; } - if (this_bundler.options.code_splitting) { + if (this_transpiler.options.code_splitting) { Output.prettyErrorln("error: Must use --outdir when code splitting is enabled", .{}); Global.exit(1); return; @@ -188,11 +188,11 @@ pub const BuildCommand = struct { break :brk2 ctx.bundler_options.root_dir; } - if (this_bundler.options.entry_points.len == 1) { - break :brk2 std.fs.path.dirname(this_bundler.options.entry_points[0]) orelse "."; + if (this_transpiler.options.entry_points.len == 1) { + break :brk2 std.fs.path.dirname(this_transpiler.options.entry_points[0]) orelse "."; } - break :brk2 resolve_path.getIfExistsLongestCommonPath(this_bundler.options.entry_points) orelse "."; + break :brk2 resolve_path.getIfExistsLongestCommonPath(this_transpiler.options.entry_points) orelse "."; }; var dir = bun.openDirForPath(&(try std.posix.toPosixPath(path))) catch |err| { @@ -207,39 +207,47 @@ pub const BuildCommand = struct { }; }; - this_bundler.options.root_dir = src_root_dir; - this_bundler.options.code_splitting = ctx.bundler_options.code_splitting; - this_bundler.options.transform_only = ctx.bundler_options.transform_only; + this_transpiler.options.root_dir = src_root_dir; + this_transpiler.options.code_splitting = ctx.bundler_options.code_splitting; + this_transpiler.options.transform_only = ctx.bundler_options.transform_only; - this_bundler.options.env.behavior = ctx.bundler_options.env_behavior; - this_bundler.options.env.prefix = ctx.bundler_options.env_prefix; + this_transpiler.options.env.behavior = ctx.bundler_options.env_behavior; + this_transpiler.options.env.prefix = ctx.bundler_options.env_prefix; - try this_bundler.configureDefines(); - this_bundler.configureLinker(); + try this_transpiler.configureDefines(); + this_transpiler.configureLinker(); - this_bundler.resolver.opts = this_bundler.options; - this_bundler.options.jsx.development = !this_bundler.options.production; - this_bundler.resolver.opts.jsx.development = this_bundler.options.jsx.development; + if (bun.FeatureFlags.breaking_changes_1_2) { + // This is currently done in DevServer by default, but not in Bun.build + if (!this_transpiler.options.production) { + try this_transpiler.options.conditions.appendSlice(&.{"development"}); + } + } + + this_transpiler.resolver.opts = this_transpiler.options; + this_transpiler.options.jsx.development = !this_transpiler.options.production; + this_transpiler.resolver.opts.jsx.development = this_transpiler.options.jsx.development; switch (ctx.debug.macros) { .disable => { - this_bundler.options.no_macros = true; + this_transpiler.options.no_macros = true; }, .map => |macros| { - this_bundler.options.macro_remap = macros; + this_transpiler.options.macro_remap = macros; }, .unspecified => {}, } - var client_bundler: bundler.Bundler = undefined; - if (this_bundler.options.server_components) { - client_bundler = try bundler.Bundler.init(allocator, log, ctx.args, null); - client_bundler.options = this_bundler.options; + var client_bundler: transpiler.Transpiler = undefined; + if (this_transpiler.options.server_components) { + client_bundler = try transpiler.Transpiler.init(allocator, log, ctx.args, null); + client_bundler.options = this_transpiler.options; client_bundler.options.target = .browser; client_bundler.options.server_components = true; - try this_bundler.options.conditions.appendSlice(&.{"react-server"}); - this_bundler.options.react_fast_refresh = false; - this_bundler.options.minify_syntax = true; + client_bundler.options.conditions = try this_transpiler.options.conditions.clone(); + try this_transpiler.options.conditions.appendSlice(&.{"react-server"}); + this_transpiler.options.react_fast_refresh = false; + this_transpiler.options.minify_syntax = true; client_bundler.options.minify_syntax = true; client_bundler.options.define = try options.Define.init( allocator, @@ -253,20 +261,20 @@ pub const BuildCommand = struct { else null, null, - this_bundler.options.define.drop_debugger, + this_transpiler.options.define.drop_debugger, ); - try bun.bake.addImportMetaDefines(allocator, this_bundler.options.define, .development, .server); + try bun.bake.addImportMetaDefines(allocator, this_transpiler.options.define, .development, .server); try bun.bake.addImportMetaDefines(allocator, client_bundler.options.define, .development, .client); - this_bundler.resolver.opts = this_bundler.options; + this_transpiler.resolver.opts = this_transpiler.options; client_bundler.resolver.opts = client_bundler.options; } - // var env_loader = this_bundler.env; + // var env_loader = this_transpiler.env; if (ctx.debug.dump_environment_variables) { - this_bundler.dumpEnvironmentVariables(); + this_transpiler.dumpEnvironmentVariables(); return; } @@ -276,12 +284,12 @@ pub const BuildCommand = struct { const output_files: []options.OutputFile = brk: { if (ctx.bundler_options.transform_only) { - this_bundler.options.import_path_format = .relative; - this_bundler.options.allow_runtime = false; - this_bundler.resolver.opts.allow_runtime = false; + this_transpiler.options.import_path_format = .relative; + this_transpiler.options.allow_runtime = false; + this_transpiler.resolver.opts.allow_runtime = false; // TODO: refactor this .transform function - const result = try this_bundler.transform( + const result = try this_transpiler.transform( ctx.allocator, ctx.log, ctx.args, @@ -301,7 +309,7 @@ pub const BuildCommand = struct { } break :brk (BundleV2.generateFromCLI( - &this_bundler, + &this_transpiler, allocator, bun.JSC.AnyEventLoop.init(ctx.allocator), ctx.debug.hot_reload == .watch, @@ -327,7 +335,7 @@ pub const BuildCommand = struct { dump: { defer Output.flush(); var writer = Output.writer(); - var output_dir = this_bundler.options.output_dir; + var output_dir = this_transpiler.options.output_dir; const will_be_one_file = // --outdir is not supported with --compile @@ -383,7 +391,7 @@ pub const BuildCommand = struct { printSummary( bundled_end, minify_duration, - this_bundler.options.minify_identifiers or this_bundler.options.minify_whitespace or this_bundler.options.minify_syntax, + this_transpiler.options.minify_identifiers or this_transpiler.options.minify_whitespace or this_transpiler.options.minify_syntax, input_code_length, reachable_file_count, output_files, @@ -406,10 +414,12 @@ pub const BuildCommand = struct { allocator, output_files, root_dir, - this_bundler.options.public_path, + this_transpiler.options.public_path, outfile, - this_bundler.env, - this_bundler.options.output_format, + this_transpiler.env, + this_transpiler.options.output_format, + ctx.bundler_options.windows_hide_console, + ctx.bundler_options.windows_icon, ); const compiled_elapsed = @divTrunc(@as(i64, @truncate(std.time.nanoTimestamp() - bundled_end)), @as(i64, std.time.ns_per_ms)); const compiled_elapsed_digit_count: isize = switch (compiled_elapsed) { @@ -468,7 +478,7 @@ pub const BuildCommand = struct { Output.printElapsedStdoutTrim( @as(f64, @floatFromInt((@divTrunc(@as(i64, @truncate(std.time.nanoTimestamp() - bun.CLI.start_time)), @as(i64, std.time.ns_per_ms))))), ); - if (this_bundler.options.transform_only) { + if (this_transpiler.options.transform_only) { Output.prettyln(" transpile", .{}); } else { Output.prettyln(" bundle {d} modules", .{ diff --git a/src/cli/bunx_command.zig b/src/cli/bunx_command.zig index 1afa6fd6ff..5eb50b4d20 100644 --- a/src/cli/bunx_command.zig +++ b/src/cli/bunx_command.zig @@ -63,13 +63,13 @@ pub const BunxCommand = struct { /// 1 day const nanoseconds_cache_valid = seconds_cache_valid * 1000000000; - fn getBinNameFromSubpath(bundler: *bun.Bundler, dir_fd: bun.FileDescriptor, subpath_z: [:0]const u8) ![]const u8 { + fn getBinNameFromSubpath(transpiler: *bun.Transpiler, dir_fd: bun.FileDescriptor, subpath_z: [:0]const u8) ![]const u8 { const target_package_json_fd = try bun.sys.openat(dir_fd, subpath_z, bun.O.RDONLY, 0).unwrap(); const target_package_json = bun.sys.File{ .handle = target_package_json_fd }; defer target_package_json.close(); - const package_json_read = target_package_json.readToEnd(bundler.allocator); + const package_json_read = target_package_json.readToEnd(transpiler.allocator); // TODO: make this better if (package_json_read.err) |err| { @@ -82,7 +82,7 @@ pub const BunxCommand = struct { bun.JSAst.Expr.Data.Store.create(); bun.JSAst.Stmt.Data.Store.create(); - const expr = try bun.JSON.parsePackageJSONUTF8(&source, bundler.log, bundler.allocator); + const expr = try bun.JSON.parsePackageJSONUTF8(&source, transpiler.log, transpiler.allocator); // choose the first package that fits if (expr.get("bin")) |bin_expr| { @@ -90,7 +90,7 @@ pub const BunxCommand = struct { .e_object => |object| { for (object.properties.slice()) |prop| { if (prop.key) |key| { - if (key.asString(bundler.allocator)) |bin_name| { + if (key.asString(transpiler.allocator)) |bin_name| { if (bin_name.len == 0) continue; return bin_name; } @@ -99,7 +99,7 @@ pub const BunxCommand = struct { }, .e_string => { if (expr.get("name")) |name_expr| { - if (name_expr.asString(bundler.allocator)) |name| { + if (name_expr.asString(transpiler.allocator)) |name| { return name; } } @@ -110,7 +110,7 @@ pub const BunxCommand = struct { if (expr.asProperty("directories")) |dirs| { if (dirs.expr.asProperty("bin")) |bin_prop| { - if (bin_prop.expr.asString(bundler.allocator)) |dir_name| { + if (bin_prop.expr.asString(transpiler.allocator)) |dir_name| { const bin_dir = try bun.sys.openatA(dir_fd, dir_name, bun.O.RDONLY | bun.O.DIRECTORY, 0).unwrap(); defer _ = bun.sys.close(bin_dir); const dir = std.fs.Dir{ .fd = bin_dir.cast() }; @@ -124,7 +124,7 @@ pub const BunxCommand = struct { if (current.kind == .file) { if (current.name.len == 0) continue; - return try bundler.allocator.dupe(u8, current.name.slice()); + return try transpiler.allocator.dupe(u8, current.name.slice()); } } } @@ -134,13 +134,13 @@ pub const BunxCommand = struct { return error.NoBinFound; } - fn getBinNameFromProjectDirectory(bundler: *bun.Bundler, dir_fd: bun.FileDescriptor, package_name: []const u8) ![]const u8 { + fn getBinNameFromProjectDirectory(transpiler: *bun.Transpiler, dir_fd: bun.FileDescriptor, package_name: []const u8) ![]const u8 { var subpath: bun.PathBuffer = undefined; const subpath_z = std.fmt.bufPrintZ(&subpath, bun.pathLiteral("node_modules/{s}/package.json"), .{package_name}) catch unreachable; - return try getBinNameFromSubpath(bundler, dir_fd, subpath_z); + return try getBinNameFromSubpath(transpiler, dir_fd, subpath_z); } - fn getBinNameFromTempDirectory(bundler: *bun.Bundler, tempdir_name: []const u8, package_name: []const u8, with_stale_check: bool) ![]const u8 { + fn getBinNameFromTempDirectory(transpiler: *bun.Transpiler, tempdir_name: []const u8, package_name: []const u8, with_stale_check: bool) ![]const u8 { var subpath: bun.PathBuffer = undefined; if (with_stale_check) { const subpath_z = std.fmt.bufPrintZ( @@ -186,19 +186,19 @@ pub const BunxCommand = struct { .{ tempdir_name, package_name }, ) catch unreachable; - return try getBinNameFromSubpath(bundler, bun.FD.cwd(), subpath_z); + return try getBinNameFromSubpath(transpiler, bun.FD.cwd(), subpath_z); } /// Check the enclosing package.json for a matching "bin" /// If not found, check bunx cache dir - fn getBinName(bundler: *bun.Bundler, toplevel_fd: bun.FileDescriptor, tempdir_name: []const u8, package_name: []const u8) error{ NoBinFound, NeedToInstall }![]const u8 { + fn getBinName(transpiler: *bun.Transpiler, toplevel_fd: bun.FileDescriptor, tempdir_name: []const u8, package_name: []const u8) error{ NoBinFound, NeedToInstall }![]const u8 { toplevel_fd.assertValid(); - return getBinNameFromProjectDirectory(bundler, toplevel_fd, package_name) catch |err| { + return getBinNameFromProjectDirectory(transpiler, toplevel_fd, package_name) catch |err| { if (err == error.NoBinFound) { return error.NoBinFound; } - return getBinNameFromTempDirectory(bundler, tempdir_name, package_name, true) catch |err2| { + return getBinNameFromTempDirectory(transpiler, tempdir_name, package_name, true) catch |err2| { if (err2 == error.NoBinFound) { return error.NoBinFound; } @@ -304,12 +304,12 @@ pub const BunxCommand = struct { // fast path: they're actually using this interchangeably with `bun run` // so we use Bun.which to check - var this_bundler: bun.Bundler = undefined; + var this_transpiler: bun.Transpiler = undefined; var ORIGINAL_PATH: string = ""; const root_dir_info = try Run.configureEnvForRun( ctx, - &this_bundler, + &this_transpiler, null, true, true, @@ -318,28 +318,28 @@ pub const BunxCommand = struct { try Run.configurePathForRun( ctx, root_dir_info, - &this_bundler, + &this_transpiler, &ORIGINAL_PATH, root_dir_info.abs_path, ctx.debug.run_in_bun, ); - this_bundler.env.map.put("npm_command", "exec") catch unreachable; - this_bundler.env.map.put("npm_lifecycle_event", "bunx") catch unreachable; - this_bundler.env.map.put("npm_lifecycle_script", package_name) catch unreachable; + this_transpiler.env.map.put("npm_command", "exec") catch unreachable; + this_transpiler.env.map.put("npm_lifecycle_event", "bunx") catch unreachable; + this_transpiler.env.map.put("npm_lifecycle_script", package_name) catch unreachable; if (strings.eqlComptime(package_name, "bun-repl")) { - this_bundler.env.map.remove("BUN_INSPECT_CONNECT_TO"); - this_bundler.env.map.remove("BUN_INSPECT_NOTIFY"); - this_bundler.env.map.remove("BUN_INSPECT"); + this_transpiler.env.map.remove("BUN_INSPECT_CONNECT_TO"); + this_transpiler.env.map.remove("BUN_INSPECT_NOTIFY"); + this_transpiler.env.map.remove("BUN_INSPECT"); } - const ignore_cwd = this_bundler.env.get("BUN_WHICH_IGNORE_CWD") orelse ""; + const ignore_cwd = this_transpiler.env.get("BUN_WHICH_IGNORE_CWD") orelse ""; if (ignore_cwd.len > 0) { - _ = this_bundler.env.map.map.swapRemove("BUN_WHICH_IGNORE_CWD"); + _ = this_transpiler.env.map.map.swapRemove("BUN_WHICH_IGNORE_CWD"); } - var PATH = this_bundler.env.get("PATH").?; + var PATH = this_transpiler.env.get("PATH").?; const display_version = if (update_request.version.literal.isEmpty()) "latest" else @@ -454,7 +454,7 @@ pub const BunxCommand = struct { ), }; - try this_bundler.env.map.put("PATH", PATH); + try this_transpiler.env.map.put("PATH", PATH); const bunx_cache_dir = PATH[0 .. temp_dir.len + "/bunx--".len + package_fmt.len + @@ -481,7 +481,7 @@ pub const BunxCommand = struct { destination_ = bun.which( &path_buf, PATH_FOR_BIN_DIRS, - if (ignore_cwd.len > 0) "" else this_bundler.fs.top_level_dir, + if (ignore_cwd.len > 0) "" else this_transpiler.fs.top_level_dir, initial_bin_name, ); } @@ -492,7 +492,7 @@ pub const BunxCommand = struct { if (destination_ orelse bun.which( &path_buf, bunx_cache_dir, - if (ignore_cwd.len > 0) "" else this_bundler.fs.top_level_dir, + if (ignore_cwd.len > 0) "" else this_transpiler.fs.top_level_dir, absolute_in_cache_dir, )) |destination| { const out = bun.asByteSlice(destination); @@ -539,10 +539,10 @@ pub const BunxCommand = struct { try Run.runBinary( ctx, - try this_bundler.fs.dirname_store.append(@TypeOf(out), out), + try this_transpiler.fs.dirname_store.append(@TypeOf(out), out), destination, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, passthrough, null, ); @@ -553,7 +553,7 @@ pub const BunxCommand = struct { // 2. The "bin" is possibly not the same as the package name, so we load the package.json to figure out what "bin" to use const root_dir_fd = root_dir_info.getFileDescriptor(); bun.assert(root_dir_fd != .zero); - if (getBinName(&this_bundler, root_dir_fd, bunx_cache_dir, initial_bin_name)) |package_name_for_bin| { + if (getBinName(&this_transpiler, root_dir_fd, bunx_cache_dir, initial_bin_name)) |package_name_for_bin| { // if we check the bin name and its actually the same, we don't need to check $PATH here again if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) { absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, bun.pathLiteral("{s}/node_modules/.bin/{s}{s}"), .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable; @@ -563,7 +563,7 @@ pub const BunxCommand = struct { destination_ = bun.which( &path_buf, bunx_cache_dir, - if (ignore_cwd.len > 0) "" else this_bundler.fs.top_level_dir, + if (ignore_cwd.len > 0) "" else this_transpiler.fs.top_level_dir, package_name_for_bin, ); } @@ -571,16 +571,16 @@ pub const BunxCommand = struct { if (destination_ orelse bun.which( &path_buf, bunx_cache_dir, - if (ignore_cwd.len > 0) "" else this_bundler.fs.top_level_dir, + if (ignore_cwd.len > 0) "" else this_transpiler.fs.top_level_dir, absolute_in_cache_dir, )) |destination| { const out = bun.asByteSlice(destination); try Run.runBinary( ctx, - try this_bundler.fs.dirname_store.append(@TypeOf(out), out), + try this_transpiler.fs.dirname_store.append(@TypeOf(out), out), destination, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, passthrough, null, ); @@ -636,12 +636,12 @@ pub const BunxCommand = struct { const argv_to_use = args.slice(); debug("installing package: {s}", .{bun.fmt.fmtSlice(argv_to_use, " ")}); - this_bundler.env.map.put("BUN_INTERNAL_BUNX_INSTALL", "true") catch bun.outOfMemory(); + this_transpiler.env.map.put("BUN_INTERNAL_BUNX_INSTALL", "true") catch bun.outOfMemory(); const spawn_result = switch ((bun.spawnSync(&.{ .argv = argv_to_use, - .envp = try this_bundler.env.map.createNullDelimitedEnvMap(bun.default_allocator), + .envp = try this_transpiler.env.map.createNullDelimitedEnvMap(bun.default_allocator), .cwd = bunx_cache_dir, .stderr = .inherit, @@ -649,7 +649,7 @@ pub const BunxCommand = struct { .stdin = .inherit, .windows = if (Environment.isWindows) .{ - .loop = bun.JSC.EventLoopHandle.init(bun.JSC.MiniEventLoop.initGlobal(this_bundler.env)), + .loop = bun.JSC.EventLoopHandle.init(bun.JSC.MiniEventLoop.initGlobal(this_transpiler.env)), } else {}, }) catch |err| { Output.prettyErrorln("error: bunx failed to install {s} due to error {s}", .{ install_param, @errorName(err) }); @@ -691,16 +691,16 @@ pub const BunxCommand = struct { if (bun.which( &path_buf, bunx_cache_dir, - if (ignore_cwd.len > 0) "" else this_bundler.fs.top_level_dir, + if (ignore_cwd.len > 0) "" else this_transpiler.fs.top_level_dir, absolute_in_cache_dir, )) |destination| { const out = bun.asByteSlice(destination); try Run.runBinary( ctx, - try this_bundler.fs.dirname_store.append(@TypeOf(out), out), + try this_transpiler.fs.dirname_store.append(@TypeOf(out), out), destination, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, passthrough, null, ); @@ -709,23 +709,23 @@ pub const BunxCommand = struct { } // 2. The "bin" is possibly not the same as the package name, so we load the package.json to figure out what "bin" to use - if (getBinNameFromTempDirectory(&this_bundler, bunx_cache_dir, result_package_name, false)) |package_name_for_bin| { + if (getBinNameFromTempDirectory(&this_transpiler, bunx_cache_dir, result_package_name, false)) |package_name_for_bin| { if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) { absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable; if (bun.which( &path_buf, bunx_cache_dir, - if (ignore_cwd.len > 0) "" else this_bundler.fs.top_level_dir, + if (ignore_cwd.len > 0) "" else this_transpiler.fs.top_level_dir, absolute_in_cache_dir, )) |destination| { const out = bun.asByteSlice(destination); try Run.runBinary( ctx, - try this_bundler.fs.dirname_store.append(@TypeOf(out), out), + try this_transpiler.fs.dirname_store.append(@TypeOf(out), out), destination, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, passthrough, null, ); diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 0c81ee3730..d611d8e259 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -25,7 +25,6 @@ const Api = @import("../api/schema.zig").Api; const resolve_path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; const Command = @import("../cli.zig").Command; -const bundler = bun.bundler; const fs = @import("../fs.zig"); const URL = @import("../url.zig").URL; @@ -1958,7 +1957,7 @@ pub const Example = struct { } } - const http_proxy: ?URL = env_loader.getHttpProxy(api_url); + const http_proxy: ?URL = env_loader.getHttpProxyFor(api_url); const mutable = try ctx.allocator.create(MutableString); mutable.* = try MutableString.init(ctx.allocator, 8192); @@ -2037,7 +2036,7 @@ pub const Example = struct { url = URL.parse(try std.fmt.bufPrint(&url_buf, "https://registry.npmjs.org/@bun-examples/{s}/latest", .{name})); - var http_proxy: ?URL = env_loader.getHttpProxy(url); + var http_proxy: ?URL = env_loader.getHttpProxyFor(url); // ensure very stable memory address var async_http: *HTTP.AsyncHTTP = ctx.allocator.create(HTTP.AsyncHTTP) catch unreachable; @@ -2120,7 +2119,7 @@ pub const Example = struct { // ensure very stable memory address const parsed_tarball_url = URL.parse(tarball_url); - http_proxy = env_loader.getHttpProxy(parsed_tarball_url); + http_proxy = env_loader.getHttpProxyFor(parsed_tarball_url); async_http.* = HTTP.AsyncHTTP.initSync( ctx.allocator, @@ -2158,7 +2157,7 @@ pub const Example = struct { pub fn fetchAll(ctx: Command.Context, env_loader: *DotEnv.Loader, progress_node: ?*Progress.Node) ![]Example { url = URL.parse(examples_url); - const http_proxy: ?URL = env_loader.getHttpProxy(url); + const http_proxy: ?URL = env_loader.getHttpProxyFor(url); var async_http: *HTTP.AsyncHTTP = ctx.allocator.create(HTTP.AsyncHTTP) catch unreachable; const mutable = try ctx.allocator.create(MutableString); diff --git a/src/cli/exec_command.zig b/src/cli/exec_command.zig index 5ea2fd36cc..0711d7b1e3 100644 --- a/src/cli/exec_command.zig +++ b/src/cli/exec_command.zig @@ -16,7 +16,7 @@ pub const ExecCommand = struct { pub fn exec(ctx: Command.Context) !void { const script = ctx.positionals[1]; // this is a hack: make dummy bundler so we can use its `.runEnvLoader()` function to populate environment variables probably should split out the functionality - var bundle = try bun.Bundler.init( + var bundle = try bun.Transpiler.init( ctx.allocator, ctx.log, try @import("../bun.js/config.zig").configureTransformOptionsForBunVM(ctx.allocator, ctx.args), diff --git a/src/cli/filter_arg.zig b/src/cli/filter_arg.zig index 99031058d5..2bf9df67a9 100644 --- a/src/cli/filter_arg.zig +++ b/src/cli/filter_arg.zig @@ -34,7 +34,7 @@ fn globIgnoreFn(val: []const u8) bool { return false; } -const GlobWalker = Glob.GlobWalker_(globIgnoreFn, Glob.DirEntryAccessor, false); +const GlobWalker = Glob.GlobWalker(globIgnoreFn, Glob.walk.DirEntryAccessor, false); pub fn getCandidatePackagePatterns(allocator: std.mem.Allocator, log: *bun.logger.Log, out_patterns: *std.ArrayList([]u8), workdir_: []const u8, root_buf: *bun.PathBuffer) ![]const u8 { bun.JSAst.Expr.Data.Store.create(); @@ -187,7 +187,7 @@ pub const FilterSet = struct { pub fn matchesPath(self: *const FilterSet, path: []const u8) bool { for (self.filters) |filter| { - if (Glob.matchImpl(filter.codepoints, path).matches()) { + if (Glob.walk.matchImpl(filter.codepoints, path).matches()) { return true; } } @@ -200,7 +200,7 @@ pub const FilterSet = struct { .name => name, .path => path, }; - if (Glob.matchImpl(filter.codepoints, target).matches()) { + if (Glob.walk.matchImpl(filter.codepoints, target).matches()) { return true; } } diff --git a/src/cli/filter_run.zig b/src/cli/filter_run.zig index aad627d3d2..9093ee3fa2 100644 --- a/src/cli/filter_run.zig +++ b/src/cli/filter_run.zig @@ -11,7 +11,7 @@ const SemverString = @import("../install/semver.zig").String; const CLI = bun.CLI; const Command = CLI.Command; -const bundler = bun.bundler; +const transpiler = bun.transpiler; const FilterArg = @import("filter_arg.zig"); @@ -29,6 +29,7 @@ const ScriptConfig = struct { // ../../node_modules/.bin // and so forth, in addition to the user's $PATH. PATH: []const u8, + elide_count: ?usize, fn cmp(_: void, a: @This(), b: @This()) bool { return bun.strings.cmpStringsAsc({}, a.package_name, b.package_name); @@ -247,7 +248,7 @@ const State = struct { if (data[data.len - 1] == '\n') { data = data[0 .. data.len - 1]; } - if (max_lines == null) return .{ .content = data, .elided_count = 0 }; + if (max_lines == null or max_lines.? == 0) return .{ .content = data, .elided_count = 0 }; var i: usize = data.len; var lines: usize = 0; while (i > 0) : (i -= 1) { @@ -282,7 +283,9 @@ const State = struct { } for (this.handles) |*handle| { // normally we truncate the output to 10 lines, but on abort we print everything to aid debugging - const e = elide(handle.buffer.items, if (is_abort) null else 10); + const elide_lines = if (is_abort) null else handle.config.elide_count orelse 10; + const e = elide(handle.buffer.items, elide_lines); + try this.draw_buf.writer().print(fmt("{s} {s} $ {s}\n"), .{ handle.config.package_name, handle.config.script_name, handle.config.script_content }); if (e.elided_count > 0) { try this.draw_buf.writer().print( @@ -459,8 +462,8 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { var root_buf: bun.PathBuffer = undefined; const resolve_root = try FilterArg.getCandidatePackagePatterns(ctx.allocator, ctx.log, &patterns, fsinstance.top_level_dir, &root_buf); - var this_bundler: bundler.Bundler = undefined; - _ = try RunCommand.configureEnvForRun(ctx, &this_bundler, null, true, false); + var this_transpiler: transpiler.Transpiler = undefined; + _ = try RunCommand.configureEnvForRun(ctx, &this_transpiler, null, true, false); var package_json_iter = try FilterArg.PackageFilterIterator.init(ctx.allocator, patterns.items, resolve_root); defer package_json_iter.deinit(); @@ -472,7 +475,7 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { const dirpath = std.fs.path.dirname(package_json_path) orelse Global.crash(); const path = bun.strings.withoutTrailingSlash(dirpath); - const pkgjson = bun.PackageJSON.parse(&this_bundler.resolver, dirpath, .zero, null, .include_scripts, .main, .no_hash) orelse { + const pkgjson = bun.PackageJSON.parse(&this_transpiler.resolver, dirpath, .zero, null, .include_scripts, .main, .no_hash) orelse { Output.warn("Failed to read package.json\n", .{}); continue; }; @@ -482,7 +485,7 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { if (!filter_instance.matches(path, pkgjson.name)) continue; - const PATH = try RunCommand.configurePathForRunWithPackageJsonDir(ctx, dirpath, &this_bundler, null, dirpath, ctx.debug.run_in_bun); + const PATH = try RunCommand.configurePathForRunWithPackageJsonDir(ctx, dirpath, &this_transpiler, null, dirpath, ctx.debug.run_in_bun); for (&[3][]const u8{ pre_script_name, script_name, post_script_name }) |name| { const original_content = pkgscripts.get(name) orelse continue; @@ -513,6 +516,7 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { .combined = copy_script.items[0 .. copy_script.items.len - 1 :0], .deps = pkgjson.dependencies, .PATH = PATH, + .elide_count = ctx.bundler_options.elide_lines, }); } } @@ -522,9 +526,9 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { Global.exit(1); } - const event_loop = bun.JSC.MiniEventLoop.initGlobal(this_bundler.env); + const event_loop = bun.JSC.MiniEventLoop.initGlobal(this_transpiler.env); const shell_bin: [:0]const u8 = if (Environment.isPosix) - RunCommand.findShell(this_bundler.env.get("PATH") orelse "", fsinstance.top_level_dir) orelse return error.MissingShell + RunCommand.findShell(this_transpiler.env.get("PATH") orelse "", fsinstance.top_level_dir) orelse return error.MissingShell else bun.selfExePath() catch return error.MissingShell; @@ -533,9 +537,15 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { .event_loop = event_loop, .pretty_output = if (Environment.isWindows) windowsIsTerminal() else Output.enable_ansi_colors_stdout, .shell_bin = shell_bin, - .env = this_bundler.env, + .env = this_transpiler.env, }; + // Check if elide-lines is used in a non-terminal environment + if (ctx.bundler_options.elide_lines != null and !state.pretty_output) { + Output.prettyErrorln("error: --elide-lines is only supported in terminal environments", .{}); + Global.exit(1); + } + // initialize the handles var map = bun.StringHashMap(std.ArrayList(*ProcessHandle)).init(ctx.allocator); for (scripts.items, 0..) |*script, i| { diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig index 86f6efd224..0504a2c6dd 100644 --- a/src/cli/init_command.zig +++ b/src/cli/init_command.zig @@ -435,7 +435,7 @@ pub const InitCommand = struct { " \"'", fields.entry_point, )) { - Output.prettyln(" bun run {any}", .{bun.fmt.formatJSONString(fields.entry_point)}); + Output.prettyln(" bun run {any}", .{bun.fmt.formatJSONStringLatin1(fields.entry_point)}); } else { Output.prettyln(" bun run {s}", .{fields.entry_point}); } diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index 575c79f354..ba1053397d 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -24,7 +24,6 @@ const Api = @import("../api/schema.zig").Api; const resolve_path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; const Command = @import("../cli.zig").Command; -const bundler = bun.bundler; const fs = @import("../fs.zig"); const URL = @import("../url.zig").URL; diff --git a/src/cli/outdated_command.zig b/src/cli/outdated_command.zig index f24d333182..caa67f7b38 100644 --- a/src/cli/outdated_command.zig +++ b/src/cli/outdated_command.zig @@ -212,14 +212,14 @@ pub const OutdatedCommand = struct { const abs_res_path = path.joinAbsString(FileSystem.instance.top_level_dir, &[_]string{res_path}, .posix); - if (!glob.matchImpl(pattern, strings.withoutTrailingSlash(abs_res_path)).matches()) { + if (!glob.walk.matchImpl(pattern, strings.withoutTrailingSlash(abs_res_path)).matches()) { break :matched false; } }, .name => |pattern| { const name = pkg_names[workspace_pkg_id].slice(string_buf); - if (!glob.matchImpl(pattern, name).matches()) { + if (!glob.walk.matchImpl(pattern, name).matches()) { break :matched false; } }, @@ -331,7 +331,7 @@ pub const OutdatedCommand = struct { .path => unreachable, .name => |name_pattern| { if (name_pattern.len == 0) continue; - if (!glob.matchImpl(name_pattern, dep.name.slice(string_buf)).matches()) { + if (!glob.walk.matchImpl(name_pattern, dep.name.slice(string_buf)).matches()) { break :match false; } }, @@ -453,10 +453,10 @@ pub const OutdatedCommand = struct { for (workspace_pkg_ids) |workspace_pkg_id| { inline for ( .{ - Behavior{ .normal = true }, - Behavior{ .dev = true }, - Behavior{ .peer = true }, - Behavior{ .optional = true }, + Behavior.prod, + Behavior.dev, + Behavior.peer, + Behavior.optional, }, ) |group_behavior| { for (outdated_ids.items) |ids| { @@ -465,7 +465,7 @@ pub const OutdatedCommand = struct { const dep_id = ids.dep_id; const dep = dependencies[dep_id]; - if (@as(u8, @bitCast(group_behavior)) & @as(u8, @bitCast(dep.behavior)) == 0) continue; + if (!dep.behavior.includes(group_behavior)) continue; const package_name = pkg_names[package_id].slice(string_buf); const resolution = pkg_resolutions[package_id]; diff --git a/src/cli/pack_command.zig b/src/cli/pack_command.zig index 195a9c3edf..8918c21233 100644 --- a/src/cli/pack_command.zig +++ b/src/cli/pack_command.zig @@ -335,7 +335,7 @@ pub const PackCommand = struct { // normally the behavior of `index.js` and `**/index.js` are the same, // but includes require `**/` const match_path = if (include.@"leading **/") entry_name else entry_subpath; - switch (glob.matchImpl(include.glob, match_path)) { + switch (glob.walk.matchImpl(include.glob, match_path)) { .match => included = true, .negate_no_match => included = false, @@ -510,8 +510,8 @@ pub const PackCommand = struct { const dir = root_dir.openDirZ("node_modules", .{ .iterate = true }) catch |err| { switch (err) { - // ignore node_modules if it isn't a directory - error.NotDir => return bundled_pack_queue, + // ignore node_modules if it isn't a directory, or doesn't exist + error.NotDir, error.FileNotFound => return bundled_pack_queue, else => { Output.err(err, "failed to open \"node_modules\" to pack bundled dependencies", .{}); @@ -976,7 +976,7 @@ pub const PackCommand = struct { // check default ignores that only apply to the root project directory for (root_default_ignore_patterns) |pattern| { - switch (glob.matchImpl(pattern, entry_name)) { + switch (glob.walk.matchImpl(pattern, entry_name)) { .match => { // cannot be reversed return .{ @@ -1003,7 +1003,7 @@ pub const PackCommand = struct { for (default_ignore_patterns) |pattern_info| { const pattern, const can_override = pattern_info; - switch (glob.matchImpl(pattern, entry_name)) { + switch (glob.walk.matchImpl(pattern, entry_name)) { .match => { if (can_override) { ignored = true; @@ -1045,7 +1045,7 @@ pub const PackCommand = struct { if (pattern.dirs_only and entry.kind != .directory) continue; const match_path = if (pattern.rel_path) rel else entry_name; - switch (glob.matchImpl(pattern.glob, match_path)) { + switch (glob.walk.matchImpl(pattern.glob, match_path)) { .match => { ignored = true; ignore_pattern = pattern.glob; @@ -1140,11 +1140,11 @@ pub const PackCommand = struct { const edited_package_json = try editRootPackageJSON(ctx.allocator, ctx.lockfile, json); - var this_bundler: bun.bundler.Bundler = undefined; + var this_transpiler: bun.transpiler.Transpiler = undefined; _ = RunCommand.configureEnvForRun( ctx.command_ctx, - &this_bundler, + &this_transpiler, manager.env, manager.options.log_level != .silent, false, @@ -1177,7 +1177,7 @@ pub const PackCommand = struct { prepublish_only, "prepublishOnly", abs_workspace_path, - this_bundler.env, + this_transpiler.env, &.{}, manager.options.log_level == .silent, ctx.command_ctx.debug.use_system_shell, @@ -1202,7 +1202,7 @@ pub const PackCommand = struct { prepack_script_str, "prepack", abs_workspace_path, - this_bundler.env, + this_transpiler.env, &.{}, manager.options.log_level == .silent, ctx.command_ctx.debug.use_system_shell, @@ -1226,7 +1226,7 @@ pub const PackCommand = struct { prepare_script_str, "prepare", abs_workspace_path, - this_bundler.env, + this_transpiler.env, &.{}, manager.options.log_level == .silent, ctx.command_ctx.debug.use_system_shell, @@ -1393,7 +1393,7 @@ pub const PackCommand = struct { .uses_workspaces = false, .publish_script = publish_script, .postpublish_script = postpublish_script, - .script_env = this_bundler.env, + .script_env = this_transpiler.env, .normalized_pkg_info = "", }; } @@ -1719,7 +1719,7 @@ pub const PackCommand = struct { .uses_workspaces = false, .publish_script = publish_script, .postpublish_script = postpublish_script, - .script_env = this_bundler.env, + .script_env = this_transpiler.env, .normalized_pkg_info = normalized_pkg_info, }; } diff --git a/src/cli/pm_trusted_command.zig b/src/cli/pm_trusted_command.zig index a26f46a4f0..1341a9aa47 100644 --- a/src/cli/pm_trusted_command.zig +++ b/src/cli/pm_trusted_command.zig @@ -97,11 +97,11 @@ pub const UntrustedCommand = struct { const package_id = pm.lockfile.buffers.resolutions.items[dep_id]; const resolution = &resolutions[package_id]; var package_scripts = scripts[package_id]; - + var not_lazy: PackageManager.LazyPackageDestinationDir = .{ .dir = node_modules_dir }; const maybe_scripts_list = package_scripts.getList( pm.log, pm.lockfile, - node_modules_dir, + ¬_lazy, abs_node_modules_path.items, alias, resolution, @@ -262,11 +262,11 @@ pub const TrustCommand = struct { } const resolution = &resolutions[package_id]; var package_scripts = scripts[package_id]; - + var not_lazy = PackageManager.LazyPackageDestinationDir{ .dir = node_modules_dir }; const maybe_scripts_list = package_scripts.getList( pm.log, pm.lockfile, - node_modules_dir, + ¬_lazy, abs_node_modules_path.items, alias, resolution, diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 78c0c3601d..ab39f134e8 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -33,7 +33,7 @@ const sync = @import("../sync.zig"); const Api = @import("../api/schema.zig").Api; const resolve_path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; -const bundler = bun.bundler; +const transpiler = bun.transpiler; const DotEnv = @import("../env_loader.zig"); const which = @import("../which.zig").which; @@ -615,13 +615,13 @@ pub const RunCommand = struct { pub fn ls(ctx: Command.Context) !void { const args = ctx.args; - var this_bundler = try bundler.Bundler.init(ctx.allocator, ctx.log, args, null); - this_bundler.options.env.behavior = Api.DotEnvBehavior.load_all; - this_bundler.options.env.prefix = ""; + var this_transpiler = try transpiler.Transpiler.init(ctx.allocator, ctx.log, args, null); + this_transpiler.options.env.behavior = Api.DotEnvBehavior.load_all; + this_transpiler.options.env.prefix = ""; - this_bundler.resolver.care_about_bin_folder = true; - this_bundler.resolver.care_about_scripts = true; - this_bundler.configureLinker(); + this_transpiler.resolver.care_about_bin_folder = true; + this_transpiler.resolver.care_about_scripts = true; + this_transpiler.configureLinker(); } pub const bun_node_dir = switch (Environment.os) { @@ -792,30 +792,30 @@ pub const RunCommand = struct { const DirInfo = @import("../resolver/dir_info.zig"); pub fn configureEnvForRun( ctx: Command.Context, - this_bundler: *bundler.Bundler, + this_transpiler: *transpiler.Transpiler, env: ?*DotEnv.Loader, log_errors: bool, store_root_fd: bool, ) !*DirInfo { const args = ctx.args; - this_bundler.* = try bundler.Bundler.init(ctx.allocator, ctx.log, args, env); - this_bundler.options.env.behavior = Api.DotEnvBehavior.load_all; - this_bundler.env.quiet = true; - this_bundler.options.env.prefix = ""; + this_transpiler.* = try transpiler.Transpiler.init(ctx.allocator, ctx.log, args, env); + this_transpiler.options.env.behavior = Api.DotEnvBehavior.load_all; + this_transpiler.env.quiet = true; + this_transpiler.options.env.prefix = ""; - this_bundler.resolver.care_about_bin_folder = true; - this_bundler.resolver.care_about_scripts = true; - this_bundler.resolver.store_fd = store_root_fd; + this_transpiler.resolver.care_about_bin_folder = true; + this_transpiler.resolver.care_about_scripts = true; + this_transpiler.resolver.store_fd = store_root_fd; - this_bundler.resolver.opts.load_tsconfig_json = false; - this_bundler.options.load_tsconfig_json = false; + this_transpiler.resolver.opts.load_tsconfig_json = false; + this_transpiler.options.load_tsconfig_json = false; - this_bundler.configureLinker(); + this_transpiler.configureLinker(); - const root_dir_info = this_bundler.resolver.readDirInfo(this_bundler.fs.top_level_dir) catch |err| { + const root_dir_info = this_transpiler.resolver.readDirInfo(this_transpiler.fs.top_level_dir) catch |err| { if (!log_errors) return error.CouldntReadCurrentDirectory; ctx.log.print(Output.errorWriter()) catch {}; - Output.prettyErrorln("error: {s} loading directory {}", .{ @errorName(err), bun.fmt.QuotedFormatter{ .text = this_bundler.fs.top_level_dir } }); + Output.prettyErrorln("error: {s} loading directory {}", .{ @errorName(err), bun.fmt.QuotedFormatter{ .text = this_transpiler.fs.top_level_dir } }); Output.flush(); return err; } orelse { @@ -825,26 +825,26 @@ pub const RunCommand = struct { return error.CouldntReadCurrentDirectory; }; - this_bundler.resolver.store_fd = false; + this_transpiler.resolver.store_fd = false; if (env == null) { - this_bundler.env.loadProcess(); + this_transpiler.env.loadProcess(); - if (this_bundler.env.get("NODE_ENV")) |node_env| { + if (this_transpiler.env.get("NODE_ENV")) |node_env| { if (strings.eqlComptime(node_env, "production")) { - this_bundler.options.production = true; + this_transpiler.options.production = true; } } - this_bundler.runEnvLoader(true) catch {}; + this_transpiler.runEnvLoader(true) catch {}; } - this_bundler.env.map.putDefault("npm_config_local_prefix", this_bundler.fs.top_level_dir) catch unreachable; + this_transpiler.env.map.putDefault("npm_config_local_prefix", this_transpiler.fs.top_level_dir) catch unreachable; // we have no way of knowing what version they're expecting without running the node executable // running the node executable is too slow // so we will just hardcode it to LTS - this_bundler.env.map.putDefault( + this_transpiler.env.map.putDefault( "npm_config_user_agent", // the use of npm/? is copying yarn // e.g. @@ -852,33 +852,33 @@ pub const RunCommand = struct { "bun/" ++ Global.package_json_version ++ " npm/? node/v" ++ Environment.reported_nodejs_version ++ " " ++ Global.os_name ++ " " ++ Global.arch_name, ) catch unreachable; - if (this_bundler.env.get("npm_execpath") == null) { + if (this_transpiler.env.get("npm_execpath") == null) { // we don't care if this fails if (bun.selfExePath()) |self_exe_path| { - this_bundler.env.map.putDefault("npm_execpath", self_exe_path) catch unreachable; + this_transpiler.env.map.putDefault("npm_execpath", self_exe_path) catch unreachable; } else |_| {} } if (root_dir_info.enclosing_package_json) |package_json| { if (package_json.name.len > 0) { - if (this_bundler.env.map.get(NpmArgs.package_name) == null) { - this_bundler.env.map.put(NpmArgs.package_name, package_json.name) catch unreachable; + if (this_transpiler.env.map.get(NpmArgs.package_name) == null) { + this_transpiler.env.map.put(NpmArgs.package_name, package_json.name) catch unreachable; } } - this_bundler.env.map.putDefault("npm_package_json", package_json.source.path.text) catch unreachable; + this_transpiler.env.map.putDefault("npm_package_json", package_json.source.path.text) catch unreachable; if (package_json.version.len > 0) { - if (this_bundler.env.map.get(NpmArgs.package_version) == null) { - this_bundler.env.map.put(NpmArgs.package_version, package_json.version) catch unreachable; + if (this_transpiler.env.map.get(NpmArgs.package_version) == null) { + this_transpiler.env.map.put(NpmArgs.package_version, package_json.version) catch unreachable; } } if (package_json.config) |config| { - try this_bundler.env.map.ensureUnusedCapacity(config.count()); + try this_transpiler.env.map.ensureUnusedCapacity(config.count()); for (config.keys(), config.values()) |k, v| { const key = try bun.strings.concat(bun.default_allocator, &.{ "npm_package_config_", k }); - this_bundler.env.map.putAssumeCapacity(key, v); + this_transpiler.env.map.putAssumeCapacity(key, v); } } } @@ -889,20 +889,20 @@ pub const RunCommand = struct { pub fn configurePathForRunWithPackageJsonDir( ctx: Command.Context, package_json_dir: string, - this_bundler: *bundler.Bundler, + this_transpiler: *transpiler.Transpiler, ORIGINAL_PATH: ?*string, cwd: string, force_using_bun: bool, ) ![]u8 { - const PATH = this_bundler.env.get("PATH") orelse ""; + const PATH = this_transpiler.env.get("PATH") orelse ""; if (ORIGINAL_PATH) |original_path| { original_path.* = PATH; } const bun_node_exe = try bunNodeFileUtf8(ctx.allocator); const bun_node_dir_win = bun.Dirname.dirname(u8, bun_node_exe) orelse return error.FailedToGetTempPath; - const found_node = this_bundler.env.loadNodeJSConfig( - this_bundler.fs, + const found_node = this_transpiler.env.loadNodeJSConfig( + this_transpiler.fs, if (force_using_bun) bun_node_exe else "", ) catch false; @@ -934,9 +934,9 @@ pub const RunCommand = struct { if (needs_to_force_bun) { createFakeTemporaryNodeExecutable(&new_path, &optional_bun_self_path) catch bun.outOfMemory(); if (!force_using_bun) { - this_bundler.env.map.put("NODE", bun_node_exe) catch bun.outOfMemory(); - this_bundler.env.map.put("npm_node_execpath", bun_node_exe) catch bun.outOfMemory(); - this_bundler.env.map.put("npm_execpath", optional_bun_self_path) catch bun.outOfMemory(); + this_transpiler.env.map.put("NODE", bun_node_exe) catch bun.outOfMemory(); + this_transpiler.env.map.put("npm_node_execpath", bun_node_exe) catch bun.outOfMemory(); + this_transpiler.env.map.put("npm_execpath", optional_bun_self_path) catch bun.outOfMemory(); } needs_to_force_bun = false; @@ -969,7 +969,7 @@ pub const RunCommand = struct { pub fn configurePathForRun( ctx: Command.Context, root_dir_info: *DirInfo, - this_bundler: *bundler.Bundler, + this_transpiler: *transpiler.Transpiler, ORIGINAL_PATH: ?*string, cwd: string, force_using_bun: bool, @@ -984,8 +984,8 @@ pub const RunCommand = struct { } } - const new_path = try configurePathForRunWithPackageJsonDir(ctx, package_json_dir, this_bundler, ORIGINAL_PATH, cwd, force_using_bun); - this_bundler.env.map.put("PATH", new_path) catch bun.outOfMemory(); + const new_path = try configurePathForRunWithPackageJsonDir(ctx, package_json_dir, this_transpiler, ORIGINAL_PATH, cwd, force_using_bun); + this_transpiler.env.map.put("PATH", new_path) catch bun.outOfMemory(); } pub fn completions(ctx: Command.Context, default_completions: ?[]const string, reject_list: []const string, comptime filter: Filter) !ShellCompletions { @@ -998,35 +998,35 @@ pub const RunCommand = struct { const args = ctx.args; - var this_bundler = bundler.Bundler.init(ctx.allocator, ctx.log, args, null) catch return shell_out; - this_bundler.options.env.behavior = Api.DotEnvBehavior.load_all; - this_bundler.options.env.prefix = ""; - this_bundler.env.quiet = true; + var this_transpiler = transpiler.Transpiler.init(ctx.allocator, ctx.log, args, null) catch return shell_out; + this_transpiler.options.env.behavior = Api.DotEnvBehavior.load_all; + this_transpiler.options.env.prefix = ""; + this_transpiler.env.quiet = true; - this_bundler.resolver.care_about_bin_folder = true; - this_bundler.resolver.care_about_scripts = true; - this_bundler.resolver.store_fd = true; + this_transpiler.resolver.care_about_bin_folder = true; + this_transpiler.resolver.care_about_scripts = true; + this_transpiler.resolver.store_fd = true; defer { - this_bundler.resolver.care_about_bin_folder = false; - this_bundler.resolver.care_about_scripts = false; + this_transpiler.resolver.care_about_bin_folder = false; + this_transpiler.resolver.care_about_scripts = false; } - this_bundler.configureLinker(); + this_transpiler.configureLinker(); - const root_dir_info = (this_bundler.resolver.readDirInfo(this_bundler.fs.top_level_dir) catch null) orelse return shell_out; + const root_dir_info = (this_transpiler.resolver.readDirInfo(this_transpiler.fs.top_level_dir) catch null) orelse return shell_out; { - this_bundler.env.loadProcess(); + this_transpiler.env.loadProcess(); - if (this_bundler.env.get("NODE_ENV")) |node_env| { + if (this_transpiler.env.get("NODE_ENV")) |node_env| { if (strings.eqlComptime(node_env, "production")) { - this_bundler.options.production = true; + this_transpiler.options.production = true; } } } const ResultList = bun.StringArrayHashMap(void); - if (this_bundler.env.get("SHELL")) |shell| { + if (this_transpiler.env.get("SHELL")) |shell| { shell_out.shell = ShellCompletions.Shell.fromEnv(@TypeOf(shell), shell); } @@ -1043,15 +1043,15 @@ pub const RunCommand = struct { } if (filter == Filter.bin or filter == Filter.all or filter == Filter.all_plus_bun_js) { - for (this_bundler.resolver.binDirs()) |bin_path| { - if (this_bundler.resolver.readDirInfo(bin_path) catch null) |bin_dir| { + for (this_transpiler.resolver.binDirs()) |bin_path| { + if (this_transpiler.resolver.readDirInfo(bin_path) catch null) |bin_dir| { if (bin_dir.getEntriesConst()) |entries| { var iter = entries.data.iterator(); var has_copied = false; var dir_slice: string = ""; while (iter.next()) |entry| { const value = entry.value_ptr.*; - if (value.kind(&this_bundler.fs.fs, true) == .file) { + if (value.kind(&this_transpiler.fs.fs, true) == .file) { if (!has_copied) { bun.copy(u8, &path_buf, value.dir); dir_slice = path_buf[0..value.dir.len]; @@ -1070,7 +1070,7 @@ pub const RunCommand = struct { } if (!(bun.sys.isExecutableFilePath(slice))) continue; // we need to dupe because the string pay point to a pointer that only exists in the current scope - _ = try results.getOrPut(this_bundler.fs.filename_store.append(@TypeOf(base), base) catch continue); + _ = try results.getOrPut(this_transpiler.fs.filename_store.append(@TypeOf(base), base) catch continue); } } } @@ -1079,21 +1079,21 @@ pub const RunCommand = struct { } if (filter == Filter.all_plus_bun_js or filter == Filter.bun_js) { - if (this_bundler.resolver.readDirInfo(this_bundler.fs.top_level_dir) catch null) |dir_info| { + if (this_transpiler.resolver.readDirInfo(this_transpiler.fs.top_level_dir) catch null) |dir_info| { if (dir_info.getEntriesConst()) |entries| { var iter = entries.data.iterator(); while (iter.next()) |entry| { const value = entry.value_ptr.*; const name = value.base(); - if (name[0] != '.' and this_bundler.options.loader(std.fs.path.extension(name)).canBeRunByBun() and + if (name[0] != '.' and this_transpiler.options.loader(std.fs.path.extension(name)).canBeRunByBun() and !strings.contains(name, ".config") and !strings.contains(name, ".d.ts") and !strings.contains(name, ".d.mts") and !strings.contains(name, ".d.cts") and - value.kind(&this_bundler.fs.fs, true) == .file) + value.kind(&this_transpiler.fs.fs, true) == .file) { - _ = try results.getOrPut(this_bundler.fs.filename_store.append(@TypeOf(name), name) catch continue); + _ = try results.getOrPut(this_transpiler.fs.filename_store.append(@TypeOf(name), name) catch continue); } } } @@ -1109,7 +1109,7 @@ pub const RunCommand = struct { } var max_description_len: usize = 20; - if (this_bundler.env.get("MAX_DESCRIPTION_LEN")) |max| { + if (this_transpiler.env.get("MAX_DESCRIPTION_LEN")) |max| { if (std.fmt.parseInt(usize, max, 10) catch null) |max_len| { max_description_len = max_len; } @@ -1390,10 +1390,10 @@ pub const RunCommand = struct { Global.configureAllocator(.{ .long_running = false }); var ORIGINAL_PATH: string = ""; - var this_bundler: bundler.Bundler = undefined; - const root_dir_info = try configureEnvForRun(ctx, &this_bundler, null, log_errors, false); - try configurePathForRun(ctx, root_dir_info, &this_bundler, &ORIGINAL_PATH, root_dir_info.abs_path, force_using_bun); - this_bundler.env.map.put("npm_command", "run-script") catch unreachable; + var this_transpiler: transpiler.Transpiler = undefined; + const root_dir_info = try configureEnvForRun(ctx, &this_transpiler, null, log_errors, false); + try configurePathForRun(ctx, root_dir_info, &this_transpiler, &ORIGINAL_PATH, root_dir_info.abs_path, force_using_bun); + this_transpiler.env.map.put("npm_command", "run-script") catch unreachable; if (script_name_to_search.len == 0) { // naked "bun run" @@ -1422,8 +1422,8 @@ pub const RunCommand = struct { ctx.allocator, prescript, temp_script_buffer[1..], - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, &.{}, ctx.debug.silent, ctx.debug.use_system_shell, @@ -1436,8 +1436,8 @@ pub const RunCommand = struct { ctx.allocator, script_content, script_name_to_search, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, passthrough, ctx.debug.silent, ctx.debug.use_system_shell, @@ -1452,8 +1452,8 @@ pub const RunCommand = struct { ctx.allocator, postscript, temp_script_buffer, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, &.{}, ctx.debug.silent, ctx.debug.use_system_shell, @@ -1536,10 +1536,10 @@ pub const RunCommand = struct { const l = root.len + cwd_len + prefix.len + script_name_to_search.len + ext.len; const path_to_use = BunXFastPath.direct_launch_buffer[0..l :0]; - BunXFastPath.tryLaunch(ctx, path_to_use, this_bundler.env, ctx.passthrough); + BunXFastPath.tryLaunch(ctx, path_to_use, this_transpiler.env, ctx.passthrough); } - const PATH = this_bundler.env.get("PATH") orelse ""; + const PATH = this_transpiler.env.get("PATH") orelse ""; var path_for_which = PATH; if (comptime bin_dirs_only) { if (ORIGINAL_PATH.len < PATH.len) { @@ -1550,14 +1550,14 @@ pub const RunCommand = struct { } if (path_for_which.len > 0) { - if (which(&path_buf, path_for_which, this_bundler.fs.top_level_dir, script_name_to_search)) |destination| { + if (which(&path_buf, path_for_which, this_transpiler.fs.top_level_dir, script_name_to_search)) |destination| { const out = bun.asByteSlice(destination); return try runBinaryWithoutBunxPath( ctx, - try this_bundler.fs.dirname_store.append(@TypeOf(out), out), + try this_transpiler.fs.dirname_store.append(@TypeOf(out), out), destination, - this_bundler.fs.top_level_dir, - this_bundler.env, + this_transpiler.fs.top_level_dir, + this_transpiler.env, passthrough, script_name_to_search, ); diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index a918be8adc..050a83880c 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -764,7 +764,7 @@ pub const CommandLineReporter = struct { @compileError("No reporters enabled"); } - const relative_dir = vm.bundler.fs.top_level_dir; + const relative_dir = vm.transpiler.fs.top_level_dir; // --- Text --- const max_filepath_length: usize = if (reporters.text) brk: { @@ -1051,7 +1051,7 @@ const Scanner = struct { } const ext = std.fs.path.extension(slice); - const loader_by_ext = JSC.VirtualMachine.get().bundler.options.loader(ext); + const loader_by_ext = JSC.VirtualMachine.get().transpiler.options.loader(ext); // allow file loader just incase they use a custom loader with a non-standard extension if (!(loader_by_ext.isJavaScriptLike() or loader_by_ext == .file)) { @@ -1261,12 +1261,13 @@ pub const TestCommand = struct { .store_fd = true, .smol = ctx.runtime_options.smol, .debugger = ctx.runtime_options.debugger, + .is_main_thread = true, }, ); vm.argv = ctx.passthrough; vm.preload = ctx.preloads; - vm.bundler.options.rewrite_jest_for_tests = true; - vm.bundler.options.env.behavior = .load_all_without_inlining; + vm.transpiler.options.rewrite_jest_for_tests = true; + vm.transpiler.options.env.behavior = .load_all_without_inlining; const node_env_entry = try env_loader.map.getOrPutWithoutValue("NODE_ENV"); if (!node_env_entry.found_existing) { @@ -1277,18 +1278,18 @@ pub const TestCommand = struct { }; } - try vm.bundler.configureDefines(); + try vm.transpiler.configureDefines(); vm.loadExtraEnvAndSourceCodePrinter(); vm.is_main_thread = true; JSC.VirtualMachine.is_main_thread_vm = true; if (ctx.test_options.coverage.enabled) { - vm.bundler.options.code_coverage = true; - vm.bundler.options.minify_syntax = false; - vm.bundler.options.minify_identifiers = false; - vm.bundler.options.minify_whitespace = false; - vm.bundler.options.dead_code_elimination = false; + vm.transpiler.options.code_coverage = true; + vm.transpiler.options.minify_syntax = false; + vm.transpiler.options.minify_identifiers = false; + vm.transpiler.options.minify_whitespace = false; + vm.transpiler.options.dead_code_elimination = false; vm.global.vm().setControlFlowProfiler(true); } @@ -1298,7 +1299,7 @@ pub const TestCommand = struct { // We use the string "Etc/UTC" instead of "UTC" so there is no normalization difference. "Etc/UTC"; - if (vm.bundler.env.get("TZ")) |tz| { + if (vm.transpiler.env.get("TZ")) |tz| { TZ_NAME = tz; } @@ -1351,8 +1352,8 @@ pub const TestCommand = struct { var scanner = Scanner{ .dirs_to_scan = Scanner.Fifo.init(ctx.allocator), - .options = &vm.bundler.options, - .fs = vm.bundler.fs, + .options = &vm.transpiler.options, + .fs = vm.transpiler.fs, .filter_names = filter_names_normalized, .results = &results, }; @@ -1379,7 +1380,7 @@ pub const TestCommand = struct { else => {}, } - // vm.bundler.fs.fs.readDirectory(_dir: string, _handle: ?std.fs.Dir) + // vm.transpiler.fs.fs.readDirectory(_dir: string, _handle: ?std.fs.Dir) runAllTests(reporter, vm, test_files, ctx.allocator); } @@ -1658,7 +1659,7 @@ pub const TestCommand = struct { defer reporter.jest.only = prev_only; const file_start = reporter.jest.files.len; - const resolution = try vm.bundler.resolveEntryPoint(file_name); + const resolution = try vm.transpiler.resolveEntryPoint(file_name); vm.clearEntryPoint(); const file_path = resolution.path_pair.primary.text; diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index d55776ad24..fb822fa4e9 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -25,7 +25,6 @@ const Api = @import("../api/schema.zig").Api; const resolve_path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; const Command = @import("../cli.zig").Command; -const bundler = bun.bundler; const fs = @import("../fs.zig"); const URL = @import("../url.zig").URL; @@ -232,7 +231,7 @@ pub const UpgradeCommand = struct { } } - const http_proxy: ?URL = env_loader.getHttpProxy(api_url); + const http_proxy: ?URL = env_loader.getHttpProxyFor(api_url); var metadata_body = try MutableString.init(allocator, 2048); @@ -502,7 +501,7 @@ pub const UpgradeCommand = struct { }; const zip_url = URL.parse(version.zip_url); - const http_proxy: ?URL = env_loader.getHttpProxy(zip_url); + const http_proxy: ?URL = env_loader.getHttpProxyFor(zip_url); { var refresher = Progress{}; diff --git a/src/codegen/bindgen-lib-internal.ts b/src/codegen/bindgen-lib-internal.ts index 3a47ad6663..7c4ee555fa 100644 --- a/src/codegen/bindgen-lib-internal.ts +++ b/src/codegen/bindgen-lib-internal.ts @@ -47,6 +47,8 @@ export const extJsFunction = (namespaceVar: string, fnLabel: string) => /** Each variant gets a dispatcher function. */ export const extDispatchVariant = (namespaceVar: string, fnLabel: string, variantNumber: number) => `bindgen_${cap(namespaceVar)}_dispatch${cap(fnLabel)}${variantNumber}`; +export const extInternalDispatchVariant = (namespaceVar: string, fnLabel: string, variantNumber: string | number) => + `bindgen_${cap(namespaceVar)}_js${cap(fnLabel)}_v${variantNumber}`; interface TypeDataDefs { /** The name */ @@ -70,12 +72,18 @@ interface TypeDataDefs { } type TypeData = K extends keyof TypeDataDefs ? TypeDataDefs[K] : any; +export const enum NodeValidator { + validateInteger = "validateInteger", +} + interface Flags { + nodeValidator?: NodeValidator; optional?: boolean; required?: boolean; - nullable?: boolean; + nonNull?: boolean; default?: any; range?: ["clamp" | "enforce", bigint, bigint] | ["clamp" | "enforce", "abi", "abi"]; + finite?: boolean; } export interface DictionaryField { @@ -85,6 +93,8 @@ export interface DictionaryField { export declare const isType: unique symbol; +const numericTypes = new Set(["f64", "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "usize"]); + /** * Implementation of the Type interface. All types are immutable and hashable. * Hashes de-duplicate structure and union definitions. Flags do not account for @@ -263,7 +273,7 @@ export class TypeImpl { } cppClassName() { - assert(this.lowersToNamedType()); + assert(this.lowersToNamedType(), `Does not lower to named type: ${inspect(this)}`); const name = this.name(); const namespace = typeHashToNamespace.get(this.hash()); return namespace ? `${namespace}::${cap(name)}` : name; @@ -327,48 +337,6 @@ export class TypeImpl { } } - // Interface definition API - get optional() { - if (this.flags.required) { - throw new Error("Cannot derive optional on a required type"); - } - if (this.flags.default) { - throw new Error("Cannot derive optional on a something with a default value (default implies optional)"); - } - return new TypeImpl(this.kind, this.data, { - ...this.flags, - optional: true, - }); - } - - get nullable() { - return new TypeImpl(this.kind, this.data, { - ...this.flags, - nullable: true, - }); - } - - get required() { - if (this.flags.required) { - throw new Error("This type already has required set"); - } - if (this.flags.required) { - throw new Error("Cannot derive required on an optional type"); - } - return new TypeImpl(this.kind, this.data, { - ...this.flags, - required: true, - }); - } - - clamp(min?: number | bigint, max?: number | bigint) { - return this.#rangeModifier(min, max, "clamp"); - } - - enforceRange(min?: number | bigint, max?: number | bigint) { - return this.#rangeModifier(min, max, "enforce"); - } - #rangeModifier(min: undefined | number | bigint, max: undefined | number | bigint, kind: "clamp" | "enforce") { if (this.flags.range) { throw new Error("This type already has a range modifier set"); @@ -467,6 +435,9 @@ export class TypeImpl { } } break; + case "undefined": + assert(value === undefined, `Expected undefined, got ${inspect(value)}`); + break; default: throw new Error(`TODO: set default value on type ${this.kind}`); } @@ -515,6 +486,8 @@ export class TypeImpl { throw new Error(`TODO: non-empty string default`); } break; + case "undefined": + throw new Error("Zero-sized type"); default: throw new Error(`TODO: set default value on type ${this.kind}`); } @@ -527,25 +500,31 @@ export class TypeImpl { throw new Error("TODO: generate non-extern struct for representing this data type"); } - default(def: any) { - if ("default" in this.flags) { - throw new Error("This type already has a default value"); - } - if (this.flags.required) { - throw new Error("Cannot derive default on a required type"); - } - this.assertDefaultIsValid(def); - return new TypeImpl(this.kind, this.data, { - ...this.flags, - default: def, - }); + isIgnoredUndefinedType() { + return this.kind === "undefined"; + } + + isStringType() { + return ( + this.kind === "DOMString" || this.kind === "ByteString" || this.kind === "USVString" || this.kind === "UTF8String" + ); + } + + isNumberType() { + return numericTypes.has(this.kind); + } + + isObjectType() { + return this.kind === "externalClass" || this.kind === "dictionary"; } [Symbol.toStringTag] = "Type"; [Bun.inspect.custom](depth, options, inspect) { return ( `${options.stylize("Type", "special")} ${ - this.nameDeduplicated ? options.stylize(JSON.stringify(this.nameDeduplicated), "string") + " " : "" + this.lowersToNamedType() && this.nameDeduplicated + ? options.stylize(JSON.stringify(this.nameDeduplicated), "string") + " " + : "" }${options.stylize( `[${this.kind}${["required", "optional", "nullable"] .filter(k => this.flags[k]) @@ -562,9 +541,105 @@ export class TypeImpl { : "") ); } + + // Public interface definition API + get optional() { + if (this.flags.required) { + throw new Error("Cannot derive optional on a required type"); + } + if (this.flags.default) { + throw new Error("Cannot derive optional on a something with a default value (default implies optional)"); + } + return new TypeImpl(this.kind, this.data, { + ...this.flags, + optional: true, + }); + } + + get finite() { + if (this.kind !== "f64") { + throw new Error("finite can only be used on f64"); + } + if (this.flags.finite) { + throw new Error("This type already has finite set"); + } + return new TypeImpl(this.kind, this.data, { + ...this.flags, + finite: true, + }); + } + + get required() { + if (this.flags.required) { + throw new Error("This type already has required set"); + } + if (this.flags.required) { + throw new Error("Cannot derive required on an optional type"); + } + return new TypeImpl(this.kind, this.data, { + ...this.flags, + required: true, + }); + } + + default(def: any) { + if ("default" in this.flags) { + throw new Error("This type already has a default value"); + } + if (this.flags.required) { + throw new Error("Cannot derive default on a required type"); + } + this.assertDefaultIsValid(def); + return new TypeImpl(this.kind, this.data, { + ...this.flags, + default: def, + }); + } + + clamp(min?: number | bigint, max?: number | bigint) { + return this.#rangeModifier(min, max, "clamp"); + } + + enforceRange(min?: number | bigint, max?: number | bigint) { + return this.#rangeModifier(min, max, "enforce"); + } + + get nonNull() { + if (this.flags.nonNull) { + throw new Error("Cannot derive nonNull on a nonNull type"); + } + return new TypeImpl(this.kind, this.data, { + ...this.flags, + nonNull: true, + }); + } + + validateInt32(min?: number, max?: number) { + if (this.kind !== "i32") { + throw new Error("validateInt32 can only be used on i32 or u32"); + } + const rangeInfo = cAbiIntegerLimits("i32"); + return this.validateInteger(min ?? rangeInfo[0], max ?? rangeInfo[1]); + } + + validateUint32(min?: number, max?: number) { + if (this.kind !== "u32") { + throw new Error("validateUint32 can only be used on i32 or u32"); + } + const rangeInfo = cAbiIntegerLimits("u32"); + return this.validateInteger(min ?? rangeInfo[0], max ?? rangeInfo[1]); + } + + validateInteger(min?: number | bigint, max?: number | bigint) { + min ??= Number.MIN_SAFE_INTEGER; + max ??= Number.MAX_SAFE_INTEGER; + const enforceRange = this.#rangeModifier(min, max, "enforce") as TypeImpl; + enforceRange.flags.nodeValidator = NodeValidator.validateInteger; + return enforceRange; + } } -function cAbiIntegerLimits(type: CAbiType) { +export function cAbiIntegerLimits(type: CAbiType) { switch (type) { case "u8": return [0, 255]; @@ -584,6 +659,8 @@ function cAbiIntegerLimits(type: CAbiType) { return [-2147483648, 2147483647]; case "i64": return [-9223372036854775808n, 9223372036854775807n]; + case "f64": + return [-Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]; default: throw new Error(`Unexpected type ${type}`); } @@ -603,9 +680,6 @@ export function oneOfImpl(types: TypeImpl[]): TypeImpl { if (type.kind === "oneOf") { out.push(...type.data); } else { - if (type.flags.nullable) { - throw new Error("Union type cannot include nullable"); - } if (type.flags.default) { throw new Error( "Union type cannot include a default value. Instead, set a default value on the union type itself", @@ -711,6 +785,8 @@ export type ArgStrategyChildItem = export type ReturnStrategy = // JSValue is special cased because it encodes exception as 0x0 | { type: "jsvalue" } + // Return value doesnt exist. function returns a boolean indicating success/error. + | { type: "void" } // For primitives and simple structures where direct assignment into a // pointer is possible. function returns a boolean indicating success/error. | { type: "basic-out-param"; abiType: CAbiType }; @@ -741,7 +817,8 @@ export function registerFunction(opts: FuncOptions) { for (const variant of opts.variants) { const { minRequiredArgs } = validateVariant(variant); variants.push({ - ...variant, + args: Object.entries(variant.args).map(([name, type]) => ({ name, type })) as Arg[], + ret: variant.ret as TypeImpl, suffix: `${i}`, minRequiredArgs, } as unknown as Variant); diff --git a/src/codegen/bindgen-lib.ts b/src/codegen/bindgen-lib.ts index 2ef06e92ed..483e6d2531 100644 --- a/src/codegen/bindgen-lib.ts +++ b/src/codegen/bindgen-lib.ts @@ -10,43 +10,107 @@ import { isFunc, } from "./bindgen-lib-internal"; -export type Type = { - [isType]: true | [T, K, Flags]; -} & (Flags extends null - ? { - /** - * Optional means the value may be omitted from a parameter definition. - * Parameters are required by default. - */ - optional: Type; - /** - * When this is used as a dictionary value, this makes that parameter - * required. Dictionary entries are optional by default. - */ - required: Type, K, false>; +/** A type definition for argument parsing. See `bindgen.md` for usage details. */ +export type Type< + /** T = JavaScript type that the Type represents */ + T, + /** K = "kind" a string pertaining to the `t.` that created this type. affects method listing */ + K extends TypeKind = TypeKind, + /** F = "flags" defining if the value is optional. null = not set, false = required, true = optional. */ + F extends TypeFlag = null, +> = F extends null + ? Props + : F extends true + ? { + [isType]: true | [T, K, F]; + nonNull: Type; + } + : { [isType]: true | [T, K, F] }; - /** Implies `optional`, this sets a default value if omitted */ - default(def: T): Type; - } & (K extends IntegerTypeKind - ? { - /** - * Applies [Clamp] semantics - * https://webidl.spec.whatwg.org/#Clamp - * If a custom numeric range is provided, it will be used instead of the built-in clamp rules. - */ - clamp(min?: T, max?: T): Type; - /** - * Applies [EnforceRange] semantics - * https://webidl.spec.whatwg.org/#EnforceRange - * If a custom numeric range is provided, it will be used instead of the built-in enforce rules. - */ - enforceRange(min?: T, max?: T): Type; - } - : {}) - : {}); +type TypeFlag = boolean | "opt-nonnull" | null; + +interface BaseTypeProps { + [isType]: true | [T, K]; + /** + * Optional means the value may be omitted from a parameter definition. + * Parameters are required by default. + */ + optional: Type; + /** + * When this is used as a dictionary value, this makes that parameter + * required. Dictionary entries are optional by default. + */ + required: Type, K, false>; + + /** Implies `optional`, this sets a default value if omitted */ + default(def: T): Type; +} + +interface NumericTypeProps extends BaseTypeProps { + /** + * Applies [Clamp] semantics + * https://webidl.spec.whatwg.org/#Clamp + * If a custom numeric range is provided, it will be used instead of the built-in clamp rules. + */ + clamp(min?: T, max?: T): Type; + /** + * Applies [EnforceRange] semantics + * https://webidl.spec.whatwg.org/#EnforceRange + * If a custom numeric range is provided, it will be used instead of the built-in enforce rules. + */ + enforceRange(min?: T, max?: T): Type; + + /** + * Equivalent to calling Node.js' `validateInteger(val, prop, min, max)` + */ + validateInteger(min?: T, max?: T): Type; +} + +interface I32TypeProps extends NumericTypeProps { + /** + * Equivalent to calling Node.js' `validateInt32(val, prop, min, max)` + */ + validateInt32(min?: number, max?: number): Type; +} + +interface U32TypeProps extends NumericTypeProps { + /** + * Equivalent to calling Node.js' `validateUint32(val, prop, min, max)` + */ + validateUint32(min?: number, max?: number): Type; +} + +interface F64TypeProps extends NumericTypeProps { + /** + * Throws an error if the input is non-finite (NaN, ±Infinity) + */ + finite: Type; + /** + * Equivalent to calling Node.js' `validateNumber(val, prop, min, max)` + */ + validateNumber(min?: number, max?: number): Type; +} + +// If an entry does not exist, then `BaseTypeProps` is assumed. +// T = JavaScript type that the Type represents +interface TypePropsMap { + // Integer types are always numbers, so T is not passed + ["u8"]: NumericTypeProps; + ["i8"]: NumericTypeProps; + ["u16"]: NumericTypeProps; + ["i16"]: NumericTypeProps; + ["u32"]: U32TypeProps; + ["i32"]: I32TypeProps; + ["u64"]: NumericTypeProps; + ["i64"]: NumericTypeProps; + // F64 is always a number, so T is not passed. + ["f64"]: F64TypeProps; +} + +type PropertyMapKeys = keyof TypePropsMap; +type Props = K extends PropertyMapKeys ? TypePropsMap[K] : BaseTypeProps; export type AcceptedDictionaryTypeKind = Exclude; -export type IntegerTypeKind = "usize" | "i32" | "i64" | "u32" | "u64" | "i8" | "u8" | "i16" | "u16"; function builtinType() { return (kind: K) => new TypeImpl(kind, undefined as any, {}) as Type as Type; @@ -78,6 +142,10 @@ export namespace t { /** Throws if the value is not a boolean. */ export const strictBoolean = builtinType()("strictBoolean"); + /** + * Equivalent to IDL's `unrestricted double`, allowing NaN and Infinity. + * To restrict to finite values, use `f64.finite`. + */ export const f64 = builtinType()("f64"); export const u8 = builtinType()("u8"); diff --git a/src/codegen/bindgen.ts b/src/codegen/bindgen.ts index ef6d870908..f9f9d5e402 100644 --- a/src/codegen/bindgen.ts +++ b/src/codegen/bindgen.ts @@ -7,7 +7,6 @@ import * as path from "node:path"; import { CodeWriter, TypeImpl, - cAbiTypeInfo, cAbiTypeName, cap, extDispatchVariant, @@ -31,10 +30,12 @@ import { alignForward, isFunc, Func, + NodeValidator, + cAbiIntegerLimits, + extInternalDispatchVariant, } from "./bindgen-lib-internal"; import assert from "node:assert"; import { argParse, readdirRecursiveWithExclusionsAndExtensionsSync, writeIfNotChanged } from "./helpers"; -import { type IntegerTypeKind } from "bindgen"; // arg parsing let { "codegen-root": codegenRoot, debug } = argParse(["codegen-root", "debug"]); @@ -54,7 +55,7 @@ function resolveVariantStrategies(vari: Variant, name: string) { argIndex += 1; // If `extern struct` can represent this type, that is the simplest way to cross the C-ABI boundary. - const isNullable = (arg.type.flags.optional && !("default" in arg.type.flags)) || arg.type.flags.nullable; + const isNullable = arg.type.flags.optional && !("default" in arg.type.flags); const abiType = !isNullable && arg.type.canDirectlyMapToCAbi(); if (abiType) { arg.loweringStrategy = { @@ -85,6 +86,10 @@ function resolveVariantStrategies(vari: Variant, name: string) { } return_strategy: { + if (vari.ret.kind === "undefined") { + vari.returnStrategy = { type: "void" }; + break return_strategy; + } if (vari.ret.kind === "any") { vari.returnStrategy = { type: "jsvalue" }; break return_strategy; @@ -109,7 +114,7 @@ function resolveNullableArgumentStrategy( prefix: string, communicationStruct: Struct, ): ArgStrategyChildItem[] { - assert((type.flags.optional && !("default" in type.flags)) || type.flags.nullable); + assert(type.flags.optional && !("default" in type.flags)); communicationStruct.add(`${prefix}Set`, "bool"); return resolveComplexArgumentStrategy(type, `${prefix}Value`, communicationStruct); } @@ -155,6 +160,10 @@ function emitCppCallToVariant(name: string, variant: Variant, dispatchFunctionNa for (const arg of variant.args) { const type = arg.type; if (type.isVirtualArgument()) continue; + if (type.isIgnoredUndefinedType()) { + i += 1; + continue; + } const exceptionContext: ExceptionContext = { type: "argument", @@ -187,21 +196,22 @@ function emitCppCallToVariant(name: string, variant: Variant, dispatchFunctionNa const jsValueRef = `arg${i}.value()`; /** If JavaScript may pass null or undefined */ - const isOptionalToUser = type.flags.nullable || type.flags.optional || "default" in type.flags; + const isOptionalToUser = type.flags.optional || "default" in type.flags; /** If the final representation may include null */ - const isNullable = type.flags.nullable || (type.flags.optional && !("default" in type.flags)); + const isNullable = type.flags.optional && !("default" in type.flags); if (isOptionalToUser) { if (needDeclare) { addHeaderForType(type); cpp.line(`${type.cppName()} ${storageLocation};`); } + const isUndefinedOrNull = type.flags.nonNull ? "isUndefined" : "isUndefinedOrNull"; if (isNullable) { assert(strategy.type === "uses-communication-buffer"); - cpp.line(`if ((${storageLocation}Set = !${jsValueRef}.isUndefinedOrNull())) {`); + cpp.line(`if ((${storageLocation}Set = !${jsValueRef}.${isUndefinedOrNull}())) {`); storageLocation = `${storageLocation}Value`; } else { - cpp.line(`if (!${jsValueRef}.isUndefinedOrNull()) {`); + cpp.line(`if (!${jsValueRef}.${isUndefinedOrNull}()) {`); } cpp.indent(); emitConvertValue(storageLocation, arg.type, jsValueRef, exceptionContext, "assign"); @@ -233,6 +243,9 @@ function emitCppCallToVariant(name: string, variant: Variant, dispatchFunctionNa cpp.line(`${cAbiTypeName(returnStrategy.abiType)} out;`); cpp.line(`if (!${dispatchFunctionName}(`); break; + case "void": + cpp.line(`if (!${dispatchFunctionName}(`); + break; default: throw new Error(`TODO: emitCppCallToVariant for ${inspect(returnStrategy)}`); } @@ -257,6 +270,8 @@ function emitCppCallToVariant(name: string, variant: Variant, dispatchFunctionNa for (const arg of variant.args) { i += 1; + if (arg.type.isIgnoredUndefinedType()) continue; + if (arg.type.isVirtualArgument()) { switch (arg.type.kind) { case "zigVirtualMachine": @@ -300,6 +315,13 @@ function emitCppCallToVariant(name: string, variant: Variant, dispatchFunctionNa } cpp.line(");"); break; + case "void": + cpp.dedent(); + cpp.line(")) {"); + cpp.line(` return {};`); + cpp.line("}"); + cpp.line("return JSC::JSValue::encode(JSC::jsUndefined());"); + break; case "basic-out-param": addCommaAfterArgument(); cpp.add("&out"); @@ -335,7 +357,6 @@ function getSimpleIdlType(type: TypeImpl): string | undefined { const map: { [K in TypeKind]?: string } = { boolean: "WebCore::IDLBoolean", undefined: "WebCore::IDLUndefined", - f64: "WebCore::IDLDouble", usize: "WebCore::IDLUnsignedLongLong", u8: "WebCore::IDLOctet", u16: "WebCore::IDLUnsignedShort", @@ -349,6 +370,11 @@ function getSimpleIdlType(type: TypeImpl): string | undefined { let entry = map[type.kind]; if (!entry) { switch (type.kind) { + case "f64": + entry = type.flags.finite // + ? "WebCore::IDLDouble" + : "WebCore::IDLUnrestrictedDouble"; + break; case "stringEnum": type.lowersToNamedType; // const cType = cAbiTypeForEnum(type.data.length); @@ -361,14 +387,27 @@ function getSimpleIdlType(type: TypeImpl): string | undefined { } if (type.flags.range) { - // TODO: when enforceRange is used, a custom adaptor should be used instead - // of chaining both `WebCore::IDLEnforceRangeAdaptor` and custom logic. - const rangeAdaptor = { - "clamp": "WebCore::IDLClampAdaptor", - "enforce": "WebCore::IDLEnforceRangeAdaptor", - }[type.flags.range[0]]; - assert(rangeAdaptor); - entry = `${rangeAdaptor}<${entry}>`; + const { range, nodeValidator } = type.flags; + if ((range[0] === "enforce" && range[1] !== "abi") || nodeValidator) { + if (nodeValidator) assert(nodeValidator === NodeValidator.validateInteger); // TODO? + + const [abiMin, abiMax] = cAbiIntegerLimits(type.kind as CAbiType); + let [_, min, max] = range as [string, bigint | number | "abi", bigint | number | "abi"]; + if (min === "abi") min = abiMin; + if (max === "abi") max = abiMax; + + headers.add("BindgenCustomEnforceRange.h"); + entry = `Bun::BindgenCustomEnforceRange<${cAbiTypeName(type.kind as CAbiType)}, ${min}, ${max}, Bun::BindgenCustomEnforceRangeKind::${ + nodeValidator ? "Node" : "Web" + }>`; + } else { + const rangeAdaptor = { + "clamp": "WebCore::IDLClampAdaptor", + "enforce": "WebCore::IDLEnforceRangeAdaptor", + }[range[0]]; + assert(rangeAdaptor); + entry = `${rangeAdaptor}<${entry}>`; + } } return entry; @@ -393,29 +432,30 @@ function emitConvertValue( if (simpleType) { const cAbiType = type.canDirectlyMapToCAbi(); assert(cAbiType); - let exceptionHandlerBody; + let exceptionHandler: ExceptionHandler | undefined; + switch (exceptionContext.type) { + case "none": + break; + case "argument": + exceptionHandler = getArgumentExceptionHandler( + type, + exceptionContext.argumentIndex, + exceptionContext.name, + exceptionContext.functionName, + ); + } switch (type.kind) { - case "zigEnum": - case "stringEnum": { - if (exceptionContext.type === "argument") { - const { argumentIndex, name, functionName: quotedFunctionName } = exceptionContext; - exceptionHandlerBody = `WebCore::throwArgumentMustBeEnumError(lexicalGlobalObject, scope, ${argumentIndex}, ${str(name)}_s, ${str(type.name())}_s, ${str(quotedFunctionName)}_s, WebCore::expectedEnumerationValues<${type.cppClassName()}>());`; - } - break; - } } if (decl === "declare") { cpp.add(`${type.cppName()} `); } - let exceptionHandler = exceptionHandlerBody - ? `, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { ${exceptionHandlerBody} }` - : ""; - cpp.line(`${storageLocation} = WebCore::convert<${simpleType}>(*global, ${jsValueRef}${exceptionHandler});`); + let exceptionHandlerText = exceptionHandler ? `, ${exceptionHandler.params} { ${exceptionHandler.body} }` : ""; + cpp.line(`${storageLocation} = WebCore::convert<${simpleType}>(*global, ${jsValueRef}${exceptionHandlerText});`); - if (type.flags.range && type.flags.range[1] !== "abi") { + if (type.flags.range && type.flags.range[0] === "clamp" && type.flags.range[1] !== "abi") { emitRangeModifierCheck(cAbiType, storageLocation, type.flags.range); } @@ -469,6 +509,47 @@ function emitConvertValue( } } +interface ExceptionHandler { + /** @example "[](JSC::JSGlobalObject& global, ThrowScope& scope)" */ + params: string; + /** @example "WebCore::throwTypeError(global, scope)" */ + body: string; +} + +function getArgumentExceptionHandler(type: TypeImpl, argumentIndex: number, name: string, functionName: string) { + const { nodeValidator } = type.flags; + if (nodeValidator) { + switch (nodeValidator) { + case NodeValidator.validateInteger: + headers.add("ErrorCode.h"); + return { + params: `[]()`, + body: `return ${str(name)}_s;`, + }; + default: + throw new Error(`TODO: implement exception thrower for node validator ${nodeValidator}`); + } + } + switch (type.kind) { + case "zigEnum": + case "stringEnum": { + return { + params: `[](JSC::JSGlobalObject& global, JSC::ThrowScope& scope)`, + body: `WebCore::throwArgumentMustBeEnumError(${[ + `global`, + `scope`, + `${argumentIndex}`, + `${str(name)}_s`, + `${str(type.name())}_s`, + `${str(functionName)}_s`, + `WebCore::expectedEnumerationValues<${type.cppClassName()}>()`, + ].join(", ")});`, + }; + break; + } + } +} + /** * The built in WebCore range adaptors do not support arbitrary ranges, but that * is something we want to have. They aren't common, so they are just tacked @@ -483,23 +564,15 @@ function emitRangeModifierCheck( if (kind === "clamp") { cpp.line(`if (${storageLocation} < ${min}) ${storageLocation} = ${min};`); cpp.line(`else if (${storageLocation} > ${max}) ${storageLocation} = ${max};`); - } else if (kind === "enforce") { - cpp.line(`if (${storageLocation} < ${min} || ${storageLocation} > ${max}) {`); - cpp.indent(); - cpp.line( - `throwTypeError(global, throwScope, rangeErrorString<${cAbiTypeName(cAbiType)}>(${storageLocation}, ${min}, ${max}));`, - ); - cpp.line(`return {};`); - cpp.dedent(); - cpp.line(`}`); } else { - throw new Error(`TODO: range modifier ${kind}`); + // Implemented in BindgenCustomEnforceRange + throw new Error(`This should not be called for 'enforceRange' types.`); } } function addHeaderForType(type: TypeImpl) { if (type.lowersToNamedType() && type.ownerFile) { - headers.add(`Generated${cap(type.ownerFile)}.h`); + headers.add(`Generated${pascal(type.ownerFile)}.h`); } } @@ -747,6 +820,7 @@ function zigTypeNameInner(type: TypeImpl): string { function returnStrategyCppType(strategy: ReturnStrategy): string { switch (strategy.type) { case "basic-out-param": + case "void": return "bool"; // true=success, false=exception case "jsvalue": return "JSC::EncodedJSValue"; @@ -760,6 +834,7 @@ function returnStrategyCppType(strategy: ReturnStrategy): string { function returnStrategyZigType(strategy: ReturnStrategy): string { switch (strategy.type) { case "basic-out-param": + case "void": return "bool"; // true=success, false=exception case "jsvalue": return "JSC.JSValue"; @@ -809,6 +884,190 @@ function emitComplexZigDecoder(w: CodeWriter, prefix: string, type: TypeImpl, ch } } +type DistinguishablePrimitive = "undefined" | "string" | "number" | "boolean" | "object"; +type DistinguishStrategy = DistinguishablePrimitive; + +function typeCanDistinguish(t: TypeImpl[]) { + const seen: Record = { + undefined: false, + string: false, + number: false, + boolean: false, + object: false, + }; + let strategies: DistinguishStrategy[] = []; + + for (const type of t) { + let primitive: DistinguishablePrimitive | null = null; + if (type.kind === "undefined") { + primitive = "undefined"; + } else if (type.isStringType()) { + primitive = "string"; + } else if (type.isNumberType()) { + primitive = "number"; + } else if (type.kind === "boolean") { + primitive = "boolean"; + } else if (type.isObjectType()) { + primitive = "object"; + } + if (primitive) { + if (seen[primitive]) { + return null; + } + seen[primitive] = true; + strategies.push(primitive); + continue; + } + return null; // TODO: + } + + return strategies; +} + +/** This is an arbitrary classifier to allow consistent sorting for distinguishing arguments */ +function typeDistinguishmentWeight(type: TypeImpl): number { + if (type.kind === "undefined") { + return 100; + } + + if (type.isObjectType()) { + return 10; + } + + if (type.isStringType()) { + return 5; + } + + if (type.isNumberType()) { + return 3; + } + + if (type.kind === "boolean") { + return -1; + } + + return 0; +} + +function getDistinguishCode(strategy: DistinguishStrategy, type: TypeImpl, value: string) { + switch (strategy) { + case "string": + return { condition: `${value}.isString()`, canThrow: false }; + case "number": + return { condition: `${value}.isNumber()`, canThrow: false }; + case "boolean": + return { condition: `${value}.isBoolean()`, canThrow: false }; + case "object": + return { condition: `${value}.isObject()`, canThrow: false }; + case "undefined": + return { condition: `${value}.isUndefined()`, canThrow: false }; + default: + throw new Error(`TODO: getDistinguishCode for ${strategy}`); + } +} + +/** The variation selector implementation decides which variation dispatch to call. */ +function emitCppVariationSelector(fn: Func, namespaceVar: string) { + let minRequiredArgs = Infinity; + let maxArgs = 0; + + const variationsByArgumentCount = new Map(); + + const pushToList = (argCount: number, vari: Variant) => { + assert(typeof argCount === "number"); + let list = variationsByArgumentCount.get(argCount); + if (!list) { + list = []; + variationsByArgumentCount.set(argCount, list); + } + list.push(vari); + }; + + for (const vari of fn.variants) { + const vmra = vari.minRequiredArgs; + minRequiredArgs = Math.min(minRequiredArgs, vmra); + maxArgs = Math.max(maxArgs, vari.args.length); + const allArgCount = vari.args.filter(arg => !arg.type.isVirtualArgument()).length; + pushToList(vmra, vari); + if (allArgCount != vmra) { + pushToList(allArgCount, vari); + } + } + + cpp.line(`auto& vm = JSC::getVM(global);`); + cpp.line(`auto throwScope = DECLARE_THROW_SCOPE(vm);`); + if (minRequiredArgs > 0) { + cpp.line(`size_t argumentCount = std::min(callFrame->argumentCount(), ${maxArgs});`); + cpp.line(`if (argumentCount < ${minRequiredArgs}) {`); + cpp.line(` return JSC::throwVMError(global, throwScope, createNotEnoughArgumentsError(global));`); + cpp.line(`}`); + } + + const sorted = [...variationsByArgumentCount.entries()] + .map(([key, value]) => ({ argCount: key, variants: value })) + .sort((a, b) => b.argCount - a.argCount); + let argCountI = 0; + for (const { argCount, variants } of sorted) { + argCountI++; + const checkArgCount = argCountI < sorted.length && argCount !== minRequiredArgs; + if (checkArgCount) { + cpp.line(`if (argumentCount >= ${argCount}) {`); + cpp.indent(); + } + + if (variants.length === 1) { + cpp.line(`return ${extInternalDispatchVariant(namespaceVar, fn.name, variants[0].suffix)}(global, callFrame);`); + } else { + let argIndex = 0; + let strategies: DistinguishStrategy[] | null = null; + while (argIndex < argCount) { + strategies = typeCanDistinguish( + variants.map(v => v.args.filter(v => !v.type.isVirtualArgument())[argIndex].type), + ); + if (strategies) { + break; + } + argIndex++; + } + if (!strategies) { + const err = new Error( + `\x1b[0mVariations with ${argCount} required arguments must have at least one argument that can distinguish between them.\n` + + `Variations:\n${variants.map(v => ` ${inspect(v.args.filter(a => !a.type.isVirtualArgument()).map(x => x.type))}`).join("\n")}`, + ); + err.stack = `Error: ${err.message}\n${fn.snapshot}`; + throw err; + } + + const getArgument = minRequiredArgs > 0 ? "uncheckedArgument" : "argument"; + cpp.line(`JSC::JSValue distinguishingValue = callFrame->${getArgument}(${argIndex});`); + const sortedVariants = variants + .map((v, i) => ({ + variant: v, + type: v.args.filter(a => !a.type.isVirtualArgument())[argIndex].type, + strategy: strategies[i], + })) + .sort((a, b) => typeDistinguishmentWeight(a.type) - typeDistinguishmentWeight(b.type)); + for (const { variant: v, strategy: s } of sortedVariants) { + const arg = v.args[argIndex]; + const { condition, canThrow } = getDistinguishCode(s, arg.type, "distinguishingValue"); + cpp.line(`if (${condition}) {`); + cpp.indent(); + cpp.line(`return ${extInternalDispatchVariant(namespaceVar, fn.name, v.suffix)}(global, callFrame);`); + cpp.dedent(); + cpp.line(`}`); + if (canThrow) { + cpp.line(`RETURN_IF_EXCEPTION(throwScope, {});`); + } + } + } + + if (checkArgCount) { + cpp.dedent(); + cpp.line(`}`); + } + } +} + // BEGIN MAIN CODE GENERATION // Search for all .bind.ts files @@ -866,12 +1125,6 @@ zigInternal.indent(); cpp.line("namespace Generated {"); cpp.line(); -cpp.line("template"); -cpp.line("static String rangeErrorString(T value, T min, T max)"); -cpp.line("{"); -cpp.line(` return makeString("Value "_s, value, " is outside the range ["_s, min, ", "_s, max, ']');`); -cpp.line("}"); -cpp.line(); cppInternal.line('// These "Arguments" definitions are for communication between C++ and Zig.'); cppInternal.line('// Field layout depends on implementation details in "bindgen.ts", and'); @@ -953,15 +1206,15 @@ for (const [filename, { functions, typedefs }] of files) { `${pascal(namespaceVar)}${pascal(fn.name)}Arguments${fn.variants.length > 1 ? variNum : ""}`, ); const dispatchName = extDispatchVariant(namespaceVar, fn.name, variNum); + const internalDispatchName = extInternalDispatchVariant(namespaceVar, fn.name, variNum); const args: string[] = []; - let argNum = 0; if (vari.globalObjectArg === "hidden") { args.push("JSC::JSGlobalObject*"); } for (const arg of vari.args) { - argNum += 1; + if (arg.type.isIgnoredUndefinedType()) continue; const strategy = arg.loweringStrategy!; switch (strategy.type) { case "c-abi-pointer": @@ -989,6 +1242,18 @@ for (const [filename, { functions, typedefs }] of files) { cpp.line(`extern "C" ${returnStrategyCppType(vari.returnStrategy!)} ${dispatchName}(${args.join(", ")});`); + if (fn.variants.length > 1) { + // Emit separate variant dispatch functions + cpp.line( + `extern "C" SYSV_ABI JSC::EncodedJSValue ${internalDispatchName}(JSC::JSGlobalObject* global, JSC::CallFrame* callFrame)`, + ); + cpp.line(`{`); + cpp.indent(); + cpp.resetTemporaries(); + emitCppCallToVariant(fn.name, vari, dispatchName); + cpp.dedent(); + cpp.line(`}`); + } variNum += 1; } @@ -1008,7 +1273,7 @@ for (const [filename, { functions, typedefs }] of files) { if (fn.variants.length === 1) { emitCppCallToVariant(fn.name, fn.variants[0], extDispatchVariant(namespaceVar, fn.name, 1)); } else { - throw new Error(`TODO: multiple variant dispatch`); + emitCppVariationSelector(fn, namespaceVar); } cpp.dedent(); @@ -1036,6 +1301,7 @@ for (const [filename, { functions, typedefs }] of files) { } let argNum = 0; for (const arg of vari.args) { + if (arg.type.isIgnoredUndefinedType()) continue; let argName = `arg_${snake(arg.name)}`; if (vari.globalObjectArg === argNum) { if (arg.type.kind !== "globalObject") { @@ -1093,6 +1359,9 @@ for (const [filename, { functions, typedefs }] of files) { case "basic-out-param": zigInternal.add(`out.* = @as(bun.JSError!${returnStrategy.abiType}, `); break; + case "void": + zigInternal.add(`@as(bun.JSError!void, `); + break; } zigInternal.line(`${zid("import_" + namespaceVar)}.${fn.zigPrefix}${fn.name + vari.suffix}(`); @@ -1100,6 +1369,8 @@ for (const [filename, { functions, typedefs }] of files) { for (const arg of vari.args) { const argName = arg.zigMappedName!; + if (arg.type.isIgnoredUndefinedType()) continue; + if (arg.type.isVirtualArgument()) { switch (arg.type.kind) { case "zigVirtualMachine": @@ -1129,7 +1400,7 @@ for (const [filename, { functions, typedefs }] of files) { case "uses-communication-buffer": const prefix = `buf.${snake(arg.name)}`; const type = arg.type; - const isNullable = (type.flags.optional && !("default" in type.flags)) || type.flags.nullable; + const isNullable = type.flags.optional && !("default" in type.flags); if (isNullable) emitNullableZigDecoder(zigInternal, prefix, type, strategy.children); else emitComplexZigDecoder(zigInternal, prefix, type, strategy.children); zigInternal.line(`,`); @@ -1144,6 +1415,7 @@ for (const [filename, { functions, typedefs }] of files) { zigInternal.line(`));`); break; case "basic-out-param": + case "void": zigInternal.line(`)) catch |err| switch (err) {`); zigInternal.line(` error.JSError => return false,`); zigInternal.line(` error.OutOfMemory => ${globalObjectArg}.throwOutOfMemory() catch return false,`); diff --git a/src/codegen/bundle-functions.ts b/src/codegen/bundle-functions.ts index 7756e44afd..0cebe25825 100644 --- a/src/codegen/bundle-functions.ts +++ b/src/codegen/bundle-functions.ts @@ -223,7 +223,9 @@ $$capture_start$$(${fn.async ? "async " : ""}${ define, target: "bun", minify: { syntax: true, whitespace: false }, + throw: true, }); + // TODO: Wait a few versions before removing this if (!build.success) { throw new AggregateError(build.logs, "Failed bundling builtin function " + fn.name + " from " + basename + ".ts"); } diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index b98828c5e5..3427102121 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -412,7 +412,6 @@ writeIfNotChanged( ESM = 5, JSONForObjectLoader = 6, ExportsObject = 7, - // Built in modules are loaded through InternalModuleRegistry by numerical ID. // In this enum are represented as \`(1 << 9) & id\` InternalModuleRegistryFlag = 1 << 9, diff --git a/src/codegen/class-definitions.ts b/src/codegen/class-definitions.ts index fe5944fce4..daf15ed5b5 100644 --- a/src/codegen/class-definitions.ts +++ b/src/codegen/class-definitions.ts @@ -58,8 +58,27 @@ export interface ClassDefinition { values?: string[]; JSType?: string; noConstructor?: boolean; - wantsThis?: boolean; + + // Do not try to track the `this` value in the constructor automatically. + // That is a memory leak. + wantsThis?: never; + + /** + * Called from any thread. + * + * Used for GC. + */ estimatedSize?: boolean; + /** + * Used in heap snapshots. + * + * If true, the class will have a `memoryCost` method that returns the size of the object in bytes. + * + * Unlike estimatedSize, this is always called on the main thread and not used for GC. + * + * If none is provided, we use the struct size. + */ + memoryCost?: boolean; hasPendingActivity?: boolean; isEventEmitter?: boolean; supportsObjectCreate?: boolean; diff --git a/src/codegen/generate-classes.ts b/src/codegen/generate-classes.ts index bc83c72f45..b333caf88b 100644 --- a/src/codegen/generate-classes.ts +++ b/src/codegen/generate-classes.ts @@ -361,12 +361,6 @@ JSC_DECLARE_CUSTOM_GETTER(js${typeName}Constructor); `; } - if (obj.wantsThis) { - externs += ` -extern JSC_CALLCONV void* JSC_HOST_CALL_ATTRIBUTES ${classSymbolName(typeName, "_setThis")}(JSC::JSGlobalObject*, void*, JSC::EncodedJSValue); -`; - } - if (obj.structuredClone) { externs += `extern JSC_CALLCONV void JSC_HOST_CALL_ATTRIBUTES ${symbolName( @@ -652,13 +646,6 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES ${name}::construct(JSC::JSGlobalObj } auto value = JSValue::encode(instance); -${ - obj.wantsThis - ? ` - ${classSymbolName(typeName, "_setThis")}(globalObject, ptr, value); -` - : "" -} RELEASE_AND_RETURN(scope, value); } @@ -1206,7 +1193,7 @@ function generateClassHeader(typeName, obj: ClassDefinition) { [...Object.values(klass), ...Object.values(proto)].find(a => !!a.cache) ? "DECLARE_VISIT_CHILDREN;\ntemplate void visitAdditionalChildren(Visitor&);\nDECLARE_VISIT_OUTPUT_CONSTRAINTS;\n" : ""; - const sizeEstimator = obj.estimatedSize ? "static size_t estimatedSize(JSCell* cell, VM& vm);" : ""; + const sizeEstimator = "static size_t estimatedSize(JSCell* cell, VM& vm);"; var weakOwner = ""; var weakInit = ``; @@ -1291,6 +1278,16 @@ function generateClassHeader(typeName, obj: ClassDefinition) { static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(${name}, m_ctx); } + /** + * Estimated size of the object from Zig including the JS wrapper. + */ + static size_t estimatedSize(JSC::JSCell* cell, JSC::VM& vm); + + /** + * Memory cost of the object from Zig, without necessarily having a JS wrapper alive. + */ + static size_t memoryCost(void* ptr); + void* m_ctx { nullptr }; @@ -1397,6 +1394,8 @@ visitor.reportExtraMemoryVisited(size); DEFINE_VISIT_CHILDREN(${name}); + + template void ${name}::visitAdditionalChildren(Visitor& visitor) { @@ -1473,9 +1472,39 @@ ${name}::~${name}() `; } + if (!obj.estimatedSize && !obj.memoryCost) { + externs += `extern "C" const size_t ${symbolName(typeName, "ZigStructSize")};`; + } else if (obj.memoryCost) { + externs += `extern JSC_CALLCONV size_t ${symbolName(typeName, "memoryCost")}(void* ptr);`; + } + + if (obj.memoryCost) { + output += ` +size_t ${name}::memoryCost(void* ptr) { + return ptr ? ${symbolName(typeName, "memoryCost")}(ptr) : 0; +} +`; + } else if (obj.estimatedSize) { + output += ` +size_t ${name}::memoryCost(void* ptr) { + return ptr ? ${symbolName(typeName, "estimatedSize")}(ptr) : 0; +} + `; + } else { + output += ` +size_t ${name}::memoryCost(void* ptr) { + return ptr ? ${symbolName(typeName, "ZigStructSize")} : 0; +} + `; + } + output += ` - +size_t ${name}::estimatedSize(JSC::JSCell* cell, JSC::VM& vm) { + auto* thisObject = jsCast<${name}*>(cell); + auto* wrapped = thisObject->wrapped(); + return Base::estimatedSize(cell, vm) + ${name}::memoryCost(wrapped); +} void ${name}::destroy(JSCell* cell) { @@ -1539,16 +1568,15 @@ extern JSC_CALLCONV bool JSC_HOST_CALL_ATTRIBUTES ${typeName}__dangerouslySetPtr return true; } - extern "C" const size_t ${typeName}__ptrOffset = ${className(typeName)}::offsetOfWrapped(); void ${name}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) { auto* thisObject = jsCast<${name}*>(cell); if (void* wrapped = thisObject->wrapped()) { - // if (thisObject->scriptExecutionContext()) - // analyzer.setLabelForCell(cell, makeString("url ", thisObject->scriptExecutionContext()->url().string())); + analyzer.setWrappedObjectForCell(cell, wrapped); } + Base::analyzeHeap(cell, analyzer); } @@ -1620,10 +1648,10 @@ function generateZig( construct, finalize, noConstructor = false, - wantsThis = false, overridesToJS = false, estimatedSize, call = false, + memoryCost, values = [], hasPendingActivity = false, structuredClone = false, @@ -1695,6 +1723,15 @@ const JavaScriptCoreBindings = struct { `; + if (memoryCost) { + exports.set("memoryCost", symbolName(typeName, "memoryCost")); + output += ` + pub fn ${symbolName(typeName, "memoryCost")}(thisValue: *${typeName}) callconv(JSC.conv) usize { + return @call(.always_inline, ${typeName}.memoryCost, .{thisValue}); + } + `; + } + if (estimatedSize) { exports.set("estimatedSize", symbolName(typeName, "estimatedSize")); output += ` @@ -1702,6 +1739,10 @@ const JavaScriptCoreBindings = struct { return @call(.always_inline, ${typeName}.estimatedSize, .{thisValue}); } `; + } else if (!memoryCost && !estimatedSize) { + output += ` + export const ${symbolName(typeName, "ZigStructSize")}: usize = @sizeOf(${typeName}); + `; } if (hasPendingActivity) { @@ -1739,16 +1780,6 @@ const JavaScriptCoreBindings = struct { `; } - if (construct && !noConstructor && wantsThis) { - exports.set("_setThis", classSymbolName(typeName, "_setThis")); - output += ` - pub fn ${classSymbolName(typeName, "_setThis")}(globalObject: *JSC.JSGlobalObject, ptr: *anyopaque, this: JSC.JSValue) callconv(JSC.conv) void { - const real: *${typeName} = @ptrCast(@alignCast(ptr)); - real.this_value.set(globalObject, this); - } - `; - } - if (call) { exports.set("call", classSymbolName(typeName, "call")); output += ` @@ -2075,6 +2106,7 @@ const GENERATED_CLASSES_IMPL_HEADER_PRE = ` #include "ZigGeneratedClasses.h" #include "ErrorCode+List.h" #include "ErrorCode.h" +#include #if !OS(WINDOWS) #define JSC_CALLCONV "C" diff --git a/src/codegen/generate-jssink.ts b/src/codegen/generate-jssink.ts index 527ef8caf0..4271cd1212 100644 --- a/src/codegen/generate-jssink.ts +++ b/src/codegen/generate-jssink.ts @@ -105,6 +105,8 @@ function header() { } static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static size_t estimatedSize(JSCell* cell, JSC::VM& vm); + static size_t memoryCost(void* sinkPtr); void ref(); void unref(); @@ -162,6 +164,8 @@ function header() { DECLARE_VISIT_CHILDREN; static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static size_t estimatedSize(JSCell* cell, JSC::VM& vm); + static size_t memoryCost(void* sinkPtr); void* m_sinkPtr; mutable WriteBarrier m_onPull; @@ -187,7 +191,7 @@ JSC_DECLARE_CUSTOM_GETTER(function${name}__getter); const outer = ` // AUTO-GENERATED FILE. DO NOT EDIT. -// Generated by 'make generate-sink' +// Generated by generate-jssink.ts // #pragma once @@ -223,10 +227,7 @@ Structure* createJSSinkControllerStructure(JSC::VM& vm, JSC::JSGlobalObject* glo async function implementation() { const head = ` // AUTO-GENERATED FILE. DO NOT EDIT. -// Generated by 'make generate-sink' -// To regenerate this file, run: -// -// make generate-sink +// Generated by 'generate-jssink.ts' // #include "root.h" #include "headers.h" @@ -284,9 +285,7 @@ extern "C" void Bun__onSinkDestroyed(uintptr_t destructor, void* sinkPtr); namespace WebCore { using namespace JSC; - - - +${classes.map(name => `extern "C" size_t ${name}__memoryCost(void* sinkPtr);`).join("\n")} JSC_DEFINE_HOST_FUNCTION(functionStartDirectStream, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame *callFrame)) { @@ -412,6 +411,28 @@ JSC_DEFINE_CUSTOM_GETTER(function${name}__getter, (JSC::JSGlobalObject * lexical return JSC::JSValue::encode(globalObject->${name}()); } +size_t ${className}::estimatedSize(JSCell* cell, JSC::VM& vm) { + return Base::estimatedSize(cell, vm) + ${className}::memoryCost(jsCast<${className}*>(cell)->wrapped()); +} + +size_t ${className}::memoryCost(void* sinkPtr) { + if (!sinkPtr) + return 0; + + return ${name}__memoryCost(sinkPtr); +} + +size_t ${controller}::memoryCost(void* sinkPtr) { + if (!sinkPtr) + return 0; + + return ${name}__memoryCost(sinkPtr); +} + +size_t ${controller}::estimatedSize(JSCell* cell, JSC::VM& vm) { + return Base::estimatedSize(cell, vm) + ${controller}::memoryCost(jsCast<${controller}*>(cell)->wrapped()); +} + JSC_DECLARE_HOST_FUNCTION(${controller}__close); JSC_DEFINE_HOST_FUNCTION(${controller}__close, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame *callFrame)) { diff --git a/src/compile_target.zig b/src/compile_target.zig index e6d9367fb6..5fee414738 100644 --- a/src/compile_target.zig +++ b/src/compile_target.zig @@ -153,7 +153,7 @@ pub fn downloadToPath(this: *const CompileTarget, env: *bun.DotEnv.Loader, alloc { var progress = refresher.start("Downloading", 0); defer progress.end(); - const http_proxy: ?bun.URL = env.getHttpProxy(url); + const http_proxy: ?bun.URL = env.getHttpProxyFor(url); async_http.* = HTTP.AsyncHTTP.initSync( allocator, diff --git a/src/css/css_internals.zig b/src/css/css_internals.zig index 46b7f133f0..09a86a80a0 100644 --- a/src/css/css_internals.zig +++ b/src/css/css_internals.zig @@ -60,18 +60,19 @@ pub fn testingImpl(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, c const expected = expected_bunstr.toUTF8(bun.default_allocator); defer expected.deinit(); - const options_arg = arguments.nextEat(); + const browser_options_arg = arguments.nextEat(); var log = bun.logger.Log.init(alloc); defer log.deinit(); + var browsers: ?bun.css.targets.Browsers = null; const parser_options = parser_options: { const opts = bun.css.ParserOptions.default(alloc, &log); // if (test_kind == .prefix) break :parser_options opts; - if (options_arg) |optargs| { + if (browser_options_arg) |optargs| { if (optargs.isObject()) { - // minify_options.targets.browsers = targetsFromJS(globalThis, optarg); + browsers = try targetsFromJS(globalThis, optargs); } } @@ -88,11 +89,7 @@ pub fn testingImpl(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, c .result => |stylesheet_| { var stylesheet = stylesheet_; var minify_options: bun.css.MinifyOptions = bun.css.MinifyOptions.default(); - if (options_arg) |optarg| { - if (optarg.isObject()) { - minify_options.targets.browsers = try targetsFromJS(globalThis, optarg); - } - } + minify_options.targets.browsers = browsers; _ = stylesheet.minify(alloc, minify_options).assert(); const result = stylesheet.toCss(alloc, bun.css.PrinterOptions{ diff --git a/src/css/css_parser.zig b/src/css/css_parser.zig index 40ceaa2966..306b4d4dcd 100644 --- a/src/css/css_parser.zig +++ b/src/css/css_parser.zig @@ -186,8 +186,14 @@ pub const VendorPrefix = packed struct(u8) { } /// Returns VendorPrefix::None if empty. - pub fn orNone(this: VendorPrefix) VendorPrefix { - return this.bitwiseOr(VendorPrefix{ .none = true }); + pub inline fn orNone(this: VendorPrefix) VendorPrefix { + return this._or(VendorPrefix{ .none = true }); + } + + /// **WARNING**: NOT THE SAME as .bitwiseOr!! + pub inline fn _or(this: VendorPrefix, other: VendorPrefix) VendorPrefix { + if (this.isEmpty()) return other; + return this; } }; diff --git a/src/css/declaration.zig b/src/css/declaration.zig index f84c4f9c11..a7ec7e1182 100644 --- a/src/css/declaration.zig +++ b/src/css/declaration.zig @@ -47,7 +47,7 @@ pub const DeclarationBlock = struct { var arraylist = ArrayList(u8){}; const w = arraylist.writer(bun.default_allocator); defer arraylist.deinit(bun.default_allocator); - var printer = css.Printer(@TypeOf(w)).new(bun.default_allocator, std.ArrayList(u8).init(bun.default_allocator), w, .{}, null); + var printer = css.Printer(@TypeOf(w)).new(bun.default_allocator, std.ArrayList(u8).init(bun.default_allocator), w, css.PrinterOptions.default(), null); defer printer.deinit(); this.self.toCss(@TypeOf(w), &printer) catch |e| return try writer.print("\n", .{@errorName(e)}); try writer.writeAll(arraylist.items); diff --git a/src/css/dependencies.zig b/src/css/dependencies.zig index 7d922244dd..453f9cefb3 100644 --- a/src/css/dependencies.zig +++ b/src/css/dependencies.zig @@ -70,7 +70,7 @@ pub const ImportDependency = struct { allocator, css.css_rules.supports.SupportsCondition, supports, - css.PrinterOptions{}, + css.PrinterOptions.default(), null, ) catch bun.Output.panic( "Unreachable code: failed to stringify SupportsCondition.\n\nThis is a bug in Bun's CSS printer. Please file a bug report at https://github.com/oven-sh/bun/issues/new/choose", @@ -80,7 +80,7 @@ pub const ImportDependency = struct { } else null; const media = if (rule.media.media_queries.items.len > 0) media: { - const s = css.to_css.string(allocator, css.MediaList, &rule.media, css.PrinterOptions{}, null) catch bun.Output.panic( + const s = css.to_css.string(allocator, css.MediaList, &rule.media, css.PrinterOptions.default(), null) catch bun.Output.panic( "Unreachable code: failed to stringify MediaList.\n\nThis is a bug in Bun's CSS printer. Please file a bug report at https://github.com/oven-sh/bun/issues/new/choose", .{}, ); diff --git a/src/css/printer.zig b/src/css/printer.zig index 9d7b029e2a..c50adb3bcd 100644 --- a/src/css/printer.zig +++ b/src/css/printer.zig @@ -26,7 +26,7 @@ pub const PrinterOptions = struct { /// An optional project root path, used to generate relative paths for sources used in CSS module hashes. project_root: ?[]const u8 = null, /// Targets to output the CSS for. - targets: Targets = .{}, + targets: Targets, /// Whether to analyze dependencies (i.e. `@import` and `url()`). /// If true, the dependencies are returned as part of the /// [ToCssResult](super::stylesheet::ToCssResult). @@ -39,6 +39,23 @@ pub const PrinterOptions = struct { /// from JavaScript. Useful for polyfills, for example. pseudo_classes: ?PseudoClasses = null, public_path: []const u8 = "", + + pub fn default() PrinterOptions { + return .{ + .targets = Targets{ + .browsers = null, + }, + }; + } + + pub fn defaultWithMinify(minify: bool) PrinterOptions { + return .{ + .targets = Targets{ + .browsers = null, + }, + .minify = minify, + }; + } }; /// A mapping of user action pseudo classes to replace with class names. diff --git a/src/css/properties/custom.zig b/src/css/properties/custom.zig index eaa7ad2f89..cfde458ae0 100644 --- a/src/css/properties/custom.zig +++ b/src/css/properties/custom.zig @@ -1433,6 +1433,10 @@ pub const UnparsedProperty = struct { pub fn deepClone(this: *const @This(), allocator: Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A CSS custom property, representing any unknown property. diff --git a/src/css/properties/generate_properties.ts b/src/css/properties/generate_properties.ts index e7f79c1e2e..f55f04d022 100644 --- a/src/css/properties/generate_properties.ts +++ b/src/css/properties/generate_properties.ts @@ -287,7 +287,8 @@ function generatePropertyImpl(property_defs: Record): strin return `.${escapeIdent(name)} => |*v| css.generic.eql(${meta.ty}, v, &rhs.${escapeIdent(name)}),`; }) .join("\n")} - .all, .unparsed => true, + .unparsed => |*u| u.eql(&rhs.unparsed), + .all => true, .custom => |*c| c.eql(&rhs.custom), }; } @@ -1077,6 +1078,7 @@ generateCode({ "margin-right": { ty: "LengthPercentageOrAuto", logical_group: { ty: "margin", category: "physical" }, + eval_branch_quota: 5000, }, "margin-block-start": { ty: "LengthPercentageOrAuto", diff --git a/src/css/properties/properties_generated.zig b/src/css/properties/properties_generated.zig index be76ee77a7..46615f071a 100644 --- a/src/css/properties/properties_generated.zig +++ b/src/css/properties/properties_generated.zig @@ -6951,7 +6951,8 @@ pub const Property = union(PropertyIdTag) { .@"mask-box-image-width" => |*v| css.generic.eql(Rect(BorderImageSideWidth), &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-width"[1]), .@"mask-box-image-outset" => |*v| css.generic.eql(Rect(LengthOrNumber), &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-outset"[1]), .@"mask-box-image-repeat" => |*v| css.generic.eql(BorderImageRepeat, &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-repeat"[1]), - .all, .unparsed => true, + .unparsed => |*u| u.eql(&rhs.unparsed), + .all => true, .custom => |*c| c.eql(&rhs.custom), }; } diff --git a/src/css/properties/properties_impl.zig b/src/css/properties/properties_impl.zig index 6a56e20d0e..871a61cdd3 100644 --- a/src/css/properties/properties_impl.zig +++ b/src/css/properties/properties_impl.zig @@ -20,6 +20,7 @@ pub fn PropertyIdImpl() type { var first = true; const name = this.name(this); const prefix_value = this.prefix().orNone(); + inline for (VendorPrefix.FIELDS) |field| { if (@field(prefix_value, field)) { var prefix: VendorPrefix = .{}; diff --git a/src/css/properties/transform.zig b/src/css/properties/transform.zig index bcc9ebce99..8a1c879798 100644 --- a/src/css/properties/transform.zig +++ b/src/css/properties/transform.zig @@ -77,9 +77,7 @@ pub const TransformList = struct { dest.allocator, scratchbuf, base_writer, - css.PrinterOptions{ - .minify = true, - }, + css.PrinterOptions.defaultWithMinify(true), dest.import_records, ); defer p.deinit(); diff --git a/src/css/rules/rules.zig b/src/css/rules/rules.zig index a66ebd6cd2..951a302c92 100644 --- a/src/css/rules/rules.zig +++ b/src/css/rules/rules.zig @@ -290,8 +290,6 @@ pub fn CssRuleList(comptime AtRule: type) type { // Attempt to merge the new rule with the last rule we added. var merged = false; - const ZACK_REMOVE_THIS = false; - _ = ZACK_REMOVE_THIS; // autofix if (rules.items.len > 0 and rules.items[rules.items.len - 1] == .style) { const last_style_rule = &rules.items[rules.items.len - 1].style; if (mergeStyleRules(AtRule, sty, last_style_rule, context)) { diff --git a/src/css/selectors/parser.zig b/src/css/selectors/parser.zig index c545c826b9..bcd01753cc 100644 --- a/src/css/selectors/parser.zig +++ b/src/css/selectors/parser.zig @@ -927,7 +927,7 @@ pub const PseudoClass = union(enum) { const writer = s.writer(dest.allocator); const W2 = @TypeOf(writer); const scratchbuf = std.ArrayList(u8).init(dest.allocator); - var printer = Printer(W2).new(dest.allocator, scratchbuf, writer, css.PrinterOptions{}, dest.import_records); + var printer = Printer(W2).new(dest.allocator, scratchbuf, writer, css.PrinterOptions.default(), dest.import_records); try serialize.serializePseudoClass(this, W2, &printer, null); return dest.writeStr(s.items); } @@ -1059,10 +1059,13 @@ pub const SelectorParser = struct { } /// Whether the given function name is an alias for the `:is()` function. - fn parseAnyPrefix(this: *const SelectorParser, name: []const u8) ?css.VendorPrefix { - _ = this; // autofix - _ = name; // autofix - return null; + fn parseAnyPrefix(_: *const SelectorParser, name: []const u8) ?css.VendorPrefix { + const Map = comptime bun.ComptimeStringMap(css.VendorPrefix, .{ + .{ "-webkit-any", css.VendorPrefix{ .webkit = true } }, + .{ "-moz-any", css.VendorPrefix{ .moz = true } }, + }); + + return Map.getAnyCase(name); } pub fn parseNonTsPseudoClass( @@ -1287,6 +1290,10 @@ pub const SelectorParser = struct { return .{ .result = pseudo_class }; } + pub fn parseHost(_: *SelectorParser) bool { + return true; + } + pub fn parseNonTsFunctionalPseudoClass( this: *SelectorParser, name: []const u8, @@ -1689,7 +1696,7 @@ pub fn GenericSelector(comptime Impl: type) type { var arraylist = ArrayList(u8){}; const w = arraylist.writer(bun.default_allocator); defer arraylist.deinit(bun.default_allocator); - var printer = css.Printer(@TypeOf(w)).new(bun.default_allocator, std.ArrayList(u8).init(bun.default_allocator), w, .{}, null); + var printer = css.Printer(@TypeOf(w)).new(bun.default_allocator, std.ArrayList(u8).init(bun.default_allocator), w, css.PrinterOptions.default(), null); defer printer.deinit(); css.selector.tocss_servo.toCss_Selector(this.this, @TypeOf(w), &printer) catch |e| return try writer.print("\n", .{@errorName(e)}); try writer.writeAll(arraylist.items); @@ -2544,7 +2551,7 @@ pub const PseudoElement = union(enum) { const writer = s.writer(dest.allocator); const W2 = @TypeOf(writer); const scratchbuf = std.ArrayList(u8).init(dest.allocator); - var printer = Printer(W2).new(dest.allocator, scratchbuf, writer, css.PrinterOptions{}, dest.import_records); + var printer = Printer(W2).new(dest.allocator, scratchbuf, writer, css.PrinterOptions.default(), dest.import_records); try serialize.serializePseudoElement(this, W2, &printer, null); return dest.writeStr(s.items); } @@ -2689,6 +2696,7 @@ pub fn parse_one_simple_selector( const S = SimpleSelectorParseResult(Impl); const start = input.state(); + const token_location = input.currentSourceLocation(); const token = switch (input.nextIncludingWhitespace()) { .result => |v| v.*, .err => { @@ -2700,7 +2708,7 @@ pub fn parse_one_simple_selector( switch (token) { .idhash => |id| { if (state.intersects(SelectorParsingState.AFTER_PSEUDO)) { - return .{ .err = input.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.invalid_state)) }; + return .{ .err = token_location.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.{ .unexpected_selector_after_pseudo_element = .{ .idhash = id } })) }; } const component: GenericComponent(Impl) = .{ .id = .{ .v = id } }; return .{ .result = S{ @@ -2709,18 +2717,20 @@ pub fn parse_one_simple_selector( }, .open_square => { if (state.intersects(SelectorParsingState.AFTER_PSEUDO)) { - return .{ .err = input.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.invalid_state)) }; + return .{ .err = token_location.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.{ .unexpected_selector_after_pseudo_element = .open_square })) }; } const Closure = struct { parser: *SelectorParser, - pub fn parsefn(this: *@This(), input2: *css.Parser) Result(GenericComponent(Impl)) { - return parse_attribute_selector(Impl, this.parser, input2); - } }; var closure = Closure{ .parser = parser, }; - const attr = switch (input.parseNestedBlock(GenericComponent(Impl), &closure, Closure.parsefn)) { + const attr = switch (input.parseNestedBlock(GenericComponent(Impl), &closure, struct { + pub fn parsefn(this: *Closure, input2: *css.Parser) Result(GenericComponent(Impl)) { + return parse_attribute_selector(Impl, this.parser, input2); + } + } + .parsefn)) { .err => |e| return .{ .err = e }, .result => |v| v, }; @@ -2878,7 +2888,7 @@ pub fn parse_one_simple_selector( switch (d) { '.' => { if (state.intersects(SelectorParsingState.AFTER_PSEUDO)) { - return .{ .err = input.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.invalid_state)) }; + return .{ .err = token_location.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.{ .unexpected_selector_after_pseudo_element = .{ .delim = '.' } })) }; } const location = input.currentSourceLocation(); const class = switch ((switch (input.nextIncludingWhitespace()) { @@ -3167,6 +3177,9 @@ pub fn parse_functional_pseudo_class( return .{ .result = .{ .non_ts_pseudo_class = result } }; } +const TreeStructuralPseudoClass = enum { @"first-child", @"last-child", @"only-child", root, empty, scope, host, @"first-of-type", @"last-of-type", @"only-of-type" }; +const TreeStructuralPseudoClassMap = bun.ComptimeEnumMap(TreeStructuralPseudoClass); + pub fn parse_simple_pseudo_class( comptime Impl: type, parser: *SelectorParser, @@ -3179,28 +3192,20 @@ pub fn parse_simple_pseudo_class( } if (state.allowsTreeStructuralPseudoClasses()) { - // css.todo_stuff.match_ignore_ascii_case - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "first-child")) { - return .{ .result = .{ .nth = NthSelectorData.first(false) } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "last-child")) { - return .{ .result = .{ .nth = NthSelectorData.last(false) } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "only-child")) { - return .{ .result = .{ .nth = NthSelectorData.only(false) } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "root")) { - return .{ .result = .root }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "empty")) { - return .{ .result = .empty }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "scope")) { - return .{ .result = .scope }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "host")) { - return .{ .result = .{ .host = null } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "first-of-type")) { - return .{ .result = .{ .nth = NthSelectorData.first(true) } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "last-of-type")) { - return .{ .result = .{ .nth = NthSelectorData.last(true) } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "only-of-type")) { - return .{ .result = .{ .nth = NthSelectorData.only(true) } }; - } else {} + if (TreeStructuralPseudoClassMap.getAnyCase(name)) |pseudo_class| { + switch (pseudo_class) { + .@"first-child" => return .{ .result = .{ .nth = NthSelectorData.first(false) } }, + .@"last-child" => return .{ .result = .{ .nth = NthSelectorData.last(false) } }, + .@"only-child" => return .{ .result = .{ .nth = NthSelectorData.only(false) } }, + .root => return .{ .result = .root }, + .empty => return .{ .result = .empty }, + .scope => return .{ .result = .scope }, + .host => if (parser.parseHost()) return .{ .result = .{ .host = null } }, + .@"first-of-type" => return .{ .result = .{ .nth = NthSelectorData.first(true) } }, + .@"last-of-type" => return .{ .result = .{ .nth = NthSelectorData.last(true) } }, + .@"only-of-type" => return .{ .result = .{ .nth = NthSelectorData.only(true) } }, + } + } } // The view-transition pseudo elements accept the :only-child pseudo class. diff --git a/src/css/selectors/selector.zig b/src/css/selectors/selector.zig index 84364080ae..6388630930 100644 --- a/src/css/selectors/selector.zig +++ b/src/css/selectors/selector.zig @@ -708,7 +708,7 @@ pub const serialize = struct { const writer = id.writer(); css.serializer.serializeIdentifier(v.value, writer) catch return dest.addFmtError(); - const s = try css.to_css.string(dest.allocator, CSSString, &v.value, css.PrinterOptions{}, dest.import_records); + const s = try css.to_css.string(dest.allocator, CSSString, &v.value, css.PrinterOptions.default(), dest.import_records); if (id.items.len > 0 and id.items.len < s.len) { try dest.writeStr(id.items); @@ -748,7 +748,7 @@ pub const serialize = struct { try dest.writeStr(":not("); }, .any => |v| { - const vp = dest.vendor_prefix.bitwiseOr(v.vendor_prefix); + const vp = dest.vendor_prefix._or(v.vendor_prefix); if (vp.intersects(css.VendorPrefix{ .webkit = true, .moz = true })) { try dest.writeChar(':'); try vp.toCss(W, dest); @@ -1039,6 +1039,7 @@ pub const serialize = struct { // If the printer has a vendor prefix override, use that. const vp = if (!d.vendor_prefix.isEmpty()) d.vendor_prefix.bitwiseAnd(prefix).orNone() else prefix; try vp.toCss(W, d); + debug("VENDOR PREFIX {d} OVERRIDE {d}", .{ vp.asBits(), d.vendor_prefix.asBits() }); return vp; } diff --git a/src/css/targets.zig b/src/css/targets.zig index ab720f8304..c5fe76f214 100644 --- a/src/css/targets.zig +++ b/src/css/targets.zig @@ -17,6 +17,27 @@ pub const Targets = struct { /// Features that should never be compiled, even when unsupported by targets. exclude: Features = .{}, + /// Set a sane default for bundler + pub fn browserDefault() Targets { + return .{ + .browsers = Browsers.browserDefault, + }; + } + + /// Set a sane default for bundler + pub fn runtimeDefault() Targets { + return .{ + .browsers = null, + }; + } + + pub fn forBundlerTarget(target: bun.transpiler.options.Target) Targets { + return switch (target) { + .node, .bun => runtimeDefault(), + .browser, .bun_macro, .bake_server_components_ssr => browserDefault(), + }; + } + pub fn prefixes(this: *const Targets, prefix: css.VendorPrefix, feature: css.prefixes.Feature) css.VendorPrefix { if (prefix.contains(css.VendorPrefix{ .none = true }) and !this.exclude.contains(css.targets.Features{ .vendor_prefixes = true })) { if (this.include.contains(css.targets.Features{ .vendor_prefixes = true })) { @@ -125,8 +146,155 @@ pub const Features = packed struct(u32) { }; pub fn BrowsersImpl(comptime T: type) type { - _ = T; // autofix - return struct {}; + return struct { + pub const browserDefault = convertFromString(&.{ + "es2020", // support import.meta.url + "edge88", + "firefox78", + "chrome87", + "safari14", + }) catch |e| std.debug.panic("WOOPSIE: {s}\n", .{@errorName(e)}); + + // pub const bundlerDefault = T{ + // .chrome = 80 << 16, + // .edge = 80 << 16, + // .firefox = 78 << 16, + // .safari = 14 << 16, + // .opera = 67 << 16, + // }; + + pub fn convertFromString(esbuild_target: []const []const u8) anyerror!T { + var browsers: T = .{}; + + for (esbuild_target) |str| { + var entries_buf: [5][]const u8 = undefined; + const entries_without_es: [][]const u8 = entries_without_es: { + if (str.len <= 2 or !(str[0] == 'e' and str[1] == 's')) { + entries_buf[0] = str; + break :entries_without_es entries_buf[0..1]; + } + + const number_part = str[2..]; + const year = try std.fmt.parseInt(u16, number_part, 10); + switch (year) { + // https://caniuse.com/?search=es2015 + 2015 => { + entries_buf[0..5].* = .{ "chrome49", "edge13", "safari10", "firefox44", "opera36" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2016 + 2016 => { + entries_buf[0..5].* = .{ "chrome50", "edge13", "safari10", "firefox43", "opera37" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2017 + 2017 => { + entries_buf[0..5].* = .{ "chrome58", "edge15", "safari11", "firefox52", "opera45" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2018 + 2018 => { + entries_buf[0..5].* = .{ "chrome63", "edge79", "safari12", "firefox58", "opera50" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2019 + 2019 => { + entries_buf[0..5].* = .{ "chrome73", "edge79", "safari12.1", "firefox64", "opera60" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2020 + 2020 => { + entries_buf[0..5].* = .{ "chrome80", "edge80", "safari14.1", "firefox80", "opera67" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2021 + 2021 => { + entries_buf[0..5].* = .{ "chrome85", "edge85", "safari14.1", "firefox80", "opera71" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2022 + 2022 => { + entries_buf[0..5].* = .{ "chrome94", "edge94", "safari16.4", "firefox93", "opera80" }; + break :entries_without_es entries_buf[0..5]; + }, + // https://caniuse.com/?search=es2023 + 2023 => { + entries_buf[0..4].* = .{ "chrome110", "edge110", "safari16.4", "opera96" }; + break :entries_without_es entries_buf[0..4]; + }, + else => { + if (@inComptime()) { + @compileLog("Invalid target: " ++ str); + } + return error.UnsupportedCSSTarget; + }, + } + }; + + for_loop: for (entries_without_es) |entry| { + if (bun.strings.eql(entry, "esnext")) continue; + const maybe_idx: ?usize = maybe_idx: { + for (entry, 0..) |c, i| { + if (std.ascii.isDigit(c)) break :maybe_idx i; + } + break :maybe_idx null; + }; + + if (maybe_idx) |idx| { + const Browser = enum { + chrome, + edge, + firefox, + ie, + ios_saf, + opera, + safari, + no_mapping, + }; + const Map = bun.ComptimeStringMap(Browser, .{ + .{ "chrome", Browser.chrome }, + .{ "edge", Browser.edge }, + .{ "firefox", Browser.firefox }, + .{ "hermes", Browser.no_mapping }, + .{ "ie", Browser.ie }, + .{ "ios", Browser.ios_saf }, + .{ "node", Browser.no_mapping }, + .{ "opera", Browser.opera }, + .{ "rhino", Browser.no_mapping }, + .{ "safari", Browser.safari }, + }); + const browser = Map.get(entry[0..idx]); + if (browser == null or browser.? == .no_mapping) continue; // No mapping available + + const major, const minor = major_minor: { + const version_str = entry[idx..]; + const dot_index = std.mem.indexOfScalar(u8, version_str, '.') orelse version_str.len; + const major = std.fmt.parseInt(u16, version_str[0..dot_index], 10) catch continue; + const minor = if (dot_index < version_str.len) + std.fmt.parseInt(u16, version_str[dot_index + 1 ..], 10) catch 0 + else + 0; + break :major_minor .{ major, minor }; + }; + + const version: u32 = (@as(u32, major) << 16) | @as(u32, minor << 8); + switch (browser.?) { + inline else => |browser_name| { + if (@field(browsers, @tagName(browser_name)) == null or + version < @field(browsers, @tagName(browser_name)).?) + { + @field(browsers, @tagName(browser_name)) = version; + } + continue :for_loop; + }, + } + } + } + } + + return browsers; + } + }; } pub fn FeaturesImpl(comptime T: type) type { diff --git a/src/css/values/color_js.zig b/src/css/values/color_js.zig index 052d92ed70..d1dd0f6abc 100644 --- a/src/css/values/color_js.zig +++ b/src/css/values/color_js.zig @@ -426,7 +426,7 @@ pub fn jsFunctionColor(globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFram allocator, std.ArrayList(u8).init(allocator), writer, - .{}, + css.PrinterOptions.default(), null, ); diff --git a/src/darwin_c.zig b/src/darwin_c.zig index 7fb07e64d9..b13c73ff9d 100644 --- a/src/darwin_c.zig +++ b/src/darwin_c.zig @@ -66,6 +66,8 @@ pub const COPYFILE_CONTINUE = @as(c_int, 0); pub const COPYFILE_SKIP = @as(c_int, 1); pub const COPYFILE_QUIT = @as(c_int, 2); +pub extern "C" fn memmem(haystack: [*]const u8, haystacklen: usize, needle: [*]const u8, needlelen: usize) ?[*]const u8; + // int clonefileat(int src_dirfd, const char * src, int dst_dirfd, const char * dst, int flags); pub extern "c" fn clonefileat(c_int, [*:0]const u8, c_int, [*:0]const u8, uint32_t: c_int) c_int; // int fclonefileat(int srcfd, int dst_dirfd, const char * dst, int flags); @@ -652,9 +654,6 @@ pub extern fn host_processor_info(host: std.c.host_t, flavor: processor_flavor_t pub extern fn getuid(...) std.posix.uid_t; pub extern fn getgid(...) std.posix.gid_t; -pub extern fn get_process_priority(pid: c_uint) i32; -pub extern fn set_process_priority(pid: c_uint, priority: c_int) i32; - pub fn get_version(buf: []u8) []const u8 { @memset(buf, 0); diff --git a/src/deps/libuv.zig b/src/deps/libuv.zig index 0940fdf3de..071b1862b1 100644 --- a/src/deps/libuv.zig +++ b/src/deps/libuv.zig @@ -2327,7 +2327,7 @@ pub const uv_rusage_t = extern struct { ru_nivcsw: u64, }; pub extern fn uv_getrusage(rusage: [*c]uv_rusage_t) c_int; -pub extern fn uv_os_homedir(buffer: [*]u8, size: [*c]usize) c_int; +pub extern fn uv_os_homedir(buffer: [*]u8, size: *usize) ReturnCode; pub extern fn uv_os_tmpdir(buffer: [*]u8, size: [*c]usize) c_int; pub extern fn uv_os_get_passwd(pwd: [*c]uv_passwd_t) c_int; pub extern fn uv_os_free_passwd(pwd: [*c]uv_passwd_t) void; diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index b683362431..d3aafb25af 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -17,6 +17,9 @@ static inline std::string_view stringViewFromC(const char* message, size_t lengt return std::string_view(); } +using TLSWebSocket = uWS::WebSocket; +using TCPWebSocket = uWS::WebSocket; + extern "C" { @@ -722,12 +725,12 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return *uws->getUserData(); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return *uws->getUserData(); } @@ -735,14 +738,14 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; uws->close(); } else { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; uws->close(); } } @@ -752,13 +755,13 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return (uws_sendstatus_t)uws->send(stringViewFromC(message, length), (uWS::OpCode)(unsigned char)opcode); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return (uws_sendstatus_t)uws->send(stringViewFromC(message, length), (uWS::OpCode)(unsigned char)opcode); } @@ -770,8 +773,8 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return (uws_sendstatus_t)uws->send(stringViewFromC(message, length), (uWS::OpCode)(unsigned char)opcode, compress, fin); @@ -779,8 +782,8 @@ extern "C" else { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return (uws_sendstatus_t)uws->send(stringViewFromC(message, length), (uWS::OpCode)(unsigned char)opcode, compress, fin); @@ -793,13 +796,13 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return (uws_sendstatus_t)uws->sendFragment( stringViewFromC(message, length), compress); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return (uws_sendstatus_t)uws->sendFragment(stringViewFromC(message, length), compress); } @@ -809,13 +812,13 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return (uws_sendstatus_t)uws->sendFirstFragment( stringViewFromC(message, length), uWS::OpCode::BINARY, compress); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return (uws_sendstatus_t)uws->sendFirstFragment( stringViewFromC(message, length), uWS::OpCode::BINARY, compress); } @@ -826,14 +829,14 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return (uws_sendstatus_t)uws->sendFirstFragment( stringViewFromC(message, length), (uWS::OpCode)(unsigned char)opcode, compress); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return (uws_sendstatus_t)uws->sendFirstFragment( stringViewFromC(message, length), (uWS::OpCode)(unsigned char)opcode, compress); @@ -844,13 +847,13 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return (uws_sendstatus_t)uws->sendLastFragment( stringViewFromC(message, length), compress); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return (uws_sendstatus_t)uws->sendLastFragment( stringViewFromC(message, length), compress); } @@ -860,14 +863,14 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; uws->end(code, stringViewFromC(message, length)); } else { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; uws->end(code, stringViewFromC(message, length)); } } @@ -877,15 +880,15 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; uws->cork([handler, user_data]() { handler(user_data); }); } else { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; uws->cork([handler, user_data]() { handler(user_data); }); @@ -896,12 +899,12 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return uws->subscribe(stringViewFromC(topic, length)); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return uws->subscribe(stringViewFromC(topic, length)); } bool uws_ws_unsubscribe(int ssl, uws_websocket_t *ws, const char *topic, @@ -909,12 +912,12 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return uws->unsubscribe(stringViewFromC(topic, length)); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return uws->unsubscribe(stringViewFromC(topic, length)); } @@ -923,12 +926,12 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return uws->isSubscribed(stringViewFromC(topic, length)); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return uws->isSubscribed(stringViewFromC(topic, length)); } void uws_ws_iterate_topics(int ssl, uws_websocket_t *ws, @@ -938,15 +941,15 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; uws->iterateTopics([callback, user_data](auto topic) { callback(topic.data(), topic.length(), user_data); }); } else { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; uws->iterateTopics([callback, user_data](auto topic) { callback(topic.data(), topic.length(), user_data); }); @@ -959,13 +962,13 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return uws->publish(stringViewFromC(topic, topic_length), stringViewFromC(message, message_length)); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return uws->publish(stringViewFromC(topic, topic_length), stringViewFromC(message, message_length)); } @@ -977,14 +980,14 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return uws->publish(stringViewFromC(topic, topic_length), stringViewFromC(message, message_length), (uWS::OpCode)(unsigned char)opcode, compress); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return uws->publish(stringViewFromC(topic, topic_length), stringViewFromC(message, message_length), (uWS::OpCode)(unsigned char)opcode, compress); @@ -994,12 +997,12 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; return uws->getBufferedAmount(); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; return uws->getBufferedAmount(); } @@ -1008,14 +1011,14 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; std::string_view value = uws->getRemoteAddress(); *dest = value.data(); return value.length(); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; std::string_view value = uws->getRemoteAddress(); *dest = value.data(); @@ -1027,15 +1030,15 @@ extern "C" { if (ssl) { - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TLSWebSocket *uws = + (TLSWebSocket *)ws; std::string_view value = uws->getRemoteAddressAsText(); *dest = value.data(); return value.length(); } - uWS::WebSocket *uws = - (uWS::WebSocket *)ws; + TCPWebSocket *uws = + (TCPWebSocket *)ws; std::string_view value = uws->getRemoteAddressAsText(); *dest = value.data(); @@ -1714,6 +1717,14 @@ __attribute__((callback (corker, ctx))) } } + size_t uws_ws_memory_cost(int ssl, uws_websocket_t *ws) { + if (ssl) { + return ((TLSWebSocket*)ws)->memoryCost(); + } else { + return ((TCPWebSocket*)ws)->memoryCost(); + } + } + void us_socket_sendfile_needs_more(us_socket_r s) { s->context->loop->data.last_write_failed = 1; us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); diff --git a/src/deps/lol-html.zig b/src/deps/lol-html.zig index d5e747c773..303588edc0 100644 --- a/src/deps/lol-html.zig +++ b/src/deps/lol-html.zig @@ -33,7 +33,9 @@ pub const HTMLRewriter = opaque { pub fn write(rewriter: *HTMLRewriter, chunk: []const u8) Error!void { auto_disable(); - if (rewriter.lol_html_rewriter_write(ptrWithoutPanic(chunk), chunk.len) < 0) + const ptr = ptrWithoutPanic(chunk); + const rc = rewriter.lol_html_rewriter_write(ptr, chunk.len); + if (rc < 0) return error.Fail; } @@ -207,17 +209,17 @@ pub const HTMLRewriter = opaque { auto_disable(); return switch (builder.lol_html_rewriter_builder_add_element_content_handlers( selector, - if (element_handler_data != null) + if (element_handler != null and element_handler_data != null) DirectiveHandler(Element, ElementHandler, element_handler.?) else null, element_handler_data, - if (comment_handler_data != null) + if (comment_handler != null and comment_handler_data != null) DirectiveHandler(Comment, CommentHandler, comment_handler.?) else null, comment_handler_data, - if (text_chunk_handler_data != null) + if (text_chunk_handler != null and text_chunk_handler_data != null) DirectiveHandler(TextChunk, TextChunkHandler, text_chunk_handler.?) else null, @@ -794,6 +796,8 @@ pub const DocType = opaque { extern fn lol_html_doctype_system_id_get(doctype: *const DocType) HTMLString; extern fn lol_html_doctype_user_data_set(doctype: *const DocType, user_data: ?*anyopaque) void; extern fn lol_html_doctype_user_data_get(doctype: *const DocType) ?*anyopaque; + extern fn lol_html_doctype_remove(doctype: *DocType) void; + extern fn lol_html_doctype_is_removed(doctype: *const DocType) bool; pub const Callback = *const fn (*DocType, ?*anyopaque) callconv(.C) Directive; @@ -809,6 +813,14 @@ pub const DocType = opaque { auto_disable(); return this.lol_html_doctype_system_id_get(); } + pub fn remove(this: *DocType) void { + auto_disable(); + return this.lol_html_doctype_remove(); + } + pub fn isRemoved(this: *const DocType) bool { + auto_disable(); + return this.lol_html_doctype_is_removed(); + } }; pub const Encoding = enum { diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 183bbacfb1..044149b731 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -2884,6 +2884,13 @@ pub const AnyWebSocket = union(enum) { }; } + pub fn memoryCost(this: AnyWebSocket) usize { + return switch (this) { + .ssl => this.ssl.memoryCost(), + .tcp => this.tcp.memoryCost(), + }; + } + pub fn close(this: AnyWebSocket) void { const ssl_flag = @intFromBool(this == .ssl); return uws_ws_close(ssl_flag, this.raw()); @@ -2974,7 +2981,13 @@ pub const AnyWebSocket = union(enum) { } }; -pub const RawWebSocket = opaque {}; +pub const RawWebSocket = opaque { + pub fn memoryCost(this: *RawWebSocket, ssl_flag: i32) usize { + return uws_ws_memory_cost(ssl_flag, this); + } + + extern fn uws_ws_memory_cost(ssl: i32, ws: *RawWebSocket) usize; +}; pub const uws_websocket_handler = ?*const fn (*RawWebSocket) callconv(.C) void; pub const uws_websocket_message_handler = ?*const fn (*RawWebSocket, [*c]const u8, usize, Opcode) callconv(.C) void; @@ -3960,6 +3973,11 @@ pub fn NewApp(comptime ssl: bool) type { pub fn sendWithOptions(this: *WebSocket, message: []const u8, opcode: Opcode, compress: bool, fin: bool) SendStatus { return uws_ws_send_with_options(ssl_flag, this.raw(), message.ptr, message.len, opcode, compress, fin); } + + pub fn memoryCost(this: *WebSocket) usize { + return this.raw().memoryCost(ssl_flag); + } + // pub fn sendFragment(this: *WebSocket, message: []const u8) SendStatus { // return uws_ws_send_fragment(ssl_flag, this.raw(), message: [*c]const u8, length: usize, compress: bool); // } diff --git a/src/env_loader.zig b/src/env_loader.zig index 77cc2aa7ec..8ea780553f 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -17,6 +17,7 @@ const Fs = @import("./fs.zig"); const URL = @import("./url.zig").URL; const Api = @import("./api/schema.zig").Api; const which = @import("./which.zig").which; +const s3 = @import("./s3.zig"); const DotEnvFileSuffix = enum { development, @@ -45,6 +46,8 @@ pub const Loader = struct { did_load_process: bool = false, reject_unauthorized: ?bool = null, + aws_credentials: ?s3.AWSCredentials = null, + pub fn iterator(this: *const Loader) Map.HashTable.Iterator { return this.map.iterator(); } @@ -112,6 +115,53 @@ pub const Loader = struct { } } + pub fn getAWSCredentials(this: *Loader) s3.AWSCredentials { + if (this.aws_credentials) |credentials| { + return credentials; + } + + var accessKeyId: []const u8 = ""; + var secretAccessKey: []const u8 = ""; + var region: []const u8 = ""; + var endpoint: []const u8 = ""; + var bucket: []const u8 = ""; + + if (this.get("S3_ACCESS_KEY_ID")) |access_key| { + accessKeyId = access_key; + } else if (this.get("AWS_ACCESS_KEY_ID")) |access_key| { + accessKeyId = access_key; + } + if (this.get("S3_SECRET_ACCESS_KEY")) |access_key| { + secretAccessKey = access_key; + } else if (this.get("AWS_SECRET_ACCESS_KEY")) |access_key| { + secretAccessKey = access_key; + } + + if (this.get("S3_REGION")) |region_| { + region = region_; + } else if (this.get("AWS_REGION")) |region_| { + region = region_; + } + if (this.get("S3_ENDPOINT")) |endpoint_| { + endpoint = bun.URL.parse(endpoint_).host; + } else if (this.get("AWS_ENDPOINT")) |endpoint_| { + endpoint = bun.URL.parse(endpoint_).host; + } + if (this.get("S3_BUCKET")) |bucket_| { + bucket = bucket_; + } else if (this.get("AWS_BUCKET")) |bucket_| { + bucket = bucket_; + } + this.aws_credentials = .{ + .accessKeyId = accessKeyId, + .secretAccessKey = secretAccessKey, + .region = region, + .endpoint = endpoint, + .bucket = bucket, + }; + + return this.aws_credentials.?; + } /// Checks whether `NODE_TLS_REJECT_UNAUTHORIZED` is set to `0` or `false`. /// /// **Prefer VirtualMachine.getTLSRejectUnauthorized()** for JavaScript, as individual workers could have different settings. @@ -134,11 +184,15 @@ pub const Loader = struct { return true; } - pub fn getHttpProxy(this: *Loader, url: URL) ?URL { + pub fn getHttpProxyFor(this: *Loader, url: URL) ?URL { + return this.getHttpProxy(url.isHTTP(), url.hostname); + } + + pub fn getHttpProxy(this: *Loader, is_http: bool, hostname: ?[]const u8) ?URL { // TODO: When Web Worker support is added, make sure to intern these strings var http_proxy: ?URL = null; - if (url.isHTTP()) { + if (is_http) { if (this.get("http_proxy") orelse this.get("HTTP_PROXY")) |proxy| { if (proxy.len > 0 and !strings.eqlComptime(proxy, "\"\"") and !strings.eqlComptime(proxy, "''")) { http_proxy = URL.parse(proxy); @@ -154,7 +208,7 @@ pub const Loader = struct { // NO_PROXY filter // See the syntax at https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/ - if (http_proxy != null) { + if (http_proxy != null and hostname != null) { if (this.get("no_proxy") orelse this.get("NO_PROXY")) |no_proxy_text| { if (no_proxy_text.len == 0 or strings.eqlComptime(no_proxy_text, "\"\"") or strings.eqlComptime(no_proxy_text, "''")) { return http_proxy; @@ -172,7 +226,7 @@ pub const Loader = struct { host = host[1..]; } //hostname ends with suffix - if (strings.endsWith(url.hostname, host)) { + if (strings.endsWith(hostname.?, host)) { return null; } next = no_proxy_list.next(); diff --git a/src/fmt.zig b/src/fmt.zig index 33d25bef1d..986a90bb8d 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -250,7 +250,7 @@ const JSONFormatterUTF8 = struct { }; /// Expects latin1 -pub fn formatJSONString(text: []const u8) JSONFormatter { +pub fn formatJSONStringLatin1(text: []const u8) JSONFormatter { return .{ .input = text }; } diff --git a/src/fs.zig b/src/fs.zig index eec9ef34a8..9f8cd22f24 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -819,10 +819,19 @@ pub const FileSystem = struct { Limit.handles_before = limit; file_limit = limit.max; Limit.handles = file_limit; - if (limit.cur < limit.max or limit.max < 163840) { + const max_to_use: @TypeOf(limit.max) = if (Environment.isMusl) + // musl has extremely low defaults here, so we really want + // to enable this on musl or tests will start failing. + @max(limit.max, 163840) + else + // apparently, requesting too high of a number can cause other processes to not start. + // https://discord.com/channels/876711213126520882/1316342194176790609/1318175562367242271 + // https://github.com/postgres/postgres/blob/fee2b3ea2ecd0da0c88832b37ac0d9f6b3bfb9a9/src/backend/storage/file/fd.c#L1072 + limit.max; + if (limit.cur < max_to_use) { var new_limit = std.mem.zeroes(std.posix.rlimit); - new_limit.cur = @max(limit.max, 163840); - new_limit.max = @max(limit.max, 163840); + new_limit.cur = max_to_use; + new_limit.max = max_to_use; std.posix.setrlimit(resource, new_limit) catch break :blk; file_limit = new_limit.max; diff --git a/src/glob.zig b/src/glob.zig index 3b97954620..09248b37ae 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -1,2200 +1,6 @@ -// Portions of this file are derived from works under the MIT License: -// -// Copyright (c) 2023 Devon Govett -// Copyright (c) 2023 Stephen Gregoratto -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -const std = @import("std"); -const bun = @import("root").bun; +pub const walk = @import("./glob/GlobWalker.zig"); +pub const Ascii = @import("./glob/ascii.zig"); -const eqlComptime = @import("./string_immutable.zig").eqlComptime; -const expect = std.testing.expect; -const isAllAscii = @import("./string_immutable.zig").isAllASCII; -const math = std.math; -const mem = std.mem; -const isWindows = @import("builtin").os.tag == .windows; - -const Allocator = std.mem.Allocator; -const Arena = std.heap.ArenaAllocator; -const ArrayList = std.ArrayListUnmanaged; -const ArrayListManaged = std.ArrayList; -const BunString = bun.String; -const C = @import("./c.zig"); -const CodepointIterator = @import("./string_immutable.zig").PackedCodepointIterator; -const Codepoint = CodepointIterator.Cursor.CodePointType; -const Dirent = @import("./bun.js/node/types.zig").Dirent; -const DirIterator = @import("./bun.js/node/dir_iterator.zig"); -const EntryKind = @import("./bun.js/node/types.zig").Dirent.Kind; -const GlobAscii = @import("./glob_ascii.zig"); -const JSC = bun.JSC; -const Maybe = JSC.Maybe; -const PathLike = @import("./bun.js/node/types.zig").PathLike; -const PathString = @import("./string_types.zig").PathString; -const ResolvePath = @import("./resolver/resolve_path.zig"); -const Syscall = bun.sys; -const ZigString = @import("./bun.js/bindings/bindings.zig").ZigString; - -// const Codepoint = u32; -const Cursor = CodepointIterator.Cursor; - -const log = bun.Output.scoped(.Glob, false); - -const CursorState = struct { - cursor: CodepointIterator.Cursor = .{}, - /// The index in terms of codepoints - // cp_idx: usize, - - fn init(iterator: *const CodepointIterator) CursorState { - var this_cursor: CodepointIterator.Cursor = .{}; - _ = iterator.next(&this_cursor); - return .{ - // .cp_idx = 0, - .cursor = this_cursor, - }; - } - - /// Return cursor pos of next codepoint without modifying the current. - /// - /// NOTE: If there is no next codepoint (cursor is at the last one), then - /// the returned cursor will have `c` as zero value and `i` will be >= - /// sourceBytes.len - fn peek(this: *const CursorState, iterator: *const CodepointIterator) CursorState { - var cpy = this.*; - // If outside of bounds - if (!iterator.next(&cpy.cursor)) { - // This will make `i >= sourceBytes.len` - cpy.cursor.i += cpy.cursor.width; - cpy.cursor.width = 1; - cpy.cursor.c = CodepointIterator.ZeroValue; - } - // cpy.cp_idx += 1; - return cpy; - } - - fn bump(this: *CursorState, iterator: *const CodepointIterator) void { - if (!iterator.next(&this.cursor)) { - this.cursor.i += this.cursor.width; - this.cursor.width = 1; - this.cursor.c = CodepointIterator.ZeroValue; - } - // this.cp_idx += 1; - } - - inline fn manualBumpAscii(this: *CursorState, i: u32, nextCp: Codepoint) void { - this.cursor.i += i; - this.cursor.c = nextCp; - this.cursor.width = 1; - } - - inline fn manualPeekAscii(this: *CursorState, i: u32, nextCp: Codepoint) CursorState { - return .{ - .cursor = CodepointIterator.Cursor{ - .i = this.cursor.i + i, - .c = @truncate(nextCp), - .width = 1, - }, - }; - } -}; - -pub const BunGlobWalker = GlobWalker_(null, SyscallAccessor, false); - -fn dummyFilterTrue(val: []const u8) bool { - _ = val; - return true; -} - -fn dummyFilterFalse(val: []const u8) bool { - _ = val; - return false; -} - -pub fn statatWindows(fd: bun.FileDescriptor, path: [:0]const u8) Maybe(bun.Stat) { - if (comptime !bun.Environment.isWindows) @compileError("oi don't use this"); - var buf: bun.PathBuffer = undefined; - const dir = switch (Syscall.getFdPath(fd, &buf)) { - .err => |e| return .{ .err = e }, - .result => |s| s, - }; - const parts: []const []const u8 = &.{ - dir[0..dir.len], - path, - }; - const statpath = ResolvePath.joinZBuf(&buf, parts, .auto); - return Syscall.stat(statpath); -} - -pub const SyscallAccessor = struct { - const count_fds = true; - - const Handle = struct { - value: bun.FileDescriptor, - - const zero = Handle{ .value = bun.FileDescriptor.zero }; - - pub fn isZero(this: Handle) bool { - return this.value == bun.FileDescriptor.zero; - } - - pub fn eql(this: Handle, other: Handle) bool { - return this.value == other.value; - } - }; - - const DirIter = struct { - value: DirIterator.WrappedIterator, - - pub inline fn next(self: *DirIter) Maybe(?DirIterator.IteratorResult) { - return self.value.next(); - } - - pub inline fn iterate(dir: Handle) DirIter { - return .{ .value = DirIterator.WrappedIterator.init(dir.value.asDir()) }; - } - }; - - pub fn open(path: [:0]const u8) !Maybe(Handle) { - return switch (Syscall.open(path, bun.O.DIRECTORY | bun.O.RDONLY, 0)) { - .err => |err| .{ .err = err }, - .result => |fd| .{ .result = Handle{ .value = fd } }, - }; - } - - pub fn statat(handle: Handle, path: [:0]const u8) Maybe(bun.Stat) { - if (comptime bun.Environment.isWindows) return statatWindows(handle.value, path); - return switch (Syscall.fstatat(handle.value, path)) { - .err => |err| .{ .err = err }, - .result => |s| .{ .result = s }, - }; - } - - pub fn openat(handle: Handle, path: [:0]const u8) !Maybe(Handle) { - return switch (Syscall.openat(handle.value, path, bun.O.DIRECTORY | bun.O.RDONLY, 0)) { - .err => |err| .{ .err = err }, - .result => |fd| .{ .result = Handle{ .value = fd } }, - }; - } - - pub fn close(handle: Handle) ?Syscall.Error { - return Syscall.close(handle.value); - } - - pub fn getcwd(path_buf: *bun.PathBuffer) Maybe([]const u8) { - return Syscall.getcwd(path_buf); - } -}; - -pub const DirEntryAccessor = struct { - const FS = bun.fs.FileSystem; - - const count_fds = false; - - const Handle = struct { - value: ?*FS.DirEntry, - - const zero = Handle{ .value = null }; - - pub fn isZero(this: Handle) bool { - return this.value == null; - } - - pub fn eql(this: Handle, other: Handle) bool { - // TODO this might not be quite right, we're comparing pointers, not the underlying directory - // On the other hand, DirEntries are only ever created once (per generation), so this should be fine? - // Realistically, as closing the handle is a no-op, this should be fine either way. - return this.value == other.value; - } - }; - - const DirIter = struct { - value: ?FS.DirEntry.EntryMap.Iterator, - - const IterResult = struct { - name: NameWrapper, - kind: std.fs.File.Kind, - - const NameWrapper = struct { - value: []const u8, - - pub fn slice(this: NameWrapper) []const u8 { - return this.value; - } - }; - }; - - pub inline fn next(self: *DirIter) Maybe(?IterResult) { - if (self.value) |*value| { - const nextval = value.next() orelse return .{ .result = null }; - const name = nextval.key_ptr.*; - const kind = nextval.value_ptr.*.kind(&FS.instance.fs, true); - const fskind = switch (kind) { - .file => std.fs.File.Kind.file, - .dir => std.fs.File.Kind.directory, - }; - return .{ - .result = .{ - .name = IterResult.NameWrapper{ .value = name }, - .kind = fskind, - }, - }; - } else { - return .{ .result = null }; - } - } - - pub inline fn iterate(dir: Handle) DirIter { - const entry = dir.value orelse return DirIter{ .value = null }; - return .{ .value = entry.data.iterator() }; - } - }; - - pub fn statat(handle: Handle, path_: [:0]const u8) Maybe(bun.Stat) { - var path: [:0]const u8 = path_; - var buf: bun.PathBuffer = undefined; - if (!bun.path.Platform.auto.isAbsolute(path)) { - if (handle.value) |entry| { - const slice = bun.path.joinStringBuf(&buf, [_][]const u8{ entry.dir, path }, .auto); - buf[slice.len] = 0; - path = buf[0..slice.len :0]; - } - } - return Syscall.stat(path); - } - - pub fn open(path: [:0]const u8) !Maybe(Handle) { - return openat(Handle.zero, path); - } - - pub fn openat(handle: Handle, path_: [:0]const u8) !Maybe(Handle) { - var path: []const u8 = path_; - var buf: bun.PathBuffer = undefined; - - if (!bun.path.Platform.auto.isAbsolute(path)) { - if (handle.value) |entry| { - path = bun.path.joinStringBuf(&buf, [_][]const u8{ entry.dir, path }, .auto); - } - } - // TODO do we want to propagate ENOTDIR through the 'Maybe' to match the SyscallAccessor? - // The glob implementation specifically checks for this error when dealing with symlinks - // return .{ .err = Syscall.Error.fromCode(bun.C.E.NOTDIR, Syscall.Tag.open) }; - const res = FS.instance.fs.readDirectory(path, null, 0, false) catch |err| { - return err; - }; - switch (res.*) { - .entries => |entry| { - return .{ .result = Handle{ .value = entry } }; - }, - .err => |err| { - return err.original_err; - }, - } - } - - pub inline fn close(handle: Handle) ?Syscall.Error { - // TODO is this a noop? - _ = handle; - return null; - } - - pub fn getcwd(path_buf: *bun.PathBuffer) Maybe([]const u8) { - @memcpy(path_buf, bun.fs.FileSystem.instance.fs.cwd); - } -}; - -pub fn GlobWalker_( - comptime ignore_filter_fn: ?*const fn ([]const u8) bool, - comptime Accessor: type, - comptime sentinel: bool, -) type { - const is_ignored: *const fn ([]const u8) bool = if (comptime ignore_filter_fn) |func| func else dummyFilterFalse; - - const count_fds = Accessor.count_fds and bun.Environment.isDebug; - - const stdJoin = comptime if (!sentinel) std.fs.path.join else std.fs.path.joinZ; - const bunJoin = comptime if (!sentinel) ResolvePath.join else ResolvePath.joinZ; - const MatchedPath = comptime if (!sentinel) []const u8 else [:0]const u8; - - return struct { - const GlobWalker = @This(); - pub const Result = Maybe(void); - - arena: Arena = undefined, - - /// not owned by this struct - pattern: []const u8 = "", - - pattern_codepoints: []u32 = &[_]u32{}, - cp_len: u32 = 0, - - /// If the pattern contains "./" or "../" - has_relative_components: bool = false, - - end_byte_of_basename_excluding_special_syntax: u32 = 0, - basename_excluding_special_syntax_component_idx: u32 = 0, - - patternComponents: ArrayList(Component) = .{}, - matchedPaths: MatchedMap = .{}, - i: u32 = 0, - - dot: bool = false, - absolute: bool = false, - - cwd: []const u8 = "", - follow_symlinks: bool = false, - error_on_broken_symlinks: bool = false, - only_files: bool = true, - - pathBuf: bun.PathBuffer = undefined, - // iteration state - workbuf: ArrayList(WorkItem) = ArrayList(WorkItem){}, - - /// Array hashmap used as a set (values are the keys) - /// to store matched paths and prevent duplicates - /// - /// BunString is used so that we can call BunString.toJSArray() - /// on the result of `.keys()` to give the result back to JS - /// - /// The only type of string impl we use is ZigString since - /// all matched paths are UTF-8 (DirIterator converts them on - /// windows) and allocated on the arnea - /// - /// Multiple patterns are not supported so right now this is - /// only possible when running a pattern like: - /// - /// `foo/**/*` - /// - /// Use `.keys()` to get the matched paths - const MatchedMap = std.ArrayHashMapUnmanaged(BunString, void, struct { - pub fn hash(_: @This(), this: BunString) u32 { - bun.assert(this.tag == .ZigString); - const slice = this.byteSlice(); - if (comptime sentinel) { - const slicez = slice[0 .. slice.len - 1 :0]; - return std.array_hash_map.hashString(slicez); - } - - return std.array_hash_map.hashString(slice); - } - - pub fn eql(_: @This(), this: BunString, other: BunString, _: usize) bool { - return this.eql(other); - } - }, true); - - /// The glob walker references the .directory.path so its not safe to - /// copy/move this - const IterState = union(enum) { - /// Pops the next item off the work stack - get_next, - - /// Currently iterating over a directory - directory: Directory, - - /// Two particular cases where this is used: - /// - /// 1. A pattern with no special glob syntax was supplied, for example: `/Users/zackradisic/foo/bar` - /// - /// In that case, the mere existence of the file/dir counts as a match, so we can eschew directory - /// iterating and walking for a simple stat call to the path. - /// - /// 2. Pattern ending in literal optimization - /// - /// With a pattern like: `packages/**/package.json`, once the iteration component index reaches - /// the final component, which is a literal string ("package.json"), we can similarly make a - /// single stat call to complete the pattern. - matched: MatchedPath, - - const Directory = struct { - fd: Accessor.Handle, - iter: Accessor.DirIter, - path: bun.PathBuffer, - dir_path: [:0]const u8, - - component_idx: u32, - pattern: *Component, - next_pattern: ?*Component, - is_last: bool, - - iter_closed: bool = false, - at_cwd: bool = false, - }; - }; - - pub const Iterator = struct { - walker: *GlobWalker, - iter_state: IterState = .get_next, - cwd_fd: Accessor.Handle = Accessor.Handle.zero, - empty_dir_path: [0:0]u8 = [0:0]u8{}, - /// This is to make sure in debug/tests that we are closing file descriptors - /// We should only have max 2 open at a time. One for the cwd, and one for the - /// directory being iterated on. - fds_open: if (count_fds) usize else u0 = 0, - - pub fn init(this: *Iterator) !Maybe(void) { - log("Iterator init pattern={s}", .{this.walker.pattern}); - var was_absolute = false; - const root_work_item = brk: { - var use_posix = bun.Environment.isPosix; - const is_absolute = if (bun.Environment.isPosix) std.fs.path.isAbsolute(this.walker.pattern) else std.fs.path.isAbsolute(this.walker.pattern) or is_absolute: { - use_posix = true; - break :is_absolute std.fs.path.isAbsolutePosix(this.walker.pattern); - }; - - if (!is_absolute) break :brk WorkItem.new(this.walker.cwd, 0, .directory); - - was_absolute = true; - - var path_without_special_syntax = this.walker.pattern[0..this.walker.end_byte_of_basename_excluding_special_syntax]; - var starting_component_idx = this.walker.basename_excluding_special_syntax_component_idx; - - if (path_without_special_syntax.len == 0) { - path_without_special_syntax = if (!bun.Environment.isWindows) "/" else ResolvePath.windowsFilesystemRoot(this.walker.cwd); - } else { - // Skip the components associated with the literal path - starting_component_idx += 1; - - // This means we got a pattern without any special glob syntax, for example: - // `/Users/zackradisic/foo/bar` - // - // In that case we don't need to do any walking and can just open up the FS entry - if (starting_component_idx >= this.walker.patternComponents.items.len) { - const path = try this.walker.arena.allocator().dupeZ(u8, path_without_special_syntax); - const fd = switch (try Accessor.open(path)) { - .err => |e| { - if (e.getErrno() == bun.C.E.NOTDIR) { - this.iter_state = .{ .matched = path }; - return Maybe(void).success; - } - // Doesn't exist - if (e.getErrno() == bun.C.E.NOENT) { - this.iter_state = .get_next; - return Maybe(void).success; - } - const errpath = try this.walker.arena.allocator().dupeZ(u8, path); - return .{ .err = e.withPath(errpath) }; - }, - .result => |fd| fd, - }; - _ = Accessor.close(fd); - this.iter_state = .{ .matched = path }; - return Maybe(void).success; - } - - // In the above branch, if `starting_compoennt_dix >= pattern_components.len` then - // it should also mean that `end_byte_of_basename_excluding_special_syntax >= pattern.len` - // - // So if we see that `end_byte_of_basename_excluding_special_syntax < this.walker.pattern.len` we - // miscalculated the values - bun.assert(this.walker.end_byte_of_basename_excluding_special_syntax < this.walker.pattern.len); - } - - break :brk WorkItem.new( - path_without_special_syntax, - starting_component_idx, - .directory, - ); - }; - - var path_buf: *bun.PathBuffer = &this.walker.pathBuf; - const root_path = root_work_item.path; - @memcpy(path_buf[0..root_path.len], root_path[0..root_path.len]); - path_buf[root_path.len] = 0; - const cwd_fd = switch (try Accessor.open(path_buf[0..root_path.len :0])) { - .err => |err| return .{ .err = this.walker.handleSysErrWithPath(err, @ptrCast(path_buf[0 .. root_path.len + 1])) }, - .result => |fd| fd, - }; - - if (comptime count_fds) { - this.fds_open += 1; - } - - this.cwd_fd = cwd_fd; - - switch (if (was_absolute) try this.transitionToDirIterState( - root_work_item, - false, - ) else try this.transitionToDirIterState( - root_work_item, - true, - )) { - .err => |err| return .{ .err = err }, - else => {}, - } - - return Maybe(void).success; - } - - pub fn deinit(this: *Iterator) void { - defer { - bun.debugAssert(this.fds_open == 0); - } - this.closeCwdFd(); - switch (this.iter_state) { - .directory => |dir| { - if (!dir.iter_closed) { - this.closeDisallowingCwd(dir.fd); - } - }, - else => {}, - } - - while (this.walker.workbuf.popOrNull()) |work_item| { - if (work_item.fd) |fd| { - this.closeDisallowingCwd(fd); - } - } - - if (comptime count_fds) { - bun.debugAssert(this.fds_open == 0); - } - } - - pub fn closeCwdFd(this: *Iterator) void { - if (this.cwd_fd.isZero()) return; - _ = Accessor.close(this.cwd_fd); - if (comptime count_fds) this.fds_open -= 1; - } - - pub fn closeDisallowingCwd(this: *Iterator, fd: Accessor.Handle) void { - if (fd.isZero() or fd.eql(this.cwd_fd)) return; - _ = Accessor.close(fd); - if (comptime count_fds) this.fds_open -= 1; - } - - pub fn bumpOpenFds(this: *Iterator) void { - if (comptime count_fds) { - this.fds_open += 1; - // If this is over 2 then this means that there is a bug in the iterator code - bun.debugAssert(this.fds_open <= 2); - } - } - - fn transitionToDirIterState( - this: *Iterator, - work_item: WorkItem, - comptime root: bool, - ) !Maybe(void) { - log("transition => {s}", .{work_item.path}); - this.iter_state = .{ .directory = .{ - .fd = Accessor.Handle.zero, - .iter = undefined, - .path = undefined, - .dir_path = undefined, - .component_idx = 0, - .pattern = undefined, - .next_pattern = null, - .is_last = false, - .iter_closed = false, - .at_cwd = false, - } }; - - var dir_path: [:0]u8 = dir_path: { - if (comptime root) { - if (!this.walker.absolute) { - this.iter_state.directory.path[0] = 0; - break :dir_path this.iter_state.directory.path[0..0 :0]; - } - } - // TODO Optimization: On posix systems filepaths are already null byte terminated so we can skip this if thats the case - @memcpy(this.iter_state.directory.path[0..work_item.path.len], work_item.path); - this.iter_state.directory.path[work_item.path.len] = 0; - break :dir_path this.iter_state.directory.path[0..work_item.path.len :0]; - }; - - var had_dot_dot = false; - const component_idx = this.walker.skipSpecialComponents(work_item.idx, &dir_path, &this.iter_state.directory.path, &had_dot_dot); - - const fd: Accessor.Handle = fd: { - if (work_item.fd) |fd| break :fd fd; - if (comptime root) { - if (had_dot_dot) break :fd switch (try Accessor.openat(this.cwd_fd, dir_path)) { - .err => |err| return .{ - .err = this.walker.handleSysErrWithPath(err, dir_path), - }, - .result => |fd_| brk: { - this.bumpOpenFds(); - break :brk fd_; - }, - }; - - this.iter_state.directory.at_cwd = true; - break :fd this.cwd_fd; - } - - break :fd switch (try Accessor.openat(this.cwd_fd, dir_path)) { - .err => |err| return .{ - .err = this.walker.handleSysErrWithPath(err, dir_path), - }, - .result => |fd_| brk: { - this.bumpOpenFds(); - break :brk fd_; - }, - }; - }; - - // Optimization: - // If we have a pattern like: - // `packages/*/package.json` - // ^ and we are at this component, with let's say - // a directory named: `packages/frontend/` - // - // Then we can just open `packages/frontend/package.json` without - // doing any iteration on the current directory. - // - // More generally, we can apply this optimization if we are on the - // last component and it is a literal with no special syntax. - if (component_idx == this.walker.patternComponents.items.len -| 1 and - this.walker.patternComponents.items[component_idx].syntax_hint == .Literal) - { - defer { - this.closeDisallowingCwd(fd); - } - const stackbuf_size = 256; - var stfb = std.heap.stackFallback(stackbuf_size, this.walker.arena.allocator()); - const pathz = try stfb.get().dupeZ(u8, this.walker.patternComponents.items[component_idx].patternSlice(this.walker.pattern)); - const stat_result: bun.Stat = switch (Accessor.statat(fd, pathz)) { - .err => |e_| { - var e: bun.sys.Error = e_; - if (e.getErrno() == bun.C.E.NOENT) { - this.iter_state = .get_next; - return Maybe(void).success; - } - return .{ .err = e.withPath(this.walker.patternComponents.items[component_idx].patternSlice(this.walker.pattern)) }; - }, - .result => |stat| stat, - }; - const matches = (bun.S.ISDIR(@intCast(stat_result.mode)) and !this.walker.only_files) or bun.S.ISREG(@intCast(stat_result.mode)) or !this.walker.only_files; - if (matches) { - if (try this.walker.prepareMatchedPath(pathz, dir_path)) |path| { - this.iter_state = .{ .matched = path }; - } else { - this.iter_state = .get_next; - } - } else { - this.iter_state = .get_next; - } - return Maybe(void).success; - } - - this.iter_state.directory.dir_path = dir_path; - this.iter_state.directory.component_idx = component_idx; - this.iter_state.directory.pattern = &this.walker.patternComponents.items[component_idx]; - this.iter_state.directory.next_pattern = if (component_idx + 1 < this.walker.patternComponents.items.len) &this.walker.patternComponents.items[component_idx + 1] else null; - this.iter_state.directory.is_last = component_idx == this.walker.patternComponents.items.len - 1; - this.iter_state.directory.at_cwd = false; - this.iter_state.directory.fd = Accessor.Handle.zero; - - log("Transition(dirpath={s}, fd={}, component_idx={d})", .{ dir_path, fd, component_idx }); - - this.iter_state.directory.fd = fd; - const iterator = Accessor.DirIter.iterate(fd); - this.iter_state.directory.iter = iterator; - this.iter_state.directory.iter_closed = false; - - return Maybe(void).success; - } - - pub fn next(this: *Iterator) !Maybe(?MatchedPath) { - while (true) { - switch (this.iter_state) { - .matched => |path| { - this.iter_state = .get_next; - return .{ .result = path }; - }, - .get_next => { - // Done - if (this.walker.workbuf.items.len == 0) return .{ .result = null }; - const work_item = this.walker.workbuf.pop(); - switch (work_item.kind) { - .directory => { - switch (try this.transitionToDirIterState(work_item, false)) { - .err => |err| return .{ .err = err }, - else => {}, - } - continue; - }, - .symlink => { - var scratch_path_buf: *bun.PathBuffer = &this.walker.pathBuf; - @memcpy(scratch_path_buf[0..work_item.path.len], work_item.path); - scratch_path_buf[work_item.path.len] = 0; - var symlink_full_path_z: [:0]u8 = scratch_path_buf[0..work_item.path.len :0]; - const entry_name = symlink_full_path_z[work_item.entry_start..symlink_full_path_z.len]; - - var has_dot_dot = false; - const component_idx = this.walker.skipSpecialComponents(work_item.idx, &symlink_full_path_z, scratch_path_buf, &has_dot_dot); - var pattern = this.walker.patternComponents.items[component_idx]; - const next_pattern = if (component_idx + 1 < this.walker.patternComponents.items.len) &this.walker.patternComponents.items[component_idx + 1] else null; - const is_last = component_idx == this.walker.patternComponents.items.len - 1; - - this.iter_state = .get_next; - const maybe_dir_fd: ?Accessor.Handle = switch (try Accessor.openat(this.cwd_fd, symlink_full_path_z)) { - .err => |err| brk: { - if (@as(usize, @intCast(err.errno)) == @as(usize, @intFromEnum(bun.C.E.NOTDIR))) { - break :brk null; - } - if (this.walker.error_on_broken_symlinks) return .{ .err = this.walker.handleSysErrWithPath(err, symlink_full_path_z) }; - // Broken symlink, but if `only_files` is false we still want to append - // it to the matched paths - if (!this.walker.only_files) { - // (See case A and B in the comment for `matchPatternFile()`) - // When we encounter a symlink we call the catch all - // matching function: `matchPatternImpl()` to see if we can avoid following the symlink. - // So for case A, we just need to check if the pattern is the last pattern. - if (is_last or - (pattern.syntax_hint == .Double and - component_idx + 1 == this.walker.patternComponents.items.len -| 1 and - next_pattern.?.syntax_hint != .Double and - this.walker.matchPatternImpl(next_pattern.?, entry_name))) - { - return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; - } - } - continue; - }, - .result => |fd| brk: { - this.bumpOpenFds(); - break :brk fd; - }, - }; - - const dir_fd = maybe_dir_fd orelse { - // No directory file descriptor, it's a file - if (is_last) - return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; - - if (pattern.syntax_hint == .Double and - component_idx + 1 == this.walker.patternComponents.items.len -| 1 and - next_pattern.?.syntax_hint != .Double and - this.walker.matchPatternImpl(next_pattern.?, entry_name)) - { - return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; - } - - continue; - }; - - var add_dir: bool = false; - // TODO this function calls `matchPatternImpl(pattern, - // entry_name)` which is redundant because we already called - // that when we first encountered the symlink - const recursion_idx_bump_ = this.walker.matchPatternDir(&pattern, next_pattern, entry_name, component_idx, is_last, &add_dir); - - if (recursion_idx_bump_) |recursion_idx_bump| { - if (recursion_idx_bump == 2) { - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.newWithFd(work_item.path, component_idx + recursion_idx_bump, .directory, dir_fd), - ); - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.newWithFd(work_item.path, component_idx, .directory, dir_fd), - ); - } else { - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.newWithFd(work_item.path, component_idx + recursion_idx_bump, .directory, dir_fd), - ); - } - } - - if (add_dir and !this.walker.only_files) { - return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; - } - - continue; - }, - } - }, - .directory => |*dir| { - const entry = switch (dir.iter.next()) { - .err => |err| { - if (!dir.at_cwd) this.closeDisallowingCwd(dir.fd); - dir.iter_closed = true; - return .{ .err = this.walker.handleSysErrWithPath(err, dir.dir_path) }; - }, - .result => |ent| ent, - } orelse { - if (!dir.at_cwd) this.closeDisallowingCwd(dir.fd); - dir.iter_closed = true; - this.iter_state = .get_next; - continue; - }; - log("dir: {s} entry: {s}", .{ dir.dir_path, entry.name.slice() }); - - const dir_iter_state: *const IterState.Directory = &this.iter_state.directory; - - const entry_name = entry.name.slice(); - switch (entry.kind) { - .file => { - const matches = this.walker.matchPatternFile(entry_name, dir_iter_state.component_idx, dir.is_last, dir_iter_state.pattern, dir_iter_state.next_pattern); - if (matches) { - const prepared = try this.walker.prepareMatchedPath(entry_name, dir.dir_path) orelse continue; - return .{ .result = prepared }; - } - continue; - }, - .directory => { - var add_dir: bool = false; - const recursion_idx_bump_ = this.walker.matchPatternDir(dir_iter_state.pattern, dir_iter_state.next_pattern, entry_name, dir_iter_state.component_idx, dir_iter_state.is_last, &add_dir); - - if (recursion_idx_bump_) |recursion_idx_bump| { - const subdir_parts: []const []const u8 = &[_][]const u8{ - dir.dir_path[0..dir.dir_path.len], - entry_name, - }; - - const subdir_entry_name = try this.walker.join(subdir_parts); - - if (recursion_idx_bump == 2) { - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.new(subdir_entry_name, dir_iter_state.component_idx + recursion_idx_bump, .directory), - ); - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.new(subdir_entry_name, dir_iter_state.component_idx, .directory), - ); - } else { - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.new(subdir_entry_name, dir_iter_state.component_idx + recursion_idx_bump, .directory), - ); - } - } - - if (add_dir and !this.walker.only_files) { - const prepared_path = try this.walker.prepareMatchedPath(entry_name, dir.dir_path) orelse continue; - return .{ .result = prepared_path }; - } - - continue; - }, - .sym_link => { - if (this.walker.follow_symlinks) { - // Following a symlink requires additional syscalls, so - // we first try it against our "catch-all" pattern match - // function - const matches = this.walker.matchPatternImpl(dir_iter_state.pattern, entry_name); - if (!matches) continue; - - const subdir_parts: []const []const u8 = &[_][]const u8{ - dir.dir_path[0..dir.dir_path.len], - entry_name, - }; - const entry_start: u32 = @intCast(if (dir.dir_path.len == 0) 0 else dir.dir_path.len + 1); - - // const subdir_entry_name = try this.arena.allocator().dupe(u8, ResolvePath.join(subdir_parts, .auto)); - const subdir_entry_name = try this.walker.join(subdir_parts); - - try this.walker.workbuf.append( - this.walker.arena.allocator(), - WorkItem.newSymlink(subdir_entry_name, dir_iter_state.component_idx, entry_start), - ); - - continue; - } - - if (this.walker.only_files) continue; - - const matches = this.walker.matchPatternFile(entry_name, dir_iter_state.component_idx, dir_iter_state.is_last, dir_iter_state.pattern, dir_iter_state.next_pattern); - if (matches) { - const prepared_path = try this.walker.prepareMatchedPath(entry_name, dir.dir_path) orelse continue; - return .{ .result = prepared_path }; - } - - continue; - }, - else => continue, - } - }, - } - } - } - }; - - const WorkItem = struct { - path: []const u8, - idx: u32, - kind: Kind, - entry_start: u32 = 0, - fd: ?Accessor.Handle = null, - - const Kind = enum { - directory, - symlink, - }; - - fn new(path: []const u8, idx: u32, kind: Kind) WorkItem { - return .{ - .path = path, - .idx = idx, - .kind = kind, - }; - } - - fn newWithFd(path: []const u8, idx: u32, kind: Kind, fd: Accessor.Handle) WorkItem { - return .{ - .path = path, - .idx = idx, - .kind = kind, - .fd = fd, - }; - } - - fn newSymlink(path: []const u8, idx: u32, entry_start: u32) WorkItem { - return .{ - .path = path, - .idx = idx, - .kind = .symlink, - .entry_start = entry_start, - }; - } - }; - - /// A component is each part of a glob pattern, separated by directory - /// separator: - /// `src/**/*.ts` -> `src`, `**`, `*.ts` - const Component = struct { - start: u32, - len: u32, - - syntax_hint: SyntaxHint = .None, - trailing_sep: bool = false, - is_ascii: bool = false, - - /// Only used when component is not ascii - unicode_set: bool = false, - start_cp: u32 = 0, - end_cp: u32 = 0, - - pub fn patternSlice(this: *const Component, pattern: []const u8) []const u8 { - return pattern[this.start .. this.start + this.len - @as(u1, @bitCast(this.trailing_sep))]; - } - - pub fn patternSliceCp(this: *const Component, pattern: []u32) []u32 { - return pattern[this.start_cp .. this.end_cp - @as(u1, @bitCast(this.trailing_sep))]; - } - - const SyntaxHint = enum { - None, - Single, - Double, - /// Uses special fast-path matching for components like: `*.ts` - WildcardFilepath, - /// Uses special fast-patch matching for literal components e.g. - /// "node_modules", becomes memcmp - Literal, - /// ./fixtures/*.ts - /// ^ - Dot, - /// ../ - DotBack, - - fn isSpecialSyntax(this: SyntaxHint) bool { - return switch (this) { - .Literal => false, - else => true, - }; - } - }; - }; - - /// The arena parameter is dereferenced and copied if all allocations go well and nothing goes wrong - pub fn init( - this: *GlobWalker, - arena: *Arena, - pattern: []const u8, - dot: bool, - absolute: bool, - follow_symlinks: bool, - error_on_broken_symlinks: bool, - only_files: bool, - ) !Maybe(void) { - return try this.initWithCwd( - arena, - pattern, - bun.fs.FileSystem.instance.top_level_dir, - dot, - absolute, - follow_symlinks, - error_on_broken_symlinks, - only_files, - ); - } - - pub fn convertUtf8ToCodepoints(codepoints: []u32, pattern: []const u8) void { - _ = bun.simdutf.convert.utf8.to.utf32.le(pattern, codepoints); - } - - pub fn debugPatternComopnents(this: *GlobWalker) void { - const pattern = this.pattern; - const components = &this.patternComponents; - const ptr = @intFromPtr(this); - log("GlobWalker(0x{x}) components:", .{ptr}); - for (components.items) |cmp| { - switch (cmp.syntax_hint) { - .Single => log(" *", .{}), - .Double => log(" **", .{}), - .Dot => log(" .", .{}), - .DotBack => log(" ../", .{}), - .Literal, .WildcardFilepath, .None => log(" hint={s} component_str={s}", .{ @tagName(cmp.syntax_hint), cmp.patternSlice(pattern) }), - } - } - } - - /// `cwd` should be allocated with the arena - /// The arena parameter is dereferenced and copied if all allocations go well and nothing goes wrong - pub fn initWithCwd( - this: *GlobWalker, - arena: *Arena, - pattern: []const u8, - cwd: []const u8, - dot: bool, - absolute: bool, - follow_symlinks: bool, - error_on_broken_symlinks: bool, - only_files: bool, - ) !Maybe(void) { - log("initWithCwd(cwd={s})", .{cwd}); - this.* = .{ - .cwd = cwd, - .pattern = pattern, - .dot = dot, - .absolute = absolute, - .follow_symlinks = follow_symlinks, - .error_on_broken_symlinks = error_on_broken_symlinks, - .only_files = only_files, - .basename_excluding_special_syntax_component_idx = 0, - .end_byte_of_basename_excluding_special_syntax = 0, - }; - - try GlobWalker.buildPatternComponents( - arena, - &this.patternComponents, - pattern, - &this.cp_len, - &this.pattern_codepoints, - &this.has_relative_components, - &this.end_byte_of_basename_excluding_special_syntax, - &this.basename_excluding_special_syntax_component_idx, - ); - - // copy arena after all allocations are successful - this.arena = arena.*; - - if (bun.Environment.allow_assert) { - this.debugPatternComopnents(); - } - - return Maybe(void).success; - } - - /// NOTE This also calls deinit on the arena, if you don't want to do that then - pub fn deinit(this: *GlobWalker, comptime clear_arena: bool) void { - log("GlobWalker.deinit", .{}); - if (comptime clear_arena) { - this.arena.deinit(); - } - } - - pub fn handleSysErrWithPath( - this: *GlobWalker, - err: Syscall.Error, - path_buf: [:0]const u8, - ) Syscall.Error { - std.mem.copyForwards(u8, this.pathBuf[0 .. path_buf.len + 1], @as([]const u8, @ptrCast(path_buf[0 .. path_buf.len + 1]))); - return err.withPath(this.pathBuf[0 .. path_buf.len + 1]); - } - - pub fn walk(this: *GlobWalker) !Maybe(void) { - if (this.patternComponents.items.len == 0) return Maybe(void).success; - - var iter = GlobWalker.Iterator{ .walker = this }; - defer iter.deinit(); - switch (try iter.init()) { - .err => |err| return .{ .err = err }, - else => {}, - } - - while (switch (try iter.next()) { - .err => |err| return .{ .err = err }, - .result => |matched_path| matched_path, - }) |path| { - log("walker: matched path: {s}", .{path}); - // The paths are already put into this.matchedPaths, which we use for the output, - // so we don't need to do anything here - } - - return Maybe(void).success; - } - - // NOTE you must check that the pattern at `idx` has `syntax_hint == .Dot` or - // `syntax_hint == .DotBack` first - fn collapseDots( - this: *GlobWalker, - idx: u32, - dir_path: *[:0]u8, - path_buf: *bun.PathBuffer, - encountered_dot_dot: *bool, - ) u32 { - var component_idx = idx; - var len = dir_path.len; - while (component_idx < this.patternComponents.items.len) { - switch (this.patternComponents.items[component_idx].syntax_hint) { - .Dot => { - defer component_idx += 1; - if (len + 2 >= bun.MAX_PATH_BYTES) @panic("Invalid path"); - if (len == 0) { - path_buf[len] = '.'; - path_buf[len + 1] = 0; - len += 1; - } else { - path_buf[len] = '/'; - path_buf[len + 1] = '.'; - path_buf[len + 2] = 0; - len += 2; - } - }, - .DotBack => { - defer component_idx += 1; - encountered_dot_dot.* = true; - if (dir_path.len + 3 >= bun.MAX_PATH_BYTES) @panic("Invalid path"); - if (len == 0) { - path_buf[len] = '.'; - path_buf[len + 1] = '.'; - path_buf[len + 2] = 0; - len += 2; - } else { - path_buf[len] = '/'; - path_buf[len + 1] = '.'; - path_buf[len + 2] = '.'; - path_buf[len + 3] = 0; - len += 3; - } - }, - else => break, - } - } - - dir_path.len = len; - - return component_idx; - } - - // NOTE you must check that the pattern at `idx` has `syntax_hint == .Double` first - fn collapseSuccessiveDoubleWildcards(this: *GlobWalker, idx: u32) u32 { - var component_idx = idx; - const pattern = this.patternComponents.items[idx]; - _ = pattern; - // Collapse successive double wildcards - while (component_idx + 1 < this.patternComponents.items.len and - this.patternComponents.items[component_idx + 1].syntax_hint == .Double) : (component_idx += 1) - {} - return component_idx; - } - - pub fn skipSpecialComponents( - this: *GlobWalker, - work_item_idx: u32, - dir_path: *[:0]u8, - scratch_path_buf: *bun.PathBuffer, - encountered_dot_dot: *bool, - ) u32 { - var component_idx = work_item_idx; - - // Skip `.` and `..` while also appending them to `dir_path` - component_idx = switch (this.patternComponents.items[component_idx].syntax_hint) { - .Dot => this.collapseDots( - component_idx, - dir_path, - scratch_path_buf, - encountered_dot_dot, - ), - .DotBack => this.collapseDots( - component_idx, - dir_path, - scratch_path_buf, - encountered_dot_dot, - ), - else => component_idx, - }; - - // Skip to the last `**` if there is a chain of them - component_idx = switch (this.patternComponents.items[component_idx].syntax_hint) { - .Double => this.collapseSuccessiveDoubleWildcards(component_idx), - else => component_idx, - }; - - return component_idx; - } - - fn matchPatternDir( - this: *GlobWalker, - pattern: *Component, - next_pattern: ?*Component, - entry_name: []const u8, - component_idx: u32, - is_last: bool, - add: *bool, - ) ?u32 { - if (!this.dot and GlobWalker.startsWithDot(entry_name)) return null; - if (is_ignored(entry_name)) return null; - - // Handle double wildcard `**`, this could possibly - // propagate the `**` to the directory's children - if (pattern.syntax_hint == .Double) { - // Stop the double wildcard if it matches the pattern afer it - // Example: src/**/*.js - // - Matches: src/bun.js/ - // src/bun.js/foo/bar/baz.js - if (!is_last and this.matchPatternImpl(next_pattern.?, entry_name)) { - // But if the next pattern is the last - // component, it should match and propagate the - // double wildcard recursion to the directory's - // children - if (component_idx + 1 == this.patternComponents.items.len - 1) { - add.* = true; - return 0; - } - - // In the normal case skip over the next pattern - // since we matched it, example: - // BEFORE: src/**/node_modules/**/*.js - // ^ - // AFTER: src/**/node_modules/**/*.js - // ^ - return 2; - } - - if (is_last) { - add.* = true; - } - - return 0; - } - - const matches = this.matchPatternImpl(pattern, entry_name); - if (matches) { - if (is_last) { - add.* = true; - return null; - } - return 1; - } - - return null; - } - - /// A file can only match if: - /// a) it matches against the last pattern, or - /// b) it matches the next pattern, provided the current - /// pattern is a double wildcard and the next pattern is - /// not a double wildcard - /// - /// Examples: - /// a -> `src/foo/index.ts` matches - /// b -> `src/**/*.ts` (on 2nd pattern) matches - fn matchPatternFile( - this: *GlobWalker, - entry_name: []const u8, - component_idx: u32, - is_last: bool, - pattern: *Component, - next_pattern: ?*Component, - ) bool { - if (pattern.trailing_sep) return false; - - // Handle case b) - if (!is_last) return pattern.syntax_hint == .Double and - component_idx + 1 == this.patternComponents.items.len -| 1 and - next_pattern.?.syntax_hint != .Double and - this.matchPatternImpl(next_pattern.?, entry_name); - - // Handle case a) - return this.matchPatternImpl(pattern, entry_name); - } - - fn matchPatternImpl( - this: *GlobWalker, - pattern_component: *Component, - filepath: []const u8, - ) bool { - log("matchPatternImpl: {s}", .{filepath}); - if (!this.dot and GlobWalker.startsWithDot(filepath)) return false; - if (is_ignored(filepath)) return false; - - return switch (pattern_component.syntax_hint) { - .Double, .Single => true, - .WildcardFilepath => if (comptime !isWindows) - matchWildcardFilepath(pattern_component.patternSlice(this.pattern), filepath) - else - this.matchPatternSlow(pattern_component, filepath), - .Literal => if (comptime !isWindows) - matchWildcardLiteral(pattern_component.patternSlice(this.pattern), filepath) - else - this.matchPatternSlow(pattern_component, filepath), - else => this.matchPatternSlow(pattern_component, filepath), - }; - } - - fn matchPatternSlow(this: *GlobWalker, pattern_component: *Component, filepath: []const u8) bool { - // windows filepaths are utf-16 so GlobAscii.match will never work - if (comptime !isWindows) { - if (pattern_component.is_ascii and isAllAscii(filepath)) - return GlobAscii.match( - pattern_component.patternSlice(this.pattern), - filepath, - ); - } - const codepoints = this.componentStringUnicode(pattern_component); - return matchImpl( - codepoints, - filepath, - ).matches(); - } - - fn componentStringUnicode(this: *GlobWalker, pattern_component: *Component) []const u32 { - if (comptime isWindows) { - return this.componentStringUnicodeWindows(pattern_component); - } else { - return this.componentStringUnicodePosix(pattern_component); - } - } - - fn componentStringUnicodeWindows(this: *GlobWalker, pattern_component: *Component) []const u32 { - return pattern_component.patternSliceCp(this.pattern_codepoints); - } - - fn componentStringUnicodePosix(this: *GlobWalker, pattern_component: *Component) []const u32 { - if (pattern_component.unicode_set) return pattern_component.patternSliceCp(this.pattern_codepoints); - - const codepoints = pattern_component.patternSliceCp(this.pattern_codepoints); - GlobWalker.convertUtf8ToCodepoints( - codepoints, - pattern_component.patternSlice(this.pattern), - ); - pattern_component.unicode_set = true; - return codepoints; - } - - inline fn matchedPathToBunString(matched_path: MatchedPath) BunString { - if (comptime sentinel) { - return BunString.fromBytes(matched_path[0 .. matched_path.len + 1]); - } - return BunString.fromBytes(matched_path); - } - - fn prepareMatchedPathSymlink(this: *GlobWalker, symlink_full_path: []const u8) !?MatchedPath { - const result = try this.matchedPaths.getOrPut(this.arena.allocator(), BunString.fromBytes(symlink_full_path)); - if (result.found_existing) { - log("(dupe) prepared match: {s}", .{symlink_full_path}); - return null; - } - if (comptime !sentinel) { - const slice = try this.arena.allocator().dupe(u8, symlink_full_path); - result.key_ptr.* = matchedPathToBunString(slice); - return slice; - } - const slicez = try this.arena.allocator().dupeZ(u8, symlink_full_path); - result.key_ptr.* = matchedPathToBunString(slicez); - return slicez; - } - - fn prepareMatchedPath(this: *GlobWalker, entry_name: []const u8, dir_name: []const u8) !?MatchedPath { - const subdir_parts: []const []const u8 = &[_][]const u8{ - dir_name[0..dir_name.len], - entry_name, - }; - const name_matched_path = try this.join(subdir_parts); - const name = matchedPathToBunString(name_matched_path); - const result = try this.matchedPaths.getOrPutValue(this.arena.allocator(), name, {}); - if (result.found_existing) { - log("(dupe) prepared match: {s}", .{name_matched_path}); - this.arena.allocator().free(name_matched_path); - return null; - } - result.key_ptr.* = name; - // if (comptime sentinel) return name[0 .. name.len - 1 :0]; - log("prepared match: {s}", .{name_matched_path}); - return name_matched_path; - } - - fn appendMatchedPath( - this: *GlobWalker, - entry_name: []const u8, - dir_name: [:0]const u8, - ) !void { - const subdir_parts: []const []const u8 = &[_][]const u8{ - dir_name[0..dir_name.len], - entry_name, - }; - const name_matched_path = try this.join(subdir_parts); - const name = matchedPathToBunString(name_matched_path); - const result = try this.matchedPaths.getOrPut(this.arena.allocator(), name); - if (result.found_existing) { - this.arena.allocator().free(name_matched_path); - log("(dupe) prepared match: {s}", .{name_matched_path}); - return; - } - result.key_ptr.* = name; - } - - fn appendMatchedPathSymlink(this: *GlobWalker, symlink_full_path: []const u8) !void { - const name = try this.arena.allocator().dupe(u8, symlink_full_path); - try this.matchedPaths.put(this.arena.allocator(), BunString.fromBytes(name), {}); - } - - inline fn join(this: *GlobWalker, subdir_parts: []const []const u8) !MatchedPath { - if (!this.absolute) { - // If relative paths enabled, stdlib join is preferred over - // ResolvePath.joinBuf because it doesn't try to normalize the path - return try stdJoin(this.arena.allocator(), subdir_parts); - } - - const out = try this.arena.allocator().dupe(u8, bunJoin(subdir_parts, .auto)); - if (comptime sentinel) return out[0 .. out.len - 1 :0]; - - return out; - } - - inline fn startsWithDot(filepath: []const u8) bool { - return filepath.len > 0 and filepath[0] == '.'; - } - - fn checkSpecialSyntax(pattern: []const u8) bool { - if (pattern.len < 16) { - for (pattern[0..]) |c| { - switch (c) { - '*', '[', '{', '?', '!' => return true, - else => {}, - } - } - return false; - } - - const syntax_tokens = comptime [_]u8{ '*', '[', '{', '?', '!' }; - const needles: [syntax_tokens.len]@Vector(16, u8) = comptime needles: { - var needles: [syntax_tokens.len]@Vector(16, u8) = undefined; - for (syntax_tokens, 0..) |tok, i| { - needles[i] = @splat(tok); - } - break :needles needles; - }; - - var i: usize = 0; - while (i + 16 <= pattern.len) : (i += 16) { - const haystack: @Vector(16, u8) = pattern[i..][0..16].*; - inline for (needles) |needle| { - if (std.simd.firstTrue(needle == haystack) != null) return true; - } - } - - if (i < pattern.len) { - for (pattern[i..]) |c| { - inline for (syntax_tokens) |tok| { - if (c == tok) return true; - } - } - } - - return false; - } - - fn makeComponent( - pattern: []const u8, - start_cp: u32, - end_cp: u32, - start_byte: u32, - end_byte: u32, - has_relative_patterns: *bool, - ) ?Component { - var component: Component = .{ - .start = start_byte, - .len = end_byte - start_byte, - .start_cp = start_cp, - .end_cp = end_cp, - }; - if (component.len == 0) return null; - - out: { - if (component.len == 1 and pattern[component.start] == '.') { - component.syntax_hint = .Dot; - has_relative_patterns.* = true; - break :out; - } - if (component.len == 2 and pattern[component.start] == '.' and pattern[component.start] == '.') { - component.syntax_hint = .DotBack; - has_relative_patterns.* = true; - break :out; - } - - if (!GlobWalker.checkSpecialSyntax(pattern[component.start .. component.start + component.len])) { - component.syntax_hint = .Literal; - break :out; - } - - switch (component.len) { - 1 => { - if (pattern[component.start] == '*') { - component.syntax_hint = .Single; - } - break :out; - }, - 2 => { - if (pattern[component.start] == '*' and pattern[component.start + 1] == '*') { - component.syntax_hint = .Double; - break :out; - } - }, - else => {}, - } - - out_of_check_wildcard_filepath: { - if (component.len > 1 and - pattern[component.start] == '*' and - pattern[component.start + 1] == '.' and - component.start + 2 < pattern.len) - { - for (pattern[component.start + 2 ..]) |c| { - switch (c) { - // The fast path checks that path[1..] == pattern[1..], - // this will obviously not work if additional - // glob syntax is present in the pattern, so we - // must not apply this optimization if we see - // special glob syntax. - // - // This is not a complete check, there can be - // false negatives, but that's okay, it just - // means we don't apply the optimization. - // - // We also don't need to look for the `!` token, - // because that only applies negation if at the - // beginning of the string. - '[', '{', '?', '*' => break :out_of_check_wildcard_filepath, - else => {}, - } - } - component.syntax_hint = .WildcardFilepath; - break :out; - } - } - } - - if (component.syntax_hint != .Single and component.syntax_hint != .Double) { - if (isAllAscii(pattern[component.start .. component.start + component.len])) { - component.is_ascii = true; - } - } else { - component.is_ascii = true; - } - - if (pattern[component.start + component.len -| 1] == '/') { - component.trailing_sep = true; - } else if (comptime bun.Environment.isWindows) { - component.trailing_sep = pattern[component.start + component.len -| 1] == '\\'; - } - - return component; - } - - fn buildPatternComponents( - arena: *Arena, - patternComponents: *ArrayList(Component), - pattern: []const u8, - out_cp_len: *u32, - out_pattern_cp: *[]u32, - has_relative_patterns: *bool, - end_byte_of_basename_excluding_special_syntax: *u32, - basename_excluding_special_syntax_component_idx: *u32, - ) !void { - var start_cp: u32 = 0; - var start_byte: u32 = 0; - - const iter = CodepointIterator.init(pattern); - var cursor = CodepointIterator.Cursor{}; - - var cp_len: u32 = 0; - var prevIsBackslash = false; - var saw_special = false; - while (iter.next(&cursor)) : (cp_len += 1) { - const c = cursor.c; - - switch (c) { - '\\' => { - if (comptime isWindows) { - var end_cp = cp_len; - var end_byte = cursor.i; - // is last char - if (cursor.i + cursor.width == pattern.len) { - end_cp += 1; - end_byte += cursor.width; - } - if (makeComponent( - pattern, - start_cp, - end_cp, - start_byte, - end_byte, - has_relative_patterns, - )) |component| { - saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); - if (!saw_special) { - basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; - } - try patternComponents.append(arena.allocator(), component); - } - start_cp = cp_len + 1; - start_byte = cursor.i + cursor.width; - continue; - } - - if (prevIsBackslash) { - prevIsBackslash = false; - continue; - } - - prevIsBackslash = true; - }, - '/' => { - var end_cp = cp_len; - var end_byte = cursor.i; - // is last char - if (cursor.i + cursor.width == pattern.len) { - end_cp += 1; - end_byte += cursor.width; - } - if (makeComponent( - pattern, - start_cp, - end_cp, - start_byte, - end_byte, - has_relative_patterns, - )) |component| { - saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); - if (!saw_special) { - basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; - } - try patternComponents.append(arena.allocator(), component); - } - start_cp = cp_len + 1; - start_byte = cursor.i + cursor.width; - }, - // TODO: Support other escaping glob syntax - else => {}, - } - } - - out_cp_len.* = cp_len; - - const codepoints = try arena.allocator().alloc(u32, cp_len); - // On Windows filepaths are UTF-16 so its better to fill the codepoints buffer upfront - if (comptime isWindows) { - GlobWalker.convertUtf8ToCodepoints(codepoints, pattern); - } - out_pattern_cp.* = codepoints; - - const end_cp = cp_len; - if (makeComponent( - pattern, - start_cp, - end_cp, - start_byte, - @intCast(pattern.len), - has_relative_patterns, - )) |component| { - saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); - if (!saw_special) { - basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; - } - try patternComponents.append(arena.allocator(), component); - } else if (!saw_special) { - basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; - } - } - }; -} - -// From: https://github.com/The-King-of-Toasters/globlin -/// State for matching a glob against a string -pub const GlobState = struct { - // These store character indices into the glob and path strings. - path_index: CursorState = .{}, - glob_index: u32 = 0, - // When we hit a * or **, we store the state for backtracking. - wildcard: Wildcard = .{}, - globstar: Wildcard = .{}, - - fn init(path_iter: *const CodepointIterator) GlobState { - var this = GlobState{}; - // this.glob_index = CursorState.init(glob_iter); - this.path_index = CursorState.init(path_iter); - return this; - } - - fn skipBraces(self: *GlobState, glob: []const u32, stop_on_comma: bool) BraceState { - var braces: u32 = 1; - var in_brackets = false; - while (self.glob_index < glob.len and braces > 0) : (self.glob_index += 1) { - switch (glob[self.glob_index]) { - // Skip nested braces - '{' => if (!in_brackets) { - braces += 1; - }, - '}' => if (!in_brackets) { - braces -= 1; - }, - ',' => if (stop_on_comma and braces == 1 and !in_brackets) { - self.glob_index += 1; - return .Comma; - }, - '*', '?', '[' => |c| if (!in_brackets) { - if (c == '[') - in_brackets = true; - }, - ']' => in_brackets = false, - '\\' => self.glob_index += 1, - else => {}, - } - } - - if (braces != 0) - return .Invalid; - return .EndBrace; - } - - inline fn backtrack(self: *GlobState) void { - self.glob_index = self.wildcard.glob_index; - self.path_index = self.wildcard.path_index; - } -}; - -const Wildcard = struct { - // Using u32 rather than usize for these results in 10% faster performance. - // glob_index: CursorState = .{}, - glob_index: u32 = 0, - path_index: CursorState = .{}, -}; - -const BraceState = enum { Invalid, Comma, EndBrace }; - -const BraceStack = struct { - stack: [10]GlobState = undefined, - len: u32 = 0, - longest_brace_match: CursorState = .{}, - - inline fn push(self: *BraceStack, state: *const GlobState) GlobState { - self.stack[self.len] = state.*; - self.len += 1; - return GlobState{ - .path_index = state.path_index, - .glob_index = state.glob_index + 1, - }; - } - - inline fn pop(self: *BraceStack, state: *const GlobState) GlobState { - self.len -= 1; - const s = GlobState{ - .glob_index = state.glob_index, - .path_index = self.longest_brace_match, - // Restore star state if needed later. - .wildcard = self.stack[self.len].wildcard, - .globstar = self.stack[self.len].globstar, - }; - if (self.len == 0) - self.longest_brace_match = .{}; - return s; - } - - inline fn last(self: *const BraceStack) *const GlobState { - return &self.stack[self.len - 1]; - } -}; - -pub const MatchResult = enum { - no_match, - match, - - negate_no_match, - negate_match, - - pub fn matches(this: MatchResult) bool { - return this == .match or this == .negate_match; - } -}; - -/// This function checks returns a boolean value if the pathname `path` matches -/// the pattern `glob`. -/// -/// The supported pattern syntax for `glob` is: -/// -/// "?" -/// Matches any single character. -/// "*" -/// Matches zero or more characters, except for path separators ('/' or '\'). -/// "**" -/// Matches zero or more characters, including path separators. -/// Must match a complete path segment, i.e. followed by a path separator or -/// at the end of the pattern. -/// "[ab]" -/// Matches one of the characters contained in the brackets. -/// Character ranges (e.g. "[a-z]") are also supported. -/// Use "[!ab]" or "[^ab]" to match any character *except* those contained -/// in the brackets. -/// "{a,b}" -/// Match one of the patterns contained in the braces. -/// Any of the wildcards listed above can be used in the sub patterns. -/// Braces may be nested up to 10 levels deep. -/// "!" -/// Negates the result when at the start of the pattern. -/// Multiple "!" characters negate the pattern multiple times. -/// "\" -/// Used to escape any of the special characters above. -pub fn matchImpl(glob: []const u32, path: []const u8) MatchResult { - const path_iter = CodepointIterator.init(path); - - // This algorithm is based on https://research.swtch.com/glob - var state = GlobState.init(&path_iter); - // Store the state when we see an opening '{' brace in a stack. - // Up to 10 nested braces are supported. - var brace_stack = BraceStack{}; - - // First, check if the pattern is negated with a leading '!' character. - // Multiple negations can occur. - var negated = false; - while (state.glob_index < glob.len and glob[state.glob_index] == '!') { - negated = !negated; - state.glob_index += 1; - } - - while (state.glob_index < glob.len or state.path_index.cursor.i < path.len) { - if (state.glob_index < glob.len) { - switch (glob[state.glob_index]) { - '*' => { - const is_globstar = state.glob_index + 1 < glob.len and glob[state.glob_index + 1] == '*'; - // const is_globstar = state.glob_index.cursor.i + state.glob_index.cursor.width < glob.len and - // state.glob_index.peek(&glob_iter).cursor.c == '*'; - if (is_globstar) { - // Coalesce multiple ** segments into one. - var index = state.glob_index + 2; - state.glob_index = skipGlobstars(glob, &index) - 2; - } - - state.wildcard.glob_index = state.glob_index; - state.wildcard.path_index = state.path_index.peek(&path_iter); - - // ** allows path separators, whereas * does not. - // However, ** must be a full path component, i.e. a/**/b not a**b. - if (is_globstar) { - // Skip wildcards - state.glob_index += 2; - - if (glob.len == state.glob_index) { - // A trailing ** segment without a following separator. - state.globstar = state.wildcard; - } else if (glob[state.glob_index] == '/' and - (state.glob_index < 3 or glob[state.glob_index - 3] == '/')) - { - // Matched a full /**/ segment. If the last character in the path was a separator, - // skip the separator in the glob so we search for the next character. - // In effect, this makes the whole segment optional so that a/**/b matches a/b. - if (state.path_index.cursor.i == 0 or - (state.path_index.cursor.i < path.len and - isSeparator(path[state.path_index.cursor.i - 1]))) - { - state.glob_index += 1; - } - - // The allows_sep flag allows separator characters in ** matches. - // one is a '/', which prevents a/**/b from matching a/bb. - state.globstar = state.wildcard; - } - } else { - state.glob_index += 1; - } - - // If we are in a * segment and hit a separator, - // either jump back to a previous ** or end the wildcard. - if (state.globstar.path_index.cursor.i != state.wildcard.path_index.cursor.i and - state.path_index.cursor.i < path.len and - isSeparator(state.path_index.cursor.c)) - { - // Special case: don't jump back for a / at the end of the glob. - if (state.globstar.path_index.cursor.i > 0 and state.path_index.cursor.i + state.path_index.cursor.width < path.len) { - state.glob_index = state.globstar.glob_index; - state.wildcard.glob_index = state.globstar.glob_index; - } else { - state.wildcard.path_index.cursor.i = 0; - } - } - - // If the next char is a special brace separator, - // skip to the end of the braces so we don't try to match it. - if (brace_stack.len > 0 and - state.glob_index < glob.len and - (glob[state.glob_index] == ',' or glob[state.glob_index] == '}')) - { - if (state.skipBraces(glob, false) == .Invalid) - return .no_match; // invalid pattern! - } - - continue; - }, - '?' => if (state.path_index.cursor.i < path.len) { - if (!isSeparator(state.path_index.cursor.c)) { - state.glob_index += 1; - state.path_index.bump(&path_iter); - continue; - } - }, - '[' => if (state.path_index.cursor.i < path.len) { - state.glob_index += 1; - const c = state.path_index.cursor.c; - - // Check if the character class is negated. - var class_negated = false; - if (state.glob_index < glob.len and - (glob[state.glob_index] == '^' or glob[state.glob_index] == '!')) - { - class_negated = true; - state.glob_index += 1; - } - - // Try each range. - var first = true; - var is_match = false; - while (state.glob_index < glob.len and (first or glob[state.glob_index] != ']')) { - var low = glob[state.glob_index]; - if (!unescape(&low, glob, &state.glob_index)) - return .no_match; // Invalid pattern - state.glob_index += 1; - - // If there is a - and the following character is not ], - // read the range end character. - const high = if (state.glob_index + 1 < glob.len and - glob[state.glob_index] == '-' and glob[state.glob_index + 1] != ']') - blk: { - state.glob_index += 1; - var h = glob[state.glob_index]; - if (!unescape(&h, glob, &state.glob_index)) - return .no_match; // Invalid pattern! - state.glob_index += 1; - break :blk h; - } else low; - - if (low <= c and c <= high) - is_match = true; - first = false; - } - if (state.glob_index >= glob.len) - return .no_match; // Invalid pattern! - state.glob_index += 1; - if (is_match != class_negated) { - state.path_index.bump(&path_iter); - continue; - } - }, - '{' => if (state.path_index.cursor.i < path.len) { - if (brace_stack.len >= brace_stack.stack.len) - return .no_match; // Invalid pattern! Too many nested braces. - - // Push old state to the stack, and reset current state. - state = brace_stack.push(&state); - continue; - }, - '}' => if (brace_stack.len > 0) { - // If we hit the end of the braces, we matched the last option. - brace_stack.longest_brace_match = if (state.path_index.cursor.i >= brace_stack.longest_brace_match.cursor.i) - state.path_index - else - brace_stack.longest_brace_match; - state.glob_index += 1; - state = brace_stack.pop(&state); - continue; - }, - ',' => if (brace_stack.len > 0) { - // If we hit a comma, we matched one of the options! - // But we still need to check the others in case there is a longer match. - brace_stack.longest_brace_match = if (state.path_index.cursor.i >= brace_stack.longest_brace_match.cursor.i) - state.path_index - else - brace_stack.longest_brace_match; - state.path_index = brace_stack.last().path_index; - state.glob_index += 1; - state.wildcard = Wildcard{}; - state.globstar = Wildcard{}; - continue; - }, - else => |c| if (state.path_index.cursor.i < path.len) { - var cc = c; - // Match escaped characters as literals. - if (!unescape(&cc, glob, &state.glob_index)) - return .no_match; // Invalid pattern; - - const is_match = if (cc == '/') - isSeparator(state.path_index.cursor.c) - else - state.path_index.cursor.c == cc; - - if (is_match) { - if (brace_stack.len > 0 and - state.glob_index > 0 and - glob[state.glob_index - 1] == '}') - { - brace_stack.longest_brace_match = state.path_index; - state = brace_stack.pop(&state); - } - state.glob_index += 1; - state.path_index.bump(&path_iter); - - // If this is not a separator, lock in the previous globstar. - if (cc != '/') - state.globstar.path_index.cursor.i = 0; - - continue; - } - }, - } - } - // If we didn't match, restore state to the previous star pattern. - if (state.wildcard.path_index.cursor.i > 0 and state.wildcard.path_index.cursor.i <= path.len) { - state.backtrack(); - continue; - } - - if (brace_stack.len > 0) { - // If in braces, find next option and reset path to index where we saw the '{' - switch (state.skipBraces(glob, true)) { - .Invalid => return .no_match, - .Comma => { - state.path_index = brace_stack.last().path_index; - continue; - }, - .EndBrace => {}, - } - - // Hit the end. Pop the stack. - // If we matched a previous option, use that. - if (brace_stack.longest_brace_match.cursor.i > 0) { - state = brace_stack.pop(&state); - continue; - } else { - // Didn't match. Restore state, and check if we need to jump back to a star pattern. - state = brace_stack.last().*; - brace_stack.len -= 1; - if (state.wildcard.path_index.cursor.i > 0 and state.wildcard.path_index.cursor.i <= path.len) { - state.backtrack(); - continue; - } - } - } - - return if (negated) .negate_match else .no_match; - } - - return if (!negated) .match else .negate_no_match; -} - -pub inline fn isSeparator(c: Codepoint) bool { - if (comptime @import("builtin").os.tag == .windows) return c == '/' or c == '\\'; - return c == '/'; -} - -inline fn unescape(c: *u32, glob: []const u32, glob_index: *u32) bool { - if (c.* == '\\') { - glob_index.* += 1; - if (glob_index.* >= glob.len) - return false; // Invalid pattern! - - c.* = switch (glob[glob_index.*]) { - 'a' => '\x61', - 'b' => '\x08', - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - else => |cc| cc, - }; - } - - return true; -} - -const GLOB_STAR_MATCH_STR: []const u32 = &[_]u32{ '/', '*', '*' }; -// src/**/**/foo.ts -inline fn skipGlobstars(glob: []const u32, glob_index: *u32) u32 { - // Coalesce multiple ** segments into one. - while (glob_index.* + 3 <= glob.len and - // std.mem.eql(u8, glob[glob_index.*..][0..3], "/**")) - std.mem.eql(u32, glob[glob_index.*..][0..3], GLOB_STAR_MATCH_STR)) - { - glob_index.* += 3; - } - - return glob_index.*; -} - -const MatchAscii = struct {}; - -pub fn matchWildcardFilepath(glob: []const u8, path: []const u8) bool { - const needle = glob[1..]; - const needle_len: u32 = @intCast(needle.len); - if (path.len < needle_len) return false; - return std.mem.eql(u8, needle, path[path.len - needle_len ..]); -} - -pub fn matchWildcardLiteral(literal: []const u8, path: []const u8) bool { - return std.mem.eql(u8, literal, path); -} - -/// Returns true if the given string contains glob syntax, -/// excluding those escaped with backslashes -/// TODO: this doesn't play nicely with Windows directory separator and -/// backslashing, should we just require the user to supply posix filepaths? -pub fn detectGlobSyntax(potential_pattern: []const u8) bool { - // Negation only allowed in the beginning of the pattern - if (potential_pattern.len > 0 and potential_pattern[0] == '!') return true; - - // In descending order of how popular the token is - const SPECIAL_SYNTAX: [4]u8 = comptime [_]u8{ '*', '{', '[', '?' }; - - inline for (SPECIAL_SYNTAX) |token| { - var slice = potential_pattern[0..]; - while (slice.len > 0) { - if (std.mem.indexOfScalar(u8, slice, token)) |idx| { - // Check for even number of backslashes preceding the - // token to know that it's not escaped - var i = idx; - var backslash_count: u16 = 0; - - while (i > 0 and potential_pattern[i - 1] == '\\') : (i -= 1) { - backslash_count += 1; - } - - if (backslash_count % 2 == 0) return true; - slice = slice[idx + 1 ..]; - } else break; - } - } - - return false; -} +pub const GlobWalker = walk.GlobWalker_; +pub const BunGlobWalker = GlobWalker(null, walk.SyscallAccessor, false); +pub const BunGlobWalkerZ = GlobWalker(null, walk.SyscallAccessor, true); diff --git a/src/glob/GlobWalker.zig b/src/glob/GlobWalker.zig new file mode 100644 index 0000000000..f41b47f7a6 --- /dev/null +++ b/src/glob/GlobWalker.zig @@ -0,0 +1,2191 @@ +// Portions of this file are derived from works under the MIT License: +// +// Copyright (c) 2023 Devon Govett +// Copyright (c) 2023 Stephen Gregoratto +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +const std = @import("std"); +const bun = @import("root").bun; + +const eqlComptime = @import("../string_immutable.zig").eqlComptime; +const expect = std.testing.expect; +const isAllAscii = @import("../string_immutable.zig").isAllASCII; +const math = std.math; +const mem = std.mem; +const isWindows = @import("builtin").os.tag == .windows; + +const Allocator = std.mem.Allocator; +const Arena = std.heap.ArenaAllocator; +const ArrayList = std.ArrayListUnmanaged; +const ArrayListManaged = std.ArrayList; +const BunString = bun.String; +const C = @import("../c.zig"); +const CodepointIterator = @import("../string_immutable.zig").PackedCodepointIterator; +const Codepoint = CodepointIterator.Cursor.CodePointType; +const Dirent = @import("../bun.js/node/types.zig").Dirent; +const DirIterator = @import("../bun.js/node/dir_iterator.zig"); +const EntryKind = @import("../bun.js/node/types.zig").Dirent.Kind; +const GlobAscii = @import("./ascii.zig"); +const JSC = bun.JSC; +const Maybe = JSC.Maybe; +const PathLike = @import("../bun.js/node/types.zig").PathLike; +const PathString = @import("../string_types.zig").PathString; +const ResolvePath = @import("../resolver/resolve_path.zig"); +const Syscall = bun.sys; +const ZigString = @import("../bun.js/bindings/bindings.zig").ZigString; + +// const Codepoint = u32; +const Cursor = CodepointIterator.Cursor; + +const log = bun.Output.scoped(.Glob, false); + +const CursorState = struct { + cursor: CodepointIterator.Cursor = .{}, + /// The index in terms of codepoints + // cp_idx: usize, + + fn init(iterator: *const CodepointIterator) CursorState { + var this_cursor: CodepointIterator.Cursor = .{}; + _ = iterator.next(&this_cursor); + return .{ + // .cp_idx = 0, + .cursor = this_cursor, + }; + } + + /// Return cursor pos of next codepoint without modifying the current. + /// + /// NOTE: If there is no next codepoint (cursor is at the last one), then + /// the returned cursor will have `c` as zero value and `i` will be >= + /// sourceBytes.len + fn peek(this: *const CursorState, iterator: *const CodepointIterator) CursorState { + var cpy = this.*; + // If outside of bounds + if (!iterator.next(&cpy.cursor)) { + // This will make `i >= sourceBytes.len` + cpy.cursor.i += cpy.cursor.width; + cpy.cursor.width = 1; + cpy.cursor.c = CodepointIterator.ZeroValue; + } + // cpy.cp_idx += 1; + return cpy; + } + + fn bump(this: *CursorState, iterator: *const CodepointIterator) void { + if (!iterator.next(&this.cursor)) { + this.cursor.i += this.cursor.width; + this.cursor.width = 1; + this.cursor.c = CodepointIterator.ZeroValue; + } + // this.cp_idx += 1; + } + + inline fn manualBumpAscii(this: *CursorState, i: u32, nextCp: Codepoint) void { + this.cursor.i += i; + this.cursor.c = nextCp; + this.cursor.width = 1; + } + + inline fn manualPeekAscii(this: *CursorState, i: u32, nextCp: Codepoint) CursorState { + return .{ + .cursor = CodepointIterator.Cursor{ + .i = this.cursor.i + i, + .c = @truncate(nextCp), + .width = 1, + }, + }; + } +}; + +fn dummyFilterTrue(val: []const u8) bool { + _ = val; + return true; +} + +fn dummyFilterFalse(val: []const u8) bool { + _ = val; + return false; +} + +pub fn statatWindows(fd: bun.FileDescriptor, path: [:0]const u8) Maybe(bun.Stat) { + if (comptime !bun.Environment.isWindows) @compileError("oi don't use this"); + var buf: bun.PathBuffer = undefined; + const dir = switch (Syscall.getFdPath(fd, &buf)) { + .err => |e| return .{ .err = e }, + .result => |s| s, + }; + const parts: []const []const u8 = &.{ + dir[0..dir.len], + path, + }; + const statpath = ResolvePath.joinZBuf(&buf, parts, .auto); + return Syscall.stat(statpath); +} + +pub const SyscallAccessor = struct { + const count_fds = true; + + const Handle = struct { + value: bun.FileDescriptor, + + const zero = Handle{ .value = bun.FileDescriptor.zero }; + + pub fn isZero(this: Handle) bool { + return this.value == bun.FileDescriptor.zero; + } + + pub fn eql(this: Handle, other: Handle) bool { + return this.value == other.value; + } + }; + + const DirIter = struct { + value: DirIterator.WrappedIterator, + + pub inline fn next(self: *DirIter) Maybe(?DirIterator.IteratorResult) { + return self.value.next(); + } + + pub inline fn iterate(dir: Handle) DirIter { + return .{ .value = DirIterator.WrappedIterator.init(dir.value.asDir()) }; + } + }; + + pub fn open(path: [:0]const u8) !Maybe(Handle) { + return switch (Syscall.open(path, bun.O.DIRECTORY | bun.O.RDONLY, 0)) { + .err => |err| .{ .err = err }, + .result => |fd| .{ .result = Handle{ .value = fd } }, + }; + } + + pub fn statat(handle: Handle, path: [:0]const u8) Maybe(bun.Stat) { + if (comptime bun.Environment.isWindows) return statatWindows(handle.value, path); + return switch (Syscall.fstatat(handle.value, path)) { + .err => |err| .{ .err = err }, + .result => |s| .{ .result = s }, + }; + } + + pub fn openat(handle: Handle, path: [:0]const u8) !Maybe(Handle) { + return switch (Syscall.openat(handle.value, path, bun.O.DIRECTORY | bun.O.RDONLY, 0)) { + .err => |err| .{ .err = err }, + .result => |fd| .{ .result = Handle{ .value = fd } }, + }; + } + + pub fn close(handle: Handle) ?Syscall.Error { + return Syscall.close(handle.value); + } + + pub fn getcwd(path_buf: *bun.PathBuffer) Maybe([]const u8) { + return Syscall.getcwd(path_buf); + } +}; + +pub const DirEntryAccessor = struct { + const FS = bun.fs.FileSystem; + + const count_fds = false; + + const Handle = struct { + value: ?*FS.DirEntry, + + const zero = Handle{ .value = null }; + + pub fn isZero(this: Handle) bool { + return this.value == null; + } + + pub fn eql(this: Handle, other: Handle) bool { + // TODO this might not be quite right, we're comparing pointers, not the underlying directory + // On the other hand, DirEntries are only ever created once (per generation), so this should be fine? + // Realistically, as closing the handle is a no-op, this should be fine either way. + return this.value == other.value; + } + }; + + const DirIter = struct { + value: ?FS.DirEntry.EntryMap.Iterator, + + const IterResult = struct { + name: NameWrapper, + kind: std.fs.File.Kind, + + const NameWrapper = struct { + value: []const u8, + + pub fn slice(this: NameWrapper) []const u8 { + return this.value; + } + }; + }; + + pub inline fn next(self: *DirIter) Maybe(?IterResult) { + if (self.value) |*value| { + const nextval = value.next() orelse return .{ .result = null }; + const name = nextval.key_ptr.*; + const kind = nextval.value_ptr.*.kind(&FS.instance.fs, true); + const fskind = switch (kind) { + .file => std.fs.File.Kind.file, + .dir => std.fs.File.Kind.directory, + }; + return .{ + .result = .{ + .name = IterResult.NameWrapper{ .value = name }, + .kind = fskind, + }, + }; + } else { + return .{ .result = null }; + } + } + + pub inline fn iterate(dir: Handle) DirIter { + const entry = dir.value orelse return DirIter{ .value = null }; + return .{ .value = entry.data.iterator() }; + } + }; + + pub fn statat(handle: Handle, path_: [:0]const u8) Maybe(bun.Stat) { + var path: [:0]const u8 = path_; + var buf: bun.PathBuffer = undefined; + if (!bun.path.Platform.auto.isAbsolute(path)) { + if (handle.value) |entry| { + const slice = bun.path.joinStringBuf(&buf, [_][]const u8{ entry.dir, path }, .auto); + buf[slice.len] = 0; + path = buf[0..slice.len :0]; + } + } + return Syscall.stat(path); + } + + pub fn open(path: [:0]const u8) !Maybe(Handle) { + return openat(Handle.zero, path); + } + + pub fn openat(handle: Handle, path_: [:0]const u8) !Maybe(Handle) { + var path: []const u8 = path_; + var buf: bun.PathBuffer = undefined; + + if (!bun.path.Platform.auto.isAbsolute(path)) { + if (handle.value) |entry| { + path = bun.path.joinStringBuf(&buf, [_][]const u8{ entry.dir, path }, .auto); + } + } + // TODO do we want to propagate ENOTDIR through the 'Maybe' to match the SyscallAccessor? + // The glob implementation specifically checks for this error when dealing with symlinks + // return .{ .err = Syscall.Error.fromCode(bun.C.E.NOTDIR, Syscall.Tag.open) }; + const res = FS.instance.fs.readDirectory(path, null, 0, false) catch |err| { + return err; + }; + switch (res.*) { + .entries => |entry| { + return .{ .result = Handle{ .value = entry } }; + }, + .err => |err| { + return err.original_err; + }, + } + } + + pub inline fn close(handle: Handle) ?Syscall.Error { + // TODO is this a noop? + _ = handle; + return null; + } + + pub fn getcwd(path_buf: *bun.PathBuffer) Maybe([]const u8) { + @memcpy(path_buf, bun.fs.FileSystem.instance.fs.cwd); + } +}; + +pub fn GlobWalker_( + comptime ignore_filter_fn: ?*const fn ([]const u8) bool, + comptime Accessor: type, + comptime sentinel: bool, +) type { + const is_ignored: *const fn ([]const u8) bool = if (comptime ignore_filter_fn) |func| func else dummyFilterFalse; + + const count_fds = Accessor.count_fds and bun.Environment.isDebug; + + const stdJoin = comptime if (!sentinel) std.fs.path.join else std.fs.path.joinZ; + const bunJoin = comptime if (!sentinel) ResolvePath.join else ResolvePath.joinZ; + const MatchedPath = comptime if (!sentinel) []const u8 else [:0]const u8; + + return struct { + const GlobWalker = @This(); + pub const Result = Maybe(void); + + arena: Arena = undefined, + + /// not owned by this struct + pattern: []const u8 = "", + + pattern_codepoints: []u32 = &[_]u32{}, + cp_len: u32 = 0, + + /// If the pattern contains "./" or "../" + has_relative_components: bool = false, + + end_byte_of_basename_excluding_special_syntax: u32 = 0, + basename_excluding_special_syntax_component_idx: u32 = 0, + + patternComponents: ArrayList(Component) = .{}, + matchedPaths: MatchedMap = .{}, + i: u32 = 0, + + dot: bool = false, + absolute: bool = false, + + cwd: []const u8 = "", + follow_symlinks: bool = false, + error_on_broken_symlinks: bool = false, + only_files: bool = true, + + pathBuf: bun.PathBuffer = undefined, + // iteration state + workbuf: ArrayList(WorkItem) = ArrayList(WorkItem){}, + + /// Array hashmap used as a set (values are the keys) + /// to store matched paths and prevent duplicates + /// + /// BunString is used so that we can call BunString.toJSArray() + /// on the result of `.keys()` to give the result back to JS + /// + /// The only type of string impl we use is ZigString since + /// all matched paths are UTF-8 (DirIterator converts them on + /// windows) and allocated on the arnea + /// + /// Multiple patterns are not supported so right now this is + /// only possible when running a pattern like: + /// + /// `foo/**/*` + /// + /// Use `.keys()` to get the matched paths + const MatchedMap = std.ArrayHashMapUnmanaged(BunString, void, struct { + pub fn hash(_: @This(), this: BunString) u32 { + bun.assert(this.tag == .ZigString); + const slice = this.byteSlice(); + if (comptime sentinel) { + const slicez = slice[0 .. slice.len - 1 :0]; + return std.array_hash_map.hashString(slicez); + } + + return std.array_hash_map.hashString(slice); + } + + pub fn eql(_: @This(), this: BunString, other: BunString, _: usize) bool { + return this.eql(other); + } + }, true); + + /// The glob walker references the .directory.path so its not safe to + /// copy/move this + const IterState = union(enum) { + /// Pops the next item off the work stack + get_next, + + /// Currently iterating over a directory + directory: Directory, + + /// Two particular cases where this is used: + /// + /// 1. A pattern with no special glob syntax was supplied, for example: `/Users/zackradisic/foo/bar` + /// + /// In that case, the mere existence of the file/dir counts as a match, so we can eschew directory + /// iterating and walking for a simple stat call to the path. + /// + /// 2. Pattern ending in literal optimization + /// + /// With a pattern like: `packages/**/package.json`, once the iteration component index reaches + /// the final component, which is a literal string ("package.json"), we can similarly make a + /// single stat call to complete the pattern. + matched: MatchedPath, + + const Directory = struct { + fd: Accessor.Handle, + iter: Accessor.DirIter, + path: bun.PathBuffer, + dir_path: [:0]const u8, + + component_idx: u32, + pattern: *Component, + next_pattern: ?*Component, + is_last: bool, + + iter_closed: bool = false, + at_cwd: bool = false, + }; + }; + + pub const Iterator = struct { + walker: *GlobWalker, + iter_state: IterState = .get_next, + cwd_fd: Accessor.Handle = Accessor.Handle.zero, + empty_dir_path: [0:0]u8 = [0:0]u8{}, + /// This is to make sure in debug/tests that we are closing file descriptors + /// We should only have max 2 open at a time. One for the cwd, and one for the + /// directory being iterated on. + fds_open: if (count_fds) usize else u0 = 0, + + pub fn init(this: *Iterator) !Maybe(void) { + log("Iterator init pattern={s}", .{this.walker.pattern}); + var was_absolute = false; + const root_work_item = brk: { + var use_posix = bun.Environment.isPosix; + const is_absolute = if (bun.Environment.isPosix) std.fs.path.isAbsolute(this.walker.pattern) else std.fs.path.isAbsolute(this.walker.pattern) or is_absolute: { + use_posix = true; + break :is_absolute std.fs.path.isAbsolutePosix(this.walker.pattern); + }; + + if (!is_absolute) break :brk WorkItem.new(this.walker.cwd, 0, .directory); + + was_absolute = true; + + var path_without_special_syntax = this.walker.pattern[0..this.walker.end_byte_of_basename_excluding_special_syntax]; + var starting_component_idx = this.walker.basename_excluding_special_syntax_component_idx; + + if (path_without_special_syntax.len == 0) { + path_without_special_syntax = if (!bun.Environment.isWindows) "/" else ResolvePath.windowsFilesystemRoot(this.walker.cwd); + } else { + // Skip the components associated with the literal path + starting_component_idx += 1; + + // This means we got a pattern without any special glob syntax, for example: + // `/Users/zackradisic/foo/bar` + // + // In that case we don't need to do any walking and can just open up the FS entry + if (starting_component_idx >= this.walker.patternComponents.items.len) { + const path = try this.walker.arena.allocator().dupeZ(u8, path_without_special_syntax); + const fd = switch (try Accessor.open(path)) { + .err => |e| { + if (e.getErrno() == bun.C.E.NOTDIR) { + this.iter_state = .{ .matched = path }; + return Maybe(void).success; + } + // Doesn't exist + if (e.getErrno() == bun.C.E.NOENT) { + this.iter_state = .get_next; + return Maybe(void).success; + } + const errpath = try this.walker.arena.allocator().dupeZ(u8, path); + return .{ .err = e.withPath(errpath) }; + }, + .result => |fd| fd, + }; + _ = Accessor.close(fd); + this.iter_state = .{ .matched = path }; + return Maybe(void).success; + } + + // In the above branch, if `starting_compoennt_dix >= pattern_components.len` then + // it should also mean that `end_byte_of_basename_excluding_special_syntax >= pattern.len` + // + // So if we see that `end_byte_of_basename_excluding_special_syntax < this.walker.pattern.len` we + // miscalculated the values + bun.assert(this.walker.end_byte_of_basename_excluding_special_syntax < this.walker.pattern.len); + } + + break :brk WorkItem.new( + path_without_special_syntax, + starting_component_idx, + .directory, + ); + }; + + var path_buf: *bun.PathBuffer = &this.walker.pathBuf; + const root_path = root_work_item.path; + @memcpy(path_buf[0..root_path.len], root_path[0..root_path.len]); + path_buf[root_path.len] = 0; + const cwd_fd = switch (try Accessor.open(path_buf[0..root_path.len :0])) { + .err => |err| return .{ .err = this.walker.handleSysErrWithPath(err, @ptrCast(path_buf[0 .. root_path.len + 1])) }, + .result => |fd| fd, + }; + + if (comptime count_fds) { + this.fds_open += 1; + } + + this.cwd_fd = cwd_fd; + + switch (if (was_absolute) try this.transitionToDirIterState( + root_work_item, + false, + ) else try this.transitionToDirIterState( + root_work_item, + true, + )) { + .err => |err| return .{ .err = err }, + else => {}, + } + + return Maybe(void).success; + } + + pub fn deinit(this: *Iterator) void { + defer { + bun.debugAssert(this.fds_open == 0); + } + this.closeCwdFd(); + switch (this.iter_state) { + .directory => |dir| { + if (!dir.iter_closed) { + this.closeDisallowingCwd(dir.fd); + } + }, + else => {}, + } + + while (this.walker.workbuf.popOrNull()) |work_item| { + if (work_item.fd) |fd| { + this.closeDisallowingCwd(fd); + } + } + + if (comptime count_fds) { + bun.debugAssert(this.fds_open == 0); + } + } + + pub fn closeCwdFd(this: *Iterator) void { + if (this.cwd_fd.isZero()) return; + _ = Accessor.close(this.cwd_fd); + if (comptime count_fds) this.fds_open -= 1; + } + + pub fn closeDisallowingCwd(this: *Iterator, fd: Accessor.Handle) void { + if (fd.isZero() or fd.eql(this.cwd_fd)) return; + _ = Accessor.close(fd); + if (comptime count_fds) this.fds_open -= 1; + } + + pub fn bumpOpenFds(this: *Iterator) void { + if (comptime count_fds) { + this.fds_open += 1; + // If this is over 2 then this means that there is a bug in the iterator code + bun.debugAssert(this.fds_open <= 2); + } + } + + fn transitionToDirIterState( + this: *Iterator, + work_item: WorkItem, + comptime root: bool, + ) !Maybe(void) { + log("transition => {s}", .{work_item.path}); + this.iter_state = .{ .directory = .{ + .fd = Accessor.Handle.zero, + .iter = undefined, + .path = undefined, + .dir_path = undefined, + .component_idx = 0, + .pattern = undefined, + .next_pattern = null, + .is_last = false, + .iter_closed = false, + .at_cwd = false, + } }; + + var dir_path: [:0]u8 = dir_path: { + if (comptime root) { + if (!this.walker.absolute) { + this.iter_state.directory.path[0] = 0; + break :dir_path this.iter_state.directory.path[0..0 :0]; + } + } + // TODO Optimization: On posix systems filepaths are already null byte terminated so we can skip this if thats the case + @memcpy(this.iter_state.directory.path[0..work_item.path.len], work_item.path); + this.iter_state.directory.path[work_item.path.len] = 0; + break :dir_path this.iter_state.directory.path[0..work_item.path.len :0]; + }; + + var had_dot_dot = false; + const component_idx = this.walker.skipSpecialComponents(work_item.idx, &dir_path, &this.iter_state.directory.path, &had_dot_dot); + + const fd: Accessor.Handle = fd: { + if (work_item.fd) |fd| break :fd fd; + if (comptime root) { + if (had_dot_dot) break :fd switch (try Accessor.openat(this.cwd_fd, dir_path)) { + .err => |err| return .{ + .err = this.walker.handleSysErrWithPath(err, dir_path), + }, + .result => |fd_| brk: { + this.bumpOpenFds(); + break :brk fd_; + }, + }; + + this.iter_state.directory.at_cwd = true; + break :fd this.cwd_fd; + } + + break :fd switch (try Accessor.openat(this.cwd_fd, dir_path)) { + .err => |err| return .{ + .err = this.walker.handleSysErrWithPath(err, dir_path), + }, + .result => |fd_| brk: { + this.bumpOpenFds(); + break :brk fd_; + }, + }; + }; + + // Optimization: + // If we have a pattern like: + // `packages/*/package.json` + // ^ and we are at this component, with let's say + // a directory named: `packages/frontend/` + // + // Then we can just open `packages/frontend/package.json` without + // doing any iteration on the current directory. + // + // More generally, we can apply this optimization if we are on the + // last component and it is a literal with no special syntax. + if (component_idx == this.walker.patternComponents.items.len -| 1 and + this.walker.patternComponents.items[component_idx].syntax_hint == .Literal) + { + defer { + this.closeDisallowingCwd(fd); + } + const stackbuf_size = 256; + var stfb = std.heap.stackFallback(stackbuf_size, this.walker.arena.allocator()); + const pathz = try stfb.get().dupeZ(u8, this.walker.patternComponents.items[component_idx].patternSlice(this.walker.pattern)); + const stat_result: bun.Stat = switch (Accessor.statat(fd, pathz)) { + .err => |e_| { + var e: bun.sys.Error = e_; + if (e.getErrno() == bun.C.E.NOENT) { + this.iter_state = .get_next; + return Maybe(void).success; + } + return .{ .err = e.withPath(this.walker.patternComponents.items[component_idx].patternSlice(this.walker.pattern)) }; + }, + .result => |stat| stat, + }; + const matches = (bun.S.ISDIR(@intCast(stat_result.mode)) and !this.walker.only_files) or bun.S.ISREG(@intCast(stat_result.mode)) or !this.walker.only_files; + if (matches) { + if (try this.walker.prepareMatchedPath(pathz, dir_path)) |path| { + this.iter_state = .{ .matched = path }; + } else { + this.iter_state = .get_next; + } + } else { + this.iter_state = .get_next; + } + return Maybe(void).success; + } + + this.iter_state.directory.dir_path = dir_path; + this.iter_state.directory.component_idx = component_idx; + this.iter_state.directory.pattern = &this.walker.patternComponents.items[component_idx]; + this.iter_state.directory.next_pattern = if (component_idx + 1 < this.walker.patternComponents.items.len) &this.walker.patternComponents.items[component_idx + 1] else null; + this.iter_state.directory.is_last = component_idx == this.walker.patternComponents.items.len - 1; + this.iter_state.directory.at_cwd = false; + this.iter_state.directory.fd = Accessor.Handle.zero; + + log("Transition(dirpath={s}, fd={}, component_idx={d})", .{ dir_path, fd, component_idx }); + + this.iter_state.directory.fd = fd; + const iterator = Accessor.DirIter.iterate(fd); + this.iter_state.directory.iter = iterator; + this.iter_state.directory.iter_closed = false; + + return Maybe(void).success; + } + + pub fn next(this: *Iterator) !Maybe(?MatchedPath) { + while (true) { + switch (this.iter_state) { + .matched => |path| { + this.iter_state = .get_next; + return .{ .result = path }; + }, + .get_next => { + // Done + if (this.walker.workbuf.items.len == 0) return .{ .result = null }; + const work_item = this.walker.workbuf.pop(); + switch (work_item.kind) { + .directory => { + switch (try this.transitionToDirIterState(work_item, false)) { + .err => |err| return .{ .err = err }, + else => {}, + } + continue; + }, + .symlink => { + var scratch_path_buf: *bun.PathBuffer = &this.walker.pathBuf; + @memcpy(scratch_path_buf[0..work_item.path.len], work_item.path); + scratch_path_buf[work_item.path.len] = 0; + var symlink_full_path_z: [:0]u8 = scratch_path_buf[0..work_item.path.len :0]; + const entry_name = symlink_full_path_z[work_item.entry_start..symlink_full_path_z.len]; + + var has_dot_dot = false; + const component_idx = this.walker.skipSpecialComponents(work_item.idx, &symlink_full_path_z, scratch_path_buf, &has_dot_dot); + var pattern = this.walker.patternComponents.items[component_idx]; + const next_pattern = if (component_idx + 1 < this.walker.patternComponents.items.len) &this.walker.patternComponents.items[component_idx + 1] else null; + const is_last = component_idx == this.walker.patternComponents.items.len - 1; + + this.iter_state = .get_next; + const maybe_dir_fd: ?Accessor.Handle = switch (try Accessor.openat(this.cwd_fd, symlink_full_path_z)) { + .err => |err| brk: { + if (@as(usize, @intCast(err.errno)) == @as(usize, @intFromEnum(bun.C.E.NOTDIR))) { + break :brk null; + } + if (this.walker.error_on_broken_symlinks) return .{ .err = this.walker.handleSysErrWithPath(err, symlink_full_path_z) }; + // Broken symlink, but if `only_files` is false we still want to append + // it to the matched paths + if (!this.walker.only_files) { + // (See case A and B in the comment for `matchPatternFile()`) + // When we encounter a symlink we call the catch all + // matching function: `matchPatternImpl()` to see if we can avoid following the symlink. + // So for case A, we just need to check if the pattern is the last pattern. + if (is_last or + (pattern.syntax_hint == .Double and + component_idx + 1 == this.walker.patternComponents.items.len -| 1 and + next_pattern.?.syntax_hint != .Double and + this.walker.matchPatternImpl(next_pattern.?, entry_name))) + { + return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; + } + } + continue; + }, + .result => |fd| brk: { + this.bumpOpenFds(); + break :brk fd; + }, + }; + + const dir_fd = maybe_dir_fd orelse { + // No directory file descriptor, it's a file + if (is_last) + return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; + + if (pattern.syntax_hint == .Double and + component_idx + 1 == this.walker.patternComponents.items.len -| 1 and + next_pattern.?.syntax_hint != .Double and + this.walker.matchPatternImpl(next_pattern.?, entry_name)) + { + return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; + } + + continue; + }; + + var add_dir: bool = false; + // TODO this function calls `matchPatternImpl(pattern, + // entry_name)` which is redundant because we already called + // that when we first encountered the symlink + const recursion_idx_bump_ = this.walker.matchPatternDir(&pattern, next_pattern, entry_name, component_idx, is_last, &add_dir); + + if (recursion_idx_bump_) |recursion_idx_bump| { + if (recursion_idx_bump == 2) { + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.newWithFd(work_item.path, component_idx + recursion_idx_bump, .directory, dir_fd), + ); + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.newWithFd(work_item.path, component_idx, .directory, dir_fd), + ); + } else { + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.newWithFd(work_item.path, component_idx + recursion_idx_bump, .directory, dir_fd), + ); + } + } + + if (add_dir and !this.walker.only_files) { + return .{ .result = try this.walker.prepareMatchedPathSymlink(symlink_full_path_z) orelse continue }; + } + + continue; + }, + } + }, + .directory => |*dir| { + const entry = switch (dir.iter.next()) { + .err => |err| { + if (!dir.at_cwd) this.closeDisallowingCwd(dir.fd); + dir.iter_closed = true; + return .{ .err = this.walker.handleSysErrWithPath(err, dir.dir_path) }; + }, + .result => |ent| ent, + } orelse { + if (!dir.at_cwd) this.closeDisallowingCwd(dir.fd); + dir.iter_closed = true; + this.iter_state = .get_next; + continue; + }; + log("dir: {s} entry: {s}", .{ dir.dir_path, entry.name.slice() }); + + const dir_iter_state: *const IterState.Directory = &this.iter_state.directory; + + const entry_name = entry.name.slice(); + switch (entry.kind) { + .file => { + const matches = this.walker.matchPatternFile(entry_name, dir_iter_state.component_idx, dir.is_last, dir_iter_state.pattern, dir_iter_state.next_pattern); + if (matches) { + const prepared = try this.walker.prepareMatchedPath(entry_name, dir.dir_path) orelse continue; + return .{ .result = prepared }; + } + continue; + }, + .directory => { + var add_dir: bool = false; + const recursion_idx_bump_ = this.walker.matchPatternDir(dir_iter_state.pattern, dir_iter_state.next_pattern, entry_name, dir_iter_state.component_idx, dir_iter_state.is_last, &add_dir); + + if (recursion_idx_bump_) |recursion_idx_bump| { + const subdir_parts: []const []const u8 = &[_][]const u8{ + dir.dir_path[0..dir.dir_path.len], + entry_name, + }; + + const subdir_entry_name = try this.walker.join(subdir_parts); + + if (recursion_idx_bump == 2) { + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.new(subdir_entry_name, dir_iter_state.component_idx + recursion_idx_bump, .directory), + ); + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.new(subdir_entry_name, dir_iter_state.component_idx, .directory), + ); + } else { + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.new(subdir_entry_name, dir_iter_state.component_idx + recursion_idx_bump, .directory), + ); + } + } + + if (add_dir and !this.walker.only_files) { + const prepared_path = try this.walker.prepareMatchedPath(entry_name, dir.dir_path) orelse continue; + return .{ .result = prepared_path }; + } + + continue; + }, + .sym_link => { + if (this.walker.follow_symlinks) { + // Following a symlink requires additional syscalls, so + // we first try it against our "catch-all" pattern match + // function + const matches = this.walker.matchPatternImpl(dir_iter_state.pattern, entry_name); + if (!matches) continue; + + const subdir_parts: []const []const u8 = &[_][]const u8{ + dir.dir_path[0..dir.dir_path.len], + entry_name, + }; + const entry_start: u32 = @intCast(if (dir.dir_path.len == 0) 0 else dir.dir_path.len + 1); + + // const subdir_entry_name = try this.arena.allocator().dupe(u8, ResolvePath.join(subdir_parts, .auto)); + const subdir_entry_name = try this.walker.join(subdir_parts); + + try this.walker.workbuf.append( + this.walker.arena.allocator(), + WorkItem.newSymlink(subdir_entry_name, dir_iter_state.component_idx, entry_start), + ); + + continue; + } + + if (this.walker.only_files) continue; + + const matches = this.walker.matchPatternFile(entry_name, dir_iter_state.component_idx, dir_iter_state.is_last, dir_iter_state.pattern, dir_iter_state.next_pattern); + if (matches) { + const prepared_path = try this.walker.prepareMatchedPath(entry_name, dir.dir_path) orelse continue; + return .{ .result = prepared_path }; + } + + continue; + }, + else => continue, + } + }, + } + } + } + }; + + const WorkItem = struct { + path: []const u8, + idx: u32, + kind: Kind, + entry_start: u32 = 0, + fd: ?Accessor.Handle = null, + + const Kind = enum { + directory, + symlink, + }; + + fn new(path: []const u8, idx: u32, kind: Kind) WorkItem { + return .{ + .path = path, + .idx = idx, + .kind = kind, + }; + } + + fn newWithFd(path: []const u8, idx: u32, kind: Kind, fd: Accessor.Handle) WorkItem { + return .{ + .path = path, + .idx = idx, + .kind = kind, + .fd = fd, + }; + } + + fn newSymlink(path: []const u8, idx: u32, entry_start: u32) WorkItem { + return .{ + .path = path, + .idx = idx, + .kind = .symlink, + .entry_start = entry_start, + }; + } + }; + + /// A component is each part of a glob pattern, separated by directory + /// separator: + /// `src/**/*.ts` -> `src`, `**`, `*.ts` + const Component = struct { + start: u32, + len: u32, + + syntax_hint: SyntaxHint = .None, + trailing_sep: bool = false, + is_ascii: bool = false, + + /// Only used when component is not ascii + unicode_set: bool = false, + start_cp: u32 = 0, + end_cp: u32 = 0, + + pub fn patternSlice(this: *const Component, pattern: []const u8) []const u8 { + return pattern[this.start .. this.start + this.len - @as(u1, @bitCast(this.trailing_sep))]; + } + + pub fn patternSliceCp(this: *const Component, pattern: []u32) []u32 { + return pattern[this.start_cp .. this.end_cp - @as(u1, @bitCast(this.trailing_sep))]; + } + + const SyntaxHint = enum { + None, + Single, + Double, + /// Uses special fast-path matching for components like: `*.ts` + WildcardFilepath, + /// Uses special fast-patch matching for literal components e.g. + /// "node_modules", becomes memcmp + Literal, + /// ./fixtures/*.ts + /// ^ + Dot, + /// ../ + DotBack, + + fn isSpecialSyntax(this: SyntaxHint) bool { + return switch (this) { + .Literal => false, + else => true, + }; + } + }; + }; + + /// The arena parameter is dereferenced and copied if all allocations go well and nothing goes wrong + pub fn init( + this: *GlobWalker, + arena: *Arena, + pattern: []const u8, + dot: bool, + absolute: bool, + follow_symlinks: bool, + error_on_broken_symlinks: bool, + only_files: bool, + ) !Maybe(void) { + return try this.initWithCwd( + arena, + pattern, + bun.fs.FileSystem.instance.top_level_dir, + dot, + absolute, + follow_symlinks, + error_on_broken_symlinks, + only_files, + ); + } + + pub fn convertUtf8ToCodepoints(codepoints: []u32, pattern: []const u8) void { + _ = bun.simdutf.convert.utf8.to.utf32.le(pattern, codepoints); + } + + pub fn debugPatternComopnents(this: *GlobWalker) void { + const pattern = this.pattern; + const components = &this.patternComponents; + const ptr = @intFromPtr(this); + log("GlobWalker(0x{x}) components:", .{ptr}); + for (components.items) |cmp| { + switch (cmp.syntax_hint) { + .Single => log(" *", .{}), + .Double => log(" **", .{}), + .Dot => log(" .", .{}), + .DotBack => log(" ../", .{}), + .Literal, .WildcardFilepath, .None => log(" hint={s} component_str={s}", .{ @tagName(cmp.syntax_hint), cmp.patternSlice(pattern) }), + } + } + } + + /// `cwd` should be allocated with the arena + /// The arena parameter is dereferenced and copied if all allocations go well and nothing goes wrong + pub fn initWithCwd( + this: *GlobWalker, + arena: *Arena, + pattern: []const u8, + cwd: []const u8, + dot: bool, + absolute: bool, + follow_symlinks: bool, + error_on_broken_symlinks: bool, + only_files: bool, + ) !Maybe(void) { + log("initWithCwd(cwd={s})", .{cwd}); + this.* = .{ + .cwd = cwd, + .pattern = pattern, + .dot = dot, + .absolute = absolute, + .follow_symlinks = follow_symlinks, + .error_on_broken_symlinks = error_on_broken_symlinks, + .only_files = only_files, + .basename_excluding_special_syntax_component_idx = 0, + .end_byte_of_basename_excluding_special_syntax = 0, + }; + + try GlobWalker.buildPatternComponents( + arena, + &this.patternComponents, + pattern, + &this.cp_len, + &this.pattern_codepoints, + &this.has_relative_components, + &this.end_byte_of_basename_excluding_special_syntax, + &this.basename_excluding_special_syntax_component_idx, + ); + + // copy arena after all allocations are successful + this.arena = arena.*; + + if (bun.Environment.allow_assert) { + this.debugPatternComopnents(); + } + + return Maybe(void).success; + } + + /// NOTE This also calls deinit on the arena, if you don't want to do that then + pub fn deinit(this: *GlobWalker, comptime clear_arena: bool) void { + log("GlobWalker.deinit", .{}); + if (comptime clear_arena) { + this.arena.deinit(); + } + } + + pub fn handleSysErrWithPath( + this: *GlobWalker, + err: Syscall.Error, + path_buf: [:0]const u8, + ) Syscall.Error { + std.mem.copyForwards(u8, this.pathBuf[0 .. path_buf.len + 1], @as([]const u8, @ptrCast(path_buf[0 .. path_buf.len + 1]))); + return err.withPath(this.pathBuf[0 .. path_buf.len + 1]); + } + + pub fn walk(this: *GlobWalker) !Maybe(void) { + if (this.patternComponents.items.len == 0) return Maybe(void).success; + + var iter = GlobWalker.Iterator{ .walker = this }; + defer iter.deinit(); + switch (try iter.init()) { + .err => |err| return .{ .err = err }, + else => {}, + } + + while (switch (try iter.next()) { + .err => |err| return .{ .err = err }, + .result => |matched_path| matched_path, + }) |path| { + log("walker: matched path: {s}", .{path}); + // The paths are already put into this.matchedPaths, which we use for the output, + // so we don't need to do anything here + } + + return Maybe(void).success; + } + + // NOTE you must check that the pattern at `idx` has `syntax_hint == .Dot` or + // `syntax_hint == .DotBack` first + fn collapseDots( + this: *GlobWalker, + idx: u32, + dir_path: *[:0]u8, + path_buf: *bun.PathBuffer, + encountered_dot_dot: *bool, + ) u32 { + var component_idx = idx; + var len = dir_path.len; + while (component_idx < this.patternComponents.items.len) { + switch (this.patternComponents.items[component_idx].syntax_hint) { + .Dot => { + defer component_idx += 1; + if (len + 2 >= bun.MAX_PATH_BYTES) @panic("Invalid path"); + if (len == 0) { + path_buf[len] = '.'; + path_buf[len + 1] = 0; + len += 1; + } else { + path_buf[len] = '/'; + path_buf[len + 1] = '.'; + path_buf[len + 2] = 0; + len += 2; + } + }, + .DotBack => { + defer component_idx += 1; + encountered_dot_dot.* = true; + if (dir_path.len + 3 >= bun.MAX_PATH_BYTES) @panic("Invalid path"); + if (len == 0) { + path_buf[len] = '.'; + path_buf[len + 1] = '.'; + path_buf[len + 2] = 0; + len += 2; + } else { + path_buf[len] = '/'; + path_buf[len + 1] = '.'; + path_buf[len + 2] = '.'; + path_buf[len + 3] = 0; + len += 3; + } + }, + else => break, + } + } + + dir_path.len = len; + + return component_idx; + } + + // NOTE you must check that the pattern at `idx` has `syntax_hint == .Double` first + fn collapseSuccessiveDoubleWildcards(this: *GlobWalker, idx: u32) u32 { + var component_idx = idx; + const pattern = this.patternComponents.items[idx]; + _ = pattern; + // Collapse successive double wildcards + while (component_idx + 1 < this.patternComponents.items.len and + this.patternComponents.items[component_idx + 1].syntax_hint == .Double) : (component_idx += 1) + {} + return component_idx; + } + + pub fn skipSpecialComponents( + this: *GlobWalker, + work_item_idx: u32, + dir_path: *[:0]u8, + scratch_path_buf: *bun.PathBuffer, + encountered_dot_dot: *bool, + ) u32 { + var component_idx = work_item_idx; + + // Skip `.` and `..` while also appending them to `dir_path` + component_idx = switch (this.patternComponents.items[component_idx].syntax_hint) { + .Dot => this.collapseDots( + component_idx, + dir_path, + scratch_path_buf, + encountered_dot_dot, + ), + .DotBack => this.collapseDots( + component_idx, + dir_path, + scratch_path_buf, + encountered_dot_dot, + ), + else => component_idx, + }; + + // Skip to the last `**` if there is a chain of them + component_idx = switch (this.patternComponents.items[component_idx].syntax_hint) { + .Double => this.collapseSuccessiveDoubleWildcards(component_idx), + else => component_idx, + }; + + return component_idx; + } + + fn matchPatternDir( + this: *GlobWalker, + pattern: *Component, + next_pattern: ?*Component, + entry_name: []const u8, + component_idx: u32, + is_last: bool, + add: *bool, + ) ?u32 { + if (!this.dot and GlobWalker.startsWithDot(entry_name)) return null; + if (is_ignored(entry_name)) return null; + + // Handle double wildcard `**`, this could possibly + // propagate the `**` to the directory's children + if (pattern.syntax_hint == .Double) { + // Stop the double wildcard if it matches the pattern afer it + // Example: src/**/*.js + // - Matches: src/bun.js/ + // src/bun.js/foo/bar/baz.js + if (!is_last and this.matchPatternImpl(next_pattern.?, entry_name)) { + // But if the next pattern is the last + // component, it should match and propagate the + // double wildcard recursion to the directory's + // children + if (component_idx + 1 == this.patternComponents.items.len - 1) { + add.* = true; + return 0; + } + + // In the normal case skip over the next pattern + // since we matched it, example: + // BEFORE: src/**/node_modules/**/*.js + // ^ + // AFTER: src/**/node_modules/**/*.js + // ^ + return 2; + } + + if (is_last) { + add.* = true; + } + + return 0; + } + + const matches = this.matchPatternImpl(pattern, entry_name); + if (matches) { + if (is_last) { + add.* = true; + return null; + } + return 1; + } + + return null; + } + + /// A file can only match if: + /// a) it matches against the last pattern, or + /// b) it matches the next pattern, provided the current + /// pattern is a double wildcard and the next pattern is + /// not a double wildcard + /// + /// Examples: + /// a -> `src/foo/index.ts` matches + /// b -> `src/**/*.ts` (on 2nd pattern) matches + fn matchPatternFile( + this: *GlobWalker, + entry_name: []const u8, + component_idx: u32, + is_last: bool, + pattern: *Component, + next_pattern: ?*Component, + ) bool { + if (pattern.trailing_sep) return false; + + // Handle case b) + if (!is_last) return pattern.syntax_hint == .Double and + component_idx + 1 == this.patternComponents.items.len -| 1 and + next_pattern.?.syntax_hint != .Double and + this.matchPatternImpl(next_pattern.?, entry_name); + + // Handle case a) + return this.matchPatternImpl(pattern, entry_name); + } + + fn matchPatternImpl( + this: *GlobWalker, + pattern_component: *Component, + filepath: []const u8, + ) bool { + log("matchPatternImpl: {s}", .{filepath}); + if (!this.dot and GlobWalker.startsWithDot(filepath)) return false; + if (is_ignored(filepath)) return false; + + return switch (pattern_component.syntax_hint) { + .Double, .Single => true, + .WildcardFilepath => if (comptime !isWindows) + matchWildcardFilepath(pattern_component.patternSlice(this.pattern), filepath) + else + this.matchPatternSlow(pattern_component, filepath), + .Literal => if (comptime !isWindows) + matchWildcardLiteral(pattern_component.patternSlice(this.pattern), filepath) + else + this.matchPatternSlow(pattern_component, filepath), + else => this.matchPatternSlow(pattern_component, filepath), + }; + } + + fn matchPatternSlow(this: *GlobWalker, pattern_component: *Component, filepath: []const u8) bool { + // windows filepaths are utf-16 so GlobAscii.match will never work + if (comptime !isWindows) { + if (pattern_component.is_ascii and isAllAscii(filepath)) + return GlobAscii.match( + pattern_component.patternSlice(this.pattern), + filepath, + ); + } + const codepoints = this.componentStringUnicode(pattern_component); + return matchImpl( + codepoints, + filepath, + ).matches(); + } + + fn componentStringUnicode(this: *GlobWalker, pattern_component: *Component) []const u32 { + if (comptime isWindows) { + return this.componentStringUnicodeWindows(pattern_component); + } else { + return this.componentStringUnicodePosix(pattern_component); + } + } + + fn componentStringUnicodeWindows(this: *GlobWalker, pattern_component: *Component) []const u32 { + return pattern_component.patternSliceCp(this.pattern_codepoints); + } + + fn componentStringUnicodePosix(this: *GlobWalker, pattern_component: *Component) []const u32 { + if (pattern_component.unicode_set) return pattern_component.patternSliceCp(this.pattern_codepoints); + + const codepoints = pattern_component.patternSliceCp(this.pattern_codepoints); + GlobWalker.convertUtf8ToCodepoints( + codepoints, + pattern_component.patternSlice(this.pattern), + ); + pattern_component.unicode_set = true; + return codepoints; + } + + inline fn matchedPathToBunString(matched_path: MatchedPath) BunString { + if (comptime sentinel) { + return BunString.fromBytes(matched_path[0 .. matched_path.len + 1]); + } + return BunString.fromBytes(matched_path); + } + + fn prepareMatchedPathSymlink(this: *GlobWalker, symlink_full_path: []const u8) !?MatchedPath { + const result = try this.matchedPaths.getOrPut(this.arena.allocator(), BunString.fromBytes(symlink_full_path)); + if (result.found_existing) { + log("(dupe) prepared match: {s}", .{symlink_full_path}); + return null; + } + if (comptime !sentinel) { + const slice = try this.arena.allocator().dupe(u8, symlink_full_path); + result.key_ptr.* = matchedPathToBunString(slice); + return slice; + } + const slicez = try this.arena.allocator().dupeZ(u8, symlink_full_path); + result.key_ptr.* = matchedPathToBunString(slicez); + return slicez; + } + + fn prepareMatchedPath(this: *GlobWalker, entry_name: []const u8, dir_name: []const u8) !?MatchedPath { + const subdir_parts: []const []const u8 = &[_][]const u8{ + dir_name[0..dir_name.len], + entry_name, + }; + const name_matched_path = try this.join(subdir_parts); + const name = matchedPathToBunString(name_matched_path); + const result = try this.matchedPaths.getOrPutValue(this.arena.allocator(), name, {}); + if (result.found_existing) { + log("(dupe) prepared match: {s}", .{name_matched_path}); + this.arena.allocator().free(name_matched_path); + return null; + } + result.key_ptr.* = name; + // if (comptime sentinel) return name[0 .. name.len - 1 :0]; + log("prepared match: {s}", .{name_matched_path}); + return name_matched_path; + } + + fn appendMatchedPath( + this: *GlobWalker, + entry_name: []const u8, + dir_name: [:0]const u8, + ) !void { + const subdir_parts: []const []const u8 = &[_][]const u8{ + dir_name[0..dir_name.len], + entry_name, + }; + const name_matched_path = try this.join(subdir_parts); + const name = matchedPathToBunString(name_matched_path); + const result = try this.matchedPaths.getOrPut(this.arena.allocator(), name); + if (result.found_existing) { + this.arena.allocator().free(name_matched_path); + log("(dupe) prepared match: {s}", .{name_matched_path}); + return; + } + result.key_ptr.* = name; + } + + fn appendMatchedPathSymlink(this: *GlobWalker, symlink_full_path: []const u8) !void { + const name = try this.arena.allocator().dupe(u8, symlink_full_path); + try this.matchedPaths.put(this.arena.allocator(), BunString.fromBytes(name), {}); + } + + inline fn join(this: *GlobWalker, subdir_parts: []const []const u8) !MatchedPath { + if (!this.absolute) { + // If relative paths enabled, stdlib join is preferred over + // ResolvePath.joinBuf because it doesn't try to normalize the path + return try stdJoin(this.arena.allocator(), subdir_parts); + } + + const out = try this.arena.allocator().dupe(u8, bunJoin(subdir_parts, .auto)); + if (comptime sentinel) return out[0 .. out.len - 1 :0]; + + return out; + } + + inline fn startsWithDot(filepath: []const u8) bool { + return filepath.len > 0 and filepath[0] == '.'; + } + + fn checkSpecialSyntax(pattern: []const u8) bool { + if (pattern.len < 16) { + for (pattern[0..]) |c| { + switch (c) { + '*', '[', '{', '?', '!' => return true, + else => {}, + } + } + return false; + } + + const syntax_tokens = comptime [_]u8{ '*', '[', '{', '?', '!' }; + const needles: [syntax_tokens.len]@Vector(16, u8) = comptime needles: { + var needles: [syntax_tokens.len]@Vector(16, u8) = undefined; + for (syntax_tokens, 0..) |tok, i| { + needles[i] = @splat(tok); + } + break :needles needles; + }; + + var i: usize = 0; + while (i + 16 <= pattern.len) : (i += 16) { + const haystack: @Vector(16, u8) = pattern[i..][0..16].*; + inline for (needles) |needle| { + if (std.simd.firstTrue(needle == haystack) != null) return true; + } + } + + if (i < pattern.len) { + for (pattern[i..]) |c| { + inline for (syntax_tokens) |tok| { + if (c == tok) return true; + } + } + } + + return false; + } + + fn makeComponent( + pattern: []const u8, + start_cp: u32, + end_cp: u32, + start_byte: u32, + end_byte: u32, + has_relative_patterns: *bool, + ) ?Component { + var component: Component = .{ + .start = start_byte, + .len = end_byte - start_byte, + .start_cp = start_cp, + .end_cp = end_cp, + }; + if (component.len == 0) return null; + + out: { + if (component.len == 1 and pattern[component.start] == '.') { + component.syntax_hint = .Dot; + has_relative_patterns.* = true; + break :out; + } + if (component.len == 2 and pattern[component.start] == '.' and pattern[component.start] == '.') { + component.syntax_hint = .DotBack; + has_relative_patterns.* = true; + break :out; + } + + if (!GlobWalker.checkSpecialSyntax(pattern[component.start .. component.start + component.len])) { + component.syntax_hint = .Literal; + break :out; + } + + switch (component.len) { + 1 => { + if (pattern[component.start] == '*') { + component.syntax_hint = .Single; + } + break :out; + }, + 2 => { + if (pattern[component.start] == '*' and pattern[component.start + 1] == '*') { + component.syntax_hint = .Double; + break :out; + } + }, + else => {}, + } + + out_of_check_wildcard_filepath: { + if (component.len > 1 and + pattern[component.start] == '*' and + pattern[component.start + 1] == '.' and + component.start + 2 < pattern.len) + { + for (pattern[component.start + 2 ..]) |c| { + switch (c) { + // The fast path checks that path[1..] == pattern[1..], + // this will obviously not work if additional + // glob syntax is present in the pattern, so we + // must not apply this optimization if we see + // special glob syntax. + // + // This is not a complete check, there can be + // false negatives, but that's okay, it just + // means we don't apply the optimization. + // + // We also don't need to look for the `!` token, + // because that only applies negation if at the + // beginning of the string. + '[', '{', '?', '*' => break :out_of_check_wildcard_filepath, + else => {}, + } + } + component.syntax_hint = .WildcardFilepath; + break :out; + } + } + } + + if (component.syntax_hint != .Single and component.syntax_hint != .Double) { + if (isAllAscii(pattern[component.start .. component.start + component.len])) { + component.is_ascii = true; + } + } else { + component.is_ascii = true; + } + + if (pattern[component.start + component.len -| 1] == '/') { + component.trailing_sep = true; + } else if (comptime bun.Environment.isWindows) { + component.trailing_sep = pattern[component.start + component.len -| 1] == '\\'; + } + + return component; + } + + /// Build an ad-hoc glob pattern. Useful when you don't need to traverse + /// a directory. + pub fn buildPattern( + arena: *Arena, + patternComponents: *ArrayList(Component), + pattern: []const u8, + out_cp_len: ?*u32, + out_pattern_cp: *[]u32, + has_relative_patterns: *bool, + end_byte_of_basename_excluding_special_syntax: ?*u32, + basename_excluding_special_syntax_component_idx: ?*u32, + ) !void { + // in case the consumer doesn't care about some outputs. + const scratchpad: [3]u32 = .{0} ** 3; + return buildPatternComponents( + arena, + patternComponents, + pattern, + out_cp_len orelse &scratchpad[0], + out_pattern_cp, + has_relative_patterns, + end_byte_of_basename_excluding_special_syntax orelse scratchpad[1], + basename_excluding_special_syntax_component_idx orelse scratchpad[2], + ); + } + + fn buildPatternComponents( + arena: *Arena, + patternComponents: *ArrayList(Component), + pattern: []const u8, + out_cp_len: *u32, + out_pattern_cp: *[]u32, + has_relative_patterns: *bool, + end_byte_of_basename_excluding_special_syntax: *u32, + basename_excluding_special_syntax_component_idx: *u32, + ) !void { + var start_cp: u32 = 0; + var start_byte: u32 = 0; + + const iter = CodepointIterator.init(pattern); + var cursor = CodepointIterator.Cursor{}; + + var cp_len: u32 = 0; + var prevIsBackslash = false; + var saw_special = false; + while (iter.next(&cursor)) : (cp_len += 1) { + const c = cursor.c; + + switch (c) { + '\\' => { + if (comptime isWindows) { + var end_cp = cp_len; + var end_byte = cursor.i; + // is last char + if (cursor.i + cursor.width == pattern.len) { + end_cp += 1; + end_byte += cursor.width; + } + if (makeComponent( + pattern, + start_cp, + end_cp, + start_byte, + end_byte, + has_relative_patterns, + )) |component| { + saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); + if (!saw_special) { + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + try patternComponents.append(arena.allocator(), component); + } + start_cp = cp_len + 1; + start_byte = cursor.i + cursor.width; + continue; + } + + if (prevIsBackslash) { + prevIsBackslash = false; + continue; + } + + prevIsBackslash = true; + }, + '/' => { + var end_cp = cp_len; + var end_byte = cursor.i; + // is last char + if (cursor.i + cursor.width == pattern.len) { + end_cp += 1; + end_byte += cursor.width; + } + if (makeComponent( + pattern, + start_cp, + end_cp, + start_byte, + end_byte, + has_relative_patterns, + )) |component| { + saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); + if (!saw_special) { + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + try patternComponents.append(arena.allocator(), component); + } + start_cp = cp_len + 1; + start_byte = cursor.i + cursor.width; + }, + // TODO: Support other escaping glob syntax + else => {}, + } + } + + out_cp_len.* = cp_len; + + const codepoints = try arena.allocator().alloc(u32, cp_len); + // On Windows filepaths are UTF-16 so its better to fill the codepoints buffer upfront + if (comptime isWindows) { + GlobWalker.convertUtf8ToCodepoints(codepoints, pattern); + } + out_pattern_cp.* = codepoints; + + const end_cp = cp_len; + if (makeComponent( + pattern, + start_cp, + end_cp, + start_byte, + @intCast(pattern.len), + has_relative_patterns, + )) |component| { + saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); + if (!saw_special) { + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + try patternComponents.append(arena.allocator(), component); + } else if (!saw_special) { + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + } + }; +} + +// From: https://github.com/The-King-of-Toasters/globlin +/// State for matching a glob against a string +pub const GlobState = struct { + // These store character indices into the glob and path strings. + path_index: CursorState = .{}, + glob_index: u32 = 0, + // When we hit a * or **, we store the state for backtracking. + wildcard: Wildcard = .{}, + globstar: Wildcard = .{}, + + fn init(path_iter: *const CodepointIterator) GlobState { + var this = GlobState{}; + // this.glob_index = CursorState.init(glob_iter); + this.path_index = CursorState.init(path_iter); + return this; + } + + fn skipBraces(self: *GlobState, glob: []const u32, stop_on_comma: bool) BraceState { + var braces: u32 = 1; + var in_brackets = false; + while (self.glob_index < glob.len and braces > 0) : (self.glob_index += 1) { + switch (glob[self.glob_index]) { + // Skip nested braces + '{' => if (!in_brackets) { + braces += 1; + }, + '}' => if (!in_brackets) { + braces -= 1; + }, + ',' => if (stop_on_comma and braces == 1 and !in_brackets) { + self.glob_index += 1; + return .Comma; + }, + '*', '?', '[' => |c| if (!in_brackets) { + if (c == '[') + in_brackets = true; + }, + ']' => in_brackets = false, + '\\' => self.glob_index += 1, + else => {}, + } + } + + if (braces != 0) + return .Invalid; + return .EndBrace; + } + + inline fn backtrack(self: *GlobState) void { + self.glob_index = self.wildcard.glob_index; + self.path_index = self.wildcard.path_index; + } +}; + +const Wildcard = struct { + // Using u32 rather than usize for these results in 10% faster performance. + // glob_index: CursorState = .{}, + glob_index: u32 = 0, + path_index: CursorState = .{}, +}; + +const BraceState = enum { Invalid, Comma, EndBrace }; + +const BraceStack = struct { + stack: [10]GlobState = undefined, + len: u32 = 0, + longest_brace_match: CursorState = .{}, + + inline fn push(self: *BraceStack, state: *const GlobState) GlobState { + self.stack[self.len] = state.*; + self.len += 1; + return GlobState{ + .path_index = state.path_index, + .glob_index = state.glob_index + 1, + }; + } + + inline fn pop(self: *BraceStack, state: *const GlobState) GlobState { + self.len -= 1; + const s = GlobState{ + .glob_index = state.glob_index, + .path_index = self.longest_brace_match, + // Restore star state if needed later. + .wildcard = self.stack[self.len].wildcard, + .globstar = self.stack[self.len].globstar, + }; + if (self.len == 0) + self.longest_brace_match = .{}; + return s; + } + + inline fn last(self: *const BraceStack) *const GlobState { + return &self.stack[self.len - 1]; + } +}; + +pub const MatchResult = enum { + no_match, + match, + + negate_no_match, + negate_match, + + pub fn matches(this: MatchResult) bool { + return this == .match or this == .negate_match; + } +}; + +/// This function checks returns a boolean value if the pathname `path` matches +/// the pattern `glob`. +/// +/// The supported pattern syntax for `glob` is: +/// +/// "?" +/// Matches any single character. +/// "*" +/// Matches zero or more characters, except for path separators ('/' or '\'). +/// "**" +/// Matches zero or more characters, including path separators. +/// Must match a complete path segment, i.e. followed by a path separator or +/// at the end of the pattern. +/// "[ab]" +/// Matches one of the characters contained in the brackets. +/// Character ranges (e.g. "[a-z]") are also supported. +/// Use "[!ab]" or "[^ab]" to match any character *except* those contained +/// in the brackets. +/// "{a,b}" +/// Match one of the patterns contained in the braces. +/// Any of the wildcards listed above can be used in the sub patterns. +/// Braces may be nested up to 10 levels deep. +/// "!" +/// Negates the result when at the start of the pattern. +/// Multiple "!" characters negate the pattern multiple times. +/// "\" +/// Used to escape any of the special characters above. +pub fn matchImpl(glob: []const u32, path: []const u8) MatchResult { + const path_iter = CodepointIterator.init(path); + + // This algorithm is based on https://research.swtch.com/glob + var state = GlobState.init(&path_iter); + // Store the state when we see an opening '{' brace in a stack. + // Up to 10 nested braces are supported. + var brace_stack = BraceStack{}; + + // First, check if the pattern is negated with a leading '!' character. + // Multiple negations can occur. + var negated = false; + while (state.glob_index < glob.len and glob[state.glob_index] == '!') { + negated = !negated; + state.glob_index += 1; + } + + while (state.glob_index < glob.len or state.path_index.cursor.i < path.len) { + if (state.glob_index < glob.len) { + switch (glob[state.glob_index]) { + '*' => { + const is_globstar = state.glob_index + 1 < glob.len and glob[state.glob_index + 1] == '*'; + // const is_globstar = state.glob_index.cursor.i + state.glob_index.cursor.width < glob.len and + // state.glob_index.peek(&glob_iter).cursor.c == '*'; + if (is_globstar) { + // Coalesce multiple ** segments into one. + var index = state.glob_index + 2; + state.glob_index = skipGlobstars(glob, &index) - 2; + } + + state.wildcard.glob_index = state.glob_index; + state.wildcard.path_index = state.path_index.peek(&path_iter); + + // ** allows path separators, whereas * does not. + // However, ** must be a full path component, i.e. a/**/b not a**b. + if (is_globstar) { + // Skip wildcards + state.glob_index += 2; + + if (glob.len == state.glob_index) { + // A trailing ** segment without a following separator. + state.globstar = state.wildcard; + } else if (glob[state.glob_index] == '/' and + (state.glob_index < 3 or glob[state.glob_index - 3] == '/')) + { + // Matched a full /**/ segment. If the last character in the path was a separator, + // skip the separator in the glob so we search for the next character. + // In effect, this makes the whole segment optional so that a/**/b matches a/b. + if (state.path_index.cursor.i == 0 or + (state.path_index.cursor.i < path.len and + isSeparator(path[state.path_index.cursor.i - 1]))) + { + state.glob_index += 1; + } + + // The allows_sep flag allows separator characters in ** matches. + // one is a '/', which prevents a/**/b from matching a/bb. + state.globstar = state.wildcard; + } + } else { + state.glob_index += 1; + } + + // If we are in a * segment and hit a separator, + // either jump back to a previous ** or end the wildcard. + if (state.globstar.path_index.cursor.i != state.wildcard.path_index.cursor.i and + state.path_index.cursor.i < path.len and + isSeparator(state.path_index.cursor.c)) + { + // Special case: don't jump back for a / at the end of the glob. + if (state.globstar.path_index.cursor.i > 0 and state.path_index.cursor.i + state.path_index.cursor.width < path.len) { + state.glob_index = state.globstar.glob_index; + state.wildcard.glob_index = state.globstar.glob_index; + } else { + state.wildcard.path_index.cursor.i = 0; + } + } + + // If the next char is a special brace separator, + // skip to the end of the braces so we don't try to match it. + if (brace_stack.len > 0 and + state.glob_index < glob.len and + (glob[state.glob_index] == ',' or glob[state.glob_index] == '}')) + { + if (state.skipBraces(glob, false) == .Invalid) + return .no_match; // invalid pattern! + } + + continue; + }, + '?' => if (state.path_index.cursor.i < path.len) { + if (!isSeparator(state.path_index.cursor.c)) { + state.glob_index += 1; + state.path_index.bump(&path_iter); + continue; + } + }, + '[' => if (state.path_index.cursor.i < path.len) { + state.glob_index += 1; + const c = state.path_index.cursor.c; + + // Check if the character class is negated. + var class_negated = false; + if (state.glob_index < glob.len and + (glob[state.glob_index] == '^' or glob[state.glob_index] == '!')) + { + class_negated = true; + state.glob_index += 1; + } + + // Try each range. + var first = true; + var is_match = false; + while (state.glob_index < glob.len and (first or glob[state.glob_index] != ']')) { + var low = glob[state.glob_index]; + if (!unescape(&low, glob, &state.glob_index)) + return .no_match; // Invalid pattern + state.glob_index += 1; + + // If there is a - and the following character is not ], + // read the range end character. + const high = if (state.glob_index + 1 < glob.len and + glob[state.glob_index] == '-' and glob[state.glob_index + 1] != ']') + blk: { + state.glob_index += 1; + var h = glob[state.glob_index]; + if (!unescape(&h, glob, &state.glob_index)) + return .no_match; // Invalid pattern! + state.glob_index += 1; + break :blk h; + } else low; + + if (low <= c and c <= high) + is_match = true; + first = false; + } + if (state.glob_index >= glob.len) + return .no_match; // Invalid pattern! + state.glob_index += 1; + if (is_match != class_negated) { + state.path_index.bump(&path_iter); + continue; + } + }, + '{' => if (state.path_index.cursor.i < path.len) { + if (brace_stack.len >= brace_stack.stack.len) + return .no_match; // Invalid pattern! Too many nested braces. + + // Push old state to the stack, and reset current state. + state = brace_stack.push(&state); + continue; + }, + '}' => if (brace_stack.len > 0) { + // If we hit the end of the braces, we matched the last option. + brace_stack.longest_brace_match = if (state.path_index.cursor.i >= brace_stack.longest_brace_match.cursor.i) + state.path_index + else + brace_stack.longest_brace_match; + state.glob_index += 1; + state = brace_stack.pop(&state); + continue; + }, + ',' => if (brace_stack.len > 0) { + // If we hit a comma, we matched one of the options! + // But we still need to check the others in case there is a longer match. + brace_stack.longest_brace_match = if (state.path_index.cursor.i >= brace_stack.longest_brace_match.cursor.i) + state.path_index + else + brace_stack.longest_brace_match; + state.path_index = brace_stack.last().path_index; + state.glob_index += 1; + state.wildcard = Wildcard{}; + state.globstar = Wildcard{}; + continue; + }, + else => |c| if (state.path_index.cursor.i < path.len) { + var cc = c; + // Match escaped characters as literals. + if (!unescape(&cc, glob, &state.glob_index)) + return .no_match; // Invalid pattern; + + const is_match = if (cc == '/') + isSeparator(state.path_index.cursor.c) + else + state.path_index.cursor.c == cc; + + if (is_match) { + if (brace_stack.len > 0 and + state.glob_index > 0 and + glob[state.glob_index - 1] == '}') + { + brace_stack.longest_brace_match = state.path_index; + state = brace_stack.pop(&state); + } + state.glob_index += 1; + state.path_index.bump(&path_iter); + + // If this is not a separator, lock in the previous globstar. + if (cc != '/') + state.globstar.path_index.cursor.i = 0; + + continue; + } + }, + } + } + // If we didn't match, restore state to the previous star pattern. + if (state.wildcard.path_index.cursor.i > 0 and state.wildcard.path_index.cursor.i <= path.len) { + state.backtrack(); + continue; + } + + if (brace_stack.len > 0) { + // If in braces, find next option and reset path to index where we saw the '{' + switch (state.skipBraces(glob, true)) { + .Invalid => return .no_match, + .Comma => { + state.path_index = brace_stack.last().path_index; + continue; + }, + .EndBrace => {}, + } + + // Hit the end. Pop the stack. + // If we matched a previous option, use that. + if (brace_stack.longest_brace_match.cursor.i > 0) { + state = brace_stack.pop(&state); + continue; + } else { + // Didn't match. Restore state, and check if we need to jump back to a star pattern. + state = brace_stack.last().*; + brace_stack.len -= 1; + if (state.wildcard.path_index.cursor.i > 0 and state.wildcard.path_index.cursor.i <= path.len) { + state.backtrack(); + continue; + } + } + } + + return if (negated) .negate_match else .no_match; + } + + return if (!negated) .match else .negate_no_match; +} + +pub inline fn isSeparator(c: Codepoint) bool { + if (comptime @import("builtin").os.tag == .windows) return c == '/' or c == '\\'; + return c == '/'; +} + +inline fn unescape(c: *u32, glob: []const u32, glob_index: *u32) bool { + if (c.* == '\\') { + glob_index.* += 1; + if (glob_index.* >= glob.len) + return false; // Invalid pattern! + + c.* = switch (glob[glob_index.*]) { + 'a' => '\x61', + 'b' => '\x08', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + else => |cc| cc, + }; + } + + return true; +} + +const GLOB_STAR_MATCH_STR: []const u32 = &[_]u32{ '/', '*', '*' }; +// src/**/**/foo.ts +inline fn skipGlobstars(glob: []const u32, glob_index: *u32) u32 { + // Coalesce multiple ** segments into one. + while (glob_index.* + 3 <= glob.len and + // std.mem.eql(u8, glob[glob_index.*..][0..3], "/**")) + std.mem.eql(u32, glob[glob_index.*..][0..3], GLOB_STAR_MATCH_STR)) + { + glob_index.* += 3; + } + + return glob_index.*; +} + +const MatchAscii = struct {}; + +pub fn matchWildcardFilepath(glob: []const u8, path: []const u8) bool { + const needle = glob[1..]; + const needle_len: u32 = @intCast(needle.len); + if (path.len < needle_len) return false; + return std.mem.eql(u8, needle, path[path.len - needle_len ..]); +} + +pub fn matchWildcardLiteral(literal: []const u8, path: []const u8) bool { + return std.mem.eql(u8, literal, path); +} diff --git a/src/glob_ascii.zig b/src/glob/ascii.zig similarity index 93% rename from src/glob_ascii.zig rename to src/glob/ascii.zig index 38914eface..69413f9505 100644 --- a/src/glob_ascii.zig +++ b/src/glob/ascii.zig @@ -480,3 +480,36 @@ inline fn skipGlobstars(glob: []const u8, glob_index: *usize) usize { return glob_index.*; } + +/// Returns true if the given string contains glob syntax, +/// excluding those escaped with backslashes +/// TODO: this doesn't play nicely with Windows directory separator and +/// backslashing, should we just require the user to supply posix filepaths? +pub fn detectGlobSyntax(potential_pattern: []const u8) bool { + // Negation only allowed in the beginning of the pattern + if (potential_pattern.len > 0 and potential_pattern[0] == '!') return true; + + // In descending order of how popular the token is + const SPECIAL_SYNTAX: [4]u8 = comptime [_]u8{ '*', '{', '[', '?' }; + + inline for (SPECIAL_SYNTAX) |token| { + var slice = potential_pattern[0..]; + while (slice.len > 0) { + if (std.mem.indexOfScalar(u8, slice, token)) |idx| { + // Check for even number of backslashes preceding the + // token to know that it's not escaped + var i = idx; + var backslash_count: u16 = 0; + + while (i > 0 and potential_pattern[i - 1] == '\\') : (i -= 1) { + backslash_count += 1; + } + + if (backslash_count % 2 == 0) return true; + slice = slice[idx + 1 ..]; + } else break; + } + } + + return false; +} diff --git a/src/hive_array.zig b/src/hive_array.zig index c3479da816..fd9085b035 100644 --- a/src/hive_array.zig +++ b/src/hive_array.zig @@ -11,7 +11,7 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { return struct { const Self = @This(); buffer: [capacity]T = undefined, - available: std.bit_set.IntegerBitSet(capacity) = std.bit_set.IntegerBitSet(capacity).initFull(), + available: bun.bit_set.IntegerBitSet(capacity) = bun.bit_set.IntegerBitSet(capacity).initFull(), pub const size = capacity; pub fn init() Self { diff --git a/src/http.zig b/src/http.zig index 3a6a27138d..47339a4543 100644 --- a/src/http.zig +++ b/src/http.zig @@ -1148,7 +1148,8 @@ pub const HTTPThread = struct { if (Environment.isWindows) { _ = std.process.getenvW(comptime bun.strings.w("SystemRoot")) orelse { - std.debug.panic("The %SystemRoot% environment variable is not set. Bun needs this set in order for network requests to work.", .{}); + bun.Output.errGeneric("The %SystemRoot% environment variable is not set. Bun needs this set in order for network requests to work.", .{}); + Global.crash(); }; } @@ -1215,11 +1216,13 @@ pub const HTTPThread = struct { } } if (client.http_proxy) |url| { - // https://github.com/oven-sh/bun/issues/11343 - if (url.protocol.len == 0 or strings.eqlComptime(url.protocol, "https") or strings.eqlComptime(url.protocol, "http")) { - return try this.context(is_ssl).connect(client, url.hostname, url.getPortAuto()); + if (url.href.len > 0) { + // https://github.com/oven-sh/bun/issues/11343 + if (url.protocol.len == 0 or strings.eqlComptime(url.protocol, "https") or strings.eqlComptime(url.protocol, "http")) { + return try this.context(is_ssl).connect(client, url.hostname, url.getPortAuto()); + } + return error.UnsupportedProxyProtocol; } - return error.UnsupportedProxyProtocol; } return try this.context(is_ssl).connect(client, client.url.hostname, client.url.getPortAuto()); } diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig index 806b3ba117..734b795835 100644 --- a/src/http/websocket_http_client.zig +++ b/src/http/websocket_http_client.zig @@ -204,16 +204,10 @@ const CppWebSocket = opaque { } }; -const body_buf_len = 16384 - 16; -const BodyBufBytes = [body_buf_len]u8; - -const BodyBufPool = ObjectPool(BodyBufBytes, null, true, 4); -const BodyBuf = BodyBufPool.Node; - pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { return struct { pub const Socket = uws.NewSocketHandler(ssl); - tcp: ?Socket = null, + tcp: Socket, outgoing_websocket: ?*CppWebSocket, input_body_buf: []u8 = &[_]u8{}, client_protocol: []const u8 = "", @@ -225,13 +219,14 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { hostname: [:0]const u8 = "", poll_ref: Async.KeepAlive = Async.KeepAlive.init(), state: State = .initializing, + ref_count: u32 = 1, const State = enum { initializing, reading, failed }; pub const name = if (ssl) "WebSocketHTTPSClient" else "WebSocketHTTPClient"; pub const shim = JSC.Shimmer("Bun", name, @This()); - pub usingnamespace bun.New(@This()); + pub usingnamespace bun.NewRefCounted(@This(), deinit); const HTTPClient = @This(); pub fn register(_: *JSC.JSGlobalObject, _: *anyopaque, ctx: *uws.SocketContext) callconv(.C) void { @@ -253,6 +248,12 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { ); } + pub fn deinit(this: *HTTPClient) void { + this.clearData(); + bun.debugAssert(this.tcp.isDetached()); + this.destroy(); + } + /// On error, this returns null. /// Returning null signals to the parent function that the connection failed. pub fn connect( @@ -284,7 +285,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { ) catch return null; var client = HTTPClient.new(.{ - .tcp = null, + .tcp = .{ .socket = .{ .detached = {} } }, .outgoing_websocket = websocket, .input_body_buf = body, .websocket_protocol = client_protocol_hash, @@ -312,8 +313,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { )) |out| { // I don't think this case gets reached. if (out.state == .failed) { - client.clearData(); - client.destroy(); + client.deref(); return null; } bun.Analytics.Features.WebSocket += 1; @@ -324,12 +324,13 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { } } - out.tcp.?.timeout(120); + out.tcp.timeout(120); out.state = .reading; + // +1 for cpp_websocket + out.ref(); return out; } else |_| { - client.clearData(); - client.destroy(); + client.deref(); } return null; @@ -347,40 +348,58 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { } pub fn cancel(this: *HTTPClient) callconv(.C) void { this.clearData(); - this.outgoing_websocket = null; - const tcp = this.tcp orelse return; - this.tcp = null; + + // Either of the below two operations - closing the TCP socket or clearing the C++ reference could trigger a deref + // Therefore, we need to make sure the `this` pointer is valid until the end of the function. + this.ref(); + defer this.deref(); + + // The C++ end of the socket is no longer holding a reference to this, sowe must clear it. + if (this.outgoing_websocket != null) { + this.outgoing_websocket = null; + this.deref(); + } + // no need to be .failure we still wanna to send pending SSL buffer + close_notify if (comptime ssl) { - tcp.close(.normal); + this.tcp.close(.normal); } else { - tcp.close(.failure); + this.tcp.close(.failure); } } pub fn fail(this: *HTTPClient, code: ErrorCode) void { - log("onFail", .{}); + log("onFail: {s}", .{@tagName(code)}); JSC.markBinding(@src()); + + this.ref(); + defer this.deref(); + + this.dispatchAbruptClose(code); + + if (comptime ssl) { + this.tcp.close(.normal); + } else { + this.tcp.close(.failure); + } + } + + fn dispatchAbruptClose(this: *HTTPClient, code: ErrorCode) void { if (this.outgoing_websocket) |ws| { this.outgoing_websocket = null; ws.didAbruptClose(code); + this.deref(); } - - this.cancel(); } pub fn handleClose(this: *HTTPClient, _: Socket, _: c_int, _: ?*anyopaque) void { log("onClose", .{}); JSC.markBinding(@src()); this.clearData(); - this.tcp = null; + this.tcp.detach(); + this.dispatchAbruptClose(ErrorCode.ended); - if (this.outgoing_websocket) |ws| { - this.outgoing_websocket = null; - - ws.didAbruptClose(ErrorCode.ended); - } - this.destroy(); + this.deref(); } pub fn terminate(this: *HTTPClient, code: ErrorCode) void { @@ -447,18 +466,18 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { } pub fn isSameSocket(this: *HTTPClient, socket: Socket) bool { - if (this.tcp) |tcp| { - return socket.socket.eq(tcp.socket); - } - return false; + return socket.socket.eq(this.tcp.socket); } pub fn handleData(this: *HTTPClient, socket: Socket, data: []const u8) void { log("onData", .{}); if (this.outgoing_websocket == null) { this.clearData(); + socket.close(.failure); return; } + this.ref(); + defer this.deref(); bun.assert(this.isSameSocket(socket)); @@ -499,10 +518,8 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { this.processResponse(response, body[@as(usize, @intCast(response.bytes_read))..]); } - pub fn handleEnd(this: *HTTPClient, socket: Socket) void { + pub fn handleEnd(this: *HTTPClient, _: Socket) void { log("onEnd", .{}); - - bun.assert(this.isSameSocket(socket)); this.terminate(ErrorCode.ended); } @@ -618,14 +635,34 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { this.clearData(); JSC.markBinding(@src()); - if (this.tcp != null and this.outgoing_websocket != null) { - this.tcp.?.timeout(0); + if (!this.tcp.isClosed() and this.outgoing_websocket != null) { + this.tcp.timeout(0); log("onDidConnect", .{}); - this.outgoing_websocket.?.didConnect(this.tcp.?.socket.get().?, overflow.ptr, overflow.len); + // Once for the outgoing_websocket. + defer this.deref(); + const ws = bun.take(&this.outgoing_websocket).?; + const socket = this.tcp; + + this.tcp.detach(); + // Once again for the TCP socket. + defer this.deref(); + + ws.didConnect(socket.socket.get().?, overflow.ptr, overflow.len); + } else if (this.tcp.isClosed()) { + this.terminate(ErrorCode.cancel); + } else if (this.outgoing_websocket == null) { + this.tcp.close(.failure); } } + pub fn memoryCost(this: *HTTPClient) callconv(.C) usize { + var cost: usize = @sizeOf(HTTPClient); + cost += this.body.capacity; + cost += this.to_send.len; + return cost; + } + pub fn handleWritable( this: *HTTPClient, socket: Socket, @@ -635,6 +672,9 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { if (this.to_send.len == 0) return; + this.ref(); + defer this.deref(); + // Do not set MSG_MORE, see https://github.com/oven-sh/bun/issues/4010 const wrote = socket.write(this.to_send, false); if (wrote < 0) { @@ -653,13 +693,13 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { // In theory, this could be called immediately // In that case, we set `state` to `failed` and return, expecting the parent to call `destroy`. pub fn handleConnectError(this: *HTTPClient, _: Socket, _: c_int) void { - this.tcp = null; + this.tcp.detach(); - // the socket is freed by usockets when the connection fails + // For the TCP socket. + defer this.deref(); if (this.state == .reading) { this.terminate(ErrorCode.failed_to_connect); - this.destroy(); } else { this.state = .failed; } @@ -669,6 +709,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { .connect = connect, .cancel = cancel, .register = register, + .memoryCost = memoryCost, }); comptime { @@ -682,6 +723,9 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { @export(register, .{ .name = Export[2].symbol_name, }); + @export(memoryCost, .{ + .name = Export[3].symbol_name, + }); } } }; @@ -951,7 +995,7 @@ const Copy = union(enum) { pub fn NewWebSocketClient(comptime ssl: bool) type { return struct { pub const Socket = uws.NewSocketHandler(ssl); - tcp: ?Socket = null, + tcp: Socket, outgoing_websocket: ?*CppWebSocket = null, receive_state: ReceiveState = ReceiveState.need_header, @@ -981,6 +1025,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { initial_data_handler: ?*InitialDataHandler = null, event_loop: *JSC.EventLoop = undefined, + ref_count: u32 = 1, pub const name = if (ssl) "WebSocketClientTLS" else "WebSocketClient"; @@ -989,7 +1034,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { const WebSocket = @This(); - pub usingnamespace bun.New(@This()); + pub usingnamespace bun.NewRefCounted(@This(), deinit); pub fn register(global: *JSC.JSGlobalObject, loop_: *anyopaque, ctx_: *anyopaque) callconv(.C) void { const vm = global.bunVM(); const loop = @as(*uws.Loop, @ptrCast(@alignCast(loop_))); @@ -1032,13 +1077,12 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { pub fn cancel(this: *WebSocket) callconv(.C) void { log("cancel", .{}); this.clearData(); - const tcp = this.tcp orelse return; - this.tcp = null; - // no need to be .failure we still wanna to send pending SSL buffer + close_notify + if (comptime ssl) { - tcp.close(.normal); + // we still want to send pending SSL buffer + close_notify + this.tcp.close(.normal); } else { - tcp.close(.failure); + this.tcp.close(.failure); } } @@ -1047,8 +1091,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { if (this.outgoing_websocket) |ws| { this.outgoing_websocket = null; log("fail ({s})", .{@tagName(code)}); - ws.didAbruptClose(code); + this.deref(); } this.cancel(); @@ -1087,10 +1131,12 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { log("onClose", .{}); JSC.markBinding(@src()); this.clearData(); - if (this.outgoing_websocket) |ws| { - this.outgoing_websocket = null; - ws.didAbruptClose(ErrorCode.ended); - } + this.tcp.detach(); + + this.dispatchAbruptClose(ErrorCode.ended); + + // For the socket. + this.deref(); } pub fn terminate(this: *WebSocket, code: ErrorCode) void { @@ -1197,6 +1243,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { // after receiving close we should ignore the data if (this.close_received) return; + this.ref(); + defer this.deref(); // Due to scheduling, it is possible for the websocket onData // handler to run with additional data before the microtask queue is @@ -1212,7 +1260,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { bun.assert(this.initial_data_handler == null); // If we disconnected for any reason in the re-entrant case, we should just ignore the data - if (this.outgoing_websocket == null or this.tcp == null or this.tcp.?.isShutdown() or this.tcp.?.isClosed()) + if (this.outgoing_websocket == null or !this.hasTCP()) return; } @@ -1523,9 +1571,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { } pub fn sendClose(this: *WebSocket) void { - if (this.tcp) |tcp| { - this.sendCloseWithBody(tcp, 1000, null, 0); - } + this.sendCloseWithBody(this.tcp, 1000, null, 0); } fn enqueueEncodedBytes( @@ -1569,11 +1615,9 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { if (do_write) { if (comptime Environment.allow_assert) { - if (this.tcp) |tcp| { - bun.assert(!tcp.isShutdown()); - bun.assert(!tcp.isClosed()); - bun.assert(tcp.isEstablished()); - } + bun.assert(!this.tcp.isShutdown()); + bun.assert(!this.tcp.isClosed()); + bun.assert(this.tcp.isEstablished()); } return this.sendBuffer(this.send_buffer.readableSlice(0)); } @@ -1587,8 +1631,10 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { ) bool { bun.assert(out_buf.len > 0); // Do not set MSG_MORE, see https://github.com/oven-sh/bun/issues/4010 - const tcp = this.tcp orelse return false; - const wrote = tcp.write(out_buf, false); + if (this.tcp.isClosed()) { + return false; + } + const wrote = this.tcp.write(out_buf, false); if (wrote < 0) { this.terminate(ErrorCode.failed_to_write); return false; @@ -1604,7 +1650,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { fn sendPong(this: *WebSocket, socket: Socket) bool { if (socket.isClosed() or socket.isShutdown()) { - this.dispatchAbruptClose(); + this.dispatchAbruptClose(ErrorCode.ended); return false; } @@ -1636,7 +1682,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { ) void { log("Sending close with code {d}", .{code}); if (socket.isClosed() or socket.isShutdown()) { - this.dispatchAbruptClose(); + this.dispatchAbruptClose(ErrorCode.ended); this.clearData(); return; } @@ -1673,15 +1719,12 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { Mask.fill(this.globalThis, mask_buf, slice[6..], slice[6..]); if (this.enqueueEncodedBytes(socket, slice)) { - this.dispatchClose(code, &reason); this.clearData(); + this.dispatchClose(code, &reason); } } pub fn isSameSocket(this: *WebSocket, socket: Socket) bool { - if (this.tcp) |tcp| { - return socket.socket.eq(tcp.socket); - } - return false; + return socket.socket.eq(this.tcp.socket); } pub fn handleEnd(this: *WebSocket, socket: Socket) void { @@ -1707,6 +1750,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { this.terminate(ErrorCode.timeout); } pub fn handleConnectError(this: *WebSocket, _: Socket, _: c_int) void { + this.tcp.detach(); this.terminate(ErrorCode.failed_to_connect); } @@ -1721,7 +1765,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { op: u8, ) callconv(.C) void { if (!this.hasTCP() or op > 0xF) { - this.dispatchAbruptClose(); + this.dispatchAbruptClose(ErrorCode.ended); return; } @@ -1733,17 +1777,14 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { if (!this.hasBackpressure() and frame_size < stack_frame_size) { var inline_buf: [stack_frame_size]u8 = undefined; bytes.copy(this.globalThis, inline_buf[0..frame_size], slice.len, opcode); - _ = this.enqueueEncodedBytes(this.tcp.?, inline_buf[0..frame_size]); + _ = this.enqueueEncodedBytes(this.tcp, inline_buf[0..frame_size]); return; } _ = this.sendData(bytes, !this.hasBackpressure(), opcode); } fn hasTCP(this: *WebSocket) bool { - if (this.tcp) |tcp| { - return !tcp.isClosed() and !tcp.isShutdown(); - } - return false; + return !this.tcp.isClosed() and !this.tcp.isShutdown(); } pub fn writeString( @@ -1753,10 +1794,10 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { ) callconv(.C) void { const str = str_.*; if (!this.hasTCP()) { - this.dispatchAbruptClose(); + this.dispatchAbruptClose(ErrorCode.ended); return; } - const tcp = this.tcp.?; + const tcp = this.tcp; // Note: 0 is valid @@ -1796,12 +1837,13 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { ); } - fn dispatchAbruptClose(this: *WebSocket) void { + fn dispatchAbruptClose(this: *WebSocket, code: ErrorCode) void { var out = this.outgoing_websocket orelse return; this.poll_ref.unref(this.globalThis.bunVM()); JSC.markBinding(@src()); this.outgoing_websocket = null; - out.didAbruptClose(ErrorCode.closed); + out.didAbruptClose(code); + this.deref(); } fn dispatchClose(this: *WebSocket, code: u16, reason: *const bun.String) void { @@ -1810,12 +1852,13 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { JSC.markBinding(@src()); this.outgoing_websocket = null; out.didClose(code, reason); + this.deref(); } pub fn close(this: *WebSocket, code: u16, reason: ?*const JSC.ZigString) callconv(.C) void { if (!this.hasTCP()) return; - const tcp = this.tcp.?; + const tcp = this.tcp; var close_reason_buf: [128]u8 = undefined; if (reason) |str| { inner: { @@ -1837,6 +1880,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { pub const Handle = JSC.AnyTask.New(@This(), handle); + pub usingnamespace bun.New(@This()); + pub fn handleWithoutDeinit(this: *@This()) void { var this_socket = this.adopted orelse return; this.adopted = null; @@ -1844,17 +1889,19 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { var ws = this.ws; defer ws.unref(); - if (this_socket.outgoing_websocket != null and this_socket.tcp != null) { - this_socket.handleData(this_socket.tcp.?, this.slice); + if (this_socket.outgoing_websocket != null and !this_socket.tcp.isClosed()) { + this_socket.handleData(this_socket.tcp, this.slice); } } pub fn handle(this: *@This()) void { - defer { - bun.default_allocator.free(this.slice); - bun.default_allocator.destroy(this); - } this.handleWithoutDeinit(); + this.deinit(); + } + + pub fn deinit(this: *@This()) void { + bun.default_allocator.free(this.slice); + this.destroy(); } }; @@ -1869,13 +1916,14 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { const tcp = @as(*uws.Socket, @ptrCast(input_socket)); const ctx = @as(*uws.SocketContext, @ptrCast(socket_ctx)); var ws = WebSocket.new(WebSocket{ - .tcp = undefined, + .tcp = .{ .socket = .{ .detached = {} } }, .outgoing_websocket = outgoing, .globalThis = globalThis, .send_buffer = bun.LinearFifo(u8, .Dynamic).init(bun.default_allocator), .receive_buffer = bun.LinearFifo(u8, .Dynamic).init(bun.default_allocator), .event_loop = globalThis.bunVM().eventLoop(), }); + if (!Socket.adoptPtr( tcp, ctx, @@ -1883,7 +1931,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { "tcp", ws, )) { - ws.destroy(); + ws.deref(); return null; } @@ -1893,12 +1941,11 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { const buffered_slice: []u8 = buffered_data[0..buffered_data_len]; if (buffered_slice.len > 0) { - const initial_data = bun.default_allocator.create(InitialDataHandler) catch unreachable; - initial_data.* = .{ + const initial_data = InitialDataHandler.new(.{ .adopted = ws, .slice = buffered_slice, .ws = outgoing, - }; + }); // Use a higher-priority callback for the initial onData handler globalThis.queueMicrotaskCallback(initial_data, InitialDataHandler.handle); @@ -1907,6 +1954,10 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { // before the initial data handler is called outgoing.ref(); } + + // And lastly, ref the new websocket since C++ has a reference to it + ws.ref(); + return @as( *anyopaque, @ptrCast(ws), @@ -1917,15 +1968,33 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { log("finalize", .{}); this.clearData(); - this.outgoing_websocket = null; - const tcp = this.tcp orelse return; - this.tcp = null; - // no need to be .failure we still wanna to send pending SSL buffer + close_notify - if (comptime ssl) { - tcp.close(.normal); - } else { - tcp.close(.failure); + // This is only called by outgoing_websocket. + if (this.outgoing_websocket != null) { + this.outgoing_websocket = null; + this.deref(); } + + if (!this.tcp.isClosed()) { + // no need to be .failure we still wanna to send pending SSL buffer + close_notify + if (comptime ssl) { + this.tcp.close(.normal); + } else { + this.tcp.close(.failure); + } + } + } + + pub fn deinit(this: *WebSocket) void { + this.clearData(); + this.destroy(); + } + + pub fn memoryCost(this: *WebSocket) callconv(.C) usize { + var cost: usize = @sizeOf(WebSocket); + cost += this.send_buffer.buf.len; + cost += this.receive_buffer.buf.len; + // This is under-estimated a little, as we don't include usockets context. + return cost; } pub const Export = shim.exportFunctions(.{ @@ -1936,6 +2005,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { .register = register, .init = init, .finalize = finalize, + .memoryCost = memoryCost, }); comptime { @@ -1947,6 +2017,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { @export(register, .{ .name = Export[4].symbol_name }); @export(init, .{ .name = Export[5].symbol_name }); @export(finalize, .{ .name = Export[6].symbol_name }); + @export(memoryCost, .{ .name = Export[7].symbol_name }); } } }; diff --git a/src/ini.zig b/src/ini.zig index d13c34d696..77fe57fe03 100644 --- a/src/ini.zig +++ b/src/ini.zig @@ -528,7 +528,7 @@ pub const IniTestingAPIs = struct { defer arena.deinit(); const envjs = callframe.argument(1); - const env = if (envjs.isEmptyOrUndefinedOrNull()) globalThis.bunVM().bundler.env else brk: { + const env = if (envjs.isEmptyOrUndefinedOrNull()) globalThis.bunVM().transpiler.env else brk: { var envmap = bun.DotEnv.Map.HashTable.init(allocator); var object_iter = JSC.JSPropertyIterator(.{ .skip_empty_name = false, @@ -611,7 +611,7 @@ pub const IniTestingAPIs = struct { const utf8str = bunstr.toUTF8(bun.default_allocator); defer utf8str.deinit(); - var parser = Parser.init(bun.default_allocator, "", utf8str.slice(), globalThis.bunVM().bundler.env); + var parser = Parser.init(bun.default_allocator, "", utf8str.slice(), globalThis.bunVM().transpiler.env); defer parser.deinit(); try parser.parse(parser.arena.allocator()); @@ -971,6 +971,68 @@ pub fn loadNpmrc( } } + if (out.asProperty("omit")) |omit| { + switch (omit.expr.data) { + .e_string => |str| { + if (str.eqlComptime("dev")) { + install.save_dev = false; + } else if (str.eqlComptime("peer")) { + install.save_peer = false; + } else if (str.eqlComptime("optional")) { + install.save_optional = false; + } + }, + .e_array => |arr| { + for (arr.items.slice()) |item| { + switch (item.data) { + .e_string => |str| { + if (str.eqlComptime("dev")) { + install.save_dev = false; + } else if (str.eqlComptime("peer")) { + install.save_peer = false; + } else if (str.eqlComptime("optional")) { + install.save_optional = false; + } + }, + else => {}, + } + } + }, + else => {}, + } + } + + if (out.asProperty("include")) |omit| { + switch (omit.expr.data) { + .e_string => |str| { + if (str.eqlComptime("dev")) { + install.save_dev = true; + } else if (str.eqlComptime("peer")) { + install.save_peer = true; + } else if (str.eqlComptime("optional")) { + install.save_optional = true; + } + }, + .e_array => |arr| { + for (arr.items.slice()) |item| { + switch (item.data) { + .e_string => |str| { + if (str.eqlComptime("dev")) { + install.save_dev = true; + } else if (str.eqlComptime("peer")) { + install.save_peer = true; + } else if (str.eqlComptime("optional")) { + install.save_optional = true; + } + }, + else => {}, + } + } + }, + else => {}, + } + } + var registry_map = install.scoped orelse bun.Schema.Api.NpmRegistryMap{}; // Process scopes diff --git a/src/install/bin.zig b/src/install/bin.zig index 9837f80206..d0162e6383 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -28,16 +28,11 @@ const Lockfile = Install.Lockfile; /// - map where keys are names of the binaries and values are file paths to the binaries pub const Bin = extern struct { tag: Tag = Tag.none, - unset: u8 = 0, - _padding_tag: [2]u8 = .{0} ** 2, + _padding_tag: [3]u8 = .{0} ** 3, // Largest member must be zero initialized value: Value = Value{ .map = ExternalStringList{} }, - pub fn isUnset(this: *const Bin) bool { - return this.unset != 0; - } - pub fn count(this: *const Bin, buf: []const u8, extern_strings: []const ExternalString, comptime StringBuilder: type, builder: StringBuilder) u32 { switch (this.tag) { .file => builder.count(this.value.file.slice(buf)), @@ -59,26 +54,67 @@ pub const Bin = extern struct { return 0; } + pub fn eql( + l: *const Bin, + r: *const Bin, + l_buf: string, + l_extern_strings: []const ExternalString, + r_buf: string, + r_extern_strings: []const ExternalString, + ) bool { + if (l.tag != r.tag) return false; + + return switch (l.tag) { + .none => true, + .file => l.value.file.eql(r.value.file, l_buf, r_buf), + .dir => l.value.dir.eql(r.value.dir, l_buf, r_buf), + .named_file => l.value.named_file[0].eql(r.value.named_file[0], l_buf, r_buf) and + l.value.named_file[1].eql(r.value.named_file[1], l_buf, r_buf), + .map => { + const l_list = l.value.map.get(l_extern_strings); + const r_list = r.value.map.get(r_extern_strings); + if (l_list.len != r_list.len) return false; + + // assuming these maps are small without duplicate keys + var i: usize = 0; + outer: while (i < l_list.len) : (i += 2) { + var j: usize = 0; + while (j < r_list.len) : (j += 2) { + if (l_list[i].hash == r_list[j].hash) { + if (l_list[i + 1].hash != r_list[j + 1].hash) { + return false; + } + + continue :outer; + } + } + + // not found + return false; + } + + return true; + }, + }; + } + pub fn clone(this: *const Bin, buf: []const u8, prev_external_strings: []const ExternalString, all_extern_strings: []ExternalString, extern_strings_slice: []ExternalString, comptime StringBuilder: type, builder: StringBuilder) Bin { switch (this.tag) { .none => { return Bin{ .tag = .none, - .unset = this.unset, .value = Value.init(.{ .none = {} }), }; }, .file => { return Bin{ .tag = .file, - .unset = this.unset, .value = Value.init(.{ .file = builder.append(String, this.value.file.slice(buf)) }), }; }, .named_file => { return Bin{ .tag = .named_file, - .unset = this.unset, .value = Value.init( .{ .named_file = [2]String{ @@ -92,7 +128,6 @@ pub const Bin = extern struct { .dir => { return Bin{ .tag = .dir, - .unset = this.unset, .value = Value.init(.{ .dir = builder.append(String, this.value.dir.slice(buf)) }), }; }, @@ -103,7 +138,6 @@ pub const Bin = extern struct { return Bin{ .tag = .map, - .unset = this.unset, .value = Value.init(.{ .map = ExternalStringList.init(all_extern_strings, extern_strings_slice) }), }; }, @@ -118,7 +152,6 @@ pub const Bin = extern struct { const cloned: Bin = .{ .tag = this.tag, - .unset = this.unset, .value = switch (this.tag) { .none => Value.init(.{ .none = {} }), @@ -236,6 +269,46 @@ pub const Bin = extern struct { return .{}; } + /// Writes value of bin to a single line, either as a string or object. Cannot be `.none` because a value is expected to be + /// written to the json, as a property value or array value. + pub fn toSingleLineJson(this: *const Bin, buf: string, extern_strings: []const ExternalString, writer: anytype) @TypeOf(writer).Error!void { + bun.debugAssert(this.tag != .none); + switch (this.tag) { + .none => {}, + .file => { + try writer.print("{}", .{this.value.file.fmtJson(buf, .{})}); + }, + .named_file => { + try writer.writeByte('{'); + try writer.print(" {}: {} ", .{ + this.value.named_file[0].fmtJson(buf, .{}), + this.value.named_file[1].fmtJson(buf, .{}), + }); + try writer.writeByte('}'); + }, + .dir => { + try writer.print("{}", .{this.value.dir.fmtJson(buf, .{})}); + }, + .map => { + try writer.writeByte('{'); + const list = this.value.map.get(extern_strings); + var first = true; + var i: usize = 0; + while (i < list.len) : (i += 2) { + if (!first) { + try writer.writeByte(','); + } + first = false; + try writer.print(" {}: {}", .{ + list[i].value.fmtJson(buf, .{}), + list[i + 1].value.fmtJson(buf, .{}), + }); + } + try writer.writeAll(" }"); + }, + } + } + pub fn init() Bin { return bun.serializable(.{ .tag = .none, .value = Value.init(.{ .none = {} }) }); } diff --git a/src/install/bun.lock.zig b/src/install/bun.lock.zig index c399aecc75..a865e21978 100644 --- a/src/install/bun.lock.zig +++ b/src/install/bun.lock.zig @@ -35,6 +35,9 @@ const Meta = BinaryLockfile.Package.Meta; const Negatable = Npm.Negatable; const DependencyID = Install.DependencyID; const invalid_dependency_id = Install.invalid_dependency_id; +const DependencyIDSlice = BinaryLockfile.DependencyIDSlice; +const Bin = Install.Bin; +const ExternalString = Semver.ExternalString; /// A property key in the `packages` field of the lockfile pub const PkgPath = struct { @@ -212,179 +215,6 @@ pub const PkgPath = struct { .depth = 0, }; } - - pub const Map = struct { - root: Node, - - const Nodes = bun.StringArrayHashMapUnmanaged(Node); - - pub const Node = struct { - pkg_id: PackageID, - dep_id: DependencyID, - parent: ?*Node, - nodes: Nodes, - - pub fn deinit(this: *Node, allocator: std.mem.Allocator) void { - for (this.nodes.values()) |*node| { - node.deinit(allocator); - } - - this.nodes.deinit(allocator); - } - }; - - pub fn init() Map { - return .{ - .root = .{ - .pkg_id = 0, - .dep_id = invalid_dependency_id, - .parent = null, - .nodes = .{}, - }, - }; - } - - pub fn deinit(this: *Map, allocator: std.mem.Allocator) void { - for (this.root.nodes.values()) |*node| { - node.deinit(allocator); - } - } - - const InsertError = OOM || error{ - InvalidPackageKey, - DuplicatePackagePath, - }; - - pub fn insert(this: *Map, allocator: std.mem.Allocator, pkg_path: string, id: PackageID) InsertError!void { - var iter = PkgPath.iterator(pkg_path); - - var parent: ?*Node = null; - var curr: *Node = &this.root; - while (try iter.next()) |name| { - const entry = try curr.nodes.getOrPut(allocator, name); - if (!entry.found_existing) { - // probably should use String.Buf for small strings and - // deduplication. - entry.key_ptr.* = try allocator.dupe(u8, name); - entry.value_ptr.* = .{ - .pkg_id = invalid_package_id, - .dep_id = invalid_dependency_id, - .parent = parent, - .nodes = .{}, - }; - } - - parent = curr; - curr = entry.value_ptr; - } - - if (parent == null) { - return error.InvalidPackageKey; - } - - if (curr.pkg_id != invalid_package_id) { - return error.DuplicatePackagePath; - } - - curr.pkg_id = id; - } - - pub fn get(this: *Map, pkg_path: string) error{InvalidPackageKey}!?*Node { - var iter = iterator(pkg_path); - var curr: *Node = &this.root; - while (try iter.next()) |name| { - curr = curr.nodes.getPtr(name) orelse return null; - } - - return curr; - } - - pub fn iterate(this: *const Map, allocator: std.mem.Allocator) OOM!Map.Iterator { - var tree_buf: std.ArrayListUnmanaged(Map.Iterator.TreeInfo) = .{}; - try tree_buf.append(allocator, .{ - .nodes = this.root.nodes, - .pkg_id = 0, - .dep_id = BinaryLockfile.Tree.root_dep_id, - .id = 0, - .parent_id = BinaryLockfile.Tree.invalid_id, - }); - return .{ - .tree_buf = tree_buf, - .deps_buf = .{}, - }; - } - - /// Breadth-first iterator - pub const Iterator = struct { - tree_buf: std.ArrayListUnmanaged(TreeInfo), - - deps_buf: std.ArrayListUnmanaged(DependencyID), - - pub const TreeInfo = struct { - // name: String, - nodes: Nodes, - pkg_id: PackageID, - dep_id: DependencyID, - id: BinaryLockfile.Tree.Id, - parent_id: BinaryLockfile.Tree.Id, - }; - - pub const Next = struct { - id: BinaryLockfile.Tree.Id, - parent_id: BinaryLockfile.Tree.Id, - tree_dep_id: DependencyID, - dep_ids: []const DependencyID, - }; - - pub fn deinit(this: *Map.Iterator, allocator: std.mem.Allocator) void { - this.tree_buf.deinit(allocator); - this.deps_buf.deinit(allocator); - } - - pub fn next(this: *Map.Iterator, allocator: std.mem.Allocator) OOM!?Next { - if (this.tree_buf.items.len == 0) { - return null; - } - - this.deps_buf.clearRetainingCapacity(); - - var next_id = this.tree_buf.getLast().id + 1; - - // TODO(dylan-conway): try doubly linked list - const tree = this.tree_buf.orderedRemove(0); - - for (tree.nodes.values()) |node| { - if (node.nodes.count() > 0) { - try this.tree_buf.append(allocator, .{ - .nodes = node.nodes, - .id = next_id, - .parent_id = tree.id, - .pkg_id = node.pkg_id, - .dep_id = node.dep_id, - }); - next_id += 1; - } - - try this.deps_buf.append(allocator, node.dep_id); - } - - return .{ - .id = tree.id, - .parent_id = tree.parent_id, - .tree_dep_id = tree.dep_id, - .dep_ids = this.deps_buf.items, - }; - - // return tree; - // .dep_id = tree.dep_id, - // .pkg_id = tree.pkg_id, - // .id = tree.tree_id, - // .parent_id = tree.parent_id, - // .nodes = tree.nodes, - // }; - } - }; - }; }; pub const Version = enum(u32) { @@ -396,6 +226,19 @@ pub const Version = enum(u32) { pub const current: Version = .v0; }; +// For sorting dependencies belonging to a node_modules folder. No duplicate names, so +// only string compare +const TreeDepsSortCtx = struct { + string_buf: string, + deps_buf: []const Dependency, + + pub fn isLessThan(this: @This(), lhs: DependencyID, rhs: DependencyID) bool { + const l = this.deps_buf[lhs]; + const r = this.deps_buf[rhs]; + return strings.cmpStringsAsc({}, l.name.slice(this.string_buf), r.name.slice(this.string_buf)); + } +}; + pub const Stringifier = struct { const indent_scalar = 2; @@ -403,11 +246,7 @@ pub const Stringifier = struct { // _ = this; // } - pub fn saveFromBinary(allocator: std.mem.Allocator, lockfile: *const BinaryLockfile) OOM!string { - var writer_buf = MutableString.initEmpty(allocator); - var buffered_writer = writer_buf.bufferedWriter(); - var writer = buffered_writer.writer(); - + pub fn saveFromBinary(allocator: std.mem.Allocator, lockfile: *const BinaryLockfile, writer: anytype) @TypeOf(writer).Error!void { const buf = lockfile.buffers.string_bytes.items; const deps_buf = lockfile.buffers.dependencies.items; const resolution_buf = lockfile.buffers.resolutions.items; @@ -417,6 +256,7 @@ pub const Stringifier = struct { const pkg_names: []String = pkgs.items(.name); const pkg_name_hashes: []PackageNameHash = pkgs.items(.name_hash); const pkg_metas: []BinaryLockfile.Package.Meta = pkgs.items(.meta); + const pkg_bins = pkgs.items(.bin); var temp_buf: std.ArrayListUnmanaged(u8) = .{}; defer temp_buf.deinit(allocator); @@ -515,10 +355,33 @@ pub const Stringifier = struct { try decIndent(writer, indent); try writer.writeAll("},\n"); + const TreeSortCtx = struct { + pub const Item = struct { []const DependencyID, string, usize }; + + pub fn isLessThan(_: void, l: Item, r: Item) bool { + _, const l_rel_path, const l_depth = l; + _, const r_rel_path, const r_depth = r; + return switch (std.math.order(l_depth, r_depth)) { + .lt => true, + .gt => false, + .eq => strings.order(l_rel_path, r_rel_path) == .lt, + }; + } + }; + + var tree_sort_buf: std.ArrayListUnmanaged(TreeSortCtx.Item) = .{}; + defer tree_sort_buf.deinit(allocator); + var pkgs_iter = BinaryLockfile.Tree.Iterator(.pkg_path).init(lockfile); // find trusted and patched dependencies. also overrides while (pkgs_iter.next({})) |node| { + try tree_sort_buf.append(allocator, .{ + node.dependencies, + try allocator.dupe(u8, node.relative_path), + node.depth, + }); + for (node.dependencies) |dep_id| { const pkg_id = resolution_buf[dep_id]; if (pkg_id == invalid_package_id) continue; @@ -570,6 +433,13 @@ pub const Stringifier = struct { pkgs_iter.reset(); + std.sort.pdq( + TreeSortCtx.Item, + tree_sort_buf.items, + {}, + TreeSortCtx.isLessThan, + ); + if (found_trusted_dependencies.count() > 0) { try writeIndent(writer, indent); try writer.writeAll( @@ -641,19 +511,8 @@ pub const Stringifier = struct { ); } - const DepSortCtx = struct { - string_buf: string, - deps_buf: []const Dependency, - - pub fn isLessThan(this: @This(), lhs: DependencyID, rhs: DependencyID) bool { - const l = this.deps_buf[lhs]; - const r = this.deps_buf[rhs]; - return strings.cmpStringsAsc({}, l.name.slice(this.string_buf), r.name.slice(this.string_buf)); - } - }; - - var deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{}; - defer deps_sort_buf.deinit(allocator); + var tree_deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{}; + defer tree_deps_sort_buf.deinit(allocator); var pkg_deps_sort_buf: std.ArrayListUnmanaged(DependencyID) = .{}; defer pkg_deps_sort_buf.deinit(allocator); @@ -661,18 +520,19 @@ pub const Stringifier = struct { try writeIndent(writer, indent); try writer.writeAll("\"packages\": {"); var first = true; - while (pkgs_iter.next({})) |node| { - deps_sort_buf.clearRetainingCapacity(); - try deps_sort_buf.appendSlice(allocator, node.dependencies); + for (tree_sort_buf.items) |item| { + const dependencies, const relative_path, const depth = item; + tree_deps_sort_buf.clearRetainingCapacity(); + try tree_deps_sort_buf.appendSlice(allocator, dependencies); std.sort.pdq( DependencyID, - deps_sort_buf.items, - DepSortCtx{ .string_buf = buf, .deps_buf = deps_buf }, - DepSortCtx.isLessThan, + tree_deps_sort_buf.items, + TreeDepsSortCtx{ .string_buf = buf, .deps_buf = deps_buf }, + TreeDepsSortCtx.isLessThan, ); - for (deps_sort_buf.items) |dep_id| { + for (tree_deps_sort_buf.items) |dep_id| { const pkg_id = resolution_buf[dep_id]; if (pkg_id == invalid_package_id) continue; @@ -690,15 +550,15 @@ pub const Stringifier = struct { try writer.writeByte('\n'); try incIndent(writer, indent); } else { - try writer.writeAll(",\n"); + try writer.writeAll(",\n\n"); try writeIndent(writer, indent); } try writer.writeByte('"'); // relative_path is empty string for root resolutions - try writer.writeAll(node.relative_path); + try writer.writeAll(relative_path); - if (node.depth != 0) { + if (depth != 0) { try writer.writeByte('/'); } @@ -709,8 +569,9 @@ pub const Stringifier = struct { dep_name, }); - const pkg_name = pkg_names[pkg_id].slice(buf); + const pkg_name = pkg_names[pkg_id]; const pkg_meta = pkg_metas[pkg_id]; + const pkg_bin = pkg_bins[pkg_id]; const pkg_deps_list = pkg_dep_lists[pkg_id]; pkg_deps_sort_buf.clearRetainingCapacity(); @@ -719,73 +580,87 @@ pub const Stringifier = struct { pkg_deps_sort_buf.appendAssumeCapacity(@intCast(pkg_dep_id)); } + // there might be duplicate names due to dependency behaviors, + // but we print behaviors in different groups so it won't affect + // the result std.sort.pdq( DependencyID, pkg_deps_sort_buf.items, - DepSortCtx{ .string_buf = buf, .deps_buf = deps_buf }, - DepSortCtx.isLessThan, + TreeDepsSortCtx{ .string_buf = buf, .deps_buf = deps_buf }, + TreeDepsSortCtx.isLessThan, ); - // first index is resolution for all dependency types - // npm -> [ "name@version", registry or "" (default), deps..., integrity, ... ] - // symlink -> [ "name@link:path", deps..., ... ] - // folder -> [ "name@path", deps..., ... ] - // workspace -> [ "name@workspace:path", version or "", deps..., ... ] - // tarball -> [ "name@tarball", deps..., ... ] - // root -> [ "name@root:" ] - // git -> [ "name@git+repo", deps..., ... ] - // github -> [ "name@github:user/repo", deps..., ... ] + // INFO = { prod/dev/optional/peer dependencies, os, cpu, libc (TODO), bin, binDir } + + // first index is resolution for each type of package + // npm -> [ "name@version", registry (TODO: remove if default), INFO, integrity] + // symlink -> [ "name@link:path", INFO ] + // folder -> [ "name@file:path", INFO ] + // workspace -> [ "name@workspace:path", INFO ] + // tarball -> [ "name@tarball", INFO ] + // root -> [ "name@root:", { bin, binDir } ] + // git -> [ "name@git+repo", INFO, .bun-tag string (TODO: remove this) ] + // github -> [ "name@github:user/repo", INFO, .bun-tag string (TODO: remove this) ] switch (res.tag) { .root => { - try writer.print("[\"{}@root:\"]", .{ - bun.fmt.formatJSONStringUTF8(pkg_name, .{ .quote = false }), + try writer.print("[\"{}@root:\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), // we don't read the root package version into the binary lockfile }); + + try writer.writeByte('{'); + if (pkg_bin.tag != .none) { + try writer.writeAll(if (pkg_bin.tag == .dir) " \"binDir\": " else " \"bin\": "); + try pkg_bin.toSingleLineJson(buf, lockfile.buffers.extern_strings.items, writer); + try writer.writeAll(" }]"); + } else { + try writer.writeAll("}]"); + } }, .folder => { - try writer.print("[\"{s}@file:{}\", ", .{ - pkg_name, - bun.fmt.formatJSONStringUTF8(res.value.folder.slice(buf), .{ .quote = false }), + try writer.print("[\"{}@file:{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), + res.value.folder.fmtJson(buf, .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); try writer.writeByte(']'); }, .local_tarball => { - try writer.print("[\"{s}@{}\", ", .{ - pkg_name, - bun.fmt.formatJSONStringUTF8(res.value.local_tarball.slice(buf), .{ .quote = false }), + try writer.print("[\"{}@{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), + res.value.local_tarball.fmtJson(buf, .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); try writer.writeByte(']'); }, .remote_tarball => { - try writer.print("[\"{s}@{}\", ", .{ - pkg_name, - bun.fmt.formatJSONStringUTF8(res.value.remote_tarball.slice(buf), .{ .quote = false }), + try writer.print("[\"{}@{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), + res.value.remote_tarball.fmtJson(buf, .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); try writer.writeByte(']'); }, .symlink => { - try writer.print("[\"{s}@link:{}\", ", .{ - pkg_name, - bun.fmt.formatJSONStringUTF8(res.value.symlink.slice(buf), .{ .quote = false }), + try writer.print("[\"{}@link:{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), + res.value.symlink.fmtJson(buf, .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); try writer.writeByte(']'); }, .npm => { - try writer.print("[\"{s}@{}\", ", .{ - pkg_name, + try writer.print("[\"{}@{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), res.value.npm.version.fmt(buf), }); @@ -797,34 +672,34 @@ pub const Stringifier = struct { res.value.npm.url.slice(buf), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); try writer.print(", \"{}\"]", .{ pkg_meta.integrity, }); }, .workspace => { - const workspace_path = res.value.workspace.slice(buf); - - try writer.print("[\"{s}@workspace:{}\", ", .{ - pkg_name, - bun.fmt.formatJSONStringUTF8(workspace_path, .{ .quote = false }), + try writer.print("[\"{}@workspace:{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), + res.value.workspace.fmtJson(buf, .{ .quote = false }), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); try writer.writeByte(']'); }, inline .git, .github => |tag| { const repo: Repository = @field(res.value, @tagName(tag)); - try writer.print("[\"{s}@{}\", ", .{ - pkg_name, + try writer.print("[\"{}@{}\", ", .{ + pkg_name.fmtJson(buf, .{ .quote = false }), repo.fmt(if (comptime tag == .git) "git+" else "github:", buf), }); - try writePackageDepsAndMeta(writer, dep_id, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, buf, &optional_peers_buf); + try writePackageInfoObject(writer, dep.behavior, deps_buf, pkg_deps_sort_buf.items, &pkg_meta, &pkg_bin, buf, &optional_peers_buf, lockfile.buffers.extern_strings.items); - try writer.writeByte(']'); + try writer.print(", {}]", .{ + repo.resolved.fmtJson(buf, .{}), + }); }, else => unreachable, } @@ -839,21 +714,20 @@ pub const Stringifier = struct { } try decIndent(writer, indent); try writer.writeAll("}\n"); - - try buffered_writer.flush(); - return writer_buf.list.items; } - /// Writes a single line object. + /// Writes a single line object. Contains dependencies, os, cpu, libc (soon), and bin /// { "devDependencies": { "one": "1.1.1", "two": "2.2.2" }, "os": "none" } - fn writePackageDepsAndMeta( + fn writePackageInfoObject( writer: anytype, - _: DependencyID, + dep_behavior: Dependency.Behavior, deps_buf: []const Dependency, pkg_dep_ids: []const DependencyID, meta: *const Meta, + bin: *const Install.Bin, buf: string, optional_peers_buf: *std.ArrayList(String), + extern_strings: []const ExternalString, ) OOM!void { defer optional_peers_buf.clearRetainingCapacity(); @@ -900,7 +774,7 @@ pub const Stringifier = struct { if (optional_peers_buf.items.len > 0) { bun.debugAssert(any); try writer.writeAll( - \\, "optionalPeerDependencies": [ + \\, "optionalPeers": [ ); for (optional_peers_buf.items, 0..) |optional_peer, i| { @@ -916,6 +790,18 @@ pub const Stringifier = struct { try writer.writeByte(']'); } + if (dep_behavior.isBundled()) { + if (any) { + try writer.writeByte(','); + } else { + any = true; + } + + try writer.writeAll( + \\ "bundled": true + ); + } + // TODO(dylan-conway) // if (meta.libc != .all) { // try writer.writeAll( @@ -949,6 +835,16 @@ pub const Stringifier = struct { try Negatable(Npm.Architecture).toJson(meta.arch, writer); } + if (bin.tag != .none) { + if (any) { + try writer.writeByte(','); + } else { + any = true; + } + try writer.writeAll(if (bin.tag == .dir) " \"binDir\": " else " \"bin\": "); + try bin.toSingleLineJson(buf, extern_strings, writer); + } + if (any) { try writer.writeAll(" }"); } else { @@ -1050,7 +946,7 @@ pub const Stringifier = struct { ); try writeIndent(writer, indent); try writer.writeAll( - \\"optionalPeerDependencies": [ + \\"optionalPeers": [ \\ ); indent.* += 1; @@ -1093,14 +989,8 @@ pub const Stringifier = struct { } }; -const dependency_groups = [3]struct { []const u8, Dependency.Behavior }{ - .{ "dependencies", Dependency.Behavior.normal }, - .{ "peerDependencies", Dependency.Behavior.normal }, - .{ "optionalDependencies", Dependency.Behavior.normal }, -}; - const workspace_dependency_groups = [4]struct { []const u8, Dependency.Behavior }{ - .{ "dependencies", Dependency.Behavior.normal }, + .{ "dependencies", Dependency.Behavior.prod }, .{ "devDependencies", Dependency.Behavior.dev }, .{ "optionalDependencies", Dependency.Behavior.optional }, .{ "peerDependencies", Dependency.Behavior.peer }, @@ -1108,6 +998,7 @@ const workspace_dependency_groups = [4]struct { []const u8, Dependency.Behavior const ParseError = OOM || error{ InvalidLockfileVersion, + UnknownLockfileVersion, InvalidOptionalValue, InvalidPeerValue, InvalidDefaultRegistry, @@ -1137,9 +1028,6 @@ pub fn parseIntoBinaryLockfile( log: *logger.Log, manager: ?*PackageManager, ) ParseError!void { - var temp_buf: std.ArrayListUnmanaged(u8) = .{}; - defer temp_buf.deinit(allocator); - lockfile.initEmpty(allocator); const lockfile_version_expr = root.get("lockfileVersion") orelse { @@ -1147,9 +1035,22 @@ pub fn parseIntoBinaryLockfile( return error.InvalidLockfileVersion; }; - const lockfile_version: u32 = switch (lockfile_version_expr.data) { - .e_number => |num| @intFromFloat(std.math.divExact(f64, num.value, 1) catch return error.InvalidLockfileVersion), - else => return error.InvalidLockfileVersion, + const lockfile_version: u32 = lockfile_version: { + err: { + switch (lockfile_version_expr.data) { + .e_number => |num| { + if (num.value < 0 or num.value > std.math.maxInt(u32)) { + break :err; + } + + break :lockfile_version @intFromFloat(std.math.divExact(f64, num.value, 1) catch break :err); + }, + else => {}, + } + } + + try log.addError(source, lockfile_version_expr.loc, "Invalid lockfile version"); + return error.InvalidLockfileVersion; }; lockfile.text_lockfile_version = std.meta.intToEnum(Version, lockfile_version) catch { @@ -1157,7 +1058,7 @@ pub fn parseIntoBinaryLockfile( return error.InvalidLockfileVersion; }; - var string_buf = String.Buf.init(allocator); + var string_buf = lockfile.stringBuf(); if (root.get("trustedDependencies")) |trusted_dependencies_expr| { var trusted_dependencies: BinaryLockfile.TrustedDependenciesSet = .{}; @@ -1322,35 +1223,70 @@ pub fn parseIntoBinaryLockfile( var optional_peers_buf: std.AutoHashMapUnmanaged(u64, void) = .{}; defer optional_peers_buf.deinit(allocator); - if (maybe_root_pkg) |root_pkg| { - // TODO(dylan-conway): maybe sort this. behavior is already sorted, but names are not - const maybe_name = if (root_pkg.get("name")) |name| name.asString(allocator) orelse { + const root_pkg_exr = maybe_root_pkg orelse { + try log.addError(source, workspaces.loc, "Expected root package"); + return error.InvalidWorkspaceObject; + }; + + { + const maybe_name = if (root_pkg_exr.get("name")) |name| name.asString(allocator) orelse { try log.addError(source, name.loc, "Expected a string"); return error.InvalidWorkspaceObject; } else null; - const off, const len = try parseAppendDependencies(lockfile, allocator, &root_pkg, &string_buf, log, source, &optional_peers_buf); + const off, var len = try parseAppendDependencies(lockfile, allocator, &root_pkg_exr, &string_buf, log, source, &optional_peers_buf); - var pkg: BinaryLockfile.Package = .{}; - pkg.meta.id = 0; + var root_pkg: BinaryLockfile.Package = .{}; + root_pkg.meta.id = 0; if (maybe_name) |name| { const name_hash = String.Builder.stringHash(name); - pkg.name = try string_buf.appendWithHash(name, name_hash); - pkg.name_hash = name_hash; + root_pkg.name = try string_buf.appendWithHash(name, name_hash); + root_pkg.name_hash = name_hash; } - pkg.dependencies = .{ .off = off, .len = len }; - pkg.resolutions = .{ .off = off, .len = len }; + workspaces: for (lockfile.workspace_paths.values()) |workspace_path| { + for (workspaces.data.e_object.properties.slice()) |prop| { + const key = prop.key.?; + const value = prop.value.?; + const path = key.asString(allocator).?; + if (!strings.eqlLong(path, workspace_path.slice(string_buf.bytes.items), true)) continue; - try lockfile.packages.append(allocator, pkg); - } else { - try log.addError(source, workspaces.loc, "Expected root package"); - return error.InvalidWorkspaceObject; + const name = value.get("name").?.asString(allocator).?; + const name_hash = String.Builder.stringHash(name); + + const dep: Dependency = .{ + .name = try string_buf.appendWithHash(name, name_hash), + .name_hash = name_hash, + .behavior = Dependency.Behavior.workspace, + .version = .{ + .tag = .workspace, + .value = .{ + .workspace = try string_buf.append(path), + }, + }, + }; + + try lockfile.buffers.dependencies.append(allocator, dep); + len += 1; + continue :workspaces; + } + } + + root_pkg.dependencies = .{ .off = off, .len = len }; + root_pkg.resolutions = .{ .off = off, .len = len }; + + root_pkg.meta.id = 0; + try lockfile.packages.append(allocator, root_pkg); + try lockfile.getOrPutID(0, root_pkg.name_hash); } - var pkg_map = PkgPath.Map.init(); - defer pkg_map.deinit(allocator); + const PkgMapEntry = struct { + pkg_id: PackageID, + bundled: bool, + }; + var pkg_map = bun.StringArrayHashMap(PkgMapEntry).init(allocator); + defer pkg_map.deinit(); if (root.get("packages")) |pkgs_expr| { if (!pkgs_expr.isObject()) { @@ -1409,11 +1345,10 @@ pub fn parseIntoBinaryLockfile( }; if (res.tag == .npm) { - if (pkg_info.len < 2) { + if (i >= pkg_info.len) { try log.addError(source, value.loc, "Missing npm registry"); return error.InvalidPackageInfo; } - const registry_expr = pkg_info.at(i); i += 1; @@ -1438,27 +1373,48 @@ pub fn parseIntoBinaryLockfile( var pkg: BinaryLockfile.Package = .{}; + var bundled = false; + // dependencies, os, cpu, libc switch (res.tag) { .npm, .folder, .git, .github, .local_tarball, .remote_tarball, .symlink, .workspace => { - const deps_os_cpu_libc_obj = pkg_info.at(i); - i += 1; - if (!deps_os_cpu_libc_obj.isObject()) { - try log.addError(source, deps_os_cpu_libc_obj.loc, "Expected an object"); + if (i >= pkg_info.len) { + try log.addError(source, value.loc, "Missing dependencies object"); return error.InvalidPackageInfo; } - // TODO(dylan-conway): maybe sort this. behavior is already sorted, but names are not - const off, const len = try parseAppendDependencies(lockfile, allocator, deps_os_cpu_libc_obj, &string_buf, log, source, &optional_peers_buf); + const deps_os_cpu_libc_bin_bundle_obj = pkg_info.at(i); + i += 1; + if (!deps_os_cpu_libc_bin_bundle_obj.isObject()) { + try log.addError(source, deps_os_cpu_libc_bin_bundle_obj.loc, "Expected an object"); + return error.InvalidPackageInfo; + } + + if (deps_os_cpu_libc_bin_bundle_obj.get("bundled")) |bundled_expr| { + if (!bundled_expr.isBoolean()) { + try log.addError(source, bundled_expr.loc, "Expected a boolean"); + return error.InvalidPackageInfo; + } + + bundled = bundled_expr.data.e_boolean.value; + } + + const off, const len = try parseAppendDependencies(lockfile, allocator, deps_os_cpu_libc_bin_bundle_obj, &string_buf, log, source, &optional_peers_buf); pkg.dependencies = .{ .off = off, .len = len }; pkg.resolutions = .{ .off = off, .len = len }; + if (deps_os_cpu_libc_bin_bundle_obj.get("bin")) |bin| { + pkg.bin = try Bin.parseAppend(allocator, bin, &string_buf, &lockfile.buffers.extern_strings); + } else if (deps_os_cpu_libc_bin_bundle_obj.get("binDir")) |bin_dir| { + pkg.bin = try Bin.parseAppendFromDirectories(allocator, bin_dir, &string_buf); + } + if (res.tag != .workspace) { - if (deps_os_cpu_libc_obj.get("os")) |os| { + if (deps_os_cpu_libc_bin_bundle_obj.get("os")) |os| { pkg.meta.os = try Negatable(Npm.OperatingSystem).fromJson(allocator, os); } - if (deps_os_cpu_libc_obj.get("cpu")) |arch| { + if (deps_os_cpu_libc_bin_bundle_obj.get("cpu")) |arch| { pkg.meta.arch = try Negatable(Npm.Architecture).fromJson(allocator, arch); } // TODO(dylan-conway) @@ -1467,12 +1423,34 @@ pub fn parseIntoBinaryLockfile( // } } }, + .root => { + if (i >= pkg_info.len) { + try log.addError(source, value.loc, "Missing package binaries object"); + return error.InvalidPackageInfo; + } + const bin_obj = pkg_info.at(i); + i += 1; + if (!bin_obj.isObject()) { + try log.addError(source, bin_obj.loc, "Expected an object"); + return error.InvalidPackageInfo; + } + + if (bin_obj.get("bin")) |bin| { + pkg.bin = try Bin.parseAppend(allocator, bin, &string_buf, &lockfile.buffers.extern_strings); + } else if (bin_obj.get("binDir")) |bin_dir| { + pkg.bin = try Bin.parseAppendFromDirectories(allocator, bin_dir, &string_buf); + } + }, else => {}, } // integrity switch (res.tag) { .npm => { + if (i >= pkg_info.len) { + try log.addError(source, value.loc, "Missing integrity"); + return error.InvalidPackageInfo; + } const integrity_expr = pkg_info.at(i); i += 1; const integrity_str = integrity_expr.asString(allocator) orelse { @@ -1482,32 +1460,42 @@ pub fn parseIntoBinaryLockfile( pkg.meta.integrity = Integrity.parse(integrity_str); }, + inline .git, .github => |tag| { + // .bun-tag + if (i >= pkg_info.len) { + try log.addError(source, value.loc, "Missing git dependency tag"); + return error.InvalidPackageInfo; + } + + const bun_tag = pkg_info.at(i); + i += 1; + + const bun_tag_str = bun_tag.asString(allocator) orelse { + try log.addError(source, bun_tag.loc, "Expected a string"); + return error.InvalidPackageInfo; + }; + + @field(res.value, @tagName(tag)).resolved = try string_buf.append(bun_tag_str); + }, else => {}, } pkg.name = name; pkg.name_hash = name_hash; pkg.resolution = res; - - // set later - pkg.bin = .{ - .unset = 1, - }; pkg.scripts = .{}; const pkg_id = try lockfile.appendPackageDedupe(&pkg, string_buf.bytes.items); - pkg_map.insert(allocator, pkg_path, pkg_id) catch |err| { - switch (err) { - error.OutOfMemory => |oom| return oom, - error.DuplicatePackagePath => { - try log.addError(source, key.loc, "Duplicate package path"); - }, - error.InvalidPackageKey => { - try log.addError(source, key.loc, "Invalid package path"); - }, - } + const entry = try pkg_map.getOrPut(pkg_path); + if (entry.found_existing) { + try log.addError(source, key.loc, "Duplicate package path"); return error.InvalidPackageKey; + } + + entry.value_ptr.* = .{ + .pkg_id = pkg_id, + .bundled = bundled, }; } @@ -1516,15 +1504,9 @@ pub fn parseIntoBinaryLockfile( @memset(lockfile.buffers.resolutions.items, invalid_package_id); const pkgs = lockfile.packages.slice(); - const pkg_names = pkgs.items(.name); - _ = pkg_names; - const pkg_name_hashes = pkgs.items(.name_hash); - _ = pkg_name_hashes; const pkg_deps = pkgs.items(.dependencies); var pkg_metas = pkgs.items(.meta); var pkg_resolutions = pkgs.items(.resolution); - const pkg_resolution_lists = pkgs.items(.resolutions); - _ = pkg_resolution_lists; { // first the root dependencies are resolved @@ -1535,81 +1517,146 @@ pub fn parseIntoBinaryLockfile( const dep_id: DependencyID = @intCast(_dep_id); const dep = lockfile.buffers.dependencies.items[dep_id]; - if (pkg_map.root.nodes.getPtr(dep.name.slice(string_buf.bytes.items))) |dep_node| { - dep_node.dep_id = dep_id; - lockfile.buffers.resolutions.items[dep_id] = dep_node.pkg_id; - } + const entry = pkg_map.get(dep.name.slice(lockfile.buffers.string_bytes.items)) orelse { + if (dep.behavior.optional) { + continue; + } + try dependencyResolutionFailure(&dep, null, allocator, lockfile.buffers.string_bytes.items, source, log, root_pkg_exr.loc); + return error.InvalidPackageInfo; + }; + + lockfile.buffers.resolutions.items[dep_id] = entry.pkg_id; + lockfile.buffers.dependencies.items[dep_id].behavior.bundled = entry.bundled; } // TODO(dylan-conway) should we handle workspaces separately here for custom hoisting } + var path_buf: bun.PathBuffer = undefined; + // then each package dependency for (pkgs_expr.data.e_object.properties.slice()) |prop| { const key = prop.key.?; - const value = prop.value.?; const pkg_path = key.asString(allocator).?; - const i: usize = 0; - _ = i; - const pkg_info = value.data.e_array.items; - _ = pkg_info; - const pkg_map_entry = try pkg_map.get(pkg_path) orelse { + const pkg_id = (pkg_map.get(pkg_path) orelse { return error.InvalidPackagesObject; - }; - - const pkg_id = pkg_map_entry.pkg_id; + }).pkg_id; // find resolutions. iterate up to root through the pkg path. deps: for (pkg_deps[pkg_id].begin()..pkg_deps[pkg_id].end()) |_dep_id| { const dep_id: DependencyID = @intCast(_dep_id); - const dep = lockfile.buffers.dependencies.items[dep_id]; + var dep = &lockfile.buffers.dependencies.items[dep_id]; + const dep_name = dep.name.slice(lockfile.buffers.string_bytes.items); - var curr: ?*PkgPath.Map.Node = pkg_map_entry; - while (curr) |node| { - if (node.nodes.getPtr(dep.name.slice(string_buf.bytes.items))) |dep_node| { - dep_node.dep_id = dep_id; - lockfile.buffers.resolutions.items[dep_id] = dep_node.pkg_id; + @memcpy(path_buf[0..pkg_path.len], pkg_path); + path_buf[pkg_path.len] = '/'; + var offset = pkg_path.len + 1; + var valid = true; + while (valid) { + @memcpy(path_buf[offset..][0..dep_name.len], dep_name); + const res_path = path_buf[0 .. offset + dep_name.len]; + + if (pkg_map.get(res_path)) |entry| { + lockfile.buffers.resolutions.items[dep_id] = entry.pkg_id; + dep.behavior.bundled = entry.bundled; continue :deps; } - curr = node.parent orelse if (curr != &pkg_map.root) &pkg_map.root else null; + + if (offset == 0) { + if (dep.behavior.optional) { + continue :deps; + } + try dependencyResolutionFailure(dep, pkg_path, allocator, lockfile.buffers.string_bytes.items, source, log, key.loc); + return error.InvalidPackageInfo; + } + + const slash = strings.lastIndexOfChar(path_buf[0 .. offset - 1], '/') orelse { + offset = 0; + continue; + }; + + // might be a scoped package + const at = strings.lastIndexOfChar(path_buf[0 .. offset - 1], '@') orelse { + offset = slash + 1; + continue; + }; + + if (at > slash) { + valid = false; + continue; + } + + const next_slash = strings.lastIndexOfChar(path_buf[0..slash], '/') orelse { + if (at != 0) { + try log.addError(source, key.loc, "Invalid package path"); + return error.InvalidPackageKey; + } + offset = 0; + continue; + }; + + if (next_slash > at) { + // there's a scoped package but it exists farther up + offset = slash + 1; + continue; + } + + if (next_slash + 1 != at) { + valid = false; + continue; + } + + offset = at; } + + try log.addError(source, key.loc, "Invalid package path"); + return error.InvalidPackageKey; } } - { - // ids are assigned, now flatten into `lockfile.buffers.trees` and `lockfile.buffers.hoisted_dependencies` - var tree_iter = try pkg_map.iterate(allocator); - defer tree_iter.deinit(allocator); - var tree_id: BinaryLockfile.Tree.Id = 0; - while (try tree_iter.next(allocator)) |tree| { - bun.debugAssert(tree_id == tree.id); - const deps_off: u32 = @intCast(lockfile.buffers.hoisted_dependencies.items.len); - const deps_len: u32 = @intCast(tree.dep_ids.len); - try lockfile.buffers.hoisted_dependencies.appendSlice(allocator, tree.dep_ids); - try lockfile.buffers.trees.append( - allocator, - .{ - .dependency_id = tree.tree_dep_id, - .id = tree_id, - .parent = tree.parent_id, - .dependencies = .{ - .off = deps_off, - .len = deps_len, - }, - }, - ); - - tree_id += 1; + lockfile.hoist(log, .resolvable, {}) catch |err| { + switch (err) { + error.OutOfMemory => |oom| return oom, + else => { + return error.InvalidPackagesObject; + }, } - } + }; + + return; } - lockfile.buffers.string_bytes = string_buf.bytes.moveToUnmanaged(); - lockfile.string_pool = string_buf.pool; + lockfile.initEmpty(allocator); +} + +fn dependencyResolutionFailure(dep: *const Dependency, pkg_path: ?string, allocator: std.mem.Allocator, buf: string, source: *const logger.Source, log: *logger.Log, loc: logger.Loc) OOM!void { + const behavior_str = if (dep.behavior.dev) + "dev" + else if (dep.behavior.optional) + "optional" + else if (dep.behavior.peer) + "peer" + else if (dep.behavior.isWorkspaceOnly()) + "workspace" + else + "prod"; + + if (pkg_path) |path| { + try log.addErrorFmt(source, loc, allocator, "Failed to resolve {s} dependency '{s}' for package '{s}'", .{ + behavior_str, + dep.name.slice(buf), + path, + }); + } else { + try log.addErrorFmt(source, loc, allocator, "Failed to resolve root {s} dependency '{s}'", .{ + behavior_str, + dep.name.slice(buf), + }); + } } fn parseAppendDependencies( @@ -1623,7 +1670,7 @@ fn parseAppendDependencies( ) ParseError!struct { u32, u32 } { defer optional_peers_buf.clearRetainingCapacity(); - if (obj.get("optionalPeerDependencies")) |optional_peers| { + if (obj.get("optionalPeers")) |optional_peers| { if (!optional_peers.isArray()) { try log.addError(source, optional_peers.loc, "Expected an array"); return error.InvalidPackageInfo; @@ -1668,10 +1715,13 @@ fn parseAppendDependencies( const version = try buf.append(version_str); const version_sliced = version.sliced(buf.bytes.items); - var dep: Dependency = .{ + const dep: Dependency = .{ .name = name.value, .name_hash = name.hash, - .behavior = group_behavior, + .behavior = if (group_behavior.peer and optional_peers_buf.contains(name.hash)) + group_behavior.add(.optional) + else + group_behavior, .version = Dependency.parse( allocator, name.value, @@ -1686,10 +1736,6 @@ fn parseAppendDependencies( }, }; - if (dep.behavior.isPeer() and optional_peers_buf.contains(name.hash)) { - dep.behavior.optional = true; - } - try lockfile.buffers.dependencies.append(allocator, dep); } } diff --git a/src/install/default-trusted-dependencies.txt b/src/install/default-trusted-dependencies.txt index 270a9eccf5..85b0876636 100644 --- a/src/install/default-trusted-dependencies.txt +++ b/src/install/default-trusted-dependencies.txt @@ -91,7 +91,6 @@ @splunk/otel @strapi/strapi @sveltejs/kit -@swc/core @syncfusion/ej2-angular-base @taquito/taquito @temporalio/core-bridge diff --git a/src/install/dependency.zig b/src/install/dependency.zig index faeb476d2d..4ded9d5682 100644 --- a/src/install/dependency.zig +++ b/src/install/dependency.zig @@ -51,7 +51,7 @@ version: Dependency.Version = .{}, /// - `peerDependencies` /// Technically, having the same package name specified under multiple fields is invalid /// But we don't want to allocate extra arrays for them. So we use a bitfield instead. -behavior: Behavior = Behavior.uninitialized, +behavior: Behavior = .{}, /// Sorting order for dependencies is: /// 1. [ `peerDependencies`, `optionalDependencies`, `devDependencies`, `dependencies` ] @@ -1301,36 +1301,32 @@ pub fn fromJS(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JS } pub const Behavior = packed struct(u8) { - pub const uninitialized: Behavior = .{}; - - // these padding fields are to have compatibility - // with older versions of lockfile v2 _unused_1: u1 = 0, - - normal: bool = false, + prod: bool = false, optional: bool = false, dev: bool = false, peer: bool = false, workspace: bool = false, + /// Is not set for transitive bundled dependencies + bundled: bool = false, + _unused_2: u1 = 0, - _unused_2: u2 = 0, - - pub const normal = Behavior{ .normal = true }; + pub const prod = Behavior{ .prod = true }; pub const optional = Behavior{ .optional = true }; pub const dev = Behavior{ .dev = true }; pub const peer = Behavior{ .peer = true }; pub const workspace = Behavior{ .workspace = true }; - pub inline fn isNormal(this: Behavior) bool { - return this.normal; + pub inline fn isProd(this: Behavior) bool { + return this.prod; } pub inline fn isOptional(this: Behavior) bool { - return this.optional and !this.isPeer(); + return this.optional and !this.peer; } pub inline fn isOptionalPeer(this: Behavior) bool { - return this.optional and this.isPeer(); + return this.optional and this.peer; } pub inline fn isDev(this: Behavior) bool { @@ -1345,38 +1341,12 @@ pub const Behavior = packed struct(u8) { return this.workspace; } + pub inline fn isBundled(this: Behavior) bool { + return this.bundled; + } + pub inline fn isWorkspaceOnly(this: Behavior) bool { - return this.workspace and !this.dev and !this.normal and !this.optional and !this.peer; - } - - pub inline fn setNormal(this: Behavior, value: bool) Behavior { - var b = this; - b.normal = value; - return b; - } - - pub inline fn setOptional(this: Behavior, value: bool) Behavior { - var b = this; - b.optional = value; - return b; - } - - pub inline fn setDev(this: Behavior, value: bool) Behavior { - var b = this; - b.dev = value; - return b; - } - - pub inline fn setPeer(this: Behavior, value: bool) Behavior { - var b = this; - b.peer = value; - return b; - } - - pub inline fn setWorkspace(this: Behavior, value: bool) Behavior { - var b = this; - b.workspace = value; - return b; + return this.workspace and !this.dev and !this.prod and !this.optional and !this.peer; } pub inline fn eq(lhs: Behavior, rhs: Behavior) bool { @@ -1387,13 +1357,25 @@ pub const Behavior = packed struct(u8) { return @as(u8, @bitCast(lhs)) & @as(u8, @bitCast(rhs)) != 0; } + pub inline fn add(this: Behavior, kind: @Type(.EnumLiteral)) Behavior { + var new = this; + @field(new, @tagName(kind)) = true; + return new; + } + + pub inline fn set(this: Behavior, kind: @Type(.EnumLiteral), value: bool) Behavior { + var new = this; + @field(new, @tagName(kind)) = value; + return new; + } + pub inline fn cmp(lhs: Behavior, rhs: Behavior) std.math.Order { if (eq(lhs, rhs)) { return .eq; } - if (lhs.isNormal() != rhs.isNormal()) { - return if (lhs.isNormal()) + if (lhs.isProd() != rhs.isProd()) { + return if (lhs.isProd()) .gt else .lt; @@ -1435,40 +1417,15 @@ pub const Behavior = packed struct(u8) { } pub fn isEnabled(this: Behavior, features: Features) bool { - return this.isNormal() or + return this.isProd() or (features.optional_dependencies and this.isOptional()) or (features.dev_dependencies and this.isDev()) or (features.peer_dependencies and this.isPeer()) or (features.workspaces and this.isWorkspaceOnly()); } - pub fn format(self: Behavior, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - const fields = .{ - "normal", - "optional", - "dev", - "peer", - "workspace", - }; - - var first = true; - inline for (fields) |field| { - if (@field(self, field)) { - if (!first) { - try writer.writeAll(" | "); - } - try writer.writeAll(field); - first = false; - } - } - - if (first) { - try writer.writeAll("-"); - } - } - comptime { - bun.assert(@as(u8, @bitCast(Behavior.normal)) == (1 << 1)); + bun.assert(@as(u8, @bitCast(Behavior.prod)) == (1 << 1)); bun.assert(@as(u8, @bitCast(Behavior.optional)) == (1 << 2)); bun.assert(@as(u8, @bitCast(Behavior.dev)) == (1 << 3)); bun.assert(@as(u8, @bitCast(Behavior.peer)) == (1 << 4)); diff --git a/src/install/extract_tarball.zig b/src/install/extract_tarball.zig index 8959383769..e8b8fb0c48 100644 --- a/src/install/extract_tarball.zig +++ b/src/install/extract_tarball.zig @@ -53,8 +53,8 @@ pub fn buildURL( full_name_: strings.StringOrTinyString, version: Semver.Version, string_buf: []const u8, -) !string { - return try buildURLWithPrinter( +) OOM!string { + return buildURLWithPrinter( registry_, full_name_, version, diff --git a/src/install/install.zig b/src/install/install.zig index 2ff59a5060..495c63ee0a 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -37,7 +37,7 @@ const Path = bun.path; const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; const Command = @import("../cli.zig").Command; const BunArguments = @import("../cli.zig").Arguments; -const bundler = bun.bundler; +const transpiler = bun.transpiler; const DotEnv = @import("../env_loader.zig"); const which = @import("../which.zig").which; @@ -155,12 +155,7 @@ const Behavior = @import("./dependency.zig").Behavior; const FolderResolution = @import("./resolvers/folder_resolver.zig").FolderResolution; pub fn ExternalSlice(comptime Type: type) type { - return ExternalSliceAligned(Type, null); -} - -pub fn ExternalSliceAligned(comptime Type: type, comptime alignment_: ?u29) type { return extern struct { - pub const alignment = alignment_ orelse @alignOf(*Type); pub const Slice = @This(); pub const Child: type = Type; @@ -168,6 +163,12 @@ pub fn ExternalSliceAligned(comptime Type: type, comptime alignment_: ?u29) type off: u32 = 0, len: u32 = 0, + pub const invalid: @This() = .{ .off = std.math.maxInt(u32), .len = std.math.maxInt(u32) }; + + pub inline fn isInvalid(this: Slice) bool { + return this.off == std.math.maxInt(u32) and this.len == std.math.maxInt(u32); + } + pub inline fn contains(this: Slice, id: u32) bool { return id >= this.off and id < (this.len + this.off); } @@ -211,10 +212,20 @@ pub fn ExternalSliceAligned(comptime Type: type, comptime alignment_: ?u29) type pub const PackageID = u32; pub const DependencyID = u32; + +// pub const DependencyID = enum(u32) { +// root = max - 1, +// invalid = max, +// _, + +// const max = std.math.maxInt(u32); +// }; + pub const invalid_package_id = std.math.maxInt(PackageID); pub const invalid_dependency_id = std.math.maxInt(DependencyID); pub const ExternalStringList = ExternalSlice(ExternalString); +pub const ExternalPackageNameHashList = ExternalSlice(PackageNameHash); pub const VersionSlice = ExternalSlice(Semver.Version); pub const ExternalStringMap = extern struct { @@ -477,13 +488,17 @@ const NetworkTask = struct { this.http.schedule(this.allocator, batch); } + pub const ForTarballError = OOM || error{ + InvalidURL, + }; + pub fn forTarball( this: *NetworkTask, allocator: std.mem.Allocator, tarball_: *const ExtractTarball, scope: *const Npm.Registry.Scope, authorization: NetworkTask.Authorization, - ) !void { + ) ForTarballError!void { this.callback = .{ .extract = tarball_.* }; const tarball = &this.callback.extract; const tarball_url = tarball.url.slice(); @@ -504,11 +519,11 @@ const NetworkTask = struct { .args = .{ bun.fmt.QuotedFormatter{ .text = this.url_buf }, bun.fmt.QuotedFormatter{ .text = tarball.name.slice() } }, }; - this.package_manager.log.addErrorFmt(null, .{}, allocator, msg.fmt, msg.args) catch unreachable; + try this.package_manager.log.addErrorFmt(null, .{}, allocator, msg.fmt, msg.args); return error.InvalidURL; } - this.response_buffer = try MutableString.init(allocator, 0); + this.response_buffer = MutableString.initEmpty(allocator); this.allocator = allocator; var header_builder = HeaderBuilder{}; @@ -946,6 +961,8 @@ pub const Task = struct { name: strings.StringOrTinyString, url: strings.StringOrTinyString, env: DotEnv.Map, + dep_id: DependencyID, + res: Resolution, }, git_checkout: struct { repo_dir: bun.FileDescriptor, @@ -1022,10 +1039,6 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { skipped: u32 = 0, successfully_installed: ?Bitset = null, - /// The lockfile used by `installPackages`. Might be different from the lockfile - /// on disk if `--production` is used and dev dependencies are removed. - lockfile_used_for_install: *Lockfile, - /// Package name hash -> number of scripts skipped. /// Multiple versions of the same package might add to the count, and each version /// might have a different number of scripts @@ -1095,7 +1108,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { fn verifyPatchHash( this: *@This(), root_node_modules_dir: std.fs.Dir, - ) VerifyResult { + ) bool { bun.debugAssert(!this.patch.isNull()); // hash from the .patch file, to be checked against bun tag @@ -1108,22 +1121,21 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { bunhashtag, }, .posix); - var destination_dir = this.node_modules.openDir(root_node_modules_dir) catch return .{}; + var destination_dir = this.node_modules.openDir(root_node_modules_dir) catch return false; defer { if (std.fs.cwd().fd != destination_dir.fd) destination_dir.close(); } if (comptime bun.Environment.isPosix) { - _ = bun.sys.fstatat(bun.toFD(destination_dir.fd), patch_tag_path).unwrap() catch return .{}; + _ = bun.sys.fstatat(bun.toFD(destination_dir.fd), patch_tag_path).unwrap() catch return false; } else { switch (bun.sys.openat(bun.toFD(destination_dir.fd), patch_tag_path, bun.O.RDONLY, 0)) { - .err => return .{}, + .err => return false, .result => |fd| _ = bun.sys.close(fd), } } - return .{ - .valid = true, - }; + + return true; } // 1. verify that .bun-tag exists (was it installed from bun?) @@ -1132,7 +1144,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { this: *@This(), repo: *const Repository, root_node_modules_dir: std.fs.Dir, - ) VerifyResult { + ) bool { bun.copy(u8, this.destination_dir_subpath_buf[this.destination_dir_subpath.len..], std.fs.path.sep_str ++ ".bun-tag"); this.destination_dir_subpath_buf[this.destination_dir_subpath.len + std.fs.path.sep_str.len + ".bun-tag".len] = 0; const bun_tag_path: [:0]u8 = this.destination_dir_subpath_buf[0 .. this.destination_dir_subpath.len + std.fs.path.sep_str.len + ".bun-tag".len :0]; @@ -1140,61 +1152,44 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { var git_tag_stack_fallback = std.heap.stackFallback(2048, bun.default_allocator); const allocator = git_tag_stack_fallback.get(); - var destination_dir = this.node_modules.openDir(root_node_modules_dir) catch return .{}; - defer { - if (std.fs.cwd().fd != destination_dir.fd) destination_dir.close(); - } - - const bun_tag_file = File.readFrom( - destination_dir, + var bun_tag_file = this.node_modules.readSmallFile( + root_node_modules_dir, bun_tag_path, allocator, - ).unwrap() catch return .{}; - defer allocator.free(bun_tag_file); + ) catch return false; + defer bun_tag_file.bytes.deinit(); - return .{ - .valid = strings.eqlLong(repo.resolved.slice(this.lockfile.buffers.string_bytes.items), bun_tag_file, true), - }; + return strings.eqlLong(repo.resolved.slice(this.lockfile.buffers.string_bytes.items), bun_tag_file.bytes.items, true); } pub fn verify( this: *@This(), resolution: *const Resolution, root_node_modules_dir: std.fs.Dir, - bin: *Bin, - ) VerifyResult { + ) bool { const verified = switch (resolution.tag) { .git => this.verifyGitResolution(&resolution.value.git, root_node_modules_dir), .github => this.verifyGitResolution(&resolution.value.github, root_node_modules_dir), .root => this.verifyTransitiveSymlinkedFolder(root_node_modules_dir), .folder => if (this.lockfile.isWorkspaceTreeId(this.node_modules.tree_id)) - this.verifyPackageJSONNameAndVersion(root_node_modules_dir, resolution.tag, bin) + this.verifyPackageJSONNameAndVersion(root_node_modules_dir, resolution.tag) else this.verifyTransitiveSymlinkedFolder(root_node_modules_dir), - else => this.verifyPackageJSONNameAndVersion(root_node_modules_dir, resolution.tag, bin), + else => this.verifyPackageJSONNameAndVersion(root_node_modules_dir, resolution.tag), }; if (comptime kind == .patch) return verified; if (this.patch.isNull()) return verified; - if (!verified.valid) return verified; + if (!verified) return false; return this.verifyPatchHash(root_node_modules_dir); } // Only check for destination directory in node_modules. We can't use package.json because // it might not exist - fn verifyTransitiveSymlinkedFolder(this: *@This(), root_node_modules_dir: std.fs.Dir) VerifyResult { - var destination_dir = this.node_modules.openDir(root_node_modules_dir) catch return .{}; - defer destination_dir.close(); - - const exists = bun.sys.directoryExistsAt(destination_dir.fd, this.destination_dir_subpath).unwrap() catch return .{}; - return if (exists) .{ .valid = true } else .{}; + fn verifyTransitiveSymlinkedFolder(this: *@This(), root_node_modules_dir: std.fs.Dir) bool { + return this.node_modules.directoryExistsAt(root_node_modules_dir, this.destination_dir_subpath); } - const VerifyResult = struct { - valid: bool = false, - update_lockfile_pointers: bool = false, - }; - fn getInstalledPackageJsonSource( this: *PackageInstall, root_node_modules_dir: std.fs.Dir, @@ -1210,12 +1205,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { const package_json_path: [:0]u8 = this.destination_dir_subpath_buf[0 .. this.destination_dir_subpath.len + std.fs.path.sep_str.len + "package.json".len :0]; defer this.destination_dir_subpath_buf[this.destination_dir_subpath.len] = 0; - var destination_dir = this.node_modules.openDir(root_node_modules_dir) catch return null; - defer { - if (std.fs.cwd().fd != destination_dir.fd) destination_dir.close(); - } - - var package_json_file = File.openat(destination_dir, package_json_path, bun.O.RDONLY, 0).unwrap() catch return null; + var package_json_file = this.node_modules.openFile(root_node_modules_dir, package_json_path) catch return null; defer package_json_file.close(); // Heuristic: most package.jsons will be less than 2048 bytes. @@ -1251,7 +1241,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { return logger.Source.initPathString(bun.span(package_json_path), mutable.list.items[0..total]); } - fn verifyPackageJSONNameAndVersion(this: *PackageInstall, root_node_modules_dir: std.fs.Dir, resolution_tag: Resolution.Tag, bin: *Bin) VerifyResult { + fn verifyPackageJSONNameAndVersion(this: *PackageInstall, root_node_modules_dir: std.fs.Dir, resolution_tag: Resolution.Tag) bool { var body_pool = Npm.Registry.BodyPool.get(this.allocator); var mutable: MutableString = body_pool.data; defer { @@ -1264,7 +1254,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { // Don't keep it open while we're parsing the JSON. // The longer the file stays open, the more likely it causes issues for // other processes on Windows. - const source = this.getInstalledPackageJsonSource(root_node_modules_dir, &mutable, resolution_tag) orelse return .{}; + const source = this.getInstalledPackageJsonSource(root_node_modules_dir, &mutable, resolution_tag) orelse return false; var log = logger.Log.init(this.allocator); defer log.deinit(); @@ -1275,12 +1265,11 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { this.allocator, &source, &log, - if (bin.isUnset()) .check_for_bin else .ignore_bin, - ) catch return .{}; - _ = package_json_checker.parseExpr(false, false) catch return .{}; - if (log.errors > 0 or !package_json_checker.has_found_name) return .{}; + ) catch return false; + _ = package_json_checker.parseExpr() catch return false; + if (log.errors > 0 or !package_json_checker.has_found_name) return false; // workspaces aren't required to have a version - if (!package_json_checker.has_found_version and resolution_tag != .workspace) return .{}; + if (!package_json_checker.has_found_version and resolution_tag != .workspace) return false; const found_version = package_json_checker.found_version; @@ -1313,40 +1302,14 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { } // If we didn't find any of these characters, there's no point in checking the version again. // it will never match. - return .{}; + return false; }; - if (!strings.eql(found_version[offset..], this.package_version)) return .{}; + if (!strings.eql(found_version[offset..], this.package_version)) return false; } // lastly, check the name. - if (strings.eql(package_json_checker.found_name, this.package_name.slice(this.lockfile.buffers.string_bytes.items))) { - // only want to set bins if up-to-date - if (bin.isUnset() and package_json_checker.has_found_bin) { - var string_buf = this.lockfile.stringBuf(); - defer string_buf.apply(this.lockfile); - - switch (package_json_checker.found_bin) { - .bin => |expr| { - bin.* = Bin.parseAppend(this.lockfile.allocator, expr, &string_buf, &this.lockfile.buffers.extern_strings) catch bun.outOfMemory(); - }, - .dir => |expr| { - bin.* = Bin.parseAppendFromDirectories(this.lockfile.allocator, expr, &string_buf) catch bun.outOfMemory(); - }, - } - - return .{ - .valid = true, - .update_lockfile_pointers = true, - }; - } - - return .{ - .valid = true, - }; - } - - return .{}; + return strings.eql(package_json_checker.found_name, this.package_name.slice(this.lockfile.buffers.string_bytes.items)); } pub const Result = union(Tag) { @@ -2805,6 +2768,44 @@ pub const PackageManager = struct { patched_dependencies_to_remove: std.ArrayHashMapUnmanaged(PackageNameAndVersionHash, void, ArrayIdentityContext.U64, false) = .{}, + active_lifecycle_scripts: LifecycleScriptSubprocess.List, + last_reported_slow_lifecycle_script_at: u64 = 0, + cached_tick_for_slow_lifecycle_script_logging: u64 = 0, + + pub fn reportSlowLifecycleScripts(this: *PackageManager, log_level: Options.LogLevel) void { + if (log_level == .silent) return; + if (bun.getRuntimeFeatureFlag("BUN_DISABLE_SLOW_LIFECYCLE_SCRIPT_LOGGING")) { + return; + } + + if (this.active_lifecycle_scripts.peek()) |active_lifecycle_script_running_for_the_longest_amount_of_time| { + if (this.cached_tick_for_slow_lifecycle_script_logging == this.event_loop.iterationNumber()) { + return; + } + this.cached_tick_for_slow_lifecycle_script_logging = this.event_loop.iterationNumber(); + const current_time = bun.timespec.now().ns(); + const time_running = current_time -| active_lifecycle_script_running_for_the_longest_amount_of_time.started_at; + const interval: u64 = if (log_level.isVerbose()) std.time.ns_per_s * 5 else std.time.ns_per_s * 30; + if (time_running > interval and current_time -| this.last_reported_slow_lifecycle_script_at > interval) { + this.last_reported_slow_lifecycle_script_at = current_time; + const package_name = active_lifecycle_script_running_for_the_longest_amount_of_time.package_name; + + if (!(package_name.len > 1 and package_name[package_name.len - 1] == 's')) { + Output.warn("{s}'s postinstall has costed you {}\n", .{ + package_name, + bun.fmt.fmtDurationOneDecimal(time_running), + }); + } else { + Output.warn("{s}' postinstall has costed you {}\n", .{ + package_name, + bun.fmt.fmtDurationOneDecimal(time_running), + }); + } + Output.flush(); + } + } + } + pub const PackageUpdateInfo = struct { original_version_literal: string, is_alias: bool, @@ -2990,7 +2991,7 @@ pub const PackageManager = struct { pub const ScriptRunEnvironment = struct { root_dir_info: *DirInfo, - bundler: bundler.Bundler, + transpiler: bun.Transpiler, }; const TimePasser = struct { @@ -3057,9 +3058,9 @@ pub const PackageManager = struct { return false; } - pub fn configureEnvForScripts(this: *PackageManager, ctx: Command.Context, log_level: Options.LogLevel) !*bundler.Bundler { + pub fn configureEnvForScripts(this: *PackageManager, ctx: Command.Context, log_level: Options.LogLevel) !*transpiler.Transpiler { if (this.env_configure) |*env_configure| { - return &env_configure.bundler; + return &env_configure.transpiler; } // We need to figure out the PATH and other environment variables @@ -3068,13 +3069,13 @@ pub const PackageManager = struct { // so we really only want to do it when strictly necessary this.env_configure = .{ .root_dir_info = undefined, - .bundler = undefined, + .transpiler = undefined, }; - const this_bundler: *bundler.Bundler = &this.env_configure.?.bundler; + const this_transpiler: *transpiler.Transpiler = &this.env_configure.?.transpiler; const root_dir_info = try RunCommand.configureEnvForRun( ctx, - this_bundler, + this_transpiler, this.env, log_level != .silent, false, @@ -3089,12 +3090,12 @@ pub const PackageManager = struct { }; } - this.env.loadCCachePath(this_bundler.fs); + this.env.loadCCachePath(this_transpiler.fs); { var node_path: bun.PathBuffer = undefined; - if (this.env.getNodePath(this_bundler.fs, &node_path)) |node_pathZ| { - _ = try this.env.loadNodeJSConfig(this_bundler.fs, bun.default_allocator.dupe(u8, node_pathZ) catch bun.outOfMemory()); + if (this.env.getNodePath(this_transpiler.fs, &node_path)) |node_pathZ| { + _ = try this.env.loadNodeJSConfig(this_transpiler.fs, bun.default_allocator.dupe(u8, node_pathZ) catch bun.outOfMemory()); } else brk: { const current_path = this.env.get("PATH") orelse ""; var PATH = try std.ArrayList(u8).initCapacity(bun.default_allocator, current_path.len); @@ -3102,17 +3103,17 @@ pub const PackageManager = struct { var bun_path: string = ""; RunCommand.createFakeTemporaryNodeExecutable(&PATH, &bun_path) catch break :brk; try this.env.map.put("PATH", PATH.items); - _ = try this.env.loadNodeJSConfig(this_bundler.fs, bun.default_allocator.dupe(u8, bun_path) catch bun.outOfMemory()); + _ = try this.env.loadNodeJSConfig(this_transpiler.fs, bun.default_allocator.dupe(u8, bun_path) catch bun.outOfMemory()); } } this.env_configure.?.root_dir_info = root_dir_info; - return this_bundler; + return this_transpiler; } pub fn httpProxy(this: *PackageManager, url: URL) ?URL { - return this.env.getHttpProxy(url); + return this.env.getHttpProxyFor(url); } pub fn tlsRejectUnauthorized(this: *PackageManager) bool { @@ -3164,6 +3165,7 @@ pub const PackageManager = struct { } fn hasNoMorePendingLifecycleScripts(this: *PackageManager) bool { + this.reportSlowLifecycleScripts(this.options.log_level); return this.pending_lifecycle_script_tasks.load(.monotonic) == 0; } @@ -3177,6 +3179,7 @@ pub const PackageManager = struct { } pub fn sleep(this: *PackageManager) void { + this.reportSlowLifecycleScripts(this.options.log_level); Output.flush(); this.event_loop.tick(this, hasNoMorePendingLifecycleScripts); } @@ -3392,18 +3395,18 @@ pub const PackageManager = struct { pub fn determinePreinstallState( manager: *PackageManager, - this: Package, + pkg: Package, lockfile: *Lockfile, out_name_and_version_hash: *?u64, out_patchfile_hash: *?u64, ) PreinstallState { - switch (manager.getPreinstallState(this.meta.id)) { + switch (manager.getPreinstallState(pkg.meta.id)) { .unknown => { // Do not automatically start downloading packages which are disabled // i.e. don't download all of esbuild's versions or SWCs - if (this.isDisabled()) { - manager.setPreinstallState(this.meta.id, lockfile, .done); + if (pkg.isDisabled()) { + manager.setPreinstallState(pkg.meta.id, lockfile, .done); return .done; } @@ -3414,37 +3417,37 @@ pub const PackageManager = struct { sfb.get(), "{s}@{}", .{ - this.name.slice(manager.lockfile.buffers.string_bytes.items), - this.resolution.fmt(manager.lockfile.buffers.string_bytes.items, .posix), + pkg.name.slice(manager.lockfile.buffers.string_bytes.items), + pkg.resolution.fmt(manager.lockfile.buffers.string_bytes.items, .posix), }, ) catch unreachable; const name_and_version_hash = String.Builder.stringHash(name_and_version); const patched_dep = manager.lockfile.patched_dependencies.get(name_and_version_hash) orelse break :brk null; defer out_name_and_version_hash.* = name_and_version_hash; if (patched_dep.patchfile_hash_is_null) { - manager.setPreinstallState(this.meta.id, manager.lockfile, .calc_patch_hash); + manager.setPreinstallState(pkg.meta.id, manager.lockfile, .calc_patch_hash); return .calc_patch_hash; } out_patchfile_hash.* = patched_dep.patchfileHash().?; break :brk patched_dep.patchfileHash().?; }; - const folder_path = switch (this.resolution.tag) { - .git => manager.cachedGitFolderNamePrintAuto(&this.resolution.value.git, patch_hash), - .github => manager.cachedGitHubFolderNamePrintAuto(&this.resolution.value.github, patch_hash), - .npm => manager.cachedNPMPackageFolderName(lockfile.str(&this.name), this.resolution.value.npm.version, patch_hash), - .local_tarball => manager.cachedTarballFolderName(this.resolution.value.local_tarball, patch_hash), - .remote_tarball => manager.cachedTarballFolderName(this.resolution.value.remote_tarball, patch_hash), + const folder_path = switch (pkg.resolution.tag) { + .git => manager.cachedGitFolderNamePrintAuto(&pkg.resolution.value.git, patch_hash), + .github => manager.cachedGitHubFolderNamePrintAuto(&pkg.resolution.value.github, patch_hash), + .npm => manager.cachedNPMPackageFolderName(lockfile.str(&pkg.name), pkg.resolution.value.npm.version, patch_hash), + .local_tarball => manager.cachedTarballFolderName(pkg.resolution.value.local_tarball, patch_hash), + .remote_tarball => manager.cachedTarballFolderName(pkg.resolution.value.remote_tarball, patch_hash), else => "", }; if (folder_path.len == 0) { - manager.setPreinstallState(this.meta.id, lockfile, .extract); + manager.setPreinstallState(pkg.meta.id, lockfile, .extract); return .extract; } if (manager.isFolderInCache(folder_path)) { - manager.setPreinstallState(this.meta.id, lockfile, .done); + manager.setPreinstallState(pkg.meta.id, lockfile, .done); return .done; } @@ -3461,16 +3464,16 @@ pub const PackageManager = struct { const non_patched_path = manager.lockfile.allocator.dupeZ(u8, non_patched_path_) catch bun.outOfMemory(); defer manager.lockfile.allocator.free(non_patched_path); if (manager.isFolderInCache(non_patched_path)) { - manager.setPreinstallState(this.meta.id, manager.lockfile, .apply_patch); + manager.setPreinstallState(pkg.meta.id, manager.lockfile, .apply_patch); // yay step 1 is already done for us return .apply_patch; } // we need to extract non-patched pkg into the cache - manager.setPreinstallState(this.meta.id, lockfile, .extract); + manager.setPreinstallState(pkg.meta.id, lockfile, .extract); return .extract; } - manager.setPreinstallState(this.meta.id, lockfile, .extract); + manager.setPreinstallState(pkg.meta.id, lockfile, .extract); return .extract; }, else => |val| return val, @@ -4293,8 +4296,6 @@ pub const PackageManager = struct { }; } else if (behavior.isPeer() and !install_peer) { return null; - } else if (behavior.isOptional() and !this.options.remote_package_features.optional_dependencies) { - return null; } // appendPackage sets the PackageID on the package @@ -4327,24 +4328,26 @@ pub const PackageManager = struct { // We don't need to download the tarball, but we should enqueue dependencies .done => .{ .package = package, .is_first_time = true }, // Do we need to download the tarball? - .extract => .{ - .package = package, - .is_first_time = true, - .task = .{ - .network_task = try this.generateNetworkTaskForTarball( - Task.Id.forNPMPackage( - this.lockfile.str(&name), - package.resolution.value.npm.version, - ), - manifest.str(&find_result.package.tarball_url), - dependency.behavior.isRequired(), - dependency_id, - package, - name_and_version_hash, - // its npm. - .allow_authorization, - ) orelse unreachable, - }, + .extract => extract: { + const task_id = Task.Id.forNPMPackage(this.lockfile.str(&name), package.resolution.value.npm.version); + bun.debugAssert(!this.network_dedupe_map.contains(task_id)); + + break :extract .{ + .package = package, + .is_first_time = true, + .task = .{ + .network_task = try this.generateNetworkTaskForTarball( + task_id, + manifest.str(&find_result.package.tarball_url), + dependency.behavior.isRequired(), + dependency_id, + package, + name_and_version_hash, + // its npm. + .allow_authorization, + ) orelse unreachable, + }, + }; }, .calc_patch_hash => .{ .package = package, @@ -4402,7 +4405,7 @@ pub const PackageManager = struct { package: Lockfile.Package, patch_name_and_version_hash: ?u64, authorization: NetworkTask.Authorization, - ) !?*NetworkTask { + ) NetworkTask.ForTarballError!?*NetworkTask { if (this.hasCreatedNetworkTask(task_id, is_required)) { return null; } @@ -4428,21 +4431,21 @@ pub const PackageManager = struct { this.allocator, &.{ .package_manager = this, - .name = try strings.StringOrTinyString.initAppendIfNeeded( + .name = strings.StringOrTinyString.initAppendIfNeeded( this.lockfile.str(&package.name), *FileSystem.FilenameStore, FileSystem.FilenameStore.instance, - ), + ) catch bun.outOfMemory(), .resolution = package.resolution, .cache_dir = this.getCacheDirectory(), .temp_dir = this.getTemporaryDirectory(), .dependency_id = dependency_id, .integrity = package.meta.integrity, - .url = try strings.StringOrTinyString.initAppendIfNeeded( + .url = strings.StringOrTinyString.initAppendIfNeeded( url, *FileSystem.FilenameStore, FileSystem.FilenameStore.instance, - ), + ) catch bun.outOfMemory(), }, scope, authorization, @@ -4888,7 +4891,9 @@ pub const PackageManager = struct { task_id: u64, name: string, repository: *const Repository, + dep_id: DependencyID, dependency: *const Dependency, + res: *const Resolution, /// if patched then we need to do apply step after network task is done patch_name_and_version_hash: ?u64, ) *ThreadPool.Task { @@ -4910,6 +4915,8 @@ pub const PackageManager = struct { FileSystem.FilenameStore.instance, ) catch unreachable, .env = Repository.shared_env.get(this.allocator, this.env), + .dep_id = dep_id, + .res = res.*, }, }, .id = task_id, @@ -5388,15 +5395,13 @@ pub const PackageManager = struct { this.allocator, this.scopeForPackageName(name_str), if (loaded_manifest) |*manifest| manifest else null, - dependency.behavior.isOptional() or !this.options.do.install_peer_dependencies, + dependency.behavior.isOptional(), ); this.enqueueNetworkTask(network_task); } } else { - if (this.options.do.install_peer_dependencies) { - try this.peer_dependencies.writeItem(id); - return; - } + try this.peer_dependencies.writeItem(id); + return; } var manifest_entry_parse = try this.task_queue.getOrPutContext(this.allocator, task_id, .{}); @@ -5468,9 +5473,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies) { - try this.peer_dependencies.writeItem(id); - } + try this.peer_dependencies.writeItem(id); return; } } @@ -5493,16 +5496,14 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies) { - try this.peer_dependencies.writeItem(id); - } + try this.peer_dependencies.writeItem(id); return; } } if (this.hasCreatedNetworkTask(clone_id, dependency.behavior.isRequired())) return; - this.task_batch.push(ThreadPool.Batch.from(this.enqueueGitClone(clone_id, alias, dep, dependency, null))); + this.task_batch.push(ThreadPool.Batch.from(this.enqueueGitClone(clone_id, alias, dep, id, dependency, &res, null))); } }, .github => { @@ -5545,9 +5546,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies) { - try this.peer_dependencies.writeItem(id); - } + try this.peer_dependencies.writeItem(id); return; } } @@ -5734,9 +5733,7 @@ pub const PackageManager = struct { if (dependency.behavior.isPeer()) { if (!install_peer) { - if (this.options.do.install_peer_dependencies) { - try this.peer_dependencies.writeItem(id); - } + try this.peer_dependencies.writeItem(id); return; } } @@ -6018,6 +6015,10 @@ pub const PackageManager = struct { resolution.value.github.resolved = builder.append(String, this.resolved); return resolution; } + + pub fn checkBundledDependencies() bool { + return true; + } }; const TarballResolver = struct { @@ -6041,6 +6042,10 @@ pub const PackageManager = struct { } return resolution; } + + pub fn checkBundledDependencies() bool { + return true; + } }; /// Returns true if we need to drain dependencies @@ -6074,7 +6079,7 @@ pub const PackageManager = struct { manager.allocator, manager.log, package_json_source, - *GitResolver, + GitResolver, &resolver, Features.npm, ) catch |err| { @@ -6147,6 +6152,11 @@ pub const PackageManager = struct { ); var package = Lockfile.Package{}; + var resolver: TarballResolver = .{ + .url = data.url, + .resolution = resolution, + }; + package.parse( manager.lockfile, manager, @@ -6154,10 +6164,7 @@ pub const PackageManager = struct { manager.log, package_json_source, TarballResolver, - TarballResolver{ - .url = data.url, - .resolution = resolution, - }, + &resolver, Features.npm, ) catch |err| { if (comptime log_level != .silent) { @@ -6804,13 +6811,20 @@ pub const PackageManager = struct { manager.extracted_count += 1; bun.Analytics.Features.extracted_packages += 1; - // GitHub and tarball URL dependencies are not fully resolved until after the tarball is downloaded & extracted. - if (manager.processExtractedTarballPackage(&package_id, dependency_id, resolution, &task.data.extract, comptime log_level)) |pkg| brk: { + if (comptime @TypeOf(callbacks.onExtract) != void and ExtractCompletionContext == *PackageInstaller) { + extract_ctx.fixCachedLockfilePackageSlices(); + callbacks.onExtract( + extract_ctx, + dependency_id, + &task.data.extract, + log_level, + ); + } else if (manager.processExtractedTarballPackage(&package_id, dependency_id, resolution, &task.data.extract, log_level)) |pkg| handle_pkg: { // In the middle of an install, you could end up needing to downlaod the github tarball for a dependency // We need to make sure we resolve the dependencies first before calling the onExtract callback // TODO: move this into a separate function var any_root = false; - var dependency_list_entry = manager.task_queue.getEntry(task.id) orelse break :brk; + var dependency_list_entry = manager.task_queue.getEntry(task.id) orelse break :handle_pkg; var dependency_list = dependency_list_entry.value_ptr.*; dependency_list_entry.value_ptr.* = .{}; @@ -6862,13 +6876,8 @@ pub const PackageManager = struct { manager.setPreinstallState(package_id, manager.lockfile, .done); - // if (task.tag == .extract and task.request.extract.network.apply_patch_task != null) { - // manager.enqueuePatchTask(task.request.extract.network.apply_patch_task.?); - // } else - if (comptime @TypeOf(callbacks.onExtract) != void) { - if (ExtractCompletionContext == *PackageInstaller) { - extract_ctx.fixCachedLockfilePackageSlices(); - } + if (comptime @TypeOf(callbacks.onExtract) != void and ExtractCompletionContext != *PackageInstaller) { + // handled *PackageInstaller above callbacks.onExtract(extract_ctx, dependency_id, &task.data.extract, comptime log_level); } @@ -6881,10 +6890,11 @@ pub const PackageManager = struct { }, .git_clone => { const clone = &task.request.git_clone; + const repo_fd = task.data.git_clone; const name = clone.name.slice(); const url = clone.url.slice(); - manager.git_repositories.put(manager.allocator, task.id, task.data.git_clone) catch unreachable; + manager.git_repositories.put(manager.allocator, task.id, repo_fd) catch unreachable; if (task.status == .fail) { const err = task.err orelse error.Failed; @@ -6911,11 +6921,49 @@ pub const PackageManager = struct { continue; } - const dependency_list_entry = manager.task_queue.getEntry(task.id).?; - const dependency_list = dependency_list_entry.value_ptr.*; - dependency_list_entry.value_ptr.* = .{}; + if (comptime @TypeOf(callbacks.onExtract) != void and ExtractCompletionContext == *PackageInstaller) { + // Installing! + // this dependency might be something other than a git dependency! only need the name and + // behavior, use the resolution from the task. + const dep_id = clone.dep_id; + const dep = manager.lockfile.buffers.dependencies.items[dep_id]; + const dep_name = dep.name.slice(manager.lockfile.buffers.string_bytes.items); - try manager.processDependencyList(dependency_list, ExtractCompletionContext, extract_ctx, callbacks, install_peer); + const git = clone.res.value.git; + const committish = git.committish.slice(manager.lockfile.buffers.string_bytes.items); + const repo = git.repo.slice(manager.lockfile.buffers.string_bytes.items); + + const resolved = try Repository.findCommit( + manager.allocator, + manager.env, + manager.log, + task.data.git_clone.asDir(), + dep_name, + committish, + task.id, + ); + + const checkout_id = Task.Id.forGitCheckout(repo, resolved); + + if (manager.hasCreatedNetworkTask(checkout_id, dep.behavior.isRequired())) continue; + + manager.task_batch.push(ThreadPool.Batch.from(manager.enqueueGitCheckout( + checkout_id, + repo_fd, + dep_id, + dep_name, + clone.res, + resolved, + null, + ))); + } else { + // Resolving! + const dependency_list_entry = manager.task_queue.getEntry(task.id).?; + const dependency_list = dependency_list_entry.value_ptr.*; + dependency_list_entry.value_ptr.* = .{}; + + try manager.processDependencyList(dependency_list, ExtractCompletionContext, extract_ctx, callbacks, install_peer); + } if (comptime log_level.showProgress()) { if (!has_updated_this_run) { @@ -6947,15 +6995,29 @@ pub const PackageManager = struct { continue; } - if (manager.processExtractedTarballPackage( + if (comptime @TypeOf(callbacks.onExtract) != void and ExtractCompletionContext == *PackageInstaller) { + // We've populated the cache, package already exists in memory. Call the package installer callback + // and don't enqueue dependencies + + // TODO(dylan-conway) most likely don't need to call this now that the package isn't appended, but + // keeping just in case for now + extract_ctx.fixCachedLockfilePackageSlices(); + + callbacks.onExtract( + extract_ctx, + git_checkout.dependency_id, + &task.data.git_checkout, + log_level, + ); + } else if (manager.processExtractedTarballPackage( &package_id, git_checkout.dependency_id, resolution, &task.data.git_checkout, - comptime log_level, - )) |pkg| brk: { + log_level, + )) |pkg| handle_pkg: { var any_root = false; - var dependency_list_entry = manager.task_queue.getEntry(task.id) orelse break :brk; + var dependency_list_entry = manager.task_queue.getEntry(task.id) orelse break :handle_pkg; var dependency_list = dependency_list_entry.value_ptr.*; dependency_list_entry.value_ptr.* = .{}; @@ -6982,18 +7044,15 @@ pub const PackageManager = struct { }, } } - } - if (comptime @TypeOf(callbacks.onExtract) != void) { - if (ExtractCompletionContext == *PackageInstaller) { - extract_ctx.fixCachedLockfilePackageSlices(); + if (comptime @TypeOf(callbacks.onExtract) != void) { + callbacks.onExtract( + extract_ctx, + git_checkout.dependency_id, + &task.data.git_checkout, + comptime log_level, + ); } - callbacks.onExtract( - extract_ctx, - git_checkout.dependency_id, - &task.data.git_checkout, - comptime log_level, - ); } if (comptime log_level.showProgress()) { @@ -7031,6 +7090,7 @@ pub const PackageManager = struct { .optional_dependencies = true, }, local_package_features: Features = .{ + .optional_dependencies = true, .dev_dependencies = true, .workspaces = true, }, @@ -7261,11 +7321,18 @@ pub const PackageManager = struct { if (config.save_dev) |save| { this.local_package_features.dev_dependencies = save; + // remote packages should never install dev dependencies + // (TODO: unless git dependency with postinstalls) + } + + if (config.save_optional) |save| { + this.remote_package_features.optional_dependencies = save; + this.local_package_features.optional_dependencies = save; } if (config.save_peer) |save| { - this.do.install_peer_dependencies = save; this.remote_package_features.peer_dependencies = save; + this.local_package_features.peer_dependencies = save; } if (config.exact) |exact| { @@ -7287,13 +7354,12 @@ pub const PackageManager = struct { } } - if (config.concurrent_scripts) |jobs| { - this.max_concurrent_lifecycle_scripts = jobs; + if (config.save_text_lockfile) |save_text_lockfile| { + this.save_text_lockfile = save_text_lockfile; } - if (config.save_optional) |save| { - this.remote_package_features.optional_dependencies = save; - this.local_package_features.optional_dependencies = save; + if (config.concurrent_scripts) |jobs| { + this.max_concurrent_lifecycle_scripts = jobs; } this.explicit_global_directory = config.global_dir orelse this.explicit_global_directory; @@ -7433,8 +7499,22 @@ pub const PackageManager = struct { this.enable.manifest_cache_control = false; } - if (cli.omit.dev) { - this.local_package_features.dev_dependencies = false; + if (cli.omit) |omit| { + if (omit.dev) { + this.local_package_features.dev_dependencies = false; + // remote packages should never install dev dependencies + // (TODO: unless git dependency with postinstalls) + } + + if (omit.optional) { + this.local_package_features.optional_dependencies = false; + this.remote_package_features.optional_dependencies = false; + } + + if (omit.peer) { + this.local_package_features.peer_dependencies = false; + this.remote_package_features.peer_dependencies = false; + } } if (cli.global or cli.ignore_scripts) { @@ -7445,9 +7525,9 @@ pub const PackageManager = struct { this.do.trust_dependencies_from_args = true; } - this.save_text_lockfile = cli.save_text_lockfile; - - this.local_package_features.optional_dependencies = !cli.omit.optional; + if (cli.save_text_lockfile) |save_text_lockfile| { + this.save_text_lockfile = save_text_lockfile; + } const disable_progress_bar = default_disable_progress_bar or cli.no_progress; @@ -7554,7 +7634,6 @@ pub const PackageManager = struct { print_meta_hash_string: bool = false, verify_integrity: bool = true, summary: bool = true, - install_peer_dependencies: bool = true, trust_dependencies_from_args: bool = false, update_to_latest: bool = false, }; @@ -7633,7 +7712,7 @@ pub const PackageManager = struct { const dependency_groups = &.{ .{ "optionalDependencies", Dependency.Behavior.optional }, .{ "devDependencies", Dependency.Behavior.dev }, - .{ "dependencies", Dependency.Behavior.normal }, + .{ "dependencies", Dependency.Behavior.prod }, .{ "peerDependencies", Dependency.Behavior.peer }, }; @@ -8771,6 +8850,9 @@ pub const PackageManager = struct { // var node = progress.start(name: []const u8, estimated_total_items: usize) manager.* = PackageManager{ .options = options, + .active_lifecycle_scripts = .{ + .context = manager, + }, .network_task_fifo = NetworkQueue.init(), .patch_task_fifo = PatchTaskFifo.init(), .allocator = ctx.allocator, @@ -8937,6 +9019,9 @@ pub const PackageManager = struct { .options = .{ .max_concurrent_lifecycle_scripts = cli.concurrent_scripts orelse cpu_count * 2, }, + .active_lifecycle_scripts = .{ + .context = manager, + }, .network_task_fifo = NetworkQueue.init(), .allocator = allocator, .log = log, @@ -9113,7 +9198,8 @@ pub const PackageManager = struct { }; lockfile.initEmpty(ctx.allocator); - try package.parse(&lockfile, manager, ctx.allocator, manager.log, package_json_source, void, {}, Features.folder); + var resolver: void = {}; + try package.parse(&lockfile, manager, ctx.allocator, manager.log, package_json_source, void, &resolver, Features.folder); name = lockfile.str(&package.name); if (name.len == 0) { if (manager.options.log_level != .silent) { @@ -9295,7 +9381,8 @@ pub const PackageManager = struct { }; lockfile.initEmpty(ctx.allocator); - try package.parse(&lockfile, manager, ctx.allocator, manager.log, package_json_source, void, {}, Features.folder); + var resolver: void = {}; + try package.parse(&lockfile, manager, ctx.allocator, manager.log, package_json_source, void, &resolver, Features.folder); name = lockfile.str(&package.name); if (name.len == 0) { if (manager.options.log_level != .silent) { @@ -9420,6 +9507,7 @@ pub const PackageManager = struct { clap.parseParam("--concurrent-scripts Maximum number of concurrent jobs for lifecycle scripts (default 5)") catch unreachable, clap.parseParam("--network-concurrency Maximum number of concurrent network requests (default 48)") catch unreachable, clap.parseParam("--save-text-lockfile Save a text-based lockfile") catch unreachable, + clap.parseParam("--omit ... Exclude 'dev', 'optional', or 'peer' dependencies from install") catch unreachable, clap.parseParam("-h, --help Print this help menu") catch unreachable, }; @@ -9532,8 +9620,7 @@ pub const PackageManager = struct { development: bool = false, optional: bool = false, - no_optional: bool = false, - omit: Omit = Omit{}, + omit: ?Omit = null, exact: bool = false, @@ -9548,7 +9635,7 @@ pub const PackageManager = struct { ca: []const string = &.{}, ca_file_name: string = "", - save_text_lockfile: bool = false, + save_text_lockfile: ?bool = null, const PatchOpts = union(enum) { nothing: struct {}, @@ -9560,16 +9647,8 @@ pub const PackageManager = struct { const Omit = struct { dev: bool = false, - optional: bool = true, + optional: bool = false, peer: bool = false, - - pub inline fn toFeatures(this: Omit) Features { - return .{ - .dev_dependencies = this.dev, - .optional_dependencies = this.optional, - .peer_dependencies = this.peer, - }; - } }; pub fn printHelp(subcommand: Subcommand) void { @@ -9906,6 +9985,25 @@ pub const PackageManager = struct { cli.save_text_lockfile = true; } + const omit_values = args.options("--omit"); + + if (omit_values.len > 0) { + var omit: Omit = .{}; + for (omit_values) |omit_value| { + if (strings.eqlComptime(omit_value, "dev")) { + omit.dev = true; + } else if (strings.eqlComptime(omit_value, "optional")) { + omit.optional = true; + } else if (strings.eqlComptime(omit_value, "peer")) { + omit.peer = true; + } else { + Output.errGeneric("invalid `omit` value: '{s}'", .{omit_value}); + Global.crash(); + } + } + cli.omit = omit; + } + // commands that support --filter if (comptime subcommand.supportsWorkspaceFiltering()) { cli.filters = args.options("--filter"); @@ -11175,8 +11273,9 @@ pub const PackageManager = struct { Global.crash(); }; + var resolver: void = {}; var package = Lockfile.Package{}; - try package.parseWithJSON(lockfile, manager, manager.allocator, manager.log, package_json_source, json, void, {}, Features.folder); + try package.parseWithJSON(lockfile, manager, manager.allocator, manager.log, package_json_source, json, void, &resolver, Features.folder); const name = lockfile.str(&package.name); const actual_package = switch (lockfile.package_index.get(package.name_hash) orelse { @@ -11588,8 +11687,9 @@ pub const PackageManager = struct { Global.crash(); }; + var resolver: void = {}; var package = Lockfile.Package{}; - try package.parseWithJSON(lockfile, manager, manager.allocator, manager.log, package_json_source, json, void, {}, Features.folder); + try package.parseWithJSON(lockfile, manager, manager.allocator, manager.log, package_json_source, json, void, &resolver, Features.folder); const name = lockfile.str(&package.name); const actual_package = switch (lockfile.package_index.get(package.name_hash) orelse { @@ -12081,6 +12181,40 @@ pub const PackageManager = struct { } } + pub const LazyPackageDestinationDir = union(enum) { + dir: std.fs.Dir, + node_modules_path: struct { + node_modules: *NodeModulesFolder, + root_node_modules_dir: std.fs.Dir, + }, + closed: void, + + pub fn getDir(this: *LazyPackageDestinationDir) !std.fs.Dir { + return switch (this.*) { + .dir => |dir| dir, + .node_modules_path => |lazy| brk: { + const dir = try lazy.node_modules.openDir(lazy.root_node_modules_dir); + this.* = .{ .dir = dir }; + break :brk dir; + }, + .closed => @panic("LazyPackageDestinationDir is closed! This should never happen. Why did this happen?! It's not your fault. Its our fault. We're sorry."), + }; + } + + pub fn close(this: *LazyPackageDestinationDir) void { + switch (this.*) { + .dir => { + if (this.dir.fd != std.fs.cwd().fd) { + this.dir.close(); + } + }, + .node_modules_path, .closed => {}, + } + + this.* = .{ .closed = {} }; + } + }; + pub const NodeModulesFolder = struct { tree_id: Lockfile.Tree.Id = 0, path: std.ArrayList(u8) = std.ArrayList(u8).init(bun.default_allocator), @@ -12089,9 +12223,73 @@ pub const PackageManager = struct { this.path.clearAndFree(); } + // Since the stack size of these functions are rather large, let's not let them be inlined. + noinline fn directoryExistsAtWithoutOpeningDirectories(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) bool { + var path_buf: bun.PathBuffer = undefined; + const parts: [2][]const u8 = .{ this.path.items, file_path }; + return bun.sys.directoryExistsAt(bun.toFD(root_node_modules_dir), bun.path.joinZBuf(&path_buf, &parts, .auto)).unwrapOr(false); + } + + pub fn directoryExistsAt(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) bool { + if (file_path.len + this.path.items.len * 2 < bun.MAX_PATH_BYTES) { + return this.directoryExistsAtWithoutOpeningDirectories(root_node_modules_dir, file_path); + } + + const dir = this.openDir(root_node_modules_dir) catch return false; + defer { + _ = bun.sys.close(bun.toFD(dir)); + } + + return bun.sys.directoryExistsAt(bun.toFD(dir), file_path).unwrapOr(false); + } + + // Since the stack size of these functions are rather large, let's not let them be inlined. + noinline fn openFileWithoutOpeningDirectories(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) bun.sys.Maybe(bun.sys.File) { + var path_buf: bun.PathBuffer = undefined; + const parts: [2][]const u8 = .{ this.path.items, file_path }; + return bun.sys.File.openat(bun.toFD(root_node_modules_dir), bun.path.joinZBuf(&path_buf, &parts, .auto), bun.O.RDONLY, 0); + } + + pub fn readFile(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8, allocator: std.mem.Allocator) !bun.sys.File.ReadToEndResult { + const file = try this.openFile(root_node_modules_dir, file_path); + defer file.close(); + return file.readToEnd(allocator); + } + + pub fn readSmallFile(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8, allocator: std.mem.Allocator) !bun.sys.File.ReadToEndResult { + const file = try this.openFile(root_node_modules_dir, file_path); + defer file.close(); + return file.readToEndSmall(allocator); + } + + pub fn openFile(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) !bun.sys.File { + if (this.path.items.len + file_path.len * 2 < bun.MAX_PATH_BYTES) { + // If we do not run the risk of ENAMETOOLONG, then let's just avoid opening the extra directories altogether. + switch (this.openFileWithoutOpeningDirectories(root_node_modules_dir, file_path)) { + .err => |e| { + switch (e.getErrno()) { + // Just incase we're wrong, let's try the fallback + .PERM, .ACCES, .INVAL, .NAMETOOLONG => { + // Use fallback + }, + else => return e.toZigErr(), + } + }, + .result => |file| return file, + } + } + + const dir = try this.openDir(root_node_modules_dir); + defer { + _ = bun.sys.close(bun.toFD(dir)); + } + + return try bun.sys.File.openat(bun.toFD(dir), file_path, bun.O.RDONLY, 0).unwrap(); + } + pub fn openDir(this: *const NodeModulesFolder, root: std.fs.Dir) !std.fs.Dir { if (comptime Environment.isPosix) { - return root.openDir(this.path.items, .{ .iterate = true, .access_sub_paths = true }); + return (try bun.sys.openat(bun.toFD(root), &try std.posix.toPosixPath(this.path.items), bun.O.DIRECTORY, 0).unwrap()).asDir(); } return (try bun.sys.openDirAtWindowsA(bun.toFD(root), this.path.items, .{ @@ -12159,8 +12357,9 @@ pub const PackageManager = struct { options: *const PackageManager.Options, metas: []const Lockfile.Package.Meta, names: []const String, + pkg_dependencies: []const Lockfile.DependencySlice, pkg_name_hashes: []const PackageNameHash, - bins: []Bin, + bins: []const Bin, resolutions: []Resolution, node: *Progress.Node, destination_dir_subpath_buf: bun.PathBuffer = undefined, @@ -12194,7 +12393,7 @@ pub const PackageManager = struct { pub fn incrementTreeInstallCount( this: *PackageInstaller, tree_id: Lockfile.Tree.Id, - maybe_destination_dir: ?std.fs.Dir, + maybe_destination_dir: ?*LazyPackageDestinationDir, comptime should_install_packages: bool, comptime log_level: Options.LogLevel, ) void { @@ -12221,22 +12420,19 @@ pub const PackageManager = struct { this.completed_trees.set(tree_id); - if (maybe_destination_dir orelse (this.node_modules.makeAndOpenDir(this.root_node_modules_folder) catch null)) |_destination_dir| { - var destination_dir = _destination_dir; - defer { - if (maybe_destination_dir == null) { - destination_dir.close(); + // Avoid opening this directory if we don't need to. + if (tree.binaries.count() > 0) { + // Don't close this directory in here. It will be closed by the caller. + if (maybe_destination_dir) |maybe| { + if (maybe.getDir() catch null) |destination_dir| { + this.seen_bin_links.clearRetainingCapacity(); + + var link_target_buf: bun.PathBuffer = undefined; + var link_dest_buf: bun.PathBuffer = undefined; + var link_rel_buf: bun.PathBuffer = undefined; + this.linkTreeBins(tree, tree_id, destination_dir, &link_target_buf, &link_dest_buf, &link_rel_buf, log_level); } } - - this.seen_bin_links.clearRetainingCapacity(); - - if (tree.binaries.count() > 0) { - var link_target_buf: bun.PathBuffer = undefined; - var link_dest_buf: bun.PathBuffer = undefined; - var link_rel_buf: bun.PathBuffer = undefined; - this.linkTreeBins(tree, tree_id, destination_dir, &link_target_buf, &link_dest_buf, &link_rel_buf, log_level); - } } if (comptime should_install_packages) { @@ -12449,10 +12645,6 @@ pub const PackageManager = struct { for (this.pending_lifecycle_scripts.items) |entry| { const package_name = entry.list.package_name; while (LifecycleScriptSubprocess.alive_count.load(.monotonic) >= this.manager.options.max_concurrent_lifecycle_scripts) { - if (PackageManager.verbose_install) { - if (PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) Output.prettyErrorln("[PackageManager] waiting for {d} scripts\n", .{LifecycleScriptSubprocess.alive_count.load(.monotonic)}); - } - this.manager.sleep(); } @@ -12484,9 +12676,7 @@ pub const PackageManager = struct { } while (this.manager.pending_lifecycle_script_tasks.load(.monotonic) > 0) { - if (PackageManager.verbose_install) { - if (PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) Output.prettyErrorln("[PackageManager] waiting for {d} scripts\n", .{LifecycleScriptSubprocess.alive_count.load(.monotonic)}); - } + this.manager.reportSlowLifecycleScripts(log_level); if (comptime log_level.showProgress()) { if (this.manager.scripts_node) |scripts_node| { @@ -12540,6 +12730,7 @@ pub const PackageManager = struct { this.pkg_name_hashes = packages.items(.name_hash); this.bins = packages.items(.bin); this.resolutions = packages.items(.resolution); + this.pkg_dependencies = packages.items(.dependencies); // fixes an assertion failure where a transitive dependency is a git dependency newly added to the lockfile after the list of dependencies has been resized // this assertion failure would also only happen after the lockfile has been written to disk and the summary is being printed. @@ -12635,7 +12826,7 @@ pub const PackageManager = struct { alias: string, package_id: PackageID, resolution_tag: Resolution.Tag, - node_modules_folder: std.fs.Dir, + node_modules_folder: *LazyPackageDestinationDir, comptime log_level: Options.LogLevel, ) usize { if (comptime Environment.allow_assert) { @@ -12937,19 +13128,10 @@ pub const PackageManager = struct { }, } - const needs_install = this.force_install or this.skip_verify_installed_version_number or !needs_verify or remove_patch or verify: { - const verified = installer.verify( - resolution, - this.root_node_modules_folder, - &this.bins[package_id], - ); - - if (verified.update_lockfile_pointers) { - this.fixCachedLockfilePackageSlices(); - } - - break :verify !verified.valid; - }; + const needs_install = this.force_install or this.skip_verify_installed_version_number or !needs_verify or remove_patch or !installer.verify( + resolution, + this.root_node_modules_folder, + ); this.summary.skipped += @intFromBool(!needs_install); if (needs_install) { @@ -12984,7 +13166,14 @@ pub const PackageManager = struct { url, context, patch_name_and_version_hash, - ); + ) catch |err| switch (err) { + error.OutOfMemory => bun.outOfMemory(), + error.InvalidURL => this.failWithInvalidUrl( + pkg_has_patch, + is_pending_package_install, + log_level, + ), + }; }, .local_tarball => { this.manager.enqueueTarballForReading( @@ -13001,7 +13190,14 @@ pub const PackageManager = struct { resolution.value.remote_tarball.slice(this.lockfile.buffers.string_bytes.items), context, patch_name_and_version_hash, - ); + ) catch |err| switch (err) { + error.OutOfMemory => bun.outOfMemory(), + error.InvalidURL => this.failWithInvalidUrl( + pkg_has_patch, + is_pending_package_install, + log_level, + ), + }; }, .npm => { if (comptime Environment.isDebug) { @@ -13023,7 +13219,14 @@ pub const PackageManager = struct { resolution.value.npm.url.slice(this.lockfile.buffers.string_bytes.items), context, patch_name_and_version_hash, - ); + ) catch |err| switch (err) { + error.OutOfMemory => bun.outOfMemory(), + error.InvalidURL => this.failWithInvalidUrl( + pkg_has_patch, + is_pending_package_install, + log_level, + ), + }; }, else => { if (comptime Environment.allow_assert) { @@ -13083,6 +13286,8 @@ pub const PackageManager = struct { if (std.fs.cwd().fd != destination_dir.fd) destination_dir.close(); } + var lazy_package_dir: LazyPackageDestinationDir = .{ .dir = destination_dir }; + const install_result = switch (resolution.tag) { .symlink, .workspace => installer.installFromLink(this.skip_delete, destination_dir), else => result: { @@ -13119,17 +13324,6 @@ pub const PackageManager = struct { this.node.completeOne(); } - if (this.bins[package_id].isUnset()) { - this.bins[package_id] = this.getPackageBin( - &installer, - pkg_name.slice(this.lockfile.buffers.string_bytes.items), - pkg_name_hash, - resolution, - ) catch |err| switch (err) { - error.OutOfMemory => bun.outOfMemory(), - }; - } - if (this.bins[package_id].tag != .none) { this.trees[this.current_tree_id].binaries.add(dependency_id) catch bun.outOfMemory(); } @@ -13146,7 +13340,7 @@ pub const PackageManager = struct { if (this.enqueueLifecycleScripts( alias.slice(this.lockfile.buffers.string_bytes.items), log_level, - destination_dir, + &lazy_package_dir, package_id, dep.behavior.optional, resolution, @@ -13174,7 +13368,7 @@ pub const PackageManager = struct { alias.slice(this.lockfile.buffers.string_bytes.items), package_id, resolution.tag, - destination_dir, + &lazy_package_dir, log_level, ); if (count > 0) { @@ -13192,7 +13386,7 @@ pub const PackageManager = struct { }, } - if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, destination_dir, !is_pending_package_install, log_level); + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, &lazy_package_dir, !is_pending_package_install, log_level); }, .fail => |cause| { if (comptime Environment.allow_assert) { @@ -13201,7 +13395,7 @@ pub const PackageManager = struct { // even if the package failed to install, we still need to increment the install // counter for this tree - if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, destination_dir, !is_pending_package_install, log_level); + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, &lazy_package_dir, !is_pending_package_install, log_level); if (cause.err == error.DanglingSymlink) { Output.prettyErrorln( @@ -13220,7 +13414,15 @@ pub const PackageManager = struct { }; if (!Singleton.node_modules_is_ok) { if (!Environment.isWindows) { - const stat = bun.sys.fstat(bun.toFD(destination_dir)).unwrap() catch |err| { + const stat = bun.sys.fstat(bun.toFD(lazy_package_dir.getDir() catch |err| { + Output.err("EACCES", "Permission denied while installing {s}", .{ + this.names[package_id].slice(this.lockfile.buffers.string_bytes.items), + }); + if (Environment.isDebug) { + Output.err(err, "Failed to stat node_modules", .{}); + } + Global.exit(1); + })).unwrap() catch |err| { Output.err("EACCES", "Permission denied while installing {s}", .{ this.names[package_id].slice(this.lockfile.buffers.string_bytes.items), }); @@ -13260,37 +13462,22 @@ pub const PackageManager = struct { }, } } else { - if (this.bins[package_id].isUnset()) { - this.bins[package_id] = this.getPackageBin( - &installer, - pkg_name.slice(this.lockfile.buffers.string_bytes.items), - pkg_name_hash, - resolution, - ) catch |err| switch (err) { - error.OutOfMemory => bun.outOfMemory(), - }; - } if (this.bins[package_id].tag != .none) { this.trees[this.current_tree_id].binaries.add(dependency_id) catch bun.outOfMemory(); } - var destination_dir = this.node_modules.makeAndOpenDir(this.root_node_modules_folder) catch |err| { - if (log_level != .silent) { - Output.err(err, "Failed to open node_modules folder for {s} in {s}", .{ - pkg_name.slice(this.lockfile.buffers.string_bytes.items), - bun.fmt.fmtPath(u8, this.node_modules.path.items, .{}), - }); - } - this.summary.fail += 1; - if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); - return; + var destination_dir: LazyPackageDestinationDir = .{ + .node_modules_path = .{ + .node_modules = &this.node_modules, + .root_node_modules_dir = this.root_node_modules_folder, + }, }; defer { - if (std.fs.cwd().fd != destination_dir.fd) destination_dir.close(); + destination_dir.close(); } - defer if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, destination_dir, !is_pending_package_install, log_level); + defer if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, &destination_dir, !is_pending_package_install, log_level); const dep = this.lockfile.buffers.dependencies.items[dependency_id]; const truncated_dep_name_hash: TruncatedPackageNameHash = @truncate(dep.name_hash); @@ -13309,7 +13496,7 @@ pub const PackageManager = struct { if (this.enqueueLifecycleScripts( alias.slice(this.lockfile.buffers.string_bytes.items), log_level, - destination_dir, + &destination_dir, package_id, dep.behavior.optional, resolution, @@ -13330,67 +13517,14 @@ pub const PackageManager = struct { } } - fn getPackageBin( + fn failWithInvalidUrl( this: *PackageInstaller, - installer: *PackageInstall, - pkg_name: string, - pkg_name_hash: PackageNameHash, - resolution: *const Resolution, - ) OOM!Bin { - defer this.fixCachedLockfilePackageSlices(); - - if (resolution.tag == .npm) { - var expired = false; - if (this.manager.manifests.byNameHashAllowExpired( - this.manager, - this.manager.scopeForPackageName(pkg_name), - pkg_name_hash, - &expired, - .load_from_memory_fallback_to_disk, - )) |manifest| { - if (manifest.findByVersion(resolution.value.npm.version)) |find| { - return find.package.bin.cloneAppend(manifest.string_buf, manifest.extern_strings_bin_entries, this.lockfile); - } - } - } - - // get it from package.json - var body_pool = Npm.Registry.BodyPool.get(this.lockfile.allocator); - var mutable = body_pool.data; - defer { - body_pool.data = mutable; - Npm.Registry.BodyPool.release(body_pool); - } - - const source = installer.getInstalledPackageJsonSource(this.root_node_modules_folder, &mutable, resolution.tag) orelse return .{}; - - initializeStore(); - - var log = logger.Log.init(this.lockfile.allocator); - defer log.deinit(); - - var bin_finder = JSON.PackageJSONVersionChecker.init( - this.lockfile.allocator, - &source, - &log, - .only_bin, - ) catch return .{}; - _ = bin_finder.parseExpr(false, false) catch return .{}; - - if (bin_finder.has_found_bin) { - var string_buf = this.lockfile.stringBuf(); - defer { - string_buf.apply(this.lockfile); - this.fixCachedLockfilePackageSlices(); - } - - return switch (bin_finder.found_bin) { - .bin => |bin| try Bin.parseAppend(this.lockfile.allocator, bin, &string_buf, &this.lockfile.buffers.extern_strings), - .dir => |dir| try Bin.parseAppendFromDirectories(this.lockfile.allocator, dir, &string_buf), - }; - } - - return .{}; + pkg_has_patch: bool, + comptime is_pending_package_install: bool, + comptime log_level: Options.LogLevel, + ) void { + this.summary.fail += 1; + if (!pkg_has_patch) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); } // returns true if scripts are enqueued @@ -13398,7 +13532,7 @@ pub const PackageManager = struct { this: *PackageInstaller, folder_name: string, comptime log_level: Options.LogLevel, - node_modules_folder: std.fs.Dir, + node_modules_folder: *LazyPackageDestinationDir, package_id: PackageID, optional: bool, resolution: *const Resolution, @@ -13463,47 +13597,18 @@ pub const PackageManager = struct { pub fn installPackage( this: *PackageInstaller, - dependency_id: DependencyID, + dep_id: DependencyID, comptime log_level: Options.LogLevel, ) void { - this.installPackageImpl(dependency_id, log_level, true); - } - - pub fn installPackageImpl( - this: *PackageInstaller, - dependency_id: DependencyID, - comptime log_level: Options.LogLevel, - comptime increment_tree_count: bool, - ) void { - const package_id = this.lockfile.buffers.resolutions.items[dependency_id]; - const meta = &this.metas[package_id]; - const is_pending_package_install = false; - - if (meta.isDisabled()) { - if (comptime log_level.showProgress()) { - this.node.completeOne(); - } - if (comptime log_level.isVerbose()) { - const name = this.lockfile.str(&this.names[package_id]); - if (!meta.os.isMatch() and !meta.arch.isMatch()) { - Output.prettyErrorln("Skip installing '{s}' cpu & os mismatch", .{name}); - } else if (!meta.os.isMatch()) { - Output.prettyErrorln("Skip installing '{s}' os mismatch", .{name}); - } else if (!meta.arch.isMatch()) { - Output.prettyErrorln("Skip installing '{s}' cpu mismatch", .{name}); - } - } - - if (comptime increment_tree_count) this.incrementTreeInstallCount(this.current_tree_id, null, !is_pending_package_install, log_level); - return; - } + const package_id = this.lockfile.buffers.resolutions.items[dep_id]; const name = this.names[package_id]; const resolution = &this.resolutions[package_id]; const needs_verify = true; + const is_pending_package_install = false; this.installPackageWithNameAndResolution( - dependency_id, + dep_id, package_id, log_level, name, @@ -13554,10 +13659,20 @@ pub const PackageManager = struct { if (clone_queue.found_existing) return; - this.task_batch.push(ThreadPool.Batch.from(this.enqueueGitClone(clone_id, alias, repository, &this.lockfile.buffers.dependencies.items[dependency_id], null))); + this.task_batch.push(ThreadPool.Batch.from(this.enqueueGitClone( + clone_id, + alias, + repository, + dependency_id, + &this.lockfile.buffers.dependencies.items[dependency_id], + resolution, + null, + ))); } } + const EnqueuePackageForDownloadError = NetworkTask.ForTarballError; + pub fn enqueuePackageForDownload( this: *PackageManager, name: []const u8, @@ -13567,23 +13682,23 @@ pub const PackageManager = struct { url: []const u8, task_context: TaskCallbackContext, patch_name_and_version_hash: ?u64, - ) void { + ) EnqueuePackageForDownloadError!void { const task_id = Task.Id.forNPMPackage(name, version); - var task_queue = this.task_queue.getOrPut(this.allocator, task_id) catch unreachable; + var task_queue = try this.task_queue.getOrPut(this.allocator, task_id); if (!task_queue.found_existing) { task_queue.value_ptr.* = .{}; } - task_queue.value_ptr.append( + try task_queue.value_ptr.append( this.allocator, task_context, - ) catch unreachable; + ); if (task_queue.found_existing) return; const is_required = this.lockfile.buffers.dependencies.items[dependency_id].behavior.isRequired(); - if (this.generateNetworkTaskForTarball( + if (try this.generateNetworkTaskForTarball( task_id, url, is_required, @@ -13591,7 +13706,7 @@ pub const PackageManager = struct { this.lockfile.packages.get(package_id), patch_name_and_version_hash, .allow_authorization, - ) catch unreachable) |task| { + )) |task| { task.schedule(&this.network_tarball_batch); if (this.network_tarball_batch.len > 0) { _ = this.scheduleTasks(); @@ -13599,6 +13714,8 @@ pub const PackageManager = struct { } } + const EnqueueTarballForDownloadError = NetworkTask.ForTarballError; + pub fn enqueueTarballForDownload( this: *PackageManager, dependency_id: DependencyID, @@ -13606,21 +13723,21 @@ pub const PackageManager = struct { url: string, task_context: TaskCallbackContext, patch_name_and_version_hash: ?u64, - ) void { + ) EnqueueTarballForDownloadError!void { const task_id = Task.Id.forTarball(url); - var task_queue = this.task_queue.getOrPut(this.allocator, task_id) catch unreachable; + var task_queue = try this.task_queue.getOrPut(this.allocator, task_id); if (!task_queue.found_existing) { task_queue.value_ptr.* = .{}; } - task_queue.value_ptr.append( + try task_queue.value_ptr.append( this.allocator, task_context, - ) catch unreachable; + ); if (task_queue.found_existing) return; - if (this.generateNetworkTaskForTarball( + if (try this.generateNetworkTaskForTarball( task_id, url, this.lockfile.buffers.dependencies.items[dependency_id].behavior.isRequired(), @@ -13628,7 +13745,7 @@ pub const PackageManager = struct { this.lockfile.packages.get(package_id), patch_name_and_version_hash, .no_authorization, - ) catch unreachable) |task| { + )) |task| { task.schedule(&this.network_tarball_batch); if (this.network_tarball_batch.len > 0) { _ = this.scheduleTasks(); @@ -13692,15 +13809,14 @@ pub const PackageManager = struct { ctx: Command.Context, comptime log_level: PackageManager.Options.LogLevel, ) !PackageInstall.Summary { - const original_lockfile = this.lockfile; - defer this.lockfile = original_lockfile; - if (!this.options.local_package_features.dev_dependencies) { - this.lockfile = try this.lockfile.maybeCloneFilteringRootPackages( - this, - this.options.local_package_features, - this.options.enable.exact_versions, - log_level, - ); + const original_trees = this.lockfile.buffers.trees; + const original_tree_dep_ids = this.lockfile.buffers.hoisted_dependencies; + + try this.lockfile.hoist(this.log, .filter, this); + + defer { + this.lockfile.buffers.trees = original_trees; + this.lockfile.buffers.hoisted_dependencies = original_tree_dep_ids; } var root_node: *Progress.Node = undefined; @@ -13715,7 +13831,7 @@ pub const PackageManager = struct { root_node = progress.start("", 0); download_node = root_node.start(ProgressStrings.download(), 0); - install_node = root_node.start(ProgressStrings.install(), this.lockfile.packages.len); + install_node = root_node.start(ProgressStrings.install(), this.lockfile.buffers.hoisted_dependencies.items.len); scripts_node = root_node.start(ProgressStrings.script(), 0); this.downloads_node = &download_node; this.scripts_node = &scripts_node; @@ -13766,9 +13882,7 @@ pub const PackageManager = struct { skip_delete = false; } - var summary = PackageInstall.Summary{ - .lockfile_used_for_install = this.lockfile, - }; + var summary = PackageInstall.Summary{}; { var iterator = Lockfile.Tree.Iterator(.node_modules).init(this.lockfile); @@ -13866,6 +13980,7 @@ pub const PackageManager = struct { .names = parts.items(.name), .pkg_name_hashes = parts.items(.name_hash), .resolutions = parts.items(.resolution), + .pkg_dependencies = parts.items(.dependencies), .lockfile = this.lockfile, .node = &install_node, .node_modules = .{ @@ -13953,8 +14068,8 @@ pub const PackageManager = struct { ); if (!installer.options.do.install_packages) return error.InstallFailed; } - this.tickLifecycleScripts(); + this.reportSlowLifecycleScripts(log_level); } for (remaining) |dependency_id| { @@ -13977,6 +14092,7 @@ pub const PackageManager = struct { if (!installer.options.do.install_packages) return error.InstallFailed; this.tickLifecycleScripts(); + this.reportSlowLifecycleScripts(log_level); } while (this.pendingTaskCount() > 0 and installer.options.do.install_packages) { @@ -14006,6 +14122,8 @@ pub const PackageManager = struct { return true; } + closure.manager.reportSlowLifecycleScripts(log_level); + if (PackageManager.verbose_install and closure.manager.pendingTaskCount() > 0) { const pending_task_count = closure.manager.pendingTaskCount(); if (pending_task_count > 0 and PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) { @@ -14032,6 +14150,7 @@ pub const PackageManager = struct { } } else { this.tickLifecycleScripts(); + this.reportSlowLifecycleScripts(log_level); } for (installer.trees) |tree| { @@ -14057,9 +14176,7 @@ pub const PackageManager = struct { installer.completeRemainingScripts(log_level); while (this.pending_lifecycle_script_tasks.load(.monotonic) > 0) { - if (PackageManager.verbose_install) { - if (PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) Output.prettyErrorln("[PackageManager] waiting for {d} scripts\n", .{this.pending_lifecycle_script_tasks.load(.monotonic)}); - } + this.reportSlowLifecycleScripts(log_level); this.sleep(); } @@ -14285,6 +14402,7 @@ pub const PackageManager = struct { lockfile.initEmpty(manager.allocator); var maybe_root = Lockfile.Package{}; + var resolver: void = {}; try maybe_root.parse( &lockfile, manager, @@ -14292,7 +14410,7 @@ pub const PackageManager = struct { manager.log, root_package_json_source, void, - {}, + &resolver, Features.main, ); const mapping = try manager.lockfile.allocator.alloc(PackageID, maybe_root.dependencies.len); @@ -14518,6 +14636,7 @@ pub const PackageManager = struct { Global.crash(); } + var resolver: void = {}; try root.parse( manager.lockfile, manager, @@ -14525,7 +14644,7 @@ pub const PackageManager = struct { manager.log, root_package_json_source, void, - {}, + &resolver, Features.main, ); @@ -14642,9 +14761,7 @@ pub const PackageManager = struct { try waitForEverythingExceptPeers(manager); } - if (manager.options.do.install_peer_dependencies) { - try waitForPeers(manager); - } + try waitForPeers(manager); if (comptime log_level.showProgress()) { manager.endProgressBar(); @@ -14659,6 +14776,8 @@ pub const PackageManager = struct { manager.log.reset(); // This operation doesn't perform any I/O, so it should be relatively cheap. + const lockfile_before_clean = manager.lockfile; + manager.lockfile = try manager.lockfile.cleanWithLogger( manager, manager.update_requests, @@ -14751,19 +14870,27 @@ pub const PackageManager = struct { const packages_len_before_install = manager.lockfile.packages.len; - if (manager.options.enable.frozen_lockfile and load_result != .not_found) { - if (manager.lockfile.hasMetaHashChanged(PackageManager.verbose_install or manager.options.do.print_meta_hash_string, packages_len_before_install) catch false) { - if (comptime log_level != .silent) { - Output.prettyErrorln("error: lockfile had changes, but lockfile is frozen", .{}); - Output.note("try re-running without --frozen-lockfile and commit the updated lockfile", .{}); + if (manager.options.enable.frozen_lockfile and load_result != .not_found) frozen_lockfile: { + if (load_result.loadedFromTextLockfile()) { + if (manager.lockfile.eql(lockfile_before_clean, packages_len_before_install, manager.allocator) catch bun.outOfMemory()) { + break :frozen_lockfile; + } + } else { + if (!(manager.lockfile.hasMetaHashChanged(PackageManager.verbose_install or manager.options.do.print_meta_hash_string, packages_len_before_install) catch false)) { + break :frozen_lockfile; } - Global.crash(); } + + if (comptime log_level != .silent) { + Output.prettyErrorln("error: lockfile had changes, but lockfile is frozen", .{}); + Output.note("try re-running without --frozen-lockfile and commit the updated lockfile", .{}); + } + Global.crash(); } - var install_summary = PackageInstall.Summary{ - .lockfile_used_for_install = manager.lockfile, - }; + const lockfile_before_install = manager.lockfile; + + var install_summary = PackageInstall.Summary{}; if (manager.options.do.install_packages) { install_summary = try manager.installPackages( ctx, @@ -14779,10 +14906,13 @@ pub const PackageManager = struct { const did_meta_hash_change = // If the lockfile was frozen, we already checked it !manager.options.enable.frozen_lockfile and + if (load_result.loadedFromTextLockfile()) + !try manager.lockfile.eql(lockfile_before_clean, packages_len_before_install, manager.allocator) + else try manager.lockfile.hasMetaHashChanged( - PackageManager.verbose_install or manager.options.do.print_meta_hash_string, - @min(packages_len_before_install, manager.lockfile.packages.len), - ); + PackageManager.verbose_install or manager.options.do.print_meta_hash_string, + @min(packages_len_before_install, manager.lockfile.packages.len), + ); const should_save_lockfile = did_meta_hash_change or had_any_diffs or @@ -14848,8 +14978,14 @@ pub const PackageManager = struct { manager.lockfile.saveToDisk(save_format, manager.options.log_level.isVerbose()); if (comptime Environment.allow_assert) { - if (manager.lockfile.hasMetaHashChanged(false, packages_len_before_install) catch false) { - Output.panic("Lockfile metahash non-deterministic after saving", .{}); + if (load_result.loadedFromTextLockfile()) { + if (!try manager.lockfile.eql(lockfile_before_install, packages_len_before_install, manager.allocator)) { + Output.panic("Lockfile non-deterministic after saving", .{}); + } + } else { + if (manager.lockfile.hasMetaHashChanged(false, packages_len_before_install) catch false) { + Output.panic("Lockfile metahash non-deterministic after saving", .{}); + } } } @@ -14905,9 +15041,7 @@ pub const PackageManager = struct { try manager.spawnPackageLifecycleScripts(ctx, scripts, optional, log_level, output_in_foreground); while (manager.pending_lifecycle_script_tasks.load(.monotonic) > 0) { - if (PackageManager.verbose_install) { - if (PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) Output.prettyErrorln("[PackageManager] waiting for {d} scripts\n", .{manager.pending_lifecycle_script_tasks.load(.monotonic)}); - } + manager.reportSlowLifecycleScripts(log_level); manager.sleep(); } @@ -14918,7 +15052,7 @@ pub const PackageManager = struct { if (comptime log_level != .silent) { if (manager.options.do.summary) { var printer = Lockfile.Printer{ - .lockfile = install_summary.lockfile_used_for_install, + .lockfile = manager.lockfile, .options = manager.options, .updates = manager.update_requests, .successfully_installed = install_summary.successfully_installed, @@ -15038,6 +15172,7 @@ pub const PackageManager = struct { const lockfile = this.lockfile; const resolutions_lists: []const Lockfile.DependencyIDSlice = lockfile.packages.items(.resolutions); const dependency_lists: []const Lockfile.DependencySlice = lockfile.packages.items(.dependencies); + const pkg_resolutions = lockfile.packages.items(.resolution); const dependencies_buffer = lockfile.buffers.dependencies.items; const resolutions_buffer = lockfile.buffers.resolutions.items; const end: PackageID = @truncate(lockfile.packages.len); @@ -15045,7 +15180,6 @@ pub const PackageManager = struct { var any_failed = false; const string_buf = lockfile.buffers.string_bytes.items; - const root_list = resolutions_lists[0]; for (resolutions_lists, dependency_lists, 0..) |resolution_list, dependency_list, parent_id| { for (resolution_list.get(resolutions_buffer), dependency_list.get(dependencies_buffer)) |package_id, failed_dep| { if (package_id < end) continue; @@ -15054,13 +15188,13 @@ pub const PackageManager = struct { // Need to keep this for now because old lockfiles might have a peer dependency without the optional flag set. if (failed_dep.behavior.isPeer()) continue; + const features = switch (pkg_resolutions[parent_id].tag) { + .root, .workspace, .folder => this.options.local_package_features, + else => this.options.remote_package_features, + }; // even if optional dependencies are enabled, it's still allowed to fail - if (failed_dep.behavior.optional or !failed_dep.behavior.isEnabled( - if (root_list.contains(@truncate(parent_id))) - this.options.local_package_features - else - this.options.remote_package_features, - )) continue; + if (failed_dep.behavior.optional or !failed_dep.behavior.isEnabled(features)) continue; + if (log_level != .silent) { if (failed_dep.name.isEmpty() or strings.eqlLong(failed_dep.name.slice(string_buf), failed_dep.version.literal.slice(string_buf), true)) { Output.errGeneric("{} failed to resolve", .{ @@ -15103,11 +15237,11 @@ pub const PackageManager = struct { try this.ensureTempNodeGypScript(); const cwd = list.cwd; - const this_bundler = try this.configureEnvForScripts(ctx, log_level); - const original_path = this_bundler.env.get("PATH") orelse ""; + const this_transpiler = try this.configureEnvForScripts(ctx, log_level); + const original_path = this_transpiler.env.get("PATH") orelse ""; var PATH = try std.ArrayList(u8).initCapacity(bun.default_allocator, original_path.len + 1 + "node_modules/.bin".len + cwd.len + 1); - var current_dir: ?*DirInfo = this_bundler.resolver.readDirInfo(cwd) catch null; + var current_dir: ?*DirInfo = this_transpiler.resolver.readDirInfo(cwd) catch null; bun.assert(current_dir != null); while (current_dir) |dir| { if (PATH.items.len > 0 and PATH.items[PATH.items.len - 1] != std.fs.path.delimiter) { @@ -15129,10 +15263,10 @@ pub const PackageManager = struct { try PATH.appendSlice(original_path); } - this_bundler.env.map.put("PATH", PATH.items) catch unreachable; + this_transpiler.env.map.put("PATH", PATH.items) catch unreachable; - const envp = try this_bundler.env.map.createNullDelimitedEnvMap(this.allocator); - try this_bundler.env.map.put("PATH", original_path); + const envp = try this_transpiler.env.map.createNullDelimitedEnvMap(this.allocator); + try this_transpiler.env.map.put("PATH", original_path); PATH.deinit(); try LifecycleScriptSubprocess.spawnPackageScripts(this, list, envp, optional, log_level, foreground); @@ -15174,7 +15308,7 @@ pub const bun_install_js_bindings = struct { const cwd = try args[0].toSliceOrNull(globalObject); defer cwd.deinit(); - var dir = bun.openDirAbsolute(cwd.slice()) catch |err| { + var dir = bun.openDirAbsoluteNotForDeletingOrRenaming(cwd.slice()) catch |err| { return globalObject.throw("failed to open: {s}, '{s}'", .{ @errorName(err), cwd.slice() }); }; defer dir.close(); @@ -15183,12 +15317,12 @@ pub const bun_install_js_bindings = struct { var lockfile: Lockfile = undefined; lockfile.initEmpty(allocator); - if (globalObject.bunVM().bundler.resolver.env_loader == null) { - globalObject.bunVM().bundler.resolver.env_loader = globalObject.bunVM().bundler.env; + if (globalObject.bunVM().transpiler.resolver.env_loader == null) { + globalObject.bunVM().transpiler.resolver.env_loader = globalObject.bunVM().transpiler.env; } // as long as we aren't migration from `package-lock.json`, leaving this undefined is okay - const manager = globalObject.bunVM().bundler.resolver.getPackageManager(); + const manager = globalObject.bunVM().transpiler.resolver.getPackageManager(); const load_result: Lockfile.LoadResult = lockfile.loadFromDir(bun.toFD(dir), manager, allocator, &log, true); diff --git a/src/install/lifecycle_script_runner.zig b/src/install/lifecycle_script_runner.zig index c2b2a089e5..6b054644a0 100644 --- a/src/install/lifecycle_script_runner.zig +++ b/src/install/lifecycle_script_runner.zig @@ -35,6 +35,15 @@ pub const LifecycleScriptSubprocess = struct { foreground: bool = false, optional: bool = false, + started_at: u64 = 0, + + heap: bun.io.heap.IntrusiveField(LifecycleScriptSubprocess) = .{}, + + pub const List = bun.io.heap.Intrusive(LifecycleScriptSubprocess, *PackageManager, sortByStartedAt); + + fn sortByStartedAt(_: *PackageManager, a: *LifecycleScriptSubprocess, b: *LifecycleScriptSubprocess) bool { + return a.started_at < b.started_at; + } pub usingnamespace bun.New(@This()); @@ -92,6 +101,12 @@ pub const LifecycleScriptSubprocess = struct { // This is only used on the main thread. var cwd_z_buf: bun.PathBuffer = undefined; + fn ensureNotInHeap(this: *LifecycleScriptSubprocess) void { + if (this.heap.child != null or this.heap.next != null or this.heap.prev != null or this.manager.active_lifecycle_scripts.root == this) { + this.manager.active_lifecycle_scripts.remove(this); + } + } + pub fn spawnNextScript(this: *LifecycleScriptSubprocess, next_script_index: u8) !void { bun.Analytics.Features.lifecycle_scripts += 1; @@ -105,6 +120,8 @@ pub const LifecycleScriptSubprocess = struct { this.has_incremented_alive_count = false; _ = alive_count.fetchSub(1, .monotonic); } + + this.ensureNotInHeap(); } const manager = this.manager; @@ -114,6 +131,8 @@ pub const LifecycleScriptSubprocess = struct { this.stdout.setParent(this); this.stderr.setParent(this); + this.ensureNotInHeap(); + this.current_script_index = next_script_index; this.has_called_process_exit = false; @@ -197,6 +216,8 @@ pub const LifecycleScriptSubprocess = struct { }; this.remaining_fds = 0; + this.started_at = bun.timespec.now().ns(); + this.manager.active_lifecycle_scripts.insert(this); var spawned = try (try bun.spawn.spawnProcess(&spawn_options, @ptrCast(&argv), this.envp)).unwrap(); if (comptime Environment.isPosix) { @@ -299,6 +320,8 @@ pub const LifecycleScriptSubprocess = struct { _ = alive_count.fetchSub(1, .monotonic); } + this.ensureNotInHeap(); + switch (status) { .exited => |exit| { const maybe_duration = if (this.timer) |*t| t.read() else null; @@ -355,8 +378,15 @@ pub const LifecycleScriptSubprocess = struct { } } + if (PackageManager.verbose_install) { + Output.prettyErrorln("[Scripts] Finished scripts for {}", .{ + bun.fmt.quote(this.package_name), + }); + } + // the last script finished _ = this.manager.pending_lifecycle_script_tasks.fetchSub(1, .monotonic); + this.deinit(); }, .signaled => |signal| { @@ -426,6 +456,7 @@ pub const LifecycleScriptSubprocess = struct { pub fn deinit(this: *LifecycleScriptSubprocess) void { this.resetPolls(); + this.ensureNotInHeap(); if (!this.manager.options.log_level.isVerbose()) { this.stdout.deinit(); diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index a1f7e748fc..e0a0f54a4a 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -7,6 +7,7 @@ const Output = bun.Output; const Global = bun.Global; const Environment = bun.Environment; const strings = bun.strings; +const Glob = bun.glob; const MutableString = bun.MutableString; const stringZ = bun.stringZ; const default_allocator = bun.default_allocator; @@ -32,7 +33,6 @@ const Path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../bun.js/config.zig").configureTransformOptionsForBun; const Command = @import("../cli.zig").Command; const BunArguments = @import("../cli.zig").Arguments; -const bundler = bun.bundler; const DotEnv = @import("../env_loader.zig"); const which = @import("../which.zig").which; @@ -126,7 +126,7 @@ fn ignoredWorkspacePaths(path: []const u8) bool { return false; } -const GlobWalker = bun.glob.GlobWalker_(ignoredWorkspacePaths, bun.glob.SyscallAccessor, false); +const GlobWalker = Glob.GlobWalker(ignoredWorkspacePaths, Glob.walk.SyscallAccessor, false); // Serialized data /// The version of the lockfile format, intended to prevent data corruption for format changes. @@ -242,6 +242,14 @@ pub const LoadResult = union(enum) { } }; + pub fn loadedFromTextLockfile(this: LoadResult) bool { + return switch (this) { + .not_found => false, + .err => |err| err.format == .text, + .ok => |ok| ok.format == .text, + }; + } + pub const Step = enum { open_file, read_file, parse_file, migrating }; }; @@ -321,6 +329,7 @@ pub fn loadFromDir( if (lockfile_format == .text) { const source = logger.Source.initPathString("bun.lock", buf); + initializeStore(); const json = JSON.parsePackageJSONUTF8(&source, log, allocator) catch |err| { return .{ .err = .{ @@ -364,16 +373,25 @@ pub fn loadFromDir( switch (result) { .ok => { - if (bun.getenvZ("BUN_DEBUG_TEST_TEXT_LOCKFILE") != null) { + if (bun.getenvZ("BUN_DEBUG_TEST_TEXT_LOCKFILE") != null and manager != null) { // Convert the loaded binary lockfile into a text lockfile in memory, then // parse it back into a binary lockfile. - const text_lockfile_bytes = TextLockfile.Stringifier.saveFromBinary(allocator, result.ok.lockfile) catch |err| { + var writer_buf = MutableString.initEmpty(allocator); + var buffered_writer = writer_buf.bufferedWriter(); + const writer = buffered_writer.writer(); + + TextLockfile.Stringifier.saveFromBinary(allocator, result.ok.lockfile, writer) catch |err| { Output.panic("failed to convert binary lockfile to text lockfile: {s}", .{@errorName(err)}); }; + buffered_writer.flush() catch bun.outOfMemory(); + + const text_lockfile_bytes = writer_buf.list.items; + const source = logger.Source.initPathString("bun.lock", text_lockfile_bytes); + initializeStore(); const json = JSON.parsePackageJSONUTF8(&source, log, allocator) catch |err| { Output.panic("failed to print valid json from binary lockfile: {s}", .{@errorName(err)}); }; @@ -474,11 +492,21 @@ pub const Tree = struct { pub const root_dep_id: DependencyID = invalid_package_id - 1; pub const invalid_id: Id = std.math.maxInt(Id); - const dependency_loop = invalid_id - 1; - const hoisted = invalid_id - 2; - const error_id = hoisted; - const SubtreeError = error{ OutOfMemory, DependencyLoop }; + pub const HoistDependencyResult = union(enum) { + dependency_loop, + hoisted, + placement: struct { + id: Id, + bundled: bool = false, + }, + // replace: struct { + // dest_id: Id, + // dep_id: DependencyID, + // }, + }; + + pub const SubtreeError = OOM || error{DependencyLoop}; // max number of node_modules folders pub const max_depth = (bun.MAX_PATH_BYTES / "node_modules".len) + 1; @@ -635,80 +663,115 @@ pub const Tree = struct { return .{ rel, depth }; } - const Builder = struct { - allocator: Allocator, - name_hashes: []const PackageNameHash, - list: bun.MultiArrayList(Entry) = .{}, - resolutions: []const PackageID, - dependencies: []const Dependency, - resolution_lists: []const Lockfile.DependencyIDSlice, - queue: Lockfile.TreeFiller, - log: *logger.Log, - lockfile: *Lockfile, - prefer_dev_dependencies: bool = false, + const BuilderMethod = enum { + /// Hoist, but include every dependency so it's resolvable if configuration + /// changes. For saving to disk. + resolvable, - pub fn maybeReportError(this: *Builder, comptime fmt: string, args: anytype) void { - this.log.addErrorFmt(null, logger.Loc.Empty, this.allocator, fmt, args) catch {}; - } - - pub fn buf(this: *const Builder) []const u8 { - return this.lockfile.buffers.string_bytes.items; - } - - pub fn packageName(this: *Builder, id: PackageID) String.Formatter { - return this.lockfile.packages.items(.name)[id].fmt(this.lockfile.buffers.string_bytes.items); - } - - pub fn packageVersion(this: *Builder, id: PackageID) Resolution.Formatter { - return this.lockfile.packages.items(.resolution)[id].fmt(this.lockfile.buffers.string_bytes.items, .auto); - } - - pub const Entry = struct { - tree: Tree, - dependencies: Lockfile.DependencyIDList, - }; - - /// Flatten the multi-dimensional ArrayList of package IDs into a single easily serializable array - pub fn clean(this: *Builder) !DependencyIDList { - const end = @as(Id, @truncate(this.list.len)); - var i: Id = 0; - var total: u32 = 0; - const trees = this.list.items(.tree); - const dependencies = this.list.items(.dependencies); - - while (i < end) : (i += 1) { - total += trees[i].dependencies.len; - } - - var dependency_ids = try DependencyIDList.initCapacity(z_allocator, total); - var next = PackageIDSlice{}; - - for (trees, dependencies) |*tree, *child| { - if (tree.dependencies.len > 0) { - const len = @as(PackageID, @truncate(child.items.len)); - next.off += next.len; - next.len = len; - tree.dependencies = next; - dependency_ids.appendSliceAssumeCapacity(child.items); - child.deinit(this.allocator); - } - } - this.queue.deinit(); - - return dependency_ids; - } + /// This will filter out disabled dependencies, resulting in more aggresive + /// hoisting compared to `hoist()`. We skip dependencies based on 'os', 'cpu', + /// 'libc' (TODO), and omitted dependency types (`--omit=dev/peer/optional`). + /// Dependencies of a disabled package are not included in the output. + filter, }; + pub fn Builder(comptime method: BuilderMethod) type { + return struct { + allocator: Allocator, + name_hashes: []const PackageNameHash, + list: bun.MultiArrayList(Entry) = .{}, + resolutions: []const PackageID, + dependencies: []const Dependency, + resolution_lists: []const Lockfile.DependencyIDSlice, + queue: Lockfile.TreeFiller, + log: *logger.Log, + lockfile: *const Lockfile, + manager: if (method == .filter) *const PackageManager else void, + sort_buf: std.ArrayListUnmanaged(DependencyID) = .{}, + + pub fn maybeReportError(this: *@This(), comptime fmt: string, args: anytype) void { + this.log.addErrorFmt(null, logger.Loc.Empty, this.allocator, fmt, args) catch {}; + } + + pub fn buf(this: *const @This()) []const u8 { + return this.lockfile.buffers.string_bytes.items; + } + + pub fn packageName(this: *@This(), id: PackageID) String.Formatter { + return this.lockfile.packages.items(.name)[id].fmt(this.lockfile.buffers.string_bytes.items); + } + + pub fn packageVersion(this: *@This(), id: PackageID) Resolution.Formatter { + return this.lockfile.packages.items(.resolution)[id].fmt(this.lockfile.buffers.string_bytes.items, .auto); + } + + pub const Entry = struct { + tree: Tree, + dependencies: Lockfile.DependencyIDList, + }; + + pub const CleanResult = struct { + trees: std.ArrayListUnmanaged(Tree), + dep_ids: std.ArrayListUnmanaged(DependencyID), + }; + + /// Flatten the multi-dimensional ArrayList of package IDs into a single easily serializable array + pub fn clean(this: *@This()) OOM!CleanResult { + var total: u32 = 0; + + const list_ptr = this.list.bytes; + const slice = this.list.toOwnedSlice(); + var trees = slice.items(.tree); + const dependencies = slice.items(.dependencies); + + for (trees) |*tree| { + total += tree.dependencies.len; + } + + var dependency_ids = try DependencyIDList.initCapacity(z_allocator, total); + var next = PackageIDSlice{}; + + for (trees, dependencies) |*tree, *child| { + if (tree.dependencies.len > 0) { + const len = @as(PackageID, @truncate(child.items.len)); + next.off += next.len; + next.len = len; + tree.dependencies = next; + dependency_ids.appendSliceAssumeCapacity(child.items); + child.deinit(this.allocator); + } + } + this.queue.deinit(); + this.sort_buf.deinit(this.allocator); + + // take over the `builder.list` pointer for only trees + if (@intFromPtr(trees.ptr) != @intFromPtr(list_ptr)) { + var new: [*]Tree = @ptrCast(list_ptr); + bun.copy(Tree, new[0..trees.len], trees); + trees = new[0..trees.len]; + } + + return .{ + .trees = std.ArrayListUnmanaged(Tree).fromOwnedSlice(trees), + .dep_ids = dependency_ids, + }; + } + }; + } + pub fn processSubtree( this: *const Tree, dependency_id: DependencyID, - builder: *Builder, + hoist_root_id: Tree.Id, + comptime method: BuilderMethod, + builder: *Builder(method), + log_level: if (method == .filter) PackageManager.Options.LogLevel else void, ) SubtreeError!void { - const package_id = switch (dependency_id) { + const parent_pkg_id = switch (dependency_id) { root_dep_id => 0, else => |id| builder.resolutions[id], }; - const resolution_list = builder.resolution_lists[package_id]; + const resolution_list = builder.resolution_lists[parent_pkg_id]; if (resolution_list.len == 0) return; @@ -727,39 +790,113 @@ pub const Tree = struct { const next: *Tree = &trees[builder.list.len - 1]; const name_hashes: []const PackageNameHash = builder.name_hashes; const max_package_id = @as(PackageID, @truncate(name_hashes.len)); - const resolutions = builder.lockfile.packages.items(.resolution); - var dep_id = resolution_list.off; - const end = dep_id + resolution_list.len; + const pkgs = builder.lockfile.packages.slice(); + const pkg_resolutions = pkgs.items(.resolution); + const pkg_metas = pkgs.items(.meta); + const pkg_names = pkgs.items(.name); - while (dep_id < end) : (dep_id += 1) { - const pid = builder.resolutions[dep_id]; + builder.sort_buf.clearRetainingCapacity(); + try builder.sort_buf.ensureUnusedCapacity(builder.allocator, resolution_list.len); + + for (resolution_list.begin()..resolution_list.end()) |dep_id| { + builder.sort_buf.appendAssumeCapacity(@intCast(dep_id)); + } + + const DepSorter = struct { + lockfile: *const Lockfile, + + pub fn isLessThan(sorter: @This(), l: DependencyID, r: DependencyID) bool { + const deps_buf = sorter.lockfile.buffers.dependencies.items; + const string_buf = sorter.lockfile.buffers.string_bytes.items; + + const l_dep = deps_buf[l]; + const r_dep = deps_buf[r]; + + return switch (l_dep.behavior.cmp(r_dep.behavior)) { + .lt => true, + .gt => false, + .eq => strings.order(l_dep.name.slice(string_buf), r_dep.name.slice(string_buf)) == .lt, + }; + } + }; + + std.sort.pdq( + DependencyID, + builder.sort_buf.items, + DepSorter{ + .lockfile = builder.lockfile, + }, + DepSorter.isLessThan, + ); + + for (builder.sort_buf.items) |dep_id| { + const pkg_id = builder.resolutions[dep_id]; // Skip unresolved packages, e.g. "peerDependencies" - if (pid >= max_package_id) continue; + if (pkg_id >= max_package_id) continue; - const dependency = builder.dependencies[dep_id]; - // Do not hoist folder dependencies - const destination = if (resolutions[pid].tag == .folder) - next.id - else - try next.hoistDependency( + // filter out disabled dependencies + if (comptime method == .filter) { + if (builder.lockfile.isResolvedDependencyDisabled( + dep_id, + switch (pkg_resolutions[parent_pkg_id].tag) { + .root, .workspace, .folder => builder.manager.options.local_package_features, + else => builder.manager.options.remote_package_features, + }, + &pkg_metas[pkg_id], + )) { + if (log_level.isVerbose()) { + const meta = &pkg_metas[pkg_id]; + const name = builder.lockfile.str(&pkg_names[pkg_id]); + if (!meta.os.isMatch() and !meta.arch.isMatch()) { + Output.prettyErrorln("Skip installing '{s}' cpu & os mismatch", .{name}); + } else if (!meta.os.isMatch()) { + Output.prettyErrorln("Skip installing '{s}' os mismatch", .{name}); + } else if (!meta.arch.isMatch()) { + Output.prettyErrorln("Skip installing '{s}' cpu mismatch", .{name}); + } + } + + continue; + } + } + + const hoisted: HoistDependencyResult = hoisted: { + const dependency = builder.dependencies[dep_id]; + + // don't hoist if it's a folder dependency or a bundled dependency. + if (dependency.behavior.isBundled()) { + break :hoisted .{ .placement = .{ .id = next.id, .bundled = true } }; + } + + if (pkg_resolutions[pkg_id].tag == .folder) { + break :hoisted .{ .placement = .{ .id = next.id } }; + } + + break :hoisted try next.hoistDependency( true, - pid, + hoist_root_id, + pkg_id, &dependency, dependency_lists, trees, + method, builder, ); + }; - switch (destination) { - Tree.dependency_loop, Tree.hoisted => continue, - else => { - dependency_lists[destination].append(builder.allocator, dep_id) catch unreachable; - trees[destination].dependencies.len += 1; - if (builder.resolution_lists[pid].len > 0) { + switch (hoisted) { + .dependency_loop, .hoisted => continue, + .placement => |dest| { + dependency_lists[dest.id].append(builder.allocator, dep_id) catch bun.outOfMemory(); + trees[dest.id].dependencies.len += 1; + if (builder.resolution_lists[pkg_id].len > 0) { try builder.queue.writeItem(.{ - .tree_id = destination, + .tree_id = dest.id, .dependency_id = dep_id, + + // if it's bundled, start a new hoist root + .hoist_root_id = if (dest.bundled) dest.id else hoist_root_id, }); } }, @@ -779,31 +916,31 @@ pub const Tree = struct { fn hoistDependency( this: *Tree, comptime as_defined: bool, + hoist_root_id: Id, package_id: PackageID, dependency: *const Dependency, dependency_lists: []Lockfile.DependencyIDList, trees: []Tree, - builder: *Builder, - ) !Id { + comptime method: BuilderMethod, + builder: *Builder(method), + ) !HoistDependencyResult { const this_dependencies = this.dependencies.get(dependency_lists[this.id].items); - for (this_dependencies) |dep_id| { + for (0..this_dependencies.len) |i| { + const dep_id = this_dependencies[i]; const dep = builder.dependencies[dep_id]; if (dep.name_hash != dependency.name_hash) continue; if (builder.resolutions[dep_id] == package_id) { // this dependency is the same package as the other, hoist - return hoisted; // 1 + return .hoisted; // 1 } if (comptime as_defined) { - // same dev dependency as another package in the same package.json, but different version. - // choose dev dep over other if enabled if (dep.behavior.isDev() != dependency.behavior.isDev()) { - if (builder.prefer_dev_dependencies and dep.behavior.isDev()) { - return hoisted; // 1 - } - - return dependency_loop; // 3 + // will only happen in workspaces and root package because + // dev dependencies won't be included in other types of + // dependencies + return .hoisted; // 1 } } @@ -815,7 +952,7 @@ pub const Tree = struct { const resolution: Resolution = builder.lockfile.packages.items(.resolution)[builder.resolutions[dep_id]]; const version = dependency.version.value.npm.version; if (resolution.tag == .npm and version.satisfies(resolution.value.npm.version, builder.buf(), builder.buf())) { - return hoisted; // 1 + return .hoisted; // 1 } } @@ -823,7 +960,7 @@ pub const Tree = struct { // to hoist other peers even if they don't satisfy the version if (builder.lockfile.isWorkspaceRootDependency(dep_id)) { // TODO: warning about peer dependency version mismatch - return hoisted; // 1 + return .hoisted; // 1 } } @@ -839,27 +976,42 @@ pub const Tree = struct { return error.DependencyLoop; } - return dependency_loop; // 3 + return .dependency_loop; // 3 } // this dependency was not found in this tree, try hoisting or placing in the next parent - if (this.parent < error_id) { + if (this.parent != invalid_id and this.id != hoist_root_id) { const id = trees[this.parent].hoistDependency( false, + hoist_root_id, package_id, dependency, dependency_lists, trees, + method, builder, ) catch unreachable; - if (!as_defined or id != dependency_loop) return id; // 1 or 2 + if (!as_defined or id != .dependency_loop) return id; // 1 or 2 } // place the dependency in the current tree - return this.id; // 2 + return .{ .placement = .{ .id = this.id } }; // 2 } }; +pub fn isResolvedDependencyDisabled( + lockfile: *const Lockfile, + dep_id: DependencyID, + features: Features, + meta: *const Package.Meta, +) bool { + if (meta.isDisabled()) return true; + + const dep = lockfile.buffers.dependencies.items[dep_id]; + + return dep.behavior.isBundled() or !dep.behavior.isEnabled(features); +} + /// This conditionally clones the lockfile with root packages marked as non-resolved /// that do not satisfy `Features`. The package may still end up installed even /// if it was e.g. in "devDependencies" and its a production install. In that case, @@ -906,26 +1058,35 @@ pub fn maybeCloneFilteringRootPackages( } fn preprocessUpdateRequests(old: *Lockfile, manager: *PackageManager, updates: []PackageManager.UpdateRequest, exact_versions: bool) !void { - const root_deps_list: Lockfile.DependencySlice = old.packages.items(.dependencies)[0]; + const workspace_package_id = manager.root_package_id.get(old, manager.workspace_name_hash); + const root_deps_list: Lockfile.DependencySlice = old.packages.items(.dependencies)[workspace_package_id]; + if (@as(usize, root_deps_list.off) < old.buffers.dependencies.items.len) { var string_builder = old.stringBuilder(); { const root_deps: []const Dependency = root_deps_list.get(old.buffers.dependencies.items); - const old_resolutions_list = old.packages.items(.resolutions)[0]; + const old_resolutions_list = old.packages.items(.resolutions)[workspace_package_id]; const old_resolutions: []const PackageID = old_resolutions_list.get(old.buffers.resolutions.items); const resolutions_of_yore: []const Resolution = old.packages.items(.resolution); for (updates) |update| { - if (update.version.tag == .uninitialized) { + if (update.package_id == invalid_package_id) { for (root_deps, old_resolutions) |dep, old_resolution| { if (dep.name_hash == String.Builder.stringHash(update.name)) { if (old_resolution > old.packages.len) continue; const res = resolutions_of_yore[old_resolution]; + if (res.tag != .npm or update.version.tag != .dist_tag) continue; + + // TODO(dylan-conway): this will need to handle updating dependencies (exact, ^, or ~) and aliases + const len = switch (exact_versions) { - false => std.fmt.count("^{}", .{res.value.npm.version.fmt(old.buffers.string_bytes.items)}), - true => std.fmt.count("{}", .{res.value.npm.version.fmt(old.buffers.string_bytes.items)}), + else => |exact| std.fmt.count("{s}{}", .{ + if (exact) "" else "^", + res.value.npm.version.fmt(old.buffers.string_bytes.items), + }), }; + if (len >= String.max_inline_len) { string_builder.cap += len; } @@ -943,20 +1104,27 @@ fn preprocessUpdateRequests(old: *Lockfile, manager: *PackageManager, updates: [ const root_deps: []Dependency = root_deps_list.mut(old.buffers.dependencies.items); const old_resolutions_list_lists = old.packages.items(.resolutions); - const old_resolutions_list = old_resolutions_list_lists[0]; + const old_resolutions_list = old_resolutions_list_lists[workspace_package_id]; const old_resolutions: []const PackageID = old_resolutions_list.get(old.buffers.resolutions.items); const resolutions_of_yore: []const Resolution = old.packages.items(.resolution); for (updates) |*update| { - if (update.version.tag == .uninitialized) { + if (update.package_id == invalid_package_id) { for (root_deps, old_resolutions) |*dep, old_resolution| { if (dep.name_hash == String.Builder.stringHash(update.name)) { if (old_resolution > old.packages.len) continue; const res = resolutions_of_yore[old_resolution]; + if (res.tag != .npm or update.version.tag != .dist_tag) continue; + + // TODO(dylan-conway): this will need to handle updating dependencies (exact, ^, or ~) and aliases + const buf = switch (exact_versions) { - false => std.fmt.bufPrint(&temp_buf, "^{}", .{res.value.npm.version.fmt(old.buffers.string_bytes.items)}) catch break, - true => std.fmt.bufPrint(&temp_buf, "{}", .{res.value.npm.version.fmt(old.buffers.string_bytes.items)}) catch break, + else => |exact| std.fmt.bufPrint(&temp_buf, "{s}{}", .{ + if (exact) "" else "^", + res.value.npm.version.fmt(old.buffers.string_bytes.items), + }) catch break, }; + const external_version = string_builder.append(ExternalString, buf); const sliced = external_version.value.sliced(old.buffers.string_bytes.items); dep.version = Dependency.parse( @@ -1267,6 +1435,10 @@ pub fn fmtMetaHash(this: *const Lockfile) MetaHashFormatter { pub const FillItem = struct { tree_id: Tree.Id, dependency_id: DependencyID, + + /// If valid, dependencies will not hoist + /// beyond this tree if they're in a subtree + hoist_root_id: Tree.Id, }; pub const TreeFiller = std.fifo.LinearFifo(FillItem, .Dynamic); @@ -1283,9 +1455,7 @@ const Cloner = struct { pub fn flush(this: *Cloner) anyerror!void { const max_package_id = this.old.packages.len; - while (this.clone_queue.popOrNull()) |to_clone_| { - const to_clone: PendingResolution = to_clone_; - + while (this.clone_queue.popOrNull()) |to_clone| { const mapping = this.mapping[to_clone.old_resolution]; if (mapping < max_package_id) { this.lockfile.buffers.resolutions.items[to_clone.resolve_id] = mapping; @@ -1308,7 +1478,7 @@ const Cloner = struct { this.manager.clearCachedItemsDependingOnLockfileBuffer(); if (this.lockfile.packages.len != 0) { - try this.hoist(this.lockfile); + try this.lockfile.hoist(this.log, .resolvable, {}); } // capacity is used for calculating byte size @@ -1316,39 +1486,53 @@ const Cloner = struct { if (this.lockfile.packages.capacity != this.lockfile.packages.len and this.lockfile.packages.len > 0) this.lockfile.packages.shrinkAndFree(this.lockfile.allocator, this.lockfile.packages.len); } - - fn hoist(this: *Cloner, lockfile: *Lockfile) anyerror!void { - const allocator = lockfile.allocator; - var slice = lockfile.packages.slice(); - var builder = Tree.Builder{ - .name_hashes = slice.items(.name_hash), - .queue = TreeFiller.init(allocator), - .resolution_lists = slice.items(.resolutions), - .resolutions = lockfile.buffers.resolutions.items, - .allocator = allocator, - .dependencies = lockfile.buffers.dependencies.items, - .log = this.log, - .lockfile = lockfile, - .prefer_dev_dependencies = this.manager.options.local_package_features.dev_dependencies, - }; - - try (Tree{}).processSubtree(Tree.root_dep_id, &builder); - // This goes breadth-first - while (builder.queue.readItem()) |item| { - try builder.list.items(.tree)[item.tree_id].processSubtree(item.dependency_id, &builder); - } - - lockfile.buffers.hoisted_dependencies = try builder.clean(); - { - const final = builder.list.items(.tree); - lockfile.buffers.trees = .{ - .items = final, - .capacity = final.len, - }; - } - } }; +/// Sets `buffers.trees` and `buffers.hoisted_dependencies` +pub fn hoist( + lockfile: *Lockfile, + log: *logger.Log, + comptime method: Tree.BuilderMethod, + manager: if (method == .filter) *PackageManager else void, +) Tree.SubtreeError!void { + const allocator = lockfile.allocator; + var slice = lockfile.packages.slice(); + var builder = Tree.Builder(method){ + .name_hashes = slice.items(.name_hash), + .queue = TreeFiller.init(allocator), + .resolution_lists = slice.items(.resolutions), + .resolutions = lockfile.buffers.resolutions.items, + .allocator = allocator, + .dependencies = lockfile.buffers.dependencies.items, + .log = log, + .lockfile = lockfile, + .manager = manager, + }; + + try (Tree{}).processSubtree( + Tree.root_dep_id, + Tree.invalid_id, + method, + &builder, + if (method == .filter) manager.options.log_level else {}, + ); + + // This goes breadth-first + while (builder.queue.readItem()) |item| { + try builder.list.items(.tree)[item.tree_id].processSubtree( + item.dependency_id, + item.hoist_root_id, + method, + &builder, + if (method == .filter) manager.options.log_level else {}, + ); + } + + const cleaned = try builder.clean(); + lockfile.buffers.trees = cleaned.trees; + lockfile.buffers.hoisted_dependencies = cleaned.dep_ids; +} + const PendingResolution = struct { old_resolution: PackageID, resolve_id: PackageID, @@ -1506,6 +1690,7 @@ pub const Printer = struct { const dependencies = lockfile.buffers.dependencies.items; const workspace_res = packages_slice.items(.resolution)[workspace_package_id]; const names = packages_slice.items(.name); + const pkg_metas = packages_slice.items(.meta); bun.assert(workspace_res.tag == .workspace or workspace_res.tag == .root); const resolutions_list = packages_slice.items(.resolutions); var printed_section_header = false; @@ -1513,7 +1698,7 @@ pub const Printer = struct { // find the updated packages for (resolutions_list[workspace_package_id].begin()..resolutions_list[workspace_package_id].end()) |dep_id| { - switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map)) { + switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map, pkg_metas)) { .yes, .no, .@"return" => {}, .update => |update_info| { printed_new_install.* = true; @@ -1535,7 +1720,7 @@ pub const Printer = struct { } for (resolutions_list[workspace_package_id].begin()..resolutions_list[workspace_package_id].end()) |dep_id| { - switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map)) { + switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map, pkg_metas)) { .@"return" => return, .yes => {}, .no, .update => continue, @@ -1584,6 +1769,7 @@ pub const Printer = struct { dep_id: DependencyID, installed: *const Bitset, id_map: ?[]DependencyID, + pkg_metas: []const Package.Meta, ) ShouldPrintPackageInstallResult { const dependencies = this.lockfile.buffers.dependencies.items; const resolutions = this.lockfile.buffers.resolutions.items; @@ -1607,6 +1793,17 @@ pub const Printer = struct { if (!installed.isSet(package_id)) return .no; + // It's possible this package was installed but the dependency is disabled. + // Have "zod@1.0.0" in dependencies and `zod2@npm:zod@1.0.0` in devDependencies + // and install with --omit=dev. + if (this.lockfile.isResolvedDependencyDisabled( + dep_id, + this.options.local_package_features, + &pkg_metas[package_id], + )) { + return .no; + } + const resolution = this.lockfile.packages.items(.resolution)[package_id]; if (resolution.tag == .npm) { const name = dependency.name.slice(this.lockfile.buffers.string_bytes.items); @@ -1726,6 +1923,7 @@ pub const Printer = struct { if (resolved.len == 0) return; const string_buf = this.lockfile.buffers.string_bytes.items; const resolutions_list = slice.items(.resolutions); + const pkg_metas = slice.items(.meta); const resolutions_buffer: []const PackageID = this.lockfile.buffers.resolutions.items; const dependencies_buffer: []const Dependency = this.lockfile.buffers.dependencies.items; if (dependencies_buffer.len == 0) return; @@ -1752,7 +1950,7 @@ pub const Printer = struct { for (workspaces_to_print.items) |workspace_dep_id| { const workspace_package_id = resolutions_buffer[workspace_dep_id]; for (resolutions_list[workspace_package_id].begin()..resolutions_list[workspace_package_id].end()) |dep_id| { - switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map)) { + switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map, pkg_metas)) { .yes => found_workspace_to_print = true, else => {}, } @@ -2094,14 +2292,14 @@ pub const Printer = struct { } if (dependencies.len > 0) { - var behavior = Behavior.uninitialized; + var behavior: Behavior = .{}; var dependency_behavior_change_count: u8 = 0; for (dependencies) |dep| { if (!dep.behavior.eq(behavior)) { if (dep.behavior.isOptional()) { try writer.writeAll(" optionalDependencies:\n"); if (comptime Environment.allow_assert) dependency_behavior_change_count += 1; - } else if (dep.behavior.isNormal()) { + } else if (dep.behavior.isProd()) { try writer.writeAll(" dependencies:\n"); if (comptime Environment.allow_assert) dependency_behavior_change_count += 1; } else if (dep.behavior.isDev()) { @@ -2167,14 +2365,23 @@ pub fn saveToDisk(this: *Lockfile, save_format: LoadResult.LockfileFormat, verbo assert(FileSystem.instance_loaded); } - const timer = std.time.Timer.start() catch unreachable; - const bytes = if (save_format == .text) - TextLockfile.Stringifier.saveFromBinary(bun.default_allocator, this) catch |err| { - switch (err) { + const bytes = bytes: { + if (save_format == .text) { + var writer_buf = MutableString.initEmpty(bun.default_allocator); + var buffered_writer = writer_buf.bufferedWriter(); + const writer = buffered_writer.writer(); + + TextLockfile.Stringifier.saveFromBinary(bun.default_allocator, this, writer) catch |err| switch (err) { error.OutOfMemory => bun.outOfMemory(), - } + }; + + buffered_writer.flush() catch |err| switch (err) { + error.OutOfMemory => bun.outOfMemory(), + }; + + break :bytes writer_buf.list.items; } - else bytes: { + var bytes = std.ArrayList(u8).init(bun.default_allocator); var total_size: usize = 0; @@ -2188,8 +2395,6 @@ pub fn saveToDisk(this: *Lockfile, save_format: LoadResult.LockfileFormat, verbo break :bytes bytes.items; }; defer bun.default_allocator.free(bytes); - _ = timer; - // std.debug.print("time to write {s}: {}\n", .{ @tagName(save_format), bun.fmt.fmtDuration(timer.read()) }); var tmpname_buf: [512]u8 = undefined; var base64_bytes: [8]u8 = undefined; @@ -2218,8 +2423,12 @@ pub fn saveToDisk(this: *Lockfile, save_format: LoadResult.LockfileFormat, verbo } if (comptime Environment.isPosix) { - // chmod 777 on posix - switch (bun.sys.fchmod(file.handle, 0o777)) { + // chmod 755 for binary, 644 for plaintext + var filemode: bun.Mode = 0o755; + if (save_format == .text) { + filemode = 0o644; + } + switch (bun.sys.fchmod(file.handle, filemode)) { .err => |err| { file.close(); _ = bun.sys.unlink(tmpname); @@ -2341,7 +2550,7 @@ pub fn appendPackageDedupe(this: *Lockfile, pkg: *Package, buf: string) OOM!Pack return new_id; } - const resolutions = this.packages.items(.resolution); + var resolutions = this.packages.items(.resolution); return switch (entry.value_ptr.*) { .id => |existing_id| { @@ -2354,6 +2563,8 @@ pub fn appendPackageDedupe(this: *Lockfile, pkg: *Package, buf: string) OOM!Pack pkg.meta.id = new_id; try this.packages.append(this.allocator, pkg.*); + resolutions = this.packages.items(.resolution); + var ids = try PackageIDList.initCapacity(this.allocator, 8); ids.items.len = 2; @@ -2380,6 +2591,8 @@ pub fn appendPackageDedupe(this: *Lockfile, pkg: *Package, buf: string) OOM!Pack pkg.meta.id = new_id; try this.packages.append(this.allocator, pkg.*); + resolutions = this.packages.items(.resolution); + for (existing_ids.items, 0..) |existing_id, i| { if (pkg.resolution.order(&resolutions[existing_id], buf, buf) == .gt) { try existing_ids.insert(this.allocator, i, new_id); @@ -2466,8 +2679,9 @@ pub inline fn stringBuilder(this: *Lockfile) Lockfile.StringBuilder { pub fn stringBuf(this: *Lockfile) String.Buf { return .{ - .bytes = this.buffers.string_bytes.toManaged(this.allocator), - .pool = this.string_pool, + .bytes = &this.buffers.string_bytes, + .allocator = this.allocator, + .pool = &this.string_pool, }; } @@ -3019,6 +3233,15 @@ pub const Package = extern struct { postprepare: String = .{}, filled: bool = false, + pub fn eql(l: *const Package.Scripts, r: *const Package.Scripts, l_buf: string, r_buf: string) bool { + return l.preinstall.eql(r.preinstall, l_buf, r_buf) and + l.install.eql(r.install, l_buf, r_buf) and + l.postinstall.eql(r.postinstall, l_buf, r_buf) and + l.preprepare.eql(r.preprepare, l_buf, r_buf) and + l.prepare.eql(r.prepare, l_buf, r_buf) and + l.postprepare.eql(r.postprepare, l_buf, r_buf); + } + pub const List = struct { items: [Lockfile.Scripts.names.len]?Lockfile.Scripts.Entry, first_index: u8, @@ -3278,7 +3501,7 @@ pub const Package = extern struct { this: *Package.Scripts, log: *logger.Log, lockfile: *Lockfile, - node_modules: std.fs.Dir, + node_modules: *PackageManager.LazyPackageDestinationDir, abs_node_modules_path: string, folder_name: string, resolution: *const Resolution, @@ -3338,13 +3561,13 @@ pub const Package = extern struct { allocator: std.mem.Allocator, string_builder: *Lockfile.StringBuilder, log: *logger.Log, - node_modules: std.fs.Dir, + node_modules: *PackageManager.LazyPackageDestinationDir, folder_name: string, ) !void { const json = brk: { const json_src = brk2: { const json_path = bun.path.joinZ([_]string{ folder_name, "package.json" }, .auto); - const buf = try bun.sys.File.readFrom(node_modules, json_path, allocator).unwrap(); + const buf = try bun.sys.File.readFrom(try node_modules.getDir(), json_path, allocator).unwrap(); break :brk2 logger.Source.initPathString(json_path, buf); }; @@ -3366,7 +3589,7 @@ pub const Package = extern struct { this: *Package.Scripts, log: *logger.Log, lockfile: *Lockfile, - node_modules: std.fs.Dir, + node_modules: *PackageManager.LazyPackageDestinationDir, abs_folder_path: string, folder_name: string, resolution_tag: Resolution.Tag, @@ -3403,7 +3626,7 @@ pub const Package = extern struct { field: string, behavior: Behavior, - pub const dependencies = DependencyGroup{ .prop = "dependencies", .field = "dependencies", .behavior = Behavior.normal }; + pub const dependencies = DependencyGroup{ .prop = "dependencies", .field = "dependencies", .behavior = Behavior.prod }; pub const dev = DependencyGroup{ .prop = "devDependencies", .field = "dev_dependencies", .behavior = Behavior.dev }; pub const optional = DependencyGroup{ .prop = "optionalDependencies", .field = "optional_dependencies", .behavior = Behavior.optional }; pub const peer = DependencyGroup{ .prop = "peerDependencies", .field = "peer_dependencies", .behavior = Behavior.peer }; @@ -3793,13 +4016,23 @@ pub const Package = extern struct { const dep_version = string_builder.appendWithHash(String, version_string_.slice(string_buf), version_string_.hash); const sliced = dep_version.sliced(lockfile.buffers.string_bytes.items); + var behavior = group.behavior; + if (comptime is_peer) { + behavior.optional = i < package_version.non_optional_peer_dependencies_start; + } + if (package_version_ptr.allDependenciesBundled()) { + behavior.bundled = true; + } else for (package_version.bundled_dependencies.get(manifest.bundled_deps_buf)) |bundled_dep_name_hash| { + if (bundled_dep_name_hash == name.hash) { + behavior.bundled = true; + break; + } + } + const dependency = Dependency{ .name = name.value, .name_hash = name.hash, - .behavior = if (comptime is_peer) - group.behavior.setOptional(i < package_version.non_optional_peer_dependencies_start) - else - group.behavior, + .behavior = behavior, .version = Dependency.parse( allocator, name.value, @@ -4112,6 +4345,7 @@ pub const Package = extern struct { const json = pm.workspace_package_json_cache.getWithSource(bun.default_allocator, log, source, .{}).unwrap() catch break :brk false; + var resolver: void = {}; try workspace.parseWithJSON( to_lockfile, pm, @@ -4120,7 +4354,7 @@ pub const Package = extern struct { source, json.root, void, - {}, + &resolver, Features.workspace, ); @@ -4201,7 +4435,7 @@ pub const Package = extern struct { log: *logger.Log, source: logger.Source, comptime ResolverContext: type, - resolver: ResolverContext, + resolver: *ResolverContext, comptime features: Features, ) !void { initializeStore(); @@ -4357,7 +4591,7 @@ pub const Package = extern struct { for (package_dependencies[0..dependencies_count]) |*dep| { if (dep.name_hash == name_hash and dep.version.tag == .workspace) { dep.* = .{ - .behavior = if (in_workspace) group.behavior.setWorkspace(true) else group.behavior, + .behavior = if (in_workspace) group.behavior.add(.workspace) else group.behavior, .name = external_alias.value, .name_hash = external_alias.hash, .version = dependency_version, @@ -4469,7 +4703,7 @@ pub const Package = extern struct { } const this_dep = Dependency{ - .behavior = if (in_workspace) group.behavior.setWorkspace(true) else group.behavior, + .behavior = if (in_workspace) group.behavior.add(.workspace) else group.behavior, .name = external_alias.value, .name_hash = external_alias.hash, .version = dependency_version, @@ -4648,7 +4882,7 @@ pub const Package = extern struct { if (input_path.len == 0 or input_path.len == 1 and input_path[0] == '.') continue; - if (bun.glob.detectGlobSyntax(input_path)) { + if (Glob.Ascii.detectGlobSyntax(input_path)) { workspace_globs.append(input_path) catch bun.outOfMemory(); continue; } @@ -4891,7 +5125,7 @@ pub const Package = extern struct { source: logger.Source, json: Expr, comptime ResolverContext: type, - resolver: ResolverContext, + resolver: *ResolverContext, comptime features: Features, ) !void { var string_builder = lockfile.stringBuilder(); @@ -4913,7 +5147,7 @@ pub const Package = extern struct { } // name is not validated by npm, so fallback to creating a new from the version literal - if (ResolverContext == *PackageManager.GitResolver) { + if (ResolverContext == PackageManager.GitResolver) { const resolution: *const Resolution = resolver.resolution; const repo = switch (resolution.tag) { .git => resolution.value.git, @@ -5203,7 +5437,7 @@ pub const Package = extern struct { const package_dependencies = lockfile.buffers.dependencies.items.ptr[off..total_len]; name: { - if (ResolverContext == *PackageManager.GitResolver) { + if (ResolverContext == PackageManager.GitResolver) { if (resolver.new_name.len != 0) { defer lockfile.allocator.free(resolver.new_name); const external_string = string_builder.append(ExternalString, resolver.new_name); @@ -5351,6 +5585,25 @@ pub const Package = extern struct { try lockfile.scratch.duplicate_checker_map.ensureTotalCapacity(total_dependencies_count); } + var bundled_deps = bun.StringSet.init(allocator); + defer bundled_deps.deinit(); + var bundle_all_deps = false; + if (comptime ResolverContext != void and ResolverContext.checkBundledDependencies()) { + if (json.get("bundleDependencies") orelse json.get("bundledDependencies")) |bundled_deps_expr| { + switch (bundled_deps_expr.data) { + .e_boolean => |boolean| { + bundle_all_deps = boolean.value; + }, + .e_array => |arr| { + for (arr.slice()) |item| { + try bundled_deps.insert(item.asString(allocator) orelse continue); + } + }, + else => {}, + } + } + } + total_dependencies_count = 0; const in_workspace = lockfile.workspace_paths.contains(package.name_hash); @@ -5451,7 +5704,7 @@ pub const Package = extern struct { )) |_dep| { var dep = _dep; if (group.behavior.isPeer() and optional_peer_dependencies.contains(external_name.hash)) { - dep.behavior = dep.behavior.setOptional(true); + dep.behavior = dep.behavior.add(.optional); } package_dependencies[total_dependencies_count] = dep; @@ -5494,7 +5747,11 @@ pub const Package = extern struct { )) |_dep| { var dep = _dep; if (group.behavior.isPeer() and optional_peer_dependencies.contains(external_name.hash)) { - dep.behavior = dep.behavior.setOptional(true); + dep.behavior.optional = true; + } + + if (bundle_all_deps or bundled_deps.contains(dep.name.slice(lockfile.buffers.string_bytes.items))) { + dep.behavior.bundled = true; } package_dependencies[total_dependencies_count] = dep; @@ -6608,6 +6865,159 @@ pub const Serializer = struct { } }; +pub const EqlSorter = struct { + string_buf: string, + pkg_names: []const String, + + // Basically placement id + pub const PathToId = struct { + pkg_id: PackageID, + tree_path: string, + }; + + pub fn isLessThan(this: @This(), l: PathToId, r: PathToId) bool { + switch (strings.order(l.tree_path, r.tree_path)) { + .lt => return true, + .gt => return false, + .eq => {}, + } + + // they exist in the same tree, name can't be the same so string + // compare. + const l_name = this.pkg_names[l.pkg_id]; + const r_name = this.pkg_names[r.pkg_id]; + return l_name.order(&r_name, this.string_buf, this.string_buf) == .lt; + } +}; + +/// `cut_off_pkg_id` should be removed when we stop appending packages to lockfile during install step +pub fn eql(l: *const Lockfile, r: *const Lockfile, cut_off_pkg_id: usize, allocator: std.mem.Allocator) OOM!bool { + const l_hoisted_deps = l.buffers.hoisted_dependencies.items; + const r_hoisted_deps = r.buffers.hoisted_dependencies.items; + const l_string_buf = l.buffers.string_bytes.items; + const r_string_buf = r.buffers.string_bytes.items; + + const l_len = l_hoisted_deps.len; + const r_len = r_hoisted_deps.len; + + if (l_len != r_len) return false; + + const sort_buf = try allocator.alloc(EqlSorter.PathToId, l_len + r_len); + defer l.allocator.free(sort_buf); + var l_buf = sort_buf[0..l_len]; + var r_buf = sort_buf[r_len..]; + + var path_buf: bun.PathBuffer = undefined; + var depth_buf: Tree.DepthBuf = undefined; + + var i: usize = 0; + for (l.buffers.trees.items) |l_tree| { + const rel_path, _ = Tree.relativePathAndDepth(l, l_tree.id, &path_buf, &depth_buf, .pkg_path); + const tree_path = try allocator.dupe(u8, rel_path); + for (l_tree.dependencies.get(l_hoisted_deps)) |l_dep_id| { + if (l_dep_id == invalid_dependency_id) continue; + const l_pkg_id = l.buffers.resolutions.items[l_dep_id]; + if (l_pkg_id == invalid_package_id or l_pkg_id >= cut_off_pkg_id) continue; + l_buf[i] = .{ + .pkg_id = l_pkg_id, + .tree_path = tree_path, + }; + i += 1; + } + } + l_buf = l_buf[0..i]; + + i = 0; + for (r.buffers.trees.items) |r_tree| { + const rel_path, _ = Tree.relativePathAndDepth(r, r_tree.id, &path_buf, &depth_buf, .pkg_path); + const tree_path = try allocator.dupe(u8, rel_path); + for (r_tree.dependencies.get(r_hoisted_deps)) |r_dep_id| { + if (r_dep_id == invalid_dependency_id) continue; + const r_pkg_id = r.buffers.resolutions.items[r_dep_id]; + if (r_pkg_id == invalid_package_id or r_pkg_id >= cut_off_pkg_id) continue; + r_buf[i] = .{ + .pkg_id = r_pkg_id, + .tree_path = tree_path, + }; + i += 1; + } + } + r_buf = r_buf[0..i]; + + if (l_buf.len != r_buf.len) return false; + + const l_pkgs = l.packages.slice(); + const r_pkgs = r.packages.slice(); + const l_pkg_names = l_pkgs.items(.name); + const r_pkg_names = r_pkgs.items(.name); + + std.sort.pdq( + EqlSorter.PathToId, + l_buf, + EqlSorter{ + .pkg_names = l_pkg_names, + .string_buf = l_string_buf, + }, + EqlSorter.isLessThan, + ); + + std.sort.pdq( + EqlSorter.PathToId, + r_buf, + EqlSorter{ + .pkg_names = r_pkg_names, + .string_buf = r_string_buf, + }, + EqlSorter.isLessThan, + ); + + const l_pkg_name_hashes = l_pkgs.items(.name_hash); + const l_pkg_resolutions = l_pkgs.items(.resolution); + const l_pkg_bins = l_pkgs.items(.bin); + const l_pkg_scripts = l_pkgs.items(.scripts); + const r_pkg_name_hashes = r_pkgs.items(.name_hash); + const r_pkg_resolutions = r_pkgs.items(.resolution); + const r_pkg_bins = r_pkgs.items(.bin); + const r_pkg_scripts = r_pkgs.items(.scripts); + + const l_extern_strings = l.buffers.extern_strings.items; + const r_extern_strings = r.buffers.extern_strings.items; + + for (l_buf, r_buf) |l_ids, r_ids| { + const l_pkg_id = l_ids.pkg_id; + const r_pkg_id = r_ids.pkg_id; + if (l_pkg_name_hashes[l_pkg_id] != r_pkg_name_hashes[r_pkg_id]) { + return false; + } + const l_res = l_pkg_resolutions[l_pkg_id]; + const r_res = r_pkg_resolutions[r_pkg_id]; + + if (l_res.tag == .uninitialized or r_res.tag == .uninitialized) { + if (l_res.tag != r_res.tag) { + return false; + } + } else if (!l_res.eql(&r_res, l_string_buf, r_string_buf)) { + return false; + } + + if (!l_pkg_bins[l_pkg_id].eql( + &r_pkg_bins[r_pkg_id], + l_string_buf, + l_extern_strings, + r_string_buf, + r_extern_strings, + )) { + return false; + } + + if (!l_pkg_scripts[l_pkg_id].eql(&r_pkg_scripts[r_pkg_id], l_string_buf, r_string_buf)) { + return false; + } + } + + return true; +} + pub fn hasMetaHashChanged(this: *Lockfile, print_name_version_string: bool, packages_len: usize) !bool { const previous_meta_hash = this.meta_hash; this.meta_hash = try this.generateMetaHash(print_name_version_string, packages_len); diff --git a/src/install/migration.zig b/src/install/migration.zig index ca04d48732..1810598c74 100644 --- a/src/install/migration.zig +++ b/src/install/migration.zig @@ -151,10 +151,6 @@ pub fn migrateNPMLockfile( bun.Analytics.Features.lockfile_migration_from_package_lock += 1; // Count pass - var builder_ = this.stringBuilder(); - var builder = &builder_; - const name = (if (json.get("name")) |expr| expr.asString(allocator) else null) orelse ""; - builder.count(name); var root_package: *E.Object = undefined; var packages_properties = brk: { @@ -198,7 +194,7 @@ pub fn migrateNPMLockfile( json_array, &json_src, wksp.loc, - builder, + null, ); debug("found {d} workspace packages", .{workspace_packages_count}); num_deps += workspace_packages_count; @@ -270,20 +266,6 @@ pub fn migrateNPMLockfile( return error.InvalidNPMLockfile; } num_deps +|= @as(u32, deps.data.e_object.properties.len); - - for (deps.data.e_object.properties.slice()) |dep| { - const dep_name = dep.key.?.asString(allocator).?; - const version_string = dep.value.?.asString(allocator) orelse return error.InvalidNPMLockfile; - - builder.count(dep_name); - builder.count(version_string); - - // If it's a folder or workspace, pessimistically assume we will need a maximum path - switch (Dependency.Version.Tag.infer(version_string)) { - .folder, .workspace => builder.cap += bun.MAX_PATH_BYTES, - else => {}, - } - } } } @@ -291,51 +273,14 @@ pub fn migrateNPMLockfile( if (bin.data != .e_object) return error.InvalidNPMLockfile; switch (bin.data.e_object.properties.len) { 0 => return error.InvalidNPMLockfile, - 1 => { - const first_bin = bin.data.e_object.properties.at(0); - const key = first_bin.key.?.asString(allocator).?; - - const workspace_entry = if (workspace_map) |map| map.map.get(pkg_path) else null; - const is_workspace = workspace_entry != null; - - const pkg_name = if (is_workspace) - workspace_entry.?.name - else if (entry.value.?.get("name")) |set_name| - (set_name.asString(this.allocator) orelse return error.InvalidNPMLockfile) - else - packageNameFromPath(pkg_path); - - if (!strings.eql(key, pkg_name)) { - builder.count(key); - } - builder.count(first_bin.value.?.asString(allocator) orelse return error.InvalidNPMLockfile); - }, + 1 => {}, else => { - for (bin.data.e_object.properties.slice()) |bin_entry| { - builder.count(bin_entry.key.?.asString(allocator).?); - builder.count(bin_entry.value.?.asString(allocator) orelse return error.InvalidNPMLockfile); - } num_extern_strings += @truncate(bin.data.e_object.properties.len * 2); }, } } - if (pkg.get("resolved")) |resolved_expr| { - const resolved = resolved_expr.asString(allocator) orelse return error.InvalidNPMLockfile; - if (strings.hasPrefixComptime(resolved, "file:")) { - builder.count(resolved[5..]); - } else if (strings.hasPrefixComptime(resolved, "git+")) { - builder.count(resolved[4..]); - } else { - builder.count(resolved); - - // this is over-counting but whatever. it would be too hard to determine if the case here - // is an `npm`/`dist_tag` version (the only times this is actually used) - if (pkg.get("version")) |v| if (v.asString(allocator)) |s| { - builder.count(s); - }; - } - } else { + if (pkg.get("resolved") == null) { const version_prop = pkg.get("version"); const pkg_name = packageNameFromPath(pkg_path); if (version_prop != null and pkg_name.len > 0) { @@ -376,10 +321,7 @@ pub fn migrateNPMLockfile( remain = remain[version_str.len..]; remain[0..".tgz".len].* = ".tgz".*; - builder.count(resolved_url); try resolved_urls.put(allocator, pkg_path, resolved_url); - } else { - builder.count(pkg_path); } } } @@ -395,7 +337,10 @@ pub fn migrateNPMLockfile( try this.packages.ensureTotalCapacity(allocator, package_idx); // The package index is overallocated, but we know the upper bound try this.package_index.ensureTotalCapacity(package_idx); - try builder.allocate(); + + // dependency on `resolved`, a dependencies version tag might change, requiring + // new strings to be allocated. + var string_buf = this.stringBuf(); if (workspace_map) |wksp| { try this.workspace_paths.ensureTotalCapacity(allocator, wksp.map.unmanaged.entries.len); @@ -408,7 +353,7 @@ pub fn migrateNPMLockfile( bun.assert(!strings.containsChar(k, '\\')); } - this.workspace_paths.putAssumeCapacity(name_hash, builder.append(String, k)); + this.workspace_paths.putAssumeCapacity(name_hash, try string_buf.append(k)); if (v.version) |version_string| { const sliced_version = Semver.SlicedString.init(version_string, version_string); @@ -446,7 +391,7 @@ pub fn migrateNPMLockfile( // the package name is different. This package doesn't exist // in node_modules, but we still allow packages to resolve to it's // resolution. - path_entry.value_ptr.* = builder.append(String, resolved_str); + path_entry.value_ptr.* = try string_buf.append(resolved_str); if (wksp_entry.version) |version_string| { const sliced_version = Semver.SlicedString.init(version_string, version_string); @@ -489,15 +434,14 @@ pub fn migrateNPMLockfile( // Instead of calling this.appendPackage, manually append // the other function has some checks that will fail since we have not set resolution+dependencies yet. this.packages.appendAssumeCapacity(Lockfile.Package{ - .name = builder.appendWithHash(String, pkg_name, name_hash), + .name = try string_buf.appendWithHash(pkg_name, name_hash), .name_hash = name_hash, // For non workspace packages these are set to .uninitialized, then in the third phase // they are resolved. This is because the resolution uses the dependant's version // specifier as a "hint" to resolve the dependency. .resolution = if (is_workspace) Resolution.init(.{ - // This string is counted by `processWorkspaceNamesArray` - .workspace = builder.append(String, pkg_path), + .workspace = try string_buf.append(pkg_path), }) else Resolution{}, // we fill this data in later @@ -571,7 +515,7 @@ pub fn migrateNPMLockfile( break :bin .{ .tag = .file, .value = Bin.Value.init(.{ - .file = builder.append(String, script_value), + .file = try string_buf.append(script_value), }), }; } @@ -580,8 +524,8 @@ pub fn migrateNPMLockfile( .tag = .named_file, .value = Bin.Value.init(.{ .named_file = .{ - builder.append(String, key), - builder.append(String, script_value), + try string_buf.append(key), + try string_buf.append(script_value), }, }), }; @@ -595,8 +539,8 @@ pub fn migrateNPMLockfile( for (bin.data.e_object.properties.slice()) |bin_entry| { const key = bin_entry.key.?.asString(this.allocator) orelse return error.InvalidNPMLockfile; const script_value = bin_entry.value.?.asString(this.allocator) orelse return error.InvalidNPMLockfile; - this.buffers.extern_strings.appendAssumeCapacity(builder.append(ExternalString, key)); - this.buffers.extern_strings.appendAssumeCapacity(builder.append(ExternalString, script_value)); + this.buffers.extern_strings.appendAssumeCapacity(try string_buf.appendExternal(key)); + this.buffers.extern_strings.appendAssumeCapacity(try string_buf.appendExternal(script_value)); } if (Environment.allow_assert) { @@ -719,8 +663,8 @@ pub fn migrateNPMLockfile( for (wksp.keys(), wksp.values()) |key, value| { const entry1 = id_map.get(key) orelse return error.InvalidNPMLockfile; const name_hash = stringHash(value.name); - const wksp_name = builder.append(String, value.name); - const wksp_path = builder.append(String, key); + const wksp_name = try string_buf.append(value.name); + const wksp_path = try string_buf.append(key); dependencies_buf[0] = Dependency{ .name = wksp_name, .name_hash = name_hash, @@ -731,9 +675,7 @@ pub fn migrateNPMLockfile( .workspace = wksp_path, }, }, - .behavior = .{ - .workspace = true, - }, + .behavior = Dependency.Behavior.workspace, }; resolutions_buf[0] = entry1.new_package_id; @@ -764,10 +706,10 @@ pub fn migrateNPMLockfile( const version_bytes = prop.value.?.asString(this.allocator) orelse return error.InvalidNPMLockfile; const name_hash = stringHash(name_bytes); - const dep_name = builder.appendWithHash(String, name_bytes, name_hash); + const dep_name = try string_buf.appendWithHash(name_bytes, name_hash); - const dep_version = builder.append(String, version_bytes); - const sliced = dep_version.sliced(this.buffers.string_bytes.items); + const dep_version = try string_buf.append(version_bytes); + const sliced = dep_version.sliced(string_buf.bytes.items); debug("parsing {s}, {s}\n", .{ name_bytes, version_bytes }); const version = Dependency.parse( @@ -824,7 +766,7 @@ pub fn migrateNPMLockfile( .name_hash = name_hash, .version = version, .behavior = .{ - .normal = dep_key == .dependencies, + .prod = dep_key == .dependencies, .optional = dep_key == .optionalDependencies, .dev = dep_key == .devDependencies, .peer = dep_key == .peerDependencies, @@ -841,11 +783,33 @@ pub fn migrateNPMLockfile( if (resolutions[id].tag == .uninitialized) { debug("resolving '{s}'", .{name_bytes}); + var res_version = version; + const res = resolved: { const dep_pkg = packages_properties.at(found.old_json_index).value.?.data.e_object; const dep_resolved: string = dep_resolved: { if (dep_pkg.get("resolved")) |resolved| { - break :dep_resolved resolved.asString(this.allocator) orelse return error.InvalidNPMLockfile; + const dep_resolved = resolved.asString(this.allocator) orelse return error.InvalidNPMLockfile; + switch (Dependency.Version.Tag.infer(dep_resolved)) { + .git, .github => |tag| { + const dep_resolved_str = try string_buf.append(dep_resolved); + const dep_resolved_sliced = dep_resolved_str.sliced(string_buf.bytes.items); + res_version = Dependency.parseWithTag( + this.allocator, + dep_name, + name_hash, + dep_resolved_sliced.slice, + tag, + &dep_resolved_sliced, + log, + manager, + ) orelse return error.InvalidNPMLockfile; + + break :dep_resolved dep_resolved; + }, + // TODO(dylan-conway): might need to handle more cases + else => break :dep_resolved dep_resolved, + } } if (version.tag == .npm) { @@ -855,14 +819,11 @@ pub fn migrateNPMLockfile( } break :resolved Resolution.init(.{ - .folder = builder.append( - String, - packages_properties.at(found.old_json_index).key.?.asString(allocator).?, - ), + .folder = try string_buf.append(packages_properties.at(found.old_json_index).key.?.asString(allocator).?), }); }; - break :resolved switch (version.tag) { + break :resolved switch (res_version.tag) { .uninitialized => std.debug.panic("Version string {s} resolved to `.uninitialized`", .{version_bytes}), .npm, .dist_tag => res: { // It is theoretically possible to hit this in a case where the resolved dependency is NOT @@ -875,25 +836,25 @@ pub fn migrateNPMLockfile( const dep_actual_version = (dep_pkg.get("version") orelse return error.InvalidNPMLockfile) .asString(this.allocator) orelse return error.InvalidNPMLockfile; - const dep_actual_version_str = builder.append(String, dep_actual_version); - const dep_actual_version_sliced = dep_actual_version_str.sliced(this.buffers.string_bytes.items); + const dep_actual_version_str = try string_buf.append(dep_actual_version); + const dep_actual_version_sliced = dep_actual_version_str.sliced(string_buf.bytes.items); break :res Resolution.init(.{ .npm = .{ - .url = builder.append(String, dep_resolved), + .url = try string_buf.append(dep_resolved), .version = Semver.Version.parse(dep_actual_version_sliced).version.min(), }, }); }, .tarball => if (strings.hasPrefixComptime(dep_resolved, "file:")) - Resolution.init(.{ .local_tarball = builder.append(String, dep_resolved[5..]) }) + Resolution.init(.{ .local_tarball = try string_buf.append(dep_resolved[5..]) }) else - Resolution.init(.{ .remote_tarball = builder.append(String, dep_resolved) }), - .folder => Resolution.init(.{ .folder = builder.append(String, dep_resolved) }), + Resolution.init(.{ .remote_tarball = try string_buf.append(dep_resolved) }), + .folder => Resolution.init(.{ .folder = try string_buf.append(dep_resolved) }), // not sure if this is possible to hit - .symlink => Resolution.init(.{ .folder = builder.append(String, dep_resolved) }), + .symlink => Resolution.init(.{ .folder = try string_buf.append(dep_resolved) }), .workspace => workspace: { - var input = builder.append(String, dep_resolved).sliced(this.buffers.string_bytes.items); + var input = (try string_buf.append(dep_resolved)).sliced(string_buf.bytes.items); if (strings.hasPrefixComptime(input.slice, "workspace:")) { input = input.sub(input.slice["workspace:".len..]); } @@ -903,17 +864,17 @@ pub fn migrateNPMLockfile( }, .git => res: { const str = (if (strings.hasPrefixComptime(dep_resolved, "git+")) - builder.append(String, dep_resolved[4..]) + try string_buf.append(dep_resolved[4..]) else - builder.append(String, dep_resolved)) - .sliced(this.buffers.string_bytes.items); + try string_buf.append(dep_resolved)) + .sliced(string_buf.bytes.items); const hash_index = strings.lastIndexOfChar(str.slice, '#') orelse return error.InvalidNPMLockfile; const commit = str.sub(str.slice[hash_index + 1 ..]).value(); break :res Resolution.init(.{ .git = .{ - .owner = version.value.git.owner, + .owner = res_version.value.git.owner, .repo = str.sub(str.slice[0..hash_index]).value(), .committish = commit, .resolved = commit, @@ -923,17 +884,17 @@ pub fn migrateNPMLockfile( }, .github => res: { const str = (if (strings.hasPrefixComptime(dep_resolved, "git+")) - builder.append(String, dep_resolved[4..]) + try string_buf.append(dep_resolved[4..]) else - builder.append(String, dep_resolved)) - .sliced(this.buffers.string_bytes.items); + try string_buf.append(dep_resolved)) + .sliced(string_buf.bytes.items); const hash_index = strings.lastIndexOfChar(str.slice, '#') orelse return error.InvalidNPMLockfile; const commit = str.sub(str.slice[hash_index + 1 ..]).value(); break :res Resolution.init(.{ .git = .{ - .owner = version.value.github.owner, + .owner = res_version.value.github.owner, .repo = str.sub(str.slice[0..hash_index]).value(), .committish = commit, .resolved = commit, @@ -943,7 +904,7 @@ pub fn migrateNPMLockfile( }, }; }; - debug("-> {}", .{res.fmtForDebug(this.buffers.string_bytes.items)}); + debug("-> {}", .{res.fmtForDebug(string_buf.bytes.items)}); resolutions[id] = res; metas[id].origin = switch (res.tag) { @@ -982,7 +943,7 @@ pub fn migrateNPMLockfile( .name_hash = name_hash, .version = version, .behavior = .{ - .normal = dep_key == .dependencies, + .prod = dep_key == .dependencies, .optional = true, .dev = dep_key == .devDependencies, .peer = dep_key == .peerDependencies, @@ -1056,16 +1017,7 @@ pub fn migrateNPMLockfile( return error.NotAllPackagesGotResolved; } - // if (Environment.isDebug) { - // const dump_file = try std.fs.cwd().createFileZ("before-clean.json", .{}); - // defer dump_file.close(); - // try std.json.stringify(this, .{ .whitespace = .indent_2 }, dump_file.writer()); - // } - - // This is definitely a memory leak, but it's fine because there is no install api, so this can only be leaked once per process. - // This operation is neccecary because callers of `loadFromCwd` assume the data is written into the passed `this`. - // You'll find that not cleaning the lockfile will cause `bun install` to not actually install anything since it doesnt have any hoisted trees. - this.* = (try this.cleanWithLogger(manager, &.{}, log, false, .silent)).*; + try this.hoist(log, .resolvable, {}); // if (Environment.isDebug) { // const dump_file = try std.fs.cwd().createFileZ("after-clean.json", .{}); @@ -1085,7 +1037,7 @@ pub fn migrateNPMLockfile( .was_migrated = true, .loaded_from_binary_lockfile = false, .serializer_result = .{}, - .format = .text, + .format = .binary, }, }; } diff --git a/src/install/npm.zig b/src/install/npm.zig index 3eeb230c78..e0801946d1 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -9,6 +9,8 @@ const string = @import("../string_types.zig").string; const strings = @import("../string_immutable.zig"); const PackageManager = @import("./install.zig").PackageManager; const ExternalStringMap = @import("./install.zig").ExternalStringMap; +const ExternalPackageNameHashList = bun.install.ExternalPackageNameHashList; +const PackageNameHash = bun.install.PackageNameHash; const ExternalStringList = @import("./install.zig").ExternalStringList; const ExternalSlice = @import("./install.zig").ExternalSlice; const initializeStore = @import("./install.zig").initializeMiniStore; @@ -857,8 +859,6 @@ pub const Architecture = enum(u16) { } }; -const BigExternalString = Semver.BigExternalString; - pub const PackageVersion = extern struct { /// `"integrity"` field || `"shasum"` field /// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#dist @@ -880,6 +880,8 @@ pub const PackageVersion = extern struct { /// We keep it in the data layout so that if it turns out we do need it, we can add it without invalidating everyone's history. dev_dependencies: ExternalStringMap = ExternalStringMap{}, + bundled_dependencies: ExternalPackageNameHashList = .{}, + /// `"bin"` field in [package.json](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bin) bin: Bin = Bin{}, @@ -909,10 +911,14 @@ pub const PackageVersion = extern struct { /// `hasInstallScript` field in registry API. has_install_script: bool = false, + + pub fn allDependenciesBundled(this: *const PackageVersion) bool { + return this.bundled_dependencies.isInvalid(); + } }; comptime { - if (@sizeOf(Npm.PackageVersion) != 224) { + if (@sizeOf(Npm.PackageVersion) != 232) { @compileError(std.fmt.comptimePrint("Npm.PackageVersion has unexpected size {d}", .{@sizeOf(Npm.PackageVersion)})); } } @@ -934,7 +940,6 @@ pub const NpmPackage = extern struct { versions_buf: VersionSlice = VersionSlice{}, string_lists_buf: ExternalStringList = ExternalStringList{}, - string_buf: BigExternalString = BigExternalString{}, }; pub const PackageManifest = struct { @@ -947,6 +952,7 @@ pub const PackageManifest = struct { external_strings_for_versions: []const ExternalString = &[_]ExternalString{}, package_versions: []const PackageVersion = &[_]PackageVersion{}, extern_strings_bin_entries: []const ExternalString = &[_]ExternalString{}, + bundled_deps_buf: []const PackageNameHash = &.{}, pub inline fn name(this: *const PackageManifest) string { return this.pkg.name.slice(this.string_buf); @@ -961,9 +967,10 @@ pub const PackageManifest = struct { } pub const Serializer = struct { - // - version 3: added serialization of registry url. it's used to invalidate when it changes - // - version 4: fixed bug with cpu & os tag not being added correctly - pub const version = "bun-npm-manifest-cache-v0.0.4\n"; + // - v0.0.3: added serialization of registry url. it's used to invalidate when it changes + // - v0.0.4: fixed bug with cpu & os tag not being added correctly + // - v0.0.5: added bundled dependencies + pub const version = "bun-npm-manifest-cache-v0.0.5\n"; const header_bytes: string = "#!/usr/bin/env bun\n" ++ version; pub const sizes = blk: { @@ -1581,6 +1588,12 @@ pub const PackageManifest = struct { var optional_peer_dep_names = std.ArrayList(u64).init(default_allocator); defer optional_peer_dep_names.deinit(); + var bundled_deps_set = bun.StringSet.init(allocator); + defer bundled_deps_set.deinit(); + var bundle_all_deps = false; + + var bundled_deps_count: usize = 0; + var string_builder = String.Builder{ .string_pool = string_pool, }; @@ -1693,6 +1706,22 @@ pub const PackageManifest = struct { } } + bundled_deps_set.map.clearRetainingCapacity(); + bundle_all_deps = false; + if (prop.value.?.get("bundleDependencies") orelse prop.value.?.get("bundledDependencies")) |bundled_deps_expr| { + switch (bundled_deps_expr.data) { + .e_boolean => |boolean| { + bundle_all_deps = boolean.value; + }, + .e_array => |arr| { + for (arr.slice()) |bundled_dep| { + try bundled_deps_set.insert(bundled_dep.asString(allocator) orelse continue); + } + }, + else => {}, + } + } + inline for (dependency_groups) |pair| { if (prop.value.?.asProperty(pair.prop)) |versioned_deps| { if (versioned_deps.expr.data == .e_object) { @@ -1700,6 +1729,11 @@ pub const PackageManifest = struct { const properties = versioned_deps.expr.data.e_object.properties.slice(); for (properties) |property| { if (property.key.?.asString(allocator)) |key| { + if (!bundle_all_deps and bundled_deps_set.swapRemove(key)) { + // swap remove the dependency name because it could exist in + // multiple behavior groups. + bundled_deps_count += 1; + } string_builder.count(key); string_builder.count(property.value.?.asString(allocator) orelse ""); } @@ -1745,6 +1779,8 @@ pub const PackageManifest = struct { var all_extern_strings_bin_entries = extern_strings_bin_entries; var all_tarball_url_strings = try allocator.alloc(ExternalString, tarball_urls_count); var tarball_url_strings = all_tarball_url_strings; + const bundled_deps_buf = try allocator.alloc(PackageNameHash, bundled_deps_count); + var bundled_deps_offset: usize = 0; if (versioned_packages.len > 0) { const versioned_packages_bytes = std.mem.sliceAsBytes(versioned_packages); @@ -1829,6 +1865,22 @@ pub const PackageManifest = struct { } if (!parsed_version.valid) continue; + bundled_deps_set.map.clearRetainingCapacity(); + bundle_all_deps = false; + if (prop.value.?.get("bundleDependencies") orelse prop.value.?.get("bundledDependencies")) |bundled_deps_expr| { + switch (bundled_deps_expr.data) { + .e_boolean => |boolean| { + bundle_all_deps = boolean.value; + }, + .e_array => |arr| { + for (arr.slice()) |bundled_dep| { + try bundled_deps_set.insert(bundled_dep.asString(allocator) orelse continue); + } + }, + else => {}, + } + } + var package_version: PackageVersion = empty_version; if (prop.value.?.asProperty("cpu")) |cpu_q| { @@ -2042,6 +2094,8 @@ pub const PackageManifest = struct { } } + const bundled_deps_begin = bundled_deps_offset; + var i: usize = 0; for (items) |item| { @@ -2051,6 +2105,11 @@ pub const PackageManifest = struct { this_names[i] = string_builder.append(ExternalString, name_str); this_versions[i] = string_builder.append(ExternalString, version_str); + if (!bundle_all_deps and bundled_deps_set.swapRemove(name_str)) { + bundled_deps_buf[bundled_deps_offset] = this_names[i].hash; + bundled_deps_offset += 1; + } + if (comptime is_peer) { if (std.mem.indexOfScalar(u64, optional_peer_dep_names.items, this_names[i].hash) != null) { // For optional peer dependencies, we store a length instead of a whole separate array @@ -2087,6 +2146,15 @@ pub const PackageManifest = struct { count = i; + if (bundle_all_deps) { + package_version.bundled_dependencies = ExternalPackageNameHashList.invalid; + } else { + package_version.bundled_dependencies = ExternalPackageNameHashList.init( + bundled_deps_buf, + bundled_deps_buf[bundled_deps_begin..bundled_deps_offset], + ); + } + var name_list = ExternalStringList.init(all_extern_strings, this_names); var version_list = ExternalStringList.init(version_extern_strings, this_versions); @@ -2368,15 +2436,11 @@ pub const PackageManifest = struct { result.external_strings_for_versions = version_extern_strings; result.package_versions = versioned_packages; result.extern_strings_bin_entries = all_extern_strings_bin_entries[0 .. all_extern_strings_bin_entries.len - extern_strings_bin_entries.len]; + result.bundled_deps_buf = bundled_deps_buf; result.pkg.public_max_age = public_max_age; if (string_builder.ptr) |ptr| { result.string_buf = ptr[0..string_builder.len]; - result.pkg.string_buf = BigExternalString{ - .off = 0, - .len = @as(u32, @truncate(string_builder.len)), - .hash = 0, - }; } return result; diff --git a/src/install/patch_install.zig b/src/install/patch_install.zig index cb8b932aa1..842ca4a01f 100644 --- a/src/install/patch_install.zig +++ b/src/install/patch_install.zig @@ -211,12 +211,13 @@ pub const PatchTask = struct { }, .extract => { debug("pkg: {s} extract", .{pkg.name.slice(manager.lockfile.buffers.string_bytes.items)}); + + const task_id = Task.Id.forNPMPackage(manager.lockfile.str(&pkg.name), pkg.resolution.value.npm.version); + bun.debugAssert(!manager.network_dedupe_map.contains(task_id)); + const network_task = try manager.generateNetworkTaskForTarball( // TODO: not just npm package - Task.Id.forNPMPackage( - manager.lockfile.str(&pkg.name), - pkg.resolution.value.npm.version, - ), + task_id, url, manager.lockfile.buffers.dependencies.items[dep_id].behavior.isRequired(), dep_id, diff --git a/src/install/resolvers/folder_resolver.zig b/src/install/resolvers/folder_resolver.zig index 30ff41f9c3..dceff73f4b 100644 --- a/src/install/resolvers/folder_resolver.zig +++ b/src/install/resolvers/folder_resolver.zig @@ -77,6 +77,10 @@ pub const FolderResolution = union(Tag) { pub fn count(this: @This(), comptime Builder: type, builder: Builder, _: JSAst.Expr) void { builder.count(this.folder_path); } + + pub fn checkBundledDependencies() bool { + return tag == .folder or tag == .symlink; + } }; } @@ -99,6 +103,10 @@ pub const FolderResolution = union(Tag) { } pub fn count(_: @This(), comptime Builder: type, _: Builder, _: JSAst.Expr) void {} + + pub fn checkBundledDependencies() bool { + return true; + } }; const Paths = struct { @@ -165,7 +173,7 @@ pub const FolderResolution = union(Tag) { version: Dependency.Version, comptime features: Features, comptime ResolverType: type, - resolver: ResolverType, + resolver: *ResolverType, ) !Lockfile.Package { var body = Npm.Registry.BodyPool.get(manager.allocator); defer Npm.Registry.BodyPool.release(body); @@ -200,7 +208,7 @@ pub const FolderResolution = union(Tag) { body.data.reset(); var man = body.data.list.toManaged(manager.allocator); defer body.data.list = man.moveToUnmanaged(); - _ = try file.readToEndWithArrayList(&man).unwrap(); + _ = try file.readToEndWithArrayList(&man, true).unwrap(); } break :brk logger.Source.initPathString(abs, body.data.list.items); @@ -262,45 +270,63 @@ pub const FolderResolution = union(Tag) { if (entry.found_existing) return entry.value_ptr.*; const package: Lockfile.Package = switch (global_or_relative) { - .global => brk: { + .global => global: { var path: bun.PathBuffer = undefined; std.mem.copyForwards(u8, &path, non_normalized_path); - break :brk readPackageJSONFromDisk( + var resolver: SymlinkResolver = .{ + .folder_path = path[0..non_normalized_path.len], + }; + break :global readPackageJSONFromDisk( manager, abs, version, Features.link, SymlinkResolver, - SymlinkResolver{ .folder_path = path[0..non_normalized_path.len] }, + &resolver, ); }, .relative => |tag| switch (tag) { - .folder => readPackageJSONFromDisk( - manager, - abs, - version, - Features.folder, - Resolver, - Resolver{ .folder_path = rel }, - ), - .workspace => readPackageJSONFromDisk( - manager, - abs, - version, - Features.workspace, - WorkspaceResolver, - WorkspaceResolver{ .folder_path = rel }, - ), + .folder => folder: { + var resolver: Resolver = .{ + .folder_path = rel, + }; + break :folder readPackageJSONFromDisk( + manager, + abs, + version, + Features.folder, + Resolver, + &resolver, + ); + }, + .workspace => workspace: { + var resolver: WorkspaceResolver = .{ + .folder_path = rel, + }; + break :workspace readPackageJSONFromDisk( + manager, + abs, + version, + Features.workspace, + WorkspaceResolver, + &resolver, + ); + }, else => unreachable, }, - .cache_folder => readPackageJSONFromDisk( - manager, - abs, - version, - Features.npm, - CacheFolderResolver, - CacheFolderResolver{ .version = version.value.npm.version.toVersion() }, - ), + .cache_folder => cache_folder: { + var resolver: CacheFolderResolver = .{ + .version = version.value.npm.version.toVersion(), + }; + break :cache_folder readPackageJSONFromDisk( + manager, + abs, + version, + Features.npm, + CacheFolderResolver, + &resolver, + ); + }, } catch |err| { if (err == error.FileNotFound or err == error.ENOENT) { entry.value_ptr.* = .{ .err = error.MissingPackageJSON }; diff --git a/src/install/semver.zig b/src/install/semver.zig index 84c7fdba0d..a3f9ca2efd 100644 --- a/src/install/semver.zig +++ b/src/install/semver.zig @@ -39,21 +39,18 @@ pub const String = extern struct { } pub const Buf = struct { - bytes: std.ArrayList(u8), - pool: Builder.StringPool, + bytes: *std.ArrayListUnmanaged(u8), + allocator: std.mem.Allocator, + pool: *Builder.StringPool, - pub fn init(allocator: std.mem.Allocator) Buf { + pub fn init(lockfile: *const Lockfile) Buf { return .{ - .bytes = std.ArrayList(u8).init(allocator), - .pool = Builder.StringPool.init(allocator), + .bytes = &lockfile.buffers.string_bytes, + .allocator = lockfile.allocator, + .pool = &lockfile.string_pool, }; } - pub fn apply(this: *Buf, lockfile: *Lockfile) void { - lockfile.buffers.string_bytes = this.bytes.moveToUnmanaged(); - lockfile.string_pool = this.pool; - } - pub fn append(this: *Buf, str: string) OOM!String { if (canInline(str)) { return String.initInline(str); @@ -66,7 +63,7 @@ pub const String = extern struct { } // new entry - const new = try String.initAppend(&this.bytes, str); + const new = try String.initAppend(this.allocator, this.bytes, str); entry.value_ptr.* = new; return new; } @@ -82,7 +79,7 @@ pub const String = extern struct { } // new entry - const new = try String.initAppend(&this.bytes, str); + const new = try String.initAppend(this.allocator, this.bytes, str); entry.value_ptr.* = new; return new; } @@ -105,7 +102,7 @@ pub const String = extern struct { }; } - const new = try String.initAppend(&this.bytes, str); + const new = try String.initAppend(this.allocator, this.bytes, str); entry.value_ptr.* = new; return .{ .value = new, @@ -129,7 +126,7 @@ pub const String = extern struct { }; } - const new = try String.initAppend(&this.bytes, str); + const new = try String.initAppend(this.allocator, this.bytes, str); entry.value_ptr.* = new; return .{ .value = new, @@ -160,6 +157,29 @@ pub const String = extern struct { } }; + /// Escapes for json. Expects string to be prequoted + pub inline fn fmtJson(self: *const String, buf: []const u8, opts: JsonFormatter.Options) JsonFormatter { + return .{ + .buf = buf, + .str = self, + .opts = opts, + }; + } + + pub const JsonFormatter = struct { + str: *const String, + buf: string, + opts: Options, + + pub const Options = struct { + quote: bool = true, + }; + + pub fn format(formatter: JsonFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + try writer.print("{}", .{bun.fmt.formatJSONStringUTF8(formatter.str.slice(formatter.buf), .{ .quote = formatter.opts.quote })}); + } + }; + pub fn Sorter(comptime direction: enum { asc, desc }) type { return struct { lhs_buf: []const u8, @@ -309,7 +329,8 @@ pub const String = extern struct { } pub fn initAppendIfNeeded( - buf: *std.ArrayList(u8), + allocator: std.mem.Allocator, + buf: *std.ArrayListUnmanaged(u8), in: string, ) OOM!String { return switch (in.len) { @@ -327,19 +348,20 @@ pub const String = extern struct { // This should only happen for non-ascii strings that are exactly 8 bytes. // so that's an edge-case if ((in[max_inline_len - 1]) >= 128) - try initAppend(buf, in) + try initAppend(allocator, buf, in) else .{ .bytes = .{ in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7] } }, - else => try initAppend(buf, in), + else => try initAppend(allocator, buf, in), }; } pub fn initAppend( - buf: *std.ArrayList(u8), + allocator: std.mem.Allocator, + buf: *std.ArrayListUnmanaged(u8), in: string, ) OOM!String { - try buf.appendSlice(in); + try buf.appendSlice(allocator, in); const in_buf = buf.items[buf.items.len - in.len ..]; return @bitCast((@as(u64, 0) | @as(u64, @as(max_addressable_space, @truncate(@as(u64, @bitCast(Pointer.init(buf.items, in_buf))))))) | 1 << 63); } @@ -682,34 +704,6 @@ pub const ExternalString = extern struct { } }; -pub const BigExternalString = extern struct { - off: u32 = 0, - len: u32 = 0, - hash: u64 = 0, - - pub fn from(in: string) BigExternalString { - return BigExternalString{ - .off = 0, - .len = @as(u32, @truncate(in.len)), - .hash = bun.Wyhash.hash(0, in), - }; - } - - pub inline fn init(buf: string, in: string, hash: u64) BigExternalString { - assert(@intFromPtr(buf.ptr) <= @intFromPtr(in.ptr) and ((@intFromPtr(in.ptr) + in.len) <= (@intFromPtr(buf.ptr) + buf.len))); - - return BigExternalString{ - .off = @as(u32, @truncate(@intFromPtr(in.ptr) - @intFromPtr(buf.ptr))), - .len = @as(u32, @truncate(in.len)), - .hash = hash, - }; - } - - pub fn slice(this: BigExternalString, buf: string) string { - return buf[this.off..][0..this.len]; - } -}; - pub const SlicedString = struct { buf: string, slice: string, @@ -749,14 +743,12 @@ pub const SlicedString = struct { } }; -const RawType = void; pub const Version = extern struct { major: u32 = 0, minor: u32 = 0, patch: u32 = 0, _tag_padding: [4]u8 = .{0} ** 4, // [see padding_checker.zig] tag: Tag = .{}, - // raw: RawType = RawType{}, /// Assumes that there is only one buffer for all the strings pub fn sortGt(ctx: []const u8, lhs: Version, rhs: Version) bool { @@ -1443,9 +1435,7 @@ pub const Version = extern struct { .none => {}, .pre => { result.tag.pre = sliced_string.sub(input[start..i]).external(); - if (comptime Environment.isDebug) { - assert(!strings.containsChar(result.tag.pre.slice(sliced_string.buf), '-')); - } + state = State.none; }, .build => { @@ -1697,10 +1687,6 @@ pub const Version = extern struct { result.len = @as(u32, @intCast(i)); - if (comptime RawType != void) { - result.version.raw = sliced_string.sub(input[0..i]).external(); - } - return result; } @@ -2221,7 +2207,7 @@ pub const Query = struct { }; } - pub const FlagsBitSet = std.bit_set.IntegerBitSet(3); + pub const FlagsBitSet = bun.bit_set.IntegerBitSet(3); pub fn isExact(this: *const Group) bool { return this.head.next == null and this.head.head.next == null and !this.head.head.range.hasRight() and this.head.head.range.left.op == .eql; diff --git a/src/io/PipeReader.zig b/src/io/PipeReader.zig index b7b8ebfbdc..668f7b55fe 100644 --- a/src/io/PipeReader.zig +++ b/src/io/PipeReader.zig @@ -704,6 +704,10 @@ const PosixBufferedReader = struct { return this.flags.is_done or this.flags.received_eof or this.flags.closed_without_reporting; } + pub fn memoryCost(this: *const PosixBufferedReader) usize { + return @sizeOf(@This()) + this._buffer.capacity; + } + pub fn from(to: *@This(), other: *PosixBufferedReader, parent_: *anyopaque) void { to.* = .{ .handle = other.handle, @@ -806,7 +810,7 @@ const PosixBufferedReader = struct { pub fn finalBuffer(this: *PosixBufferedReader) *std.ArrayList(u8) { if (this.flags.memfd and this.handle == .fd) { defer this.handle.close(null, {}); - _ = bun.sys.File.readToEndWithArrayList(.{ .handle = this.handle.fd }, this.buffer()).unwrap() catch |err| { + _ = bun.sys.File.readToEndWithArrayList(.{ .handle = this.handle.fd }, this.buffer(), false).unwrap() catch |err| { bun.Output.debugWarn("error reading from memfd\n{}", .{err}); return this.buffer(); }; @@ -972,6 +976,10 @@ pub const WindowsBufferedReader = struct { const WindowsOutputReader = @This(); + pub fn memoryCost(this: *const WindowsOutputReader) usize { + return @sizeOf(@This()) + this._buffer.capacity; + } + const Flags = packed struct { is_done: bool = false, pollable: bool = false, diff --git a/src/io/PipeWriter.zig b/src/io/PipeWriter.zig index fd41ae637c..622fda1527 100644 --- a/src/io/PipeWriter.zig +++ b/src/io/PipeWriter.zig @@ -199,6 +199,10 @@ pub fn PosixBufferedWriter( closed_without_reporting: bool = false, close_fd: bool = true, + pub fn memoryCost(_: *const @This()) usize { + return @sizeOf(@This()); + } + const PosixWriter = @This(); pub const auto_poll = if (@hasDecl(Parent, "auto_poll")) Parent.auto_poll else true; @@ -393,6 +397,10 @@ pub fn PosixStreamingWriter( // TODO: chunk_size: usize = 0, + pub fn memoryCost(this: *const @This()) usize { + return @sizeOf(@This()) + this.buffer.capacity; + } + pub fn getPoll(this: *@This()) ?*Async.FilePoll { return this.handle.getPoll(); } @@ -909,6 +917,10 @@ pub fn WindowsBufferedWriter( } } + pub fn memoryCost(this: *const WindowsWriter) usize { + return @sizeOf(@This()) + this.write_buffer.len; + } + pub fn startWithCurrentPipe(this: *WindowsWriter) bun.JSC.Maybe(void) { bun.assert(this.source != null); this.is_done = false; @@ -1025,6 +1037,10 @@ pub const StreamBuffer = struct { this.list.clearRetainingCapacity(); } + pub fn memoryCost(this: *const StreamBuffer) usize { + return this.list.capacity; + } + pub fn size(this: *const StreamBuffer) usize { return this.list.items.len - this.cursor; } @@ -1153,6 +1169,10 @@ pub fn WindowsStreamingWriter( pub usingnamespace BaseWindowsPipeWriter(WindowsWriter, Parent); + pub fn memoryCost(this: *const WindowsWriter) usize { + return @sizeOf(@This()) + this.current_payload.memoryCost() + this.outgoing.memoryCost(); + } + fn onCloseSource(this: *WindowsWriter) void { this.source = null; if (this.closed_without_reporting) { diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 0c04b13631..b3cb19b816 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -255,6 +255,7 @@ using namespace JSC; macro(writing) \ macro(written) \ macro(napiDlopenHandle) \ + macro(napiWrappedContents) \ BUN_ADDITIONAL_BUILTIN_NAMES(macro) // --- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME --- diff --git a/src/js/builtins/ProcessObjectInternals.ts b/src/js/builtins/ProcessObjectInternals.ts index c2a0a879fc..d8518c23f0 100644 --- a/src/js/builtins/ProcessObjectInternals.ts +++ b/src/js/builtins/ProcessObjectInternals.ts @@ -117,7 +117,7 @@ export function getStdinStream(fd) { let stream_destroyed = false; let stream_endEmitted = false; - stream.on = function (event, listener) { + stream.addListener = stream.on = function (event, listener) { // Streams don't generally required to present any data when only // `readable` events are present, i.e. `readableFlowing === false` // @@ -334,8 +334,13 @@ export function setMainModule(value) { } type InternalEnvMap = Record; +type EditWindowsEnvVarCb = (key: string, value: null | string) => void; -export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array) { +export function windowsEnv( + internalEnv: InternalEnvMap, + envMapList: Array, + editWindowsEnvVar: EditWindowsEnvVarCb, +) { // The use of String(key) here is intentional because Node.js as of v21.5.0 will throw // on symbol keys as it seems they assume the user uses string keys: // @@ -364,7 +369,10 @@ export function windowsEnv(internalEnv: InternalEnvMap, envMapList: Array { @@ -182,7 +191,7 @@ class Query extends PublicPromise { Object.defineProperty(Query, Symbol.species, { value: PublicPromise }); Object.defineProperty(Query, Symbol.toStringTag, { value: "Query" }); init( - function (query, result, commandTag, count) { + function onResolvePostgresQuery(query, result, commandTag, count, queries) { $assert(result instanceof SQLResultArray, "Invalid result array"); if (typeof commandTag === "string") { if (commandTag.length > 0) { @@ -194,18 +203,48 @@ init( result.count = count || 0; + if (queries) { + const queriesIndex = queries.indexOf(query); + if (queriesIndex !== -1) { + queries.splice(queriesIndex, 1); + } + } + try { query.resolve(result); } catch (e) {} }, - function (query, reject) { + function onRejectPostgresQuery(query, reject, queries) { + if (queries) { + const queriesIndex = queries.indexOf(query); + if (queriesIndex !== -1) { + queries.splice(queriesIndex, 1); + } + } + try { query.reject(reject); } catch (e) {} }, ); -function createConnection({ hostname, port, username, password, tls, query, database, sslMode }, onConnected, onClose) { +function createConnection( + { + hostname, + port, + username, + password, + tls, + query, + database, + sslMode, + idleTimeout = 0, + connectionTimeout = 30 * 1000, + maxLifetime = 0, + }, + onConnected, + onClose, +) { return _createConnection( hostname, Number(port), @@ -221,6 +260,9 @@ function createConnection({ hostname, port, username, password, tls, query, data query || "", onConnected, onClose, + idleTimeout, + connectionTimeout, + maxLifetime, ); } @@ -312,7 +354,20 @@ class SQLArrayParameter { } function loadOptions(o) { - var hostname, port, username, password, database, tls, url, query, adapter; + var hostname, + port, + username, + password, + database, + tls, + url, + query, + adapter, + idleTimeout, + connectionTimeout, + maxLifetime, + onconnect, + onclose; const env = Bun.env; var sslMode: SSLMode = SSLMode.disable; @@ -375,6 +430,48 @@ function loadOptions(o) { tls ||= o.tls || o.ssl; adapter ||= o.adapter || "postgres"; + idleTimeout ??= o.idleTimeout; + idleTimeout ??= o.idle_timeout; + connectionTimeout ??= o.connectionTimeout; + connectionTimeout ??= o.connection_timeout; + maxLifetime ??= o.maxLifetime; + maxLifetime ??= o.max_lifetime; + + onconnect ??= o.onconnect; + onclose ??= o.onclose; + if (onconnect !== undefined) { + if (!$isCallable(onconnect)) { + throw $ERR_INVALID_ARG_TYPE("onconnect", "function", onconnect); + } + } + + if (onclose !== undefined) { + if (!$isCallable(onclose)) { + throw $ERR_INVALID_ARG_TYPE("onclose", "function", onclose); + } + } + + if (idleTimeout != null) { + idleTimeout = Number(idleTimeout); + if (idleTimeout > 2 ** 31 || idleTimeout < 0 || idleTimeout !== idleTimeout) { + throw $ERR_INVALID_ARG_VALUE("idle_timeout must be a non-negative integer less than 2^31"); + } + } + + if (connectionTimeout != null) { + connectionTimeout = Number(connectionTimeout); + if (connectionTimeout > 2 ** 31 || connectionTimeout < 0 || connectionTimeout !== connectionTimeout) { + throw $ERR_INVALID_ARG_VALUE("connection_timeout must be a non-negative integer less than 2^31"); + } + } + + if (maxLifetime != null) { + maxLifetime = Number(maxLifetime); + if (maxLifetime > 2 ** 31 || maxLifetime < 0 || maxLifetime !== maxLifetime) { + throw $ERR_INVALID_ARG_VALUE("max_lifetime must be a non-negative integer less than 2^31"); + } + } + if (sslMode !== SSLMode.disable && !tls?.serverName) { if (hostname) { tls = { @@ -398,7 +495,23 @@ function loadOptions(o) { throw new Error(`Unsupported adapter: ${adapter}. Only \"postgres\" is supported for now`); } - return { hostname, port, username, password, database, tls, query, sslMode }; + const ret: any = { hostname, port, username, password, database, tls, query, sslMode }; + if (idleTimeout != null) { + ret.idleTimeout = idleTimeout; + } + if (connectionTimeout != null) { + ret.connectionTimeout = connectionTimeout; + } + if (maxLifetime != null) { + ret.maxLifetime = maxLifetime; + } + if (onconnect !== undefined) { + ret.onconnect = onconnect; + } + if (onclose !== undefined) { + ret.onclose = onclose; + } + return ret; } function SQL(o) { @@ -407,6 +520,7 @@ function SQL(o) { connecting = false, closed = false, onConnect: any[] = [], + storedErrorForClosedConnection, connectionInfo = loadOptions(o); function connectedHandler(query, handle, err) { @@ -415,7 +529,7 @@ function SQL(o) { } if (!connected) { - return query.reject(new Error("Not connected")); + return query.reject(storedErrorForClosedConnection || new Error("Not connected")); } if (query.cancelled) { @@ -423,6 +537,10 @@ function SQL(o) { } handle.run(connection, query); + + // if the above throws, we don't want it to be in the array. + // This array exists mostly to keep the in-flight queries alive. + connection.queries.push(query); } function pendingConnectionHandler(query, handle) { @@ -434,7 +552,7 @@ function SQL(o) { } function closedConnectionHandler(query, handle) { - query.reject(new Error("Connection closed")); + query.reject(storedErrorForClosedConnection || new Error("Connection closed")); } function onConnected(err, result) { @@ -443,11 +561,31 @@ function SQL(o) { handler(err); } onConnect = []; + + if (connected && connectionInfo?.onconnect) { + connectionInfo.onconnect(err); + } } - function onClose(err) { + function onClose(err, queries) { closed = true; + storedErrorForClosedConnection = err; + if (sql === lazyDefaultSQL) { + resetDefaultSQL(initialDefaultSQL); + } + onConnected(err, undefined); + if (queries) { + const queriesCopy = queries.slice(); + queries.length = 0; + for (const handler of queriesCopy) { + handler.reject(err); + } + } + + if (connectionInfo?.onclose) { + connectionInfo.onclose(err); + } } function doCreateQuery(strings, values) { @@ -568,18 +706,23 @@ function SQL(o) { } var lazyDefaultSQL; -var defaultSQLObject = function sql(strings, ...values) { + +function resetDefaultSQL(sql) { + lazyDefaultSQL = sql; + Object.assign(defaultSQLObject, lazyDefaultSQL); + exportsObject.default = exportsObject.sql = lazyDefaultSQL; +} + +var initialDefaultSQL; +var defaultSQLObject = (initialDefaultSQL = function sql(strings, ...values) { if (new.target) { return SQL(strings); } - if (!lazyDefaultSQL) { - lazyDefaultSQL = SQL(undefined); - Object.assign(defaultSQLObject, lazyDefaultSQL); - exportsObject.default = exportsObject.sql = lazyDefaultSQL; + resetDefaultSQL(SQL(undefined)); } return lazyDefaultSQL(strings, ...values); -}; +}); var exportsObject = { sql: defaultSQLObject, diff --git a/src/js/internal/errors.ts b/src/js/internal/errors.ts index fd5036ec86..739958bf03 100644 --- a/src/js/internal/errors.ts +++ b/src/js/internal/errors.ts @@ -10,4 +10,5 @@ export default { ERR_BUFFER_TOO_LARGE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_BUFFER_TOO_LARGE", 0), ERR_ZLIB_INITIALIZATION_FAILED: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_ZLIB_INITIALIZATION_FAILED", 0), ERR_BUFFER_OUT_OF_BOUNDS: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_BUFFER_OUT_OF_BOUNDS", 0), + ERR_UNHANDLED_ERROR: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_UNHANDLED_ERROR", 0), }; diff --git a/src/js/internal/util/inspect.js b/src/js/internal/util/inspect.js index bb5f90eac0..70abab2573 100644 --- a/src/js/internal/util/inspect.js +++ b/src/js/internal/util/inspect.js @@ -141,6 +141,21 @@ const kRejected = Symbol("kRejected"); // state ID 2 const ALL_PROPERTIES = 0; const ONLY_ENUMERABLE = 2; +/** + * Fast path for {@link extractedSplitNewLines} for ASCII/Latin1 strings. + * @returns `value` split on newlines (newline included at end), or `undefined` + * if non-ascii UTF8/UTF16. + * + * Passing this a non-string will cause a panic. + * + * @type {(value: string) => string[] | undefined} + */ +const extractedSplitNewLinesFastPathStringsOnly = $newZigFunction( + "node_util_binding.zig", + "extractedSplitNewLinesFastPathStringsOnly", + 1, +); + const isAsyncFunction = v => typeof v === "function" && StringPrototypeStartsWith(FunctionPrototypeToString(v), "async"); const isGeneratorFunction = v => @@ -397,7 +412,7 @@ let strEscapeSequencesRegExp, strEscapeSequencesReplacer, strEscapeSequencesRegExpSingle, strEscapeSequencesReplacerSingle, - extractedSplitNewLines; + extractedSplitNewLinesSlow; try { // Change from regex literals to RegExp constructors to avoid unrecoverable // syntax error at load time. @@ -416,7 +431,7 @@ try { "g", ); const extractedNewLineRe = new RegExp("(?<=\\n)"); - extractedSplitNewLines = value => RegExpPrototypeSymbolSplit(extractedNewLineRe, value); + extractedSplitNewLinesSlow = value => RegExpPrototypeSymbolSplit(extractedNewLineRe, value); // CI doesn't run in an elderly runtime } catch { // These are from a previous version of node, @@ -426,7 +441,7 @@ try { strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c\x7f-\x9f]/g; strEscapeSequencesRegExpSingle = /[\x00-\x1f\x5c\x7f-\x9f]/; strEscapeSequencesReplacerSingle = /[\x00-\x1f\x5c\x7f-\x9f]/g; - extractedSplitNewLines = value => { + extractedSplitNewLinesSlow = value => { const lines = RegExpPrototypeSymbolSplit(/\n/, value); const last = ArrayPrototypePop(lines); const nlLines = ArrayPrototypeMap(lines, line => line + "\n"); @@ -437,6 +452,13 @@ try { }; } +const extractedSplitNewLines = value => { + if (typeof value === "string") { + return extractedSplitNewLinesFastPathStringsOnly(value) || extractedSplitNewLinesSlow(value); + } + return extractedSplitNewLinesSlow(value); +} + const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/; const numberRegExp = /^(0|[1-9][0-9]*)$/; diff --git a/src/js/node/async_hooks.ts b/src/js/node/async_hooks.ts index 5f109ae3ea..840afac3b9 100644 --- a/src/js/node/async_hooks.ts +++ b/src/js/node/async_hooks.ts @@ -23,7 +23,7 @@ // calls to $assert which will verify this invariant (only during bun-debug) // const [setAsyncHooksEnabled, cleanupLater] = $cpp("NodeAsyncHooks.cpp", "createAsyncHooksBinding"); -const { validateFunction, validateString } = require("internal/validators"); +const { validateFunction, validateString, validateObject } = require("internal/validators"); // Only run during debug function assertValidAsyncContextArray(array: unknown): array is ReadonlyArray | undefined { @@ -260,8 +260,22 @@ class AsyncResource { type; #snapshot; - constructor(type, options?) { + constructor(type, opts?) { validateString(type, "type"); + + let triggerAsyncId = opts; + if (opts != null) { + if (typeof opts !== "number") { + triggerAsyncId = opts.triggerAsyncId === undefined ? 1 : opts.triggerAsyncId; + } + if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { + throw $ERR_INVALID_ASYNC_ID(`Invalid triggerAsyncId value: ${triggerAsyncId}`); + } + } + if (hasEnabledCreateHook && type.length === 0) { + throw $ERR_ASYNC_TYPE(`Invalid name for async "type": ${type}`); + } + setAsyncHooksEnabled(true); this.type = type; this.#snapshot = get(); @@ -300,6 +314,7 @@ class AsyncResource { } bind(fn, thisArg) { + validateFunction(fn, "fn"); return this.runInAsyncScope.bind(this, fn, thisArg ?? this); } @@ -354,10 +369,28 @@ const createHookNotImpl = createWarning( true, ); -function createHook(callbacks) { +let hasEnabledCreateHook = false; +function createHook(hook) { + validateObject(hook, "hook"); + const { init, before, after, destroy, promiseResolve } = hook; + if (init !== undefined && typeof init !== "function") throw $ERR_ASYNC_CALLBACK("hook.init must be a function"); + if (before !== undefined && typeof before !== "function") throw $ERR_ASYNC_CALLBACK("hook.before must be a function"); + if (after !== undefined && typeof after !== "function") throw $ERR_ASYNC_CALLBACK("hook.after must be a function"); + if (destroy !== undefined && typeof destroy !== "function") + throw $ERR_ASYNC_CALLBACK("hook.destroy must be a function"); + if (promiseResolve !== undefined && typeof promiseResolve !== "function") + throw $ERR_ASYNC_CALLBACK("hook.promiseResolve must be a function"); + return { - enable: () => createHookNotImpl(callbacks), - disable: createHookNotImpl, + enable() { + createHookNotImpl(hook); + hasEnabledCreateHook = true; + return this; + }, + disable() { + createHookNotImpl(); + return this; + }, }; } diff --git a/src/js/node/domain.ts b/src/js/node/domain.ts index 7bed189e53..6a712a0a3f 100644 --- a/src/js/node/domain.ts +++ b/src/js/node/domain.ts @@ -1,5 +1,8 @@ // Import Events var EventEmitter = require("node:events"); +const { ERR_UNHANDLED_ERROR } = require("internal/errors"); + +const ObjectDefineProperty = Object.defineProperty; // Export Domain var domain: any = {}; @@ -7,6 +10,18 @@ domain.createDomain = domain.create = function () { var d = new EventEmitter(); function emitError(e) { + e ||= ERR_UNHANDLED_ERROR(); + if (typeof e === "object") { + e.domainEmitter = this; + ObjectDefineProperty(e, "domain", { + __proto__: null, + configurable: true, + enumerable: false, + value: domain, + writable: true, + }); + e.domainThrown = false; + } d.emit("error", e); } diff --git a/src/js/node/events.ts b/src/js/node/events.ts index 8e3d875d45..85a5c7707c 100644 --- a/src/js/node/events.ts +++ b/src/js/node/events.ts @@ -23,16 +23,22 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -const { ERR_INVALID_ARG_TYPE } = require("internal/errors"); +const { ERR_INVALID_ARG_TYPE, ERR_UNHANDLED_ERROR } = require("internal/errors"); const { validateObject, validateInteger, validateAbortSignal, validateNumber, validateBoolean, + validateFunction, } = require("internal/validators"); +const { inspect, types } = require("node:util"); + const SymbolFor = Symbol.for; +const ArrayPrototypeSlice = Array.prototype.slice; +const ArrayPrototypeSplice = Array.prototype.splice; +const ReflectOwnKeys = Reflect.ownKeys; const kCapture = Symbol("kCapture"); const kErrorMonitor = SymbolFor("events.errorMonitor"); @@ -42,7 +48,6 @@ const kWatermarkData = SymbolFor("nodejs.watermarkData"); const kRejection = SymbolFor("nodejs.rejection"); const kFirstEventParam = SymbolFor("nodejs.kFirstEventParam"); const captureRejectionSymbol = SymbolFor("nodejs.rejection"); -const ArrayPrototypeSlice = Array.prototype.slice; let FixedQueue; const kEmptyObject = Object.freeze({ __proto__: null }); @@ -77,26 +82,48 @@ Object.defineProperty(EventEmitterPrototype.setMaxListeners, "name", { value: "s EventEmitterPrototype.constructor = EventEmitter; EventEmitterPrototype.getMaxListeners = function getMaxListeners() { - return this?._maxListeners ?? defaultMaxListeners; + return _getMaxListeners(this); }; Object.defineProperty(EventEmitterPrototype.getMaxListeners, "name", { value: "getMaxListeners" }); function emitError(emitter, args) { var { _events: events } = emitter; - args[0] ??= new Error("Unhandled error."); - if (!events) throw args[0]; - var errorMonitor = events[kErrorMonitor]; - if (errorMonitor) { - for (var handler of ArrayPrototypeSlice.$call(errorMonitor)) { - handler.$apply(emitter, args); + + if (events !== undefined) { + const errorMonitor = events[kErrorMonitor]; + if (errorMonitor) { + for (const handler of ArrayPrototypeSlice.$call(errorMonitor)) { + handler.$apply(emitter, args); + } + } + + const handlers = events.error; + if (handlers) { + for (var handler of ArrayPrototypeSlice.$call(handlers)) { + handler.$apply(emitter, args); + } + return true; } } - var handlers = events.error; - if (!handlers) throw args[0]; - for (var handler of ArrayPrototypeSlice.$call(handlers)) { - handler.$apply(emitter, args); + + let er; + if (args.length > 0) er = args[0]; + + if (er instanceof Error) { + throw er; // Unhandled 'error' event } - return true; + + let stringifiedEr; + try { + stringifiedEr = inspect(er); + } catch { + stringifiedEr = er; + } + + // At least give some kind of context to the user + const err = ERR_UNHANDLED_ERROR(stringifiedEr); + err.context = er; + throw err; // Unhandled 'error' event } function addCatch(emitter, promise, type, args) { @@ -215,7 +242,7 @@ EventEmitterPrototype.addListener = function addListener(type, fn) { this._eventsCount++; } else { handlers.push(fn); - var m = this._maxListeners ?? defaultMaxListeners; + var m = _getMaxListeners(this); if (m > 0 && handlers.length > m && !handlers.warned) { overflowWarning(this, type, handlers); } @@ -240,7 +267,7 @@ EventEmitterPrototype.prependListener = function prependListener(type, fn) { this._eventsCount++; } else { handlers.unshift(fn); - var m = this._maxListeners ?? defaultMaxListeners; + var m = _getMaxListeners(this); if (m > 0 && handlers.length > m && !handlers.warned) { overflowWarning(this, type, handlers); } @@ -251,8 +278,7 @@ EventEmitterPrototype.prependListener = function prependListener(type, fn) { function overflowWarning(emitter, type, handlers) { handlers.warned = true; const warn = new Error( - `Possible EventEmitter memory leak detected. ${handlers.length} ${String(type)} listeners ` + - `added to [${emitter.constructor.name}]. Use emitter.setMaxListeners() to increase limit`, + `Possible EventTarget memory leak detected. ${handlers.length} ${String(type)} listeners added to ${inspect(emitter, { depth: -1 })}. MaxListeners is ${emitter._maxListeners}. Use events.setMaxListeners() to increase limit`, ); warn.name = "MaxListenersExceededWarning"; warn.emitter = emitter; @@ -261,67 +287,100 @@ function overflowWarning(emitter, type, handlers) { process.emitWarning(warn); } -function onceWrapper(type, listener, ...args) { - this.removeListener(type, listener); - listener.$apply(this, args); +function _onceWrap(target, type, listener) { + const state = { fired: false, wrapFn: undefined, target, type, listener }; + const wrapped = onceWrapper.bind(state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + if (arguments.length === 0) return this.listener.$call(this.target); + return this.listener.$apply(this.target, arguments); + } } EventEmitterPrototype.once = function once(type, fn) { checkListener(fn); - const bound = onceWrapper.bind(this, type, fn); - bound.listener = fn; - this.addListener(type, bound); + this.on(type, _onceWrap(this, type, fn)); return this; }; Object.defineProperty(EventEmitterPrototype.once, "name", { value: "once" }); EventEmitterPrototype.prependOnceListener = function prependOnceListener(type, fn) { checkListener(fn); - const bound = onceWrapper.bind(this, type, fn); - bound.listener = fn; - this.prependListener(type, bound); + this.prependListener(type, _onceWrap(this, type, fn)); return this; }; -EventEmitterPrototype.removeListener = function removeListener(type, fn) { - checkListener(fn); - var { _events: events } = this; - if (!events) return this; - var handlers = events[type]; - if (!handlers) return this; - var length = handlers.length; +EventEmitterPrototype.removeListener = function removeListener(type, listener) { + checkListener(listener); + + const events = this._events; + if (events === undefined) return this; + + const list = events[type]; + if (list === undefined) return this; + let position = -1; - for (let i = length - 1; i >= 0; i--) { - if (handlers[i] === fn || handlers[i].listener === fn) { + for (let i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { position = i; break; } } if (position < 0) return this; - if (position === 0) { - handlers.shift(); - } else { - handlers.splice(position, 1); - } - if (handlers.length === 0) { + + if (position === 0) list.shift(); + else ArrayPrototypeSplice.$call(list, position, 1); + + if (list.length === 0) { delete events[type]; this._eventsCount--; } + + if (events.removeListener !== undefined) this.emit("removeListener", type, listener.listener || listener); + return this; }; EventEmitterPrototype.off = EventEmitterPrototype.removeListener; EventEmitterPrototype.removeAllListeners = function removeAllListeners(type) { - var { _events: events } = this; - if (type && events) { - if (events[type]) { - delete events[type]; - this._eventsCount--; + const events = this._events; + if (events === undefined) return this; + + if (events.removeListener === undefined) { + if (type) { + if (events[type]) { + delete events[type]; + this._eventsCount--; + } + } else { + this._events = { __proto__: null }; } - } else { - this._events = { __proto__: null }; + return this; } + + // Emit removeListener for all listeners on all events + if (!type) { + for (const key of ReflectOwnKeys(events)) { + if (key === "removeListener") continue; + this.removeAllListeners(key); + } + this.removeAllListeners("removeListener"); + this._events = { __proto__: null }; + this._eventsCount = 0; + return this; + } + + // emit in LIFO order + const listeners = events[type]; + for (let i = listeners.length - 1; i >= 0; i--) this.removeListener(type, listeners[i]); return this; }; @@ -591,20 +650,20 @@ function getEventListeners(emitter, type) { // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/events.js#L315-L339 function setMaxListeners(n = defaultMaxListeners, ...eventTargets) { validateNumber(n, "setMaxListeners", 0); - const length = eventTargets?.length; - if (length) { - for (let eventTargetOrEmitter of eventTargets) { - // TODO: EventTarget setMaxListeners is not implemented yet. - // Only EventEmitter has it. - if ($isCallable(eventTargetOrEmitter?.setMaxListeners)) { - eventTargetOrEmitter.setMaxListeners(n); - } else if ($isObject(eventTargetOrEmitter) && eventTargetOrEmitter instanceof EventTarget) { - // This is a fake number so that the number can be checked against with getMaxListeners() - eventTargetOrEmitter[eventTargetMaxListenersSymbol] = n; + if (eventTargets.length === 0) { + defaultMaxListeners = n; + } else { + for (let i = 0; i < eventTargets.length; i++) { + const target = eventTargets[i]; + if (types.isEventTarget(target)) { + target[kMaxEventTargetListeners] = n; + target[kMaxEventTargetListenersWarned] = false; + } else if (typeof target.setMaxListeners === "function") { + target.setMaxListeners(n); + } else { + throw ERR_INVALID_ARG_TYPE("eventTargets", ["EventEmitter", "EventTarget"], target); } } - } else { - defaultMaxListeners = n; } } Object.defineProperty(setMaxListeners, "name", { value: "setMaxListeners" }); @@ -666,16 +725,23 @@ function ERR_OUT_OF_RANGE(name, range, value) { } function checkListener(listener) { - if (typeof listener !== "function") { - throw new TypeError("The listener must be a function"); - } + validateFunction(listener, "listener"); +} + +function _getMaxListeners(emitter) { + return emitter?._maxListeners ?? defaultMaxListeners; } let AsyncResource = null; -const eventTargetMaxListenersSymbol = Symbol("EventTarget.maxListeners"); function getMaxListeners(emitterOrTarget) { - return emitterOrTarget?.[eventTargetMaxListenersSymbol] ?? emitterOrTarget?._maxListeners ?? defaultMaxListeners; + if (typeof emitterOrTarget?.getMaxListeners === "function") { + return _getMaxListeners(emitterOrTarget); + } else if (types.isEventTarget(emitterOrTarget)) { + emitterOrTarget[kMaxEventTargetListeners] ??= defaultMaxListeners; + return emitterOrTarget[kMaxEventTargetListeners]; + } + throw ERR_INVALID_ARG_TYPE("emitter", ["EventEmitter", "EventTarget"], emitterOrTarget); } Object.defineProperty(getMaxListeners, "name", { value: "getMaxListeners" }); diff --git a/src/js/node/os.ts b/src/js/node/os.ts index f962ed31e2..53d6fd5b8f 100644 --- a/src/js/node/os.ts +++ b/src/js/node/os.ts @@ -1,5 +1,4 @@ // Hardcoded module "node:os" - var tmpdir = function () { var env = Bun.env; @@ -19,6 +18,8 @@ var tmpdir = function () { return path; }; + tmpdir[Symbol.toPrimitive] = tmpdir; + return tmpdir(); }; @@ -85,7 +86,7 @@ function lazyCpus({ cpus }) { } // all logic based on `process.platform` and `process.arch` is inlined at bundle time -function bound(obj) { +function bound(binding) { return { availableParallelism: function () { return navigator.hardwareConcurrency; @@ -93,25 +94,27 @@ function bound(obj) { arch: function () { return process.arch; }, - cpus: lazyCpus(obj), + cpus: lazyCpus(binding), endianness: function () { - return process.arch === "arm64" || process.arch === "x64" ? "LE" : $bundleError("TODO: endianness"); + return process.arch === "arm64" || process.arch === "x64" // + ? "LE" + : $bundleError("TODO: endianness"); }, - freemem: obj.freemem.bind(obj), - getPriority: obj.getPriority.bind(obj), - homedir: obj.homedir.bind(obj), - hostname: obj.hostname.bind(obj), - loadavg: obj.loadavg.bind(obj), - networkInterfaces: obj.networkInterfaces.bind(obj), + freemem: binding.freemem, + getPriority: binding.getPriority, + homedir: binding.homedir, + hostname: binding.hostname, + loadavg: binding.loadavg, + networkInterfaces: binding.networkInterfaces, platform: function () { return process.platform; }, - release: obj.release.bind(obj), - setPriority: obj.setPriority.bind(obj), + release: binding.release, + setPriority: binding.setPriority, get tmpdir() { return tmpdir; }, - totalmem: obj.totalmem.bind(obj), + totalmem: binding.totalmem, type: function () { return process.platform === "win32" ? "Windows_NT" @@ -121,17 +124,25 @@ function bound(obj) { ? "Linux" : $bundleError("TODO: type"); }, - uptime: obj.uptime.bind(obj), - userInfo: obj.userInfo.bind(obj), - version: obj.version.bind(obj), - machine: obj.machine.bind(obj), + uptime: binding.uptime, + userInfo: binding.userInfo, + version: binding.version, + machine: function () { + return process.arch === "arm64" // + ? "arm64" + : process.arch === "x64" + ? "x86_64" + : $bundleError("TODO: machine"); + }, devNull: process.platform === "win32" ? "\\\\.\\nul" : "/dev/null", - EOL: process.platform === "win32" ? "\r\n" : "\n", + get EOL() { + return process.platform === "win32" ? "\r\n" : "\n"; + }, constants: $processBindingConstants.os, }; } -const out = bound($zig("node_os.zig", "OS.create")); +const out = bound($zig("node_os.zig", "createNodeOsBinding")); symbolToStringify(out, "arch"); symbolToStringify(out, "availableParallelism"); @@ -147,8 +158,10 @@ symbolToStringify(out, "type"); symbolToStringify(out, "uptime"); symbolToStringify(out, "version"); symbolToStringify(out, "machine"); + function symbolToStringify(obj, key) { - obj[key][Symbol.toPrimitive] = function (hint) { + $assert(obj[key] !== undefined, `Missing ${key}`); + obj[key][Symbol.toPrimitive] = function (hint: string) { return obj[key](); }; } diff --git a/src/js/node/util.ts b/src/js/node/util.ts index 3541d1b806..562aad9d15 100644 --- a/src/js/node/util.ts +++ b/src/js/node/util.ts @@ -206,7 +206,28 @@ function styleText(format, text) { e.code = "ERR_INVALID_ARG_TYPE"; throw e; } - const formatCodes = inspect.colors[format]; + + if ($isJSArray(format)) { + let left = ""; + let right = ""; + for (const key of format) { + const formatCodes = inspect.colors[key]; + if (formatCodes == null) { + const e = new Error( + `The value "${typeof key === "symbol" ? key.description : key}" is invalid for argument 'format'. Reason: must be one of: ${Object.keys(inspect.colors).join(", ")}`, + ); + e.code = "ERR_INVALID_ARG_VALUE"; + throw e; + } + left += `\u001b[${formatCodes[0]}m`; + right = `\u001b[${formatCodes[1]}m${right}`; + } + + return `${left}${text}${right}`; + } + + let formatCodes = inspect.colors[format]; + if (formatCodes == null) { const e = new Error( `The value "${typeof format === "symbol" ? format.description : format}" is invalid for argument 'format'. Reason: must be one of: ${Object.keys(inspect.colors).join(", ")}`, diff --git a/src/js/private.d.ts b/src/js/private.d.ts index aeb86e97d2..b058cc40c5 100644 --- a/src/js/private.d.ts +++ b/src/js/private.d.ts @@ -210,6 +210,9 @@ declare function $newZigFunction any>( argCount: number, ): T; /** + * Retrieves a handle to a function defined in Zig or C++, defined in a + * `.bind.ts` file. For more information on how to define bindgen functions, see + * [bindgen's documentation](https://bun.sh/docs/project/bindgen). * @param filename - The basename of the `.bind.ts` file. * @param symbol - The name of the function to call. */ diff --git a/src/js_ast.zig b/src/js_ast.zig index ce24d226f5..3216d457a1 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -29,7 +29,7 @@ const TypeScript = @import("./js_parser.zig").TypeScript; const ThreadlocalArena = @import("./mimalloc_arena.zig").Arena; const MimeType = bun.http.MimeType; const OOM = bun.OOM; - +const Loader = bun.options.Loader; /// This is the index to the automatically-generated part containing code that /// calls "__export(exports, { ... getters ... })". This is used to generate /// getters on an exports object for ES6 export statements, and is both for @@ -3044,7 +3044,7 @@ pub const Stmt = struct { return Stmt.allocate(allocator, S.SExpr, S.SExpr{ .value = expr }, expr.loc); } - pub const Tag = enum(u6) { + pub const Tag = enum { s_block, s_break, s_class, @@ -3126,7 +3126,13 @@ pub const Stmt = struct { s_empty: S.Empty, // special case, its a zero value type s_debugger: S.Debugger, - s_lazy_export: Expr.Data, + s_lazy_export: *Expr.Data, + + comptime { + if (@sizeOf(Stmt) > 24) { + @compileLog("Expected Stmt to be <= 24 bytes, but it is", @sizeOf(Stmt), " bytes"); + } + } pub const Store = struct { const StoreType = NewStore(&.{ @@ -4564,7 +4570,7 @@ pub const Expr = struct { }; } - pub const Tag = enum(u6) { + pub const Tag = enum { e_array, e_unary, e_binary, @@ -7008,7 +7014,7 @@ pub const BundledAst = struct { hashbang: string = "", parts: Part.List = .{}, css: ?*bun.css.BundlerStyleSheet = null, - url_for_css: ?[]const u8 = null, + url_for_css: []const u8 = "", symbols: Symbol.List = .{}, module_scope: Scope = .{}, char_freq: CharFreq = undefined, @@ -7125,7 +7131,6 @@ pub const BundledAst = struct { .import_records = ast.import_records, .hashbang = ast.hashbang, - // .url_for_css = ast.url_for_css orelse "", .parts = ast.parts, // This list may be mutated later, so we should store the capacity .symbols = ast.symbols, @@ -7173,12 +7178,12 @@ pub const BundledAst = struct { pub fn addUrlForCss( this: *BundledAst, allocator: std.mem.Allocator, - css_enabled: bool, + experimental: Loader.Experimental, source: *const logger.Source, mime_type_: ?[]const u8, unique_key: ?[]const u8, ) void { - if (css_enabled) { + if (experimental.css) { const mime_type = if (mime_type_) |m| m else MimeType.byExtension(bun.strings.trimLeadingChar(std.fs.path.extension(source.path.text), '.')).value; const contents = source.contents; // TODO: make this configurable @@ -7902,8 +7907,8 @@ pub const Macro = struct { const DotEnv = @import("./env_loader.zig"); const js = @import("./bun.js/javascript_core_c_api.zig"); const Zig = @import("./bun.js/bindings/exports.zig"); - const Bundler = bun.Bundler; - const MacroEntryPoint = bun.bundler.MacroEntryPoint; + const Transpiler = bun.Transpiler; + const MacroEntryPoint = bun.transpiler.MacroEntryPoint; const MacroRemap = @import("./resolver/package_json.zig").MacroMap; pub const MacroRemapEntry = @import("./resolver/package_json.zig").MacroImportReplacementMap; @@ -7928,12 +7933,12 @@ pub const Macro = struct { return this.remap.get(path); } - pub fn init(bundler: *Bundler) MacroContext { + pub fn init(transpiler: *Transpiler) MacroContext { return MacroContext{ .macros = MacroMap.init(default_allocator), - .resolver = &bundler.resolver, - .env = bundler.env, - .remap = bundler.options.macro_remap, + .resolver = &transpiler.resolver, + .env = transpiler.env, + .remap = transpiler.options.macro_remap, }; } @@ -8096,7 +8101,7 @@ pub const Macro = struct { _vm.enableMacroMode(); _vm.eventLoop().ensureWaker(); - try _vm.bundler.configureDefines(); + try _vm.transpiler.configureDefines(); break :brk _vm; }; diff --git a/src/js_lexer.zig b/src/js_lexer.zig index ca946482f3..1560f02621 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -153,13 +153,6 @@ fn NewLexer_( code_point: CodePoint = -1, identifier: []const u8 = "", jsx_pragma: JSXPragma = .{}, - bun_pragma: enum { - none, - bun, - bun_cjs, - bytecode, - bytecode_cjs, - } = .none, source_mapping_url: ?js_ast.Span = null, number: f64 = 0.0, rescan_close_brace_as_template_token: bool = false, @@ -1957,9 +1950,7 @@ fn NewLexer_( // } } - if (lexer.bun_pragma == .none and strings.hasPrefixWithWordBoundary(chunk, "bun")) { - lexer.bun_pragma = .bun; - } else if (strings.hasPrefixWithWordBoundary(chunk, "jsx")) { + if (strings.hasPrefixWithWordBoundary(chunk, "jsx")) { if (PragmaArg.scan(.skip_space_first, lexer.start + i + 1, "jsx", chunk)) |span| { lexer.jsx_pragma._jsx = span; } @@ -1979,10 +1970,6 @@ fn NewLexer_( if (PragmaArg.scan(.no_space_first, lexer.start + i + 1, " sourceMappingURL=", chunk)) |span| { lexer.source_mapping_url = span; } - } else if ((lexer.bun_pragma == .bun or lexer.bun_pragma == .bun_cjs) and strings.hasPrefixWithWordBoundary(chunk, "bytecode")) { - lexer.bun_pragma = if (lexer.bun_pragma == .bun) .bytecode else .bytecode_cjs; - } else if ((lexer.bun_pragma == .bytecode or lexer.bun_pragma == .bun) and strings.hasPrefixWithWordBoundary(chunk, "bun-cjs")) { - lexer.bun_pragma = if (lexer.bun_pragma == .bytecode) .bytecode_cjs else .bun_cjs; } }, else => {}, @@ -2012,9 +1999,7 @@ fn NewLexer_( } } - if (lexer.bun_pragma == .none and strings.hasPrefixWithWordBoundary(chunk, "bun")) { - lexer.bun_pragma = .bun; - } else if (strings.hasPrefixWithWordBoundary(chunk, "jsx")) { + if (strings.hasPrefixWithWordBoundary(chunk, "jsx")) { if (PragmaArg.scan(.skip_space_first, lexer.start + i + 1, "jsx", chunk)) |span| { lexer.jsx_pragma._jsx = span; } @@ -2034,10 +2019,6 @@ fn NewLexer_( if (PragmaArg.scan(.no_space_first, lexer.start + i + 1, " sourceMappingURL=", chunk)) |span| { lexer.source_mapping_url = span; } - } else if ((lexer.bun_pragma == .bun or lexer.bun_pragma == .bun_cjs) and strings.hasPrefixWithWordBoundary(chunk, "bytecode")) { - lexer.bun_pragma = if (lexer.bun_pragma == .bun) .bytecode else .bytecode_cjs; - } else if ((lexer.bun_pragma == .bytecode or lexer.bun_pragma == .bun) and strings.hasPrefixWithWordBoundary(chunk, "bun-cjs")) { - lexer.bun_pragma = if (lexer.bun_pragma == .bytecode) .bytecode_cjs else .bun_cjs; } }, else => {}, @@ -2162,7 +2143,7 @@ fn NewLexer_( const flag_characters = "dgimsuvy"; const min_flag = comptime std.mem.min(u8, flag_characters); const max_flag = comptime std.mem.max(u8, flag_characters); - const RegexpFlags = std.bit_set.IntegerBitSet((max_flag - min_flag) + 1); + const RegexpFlags = bun.bit_set.IntegerBitSet((max_flag - min_flag) + 1); var flags = RegexpFlags.initEmpty(); while (isIdentifierContinue(lexer.code_point)) { switch (lexer.code_point) { @@ -3062,18 +3043,10 @@ pub const Lexer = NewLexer(.{}); const JSIdentifier = @import("./js_lexer/identifier.zig"); pub inline fn isIdentifierStart(codepoint: i32) bool { - if (comptime Environment.isWasm) { - return JSIdentifier.JumpTable.isIdentifierStart(codepoint); - } - - return JSIdentifier.Bitset.isIdentifierStart(codepoint); + return JSIdentifier.isIdentifierStart(codepoint); } pub inline fn isIdentifierContinue(codepoint: i32) bool { - if (comptime Environment.isWasm) { - return JSIdentifier.JumpTable.isIdentifierPart(codepoint); - } - - return JSIdentifier.Bitset.isIdentifierPart(codepoint); + return JSIdentifier.isIdentifierPart(codepoint); } pub fn isWhitespace(codepoint: CodePoint) bool { diff --git a/src/js_lexer/identifier.zig b/src/js_lexer/identifier.zig index b8da1da65f..6df764bb72 100644 --- a/src/js_lexer/identifier.zig +++ b/src/js_lexer/identifier.zig @@ -1,2024 +1,78 @@ -// This file benchmarks different approaches for determinig whether or not a unicode codepoint is possibly a JS identifier -// these values are copy-pasted from "typescript/lib/typescriptServices.js" const std = @import("std"); -pub const SerializedBitset = extern struct {}; -pub const Bitset = struct { - const Cache = @import("identifier_cache.zig"); - const id_start_range: [2]i32 = Cache.id_start_meta.range; - const id_end_range: [2]i32 = Cache.id_continue_meta.range; - // this is a pointer because otherwise it may be copied onto the stack - // and it's a huge bitset - const id_start = &Cache.id_start; - // this is a pointer because otherwise it may be copied onto the stack - // and it's a huge bitset - const id_continue = &Cache.id_continue; +pub fn isIdentifierStart(codepoint: i32) bool { + return switch (codepoint) { + 'a'...'z', 'A'...'Z', '_', '$' => true, + std.math.minInt(i32)...0, 0x10FFFF...std.math.maxInt(i32) => false, + else => isIDStartESNext(@intCast(codepoint)), + }; +} - pub fn init() void {} +pub fn isIdentifierPart(codepoint: i32) bool { + return switch (codepoint) { + 'a'...'z', 'A'...'Z', '0'...'9', '_', '$' => true, + std.math.minInt(i32)...0, 0x10FFFF...std.math.maxInt(i32) => false, + else => isIDContinueESNext(@intCast(codepoint)), + }; +} - pub fn isIdentifierStart(codepoint: i32) bool { - return codepoint >= (comptime id_start_range[0]) and - codepoint <= (comptime id_start_range[1]) and - id_start.isSet((comptime @as(usize, @intCast(id_start_range[1]))) - @as( - usize, - @intCast(codepoint), - )); - } - - pub fn isIdentifierPart(codepoint: i32) bool { - return codepoint >= (comptime id_end_range[0]) and - codepoint <= (comptime id_end_range[1]) and - id_continue.isSet( - (comptime @as(usize, @intCast(id_end_range[1]))) - @as( - usize, - @intCast(codepoint), - ), - ); - } +/// This file is auto-generated. Do not edit. +/// isIDStartES5 checks if a codepoint is valid in the isIDStartES5 category +pub fn isIDStartES5(cp: u21) bool { + const high = cp >> 8; + const low = cp & 0xFF; + const stage2_idx = idStartES5.stage1[high]; + const bit_pos = stage2_idx + low; + const u64_idx = bit_pos >> 6; + const bit_idx = @as(u6, @intCast(bit_pos & 63)); + return (idStartES5.stage2[u64_idx] & (@as(u64, 1) << bit_idx)) != 0; +} +const idStartES5 = struct { + pub const stage1 = [_]u16{ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096, 4352, 4608, 4864, 5120, 256, 5376, 5632, 5888, 2048, 2048, 2048, 2048, 2048, 6144, 6400, 6656, 6912, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 7168, 7424, 2048, 2048, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 7680, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 7936, 256, 256, 256, 256, 8192, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 8448, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 256, 8704, 8960, 256, 9216, 9472, 9728, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048 }; + pub const stage2 = [_]u64{ 0, 576460743847706622, 297241973452963840, 18410715276682199039, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4503586742468607, 18446744073709486080, 18014187403249451007, 70501888360451, 0, 288230376151711744, 18446744056529672000, 4503599577006079, 18446744073709551615, 18446744073709551615, 18446744073709547523, 234187180623206815, 18446181123756130304, 18446744065161560063, 255, 1979120929931264, 576460743713488896, 18446181123756132351, 18446744073709551615, 2017613045381988351, 35184371892224, 0, 274877906943, 0, 0, 0, 0, 0, 2594073385365405664, 17163157504, 271902628478820320, 844440767823872, 247132830528276448, 7881300924956672, 2589004636761075680, 4295032832, 2579997437506199520, 15837691904, 270153412153034720, 0, 283724577500946400, 12884901888, 283724577500946400, 13958643712, 288228177128316896, 12884901888, 3457638613854978016, 127, 3940649673949182, 127, 2309762420256548246, 805306463, 1, 8796093021951, 3840, 0, 7679401525247, 4128768, 18446744069414584320, 36028797018898495, 18446744073709551615, 18446744071629176831, 18446743008557662207, 288230376151711743, 18446744073709551487, 18446744070446333311, 9168625153884503423, 18446603336212774717, 18446744071549321215, 134217599, 18446744069414584320, 9007199254740991, 18446744073709551614, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 35923243902697471, 18446744069548802046, 8796093022207, 0, 0, 4503599627370495, 0, 18446744069414584320, 72057594037927935, 2199023255551, 0, 18446744073709551615, 18446744073709551615, 18446744069683019775, 288230376151711743, 18446744070475743231, 4611686017001275199, 6908521828386340863, 2295745090394464220, 0, 9223372036854775808, 0, 0, 287031153606524036, 0, 0, 0, 17451448556060768, 18446744073709551614, 18446744066732326911, 8646911284551352319, 18446216308128219104, 18446744073709551615, 72057589742993407, 0, 18446744073709551615, 18446744073709551615, 18014398509481983, 0, 18446744073709551615, 18446744073709551615, 274877906943, 0, 18446744073709551615, 18446744073709551615, 8191, 0, 18446744073709551615, 18446744073709551615, 68719476735, 0, 70368744177663, 0, 0, 0, 6881498030004502655, 18446744073709551579, 1125899906842623, 18446744073709027328, 4611686018427387903, 18446744073709486080, 18446744073709355007, 1152640029630136575, 0, 18435203599664414720, 18446744073709551615, 2305843009213693951, 576460743713488896, 18446743798965862398, 9223372036854775807, 486341884 }; }; -/// In WASM, we use the JumpTable version -pub const JumpTable = struct { - const minInt = @import("std").math.minInt; - const maxInt = @import("std").math.maxInt; - const max_codepoint = 0x10FFFF; - noinline fn isIdentifierPartSlow(codepoint: i32) bool { - @setCold(true); - return switch (codepoint) { - // explicitly tell LLVM's optimizer about values we know will not be in the range of this switch statement - 0xaa...0xffd7 => isIdentifierPartSlow16(@as(u16, @intCast(codepoint))), - (0xffd7 + 1)...0xe01ef => isIdentifierPartSlow32(codepoint), - - else => false, - }; - } - - fn isIdentifierPartSlow16(codepoint: u16) bool { - return switch (codepoint) { - minInt(u16)...(0xaa - 1) => unreachable, - 0xaa...0xaa, 0xb5...0xb5, 0xb7...0xb7, 0xba...0xba, 0xc0...0xd6, 0xd8...0xf6, 0xf8...0x2c1, 0x2c6...0x2d1, 0x2e0...0x2e4, 0x2ec...0x2ec, 0x2ee...0x2ee, 0x300...0x374, 0x376...0x377, 0x37a...0x37d, 0x37f...0x37f, 0x386...0x38a, 0x38c...0x38c, 0x38e...0x3a1, 0x3a3...0x3f5, 0x3f7...0x481, 0x483...0x487, 0x48a...0x52f, 0x531...0x556, 0x559...0x559, 0x560...0x588, 0x591...0x5bd, 0x5bf...0x5bf, 0x5c1...0x5c2, 0x5c4...0x5c5, 0x5c7...0x5c7, 0x5d0...0x5ea, 0x5ef...0x5f2, 0x610...0x61a, 0x620...0x669, 0x66e...0x6d3, 0x6d5...0x6dc, 0x6df...0x6e8, 0x6ea...0x6fc, 0x6ff...0x6ff, 0x710...0x74a, 0x74d...0x7b1, 0x7c0...0x7f5, 0x7fa...0x7fa, 0x7fd...0x7fd, 0x800...0x82d, 0x840...0x85b, 0x860...0x86a, 0x8a0...0x8b4, 0x8b6...0x8c7, 0x8d3...0x8e1, 0x8e3...0x963, 0x966...0x96f, 0x971...0x983, 0x985...0x98c, 0x98f...0x990, 0x993...0x9a8, 0x9aa...0x9b0, 0x9b2...0x9b2, 0x9b6...0x9b9, 0x9bc...0x9c4, 0x9c7...0x9c8, 0x9cb...0x9ce, 0x9d7...0x9d7, 0x9dc...0x9dd, 0x9df...0x9e3, 0x9e6...0x9f1, 0x9fc...0x9fc, 0x9fe...0x9fe, 0xa01...0xa03, 0xa05...0xa0a, 0xa0f...0xa10, 0xa13...0xa28, 0xa2a...0xa30, 0xa32...0xa33, 0xa35...0xa36, 0xa38...0xa39, 0xa3c...0xa3c, 0xa3e...0xa42, 0xa47...0xa48, 0xa4b...0xa4d, 0xa51...0xa51, 0xa59...0xa5c, 0xa5e...0xa5e, 0xa66...0xa75, 0xa81...0xa83, 0xa85...0xa8d, 0xa8f...0xa91, 0xa93...0xaa8, 0xaaa...0xab0, 0xab2...0xab3, 0xab5...0xab9, 0xabc...0xac5, 0xac7...0xac9, 0xacb...0xacd, 0xad0...0xad0, 0xae0...0xae3, 0xae6...0xaef, 0xaf9...0xaff, 0xb01...0xb03, 0xb05...0xb0c, 0xb0f...0xb10, 0xb13...0xb28, 0xb2a...0xb30, 0xb32...0xb33, 0xb35...0xb39, 0xb3c...0xb44, 0xb47...0xb48, 0xb4b...0xb4d, 0xb55...0xb57, 0xb5c...0xb5d, 0xb5f...0xb63, 0xb66...0xb6f, 0xb71...0xb71, 0xb82...0xb83, 0xb85...0xb8a, 0xb8e...0xb90, 0xb92...0xb95, 0xb99...0xb9a, 0xb9c...0xb9c, 0xb9e...0xb9f, 0xba3...0xba4, 0xba8...0xbaa, 0xbae...0xbb9, 0xbbe...0xbc2, 0xbc6...0xbc8, 0xbca...0xbcd, 0xbd0...0xbd0, 0xbd7...0xbd7, 0xbe6...0xbef, 0xc00...0xc0c, 0xc0e...0xc10, 0xc12...0xc28, 0xc2a...0xc39, 0xc3d...0xc44, 0xc46...0xc48, 0xc4a...0xc4d, 0xc55...0xc56, 0xc58...0xc5a, 0xc60...0xc63, 0xc66...0xc6f, 0xc80...0xc83, 0xc85...0xc8c, 0xc8e...0xc90, 0xc92...0xca8, 0xcaa...0xcb3, 0xcb5...0xcb9, 0xcbc...0xcc4, 0xcc6...0xcc8, 0xcca...0xccd, 0xcd5...0xcd6, 0xcde...0xcde, 0xce0...0xce3, 0xce6...0xcef, 0xcf1...0xcf2, 0xd00...0xd0c, 0xd0e...0xd10, 0xd12...0xd44, 0xd46...0xd48, 0xd4a...0xd4e, 0xd54...0xd57, 0xd5f...0xd63, 0xd66...0xd6f, 0xd7a...0xd7f, 0xd81...0xd83, 0xd85...0xd96, 0xd9a...0xdb1, 0xdb3...0xdbb, 0xdbd...0xdbd, 0xdc0...0xdc6, 0xdca...0xdca, 0xdcf...0xdd4, 0xdd6...0xdd6, 0xdd8...0xddf, 0xde6...0xdef, 0xdf2...0xdf3, 0xe01...0xe3a, 0xe40...0xe4e, 0xe50...0xe59, 0xe81...0xe82, 0xe84...0xe84, 0xe86...0xe8a, 0xe8c...0xea3, 0xea5...0xea5, 0xea7...0xebd, 0xec0...0xec4, 0xec6...0xec6, 0xec8...0xecd, 0xed0...0xed9, 0xedc...0xedf, 0xf00...0xf00, 0xf18...0xf19, 0xf20...0xf29, 0xf35...0xf35, 0xf37...0xf37, 0xf39...0xf39, 0xf3e...0xf47, 0xf49...0xf6c, 0xf71...0xf84, 0xf86...0xf97, 0xf99...0xfbc, 0xfc6...0xfc6, 0x1000...0x1049, 0x1050...0x109d, 0x10a0...0x10c5, 0x10c7...0x10c7, 0x10cd...0x10cd, 0x10d0...0x10fa, 0x10fc...0x1248, 0x124a...0x124d, 0x1250...0x1256, 0x1258...0x1258, 0x125a...0x125d, 0x1260...0x1288, 0x128a...0x128d, 0x1290...0x12b0, 0x12b2...0x12b5, 0x12b8...0x12be, 0x12c0...0x12c0, 0x12c2...0x12c5, 0x12c8...0x12d6, 0x12d8...0x1310, 0x1312...0x1315, 0x1318...0x135a, 0x135d...0x135f, 0x1369...0x1371, 0x1380...0x138f, 0x13a0...0x13f5, 0x13f8...0x13fd, 0x1401...0x166c, 0x166f...0x167f, 0x1681...0x169a, 0x16a0...0x16ea, 0x16ee...0x16f8, 0x1700...0x170c, 0x170e...0x1714, 0x1720...0x1734, 0x1740...0x1753, 0x1760...0x176c, 0x176e...0x1770, 0x1772...0x1773, 0x1780...0x17d3, 0x17d7...0x17d7, 0x17dc...0x17dd, 0x17e0...0x17e9, 0x180b...0x180d, 0x1810...0x1819, 0x1820...0x1878, 0x1880...0x18aa, 0x18b0...0x18f5, 0x1900...0x191e, 0x1920...0x192b, 0x1930...0x193b, 0x1946...0x196d, 0x1970...0x1974, 0x1980...0x19ab, 0x19b0...0x19c9, 0x19d0...0x19da, 0x1a00...0x1a1b, 0x1a20...0x1a5e, 0x1a60...0x1a7c, 0x1a7f...0x1a89, 0x1a90...0x1a99, 0x1aa7...0x1aa7, 0x1ab0...0x1abd, 0x1abf...0x1ac0, 0x1b00...0x1b4b, 0x1b50...0x1b59, 0x1b6b...0x1b73, 0x1b80...0x1bf3, 0x1c00...0x1c37, 0x1c40...0x1c49, 0x1c4d...0x1c7d, 0x1c80...0x1c88, 0x1c90...0x1cba, 0x1cbd...0x1cbf, 0x1cd0...0x1cd2, 0x1cd4...0x1cfa, 0x1d00...0x1df9, 0x1dfb...0x1f15, 0x1f18...0x1f1d, 0x1f20...0x1f45, 0x1f48...0x1f4d, 0x1f50...0x1f57, 0x1f59...0x1f59, 0x1f5b...0x1f5b, 0x1f5d...0x1f5d, 0x1f5f...0x1f7d, 0x1f80...0x1fb4, 0x1fb6...0x1fbc, 0x1fbe...0x1fbe, 0x1fc2...0x1fc4, 0x1fc6...0x1fcc, 0x1fd0...0x1fd3, 0x1fd6...0x1fdb, 0x1fe0...0x1fec, 0x1ff2...0x1ff4, 0x1ff6...0x1ffc, 0x203f...0x2040, 0x2054...0x2054, 0x2071...0x2071, 0x207f...0x207f, 0x2090...0x209c, 0x20d0...0x20dc, 0x20e1...0x20e1, 0x20e5...0x20f0, 0x2102...0x2102, 0x2107...0x2107, 0x210a...0x2113, 0x2115...0x2115, 0x2118...0x211d, 0x2124...0x2124, 0x2126...0x2126, 0x2128...0x2128, 0x212a...0x2139, 0x213c...0x213f, 0x2145...0x2149, 0x214e...0x214e, 0x2160...0x2188, 0x2c00...0x2c2e, 0x2c30...0x2c5e, 0x2c60...0x2ce4, 0x2ceb...0x2cf3, 0x2d00...0x2d25, 0x2d27...0x2d27, 0x2d2d...0x2d2d, 0x2d30...0x2d67, 0x2d6f...0x2d6f, 0x2d7f...0x2d96, 0x2da0...0x2da6, 0x2da8...0x2dae, 0x2db0...0x2db6, 0x2db8...0x2dbe, 0x2dc0...0x2dc6, 0x2dc8...0x2dce, 0x2dd0...0x2dd6, 0x2dd8...0x2dde, 0x2de0...0x2dff, 0x3005...0x3007, 0x3021...0x302f, 0x3031...0x3035, 0x3038...0x303c, 0x3041...0x3096, 0x3099...0x309f, 0x30a1...0x30ff, 0x3105...0x312f, 0x3131...0x318e, 0x31a0...0x31bf, 0x31f0...0x31ff, 0x3400...0x4dbf, 0x4e00...0x9ffc, 0xa000...0xa48c, 0xa4d0...0xa4fd, 0xa500...0xa60c, 0xa610...0xa62b, 0xa640...0xa66f, 0xa674...0xa67d, 0xa67f...0xa6f1, 0xa717...0xa71f, 0xa722...0xa788, 0xa78b...0xa7bf, 0xa7c2...0xa7ca, 0xa7f5...0xa827, 0xa82c...0xa82c, 0xa840...0xa873, 0xa880...0xa8c5, 0xa8d0...0xa8d9, 0xa8e0...0xa8f7, 0xa8fb...0xa8fb, 0xa8fd...0xa92d, 0xa930...0xa953, 0xa960...0xa97c, 0xa980...0xa9c0, 0xa9cf...0xa9d9, 0xa9e0...0xa9fe, 0xaa00...0xaa36, 0xaa40...0xaa4d, 0xaa50...0xaa59, 0xaa60...0xaa76, 0xaa7a...0xaac2, 0xaadb...0xaadd, 0xaae0...0xaaef, 0xaaf2...0xaaf6, 0xab01...0xab06, 0xab09...0xab0e, 0xab11...0xab16, 0xab20...0xab26, 0xab28...0xab2e, 0xab30...0xab5a, 0xab5c...0xab69, 0xab70...0xabea, 0xabec...0xabed, 0xabf0...0xabf9, 0xac00...0xd7a3, 0xd7b0...0xd7c6, 0xd7cb...0xd7fb, 0xf900...0xfa6d, 0xfa70...0xfad9, 0xfb00...0xfb06, 0xfb13...0xfb17, 0xfb1d...0xfb28, 0xfb2a...0xfb36, 0xfb38...0xfb3c, 0xfb3e...0xfb3e, 0xfb40...0xfb41, 0xfb43...0xfb44, 0xfb46...0xfbb1, 0xfbd3...0xfd3d, 0xfd50...0xfd8f, 0xfd92...0xfdc7, 0xfdf0...0xfdfb, 0xfe00...0xfe0f, 0xfe20...0xfe2f, 0xfe33...0xfe34, 0xfe4d...0xfe4f, 0xfe70...0xfe74, 0xfe76...0xfefc, 0xff10...0xff19, 0xff21...0xff3a, 0xff3f...0xff3f, 0xff41...0xff5a, 0xff65...0xffbe, 0xffc2...0xffc7, 0xffca...0xffcf, 0xffd2...0xffd7 => true, - else => false, - }; - } - - fn isIdentifierPartSlow32(codepoint: i32) bool { - return switch (codepoint) { - 0xffda...0xffdc, 0x10000...0x1000b, 0x1000d...0x10026, 0x10028...0x1003a, 0x1003c...0x1003d, 0x1003f...0x1004d, 0x10050...0x1005d, 0x10080...0x100fa, 0x10140...0x10174, 0x101fd...0x101fd, 0x10280...0x1029c, 0x102a0...0x102d0, 0x102e0...0x102e0, 0x10300...0x1031f, 0x1032d...0x1034a, 0x10350...0x1037a, 0x10380...0x1039d, 0x103a0...0x103c3, 0x103c8...0x103cf, 0x103d1...0x103d5, 0x10400...0x1049d, 0x104a0...0x104a9, 0x104b0...0x104d3, 0x104d8...0x104fb, 0x10500...0x10527, 0x10530...0x10563, 0x10600...0x10736, 0x10740...0x10755, 0x10760...0x10767, 0x10800...0x10805, 0x10808...0x10808, 0x1080a...0x10835, 0x10837...0x10838, 0x1083c...0x1083c, 0x1083f...0x10855, 0x10860...0x10876, 0x10880...0x1089e, 0x108e0...0x108f2, 0x108f4...0x108f5, 0x10900...0x10915, 0x10920...0x10939, 0x10980...0x109b7, 0x109be...0x109bf, 0x10a00...0x10a03, 0x10a05...0x10a06, 0x10a0c...0x10a13, 0x10a15...0x10a17, 0x10a19...0x10a35, 0x10a38...0x10a3a, 0x10a3f...0x10a3f, 0x10a60...0x10a7c, 0x10a80...0x10a9c, 0x10ac0...0x10ac7, 0x10ac9...0x10ae6, 0x10b00...0x10b35, 0x10b40...0x10b55, 0x10b60...0x10b72, 0x10b80...0x10b91, 0x10c00...0x10c48, 0x10c80...0x10cb2, 0x10cc0...0x10cf2, 0x10d00...0x10d27, 0x10d30...0x10d39, 0x10e80...0x10ea9, 0x10eab...0x10eac, 0x10eb0...0x10eb1, 0x10f00...0x10f1c, 0x10f27...0x10f27, 0x10f30...0x10f50, 0x10fb0...0x10fc4, 0x10fe0...0x10ff6, 0x11000...0x11046, 0x11066...0x1106f, 0x1107f...0x110ba, 0x110d0...0x110e8, 0x110f0...0x110f9, 0x11100...0x11134, 0x11136...0x1113f, 0x11144...0x11147, 0x11150...0x11173, 0x11176...0x11176, 0x11180...0x111c4, 0x111c9...0x111cc, 0x111ce...0x111da, 0x111dc...0x111dc, 0x11200...0x11211, 0x11213...0x11237, 0x1123e...0x1123e, 0x11280...0x11286, 0x11288...0x11288, 0x1128a...0x1128d, 0x1128f...0x1129d, 0x1129f...0x112a8, 0x112b0...0x112ea, 0x112f0...0x112f9, 0x11300...0x11303, 0x11305...0x1130c, 0x1130f...0x11310, 0x11313...0x11328, 0x1132a...0x11330, 0x11332...0x11333, 0x11335...0x11339, 0x1133b...0x11344, 0x11347...0x11348, 0x1134b...0x1134d, 0x11350...0x11350, 0x11357...0x11357, 0x1135d...0x11363, 0x11366...0x1136c, 0x11370...0x11374, 0x11400...0x1144a, 0x11450...0x11459, 0x1145e...0x11461, 0x11480...0x114c5, 0x114c7...0x114c7, 0x114d0...0x114d9, 0x11580...0x115b5, 0x115b8...0x115c0, 0x115d8...0x115dd, 0x11600...0x11640, 0x11644...0x11644, 0x11650...0x11659, 0x11680...0x116b8, 0x116c0...0x116c9, 0x11700...0x1171a, 0x1171d...0x1172b, 0x11730...0x11739, 0x11800...0x1183a, 0x118a0...0x118e9, 0x118ff...0x11906, 0x11909...0x11909, 0x1190c...0x11913, 0x11915...0x11916, 0x11918...0x11935, 0x11937...0x11938, 0x1193b...0x11943, 0x11950...0x11959, 0x119a0...0x119a7, 0x119aa...0x119d7, 0x119da...0x119e1, 0x119e3...0x119e4, 0x11a00...0x11a3e, 0x11a47...0x11a47, 0x11a50...0x11a99, 0x11a9d...0x11a9d, 0x11ac0...0x11af8, 0x11c00...0x11c08, 0x11c0a...0x11c36, 0x11c38...0x11c40, 0x11c50...0x11c59, 0x11c72...0x11c8f, 0x11c92...0x11ca7, 0x11ca9...0x11cb6, 0x11d00...0x11d06, 0x11d08...0x11d09, 0x11d0b...0x11d36, 0x11d3a...0x11d3a, 0x11d3c...0x11d3d, 0x11d3f...0x11d47, 0x11d50...0x11d59, 0x11d60...0x11d65, 0x11d67...0x11d68, 0x11d6a...0x11d8e, 0x11d90...0x11d91, 0x11d93...0x11d98, 0x11da0...0x11da9, 0x11ee0...0x11ef6, 0x11fb0...0x11fb0, 0x12000...0x12399, 0x12400...0x1246e, 0x12480...0x12543, 0x13000...0x1342e, 0x14400...0x14646, 0x16800...0x16a38, 0x16a40...0x16a5e, 0x16a60...0x16a69, 0x16ad0...0x16aed, 0x16af0...0x16af4, 0x16b00...0x16b36, 0x16b40...0x16b43, 0x16b50...0x16b59, 0x16b63...0x16b77, 0x16b7d...0x16b8f, 0x16e40...0x16e7f, 0x16f00...0x16f4a, 0x16f4f...0x16f87, 0x16f8f...0x16f9f, 0x16fe0...0x16fe1, 0x16fe3...0x16fe4, 0x16ff0...0x16ff1, 0x17000...0x187f7, 0x18800...0x18cd5, 0x18d00...0x18d08, 0x1b000...0x1b11e, 0x1b150...0x1b152, 0x1b164...0x1b167, 0x1b170...0x1b2fb, 0x1bc00...0x1bc6a, 0x1bc70...0x1bc7c, 0x1bc80...0x1bc88, 0x1bc90...0x1bc99, 0x1bc9d...0x1bc9e, 0x1d165...0x1d169, 0x1d16d...0x1d172, 0x1d17b...0x1d182, 0x1d185...0x1d18b, 0x1d1aa...0x1d1ad, 0x1d242...0x1d244, 0x1d400...0x1d454, 0x1d456...0x1d49c, 0x1d49e...0x1d49f, 0x1d4a2...0x1d4a2, 0x1d4a5...0x1d4a6, 0x1d4a9...0x1d4ac, 0x1d4ae...0x1d4b9, 0x1d4bb...0x1d4bb, 0x1d4bd...0x1d4c3, 0x1d4c5...0x1d505, 0x1d507...0x1d50a, 0x1d50d...0x1d514, 0x1d516...0x1d51c, 0x1d51e...0x1d539, 0x1d53b...0x1d53e, 0x1d540...0x1d544, 0x1d546...0x1d546, 0x1d54a...0x1d550, 0x1d552...0x1d6a5, 0x1d6a8...0x1d6c0, 0x1d6c2...0x1d6da, 0x1d6dc...0x1d6fa, 0x1d6fc...0x1d714, 0x1d716...0x1d734, 0x1d736...0x1d74e, 0x1d750...0x1d76e, 0x1d770...0x1d788, 0x1d78a...0x1d7a8, 0x1d7aa...0x1d7c2, 0x1d7c4...0x1d7cb, 0x1d7ce...0x1d7ff, 0x1da00...0x1da36, 0x1da3b...0x1da6c, 0x1da75...0x1da75, 0x1da84...0x1da84, 0x1da9b...0x1da9f, 0x1daa1...0x1daaf, 0x1e000...0x1e006, 0x1e008...0x1e018, 0x1e01b...0x1e021, 0x1e023...0x1e024, 0x1e026...0x1e02a, 0x1e100...0x1e12c, 0x1e130...0x1e13d, 0x1e140...0x1e149, 0x1e14e...0x1e14e, 0x1e2c0...0x1e2f9, 0x1e800...0x1e8c4, 0x1e8d0...0x1e8d6, 0x1e900...0x1e94b, 0x1e950...0x1e959, 0x1ee00...0x1ee03, 0x1ee05...0x1ee1f, 0x1ee21...0x1ee22, 0x1ee24...0x1ee24, 0x1ee27...0x1ee27, 0x1ee29...0x1ee32, 0x1ee34...0x1ee37, 0x1ee39...0x1ee39, 0x1ee3b...0x1ee3b, 0x1ee42...0x1ee42, 0x1ee47...0x1ee47, 0x1ee49...0x1ee49, 0x1ee4b...0x1ee4b, 0x1ee4d...0x1ee4f, 0x1ee51...0x1ee52, 0x1ee54...0x1ee54, 0x1ee57...0x1ee57, 0x1ee59...0x1ee59, 0x1ee5b...0x1ee5b, 0x1ee5d...0x1ee5d, 0x1ee5f...0x1ee5f, 0x1ee61...0x1ee62, 0x1ee64...0x1ee64, 0x1ee67...0x1ee6a, 0x1ee6c...0x1ee72, 0x1ee74...0x1ee77, 0x1ee79...0x1ee7c, 0x1ee7e...0x1ee7e, 0x1ee80...0x1ee89, 0x1ee8b...0x1ee9b, 0x1eea1...0x1eea3, 0x1eea5...0x1eea9, 0x1eeab...0x1eebb, 0x1fbf0...0x1fbf9, 0x20000...0x2a6dd, 0x2a700...0x2b734, 0x2b740...0x2b81d, 0x2b820...0x2cea1, 0x2ceb0...0x2ebe0, 0x2f800...0x2fa1d, 0x30000...0x3134a, 0xe0100...0xe01ef => true, - else => false, - }; - } - - fn isIdentifierStartSlow16(codepoint: u16) bool { - return switch (codepoint) { - 0xaa...0xaa, 0xb5...0xb5, 0xba...0xba, 0xc0...0xd6, 0xd8...0xf6, 0xf8...0x2c1, 0x2c6...0x2d1, 0x2e0...0x2e4, 0x2ec...0x2ec, 0x2ee...0x2ee, 0x370...0x374, 0x376...0x377, 0x37a...0x37d, 0x37f...0x37f, 0x386...0x386, 0x388...0x38a, 0x38c...0x38c, 0x38e...0x3a1, 0x3a3...0x3f5, 0x3f7...0x481, 0x48a...0x52f, 0x531...0x556, 0x559...0x559, 0x560...0x588, 0x5d0...0x5ea, 0x5ef...0x5f2, 0x620...0x64a, 0x66e...0x66f, 0x671...0x6d3, 0x6d5...0x6d5, 0x6e5...0x6e6, 0x6ee...0x6ef, 0x6fa...0x6fc, 0x6ff...0x6ff, 0x710...0x710, 0x712...0x72f, 0x74d...0x7a5, 0x7b1...0x7b1, 0x7ca...0x7ea, 0x7f4...0x7f5, 0x7fa...0x7fa, 0x800...0x815, 0x81a...0x81a, 0x824...0x824, 0x828...0x828, 0x840...0x858, 0x860...0x86a, 0x8a0...0x8b4, 0x8b6...0x8c7, 0x904...0x939, 0x93d...0x93d, 0x950...0x950, 0x958...0x961, 0x971...0x980, 0x985...0x98c, 0x98f...0x990, 0x993...0x9a8, 0x9aa...0x9b0, 0x9b2...0x9b2, 0x9b6...0x9b9, 0x9bd...0x9bd, 0x9ce...0x9ce, 0x9dc...0x9dd, 0x9df...0x9e1, 0x9f0...0x9f1, 0x9fc...0x9fc, 0xa05...0xa0a, 0xa0f...0xa10, 0xa13...0xa28, 0xa2a...0xa30, 0xa32...0xa33, 0xa35...0xa36, 0xa38...0xa39, 0xa59...0xa5c, 0xa5e...0xa5e, 0xa72...0xa74, 0xa85...0xa8d, 0xa8f...0xa91, 0xa93...0xaa8, 0xaaa...0xab0, 0xab2...0xab3, 0xab5...0xab9, 0xabd...0xabd, 0xad0...0xad0, 0xae0...0xae1, 0xaf9...0xaf9, 0xb05...0xb0c, 0xb0f...0xb10, 0xb13...0xb28, 0xb2a...0xb30, 0xb32...0xb33, 0xb35...0xb39, 0xb3d...0xb3d, 0xb5c...0xb5d, 0xb5f...0xb61, 0xb71...0xb71, 0xb83...0xb83, 0xb85...0xb8a, 0xb8e...0xb90, 0xb92...0xb95, 0xb99...0xb9a, 0xb9c...0xb9c, 0xb9e...0xb9f, 0xba3...0xba4, 0xba8...0xbaa, 0xbae...0xbb9, 0xbd0...0xbd0, 0xc05...0xc0c, 0xc0e...0xc10, 0xc12...0xc28, 0xc2a...0xc39, 0xc3d...0xc3d, 0xc58...0xc5a, 0xc60...0xc61, 0xc80...0xc80, 0xc85...0xc8c, 0xc8e...0xc90, 0xc92...0xca8, 0xcaa...0xcb3, 0xcb5...0xcb9, 0xcbd...0xcbd, 0xcde...0xcde, 0xce0...0xce1, 0xcf1...0xcf2, 0xd04...0xd0c, 0xd0e...0xd10, 0xd12...0xd3a, 0xd3d...0xd3d, 0xd4e...0xd4e, 0xd54...0xd56, 0xd5f...0xd61, 0xd7a...0xd7f, 0xd85...0xd96, 0xd9a...0xdb1, 0xdb3...0xdbb, 0xdbd...0xdbd, 0xdc0...0xdc6, 0xe01...0xe30, 0xe32...0xe33, 0xe40...0xe46, 0xe81...0xe82, 0xe84...0xe84, 0xe86...0xe8a, 0xe8c...0xea3, 0xea5...0xea5, 0xea7...0xeb0, 0xeb2...0xeb3, 0xebd...0xebd, 0xec0...0xec4, 0xec6...0xec6, 0xedc...0xedf, 0xf00...0xf00, 0xf40...0xf47, 0xf49...0xf6c, 0xf88...0xf8c, 0x1000...0x102a, 0x103f...0x103f, 0x1050...0x1055, 0x105a...0x105d, 0x1061...0x1061, 0x1065...0x1066, 0x106e...0x1070, 0x1075...0x1081, 0x108e...0x108e, 0x10a0...0x10c5, 0x10c7...0x10c7, 0x10cd...0x10cd, 0x10d0...0x10fa, 0x10fc...0x1248, 0x124a...0x124d, 0x1250...0x1256, 0x1258...0x1258, 0x125a...0x125d, 0x1260...0x1288, 0x128a...0x128d, 0x1290...0x12b0, 0x12b2...0x12b5, 0x12b8...0x12be, 0x12c0...0x12c0, 0x12c2...0x12c5, 0x12c8...0x12d6, 0x12d8...0x1310, 0x1312...0x1315, 0x1318...0x135a, 0x1380...0x138f, 0x13a0...0x13f5, 0x13f8...0x13fd, 0x1401...0x166c, 0x166f...0x167f, 0x1681...0x169a, 0x16a0...0x16ea, 0x16ee...0x16f8, 0x1700...0x170c, 0x170e...0x1711, 0x1720...0x1731, 0x1740...0x1751, 0x1760...0x176c, 0x176e...0x1770, 0x1780...0x17b3, 0x17d7...0x17d7, 0x17dc...0x17dc, 0x1820...0x1878, 0x1880...0x18a8, 0x18aa...0x18aa, 0x18b0...0x18f5, 0x1900...0x191e, 0x1950...0x196d, 0x1970...0x1974, 0x1980...0x19ab, 0x19b0...0x19c9, 0x1a00...0x1a16, 0x1a20...0x1a54, 0x1aa7...0x1aa7, 0x1b05...0x1b33, 0x1b45...0x1b4b, 0x1b83...0x1ba0, 0x1bae...0x1baf, 0x1bba...0x1be5, 0x1c00...0x1c23, 0x1c4d...0x1c4f, 0x1c5a...0x1c7d, 0x1c80...0x1c88, 0x1c90...0x1cba, 0x1cbd...0x1cbf, 0x1ce9...0x1cec, 0x1cee...0x1cf3, 0x1cf5...0x1cf6, 0x1cfa...0x1cfa, 0x1d00...0x1dbf, 0x1e00...0x1f15, 0x1f18...0x1f1d, 0x1f20...0x1f45, 0x1f48...0x1f4d, 0x1f50...0x1f57, 0x1f59...0x1f59, 0x1f5b...0x1f5b, 0x1f5d...0x1f5d, 0x1f5f...0x1f7d, 0x1f80...0x1fb4, 0x1fb6...0x1fbc, 0x1fbe...0x1fbe, 0x1fc2...0x1fc4, 0x1fc6...0x1fcc, 0x1fd0...0x1fd3, 0x1fd6...0x1fdb, 0x1fe0...0x1fec, 0x1ff2...0x1ff4, 0x1ff6...0x1ffc, 0x2071...0x2071, 0x207f...0x207f, 0x2090...0x209c, 0x2102...0x2102, 0x2107...0x2107, 0x210a...0x2113, 0x2115...0x2115, 0x2118...0x211d, 0x2124...0x2124, 0x2126...0x2126, 0x2128...0x2128, 0x212a...0x2139, 0x213c...0x213f, 0x2145...0x2149, 0x214e...0x214e, 0x2160...0x2188, 0x2c00...0x2c2e, 0x2c30...0x2c5e, 0x2c60...0x2ce4, 0x2ceb...0x2cee, 0x2cf2...0x2cf3, 0x2d00...0x2d25, 0x2d27...0x2d27, 0x2d2d...0x2d2d, 0x2d30...0x2d67, 0x2d6f...0x2d6f, 0x2d80...0x2d96, 0x2da0...0x2da6, 0x2da8...0x2dae, 0x2db0...0x2db6, 0x2db8...0x2dbe, 0x2dc0...0x2dc6, 0x2dc8...0x2dce, 0x2dd0...0x2dd6, 0x2dd8...0x2dde, 0x3005...0x3007, 0x3021...0x3029, 0x3031...0x3035, 0x3038...0x303c, 0x3041...0x3096, 0x309b...0x309f, 0x30a1...0x30fa, 0x30fc...0x30ff, 0x3105...0x312f, 0x3131...0x318e, 0x31a0...0x31bf, 0x31f0...0x31ff, 0x3400...0x4dbf, 0x4e00...0x9ffc, 0xa000...0xa48c, 0xa4d0...0xa4fd, 0xa500...0xa60c, 0xa610...0xa61f, 0xa62a...0xa62b, 0xa640...0xa66e, 0xa67f...0xa69d, 0xa6a0...0xa6ef, 0xa717...0xa71f, 0xa722...0xa788, 0xa78b...0xa7bf, 0xa7c2...0xa7ca, 0xa7f5...0xa801, 0xa803...0xa805, 0xa807...0xa80a, 0xa80c...0xa822, 0xa840...0xa873, 0xa882...0xa8b3, 0xa8f2...0xa8f7, 0xa8fb...0xa8fb, 0xa8fd...0xa8fe, 0xa90a...0xa925, 0xa930...0xa946, 0xa960...0xa97c, 0xa984...0xa9b2, 0xa9cf...0xa9cf, 0xa9e0...0xa9e4, 0xa9e6...0xa9ef, 0xa9fa...0xa9fe, 0xaa00...0xaa28, 0xaa40...0xaa42, 0xaa44...0xaa4b, 0xaa60...0xaa76, 0xaa7a...0xaa7a, 0xaa7e...0xaaaf, 0xaab1...0xaab1, 0xaab5...0xaab6, 0xaab9...0xaabd, 0xaac0...0xaac0, 0xaac2...0xaac2, 0xaadb...0xaadd, 0xaae0...0xaaea, 0xaaf2...0xaaf4, 0xab01...0xab06, 0xab09...0xab0e, 0xab11...0xab16, 0xab20...0xab26, 0xab28...0xab2e, 0xab30...0xab5a, 0xab5c...0xab69, 0xab70...0xabe2, 0xac00...0xd7a3, 0xd7b0...0xd7c6, 0xd7cb...0xd7fb, 0xf900...0xfa6d, 0xfa70...0xfad9, 0xfb00...0xfb06, 0xfb13...0xfb17, 0xfb1d...0xfb1d, 0xfb1f...0xfb28, 0xfb2a...0xfb36, 0xfb38...0xfb3c, 0xfb3e...0xfb3e, 0xfb40...0xfb41, 0xfb43...0xfb44, 0xfb46...0xfbb1, 0xfbd3...0xfd3d, 0xfd50...0xfd8f, 0xfd92...0xfdc7 => true, - else => false, - }; - } - - fn isIdentifierStartSlow32(codepoint: i32) bool { - return switch (codepoint) { - 0xfdf0...0xfdfb, 0xfe70...0xfe74, 0xfe76...0xfefc, 0xff21...0xff3a, 0xff41...0xff5a, 0xff66...0xffbe, 0xffc2...0xffc7, 0xffca...0xffcf, 0xffd2...0xffd7, 0xffda...0xffdc, 0x10000...0x1000b, 0x1000d...0x10026, 0x10028...0x1003a, 0x1003c...0x1003d, 0x1003f...0x1004d, 0x10050...0x1005d, 0x10080...0x100fa, 0x10140...0x10174, 0x10280...0x1029c, 0x102a0...0x102d0, 0x10300...0x1031f, 0x1032d...0x1034a, 0x10350...0x10375, 0x10380...0x1039d, 0x103a0...0x103c3, 0x103c8...0x103cf, 0x103d1...0x103d5, 0x10400...0x1049d, 0x104b0...0x104d3, 0x104d8...0x104fb, 0x10500...0x10527, 0x10530...0x10563, 0x10600...0x10736, 0x10740...0x10755, 0x10760...0x10767, 0x10800...0x10805, 0x10808...0x10808, 0x1080a...0x10835, 0x10837...0x10838, 0x1083c...0x1083c, 0x1083f...0x10855, 0x10860...0x10876, 0x10880...0x1089e, 0x108e0...0x108f2, 0x108f4...0x108f5, 0x10900...0x10915, 0x10920...0x10939, 0x10980...0x109b7, 0x109be...0x109bf, 0x10a00...0x10a00, 0x10a10...0x10a13, 0x10a15...0x10a17, 0x10a19...0x10a35, 0x10a60...0x10a7c, 0x10a80...0x10a9c, 0x10ac0...0x10ac7, 0x10ac9...0x10ae4, 0x10b00...0x10b35, 0x10b40...0x10b55, 0x10b60...0x10b72, 0x10b80...0x10b91, 0x10c00...0x10c48, 0x10c80...0x10cb2, 0x10cc0...0x10cf2, 0x10d00...0x10d23, 0x10e80...0x10ea9, 0x10eb0...0x10eb1, 0x10f00...0x10f1c, 0x10f27...0x10f27, 0x10f30...0x10f45, 0x10fb0...0x10fc4, 0x10fe0...0x10ff6, 0x11003...0x11037, 0x11083...0x110af, 0x110d0...0x110e8, 0x11103...0x11126, 0x11144...0x11144, 0x11147...0x11147, 0x11150...0x11172, 0x11176...0x11176, 0x11183...0x111b2, 0x111c1...0x111c4, 0x111da...0x111da, 0x111dc...0x111dc, 0x11200...0x11211, 0x11213...0x1122b, 0x11280...0x11286, 0x11288...0x11288, 0x1128a...0x1128d, 0x1128f...0x1129d, 0x1129f...0x112a8, 0x112b0...0x112de, 0x11305...0x1130c, 0x1130f...0x11310, 0x11313...0x11328, 0x1132a...0x11330, 0x11332...0x11333, 0x11335...0x11339, 0x1133d...0x1133d, 0x11350...0x11350, 0x1135d...0x11361, 0x11400...0x11434, 0x11447...0x1144a, 0x1145f...0x11461, 0x11480...0x114af, 0x114c4...0x114c5, 0x114c7...0x114c7, 0x11580...0x115ae, 0x115d8...0x115db, 0x11600...0x1162f, 0x11644...0x11644, 0x11680...0x116aa, 0x116b8...0x116b8, 0x11700...0x1171a, 0x11800...0x1182b, 0x118a0...0x118df, 0x118ff...0x11906, 0x11909...0x11909, 0x1190c...0x11913, 0x11915...0x11916, 0x11918...0x1192f, 0x1193f...0x1193f, 0x11941...0x11941, 0x119a0...0x119a7, 0x119aa...0x119d0, 0x119e1...0x119e1, 0x119e3...0x119e3, 0x11a00...0x11a00, 0x11a0b...0x11a32, 0x11a3a...0x11a3a, 0x11a50...0x11a50, 0x11a5c...0x11a89, 0x11a9d...0x11a9d, 0x11ac0...0x11af8, 0x11c00...0x11c08, 0x11c0a...0x11c2e, 0x11c40...0x11c40, 0x11c72...0x11c8f, 0x11d00...0x11d06, 0x11d08...0x11d09, 0x11d0b...0x11d30, 0x11d46...0x11d46, 0x11d60...0x11d65, 0x11d67...0x11d68, 0x11d6a...0x11d89, 0x11d98...0x11d98, 0x11ee0...0x11ef2, 0x11fb0...0x11fb0, 0x12000...0x12399, 0x12400...0x1246e, 0x12480...0x12543, 0x13000...0x1342e, 0x14400...0x14646, 0x16800...0x16a38, 0x16a40...0x16a5e, 0x16ad0...0x16aed, 0x16b00...0x16b2f, 0x16b40...0x16b43, 0x16b63...0x16b77, 0x16b7d...0x16b8f, 0x16e40...0x16e7f, 0x16f00...0x16f4a, 0x16f50...0x16f50, 0x16f93...0x16f9f, 0x16fe0...0x16fe1, 0x16fe3...0x16fe3, 0x17000...0x187f7, 0x18800...0x18cd5, 0x18d00...0x18d08, 0x1b000...0x1b11e, 0x1b150...0x1b152, 0x1b164...0x1b167, 0x1b170...0x1b2fb, 0x1bc00...0x1bc6a, 0x1bc70...0x1bc7c, 0x1bc80...0x1bc88, 0x1bc90...0x1bc99, 0x1d400...0x1d454, 0x1d456...0x1d49c, 0x1d49e...0x1d49f, 0x1d4a2...0x1d4a2, 0x1d4a5...0x1d4a6, 0x1d4a9...0x1d4ac, 0x1d4ae...0x1d4b9, 0x1d4bb...0x1d4bb, 0x1d4bd...0x1d4c3, 0x1d4c5...0x1d505, 0x1d507...0x1d50a, 0x1d50d...0x1d514, 0x1d516...0x1d51c, 0x1d51e...0x1d539, 0x1d53b...0x1d53e, 0x1d540...0x1d544, 0x1d546...0x1d546, 0x1d54a...0x1d550, 0x1d552...0x1d6a5, 0x1d6a8...0x1d6c0, 0x1d6c2...0x1d6da, 0x1d6dc...0x1d6fa, 0x1d6fc...0x1d714, 0x1d716...0x1d734, 0x1d736...0x1d74e, 0x1d750...0x1d76e, 0x1d770...0x1d788, 0x1d78a...0x1d7a8, 0x1d7aa...0x1d7c2, 0x1d7c4...0x1d7cb, 0x1e100...0x1e12c, 0x1e137...0x1e13d, 0x1e14e...0x1e14e, 0x1e2c0...0x1e2eb, 0x1e800...0x1e8c4, 0x1e900...0x1e943, 0x1e94b...0x1e94b, 0x1ee00...0x1ee03, 0x1ee05...0x1ee1f, 0x1ee21...0x1ee22, 0x1ee24...0x1ee24, 0x1ee27...0x1ee27, 0x1ee29...0x1ee32, 0x1ee34...0x1ee37, 0x1ee39...0x1ee39, 0x1ee3b...0x1ee3b, 0x1ee42...0x1ee42, 0x1ee47...0x1ee47, 0x1ee49...0x1ee49, 0x1ee4b...0x1ee4b, 0x1ee4d...0x1ee4f, 0x1ee51...0x1ee52, 0x1ee54...0x1ee54, 0x1ee57...0x1ee57, 0x1ee59...0x1ee59, 0x1ee5b...0x1ee5b, 0x1ee5d...0x1ee5d, 0x1ee5f...0x1ee5f, 0x1ee61...0x1ee62, 0x1ee64...0x1ee64, 0x1ee67...0x1ee6a, 0x1ee6c...0x1ee72, 0x1ee74...0x1ee77, 0x1ee79...0x1ee7c, 0x1ee7e...0x1ee7e, 0x1ee80...0x1ee89, 0x1ee8b...0x1ee9b, 0x1eea1...0x1eea3, 0x1eea5...0x1eea9, 0x1eeab...0x1eebb, 0x20000...0x2a6dd, 0x2a700...0x2b734, 0x2b740...0x2b81d, 0x2b820...0x2cea1, 0x2ceb0...0x2ebe0, 0x2f800...0x2fa1d, 0x30000...0x3134a => true, - else => false, - }; - } - - noinline fn isIdentifierStartSlow(codepoint: i32) bool { - @setCold(true); - return switch (codepoint) { - // explicitly tell LLVM's optimizer about values we know will not be in the range of this switch statement - - (max_codepoint + 1)...maxInt(i32), minInt(i32)...127 => unreachable, - 128...0xfdc7 => isIdentifierStartSlow16(@as(u16, @intCast(codepoint))), - 0xfdf0...0x3134a => isIdentifierStartSlow32(codepoint), - else => false, - }; - } - - pub inline fn isIdentifierStart(codepoint: i32) bool { - return switch (codepoint) { - 'A'...'Z', 'a'...'z', '$', '_' => true, - else => if (codepoint < 128) - return false - else - return isIdentifierStartSlow(codepoint), - }; - } - - pub inline fn isIdentifierPart(codepoint: i32) bool { - return switch (codepoint) { - 'A'...'Z', 'a'...'z', '0'...'9', '$', '_' => true, - else => if (codepoint < 128) - return false - else if (codepoint == 0x200C or codepoint == 0x200D) - return true - else - return isIdentifierPartSlow(codepoint), - }; - } +/// isIDContinueES5 checks if a codepoint is valid in the isIDContinueES5 category +pub fn isIDContinueES5(cp: u21) bool { + const high = cp >> 8; + const low = cp & 0xFF; + const stage2_idx = idContinueES5.stage1[high]; + const bit_pos = stage2_idx + low; + const u64_idx = bit_pos >> 6; + const bit_idx = @as(u6, @intCast(bit_pos & 63)); + return (idContinueES5.stage2[u64_idx] & (@as(u64, 1) << bit_idx)) != 0; +} +const idContinueES5 = struct { + pub const stage1 = [_]u16{ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096, 4352, 4608, 4864, 5120, 256, 5376, 5632, 5888, 2048, 2048, 2048, 2048, 2048, 6144, 6400, 6656, 6912, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 7168, 7424, 2048, 2048, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 7680, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 7936, 256, 256, 256, 256, 8192, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 8448, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 256, 8704, 8960, 256, 9216, 9472, 9728, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048 }; + pub const stage2 = [_]u64{ 287948901175001088, 576460745995190270, 297241973452963840, 18410715276682199039, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4503586742468607, 18446744073709486080, 18014187403249451007, 70501888360451, 18446744073709551615, 288230406216515583, 18446744056529672000, 4503599577006079, 18446744073709551615, 18446744073709551615, 18446744073709547643, 234187180623206815, 18446181123756130304, 18446744065161560063, 13546827661950451967, 1979120929931286, 576460743713488896, 18446466992488579071, 18446744073709551615, 2305629702346244095, 18446497783104864256, 2047, 562949953421311, 0, 0, 0, 0, 0, 17582052945254416366, 281268803551231, 15259882188367831022, 1125692414638495, 15235112390417287140, 9006925953907079, 17576984196650086382, 281204393851839, 17567976997395210222, 281215949093263, 14105211467435198444, 280925229301191, 14118782632783110126, 281212990012895, 14118782632783110124, 281214063754719, 14123286232410480620, 281212992110031, 3457638613854978028, 3377704004977791, 576460752303423486, 67076095, 4323434403644581270, 872365919, 14024213633433600001, 18446189919849152255, 2305843009196855263, 64, 272457864671395839, 67044351, 18446744069414584320, 36028797018898495, 18446744073709551615, 18446744071629176831, 18446743008557662207, 288230376151711743, 18446744073709551487, 18446744070446333311, 9168625153884503423, 18446603336212774717, 18446744071549321215, 1123701017804671, 18446744069414584320, 9007199254740991, 18446744073709551614, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 35923243902697471, 18446744069548802046, 8796093022207, 0, 0, 18446744073709551615, 4393752592383, 18446744069481627648, 72057594037927935, 4398046511103, 0, 18446744073709551615, 18446744073709551615, 18446744069683019775, 288230376151711743, 18446744070475743231, 4611686017001275199, 6908521828386340863, 2295745090394464220, 9223372036854775808, 9223372036854775809, 0, 9126739968, 287031153606524036, 0, 0, 0, 17728525486260320, 18446744073709551614, 18446744066832990207, 9223372036854775807, 18446216308128219104, 18446744073709551615, 72057589742993407, 0, 18446744073709551615, 18446744073709551615, 18014398509481983, 0, 18446744073709551615, 18446744073709551615, 274877906943, 0, 18446744073709551615, 18446744073709551615, 8191, 0, 18446744073709551615, 18446744073709551615, 68719476735, 0, 70368744177663, 0, 0, 0, 6881498031078244479, 18446744073709551579, 1125899906842623, 18446744073709027328, 4611686018427387903, 18446744073709486080, 18446744073709355007, 1152640029630136575, 6755463865565184, 18435203599664472064, 18446744073709551615, 2305843009213693951, 9799832780635308032, 18446743936404815870, 9223372036854775807, 486341884 }; }; -pub const JumpTableInline = struct { - pub inline fn isIdentifierStart(codepoint: i32) bool { - return switch (codepoint) { - 'A'...'Z', 'a'...'z', '$', '_' => true, - - else => switch (codepoint) { - 0x41...0x5a, 0x61...0x7a, 0xaa...0xaa, 0xb5...0xb5, 0xba...0xba, 0xc0...0xd6, 0xd8...0xf6, 0xf8...0x2c1, 0x2c6...0x2d1, 0x2e0...0x2e4, 0x2ec...0x2ec, 0x2ee...0x2ee, 0x370...0x374, 0x376...0x377, 0x37a...0x37d, 0x37f...0x37f, 0x386...0x386, 0x388...0x38a, 0x38c...0x38c, 0x38e...0x3a1, 0x3a3...0x3f5, 0x3f7...0x481, 0x48a...0x52f, 0x531...0x556, 0x559...0x559, 0x560...0x588, 0x5d0...0x5ea, 0x5ef...0x5f2, 0x620...0x64a, 0x66e...0x66f, 0x671...0x6d3, 0x6d5...0x6d5, 0x6e5...0x6e6, 0x6ee...0x6ef, 0x6fa...0x6fc, 0x6ff...0x6ff, 0x710...0x710, 0x712...0x72f, 0x74d...0x7a5, 0x7b1...0x7b1, 0x7ca...0x7ea, 0x7f4...0x7f5, 0x7fa...0x7fa, 0x800...0x815, 0x81a...0x81a, 0x824...0x824, 0x828...0x828, 0x840...0x858, 0x860...0x86a, 0x8a0...0x8b4, 0x8b6...0x8c7, 0x904...0x939, 0x93d...0x93d, 0x950...0x950, 0x958...0x961, 0x971...0x980, 0x985...0x98c, 0x98f...0x990, 0x993...0x9a8, 0x9aa...0x9b0, 0x9b2...0x9b2, 0x9b6...0x9b9, 0x9bd...0x9bd, 0x9ce...0x9ce, 0x9dc...0x9dd, 0x9df...0x9e1, 0x9f0...0x9f1, 0x9fc...0x9fc, 0xa05...0xa0a, 0xa0f...0xa10, 0xa13...0xa28, 0xa2a...0xa30, 0xa32...0xa33, 0xa35...0xa36, 0xa38...0xa39, 0xa59...0xa5c, 0xa5e...0xa5e, 0xa72...0xa74, 0xa85...0xa8d, 0xa8f...0xa91, 0xa93...0xaa8, 0xaaa...0xab0, 0xab2...0xab3, 0xab5...0xab9, 0xabd...0xabd, 0xad0...0xad0, 0xae0...0xae1, 0xaf9...0xaf9, 0xb05...0xb0c, 0xb0f...0xb10, 0xb13...0xb28, 0xb2a...0xb30, 0xb32...0xb33, 0xb35...0xb39, 0xb3d...0xb3d, 0xb5c...0xb5d, 0xb5f...0xb61, 0xb71...0xb71, 0xb83...0xb83, 0xb85...0xb8a, 0xb8e...0xb90, 0xb92...0xb95, 0xb99...0xb9a, 0xb9c...0xb9c, 0xb9e...0xb9f, 0xba3...0xba4, 0xba8...0xbaa, 0xbae...0xbb9, 0xbd0...0xbd0, 0xc05...0xc0c, 0xc0e...0xc10, 0xc12...0xc28, 0xc2a...0xc39, 0xc3d...0xc3d, 0xc58...0xc5a, 0xc60...0xc61, 0xc80...0xc80, 0xc85...0xc8c, 0xc8e...0xc90, 0xc92...0xca8, 0xcaa...0xcb3, 0xcb5...0xcb9, 0xcbd...0xcbd, 0xcde...0xcde, 0xce0...0xce1, 0xcf1...0xcf2, 0xd04...0xd0c, 0xd0e...0xd10, 0xd12...0xd3a, 0xd3d...0xd3d, 0xd4e...0xd4e, 0xd54...0xd56, 0xd5f...0xd61, 0xd7a...0xd7f, 0xd85...0xd96, 0xd9a...0xdb1, 0xdb3...0xdbb, 0xdbd...0xdbd, 0xdc0...0xdc6, 0xe01...0xe30, 0xe32...0xe33, 0xe40...0xe46, 0xe81...0xe82, 0xe84...0xe84, 0xe86...0xe8a, 0xe8c...0xea3, 0xea5...0xea5, 0xea7...0xeb0, 0xeb2...0xeb3, 0xebd...0xebd, 0xec0...0xec4, 0xec6...0xec6, 0xedc...0xedf, 0xf00...0xf00, 0xf40...0xf47, 0xf49...0xf6c, 0xf88...0xf8c, 0x1000...0x102a, 0x103f...0x103f, 0x1050...0x1055, 0x105a...0x105d, 0x1061...0x1061, 0x1065...0x1066, 0x106e...0x1070, 0x1075...0x1081, 0x108e...0x108e, 0x10a0...0x10c5, 0x10c7...0x10c7, 0x10cd...0x10cd, 0x10d0...0x10fa, 0x10fc...0x1248, 0x124a...0x124d, 0x1250...0x1256, 0x1258...0x1258, 0x125a...0x125d, 0x1260...0x1288, 0x128a...0x128d, 0x1290...0x12b0, 0x12b2...0x12b5, 0x12b8...0x12be, 0x12c0...0x12c0, 0x12c2...0x12c5, 0x12c8...0x12d6, 0x12d8...0x1310, 0x1312...0x1315, 0x1318...0x135a, 0x1380...0x138f, 0x13a0...0x13f5, 0x13f8...0x13fd, 0x1401...0x166c, 0x166f...0x167f, 0x1681...0x169a, 0x16a0...0x16ea, 0x16ee...0x16f8, 0x1700...0x170c, 0x170e...0x1711, 0x1720...0x1731, 0x1740...0x1751, 0x1760...0x176c, 0x176e...0x1770, 0x1780...0x17b3, 0x17d7...0x17d7, 0x17dc...0x17dc, 0x1820...0x1878, 0x1880...0x18a8, 0x18aa...0x18aa, 0x18b0...0x18f5, 0x1900...0x191e, 0x1950...0x196d, 0x1970...0x1974, 0x1980...0x19ab, 0x19b0...0x19c9, 0x1a00...0x1a16, 0x1a20...0x1a54, 0x1aa7...0x1aa7, 0x1b05...0x1b33, 0x1b45...0x1b4b, 0x1b83...0x1ba0, 0x1bae...0x1baf, 0x1bba...0x1be5, 0x1c00...0x1c23, 0x1c4d...0x1c4f, 0x1c5a...0x1c7d, 0x1c80...0x1c88, 0x1c90...0x1cba, 0x1cbd...0x1cbf, 0x1ce9...0x1cec, 0x1cee...0x1cf3, 0x1cf5...0x1cf6, 0x1cfa...0x1cfa, 0x1d00...0x1dbf, 0x1e00...0x1f15, 0x1f18...0x1f1d, 0x1f20...0x1f45, 0x1f48...0x1f4d, 0x1f50...0x1f57, 0x1f59...0x1f59, 0x1f5b...0x1f5b, 0x1f5d...0x1f5d, 0x1f5f...0x1f7d, 0x1f80...0x1fb4, 0x1fb6...0x1fbc, 0x1fbe...0x1fbe, 0x1fc2...0x1fc4, 0x1fc6...0x1fcc, 0x1fd0...0x1fd3, 0x1fd6...0x1fdb, 0x1fe0...0x1fec, 0x1ff2...0x1ff4, 0x1ff6...0x1ffc, 0x2071...0x2071, 0x207f...0x207f, 0x2090...0x209c, 0x2102...0x2102, 0x2107...0x2107, 0x210a...0x2113, 0x2115...0x2115, 0x2118...0x211d, 0x2124...0x2124, 0x2126...0x2126, 0x2128...0x2128, 0x212a...0x2139, 0x213c...0x213f, 0x2145...0x2149, 0x214e...0x214e, 0x2160...0x2188, 0x2c00...0x2c2e, 0x2c30...0x2c5e, 0x2c60...0x2ce4, 0x2ceb...0x2cee, 0x2cf2...0x2cf3, 0x2d00...0x2d25, 0x2d27...0x2d27, 0x2d2d...0x2d2d, 0x2d30...0x2d67, 0x2d6f...0x2d6f, 0x2d80...0x2d96, 0x2da0...0x2da6, 0x2da8...0x2dae, 0x2db0...0x2db6, 0x2db8...0x2dbe, 0x2dc0...0x2dc6, 0x2dc8...0x2dce, 0x2dd0...0x2dd6, 0x2dd8...0x2dde, 0x3005...0x3007, 0x3021...0x3029, 0x3031...0x3035, 0x3038...0x303c, 0x3041...0x3096, 0x309b...0x309f, 0x30a1...0x30fa, 0x30fc...0x30ff, 0x3105...0x312f, 0x3131...0x318e, 0x31a0...0x31bf, 0x31f0...0x31ff, 0x3400...0x4dbf, 0x4e00...0x9ffc, 0xa000...0xa48c, 0xa4d0...0xa4fd, 0xa500...0xa60c, 0xa610...0xa61f, 0xa62a...0xa62b, 0xa640...0xa66e, 0xa67f...0xa69d, 0xa6a0...0xa6ef, 0xa717...0xa71f, 0xa722...0xa788, 0xa78b...0xa7bf, 0xa7c2...0xa7ca, 0xa7f5...0xa801, 0xa803...0xa805, 0xa807...0xa80a, 0xa80c...0xa822, 0xa840...0xa873, 0xa882...0xa8b3, 0xa8f2...0xa8f7, 0xa8fb...0xa8fb, 0xa8fd...0xa8fe, 0xa90a...0xa925, 0xa930...0xa946, 0xa960...0xa97c, 0xa984...0xa9b2, 0xa9cf...0xa9cf, 0xa9e0...0xa9e4, 0xa9e6...0xa9ef, 0xa9fa...0xa9fe, 0xaa00...0xaa28, 0xaa40...0xaa42, 0xaa44...0xaa4b, 0xaa60...0xaa76, 0xaa7a...0xaa7a, 0xaa7e...0xaaaf, 0xaab1...0xaab1, 0xaab5...0xaab6, 0xaab9...0xaabd, 0xaac0...0xaac0, 0xaac2...0xaac2, 0xaadb...0xaadd, 0xaae0...0xaaea, 0xaaf2...0xaaf4, 0xab01...0xab06, 0xab09...0xab0e, 0xab11...0xab16, 0xab20...0xab26, 0xab28...0xab2e, 0xab30...0xab5a, 0xab5c...0xab69, 0xab70...0xabe2, 0xac00...0xd7a3, 0xd7b0...0xd7c6, 0xd7cb...0xd7fb, 0xf900...0xfa6d, 0xfa70...0xfad9, 0xfb00...0xfb06, 0xfb13...0xfb17, 0xfb1d...0xfb1d, 0xfb1f...0xfb28, 0xfb2a...0xfb36, 0xfb38...0xfb3c, 0xfb3e...0xfb3e, 0xfb40...0xfb41, 0xfb43...0xfb44, 0xfb46...0xfbb1, 0xfbd3...0xfd3d, 0xfd50...0xfd8f, 0xfd92...0xfdc7, 0xfdf0...0xfdfb, 0xfe70...0xfe74, 0xfe76...0xfefc, 0xff21...0xff3a, 0xff41...0xff5a, 0xff66...0xffbe, 0xffc2...0xffc7, 0xffca...0xffcf, 0xffd2...0xffd7, 0xffda...0xffdc, 0x10000...0x1000b, 0x1000d...0x10026, 0x10028...0x1003a, 0x1003c...0x1003d, 0x1003f...0x1004d, 0x10050...0x1005d, 0x10080...0x100fa, 0x10140...0x10174, 0x10280...0x1029c, 0x102a0...0x102d0, 0x10300...0x1031f, 0x1032d...0x1034a, 0x10350...0x10375, 0x10380...0x1039d, 0x103a0...0x103c3, 0x103c8...0x103cf, 0x103d1...0x103d5, 0x10400...0x1049d, 0x104b0...0x104d3, 0x104d8...0x104fb, 0x10500...0x10527, 0x10530...0x10563, 0x10600...0x10736, 0x10740...0x10755, 0x10760...0x10767, 0x10800...0x10805, 0x10808...0x10808, 0x1080a...0x10835, 0x10837...0x10838, 0x1083c...0x1083c, 0x1083f...0x10855, 0x10860...0x10876, 0x10880...0x1089e, 0x108e0...0x108f2, 0x108f4...0x108f5, 0x10900...0x10915, 0x10920...0x10939, 0x10980...0x109b7, 0x109be...0x109bf, 0x10a00...0x10a00, 0x10a10...0x10a13, 0x10a15...0x10a17, 0x10a19...0x10a35, 0x10a60...0x10a7c, 0x10a80...0x10a9c, 0x10ac0...0x10ac7, 0x10ac9...0x10ae4, 0x10b00...0x10b35, 0x10b40...0x10b55, 0x10b60...0x10b72, 0x10b80...0x10b91, 0x10c00...0x10c48, 0x10c80...0x10cb2, 0x10cc0...0x10cf2, 0x10d00...0x10d23, 0x10e80...0x10ea9, 0x10eb0...0x10eb1, 0x10f00...0x10f1c, 0x10f27...0x10f27, 0x10f30...0x10f45, 0x10fb0...0x10fc4, 0x10fe0...0x10ff6, 0x11003...0x11037, 0x11083...0x110af, 0x110d0...0x110e8, 0x11103...0x11126, 0x11144...0x11144, 0x11147...0x11147, 0x11150...0x11172, 0x11176...0x11176, 0x11183...0x111b2, 0x111c1...0x111c4, 0x111da...0x111da, 0x111dc...0x111dc, 0x11200...0x11211, 0x11213...0x1122b, 0x11280...0x11286, 0x11288...0x11288, 0x1128a...0x1128d, 0x1128f...0x1129d, 0x1129f...0x112a8, 0x112b0...0x112de, 0x11305...0x1130c, 0x1130f...0x11310, 0x11313...0x11328, 0x1132a...0x11330, 0x11332...0x11333, 0x11335...0x11339, 0x1133d...0x1133d, 0x11350...0x11350, 0x1135d...0x11361, 0x11400...0x11434, 0x11447...0x1144a, 0x1145f...0x11461, 0x11480...0x114af, 0x114c4...0x114c5, 0x114c7...0x114c7, 0x11580...0x115ae, 0x115d8...0x115db, 0x11600...0x1162f, 0x11644...0x11644, 0x11680...0x116aa, 0x116b8...0x116b8, 0x11700...0x1171a, 0x11800...0x1182b, 0x118a0...0x118df, 0x118ff...0x11906, 0x11909...0x11909, 0x1190c...0x11913, 0x11915...0x11916, 0x11918...0x1192f, 0x1193f...0x1193f, 0x11941...0x11941, 0x119a0...0x119a7, 0x119aa...0x119d0, 0x119e1...0x119e1, 0x119e3...0x119e3, 0x11a00...0x11a00, 0x11a0b...0x11a32, 0x11a3a...0x11a3a, 0x11a50...0x11a50, 0x11a5c...0x11a89, 0x11a9d...0x11a9d, 0x11ac0...0x11af8, 0x11c00...0x11c08, 0x11c0a...0x11c2e, 0x11c40...0x11c40, 0x11c72...0x11c8f, 0x11d00...0x11d06, 0x11d08...0x11d09, 0x11d0b...0x11d30, 0x11d46...0x11d46, 0x11d60...0x11d65, 0x11d67...0x11d68, 0x11d6a...0x11d89, 0x11d98...0x11d98, 0x11ee0...0x11ef2, 0x11fb0...0x11fb0, 0x12000...0x12399, 0x12400...0x1246e, 0x12480...0x12543, 0x13000...0x1342e, 0x14400...0x14646, 0x16800...0x16a38, 0x16a40...0x16a5e, 0x16ad0...0x16aed, 0x16b00...0x16b2f, 0x16b40...0x16b43, 0x16b63...0x16b77, 0x16b7d...0x16b8f, 0x16e40...0x16e7f, 0x16f00...0x16f4a, 0x16f50...0x16f50, 0x16f93...0x16f9f, 0x16fe0...0x16fe1, 0x16fe3...0x16fe3, 0x17000...0x187f7, 0x18800...0x18cd5, 0x18d00...0x18d08, 0x1b000...0x1b11e, 0x1b150...0x1b152, 0x1b164...0x1b167, 0x1b170...0x1b2fb, 0x1bc00...0x1bc6a, 0x1bc70...0x1bc7c, 0x1bc80...0x1bc88, 0x1bc90...0x1bc99, 0x1d400...0x1d454, 0x1d456...0x1d49c, 0x1d49e...0x1d49f, 0x1d4a2...0x1d4a2, 0x1d4a5...0x1d4a6, 0x1d4a9...0x1d4ac, 0x1d4ae...0x1d4b9, 0x1d4bb...0x1d4bb, 0x1d4bd...0x1d4c3, 0x1d4c5...0x1d505, 0x1d507...0x1d50a, 0x1d50d...0x1d514, 0x1d516...0x1d51c, 0x1d51e...0x1d539, 0x1d53b...0x1d53e, 0x1d540...0x1d544, 0x1d546...0x1d546, 0x1d54a...0x1d550, 0x1d552...0x1d6a5, 0x1d6a8...0x1d6c0, 0x1d6c2...0x1d6da, 0x1d6dc...0x1d6fa, 0x1d6fc...0x1d714, 0x1d716...0x1d734, 0x1d736...0x1d74e, 0x1d750...0x1d76e, 0x1d770...0x1d788, 0x1d78a...0x1d7a8, 0x1d7aa...0x1d7c2, 0x1d7c4...0x1d7cb, 0x1e100...0x1e12c, 0x1e137...0x1e13d, 0x1e14e...0x1e14e, 0x1e2c0...0x1e2eb, 0x1e800...0x1e8c4, 0x1e900...0x1e943, 0x1e94b...0x1e94b, 0x1ee00...0x1ee03, 0x1ee05...0x1ee1f, 0x1ee21...0x1ee22, 0x1ee24...0x1ee24, 0x1ee27...0x1ee27, 0x1ee29...0x1ee32, 0x1ee34...0x1ee37, 0x1ee39...0x1ee39, 0x1ee3b...0x1ee3b, 0x1ee42...0x1ee42, 0x1ee47...0x1ee47, 0x1ee49...0x1ee49, 0x1ee4b...0x1ee4b, 0x1ee4d...0x1ee4f, 0x1ee51...0x1ee52, 0x1ee54...0x1ee54, 0x1ee57...0x1ee57, 0x1ee59...0x1ee59, 0x1ee5b...0x1ee5b, 0x1ee5d...0x1ee5d, 0x1ee5f...0x1ee5f, 0x1ee61...0x1ee62, 0x1ee64...0x1ee64, 0x1ee67...0x1ee6a, 0x1ee6c...0x1ee72, 0x1ee74...0x1ee77, 0x1ee79...0x1ee7c, 0x1ee7e...0x1ee7e, 0x1ee80...0x1ee89, 0x1ee8b...0x1ee9b, 0x1eea1...0x1eea3, 0x1eea5...0x1eea9, 0x1eeab...0x1eebb, 0x20000...0x2a6dd, 0x2a700...0x2b734, 0x2b740...0x2b81d, 0x2b820...0x2cea1, 0x2ceb0...0x2ebe0, 0x2f800...0x2fa1d, 0x30000...0x3134a => true, - else => false, - }, - }; - } - - pub inline fn isIdentifierPart(codepoint: i32) bool { - return switch (codepoint) { - 'A'...'Z', 'a'...'z', '0'...'9', '$', '_' => true, - else => switch (codepoint) { - 0x30...0x39, 0x41...0x5a, 0x5f...0x5f, 0x61...0x7a, 0xaa...0xaa, 0xb5...0xb5, 0xb7...0xb7, 0xba...0xba, 0xc0...0xd6, 0xd8...0xf6, 0xf8...0x2c1, 0x2c6...0x2d1, 0x2e0...0x2e4, 0x2ec...0x2ec, 0x2ee...0x2ee, 0x300...0x374, 0x376...0x377, 0x37a...0x37d, 0x37f...0x37f, 0x386...0x38a, 0x38c...0x38c, 0x38e...0x3a1, 0x3a3...0x3f5, 0x3f7...0x481, 0x483...0x487, 0x48a...0x52f, 0x531...0x556, 0x559...0x559, 0x560...0x588, 0x591...0x5bd, 0x5bf...0x5bf, 0x5c1...0x5c2, 0x5c4...0x5c5, 0x5c7...0x5c7, 0x5d0...0x5ea, 0x5ef...0x5f2, 0x610...0x61a, 0x620...0x669, 0x66e...0x6d3, 0x6d5...0x6dc, 0x6df...0x6e8, 0x6ea...0x6fc, 0x6ff...0x6ff, 0x710...0x74a, 0x74d...0x7b1, 0x7c0...0x7f5, 0x7fa...0x7fa, 0x7fd...0x7fd, 0x800...0x82d, 0x840...0x85b, 0x860...0x86a, 0x8a0...0x8b4, 0x8b6...0x8c7, 0x8d3...0x8e1, 0x8e3...0x963, 0x966...0x96f, 0x971...0x983, 0x985...0x98c, 0x98f...0x990, 0x993...0x9a8, 0x9aa...0x9b0, 0x9b2...0x9b2, 0x9b6...0x9b9, 0x9bc...0x9c4, 0x9c7...0x9c8, 0x9cb...0x9ce, 0x9d7...0x9d7, 0x9dc...0x9dd, 0x9df...0x9e3, 0x9e6...0x9f1, 0x9fc...0x9fc, 0x9fe...0x9fe, 0xa01...0xa03, 0xa05...0xa0a, 0xa0f...0xa10, 0xa13...0xa28, 0xa2a...0xa30, 0xa32...0xa33, 0xa35...0xa36, 0xa38...0xa39, 0xa3c...0xa3c, 0xa3e...0xa42, 0xa47...0xa48, 0xa4b...0xa4d, 0xa51...0xa51, 0xa59...0xa5c, 0xa5e...0xa5e, 0xa66...0xa75, 0xa81...0xa83, 0xa85...0xa8d, 0xa8f...0xa91, 0xa93...0xaa8, 0xaaa...0xab0, 0xab2...0xab3, 0xab5...0xab9, 0xabc...0xac5, 0xac7...0xac9, 0xacb...0xacd, 0xad0...0xad0, 0xae0...0xae3, 0xae6...0xaef, 0xaf9...0xaff, 0xb01...0xb03, 0xb05...0xb0c, 0xb0f...0xb10, 0xb13...0xb28, 0xb2a...0xb30, 0xb32...0xb33, 0xb35...0xb39, 0xb3c...0xb44, 0xb47...0xb48, 0xb4b...0xb4d, 0xb55...0xb57, 0xb5c...0xb5d, 0xb5f...0xb63, 0xb66...0xb6f, 0xb71...0xb71, 0xb82...0xb83, 0xb85...0xb8a, 0xb8e...0xb90, 0xb92...0xb95, 0xb99...0xb9a, 0xb9c...0xb9c, 0xb9e...0xb9f, 0xba3...0xba4, 0xba8...0xbaa, 0xbae...0xbb9, 0xbbe...0xbc2, 0xbc6...0xbc8, 0xbca...0xbcd, 0xbd0...0xbd0, 0xbd7...0xbd7, 0xbe6...0xbef, 0xc00...0xc0c, 0xc0e...0xc10, 0xc12...0xc28, 0xc2a...0xc39, 0xc3d...0xc44, 0xc46...0xc48, 0xc4a...0xc4d, 0xc55...0xc56, 0xc58...0xc5a, 0xc60...0xc63, 0xc66...0xc6f, 0xc80...0xc83, 0xc85...0xc8c, 0xc8e...0xc90, 0xc92...0xca8, 0xcaa...0xcb3, 0xcb5...0xcb9, 0xcbc...0xcc4, 0xcc6...0xcc8, 0xcca...0xccd, 0xcd5...0xcd6, 0xcde...0xcde, 0xce0...0xce3, 0xce6...0xcef, 0xcf1...0xcf2, 0xd00...0xd0c, 0xd0e...0xd10, 0xd12...0xd44, 0xd46...0xd48, 0xd4a...0xd4e, 0xd54...0xd57, 0xd5f...0xd63, 0xd66...0xd6f, 0xd7a...0xd7f, 0xd81...0xd83, 0xd85...0xd96, 0xd9a...0xdb1, 0xdb3...0xdbb, 0xdbd...0xdbd, 0xdc0...0xdc6, 0xdca...0xdca, 0xdcf...0xdd4, 0xdd6...0xdd6, 0xdd8...0xddf, 0xde6...0xdef, 0xdf2...0xdf3, 0xe01...0xe3a, 0xe40...0xe4e, 0xe50...0xe59, 0xe81...0xe82, 0xe84...0xe84, 0xe86...0xe8a, 0xe8c...0xea3, 0xea5...0xea5, 0xea7...0xebd, 0xec0...0xec4, 0xec6...0xec6, 0xec8...0xecd, 0xed0...0xed9, 0xedc...0xedf, 0xf00...0xf00, 0xf18...0xf19, 0xf20...0xf29, 0xf35...0xf35, 0xf37...0xf37, 0xf39...0xf39, 0xf3e...0xf47, 0xf49...0xf6c, 0xf71...0xf84, 0xf86...0xf97, 0xf99...0xfbc, 0xfc6...0xfc6, 0x1000...0x1049, 0x1050...0x109d, 0x10a0...0x10c5, 0x10c7...0x10c7, 0x10cd...0x10cd, 0x10d0...0x10fa, 0x10fc...0x1248, 0x124a...0x124d, 0x1250...0x1256, 0x1258...0x1258, 0x125a...0x125d, 0x1260...0x1288, 0x128a...0x128d, 0x1290...0x12b0, 0x12b2...0x12b5, 0x12b8...0x12be, 0x12c0...0x12c0, 0x12c2...0x12c5, 0x12c8...0x12d6, 0x12d8...0x1310, 0x1312...0x1315, 0x1318...0x135a, 0x135d...0x135f, 0x1369...0x1371, 0x1380...0x138f, 0x13a0...0x13f5, 0x13f8...0x13fd, 0x1401...0x166c, 0x166f...0x167f, 0x1681...0x169a, 0x16a0...0x16ea, 0x16ee...0x16f8, 0x1700...0x170c, 0x170e...0x1714, 0x1720...0x1734, 0x1740...0x1753, 0x1760...0x176c, 0x176e...0x1770, 0x1772...0x1773, 0x1780...0x17d3, 0x17d7...0x17d7, 0x17dc...0x17dd, 0x17e0...0x17e9, 0x180b...0x180d, 0x1810...0x1819, 0x1820...0x1878, 0x1880...0x18aa, 0x18b0...0x18f5, 0x1900...0x191e, 0x1920...0x192b, 0x1930...0x193b, 0x1946...0x196d, 0x1970...0x1974, 0x1980...0x19ab, 0x19b0...0x19c9, 0x19d0...0x19da, 0x1a00...0x1a1b, 0x1a20...0x1a5e, 0x1a60...0x1a7c, 0x1a7f...0x1a89, 0x1a90...0x1a99, 0x1aa7...0x1aa7, 0x1ab0...0x1abd, 0x1abf...0x1ac0, 0x1b00...0x1b4b, 0x1b50...0x1b59, 0x1b6b...0x1b73, 0x1b80...0x1bf3, 0x1c00...0x1c37, 0x1c40...0x1c49, 0x1c4d...0x1c7d, 0x1c80...0x1c88, 0x1c90...0x1cba, 0x1cbd...0x1cbf, 0x1cd0...0x1cd2, 0x1cd4...0x1cfa, 0x1d00...0x1df9, 0x1dfb...0x1f15, 0x1f18...0x1f1d, 0x1f20...0x1f45, 0x1f48...0x1f4d, 0x1f50...0x1f57, 0x1f59...0x1f59, 0x1f5b...0x1f5b, 0x1f5d...0x1f5d, 0x1f5f...0x1f7d, 0x1f80...0x1fb4, 0x1fb6...0x1fbc, 0x1fbe...0x1fbe, 0x1fc2...0x1fc4, 0x1fc6...0x1fcc, 0x1fd0...0x1fd3, 0x1fd6...0x1fdb, 0x1fe0...0x1fec, 0x1ff2...0x1ff4, 0x1ff6...0x1ffc, 0x203f...0x2040, 0x2054...0x2054, 0x2071...0x2071, 0x207f...0x207f, 0x2090...0x209c, 0x20d0...0x20dc, 0x20e1...0x20e1, 0x20e5...0x20f0, 0x2102...0x2102, 0x2107...0x2107, 0x210a...0x2113, 0x2115...0x2115, 0x2118...0x211d, 0x2124...0x2124, 0x2126...0x2126, 0x2128...0x2128, 0x212a...0x2139, 0x213c...0x213f, 0x2145...0x2149, 0x214e...0x214e, 0x2160...0x2188, 0x2c00...0x2c2e, 0x2c30...0x2c5e, 0x2c60...0x2ce4, 0x2ceb...0x2cf3, 0x2d00...0x2d25, 0x2d27...0x2d27, 0x2d2d...0x2d2d, 0x2d30...0x2d67, 0x2d6f...0x2d6f, 0x2d7f...0x2d96, 0x2da0...0x2da6, 0x2da8...0x2dae, 0x2db0...0x2db6, 0x2db8...0x2dbe, 0x2dc0...0x2dc6, 0x2dc8...0x2dce, 0x2dd0...0x2dd6, 0x2dd8...0x2dde, 0x2de0...0x2dff, 0x3005...0x3007, 0x3021...0x302f, 0x3031...0x3035, 0x3038...0x303c, 0x3041...0x3096, 0x3099...0x309f, 0x30a1...0x30ff, 0x3105...0x312f, 0x3131...0x318e, 0x31a0...0x31bf, 0x31f0...0x31ff, 0x3400...0x4dbf, 0x4e00...0x9ffc, 0xa000...0xa48c, 0xa4d0...0xa4fd, 0xa500...0xa60c, 0xa610...0xa62b, 0xa640...0xa66f, 0xa674...0xa67d, 0xa67f...0xa6f1, 0xa717...0xa71f, 0xa722...0xa788, 0xa78b...0xa7bf, 0xa7c2...0xa7ca, 0xa7f5...0xa827, 0xa82c...0xa82c, 0xa840...0xa873, 0xa880...0xa8c5, 0xa8d0...0xa8d9, 0xa8e0...0xa8f7, 0xa8fb...0xa8fb, 0xa8fd...0xa92d, 0xa930...0xa953, 0xa960...0xa97c, 0xa980...0xa9c0, 0xa9cf...0xa9d9, 0xa9e0...0xa9fe, 0xaa00...0xaa36, 0xaa40...0xaa4d, 0xaa50...0xaa59, 0xaa60...0xaa76, 0xaa7a...0xaac2, 0xaadb...0xaadd, 0xaae0...0xaaef, 0xaaf2...0xaaf6, 0xab01...0xab06, 0xab09...0xab0e, 0xab11...0xab16, 0xab20...0xab26, 0xab28...0xab2e, 0xab30...0xab5a, 0xab5c...0xab69, 0xab70...0xabea, 0xabec...0xabed, 0xabf0...0xabf9, 0xac00...0xd7a3, 0xd7b0...0xd7c6, 0xd7cb...0xd7fb, 0xf900...0xfa6d, 0xfa70...0xfad9, 0xfb00...0xfb06, 0xfb13...0xfb17, 0xfb1d...0xfb28, 0xfb2a...0xfb36, 0xfb38...0xfb3c, 0xfb3e...0xfb3e, 0xfb40...0xfb41, 0xfb43...0xfb44, 0xfb46...0xfbb1, 0xfbd3...0xfd3d, 0xfd50...0xfd8f, 0xfd92...0xfdc7, 0xfdf0...0xfdfb, 0xfe00...0xfe0f, 0xfe20...0xfe2f, 0xfe33...0xfe34, 0xfe4d...0xfe4f, 0xfe70...0xfe74, 0xfe76...0xfefc, 0xff10...0xff19, 0xff21...0xff3a, 0xff3f...0xff3f, 0xff41...0xff5a, 0xff65...0xffbe, 0xffc2...0xffc7, 0xffca...0xffcf, 0xffd2...0xffd7, 0xffda...0xffdc, 0x10000...0x1000b, 0x1000d...0x10026, 0x10028...0x1003a, 0x1003c...0x1003d, 0x1003f...0x1004d, 0x10050...0x1005d, 0x10080...0x100fa, 0x10140...0x10174, 0x101fd...0x101fd, 0x10280...0x1029c, 0x102a0...0x102d0, 0x102e0...0x102e0, 0x10300...0x1031f, 0x1032d...0x1034a, 0x10350...0x1037a, 0x10380...0x1039d, 0x103a0...0x103c3, 0x103c8...0x103cf, 0x103d1...0x103d5, 0x10400...0x1049d, 0x104a0...0x104a9, 0x104b0...0x104d3, 0x104d8...0x104fb, 0x10500...0x10527, 0x10530...0x10563, 0x10600...0x10736, 0x10740...0x10755, 0x10760...0x10767, 0x10800...0x10805, 0x10808...0x10808, 0x1080a...0x10835, 0x10837...0x10838, 0x1083c...0x1083c, 0x1083f...0x10855, 0x10860...0x10876, 0x10880...0x1089e, 0x108e0...0x108f2, 0x108f4...0x108f5, 0x10900...0x10915, 0x10920...0x10939, 0x10980...0x109b7, 0x109be...0x109bf, 0x10a00...0x10a03, 0x10a05...0x10a06, 0x10a0c...0x10a13, 0x10a15...0x10a17, 0x10a19...0x10a35, 0x10a38...0x10a3a, 0x10a3f...0x10a3f, 0x10a60...0x10a7c, 0x10a80...0x10a9c, 0x10ac0...0x10ac7, 0x10ac9...0x10ae6, 0x10b00...0x10b35, 0x10b40...0x10b55, 0x10b60...0x10b72, 0x10b80...0x10b91, 0x10c00...0x10c48, 0x10c80...0x10cb2, 0x10cc0...0x10cf2, 0x10d00...0x10d27, 0x10d30...0x10d39, 0x10e80...0x10ea9, 0x10eab...0x10eac, 0x10eb0...0x10eb1, 0x10f00...0x10f1c, 0x10f27...0x10f27, 0x10f30...0x10f50, 0x10fb0...0x10fc4, 0x10fe0...0x10ff6, 0x11000...0x11046, 0x11066...0x1106f, 0x1107f...0x110ba, 0x110d0...0x110e8, 0x110f0...0x110f9, 0x11100...0x11134, 0x11136...0x1113f, 0x11144...0x11147, 0x11150...0x11173, 0x11176...0x11176, 0x11180...0x111c4, 0x111c9...0x111cc, 0x111ce...0x111da, 0x111dc...0x111dc, 0x11200...0x11211, 0x11213...0x11237, 0x1123e...0x1123e, 0x11280...0x11286, 0x11288...0x11288, 0x1128a...0x1128d, 0x1128f...0x1129d, 0x1129f...0x112a8, 0x112b0...0x112ea, 0x112f0...0x112f9, 0x11300...0x11303, 0x11305...0x1130c, 0x1130f...0x11310, 0x11313...0x11328, 0x1132a...0x11330, 0x11332...0x11333, 0x11335...0x11339, 0x1133b...0x11344, 0x11347...0x11348, 0x1134b...0x1134d, 0x11350...0x11350, 0x11357...0x11357, 0x1135d...0x11363, 0x11366...0x1136c, 0x11370...0x11374, 0x11400...0x1144a, 0x11450...0x11459, 0x1145e...0x11461, 0x11480...0x114c5, 0x114c7...0x114c7, 0x114d0...0x114d9, 0x11580...0x115b5, 0x115b8...0x115c0, 0x115d8...0x115dd, 0x11600...0x11640, 0x11644...0x11644, 0x11650...0x11659, 0x11680...0x116b8, 0x116c0...0x116c9, 0x11700...0x1171a, 0x1171d...0x1172b, 0x11730...0x11739, 0x11800...0x1183a, 0x118a0...0x118e9, 0x118ff...0x11906, 0x11909...0x11909, 0x1190c...0x11913, 0x11915...0x11916, 0x11918...0x11935, 0x11937...0x11938, 0x1193b...0x11943, 0x11950...0x11959, 0x119a0...0x119a7, 0x119aa...0x119d7, 0x119da...0x119e1, 0x119e3...0x119e4, 0x11a00...0x11a3e, 0x11a47...0x11a47, 0x11a50...0x11a99, 0x11a9d...0x11a9d, 0x11ac0...0x11af8, 0x11c00...0x11c08, 0x11c0a...0x11c36, 0x11c38...0x11c40, 0x11c50...0x11c59, 0x11c72...0x11c8f, 0x11c92...0x11ca7, 0x11ca9...0x11cb6, 0x11d00...0x11d06, 0x11d08...0x11d09, 0x11d0b...0x11d36, 0x11d3a...0x11d3a, 0x11d3c...0x11d3d, 0x11d3f...0x11d47, 0x11d50...0x11d59, 0x11d60...0x11d65, 0x11d67...0x11d68, 0x11d6a...0x11d8e, 0x11d90...0x11d91, 0x11d93...0x11d98, 0x11da0...0x11da9, 0x11ee0...0x11ef6, 0x11fb0...0x11fb0, 0x12000...0x12399, 0x12400...0x1246e, 0x12480...0x12543, 0x13000...0x1342e, 0x14400...0x14646, 0x16800...0x16a38, 0x16a40...0x16a5e, 0x16a60...0x16a69, 0x16ad0...0x16aed, 0x16af0...0x16af4, 0x16b00...0x16b36, 0x16b40...0x16b43, 0x16b50...0x16b59, 0x16b63...0x16b77, 0x16b7d...0x16b8f, 0x16e40...0x16e7f, 0x16f00...0x16f4a, 0x16f4f...0x16f87, 0x16f8f...0x16f9f, 0x16fe0...0x16fe1, 0x16fe3...0x16fe4, 0x16ff0...0x16ff1, 0x17000...0x187f7, 0x18800...0x18cd5, 0x18d00...0x18d08, 0x1b000...0x1b11e, 0x1b150...0x1b152, 0x1b164...0x1b167, 0x1b170...0x1b2fb, 0x1bc00...0x1bc6a, 0x1bc70...0x1bc7c, 0x1bc80...0x1bc88, 0x1bc90...0x1bc99, 0x1bc9d...0x1bc9e, 0x1d165...0x1d169, 0x1d16d...0x1d172, 0x1d17b...0x1d182, 0x1d185...0x1d18b, 0x1d1aa...0x1d1ad, 0x1d242...0x1d244, 0x1d400...0x1d454, 0x1d456...0x1d49c, 0x1d49e...0x1d49f, 0x1d4a2...0x1d4a2, 0x1d4a5...0x1d4a6, 0x1d4a9...0x1d4ac, 0x1d4ae...0x1d4b9, 0x1d4bb...0x1d4bb, 0x1d4bd...0x1d4c3, 0x1d4c5...0x1d505, 0x1d507...0x1d50a, 0x1d50d...0x1d514, 0x1d516...0x1d51c, 0x1d51e...0x1d539, 0x1d53b...0x1d53e, 0x1d540...0x1d544, 0x1d546...0x1d546, 0x1d54a...0x1d550, 0x1d552...0x1d6a5, 0x1d6a8...0x1d6c0, 0x1d6c2...0x1d6da, 0x1d6dc...0x1d6fa, 0x1d6fc...0x1d714, 0x1d716...0x1d734, 0x1d736...0x1d74e, 0x1d750...0x1d76e, 0x1d770...0x1d788, 0x1d78a...0x1d7a8, 0x1d7aa...0x1d7c2, 0x1d7c4...0x1d7cb, 0x1d7ce...0x1d7ff, 0x1da00...0x1da36, 0x1da3b...0x1da6c, 0x1da75...0x1da75, 0x1da84...0x1da84, 0x1da9b...0x1da9f, 0x1daa1...0x1daaf, 0x1e000...0x1e006, 0x1e008...0x1e018, 0x1e01b...0x1e021, 0x1e023...0x1e024, 0x1e026...0x1e02a, 0x1e100...0x1e12c, 0x1e130...0x1e13d, 0x1e140...0x1e149, 0x1e14e...0x1e14e, 0x1e2c0...0x1e2f9, 0x1e800...0x1e8c4, 0x1e8d0...0x1e8d6, 0x1e900...0x1e94b, 0x1e950...0x1e959, 0x1ee00...0x1ee03, 0x1ee05...0x1ee1f, 0x1ee21...0x1ee22, 0x1ee24...0x1ee24, 0x1ee27...0x1ee27, 0x1ee29...0x1ee32, 0x1ee34...0x1ee37, 0x1ee39...0x1ee39, 0x1ee3b...0x1ee3b, 0x1ee42...0x1ee42, 0x1ee47...0x1ee47, 0x1ee49...0x1ee49, 0x1ee4b...0x1ee4b, 0x1ee4d...0x1ee4f, 0x1ee51...0x1ee52, 0x1ee54...0x1ee54, 0x1ee57...0x1ee57, 0x1ee59...0x1ee59, 0x1ee5b...0x1ee5b, 0x1ee5d...0x1ee5d, 0x1ee5f...0x1ee5f, 0x1ee61...0x1ee62, 0x1ee64...0x1ee64, 0x1ee67...0x1ee6a, 0x1ee6c...0x1ee72, 0x1ee74...0x1ee77, 0x1ee79...0x1ee7c, 0x1ee7e...0x1ee7e, 0x1ee80...0x1ee89, 0x1ee8b...0x1ee9b, 0x1eea1...0x1eea3, 0x1eea5...0x1eea9, 0x1eeab...0x1eebb, 0x1fbf0...0x1fbf9, 0x20000...0x2a6dd, 0x2a700...0x2b734, 0x2b740...0x2b81d, 0x2b820...0x2cea1, 0x2ceb0...0x2ebe0, 0x2f800...0x2fa1d, 0x30000...0x3134a, 0xe0100...0xe01ef => true, - else => false, - }, - }; - } +/// isIDStartESNext checks if a codepoint is valid in the isIDStartESNext category +pub fn isIDStartESNext(cp: u21) bool { + const high = cp >> 8; + const low = cp & 0xFF; + const stage2_idx = idStartESNext.stage1[high]; + const bit_pos = stage2_idx + low; + const u64_idx = bit_pos >> 6; + const bit_idx = @as(u6, @intCast(bit_pos & 63)); + return (idStartESNext.stage2[u64_idx] & (@as(u64, 1) << bit_idx)) != 0; +} +const idStartESNext = struct { + pub const stage1 = [_]u16{ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096, 256, 4352, 4608, 4864, 256, 5120, 5376, 5632, 5888, 6144, 6400, 6656, 6912, 256, 7168, 7424, 7680, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 8192, 8448, 7936, 7936, 8704, 8960, 7936, 7936, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 6912, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 9216, 256, 9472, 9728, 9984, 10240, 10496, 10752, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 11008, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 256, 11264, 11520, 256, 11776, 12032, 12288, 12544, 12800, 13056, 13312, 13568, 13824, 256, 14080, 14336, 14592, 14848, 15104, 15360, 15616, 15872, 16128, 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176, 18432, 18688, 18944, 7936, 19200, 19456, 19712, 19968, 256, 256, 256, 20224, 20480, 20736, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 20992, 256, 256, 256, 256, 21248, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 256, 256, 21504, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 256, 256, 21760, 22016, 7936, 7936, 22272, 22528, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 22784, 256, 256, 256, 256, 23040, 23296, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 23552, 256, 23808, 24064, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 24320, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 24576, 24832, 25088, 25344, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 25600, 25856, 26112, 26368, 7936, 26624, 7936, 7936, 26880, 27136, 27392, 7936, 7936, 7936, 7936, 27648, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 27904, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 28160, 28416, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 28672, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 28928, 256, 256, 29184, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 256, 256, 29440, 7936, 7936, 7936, 7936, 7936, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 29696, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 29952, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936, 7936 }; + pub const stage2 = [_]u64{ 0, 576460743847706622, 297241973452963840, 18410715276682199039, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 88094074470339, 0, 13609596598936928256, 18446744056529672000, 18428729675200069631, 18446744073709551615, 18446744073709551615, 18446744073709550595, 18446744073709551615, 18446462598732840959, 18446744069456527359, 511, 2119858418286592, 18446744069414584320, 18446392229988665343, 18446744073709551615, 11241196188469297151, 281474976514048, 18446744073709543424, 563224831328255, 301749971126844416, 1168302407679, 18446471390564450303, 18446744069414616831, 1023, 2594073385365405680, 18446181140919287808, 2577745637692514273, 1153765945374687232, 247132830528276448, 7881300924956672, 2589004636761079776, 144115200960823296, 2589004636760940512, 562965791113216, 288167810662516712, 65536, 2594071186342010848, 13539213312, 2589567586714640353, 1688864355778560, 2882303761516978160, 18158513712597581824, 3457638613854978016, 127, 3940649673949182, 127, 2309783315290257366, 4026531935, 1, 35184372088575, 7936, 0, 9223380832947798015, 18438229877581611008, 18446744069414600707, 17870283321406070975, 18446744073709551615, 18446744070446333439, 9168765891372858879, 18446744073701162813, 18446744073696837631, 134217727, 18446744069414649855, 4557642822898941951, 18446744073709551614, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446638520593285119, 18446744069548802046, 144053615424700415, 1125897759621119, 527761286627327, 4503599627370495, 276824064, 18446744069414584320, 144115188075855871, 18446469195802607615, 18014398509481983, 2147483647, 8796093022142464, 18446480190918885375, 1023, 18446744069422972927, 2097151, 549755813888, 0, 4503599627370464, 8160, 18158724812380307448, 274877906943, 68719476735, 4611686018360336384, 16717361816799216127, 319718190147960832, 18446744073709551615, 18446744073709551615, 18446744073709551615, 0, 18446744070475743231, 4611686017001275199, 6908521828386340863, 2295745090394464220, 0, 9223934986808197120, 536805376, 0, 17582049991377026180, 18446744069414601696, 511, 0, 0, 0, 0, 0, 18446744073709551615, 18446744073709551615, 18446744073709551615, 3509778554814463, 18446498607738650623, 141836999983103, 9187201948305063935, 2139062143, 2251241253188403424, 18446744073709551614, 18446744069288755199, 17870283321406128127, 18446462598732840928, 18446744073709551615, 18446744069414617087, 18446462598732840960, 18446744073709551615, 18446744073709551615, 8191, 4611686018427322368, 13198434443263, 9223512774343131135, 18446744070488326143, 281474976710655, 18446744060816261120, 18446744073709551615, 18446744073709550079, 18445618173868443647, 34359736251, 4503599627370495, 4503599627370492, 7564921474075590656, 18446462873610746880, 2305843004918726783, 2251799813685232, 8935422993945886720, 2199023255551, 14159317224157876215, 4495436853045886975, 7890092085477381, 18446602782178705022, 18446466996645134335, 18446744073709551615, 34359738367, 18446744073709551615, 18446744073709551615, 18446462667452317695, 1152921504606845055, 18446744073709551615, 18446532967477018623, 18446744073709551615, 67108863, 6881498030004502655, 18446744073709551579, 1125899906842623, 18446744073709027328, 4611686018427387903, 18446744073709486080, 18446744073709355007, 1152640029630136575, 0, 18437455399478099968, 18446744073709551615, 2305843009213693951, 576460743713488896, 18446743798965862398, 9223372036854775807, 486341884, 13258596753222922239, 1073692671, 18446744073709551615, 576460752303423487, 0, 9007199254740991, 0, 0, 0, 0, 18446744069951455231, 131071, 18446708893632430079, 18014398509418495, 18446744070488326143, 4128527, 18446744073709551615, 18446744073709551615, 18446462599806582783, 1152921504591118335, 18446463698244468735, 17870001915148894207, 2016486715970549759, 0, 36028797018963967, 1095220854783, 575897802350002111, 0, 10502394331027995967, 36028792728190975, 2147483647, 15762594400829440, 288230371860938751, 0, 13907115649320091647, 0, 18014398491590657, 2305843004918726656, 536870911, 137438953215, 18014398509481983, 2251795522912255, 262143, 0, 18446744073709551615, 511, 2251799813685247, 2251799813685247, 68719476735, 0, 0, 0, 0, 0, 848822976643071, 0, 18446463149025525759, 18446462598732841023, 18446462598732840963, 36028792723996703, 72057594037927928, 10696049115004928, 281474976710648, 2199023190016, 549755813880, 20266198323101840, 2251799813685240, 335544350, 9223389629040558079, 1, 18446464796682337663, 2147483647, 2589004636760940512, 16643063808, 0, 0, 9007199254740991, 15032387456, 281474976710655, 176, 0, 0, 140737488355327, 251658240, 281474976710655, 16, 72066390130950143, 0, 134217727, 127, 0, 0, 17592186044415, 0, 18446744069414584320, 9223372041149743103, 9223653511822045823, 2, 18446740770879700992, 42949804031, 290482175965394945, 18446744073441181696, 18446462599269712895, 144115188075855871, 140737488354815, 18445618173802708993, 65535, 0, 562949953420159, 18446741595513421888, 16778239, 0, 0, 0, 0, 2251795518717952, 4503599627239412, 0, 281474976710656, 0, 18446744073709551615, 18446744073709551615, 67108863, 0, 18446744073709551615, 140737488355327, 18446744073709551615, 18446744073709551615, 18446744073709551615, 15, 0, 0, 0, 0, 18446744073709486080, 562949953421311, 281474976710655, 126, 0, 0, 18446744073709551615, 127, 0, 0, 144115188075855871, 18446462600880324607, 9223372036854775807, 70368744112128, 281474976710655, 16212958624174047247, 65535, 0, 0, 18446744073709551615, 0, 0, 18446744073709551615, 67583, 4294443008, 47244640256, 18446744073709551615, 18446744073709551615, 18446744073709551615, 72057594037927935, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4194303, 511, 0, 0, 0, 0, 0, 0, 8065665457643847680, 1125934266580991, 18446463629527547904, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 1152921504606846975, 18446744073709551615, 2305570330330005503, 67043839, 0, 18446744073709551615, 18446744073707454463, 17005555242810474495, 18446744073709551599, 8935141660164089791, 18446744073709419615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446743249075830783, 17870283321271910397, 18437736874452713471, 18446603336221163519, 18446741874686295551, 4087, 8660801552383, 0, 0, 0, 18446462598732840960, 70368744177663, 0, 0, 4575692405780512767, 16384, 0, 0, 0, 0, 70368744112128, 17592186044415, 0, 0, 0, 17592185978880, 0, 0, 0, 9223213153129594880, 18446744073709551615, 18446744073709551615, 18446744073709551615, 31, 18446744073709551615, 2063, 0, 0, 790380184120328175, 6843210385291930244, 1152917029519358975, 0, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4294967295, 288230376151711743, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744070488326143, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446462615912710143, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446462607322775551, 18446744073709551615, 1073741823, 0, 0, 1073741823, 0, 0, 0, 18446744073709551615, 18446744073709488127, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 281474976710655, 0 }; }; -// // ----- The benchmark ------ - -// const std = @import("std"); - -// const part_codepoints_slice: []const i32 = &start_codepoints; -// const start_codepoints_slice: []const i32 = &part_codepoints; - -// pub const HashTable = struct { -// var starts: std.AutoHashMap(i32, void) = undefined; -// var parts: std.AutoHashMap(i32, void) = undefined; - -// pub fn isIdentifierStart(codepoint: i32) bool { -// if (codepoint > 255) return starts.contains(codepoint); -// return switch (codepoint) { -// 'A'...'Z', 'a'...'z', '$', '_' => true, -// else => false, -// }; -// } - -// pub fn isIdentifierPart(codepoint: i32) bool { -// if (codepoint > 255) return parts.contains(codepoint); -// return switch (codepoint) { -// 'A'...'Z', 'a'...'z', '0'...'9', '$', '_' => true, -// else => false, -// }; -// } - -// pub fn init(allocator: std.mem.Allocator) !void { -// starts = std.AutoHashMap(i32, void).init(allocator); -// parts = std.AutoHashMap(i32, void).init(allocator); - -// var i: i32 = 0; -// var j: i32 = 0; - -// while (i < start_codepoints.len) : (i += 2) { -// j = start_codepoints[i]; -// while (j <= start_codepoints[i + 1]) : (j += 1) { -// try starts.put(j, {}); -// } -// } -// i = 0; -// while (i < part_codepoints.len) : (i += 2) { -// j = part_codepoints[i]; -// while (j <= part_codepoints[i + 1]) : (j += 1) { -// try parts.put(j, {}); -// } -// } -// } -// }; - -// pub const BinarySearch = struct { - -// // "lookupInUnicodeMap" in TypeScript -// // esbuild does something similar -// fn search(comptime map: []const i32, code: i32) bool { -// // Bail out quickly if it couldn't possibly be in the map. -// if (code < map[0]) { -// return false; -// } - -// // Perform binary search in one of the Unicode range maps -// var lo: i32 = 0; -// var hi: i32 = map.len; -// var mid: i32 = undefined; - -// while (lo + 1 < hi) { -// mid = lo + (hi - lo) / 2; -// // mid has to be even to catch a range's beginning -// mid -= mid % 2; -// if (map[mid] <= code and code <= map[mid + 1]) { -// return true; -// } -// if (code < map[mid]) { -// hi = mid; -// } else { -// lo = mid + 2; -// } -// } - -// return false; -// } - -// // https://source.chromium.org/chromium/v8/v8.git/+/master:src/strings/char-predicates-inl.h;l=133 -// pub fn isIdentifierStart(codepoint: i32) bool { -// if (codepoint > 255) return search(start_codepoints_slice, codepoint); -// return switch (codepoint) { -// 'A'...'Z', 'a'...'z', '$', '_' => true, -// else => false, -// }; -// } - -// pub fn isIdentifierPart(codepoint: i32) bool { -// if (codepoint > 255) return search(part_codepoints_slice, codepoint); -// return switch (codepoint) { -// 'A'...'Z', 'a'...'z', '0'...'9', '$', '_' => true, -// else => false, -// }; -// } -// }; - -// const unicode_text: []const u8 = -// \\ -// \\_a["" + "constructor"] = 133 /* ConstructorKeyword */, -// \\_a.debugger = 87 /* DebuggerKeyword */, -// \\_a.declare = 134 /* DeclareKeyword */, -// \\_a.default = 88 /* DefaultKeyword */, -// \\_a.delete = 89 /* DeleteKeyword */, -// \\_a.do = 90 /* DoKeyword */, -// \\_a.else = 91 /* ElseKeyword */, -// \\_a.enum = 92 /* EnumKeyword */, -// \\_a.export = 93 /* ExportKeyword */, -// \\_a.extends = 94 /* ExtendsKeyword */, -// \\_a.false = 95 /* FalseKeyword */, -// \\_a.finally = 96 /* FinallyKeyword */, -// \\_a.for = 97 /* ForKeyword */, -// \\_a.from = 154 /* FromKeyword */, -// \\_a.function = 98 /* FunctionKeyword */, -// \\_a.get = 135 /* GetKeyword */, -// \\_a.if = 99 /* IfKeyword */, -// \\_a.implements = 117 /* ImplementsKeyword */, -// \\_a.import = 100 /* ImportKeyword */, -// \\_a.in = 101 /* InKeyword */, -// \\_a.infer = 136 /* InferKeyword */, -// \\_a.instanceof = 102 /* InstanceOfKeyword */, -// \\_a.interface = 118 /* InterfaceKeyword */, -// \\_a.intrinsic = 137 /* IntrinsicKeyword */, -// \\_a.is = 138 /* IsKeyword */, -// \\_a.keyof = 139 /* KeyOfKeyword */, -// \\_a.let = 119 /* LetKeyword */, -// \\_a.module = 140 /* ModuleKeyword */, -// \\_a.namespace = 141 /* NamespaceKeyword */, -// \\_a.never = 142 /* NeverKeyword */, -// \\_a.new = 103 /* NewKeyword */, -// \\_a.null = 104 /* NullKeyword */, -// \\_a.number = 145 /* NumberKeyword */, -// \\_a.object = 146 /* ObjectKeyword */, -// \\_a.package = 120 /* PackageKeyword */, -// \\_a.private = 121 /* PrivateKeyword */, -// \\_a.protected = 122 /* ProtectedKeyword */, -// \\_a.public = 123 /* PublicKeyword */, -// \\_a.override = 157 /* OverrideKeyword */, -// \\_a.readonly = 143 /* ReadonlyKeyword */, -// \\_a.require = 144 /* RequireKeyword */, -// \\_a.global = 155 /* GlobalKeyword */, -// \\_a.return = 105 /* ReturnKeyword */, -// \\_a.set = 147 /* SetKeyword */, -// \\_a.static = 124 /* StaticKeyword */, -// \\_a.string = 148 /* StringKeyword */, -// \\_a.super = 106 /* SuperKeyword */, -// \\_a.switch = 107 /* SwitchKeyword */, -// \\_a.symbol = 149 /* SymbolKeyword */, -// \\_a.this = 108 /* ThisKeyword */, -// \\_a.throw = 109 /* ThrowKeyword */, -// \\_a.true = 110 /* TrueKeyword */, -// \\_a.try = 111 /* TryKeyword */, -// \\_a.type = 150 /* TypeKeyword */, -// \\_a.typeof = 112 /* TypeOfKeyword */, -// \\_a.undefined = 151 /* UndefinedKeyword */, -// \\_a.unique = 152 /* UniqueKeyword */, -// \\_a.unknown = 153 /* UnknownKeyword */, -// \\_a.var = 113 /* VarKeyword */, -// \\_a.void = 114 /* VoidKeyword */, -// \\_a.while = 115 /* WhileKeyword */, -// \\_a.with = 116 /* WithKeyword */, -// \\_a.yield = 125 /* YieldKeyword */, -// \\_a.async = 130 /* AsyncKeyword */, -// \\_a.await = 131 /* AwaitKeyword */, -// \\_a.of = 158 /* OfKeyword */, -// \\_a); -// \\var textToKeyword = new ts.Map(ts.getEntries(ts.textToKeywordObj)); -// \\var textToToken = new ts.Map(ts.getEntries(__assign(__assign({}, ts.textToKeywordObj), { "{": 18 /* OpenBraceToken */, "}": 19 /* CloseBraceToken */, "(": 20 /* OpenParenToken */, ")": 21 /* CloseParenToken */, "[": 22 /* OpenBracketToken */, "]": 23 /* CloseBracketToken */, ".": 24 /* DotToken */, "...": 25 /* DotDotDotToken */, ";": 26 /* SemicolonToken */, ",": 27 /* CommaToken */, "<": 29 /* LessThanToken */, ">": 31 /* GreaterThanToken */, "<=": 32 /* LessThanEqualsToken */, ">=": 33 /* GreaterThanEqualsToken */, "==": 34 /* EqualsEqualsToken */, "!=": 35 /* ExclamationEqualsToken */, "===": 36 /* EqualsEqualsEqualsToken */, "!==": 37 /* ExclamationEqualsEqualsToken */, "=>": 38 /* EqualsGreaterThanToken */, "+": 39 /* PlusToken */, "-": 40 /* MinusToken */, "**": 42 /* AsteriskAsteriskToken */, "*": 41 /* AsteriskToken */, "/": 43 /* SlashToken */, "%": 44 /* PercentToken */, "++": 45 /* PlusPlusToken */, "--": 46 /* MinusMinusToken */, "<<": 47 /* LessThanLessThanToken */, ">": 48 /* GreaterThanGreaterThanToken */, ">>>": 49 /* GreaterThanGreaterThanGreaterThanToken */, "&": 50 /* AmpersandToken */, "|": 51 /* BarToken */, "^": 52 /* CaretToken */, "!": 53 /* ExclamationToken */, "~": 54 /* TildeToken */, "&&": 55 /* AmpersandAmpersandToken */, "||": 56 /* BarBarToken */, "?": 57 /* QuestionToken */, "??": 60 /* QuestionQuestionToken */, "?.": 28 /* QuestionDotToken */, ":": 58 /* ColonToken */, "=": 63 /* EqualsToken */, "+=": 64 /* PlusEqualsToken */, "-=": 65 /* MinusEqualsToken */, "*=": 66 /* AsteriskEqualsToken */, "**=": 67 /* AsteriskAsteriskEqualsToken */, "/=": 68 /* SlashEqualsToken */, "%=": 69 /* PercentEqualsToken */, "<<=": 70 /* LessThanLessThanEqualsToken */, ">>=": 71 /* GreaterThanGreaterThanEqualsToken */, ">>>=": 72 /* GreaterThanGreaterThanGreaterThanEqualsToken */, "&=": 73 /* AmpersandEqualsToken */, "|=": 74 /* BarEqualsToken */, "^=": 78 /* CaretEqualsToken */, "||=": 75 /* BarBarEqualsToken */, "&&=": 76 /* AmpersandAmpersandEqualsToken */, "??=": 77 /* QuestionQuestionEqualsToken */, "@": 59 /* AtToken */, "#": 62 /* HashToken */, "`": 61 /* BacktickToken */ }))); -// \\/* -// \\As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers -// \\IdentifierStart :: -// \\Can contain Unicode 3.0.0 categories: -// \\Uppercase letter (Lu), -// \\Lowercase letter (Ll), -// \\Titlecase letter (Lt), -// \\Modifier letter (Lm), -// \\Other letter (Lo), or -// \\Letter number (Nl). -// \\IdentifierPart :: = -// \\Can contain IdentifierStart + Unicode 3.0.0 categories: -// \\Non-spacing mark (Mn), -// \\Combining spacing mark (Mc), -// \\Decimal number (Nd), or -// \\Connector punctuation (Pc). -// \\ -// \\Codepoint ranges for ES3 Identifiers are extracted from the Unicode 3.0.0 specification at: -// \\http://www.unicode.org/Public/3.0-Update/UnicodeData-3.0.0.txt -// \\*/ -// \\var unicodeES3IdentifierStart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736, 740, 750, 750, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1569, 1594, 1600, 1610, 1649, 1747, 1749, 1749, 1765, 1766, 1786, 1788, 1808, 1808, 1810, 1836, 1920, 1957, 2309, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2784, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3294, 3294, 3296, 3297, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3424, 3425, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, 3840, 3840, 3904, 3911, 3913, 3946, 3976, 3979, 4096, 4129, 4131, 4135, 4137, 4138, 4176, 4181, 4256, 4293, 4304, 4342, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6067, 6176, 6263, 6272, 6312, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8319, 8319, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12346, 12353, 12436, 12445, 12446, 12449, 12538, 12540, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65138, 65140, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; -// \\var unicodeES3IdentifierPart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736, 740, 750, 750, 768, 846, 864, 866, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1155, 1158, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1441, 1443, 1465, 1467, 1469, 1471, 1471, 1473, 1474, 1476, 1476, 1488, 1514, 1520, 1522, 1569, 1594, 1600, 1621, 1632, 1641, 1648, 1747, 1749, 1756, 1759, 1768, 1770, 1773, 1776, 1788, 1808, 1836, 1840, 1866, 1920, 1968, 2305, 2307, 2309, 2361, 2364, 2381, 2384, 2388, 2392, 2403, 2406, 2415, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2492, 2494, 2500, 2503, 2504, 2507, 2509, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2562, 2562, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2649, 2652, 2654, 2654, 2662, 2676, 2689, 2691, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2784, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870, 2873, 2876, 2883, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2913, 2918, 2927, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3031, 3031, 3047, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3134, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3168, 3169, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3262, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3297, 3302, 3311, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3390, 3395, 3398, 3400, 3402, 3405, 3415, 3415, 3424, 3425, 3430, 3439, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3805, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3946, 3953, 3972, 3974, 3979, 3984, 3991, 3993, 4028, 4038, 4038, 4096, 4129, 4131, 4135, 4137, 4138, 4140, 4146, 4150, 4153, 4160, 4169, 4176, 4185, 4256, 4293, 4304, 4342, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 4969, 4977, 5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6099, 6112, 6121, 6160, 6169, 6176, 6263, 6272, 6313, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8319, 8319, 8400, 8412, 8417, 8417, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12346, 12353, 12436, 12441, 12442, 12445, 12446, 12449, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65056, 65059, 65075, 65076, 65101, 65103, 65136, 65138, 65140, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; -// \\/* -// \\As per ECMAScript Language Specification 5th Edition, Section 7.6: ISyntaxToken Names and Identifiers -// \\IdentifierStart :: -// \\Can contain Unicode 6.2 categories: -// \\Uppercase letter (Lu), -// \\Lowercase letter (Ll), -// \\Titlecase letter (Lt), -// \\Modifier letter (Lm), -// \\Other letter (Lo), or -// \\Letter number (Nl). -// \\IdentifierPart ::㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ -// \\Can contain IdentifierStart + Unicode 6.2 categories: -// \\Non-spacing mark (Mn), -// \\Combining spacing mark (Mc), -// \\Decimal number (Nd), -// \\Connector punctuation (Pc), -// \\, or -// \\. -// \\ -// \\Codepoint ranges for ES5 Identifiers are extracted from the Unicode 6.2 specification at: -// \\http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt -// \\*/ -// \\var unicodeES5IdentifierStart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2208, 2208, 2210, 2220, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, 7413, 7414, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; -// \\var unicodeES5IdentifierPart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1520, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2048, 2093, 2112, 2139, 2208, 2208, 2210, 2220, 2276, 2302, 2304, 2403, 2406, 2415, 2417, 2423, 2425, 2431, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3161, 3168, 3171, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3396, 3398, 3400, 3402, 3406, 3415, 3415, 3424, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6263, 6272, 6314, 6320, 6389, 6400, 6428, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6617, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7376, 7378, 7380, 7414, 7424, 7654, 7676, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 11823, 11823, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12442, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42647, 42655, 42737, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43047, 43072, 43123, 43136, 43204, 43216, 43225, 43232, 43255, 43259, 43259, 43264, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43643, 43648, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65062, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; -// \\/** -// \\* Generated by scripts/regenerate-unicode-identifier-parts.js on node v12.4.0 with unicode 12.1 -// \\* based on http://www.unicode.org/reports/tr31/ and https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords -// \\* unicodeESNextIdentifierSt💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 art corresponds to the ID_Start and Other_ID_Start property, and -// \\* unicodeESNextIdentifierPart corresponds to ID_Continue, Other_ID_Continue, plus ID_Start and Other_ID_Start -// \\*/ -// \\var unicodeESNextIdentifierStart = [65, 90, 97, 122, 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 895, 895, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1488, 1514, 1519, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2144, 2154, 2208, 2228, 2230, 2237, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2432, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2556, 2556, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2809, 2809, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3133, 3160, 3162, 3168, 3169, 3200, 3200, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3412, 3414, 3423, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6264, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6430, 6480, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7401, 7404, 7406, 7411, 7413, 7414, 7418, 7418, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42653, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43261, 43262, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43488, 43492, 43494, 43503, 43514, 43518, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43646, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66335, 66349, 66378, 66384, 66421, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68096, 68112, 68115, 68117, 68119, 68121, 68149, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68324, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68899, 69376, 69404, 69415, 69415, 69424, 69445, 69600, 69622, 69635, 69687, 69763, 69807, 69840, 69864, 69891, 69926, 69956, 69956, 69968, 70002, 70006, 70006, 70019, 70066, 70081, 70084, 70106, 70106, 70108, 70108, 70144, 70161, 70163, 70187, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70366, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70461, 70461, 70480, 70480, 70493, 70497, 70656, 70708, 70727, 70730, 70751, 70751, 70784, 70831, 70852, 70853, 70855, 70855, 71040, 71086, 71128, 71131, 71168, 71215, 71236, 71236, 71296, 71338, 71352, 71352, 71424, 71450, 71680, 71723, 71840, 71903, 71935, 71935, 72096, 72103, 72106, 72144, 72161, 72161, 72163, 72163, 72192, 72192, 72203, 72242, 72250, 72250, 72272, 72272, 72284, 72329, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72750, 72768, 72768, 72818, 72847, 72960, 72966, 72968, 72969, 72971, 73008, 73030, 73030, 73056, 73061, 73063, 73064, 73066, 73097, 73112, 73112, 73440, 73458, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92880, 92909, 92928, 92975, 92992, 92995, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94032, 94032, 94099, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 123136, 123180, 123191, 123197, 123214, 123214, 123584, 123627, 124928, 125124, 125184, 125251, 125259, 125259, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101]; -// \\var unicodeESNextIdentifierPart = [48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2208, 2228, 2230, 2237, 2259, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3328, 3331, 3333, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7673, 7675, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43047, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69376, 69404, 69415, 69415, 69424, 69456, 69600, 69622, 69632, 69702, 69734, 69743, 69759, 69818, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69958, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70096, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70206, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70751, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71680, 71738, 71840, 71913, 71935, 71935, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123584, 123641, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101, 917760, 917999]; -// \\/** -// \\* Test for whether a single line comment with leading whitespace trimmed's text contains a directive. -// \\*/ -// \\var commentDirectiveRegExSingleLine = /^\/\/\/?\s*@(ts-expect-error|ts-ignore)/; -// \\/** -// \\* Test for whether a multi-line comment with leading whitespace trimmed's last line contains a directive. -// \\*/ -// \\var commentDirectiveRegExMultiLine = /^(?:\/|\*)*\s*@(ts-expect-error|ts-ignore)/; -// \\ /** @deprecated Use `factory.updateTaggedTemplate` or the factory supplied by your transformation context instead. */ -// \\ ts.updateTaggedTemplate = ts.Debug.deprecate(function updateTaggedTemplate(node, tag, typeArgumentsOrTemplate, template) { -// \\ var typeArguments; -// \\ if (template) { -// \\ typeArguments = typeArgumentsOrTemplate; -// \\ } -// \\ else { -// \\ template = typeArgumentsOrTemplate; -// \\ } -// \\ return ts.factory.updateTaggedTemplateExpression(node, tag, typeArguments, template); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.updateBinary` or the factory supplied by your transformation context instead. */ -// \\ ts.updateBinary = ts.Debug.deprecate(function updateBinary(node, left, right, operator) { -// \\ if (operator === void 0) { operator = node.operatorToken; } -// \\ if (typeof operator === "number") { -// \\ operator = operator === node.operatorToken.kind ? node.operatorToken : ts.factory.createToken(operator); -// \\ } -// \\ return ts.factory.updateBinaryExpression(node, left, operator, right); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createConditional` or the factory supplied by your transformation context instead. */ -// \\ ts.createConditional = ts.Debug.deprecate(function createConditional(condition, questionTokenOrWhenTrue, whenTrueOrWhenFalse, colonToken, whenFalse) { -// \\ return arguments.length === 5 ? ts.factory.createCondit 🤎💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 ionalExpression(condition, questionTokenOrWhenTrue, whenTrueOrWhenFalse, colonToken, whenFalse) : -// \\ arguments.length === 3 ? ts.factory.createConditionalExpression(condition, ts.factory.createToken(57 /* QuestionToken */), questionTokenOrWhenTrue, ts.factory.createToken(58 /* ColonToken */), whenTrueOrWhenFalse) : -// \\ ts.Debug.fail("Argument count mismatch"); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createYield` or the factory supplied by your transformation context instead. */ -// \\ ts.createYield = ts.Debug.deprecate(function createYield(asteriskTokenOrExpression, expression) { -// \\ var asteriskToken; -// \\ if (expression) { -// \\ asteriskToken = asteriskTokenOrExpression; -// \\ } -// \\ else { -// \\ expression = asteriskTokenOrExpression; -// \\ } -// \\ return ts.factory.createYieldExpre 🤎💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 ssion(asteriskToken, expression); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createClassExpression` or the factory supplied by your transformation context instead. */ -// \\ ts.createClassExpression = ts.Debug.deprecate(function createClassExpression(modifiers, name, typeParameters, heritageClauses, members) { -// \\ return ts.factory.createClassExpression(/*decorators*/ undefined, modifiers, name, typeParameters, heritageClauses, members); -// \\ }, factoryDeprecation); 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ -// \\ /** @deprecated Use `factory.updateClassExpression` or the factory supplied by your transformation context instead. */ -// \\ ts.updateClassExpression = ts.Debug.deprecate(function updateClassExpression(node, modifiers, name, typeParameters, heritageClauses, members) { -// \\ return ts.factory.updateClassExpression(node, /*decorato 🤎💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 rs*/ undefined, modifiers, name, typeParameters, heritageClauses, members); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createPropertySignature` or the factory supplied by your transformation context instead. */ -// \\ ts.createPropertySignature = ts.Debug.deprecate(function createPropertySignature(modifiers, name, questionToken, type, initializer) { -// \\ var node = ts.factory.createPropertySignature(modifiers, name, questionToken, type); -// \\ node.initializer = initializer; -// \\ return node; -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.updatePropertySignature` or the factory supplied by your transformation context instead. */ -// \\ ts.updatePropertySignature = ts.Debug.deprecate(function updatePropertySignature(node, modifiers, name, questionToken, type, initializer) { -// \\ var updated = ts.factory.updatePropertySignature(node, modifiers, name, questionToken, type); -// \\ if (node.initia 💛 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 lizer !== initializer) { -// \\ if (updated === node) { -// \\ updated = ts 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 .factory.cloneNode(node); -// \\ } -// \\ updated.ini ` 💛 💚 💙 💜 🖤 🤍 🤎 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 tializer = initializer; -// \\ } -// \\ return updated;⏏ ♀ 💚 💙 💜 🖤 🤍 🤎📢 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 ♂ ⚕ ♾️ -// \\ }, factoryDeprecation 💛 💚 💙 💜 🖤 🤍🕞 💛⏏ ♀ ♂ ⚕ ♾️ -// \\ /** @deprecated Use⏏ ♀ 💚 💙 💜 🖤 🤍 🤎📢 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 ♂ ⚕ ♾️ -// \\ ts.createExpression ` 💛 💚 💙 💜 🖤 🤍 🤎💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 Wi 💛⏏ ♀ ♂ ⚕ ♾️ -// \\ return ts.factory⏏ 💚 💙 💜 🖤 🤍 🤎 👁‍🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 ♀ ♂ ⚕ ♾️ -// \\ }, factoryDeprecation 💛⏏ ♀ ♂ ⚕ ♾️ -// \\ /** @deprecated Use⏏ ♀ ♂ ⚕ ♾️ -// \\ ts.updateExpressionWi 💛⏏ ♀ ♂ ⚕ ♾️ -// \\ }, factoryDeprecation);👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ /** @deprecated Use `factory.createArrowFunction` or the factory supplied by your transformation context instead. */👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ ts.createArrowFunction = ts.Debug.deprecate(function createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanTokenOrBody, body) { -// \\ return arguments.length === 6 ? ts.factory.createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanTokenOrBody, body) : -// \\ arguments.length === 5 ? ts.factory.createA 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 rrowFunction(modifiers, typeParamete👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ }, factoryDeprecation); 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 -// \\ /** @deprecated Use `factory.updateArrowFunction` o 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍 ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 r the factory supplied by your transformation context instead. */ -// \\ ts.updateArrowFunction = ts.Debug.deprecate(functio 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 n updateArrowFunction(node, modifiers, typeParameters, parameters, type, equalsGreaterThanTokenOrBody, body) { -// \\ return arguments.length === 7 ? ts.factory.upda 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 teArrowFunction(node, modifiers, typeParameters, parameters, type, equalsGreaterThanTokenOrBody, body) : -// \\ arguments.length === 6 ? ts.factory.updateA 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 rrowFunction(node, modifiers, typeParameters, parameters, type, node.equalsGreaterThanToken, equalsGreaterThanTokenOrBody) : -// \\ ts.Debug.fail("Argument count mismatch" 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 -// \\ 💔 ❣️ 💕 💞 💓 -// \\ 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁‍🗨 💬 -// \\💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 );👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽eDeclaration(name, exclamationTokenOrType, typeOrInitializer, initializer) { -// \\ return arguments.length === 4 ? ts.factory.createVariableDeclaration(name, exclamationTokenOrType, typeOrInitializer, initializer) :👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ argu💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 ✔️ ☑️ 🔘 🔴 🟠 🟡 🟢 🔵 ments.length >= 1 && arguments.length <= 3 ? ts.factory.createVariableDeclaration(name, /*exclamationToken*/ undefined, exclamationTokenOrType, typeOrInitializer) : -// \\ ts.Debug.fail("Argument count mismatch");👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.updateVariableDeclaration` or the factory supplied by your transformation context instead. */👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ return arguments.length === 5 ? ts.factory.updateVariableDeclaration(node, name, exclamationTokenOrType, typeOrInitializer, initializer) : -// \\ arguments.length === 4 ? ts.factory.updateVariableDeclaration(node, name, node.exclamationToken, exclamationTokenOrType, typeOrInitializer) :👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ ts.Debug.fail("Argument count mismatch"); -// \\ }, factoryDeprecation); -// \\ 😀 😃 😄 😁 😆 🤩 😅 😂 🤣 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 😥 🤤 😭 😓 😪 😴 🥱 🙄 🤨 🧐 🤔 🤫 🤭 🤥 😬 🤐 🤢 🤮 🤧 😷 🤒 🤕 😈 👿 👹 👺 💩 👻 💀 ☠️ 👽 👾 🤖 🎃 😺 😸 😹 😻 😼 😽 🙀 😿 😾 👐 🙌 👏 🙏 🤲 🤝 👍 👎 👊 ✊ 🤛 🤜 🤞 ✌️ 🤘 🤏 👌 👈 👉 👆 👇 ☝️ ✋ 🤚 🖐 🖖 👋 🤙 💪 🖕 🤟 ✍️ 🤳 💅 🖖 💄 💋 👄 👅 👂 🦻 👃 🦵 🦶 🦾 🦿 👣 👁 👀 🗣 👤 👥 👶 👦 👧 🧒 👨 👩 🧑 👱‍♀️ 👱 🧔 👴 👵 🧓 👲 👳‍♀️ 👳 🧕 👮‍♀️ 👮 👷‍♀️ 👷 💂‍♀️ 💂 🕵️‍♀️ 🕵️ 👩‍⚕️ 👨‍⚕️ 👩‍🌾 👨‍🌾 👩‍🍳 👨‍🍳 👩‍🎓 👨‍🎓 👩‍🎤 👨‍🎤 👩‍🏫 👨‍🏫 👩‍🏭 👨‍🏭 👩‍💻 👨‍💻 👩‍💼 👨‍💼 👩‍🔧 👨‍🔧 👩‍🔬 👨‍🔬 👩‍🎨 👨‍🎨 👩‍🚒 👨‍🚒 👩‍✈️ 👨‍✈️ 👩‍🚀 👨‍🚀 👩‍⚖️ 👨‍⚖️ 🤶 🎅 👸 🤴 👰 🤵 👼 🤰 🤱 🙇‍♀️ 🙇 💁 💁‍♂️ 🙅 🙅‍♂️ 🙆 🙆‍♂️ 🙋 🙋‍♂️ 🤦‍♀️ 🤦‍♂️ 🤷‍♀️ 🤷‍♂️ 🙎 🙎‍♂️ 🙍 🙍‍♂️ 💇 💇‍♂️ 💆 💆‍♂️ 🧖‍♀️ 🧖‍♂️ 🧏 🧏‍♂️ 🧏‍♀️ 🧙‍♀️ 🧙‍♂️ 🧛‍♀️ 🧛‍♂️ 🧟‍♀️ 🧟‍♂️ 🧚‍♀️ 🧚‍♂️ 🧜‍♀️ 🧜‍♂️ 🧝‍♀️ 🧝‍♂️ 🧞‍♀️ 🧞‍♂️ 🕴 💃 🕺 👯 👯‍♂️ 🚶‍♀️ 🚶 🏃‍♀️ 🏃 🧍 🧍‍♂️ 🧍‍♀️ 🧎 🧎‍♂️ 🧎‍♀️ 👨‍🦯 👩‍🦯 👨‍🦼 👩‍🦼 👨‍🦽 👩‍🦽 🧑‍🤝‍🧑 👫 👭 👬 💑 👩‍❤️‍👩 👨‍❤️‍👨 💏 👩‍❤️‍💋‍👩 👨‍❤️‍💋‍👨 👪 👨‍👩‍👧 👨‍👩‍👧‍👦 👨‍👩‍👦‍👦 👨‍👩‍👧‍👧 👩‍👩‍👦 👩‍👩‍👧 👩‍👩‍👧‍👦 👩‍👩‍👦‍👦 👩‍👩‍👧‍👧 👨‍👨‍👦 👨‍👨‍👧 👨‍👨‍👧‍👦 👨‍👨‍👦‍👦 👨‍👨‍👧‍👧 👩‍👦 👩‍👧 👩‍👧‍👦 👩‍👦‍👦 👩‍👧‍👧 👨‍👦 👨‍👧 👨‍👧‍👦 👨‍👦‍👦 👨‍👧‍👧 👚 👕 👖 👔 👗 👙 👘 👠 👡 👢 👞 👟 👒 🎩 🎓 👑 ⛑ 🎒 👝 👛 👜 💼 👓 🕶 🤿 🌂 ☂️ 🧣 🧤 🧥 🦺 🥻 🩱 🩲 🩳 🩰 🧦 🧢 ⛷ 🏂 🏋️‍♀️ 🏋️ 🤺 🤼‍♀️ 🤼‍♂️ 🤸‍♀️ 🤸‍♂️ ⛹️‍♀️ ⛹️ 🤾‍♀️ 🤾‍♂️ 🏌️‍♀️ 🏌️ 🏄‍♀️ 🏄 🏊‍♀️ 🏊 🤽‍♀️ 🤽‍♂️ 🚣‍♀️ 🚣 🏇 🚴‍♀️ 🚴 🚵‍♀️ 🚵 🤹‍♀️ 🤹‍♂️ 🧗‍♀️ 🧗‍♂️ 🧘‍♀️ 🧘‍♂️ 🥰 🥵 🥶 🥳 🥴 🥺 🦸 🦹 🧑‍🦰 🧑‍🦱 🧑‍🦳 🧑‍🦲 🧑‍⚕️ 🧑‍🎓 🧑‍🏫 🧑‍⚖️ 🧑‍🌾 🧑‍🍳 🧑‍🔧 🧑‍🏭 🧑‍💼 🧑‍🔬 🧑‍💻 🧑‍🎤 🧑‍🎨 🧑‍✈️ 🧑‍🚀 🧑‍🚒 🧑‍🦯 🧑‍🦼 🧑‍🦽 🦰 🦱 🦲 🦳 -// \\ /** @deprecated Use `factory.createImportClause` or the factory supplied by your transformation context instead. */ -// \\ ts.createImportClause = ts.Debug.deprecate(function createImportClause(name, namedBindings, isTypeOnly) {👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ return ts.factory.createImportClause(isTypeOnly, name, namedBindings); -// \\ }, factoryDeprecation);👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽ry supplied by your transformation context instead. */ -// \\ ts.updateImportClause = ts.Debug.deprecate(function updateImportClause(node, name, namedBindings, isTypeOnly) { -// \\ return ts.factory.updateImportClause(node, isTypeOnly, name, namedBindings); -// \\ }, factoryDeprecation);👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ /** @deprecated Use `factory.createExportDeclaration` or the factory supplied by your transformation context instead. */👨🏿‍🎤 👩🏿‍🏫 👨🏿‍🏫 👩🏿‍🏭 👨🏿‍🏭 👩🏿‍💻 👨🏿‍💻 👩🏿‍💼 👨🏿‍💼 👩🏿‍🔧 👨🏿‍🔧 👩🏿‍🔬 👨🏿‍🔬 👩🏿‍🎨 👨🏿‍🎨 👩🏿‍🚒 👨🏿‍🚒 👩🏿‍✈️ 👨🏿‍✈️ 👩🏿‍🚀 👨🏿‍🚀 👩🏿‍⚖️ 👨🏿‍⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿‍♀️ 🙇🏿 💁🏿 💁🏿‍♂️ 🙅🏿 🙅🏿‍♂️ 🙆🏿 🙆🏿‍♂️ 🙋🏿 🙋🏿‍♂️ 🤦🏿‍♀️ 🤦🏿‍♂️ 🤷🏿‍♀️ 🤷🏿‍♂️ 🙎🏿 🙎🏿‍♂️ 🙍🏿 🙍🏿‍♂️ 💇🏿 💇🏿‍♂️ 💆🏿 💆🏿‍♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿‍♀️ 🚶🏿 🏃🏿‍♀️ 🏃🏿 🏋🏿‍♀️ 🏋🏿 🤸🏿‍♀️ 🤸🏿‍♂️ ⛹🏿‍♀️ ⛹🏿 🤾🏿‍♀️ 🤾🏿‍♂️ 🏌🏿‍♀️ 🏌🏿 🏄🏿‍♀️ 🏄🏿 🏊🏿‍♀️ 🏊🏿 🤽🏿‍♀️ 🤽🏿‍♂️ 🚣🏿‍♀️ 🚣🏿 🏇🏿 🚴🏿‍♀️ 🚴🏿 🚵🏿‍♀️ 🚵🏿 🤹🏿‍♀️ 🤹🏿‍♂️ 🛀🏿 🧒🏿 🧑🏿 🧓🏿 🧕🏿 🧔🏿 🤱🏿 🧙🏿‍♀️ 🧙🏿‍♂️ 🧚🏿‍♀️ 🧚🏿‍♂️ 🧛🏿‍♀️ 🧛🏿‍♂️ 🧜🏿‍♀️ 🧜🏿‍♂️ 🧝🏿‍♀️ 🧝🏿‍♂️ 🧖🏿‍♀️ 🧖🏿‍♂️ 🧗🏿‍♀️ 🧗🏿‍♂️ 🧘🏿‍♀️ 🧘🏿‍♂️ 🤟🏿 🤲🏿 💏🏿 💑🏿 🤏🏿 🦻🏿 🧏🏿 🧏🏿‍♂️ 🧏🏿‍♀️ 🧍🏿 🧍🏿‍♂️ 🧍🏿‍♀️ 🧎🏿 🧎🏿‍♂️ 🧎🏿‍♀️ 👨🏿‍🦯 👩🏿‍🦯 👨🏿‍🦼 👩🏿‍🦼 👨🏿‍🦽 👩🏿‍🦽 🧑🏿‍🤝‍🧑🏿 🧑🏿‍🦰 🧑🏿‍🦱 🧑🏿‍🦳 🧑🏿‍🦲 🧑🏿‍⚕️ 🧑🏿‍🎓 🧑🏿‍🏫 🧑🏿‍⚖️ 🧑🏿‍🌾 🧑🏿‍🍳 🧑🏿‍🔧 🧑🏿‍🏭 🧑🏿‍💼 🧑🏿‍🔬 🧑🏿‍💻 🧑🏿‍🎤 🧑🏿‍🎨 🧑🏿‍✈️ 🧑🏿‍🚀 🧑🏿‍🚒 🧑🏿‍🦯 🧑🏿‍🦼 🧑🏿‍🦽 -// \\ ts.createExportDeclaration = ts.Debug.deprecate(function createExportDeclaration(decorators, modifiers, exportClause, moduleSpecifier, isTypeOnly) { -// \\ if (isTypeOnly === void 0) { isTypeOnly = false; } -// \\ return ts.factory.createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier);😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.updateExportDeclaration` or the factory supplied by your transformation context instead. */ -// \\ ts.updateEx 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍portDeclaration = ts.Debug.deprecate(function updateExportDeclaration(node, decorators, modifiers, exportClause, moduleSpecifier, isTypeOnly) { -// \\ return ts.factory.updateExportDeclaration(node, decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier); -// \\ }, factory 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍Deprecation); -// \\ /** @deprecated Use `factory.createJSDocParameterTag` or the factory supplied by your transformation context instead. */ -// \\ ts.createJSDocPar 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍amTag = ts.Debug 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍.deprecate(function createJSDocParamTag(name, isBracketed, typeExpression, comment) { -// \\ return ts.factory.createJSDocParameterTag(/*tagName*/ undefined, name, isBracketed, typeExpression, /*isNameFirst*/ false, comment ? ts.factory.createNodeArray([ts.factory.createJSDocText(comment)]) : undefivned); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createComma` or the factory supplied by your transformation context instead. */ -// \\ ts.createComma = ts.Debug.deprecate(function createComma(left, right) { -// \\ return ts.factory.createComma(left, right); -// \\ 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍🐱‍🏍 }, factoryDeprecation); -// \\ /** @deprecated 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍Use `factory.createLessThan` or the factory supplied by your transformation context instead. */ -// \\ ts.createLessThan = ts.Debug.deprecate(function createLessThan(left, right) {😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, factoryDeprecation); -// \\ /** @ 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍deprecated Use `factory.createAssignment` or the factory supplied by your transformation context instead. */😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ /** @deprecated Use `factory.createStrictEquality` or the factory supplied by your transformation context instead. */ -// \\ ts.createStrictEquality = ts.Debug.dep 🐱‍👤 🐱‍🚀 🐱‍🐉 🐱‍💻 🐱‍🏍recate(function createStrictEquality(left, right) { -// \\ return ts.factory.createStrictEquality(left, right);😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, factoryDeprecation); -// \\ /** @deprecated 🐱‍🐉 🐱‍💻 🐱‍👤 🐱‍🚀Use `factory.createStrictInequality` or the factory supplied by your transformation context instead. */ -// \\ ts.createStrictInequality = ts.Debug.deprecate(function createStrictInequality(left, right) { -// \\ return ts.factory.createStrictInequality(left, right); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createAdd` or the factory supplied b😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ return ts.factory.createSubtract(left, right); -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createLogicalAnd` or the factory supplied by your transformation context instead. */ -// \\ ts.createLogicalAnd = ts.Debug.deprecate(function createLogicalAnd(left, right) { -// \\ return ts.factory.createLogicalAnd(left, right);😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use `factory.createLogicalOr` or the factory supplied by your transformation context instead. */ -// \\ ts.createLogicalOr = ts.Debug.deprecate(function createLogicalOr(left, right) {😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ return ts.factory.createLogicalOr(left, right);😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, factoryDeprecation);😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢supplied by your transformation context instead. */ -// \\ ts.createPostfixIncrement = ts.Debug.deprecate(function createPostfixIncrement(operand) {😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ return ts.factory.createPostfixIncrement(operand);😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, factoryDeprecation); -// \\ /** @deprecated Use an appropriate `factory` method instead. */ -// \\ ts.createNode = ts.Debug.deprecate(function createNode(kind, pos, end) { -// \\ if (pos === void 0) { pos = 0; }😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢NodeFactory.createBaseSourceFileNode(kind) : -// \\ kind === 79 /* Identifier */ ? ts.parseBaseNodeFactory.createBaseIdentifierNode(kind) : -// \\ kind === 80 /* PrivateIdentifier */ ? ts.parseBaseNodeFactory.createBasePrivateIdentifierNode(kind) : -// \\ !ts.isNodeKind(kind) ? ts.parseBaseNodeFactory.createBaseTokenNode(kind) :😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢a node ~for mutation~ with its `pos`, `end`, and `parent` set. -// \\ * -// \\ * NOTE: It is unsafe to change any properties of a `Node` that relate to its AST children, as those changes won't be -// \\ * captured with respect to transformations. -// \\ * -// \\ * @deprecated Use an appropriate `factory.update...` method instead, use `setCommentRange` or `setSourceMapRange`, and avoid setting `parent`.😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ ts.setTextRange(clone, node); -// \\ ts.setParent(clone, node.parent); -// \\ return clone;😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 🤪 😜 😝 😛 🤑 🤗 🤓 😎 🤡 🤠 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 😤 😠 😡 🤬 😶 😐 😑 😯 😦 😧 😮 😲 😵 🤯 😳 😱 😨 😰 😢 -// \\ }, { since: "4.0", warnAfter: "4.1", message: "Use an appropriate `factory.update...` method instead, use `setCommentRange` or `setSourceMapRange`, and avoid setting `parent`." }); -// \\ // #endregion Node Factory top-level exports -// \\ // DEPRECATION: Renamed node tests -// \\ // DEPRECATION PLAN:🟣 ⚫️ ⚪️ 🟤 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🟥 🟧 🟨 🟩 🟦 🟪 🟫 🔈 🔇 🔉 🔊 🔔 🔕 📣 -// \\ // - soft: 4.0 -// \\ // - warn: 4.1 -// \\ // - error: TBD -// \\ // #region Renamed node Tests -// \\ /** @deprecated Use `isTypeAssertionExpression` instead. */ -// \\ ts.isTypeAssertion = ts.Debug.deprecate(function isTypeAssertion(node) { -// \\ return node.kind === 209 /* TypeAssertionExpression */; -// \\ }, { -// \\ since: "4.0", -// \\ warnAfter: "4.1", -// \\ message: "Use `isTypeAssertionExpression` instead." -// \\ }); -// \\ // #endregion -// \\ // DEPRECATION: Renamed node tests -// \\ // DEPRECATION PLAN: -// \\ // - soft: 4.2 -// \\ // - warn: 4.3 -// \\ // - error: TBD -// \\ // #region Renamed node Tests -// \\ /** -// \\ * @deprecated Use `isMemberName` instead. -// \\ */ -// \\ ts.isIdentifierOrPrivateIdentifier = ts.Debug.deprecate(function isIdentifierOrPrivateIdentifier(node) { -// \\ return ts.isMemberName(node); -// \\ }, { -// \\ since: "4.2", -// \\ warnAfter: "4.3", -// \\ message: "Use `isMemberName` instead." -// \\ }); -// \\ // #endregion Renamed node Tests -// \\})(ts || (ts = {})); -// ; -// const ascii_text: []const u8 = -// \\ -// \\package js_lexer -// \\ -// \\// The lexer converts a source file to a stream of tokens. Unlike many -// \\// compilers, esbuild does not run the lexer to completion before the parser is -// \\// started. Instead, the lexer is called repeatedly by the parser as the parser -// \\// parses the file. This is because many tokens are context-sensitive and need -// \\// high-level information from the parser. Examples are regular expression -// \\// literals and JSX elements. -// \\// -// \\// For efficiency, the text associated with textual tokens is stored in two -// \\// separate ways depending on the token. Identifiers use UTF-8 encoding which -// \\// allows them to be slices of the input file without allocating extra memory. -// \\// Strings use UTF-16 encoding so they can represent unicode surrogates -// \\// accurately. -// \\ -// \\import ( -// \\"fmt" -// \\"strconv" -// \\"strings" -// \\"unicode" -// \\"unicode/utf8" -// \\ -// \\"github.com/evanw/esbuild/internal/js_ast" -// \\"github.com/evanw/esbuild/internal/logger" -// \\) -// \\ -// \\type T uint -// \\ -// \\// If you add a new token, remember to add it to "tokenToString" too -// \\const ( -// \\TEndOfFile T = iota -// \\TSyntaxError -// \\ -// \\// "#!/usr/bin/env node" -// \\THashbang -// \\ -// \\// Literals -// \\TNoSubstitutionTemplateLiteral // Contents are in lexer.StringLiteral ([]uint16) -// \\TNumericLiteral // Contents are in lexer.Number (float64) -// \\TStringLiteral // Contents are in lexer.StringLiteral ([]uint16) -// \\TBigIntegerLiteral // Contents are in lexer.Identifier (string) -// \\ -// \\// Pseudo-literals -// \\TTemplateHead // Contents are in lexer.StringLiteral ([]uint16) -// \\TTemplateMiddle // Contents are in lexer.StringLiteral ([]uint16) -// \\TTemplateTail // Contents are in lexer.StringLiteral ([]uint16) -// \\ -// \\// Punctuation -// \\TAmpersand -// \\TAmpersandAmpersand -// \\TAsterisk -// \\TAsteriskAsterisk -// \\TAt -// \\TBar -// \\TBarBar -// \\TCaret -// \\TCloseBrace -// \\TCloseBracket -// \\TCloseParen -// \\TColon -// \\TComma -// \\TDot -// \\TDotDotDot -// \\TEqualsEquals -// \\TEqualsEqualsEquals -// \\TEqualsGreaterThan -// \\TExclamation -// \\TExclamationEquals -// \\TExclamationEqualsEquals -// \\TGreaterThan -// \\TGreaterThanEquals -// \\TGreaterThanGreaterThan -// \\TGreaterThanGreaterThanGreaterThan -// \\TLessThan -// \\TLessThanEquals -// \\TLessThanLessThan -// \\TMinus -// \\TMinusMinus -// \\TOpenBrace -// \\TOpenBracket -// \\TOpenParen -// \\TPercent -// \\TPlus -// \\TPlusPlus -// \\TQuestion -// \\TQuestionDot -// \\TQuestionQuestion -// \\TSemicolon -// \\TSlash -// \\TTilde -// \\ -// \\// Assignments (keep in sync with IsAssign() below) -// \\TAmpersandAmpersandEquals -// \\TAmpersandEquals -// \\TAsteriskAsteriskEquals -// \\TAsteriskEquals -// \\TBarBarEquals -// \\TBarEquals -// \\TCaretEquals -// \\TEquals -// \\TGreaterThanGreaterThanEquals -// \\TGreaterThanGreaterThanGreaterThanEquals -// \\TLessThanLessThanEquals -// \\TMinusEquals -// \\TPercentEquals -// \\TPlusEquals -// \\TQuestionQuestionEquals -// \\TSlashEquals -// \\ -// \\// Class-private fields and methods -// \\TPrivateIdentifier -// \\ -// \\// Identifiers -// \\TIdentifier // Contents are in lexer.Identifier (string) -// \\TEscapedKeyword // A keyword that has been escaped as an identifer -// \\ -// \\// Reserved words -// \\TBreak -// \\TCase -// \\TCatch -// \\TClass -// \\TConst -// \\TContinue -// \\TDebugger -// \\TDefault -// \\TDelete -// \\TDo -// \\TElse -// \\TEnum -// \\TExport -// \\TExtends -// \\TFalse -// \\TFinally -// \\TFor -// \\TFunction -// \\TIf -// \\TImport -// \\TIn -// \\TInstanceof -// \\TNew -// \\TNull -// \\TReturn -// \\TSuper -// \\TSwitch -// \\TThis -// \\TThrow -// \\TTrue -// \\TTry -// \\TTypeof -// \\TVar -// \\TVoid -// \\TWhile -// \\TWith -// \\) -// \\ -// \\func (t T) IsAssign() bool { -// \\return t >= TAmpersandAmpersandEquals && t <= TSlashEquals -// \\} -// \\ -// \\var Keywords = map[string]T{ -// \\// Reserved words -// \\"break": TBreak, -// \\"case": TCase, -// \\"catch": TCatch, -// \\"class": TClass, -// \\"const": TConst, -// \\"continue": TContinue, -// \\"debugger": TDebugger, -// \\"default": TDefault, -// \\"delete": TDelete, -// \\"do": TDo, -// \\"else": TElse, -// \\"enum": TEnum, -// \\"export": TExport, -// \\"extends": TExtends, -// \\"false": TFalse, -// \\"finally": TFinally, -// \\"for": TFor, -// \\"function": TFunction, -// \\"if": TIf, -// \\"import": TImport, -// \\"in": TIn, -// \\"instanceof": TInstanceof, -// \\"new": TNew, -// \\"null": TNull, -// \\"return": TReturn, -// \\"super": TSuper, -// \\"switch": TSwitch, -// \\"this": TThis, -// \\"throw": TThrow, -// \\"true": TTrue, -// \\"try": TTry, -// \\"typeof": TTypeof, -// \\"var": TVar, -// \\"void": TVoid, -// \\"while": TWhile, -// \\"with": TWith, -// \\} -// \\ -// \\var StrictModeReservedWords = map[string]bool{ -// \\"implements": true, -// \\"interface": true, -// \\"let": true, -// \\"package": true, -// \\"private": true, -// \\"protected": true, -// \\"public": true, -// \\"static": true, -// \\"yield": true, -// \\} -// \\ -// \\type json struct { -// \\parse bool -// \\allowComments bool -// \\} -// \\ -// \\type Lexer struct { -// \\log logger.Log -// \\source logger.Source -// \\tracker logger.LineColumnTracker -// \\current int -// \\start int -// \\end int -// \\ApproximateNewlineCount int -// \\LegacyOctalLoc logger.Loc -// \\AwaitKeywordLoc logger.Loc -// \\FnOrArrowStartLoc logger.Loc -// \\PreviousBackslashQuoteInJSX logger.Range -// \\LegacyHTMLCommentRange logger.Range -// \\Token T -// \\HasNewlineBefore bool -// \\HasPureCommentBefore bool -// \\PreserveAllCommentsBefore bool -// \\IsLegacyOctalLiteral bool -// \\PrevTokenWasAwaitKeyword bool -// \\CommentsToPreserveBefore []js_ast.Comment -// \\AllOriginalComments []js_ast.Comment -// \\codePoint rune -// \\Identifier string -// \\JSXFactoryPragmaComment logger.Span -// \\JSXFragmentPragmaComment logger.Span -// \\SourceMappingURL logger.Span -// \\Number float64 -// \\rescanCloseBraceAsTemplateToken bool -// \\forGlobalName bool -// \\json json -// \\prevErrorLoc logger.Loc -// \\ -// \\// Escape sequences in string literals are decoded lazily because they are -// \\// not interpreted inside tagged templates, and tagged templates can contain -// \\// invalid escape sequences. If the decoded array is nil, the encoded value -// \\// should be passed to "tryToDecodeEscapeSequences" first. -// \\decodedStringLiteralOrNil []uint16 -// \\encodedStringLiteralStart int -// \\encodedStringLiteralText string -// \\ -// \\// The log is disabled during speculative scans that may backtrack -// \\IsLogDisabled bool -// \\} -// \\ -// \\type LexerPanic struct{} -// \\ -// \\func NewLexer(log logger.Log, source logger.Source) Lexer { -// \\lexer := Lexer{ -// \\log: log, -// \\source: source, -// \\tracker: logger.MakeLineColumnTracker(&source), -// \\prevErrorLoc: logger.Loc{Start: -1}, -// \\FnOrArrowStartLoc: logger.Loc{Start: -1}, -// \\} -// \\lexer.step() -// \\lexer.Next() -// \\return lexer -// \\} -// \\ -// \\func NewLexerGlobalName(log logger.Log, source logger.Source) Lexer { -// \\lexer := Lexer{ -// \\log: log, -// \\source: source, -// \\tracker: logger.MakeLineColumnTracker(&source), -// \\prevErrorLoc: logger.Loc{Start: -1}, -// \\FnOrArrowStartLoc: logger.Loc{Start: -1}, -// \\forGlobalName: true, -// \\} -// \\lexer.step() -// \\lexer.Next() -// \\return lexer -// \\} -// \\ -// \\func NewLexerJSON(log logger.Log, source logger.Source, allowComments bool) Lexer { -// \\lexer := Lexer{ -// \\log: log, -// \\source: source, -// \\tracker: logger.MakeLineColumnTracker(&source), -// \\prevErrorLoc: logger.Loc{Start: -1}, -// \\FnOrArrowStartLoc: logger.Loc{Start: -1}, -// \\json: json{ -// \\parse: true, -// \\allowComments: allowComments, -// \\}, -// \\} -// \\lexer.step() -// \\lexer.Next() -// \\return lexer -// \\} -// \\ -// \\func (lexer *Lexer) Loc() logger.Loc { -// \\return logger.Loc{Start: int32(lexer.start)} -// \\} -// \\ -// \\func (lexer *Lexer) Range() logger.Range { -// \\return logger.Range{Loc: logger.Loc{Start: int32(lexer.start)}, Len: int32(lexer.end - lexer.start)} -// \\} -// \\ -// \\func (lexer *Lexer) Raw() string { -// \\return lexer.source.Contents[lexer.start:lexer.end] -// \\} -// \\ -// \\func (lexer *Lexer) StringLiteral() []uint16 { -// \\if lexer.decodedStringLiteralOrNil == nil { -// \\// Lazily decode escape sequences if needed -// \\if decoded, ok, end := lexer.tryToDecodeEscapeSequences(lexer.encodedStringLiteralStart, lexer.encodedStringLiteralText, true /* reportErrors */); !ok { -// \\lexer.end = end -// \\lexer.SyntaxError() -// \\} else { -// \\lexer.decodedStringLiteralOrNil = decoded -// \\} -// \\} -// \\return lexer.decodedStringLiteralOrNil -// \\} -// \\ -// \\func (lexer *Lexer) CookedAndRawTemplateContents() ([]uint16, string) { -// \\var raw string -// \\ -// \\switch lexer.Token { -// \\case TNoSubstitutionTemplateLiteral, TTemplateTail: -// \\// "`x`" or "}x`" -// \\raw = lexer.source.Contents[lexer.start+1 : lexer.end-1] -// \\ -// \\case TTemplateHead, TTemplateMiddle: -// \\// "`x${" or "}x${" -// \\raw = lexer.source.Contents[lexer.start+1 : lexer.end-2] -// \\} -// \\ -// \\if strings.IndexByte(raw, '\r') != -1 { -// \\// From the specification: -// \\// -// \\// 11.8.6.1 Static Semantics: TV and TRV -// \\// -// \\// TV excludes the code units of LineContinuation while TRV includes -// \\// them. and LineTerminatorSequences are normalized to -// \\// for both TV and TRV. An explicit EscapeSequence is needed to -// \\// include a or sequence. -// \\ -// \\bytes := []byte(raw) -// \\end := 0 -// \\i := 0 -// \\ -// \\for i < len(bytes) { -// \\c := bytes[i] -// \\i++ -// \\ -// \\if c == '\r' { -// \\// Convert '\r\n' into '\n' -// \\if i < len(bytes) && bytes[i] == '\n' { -// \\i++ -// \\} -// \\ -// \\// Convert '\r' into '\n' -// \\c = '\n' -// \\} -// \\ -// \\bytes[end] = c -// \\end++ -// \\} -// \\ -// \\raw = string(bytes[:end]) -// \\} -// \\ -// \\// This will return nil on failure, which will become "undefined" for the tag -// \\cooked, _, _ := lexer.tryToDecodeEscapeSequences(lexer.start+1, raw, false /* reportErrors */) -// \\return cooked, raw -// \\} -// \\ -// \\func (lexer *Lexer) IsIdentifierOrKeyword() bool { -// \\return lexer.Token >= TIdentifier -// \\} -// \\ -// \\func (lexer *Lexer) IsContextualKeyword(text string) bool { -// \\return lexer.Token == TIdentifier && lexer.Raw() == text -// \\} -// \\ -// \\func (lexer *Lexer) ExpectContextualKeyword(text string) { -// \\if !lexer.IsContextualKeyword(text) { -// \\lexer.ExpectedString(fmt.Sprintf("%q", text)) -// \\} -// \\lexer.Next() -// \\} -// \\ -// \\func (lexer *Lexer) SyntaxError() { -// \\loc := logger.Loc{Start: int32(lexer.end)} -// \\message := "Unexpected end of file" -// \\if lexer.end < len(lexer.source.Contents) { -// \\c, _ := utf8.DecodeRuneInString(lexer.source.Contents[lexer.end:]) -// \\if c < 0x20 { -// \\message = fmt.Sprintf("Syntax error \"\\x%02X\"", c) -// \\} else if c >= 0x80 { -// \\message = fmt.Sprintf("Syntax error \"\\u{%x}\"", c) -// \\} else if c != '"' { -// \\message = fmt.Sprintf("Syntax error \"%c\"", c) -// \\} else { -// \\message = "Syntax error '\"'" -// \\} -// \\} -// \\lexer.addError(loc, message) -// \\panic(LexerPanic{}) -// \\} -// \\ -// \\func (lexer *Lexer) ExpectedString(text string) { -// \\// Provide a friendly error message about "await" without "async" -// \\if lexer.PrevTokenWasAwaitKeyword { -// \\var notes []logger.MsgData -// \\if lexer.FnOrArrowStartLoc.Start != -1 { -// \\note := logger.RangeData(&lexer.tracker, logger.Range{Loc: lexer.FnOrArrowStartLoc}, -// \\"Consider adding the \"async\" keyword here") -// \\note.Location.Suggestion = "async" -// \\notes = []logger.MsgData{note} -// \\} -// \\lexer.addRangeErrorWithNotes(RangeOfIdentifier(lexer.source, lexer.AwaitKeywordLoc), -// \\"\"await\" can only be used inside an \"async\" function", -// \\notes) -// \\panic(LexerPanic{}) -// \\} -// \\ -// \\found := fmt.Sprintf("%q", lexer.Raw()) -// \\if lexer.start == len(lexer.source.Contents) { -// \\found = "end of file" -// \\} -// \\lexer.addRangeError(lexer.Range(), fmt.Sprintf("Expected %s but found %s", text, found)) -// \\panic(LexerPanic{}) -// \\} -// \\ -// \\func (lexer *Lexer) Expected(token T) { -// \\if text, ok := tokenToString[token]; ok { -// \\lexer.ExpectedString(text) -// \\} else { -// \\lexer.Unexpected() -// \\} -// \\} -// \\ -// \\func (lexer *Lexer) Unexpected() { -// \\found := fmt.Sprintf("%q", lexer.Raw()) -// \\if lexer.start == len(lexer.source.Contents) { -// \\found = "end of file" -// \\} -// \\lexer.addRangeError(lexer.Range(), fmt.Sprintf("Unexpected %s", found)) -// \\panic(LexerPanic{}) -// \\} -// \\ -// \\func (lexer *Lexer) Expect(token T) { -// \\if lexer.Token != token { -// \\lexer.Expected(token) -// \\} -// \\lexer.Next() -// \\} -// \\ -// \\func (lexer *Lexer) ExpectOrInsertSemicolon() { -// \\if lexer.Token == TSemicolon || (!lexer.HasNewlineBefore && -// \\lexer.Token != TCloseBrace && lexer.Token != TEndOfFile) { -// \\lexer.Expect(TSemicolon) -// \\} -// \\} -// \\func (lexer *Lexer) ExpectLessThan(isInsideJSXElement bool) { -// \\switch lexer.Token { -// \\case TLessThan: -// \\if isInsideJSXElement { -// \\lexer.NextInsideJSXElement() -// \\} else { -// \\lexer.Next() -// \\} -// \\ -// \\case TLessThanEquals: -// \\lexer.Token = TEquals -// \\lexer.start++ -// \\lexer.maybeExpandEquals() -// \\ -// \\case TLessThanLessThan: -// \\lexer.Token = TLessThan -// \\lexer.start++ -// \\ -// \\case TLessThanLessThanEquals: -// \\lexer.Token = TLessThanEquals -// \\lexer.start++ -// \\ -// \\default: -// \\lexer.Expected(TLessThan) -// \\} -// \\} -// \\ -// \\// This parses a single ">" token. If that is the first part of a longer token, -// \\// this function splits off the first ">" and leaves the remainder of the -// \\// current token as another, smaller token. For example, ">>=" becomes ">=". -// \\func (lexer *Lexer) ExpectGreaterThan(isInsideJSXElement bool) { -// \\switch lexer.Token { -// \\case TGreaterThan: -// \\if isInsideJSXElement { -// \\lexer.NextInsideJSXElement() -// \\} else { -// \\lexer.Next() -// \\} -// \\ -// \\case TGreaterThanEquals: -// \\lexer.Token = TEquals -// \\lexer.start++ -// \\lexer.maybeExpandEquals() -// \\ -// \\case TGreaterThanGreaterThan: -// \\lexer.Token = TGreaterThan -// \\lexer.start++ -// \\ -// \\case TGreaterThanGreaterThanEquals: -// \\lexer.Token = TGreaterThanEquals -// \\lexer.start++ -// \\ -// \\case TGreaterThanGreaterThanGreaterThan: -// \\lexer.Token = TGreaterThanGreaterThan -// \\lexer.start++ -// \\ -// \\case TGreaterThanGreaterThanGreaterThanEquals: -// \\lexer.Token = TGreaterThanGreaterThanEquals -// \\lexer.start++ -// \\ -// \\default: -// \\lexer.Expected(TGreaterThan) -// \\} -// \\} -// \\ -// \\func (lexer *Lexer) maybeExpandEquals() { -// \\switch lexer.codePoint { -// \\case '>': -// \\// "=" + ">" = "=>" -// \\lexer.Token = TEqualsGreaterThan -// \\lexer.step() -// \\ -// \\case '=': -// \\// "=" + "=" = "==" -// \\lexer.Token = TEqualsEquals -// \\lexer.step() -// \\ -// \\if lexer.Token == '=' { -// \\// "=" + "==" = "===" -// \\lexer.Token = TEqualsEqualsEquals -// \\lexer.step() -// \\} -// \\} -// \\} -// \\ -// \\func IsIdentifier(text string) bool { -// \\if len(text) == 0 { -// \\return false -// \\} -// \\for i, codePoint := range text { -// \\if i == 0 { -// \\if !IsIdentifierStart(codePoint) { -// \\return false -// \\} -// \\} else { -// \\if !IsIdentifierContinue(codePoint) { -// \\return false -// \\} -// \\} -// \\} -// \\return true -// \\} -// \\ -// \\func IsIdentifierES5AndESNext(text string) bool { -// \\if len(text) == 0 { -// \\return false -// \\} -// \\for i, codePoint := range text { -// \\if i == 0 { -// \\if !IsIdentifierStartES5AndESNext(codePoint) { -// \\return false -// \\} -// \\} else { -// \\if !IsIdentifierContinueES5AndESNext(codePoint) { -// \\return false -// \\} -// \\} -// \\} -// \\return true -// \\} -// \\ -// \\func ForceValidIdentifier(text string) string { -// \\if IsIdentifier(text) { -// \\return text -// \\} -// \\sb := strings.Builder{} -// \\ -// \\// Identifier start -// \\c, width := utf8.DecodeRuneInString(text) -// \\text = text[width:] -// \\if IsIdentifierStart(c) { -// \\sb.WriteRune(c) -// \\} else { -// \\sb.WriteRune('_') -// \\} -// \\ -// \\// Identifier continue -// \\for text != "" { -// \\c, width := utf8.DecodeRuneInString(text) -// \\text = text[width:] -// \\if IsIdentifierContinue(c) { -// \\sb.WriteRune(c) -// \\} else { -// \\sb.WriteRune('_') -// \\} -// \\} -// \\ -// \\return sb.String() -// \\} -// \\ -// \\// This does "IsIdentifier(UTF16ToString(text))" without any allocations -// \\func IsIdentifierUTF16(text []uint16) bool { -// \\n := len(text) -// \\if n == 0 { -// \\return false -// \\} -// \\for i := 0; i < n; i++ { -// \\isStart := i == 0 -// \\r1 := rune(text[i]) -// \\if r1 >= 0xD800 && r1 <= 0xDBFF && i+1 < n { -// \\if r2 := rune(text[i+1]); r2 >= 0xDC00 && r2 <= 0xDFFF { -// \\r1 = (r1 << 10) + r2 + (0x10000 - (0xD800 << 10) - 0xDC00) -// \\i++ -// \\} -// \\} -// \\if isStart { -// \\if !IsIdentifierStart(r1) { -// \\return false -// \\} -// \\} else { -// \\if !IsIdentifierContinue(r1) { -// \\return false -// \\} -// \\} -// \\} -// \\return true -// \\} -// \\ -// \\// This does "IsIdentifierES5AndESNext(UTF16ToString(text))" without any allocations -// \\func IsIdentifierES5AndESNextUTF16(text []uint16) bool { -// \\n := len(text) -// \\if n == 0 { -// \\return false -// \\} -// \\for i := 0; i < n; i++ { -// \\isStart := i == 0 -// \\r1 := rune(text[i]) -// \\if r1 >= 0xD800 && r1 <= 0xDBFF && i+1 < n { -// \\if r2 := rune(text[i+1]); r2 >= 0xDC00 && r2 <= 0xDFFF { -// \\r1 = (r1 << 10) + r2 + (0x10000 - (0xD800 << 10) - 0xDC00) -// \\i++ -// \\} -// \\} -// \\if isStart { -// \\if !IsIdentifierStartES5AndESNext(r1) { -// \\return false -// \\} -// \\} else { -// \\if !IsIdentifierContinueES5AndESNext(r1) { -// \\return false -// \\} -// \\} -// \\} -// \\return true -// \\} -// \\ -// \\func IsIdentifierStart(codePoint rune) bool { -// \\switch codePoint { -// \\case '_', '$', -// \\'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', -// \\'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -// \\'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', -// \\'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': -// \\return true -// \\} -// \\ -// \\// All ASCII identifier start code points are listed above -// \\if codePoint < 0x7F { -// \\return false -// \\} -// \\ -// \\return unicode.Is(idStartES5OrESNext, codePoint) -// \\} -// \\ -// \\func IsIdentifierContinue(codePoint rune) bool { -// \\switch codePoint { -// \\case '_', '$', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', -// \\'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', -// \\'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -// \\'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', -// \\'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': -// \\return true -// \\} -// \\ -// \\// All ASCII identifier start code points are listed above -// \\if codePoint < 0x7F { -// \\return false -// \\} -// \\ -// \\// ZWNJ and ZWJ are allowed in identifiers -// \\if codePoint == 0x200C || codePoint == 0x200D { -// \\return true -// \\} -// \\ -// \\return unicode.Is(idContinueES5OrESNext, codePoint) -// \\} -// \\ -// \\func IsIdentifierStartES5AndESNext(codePoint rune) bool { -// \\switch codePoint { -// \\case '_', '$', -// \\'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', -// \\'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -// \\'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', -// \\'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': -// \\return true -// \\} -// \\ -// \\// All ASCII identifier start code points are listed above -// \\if codePoint < 0x7F { -// \\return false -// \\} -// \\ -// \\return unicode.Is(idStartES5AndESNext, codePoint) -// \\} -// \\ -// \\func IsIdentifierContinueES5AndESNext(codePoint rune) bool { -// \\switch codePoint { -// \\case '_', '$', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', -// \\'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', -// \\'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -// \\'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', -// \\'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': -// \\return true -// \\} -// \\ -// \\// All ASCII identifier start code points are listed above -// \\if codePoint < 0x7F { -// \\return false -// \\} -// \\ -// \\// ZWNJ and ZWJ are allowed in identifiers -// \\if codePoint == 0x200C || codePoint == 0x200D { -// \\return true -// \\} -// \\ -// \\return unicode.Is(idContinueES5AndESNext, codePoint) -// \\} -// \\ -// \\// See the "White Space Code Points" table in the ECMAScript standard -// \\func IsWhitespace(codePoint rune) bool { -// \\switch codePoint { -// \\case -// \\'\u0009', // character tabulation -// \\'\u000B', // line tabulation -// \\'\u000C', // form feed -// \\'\u0020', // space -// \\'\u00A0', // no-break space -// \\ -// \\// Unicode "Space_Separator" code points -// \\'\u1680', // ogham space mark -// \\'\u2000', // en quad -// \\'\u2001', // em quad -// \\'\u2002', // en space -// \\'\u2003', // em space -// \\'\u2004', // three-per-em space -// \\'\u2005', // four-per-em space -// \\'\u2006', // six-per-em space -// \\'\u2007', // figure space -// \\'\u2008', // punctuation space -// \\'\u2009', // thin space -// \\'\u200A', // hair space -// \\'\u202F', // narrow no-break space -// \\'\u205F', // medium mathematical space -// \\'\u3000', // ideographic space -// \\ -// \\'\uFEFF': // zero width non-breaking space -// \\return true -// \\ -// \\default: -// \\return false -// \\} -// \\} -// \\ -// \\func RangeOfIdentifier(source logger.Source, loc logger.Loc) logger.Range { -// \\text := source.Contents[loc.Start:] -// \\if len(text) == 0 { -// \\return logger.Range{Loc: loc, Len: 0} -// \\} -// \\ -// \\i := 0 -// \\c, _ := utf8.DecodeRuneInString(text[i:]) -// \\ -// \\// Handle private names -// \\if c == '#' { -// \\i++ -// \\c, _ = utf8.DecodeRuneInString(text[i:]) -// \\} -// \\ -// \\if IsIdentifierStart(c) || c == '\\' { -// \\// Search for the end of the identifier -// \\for i < len(text) { -// \\c2, width2 := utf8.DecodeRuneInString(text[i:]) -// \\if c2 == '\\' { -// \\i += width2 -// \\ -// \\// Skip over bracketed unicode escapes such as "\u{10000}" -// \\if i+2 < len(text) && text[i] == 'u' && text[i+1] == '{' { -// \\i += 2 -// \\for i < len(text) { -// \\if text[i] == '}' { -// \\i++ -// \\break -// \\} -// \\i++ -// \\} -// \\} -// \\} else if !IsIdentifierContinue(c2) { -// \\return logger.Range{Loc: loc, Len: int32(i)} -// \\} else { -// \\i += width2 -// \\} -// \\} -// \\} -// \\ -// \\// When minifying, this identifier may have originally been a string -// \\return source.RangeOfString(loc) -// \\} -// \\ -// \\func (lexer *Lexer) ExpectJSXElementChild(token T) { -// \\if lexer.Token != token { -// \\lexer.Expected(token) -// \\} -// \\lexer.NextJSXElementChild() -// \\} -// \\ -// \\func (lexer *Lexer) NextJSXElementChild() { -// \\lexer.HasNewlineBefore = false -// \\originalStart := lexer.end -// \\ -// \\for { -// \\lexer.start = lexer.end -// \\lexer.Token = 0 -// \\ -// \\switch lexer.codePoint { -// \\case -1: // This indicates the end of the file -// \\lexer.Token = TEndOfFile -// \\ -// \\case '{': -// \\lexer.step() -// \\lexer.Token = TOpenBrace -// \\ -// \\case '<': -// \\lexer.step() -// \\lexer.Token = TLessThan -// \\ -// \\default: -// \\needsFixing := false -// \\ -// \\stringLiteral: -// \\for { -// \\switch lexer.codePoint { -// \\case -1: -// \\// Reaching the end of the file without a closing element is an error -// \\lexer.SyntaxError() -// \\ -// \\case '&', '\r', '\n', '\u2028', '\u2029': -// \\// This needs fixing if it has an entity or if it's a multi-line string -// \\needsFixing = true -// \\lexer.step() -// \\ -// \\case '{', '<': -// \\// Stop when the string ends -// \\break stringLiteral -// \\ -// \\default: -// \\// Non-ASCII strings need the slow path -// \\if lexer.codePoint >= 0x80 { -// \\needsFixing = true -// \\} -// \\lexer.step() -// \\} -// \\} -// \\ -// \\lexer.Token = TStringLiteral -// \\text := lexer.source.Contents[originalStart:lexer.end] -// \\ -// \\if needsFixing { -// \\// Slow path -// \\lexer.decodedStringLiteralOrNil = fixWhitespaceAndDecodeJSXEntities(text) -// \\ -// \\// Skip this token if it turned out to be empty after trimming -// \\if len(lexer.decodedStringLiteralOrNil) == 0 { -// \\lexer.HasNewlineBefore = true -// \\continue -// \\} -// \\} else { -// \\// Fast path -// \\n := len(text) -// \\copy := make([]uint16, n) -// \\for i := 0; i < n; i++ { -// \\copy[i] = uint16(text[i]) -// \\} -// \\lexer.decodedStringLiteralOrNil = copy -// \\} -// \\} -// \\ -// \\break -// \\} -// \\} -// \\ -// \\func (lexer *Lexer) ExpectInsideJSXElement(token T) { -// \\if lexer.Token != token { -// \\lexer.Expected(token) -// \\} -// \\lexer.NextInsideJSXElement() -// \\} -// \\ -// \\func (lexer *Lexer) NextInsideJSXElement() { -// \\lexer.HasNewlineBefore = false -// \\ -// \\for { -// \\lexer.start = lexer.end -// \\lexer.Token = 0 -// \\ -// \\switch lexer.codePoint { -// \\case -1: // This indicates the end of the file -// \\lexer.Token = TEndOfFile -// \\ -// \\case '\r', '\n', '\u2028', '\u2029': -// \\lexer.step() -// \\lexer.HasNewlineBefore = true -// \\continue -// \\ -// \\case '\t', ' ': -// \\lexer.step() -// \\continue -// \\ -// \\case '.': -// \\lexer.step() -// \\lexer.Token = TDot -// \\ -// \\case '=': -// \\lexer.step() -// \\lexer.Token = TEquals -// \\ -// \\case '{': -// \\lexer.step() -// \\lexer.Token = TOpenBrace -// \\ -// \\case '}': -// \\lexer.step() -// \\lexer.Token = TCloseBrace -// \\ -// \\case '<': -// \\lexer.step() -// \\lexer.Token = TLessThan -// \\ -// \\case '>': -// \\lexer.step() -// \\lexer.Token = TGreaterThan -// \\ -// \\case '/': -// \\// '/' or '//' or '/* ... */' -// \\lexer.step() -// \\switch lexer.codePoint { -// \\case '/': -// \\singleLineComment: -// \\for { -// \\lexer.step() -// \\switch lexer.codePoint { -// \\case '\r', '\n', '\u2028', '\u2029': -// \\break singleLineComment -// \\ -// \\case -1: // This indicates the end of the file -// \\break singleLineComment -// \\} -// \\} -// \\continue -// \\ -// \\case '*': -// \\lexer.step() -// \\startRange := lexer.Range() -// \\multiLineComment: -// \\for { -// \\switch lexer.codePoint { -// \\case '*': -// \\lexer.step() -// \\if lexer.codePoint == '/' { -// \\lexer.step() -// \\break multiLineComment -// \\} -// \\ -// \\case '\r', '\n', '\u2028', '\u2029': -// \\lexer.step() -// \\lexer.HasNewlineBefore = true -// \\ -// \\case -1: // This indicates the end of the file -// \\lexer.start = lexer.end -// \\lexer.addErrorWithNotes(lexer.Loc(), "Expected \"*/\" to terminate multi-line comment", -// \\[]logger.MsgData{logger.RangeData(&lexer.tracker, startRange, "The multi-line comment starts here")}) -// \\panic(LexerPanic{}) -// \\ -// \\default: -// \\lexer.step() -// \\} -// \\} -// \\continue -// \\ -// \\default: -// \\lexer.Token = TSlash -// \\} -// \\ -// \\case '\'', '"': -// \\var backslash logger.Range -// \\quote := lexer.codePoint -// \\needsDecode := false -// \\lexer.step() -// \\ -// \\stringLiteral: -// \\for { -// \\switch lexer.codePoint { -// \\case -1: // This indicates the end of the file -// \\lexer.SyntaxError() -// \\ -// \\case '&': -// \\needsDecode = true -// \\lexer.step() -// \\ -// \\case '\\': -// \\backslash = logger.Range{Loc: logger.Loc{Start: int32(lexer.end)}, Len: 1} -// \\lexer.step() -// \\continue -// \\ -// \\case quote: -// \\if backslash.Len > 0 { -// \\backslash.Len++ -// \\lexer.PreviousBackslashQuoteInJSX = backslash -// \\} -// \\lexer.step() -// \\break stringLiteral -// \\ -// \\default: -// \\// Non-ASCII strings need the slow path -// \\if lexer.codePoint >= 0x80 { -// \\needsDecode = true -// \\} -// \\lexer.step() -// \\} -// \\backslash = logger.Range{} -// \\} -// \\ -// \\lexer.Token = TStringLiteral -// \\text := lexer.source.Contents[lexer.start+1 : lexer.end-1] -// \\ -// \\if needsDecode { -// \\// Slow path -// \\lexer.decodedStringLiteralOrNil = decodeJSXEntities([]uint16{}, text) -// \\} else { -// \\// Fast path -// \\n := len(text) -// \\copy := make([]uint16, n) -// \\for i := 0; i < n; i++ { -// \\copy[i] = uint16(text[i]) -// \\} -// \\lexer.decodedStringLiteralOrNil = copy -// \\} -// \\ -// \\default: -// \\// Check for unusual whitespace characters -// \\if IsWhitespace(lexer.codePoint) { -// \\lexer.step() -// \\continue -// \\} -// \\ -// \\if IsIdentifierStart(lexer.codePoint) { -// \\lexer.step() -// \\for IsIdentifierContinue(lexer.codePoint) || lexer.codePoint == '-' { -// \\lexer.step() -// \\} -// \\ -// \\// Parse JSX namespaces. These are not supported by React or TypeScript -// \\// but someone using JSX syntax in more obscure ways may find a use for -// \\// them. A namespaced name is just always turned into a string so you -// \\// can't use this feature to reference JavaScript identifiers. -// \\if lexer.codePoint == ':' { -// \\lexer.step() -// \\if IsIdentifierStart(lexer.codePoint) { -// \\lexer.step() -// \\for IsIdentifierContinue(lexer.codePoint) || lexer.codePoint == '-' { -// \\lexer.step() -// \\} -// \\} else { -// \\lexer.addError(logger.Loc{Start: lexer.Range().End()}, -// \\fmt.Sprintf("Expected identifier after %q in namespaced JSX name", lexer.Raw())) -// \\} -// \\} -// \\ -// \\lexer.Identifier = lexer.Raw() -// \\lexer.Token = TIdentifier -// \\break -// \\} -// \\ -// \\lexer.end = lexer.current -// \\lexer.Token = TSyntaxError -// \\} -// \\ -// \\return -// \\} -// \\} -// \\ -// \\func (lexer *Lexer) Next() { -// \\lexer.HasNewlineBefore = lexer.end == 0 -// \\lexer.HasPureCommentBefore = false -// \\lexer.PrevTokenWasAwaitKeyword = false -// \\lexer.CommentsToPreserveBefore = nil -// \\ -// \\for { -// \\lexer.start = lexer.end -// \\lexer.Token = 0 -// \\ -// \\switch lexer.codePoint { -// \\case -1: // This indicates the end of the file -// \\lexer.Token = TEndOfFile -// \\ -// \\case '#': -// \\if lexer.start == 0 && strings.HasPrefix(lexer.source.Contents, "#!") { -// \\// "#!/usr/bin/env node" -// \\lexer.Token = THashbang -// \\hashbang: -// \\for { -// \\lexer.step() -// \\switch lexer.codePoint { -// \\case '\r', '\n', '\u2028', '\u2029': -// \\break hashbang -// \\ -// \\case -1: // This indicates the end of the file -// \\break hashbang -// \\} -// \\} -// \\lexer.Identifier = lexer.Raw() -// \\} else { -// \\// "#foo" -// \\lexer.step() -// \\} -// ; - -// const repeat_count: usize = 1; -// const loop_count: usize = 1000; - -// pub fn main() anyerror!void { -// try HashTable.init(std.heap.c_allocator); -// Bitset.init(); -// { - -// // Ensure that the optimizer doesn't do something fancy with static memory addresses -// var code = try std.heap.c_allocator.dupe(u8, unicode_text); - -// var iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// var hash_table_count: usize = 0; -// var jump_table_count: usize = 0; -// var jump_table_elapsed: u64 = 0; -// var hash_table_elapsed: u64 = 0; -// var binary_search_elapsed: u64 = 0; -// var binary_search_count: usize = 0; -// var bitset_elapsed: u64 = 0; -// var bitset_count: usize = 0; - -// // change up the order these run in -// var loop_i: usize = 0; -// while (loop_i < loop_count) : (loop_i += 1) { -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// hash_table_count = 0; -// while (iter.nextCodepoint()) |cp| { -// hash_table_count += @as(usize, @intFromBool(HashTable.isIdentifierStart(cp) or HashTable.isIdentifierPart(cp))); -// } -// } -// hash_table_elapsed += timer.read(); -// } - -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// jump_table_count = 0; -// while (iter.nextCodepoint()) |cp| { -// jump_table_count += @as( -// usize, -// @intFromBool(JumpTable.isIdentifierStart(cp) or JumpTable.isIdentifierPart(cp)), -// ); -// } -// } -// jump_table_elapsed += timer.read(); -// } - -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// binary_search_count = 0; -// while (iter.nextCodepoint()) |cp| { -// binary_search_count += @as( -// usize, -// @intFromBool( -// BinarySearch.isIdentifierStart( -// cp, -// ) or BinarySearch.isIdentifierPart( -// cp, -// ), -// ), -// ); -// } -// } -// binary_search_elapsed += timer.read(); -// } - -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// bitset_count = 0; -// while (iter.nextCodepoint()) |cp| { -// bitset_count += @as( -// usize, -// @intFromBool( -// Bitset.isIdentifierStart( -// cp, -// ) or Bitset.isIdentifierPart( -// cp, -// ), -// ), -// ); -// } -// } -// bitset_elapsed += timer.read(); -// } -// } - -// .print( -// \\---- Unicode text ----- -// \\ -// \\Timings (sum of running {d} times each, lower is better): -// \\ -// \\ Binary Search : {d}ns -// \\ Hash Table : {d}ns -// \\ Switch statement : {d}ns -// \\ Bitset : {d}ns -// \\ -// \\Match count (these should be the same): -// \\ -// \\ Binary Search : {d} -// \\ Hash Table : {d} -// \\ Switch statement : {d} -// \\ Bitset : {d} -// \\ -// \\ -// , -// .{ -// repeat_count * loop_count, -// binary_search_elapsed, -// hash_table_elapsed, -// jump_table_elapsed, -// bitset_elapsed, - -// binary_search_count, -// hash_table_count, -// jump_table_count, -// bitset_count, -// }, -// ); -// } - -// { - -// // Ensure that the optimizer doesn't do something fancy with static memory addresses -// var code = try std.heap.c_allocator.dupe(u8, ascii_text); - -// var iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// var hash_table_count: usize = 0; -// var jump_table_count: usize = 0; -// var jump_table_elapsed: u64 = 0; -// var hash_table_elapsed: u64 = 0; -// var binary_search_elapsed: u64 = 0; -// var binary_search_count: usize = 0; -// var bitset_count: usize = 0; -// var bitset_elapsed: u64 = 0; - -// // change up the order these run in -// var loop_i: usize = 0; -// while (loop_i < loop_count) : (loop_i += 1) { -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// hash_table_count = 0; -// while (iter.nextCodepoint()) |cp| { -// hash_table_count += @as(usize, @intFromBool(HashTable.isIdentifierStart(cp) or HashTable.isIdentifierPart(cp))); -// } -// } -// hash_table_elapsed += timer.read(); -// } - -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// jump_table_count = 0; -// while (iter.nextCodepoint()) |cp| { -// jump_table_count += @as( -// usize, -// @intFromBool(JumpTable.isIdentifierStart(cp) or JumpTable.isIdentifierPart(cp)), -// ); -// } -// } -// jump_table_elapsed += timer.read(); -// } - -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// binary_search_count = 0; -// while (iter.nextCodepoint()) |cp| { -// binary_search_count += @as( -// usize, -// @intFromBool( -// BinarySearch.isIdentifierStart( -// cp, -// ) or BinarySearch.isIdentifierPart( -// cp, -// ), -// ), -// ); -// } -// } -// binary_search_elapsed += timer.read(); -// } - -// { -// var iteration_i: usize = 0; -// var timer = try std.time.Timer.start(); -// while (iteration_i < repeat_count) : (iteration_i += 1) { -// @setEvalBranchQuota(99999); -// iter = std.unicode.Utf8Iterator{ .bytes = code, .i = 0 }; -// bitset_count = 0; -// while (iter.nextCodepoint()) |cp| { -// bitset_count += @as( -// usize, -// @intFromBool( -// Bitset.isIdentifierStart( -// cp, -// ) or Bitset.isIdentifierPart( -// cp, -// ), -// ), -// ); -// } -// } -// bitset_elapsed += timer.read(); -// } -// } - -// { -// iter = std.unicode.Utf8Iterator{ .bytes = ascii_text, .i = 0 }; -// while (iter.nextCodepoint()) |cp| { -// if (cp > 127) std.debug.panic("This is not ASCII at {d}", .{iter.i}); -// } -// } - -// ( -// \\---- ASCII text ----- -// \\ -// \\Timings (sum of running {d} times each, lower is better): -// \\ -// \\ Binary Search : {d}ns -// \\ Hash Table : {d}ns -// \\ Switch statement : {d}ns -// \\ Bitset : {d}ns -// \\ -// \\Match count (these should be the same): -// \\ -// \\ Binary Search : {d} -// \\ Hash Table : {d} -// \\ Switch statement : {d} -// \\ Bitset : {d} -// \\ -// \\ -// , -// .{ -// repeat_count * loop_count, -// binary_search_elapsed, -// hash_table_elapsed, -// jump_table_elapsed, -// bitset_elapsed, - -// binary_search_count, -// hash_table_count, -// jump_table_count, -// bitset_count, -// }, -// ); -// } -// } +/// isIDContinueESNext checks if a codepoint is valid in the isIDContinueESNext category +pub fn isIDContinueESNext(cp: u21) bool { + const high = cp >> 8; + const low = cp & 0xFF; + const stage2_idx = idContinueESNext.stage1[high]; + const bit_pos = stage2_idx + low; + const u64_idx = bit_pos >> 6; + const bit_idx = @as(u6, @intCast(bit_pos & 63)); + return (idContinueESNext.stage2[u64_idx] & (@as(u64, 1) << bit_idx)) != 0; +} +const idContinueESNext = struct { + pub const stage1 = [_]u16{ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096, 256, 4352, 4608, 4864, 256, 5120, 5376, 5632, 5888, 6144, 6400, 6656, 256, 256, 6912, 7168, 7424, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7936, 8192, 7680, 7680, 8448, 8704, 7680, 7680, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 8960, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 9216, 256, 9472, 9728, 9984, 10240, 10496, 10752, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 11008, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 256, 11264, 11520, 256, 11776, 12032, 12288, 12544, 12800, 13056, 13312, 13568, 13824, 256, 14080, 14336, 14592, 14848, 15104, 15360, 15616, 15872, 16128, 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176, 18432, 18688, 18944, 7680, 19200, 19456, 19712, 19968, 256, 256, 256, 20224, 20480, 20736, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 20992, 256, 256, 256, 256, 21248, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 256, 256, 21504, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 256, 256, 21760, 22016, 7680, 7680, 22272, 22528, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 22784, 256, 256, 256, 256, 23040, 23296, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 23552, 256, 23808, 24064, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 24320, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 24576, 7680, 24832, 25088, 7680, 25344, 25600, 25856, 26112, 7680, 7680, 26368, 7680, 7680, 7680, 7680, 26624, 26880, 27136, 27392, 7680, 27648, 7680, 7680, 27904, 28160, 28416, 7680, 7680, 7680, 7680, 28672, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 28928, 7680, 7680, 7680, 7680, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 29184, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 29440, 29696, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 29952, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 30208, 256, 256, 30464, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 256, 256, 30720, 7680, 7680, 7680, 7680, 7680, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 30976, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 31232, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 31488, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680 }; + pub const stage2 = [_]u64{ 287948901175001088, 576460745995190270, 333270770471927808, 18410715276682199039, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 88094074470339, 18446744073709551615, 13609878073913638911, 18446744056529672128, 18428729675200069631, 18446744073709551615, 18446744073709551615, 18446744073709550843, 18446744073709551615, 18446462598732840959, 18446744069456527359, 13835058055282033151, 2119858418286774, 18446744069548736512, 18446678103011885055, 18446744073709551615, 11529212845433552895, 18446744073709486080, 18446744073709545471, 1125899906842623, 2612087783874887679, 70368744177663, 18446471390799331327, 18446744073692806911, 18446744056529682431, 18446744073709551615, 18446462392574410751, 17565725197581524975, 5765733215448889759, 15235112390417287150, 18014125208779143, 17576984196650090478, 18302910150157089727, 17576984196649951214, 844217444219295, 14123225865944680428, 281200107273671, 17582050746231021567, 281265183931871, 17577547146603651055, 4221915814182367, 18446744073709412351, 18158794964244397535, 3457638613854978030, 3658904103781503, 576460752303423486, 67076095, 4611685674830002134, 4093607775, 14024213633433600001, 18446216308128218879, 2305843009196916703, 64, 18446744073709551615, 18446744073709487103, 18446744070488326143, 17870283321406070975, 18446744073709551615, 18446744070446333439, 9168765891372858879, 18446744073701162813, 18446744073696837631, 1123704775901183, 18446744069414649855, 4557642822898941951, 18446744073709551614, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446638520593285119, 18446744069548802046, 144053615424700415, 9007197111451647, 3905461007941631, 18446744073709551615, 4394566287359, 18446744069481674752, 144115188075855871, 18446471394825863167, 18014398509481983, 1152657619668697087, 8796093022207936, 18446480190918885375, 134153215, 18446744069683019775, 11529215043920986111, 13834777130128311295, 32767, 18446744073709551615, 4494803601399807, 18446744073709551615, 4503599627370495, 72057594037927935, 4611686018427380735, 16717361816799216127, 576460752302833664, 18446744070475743231, 4611686017001275199, 6908521828386340863, 2295745090394464220, 9223372036854788096, 9223934986809245697, 536805376, 562821641207808, 17582049991377026180, 18446744069414601696, 511, 0, 0, 0, 0, 0, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4494940973301759, 18446498607738650623, 9223513873854758911, 9187201948305063935, 18446744071553646463, 2251518330118602976, 18446744073709551614, 18446744069389418495, 17870283321406128127, 18446462598732840928, 18446744073709551615, 18446744069414617087, 18446462598732840960, 18446744073709551615, 18446744073709551615, 18446744073709551615, 0, 18446744073709551615, 18446744073709551615, 8191, 4611686018427322368, 17592185987071, 13830835930631503871, 18446744073709551615, 1125899906842623, 18446744060816261120, 18446744073709551615, 18446744073709550079, 18445618173868443647, 18691697672191, 4503599627370495, 18446744073709551615, 16789419406609285183, 18446532967477018623, 2305843004919775231, 18446744073709551615, 9223372032626884609, 36028797018963967, 18194542490348896255, 18446744073709551615, 35184368733388807, 18446602782178705022, 18446466996645134335, 18446744073709551615, 288010473826156543, 18446744073709551615, 18446744073709551615, 18446462667452317695, 1152921504606845055, 18446744073709551615, 18446532967477018623, 18446744073709551615, 67108863, 6881498031078244479, 18446744073709551579, 1125899906842623, 18446744073709027328, 4611686018427387903, 18446744073709486080, 18446744073709355007, 1152640029630136575, 7036870122864639, 18437455399478157312, 18446744073709551615, 2305843009213693951, 9799832780635308032, 18446743798965862398, 9223372036854775807, 486341884, 13258596753222922239, 1073692671, 18446744073709551615, 576460752303423487, 0, 9007199254740991, 0, 2305843009213693952, 0, 0, 18446744069951455231, 4295098367, 18446708893632430079, 576460752303359999, 18446744070488326143, 4128527, 18446744073709551615, 18446744073709551615, 18446466993558126591, 1152921504591118335, 18446463698244468735, 17870001915148894207, 2016486715970549759, 0, 36028797018963967, 1095220854783, 575897802350002111, 0, 10502394331027995967, 36028792728190975, 2147483647, 15762594400829440, 288230371860938751, 0, 13907115649320091647, 0, 9745789593611923567, 2305843004918726656, 536870911, 549755813631, 18014398509481983, 2251795522912255, 262143, 0, 18446744073709551615, 511, 2251799813685247, 2251799813685247, 287950000686628863, 0, 0, 0, 0, 0, 875211255709695, 16140901064495857664, 18446463149025525759, 18446462598732972031, 18446462598732841023, 36028792723996703, 18446744073709551615, 9241386160486350975, 576460752303423487, 287951100198191108, 18437736874454810623, 22517998136787184, 18446744073709551615, 402644511, 13907115649319829503, 3, 18446464796682337663, 287957697268023295, 18153444948953374703, 8760701963286943, 0, 0, 18446744073709551615, 16173172735, 18446744073709551615, 67043519, 0, 0, 18392700878181105663, 1056964609, 18446744073709551615, 67043345, 144115188075855871, 1023, 287966492958392319, 127, 0, 0, 576460752303423487, 0, 18446744069414584320, 9223376434901286911, 17996384110963061375, 67043343, 18446740770879700992, 120208752639, 9223372036854775807, 18446744073709486208, 18446462599336820735, 144115188075855871, 18410715276690587135, 18445618173869752321, 36027697507139583, 0, 13006395723845991295, 18446741595580465407, 4393784803327, 0, 0, 0, 0, 36028792723996672, 14411518807585456127, 67043335, 281474976710656, 0, 18446744073709551615, 18446744073709551615, 67108863, 0, 18446744073709551615, 140737488355327, 18446744073709551615, 18446744073709551615, 18446744073709551615, 15, 0, 0, 0, 0, 18446744073709486080, 562949953421311, 281474976710655, 4194303, 0, 0, 18446744073709551615, 127, 0, 0, 144115188075855871, 18446466994631868415, 9223372036854775807, 8796093022143487, 36028797018963967, 16212958624241090575, 65535, 0, 0, 18446744073709551615, 0, 0, 18446744073709551615, 18446744073709520895, 4294934783, 844540894248960, 18446744073709551615, 18446744073709551615, 18446744073709551615, 72057594037927935, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4194303, 511, 0, 0, 0, 0, 0, 0, 8065665457643847680, 1125934266580991, 18446463629527547904, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 1152921504606846975, 18446744073709551615, 2305570330330005503, 1677656575, 0, 18446532967477018623, 127, 0, 0, 0, 17872504197455282176, 65970697670631, 0, 0, 28, 0, 0, 18446744073709551615, 18446744073707454463, 17005555242810474495, 18446744073709551599, 8935141660164089791, 18446744073709419615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446743249075830783, 17870283321271910397, 18437736874452713471, 18446603336221163519, 18446741874686295551, 18446744073709539319, 17906312118425092095, 9042383626829823, 281470547525648, 0, 8660801552383, 0, 0, 0, 18446471240106377087, 70368744177663, 32768, 0, 4611439727822766079, 17407, 0, 0, 0, 0, 140737488289792, 288230376151711743, 0, 0, 0, 288230376151646208, 0, 0, 0, 9223213153129594880, 18446744073709551615, 18446744073709551615, 18446744073709551615, 8323103, 18446744073709551615, 67047423, 0, 0, 790380184120328175, 6843210385291930244, 1152917029519358975, 0, 0, 0, 0, 287948901175001088, 18446744073709551615, 18446744073709551615, 18446744073709551615, 4294967295, 288230376151711743, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744070488326143, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446462615912710143, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446462607322775551, 18446744073709551615, 1073741823, 0, 0, 1073741823, 0, 0, 0, 18446744073709551615, 18446744073709488127, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 281474976710655, 0, 18446744073709551615, 18446744073709551615, 18446744073709551615, 281474976710655 }; +}; diff --git a/src/js_lexer/identifier_cache.zig b/src/js_lexer/identifier_cache.zig deleted file mode 100644 index de1ea0e771..0000000000 --- a/src/js_lexer/identifier_cache.zig +++ /dev/null @@ -1,22 +0,0 @@ -const std = @import("std"); -const bun = @import("root").bun; -const identifier_data = @import("./identifier_data.zig"); - -pub const CachedBitset = extern struct { - range: [2]i32, - len: u32, - - pub fn fromFile(comptime filename: anytype) CachedBitset { - return comptime @as(CachedBitset, @bitCast(bun.asByteSlice(@embedFile(filename)).ptr[0..@sizeOf(CachedBitset)].*)); - } -}; - -pub fn setMasks(masks: [*:0]const u8, comptime MaskType: type, masky: MaskType) void { - const FieldInfo: std.builtin.Type.StructField = std.meta.fieldInfo(MaskType, "masks"); - masky.masks = @as(masks, @bitCast(FieldInfo.type)); -} - -pub const id_start_meta = identifier_data.id_start_cached; -pub const id_continue_meta = identifier_data.id_continue_cached; -pub const id_start = identifier_data.id_start; -pub const id_continue = identifier_data.id_continue; diff --git a/src/js_lexer/identifier_data.zig b/src/js_lexer/identifier_data.zig deleted file mode 100644 index d82751e620..0000000000 --- a/src/js_lexer/identifier_data.zig +++ /dev/null @@ -1,177 +0,0 @@ -const std = @import("std"); - -const ZWJ = .{ 0x200C, 0x200D }; - -// "unicodeESNextIdentifierStart" -pub const start_codepoints = [_]i32{ 65, 90, 97, 122, 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 895, 895, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1488, 1514, 1519, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2144, 2154, 2208, 2228, 2230, 2237, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2432, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2556, 2556, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2809, 2809, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3133, 3160, 3162, 3168, 3169, 3200, 3200, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3412, 3414, 3423, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6264, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6430, 6480, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7401, 7404, 7406, 7411, 7413, 7414, 7418, 7418, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42653, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43261, 43262, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43488, 43492, 43494, 43503, 43514, 43518, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43646, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66335, 66349, 66378, 66384, 66421, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68096, 68112, 68115, 68117, 68119, 68121, 68149, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68324, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68899, 69376, 69404, 69415, 69415, 69424, 69445, 69600, 69622, 69635, 69687, 69763, 69807, 69840, 69864, 69891, 69926, 69956, 69956, 69968, 70002, 70006, 70006, 70019, 70066, 70081, 70084, 70106, 70106, 70108, 70108, 70144, 70161, 70163, 70187, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70366, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70461, 70461, 70480, 70480, 70493, 70497, 70656, 70708, 70727, 70730, 70751, 70751, 70784, 70831, 70852, 70853, 70855, 70855, 71040, 71086, 71128, 71131, 71168, 71215, 71236, 71236, 71296, 71338, 71352, 71352, 71424, 71450, 71680, 71723, 71840, 71903, 71935, 71935, 72096, 72103, 72106, 72144, 72161, 72161, 72163, 72163, 72192, 72192, 72203, 72242, 72250, 72250, 72272, 72272, 72284, 72329, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72750, 72768, 72768, 72818, 72847, 72960, 72966, 72968, 72969, 72971, 73008, 73030, 73030, 73056, 73061, 73063, 73064, 73066, 73097, 73112, 73112, 73440, 73458, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92880, 92909, 92928, 92975, 92992, 92995, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94032, 94032, 94099, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 123136, 123180, 123191, 123197, 123214, 123214, 123584, 123627, 124928, 125124, 125184, 125251, 125259, 125259, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101 }; -// "unicodeESNextIdentifierPart" -pub const part_codepoints = ZWJ ++ [_]i32{ 48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2208, 2228, 2230, 2237, 2259, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3328, 3331, 3333, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7673, 7675, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40943, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42943, 42946, 42950, 42999, 43047, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43879, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 67072, 67382, 67392, 67413, 67424, 67431, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69376, 69404, 69415, 69415, 69424, 69456, 69600, 69622, 69632, 69702, 69734, 69743, 69759, 69818, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69958, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70096, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70206, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70751, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71680, 71738, 71840, 71913, 71935, 71935, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72384, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73728, 74649, 74752, 74862, 74880, 75075, 77824, 78894, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101106, 110592, 110878, 110928, 110930, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123584, 123641, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173782, 173824, 177972, 177984, 178205, 178208, 183969, 183984, 191456, 194560, 195101, 917760, 917999 }; - -const start_codepoints_including_ascii = [_]i32{ - 'a', - 'z', - 'A', - 'Z', - '_', - '_', - '$', - '$', -} ++ start_codepoints; -const part_codepoints_including_ascii = [_]i32{ - 'a', - 'z', - 'A', - 'Z', - '0', - '9', - '_', - '_', - '$', - '$', -} ++ part_codepoints; - -const id_start_range: [2]i32 = brk: { - var minmax = [2]i32{ std.math.maxInt(i32), 0 }; - - for (start_codepoints_including_ascii) |c| { - @setEvalBranchQuota(9999); - minmax[0] = if (c < minmax[0]) c else minmax[0]; - minmax[1] = if (c > minmax[1]) c else minmax[1]; - } - - break :brk minmax; -}; -const id_start_count = id_start_range[1] - id_start_range[0] + 1; - -const id_end_range: [2]i32 = brk: { - var minmax = [2]i32{ std.math.maxInt(i32), 0 }; - - for (part_codepoints_including_ascii) |c| { - @setEvalBranchQuota(9999); - minmax[0] = if (c < minmax[0]) c else minmax[0]; - minmax[1] = if (c > minmax[1]) c else minmax[1]; - } - - break :brk minmax; -}; - -const id_end_count = id_end_range[1] - id_end_range[0] + 1; - -pub const IDStartType = std.bit_set.StaticBitSet(id_start_count + 1); -pub const IDContinueType = std.bit_set.StaticBitSet(id_end_count + 1); - -pub const id_start: IDStartType = brk: { - var bits: IDStartType = IDStartType.initEmpty(); - var i: usize = 0; - - @setEvalBranchQuota(999999); - while (i < start_codepoints_including_ascii.len) : (i += 2) { - var min = start_codepoints_including_ascii[i]; - const max = start_codepoints_including_ascii[i + 1]; - while (min <= max) : (min += 1) { - @setEvalBranchQuota(999999); - bits.set(id_start_range[1] - min); - } - } - break :brk bits; -}; - -pub const id_continue: IDContinueType = brk: { - var bits: IDContinueType = IDContinueType.initEmpty(); - var i: usize = 0; - - while (i < part_codepoints_including_ascii.len) : (i += 2) { - var min = part_codepoints_including_ascii[i]; - const max = part_codepoints_including_ascii[i + 1]; - @setEvalBranchQuota(999999); - while (min <= max) : (min += 1) { - @setEvalBranchQuota(999999); - bits.set(id_end_range[1] - min); - } - } - break :brk bits; -}; - -const Cache = @import("./identifier_cache.zig"); - -pub const id_start_cached = Cache.CachedBitset{ .range = id_start_range, .len = id_start_count + 1 }; -pub const id_continue_cached = Cache.CachedBitset{ .range = id_end_range, .len = id_end_count + 1 }; - -fn main() anyerror!void { - const id_continue_data = std.mem.asBytes(&id_continue.masks); - const id_start_data = std.mem.asBytes(&id_start.masks); - - try std.posix.chdir(std.fs.path.dirname(@src().file).?); - var start = try std.fs.cwd().createFileZ("id_start_bitset.meta.blob", .{ .truncate = true }); - try start.writeAll(std.mem.asBytes(&id_start_cached)); - start.close(); - - var start_masks = try std.fs.cwd().createFileZ("id_start_bitset.blob", .{ .truncate = true }); - try start_masks.writeAll(id_start_data); - start_masks.close(); - - var continue_meta = try std.fs.cwd().createFileZ("id_continue_bitset.meta.blob", .{ .truncate = true }); - var continue_blob = try std.fs.cwd().createFileZ("id_continue_bitset.blob", .{ .truncate = true }); - - try continue_meta.writeAll(std.mem.asBytes(&id_continue_cached)); - continue_meta.close(); - try continue_blob.writeAll(std.mem.asBytes(id_continue_data)); - continue_blob.close(); -} - -test "Check" { - const id_start_cached_correct = Cache.CachedBitset{ .range = id_start_range, .len = id_start_count + 1 }; - const id_continue_cached_correct = Cache.CachedBitset{ .range = id_end_range, .len = id_end_count + 1 }; - try std.posix.chdir(std.fs.path.dirname(@src().file).?); - var start_cached = try std.fs.cwd().openFileZ("id_start_bitset.meta.blob", .{ .mode = .read_only }); - const start_cached_data = try start_cached.readToEndAlloc(std.heap.c_allocator, 4096); - - try std.testing.expectEqualSlices(u8, start_cached_data, std.mem.asBytes(&id_start_cached_correct)); - - var continue_cached = try std.fs.cwd().openFileZ("id_continue_bitset.meta.blob", .{ .mode = .read_only }); - const continue_cached_data = try continue_cached.readToEndAlloc(std.heap.c_allocator, 4096); - - try std.testing.expectEqualSlices(u8, continue_cached_data, std.mem.asBytes(&id_continue_cached_correct)); - - var start_blob_file = try std.fs.cwd().openFileZ("id_start_bitset.blob", .{ .mode = .read_only }); - const start_blob_data = try start_blob_file.readToEndAlloc(std.heap.c_allocator, try start_blob_file.getEndPos()); - var continue_blob_file = try std.fs.cwd().openFileZ("id_continue_bitset.blob", .{ .mode = .read_only }); - const continue_blob_data = try continue_blob_file.readToEndAlloc(std.heap.c_allocator, try continue_blob_file.getEndPos()); - - try std.testing.expectEqualSlices(u8, start_blob_data, std.mem.asBytes(&id_start.masks)); - try std.testing.expectEqualSlices(u8, continue_blob_data, std.mem.asBytes(&id_continue.masks)); -} - -test "Check #2" { - const id_start_cached_correct = Cache.CachedBitset{ .range = id_start_range, .len = id_start_count + 1 }; - const id_continue_cached_correct = Cache.CachedBitset{ .range = id_end_range, .len = id_end_count + 1 }; - try std.posix.chdir(std.fs.path.dirname(@src().file).?); - const start_cached_data = std.mem.asBytes(&Cache.id_start_meta); - - try std.testing.expectEqualSlices(u8, start_cached_data, std.mem.asBytes(&id_start_cached_correct)); - - const continue_cached_data = std.mem.asBytes(&Cache.id_continue_meta); - - try std.testing.expectEqualSlices(u8, continue_cached_data, std.mem.asBytes(&id_continue_cached_correct)); - - const start_blob_data = Cache.id_start_masks; - const continue_blob_data = Cache.id_continue_masks; - - try std.testing.expectEqualSlices(u8, start_blob_data, std.mem.asBytes(&id_start.masks)); - try std.testing.expectEqualSlices(u8, continue_blob_data, std.mem.asBytes(&id_continue.masks)); -} - -test "Check #3" { - try std.testing.expectEqualSlices(usize, &Cache.id_start.masks, &id_start.masks); - try std.testing.expectEqualSlices(usize, &Cache.id_continue.masks, &id_continue.masks); - - var i: i32 = id_end_range[0]; - while (i < id_end_range[1]) : (i += 1) { - try std.testing.expectEqual(id_continue.isSet(@as(usize, @intCast(id_end_range[1] - i))), Cache.id_continue.isSet(@as(usize, @intCast(id_end_range[1] - i)))); - } - - i = id_start_range[0]; - while (i < id_start_range[1]) : (i += 1) { - try std.testing.expectEqual(id_start.isSet(@as(usize, @intCast(id_start_range[1] - i))), Cache.id_start.isSet(@as(usize, @intCast(id_start_range[1] - i)))); - } -} diff --git a/src/js_parser.zig b/src/js_parser.zig index e0fe7510c6..9d3803f751 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -3,6 +3,7 @@ /// ** you must also increment the `expected_version` in RuntimeTranspilerCache.zig ** /// ** IMPORTANT ** pub const std = @import("std"); +const bun = @import("root").bun; pub const logger = bun.logger; pub const js_lexer = bun.js_lexer; pub const importRecord = @import("./import_record.zig"); @@ -15,7 +16,6 @@ pub const RuntimeImports = _runtime.Runtime.Imports; pub const RuntimeFeatures = _runtime.Runtime.Features; pub const RuntimeNames = _runtime.Runtime.Names; pub const fs = @import("./fs.zig"); -const bun = @import("root").bun; const string = bun.string; const Output = bun.Output; const Global = bun.Global; @@ -187,18 +187,15 @@ const JSXFactoryName = "JSX"; const JSXAutomaticName = "jsx_module"; // kept as a static reference const exports_string_name: string = "exports"; -const MacroRefs = std.AutoArrayHashMap(Ref, u32); -pub const AllocatedNamesPool = ObjectPool( - std.ArrayList(string), - struct { - pub fn init(allocator: std.mem.Allocator) anyerror!std.ArrayList(string) { - return std.ArrayList(string).init(allocator); - } - }.init, - true, - 4, -); +const MacroRefData = struct { + import_record_id: u32, + // if name is null the macro is imported as a namespace import + // import * as macros from "./macros.js" with {type: "macro"}; + name: ?string = null, +}; + +const MacroRefs = std.AutoArrayHashMap(Ref, MacroRefData); const Substitution = union(enum) { success: Expr, @@ -1604,40 +1601,51 @@ pub const SideEffects = enum(u1) { pub fn simplifyBoolean(p: anytype, expr: Expr) Expr { if (!p.options.features.dead_code_elimination) return expr; - switch (expr.data) { - .e_unary => |e| { - if (e.op == .un_not) { - // "!!a" => "a" - if (e.value.data == .e_unary and e.value.data.e_unary.op == .un_not) { - return simplifyBoolean(p, e.value.data.e_unary.value); + + var result: Expr = expr; + _simplifyBoolean(p, &result); + return result; + } + + fn _simplifyBoolean(p: anytype, expr: *Expr) void { + while (true) { + switch (expr.data) { + .e_unary => |e| { + if (e.op == .un_not) { + // "!!a" => "a" + if (e.value.data == .e_unary and e.value.data.e_unary.op == .un_not) { + expr.* = e.value.data.e_unary.value; + continue; + } + + _simplifyBoolean(p, &e.value); } - - e.value = simplifyBoolean(p, e.value); - } - }, - .e_binary => |e| { - switch (e.op) { - .bin_logical_and => { - const effects = SideEffects.toBoolean(p, e.right.data); - if (effects.ok and effects.value and effects.side_effects == .no_side_effects) { - // "if (anything && truthyNoSideEffects)" => "if (anything)" - return e.left; - } - }, - .bin_logical_or => { - const effects = SideEffects.toBoolean(p, e.right.data); - if (effects.ok and !effects.value and effects.side_effects == .no_side_effects) { - // "if (anything || falsyNoSideEffects)" => "if (anything)" - return e.left; - } - }, - else => {}, - } - }, - else => {}, + }, + .e_binary => |e| { + switch (e.op) { + .bin_logical_and => { + const effects = SideEffects.toBoolean(p, e.right.data); + if (effects.ok and effects.value and effects.side_effects == .no_side_effects) { + // "if (anything && truthyNoSideEffects)" => "if (anything)" + expr.* = e.left; + continue; + } + }, + .bin_logical_or => { + const effects = SideEffects.toBoolean(p, e.right.data); + if (effects.ok and !effects.value and effects.side_effects == .no_side_effects) { + // "if (anything || falsyNoSideEffects)" => "if (anything)" + expr.* = e.left; + continue; + } + }, + else => {}, + } + }, + else => {}, + } + break; } - - return expr; } pub const toNumber = Expr.Data.toNumber; @@ -2266,10 +2274,18 @@ pub const SideEffects = enum(u1) { } pub fn toBoolean(p: anytype, exp: Expr.Data) Result { + // Only do this check once. if (!p.options.features.dead_code_elimination) { // value should not be read if ok is false, all existing calls to this function already adhere to this return Result{ .ok = false, .value = undefined, .side_effects = .could_have_side_effects }; } + + return toBooleanWithoutDCECheck(exp); + } + + // Avoid passing through *P + // This is a very recursive function. + fn toBooleanWithoutDCECheck(exp: Expr.Data) Result { switch (exp) { .e_null, .e_undefined => { return Result{ .ok = true, .value = false, .side_effects = .no_side_effects }; @@ -2303,10 +2319,9 @@ pub const SideEffects = enum(u1) { return Result{ .ok = true, .value = true, .side_effects = .could_have_side_effects }; }, .un_not => { - var result = toBoolean(p, e_.value.data); + const result = toBooleanWithoutDCECheck(e_.value.data); if (result.ok) { - result.value = !result.value; - return result; + return .{ .ok = true, .value = !result.value, .side_effects = result.side_effects }; } }, else => {}, @@ -2316,21 +2331,21 @@ pub const SideEffects = enum(u1) { switch (e_.op) { .bin_logical_or => { // "anything || truthy" is truthy - const result = toBoolean(p, e_.right.data); + const result = toBooleanWithoutDCECheck(e_.right.data); if (result.value and result.ok) { return Result{ .ok = true, .value = true, .side_effects = .could_have_side_effects }; } }, .bin_logical_and => { // "anything && falsy" is falsy - const result = toBoolean(p, e_.right.data); + const result = toBooleanWithoutDCECheck(e_.right.data); if (!result.value and result.ok) { return Result{ .ok = true, .value = false, .side_effects = .could_have_side_effects }; } }, .bin_comma => { // "anything, truthy/falsy" is truthy/falsy - var result = toBoolean(p, e_.right.data); + var result = toBooleanWithoutDCECheck(e_.right.data); if (result.ok) { result.side_effects = .could_have_side_effects; return result; @@ -2368,7 +2383,7 @@ pub const SideEffects = enum(u1) { } }, .e_inlined_enum => |inlined| { - return toBoolean(p, inlined.value.data); + return toBooleanWithoutDCECheck(inlined.value.data); }, else => {}, } @@ -2603,7 +2618,7 @@ const StmtList = ListManaged(Stmt); // This hash table is used every time we parse function args // Rather than allocating a new hash table each time, we can just reuse the previous allocation -const StringVoidMap = struct { +pub const StringVoidMap = struct { allocator: Allocator, map: bun.StringHashMapUnmanaged(void) = bun.StringHashMapUnmanaged(void){}, @@ -2972,7 +2987,14 @@ pub const Parser = struct { // Which makes sense. // June 4: "Parsing took: 18028000" // June 4: "Rest of this took: 8003000" - _ = try p.parseStmtsUpTo(js_lexer.T.t_end_of_file, &opts); + _ = p.parseStmtsUpTo(js_lexer.T.t_end_of_file, &opts) catch |err| { + if (err == error.StackOverflow) { + // The lexer location won't be totally accurate, but it's kind of helpful. + try p.log.addError(p.source, p.lexer.loc(), "Maximum call stack size exceeded"); + return; + } + return err; + }; // if (comptime ParserType.parser_features.typescript) { @@ -3053,7 +3075,11 @@ pub const Parser = struct { var stmts = try p.allocator.alloc(js_ast.Stmt, 1); stmts[0] = Stmt{ .data = .{ - .s_lazy_export = expr.data, + .s_lazy_export = brk: { + const data = try p.allocator.create(Expr.Data); + data.* = expr.data; + break :brk data; + }, }, .loc = expr.loc, }; @@ -3062,8 +3088,8 @@ pub const Parser = struct { .symbol_uses = p.symbol_uses, }; p.symbol_uses = .{}; - var parts = try p.allocator.alloc(js_ast.Part, 2); - parts[0..2].* = .{ ns_export_part, part }; + var parts = try ListManaged(js_ast.Part).initCapacity(p.allocator, 2); + parts.appendSliceAssumeCapacity(&.{ ns_export_part, part }); const exports_kind: js_ast.ExportsKind = brk: { if (expr.data == .e_undefined) { @@ -3072,7 +3098,7 @@ pub const Parser = struct { } break :brk .none; }; - return .{ .ast = try p.toAST(parts, exports_kind, .none, "") }; + return .{ .ast = try p.toAST(&parts, exports_kind, .none, "") }; } pub fn parse(self: *Parser) !js_ast.Result { @@ -3218,16 +3244,12 @@ pub const Parser = struct { } // Detect a leading "// @bun" pragma - if (p.lexer.bun_pragma != .none and p.options.features.dont_bundle_twice) { - return js_ast.Result{ - .already_bundled = switch (p.lexer.bun_pragma) { - .bun => .bun, - .bytecode => .bytecode, - .bytecode_cjs => .bytecode_cjs, - .bun_cjs => .bun_cjs, - else => unreachable, - }, - }; + if (self.options.features.dont_bundle_twice) { + if (self.hasBunPragma(hashbang.len > 0)) |pragma| { + return js_ast.Result{ + .already_bundled = pragma, + }; + } } // We must check the cache only after we've consumed the hashbang and leading // @bun pragma @@ -3251,7 +3273,18 @@ pub const Parser = struct { // Which makes sense. // June 4: "Parsing took: 18028000" // June 4: "Rest of this took: 8003000" - const stmts = try p.parseStmtsUpTo(js_lexer.T.t_end_of_file, &opts); + const stmts = p.parseStmtsUpTo(js_lexer.T.t_end_of_file, &opts) catch |err| { + parse_tracer.end(); + if (err == error.StackOverflow) { + // The lexer location won't be totally accurate, but it's kind of helpful. + try p.log.addError(p.source, p.lexer.loc(), "Maximum call stack size exceeded"); + + // Return a SyntaxError so that we reuse existing code for handling erorrs. + return error.SyntaxError; + } + + return err; + }; parse_tracer.end(); @@ -3952,9 +3985,10 @@ pub const Parser = struct { if (uses_any_import_statements) { exports_kind = .esm; - - // Otherwise, if they use CommonJS features its CommonJS - } else if (p.symbols.items[p.require_ref.innerIndex()].use_count_estimate > 0 or uses_dirname or uses_filename) { + } + // Otherwise, if they use CommonJS features its CommonJS. + // If you add a 'use strict'; at the top, you probably meant CommonJS because "use strict"; does nothing in ESM. + else if (p.symbols.items[p.require_ref.innerIndex()].use_count_estimate > 0 or uses_dirname or uses_filename or (!p.options.bundle and p.module_scope.strict_mode == .explicit_strict_mode)) { exports_kind = .cjs; } else { // If unknown, we default to ESM @@ -4207,41 +4241,21 @@ pub const Parser = struct { ); } - var parts_slice: []js_ast.Part = &([_]js_ast.Part{}); - if (before.items.len > 0 or after.items.len > 0) { - const before_len = before.items.len; - const after_len = after.items.len; + try parts.ensureUnusedCapacity(before.items.len + after.items.len); const parts_len = parts.items.len; + parts.items.len += before.items.len + after.items.len; - const _parts = try p.allocator.alloc( - js_ast.Part, - before_len + - after_len + - parts_len, - ); - - var remaining_parts = _parts; - if (before_len > 0) { - const parts_to_copy = before.items; - bun.copy(js_ast.Part, remaining_parts, parts_to_copy); - remaining_parts = remaining_parts[parts_to_copy.len..]; + if (before.items.len > 0) { + if (parts_len > 0) { + // first copy parts to the middle if before exists + bun.copy(js_ast.Part, parts.items[before.items.len..][0..parts_len], parts.items[0..parts_len]); + } + bun.copy(js_ast.Part, parts.items[0..before.items.len], before.items); } - - if (parts_len > 0) { - const parts_to_copy = parts.items; - bun.copy(js_ast.Part, remaining_parts, parts_to_copy); - remaining_parts = remaining_parts[parts_to_copy.len..]; + if (after.items.len > 0) { + bun.copy(js_ast.Part, parts.items[parts_len + before.items.len ..][0..after.items.len], after.items); } - - if (after_len > 0) { - const parts_to_copy = after.items; - bun.copy(js_ast.Part, remaining_parts, parts_to_copy); - } - - parts_slice = _parts; - } else { - parts_slice = parts.items; } // Pop the module scope to apply the "ContainsDirectEval" rules @@ -4260,7 +4274,7 @@ pub const Parser = struct { } } - return js_ast.Result{ .ast = try p.toAST(parts_slice, exports_kind, wrap_mode, hashbang) }; + return js_ast.Result{ .ast = try p.toAST(&parts, exports_kind, wrap_mode, hashbang) }; } pub fn init(_options: Options, log: *logger.Log, source: *const logger.Source, define: *Define, allocator: Allocator) !Parser { @@ -4273,6 +4287,64 @@ pub const Parser = struct { .log = log, }; } + + const PragmaState = packed struct { seen_cjs: bool = false, seen_bytecode: bool = false }; + + fn hasBunPragma(self: *const Parser, has_hashbang: bool) ?js_ast.Result.AlreadyBundled { + const BUN_PRAGMA = "// @bun"; + const contents = self.lexer.source.contents; + const end = contents.len; + + // pragmas may appear after a hashbang comment + // + // ```js + // #!/usr/bin/env bun + // // @bun + // const myCode = 1; + // ``` + var cursor: usize = 0; + if (has_hashbang) { + while (contents[cursor] != '\n') { + cursor += 1; + if (cursor >= end) return null; + } + + // eat the last newline + // NOTE: in windows, \n comes after \r so no extra work needs to be done + cursor += 1; + } + + if (!bun.strings.startsWith(contents[cursor..], BUN_PRAGMA)) return null; + cursor += BUN_PRAGMA.len; + + var state: PragmaState = .{}; + + while (cursor < self.lexer.end) : (cursor += 1) { + switch (contents[cursor]) { + '\n' => break, + '@' => { + cursor += 1; + if (cursor >= contents.len) break; + if (contents[cursor] != 'b') continue; + const slice = contents[cursor..]; + if (bun.strings.startsWith(slice, "bun-cjs")) { + state.seen_cjs = true; + cursor += "bun-cjs".len; + } else if (bun.strings.startsWith(slice, "bytecode")) { + state.seen_bytecode = true; + cursor += "bytecode".len; + } + }, + else => {}, + } + } + + if (state.seen_cjs) { + return if (state.seen_bytecode) .bytecode_cjs else .bun_cjs; + } else { + return if (state.seen_bytecode) .bytecode else .bun; + } + } }; const FindLabelSymbolResult = struct { ref: Ref, is_loop: bool, found: bool = false }; @@ -4769,6 +4841,8 @@ fn NewParser_( /// Used by commonjs_at_runtime has_commonjs_export_names: bool = false, + stack_check: bun.StackCheck, + /// When this flag is enabled, we attempt to fold all expressions that /// TypeScript would consider to be "constant expressions". This flag is /// enabled inside each enum body block since TypeScript requires numeric @@ -8937,20 +9011,33 @@ fn NewParser_( const name = p.loadNameFromRef(name_loc.ref.?); const ref = try p.declareSymbol(.other, name_loc.loc, name); try p.is_import_item.put(p.allocator, ref, {}); - try p.macro.refs.put(ref, id); + try p.macro.refs.put(ref, .{ + .import_record_id = id, + .name = "default", + }); + } + + if (stmt.star_name_loc) |star| { + const name = p.loadNameFromRef(stmt.namespace_ref); + const ref = try p.declareSymbol(.other, star, name); + stmt.namespace_ref = ref; + try p.macro.refs.put(ref, .{ .import_record_id = id }); } for (stmt.items) |item| { const name = p.loadNameFromRef(item.name.ref.?); const ref = try p.declareSymbol(.other, item.name.loc, name); try p.is_import_item.put(p.allocator, ref, {}); - try p.macro.refs.put(ref, id); + try p.macro.refs.put(ref, .{ + .import_record_id = id, + .name = item.alias, + }); } return p.s(S.Empty{}, loc); } - const macro_remap = if ((comptime allow_macros) and !is_macro) + const macro_remap = if (comptime allow_macros) p.options.macro_context.getRemap(path.text) else null; @@ -8969,6 +9056,8 @@ fn NewParser_( .import_record_index = stmt.import_record_index, }) catch unreachable; } + + // TODO: not sure how to handle macro remappings for namespace imports } else { var path_name = fs.PathName.init(path.text); const name = try strings.append(p.allocator, "import_", try path_name.nonUniqueNameString(p.allocator)); @@ -9009,7 +9098,10 @@ fn NewParser_( if (macro_remap) |*remap| { if (remap.get("default")) |remapped_path| { const new_import_id = p.addImportRecord(.stmt, path.loc, remapped_path); - try p.macro.refs.put(ref, new_import_id); + try p.macro.refs.put(ref, .{ + .import_record_id = new_import_id, + .name = "default", + }); p.import_records.items[new_import_id].path.namespace = js_ast.Macro.namespace; p.import_records.items[new_import_id].is_unused = true; @@ -9030,12 +9122,6 @@ fn NewParser_( }) catch unreachable; } - if (is_macro) { - try p.macro.refs.put(ref, stmt.import_record_index); - stmt.default_name = null; - break :outer; - } - if (comptime ParsePassSymbolUsageType != void) { p.parse_pass_symbol_uses.put(name, .{ .ref = ref, @@ -9071,7 +9157,10 @@ fn NewParser_( if (macro_remap) |*remap| { if (remap.get(item.alias)) |remapped_path| { const new_import_id = p.addImportRecord(.stmt, path.loc, remapped_path); - try p.macro.refs.put(ref, new_import_id); + try p.macro.refs.put(ref, .{ + .import_record_id = new_import_id, + .name = item.alias, + }); p.import_records.items[new_import_id].path.namespace = js_ast.Macro.namespace; p.import_records.items[new_import_id].is_unused = true; @@ -9437,6 +9526,10 @@ fn NewParser_( } fn parseStmt(p: *P, opts: *ParseStatementOptions) anyerror!Stmt { + if (!p.stack_check.isSafeToRecurse()) { + try bun.throwStackOverflow(); + } + const loc = p.lexer.loc(); switch (p.lexer.token) { @@ -9946,28 +10039,60 @@ fn NewParser_( return p.s(S.Local{ .kind = .k_const, .decls = Decl.List.fromList(decls), .is_export = opts.is_export }, loc); }, .t_if => { - try p.lexer.next(); - try p.lexer.expect(.t_open_paren); - const test_ = try p.parseExpr(.lowest); - try p.lexer.expect(.t_close_paren); - var stmtOpts = ParseStatementOptions{ - .lexical_decl = .allow_fn_inside_if, - }; - const yes = try p.parseStmt(&stmtOpts); - var no: ?Stmt = null; - if (p.lexer.token == .t_else) { + var current_loc = loc; + var root_if: ?Stmt = null; + var current_if: ?*S.If = null; + + while (true) { try p.lexer.next(); - stmtOpts = ParseStatementOptions{ + try p.lexer.expect(.t_open_paren); + const test_ = try p.parseExpr(.lowest); + try p.lexer.expect(.t_close_paren); + var stmtOpts = ParseStatementOptions{ .lexical_decl = .allow_fn_inside_if, }; - no = try p.parseStmt(&stmtOpts); + const yes = try p.parseStmt(&stmtOpts); + + // Create the if node + const if_stmt = p.s(S.If{ + .test_ = test_, + .yes = yes, + .no = null, + }, current_loc); + + // First if statement becomes root + if (root_if == null) { + root_if = if_stmt; + } + + // Link to previous if statement's else branch + if (current_if) |prev_if| { + prev_if.no = if_stmt; + } + + // Set current if for next iteration + current_if = if_stmt.data.s_if; + + if (p.lexer.token != .t_else) { + return root_if.?; + } + + try p.lexer.next(); + + // Handle final else + if (p.lexer.token != .t_if) { + stmtOpts = ParseStatementOptions{ + .lexical_decl = .allow_fn_inside_if, + }; + current_if.?.no = try p.parseStmt(&stmtOpts); + return root_if.?; + } + + // Continue with else if + current_loc = p.lexer.loc(); } - return p.s(S.If{ - .test_ = test_, - .yes = yes, - .no = no, - }, loc); + unreachable; }, .t_do => { try p.lexer.next(); @@ -10087,7 +10212,6 @@ fn NewParser_( var binding: ?js_ast.Binding = null; // The catch binding is optional, and can be omitted - // jarred: TIL! if (p.lexer.token != .t_open_brace) { try p.lexer.expect(.t_open_paren); var value = try p.parseBinding(.{}); @@ -11793,6 +11917,8 @@ fn NewParser_( needs_symbol = true; } else { try p.lexer.expect(.t_identifier); + // error early, name is still `undefined` + return error.SyntaxError; } try p.lexer.next(); @@ -12977,7 +13103,11 @@ fn NewParser_( return try p.parseExprCommon(level, null, flags); } - pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr { + fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr { + if (!p.stack_check.isSafeToRecurse()) { + try bun.throwStackOverflow(); + } + const had_pure_comment_before = p.lexer.has_pure_comment_before and !p.options.ignore_dce_annotations; var expr = try p.parsePrefix(level, errors, flags); @@ -14630,48 +14760,6 @@ fn NewParser_( } } - pub const MacroVisitor = struct { - p: *P, - - loc: logger.Loc, - - pub fn visitImport(this: MacroVisitor, import_data: js_ast.Macro.JSNode.ImportData) void { - var p = this.p; - - const record_id = p.addImportRecord(.stmt, this.loc, import_data.path); - var record: *ImportRecord = &p.import_records.items[record_id]; - record.was_injected_by_macro = true; - p.macro.imports.ensureUnusedCapacity(import_data.import.items.len) catch unreachable; - var import = import_data.import; - import.import_record_index = record_id; - - p.is_import_item.ensureUnusedCapacity( - p.allocator, - @as(u32, @intCast(p.is_import_item.count() + import.items.len)), - ) catch unreachable; - - for (import.items) |*clause| { - const import_hash_name = clause.original_name; - - if (strings.eqlComptime(clause.alias, "default")) { - const non_unique_name = record.path.name.nonUniqueNameString(p.allocator) catch unreachable; - clause.original_name = std.fmt.allocPrint(p.allocator, "{s}_default", .{non_unique_name}) catch unreachable; - record.contains_default_alias = true; - } - const name_ref = p.declareSymbol(.import, this.loc, clause.original_name) catch unreachable; - clause.name = LocRef{ .loc = this.loc, .ref = name_ref }; - - p.is_import_item.putAssumeCapacity(name_ref, {}); - - p.macro.imports.putAssumeCapacity(js_ast.Macro.JSNode.SymbolMap.generateImportHash(import_hash_name, import_data.path), name_ref); - - // Ensure we don't accidentally think this is an export from - } - - p.macro.prepend_stmts.append(p.s(import, this.loc)) catch unreachable; - } - }; - pub fn panic(p: *P, comptime fmt: string, args: anytype) noreturn { p.panicLoc(fmt, args, null); @setCold(true); @@ -15861,15 +15949,19 @@ fn NewParser_( fn bindingCanBeRemovedIfUnused(p: *P, binding: Binding) bool { if (!p.options.features.dead_code_elimination) return false; + return bindingCanBeRemovedIfUnusedWithoutDCECheck(p, binding); + } + + fn bindingCanBeRemovedIfUnusedWithoutDCECheck(p: *P, binding: Binding) bool { switch (binding.data) { .b_array => |bi| { for (bi.items) |*item| { - if (!p.bindingCanBeRemovedIfUnused(item.binding)) { + if (!p.bindingCanBeRemovedIfUnusedWithoutDCECheck(item.binding)) { return false; } if (item.default_value) |*default| { - if (!p.exprCanBeRemovedIfUnused(default)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(default)) { return false; } } @@ -15877,16 +15969,16 @@ fn NewParser_( }, .b_object => |bi| { for (bi.properties) |*property| { - if (!property.flags.contains(.is_spread) and !p.exprCanBeRemovedIfUnused(&property.key)) { + if (!property.flags.contains(.is_spread) and !p.exprCanBeRemovedIfUnusedWithoutDCECheck(&property.key)) { return false; } - if (!p.bindingCanBeRemovedIfUnused(property.value)) { + if (!p.bindingCanBeRemovedIfUnusedWithoutDCECheck(property.value)) { return false; } if (property.default_value) |*default| { - if (!p.exprCanBeRemovedIfUnused(default)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(default)) { return false; } } @@ -15900,6 +15992,10 @@ fn NewParser_( fn stmtsCanBeRemovedIfUnused(p: *P, stmts: []Stmt) bool { if (!p.options.features.dead_code_elimination) return false; + return stmtsCanBeRemovedifUnusedWithoutDCECheck(p, stmts); + } + + fn stmtsCanBeRemovedifUnusedWithoutDCECheck(p: *P, stmts: []Stmt) bool { for (stmts) |stmt| { switch (stmt.data) { // These never have side effects @@ -15924,7 +16020,7 @@ fn NewParser_( continue; } - if (!p.exprCanBeRemovedIfUnused(&st.value)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(&st.value)) { return false; } }, @@ -15934,12 +16030,12 @@ fn NewParser_( if (st.kind == .k_await_using) return false; for (st.decls.slice()) |*decl| { - if (!p.bindingCanBeRemovedIfUnused(decl.binding)) { + if (!p.bindingCanBeRemovedIfUnusedWithoutDCECheck(decl.binding)) { return false; } if (decl.value) |*decl_value| { - if (!p.exprCanBeRemovedIfUnused(decl_value)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(decl_value)) { return false; } else if (st.kind == .k_using) { // "using" declarations are only side-effect free if they are initialized to null or undefined @@ -15952,7 +16048,7 @@ fn NewParser_( }, .s_try => |try_| { - if (!p.stmtsCanBeRemovedIfUnused(try_.body) or (try_.finally != null and !p.stmtsCanBeRemovedIfUnused(try_.finally.?.stmts))) { + if (!p.stmtsCanBeRemovedifUnusedWithoutDCECheck(try_.body) or (try_.finally != null and !p.stmtsCanBeRemovedifUnusedWithoutDCECheck(try_.finally.?.stmts))) { return false; } }, @@ -15965,7 +16061,7 @@ fn NewParser_( .stmt => |s2| { switch (s2.data) { .s_expr => |s_expr| { - if (!p.exprCanBeRemovedIfUnused(&s_expr.value)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(&s_expr.value)) { return false; } }, @@ -15984,7 +16080,7 @@ fn NewParser_( } }, .expr => |*exp| { - if (!p.exprCanBeRemovedIfUnused(exp)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(exp)) { return false; } }, @@ -16034,7 +16130,7 @@ fn NewParser_( } // public for JSNode.JSXWriter usage - pub fn visitExpr(p: *P, expr: Expr) Expr { + pub inline fn visitExpr(p: *P, expr: Expr) Expr { if (only_scan_imports_and_do_not_visit) { @compileError("only_scan_imports_and_do_not_visit must not run this."); } @@ -16499,12 +16595,15 @@ fn NewParser_( e_.tag = p.visitExpr(tag); if (comptime allow_macros) { - if (e_.tag.?.data == .e_import_identifier and !p.options.features.is_macro_runtime) { - const ref = e_.tag.?.data.e_import_identifier.ref; + const ref = switch (e_.tag.?.data) { + .e_import_identifier => |ident| ident.ref, + .e_dot => |dot| if (dot.target.data == .e_identifier) dot.target.data.e_identifier.ref else null, + else => null, + }; - if (p.macro.refs.get(ref)) |import_record_id| { - const name = p.symbols.items[ref.innerIndex()].original_name; - p.ignoreUsage(ref); + if (ref != null and !p.options.features.is_macro_runtime) { + if (p.macro.refs.get(ref.?)) |macro_ref_data| { + p.ignoreUsage(ref.?); if (p.is_control_flow_dead) { return p.newExpr(E.Undefined{}, e_.tag.?.loc); } @@ -16521,7 +16620,8 @@ fn NewParser_( } p.macro_call_count += 1; - const record = &p.import_records.items[import_record_id]; + const name = macro_ref_data.name orelse e_.tag.?.data.e_dot.name; + const record = &p.import_records.items[macro_ref_data.import_record_id]; // We must visit it to convert inline_identifiers and record usage const macro_result = (p.options.macro_context.call( record.path.text, @@ -17296,10 +17396,18 @@ fn NewParser_( else => {}, } - const is_macro_ref: bool = if (comptime FeatureFlags.is_macro_enabled) - e_.target.data == .e_import_identifier and p.macro.refs.contains(e_.target.data.e_import_identifier.ref) - else - false; + const is_macro_ref: bool = if (comptime allow_macros) brk: { + const possible_macro_ref = switch (e_.target.data) { + .e_import_identifier => |ident| ident.ref, + .e_dot => |dot| if (dot.target.data == .e_identifier) + dot.target.data.e_identifier.ref + else + null, + else => null, + }; + + break :brk possible_macro_ref != null and p.macro.refs.contains(possible_macro_ref.?); + } else false; { const old_ce = p.options.ignore_dce_annotations; @@ -17425,8 +17533,13 @@ fn NewParser_( if (comptime allow_macros) { if (is_macro_ref and !p.options.features.is_macro_runtime) { - const ref = e_.target.data.e_import_identifier.ref; - const import_record_id = p.macro.refs.get(ref).?; + const ref = switch (e_.target.data) { + .e_import_identifier => |ident| ident.ref, + .e_dot => |dot| dot.target.data.e_identifier.ref, + else => unreachable, + }; + + const macro_ref_data = p.macro.refs.get(ref).?; p.ignoreUsage(ref); if (p.is_control_flow_dead) { return p.newExpr(E.Undefined{}, e_.target.loc); @@ -17442,8 +17555,8 @@ fn NewParser_( return p.newExpr(E.Undefined{}, expr.loc); } - const name = p.symbols.items[ref.innerIndex()].original_name; - const record = &p.import_records.items[import_record_id]; + const name = macro_ref_data.name orelse e_.target.data.e_dot.name; + const record = &p.import_records.items[macro_ref_data.import_record_id]; const copied = Expr{ .loc = expr.loc, .data = .{ .e_call = e_ } }; const start_error_count = p.log.msgs.items.len; p.macro_call_count += 1; @@ -17778,34 +17891,34 @@ fn NewParser_( return true; } + // This one is never called in places that haven't already checked if DCE is enabled. pub fn classCanBeRemovedIfUnused(p: *P, class: *G.Class) bool { - if (!p.options.features.dead_code_elimination) return false; if (class.extends) |*extends| { - if (!p.exprCanBeRemovedIfUnused(extends)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(extends)) { return false; } } for (class.properties) |*property| { if (property.kind == .class_static_block) { - if (!p.stmtsCanBeRemovedIfUnused(property.class_static_block.?.stmts.slice())) { + if (!p.stmtsCanBeRemovedifUnusedWithoutDCECheck(property.class_static_block.?.stmts.slice())) { return false; } continue; } - if (!p.exprCanBeRemovedIfUnused(&(property.key orelse unreachable))) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(&(property.key orelse unreachable))) { return false; } if (property.value) |*val| { - if (!p.exprCanBeRemovedIfUnused(val)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(val)) { return false; } } if (property.initializer) |*val| { - if (!p.exprCanBeRemovedIfUnused(val)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(val)) { return false; } } @@ -17819,6 +17932,11 @@ fn NewParser_( // This is to improve the reliability of fast refresh between page loads. pub fn exprCanBeRemovedIfUnused(p: *P, expr: *const Expr) bool { if (!p.options.features.dead_code_elimination) return false; + + return exprCanBeRemovedIfUnusedWithoutDCECheck(p, expr); + } + + fn exprCanBeRemovedIfUnusedWithoutDCECheck(p: *P, expr: *const Expr) bool { switch (expr.data) { .e_null, .e_undefined, @@ -17836,7 +17954,7 @@ fn NewParser_( return true; }, - .e_inlined_enum => |e| return p.exprCanBeRemovedIfUnused(&e.value), + .e_inlined_enum => |e| return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&e.value), .e_dot => |ex| { return ex.can_be_removed_if_unused; @@ -17895,24 +18013,24 @@ fn NewParser_( return true; }, .e_if => |ex| { - return p.exprCanBeRemovedIfUnused(&ex.test_) and + return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.test_) and (p.isSideEffectFreeUnboundIdentifierRef( ex.yes, ex.test_, true, ) or - p.exprCanBeRemovedIfUnused(&ex.yes)) and + p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.yes)) and (p.isSideEffectFreeUnboundIdentifierRef( ex.no, ex.test_, false, - ) or p.exprCanBeRemovedIfUnused( + ) or p.exprCanBeRemovedIfUnusedWithoutDCECheck( &ex.no, )); }, .e_array => |ex| { for (ex.items.slice()) |*item| { - if (!p.exprCanBeRemovedIfUnused(item)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(item)) { return false; } } @@ -17928,7 +18046,7 @@ fn NewParser_( } if (property.value) |*val| { - if (!p.exprCanBeRemovedIfUnused(val)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(val)) { return false; } } @@ -17940,7 +18058,7 @@ fn NewParser_( // can be removed. The annotation causes us to ignore the target. if (ex.can_be_unwrapped_if_unused) { for (ex.args.slice()) |*arg| { - if (!p.exprCanBeRemovedIfUnused(arg)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(arg)) { return false; } } @@ -17953,7 +18071,7 @@ fn NewParser_( // can be removed. The annotation causes us to ignore the target. if (ex.can_be_unwrapped_if_unused) { for (ex.args.slice()) |*arg| { - if (!p.exprCanBeRemovedIfUnused(arg)) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(arg)) { return false; } } @@ -17966,7 +18084,7 @@ fn NewParser_( // These operators must not have any type conversions that can execute code // such as "toString" or "valueOf". They must also never throw any exceptions. .un_void, .un_not => { - return p.exprCanBeRemovedIfUnused(&ex.value); + return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.value); }, // The "typeof" operator doesn't do any type conversions so it can be removed @@ -17984,7 +18102,7 @@ fn NewParser_( return true; } - return p.exprCanBeRemovedIfUnused(&ex.value); + return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.value); }, else => {}, @@ -17998,15 +18116,15 @@ fn NewParser_( .bin_strict_ne, .bin_comma, .bin_nullish_coalescing, - => return p.exprCanBeRemovedIfUnused(&ex.left) and p.exprCanBeRemovedIfUnused(&ex.right), + => return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.left) and p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.right), // Special-case "||" to make sure "typeof x === 'undefined' || x" can be removed - .bin_logical_or => return p.exprCanBeRemovedIfUnused(&ex.left) and - (p.isSideEffectFreeUnboundIdentifierRef(ex.right, ex.left, false) or p.exprCanBeRemovedIfUnused(&ex.right)), + .bin_logical_or => return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.left) and + (p.isSideEffectFreeUnboundIdentifierRef(ex.right, ex.left, false) or p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.right)), // Special-case "&&" to make sure "typeof x !== 'undefined' && x" can be removed - .bin_logical_and => return p.exprCanBeRemovedIfUnused(&ex.left) and - (p.isSideEffectFreeUnboundIdentifierRef(ex.right, ex.left, true) or p.exprCanBeRemovedIfUnused(&ex.right)), + .bin_logical_and => return p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.left) and + (p.isSideEffectFreeUnboundIdentifierRef(ex.right, ex.left, true) or p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.right)), // For "==" and "!=", pretend the operator was actually "===" or "!==". If // we know that we can convert it to "==" or "!=", then we can consider the @@ -18018,14 +18136,14 @@ fn NewParser_( ex.left.data, ex.right.data, ) and - p.exprCanBeRemovedIfUnused(&ex.left) and p.exprCanBeRemovedIfUnused(&ex.right), + p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.left) and p.exprCanBeRemovedIfUnusedWithoutDCECheck(&ex.right), else => {}, } }, .e_template => |templ| { if (templ.tag == null) { for (templ.parts) |part| { - if (!p.exprCanBeRemovedIfUnused(&part.value) or part.value.knownPrimitive() == .unknown) { + if (!p.exprCanBeRemovedIfUnusedWithoutDCECheck(&part.value) or part.value.knownPrimitive() == .unknown) { return false; } } @@ -18039,7 +18157,7 @@ fn NewParser_( return false; } - // // This is based on exprCanBeRemovedIfUnused. + // // This is based on exprCanBeRemoved // // The main difference: identifiers, functions, arrow functions cause it to return false // pub fn exprCanBeHoistedForJSX(p: *P, expr: *const Expr) bool { // if (comptime jsx_transform_type != .react) { @@ -18951,809 +19069,985 @@ fn NewParser_( const was_after_after_const_local_prefix = p.current_scope.is_after_const_local_prefix; p.current_scope.is_after_const_local_prefix = true; - switch (stmt.data) { - // These don't contain anything to traverse + switch (@as(Stmt.Tag, stmt.data)) { + .s_directive, .s_comment, .s_empty => { + p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; + try stmts.append(stmt.*); + }, + .s_type_script => { + p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; + return; + }, .s_debugger => { p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; if (p.define.drop_debugger) { return; } + try stmts.append(stmt.*); }, - .s_empty, .s_comment => { - p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; - }, - .s_type_script => { - p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; - // Erase TypeScript constructs from the output completely + + inline .s_enum, .s_local => |tag| return @field(visitors, @tagName(tag))(p, stmts, stmt, @field(stmt.data, @tagName(tag)), was_after_after_const_local_prefix), + inline else => |tag| return @field(visitors, @tagName(tag))(p, stmts, stmt, @field(stmt.data, @tagName(tag))), + + // Only used by the bundler for lazy export ASTs. + .s_lazy_export => unreachable, + } + } + + const visitors = struct { + pub fn s_import(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Import) !void { + try p.recordDeclaredSymbol(data.namespace_ref); + + if (data.default_name) |default_name| { + try p.recordDeclaredSymbol(default_name.ref.?); + } + + if (data.items.len > 0) { + for (data.items) |*item| { + try p.recordDeclaredSymbol(item.name.ref.?); + } + } + + try stmts.append(stmt.*); + } + pub fn s_export_clause(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ExportClause) !void { + // "export {foo}" + var end: usize = 0; + var any_replaced = false; + if (p.options.features.replace_exports.count() > 0) { + for (data.items) |*item| { + const name = p.loadNameFromRef(item.name.ref.?); + + const symbol = try p.findSymbol(item.alias_loc, name); + const ref = symbol.ref; + + if (p.options.features.replace_exports.getPtr(name)) |entry| { + if (entry.* != .replace) p.ignoreUsage(symbol.ref); + _ = p.injectReplacementExport(stmts, symbol.ref, stmt.loc, entry); + any_replaced = true; + continue; + } + + if (p.symbols.items[ref.innerIndex()].kind == .unbound) { + // Silently strip exports of non-local symbols in TypeScript, since + // those likely correspond to type-only exports. But report exports of + // non-local symbols as errors in JavaScript. + if (!is_typescript_enabled) { + const r = js_lexer.rangeOfIdentifier(p.source, item.name.loc); + try p.log.addRangeErrorFmt(p.source, r, p.allocator, "\"{s}\" is not declared in this file", .{name}); + } + continue; + } + + item.name.ref = ref; + data.items[end] = item.*; + end += 1; + } + } else { + for (data.items) |*item| { + const name = p.loadNameFromRef(item.name.ref.?); + const symbol = try p.findSymbol(item.alias_loc, name); + const ref = symbol.ref; + + if (p.symbols.items[ref.innerIndex()].kind == .unbound) { + // Silently strip exports of non-local symbols in TypeScript, since + // those likely correspond to type-only exports. But report exports of + // non-local symbols as errors in JavaScript. + if (!is_typescript_enabled) { + const r = js_lexer.rangeOfIdentifier(p.source, item.name.loc); + try p.log.addRangeErrorFmt(p.source, r, p.allocator, "\"{s}\" is not declared in this file", .{name}); + continue; + } + continue; + } + + item.name.ref = ref; + data.items[end] = item.*; + end += 1; + } + } + + const remove_for_tree_shaking = any_replaced and end == 0 and data.items.len > 0 and p.options.tree_shaking; + data.items.len = end; + + if (remove_for_tree_shaking) { return; - }, - .s_directive => { - p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; - }, - .s_import => |data| { - try p.recordDeclaredSymbol(data.namespace_ref); + } - if (data.default_name) |default_name| { - try p.recordDeclaredSymbol(default_name.ref.?); - } + try stmts.append(stmt.*); + } + pub fn s_export_from(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ExportFrom) !void { - if (data.items.len > 0) { - for (data.items) |*item| { - try p.recordDeclaredSymbol(item.name.ref.?); - } - } - }, - .s_export_clause => |data| { - // "export {foo}" - var end: usize = 0; - var any_replaced = false; - if (p.options.features.replace_exports.count() > 0) { - for (data.items) |*item| { - const name = p.loadNameFromRef(item.name.ref.?); + // "export {foo} from 'path'" + const name = p.loadNameFromRef(data.namespace_ref); - const symbol = try p.findSymbol(item.alias_loc, name); - const ref = symbol.ref; + data.namespace_ref = try p.newSymbol(.other, name); + try p.current_scope.generated.push(p.allocator, data.namespace_ref); + try p.recordDeclaredSymbol(data.namespace_ref); + + if (p.options.features.replace_exports.count() > 0) { + var j: usize = 0; + // This is a re-export and the symbols created here are used to reference + for (data.items) |item| { + const old_ref = item.name.ref.?; + + if (p.options.features.replace_exports.count() > 0) { + if (p.options.features.replace_exports.getPtr(item.alias)) |entry| { + _ = p.injectReplacementExport(stmts, old_ref, logger.Loc.Empty, entry); - if (p.options.features.replace_exports.getPtr(name)) |entry| { - if (entry.* != .replace) p.ignoreUsage(symbol.ref); - _ = p.injectReplacementExport(stmts, symbol.ref, stmt.loc, entry); - any_replaced = true; continue; } - - if (p.symbols.items[ref.innerIndex()].kind == .unbound) { - // Silently strip exports of non-local symbols in TypeScript, since - // those likely correspond to type-only exports. But report exports of - // non-local symbols as errors in JavaScript. - if (!is_typescript_enabled) { - const r = js_lexer.rangeOfIdentifier(p.source, item.name.loc); - try p.log.addRangeErrorFmt(p.source, r, p.allocator, "\"{s}\" is not declared in this file", .{name}); - } - continue; - } - - item.name.ref = ref; - data.items[end] = item.*; - end += 1; } - } else { - for (data.items) |*item| { - const name = p.loadNameFromRef(item.name.ref.?); - const symbol = try p.findSymbol(item.alias_loc, name); - const ref = symbol.ref; - if (p.symbols.items[ref.innerIndex()].kind == .unbound) { - // Silently strip exports of non-local symbols in TypeScript, since - // those likely correspond to type-only exports. But report exports of - // non-local symbols as errors in JavaScript. - if (!is_typescript_enabled) { - const r = js_lexer.rangeOfIdentifier(p.source, item.name.loc); - try p.log.addRangeErrorFmt(p.source, r, p.allocator, "\"{s}\" is not declared in this file", .{name}); - continue; - } - continue; - } + const _name = p.loadNameFromRef(old_ref); - item.name.ref = ref; - data.items[end] = item.*; - end += 1; - } + const ref = try p.newSymbol(.import, _name); + try p.current_scope.generated.push(p.allocator, ref); + try p.recordDeclaredSymbol(ref); + data.items[j] = item; + data.items[j].name.ref = ref; + j += 1; } - const remove_for_tree_shaking = any_replaced and end == 0 and data.items.len > 0 and p.options.tree_shaking; - data.items.len = end; + data.items.len = j; - if (remove_for_tree_shaking) { + if (j == 0 and data.items.len > 0) { return; } - }, - .s_export_from => |data| { - // "export {foo} from 'path'" - const name = p.loadNameFromRef(data.namespace_ref); + } else { + // This is a re-export and the symbols created here are used to reference + for (data.items) |*item| { + const _name = p.loadNameFromRef(item.name.ref.?); + const ref = try p.newSymbol(.import, _name); + try p.current_scope.generated.push(p.allocator, ref); + try p.recordDeclaredSymbol(ref); + item.name.ref = ref; + } + } - data.namespace_ref = try p.newSymbol(.other, name); - try p.current_scope.generated.push(p.allocator, data.namespace_ref); - try p.recordDeclaredSymbol(data.namespace_ref); + try stmts.append(stmt.*); + } + pub fn s_export_star(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ExportStar) !void { + // "export * from 'path'" + const name = p.loadNameFromRef(data.namespace_ref); + data.namespace_ref = try p.newSymbol(.other, name); + try p.current_scope.generated.push(p.allocator, data.namespace_ref); + try p.recordDeclaredSymbol(data.namespace_ref); + + // "export * as ns from 'path'" + if (data.alias) |alias| { if (p.options.features.replace_exports.count() > 0) { - var j: usize = 0; - // This is a re-export and the symbols created here are used to reference - for (data.items) |item| { - const old_ref = item.name.ref.?; - - if (p.options.features.replace_exports.count() > 0) { - if (p.options.features.replace_exports.getPtr(item.alias)) |entry| { - _ = p.injectReplacementExport(stmts, old_ref, logger.Loc.Empty, entry); - - continue; - } - } - - const _name = p.loadNameFromRef(old_ref); - - const ref = try p.newSymbol(.import, _name); - try p.current_scope.generated.push(p.allocator, ref); - try p.recordDeclaredSymbol(ref); - data.items[j] = item; - data.items[j].name.ref = ref; - j += 1; - } - - data.items.len = j; - - if (j == 0 and data.items.len > 0) { + if (p.options.features.replace_exports.getPtr(alias.original_name)) |entry| { + _ = p.injectReplacementExport(stmts, p.declareSymbol(.other, logger.Loc.Empty, alias.original_name) catch unreachable, logger.Loc.Empty, entry); return; } - } else { - // This is a re-export and the symbols created here are used to reference - for (data.items) |*item| { - const _name = p.loadNameFromRef(item.name.ref.?); - const ref = try p.newSymbol(.import, _name); - try p.current_scope.generated.push(p.allocator, ref); - try p.recordDeclaredSymbol(ref); - item.name.ref = ref; + } + } + + try stmts.append(stmt.*); + } + pub fn s_export_default(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ExportDefault) !void { + defer { + if (data.default_name.ref) |ref| { + p.recordDeclaredSymbol(ref) catch unreachable; + } + } + + var mark_for_replace: bool = false; + + const orig_dead = p.is_control_flow_dead; + if (p.options.features.replace_exports.count() > 0) { + if (p.options.features.replace_exports.getPtr("default")) |entry| { + p.is_control_flow_dead = p.options.features.dead_code_elimination and (entry.* != .replace); + mark_for_replace = true; + } + } + + defer { + p.is_control_flow_dead = orig_dead; + } + + switch (data.value) { + .expr => |expr| { + const was_anonymous_named_expr = expr.isAnonymousNamed(); + + data.value.expr = p.visitExpr(expr); + + if (p.is_control_flow_dead) { + return; } - } - }, - .s_export_star => |data| { - // "export * from 'path'" - const name = p.loadNameFromRef(data.namespace_ref); - data.namespace_ref = try p.newSymbol(.other, name); - try p.current_scope.generated.push(p.allocator, data.namespace_ref); - try p.recordDeclaredSymbol(data.namespace_ref); - // "export * as ns from 'path'" - if (data.alias) |alias| { - if (p.options.features.replace_exports.count() > 0) { - if (p.options.features.replace_exports.getPtr(alias.original_name)) |entry| { - _ = p.injectReplacementExport(stmts, p.declareSymbol(.other, logger.Loc.Empty, alias.original_name) catch unreachable, logger.Loc.Empty, entry); - return; - } - } - } - }, - .s_export_default => |data| { - defer { - if (data.default_name.ref) |ref| { - p.recordDeclaredSymbol(ref) catch unreachable; - } - } + // Optionally preserve the name - var mark_for_replace: bool = false; + data.value.expr = p.maybeKeepExprSymbolName(data.value.expr, js_ast.ClauseItem.default_alias, was_anonymous_named_expr); - const orig_dead = p.is_control_flow_dead; - if (p.options.features.replace_exports.count() > 0) { - if (p.options.features.replace_exports.getPtr("default")) |entry| { - p.is_control_flow_dead = p.options.features.dead_code_elimination and (entry.* != .replace); - mark_for_replace = true; - } - } - - defer { - p.is_control_flow_dead = orig_dead; - } - - switch (data.value) { - .expr => |expr| { - const was_anonymous_named_expr = expr.isAnonymousNamed(); - - data.value.expr = p.visitExpr(expr); - - if (p.is_control_flow_dead) { - return; - } - - // Optionally preserve the name - - data.value.expr = p.maybeKeepExprSymbolName(data.value.expr, js_ast.ClauseItem.default_alias, was_anonymous_named_expr); - - // Discard type-only export default statements - if (is_typescript_enabled) { - switch (data.value.expr.data) { - .e_identifier => |ident| { - if (!ident.ref.isSourceContentsSlice()) { - const symbol = p.symbols.items[ident.ref.innerIndex()]; - if (symbol.kind == .unbound) { - if (p.local_type_names.get(symbol.original_name)) |local_type| { - if (local_type) { - // the name points to a type - // don't try to declare this symbol - data.default_name.ref = null; - return; - } + // Discard type-only export default statements + if (is_typescript_enabled) { + switch (data.value.expr.data) { + .e_identifier => |ident| { + if (!ident.ref.isSourceContentsSlice()) { + const symbol = p.symbols.items[ident.ref.innerIndex()]; + if (symbol.kind == .unbound) { + if (p.local_type_names.get(symbol.original_name)) |local_type| { + if (local_type) { + // the name points to a type + // don't try to declare this symbol + data.default_name.ref = null; + return; } } } - }, - else => {}, - } - } - - if (data.default_name.ref.?.isSourceContentsSlice()) { - data.default_name = createDefaultName(p, data.value.expr.loc) catch unreachable; - } - - if (p.options.features.server_components.wrapsExports()) { - data.value.expr = p.wrapValueForServerComponentReference(data.value.expr, "default"); - } - - // If there are lowered "using" declarations, change this into a "var" - if (p.current_scope.parent == null and p.will_wrap_module_in_try_catch_for_using) { - try stmts.ensureUnusedCapacity(2); - - const decls = p.allocator.alloc(G.Decl, 1) catch bun.outOfMemory(); - decls[0] = .{ - .binding = p.b(B.Identifier{ .ref = data.default_name.ref.? }, data.default_name.loc), - .value = data.value.expr, - }; - stmts.appendAssumeCapacity(p.s(S.Local{ - .decls = G.Decl.List.init(decls), - }, stmt.loc)); - const items = p.allocator.alloc(js_ast.ClauseItem, 1) catch bun.outOfMemory(); - items[0] = js_ast.ClauseItem{ - .alias = "default", - .alias_loc = data.default_name.loc, - .name = data.default_name, - }; - stmts.appendAssumeCapacity(p.s(S.ExportClause{ - .items = items, - }, stmt.loc)); - } - - if (mark_for_replace) { - const entry = p.options.features.replace_exports.getPtr("default").?; - if (entry.* == .replace) { - data.value.expr = entry.replace; - } else { - _ = p.injectReplacementExport(stmts, Ref.None, logger.Loc.Empty, entry); - return; - } - } - }, - - .stmt => |s2| { - switch (s2.data) { - .s_function => |func| { - var name: string = ""; - if (func.func.name) |func_loc| { - name = p.loadNameFromRef(func_loc.ref.?); - } else { - func.func.name = data.default_name; - name = js_ast.ClauseItem.default_alias; } - - var react_hook_data: ?ReactRefresh.HookContext = null; - const prev = p.react_refresh.hook_ctx_storage; - defer p.react_refresh.hook_ctx_storage = prev; - p.react_refresh.hook_ctx_storage = &react_hook_data; - - func.func = p.visitFunc(func.func, func.func.open_parens_loc); - - if (react_hook_data) |*hook| { - stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)) catch bun.outOfMemory(); - - data.value = .{ - .expr = p.getReactRefreshHookSignalInit(hook, p.newExpr( - E.Function{ .func = func.func }, - stmt.loc, - )), - }; - } - - if (p.is_control_flow_dead) { - return; - } - - if (mark_for_replace) { - const entry = p.options.features.replace_exports.getPtr("default").?; - if (entry.* == .replace) { - data.value = .{ .expr = entry.replace }; - } else { - _ = p.injectReplacementExport(stmts, Ref.None, logger.Loc.Empty, entry); - return; - } - } - - if (data.default_name.ref.?.isSourceContentsSlice()) { - data.default_name = createDefaultName(p, stmt.loc) catch unreachable; - } - - if (p.options.features.server_components.wrapsExports()) { - data.value = .{ .expr = p.wrapValueForServerComponentReference(p.newExpr(E.Function{ .func = func.func }, stmt.loc), "default") }; - } - - stmts.append(stmt.*) catch unreachable; - - // if (func.func.name != null and func.func.name.?.ref != null) { - // stmts.append(p.keepStmtSymbolName(func.func.name.?.loc, func.func.name.?.ref.?, name)) catch unreachable; - // } - // prevent doubling export default function name - return; - }, - .s_class => |class| { - _ = p.visitClass(s2.loc, &class.class, data.default_name.ref.?); - - if (p.is_control_flow_dead) - return; - - if (mark_for_replace) { - const entry = p.options.features.replace_exports.getPtr("default").?; - if (entry.* == .replace) { - data.value = .{ .expr = entry.replace }; - } else { - _ = p.injectReplacementExport(stmts, Ref.None, logger.Loc.Empty, entry); - return; - } - } - - if (data.default_name.ref.?.isSourceContentsSlice()) { - data.default_name = createDefaultName(p, stmt.loc) catch unreachable; - } - - // We only inject a name into classes when there is a decorator - if (class.class.has_decorators) { - if (class.class.class_name == null or - class.class.class_name.?.ref == null) - { - class.class.class_name = data.default_name; - } - } - - // This is to handle TS decorators, mostly. - var class_stmts = p.lowerClass(.{ .stmt = s2 }); - bun.assert(class_stmts[0].data == .s_class); - - if (class_stmts.len > 1) { - data.value.stmt = class_stmts[0]; - stmts.append(stmt.*) catch {}; - stmts.appendSlice(class_stmts[1..]) catch {}; - } else { - data.value.stmt = class_stmts[0]; - stmts.append(stmt.*) catch {}; - } - - if (p.options.features.server_components.wrapsExports()) { - data.value = .{ .expr = p.wrapValueForServerComponentReference(p.newExpr(class.class, stmt.loc), "default") }; - } - - return; }, else => {}, } - }, - } - }, - .s_export_equals => |data| { - // "module.exports = value" - stmts.append( - Stmt.assign( - p.@"module.exports"(stmt.loc), - p.visitExpr(data.value), - ), - ) catch unreachable; - p.recordUsage(p.module_ref); - return; - }, - .s_break => |data| { - if (data.label) |*label| { - const name = p.loadNameFromRef(label.ref orelse p.panicLoc("Expected label to have a ref", .{}, label.loc)); - const res = p.findLabelSymbol(label.loc, name); - if (res.found) { - label.ref = res.ref; - } else { - data.label = null; } - } else if (!p.fn_or_arrow_data_visit.is_inside_loop and !p.fn_or_arrow_data_visit.is_inside_switch) { - const r = js_lexer.rangeOfIdentifier(p.source, stmt.loc); - p.log.addRangeError(p.source, r, "Cannot use \"break\" here") catch unreachable; - } - }, - .s_continue => |data| { - if (data.label) |*label| { - const name = p.loadNameFromRef(label.ref orelse p.panicLoc("Expected continue label to have a ref", .{}, label.loc)); - const res = p.findLabelSymbol(label.loc, name); - label.ref = res.ref; - if (res.found and !res.is_loop) { - const r = js_lexer.rangeOfIdentifier(p.source, stmt.loc); - p.log.addRangeErrorFmt(p.source, r, p.allocator, "Cannot \"continue\" to label {s}", .{name}) catch unreachable; + + if (data.default_name.ref.?.isSourceContentsSlice()) { + data.default_name = createDefaultName(p, data.value.expr.loc) catch unreachable; } - } else if (!p.fn_or_arrow_data_visit.is_inside_loop) { - const r = js_lexer.rangeOfIdentifier(p.source, stmt.loc); - p.log.addRangeError(p.source, r, "Cannot use \"continue\" here") catch unreachable; - } - }, - .s_label => |data| { - p.pushScopeForVisitPass(.label, stmt.loc) catch unreachable; - const name = p.loadNameFromRef(data.name.ref.?); - const ref = p.newSymbol(.label, name) catch unreachable; - data.name.ref = ref; - p.current_scope.label_ref = ref; - switch (data.stmt.data) { - .s_for, .s_for_in, .s_for_of, .s_while, .s_do_while => { - p.current_scope.label_stmt_is_loop = true; - }, - else => {}, - } - data.stmt = p.visitSingleStmt(data.stmt, StmtsKind.none); - p.popScope(); - }, - .s_local => |data| { - // TODO: Silently remove unsupported top-level "await" in dead code branches - // (this was from 'await using' syntax) + if (p.options.features.server_components.wrapsExports()) { + data.value.expr = p.wrapValueForServerComponentReference(data.value.expr, "default"); + } - // Local statements do not end the const local prefix - p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; + // If there are lowered "using" declarations, change this into a "var" + if (p.current_scope.parent == null and p.will_wrap_module_in_try_catch_for_using) { + try stmts.ensureUnusedCapacity(2); - const decls_len = if (!(data.is_export and p.options.features.replace_exports.entries.len > 0)) - p.visitDecls(data.decls.slice(), data.kind == .k_const, false) - else - p.visitDecls(data.decls.slice(), data.kind == .k_const, true); + const decls = p.allocator.alloc(G.Decl, 1) catch bun.outOfMemory(); + decls[0] = .{ + .binding = p.b(B.Identifier{ .ref = data.default_name.ref.? }, data.default_name.loc), + .value = data.value.expr, + }; + stmts.appendAssumeCapacity(p.s(S.Local{ + .decls = G.Decl.List.init(decls), + }, stmt.loc)); + const items = p.allocator.alloc(js_ast.ClauseItem, 1) catch bun.outOfMemory(); + items[0] = js_ast.ClauseItem{ + .alias = "default", + .alias_loc = data.default_name.loc, + .name = data.default_name, + }; + stmts.appendAssumeCapacity(p.s(S.ExportClause{ + .items = items, + }, stmt.loc)); + } - const is_now_dead = data.decls.len > 0 and decls_len == 0; - if (is_now_dead) { - return; - } - - data.decls.len = @as(u32, @truncate(decls_len)); - - // Handle being exported inside a namespace - if (data.is_export and p.enclosing_namespace_arg_ref != null) { - for (data.decls.slice()) |*d| { - if (d.value) |val| { - p.recordUsage((p.enclosing_namespace_arg_ref orelse unreachable)); - // TODO: is it necessary to lowerAssign? why does esbuild do it _most_ of the time? - stmts.append(p.s(S.SExpr{ - .value = Expr.assign(Binding.toExpr(&d.binding, p.to_expr_wrapper_namespace), val), - }, stmt.loc)) catch unreachable; + if (mark_for_replace) { + const entry = p.options.features.replace_exports.getPtr("default").?; + if (entry.* == .replace) { + data.value.expr = entry.replace; + } else { + _ = p.injectReplacementExport(stmts, Ref.None, logger.Loc.Empty, entry); + return; } } + }, - return; - } - - // Optimization: Avoid unnecessary "using" machinery by changing ones - // initialized to "null" or "undefined" into a normal variable. Note that - // "await using" still needs the "await", so we can't do it for those. - if (p.options.features.minify_syntax and data.kind == .k_using) { - data.kind = .k_let; - for (data.decls.slice()) |*d| { - if (d.value) |val| { - if (val.data != .e_null and val.data != .e_undefined) { - data.kind = .k_using; - break; + .stmt => |s2| { + switch (s2.data) { + .s_function => |func| { + var name: string = ""; + if (func.func.name) |func_loc| { + name = p.loadNameFromRef(func_loc.ref.?); + } else { + func.func.name = data.default_name; + name = js_ast.ClauseItem.default_alias; } - } - } - } - // We must relocate vars in order to safely handle removing if/else depending on NODE_ENV. - // Edgecase: - // `export var` is skipped because it's unnecessary. That *should* be a noop, but it loses the `is_export` flag if we're in HMR. - const kind = p.selectLocalKind(data.kind); - if (kind == .k_var and !data.is_export) { - const relocated = p.maybeRelocateVarsToTopLevel(data.decls.slice(), .normal); - if (relocated.ok) { - if (relocated.stmt) |new_stmt| { - stmts.append(new_stmt) catch unreachable; - } + var react_hook_data: ?ReactRefresh.HookContext = null; + const prev = p.react_refresh.hook_ctx_storage; + defer p.react_refresh.hook_ctx_storage = prev; + p.react_refresh.hook_ctx_storage = &react_hook_data; - return; - } - } + func.func = p.visitFunc(func.func, func.func.open_parens_loc); - data.kind = kind; - try stmts.append(stmt.*); + if (react_hook_data) |*hook| { + stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)) catch bun.outOfMemory(); - if (data.is_export and p.options.features.server_components.wrapsExports()) { - for (data.decls.slice()) |*decl| try_annotate: { - const val = decl.value orelse break :try_annotate; - switch (val.data) { - .e_arrow, .e_function => {}, - else => break :try_annotate, - } - const id = switch (decl.binding.data) { - .b_identifier => |id| id.ref, - else => break :try_annotate, - }; - const original_name = p.symbols.items[id.innerIndex()].original_name; - decl.value = p.wrapValueForServerComponentReference(val, original_name); - } - } - - if (p.options.features.react_fast_refresh and p.current_scope == p.module_scope) { - for (data.decls.slice()) |decl| try_register: { - const val = decl.value orelse break :try_register; - switch (val.data) { - .e_arrow, .e_function => {}, - else => break :try_register, - } - const id = switch (decl.binding.data) { - .b_identifier => |id| id.ref, - else => break :try_register, - }; - const original_name = p.symbols.items[id.innerIndex()].original_name; - try p.handleReactRefreshRegister(stmts, original_name, id); - } - } - - return; - }, - .s_expr => |data| { - const should_trim_primitive = p.options.features.dead_code_elimination and - (p.options.features.minify_syntax and data.value.isPrimitiveLiteral()); - p.stmt_expr_value = data.value.data; - defer p.stmt_expr_value = .{ .e_missing = .{} }; - - const is_top_level = p.current_scope == p.module_scope; - if (p.shouldUnwrapCommonJSToESM()) { - p.commonjs_named_exports_needs_conversion = if (is_top_level) - std.math.maxInt(u32) - else - p.commonjs_named_exports_needs_conversion; - } - - data.value = p.visitExpr(data.value); - - if (should_trim_primitive and data.value.isPrimitiveLiteral()) { - return; - } - - // simplify unused - data.value = SideEffects.simplifyUnusedExpr(p, data.value) orelse return; - - if (p.shouldUnwrapCommonJSToESM()) { - if (is_top_level) { - if (data.value.data == .e_binary) { - const to_convert = p.commonjs_named_exports_needs_conversion; - if (to_convert != std.math.maxInt(u32)) { - p.commonjs_named_exports_needs_conversion = std.math.maxInt(u32); - convert: { - const bin: *E.Binary = data.value.data.e_binary; - if (bin.op == .bin_assign and bin.left.data == .e_commonjs_export_identifier) { - var last = &p.commonjs_named_exports.values()[to_convert]; - if (!last.needs_decl) break :convert; - last.needs_decl = false; - - var decls = p.allocator.alloc(Decl, 1) catch unreachable; - const ref = bin.left.data.e_commonjs_export_identifier.ref; - decls[0] = .{ - .binding = p.b(B.Identifier{ .ref = ref }, bin.left.loc), - .value = bin.right, - }; - // we have to ensure these are known to be top-level - p.declared_symbols.append(p.allocator, .{ - .ref = ref, - .is_top_level = true, - }) catch unreachable; - p.esm_export_keyword.loc = stmt.loc; - p.esm_export_keyword.len = 5; - p.had_commonjs_named_exports_this_visit = true; - var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable; - clause_items[0] = js_ast.ClauseItem{ - // We want the generated name to not conflict - .alias = p.commonjs_named_exports.keys()[to_convert], - .alias_loc = bin.left.loc, - .name = .{ - .ref = ref, - .loc = last.loc_ref.loc, - }, - }; - stmts.appendSlice( - &[_]Stmt{ - p.s( - S.Local{ - .kind = .k_var, - .is_export = false, - .was_commonjs_export = true, - .decls = G.Decl.List.init(decls), - }, - stmt.loc, - ), - p.s( - S.ExportClause{ - .items = clause_items, - .is_single_line = true, - }, - stmt.loc, - ), - }, - ) catch unreachable; - - return; - } - } - } else if (p.commonjs_replacement_stmts.len > 0) { - if (stmts.items.len == 0) { - stmts.items = p.commonjs_replacement_stmts; - stmts.capacity = p.commonjs_replacement_stmts.len; - p.commonjs_replacement_stmts.len = 0; - } else { - stmts.appendSlice(p.commonjs_replacement_stmts) catch unreachable; - p.commonjs_replacement_stmts.len = 0; - } + data.value = .{ + .expr = p.getReactRefreshHookSignalInit(hook, p.newExpr( + E.Function{ .func = func.func }, + stmt.loc, + )), + }; + } + if (p.is_control_flow_dead) { return; } + + if (mark_for_replace) { + const entry = p.options.features.replace_exports.getPtr("default").?; + if (entry.* == .replace) { + data.value = .{ .expr = entry.replace }; + } else { + _ = p.injectReplacementExport(stmts, Ref.None, logger.Loc.Empty, entry); + return; + } + } + + if (data.default_name.ref.?.isSourceContentsSlice()) { + data.default_name = createDefaultName(p, stmt.loc) catch unreachable; + } + + if (p.options.features.server_components.wrapsExports()) { + data.value = .{ .expr = p.wrapValueForServerComponentReference(p.newExpr(E.Function{ .func = func.func }, stmt.loc), "default") }; + } + + stmts.append(stmt.*) catch unreachable; + + // if (func.func.name != null and func.func.name.?.ref != null) { + // stmts.append(p.keepStmtSymbolName(func.func.name.?.loc, func.func.name.?.ref.?, name)) catch unreachable; + // } + // prevent doubling export default function name + return; + }, + .s_class => |class| { + _ = p.visitClass(s2.loc, &class.class, data.default_name.ref.?); + + if (p.is_control_flow_dead) + return; + + if (mark_for_replace) { + const entry = p.options.features.replace_exports.getPtr("default").?; + if (entry.* == .replace) { + data.value = .{ .expr = entry.replace }; + } else { + _ = p.injectReplacementExport(stmts, Ref.None, logger.Loc.Empty, entry); + return; + } + } + + if (data.default_name.ref.?.isSourceContentsSlice()) { + data.default_name = createDefaultName(p, stmt.loc) catch unreachable; + } + + // We only inject a name into classes when there is a decorator + if (class.class.has_decorators) { + if (class.class.class_name == null or + class.class.class_name.?.ref == null) + { + class.class.class_name = data.default_name; + } + } + + // This is to handle TS decorators, mostly. + var class_stmts = p.lowerClass(.{ .stmt = s2 }); + bun.assert(class_stmts[0].data == .s_class); + + if (class_stmts.len > 1) { + data.value.stmt = class_stmts[0]; + stmts.append(stmt.*) catch {}; + stmts.appendSlice(class_stmts[1..]) catch {}; + } else { + data.value.stmt = class_stmts[0]; + stmts.append(stmt.*) catch {}; + } + + if (p.options.features.server_components.wrapsExports()) { + data.value = .{ .expr = p.wrapValueForServerComponentReference(p.newExpr(class.class, stmt.loc), "default") }; + } + + return; + }, + else => {}, + } + }, + } + + try stmts.append(stmt.*); + } + pub fn s_function(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Function) !void { + // We mark it as dead, but the value may not actually be dead + // We just want to be sure to not increment the usage counts for anything in the function + const mark_as_dead = p.options.features.dead_code_elimination and data.func.flags.contains(.is_export) and + p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.func.name.?.ref.?); + const original_is_dead = p.is_control_flow_dead; + + if (mark_as_dead) { + p.is_control_flow_dead = true; + } + defer { + if (mark_as_dead) { + p.is_control_flow_dead = original_is_dead; + } + } + + var react_hook_data: ?ReactRefresh.HookContext = null; + const prev = p.react_refresh.hook_ctx_storage; + defer p.react_refresh.hook_ctx_storage = prev; + p.react_refresh.hook_ctx_storage = &react_hook_data; + + data.func = p.visitFunc(data.func, data.func.open_parens_loc); + + const name_ref = data.func.name.?.ref.?; + bun.assert(name_ref.tag == .symbol); + const name_symbol = &p.symbols.items[name_ref.innerIndex()]; + const original_name = name_symbol.original_name; + + // Handle exporting this function from a namespace + if (data.func.flags.contains(.is_export) and p.enclosing_namespace_arg_ref != null) { + data.func.flags.remove(.is_export); + + const enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref orelse bun.outOfMemory(); + stmts.ensureUnusedCapacity(3) catch bun.outOfMemory(); + stmts.appendAssumeCapacity(stmt.*); + stmts.appendAssumeCapacity(Stmt.assign( + p.newExpr(E.Dot{ + .target = p.newExpr(E.Identifier{ .ref = enclosing_namespace_arg_ref }, stmt.loc), + .name = original_name, + .name_loc = data.func.name.?.loc, + }, stmt.loc), + p.newExpr(E.Identifier{ .ref = data.func.name.?.ref.? }, data.func.name.?.loc), + )); + } else if (!mark_as_dead) { + if (name_symbol.remove_overwritten_function_declaration) { + return; + } + + if (p.options.features.server_components.wrapsExports() and data.func.flags.contains(.is_export)) { + // Convert this into `export var = registerClientReference(, ...);` + const name = data.func.name.?; + // From the inner scope, have code reference the wrapped function. + data.func.name = null; + try stmts.append(p.s(S.Local{ + .kind = .k_var, + .is_export = true, + .decls = try G.Decl.List.fromSlice(p.allocator, &.{.{ + .binding = p.b(B.Identifier{ .ref = name_ref }, name.loc), + .value = p.wrapValueForServerComponentReference( + p.newExpr(E.Function{ .func = data.func }, stmt.loc), + original_name, + ), + }}), + }, stmt.loc)); + } else { + stmts.append(stmt.*) catch bun.outOfMemory(); + } + } else if (mark_as_dead) { + if (p.options.features.replace_exports.getPtr(original_name)) |replacement| { + _ = p.injectReplacementExport(stmts, name_ref, data.func.name.?.loc, replacement); + } + } + + if (p.options.features.react_fast_refresh) { + if (react_hook_data) |*hook| { + try stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)); + try stmts.append(p.s(S.SExpr{ + .value = p.getReactRefreshHookSignalInit(hook, Expr.initIdentifier(name_ref, logger.Loc.Empty)), + }, logger.Loc.Empty)); + } + + if (p.current_scope == p.module_scope) { + try p.handleReactRefreshRegister(stmts, original_name, name_ref); + } + } + + return; + } + + pub fn s_class(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Class) !void { + const mark_as_dead = p.options.features.dead_code_elimination and data.is_export and + p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.class.class_name.?.ref.?); + const original_is_dead = p.is_control_flow_dead; + + if (mark_as_dead) { + p.is_control_flow_dead = true; + } + defer { + if (mark_as_dead) { + p.is_control_flow_dead = original_is_dead; + } + } + + _ = p.visitClass(stmt.loc, &data.class, Ref.None); + + // Remove the export flag inside a namespace + const was_export_inside_namespace = data.is_export and p.enclosing_namespace_arg_ref != null; + if (was_export_inside_namespace) { + data.is_export = false; + } + + const lowered = p.lowerClass(js_ast.StmtOrExpr{ .stmt = stmt.* }); + + if (!mark_as_dead or was_export_inside_namespace) + // Lower class field syntax for browsers that don't support it + stmts.appendSlice(lowered) catch unreachable + else { + const ref = data.class.class_name.?.ref.?; + if (p.options.features.replace_exports.getPtr(p.loadNameFromRef(ref))) |replacement| { + if (p.injectReplacementExport(stmts, ref, data.class.class_name.?.loc, replacement)) { + p.is_control_flow_dead = original_is_dead; + } + } + } + + // Handle exporting this class from a namespace + if (was_export_inside_namespace) { + stmts.append( + Stmt.assign( + p.newExpr( + E.Dot{ + .target = p.newExpr( + E.Identifier{ .ref = p.enclosing_namespace_arg_ref.? }, + stmt.loc, + ), + .name = p.symbols.items[data.class.class_name.?.ref.?.innerIndex()].original_name, + .name_loc = data.class.class_name.?.loc, + }, + stmt.loc, + ), + p.newExpr( + E.Identifier{ .ref = data.class.class_name.?.ref.? }, + data.class.class_name.?.loc, + ), + ), + ) catch unreachable; + } + + return; + } + pub fn s_export_equals(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ExportEquals) !void { + // "module.exports = value" + stmts.append( + Stmt.assign( + p.@"module.exports"(stmt.loc), + p.visitExpr(data.value), + ), + ) catch unreachable; + p.recordUsage(p.module_ref); + return; + } + pub fn s_break(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Break) !void { + if (data.label) |*label| { + const name = p.loadNameFromRef(label.ref orelse p.panicLoc("Expected label to have a ref", .{}, label.loc)); + const res = p.findLabelSymbol(label.loc, name); + if (res.found) { + label.ref = res.ref; + } else { + data.label = null; + } + } else if (!p.fn_or_arrow_data_visit.is_inside_loop and !p.fn_or_arrow_data_visit.is_inside_switch) { + const r = js_lexer.rangeOfIdentifier(p.source, stmt.loc); + p.log.addRangeError(p.source, r, "Cannot use \"break\" here") catch unreachable; + } + + try stmts.append(stmt.*); + } + pub fn s_continue(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Continue) !void { + if (data.label) |*label| { + const name = p.loadNameFromRef(label.ref orelse p.panicLoc("Expected continue label to have a ref", .{}, label.loc)); + const res = p.findLabelSymbol(label.loc, name); + label.ref = res.ref; + if (res.found and !res.is_loop) { + const r = js_lexer.rangeOfIdentifier(p.source, stmt.loc); + p.log.addRangeErrorFmt(p.source, r, p.allocator, "Cannot \"continue\" to label {s}", .{name}) catch unreachable; + } + } else if (!p.fn_or_arrow_data_visit.is_inside_loop) { + const r = js_lexer.rangeOfIdentifier(p.source, stmt.loc); + p.log.addRangeError(p.source, r, "Cannot use \"continue\" here") catch unreachable; + } + + try stmts.append(stmt.*); + } + pub fn s_label(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Label) !void { + p.pushScopeForVisitPass(.label, stmt.loc) catch unreachable; + const name = p.loadNameFromRef(data.name.ref.?); + const ref = p.newSymbol(.label, name) catch unreachable; + data.name.ref = ref; + p.current_scope.label_ref = ref; + switch (data.stmt.data) { + .s_for, .s_for_in, .s_for_of, .s_while, .s_do_while => { + p.current_scope.label_stmt_is_loop = true; + }, + else => {}, + } + + data.stmt = p.visitSingleStmt(data.stmt, StmtsKind.none); + p.popScope(); + + try stmts.append(stmt.*); + } + pub fn s_local(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Local, was_after_after_const_local_prefix: bool) !void { + // TODO: Silently remove unsupported top-level "await" in dead code branches + // (this was from 'await using' syntax) + + // Local statements do not end the const local prefix + p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; + + const decls_len = if (!(data.is_export and p.options.features.replace_exports.entries.len > 0)) + p.visitDecls(data.decls.slice(), data.kind == .k_const, false) + else + p.visitDecls(data.decls.slice(), data.kind == .k_const, true); + + const is_now_dead = data.decls.len > 0 and decls_len == 0; + if (is_now_dead) { + return; + } + + data.decls.len = @as(u32, @truncate(decls_len)); + + // Handle being exported inside a namespace + if (data.is_export and p.enclosing_namespace_arg_ref != null) { + for (data.decls.slice()) |*d| { + if (d.value) |val| { + p.recordUsage((p.enclosing_namespace_arg_ref orelse unreachable)); + // TODO: is it necessary to lowerAssign? why does esbuild do it _most_ of the time? + stmts.append(p.s(S.SExpr{ + .value = Expr.assign(Binding.toExpr(&d.binding, p.to_expr_wrapper_namespace), val), + }, stmt.loc)) catch unreachable; + } + } + + return; + } + + // Optimization: Avoid unnecessary "using" machinery by changing ones + // initialized to "null" or "undefined" into a normal variable. Note that + // "await using" still needs the "await", so we can't do it for those. + if (p.options.features.minify_syntax and data.kind == .k_using) { + data.kind = .k_let; + for (data.decls.slice()) |*d| { + if (d.value) |val| { + if (val.data != .e_null and val.data != .e_undefined) { + data.kind = .k_using; + break; } } } - }, - .s_throw => |data| { - data.value = p.visitExpr(data.value); - }, - .s_return => |data| { - // Forbid top-level return inside modules with ECMAScript-style exports - if (p.fn_or_arrow_data_visit.is_outside_fn_or_arrow) { - const where = where: { - if (p.esm_export_keyword.len > 0) { - break :where p.esm_export_keyword; - } else if (p.top_level_await_keyword.len > 0) { - break :where p.top_level_await_keyword; - } else { - break :where logger.Range.None; - } + } + + // We must relocate vars in order to safely handle removing if/else depending on NODE_ENV. + // Edgecase: + // `export var` is skipped because it's unnecessary. That *should* be a noop, but it loses the `is_export` flag if we're in HMR. + const kind = p.selectLocalKind(data.kind); + if (kind == .k_var and !data.is_export) { + const relocated = p.maybeRelocateVarsToTopLevel(data.decls.slice(), .normal); + if (relocated.ok) { + if (relocated.stmt) |new_stmt| { + stmts.append(new_stmt) catch unreachable; + } + + return; + } + } + + data.kind = kind; + try stmts.append(stmt.*); + + if (data.is_export and p.options.features.server_components.wrapsExports()) { + for (data.decls.slice()) |*decl| try_annotate: { + const val = decl.value orelse break :try_annotate; + switch (val.data) { + .e_arrow, .e_function => {}, + else => break :try_annotate, + } + const id = switch (decl.binding.data) { + .b_identifier => |id| id.ref, + else => break :try_annotate, }; + const original_name = p.symbols.items[id.innerIndex()].original_name; + decl.value = p.wrapValueForServerComponentReference(val, original_name); + } + } - if (where.len > 0) { - p.log.addRangeError(p.source, where, "Top-level return cannot be used inside an ECMAScript module") catch unreachable; + if (p.options.features.react_fast_refresh and p.current_scope == p.module_scope) { + for (data.decls.slice()) |decl| try_register: { + const val = decl.value orelse break :try_register; + switch (val.data) { + .e_arrow, .e_function => {}, + else => break :try_register, + } + const id = switch (decl.binding.data) { + .b_identifier => |id| id.ref, + else => break :try_register, + }; + const original_name = p.symbols.items[id.innerIndex()].original_name; + try p.handleReactRefreshRegister(stmts, original_name, id); + } + } + + return; + } + pub fn s_expr(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.SExpr) !void { + const should_trim_primitive = p.options.features.dead_code_elimination and + (p.options.features.minify_syntax and data.value.isPrimitiveLiteral()); + p.stmt_expr_value = data.value.data; + defer p.stmt_expr_value = .{ .e_missing = .{} }; + + const is_top_level = p.current_scope == p.module_scope; + if (p.shouldUnwrapCommonJSToESM()) { + p.commonjs_named_exports_needs_conversion = if (is_top_level) + std.math.maxInt(u32) + else + p.commonjs_named_exports_needs_conversion; + } + + data.value = p.visitExpr(data.value); + + if (should_trim_primitive and data.value.isPrimitiveLiteral()) { + return; + } + + // simplify unused + data.value = SideEffects.simplifyUnusedExpr(p, data.value) orelse return; + + if (p.shouldUnwrapCommonJSToESM()) { + if (is_top_level) { + if (data.value.data == .e_binary) { + const to_convert = p.commonjs_named_exports_needs_conversion; + if (to_convert != std.math.maxInt(u32)) { + p.commonjs_named_exports_needs_conversion = std.math.maxInt(u32); + convert: { + const bin: *E.Binary = data.value.data.e_binary; + if (bin.op == .bin_assign and bin.left.data == .e_commonjs_export_identifier) { + var last = &p.commonjs_named_exports.values()[to_convert]; + if (!last.needs_decl) break :convert; + last.needs_decl = false; + + var decls = p.allocator.alloc(Decl, 1) catch unreachable; + const ref = bin.left.data.e_commonjs_export_identifier.ref; + decls[0] = .{ + .binding = p.b(B.Identifier{ .ref = ref }, bin.left.loc), + .value = bin.right, + }; + // we have to ensure these are known to be top-level + p.declared_symbols.append(p.allocator, .{ + .ref = ref, + .is_top_level = true, + }) catch unreachable; + p.esm_export_keyword.loc = stmt.loc; + p.esm_export_keyword.len = 5; + p.had_commonjs_named_exports_this_visit = true; + var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable; + clause_items[0] = js_ast.ClauseItem{ + // We want the generated name to not conflict + .alias = p.commonjs_named_exports.keys()[to_convert], + .alias_loc = bin.left.loc, + .name = .{ + .ref = ref, + .loc = last.loc_ref.loc, + }, + }; + stmts.appendSlice( + &[_]Stmt{ + p.s( + S.Local{ + .kind = .k_var, + .is_export = false, + .was_commonjs_export = true, + .decls = G.Decl.List.init(decls), + }, + stmt.loc, + ), + p.s( + S.ExportClause{ + .items = clause_items, + .is_single_line = true, + }, + stmt.loc, + ), + }, + ) catch unreachable; + + return; + } + } + } else if (p.commonjs_replacement_stmts.len > 0) { + if (stmts.items.len == 0) { + stmts.items = p.commonjs_replacement_stmts; + stmts.capacity = p.commonjs_replacement_stmts.len; + p.commonjs_replacement_stmts.len = 0; + } else { + stmts.appendSlice(p.commonjs_replacement_stmts) catch unreachable; + p.commonjs_replacement_stmts.len = 0; + } + + return; + } } } + } - if (data.value) |val| { - data.value = p.visitExpr(val); - - // "return undefined;" can safely just always be "return;" - if (data.value != null and @as(Expr.Tag, data.value.?.data) == .e_undefined) { - // Returning undefined is implicit - data.value = null; + try stmts.append(stmt.*); + } + pub fn s_throw(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Throw) !void { + data.value = p.visitExpr(data.value); + try stmts.append(stmt.*); + } + pub fn s_return(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Return) !void { + // Forbid top-level return inside modules with ECMAScript-style exports + if (p.fn_or_arrow_data_visit.is_outside_fn_or_arrow) { + const where = where: { + if (p.esm_export_keyword.len > 0) { + break :where p.esm_export_keyword; + } else if (p.top_level_await_keyword.len > 0) { + break :where p.top_level_await_keyword; + } else { + break :where logger.Range.None; } + }; + + if (where.len > 0) { + p.log.addRangeError(p.source, where, "Top-level return cannot be used inside an ECMAScript module") catch unreachable; } - }, - .s_block => |data| { - { - p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + } - // Pass the "is loop body" status on to the direct children of a block used - // as a loop body. This is used to enable optimizations specific to the - // topmost scope in a loop body block. - const kind = if (std.meta.eql(p.loop_body, stmt.data)) StmtsKind.loop_body else StmtsKind.none; - var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, data.stmts); - p.visitStmts(&_stmts, kind) catch unreachable; - data.stmts = _stmts.items; - p.popScope(); + if (data.value) |val| { + data.value = p.visitExpr(val); + + // "return undefined;" can safely just always be "return;" + if (data.value != null and @as(Expr.Tag, data.value.?.data) == .e_undefined) { + // Returning undefined is implicit + data.value = null; } + } - if (p.options.features.minify_syntax) { - // // trim empty statements - if (data.stmts.len == 0) { - stmts.append(Stmt{ .data = Prefill.Data.SEmpty, .loc = stmt.loc }) catch unreachable; - return; - } else if (data.stmts.len == 1 and !statementCaresAboutScope(data.stmts[0])) { - // Unwrap blocks containing a single statement - stmts.append(data.stmts[0]) catch unreachable; - return; - } - } - }, - .s_with => |data| { - data.value = p.visitExpr(data.value); - - p.pushScopeForVisitPass(.with, data.body_loc) catch unreachable; - - // This can be many different kinds of statements. - // example code: - // - // with(this.document.defaultView || Object.create(null)) - // with(this.document) - // with(this.form) - // with(this.element) - // - data.body = p.visitSingleStmt(data.body, StmtsKind.none); + try stmts.append(stmt.*); + } + pub fn s_block(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Block) !void { + { + p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + // Pass the "is loop body" status on to the direct children of a block used + // as a loop body. This is used to enable optimizations specific to the + // topmost scope in a loop body block. + const kind = if (std.meta.eql(p.loop_body, stmt.data)) StmtsKind.loop_body else StmtsKind.none; + var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, data.stmts); + p.visitStmts(&_stmts, kind) catch unreachable; + data.stmts = _stmts.items; p.popScope(); - }, - .s_while => |data| { - data.test_ = p.visitExpr(data.test_); - data.body = p.visitLoopBody(data.body); + } - data.test_ = SideEffects.simplifyBoolean(p, data.test_); - const result = SideEffects.toBoolean(p, data.test_.data); - if (result.ok and result.side_effects == .no_side_effects) { - data.test_ = p.newExpr(E.Boolean{ .value = result.value }, data.test_.loc); + if (p.options.features.minify_syntax) { + // // trim empty statements + if (data.stmts.len == 0) { + stmts.append(Stmt{ .data = Prefill.Data.SEmpty, .loc = stmt.loc }) catch unreachable; + return; + } else if (data.stmts.len == 1 and !statementCaresAboutScope(data.stmts[0])) { + // Unwrap blocks containing a single statement + stmts.append(data.stmts[0]) catch unreachable; + return; } - }, - .s_do_while => |data| { - data.body = p.visitLoopBody(data.body); - data.test_ = p.visitExpr(data.test_); + } + try stmts.append(stmt.*); + } + pub fn s_with(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.With) !void { + data.value = p.visitExpr(data.value); + + p.pushScopeForVisitPass(.with, data.body_loc) catch unreachable; + + // This can be many different kinds of statements. + // example code: + // + // with(this.document.defaultView || Object.create(null)) + // with(this.document) + // with(this.form) + // with(this.element) + // + data.body = p.visitSingleStmt(data.body, StmtsKind.none); + + p.popScope(); + try stmts.append(stmt.*); + } + pub fn s_while(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.While) !void { + data.test_ = p.visitExpr(data.test_); + data.body = p.visitLoopBody(data.body); + + data.test_ = SideEffects.simplifyBoolean(p, data.test_); + const result = SideEffects.toBoolean(p, data.test_.data); + if (result.ok and result.side_effects == .no_side_effects) { + data.test_ = p.newExpr(E.Boolean{ .value = result.value }, data.test_.loc); + } + + try stmts.append(stmt.*); + } + pub fn s_do_while(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.DoWhile) !void { + data.body = p.visitLoopBody(data.body); + data.test_ = p.visitExpr(data.test_); + + data.test_ = SideEffects.simplifyBoolean(p, data.test_); + try stmts.append(stmt.*); + } + pub fn s_if(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.If) !void { + data.test_ = p.visitExpr(data.test_); + + if (p.options.features.minify_syntax) { data.test_ = SideEffects.simplifyBoolean(p, data.test_); - }, - .s_if => |data| { - data.test_ = p.visitExpr(data.test_); + } - if (p.options.features.minify_syntax) { - data.test_ = SideEffects.simplifyBoolean(p, data.test_); - } + const effects = SideEffects.toBoolean(p, data.test_.data); + if (effects.ok and !effects.value) { + const old = p.is_control_flow_dead; + p.is_control_flow_dead = true; + data.yes = p.visitSingleStmt(data.yes, StmtsKind.none); + p.is_control_flow_dead = old; + } else { + data.yes = p.visitSingleStmt(data.yes, StmtsKind.none); + } - const effects = SideEffects.toBoolean(p, data.test_.data); - if (effects.ok and !effects.value) { + // The "else" clause is optional + if (data.no) |no| { + if (effects.ok and effects.value) { const old = p.is_control_flow_dead; p.is_control_flow_dead = true; - data.yes = p.visitSingleStmt(data.yes, StmtsKind.none); - p.is_control_flow_dead = old; + defer p.is_control_flow_dead = old; + data.no = p.visitSingleStmt(no, .none); } else { - data.yes = p.visitSingleStmt(data.yes, StmtsKind.none); - } - - // The "else" clause is optional - if (data.no) |no| { - if (effects.ok and effects.value) { - const old = p.is_control_flow_dead; - p.is_control_flow_dead = true; - defer p.is_control_flow_dead = old; - data.no = p.visitSingleStmt(no, .none); - } else { - data.no = p.visitSingleStmt(no, .none); - } - - // Trim unnecessary "else" clauses - if (p.options.features.minify_syntax) { - if (data.no != null and @as(Stmt.Tag, data.no.?.data) == .s_empty) { - data.no = null; - } - } + data.no = p.visitSingleStmt(no, .none); } + // Trim unnecessary "else" clauses if (p.options.features.minify_syntax) { - if (effects.ok) { - if (effects.value) { - if (data.no == null or !SideEffects.shouldKeepStmtInDeadControlFlow(p, data.no.?, p.allocator)) { - if (effects.side_effects == .could_have_side_effects) { - // Keep the condition if it could have side effects (but is still known to be truthy) - if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| { - stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable; - } - } + if (data.no != null and @as(Stmt.Tag, data.no.?.data) == .s_empty) { + data.no = null; + } + } + } - return try p.appendIfBodyPreservingScope(stmts, data.yes); - } else { - // We have to keep the "no" branch + if (p.options.features.minify_syntax) { + if (effects.ok) { + if (effects.value) { + if (data.no == null or !SideEffects.shouldKeepStmtInDeadControlFlow(p, data.no.?, p.allocator)) { + if (effects.side_effects == .could_have_side_effects) { + // Keep the condition if it could have side effects (but is still known to be truthy) + if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| { + stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable; + } } + + return try p.appendIfBodyPreservingScope(stmts, data.yes); } else { - // The test is falsy - if (!SideEffects.shouldKeepStmtInDeadControlFlow(p, data.yes, p.allocator)) { - if (effects.side_effects == .could_have_side_effects) { - // Keep the condition if it could have side effects (but is still known to be truthy) - if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| { - stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable; - } + // We have to keep the "no" branch + } + } else { + // The test is falsy + if (!SideEffects.shouldKeepStmtInDeadControlFlow(p, data.yes, p.allocator)) { + if (effects.side_effects == .could_have_side_effects) { + // Keep the condition if it could have side effects (but is still known to be truthy) + if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| { + stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable; } - - if (data.no == null) { - return; - } - - return try p.appendIfBodyPreservingScope(stmts, data.no.?); } + + if (data.no == null) { + return; + } + + return try p.appendIfBodyPreservingScope(stmts, data.no.?); } } + } - // TODO: more if statement syntax minification - const can_remove_test = p.exprCanBeRemovedIfUnused(&data.test_); - switch (data.yes.data) { - .s_expr => |yes_expr| { - if (yes_expr.value.isMissing()) { - if (data.no == null) { - if (can_remove_test) { - return; - } - } else if (data.no.?.isMissingExpr() and can_remove_test) { - return; - } - } - }, - .s_empty => { + // TODO: more if statement syntax minification + const can_remove_test = p.exprCanBeRemovedIfUnused(&data.test_); + switch (data.yes.data) { + .s_expr => |yes_expr| { + if (yes_expr.value.isMissing()) { if (data.no == null) { if (can_remove_test) { return; @@ -19761,590 +20055,457 @@ fn NewParser_( } else if (data.no.?.isMissingExpr() and can_remove_test) { return; } - }, - else => {}, - } - } - }, - .s_for => |data| { - p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; - - if (data.init) |initst| { - data.init = p.visitForLoopInit(initst, false); - } - - if (data.test_) |test_| { - data.test_ = SideEffects.simplifyBoolean(p, p.visitExpr(test_)); - - const result = SideEffects.toBoolean(p, data.test_.?.data); - if (result.ok and result.value and result.side_effects == .no_side_effects) { - data.test_ = null; - } - } - - if (data.update) |update| { - data.update = p.visitExpr(update); - } - - data.body = p.visitLoopBody(data.body); - - if (data.init) |for_init| { - if (for_init.data == .s_local) { - // Potentially relocate "var" declarations to the top level. Note that this - // must be done inside the scope of the for loop or they won't be relocated. - if (for_init.data.s_local.kind == .k_var) { - const relocate = p.maybeRelocateVarsToTopLevel(for_init.data.s_local.decls.slice(), .normal); - if (relocate.stmt) |relocated| { - data.init = relocated; + } + }, + .s_empty => { + if (data.no == null) { + if (can_remove_test) { + return; } + } else if (data.no.?.isMissingExpr() and can_remove_test) { + return; + } + }, + else => {}, + } + } + + try stmts.append(stmt.*); + } + pub fn s_for(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.For) !void { + p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + + if (data.init) |initst| { + data.init = p.visitForLoopInit(initst, false); + } + + if (data.test_) |test_| { + data.test_ = SideEffects.simplifyBoolean(p, p.visitExpr(test_)); + + const result = SideEffects.toBoolean(p, data.test_.?.data); + if (result.ok and result.value and result.side_effects == .no_side_effects) { + data.test_ = null; + } + } + + if (data.update) |update| { + data.update = p.visitExpr(update); + } + + data.body = p.visitLoopBody(data.body); + + if (data.init) |for_init| { + if (for_init.data == .s_local) { + // Potentially relocate "var" declarations to the top level. Note that this + // must be done inside the scope of the for loop or they won't be relocated. + if (for_init.data.s_local.kind == .k_var) { + const relocate = p.maybeRelocateVarsToTopLevel(for_init.data.s_local.decls.slice(), .normal); + if (relocate.stmt) |relocated| { + data.init = relocated; } } } + } - p.popScope(); - }, - .s_for_in => |data| { - { - p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; - defer p.popScope(); - _ = p.visitForLoopInit(data.init, true); - data.value = p.visitExpr(data.value); - data.body = p.visitLoopBody(data.body); + p.popScope(); - // Check for a variable initializer - if (data.init.data == .s_local and data.init.data.s_local.kind == .k_var) { - // Lower for-in variable initializers in case the output is used in strict mode - var local = data.init.data.s_local; - if (local.decls.len == 1) { - var decl: *G.Decl = &local.decls.ptr[0]; - if (decl.binding.data == .b_identifier) { - if (decl.value) |val| { - stmts.append( - Stmt.assign( - Expr.initIdentifier(decl.binding.data.b_identifier.ref, decl.binding.loc), - val, - ), - ) catch unreachable; - decl.value = null; - } - } - } - - const relocate = p.maybeRelocateVarsToTopLevel(data.init.data.s_local.decls.slice(), RelocateVars.Mode.for_in_or_for_of); - if (relocate.stmt) |relocated_stmt| { - data.init = relocated_stmt; - } - } - } - }, - .s_for_of => |data| { + try stmts.append(stmt.*); + } + pub fn s_for_in(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ForIn) !void { + { p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; defer p.popScope(); _ = p.visitForLoopInit(data.init, true); data.value = p.visitExpr(data.value); data.body = p.visitLoopBody(data.body); - if (data.init.data == .s_local) { - if (data.init.data.s_local.kind == .k_var) { - const relocate = p.maybeRelocateVarsToTopLevel(data.init.data.s_local.decls.slice(), RelocateVars.Mode.for_in_or_for_of); - if (relocate.stmt) |relocated_stmt| { - data.init = relocated_stmt; + // Check for a variable initializer + if (data.init.data == .s_local and data.init.data.s_local.kind == .k_var) { + // Lower for-in variable initializers in case the output is used in strict mode + var local = data.init.data.s_local; + if (local.decls.len == 1) { + var decl: *G.Decl = &local.decls.ptr[0]; + if (decl.binding.data == .b_identifier) { + if (decl.value) |val| { + stmts.append( + Stmt.assign( + Expr.initIdentifier(decl.binding.data.b_identifier.ref, decl.binding.loc), + val, + ), + ) catch unreachable; + decl.value = null; + } } } - // Handle "for (using x of y)" and "for (await using x of y)" - if (data.init.data == .s_local and data.init.data.s_local.kind.isUsing() and p.options.features.lower_using) { - // fn lowerUsingDeclarationInForOf() - const loc = data.init.loc; - const init2 = data.init.data.s_local; - const binding = init2.decls.at(0).binding; - var id = binding.data.b_identifier; - const temp_ref = p.generateTempRef(p.symbols.items[id.ref.inner_index].original_name); - - const first = p.s(S.Local{ - .kind = init2.kind, - .decls = bindings: { - const decls = p.allocator.alloc(G.Decl, 1) catch bun.outOfMemory(); - decls[0] = .{ - .binding = p.b(B.Identifier{ .ref = id.ref }, loc), - .value = p.newExpr(E.Identifier{ .ref = temp_ref }, loc), - }; - break :bindings G.Decl.List.init(decls); - }, - }, loc); - - const length = if (data.body.data == .s_block) data.body.data.s_block.stmts.len else 1; - const statements = p.allocator.alloc(Stmt, 1 + length) catch bun.outOfMemory(); - statements[0] = first; - if (data.body.data == .s_block) { - @memcpy(statements[1..], data.body.data.s_block.stmts); - } else { - statements[1] = data.body; - } - - var ctx = try P.LowerUsingDeclarationsContext.init(p); - ctx.scanStmts(p, statements); - const visited_stmts = ctx.finalize(p, statements, p.will_wrap_module_in_try_catch_for_using and p.current_scope.parent == null); - if (data.body.data == .s_block) { - data.body.data.s_block.stmts = visited_stmts.items; - } else { - data.body = p.s(S.Block{ - .stmts = visited_stmts.items, - }, loc); - } - id.ref = temp_ref; - init2.kind = .k_const; + const relocate = p.maybeRelocateVarsToTopLevel(data.init.data.s_local.decls.slice(), RelocateVars.Mode.for_in_or_for_of); + if (relocate.stmt) |relocated_stmt| { + data.init = relocated_stmt; } } - }, - .s_try => |data| { - p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + } + + try stmts.append(stmt.*); + } + pub fn s_for_of(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.ForOf) !void { + p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + defer p.popScope(); + _ = p.visitForLoopInit(data.init, true); + data.value = p.visitExpr(data.value); + data.body = p.visitLoopBody(data.body); + + if (data.init.data == .s_local) { + if (data.init.data.s_local.kind == .k_var) { + const relocate = p.maybeRelocateVarsToTopLevel(data.init.data.s_local.decls.slice(), RelocateVars.Mode.for_in_or_for_of); + if (relocate.stmt) |relocated_stmt| { + data.init = relocated_stmt; + } + } + + // Handle "for (using x of y)" and "for (await using x of y)" + if (data.init.data == .s_local and data.init.data.s_local.kind.isUsing() and p.options.features.lower_using) { + // fn lowerUsingDeclarationInForOf() + const loc = data.init.loc; + const init2 = data.init.data.s_local; + const binding = init2.decls.at(0).binding; + var id = binding.data.b_identifier; + const temp_ref = p.generateTempRef(p.symbols.items[id.ref.inner_index].original_name); + + const first = p.s(S.Local{ + .kind = init2.kind, + .decls = bindings: { + const decls = p.allocator.alloc(G.Decl, 1) catch bun.outOfMemory(); + decls[0] = .{ + .binding = p.b(B.Identifier{ .ref = id.ref }, loc), + .value = p.newExpr(E.Identifier{ .ref = temp_ref }, loc), + }; + break :bindings G.Decl.List.init(decls); + }, + }, loc); + + const length = if (data.body.data == .s_block) data.body.data.s_block.stmts.len else 1; + const statements = p.allocator.alloc(Stmt, 1 + length) catch bun.outOfMemory(); + statements[0] = first; + if (data.body.data == .s_block) { + @memcpy(statements[1..], data.body.data.s_block.stmts); + } else { + statements[1] = data.body; + } + + var ctx = try P.LowerUsingDeclarationsContext.init(p); + ctx.scanStmts(p, statements); + const visited_stmts = ctx.finalize(p, statements, p.will_wrap_module_in_try_catch_for_using and p.current_scope.parent == null); + if (data.body.data == .s_block) { + data.body.data.s_block.stmts = visited_stmts.items; + } else { + data.body = p.s(S.Block{ + .stmts = visited_stmts.items, + }, loc); + } + id.ref = temp_ref; + init2.kind = .k_const; + } + } + + try stmts.append(stmt.*); + } + pub fn s_try(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Try) !void { + p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + { + var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, data.body); + p.fn_or_arrow_data_visit.try_body_count += 1; + p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; + p.fn_or_arrow_data_visit.try_body_count -= 1; + data.body = _stmts.items; + } + p.popScope(); + + if (data.catch_) |*catch_| { + p.pushScopeForVisitPass(.catch_binding, catch_.loc) catch unreachable; { - var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, data.body); - p.fn_or_arrow_data_visit.try_body_count += 1; + if (catch_.binding) |catch_binding| { + p.visitBinding(catch_binding, null); + } + var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, catch_.body); + p.pushScopeForVisitPass(.block, catch_.body_loc) catch unreachable; p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; - p.fn_or_arrow_data_visit.try_body_count -= 1; - data.body = _stmts.items; + p.popScope(); + catch_.body = _stmts.items; } p.popScope(); + } - if (data.catch_) |*catch_| { - p.pushScopeForVisitPass(.catch_binding, catch_.loc) catch unreachable; - { - if (catch_.binding) |catch_binding| { - p.visitBinding(catch_binding, null); - } - var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, catch_.body); - p.pushScopeForVisitPass(.block, catch_.body_loc) catch unreachable; - p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; - p.popScope(); - catch_.body = _stmts.items; - } - p.popScope(); - } - - if (data.finally) |*finally| { - p.pushScopeForVisitPass(.block, finally.loc) catch unreachable; - { - var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, finally.stmts); - p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; - finally.stmts = _stmts.items; - } - p.popScope(); - } - }, - .s_switch => |data| { - data.test_ = p.visitExpr(data.test_); + if (data.finally) |*finally| { + p.pushScopeForVisitPass(.block, finally.loc) catch unreachable; { - p.pushScopeForVisitPass(.block, data.body_loc) catch unreachable; - defer p.popScope(); - const old_is_inside_Swsitch = p.fn_or_arrow_data_visit.is_inside_switch; - p.fn_or_arrow_data_visit.is_inside_switch = true; - defer p.fn_or_arrow_data_visit.is_inside_switch = old_is_inside_Swsitch; - for (data.cases, 0..) |case, i| { - if (case.value) |val| { - data.cases[i].value = p.visitExpr(val); - // TODO: error messages - // Check("case", *c.Value, c.Value.Loc) - // p.warnAboutTypeofAndString(s.Test, *c.Value) - } - var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, case.body); - p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; - data.cases[i].body = _stmts.items; - } + var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, finally.stmts); + p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; + finally.stmts = _stmts.items; } - // TODO: duplicate case checker + p.popScope(); + } - }, - .s_function => |data| { - // We mark it as dead, but the value may not actually be dead - // We just want to be sure to not increment the usage counts for anything in the function - const mark_as_dead = p.options.features.dead_code_elimination and data.func.flags.contains(.is_export) and - p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.func.name.?.ref.?); - const original_is_dead = p.is_control_flow_dead; - - if (mark_as_dead) { - p.is_control_flow_dead = true; - } - defer { - if (mark_as_dead) { - p.is_control_flow_dead = original_is_dead; - } - } - - var react_hook_data: ?ReactRefresh.HookContext = null; - const prev = p.react_refresh.hook_ctx_storage; - defer p.react_refresh.hook_ctx_storage = prev; - p.react_refresh.hook_ctx_storage = &react_hook_data; - - data.func = p.visitFunc(data.func, data.func.open_parens_loc); - - const name_ref = data.func.name.?.ref.?; - bun.assert(name_ref.tag == .symbol); - const name_symbol = &p.symbols.items[name_ref.innerIndex()]; - const original_name = name_symbol.original_name; - - // Handle exporting this function from a namespace - if (data.func.flags.contains(.is_export) and p.enclosing_namespace_arg_ref != null) { - data.func.flags.remove(.is_export); - - const enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref orelse bun.outOfMemory(); - stmts.ensureUnusedCapacity(3) catch bun.outOfMemory(); - stmts.appendAssumeCapacity(stmt.*); - stmts.appendAssumeCapacity(Stmt.assign( - p.newExpr(E.Dot{ - .target = p.newExpr(E.Identifier{ .ref = enclosing_namespace_arg_ref }, stmt.loc), - .name = original_name, - .name_loc = data.func.name.?.loc, - }, stmt.loc), - p.newExpr(E.Identifier{ .ref = data.func.name.?.ref.? }, data.func.name.?.loc), - )); - } else if (!mark_as_dead) { - if (name_symbol.remove_overwritten_function_declaration) { - return; - } - - if (p.options.features.server_components.wrapsExports() and data.func.flags.contains(.is_export)) { - // Convert this into `export var = registerClientReference(, ...);` - const name = data.func.name.?; - // From the inner scope, have code reference the wrapped function. - data.func.name = null; - try stmts.append(p.s(S.Local{ - .kind = .k_var, - .is_export = true, - .decls = try G.Decl.List.fromSlice(p.allocator, &.{.{ - .binding = p.b(B.Identifier{ .ref = name_ref }, name.loc), - .value = p.wrapValueForServerComponentReference( - p.newExpr(E.Function{ .func = data.func }, stmt.loc), - original_name, - ), - }}), - }, stmt.loc)); - } else { - stmts.append(stmt.*) catch bun.outOfMemory(); - } - } else if (mark_as_dead) { - if (p.options.features.replace_exports.getPtr(original_name)) |replacement| { - _ = p.injectReplacementExport(stmts, name_ref, data.func.name.?.loc, replacement); - } - } - - if (p.options.features.react_fast_refresh) { - if (react_hook_data) |*hook| { - try stmts.append(p.getReactRefreshHookSignalDecl(hook.signature_cb)); - try stmts.append(p.s(S.SExpr{ - .value = p.getReactRefreshHookSignalInit(hook, Expr.initIdentifier(name_ref, logger.Loc.Empty)), - }, logger.Loc.Empty)); - } - - if (p.current_scope == p.module_scope) { - try p.handleReactRefreshRegister(stmts, original_name, name_ref); - } - } - - return; - }, - .s_class => |data| { - const mark_as_dead = p.options.features.dead_code_elimination and data.is_export and - p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.class.class_name.?.ref.?); - const original_is_dead = p.is_control_flow_dead; - - if (mark_as_dead) { - p.is_control_flow_dead = true; - } - defer { - if (mark_as_dead) { - p.is_control_flow_dead = original_is_dead; - } - } - - _ = p.visitClass(stmt.loc, &data.class, Ref.None); - - // Remove the export flag inside a namespace - const was_export_inside_namespace = data.is_export and p.enclosing_namespace_arg_ref != null; - if (was_export_inside_namespace) { - data.is_export = false; - } - - const lowered = p.lowerClass(js_ast.StmtOrExpr{ .stmt = stmt.* }); - - if (!mark_as_dead or was_export_inside_namespace) - // Lower class field syntax for browsers that don't support it - stmts.appendSlice(lowered) catch unreachable - else { - const ref = data.class.class_name.?.ref.?; - if (p.options.features.replace_exports.getPtr(p.loadNameFromRef(ref))) |replacement| { - if (p.injectReplacementExport(stmts, ref, data.class.class_name.?.loc, replacement)) { - p.is_control_flow_dead = original_is_dead; - } - } - } - - // Handle exporting this class from a namespace - if (was_export_inside_namespace) { - stmts.append( - Stmt.assign( - p.newExpr( - E.Dot{ - .target = p.newExpr( - E.Identifier{ .ref = p.enclosing_namespace_arg_ref.? }, - stmt.loc, - ), - .name = p.symbols.items[data.class.class_name.?.ref.?.innerIndex()].original_name, - .name_loc = data.class.class_name.?.loc, - }, - stmt.loc, - ), - p.newExpr( - E.Identifier{ .ref = data.class.class_name.?.ref.? }, - data.class.class_name.?.loc, - ), - ), - ) catch unreachable; - } - - return; - }, - .s_enum => |data| { - // Do not end the const local prefix after TypeScript enums. We process - // them first within their scope so that they are inlined into all code in - // that scope. We don't want that to cause the const local prefix to end. - p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; - - // Track cross-module enum constants during bundling. This - // part of the code is different from esbuilt in that we are - // only storing a list of enum indexes. At the time of - // referencing, `esbuild` builds a separate hash map of hash - // maps. We are avoiding that to reduce memory usage, since - // enum inlining already uses alot of hash maps. - if (p.current_scope == p.module_scope and p.options.bundle) { - try p.top_level_enums.append(p.allocator, data.name.ref.?); - } - - p.recordDeclaredSymbol(data.name.ref.?) catch bun.outOfMemory(); - p.pushScopeForVisitPass(.entry, stmt.loc) catch bun.outOfMemory(); + try stmts.append(stmt.*); + } + pub fn s_switch(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Switch) !void { + data.test_ = p.visitExpr(data.test_); + { + p.pushScopeForVisitPass(.block, data.body_loc) catch unreachable; defer p.popScope(); - p.recordDeclaredSymbol(data.arg) catch bun.outOfMemory(); - - const allocator = p.allocator; - // Scan ahead for any variables inside this namespace. This must be done - // ahead of time before visiting any statements inside the namespace - // because we may end up visiting the uses before the declarations. - // We need to convert the uses into property accesses on the namespace. - for (data.values) |value| { - if (value.ref.isValid()) { - p.is_exported_inside_namespace.put(allocator, value.ref, data.arg) catch bun.outOfMemory(); + const old_is_inside_Swsitch = p.fn_or_arrow_data_visit.is_inside_switch; + p.fn_or_arrow_data_visit.is_inside_switch = true; + defer p.fn_or_arrow_data_visit.is_inside_switch = old_is_inside_Swsitch; + for (data.cases, 0..) |case, i| { + if (case.value) |val| { + data.cases[i].value = p.visitExpr(val); + // TODO: error messages + // Check("case", *c.Value, c.Value.Loc) + // p.warnAboutTypeofAndString(s.Test, *c.Value) } + var _stmts = ListManaged(Stmt).fromOwnedSlice(p.allocator, case.body); + p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; + data.cases[i].body = _stmts.items; + } + } + // TODO: duplicate case checker + + try stmts.append(stmt.*); + } + + pub fn s_enum(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Enum, was_after_after_const_local_prefix: bool) !void { + + // Do not end the const local prefix after TypeScript enums. We process + // them first within their scope so that they are inlined into all code in + // that scope. We don't want that to cause the const local prefix to end. + p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; + + // Track cross-module enum constants during bundling. This + // part of the code is different from esbuilt in that we are + // only storing a list of enum indexes. At the time of + // referencing, `esbuild` builds a separate hash map of hash + // maps. We are avoiding that to reduce memory usage, since + // enum inlining already uses alot of hash maps. + if (p.current_scope == p.module_scope and p.options.bundle) { + try p.top_level_enums.append(p.allocator, data.name.ref.?); + } + + p.recordDeclaredSymbol(data.name.ref.?) catch bun.outOfMemory(); + p.pushScopeForVisitPass(.entry, stmt.loc) catch bun.outOfMemory(); + defer p.popScope(); + p.recordDeclaredSymbol(data.arg) catch bun.outOfMemory(); + + const allocator = p.allocator; + // Scan ahead for any variables inside this namespace. This must be done + // ahead of time before visiting any statements inside the namespace + // because we may end up visiting the uses before the declarations. + // We need to convert the uses into property accesses on the namespace. + for (data.values) |value| { + if (value.ref.isValid()) { + p.is_exported_inside_namespace.put(allocator, value.ref, data.arg) catch bun.outOfMemory(); + } + } + + // Values without initializers are initialized to one more than the + // previous value if the previous value is numeric. Otherwise values + // without initializers are initialized to undefined. + var next_numeric_value: ?f64 = 0.0; + + var value_exprs = ListManaged(Expr).initCapacity(allocator, data.values.len) catch bun.outOfMemory(); + + var all_values_are_pure = true; + + const exported_members = p.current_scope.ts_namespace.?.exported_members; + + // We normally don't fold numeric constants because they might increase code + // size, but it's important to fold numeric constants inside enums since + // that's what the TypeScript compiler does. + const old_should_fold_typescript_constant_expressions = p.should_fold_typescript_constant_expressions; + p.should_fold_typescript_constant_expressions = true; + + // Create an assignment for each enum value + for (data.values) |*value| { + const name = value.name; + + var has_string_value = false; + if (value.value) |enum_value| { + next_numeric_value = null; + + const visited = p.visitExpr(enum_value); + + // "See through" any wrapped comments + const underlying_value = if (visited.data == .e_inlined_enum) + visited.data.e_inlined_enum.value + else + visited; + value.value = underlying_value; + + switch (underlying_value.data) { + .e_number => |num| { + exported_members.getPtr(name).?.data = .{ .enum_number = num.value }; + + p.ref_to_ts_namespace_member.put( + p.allocator, + value.ref, + .{ .enum_number = num.value }, + ) catch bun.outOfMemory(); + + next_numeric_value = num.value + 1.0; + }, + .e_string => |str| { + has_string_value = true; + + exported_members.getPtr(name).?.data = .{ .enum_string = str }; + + p.ref_to_ts_namespace_member.put( + p.allocator, + value.ref, + .{ .enum_string = str }, + ) catch bun.outOfMemory(); + }, + else => { + if (visited.knownPrimitive() == .string) { + has_string_value = true; + } + + if (!p.exprCanBeRemovedIfUnused(&visited)) { + all_values_are_pure = false; + } + }, + } + } else if (next_numeric_value) |num| { + value.value = p.newExpr(E.Number{ .value = num }, value.loc); + + next_numeric_value = num + 1; + + exported_members.getPtr(name).?.data = .{ .enum_number = num }; + + p.ref_to_ts_namespace_member.put( + p.allocator, + value.ref, + .{ .enum_number = num }, + ) catch bun.outOfMemory(); + } else { + value.value = p.newExpr(E.Undefined{}, value.loc); } - // Values without initializers are initialized to one more than the - // previous value if the previous value is numeric. Otherwise values - // without initializers are initialized to undefined. - var next_numeric_value: ?f64 = 0.0; + const is_assign_target = p.options.features.minify_syntax and bun.js_lexer.isIdentifier(value.name); - var value_exprs = ListManaged(Expr).initCapacity(allocator, data.values.len) catch bun.outOfMemory(); + const name_as_e_string = if (!is_assign_target or !has_string_value) + p.newExpr(value.nameAsEString(allocator), value.loc) + else + null; - var all_values_are_pure = true; + const assign_target = if (is_assign_target) + // "Enum.Name = value" + Expr.assign( + p.newExpr(E.Dot{ + .target = p.newExpr( + E.Identifier{ .ref = data.arg }, + value.loc, + ), + .name = value.name, + .name_loc = value.loc, + }, value.loc), + value.value.?, + ) + else + // "Enum['Name'] = value" + Expr.assign( + p.newExpr(E.Index{ + .target = p.newExpr( + E.Identifier{ .ref = data.arg }, + value.loc, + ), + .index = name_as_e_string.?, + }, value.loc), + value.value.?, + ); - const exported_members = p.current_scope.ts_namespace.?.exported_members; + p.recordUsage(data.arg); - // We normally don't fold numeric constants because they might increase code - // size, but it's important to fold numeric constants inside enums since - // that's what the TypeScript compiler does. - const old_should_fold_typescript_constant_expressions = p.should_fold_typescript_constant_expressions; - p.should_fold_typescript_constant_expressions = true; - - // Create an assignment for each enum value - for (data.values) |*value| { - const name = value.name; - - var has_string_value = false; - if (value.value) |enum_value| { - next_numeric_value = null; - - const visited = p.visitExpr(enum_value); - - // "See through" any wrapped comments - const underlying_value = if (visited.data == .e_inlined_enum) - visited.data.e_inlined_enum.value - else - visited; - value.value = underlying_value; - - switch (underlying_value.data) { - .e_number => |num| { - exported_members.getPtr(name).?.data = .{ .enum_number = num.value }; - - p.ref_to_ts_namespace_member.put( - p.allocator, - value.ref, - .{ .enum_number = num.value }, - ) catch bun.outOfMemory(); - - next_numeric_value = num.value + 1.0; - }, - .e_string => |str| { - has_string_value = true; - - exported_members.getPtr(name).?.data = .{ .enum_string = str }; - - p.ref_to_ts_namespace_member.put( - p.allocator, - value.ref, - .{ .enum_string = str }, - ) catch bun.outOfMemory(); - }, - else => { - if (visited.knownPrimitive() == .string) { - has_string_value = true; - } - - if (!p.exprCanBeRemovedIfUnused(&visited)) { - all_values_are_pure = false; - } - }, - } - } else if (next_numeric_value) |num| { - value.value = p.newExpr(E.Number{ .value = num }, value.loc); - - next_numeric_value = num + 1; - - exported_members.getPtr(name).?.data = .{ .enum_number = num }; - - p.ref_to_ts_namespace_member.put( - p.allocator, - value.ref, - .{ .enum_number = num }, - ) catch bun.outOfMemory(); - } else { - value.value = p.newExpr(E.Undefined{}, value.loc); - } - - const is_assign_target = p.options.features.minify_syntax and bun.js_lexer.isIdentifier(value.name); - - const name_as_e_string = if (!is_assign_target or !has_string_value) - p.newExpr(value.nameAsEString(allocator), value.loc) - else - null; - - const assign_target = if (is_assign_target) - // "Enum.Name = value" - Expr.assign( - p.newExpr(E.Dot{ - .target = p.newExpr( - E.Identifier{ .ref = data.arg }, - value.loc, - ), - .name = value.name, - .name_loc = value.loc, - }, value.loc), - value.value.?, - ) - else - // "Enum['Name'] = value" + // String-valued enums do not form a two-way map + if (has_string_value) { + value_exprs.append(assign_target) catch bun.outOfMemory(); + } else { + // "Enum[assignTarget] = 'Name'" + value_exprs.append( Expr.assign( p.newExpr(E.Index{ .target = p.newExpr( E.Identifier{ .ref = data.arg }, value.loc, ), - .index = name_as_e_string.?, + .index = assign_target, }, value.loc), - value.value.?, - ); - + name_as_e_string.?, + ), + ) catch bun.outOfMemory(); p.recordUsage(data.arg); - - // String-valued enums do not form a two-way map - if (has_string_value) { - value_exprs.append(assign_target) catch bun.outOfMemory(); - } else { - // "Enum[assignTarget] = 'Name'" - value_exprs.append( - Expr.assign( - p.newExpr(E.Index{ - .target = p.newExpr( - E.Identifier{ .ref = data.arg }, - value.loc, - ), - .index = assign_target, - }, value.loc), - name_as_e_string.?, - ), - ) catch bun.outOfMemory(); - p.recordUsage(data.arg); - } } + } - p.should_fold_typescript_constant_expressions = old_should_fold_typescript_constant_expressions; + p.should_fold_typescript_constant_expressions = old_should_fold_typescript_constant_expressions; - var value_stmts = ListManaged(Stmt).initCapacity(allocator, value_exprs.items.len) catch unreachable; - // Generate statements from expressions - for (value_exprs.items) |expr| { - value_stmts.appendAssumeCapacity(p.s(S.SExpr{ .value = expr }, expr.loc)); - } - value_exprs.deinit(); - try p.generateClosureForTypeScriptNamespaceOrEnum( - stmts, - stmt.loc, - data.is_export, - data.name.loc, - data.name.ref.?, - data.arg, - value_stmts.items, - all_values_are_pure, - ); - return; - }, - .s_namespace => |data| { - p.recordDeclaredSymbol(data.name.ref.?) catch unreachable; - - // Scan ahead for any variables inside this namespace. This must be done - // ahead of time before visiting any statements inside the namespace - // because we may end up visiting the uses before the declarations. - // We need to convert the uses into property accesses on the namespace. - for (data.stmts) |child_stmt| { - switch (child_stmt.data) { - .s_local => |local| { - if (local.is_export) { - p.markExportedDeclsInsideNamespace(data.arg, local.decls.slice()); - } - }, - else => {}, - } - } - - var prepend_temp_refs = PrependTempRefsOpts{ .kind = StmtsKind.fn_body }; - var prepend_list = ListManaged(Stmt).fromOwnedSlice(p.allocator, data.stmts); - - const old_enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref; - p.enclosing_namespace_arg_ref = data.arg; - p.pushScopeForVisitPass(.entry, stmt.loc) catch unreachable; - p.recordDeclaredSymbol(data.arg) catch unreachable; - try p.visitStmtsAndPrependTempRefs(&prepend_list, &prepend_temp_refs); - p.popScope(); - p.enclosing_namespace_arg_ref = old_enclosing_namespace_arg_ref; - - try p.generateClosureForTypeScriptNamespaceOrEnum( - stmts, - stmt.loc, - data.is_export, - data.name.loc, - data.name.ref.?, - data.arg, - prepend_list.items, - false, - ); - return; - }, - else => { - notimpl(); - }, + var value_stmts = ListManaged(Stmt).initCapacity(allocator, value_exprs.items.len) catch unreachable; + // Generate statements from expressions + for (value_exprs.items) |expr| { + value_stmts.appendAssumeCapacity(p.s(S.SExpr{ .value = expr }, expr.loc)); + } + value_exprs.deinit(); + try p.generateClosureForTypeScriptNamespaceOrEnum( + stmts, + stmt.loc, + data.is_export, + data.name.loc, + data.name.ref.?, + data.arg, + value_stmts.items, + all_values_are_pure, + ); + return; } + pub fn s_namespace(p: *P, stmts: *ListManaged(Stmt), stmt: *Stmt, data: *S.Namespace) !void { + p.recordDeclaredSymbol(data.name.ref.?) catch unreachable; - // if we get this far, it stays - try stmts.append(stmt.*); - } + // Scan ahead for any variables inside this namespace. This must be done + // ahead of time before visiting any statements inside the namespace + // because we may end up visiting the uses before the declarations. + // We need to convert the uses into property accesses on the namespace. + for (data.stmts) |child_stmt| { + switch (child_stmt.data) { + .s_local => |local| { + if (local.is_export) { + p.markExportedDeclsInsideNamespace(data.arg, local.decls.slice()); + } + }, + else => {}, + } + } + + var prepend_temp_refs = PrependTempRefsOpts{ .kind = StmtsKind.fn_body }; + var prepend_list = ListManaged(Stmt).fromOwnedSlice(p.allocator, data.stmts); + + const old_enclosing_namespace_arg_ref = p.enclosing_namespace_arg_ref; + p.enclosing_namespace_arg_ref = data.arg; + p.pushScopeForVisitPass(.entry, stmt.loc) catch unreachable; + p.recordDeclaredSymbol(data.arg) catch unreachable; + try p.visitStmtsAndPrependTempRefs(&prepend_list, &prepend_temp_refs); + p.popScope(); + p.enclosing_namespace_arg_ref = old_enclosing_namespace_arg_ref; + + try p.generateClosureForTypeScriptNamespaceOrEnum( + stmts, + stmt.loc, + data.is_export, + data.name.loc, + data.name.ref.?, + data.arg, + prepend_list.items, + false, + ); + return; + } + }; fn isExportToEliminate(p: *P, ref: Ref) bool { const symbol_name = p.loadNameFromRef(ref); @@ -21562,20 +21723,24 @@ fn NewParser_( return res; } + fn visitSingleStmtBlock(p: *P, stmt: Stmt, kind: StmtsKind) Stmt { + var new_stmt = stmt; + p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; + var stmts = ListManaged(Stmt).initCapacity(p.allocator, stmt.data.s_block.stmts.len) catch unreachable; + stmts.appendSlice(stmt.data.s_block.stmts) catch unreachable; + p.visitStmts(&stmts, kind) catch unreachable; + p.popScope(); + new_stmt.data.s_block.stmts = stmts.items; + if (p.options.features.minify_syntax) { + new_stmt = p.stmtsToSingleStmt(stmt.loc, stmts.items); + } + + return new_stmt; + } + fn visitSingleStmt(p: *P, stmt: Stmt, kind: StmtsKind) Stmt { if (stmt.data == .s_block) { - var new_stmt = stmt; - p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; - var stmts = ListManaged(Stmt).initCapacity(p.allocator, stmt.data.s_block.stmts.len) catch unreachable; - stmts.appendSlice(stmt.data.s_block.stmts) catch unreachable; - p.visitStmts(&stmts, kind) catch unreachable; - p.popScope(); - new_stmt.data.s_block.stmts = stmts.items; - if (p.options.features.minify_syntax) { - new_stmt = p.stmtsToSingleStmt(stmt.loc, stmts.items); - } - - return new_stmt; + return p.visitSingleStmtBlock(stmt, kind); } const has_if_scope = switch (stmt.data) { @@ -23283,13 +23448,12 @@ fn NewParser_( pub fn toAST( p: *P, - input_parts: []js_ast.Part, + parts: *ListManaged(js_ast.Part), exports_kind: js_ast.ExportsKind, wrap_mode: WrapMode, hashbang: []const u8, ) !js_ast.Ast { const allocator = p.allocator; - var parts = input_parts; // if (p.options.tree_shaking and p.options.features.trim_unused_imports) { // p.treeShake(&parts, false); @@ -23307,20 +23471,20 @@ fn NewParser_( bun.assert(!p.options.tree_shaking); bun.assert(p.options.features.hot_module_reloading); - var hmr_transform_ctx = ConvertESMExportsForHmr{ .last_part = &parts[parts.len - 1] }; + var hmr_transform_ctx = ConvertESMExportsForHmr{ .last_part = &parts.items[parts.items.len - 1] }; try hmr_transform_ctx.stmts.ensureTotalCapacity(p.allocator, prealloc_count: { // get a estimate on how many statements there are going to be var count: usize = 0; - for (parts) |part| count += part.stmts.len; + for (parts.items) |part| count += part.stmts.len; break :prealloc_count count + 2; }); - for (parts) |part| { + for (parts.items) |part| { // Bake does not care about 'import =', as it handles it on it's own _ = try ImportScanner.scan(P, p, part.stmts, wrap_mode != .none, true, &hmr_transform_ctx); } - parts = try hmr_transform_ctx.finalize(p, parts); + try hmr_transform_ctx.finalize(p, parts.items); } else { // Handle import paths after the whole file has been visited because we need // symbol usage counts to be able to remove unused type-only imports in @@ -23332,7 +23496,7 @@ fn NewParser_( const begin = parts_end; // Potentially remove some statements, then filter out parts to remove any // with no statements - for (parts[begin..]) |part_| { + for (parts.items[begin..]) |part_| { var part = part_; p.import_records_for_current_part.clearRetainingCapacity(); p.declared_symbols.clearRetainingCapacity(); @@ -23363,7 +23527,7 @@ fn NewParser_( part.import_record_indices.append(p.allocator, p.import_records_for_current_part.items) catch unreachable; } - parts[parts_end] = part; + parts.items[parts_end] = part; parts_end += 1; } } @@ -23376,11 +23540,11 @@ fn NewParser_( } // leave the first part in there for namespace export when bundling - parts = parts[0..parts_end]; + parts.items.len = parts_end; // Do a second pass for exported items now that imported items are filled out. // This isn't done for HMR because it already deletes all `.s_export_clause`s - for (parts) |part| { + for (parts.items) |part| { for (part.stmts) |stmt| { switch (stmt.data) { .s_export_clause => |clause| { @@ -23418,14 +23582,14 @@ fn NewParser_( } var total_stmts_count: usize = 0; - for (parts) |part| { + for (parts.items) |part| { total_stmts_count += part.stmts.len; } const preserve_strict_mode = p.module_scope.strict_mode == .explicit_strict_mode and - !(parts.len > 0 and - parts[0].stmts.len > 0 and - parts[0].stmts[0].data == .s_directive); + !(parts.items.len > 0 and + parts.items[0].stmts.len > 0 and + parts.items[0].stmts[0].data == .s_directive); total_stmts_count += @as(usize, @intCast(@intFromBool(preserve_strict_mode))); @@ -23442,7 +23606,7 @@ fn NewParser_( remaining_stmts = remaining_stmts[1..]; } - for (parts) |part| { + for (parts.items) |part| { for (part.stmts, remaining_stmts[0..part.stmts.len]) |src, *dest| { dest.* = src; } @@ -23464,14 +23628,16 @@ fn NewParser_( ); var top_level_stmts = p.allocator.alloc(Stmt, 1) catch bun.outOfMemory(); - parts[0].stmts = top_level_stmts; top_level_stmts[0] = p.s( S.SExpr{ .value = wrapper, }, logger.Loc.Empty, ); - parts.len = 1; + + try parts.ensureUnusedCapacity(1); + parts.items.len = 1; + parts.items[0].stmts = top_level_stmts; } var top_level_symbols_to_parts = js_ast.Ast.TopLevelSymbolToParts{}; @@ -23504,7 +23670,7 @@ fn NewParser_( }; // Each part tracks the other parts it depends on within this file - for (parts, 0..) |*part, part_index| { + for (parts.items, 0..) |*part, part_index| { const decls = &part.declared_symbols; const ctx = Ctx{ .allocator = p.allocator, @@ -23530,7 +23696,7 @@ fn NewParser_( } const wrapper_ref: Ref = brk: { - if (p.options.bundle and p.needsWrapperRef(parts)) { + if (p.options.bundle and p.needsWrapperRef(parts.items)) { break :brk p.newSymbol( .other, std.fmt.allocPrint( @@ -23544,8 +23710,7 @@ fn NewParser_( break :brk Ref.None; }; - var parts_list = bun.BabyList(js_ast.Part).init(parts); - parts_list.cap = @intCast(input_parts.len); + const parts_list = bun.BabyList(js_ast.Part).fromList(parts); return .{ .runtime_imports = p.runtime_imports, @@ -23684,6 +23849,7 @@ fn NewParser_( .named_imports = undefined, .named_exports = .{}, .log = log, + .stack_check = bun.StackCheck.init(), .allocator = allocator, .options = opts, .then_catch_chain = ThenCatchChain{ .next_target = nullExprData }, @@ -24106,7 +24272,7 @@ pub const ConvertESMExportsForHmr = struct { } } - pub fn finalize(ctx: *ConvertESMExportsForHmr, p: anytype, all_parts: []js_ast.Part) ![]js_ast.Part { + pub fn finalize(ctx: *ConvertESMExportsForHmr, p: anytype, all_parts: []js_ast.Part) !void { if (ctx.export_props.items.len > 0) { try ctx.stmts.append(p.allocator, Stmt.alloc(S.SExpr, .{ .value = Expr.assign( @@ -24153,8 +24319,6 @@ pub const ConvertESMExportsForHmr = struct { ctx.last_part.stmts = ctx.stmts.items; ctx.last_part.tag = .none; - - return all_parts; } }; diff --git a/src/js_printer.zig b/src/js_printer.zig index 2963b96e65..3116a92532 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -6003,13 +6003,13 @@ pub fn printWithWriterAndPlatform( printer.printFnArgs(func.open_parens_loc, func.args, func.flags.contains(.has_rest_arg), false); printer.printSpace(); printer.print("{\n"); - if (func.body.stmts[0].data.s_lazy_export != .e_undefined) { + if (func.body.stmts[0].data.s_lazy_export.* != .e_undefined) { printer.indent(); printer.printIndent(); printer.printSymbol(printer.options.commonjs_module_ref); printer.print(".exports = "); printer.printExpr(.{ - .data = func.body.stmts[0].data.s_lazy_export, + .data = func.body.stmts[0].data.s_lazy_export.*, .loc = func.body.stmts[0].loc, }, .comma, .{}); printer.print("; // bun .s_lazy_export\n"); diff --git a/src/json_parser.zig b/src/json_parser.zig index 22ef5f4632..47267b62ee 100644 --- a/src/json_parser.zig +++ b/src/json_parser.zig @@ -347,16 +347,14 @@ fn JSONLikeParser_( }; } -// This is a special JSON parser that stops as soon as it finds combinations of +// This is a special JSON parser that stops as soon as it finds // { // "name": "NAME_IN_HERE", // "version": "VERSION_IN_HERE", -// "bin": ... or "directories": { "bin": ... } // } -// and then returns the name, version, and bin -// More precisely, it stops as soon as it finds a top-level "name" and "version" (and/or "bin"). -// In most cases, it should perform zero heap allocations because it does not create arrays or objects (It just skips them). -// If searching for "bin", objects are only created if the key is top level "bin". "bin" within "directories" can only be a string. +// and then returns the name and version. +// More precisely, it stops as soon as it finds a top-level "name" and "version" property which are strings +// In most cases, it should perform zero heap allocations because it does not create arrays or objects (It just skips them) pub const PackageJSONVersionChecker = struct { const Lexer = js_lexer.NewLexer(opts); @@ -371,14 +369,9 @@ pub const PackageJSONVersionChecker = struct { found_name: []const u8 = "", found_version: []const u8 = "", - found_bin: union(enum) { - bin: Expr, - dir: Expr, - } = .{ .bin = Expr.empty }, has_found_name: bool = false, has_found_version: bool = false, - has_found_bin: bool = false, name_loc: logger.Loc = logger.Loc.Empty, @@ -389,24 +382,21 @@ pub const PackageJSONVersionChecker = struct { .allow_comments = true, }; - pub fn init(allocator: std.mem.Allocator, source: *const logger.Source, log: *logger.Log, checks: enum { check_for_bin, ignore_bin, only_bin }) !Parser { + pub fn init(allocator: std.mem.Allocator, source: *const logger.Source, log: *logger.Log) !Parser { return Parser{ .lexer = try Lexer.init(log, source.*, allocator), .allocator = allocator, .log = log, .source = source, - .has_found_bin = checks == .ignore_bin, - .has_found_name = checks == .only_bin, - .has_found_version = checks == .only_bin, }; } const Parser = @This(); - pub fn parseExpr(p: *Parser, collect_props: bool, parent_is_directories: bool) anyerror!Expr { + pub fn parseExpr(p: *Parser) anyerror!Expr { const loc = p.lexer.loc(); - if (p.has_found_name and p.has_found_version and p.has_found_bin) return newExpr(E.Missing{}, loc); + if (p.has_found_name and p.has_found_version) return newExpr(E.Missing{}, loc); switch (p.lexer.token) { .t_false => { @@ -453,7 +443,7 @@ pub const PackageJSONVersionChecker = struct { } } - _ = try p.parseExpr(false, false); + _ = try p.parseExpr(); has_exprs = true; } @@ -465,8 +455,6 @@ pub const PackageJSONVersionChecker = struct { p.depth += 1; defer p.depth -= 1; - var properties = std.ArrayList(G.Property).init(p.allocator); - var has_properties = false; while (p.lexer.token != .t_close_brace) { if (has_properties) { @@ -483,95 +471,40 @@ pub const PackageJSONVersionChecker = struct { try p.lexer.expect(.t_colon); - var collect_prop_props = false; - var is_directories = false; - - if (!p.has_found_bin and - p.depth == 1 and - // next is going to be a top level property - // with an object value. check if it is "bin" - // or "directories" - p.lexer.token == .t_open_brace and - key.data == .e_string) - { - if (strings.eqlComptime(key.data.e_string.data, "bin")) { - collect_prop_props = true; - } else if (strings.eqlComptime(key.data.e_string.data, "directories")) { - is_directories = true; - } - - // if bin is in directories it can only be a string, so - // don't need to set collect_prop_props when depth == 2 - // and in parent_is_directories == true. - - } - - const value = try p.parseExpr(collect_prop_props, is_directories); + const value = try p.parseExpr(); if (p.depth == 1) { // if you have multiple "name" fields in the package.json.... // first one wins - if (key.data == .e_string) { - if (value.data == .e_string) { - if (!p.has_found_name and strings.eqlComptime(key.data.e_string.data, "name")) { - const len = @min( - value.data.e_string.data.len, - p.found_name_buf.len, - ); + if (key.data == .e_string and value.data == .e_string) { + if (!p.has_found_name and strings.eqlComptime(key.data.e_string.data, "name")) { + const len = @min( + value.data.e_string.data.len, + p.found_name_buf.len, + ); - bun.copy(u8, &p.found_name_buf, value.data.e_string.data[0..len]); - p.found_name = p.found_name_buf[0..len]; - p.has_found_name = true; - p.name_loc = value.loc; - } else if (!p.has_found_version and strings.eqlComptime(key.data.e_string.data, "version")) { - const len = @min( - value.data.e_string.data.len, - p.found_version_buf.len, - ); - bun.copy(u8, &p.found_version_buf, value.data.e_string.data[0..len]); - p.found_version = p.found_version_buf[0..len]; - p.has_found_version = true; - } - } - - if (!p.has_found_bin and strings.eqlComptime(key.data.e_string.data, "bin")) { - p.found_bin = .{ - .bin = value, - }; - p.has_found_bin = true; - } - } - } else if (parent_is_directories) { - if (key.data == .e_string) { - if (!p.has_found_bin and strings.eqlComptime(key.data.e_string.data, "bin")) { - p.found_bin = .{ - .dir = value, - }; - p.has_found_bin = true; + bun.copy(u8, &p.found_name_buf, value.data.e_string.data[0..len]); + p.found_name = p.found_name_buf[0..len]; + p.has_found_name = true; + p.name_loc = value.loc; + } else if (!p.has_found_version and strings.eqlComptime(key.data.e_string.data, "version")) { + const len = @min( + value.data.e_string.data.len, + p.found_version_buf.len, + ); + bun.copy(u8, &p.found_version_buf, value.data.e_string.data[0..len]); + p.found_version = p.found_version_buf[0..len]; + p.has_found_version = true; } } } - if (p.has_found_name and p.has_found_version and p.has_found_bin) return newExpr(E.Missing{}, loc); + if (p.has_found_name and p.has_found_version) return newExpr(E.Missing{}, loc); has_properties = true; - if (collect_props) { - properties.append(.{ - .key = key, - .value = value, - .kind = .normal, - .initializer = null, - }) catch bun.outOfMemory(); - } } try p.lexer.expect(.t_close_brace); - - if (collect_props) { - return newExpr(E.Object{ - .properties = G.Property.List.fromList(properties), - }, loc); - } return newExpr(E.Missing{}, loc); }, else => { @@ -1082,8 +1015,8 @@ const js_printer = bun.js_printer; const renamer = @import("renamer.zig"); const SymbolList = [][]Symbol; -const Bundler = bun.Bundler; -const ParseResult = bun.bundler.ParseResult; +const Transpiler = bun.Transpiler; +const ParseResult = bun.transpiler.ParseResult; fn expectPrintedJSON(_contents: string, expected: string) !void { Expr.Data.Store.create(default_allocator); Stmt.Data.Store.create(default_allocator); diff --git a/src/linker.zig b/src/linker.zig index f0136706d5..dcaecf25e8 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -32,15 +32,15 @@ const ImportKind = _import_record.ImportKind; const allocators = @import("./allocators.zig"); const MimeType = @import("./http/mime_type.zig"); const resolve_path = @import("./resolver/resolve_path.zig"); -const _bundler = bun.bundler; -const Bundler = _bundler.Bundler; -const ResolveQueue = _bundler.ResolveQueue; +const _transpiler = bun.transpiler; +const Transpiler = _transpiler.Transpiler; +const ResolveQueue = _transpiler.ResolveQueue; const ResolverType = Resolver.Resolver; const ESModule = @import("./resolver/package_json.zig").ESModule; const Runtime = @import("./runtime.zig").Runtime; const URL = @import("url.zig").URL; const JSC = bun.JSC; -const PluginRunner = bun.bundler.PluginRunner; +const PluginRunner = bun.transpiler.PluginRunner; pub const CSSResolveError = error{ResolveMessage}; pub const OnImportCallback = *const fn (resolve_result: *const Resolver.Result, import_record: *ImportRecord, origin: URL) void; @@ -54,7 +54,7 @@ pub const Linker = struct { log: *logger.Log, resolve_queue: *ResolveQueue, resolver: *ResolverType, - resolve_results: *_bundler.ResolveResults, + resolve_results: *_transpiler.ResolveResults, any_needs_runtime: bool = false, runtime_import_record: ?ImportRecord = null, hashed_filenames: HashedFileNameMap, @@ -80,7 +80,7 @@ pub const Linker = struct { resolve_queue: *ResolveQueue, options: *Options.BundleOptions, resolver: *ResolverType, - resolve_results: *_bundler.ResolveResults, + resolve_results: *_transpiler.ResolveResults, fs: *Fs.FileSystem, ) ThisLinker { relative_paths_list = ImportPathsList.init(allocator); @@ -116,7 +116,7 @@ pub const Linker = struct { file_path: Fs.Path, fd: ?FileDescriptorType, ) !string { - if (Bundler.isCacheEnabled) { + if (Transpiler.isCacheEnabled) { const hashed = bun.hash(file_path.text); const hashed_result = try this.hashed_filenames.getOrPut(hashed); if (hashed_result.found_existing) { @@ -127,7 +127,7 @@ pub const Linker = struct { const modkey = try this.getModKey(file_path, fd); const hash_name = modkey.hashName(file_path.text); - if (Bundler.isCacheEnabled) { + if (Transpiler.isCacheEnabled) { const hashed = bun.hash(file_path.text); try this.hashed_filenames.put(hashed, try this.allocator.dupe(u8, hash_name)); } @@ -183,7 +183,7 @@ pub const Linker = struct { pub fn link( linker: *ThisLinker, file_path: Fs.Path, - result: *_bundler.ParseResult, + result: *_transpiler.ParseResult, origin: URL, comptime import_path_format: Options.BundleOptions.ImportPathFormat, comptime ignore_runtime: bool, @@ -603,7 +603,7 @@ pub const Linker = struct { fn whenModuleNotFound( linker: *ThisLinker, import_record: *ImportRecord, - result: *_bundler.ParseResult, + result: *_transpiler.ParseResult, comptime is_bun: bool, ) !bool { if (import_record.handles_import_errors) { diff --git a/src/linux_c.zig b/src/linux_c.zig index bf99c08271..d29d846da3 100644 --- a/src/linux_c.zig +++ b/src/linux_c.zig @@ -1,5 +1,6 @@ const std = @import("std"); const bun = @import("root").bun; +pub extern "C" fn memmem(haystack: [*]const u8, haystacklen: usize, needle: [*]const u8, needlelen: usize) ?[*]const u8; pub const SystemErrno = enum(u8) { SUCCESS = 0, EPERM = 1, diff --git a/src/logger.zig b/src/logger.zig index 516dd70ed8..d0af8cb71e 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -742,35 +742,15 @@ pub const Log = struct { } /// unlike toJS, this always produces an AggregateError object - pub fn toJSAggregateError(this: Log, global: *JSC.JSGlobalObject, message: []const u8) JSC.JSValue { - const msgs: []const Msg = this.msgs.items; - - // TODO: remove arbitrary upper limit. cannot use the heap because - // values could be GC'd. to do this correctly, expose a binding that - // allows creating an AggregateError using an array - var errors_stack: [256]JSC.JSValue = undefined; - - const count = @min(msgs.len, errors_stack.len); - - for (msgs[0..count], 0..) |msg, i| { - errors_stack[i] = switch (msg.metadata) { - .build => JSC.BuildMessage.create(global, bun.default_allocator, msg), - .resolve => JSC.ResolveMessage.create(global, bun.default_allocator, msg, ""), - }; - } - - const out = JSC.ZigString.init(message); - return global.createAggregateError(errors_stack[0..count], &out); + pub fn toJSAggregateError(this: Log, global: *JSC.JSGlobalObject, message: bun.String) JSC.JSValue { + return global.createAggregateErrorWithArray(message, this.toJSArray(global, bun.default_allocator)); } pub fn toJSArray(this: Log, global: *JSC.JSGlobalObject, allocator: std.mem.Allocator) JSC.JSValue { const msgs: []const Msg = this.msgs.items; - const errors_stack: [256]*anyopaque = undefined; - const count = @as(u16, @intCast(@min(msgs.len, errors_stack.len))); - var arr = JSC.JSValue.createEmptyArray(global, count); - - for (msgs[0..count], 0..) |msg, i| { + const arr = JSC.JSValue.createEmptyArray(global, msgs.len); + for (msgs, 0..) |msg, i| { arr.putIndex(global, @as(u32, @intCast(i)), msg.toJS(global, allocator)); } diff --git a/src/main.zig b/src/main.zig index 9c4df6072f..d3b73b7ee5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -44,7 +44,7 @@ pub fn main() void { if (Environment.isX64 and Environment.enableSIMD and Environment.isPosix) { bun_warn_avx_missing(@import("./cli/upgrade_command.zig").Version.Bun__githubBaselineURL.ptr); } - + bun.StackCheck.configureThread(); bun.CLI.Cli.start(bun.default_allocator); bun.Global.exit(0); } diff --git a/src/napi/napi.zig b/src/napi/napi.zig index c8d735d0b2..ab089d9b6d 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -1032,26 +1032,8 @@ pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*na return .ok; } pub extern fn napi_create_bigint_words(env: napi_env, sign_bit: c_int, word_count: usize, words: [*c]const u64, result: *napi_value) napi_status; -// TODO: lossless -pub export fn napi_get_value_bigint_int64(_: napi_env, value_: napi_value, result_: ?*i64, _: *bool) napi_status { - log("napi_get_value_bigint_int64", .{}); - const result = result_ orelse { - return invalidArg(); - }; - const value = value_.get(); - result.* = value.toInt64(); - return .ok; -} -// TODO: lossless -pub export fn napi_get_value_bigint_uint64(_: napi_env, value_: napi_value, result_: ?*u64, _: *bool) napi_status { - log("napi_get_value_bigint_uint64", .{}); - const result = result_ orelse { - return invalidArg(); - }; - const value = value_.get(); - result.* = value.toUInt64NoTruncate(); - return .ok; -} +pub extern fn napi_get_value_bigint_int64(env: napi_env, value: napi_value, result: ?*i64, lossless: ?*bool) napi_status; +pub extern fn napi_get_value_bigint_uint64(env: napi_env, value: napi_value, result: ?*u64, lossless: ?*bool) napi_status; pub extern fn napi_get_value_bigint_words(env: napi_env, value: napi_value, sign_bit: [*c]c_int, word_count: [*c]usize, words: [*c]u64) napi_status; pub extern fn napi_get_all_property_names(env: napi_env, object: napi_value, key_mode: napi_key_collection_mode, key_filter: napi_key_filter, key_conversion: napi_key_conversion, result: *napi_value) napi_status; diff --git a/src/options.zig b/src/options.zig index 4bccea93e6..63e6582d1a 100644 --- a/src/options.zig +++ b/src/options.zig @@ -644,6 +644,14 @@ pub const Loader = enum(u8) { bunsh, sqlite, sqlite_embedded, + html, + + pub fn disableHTML(this: Loader) Loader { + return switch (this) { + .html => .file, + else => this, + }; + } pub inline fn isSQLite(this: Loader) bool { return switch (this) { @@ -652,28 +660,22 @@ pub const Loader = enum(u8) { }; } - pub fn shouldCopyForBundling(this: Loader, experimental_css: bool) bool { - if (experimental_css) { - return switch (this) { - .file, - .napi, - .sqlite, - .sqlite_embedded, - // TODO: loader for reading bytes and creating module or instance - .wasm, - => true, - else => false, - }; - } + pub const Experimental = struct { + css: bool = bun.FeatureFlags.breaking_changes_1_2, + html: bool = bun.FeatureFlags.breaking_changes_1_2, + }; + + pub fn shouldCopyForBundling(this: Loader, experimental: Experimental) bool { return switch (this) { .file, - .css, .napi, .sqlite, .sqlite_embedded, // TODO: loader for reading bytes and creating module or instance .wasm, => true, + .css => !experimental.css, + .html => !experimental.html, else => false, }; } @@ -684,6 +686,7 @@ pub const Loader = enum(u8) { .css => bun.http.MimeType.css, .toml, .json => bun.http.MimeType.json, .wasm => bun.http.MimeType.wasm, + .html => bun.http.MimeType.html, else => bun.http.MimeType.other, }; } @@ -719,6 +722,7 @@ pub const Loader = enum(u8) { map.set(.napi, "input.node"); map.set(.text, "input.txt"); map.set(.bunsh, "input.sh"); + map.set(.html, "input.html"); break :brk map; }; @@ -764,6 +768,7 @@ pub const Loader = enum(u8) { .{ "sh", .bunsh }, .{ "sqlite", .sqlite }, .{ "sqlite_embedded", .sqlite_embedded }, + .{ "html", .html }, }); pub const api_names = bun.ComptimeStringMap(Api.Loader, .{ @@ -787,6 +792,7 @@ pub const Loader = enum(u8) { .{ "text", .text }, .{ "sh", .file }, .{ "sqlite", .sqlite }, + .{ "html", .html }, }); pub fn fromString(slice_: string) ?Loader { @@ -812,6 +818,7 @@ pub const Loader = enum(u8) { .ts => .ts, .tsx => .tsx, .css => .css, + .html => .html, .file, .bunsh => .file, .json => .json, .toml => .toml, @@ -840,6 +847,7 @@ pub const Loader = enum(u8) { .base64 => .base64, .dataurl => .dataurl, .text => .text, + .html => .html, .sqlite => .sqlite, _ => .file, }; @@ -899,6 +907,7 @@ const default_loaders_posix = .{ .{ ".node", .napi }, .{ ".txt", .text }, .{ ".text", .text }, + .{ ".html", .html }, }; const default_loaders_win32 = default_loaders_posix ++ .{ .{ ".sh", .bunsh }, @@ -909,9 +918,9 @@ pub const defaultLoaders = bun.ComptimeStringMap(Loader, default_loaders); // https://webpack.js.org/guides/package-exports/#reference-syntax pub const ESMConditions = struct { - default: ConditionsMap = undefined, - import: ConditionsMap = undefined, - require: ConditionsMap = undefined, + default: ConditionsMap, + import: ConditionsMap, + require: ConditionsMap, pub fn init(allocator: std.mem.Allocator, defaults: []const string) !ESMConditions { var default_condition_amp = ConditionsMap.init(allocator); @@ -936,22 +945,37 @@ pub const ESMConditions = struct { import_condition_map.putAssumeCapacity("default", {}); require_condition_map.putAssumeCapacity("default", {}); - return ESMConditions{ + return .{ .default = default_condition_amp, .import = import_condition_map, .require = require_condition_map, }; } + pub fn clone(self: *const ESMConditions) !ESMConditions { + var default = try self.default.clone(); + errdefer default.deinit(); + var import = try self.import.clone(); + errdefer import.deinit(); + var require = try self.require.clone(); + errdefer require.deinit(); + + return .{ + .default = default, + .import = import, + .require = require, + }; + } + pub fn appendSlice(self: *ESMConditions, conditions: []const string) !void { try self.default.ensureUnusedCapacity(conditions.len); try self.import.ensureUnusedCapacity(conditions.len); try self.require.ensureUnusedCapacity(conditions.len); for (conditions) |condition| { - self.default.putAssumeCapacityNoClobber(condition, {}); - self.import.putAssumeCapacityNoClobber(condition, {}); - self.require.putAssumeCapacityNoClobber(condition, {}); + self.default.putAssumeCapacity(condition, {}); + self.import.putAssumeCapacity(condition, {}); + self.require.putAssumeCapacity(condition, {}); } } }; @@ -1274,6 +1298,11 @@ const default_loader_ext = [_]string{ ".txt", ".text", }; +// Only set it for browsers by default. +const default_loader_ext_browser = [_]string{ + ".html", +}; + const node_modules_default_loader_ext_bun = [_]string{".node"}; const node_modules_default_loader_ext = [_]string{ ".jsx", @@ -1290,6 +1319,7 @@ const node_modules_default_loader_ext = [_]string{ ".cts", ".wasm", ".text", + ".html", }; pub const ResolveFileExtensions = struct { @@ -1344,6 +1374,12 @@ pub fn loadersFromTransformOptions(allocator: std.mem.Allocator, _loaders: ?Api. } } + if (target == .browser) { + inline for (default_loader_ext_browser) |ext| { + _ = try loaders.getOrPutValue(ext, defaultLoaders.get(ext).?); + } + } + return loaders; } @@ -1498,7 +1534,7 @@ pub const BundleOptions = struct { minify_identifiers: bool = false, dead_code_elimination: bool = true, - experimental_css: bool, + experimental: Loader.Experimental = .{}, css_chunking: bool, ignore_dce_annotations: bool = false, @@ -1683,7 +1719,7 @@ pub const BundleOptions = struct { .out_extensions = undefined, .env = Env.init(allocator), .transform_options = transform, - .experimental_css = false, + .experimental = .{}, .css_chunking = false, .drop = transform.drop, }; @@ -1714,11 +1750,6 @@ pub const BundleOptions = struct { opts.conditions = try ESMConditions.init(allocator, opts.target.defaultConditions()); - if (bun.FeatureFlags.breaking_changes_1_2) { - // This is currently done in DevServer by default, but not in Bun.build - @compileError("if (!production) { add \"development\" condition }"); - } - if (transform.conditions.len > 0) { opts.conditions.appendSlice(transform.conditions) catch bun.outOfMemory(); } diff --git a/src/output.zig b/src/output.zig index ec0b60ac21..29fb3019d2 100644 --- a/src/output.zig +++ b/src/output.zig @@ -88,6 +88,7 @@ pub const Source = struct { if (source_set) return; bun.debugAssert(stdout_stream_set); source = Source.init(stdout_stream, stderr_stream); + bun.StackCheck.configureThread(); } pub fn configureNamedThread(name: StringTypes.stringZ) void { diff --git a/src/patch.zig b/src/patch.zig index 55b5780a3b..363184f47e 100644 --- a/src/patch.zig +++ b/src/patch.zig @@ -915,7 +915,7 @@ const PatchLinesParser = struct { fn parseHunkHeaderLineImpl(text_: []const u8) ParseErr!struct { line_nr: u32, line_count: u32, rest: []const u8 } { var text = text_; const DIGITS = brk: { - var set = std.bit_set.IntegerBitSet(256).initEmpty(); + var set = bun.bit_set.IntegerBitSet(256).initEmpty(); for ('0'..'9' + 1) |c| set.set(c); break :brk set; }; @@ -1026,8 +1026,8 @@ const PatchLinesParser = struct { const delimiter_start = std.mem.indexOf(u8, line, "..") orelse return null; - const VALID_CHARS: std.bit_set.IntegerBitSet(256) = comptime brk: { - var bitset = std.bit_set.IntegerBitSet(256).initEmpty(); + const VALID_CHARS: bun.bit_set.IntegerBitSet(256) = comptime brk: { + var bitset = bun.bit_set.IntegerBitSet(256).initEmpty(); // TODO: the regex uses \w which is [a-zA-Z0-9_] for ('0'..'9' + 1) |c| bitset.set(c); for ('a'..'z' + 1) |c| bitset.set(c); diff --git a/src/pool.zig b/src/pool.zig index 7d994c752f..30e5d3e31f 100644 --- a/src/pool.zig +++ b/src/pool.zig @@ -233,5 +233,21 @@ pub fn ObjectPool( data().list = LinkedList{ .first = node }; data().loaded = true; } + + pub fn deleteAll() void { + var dat = data(); + if (!dat.loaded) { + return; + } + dat.loaded = false; + dat.count = 0; + var next = dat.list.first; + dat.list.first = null; + while (next) |node| { + next = node.next; + if (std.meta.hasFn(Type, "deinit")) node.data.deinit(); + node.allocator.destroy(node); + } + } }; } diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index eaed43a969..f073394c1f 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -2048,7 +2048,7 @@ export fn ResolvePath__joinAbsStringBufCurrentPlatformBunString( defer str.deinit(); const out_slice = joinAbsStringBuf( - globalObject.bunVM().bundler.fs.top_level_dir, + globalObject.bunVM().transpiler.fs.top_level_dir, &join_buf, &.{str.slice()}, comptime Platform.auto.resolve(), diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index c765c9f79b..3ef06e90ee 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -1797,7 +1797,7 @@ pub const Resolver = struct { // check the global cache directory for a package.json file. const manager = r.getPackageManager(); var dependency_version = Dependency.Version{}; - var dependency_behavior = Dependency.Behavior.normal; + var dependency_behavior = Dependency.Behavior.prod; var string_buf = esm.version; // const initial_pending_tasks = manager.pending_tasks; @@ -1939,7 +1939,7 @@ pub const Resolver = struct { .root_request_id = 0, }, null, - ); + ) catch |enqueue_download_err| return .{ .failure = enqueue_download_err }; return .{ .pending = .{ @@ -3307,7 +3307,7 @@ pub const Resolver = struct { const in_str = argument.toBunString(globalThis); defer in_str.deref(); - const r = &globalThis.bunVM().bundler.resolver; + const r = &globalThis.bunVM().transpiler.resolver; return nodeModulePathsJSValue(r, in_str, globalThis); } @@ -3315,7 +3315,7 @@ pub const Resolver = struct { bun.JSC.markBinding(@src()); const in_str = bun.String.createUTF8("."); - const r = &globalThis.bunVM().bundler.resolver; + const r = &globalThis.bunVM().transpiler.resolver; return nodeModulePathsJSValue(r, in_str, globalThis); } diff --git a/src/s3.zig b/src/s3.zig new file mode 100644 index 0000000000..aa12dadd49 --- /dev/null +++ b/src/s3.zig @@ -0,0 +1,2182 @@ +const bun = @import("root").bun; +const picohttp = bun.picohttp; +const std = @import("std"); +const DotEnv = @import("./env_loader.zig"); +pub const RareData = @import("./bun.js/rare_data.zig"); + +const JSC = bun.JSC; +const strings = bun.strings; + +pub const AWSCredentials = struct { + accessKeyId: []const u8, + secretAccessKey: []const u8, + region: []const u8, + endpoint: []const u8, + bucket: []const u8, + + ref_count: u32 = 1, + pub usingnamespace bun.NewRefCounted(@This(), @This().deinit); + + pub fn estimatedSize(this: *const @This()) usize { + return @sizeOf(AWSCredentials) + this.accessKeyId.len + this.region.len + this.secretAccessKey.len + this.endpoint.len + this.bucket.len; + } + + pub const AWSCredentialsWithOptions = struct { + credentials: AWSCredentials, + options: MultiPartUpload.MultiPartUploadOptions = .{}, + + _accessKeyIdSlice: ?JSC.ZigString.Slice = null, + _secretAccessKeySlice: ?JSC.ZigString.Slice = null, + _regionSlice: ?JSC.ZigString.Slice = null, + _endpointSlice: ?JSC.ZigString.Slice = null, + _bucketSlice: ?JSC.ZigString.Slice = null, + + pub fn deinit(this: *@This()) void { + if (this._accessKeyIdSlice) |slice| slice.deinit(); + if (this._secretAccessKeySlice) |slice| slice.deinit(); + if (this._regionSlice) |slice| slice.deinit(); + if (this._endpointSlice) |slice| slice.deinit(); + if (this._bucketSlice) |slice| slice.deinit(); + } + }; + pub fn getCredentialsWithOptions(this: AWSCredentials, options: ?JSC.JSValue, globalObject: *JSC.JSGlobalObject) bun.JSError!AWSCredentialsWithOptions { + // get ENV config + var new_credentials = AWSCredentialsWithOptions{ + .credentials = this, + .options = .{}, + }; + errdefer { + new_credentials.deinit(); + } + + if (options) |opts| { + if (opts.isObject()) { + if (try opts.getTruthyComptime(globalObject, "accessKeyId")) |js_value| { + if (!js_value.isEmptyOrUndefinedOrNull()) { + if (js_value.isString()) { + const str = bun.String.fromJS(js_value, globalObject); + defer str.deref(); + if (str.tag != .Empty and str.tag != .Dead) { + new_credentials._accessKeyIdSlice = str.toUTF8(bun.default_allocator); + new_credentials.credentials.accessKeyId = new_credentials._accessKeyIdSlice.?.slice(); + } + } else { + return globalObject.throwInvalidArgumentTypeValue("accessKeyId", "string", js_value); + } + } + } + if (try opts.getTruthyComptime(globalObject, "secretAccessKey")) |js_value| { + if (!js_value.isEmptyOrUndefinedOrNull()) { + if (js_value.isString()) { + const str = bun.String.fromJS(js_value, globalObject); + defer str.deref(); + if (str.tag != .Empty and str.tag != .Dead) { + new_credentials._secretAccessKeySlice = str.toUTF8(bun.default_allocator); + new_credentials.credentials.secretAccessKey = new_credentials._secretAccessKeySlice.?.slice(); + } + } else { + return globalObject.throwInvalidArgumentTypeValue("secretAccessKey", "string", js_value); + } + } + } + if (try opts.getTruthyComptime(globalObject, "region")) |js_value| { + if (!js_value.isEmptyOrUndefinedOrNull()) { + if (js_value.isString()) { + const str = bun.String.fromJS(js_value, globalObject); + defer str.deref(); + if (str.tag != .Empty and str.tag != .Dead) { + new_credentials._regionSlice = str.toUTF8(bun.default_allocator); + new_credentials.credentials.region = new_credentials._regionSlice.?.slice(); + } + } else { + return globalObject.throwInvalidArgumentTypeValue("region", "string", js_value); + } + } + } + if (try opts.getTruthyComptime(globalObject, "endpoint")) |js_value| { + if (!js_value.isEmptyOrUndefinedOrNull()) { + if (js_value.isString()) { + const str = bun.String.fromJS(js_value, globalObject); + defer str.deref(); + if (str.tag != .Empty and str.tag != .Dead) { + new_credentials._endpointSlice = str.toUTF8(bun.default_allocator); + const normalized_endpoint = bun.URL.parse(new_credentials._endpointSlice.?.slice()).host; + if (normalized_endpoint.len > 0) { + new_credentials.credentials.endpoint = normalized_endpoint; + } + } + } else { + return globalObject.throwInvalidArgumentTypeValue("endpoint", "string", js_value); + } + } + } + if (try opts.getTruthyComptime(globalObject, "bucket")) |js_value| { + if (!js_value.isEmptyOrUndefinedOrNull()) { + if (js_value.isString()) { + const str = bun.String.fromJS(js_value, globalObject); + defer str.deref(); + if (str.tag != .Empty and str.tag != .Dead) { + new_credentials._bucketSlice = str.toUTF8(bun.default_allocator); + new_credentials.credentials.bucket = new_credentials._bucketSlice.?.slice(); + } + } else { + return globalObject.throwInvalidArgumentTypeValue("bucket", "string", js_value); + } + } + } + + if (try opts.getOptional(globalObject, "pageSize", i32)) |pageSize| { + if (pageSize < MultiPartUpload.MIN_SINGLE_UPLOAD_SIZE_IN_MiB and pageSize > MultiPartUpload.MAX_SINGLE_UPLOAD_SIZE_IN_MiB) { + return globalObject.throwRangeError(pageSize, .{ + .min = @intCast(MultiPartUpload.MIN_SINGLE_UPLOAD_SIZE_IN_MiB), + .max = @intCast(MultiPartUpload.MAX_SINGLE_UPLOAD_SIZE_IN_MiB), + .field_name = "pageSize", + }); + } else { + new_credentials.options.partSize = @intCast(pageSize); + } + } + + if (try opts.getOptional(globalObject, "queueSize", i32)) |queueSize| { + if (queueSize < 1) { + return globalObject.throwRangeError(queueSize, .{ + .min = 1, + .field_name = "queueSize", + }); + } else { + new_credentials.options.queueSize = @intCast(@max(queueSize, std.math.maxInt(u8))); + } + } + } + } + return new_credentials; + } + pub fn dupe(this: *const @This()) *AWSCredentials { + return AWSCredentials.new(.{ + .accessKeyId = if (this.accessKeyId.len > 0) + bun.default_allocator.dupe(u8, this.accessKeyId) catch bun.outOfMemory() + else + "", + + .secretAccessKey = if (this.secretAccessKey.len > 0) + bun.default_allocator.dupe(u8, this.secretAccessKey) catch bun.outOfMemory() + else + "", + + .region = if (this.region.len > 0) + bun.default_allocator.dupe(u8, this.region) catch bun.outOfMemory() + else + "", + + .endpoint = if (this.endpoint.len > 0) + bun.default_allocator.dupe(u8, this.endpoint) catch bun.outOfMemory() + else + "", + + .bucket = if (this.bucket.len > 0) + bun.default_allocator.dupe(u8, this.bucket) catch bun.outOfMemory() + else + "", + }); + } + pub fn deinit(this: *@This()) void { + if (this.accessKeyId.len > 0) { + bun.default_allocator.free(this.accessKeyId); + } + if (this.secretAccessKey.len > 0) { + bun.default_allocator.free(this.secretAccessKey); + } + if (this.region.len > 0) { + bun.default_allocator.free(this.region); + } + if (this.endpoint.len > 0) { + bun.default_allocator.free(this.endpoint); + } + if (this.bucket.len > 0) { + bun.default_allocator.free(this.bucket); + } + this.destroy(); + } + + const log = bun.Output.scoped(.AWS, false); + + const DateResult = struct { + // numeric representation of year, month and day (excluding time components) + numeric_day: u64, + date: []const u8, + }; + + fn getAMZDate(allocator: std.mem.Allocator) DateResult { + // We can also use Date.now() but would be slower and would add JSC dependency + // var buffer: [28]u8 = undefined; + // the code bellow is the same as new Date(Date.now()).toISOString() + // JSC.JSValue.getDateNowISOString(globalObject, &buffer); + + // Create UTC timestamp + const secs: u64 = @intCast(@divFloor(std.time.milliTimestamp(), 1000)); + const utc_seconds = std.time.epoch.EpochSeconds{ .secs = secs }; + const utc_day = utc_seconds.getEpochDay(); + const year_and_day = utc_day.calculateYearDay(); + const month_and_day = year_and_day.calculateMonthDay(); + // Get UTC date components + const year = year_and_day.year; + const day = @as(u32, month_and_day.day_index) + 1; // this starts in 0 + const month = month_and_day.month.numeric(); // starts in 1 + + // Get UTC time components + const time = utc_seconds.getDaySeconds(); + const hours = time.getHoursIntoDay(); + const minutes = time.getMinutesIntoHour(); + const seconds = time.getSecondsIntoMinute(); + + // Format the date + return .{ + .numeric_day = secs - time.secs, + .date = std.fmt.allocPrint(allocator, "{d:0>4}{d:0>2}{d:0>2}T{d:0>2}{d:0>2}{d:0>2}Z", .{ + year, + month, + day, + hours, + minutes, + seconds, + }) catch bun.outOfMemory(), + }; + } + + const DIGESTED_HMAC_256_LEN = 32; + pub const SignResult = struct { + amz_date: []const u8, + host: []const u8, + authorization: []const u8, + url: []const u8, + + content_disposition: []const u8, + _headers: [5]picohttp.Header, + _headers_len: u8 = 4, + + pub fn headers(this: *const @This()) []const picohttp.Header { + return this._headers[0..this._headers_len]; + } + + pub fn deinit(this: *const @This()) void { + if (this.amz_date.len > 0) { + bun.default_allocator.free(this.amz_date); + } + + if (this.content_disposition.len > 0) { + bun.default_allocator.free(this.content_disposition); + } + + if (this.host.len > 0) { + bun.default_allocator.free(this.host); + } + + if (this.authorization.len > 0) { + bun.default_allocator.free(this.authorization); + } + + if (this.url.len > 0) { + bun.default_allocator.free(this.url); + } + } + }; + + pub const SignQueryOptions = struct { + expires: usize = 86400, + }; + + pub const SignOptions = struct { + path: []const u8, + method: bun.http.Method, + content_hash: ?[]const u8 = null, + search_params: ?[]const u8 = null, + content_disposition: ?[]const u8 = null, + }; + fn guessRegion(endpoint: []const u8) []const u8 { + if (endpoint.len > 0) { + if (strings.endsWith(endpoint, ".r2.cloudflarestorage.com")) return "auto"; + if (strings.indexOf(endpoint, ".amazonaws.com")) |end| { + if (strings.indexOf(endpoint, "s3.")) |start| { + return endpoint[start + 3 .. end]; + } + } + } + return "us-east-1"; + } + fn toHexChar(value: u8) !u8 { + return switch (value) { + 0...9 => value + '0', + 10...15 => (value - 10) + 'A', + else => error.InvalidHexChar, + }; + } + fn encodeURIComponent(input: []const u8, buffer: []u8) ![]const u8 { + var written: usize = 0; + + for (input) |c| { + switch (c) { + // RFC 3986 Unreserved Characters (do not encode) + 'A'...'Z', 'a'...'z', '0'...'9', '-', '_', '.', '~' => { + if (written >= buffer.len) return error.BufferTooSmall; + buffer[written] = c; + written += 1; + }, + // All other characters need to be percent-encoded + else => { + if (written + 3 > buffer.len) return error.BufferTooSmall; + buffer[written] = '%'; + // Convert byte to hex + const high_nibble: u8 = (c >> 4) & 0xF; + const low_nibble: u8 = c & 0xF; + buffer[written + 1] = try toHexChar(high_nibble); + buffer[written + 2] = try toHexChar(low_nibble); + written += 3; + }, + } + } + + return buffer[0..written]; + } + + const ErrorCodeAndMessage = struct { + code: []const u8, + message: []const u8, + }; + fn getSignErrorMessage(comptime err: anyerror) [:0]const u8 { + return switch (err) { + error.MissingCredentials => return "missing s3 credentials", + error.InvalidMethod => return "method must be GET, PUT, DELETE or HEAD when using s3 protocol", + error.InvalidPath => return "invalid s3 bucket, key combination", + error.InvalidEndpoint => return "invalid s3 endpoint", + else => return "failed to retrieve s3 content check your credentials", + }; + } + pub fn getJSSignError(err: anyerror, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + return switch (err) { + error.MissingCredentials => return globalThis.ERR_AWS_MISSING_CREDENTIALS(getSignErrorMessage(error.MissingCredentials), .{}).toJS(), + error.InvalidMethod => return globalThis.ERR_AWS_INVALID_METHOD(getSignErrorMessage(error.InvalidMethod), .{}).toJS(), + error.InvalidPath => return globalThis.ERR_AWS_INVALID_PATH(getSignErrorMessage(error.InvalidPath), .{}).toJS(), + error.InvalidEndpoint => return globalThis.ERR_AWS_INVALID_ENDPOINT(getSignErrorMessage(error.InvalidEndpoint), .{}).toJS(), + else => return globalThis.ERR_AWS_INVALID_SIGNATURE(getSignErrorMessage(error.SignError), .{}).toJS(), + }; + } + pub fn throwSignError(err: anyerror, globalThis: *JSC.JSGlobalObject) bun.JSError { + return switch (err) { + error.MissingCredentials => globalThis.ERR_AWS_MISSING_CREDENTIALS(getSignErrorMessage(error.MissingCredentials), .{}).throw(), + error.InvalidMethod => globalThis.ERR_AWS_INVALID_METHOD(getSignErrorMessage(error.InvalidMethod), .{}).throw(), + error.InvalidPath => globalThis.ERR_AWS_INVALID_PATH(getSignErrorMessage(error.InvalidPath), .{}).throw(), + error.InvalidEndpoint => globalThis.ERR_AWS_INVALID_ENDPOINT(getSignErrorMessage(error.InvalidEndpoint), .{}).throw(), + else => globalThis.ERR_AWS_INVALID_SIGNATURE(getSignErrorMessage(error.SignError), .{}).throw(), + }; + } + pub fn getSignErrorCodeAndMessage(err: anyerror) ErrorCodeAndMessage { + return switch (err) { + error.MissingCredentials => .{ .code = "MissingCredentials", .message = getSignErrorMessage(error.MissingCredentials) }, + error.InvalidMethod => .{ .code = "InvalidMethod", .message = getSignErrorMessage(error.InvalidMethod) }, + error.InvalidPath => .{ .code = "InvalidPath", .message = getSignErrorMessage(error.InvalidPath) }, + error.InvalidEndpoint => .{ .code = "InvalidEndpoint", .message = getSignErrorMessage(error.InvalidEndpoint) }, + else => .{ .code = "SignError", .message = getSignErrorMessage(error.SignError) }, + }; + } + pub fn signRequest(this: *const @This(), signOptions: SignOptions, signQueryOption: ?SignQueryOptions) !SignResult { + const method = signOptions.method; + const request_path = signOptions.path; + const content_hash = signOptions.content_hash; + const search_params = signOptions.search_params; + + var content_disposition = signOptions.content_disposition; + if (content_disposition != null and content_disposition.?.len == 0) { + content_disposition = null; + } + + if (this.accessKeyId.len == 0 or this.secretAccessKey.len == 0) return error.MissingCredentials; + const signQuery = signQueryOption != null; + const expires = if (signQueryOption) |options| options.expires else 0; + const method_name = switch (method) { + .GET => "GET", + .POST => "POST", + .PUT => "PUT", + .DELETE => "DELETE", + .HEAD => "HEAD", + else => return error.InvalidMethod, + }; + + const region = if (this.region.len > 0) this.region else guessRegion(this.endpoint); + var full_path = request_path; + if (strings.startsWith(full_path, "/")) { + full_path = full_path[1..]; + } + var path: []const u8 = full_path; + var bucket: []const u8 = this.bucket; + + if (bucket.len == 0) { + //TODO: r2 supports bucket in the endpoint + + // guess bucket using path + if (strings.indexOf(full_path, "/")) |end| { + bucket = full_path[0..end]; + path = full_path[end + 1 ..]; + } else { + return error.InvalidPath; + } + } + if (strings.endsWith(path, "/")) { + path = path[0..path.len]; + } + if (strings.startsWith(path, "/")) { + path = path[1..]; + } + + // if we allow path.len == 0 it will list the bucket for now we disallow + if (path.len == 0) return error.InvalidPath; + + var path_buffer: [1024 + 63 + 2]u8 = undefined; // 1024 max key size and 63 max bucket name + + const normalizedPath = std.fmt.bufPrint(&path_buffer, "/{s}/{s}", .{ bucket, path }) catch return error.InvalidPath; + + const date_result = getAMZDate(bun.default_allocator); + const amz_date = date_result.date; + errdefer bun.default_allocator.free(amz_date); + + const amz_day = amz_date[0..8]; + const signed_headers = if (signQuery) "host" else brk: { + if (content_disposition != null) { + break :brk "content-disposition;host;x-amz-content-sha256;x-amz-date"; + } else { + break :brk "host;x-amz-content-sha256;x-amz-date"; + } + }; + // detect service name and host from region or endpoint + var encoded_host_buffer: [512]u8 = undefined; + var encoded_host: []const u8 = ""; + const host = brk_host: { + if (this.endpoint.len > 0) { + encoded_host = encodeURIComponent(this.endpoint, &encoded_host_buffer) catch return error.InvalidEndpoint; + break :brk_host try bun.default_allocator.dupe(u8, this.endpoint); + } else { + break :brk_host try std.fmt.allocPrint(bun.default_allocator, "s3.{s}.amazonaws.com", .{region}); + } + }; + const service_name = "s3"; + + errdefer bun.default_allocator.free(host); + + const aws_content_hash = if (content_hash) |hash| hash else ("UNSIGNED-PAYLOAD"); + var tmp_buffer: [2048]u8 = undefined; + + const authorization = brk: { + // we hash the hash so we need 2 buffers + var hmac_sig_service: [bun.BoringSSL.EVP_MAX_MD_SIZE]u8 = undefined; + var hmac_sig_service2: [bun.BoringSSL.EVP_MAX_MD_SIZE]u8 = undefined; + + const sigDateRegionServiceReq = brk_sign: { + const key = try std.fmt.bufPrint(&tmp_buffer, "{s}{s}{s}", .{ region, service_name, this.secretAccessKey }); + var cache = (JSC.VirtualMachine.getMainThreadVM() orelse JSC.VirtualMachine.get()).rareData().awsCache(); + if (cache.get(date_result.numeric_day, key)) |cached| { + break :brk_sign cached; + } + // not cached yet lets generate a new one + const sigDate = bun.hmac.generate(try std.fmt.bufPrint(&tmp_buffer, "AWS4{s}", .{this.secretAccessKey}), amz_day, .sha256, &hmac_sig_service) orelse return error.FailedToGenerateSignature; + const sigDateRegion = bun.hmac.generate(sigDate, region, .sha256, &hmac_sig_service2) orelse return error.FailedToGenerateSignature; + const sigDateRegionService = bun.hmac.generate(sigDateRegion, service_name, .sha256, &hmac_sig_service) orelse return error.FailedToGenerateSignature; + const result = bun.hmac.generate(sigDateRegionService, "aws4_request", .sha256, &hmac_sig_service2) orelse return error.FailedToGenerateSignature; + + cache.set(date_result.numeric_day, key, hmac_sig_service2[0..DIGESTED_HMAC_256_LEN].*); + break :brk_sign result; + }; + if (signQuery) { + const canonical = try std.fmt.bufPrint(&tmp_buffer, "{s}\n{s}\nX-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential={s}%2F{s}%2F{s}%2F{s}%2Faws4_request&X-Amz-Date={s}&X-Amz-Expires={}&X-Amz-SignedHeaders=host\nhost:{s}\n\n{s}\n{s}", .{ method_name, normalizedPath, this.accessKeyId, amz_day, region, service_name, amz_date, expires, if (encoded_host.len > 0) encoded_host else host, signed_headers, aws_content_hash }); + var sha_digest = std.mem.zeroes(bun.sha.SHA256.Digest); + bun.sha.SHA256.hash(canonical, &sha_digest, JSC.VirtualMachine.get().rareData().boringEngine()); + + const signValue = try std.fmt.bufPrint(&tmp_buffer, "AWS4-HMAC-SHA256\n{s}\n{s}/{s}/{s}/aws4_request\n{s}", .{ amz_date, amz_day, region, service_name, bun.fmt.bytesToHex(sha_digest[0..bun.sha.SHA256.digest], .lower) }); + + const signature = bun.hmac.generate(sigDateRegionServiceReq, signValue, .sha256, &hmac_sig_service) orelse return error.FailedToGenerateSignature; + break :brk try std.fmt.allocPrint( + bun.default_allocator, + "https://{s}{s}?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential={s}%2F{s}%2F{s}%2F{s}%2Faws4_request&X-Amz-Date={s}&X-Amz-Expires={}&X-Amz-SignedHeaders=host&X-Amz-Signature={s}", + .{ host, normalizedPath, this.accessKeyId, amz_day, region, service_name, amz_date, expires, bun.fmt.bytesToHex(signature[0..DIGESTED_HMAC_256_LEN], .lower) }, + ); + } else { + var encoded_content_disposition_buffer: [255]u8 = undefined; + const encoded_content_disposition: []const u8 = if (content_disposition) |cd| encodeURIComponent(cd, &encoded_content_disposition_buffer) catch return error.ContentTypeIsTooLong else ""; + const canonical = brk_canonical: { + if (content_disposition != null) { + break :brk_canonical try std.fmt.bufPrint(&tmp_buffer, "{s}\n{s}\n{s}\ncontent-disposition:{s}\nhost:{s}\nx-amz-content-sha256:{s}\nx-amz-date:{s}\n\n{s}\n{s}", .{ method_name, normalizedPath, if (search_params) |p| p[1..] else "", encoded_content_disposition, if (encoded_host.len > 0) encoded_host else host, aws_content_hash, amz_date, signed_headers, aws_content_hash }); + } else { + break :brk_canonical try std.fmt.bufPrint(&tmp_buffer, "{s}\n{s}\n{s}\nhost:{s}\nx-amz-content-sha256:{s}\nx-amz-date:{s}\n\n{s}\n{s}", .{ method_name, normalizedPath, if (search_params) |p| p[1..] else "", if (encoded_host.len > 0) encoded_host else host, aws_content_hash, amz_date, signed_headers, aws_content_hash }); + } + }; + var sha_digest = std.mem.zeroes(bun.sha.SHA256.Digest); + bun.sha.SHA256.hash(canonical, &sha_digest, JSC.VirtualMachine.get().rareData().boringEngine()); + + const signValue = try std.fmt.bufPrint(&tmp_buffer, "AWS4-HMAC-SHA256\n{s}\n{s}/{s}/{s}/aws4_request\n{s}", .{ amz_date, amz_day, region, service_name, bun.fmt.bytesToHex(sha_digest[0..bun.sha.SHA256.digest], .lower) }); + + const signature = bun.hmac.generate(sigDateRegionServiceReq, signValue, .sha256, &hmac_sig_service) orelse return error.FailedToGenerateSignature; + + break :brk try std.fmt.allocPrint( + bun.default_allocator, + "AWS4-HMAC-SHA256 Credential={s}/{s}/{s}/{s}/aws4_request, SignedHeaders={s}, Signature={s}", + .{ this.accessKeyId, amz_day, region, service_name, signed_headers, bun.fmt.bytesToHex(signature[0..DIGESTED_HMAC_256_LEN], .lower) }, + ); + } + }; + errdefer bun.default_allocator.free(authorization); + + if (signQuery) { + defer bun.default_allocator.free(host); + defer bun.default_allocator.free(amz_date); + + return SignResult{ + .amz_date = "", + .host = "", + .authorization = "", + .url = authorization, + .content_disposition = "", + ._headers = .{ + .{ .name = "", .value = "" }, + .{ .name = "", .value = "" }, + .{ .name = "", .value = "" }, + .{ .name = "", .value = "" }, + .{ .name = "", .value = "" }, + }, + ._headers_len = 0, + }; + } + + if (content_disposition) |cd| { + const content_disposition_value = bun.default_allocator.dupe(u8, cd) catch bun.outOfMemory(); + return SignResult{ + .amz_date = amz_date, + .host = host, + .authorization = authorization, + .url = try std.fmt.allocPrint(bun.default_allocator, "https://{s}{s}{s}", .{ host, normalizedPath, if (search_params) |s| s else "" }), + .content_disposition = content_disposition_value, + ._headers = .{ + .{ .name = "x-amz-content-sha256", .value = aws_content_hash }, + .{ .name = "x-amz-date", .value = amz_date }, + .{ .name = "Authorization", .value = authorization[0..] }, + .{ .name = "Host", .value = host }, + .{ .name = "Content-Disposition", .value = content_disposition_value }, + }, + ._headers_len = 5, + }; + } + return SignResult{ + .amz_date = amz_date, + .host = host, + .authorization = authorization, + .url = try std.fmt.allocPrint(bun.default_allocator, "https://{s}{s}{s}", .{ host, normalizedPath, if (search_params) |s| s else "" }), + .content_disposition = "", + ._headers = .{ + .{ .name = "x-amz-content-sha256", .value = aws_content_hash }, + .{ .name = "x-amz-date", .value = amz_date }, + .{ .name = "Authorization", .value = authorization[0..] }, + .{ .name = "Host", .value = host }, + .{ .name = "", .value = "" }, + }, + ._headers_len = 4, + }; + } + pub const S3Error = struct { + code: []const u8, + message: []const u8, + + pub fn toJS(err: *const @This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { + const js_err = globalObject.createErrorInstance("{s}", .{err.message}); + js_err.put(globalObject, JSC.ZigString.static("code"), JSC.ZigString.init(err.code).toJS(globalObject)); + return js_err; + } + }; + pub const S3StatResult = union(enum) { + success: struct { + size: usize = 0, + /// etag is not owned and need to be copied if used after this callback + etag: []const u8 = "", + }, + not_found: void, + + /// failure error is not owned and need to be copied if used after this callback + failure: S3Error, + }; + pub const S3DownloadResult = union(enum) { + success: struct { + /// etag is not owned and need to be copied if used after this callback + etag: []const u8 = "", + /// body is owned and dont need to be copied, but dont forget to free it + body: bun.MutableString, + }, + not_found: void, + /// failure error is not owned and need to be copied if used after this callback + failure: S3Error, + }; + pub const S3UploadResult = union(enum) { + success: void, + /// failure error is not owned and need to be copied if used after this callback + failure: S3Error, + }; + pub const S3DeleteResult = union(enum) { + success: void, + not_found: void, + + /// failure error is not owned and need to be copied if used after this callback + failure: S3Error, + }; + // commit result also fails if status 200 but with body containing an Error + pub const S3CommitResult = union(enum) { + success: void, + /// failure error is not owned and need to be copied if used after this callback + failure: S3Error, + }; + // commit result also fails if status 200 but with body containing an Error + pub const S3PartResult = union(enum) { + etag: []const u8, + /// failure error is not owned and need to be copied if used after this callback + failure: S3Error, + }; + pub const S3HttpSimpleTask = struct { + http: bun.http.AsyncHTTP, + vm: *JSC.VirtualMachine, + sign_result: SignResult, + headers: JSC.WebCore.Headers, + callback_context: *anyopaque, + callback: Callback, + response_buffer: bun.MutableString = .{ + .allocator = bun.default_allocator, + .list = .{ + .items = &.{}, + .capacity = 0, + }, + }, + result: bun.http.HTTPClientResult = .{}, + concurrent_task: JSC.ConcurrentTask = .{}, + range: ?[]const u8, + poll_ref: bun.Async.KeepAlive = bun.Async.KeepAlive.init(), + + usingnamespace bun.New(@This()); + pub const Callback = union(enum) { + stat: *const fn (S3StatResult, *anyopaque) void, + download: *const fn (S3DownloadResult, *anyopaque) void, + upload: *const fn (S3UploadResult, *anyopaque) void, + delete: *const fn (S3DeleteResult, *anyopaque) void, + commit: *const fn (S3CommitResult, *anyopaque) void, + part: *const fn (S3PartResult, *anyopaque) void, + + pub fn fail(this: @This(), code: []const u8, message: []const u8, context: *anyopaque) void { + switch (this) { + inline .upload, + .download, + .stat, + .delete, + .commit, + .part, + => |callback| callback(.{ + .failure = .{ + .code = code, + .message = message, + }, + }, context), + } + } + }; + pub fn deinit(this: *@This()) void { + if (this.result.certificate_info) |*certificate| { + certificate.deinit(bun.default_allocator); + } + this.poll_ref.unref(this.vm); + this.response_buffer.deinit(); + this.headers.deinit(); + this.sign_result.deinit(); + this.http.clearData(); + if (this.range) |range| { + bun.default_allocator.free(range); + } + if (this.result.metadata) |*metadata| { + metadata.deinit(bun.default_allocator); + } + this.destroy(); + } + + fn fail(this: *@This()) void { + var code: []const u8 = "UnknownError"; + var message: []const u8 = "an unexpected error has occurred"; + if (this.result.fail) |err| { + code = @errorName(err); + } else if (this.result.body) |body| { + const bytes = body.list.items; + if (bytes.len > 0) { + message = bytes[0..]; + if (strings.indexOf(bytes, "")) |start| { + if (strings.indexOf(bytes, "")) |end| { + code = bytes[start + "".len .. end]; + } + } + if (strings.indexOf(bytes, "")) |start| { + if (strings.indexOf(bytes, "")) |end| { + message = bytes[start + "".len .. end]; + } + } + } + } + this.callback.fail(code, message, this.callback_context); + } + + fn failIfContainsError(this: *@This(), status: u32) bool { + var code: []const u8 = "UnknownError"; + var message: []const u8 = "an unexpected error has occurred"; + + if (this.result.fail) |err| { + code = @errorName(err); + } else if (this.result.body) |body| { + const bytes = body.list.items; + var has_error = false; + if (bytes.len > 0) { + message = bytes[0..]; + if (strings.indexOf(bytes, "") != null) { + has_error = true; + if (strings.indexOf(bytes, "")) |start| { + if (strings.indexOf(bytes, "")) |end| { + code = bytes[start + "".len .. end]; + } + } + if (strings.indexOf(bytes, "")) |start| { + if (strings.indexOf(bytes, "")) |end| { + message = bytes[start + "".len .. end]; + } + } + } + } + if (!has_error and status == 200 or status == 206) { + return false; + } + } else if (status == 200 or status == 206) { + return false; + } + this.callback.fail(code, message, this.callback_context); + return true; + } + + pub fn onResponse(this: *@This()) void { + defer this.deinit(); + if (!this.result.isSuccess()) { + this.fail(); + return; + } + bun.assert(this.result.metadata != null); + const response = this.result.metadata.?.response; + switch (this.callback) { + .stat => |callback| { + switch (response.status_code) { + 404 => { + callback(.{ .not_found = {} }, this.callback_context); + }, + 200 => { + callback(.{ + .success = .{ + .etag = response.headers.get("etag") orelse "", + .size = if (response.headers.get("content-length")) |content_len| (std.fmt.parseInt(usize, content_len, 10) catch 0) else 0, + }, + }, this.callback_context); + }, + else => { + this.fail(); + }, + } + }, + .delete => |callback| { + switch (response.status_code) { + 404 => { + callback(.{ .not_found = {} }, this.callback_context); + }, + 200, 204 => { + callback(.{ .success = {} }, this.callback_context); + }, + else => { + this.fail(); + }, + } + }, + .upload => |callback| { + switch (response.status_code) { + 200 => { + callback(.{ .success = {} }, this.callback_context); + }, + else => { + this.fail(); + }, + } + }, + .download => |callback| { + switch (response.status_code) { + 404 => { + callback(.{ .not_found = {} }, this.callback_context); + }, + 200, 204, 206 => { + const body = this.response_buffer; + this.response_buffer = .{ + .allocator = bun.default_allocator, + .list = .{ + .items = &.{}, + .capacity = 0, + }, + }; + callback(.{ + .success = .{ + .etag = response.headers.get("etag") orelse "", + .body = body, + }, + }, this.callback_context); + }, + else => { + //error + this.fail(); + }, + } + }, + .commit => |callback| { + // commit multipart upload can fail with status 200 + if (!this.failIfContainsError(response.status_code)) { + callback(.{ .success = {} }, this.callback_context); + } + }, + .part => |callback| { + if (!this.failIfContainsError(response.status_code)) { + if (response.headers.get("etag")) |etag| { + callback(.{ .etag = etag }, this.callback_context); + } else { + this.fail(); + } + } + }, + } + } + + pub fn http_callback(this: *@This(), async_http: *bun.http.AsyncHTTP, result: bun.http.HTTPClientResult) void { + const is_done = !result.has_more; + this.result = result; + this.http = async_http.*; + this.response_buffer = async_http.response_buffer.*; + if (is_done) { + this.vm.eventLoop().enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); + } + } + }; + + pub const S3HttpDownloadStreamingTask = struct { + http: bun.http.AsyncHTTP, + vm: *JSC.VirtualMachine, + sign_result: SignResult, + headers: JSC.WebCore.Headers, + callback_context: *anyopaque, + // this transfers ownership from the chunk + callback: *const fn (chunk: bun.MutableString, has_more: bool, err: ?S3Error, *anyopaque) void, + has_schedule_callback: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), + signal_store: bun.http.Signals.Store = .{}, + signals: bun.http.Signals = .{}, + poll_ref: bun.Async.KeepAlive = bun.Async.KeepAlive.init(), + + response_buffer: bun.MutableString = .{ + .allocator = bun.default_allocator, + .list = .{ + .items = &.{}, + .capacity = 0, + }, + }, + reported_response_lock: bun.Lock = .{}, + reported_response_buffer: bun.MutableString = .{ + .allocator = bun.default_allocator, + .list = .{ + .items = &.{}, + .capacity = 0, + }, + }, + state: State.AtomicType = State.AtomicType.init(0), + + concurrent_task: JSC.ConcurrentTask = .{}, + range: ?[]const u8, + proxy_url: []const u8, + + usingnamespace bun.New(@This()); + pub const State = packed struct(u64) { + pub const AtomicType = std.atomic.Value(u64); + status_code: u32 = 0, + request_error: u16 = 0, + has_more: bool = false, + _reserved: u15 = 0, + }; + + pub fn getState(this: @This()) State { + const state: State = @bitCast(this.state.load(.acquire)); + return state; + } + + pub fn setState(this: *@This(), state: State) void { + this.state.store(@bitCast(state), .monotonic); + } + + pub fn deinit(this: *@This()) void { + this.poll_ref.unref(this.vm); + this.response_buffer.deinit(); + this.reported_response_buffer.deinit(); + this.headers.deinit(); + this.sign_result.deinit(); + this.http.clearData(); + if (this.range) |range| { + bun.default_allocator.free(range); + } + if (this.proxy_url.len > 0) { + bun.default_allocator.free(this.proxy_url); + } + + this.destroy(); + } + + fn reportProgress(this: *@This()) bool { + var has_more = true; + var err: ?S3Error = null; + var failed = false; + this.reported_response_lock.lock(); + defer this.reported_response_lock.unlock(); + const chunk = brk: { + const state = this.getState(); + has_more = state.has_more; + switch (state.status_code) { + 200, 204, 206 => { + failed = state.request_error != 0; + }, + else => { + failed = true; + }, + } + if (failed) { + if (!has_more) { + var has_body_code = false; + var has_body_message = false; + + var code: []const u8 = "UnknownError"; + var message: []const u8 = "an unexpected error has occurred"; + if (state.request_error != 0) { + const req_err = @errorFromInt(state.request_error); + code = @errorName(req_err); + has_body_code = true; + } else { + const bytes = this.reported_response_buffer.list.items; + if (bytes.len > 0) { + message = bytes[0..]; + + if (strings.indexOf(bytes, "")) |start| { + if (strings.indexOf(bytes, "")) |end| { + code = bytes[start + "".len .. end]; + has_body_code = true; + } + } + if (strings.indexOf(bytes, "")) |start| { + if (strings.indexOf(bytes, "")) |end| { + message = bytes[start + "".len .. end]; + has_body_message = true; + } + } + } + } + if (state.status_code == 404) { + if (!has_body_code) { + code = "FileNotFound"; + } + if (!has_body_message) { + message = "File not found"; + } + } + err = .{ + .code = code, + .message = message, + }; + } + break :brk bun.MutableString{ .allocator = bun.default_allocator, .list = .{} }; + } else { + const buffer = this.reported_response_buffer; + break :brk buffer; + } + }; + log("reportProgres failed: {} has_more: {} len: {d}", .{ failed, has_more, chunk.list.items.len }); + if (failed) { + if (!has_more) { + this.callback(chunk, false, err, this.callback_context); + } + } else { + // dont report empty chunks if we have more data to read + if (!has_more or chunk.list.items.len > 0) { + this.callback(chunk, has_more, null, this.callback_context); + this.reported_response_buffer.reset(); + } + } + + return has_more; + } + + pub fn onResponse(this: *@This()) void { + this.has_schedule_callback.store(false, .monotonic); + const has_more = this.reportProgress(); + if (!has_more) this.deinit(); + } + + pub fn http_callback(this: *@This(), async_http: *bun.http.AsyncHTTP, result: bun.http.HTTPClientResult) void { + const is_done = !result.has_more; + var state = this.getState(); + + var wait_until_done = false; + { + state.has_more = !is_done; + + state.request_error = if (result.fail) |err| @intFromError(err) else 0; + if (state.status_code == 0) { + if (result.certificate_info) |*certificate| { + certificate.deinit(bun.default_allocator); + } + if (result.metadata) |m| { + var metadata = m; + state.status_code = metadata.response.status_code; + metadata.deinit(bun.default_allocator); + } + } + switch (state.status_code) { + 200, 204, 206 => wait_until_done = state.request_error != 0, + else => wait_until_done = true, + } + this.setState(state); + this.http = async_http.*; + } + // if we got a error or fail wait until we are done buffering the response body to report + const should_enqueue = !wait_until_done or is_done; + log("state err: {} status_code: {} has_more: {} should_enqueue: {}", .{ state.request_error, state.status_code, state.has_more, should_enqueue }); + if (should_enqueue) { + if (result.body) |body| { + this.reported_response_lock.lock(); + defer this.reported_response_lock.unlock(); + this.response_buffer = body.*; + if (body.list.items.len > 0) { + _ = this.reported_response_buffer.write(body.list.items) catch bun.outOfMemory(); + } + this.response_buffer.reset(); + if (this.reported_response_buffer.list.items.len == 0 and !is_done) { + return; + } + } else if (!is_done) { + return; + } + if (this.has_schedule_callback.cmpxchgStrong(false, true, .acquire, .monotonic)) |has_schedule_callback| { + if (has_schedule_callback) { + return; + } + } + this.vm.eventLoop().enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); + } + } + }; + + pub const S3SimpleRequestOptions = struct { + // signing options + path: []const u8, + method: bun.http.Method, + search_params: ?[]const u8 = null, + content_type: ?[]const u8 = null, + content_disposition: ?[]const u8 = null, + + // http request options + body: []const u8, + proxy_url: ?[]const u8 = null, + range: ?[]const u8 = null, + }; + + pub fn executeSimpleS3Request( + this: *const @This(), + options: S3SimpleRequestOptions, + callback: S3HttpSimpleTask.Callback, + callback_context: *anyopaque, + ) void { + var result = this.signRequest(.{ + .path = options.path, + .method = options.method, + .search_params = options.search_params, + .content_disposition = options.content_disposition, + }, null) catch |sign_err| { + if (options.range) |range_| bun.default_allocator.free(range_); + const error_code_and_message = getSignErrorCodeAndMessage(sign_err); + callback.fail(error_code_and_message.code, error_code_and_message.message, callback_context); + return; + }; + + const headers = brk: { + if (options.range) |range_| { + const _headers = result.headers(); + var headersWithRange: [5]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + .{ .name = "range", .value = range_ }, + }; + break :brk JSC.WebCore.Headers.fromPicoHttpHeaders(&headersWithRange, bun.default_allocator) catch bun.outOfMemory(); + } else { + if (options.content_type) |content_type| { + if (content_type.len > 0) { + const _headers = result.headers(); + if (_headers.len > 4) { + var headersWithContentType: [6]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + _headers[4], + .{ .name = "Content-Type", .value = content_type }, + }; + break :brk JSC.WebCore.Headers.fromPicoHttpHeaders(&headersWithContentType, bun.default_allocator) catch bun.outOfMemory(); + } + + var headersWithContentType: [5]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + .{ .name = "Content-Type", .value = content_type }, + }; + break :brk JSC.WebCore.Headers.fromPicoHttpHeaders(&headersWithContentType, bun.default_allocator) catch bun.outOfMemory(); + } + } + + break :brk JSC.WebCore.Headers.fromPicoHttpHeaders(result.headers(), bun.default_allocator) catch bun.outOfMemory(); + } + }; + const task = S3HttpSimpleTask.new(.{ + .http = undefined, + .sign_result = result, + .callback_context = callback_context, + .callback = callback, + .range = options.range, + .headers = headers, + .vm = JSC.VirtualMachine.get(), + }); + task.poll_ref.ref(task.vm); + + const url = bun.URL.parse(result.url); + const proxy = options.proxy_url orelse ""; + task.http = bun.http.AsyncHTTP.init( + bun.default_allocator, + options.method, + url, + task.headers.entries, + task.headers.buf.items, + &task.response_buffer, + options.body, + bun.http.HTTPClientResult.Callback.New( + *S3HttpSimpleTask, + S3HttpSimpleTask.http_callback, + ).init(task), + .follow, + .{ + .http_proxy = if (proxy.len > 0) bun.URL.parse(proxy) else null, + .verbose = .none, + .reject_unauthorized = task.vm.getTLSRejectUnauthorized(), + }, + ); + // queue http request + bun.http.HTTPThread.init(&.{}); + var batch = bun.ThreadPool.Batch{}; + task.http.schedule(bun.default_allocator, &batch); + bun.http.http_thread.schedule(batch); + } + + pub fn s3Stat(this: *const @This(), path: []const u8, callback: *const fn (S3StatResult, *anyopaque) void, callback_context: *anyopaque, proxy_url: ?[]const u8) void { + this.executeSimpleS3Request(.{ + .path = path, + .method = .HEAD, + .proxy_url = proxy_url, + .body = "", + }, .{ .stat = callback }, callback_context); + } + + pub fn s3Download(this: *const @This(), path: []const u8, callback: *const fn (S3DownloadResult, *anyopaque) void, callback_context: *anyopaque, proxy_url: ?[]const u8) void { + this.executeSimpleS3Request(.{ + .path = path, + .method = .GET, + .proxy_url = proxy_url, + .body = "", + }, .{ .download = callback }, callback_context); + } + + pub fn s3DownloadSlice(this: *const @This(), path: []const u8, offset: usize, size: ?usize, callback: *const fn (S3DownloadResult, *anyopaque) void, callback_context: *anyopaque, proxy_url: ?[]const u8) void { + const range = brk: { + if (size) |size_| { + if (offset == 0) break :brk null; + + var end = (offset + size_); + if (size_ > 0) { + end -= 1; + } + break :brk std.fmt.allocPrint(bun.default_allocator, "bytes={}-{}", .{ offset, end }) catch bun.outOfMemory(); + } + if (offset == 0) break :brk null; + break :brk std.fmt.allocPrint(bun.default_allocator, "bytes={}-", .{offset}) catch bun.outOfMemory(); + }; + + this.executeSimpleS3Request(.{ + .path = path, + .method = .GET, + .proxy_url = proxy_url, + .body = "", + .range = range, + }, .{ .download = callback }, callback_context); + } + + pub fn s3StreamDownload(this: *@This(), path: []const u8, offset: usize, size: ?usize, proxy_url: ?[]const u8, callback: *const fn (chunk: bun.MutableString, has_more: bool, err: ?S3Error, *anyopaque) void, callback_context: *anyopaque) void { + const range = brk: { + if (size) |size_| { + if (offset == 0) break :brk null; + + var end = (offset + size_); + if (size_ > 0) { + end -= 1; + } + break :brk std.fmt.allocPrint(bun.default_allocator, "bytes={}-{}", .{ offset, end }) catch bun.outOfMemory(); + } + if (offset == 0) break :brk null; + break :brk std.fmt.allocPrint(bun.default_allocator, "bytes={}-", .{offset}) catch bun.outOfMemory(); + }; + + var result = this.signRequest(.{ + .path = path, + .method = .GET, + }, null) catch |sign_err| { + if (range) |range_| bun.default_allocator.free(range_); + const error_code_and_message = getSignErrorCodeAndMessage(sign_err); + callback(.{ .allocator = bun.default_allocator, .list = .{} }, false, .{ .code = error_code_and_message.code, .message = error_code_and_message.message }, callback_context); + return; + }; + + const headers = brk: { + if (range) |range_| { + const _headers = result.headers(); + var headersWithRange: [5]picohttp.Header = .{ + _headers[0], + _headers[1], + _headers[2], + _headers[3], + .{ .name = "range", .value = range_ }, + }; + break :brk JSC.WebCore.Headers.fromPicoHttpHeaders(&headersWithRange, bun.default_allocator) catch bun.outOfMemory(); + } else { + break :brk JSC.WebCore.Headers.fromPicoHttpHeaders(result.headers(), bun.default_allocator) catch bun.outOfMemory(); + } + }; + const proxy = proxy_url orelse ""; + const owned_proxy = if (proxy.len > 0) bun.default_allocator.dupe(u8, proxy) catch bun.outOfMemory() else ""; + const task = S3HttpDownloadStreamingTask.new(.{ + .http = undefined, + .sign_result = result, + .proxy_url = owned_proxy, + .callback_context = callback_context, + .callback = callback, + .range = range, + .headers = headers, + .vm = JSC.VirtualMachine.get(), + }); + task.poll_ref.ref(task.vm); + + const url = bun.URL.parse(result.url); + + task.signals = task.signal_store.to(); + + task.http = bun.http.AsyncHTTP.init( + bun.default_allocator, + .GET, + url, + task.headers.entries, + task.headers.buf.items, + &task.response_buffer, + "", + bun.http.HTTPClientResult.Callback.New( + *S3HttpDownloadStreamingTask, + S3HttpDownloadStreamingTask.http_callback, + ).init(task), + .follow, + .{ + .http_proxy = if (owned_proxy.len > 0) bun.URL.parse(owned_proxy) else null, + .verbose = .none, + .signals = task.signals, + .reject_unauthorized = task.vm.getTLSRejectUnauthorized(), + }, + ); + // enable streaming + task.http.enableBodyStreaming(); + // queue http request + bun.http.HTTPThread.init(&.{}); + var batch = bun.ThreadPool.Batch{}; + task.http.schedule(bun.default_allocator, &batch); + bun.http.http_thread.schedule(batch); + } + + pub fn s3ReadableStream(this: *@This(), path: []const u8, offset: usize, size: ?usize, proxy_url: ?[]const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var reader = JSC.WebCore.ByteStream.Source.new(.{ + .context = undefined, + .globalThis = globalThis, + }); + + reader.context.setup(); + const readable_value = reader.toReadableStream(globalThis); + + this.s3StreamDownload(path, offset, size, proxy_url, @ptrCast(&S3DownloadStreamWrapper.callback), S3DownloadStreamWrapper.new(.{ + .readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(.{ + .ptr = .{ .Bytes = &reader.context }, + .value = readable_value, + }, globalThis), + })); + return readable_value; + } + + const S3DownloadStreamWrapper = struct { + readable_stream_ref: JSC.WebCore.ReadableStream.Strong, + pub usingnamespace bun.New(@This()); + + pub fn callback(chunk: bun.MutableString, has_more: bool, request_err: ?S3Error, this: *@This()) void { + defer if (!has_more) this.deinit(); + + if (this.readable_stream_ref.get()) |readable| { + if (readable.ptr == .Bytes) { + const globalThis = this.readable_stream_ref.globalThis().?; + + if (request_err) |err| { + log("S3DownloadStreamWrapper.callback .temporary", .{}); + + readable.ptr.Bytes.onData( + .{ + .err = .{ .JSValue = err.toJS(globalThis) }, + }, + bun.default_allocator, + ); + return; + } + if (has_more) { + log("S3DownloadStreamWrapper.callback .temporary", .{}); + + readable.ptr.Bytes.onData( + .{ + .temporary = bun.ByteList.initConst(chunk.list.items), + }, + bun.default_allocator, + ); + return; + } + log("S3DownloadStreamWrapper.callback .temporary_and_done", .{}); + + readable.ptr.Bytes.onData( + .{ + .temporary_and_done = bun.ByteList.initConst(chunk.list.items), + }, + bun.default_allocator, + ); + return; + } + } + log("S3DownloadStreamWrapper.callback invalid readable stream", .{}); + } + + pub fn deinit(this: *@This()) void { + this.readable_stream_ref.deinit(); + this.destroy(); + } + }; + + pub fn s3Delete(this: *const @This(), path: []const u8, callback: *const fn (S3DeleteResult, *anyopaque) void, callback_context: *anyopaque, proxy_url: ?[]const u8) void { + this.executeSimpleS3Request(.{ + .path = path, + .method = .DELETE, + .proxy_url = proxy_url, + .body = "", + }, .{ .delete = callback }, callback_context); + } + + pub fn s3Upload(this: *const @This(), path: []const u8, content: []const u8, content_type: ?[]const u8, proxy_url: ?[]const u8, callback: *const fn (S3UploadResult, *anyopaque) void, callback_context: *anyopaque) void { + this.executeSimpleS3Request(.{ + .path = path, + .method = .PUT, + .proxy_url = proxy_url, + .body = content, + .content_type = content_type, + }, .{ .upload = callback }, callback_context); + } + + const S3UploadStreamWrapper = struct { + readable_stream_ref: JSC.WebCore.ReadableStream.Strong, + sink: *JSC.WebCore.FetchTaskletChunkedRequestSink, + callback: ?*const fn (S3UploadResult, *anyopaque) void, + callback_context: *anyopaque, + ref_count: u32 = 1, + pub usingnamespace bun.NewRefCounted(@This(), @This().deinit); + pub fn resolve(result: S3UploadResult, self: *@This()) void { + const sink = self.sink; + defer self.deref(); + + if (sink.endPromise.globalObject()) |globalObject| { + switch (result) { + .success => sink.endPromise.resolve(globalObject, JSC.jsNumber(0)), + .failure => |err| { + if (!sink.done) { + sink.abort(); + return; + } + sink.endPromise.rejectOnNextTick(globalObject, err.toJS(globalObject)); + }, + } + } + if (self.callback) |callback| { + callback(result, self.callback_context); + } + } + + pub fn deinit(self: *@This()) void { + self.readable_stream_ref.deinit(); + self.sink.finalize(); + self.sink.destroy(); + self.destroy(); + } + }; + pub fn onUploadStreamResolveRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + var args = callframe.arguments_old(2); + var this = args.ptr[args.len - 1].asPromisePtr(S3UploadStreamWrapper); + defer this.deref(); + if (this.sink.endPromise.hasValue()) { + this.sink.endPromise.resolve(globalThis, JSC.jsNumber(0)); + } + if (this.readable_stream_ref.get()) |stream| { + stream.done(globalThis); + } + this.readable_stream_ref.deinit(); + + return .undefined; + } + + pub fn onUploadStreamRejectRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const args = callframe.arguments_old(2); + var this = args.ptr[args.len - 1].asPromisePtr(S3UploadStreamWrapper); + defer this.deref(); + const err = args.ptr[0]; + if (this.sink.endPromise.hasValue()) { + this.sink.endPromise.rejectOnNextTick(globalThis, err); + } + + if (this.readable_stream_ref.get()) |stream| { + stream.cancel(globalThis); + this.readable_stream_ref.deinit(); + } + if (this.sink.task) |task| { + if (task == .s3_upload) { + task.s3_upload.fail(.{ + .code = "UnknownError", + .message = "ReadableStream ended with an error", + }); + } + } + return .undefined; + } + pub const shim = JSC.Shimmer("Bun", "S3UploadStream", @This()); + + pub const Export = shim.exportFunctions(.{ + .onResolveRequestStream = onUploadStreamResolveRequestStream, + .onRejectRequestStream = onUploadStreamRejectRequestStream, + }); + comptime { + const jsonResolveRequestStream = JSC.toJSHostFunction(onUploadStreamResolveRequestStream); + @export(jsonResolveRequestStream, .{ .name = Export[0].symbol_name }); + const jsonRejectRequestStream = JSC.toJSHostFunction(onUploadStreamRejectRequestStream); + @export(jsonRejectRequestStream, .{ .name = Export[1].symbol_name }); + } + + /// consumes the readable stream and upload to s3 + pub fn s3UploadStream(this: *@This(), path: []const u8, readable_stream: JSC.WebCore.ReadableStream, globalThis: *JSC.JSGlobalObject, options: MultiPartUpload.MultiPartUploadOptions, content_type: ?[]const u8, proxy: ?[]const u8, callback: ?*const fn (S3UploadResult, *anyopaque) void, callback_context: *anyopaque) JSC.JSValue { + this.ref(); // ref the credentials + const proxy_url = (proxy orelse ""); + + const task = MultiPartUpload.new(.{ + .credentials = this, + .path = bun.default_allocator.dupe(u8, path) catch bun.outOfMemory(), + .proxy = if (proxy_url.len > 0) bun.default_allocator.dupe(u8, proxy_url) catch bun.outOfMemory() else "", + .content_type = if (content_type) |ct| bun.default_allocator.dupe(u8, ct) catch bun.outOfMemory() else null, + .callback = @ptrCast(&S3UploadStreamWrapper.resolve), + .callback_context = undefined, + .globalThis = globalThis, + .options = options, + .vm = JSC.VirtualMachine.get(), + }); + + task.poll_ref.ref(task.vm); + + task.ref(); // + 1 for the stream + + var response_stream = JSC.WebCore.FetchTaskletChunkedRequestSink.new(.{ + .task = .{ .s3_upload = task }, + .buffer = .{}, + .globalThis = globalThis, + .encoded = false, + .endPromise = JSC.JSPromise.Strong.init(globalThis), + }).toSink(); + const endPromise = response_stream.sink.endPromise.value(); + const ctx = S3UploadStreamWrapper.new(.{ + .readable_stream_ref = JSC.WebCore.ReadableStream.Strong.init(readable_stream, globalThis), + .sink = &response_stream.sink, + .callback = callback, + .callback_context = callback_context, + }); + task.callback_context = @ptrCast(ctx); + var signal = &response_stream.sink.signal; + + signal.* = JSC.WebCore.FetchTaskletChunkedRequestSink.JSSink.SinkSignal.init(.zero); + + // explicitly set it to a dead pointer + // we use this memory address to disable signals being sent + signal.clear(); + bun.assert(signal.isDead()); + + // We are already corked! + const assignment_result: JSC.JSValue = JSC.WebCore.FetchTaskletChunkedRequestSink.JSSink.assignToStream( + globalThis, + readable_stream.value, + response_stream, + @as(**anyopaque, @ptrCast(&signal.ptr)), + ); + + assignment_result.ensureStillAlive(); + + // assert that it was updated + bun.assert(!signal.isDead()); + + if (assignment_result.toError()) |err| { + readable_stream.cancel(globalThis); + if (response_stream.sink.endPromise.hasValue()) { + response_stream.sink.endPromise.rejectOnNextTick(globalThis, err); + } + task.fail(.{ + .code = "UnknownError", + .message = "ReadableStream ended with an error", + }); + return endPromise; + } + + if (!assignment_result.isEmptyOrUndefinedOrNull()) { + task.vm.drainMicrotasks(); + + assignment_result.ensureStillAlive(); + // it returns a Promise when it goes through ReadableStreamDefaultReader + if (assignment_result.asAnyPromise()) |promise| { + switch (promise.status(globalThis.vm())) { + .pending => { + ctx.ref(); + assignment_result.then( + globalThis, + task.callback_context, + onUploadStreamResolveRequestStream, + onUploadStreamRejectRequestStream, + ); + }, + .fulfilled => { + readable_stream.done(globalThis); + if (response_stream.sink.endPromise.hasValue()) { + response_stream.sink.endPromise.resolve(globalThis, JSC.jsNumber(0)); + } + }, + .rejected => { + readable_stream.cancel(globalThis); + if (response_stream.sink.endPromise.hasValue()) { + response_stream.sink.endPromise.rejectOnNextTick(globalThis, promise.result(globalThis.vm())); + } + task.fail(.{ + .code = "UnknownError", + .message = "ReadableStream ended with an error", + }); + }, + } + } else { + readable_stream.cancel(globalThis); + if (response_stream.sink.endPromise.hasValue()) { + response_stream.sink.endPromise.rejectOnNextTick(globalThis, assignment_result); + } + task.fail(.{ + .code = "UnknownError", + .message = "ReadableStream ended with an error", + }); + } + } + return endPromise; + } + /// returns a writable stream that writes to the s3 path + pub fn s3WritableStream(this: *@This(), path: []const u8, globalThis: *JSC.JSGlobalObject, options: MultiPartUpload.MultiPartUploadOptions, content_type: ?[]const u8, proxy: ?[]const u8) bun.JSError!JSC.JSValue { + const Wrapper = struct { + pub fn callback(result: S3UploadResult, sink: *JSC.WebCore.FetchTaskletChunkedRequestSink) void { + if (sink.endPromise.globalObject()) |globalObject| { + const event_loop = globalObject.bunVM().eventLoop(); + event_loop.enter(); + defer event_loop.exit(); + switch (result) { + .success => { + sink.endPromise.resolve(globalObject, JSC.jsNumber(0)); + }, + .failure => |err| { + if (!sink.done) { + sink.abort(); + return; + } + + sink.endPromise.rejectOnNextTick(globalObject, err.toJS(globalObject)); + }, + } + } + sink.finalize(); + } + }; + const proxy_url = (proxy orelse ""); + this.ref(); // ref the credentials + const task = MultiPartUpload.new(.{ + .credentials = this, + .path = bun.default_allocator.dupe(u8, path) catch bun.outOfMemory(), + .proxy = if (proxy_url.len > 0) bun.default_allocator.dupe(u8, proxy_url) catch bun.outOfMemory() else "", + .content_type = if (content_type) |ct| bun.default_allocator.dupe(u8, ct) catch bun.outOfMemory() else null, + + .callback = @ptrCast(&Wrapper.callback), + .callback_context = undefined, + .globalThis = globalThis, + .options = options, + .vm = JSC.VirtualMachine.get(), + }); + + task.poll_ref.ref(task.vm); + + task.ref(); // + 1 for the stream + var response_stream = JSC.WebCore.FetchTaskletChunkedRequestSink.new(.{ + .task = .{ .s3_upload = task }, + .buffer = .{}, + .globalThis = globalThis, + .encoded = false, + .endPromise = JSC.JSPromise.Strong.init(globalThis), + }).toSink(); + + task.callback_context = @ptrCast(response_stream); + var signal = &response_stream.sink.signal; + + signal.* = JSC.WebCore.FetchTaskletChunkedRequestSink.JSSink.SinkSignal.init(.zero); + + // explicitly set it to a dead pointer + // we use this memory address to disable signals being sent + signal.clear(); + bun.assert(signal.isDead()); + return response_stream.sink.toJS(globalThis); + } +}; + +pub const MultiPartUpload = struct { + pub const OneMiB: usize = 1048576; + pub const MAX_SINGLE_UPLOAD_SIZE_IN_MiB: usize = 5120; // we limit to 5 GiB + pub const MAX_SINGLE_UPLOAD_SIZE: usize = MAX_SINGLE_UPLOAD_SIZE_IN_MiB * OneMiB; // we limit to 5 GiB + pub const MIN_SINGLE_UPLOAD_SIZE_IN_MiB: usize = 5; + pub const DefaultPartSize = OneMiB * MIN_SINGLE_UPLOAD_SIZE_IN_MiB; + const MAX_QUEUE_SIZE = 64; // dont make sense more than this because we use fetch anything greater will be 64 + const AWS = AWSCredentials; + queue: std.ArrayListUnmanaged(UploadPart) = .{}, + available: bun.bit_set.IntegerBitSet(MAX_QUEUE_SIZE) = bun.bit_set.IntegerBitSet(MAX_QUEUE_SIZE).initFull(), + + currentPartNumber: u16 = 1, + ref_count: u16 = 1, + ended: bool = false, + + options: MultiPartUploadOptions = .{}, + credentials: *AWSCredentials, + poll_ref: bun.Async.KeepAlive = bun.Async.KeepAlive.init(), + vm: *JSC.VirtualMachine, + globalThis: *JSC.JSGlobalObject, + + buffered: std.ArrayListUnmanaged(u8) = .{}, + offset: usize = 0, + + path: []const u8, + proxy: []const u8, + content_type: ?[]const u8 = null, + upload_id: []const u8 = "", + uploadid_buffer: bun.MutableString = .{ .allocator = bun.default_allocator, .list = .{} }, + + multipart_etags: std.ArrayListUnmanaged(UploadPart.UploadPartResult) = .{}, + multipart_upload_list: bun.ByteList = .{}, + + state: enum { + not_started, + multipart_started, + multipart_completed, + singlefile_started, + finished, + } = .not_started, + + callback: *const fn (AWS.S3UploadResult, *anyopaque) void, + callback_context: *anyopaque, + + pub usingnamespace bun.NewRefCounted(@This(), @This().deinit); + + const log = bun.Output.scoped(.S3MultiPartUpload, true); + pub const MultiPartUploadOptions = struct { + /// more than 255 dont make sense http thread cannot handle more than that + queueSize: u8 = 5, + /// in s3 client sdk they set it in bytes but the min is still 5 MiB + /// var params = {Bucket: 'bucket', Key: 'key', Body: stream}; + /// var options = {partSize: 10 * 1024 * 1024, queueSize: 1}; + /// s3.upload(params, options, function(err, data) { + /// console.log(err, data); + /// }); + /// See. https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property + /// The value is in MiB min is 5 and max 5120 (but we limit to 4 GiB aka 4096) + partSize: u16 = 5, + /// default is 3 max 255 + retry: u8 = 3, + }; + + pub const UploadPart = struct { + data: []const u8, + state: enum { + pending, + started, + completed, + canceled, + }, + owns_data: bool, + partNumber: u16, // max is 10,000 + retry: u8, // auto retry, decrement until 0 and fail after this + index: u8, + ctx: *MultiPartUpload, + + pub const UploadPartResult = struct { + number: u16, + etag: []const u8, + }; + fn sortEtags(_: *MultiPartUpload, a: UploadPart.UploadPartResult, b: UploadPart.UploadPartResult) bool { + return a.number < b.number; + } + + pub fn onPartResponse(result: AWS.S3PartResult, this: *@This()) void { + if (this.state == .canceled) { + log("onPartResponse {} canceled", .{this.partNumber}); + if (this.owns_data) bun.default_allocator.free(this.data); + this.ctx.deref(); + return; + } + + this.state = .completed; + + switch (result) { + .failure => |err| { + if (this.retry > 0) { + log("onPartResponse {} retry", .{this.partNumber}); + this.retry -= 1; + // retry failed + this.perform(); + return; + } else { + log("onPartResponse {} failed", .{this.partNumber}); + if (this.owns_data) bun.default_allocator.free(this.data); + defer this.ctx.deref(); + return this.ctx.fail(err); + } + }, + .etag => |etag| { + log("onPartResponse {} success", .{this.partNumber}); + + if (this.owns_data) bun.default_allocator.free(this.data); + // we will need to order this + this.ctx.multipart_etags.append(bun.default_allocator, .{ + .number = this.partNumber, + .etag = bun.default_allocator.dupe(u8, etag) catch bun.outOfMemory(), + }) catch bun.outOfMemory(); + + defer this.ctx.deref(); + // mark as available + this.ctx.available.set(this.index); + // drain more + this.ctx.drainEnqueuedParts(); + }, + } + } + + fn perform(this: *@This()) void { + var params_buffer: [2048]u8 = undefined; + const search_params = std.fmt.bufPrint(¶ms_buffer, "?partNumber={}&uploadId={s}&x-id=UploadPart", .{ + this.partNumber, + this.ctx.upload_id, + }) catch unreachable; + this.ctx.credentials.executeSimpleS3Request(.{ + .path = this.ctx.path, + .method = .PUT, + .proxy_url = this.ctx.proxyUrl(), + .body = this.data, + .search_params = search_params, + }, .{ .part = @ptrCast(&onPartResponse) }, this); + } + pub fn start(this: *@This()) void { + if (this.state != .pending or this.ctx.state != .multipart_completed) return; + this.ctx.ref(); + this.state = .started; + this.perform(); + } + pub fn cancel(this: *@This()) void { + const state = this.state; + this.state = .canceled; + + switch (state) { + .pending => { + if (this.owns_data) bun.default_allocator.free(this.data); + }, + // if is not pending we will free later or is already freed + else => {}, + } + } + }; + + fn deinit(this: *@This()) void { + log("deinit", .{}); + if (this.queue.capacity > 0) + this.queue.deinit(bun.default_allocator); + this.poll_ref.unref(this.vm); + bun.default_allocator.free(this.path); + if (this.proxy.len > 0) { + bun.default_allocator.free(this.proxy); + } + if (this.content_type) |ct| { + if (ct.len > 0) { + bun.default_allocator.free(ct); + } + } + this.credentials.deref(); + this.uploadid_buffer.deinit(); + for (this.multipart_etags.items) |tag| { + bun.default_allocator.free(tag.etag); + } + if (this.multipart_etags.capacity > 0) + this.multipart_etags.deinit(bun.default_allocator); + if (this.multipart_upload_list.cap > 0) + this.multipart_upload_list.deinitWithAllocator(bun.default_allocator); + this.destroy(); + } + + pub fn singleSendUploadResponse(result: AWS.S3UploadResult, this: *@This()) void { + switch (result) { + .failure => |err| { + if (this.options.retry > 0) { + log("singleSendUploadResponse {} retry", .{this.options.retry}); + this.options.retry -= 1; + // retry failed + this.credentials.executeSimpleS3Request(.{ + .path = this.path, + .method = .PUT, + .proxy_url = this.proxyUrl(), + .body = this.buffered.items, + .content_type = this.content_type, + }, .{ .upload = @ptrCast(&singleSendUploadResponse) }, this); + + return; + } else { + log("singleSendUploadResponse failed", .{}); + return this.fail(err); + } + }, + .success => { + log("singleSendUploadResponse success", .{}); + this.done(); + }, + } + } + + fn getCreatePart(this: *@This(), chunk: []const u8, owns_data: bool) ?*UploadPart { + const index = this.available.findFirstSet() orelse { + // this means that the queue is full and we cannot flush it + return null; + }; + + if (index >= this.options.queueSize) { + // ops too much concurrency wait more + return null; + } + this.available.unset(index); + defer this.currentPartNumber += 1; + + if (this.queue.items.len <= index) { + this.queue.append(bun.default_allocator, .{ + .data = chunk, + .partNumber = this.currentPartNumber, + .owns_data = owns_data, + .ctx = this, + .index = @truncate(index), + .retry = this.options.retry, + .state = .pending, + }) catch bun.outOfMemory(); + return &this.queue.items[index]; + } + this.queue.items[index] = .{ + .data = chunk, + .partNumber = this.currentPartNumber, + .owns_data = owns_data, + .ctx = this, + .index = @truncate(index), + .retry = this.options.retry, + .state = .pending, + }; + return &this.queue.items[index]; + } + + fn drainEnqueuedParts(this: *@This()) void { + // check pending to start or transformed buffered ones into tasks + if (this.state == .multipart_completed) { + for (this.queue.items) |*part| { + if (part.state == .pending) { + // lets start the part request + part.start(); + } + } + } + const partSize = this.partSizeInBytes(); + if (this.ended or this.buffered.items.len >= partSize) { + this.processMultiPart(partSize); + } + + if (this.ended and this.available.mask == std.bit_set.IntegerBitSet(MAX_QUEUE_SIZE).initFull().mask) { + // we are done + this.done(); + } + } + pub fn fail(this: *@This(), _err: AWS.S3Error) void { + log("fail {s}:{s}", .{ _err.code, _err.message }); + for (this.queue.items) |*task| { + task.cancel(); + } + if (this.state != .finished) { + this.callback(.{ .failure = _err }, this.callback_context); + this.state = .finished; + if (this.state == .multipart_completed) { + // will deref after rollback + this.rollbackMultiPartRequest(); + } else { + this.deref(); + } + } + } + + fn done(this: *@This()) void { + if (this.state == .multipart_completed) { + this.state = .finished; + + std.sort.block(UploadPart.UploadPartResult, this.multipart_etags.items, this, UploadPart.sortEtags); + this.multipart_upload_list.append(bun.default_allocator, "") catch bun.outOfMemory(); + for (this.multipart_etags.items) |tag| { + this.multipart_upload_list.appendFmt(bun.default_allocator, "{}{s}", .{ tag.number, tag.etag }) catch bun.outOfMemory(); + + bun.default_allocator.free(tag.etag); + } + this.multipart_etags.deinit(bun.default_allocator); + this.multipart_etags = .{}; + this.multipart_upload_list.append(bun.default_allocator, "") catch bun.outOfMemory(); + // will deref and ends after commit + this.commitMultiPartRequest(); + } else { + this.callback(.{ .success = {} }, this.callback_context); + this.state = .finished; + this.deref(); + } + } + pub fn startMultiPartRequestResult(result: AWS.S3DownloadResult, this: *@This()) void { + switch (result) { + .failure => |err| { + log("startMultiPartRequestResult {s} failed {s}: {s}", .{ this.path, err.message, err.message }); + this.fail(err); + }, + .success => |response| { + const slice = response.body.list.items; + this.uploadid_buffer = result.success.body; + + if (strings.indexOf(slice, "")) |start| { + if (strings.indexOf(slice, "")) |end| { + this.upload_id = slice[start + 10 .. end]; + } + } + if (this.upload_id.len == 0) { + // Unknown type of response error from AWS + log("startMultiPartRequestResult {s} failed invalid id", .{this.path}); + this.fail(.{ + .code = "UnknownError", + .message = "Failed to initiate multipart upload", + }); + return; + } + log("startMultiPartRequestResult {s} success id: {s}", .{ this.path, this.upload_id }); + this.state = .multipart_completed; + this.drainEnqueuedParts(); + }, + // this is "unreachable" but we cover in case AWS returns 404 + .not_found => this.fail(.{ + .code = "UnknownError", + .message = "Failed to initiate multipart upload", + }), + } + } + + pub fn onCommitMultiPartRequest(result: AWS.S3CommitResult, this: *@This()) void { + log("onCommitMultiPartRequest {s}", .{this.upload_id}); + switch (result) { + .failure => |err| { + if (this.options.retry > 0) { + this.options.retry -= 1; + // retry commit + this.commitMultiPartRequest(); + return; + } + this.callback(.{ .failure = err }, this.callback_context); + this.deref(); + }, + .success => { + this.callback(.{ .success = {} }, this.callback_context); + this.state = .finished; + this.deref(); + }, + } + } + + pub fn onRollbackMultiPartRequest(result: AWS.S3UploadResult, this: *@This()) void { + log("onRollbackMultiPartRequest {s}", .{this.upload_id}); + switch (result) { + .failure => { + if (this.options.retry > 0) { + this.options.retry -= 1; + // retry rollback + this.rollbackMultiPartRequest(); + return; + } + this.deref(); + }, + .success => { + this.deref(); + }, + } + } + + fn commitMultiPartRequest(this: *@This()) void { + log("commitMultiPartRequest {s}", .{this.upload_id}); + var params_buffer: [2048]u8 = undefined; + const searchParams = std.fmt.bufPrint(¶ms_buffer, "?uploadId={s}", .{ + this.upload_id, + }) catch unreachable; + + this.credentials.executeSimpleS3Request(.{ + .path = this.path, + .method = .POST, + .proxy_url = this.proxyUrl(), + .body = this.multipart_upload_list.slice(), + .search_params = searchParams, + }, .{ .commit = @ptrCast(&onCommitMultiPartRequest) }, this); + } + fn rollbackMultiPartRequest(this: *@This()) void { + log("rollbackMultiPartRequest {s}", .{this.upload_id}); + var params_buffer: [2048]u8 = undefined; + const search_params = std.fmt.bufPrint(¶ms_buffer, "?uploadId={s}", .{ + this.upload_id, + }) catch unreachable; + + this.credentials.executeSimpleS3Request(.{ + .path = this.path, + .method = .DELETE, + .proxy_url = this.proxyUrl(), + .body = "", + .search_params = search_params, + }, .{ .upload = @ptrCast(&onRollbackMultiPartRequest) }, this); + } + fn enqueuePart(this: *@This(), chunk: []const u8, owns_data: bool) bool { + const part = this.getCreatePart(chunk, owns_data) orelse return false; + + if (this.state == .not_started) { + // will auto start later + this.state = .multipart_started; + this.credentials.executeSimpleS3Request(.{ + .path = this.path, + .method = .POST, + .proxy_url = this.proxyUrl(), + .body = "", + .search_params = "?uploads=", + .content_type = this.content_type, + }, .{ .download = @ptrCast(&startMultiPartRequestResult) }, this); + } else if (this.state == .multipart_completed) { + part.start(); + } + return true; + } + + fn processMultiPart(this: *@This(), part_size: usize) void { + // need to split in multiple parts because of the size + var buffer = this.buffered.items[this.offset..]; + var queue_full = false; + defer if (!this.ended and queue_full == false) { + this.buffered = .{}; + this.offset = 0; + }; + + while (buffer.len > 0) { + const len = @min(part_size, buffer.len); + const slice = buffer[0..len]; + buffer = buffer[len..]; + // its one big buffer lets free after we are done with everything, part dont own the data + if (this.enqueuePart(slice, this.ended)) { + this.offset += len; + } else { + queue_full = true; + break; + } + } + } + + pub fn proxyUrl(this: *@This()) ?[]const u8 { + return this.proxy; + } + fn processBuffered(this: *@This(), part_size: usize) void { + if (this.ended and this.buffered.items.len < this.partSizeInBytes() and this.state == .not_started) { + log("processBuffered {s} singlefile_started", .{this.path}); + this.state = .singlefile_started; + // we can do only 1 request + this.credentials.executeSimpleS3Request(.{ + .path = this.path, + .method = .PUT, + .proxy_url = this.proxyUrl(), + .body = this.buffered.items, + .content_type = this.content_type, + }, .{ .upload = @ptrCast(&singleSendUploadResponse) }, this); + } else { + // we need to split + this.processMultiPart(part_size); + } + } + + pub fn partSizeInBytes(this: *@This()) usize { + return this.options.partSize * OneMiB; + } + + pub fn sendRequestData(this: *@This(), chunk: []const u8, is_last: bool) void { + if (this.ended) return; + + if (is_last) { + this.ended = true; + if (chunk.len > 0) { + this.buffered.appendSlice(bun.default_allocator, chunk) catch bun.outOfMemory(); + } + this.processBuffered(this.partSizeInBytes()); + } else { + // still have more data and receive empty, nothing todo here + if (chunk.len == 0) return; + this.buffered.appendSlice(bun.default_allocator, chunk) catch bun.outOfMemory(); + const partSize = this.partSizeInBytes(); + if (this.buffered.items.len >= partSize) { + // send the part we have enough data + this.processBuffered(partSize); + return; + } + + // wait for more + } + } +}; diff --git a/src/shell/interpreter.zig b/src/shell/interpreter.zig index 38b8976520..58a3a915f7 100644 --- a/src/shell/interpreter.zig +++ b/src/shell/interpreter.zig @@ -50,7 +50,7 @@ const ShellError = shell.ShellError; const ast = shell.AST; const SmolList = shell.SmolList; -const GlobWalker = Glob.GlobWalker_(null, Glob.SyscallAccessor, true); +const GlobWalker = Glob.BunGlobWalkerZ; const stdin_no = 0; const stdout_no = 1; @@ -1303,7 +1303,7 @@ pub const Interpreter = struct { var env_loader: *bun.DotEnv.Loader = env_loader: { if (event_loop == .js) { - break :env_loader event_loop.js.virtual_machine.bundler.env; + break :env_loader event_loop.js.virtual_machine.transpiler.env; } break :env_loader event_loop.env(); @@ -1323,8 +1323,10 @@ pub const Interpreter = struct { break :brk export_env; }; - var pathbuf: bun.PathBuffer = undefined; - const cwd: [:0]const u8 = switch (Syscall.getcwdZ(&pathbuf)) { + // Avoid the large stack allocation on Windows. + const pathbuf = bun.default_allocator.create(bun.PathBuffer) catch bun.outOfMemory(); + defer bun.default_allocator.destroy(pathbuf); + const cwd: [:0]const u8 = switch (Syscall.getcwdZ(pathbuf)) { .result => |cwd| cwd, .err => |err| { return .{ .err = .{ .sys = err.toSystemError() } }; @@ -4883,8 +4885,9 @@ pub const Interpreter = struct { return; } - var path_buf: bun.PathBuffer = undefined; - const resolved = which(&path_buf, spawn_args.PATH, spawn_args.cwd, first_arg_real) orelse blk: { + const path_buf = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(path_buf); + const resolved = which(path_buf, spawn_args.PATH, spawn_args.cwd, first_arg_real) orelse blk: { if (bun.strings.eqlComptime(first_arg_real, "bun") or bun.strings.eqlComptime(first_arg_real, "bun-debug")) blk2: { break :blk bun.selfExePath() catch break :blk2; } @@ -7158,12 +7161,13 @@ pub const Interpreter = struct { } if (this.bltn.stdout.needsIO() == null) { - var path_buf: bun.PathBuffer = undefined; + const path_buf = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(path_buf); const PATH = this.bltn.parentCmd().base.shell.export_env.get(EnvStr.initSlice("PATH")) orelse EnvStr.initSlice(""); var had_not_found = false; for (args) |arg_raw| { const arg = arg_raw[0..std.mem.len(arg_raw)]; - const resolved = which(&path_buf, PATH.slice(), this.bltn.parentCmd().base.shell.cwdZ(), arg) orelse { + const resolved = which(path_buf, PATH.slice(), this.bltn.parentCmd().base.shell.cwdZ(), arg) orelse { had_not_found = true; const buf = this.bltn.fmtErrorArena(.which, "{s} not found\n", .{arg}); _ = this.bltn.writeNoIO(.stdout, buf); @@ -7198,10 +7202,11 @@ pub const Interpreter = struct { const arg_raw = multiargs.args_slice[multiargs.arg_idx]; const arg = arg_raw[0..std.mem.len(arg_raw)]; - var path_buf: bun.PathBuffer = undefined; + const path_buf = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(path_buf); const PATH = this.bltn.parentCmd().base.shell.export_env.get(EnvStr.initSlice("PATH")) orelse EnvStr.initSlice(""); - const resolved = which(&path_buf, PATH.slice(), this.bltn.parentCmd().base.shell.cwdZ(), arg) orelse { + const resolved = which(path_buf, PATH.slice(), this.bltn.parentCmd().base.shell.cwdZ(), arg) orelse { multiargs.had_not_found = true; if (this.bltn.stdout.needsIO()) |safeguard| { multiargs.state = .waiting_write; diff --git a/src/shell/shell.zig b/src/shell/shell.zig index a475cdd763..ae35c27140 100644 --- a/src/shell/shell.zig +++ b/src/shell/shell.zig @@ -227,7 +227,7 @@ pub const GlobalJS = struct { } pub inline fn createNullDelimitedEnvMap(this: @This(), alloc: Allocator) ![:null]?[*:0]u8 { - return this.globalThis.bunVM().bundler.env.map.createNullDelimitedEnvMap(alloc); + return this.globalThis.bunVM().transpiler.env.map.createNullDelimitedEnvMap(alloc); } pub inline fn getAllocator(this: @This()) Allocator { @@ -239,11 +239,11 @@ pub const GlobalJS = struct { } pub inline fn topLevelDir(this: @This()) []const u8 { - return this.globalThis.bunVM().bundler.fs.top_level_dir; + return this.globalThis.bunVM().transpiler.fs.top_level_dir; } pub inline fn env(this: @This()) *bun.DotEnv.Loader { - return this.globalThis.bunVM().bundler.env; + return this.globalThis.bunVM().transpiler.env; } pub inline fn platformEventLoop(this: @This()) *JSC.PlatformEventLoop { @@ -3987,8 +3987,8 @@ pub const ShellSrcBuilder = struct { /// Characters that need to escaped const SPECIAL_CHARS = [_]u8{ '~', '[', ']', '#', ';', '\n', '*', '{', ',', '}', '`', '$', '=', '(', ')', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '|', '>', '<', '&', '\'', '"', ' ', '\\' }; -const SPECIAL_CHARS_TABLE: std.bit_set.IntegerBitSet(256) = brk: { - var table = std.bit_set.IntegerBitSet(256).initEmpty(); +const SPECIAL_CHARS_TABLE: bun.bit_set.IntegerBitSet(256) = brk: { + var table = bun.bit_set.IntegerBitSet(256).initEmpty(); for (SPECIAL_CHARS) |c| { table.set(c); } diff --git a/src/shell/subproc.zig b/src/shell/subproc.zig index 1eed3bdf53..672abdcf22 100644 --- a/src/shell/subproc.zig +++ b/src/shell/subproc.zig @@ -704,7 +704,7 @@ pub const ShellSubprocess = struct { } pub fn fillEnvFromProcess(this: *SpawnArgs, globalThis: *JSGlobalObject) void { - var env_iter = EnvMapIter.init(globalThis.bunVM().bundler.env.map, this.arena.allocator()); + var env_iter = EnvMapIter.init(globalThis.bunVM().transpiler.env.map, this.arena.allocator()); return this.fillEnv(globalThis, &env_iter, false); } @@ -782,7 +782,7 @@ pub const ShellSubprocess = struct { const is_sync = config.is_sync; if (!spawn_args.override_env and spawn_args.env_array.items.len == 0) { - // spawn_args.env_array.items = jsc_vm.bundler.env.map.createNullDelimitedEnvMap(allocator) catch bun.outOfMemory(); + // spawn_args.env_array.items = jsc_vm.transpiler.env.map.createNullDelimitedEnvMap(allocator) catch bun.outOfMemory(); spawn_args.env_array.items = event_loop.createNullDelimitedEnvMap(allocator) catch bun.outOfMemory(); spawn_args.env_array.capacity = spawn_args.env_array.items.len; } diff --git a/src/sql/postgres.zig b/src/sql/postgres.zig index 026706100e..296ce7ee1c 100644 --- a/src/sql/postgres.zig +++ b/src/sql/postgres.zig @@ -13,6 +13,37 @@ pub const PostgresShort = u16; const Crypto = JSC.API.Bun.Crypto; const JSValue = JSC.JSValue; const BoringSSL = @import("../boringssl.zig"); +pub const AnyPostgresError = error{ + ConnectionClosed, + ExpectedRequest, + ExpectedStatement, + InvalidBackendKeyData, + InvalidBinaryData, + InvalidByteSequence, + InvalidByteSequenceForEncoding, + InvalidCharacter, + InvalidMessage, + InvalidMessageLength, + InvalidQueryBinding, + InvalidServerKey, + InvalidServerSignature, + JSError, + MultidimensionalArrayNotSupportedYet, + NullsInArrayNotSupportedYet, + OutOfMemory, + Overflow, + PBKDFD2, + SASL_SIGNATURE_MISMATCH, + SASL_SIGNATURE_INVALID_BASE64, + ShortRead, + TLSNotAvailable, + TLSUpgradeFailed, + UnexpectedMessage, + UNKNOWN_AUTHENTICATION_METHOD, + UNSUPPORTED_AUTHENTICATION_METHOD, + UnsupportedByteaFormat, + UnsupportedIntegerSize, +}; pub const SSLMode = enum(u8) { disable = 0, @@ -176,16 +207,23 @@ pub const PostgresSQLQuery = struct { statement: ?*PostgresSQLStatement = null, query: bun.String = bun.String.empty, cursor_name: bun.String = bun.String.empty, + + // Kept alive by being in the "queries" array from JS. thisValue: JSValue = .undefined, - target: JSC.Strong = JSC.Strong.init(), + status: Status = Status.pending, is_done: bool = false, ref_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(1), binary: bool = false, - pending_value: JSC.Strong = .{}, pub usingnamespace JSC.Codegen.JSPostgresSQLQuery; + pub fn getTarget(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + const target = PostgresSQLQuery.targetGetCached(this.thisValue) orelse return .zero; + PostgresSQLQuery.targetSetCached(this.thisValue, globalObject, .zero); + return target; + } + pub const Status = enum(u8) { pending, written, @@ -209,9 +247,6 @@ pub const PostgresSQLQuery = struct { } this.query.deref(); this.cursor_name.deref(); - this.target.deinit(); - this.pending_value.deinit(); - bun.default_allocator.destroy(this); } @@ -233,12 +268,12 @@ pub const PostgresSQLQuery = struct { bun.assert(this.ref_count.fetchAdd(1, .monotonic) > 0); } - pub fn onNoData(this: *@This(), globalObject: *JSC.JSGlobalObject) void { + pub fn onNoData(this: *@This(), globalObject: *JSC.JSGlobalObject, queries_array: JSValue) void { this.status = .success; defer this.deref(); const thisValue = this.thisValue; - const targetValue = this.target.trySwap() orelse JSValue.zero; + const targetValue = this.getTarget(globalObject); if (thisValue == .zero or targetValue == .zero) { return; } @@ -251,13 +286,18 @@ pub const PostgresSQLQuery = struct { this.pending_value.trySwap() orelse .undefined, JSValue.jsNumber(0), JSValue.jsNumber(0), + queries_array, }); } - pub fn onWriteFail(this: *@This(), err: anyerror, globalObject: *JSC.JSGlobalObject) void { + pub fn onWriteFail( + this: *@This(), + err: AnyPostgresError, + globalObject: *JSC.JSGlobalObject, + queries_array: JSValue, + ) void { this.status = .fail; - this.pending_value.deinit(); const thisValue = this.thisValue; - const targetValue = this.target.trySwap() orelse JSValue.zero; + const targetValue = this.getTarget(globalObject); if (thisValue == .zero or targetValue == .zero) { return; } @@ -271,6 +311,7 @@ pub const PostgresSQLQuery = struct { event_loop.runCallback(function, globalObject, thisValue, &.{ targetValue, instance, + queries_array, }); } @@ -279,7 +320,7 @@ pub const PostgresSQLQuery = struct { defer this.deref(); const thisValue = this.thisValue; - const targetValue = this.target.trySwap() orelse JSValue.zero; + const targetValue = this.getTarget(globalObject); if (thisValue == .zero or targetValue == .zero) { return; } @@ -388,14 +429,31 @@ pub const PostgresSQLQuery = struct { } }; - pub fn onSuccess(this: *@This(), command_tag_str: []const u8, globalObject: *JSC.JSGlobalObject) void { + pub fn allowGC(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject) void { + if (thisValue == .zero) { + return; + } + + defer thisValue.ensureStillAlive(); + PostgresSQLQuery.bindingSetCached(thisValue, globalObject, .zero); + PostgresSQLQuery.pendingValueSetCached(thisValue, globalObject, .zero); + PostgresSQLQuery.targetSetCached(thisValue, globalObject, .zero); + } + + fn consumePendingValue(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject) ?JSValue { + const pending_value = PostgresSQLQuery.pendingValueGetCached(thisValue) orelse return null; + PostgresSQLQuery.pendingValueSetCached(thisValue, globalObject, .zero); + return pending_value; + } + + pub fn onSuccess(this: *@This(), command_tag_str: []const u8, globalObject: *JSC.JSGlobalObject, connection: JSC.JSValue) void { this.status = .success; defer this.deref(); const thisValue = this.thisValue; - const targetValue = this.target.trySwap() orelse JSValue.zero; + const targetValue = this.getTarget(globalObject); + defer allowGC(thisValue, globalObject); if (thisValue == .zero or targetValue == .zero) { - this.pending_value.deinit(); return; } @@ -407,9 +465,10 @@ pub const PostgresSQLQuery = struct { event_loop.runCallback(function, globalObject, thisValue, &.{ targetValue, - this.pending_value.trySwap() orelse .undefined, + consumePendingValue(thisValue, globalObject) orelse .undefined, tag.toJSTag(globalObject), tag.toJSNumber(), + PostgresSQLConnection.queriesGetCached(connection) orelse .undefined, }); } @@ -458,7 +517,6 @@ pub const PostgresSQLQuery = struct { if (columns != .undefined) { PostgresSQLQuery.columnsSetCached(this_value, globalThis, columns); } - ptr.pending_value.set(globalThis, pending_value); return this_value; } @@ -477,7 +535,7 @@ pub const PostgresSQLQuery = struct { pub fn doRun(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { var arguments_ = callframe.arguments_old(2); const arguments = arguments_.slice(); - var connection = arguments[0].as(PostgresSQLConnection) orelse { + const connection: *PostgresSQLConnection = arguments[0].as(PostgresSQLConnection) orelse { return globalObject.throw("connection must be a PostgresSQLConnection", .{}); }; var query = arguments[1]; @@ -486,11 +544,11 @@ pub const PostgresSQLQuery = struct { return globalObject.throwInvalidArgumentType("run", "query", "Query"); } - this.target.set(globalObject, query); - const binding_value = PostgresSQLQuery.bindingGetCached(callframe.this()) orelse .zero; + const this_value = callframe.this(); + const binding_value = PostgresSQLQuery.bindingGetCached(this_value) orelse .zero; var query_str = this.query.toUTF8(bun.default_allocator); defer query_str.deinit(); - const columns_value = PostgresSQLQuery.columnsGetCached(callframe.this()) orelse .undefined; + const columns_value = PostgresSQLQuery.columnsGetCached(this_value) orelse .undefined; var signature = Signature.generate(globalObject, query_str.slice(), binding_value, columns_value) catch |err| { if (!globalObject.hasException()) @@ -568,9 +626,12 @@ pub const PostgresSQLQuery = struct { connection.requests.writeItem(this) catch {}; this.ref(); this.status = if (did_write) .binding else .pending; + PostgresSQLQuery.targetSetCached(this_value, globalObject, query); if (connection.is_ready_for_query) - connection.flushData(); + connection.flushDataAndResetTimeout() + else if (did_write) + connection.resetConnectionTimeout(); return .undefined; } @@ -665,8 +726,10 @@ pub const PostgresRequest = struct { try writer.int4(@bitCast(@as(i32, -1))); continue; } + if (comptime bun.Environment.enable_logs) { + debug(" -> {s}", .{tag.name() orelse "(unknown)"}); + } - debug(" -> {s}", .{@tagName(tag)}); switch ( // If they pass a value as a string, let's avoid attempting to // convert it to the binary representation. This minimizes the room @@ -674,7 +737,7 @@ pub const PostgresRequest = struct { // differently than what Postgres does when given a timestamp with // timezone. if (tag.isBinaryFormatSupported() and value.isString()) .text else tag) { - .json => { + .jsonb, .json => { var str = bun.String.empty; defer str.deref(); value.jsonStringify(globalObject, 0, &str); @@ -761,7 +824,7 @@ pub const PostgresRequest = struct { params: []const int4, comptime Context: type, writer: protocol.NewWriter(Context), - ) !void { + ) AnyPostgresError!void { { var q = protocol.Parse{ .name = name, @@ -790,7 +853,7 @@ pub const PostgresRequest = struct { comptime Context: type, writer: protocol.NewWriter(Context), signature: *Signature, - ) !void { + ) AnyPostgresError!void { try writeQuery(query, signature.name, signature.fields, Context, writer); try writeBind(signature.name, bun.String.empty, globalObject, array_value, .zero, &.{}, &.{}, Context, writer); var exec = protocol.Execute{ @@ -863,7 +926,7 @@ pub const PostgresRequest = struct { connection.tls_status = .ssl_not_available; debug("Server does not support SSL", .{}); if (connection.ssl_mode == .require) { - connection.fail("Server does not support SSL", error.SSLNotAvailable); + connection.fail("Server does not support SSL", error.TLSNotAvailable); return; } continue; @@ -912,9 +975,6 @@ pub const PostgresSQLConnection = struct { pending_disconnect: bool = false, - on_connect: JSC.Strong = .{}, - on_close: JSC.Strong = .{}, - database: []const u8 = "", user: []const u8 = "", password: []const u8 = "", @@ -928,6 +988,31 @@ pub const PostgresSQLConnection = struct { tls_status: TLSStatus = .none, ssl_mode: SSLMode = .disable, + idle_timeout_interval_ms: u32 = 0, + connection_timeout_ms: u32 = 0, + + /// Before being connected, this is a connection timeout timer. + /// After being connected, this is an idle timeout timer. + timer: JSC.BunTimer.EventLoopTimer = .{ + .tag = .PostgresSQLConnectionTimeout, + .next = .{ + .sec = 0, + .nsec = 0, + }, + }, + + /// This timer controls the maximum lifetime of a connection. + /// It starts when the connection successfully starts (i.e. after handshake is complete). + /// It stops when the connection is closed. + max_lifetime_interval_ms: u32 = 0, + max_lifetime_timer: JSC.BunTimer.EventLoopTimer = .{ + .tag = .PostgresSQLConnectionMaxLifetime, + .next = .{ + .sec = 0, + .nsec = 0, + }, + }, + pub const TLSStatus = union(enum) { none, pending, @@ -942,100 +1027,107 @@ pub const PostgresSQLConnection = struct { pub const AuthenticationState = union(enum) { pending: void, - SASL: SASL, + none: void, ok: void, + SASL: SASL, + md5: void, pub fn zero(this: *AuthenticationState) void { - const bytes = std.mem.asBytes(this); - @memset(bytes, 0); + switch (this.*) { + .SASL => |*sasl| { + sasl.deinit(); + }, + else => {}, + } + this.* = .{ .none = {} }; + } + }; + + pub const SASL = struct { + const nonce_byte_len = 18; + const nonce_base64_len = bun.base64.encodeLenFromSize(nonce_byte_len); + + const server_signature_byte_len = 32; + const server_signature_base64_len = bun.base64.encodeLenFromSize(server_signature_byte_len); + + const salted_password_byte_len = 32; + + nonce_base64_bytes: [nonce_base64_len]u8 = .{0} ** nonce_base64_len, + nonce_len: u8 = 0, + + server_signature_base64_bytes: [server_signature_base64_len]u8 = .{0} ** server_signature_base64_len, + server_signature_len: u8 = 0, + + salted_password_bytes: [salted_password_byte_len]u8 = .{0} ** salted_password_byte_len, + salted_password_created: bool = false, + + status: SASLStatus = .init, + + pub const SASLStatus = enum { + init, + @"continue", + }; + + fn hmac(password: []const u8, data: []const u8) ?[32]u8 { + var buf = std.mem.zeroes([bun.BoringSSL.EVP_MAX_MD_SIZE]u8); + + // TODO: I don't think this is failable. + const result = bun.hmac.generate(password, data, .sha256, &buf) orelse return null; + + assert(result.len == 32); + return buf[0..32].*; } - pub const SASL = struct { - const nonce_byte_len = 18; - const nonce_base64_len = bun.base64.encodeLenFromSize(nonce_byte_len); - - const server_signature_byte_len = 32; - const server_signature_base64_len = bun.base64.encodeLenFromSize(server_signature_byte_len); - - const salted_password_byte_len = 32; - - nonce_base64_bytes: [nonce_base64_len]u8 = .{0} ** nonce_base64_len, - nonce_len: u8 = 0, - - server_signature_base64_bytes: [server_signature_base64_len]u8 = .{0} ** server_signature_base64_len, - server_signature_len: u8 = 0, - - salted_password_bytes: [salted_password_byte_len]u8 = .{0} ** salted_password_byte_len, - salted_password_created: bool = false, - - status: SASLStatus = .init, - - pub const SASLStatus = enum { - init, - @"continue", - }; - - fn hmac(password: []const u8, data: []const u8) ?[32]u8 { - var buf = std.mem.zeroes([bun.BoringSSL.EVP_MAX_MD_SIZE]u8); - - // TODO: I don't think this is failable. - const result = bun.hmac.generate(password, data, .sha256, &buf) orelse return null; - - assert(result.len == 32); - return buf[0..32].*; + pub fn computeSaltedPassword(this: *SASL, salt_bytes: []const u8, iteration_count: u32, connection: *PostgresSQLConnection) !void { + this.salted_password_created = true; + if (Crypto.EVP.pbkdf2(&this.salted_password_bytes, connection.password, salt_bytes, iteration_count, .sha256) == null) { + return error.PBKDFD2; } + } - pub fn computeSaltedPassword(this: *SASL, salt_bytes: []const u8, iteration_count: u32, connection: *PostgresSQLConnection) !void { - this.salted_password_created = true; - if (Crypto.EVP.pbkdf2(&this.salted_password_bytes, connection.password, salt_bytes, iteration_count, .sha256) == null) { - return error.PBKDF2Failed; - } + pub fn saltedPassword(this: *const SASL) []const u8 { + assert(this.salted_password_created); + return this.salted_password_bytes[0..salted_password_byte_len]; + } + + pub fn serverSignature(this: *const SASL) []const u8 { + assert(this.server_signature_len > 0); + return this.server_signature_base64_bytes[0..this.server_signature_len]; + } + + pub fn computeServerSignature(this: *SASL, auth_string: []const u8) !void { + assert(this.server_signature_len == 0); + + const server_key = hmac(this.saltedPassword(), "Server Key") orelse return error.InvalidServerKey; + const server_signature_bytes = hmac(&server_key, auth_string) orelse return error.InvalidServerSignature; + this.server_signature_len = @intCast(bun.base64.encode(&this.server_signature_base64_bytes, &server_signature_bytes)); + } + + pub fn clientKey(this: *const SASL) [32]u8 { + return hmac(this.saltedPassword(), "Client Key").?; + } + + pub fn clientKeySignature(_: *const SASL, client_key: []const u8, auth_string: []const u8) [32]u8 { + var sha_digest = std.mem.zeroes(bun.sha.SHA256.Digest); + bun.sha.SHA256.hash(client_key, &sha_digest, JSC.VirtualMachine.get().rareData().boringEngine()); + return hmac(&sha_digest, auth_string).?; + } + + pub fn nonce(this: *SASL) []const u8 { + if (this.nonce_len == 0) { + var bytes: [nonce_byte_len]u8 = .{0} ** nonce_byte_len; + bun.rand(&bytes); + this.nonce_len = @intCast(bun.base64.encode(&this.nonce_base64_bytes, &bytes)); } + return this.nonce_base64_bytes[0..this.nonce_len]; + } - pub fn saltedPassword(this: *const SASL) []const u8 { - assert(this.salted_password_created); - return this.salted_password_bytes[0..salted_password_byte_len]; - } - - pub fn serverSignature(this: *const SASL) []const u8 { - assert(this.server_signature_len > 0); - return this.server_signature_base64_bytes[0..this.server_signature_len]; - } - - pub fn computeServerSignature(this: *SASL, auth_string: []const u8) !void { - assert(this.server_signature_len == 0); - - const server_key = hmac(this.saltedPassword(), "Server Key") orelse return error.InvalidServerKey; - const server_signature_bytes = hmac(&server_key, auth_string) orelse return error.InvalidServerSignature; - this.server_signature_len = @intCast(bun.base64.encode(&this.server_signature_base64_bytes, &server_signature_bytes)); - } - - pub fn clientKey(this: *const SASL) [32]u8 { - return hmac(this.saltedPassword(), "Client Key").?; - } - - pub fn clientKeySignature(_: *const SASL, client_key: []const u8, auth_string: []const u8) [32]u8 { - var sha_digest = std.mem.zeroes(bun.sha.SHA256.Digest); - bun.sha.SHA256.hash(client_key, &sha_digest, JSC.VirtualMachine.get().rareData().boringEngine()); - return hmac(&sha_digest, auth_string).?; - } - - pub fn nonce(this: *SASL) []const u8 { - if (this.nonce_len == 0) { - var bytes: [nonce_byte_len]u8 = .{0} ** nonce_byte_len; - bun.rand(&bytes); - this.nonce_len = @intCast(bun.base64.encode(&this.nonce_base64_bytes, &bytes)); - } - return this.nonce_base64_bytes[0..this.nonce_len]; - } - - pub fn deinit(this: *SASL) void { - this.nonce_len = 0; - this.salted_password_created = false; - this.server_signature_len = 0; - this.status = .init; - } - }; + pub fn deinit(this: *SASL) void { + this.nonce_len = 0; + this.salted_password_created = false; + this.server_signature_len = 0; + this.status = .init; + } }; pub const Status = enum { @@ -1050,6 +1142,64 @@ pub const PostgresSQLConnection = struct { pub usingnamespace JSC.Codegen.JSPostgresSQLConnection; + fn getTimeoutInterval(this: *const PostgresSQLConnection) u32 { + return switch (this.status) { + .connected => this.idle_timeout_interval_ms, + .failed => 0, + else => this.connection_timeout_ms, + }; + } + + pub fn resetConnectionTimeout(this: *PostgresSQLConnection) void { + const interval = this.getTimeoutInterval(); + if (this.timer.state == .ACTIVE) { + this.globalObject.bunVM().timer.remove(&this.timer); + } + if (interval == 0) { + return; + } + + this.timer.next = bun.timespec.msFromNow(@intCast(interval)); + this.globalObject.bunVM().timer.insert(&this.timer); + } + + pub fn getQueries(_: *PostgresSQLConnection, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + if (PostgresSQLConnection.queriesGetCached(thisValue)) |value| { + return value; + } + + const array = JSC.JSValue.createEmptyArray(globalObject, 0); + PostgresSQLConnection.queriesSetCached(thisValue, globalObject, array); + + return array; + } + + pub fn getOnConnect(_: *PostgresSQLConnection, thisValue: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { + if (PostgresSQLConnection.onconnectGetCached(thisValue)) |value| { + return value; + } + + return .undefined; + } + + pub fn setOnConnect(_: *PostgresSQLConnection, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) bool { + PostgresSQLConnection.onconnectSetCached(thisValue, globalObject, value); + return true; + } + + pub fn getOnClose(_: *PostgresSQLConnection, thisValue: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { + if (PostgresSQLConnection.oncloseGetCached(thisValue)) |value| { + return value; + } + + return .undefined; + } + + pub fn setOnClose(_: *PostgresSQLConnection, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) bool { + PostgresSQLConnection.oncloseSetCached(thisValue, globalObject, value); + return true; + } + pub fn setupTLS(this: *PostgresSQLConnection) void { debug("setupTLS", .{}); const new_socket = uws.us_socket_upgrade_to_tls(this.socket.SocketTCP.socket.connected, this.tls_ctx.?, this.tls_config.server_name) orelse { @@ -1066,8 +1216,47 @@ pub const PostgresSQLConnection = struct { this.start(); } + fn setupMaxLifetimeTimerIfNecessary(this: *PostgresSQLConnection) void { + if (this.max_lifetime_interval_ms == 0) return; + if (this.max_lifetime_timer.state == .ACTIVE) return; + + this.max_lifetime_timer.next = bun.timespec.msFromNow(@intCast(this.max_lifetime_interval_ms)); + this.globalObject.bunVM().timer.insert(&this.max_lifetime_timer); + } + + pub fn onConnectionTimeout(this: *PostgresSQLConnection) JSC.BunTimer.EventLoopTimer.Arm { + debug("onConnectionTimeout", .{}); + this.timer.state = .FIRED; + if (this.getTimeoutInterval() == 0) { + this.resetConnectionTimeout(); + return .disarm; + } + + switch (this.status) { + .connected => { + this.failFmt(.ERR_POSTGRES_IDLE_TIMEOUT, "Idle timeout reached after {}", .{bun.fmt.fmtDurationOneDecimal(@as(u64, this.idle_timeout_interval_ms) *| std.time.ns_per_ms)}); + }, + else => { + this.failFmt(.ERR_POSTGRES_CONNECTION_TIMEOUT, "Connection timeout after {}", .{bun.fmt.fmtDurationOneDecimal(@as(u64, this.connection_timeout_ms) *| std.time.ns_per_ms)}); + }, + .sent_startup_message => { + this.failFmt(.ERR_POSTGRES_CONNECTION_TIMEOUT, "Connection timed out after {} (sent startup message, but never received response)", .{bun.fmt.fmtDurationOneDecimal(@as(u64, this.connection_timeout_ms) *| std.time.ns_per_ms)}); + }, + } + return .disarm; + } + + pub fn onMaxLifetimeTimeout(this: *PostgresSQLConnection) JSC.BunTimer.EventLoopTimer.Arm { + debug("onMaxLifetimeTimeout", .{}); + this.max_lifetime_timer.state = .FIRED; + if (this.status == .failed) return .disarm; + this.failFmt(.ERR_POSTGRES_LIFETIME_TIMEOUT, "Max lifetime timeout reached after {}", .{bun.fmt.fmtDurationOneDecimal(@as(u64, this.max_lifetime_interval_ms) *| std.time.ns_per_ms)}); + return .disarm; + } fn start(this: *PostgresSQLConnection) void { + this.setupMaxLifetimeTimerIfNecessary(); + this.resetConnectionTimeout(); this.sendStartupMessage(); const event_loop = this.globalObject.bunVM().eventLoop(); @@ -1094,10 +1283,11 @@ pub const PostgresSQLConnection = struct { if (this.status == status) return; this.status = status; + this.resetConnectionTimeout(); + switch (status) { .connected => { - const on_connect = this.on_connect.swap(); - if (on_connect == .zero) return; + const on_connect = this.consumeOnConnectCallback(this.globalObject) orelse return; const js_value = this.js_value; js_value.ensureStillAlive(); this.globalObject.queueMicrotask(on_connect, &[_]JSValue{ JSValue.jsNull(), js_value }); @@ -1110,10 +1300,16 @@ pub const PostgresSQLConnection = struct { pub fn finalize(this: *PostgresSQLConnection) void { debug("PostgresSQLConnection finalize", .{}); + this.stopTimers(); this.js_value = .zero; this.deref(); } + pub fn flushDataAndResetTimeout(this: *PostgresSQLConnection) void { + this.resetConnectionTimeout(); + this.flushData(); + } + pub fn flushData(this: *PostgresSQLConnection) void { const chunk = this.write_buffer.remaining(); if (chunk.len == 0) return; @@ -1126,27 +1322,78 @@ pub const PostgresSQLConnection = struct { pub fn failWithJSValue(this: *PostgresSQLConnection, value: JSValue) void { defer this.updateHasPendingActivity(); + this.stopTimers(); if (this.status == .failed) return; this.status = .failed; - if (!this.socket.isClosed()) this.socket.close(); - const on_close = this.on_close.swap(); - if (on_close == .zero) return; + this.ref(); + defer this.deref(); + if (!this.socket.isClosed()) this.socket.close(); + const on_close = this.consumeOnCloseCallback(this.globalObject) orelse return; + + const loop = this.globalObject.bunVM().eventLoop(); + loop.enter(); + defer loop.exit(); _ = on_close.call( this.globalObject, this.js_value, &[_]JSValue{ value, + this.getQueriesArray(), }, ) catch |e| this.globalObject.reportActiveExceptionAsUnhandled(e); } - pub fn fail(this: *PostgresSQLConnection, message: []const u8, err: anyerror) void { + pub fn failFmt(this: *PostgresSQLConnection, comptime error_code: JSC.Error, comptime fmt: [:0]const u8, args: anytype) void { + this.failWithJSValue(error_code.fmt(this.globalObject, fmt, args)); + } + + pub fn fail(this: *PostgresSQLConnection, message: []const u8, err: AnyPostgresError) void { debug("failed: {s}: {s}", .{ message, @errorName(err) }); - const instance = this.globalObject.createErrorInstance("{s}", .{message}); - instance.put(this.globalObject, JSC.ZigString.static("code"), String.init(@errorName(err)).toJS(this.globalObject)); - this.failWithJSValue(instance); + + const globalObject = this.globalObject; + const error_code: JSC.Error = switch (err) { + error.ConnectionClosed => JSC.Error.ERR_POSTGRES_CONNECTION_CLOSED, + error.ExpectedRequest => JSC.Error.ERR_POSTGRES_EXPECTED_REQUEST, + error.ExpectedStatement => JSC.Error.ERR_POSTGRES_EXPECTED_STATEMENT, + error.InvalidBackendKeyData => JSC.Error.ERR_POSTGRES_INVALID_BACKEND_KEY_DATA, + error.InvalidBinaryData => JSC.Error.ERR_POSTGRES_INVALID_BINARY_DATA, + error.InvalidByteSequence => JSC.Error.ERR_POSTGRES_INVALID_BYTE_SEQUENCE, + error.InvalidByteSequenceForEncoding => JSC.Error.ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING, + error.InvalidCharacter => JSC.Error.ERR_POSTGRES_INVALID_CHARACTER, + error.InvalidMessage => JSC.Error.ERR_POSTGRES_INVALID_MESSAGE, + error.InvalidMessageLength => JSC.Error.ERR_POSTGRES_INVALID_MESSAGE_LENGTH, + error.InvalidQueryBinding => JSC.Error.ERR_POSTGRES_INVALID_QUERY_BINDING, + error.InvalidServerKey => JSC.Error.ERR_POSTGRES_INVALID_SERVER_KEY, + error.InvalidServerSignature => JSC.Error.ERR_POSTGRES_INVALID_SERVER_SIGNATURE, + error.MultidimensionalArrayNotSupportedYet => JSC.Error.ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET, + error.NullsInArrayNotSupportedYet => JSC.Error.ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET, + error.Overflow => JSC.Error.ERR_POSTGRES_OVERFLOW, + error.PBKDFD2 => JSC.Error.ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2, + error.SASL_SIGNATURE_MISMATCH => JSC.Error.ERR_POSTGRES_SASL_SIGNATURE_MISMATCH, + error.SASL_SIGNATURE_INVALID_BASE64 => JSC.Error.ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64, + error.TLSNotAvailable => JSC.Error.ERR_POSTGRES_TLS_NOT_AVAILABLE, + error.TLSUpgradeFailed => JSC.Error.ERR_POSTGRES_TLS_UPGRADE_FAILED, + error.UnexpectedMessage => JSC.Error.ERR_POSTGRES_UNEXPECTED_MESSAGE, + error.UNKNOWN_AUTHENTICATION_METHOD => JSC.Error.ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHOD, + error.UNSUPPORTED_AUTHENTICATION_METHOD => JSC.Error.ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD, + error.UnsupportedByteaFormat => JSC.Error.ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT, + error.UnsupportedIntegerSize => JSC.Error.ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE, + error.JSError => { + this.failWithJSValue(globalObject.takeException(error.JSError)); + return; + }, + error.OutOfMemory => { + // TODO: add binding for creating an out of memory error? + this.failWithJSValue(globalObject.takeException(globalObject.throwOutOfMemory())); + return; + }, + error.ShortRead => { + bun.unreachablePanic("Assertion failed: ShortRead should be handled by the caller in postgres", .{}); + }, + }; + this.failWithJSValue(error_code.fmt(globalObject, "{s}", .{message})); } pub fn onClose(this: *PostgresSQLConnection) void { @@ -1210,22 +1457,35 @@ pub const PostgresSQLConnection = struct { pub fn onHandshake(this: *PostgresSQLConnection, success: i32, ssl_error: uws.us_bun_verify_error_t) void { debug("onHandshake: {d} {d}", .{ success, ssl_error.error_no }); + if (this.tls_config.reject_unauthorized == 0) { + return; + } + + const do_tls_verification = switch (this.ssl_mode) { + // https://github.com/porsager/postgres/blob/6ec85a432b17661ccacbdf7f765c651e88969d36/src/connection.js#L272-L279 + .verify_ca, .verify_full => true, + else => false, + }; + + if (!do_tls_verification) { + return; + } + if (success != 1) { this.failWithJSValue(ssl_error.toJS(this.globalObject)); return; } - if (this.tls_config.reject_unauthorized == 1) { - if (ssl_error.error_no != 0) { + if (ssl_error.error_no != 0) { + this.failWithJSValue(ssl_error.toJS(this.globalObject)); + return; + } + + const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); + if (BoringSSL.SSL_get_servername(ssl_ptr, 0)) |servername| { + const hostname = servername[0..bun.len(servername)]; + if (!BoringSSL.checkServerIdentity(ssl_ptr, hostname)) { this.failWithJSValue(ssl_error.toJS(this.globalObject)); - return; - } - const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle())); - if (BoringSSL.SSL_get_servername(ssl_ptr, 0)) |servername| { - const hostname = servername[0..bun.len(servername)]; - if (!BoringSSL.checkServerIdentity(ssl_ptr, hostname)) { - this.failWithJSValue(ssl_error.toJS(this.globalObject)); - } } } } @@ -1264,6 +1524,7 @@ pub const PostgresSQLConnection = struct { this.poll_ref.ref(vm); } + this.resetConnectionTimeout(); this.deref(); } @@ -1299,11 +1560,8 @@ pub const PostgresSQLConnection = struct { this.read_buffer.byte_list.len = 0; this.read_buffer.write(bun.default_allocator, data[offset..]) catch @panic("failed to write to read buffer"); } else { - if (comptime bun.Environment.allow_assert) { - if (@errorReturnTrace()) |trace| { - debug("Error: {s}\n{}", .{ @errorName(err), trace }); - } - } + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + this.fail("Failed to read data", err); } }; @@ -1315,11 +1573,7 @@ pub const PostgresSQLConnection = struct { this.read_buffer.write(bun.default_allocator, data) catch @panic("failed to write to read buffer"); PostgresRequest.onData(this, Reader, this.bufferedReader()) catch |err| { if (err != error.ShortRead) { - if (comptime bun.Environment.allow_assert) { - if (@errorReturnTrace()) |trace| { - debug("Error: {s}\n{}", .{ @errorName(err), trace }); - } - } + bun.handleErrorReturnTrace(err, @errorReturnTrace()); this.fail("Failed to read data", err); return; } @@ -1363,7 +1617,7 @@ pub const PostgresSQLConnection = struct { pub fn call(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { var vm = globalObject.bunVM(); - const arguments = callframe.arguments_old(10).slice(); + const arguments = callframe.arguments_old(13).slice(); const hostname_str = arguments[0].toBunString(globalObject); defer hostname_str.deref(); const port = arguments[1].coerce(i32, globalObject); @@ -1460,13 +1714,15 @@ pub const PostgresSQLConnection = struct { const on_connect = arguments[8]; const on_close = arguments[9]; + const idle_timeout = arguments[10].toInt32(); + const connection_timeout = arguments[11].toInt32(); + const max_lifetime = arguments[12].toInt32(); - var ptr = try bun.default_allocator.create(PostgresSQLConnection); + const ptr: *PostgresSQLConnection = try bun.default_allocator.create(PostgresSQLConnection); ptr.* = PostgresSQLConnection{ .globalObject = globalObject, - .on_connect = JSC.Strong.create(on_connect, globalObject), - .on_close = JSC.Strong.create(on_close, globalObject), + .database = database, .user = username, .password = password, @@ -1479,6 +1735,9 @@ pub const PostgresSQLConnection = struct { .tls_ctx = tls_ctx, .ssl_mode = ssl_mode, .tls_status = if (ssl_mode != .disable) .pending else .none, + .idle_timeout_interval_ms = @intCast(idle_timeout), + .connection_timeout_ms = @intCast(connection_timeout), + .max_lifetime_interval_ms = @intCast(max_lifetime), }; ptr.updateHasPendingActivity(); @@ -1487,6 +1746,9 @@ pub const PostgresSQLConnection = struct { js_value.ensureStillAlive(); ptr.js_value = js_value; + PostgresSQLConnection.onconnectSetCached(js_value, globalObject, on_connect); + PostgresSQLConnection.oncloseSetCached(js_value, globalObject, on_close); + { const hostname = hostname_str.toUTF8(bun.default_allocator); defer hostname.deinit(); @@ -1498,6 +1760,7 @@ pub const PostgresSQLConnection = struct { vm.rareData().postgresql_context.tcp = ctx_; break :brk ctx_; }; + ptr.socket = .{ .SocketTCP = uws.SocketTCP.connectAnon(hostname.slice(), port, ctx, ptr, false) catch |err| { tls_config.deinit(); @@ -1508,6 +1771,8 @@ pub const PostgresSQLConnection = struct { return globalObject.throwError(err, "failed to connect to postgresql"); }, }; + + ptr.resetConnectionTimeout(); } return js_value; @@ -1600,7 +1865,17 @@ pub const PostgresSQLConnection = struct { return .undefined; } + pub fn stopTimers(this: *PostgresSQLConnection) void { + if (this.timer.state == .ACTIVE) { + this.globalObject.bunVM().timer.remove(&this.timer); + } + if (this.max_lifetime_timer.state == .ACTIVE) { + this.globalObject.bunVM().timer.remove(&this.max_lifetime_timer); + } + } + pub fn deinit(this: *@This()) void { + this.stopTimers(); var iter = this.statements.valueIterator(); while (iter.next()) |stmt_ptr| { var stmt = stmt_ptr.*; @@ -1609,8 +1884,6 @@ pub const PostgresSQLConnection = struct { this.statements.deinit(bun.default_allocator); this.write_buffer.deinit(bun.default_allocator); this.read_buffer.deinit(bun.default_allocator); - this.on_close.deinit(); - this.on_connect.deinit(); this.backend_parameters.deinit(); bun.default_allocator.free(this.options_buf); this.tls_config.deinit(); @@ -1618,6 +1891,8 @@ pub const PostgresSQLConnection = struct { } pub fn disconnect(this: *@This()) void { + this.stopTimers(); + if (this.status == .connected) { this.status = .disconnected; this.poll_ref.disable(); @@ -1636,12 +1911,12 @@ pub const PostgresSQLConnection = struct { pub const Writer = struct { connection: *PostgresSQLConnection, - pub fn write(this: Writer, data: []const u8) anyerror!void { + pub fn write(this: Writer, data: []const u8) AnyPostgresError!void { var buffer = &this.connection.write_buffer; try buffer.write(bun.default_allocator, data); } - pub fn pwrite(this: Writer, data: []const u8, index: usize) anyerror!void { + pub fn pwrite(this: Writer, data: []const u8, index: usize) AnyPostgresError!void { @memcpy(this.connection.write_buffer.byte_list.slice()[index..][0..data.len], data); } @@ -1676,7 +1951,7 @@ pub const PostgresSQLConnection = struct { pub fn ensureCapacity(this: Reader, count: usize) bool { return @as(usize, this.connection.read_buffer.head) + count <= @as(usize, this.connection.read_buffer.byte_list.len); } - pub fn read(this: Reader, count: usize) anyerror!Data { + pub fn read(this: Reader, count: usize) AnyPostgresError!Data { var remaining = this.connection.read_buffer.remaining(); if (@as(usize, remaining.len) < count) { return error.ShortRead; @@ -1687,7 +1962,7 @@ pub const PostgresSQLConnection = struct { .temporary = remaining[0..count], }; } - pub fn readZ(this: Reader) anyerror!Data { + pub fn readZ(this: Reader) AnyPostgresError!Data { const remain = this.connection.read_buffer.remaining(); if (bun.strings.indexOfChar(remain, 0)) |zero| { @@ -1799,7 +2074,7 @@ pub const PostgresSQLConnection = struct { } } - pub fn fromBytes(binary: bool, oid: int4, bytes: []const u8, globalObject: *JSC.JSGlobalObject) anyerror!DataCell { + pub fn fromBytes(binary: bool, oid: int4, bytes: []const u8, globalObject: *JSC.JSGlobalObject) !DataCell { switch (@as(types.Tag, @enumFromInt(@as(short, @intCast(oid))))) { // TODO: .int2_array, .float8_array inline .int4_array, .float4_array => |tag| { @@ -1876,7 +2151,7 @@ pub const PostgresSQLConnection = struct { return DataCell{ .tag = .float8, .value = .{ .float8 = float4 } }; } }, - .json => { + .jsonb, .json => { return DataCell{ .tag = .json, .value = .{ .json = String.createUTF8(bytes).value.WTFStringImpl }, .free_value = 1 }; }, .bool => { @@ -1956,7 +2231,7 @@ pub const PostgresSQLConnection = struct { return pg_ntoT(32, x); } - pub fn parseBinary(comptime tag: types.Tag, comptime ReturnType: type, bytes: []const u8) anyerror!ReturnType { + pub fn parseBinary(comptime tag: types.Tag, comptime ReturnType: type, bytes: []const u8) AnyPostgresError!ReturnType { switch (comptime tag) { .float8 => { return @as(f64, @bitCast(try parseBinary(.int8, i64, bytes))); @@ -2017,7 +2292,7 @@ pub const PostgresSQLConnection = struct { return JSC__constructObjectFromDataCell(globalObject, array, structure, this.list.ptr, @truncate(this.fields.len)); } - pub fn put(this: *Putter, index: u32, optional_bytes: ?*Data) anyerror!bool { + pub fn put(this: *Putter, index: u32, optional_bytes: ?*Data) !bool { const oid = this.fields[index].type_oid; debug("index: {d}, oid: {d}", .{ index, oid }); @@ -2072,7 +2347,7 @@ pub const PostgresSQLConnection = struct { const binding_value = PostgresSQLQuery.bindingGetCached(req.thisValue) orelse .zero; const columns_value = PostgresSQLQuery.columnsGetCached(req.thisValue) orelse .zero; PostgresRequest.bindAndExecute(this.globalObject, stmt, binding_value, columns_value, PostgresSQLConnection.Writer, this.writer()) catch |err| { - req.onWriteFail(err, this.globalObject); + req.onWriteFail(err, this.globalObject, this.getQueriesArray()); req.deref(); this.requests.discard(1); continue; @@ -2091,7 +2366,11 @@ pub const PostgresSQLConnection = struct { return any; } - pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.EnumLiteral), comptime Context: type, reader: protocol.NewReader(Context)) !void { + pub fn getQueriesArray(this: *const PostgresSQLConnection) JSValue { + return PostgresSQLConnection.queriesGetCached(this.js_value) orelse .zero; + } + + pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.EnumLiteral), comptime Context: type, reader: protocol.NewReader(Context)) AnyPostgresError!void { debug("on({s})", .{@tagName(MessageType)}); if (comptime MessageType != .ReadyForQuery) { this.is_ready_for_query = false; @@ -2181,7 +2460,7 @@ pub const PostgresSQLConnection = struct { debug("-> {s}", .{cmd.command_tag.slice()}); _ = this.requests.discard(1); defer this.updateRef(); - request.onSuccess(cmd.command_tag.slice(), this.globalObject); + request.onSuccess(cmd.command_tag.slice(), this.globalObject, this.js_value); }, .BindComplete => { try reader.eatMessage(protocol.BindComplete); @@ -2255,7 +2534,12 @@ pub const PostgresSQLConnection = struct { const iteration_count = try cont.iterationCount(); - const server_salt_decoded_base64 = try bun.base64.decodeAlloc(bun.z_allocator, cont.s); + const server_salt_decoded_base64 = bun.base64.decodeAlloc(bun.z_allocator, cont.s) catch |err| { + return switch (err) { + error.DecodingFailed => error.SASL_SIGNATURE_INVALID_BASE64, + else => |e| e, + }; + }; defer bun.z_allocator.free(server_salt_decoded_base64); try sasl.computeSaltedPassword(server_salt_decoded_base64, iteration_count, this); @@ -2352,8 +2636,46 @@ pub const PostgresSQLConnection = struct { this.flushData(); }, + .MD5Password => |md5| { + debug("MD5Password", .{}); + // Format is: md5 + md5(md5(password + username) + salt) + var first_hash_buf: bun.sha.MD5.Digest = undefined; + var first_hash_str: [32]u8 = undefined; + var final_hash_buf: bun.sha.MD5.Digest = undefined; + var final_hash_str: [32]u8 = undefined; + var final_password_buf: [36]u8 = undefined; + + // First hash: md5(password + username) + var first_hasher = bun.sha.MD5.init(); + first_hasher.update(this.password); + first_hasher.update(this.user); + first_hasher.final(&first_hash_buf); + const first_hash_str_output = std.fmt.bufPrint(&first_hash_str, "{x}", .{std.fmt.fmtSliceHexLower(&first_hash_buf)}) catch unreachable; + + // Second hash: md5(first_hash + salt) + var final_hasher = bun.sha.MD5.init(); + final_hasher.update(first_hash_str_output); + final_hasher.update(&md5.salt); + final_hasher.final(&final_hash_buf); + const final_hash_str_output = std.fmt.bufPrint(&final_hash_str, "{x}", .{std.fmt.fmtSliceHexLower(&final_hash_buf)}) catch unreachable; + + // Format final password as "md5" + final_hash + const final_password = std.fmt.bufPrintZ(&final_password_buf, "md5{s}", .{final_hash_str_output}) catch unreachable; + + var response = protocol.PasswordMessage{ + .password = .{ + .temporary = final_password, + }, + }; + + this.authentication_state = .{ .md5 = {} }; + try response.writeInternal(PostgresSQLConnection.Writer, this.writer()); + this.flushData(); + }, + else => { debug("TODO auth: {s}", .{@tagName(std.meta.activeTag(auth))}); + this.fail("TODO: support authentication method: {s}", error.UNSUPPORTED_AUTHENTICATION_METHOD); }, } }, @@ -2371,19 +2693,12 @@ pub const PostgresSQLConnection = struct { var err: protocol.ErrorResponse = undefined; try err.decodeInternal(Context, reader); - if (this.status == .connecting) { - this.status = .failed; + if (this.status == .connecting or this.status == .sent_startup_message) { defer { err.deinit(); - this.poll_ref.unref(this.globalObject.bunVM()); - this.updateHasPendingActivity(); } - const on_connect = this.on_connect.swap(); - if (on_connect == .zero) return; - const js_value = this.js_value; - js_value.ensureStillAlive(); - this.globalObject.queueMicrotask(on_connect, &[_]JSValue{ err.toJS(this.globalObject), js_value }); + this.failWithJSValue(err.toJS(this.globalObject)); // it shouldn't enqueue any requests while connecting bun.assert(this.requests.count == 0); @@ -2426,7 +2741,7 @@ pub const PostgresSQLConnection = struct { try reader.eatMessage(protocol.CloseComplete); var request = this.current() orelse return error.ExpectedRequest; _ = this.requests.discard(1); - request.onSuccess("CLOSECOMPLETE", this.globalObject); + request.onSuccess("CLOSECOMPLETE", this.globalObject, this.getQueriesArray()); }, .CopyInResponse => { debug("TODO CopyInResponse", .{}); @@ -2443,7 +2758,7 @@ pub const PostgresSQLConnection = struct { var request = this.current() orelse return error.ExpectedRequest; _ = this.requests.discard(1); this.updateRef(); - request.onSuccess("", this.globalObject); + request.onSuccess("", this.globalObject, this.getQueriesArray()); }, .CopyOutResponse => { debug("TODO CopyOutResponse", .{}); @@ -2467,25 +2782,21 @@ pub const PostgresSQLConnection = struct { } } - pub fn doFlush(this: *PostgresSQLConnection, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { - _ = callframe; - _ = globalObject; - _ = this; - - return .undefined; - } - - pub fn createQuery(this: *PostgresSQLConnection, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { - _ = callframe; - _ = globalObject; - _ = this; - - return .undefined; - } - pub fn getConnected(this: *PostgresSQLConnection, _: *JSC.JSGlobalObject) JSValue { return JSValue.jsBoolean(this.status == Status.connected); } + + pub fn consumeOnConnectCallback(this: *const PostgresSQLConnection, globalObject: *JSC.JSGlobalObject) ?JSC.JSValue { + const on_connect = PostgresSQLConnection.onconnectGetCached(this.js_value) orelse return null; + PostgresSQLConnection.onconnectSetCached(this.js_value, globalObject, .zero); + return on_connect; + } + + pub fn consumeOnCloseCallback(this: *const PostgresSQLConnection, globalObject: *JSC.JSGlobalObject) ?JSC.JSValue { + const on_close = PostgresSQLConnection.oncloseGetCached(this.js_value) orelse return null; + PostgresSQLConnection.oncloseSetCached(this.js_value, globalObject, .zero); + return on_close; + } }; pub const PostgresSQLStatement = struct { @@ -2723,7 +3034,7 @@ const Signature = struct { .float8 => try name.appendSlice(".float8"), .float4 => try name.appendSlice(".float4"), .numeric => try name.appendSlice(".numeric"), - .json => try name.appendSlice(".json"), + .json, .jsonb => try name.appendSlice(".json"), .bool => try name.appendSlice(".bool"), .timestamp => try name.appendSlice(".timestamp"), .timestamptz => try name.appendSlice(".timestamptz"), diff --git a/src/sql/postgres/postgres_protocol.zig b/src/sql/postgres/postgres_protocol.zig index 4aee1791f9..60eeaf9f9d 100644 --- a/src/sql/postgres/postgres_protocol.zig +++ b/src/sql/postgres/postgres_protocol.zig @@ -15,7 +15,7 @@ const int4 = postgres.int4; const int8 = postgres.int8; const PostgresInt64 = postgres.PostgresInt64; const types = postgres.types; - +const AnyPostgresError = postgres.AnyPostgresError; pub const ArrayList = struct { array: *std.ArrayList(u8), @@ -23,11 +23,11 @@ pub const ArrayList = struct { return this.array.items.len; } - pub fn write(this: @This(), bytes: []const u8) anyerror!void { + pub fn write(this: @This(), bytes: []const u8) AnyPostgresError!void { try this.array.appendSlice(bytes); } - pub fn pwrite(this: @This(), bytes: []const u8, i: usize) anyerror!void { + pub fn pwrite(this: @This(), bytes: []const u8, i: usize) AnyPostgresError!void { @memcpy(this.array.items[i..][0..bytes.len], bytes); } @@ -71,7 +71,7 @@ pub const StackReader = struct { pub fn ensureCapacity(this: StackReader, count: usize) bool { return this.buffer.len >= (this.offset.* + count); } - pub fn read(this: StackReader, count: usize) anyerror!Data { + pub fn read(this: StackReader, count: usize) AnyPostgresError!Data { const offset = this.offset.*; if (!this.ensureCapacity(count)) { return error.ShortRead; @@ -82,7 +82,7 @@ pub const StackReader = struct { .temporary = this.buffer[offset..this.offset.*], }; } - pub fn readZ(this: StackReader) anyerror!Data { + pub fn readZ(this: StackReader) AnyPostgresError!Data { const remaining = this.peek(); if (bun.strings.indexOfChar(remaining, 0)) |zero| { this.skip(zero + 1); @@ -98,8 +98,8 @@ pub const StackReader = struct { pub fn NewWriterWrap( comptime Context: type, comptime offsetFn_: (fn (ctx: Context) usize), - comptime writeFunction_: (fn (ctx: Context, bytes: []const u8) anyerror!void), - comptime pwriteFunction_: (fn (ctx: Context, bytes: []const u8, offset: usize) anyerror!void), + comptime writeFunction_: (fn (ctx: Context, bytes: []const u8) AnyPostgresError!void), + comptime pwriteFunction_: (fn (ctx: Context, bytes: []const u8, offset: usize) AnyPostgresError!void), ) type { return struct { wrapped: Context, @@ -111,7 +111,7 @@ pub fn NewWriterWrap( pub const WrappedWriter = @This(); - pub inline fn write(this: @This(), data: []const u8) anyerror!void { + pub inline fn write(this: @This(), data: []const u8) AnyPostgresError!void { try writeFn(this.wrapped, data); } @@ -119,16 +119,16 @@ pub fn NewWriterWrap( index: usize, context: WrappedWriter, - pub fn write(this: LengthWriter) anyerror!void { + pub fn write(this: LengthWriter) AnyPostgresError!void { try this.context.pwrite(&Int32(this.context.offset() - this.index), this.index); } - pub fn writeExcludingSelf(this: LengthWriter) anyerror!void { + pub fn writeExcludingSelf(this: LengthWriter) AnyPostgresError!void { try this.context.pwrite(&Int32(this.context.offset() -| (this.index + 4)), this.index); } }; - pub inline fn length(this: @This()) anyerror!LengthWriter { + pub inline fn length(this: @This()) AnyPostgresError!LengthWriter { const i = this.offset(); try this.int4(0); return LengthWriter{ @@ -141,7 +141,7 @@ pub fn NewWriterWrap( return offsetFn(this.wrapped); } - pub inline fn pwrite(this: @This(), data: []const u8, i: usize) anyerror!void { + pub inline fn pwrite(this: @This(), data: []const u8, i: usize) AnyPostgresError!void { try pwriteFn(this.wrapped, data, i); } @@ -208,81 +208,81 @@ pub fn NewWriterWrap( pub const FieldType = enum(u8) { /// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a localized translation of one of these. Always present. - S = 'S', + severity = 'S', /// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message). This is identical to the S field except that the contents are never localized. This is present only in messages generated by PostgreSQL versions 9.6 and later. - V = 'V', + localized_severity = 'V', /// Code: the SQLSTATE code for the error (see Appendix A). Not localizable. Always present. - C = 'C', + code = 'C', /// Message: the primary human-readable error message. This should be accurate but terse (typically one line). Always present. - M = 'M', + message = 'M', /// Detail: an optional secondary error message carrying more detail about the problem. Might run to multiple lines. - D = 'D', + detail = 'D', /// Hint: an optional suggestion what to do about the problem. This is intended to differ from Detail in that it offers advice (potentially inappropriate) rather than hard facts. Might run to multiple lines. - H = 'H', + hint = 'H', /// Position: the field value is a decimal ASCII integer, indicating an error cursor position as an index into the original query string. The first character has index 1, and positions are measured in characters not bytes. - P = 'P', + position = 'P', /// Internal position: this is defined the same as the P field, but it is used when the cursor position refers to an internally generated command rather than the one submitted by the client. The q field will always appear when this field appears. - p = 'p', + internal_position = 'p', /// Internal query: the text of a failed internally-generated command. This could be, for example, an SQL query issued by a PL/pgSQL function. - q = 'q', + internal = 'q', /// Where: an indication of the context in which the error occurred. Presently this includes a call stack traceback of active procedural language functions and internally-generated queries. The trace is one entry per line, most recent first. - W = 'W', + where = 'W', /// Schema name: if the error was associated with a specific database object, the name of the schema containing that object, if any. - s = 's', + schema = 's', /// Table name: if the error was associated with a specific table, the name of the table. (Refer to the schema name field for the name of the table's schema.) - t = 't', + table = 't', /// Column name: if the error was associated with a specific table column, the name of the column. (Refer to the schema and table name fields to identify the table.) - c = 'c', + column = 'c', /// Data type name: if the error was associated with a specific data type, the name of the data type. (Refer to the schema name field for the name of the data type's schema.) - d = 'd', + datatype = 'd', /// Constraint name: if the error was associated with a specific constraint, the name of the constraint. Refer to fields listed above for the associated table or domain. (For this purpose, indexes are treated as constraints, even if they weren't created with constraint syntax.) - n = 'n', + constraint = 'n', /// File: the file name of the source-code location where the error was reported. - F = 'F', + file = 'F', /// Line: the line number of the source-code location where the error was reported. - L = 'L', + line = 'L', /// Routine: the name of the source-code routine reporting the error. - R = 'R', + routine = 'R', _, }; pub const FieldMessage = union(FieldType) { - S: String, - V: String, - C: String, - M: String, - D: String, - H: String, - P: String, - p: String, - q: String, - W: String, - s: String, - t: String, - c: String, - d: String, - n: String, - F: String, - L: String, - R: String, + severity: String, + localized_severity: String, + code: String, + message: String, + detail: String, + hint: String, + position: String, + internal_position: String, + internal: String, + where: String, + schema: String, + table: String, + column: String, + datatype: String, + constraint: String, + file: String, + line: String, + routine: String, pub fn format(this: FieldMessage, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { switch (this) { @@ -319,24 +319,25 @@ pub const FieldMessage = union(FieldType) { pub fn init(tag: FieldType, message: []const u8) !FieldMessage { return switch (tag) { - .S => FieldMessage{ .S = String.createUTF8(message) }, - .V => FieldMessage{ .V = String.createUTF8(message) }, - .C => FieldMessage{ .C = String.createUTF8(message) }, - .M => FieldMessage{ .M = String.createUTF8(message) }, - .D => FieldMessage{ .D = String.createUTF8(message) }, - .H => FieldMessage{ .H = String.createUTF8(message) }, - .P => FieldMessage{ .P = String.createUTF8(message) }, - .p => FieldMessage{ .p = String.createUTF8(message) }, - .q => FieldMessage{ .q = String.createUTF8(message) }, - .W => FieldMessage{ .W = String.createUTF8(message) }, - .s => FieldMessage{ .s = String.createUTF8(message) }, - .t => FieldMessage{ .t = String.createUTF8(message) }, - .c => FieldMessage{ .c = String.createUTF8(message) }, - .d => FieldMessage{ .d = String.createUTF8(message) }, - .n => FieldMessage{ .n = String.createUTF8(message) }, - .F => FieldMessage{ .F = String.createUTF8(message) }, - .L => FieldMessage{ .L = String.createUTF8(message) }, - .R => FieldMessage{ .R = String.createUTF8(message) }, + .severity => FieldMessage{ .severity = String.createUTF8(message) }, + // Ignore this one for now. + // .localized_severity => FieldMessage{ .localized_severity = String.createUTF8(message) }, + .code => FieldMessage{ .code = String.createUTF8(message) }, + .message => FieldMessage{ .message = String.createUTF8(message) }, + .detail => FieldMessage{ .detail = String.createUTF8(message) }, + .hint => FieldMessage{ .hint = String.createUTF8(message) }, + .position => FieldMessage{ .position = String.createUTF8(message) }, + .internal_position => FieldMessage{ .internal_position = String.createUTF8(message) }, + .internal => FieldMessage{ .internal = String.createUTF8(message) }, + .where => FieldMessage{ .where = String.createUTF8(message) }, + .schema => FieldMessage{ .schema = String.createUTF8(message) }, + .table => FieldMessage{ .table = String.createUTF8(message) }, + .column => FieldMessage{ .column = String.createUTF8(message) }, + .datatype => FieldMessage{ .datatype = String.createUTF8(message) }, + .constraint => FieldMessage{ .constraint = String.createUTF8(message) }, + .file => FieldMessage{ .file = String.createUTF8(message) }, + .line => FieldMessage{ .line = String.createUTF8(message) }, + .routine => FieldMessage{ .routine = String.createUTF8(message) }, else => error.UnknownFieldType, }; } @@ -348,8 +349,8 @@ pub fn NewReaderWrap( comptime peekFn_: (fn (ctx: Context) []const u8), comptime skipFn_: (fn (ctx: Context, count: usize) void), comptime ensureCapacityFn_: (fn (ctx: Context, count: usize) bool), - comptime readFunction_: (fn (ctx: Context, count: usize) anyerror!Data), - comptime readZ_: (fn (ctx: Context) anyerror!Data), + comptime readFunction_: (fn (ctx: Context, count: usize) AnyPostgresError!Data), + comptime readZ_: (fn (ctx: Context) AnyPostgresError!Data), ) type { return struct { wrapped: Context, @@ -366,11 +367,11 @@ pub fn NewReaderWrap( markMessageStartFn(this.wrapped); } - pub inline fn read(this: @This(), count: usize) anyerror!Data { + pub inline fn read(this: @This(), count: usize) AnyPostgresError!Data { return try readFn(this.wrapped, count); } - pub inline fn eatMessage(this: @This(), comptime msg_: anytype) anyerror!void { + pub inline fn eatMessage(this: @This(), comptime msg_: anytype) AnyPostgresError!void { const msg = msg_[1..]; try this.ensureCapacity(msg.len); @@ -380,7 +381,7 @@ pub fn NewReaderWrap( return error.InvalidMessage; } - pub fn skip(this: @This(), count: usize) anyerror!void { + pub fn skip(this: @This(), count: usize) AnyPostgresError!void { skipFn(this.wrapped, count); } @@ -388,11 +389,11 @@ pub fn NewReaderWrap( return peekFn(this.wrapped); } - pub inline fn readZ(this: @This()) anyerror!Data { + pub inline fn readZ(this: @This()) AnyPostgresError!Data { return try readZFn(this.wrapped); } - pub inline fn ensureCapacity(this: @This(), count: usize) anyerror!void { + pub inline fn ensureCapacity(this: @This(), count: usize) AnyPostgresError!void { if (!ensureCapacityFn(this.wrapped, count)) { return error.ShortRead; } @@ -457,7 +458,7 @@ pub fn NewWriter(comptime Context: type) type { fn decoderWrap(comptime Container: type, comptime decodeFn: anytype) type { return struct { - pub fn decode(this: *Container, context: anytype) anyerror!void { + pub fn decode(this: *Container, context: anytype) AnyPostgresError!void { const Context = @TypeOf(context); try decodeFn(this, Context, NewReader(Context){ .wrapped = context }); } @@ -466,7 +467,7 @@ fn decoderWrap(comptime Container: type, comptime decodeFn: anytype) type { fn writeWrap(comptime Container: type, comptime writeFn: anytype) type { return struct { - pub fn write(this: *Container, context: anytype) anyerror!void { + pub fn write(this: *Container, context: anytype) AnyPostgresError!void { const Context = @TypeOf(context); try writeFn(this, Context, NewWriter(Context){ .wrapped = context }); } @@ -538,9 +539,6 @@ pub const Authentication = union(enum) { }, 5 => { if (message_length != 12) return error.InvalidMessageLength; - if (!try reader.expectInt(u32, 5)) { - return error.InvalidMessage; - } var salt_data = try reader.bytes(4); defer salt_data.deinit(); this.* = .{ @@ -722,23 +720,117 @@ pub const ErrorResponse = struct { var b = bun.StringBuilder{}; defer b.deinit(bun.default_allocator); - for (this.messages.items) |msg| { - b.cap += switch (msg) { + // Pre-calculate capacity to avoid reallocations + for (this.messages.items) |*msg| { + b.cap += switch (msg.*) { inline else => |m| m.utf8ByteLength(), } + 1; } b.allocate(bun.default_allocator) catch {}; - for (this.messages.items) |msg| { - var str = switch (msg) { - inline else => |m| m.toUTF8(bun.default_allocator), - }; - defer str.deinit(); - _ = b.append(str.slice()); - _ = b.append("\n"); + // Build a more structured error message + var severity: String = String.dead; + var code: String = String.dead; + var message: String = String.dead; + var detail: String = String.dead; + var hint: String = String.dead; + var position: String = String.dead; + var where: String = String.dead; + var schema: String = String.dead; + var table: String = String.dead; + var column: String = String.dead; + var datatype: String = String.dead; + var constraint: String = String.dead; + var file: String = String.dead; + var line: String = String.dead; + var routine: String = String.dead; + + for (this.messages.items) |*msg| { + switch (msg.*) { + .severity => |str| severity = str, + .code => |str| code = str, + .message => |str| message = str, + .detail => |str| detail = str, + .hint => |str| hint = str, + .position => |str| position = str, + .where => |str| where = str, + .schema => |str| schema = str, + .table => |str| table = str, + .column => |str| column = str, + .datatype => |str| datatype = str, + .constraint => |str| constraint = str, + .file => |str| file = str, + .line => |str| line = str, + .routine => |str| routine = str, + else => {}, + } } - return globalObject.createSyntaxErrorInstance("Postgres error occurred\n{s}", .{b.allocatedSlice()[0..b.len]}); + var needs_newline = false; + construct_message: { + if (!message.isEmpty()) { + _ = b.appendStr(message); + needs_newline = true; + break :construct_message; + } + if (!detail.isEmpty()) { + if (needs_newline) { + _ = b.append("\n"); + } else { + _ = b.append(" "); + } + needs_newline = true; + _ = b.appendStr(detail); + } + if (!hint.isEmpty()) { + if (needs_newline) { + _ = b.append("\n"); + } else { + _ = b.append(" "); + } + needs_newline = true; + _ = b.appendStr(hint); + } + } + + const possible_fields = .{ + .{ "detail", detail, void }, + .{ "hint", hint, void }, + .{ "column", column, void }, + .{ "constraint", constraint, void }, + .{ "datatype", datatype, void }, + .{ "errno", code, i32 }, + .{ "position", position, i32 }, + .{ "schema", schema, void }, + .{ "table", table, void }, + .{ "where", where, void }, + }; + + const error_code: JSC.Error = + // https://www.postgresql.org/docs/8.1/errcodes-appendix.html + if (code.toInt32() orelse 0 == 42601) + JSC.Error.ERR_POSTGRES_SYNTAX_ERROR + else + JSC.Error.ERR_POSTGRES_SERVER_ERROR; + const err = error_code.fmt(globalObject, "{s}", .{b.allocatedSlice()[0..b.len]}); + + inline for (possible_fields) |field| { + if (!field.@"1".isEmpty()) { + const value = brk: { + if (field.@"2" == i32) { + if (field.@"1".toInt32()) |val| { + break :brk JSC.JSValue.jsNumberFromInt32(val); + } + } + + break :brk field.@"1".toJS(globalObject); + }; + + err.put(globalObject, JSC.ZigString.static(field.@"0"), value); + } + } + + return err; } }; @@ -847,7 +939,7 @@ pub const FormatCode = enum { pub const null_int4 = 4294967295; pub const DataRow = struct { - pub fn decode(context: anytype, comptime ContextType: type, reader: NewReader(ContextType), comptime forEach: fn (@TypeOf(context), index: u32, bytes: ?*Data) anyerror!bool) anyerror!void { + pub fn decode(context: anytype, comptime ContextType: type, reader: NewReader(ContextType), comptime forEach: fn (@TypeOf(context), index: u32, bytes: ?*Data) AnyPostgresError!bool) AnyPostgresError!void { var remaining_bytes = try reader.length(); remaining_bytes -|= 4; @@ -885,7 +977,7 @@ pub const FieldDescription = struct { this.name.deinit(); } - pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) !void { + pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReader(Container)) AnyPostgresError!void { var name = try reader.readZ(); errdefer { name.deinit(); @@ -1355,6 +1447,29 @@ pub const NoticeResponse = struct { } } pub const decode = decoderWrap(NoticeResponse, decodeInternal).decode; + + pub fn toJS(this: NoticeResponse, globalObject: *JSC.JSGlobalObject) JSValue { + var b = bun.StringBuilder{}; + defer b.deinit(bun.default_allocator); + + for (this.messages.items) |msg| { + b.cap += switch (msg) { + inline else => |m| m.utf8ByteLength(), + } + 1; + } + b.allocate(bun.default_allocator) catch {}; + + for (this.messages.items) |msg| { + var str = switch (msg) { + inline else => |m| m.toUTF8(bun.default_allocator), + }; + defer str.deinit(); + _ = b.append(str.slice()); + _ = b.append("\n"); + } + + return JSC.ZigString.init(b.allocatedSlice()[0..b.len]).toJS(globalObject); + } }; pub const CopyFail = struct { diff --git a/src/sql/postgres/postgres_types.zig b/src/sql/postgres/postgres_types.zig index 498b7f1d0a..74e8ff104b 100644 --- a/src/sql/postgres/postgres_types.zig +++ b/src/sql/postgres/postgres_types.zig @@ -12,6 +12,7 @@ const JSValue = JSC.JSValue; const JSC = bun.JSC; const short = postgres.short; const int4 = postgres.int4; +const AnyPostgresError = postgres.AnyPostgresError; // select b.typname, b.oid, b.typarray // from pg_catalog.pg_type a @@ -169,8 +170,17 @@ pub const Tag = enum(short) { bit_array = 1561, varbit_array = 1563, numeric_array = 1231, + jsonb = 3802, + jsonb_array = 3807, + // Not really sure what this is. + jsonpath = 4072, + jsonpath_array = 4073, _, + pub fn name(this: Tag) ?[]const u8 { + return std.enums.tagName(Tag, this); + } + pub fn isBinaryFormatSupported(this: Tag) bool { return switch (this) { // TODO: .int2_array, .float8_array, @@ -282,7 +292,7 @@ pub const Tag = enum(short) { globalObject: *JSC.JSGlobalObject, comptime Type: type, value: Type, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { switch (tag) { .numeric => { return numeric.toJS(globalObject, value); @@ -292,7 +302,7 @@ pub const Tag = enum(short) { return numeric.toJS(globalObject, value); }, - .json => { + .json, .jsonb => { return json.toJS(globalObject, value); }, @@ -326,7 +336,7 @@ pub const Tag = enum(short) { tag: Tag, globalObject: *JSC.JSGlobalObject, value: anytype, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { return toJSWithType(tag, globalObject, @TypeOf(value), value); } @@ -363,16 +373,16 @@ pub const Tag = enum(short) { // Ban these types: if (tag == .NumberObject) { - return error.JSError; + return globalObject.ERR_INVALID_ARG_TYPE("Number object is ambiguous and cannot be used as a PostgreSQL type", .{}).throw(); } if (tag == .BooleanObject) { - return error.JSError; + return globalObject.ERR_INVALID_ARG_TYPE("Boolean object is ambiguous and cannot be used as a PostgreSQL type", .{}).throw(); } // It's something internal if (!tag.isIndexable()) { - return error.JSError; + return globalObject.ERR_INVALID_ARG_TYPE("Unknown object is not a valid PostgreSQL type", .{}).throw(); } // We will JSON.stringify anything else. @@ -414,7 +424,7 @@ pub const string = struct { globalThis: *JSC.JSGlobalObject, comptime Type: type, value: Type, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { switch (comptime Type) { [:0]u8, []u8, []const u8, [:0]const u8 => { var str = String.fromUTF8(value); @@ -456,7 +466,7 @@ pub const numeric = struct { pub fn toJS( _: *JSC.JSGlobalObject, value: anytype, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { return JSValue.jsNumber(value); } }; @@ -468,12 +478,12 @@ pub const json = struct { pub fn toJS( globalObject: *JSC.JSGlobalObject, value: *Data, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { defer value.deinit(); var str = bun.String.fromUTF8(value.slice()); defer str.deref(); const parse_result = JSValue.parse(str.toJS(globalObject), globalObject); - if (parse_result.isAnyError()) { + if (parse_result.AnyPostgresError()) { return globalObject.throwValue(parse_result); } @@ -488,7 +498,7 @@ pub const @"bool" = struct { pub fn toJS( _: *JSC.JSGlobalObject, value: bool, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { return JSValue.jsBoolean(value); } }; @@ -548,7 +558,7 @@ pub const bytea = struct { pub fn toJS( globalObject: *JSC.JSGlobalObject, value: *Data, - ) anyerror!JSValue { + ) AnyPostgresError!JSValue { defer value.deinit(); // var slice = value.slice()[@min(1, value.len)..]; diff --git a/src/string.zig b/src/string.zig index 054fe37e51..4f874a9864 100644 --- a/src/string.zig +++ b/src/string.zig @@ -47,6 +47,10 @@ pub const WTFStringImplStruct = extern struct { return this.m_refCount / s_refCountIncrement; } + pub fn memoryCost(this: WTFStringImpl) usize { + return this.byteLength(); + } + pub fn isStatic(this: WTFStringImpl) bool { return this.m_refCount & s_refCountIncrement != 0; } @@ -193,6 +197,14 @@ pub const WTFStringImplStruct = extern struct { return this.is8Bit() and bun.strings.isAllASCII(this.latin1Slice()); } + pub fn utf16ByteLength(this: WTFStringImpl) usize { + if (this.is8Bit()) { + return this.length() * 2; + } else { + return this.length(); + } + } + pub fn utf8ByteLength(this: WTFStringImpl) usize { if (this.is8Bit()) { const input = this.latin1Slice(); @@ -203,11 +215,6 @@ pub const WTFStringImplStruct = extern struct { } } - pub fn utf16ByteLength(this: WTFStringImpl) usize { - // All latin1 characters fit in a single UTF-16 code unit. - return this.length() * 2; - } - pub fn latin1ByteLength(this: WTFStringImpl) usize { // Not all UTF-16 characters fit are representable in latin1. // Those get truncated? @@ -316,6 +323,12 @@ pub const String = extern struct { extern fn BunString__fromUTF16ToLatin1(bytes: [*]const u16, len: usize) String; extern fn BunString__fromLatin1Unitialized(len: usize) String; extern fn BunString__fromUTF16Unitialized(len: usize) String; + extern fn BunString__toInt32(this: String) i64; + pub fn toInt32(this: String) ?i32 { + const val = BunString__toInt32(this); + if (val > std.math.maxInt(i32)) return null; + return @intCast(val); + } pub fn ascii(bytes: []const u8) String { return String{ .tag = .ZigString, .value = .{ .ZigString = ZigString.init(bytes) } }; diff --git a/src/string_builder.zig b/src/string_builder.zig index e5e3d0bd47..0178663a4f 100644 --- a/src/string_builder.zig +++ b/src/string_builder.zig @@ -89,6 +89,12 @@ pub fn appendZ(this: *StringBuilder, slice: string) [:0]const u8 { return result; } +pub fn appendStr(this: *StringBuilder, str: bun.String) string { + const slice = str.toUTF8(bun.default_allocator); + defer slice.deinit(); + return this.append(slice.slice()); +} + pub fn append(this: *StringBuilder, slice: string) string { if (comptime Environment.allow_assert) { assert(this.len <= this.cap); // didn't count everything diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 3012b8b44b..5a79b83aa1 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -9,6 +9,7 @@ const log = bun.Output.scoped(.STR, true); const js_lexer = @import("./js_lexer.zig"); const grapheme = @import("./grapheme.zig"); const JSC = bun.JSC; +const OOM = bun.OOM; pub const Encoding = enum { ascii, @@ -640,7 +641,7 @@ pub const StringOrTinyString = struct { // allocator.free(slice_); } - pub fn initAppendIfNeeded(stringy: string, comptime Appender: type, appendy: Appender) !StringOrTinyString { + pub fn initAppendIfNeeded(stringy: string, comptime Appender: type, appendy: Appender) OOM!StringOrTinyString { if (stringy.len <= StringOrTinyString.Max) { return StringOrTinyString.init(stringy); } @@ -648,7 +649,7 @@ pub const StringOrTinyString = struct { return StringOrTinyString.init(try appendy.append(string, stringy)); } - pub fn initLowerCaseAppendIfNeeded(stringy: string, comptime Appender: type, appendy: Appender) !StringOrTinyString { + pub fn initLowerCaseAppendIfNeeded(stringy: string, comptime Appender: type, appendy: Appender) OOM!StringOrTinyString { if (stringy.len <= StringOrTinyString.Max) { return StringOrTinyString.initLowerCase(stringy); } @@ -1948,9 +1949,10 @@ pub fn toWPathNormalizeAutoExtend(wbuf: []u16, utf8: []const u8) [:0]const u16 { } pub fn toWPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { - var renormalized: bun.PathBuffer = undefined; + const renormalized = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(renormalized); - var path_to_use = normalizeSlashesOnly(&renormalized, utf8, '\\'); + var path_to_use = normalizeSlashesOnly(renormalized, utf8, '\\'); // is there a trailing slash? Let's remove it before converting to UTF-16 if (path_to_use.len > 3 and bun.path.isSepAny(path_to_use[path_to_use.len - 1])) { @@ -1960,9 +1962,10 @@ pub fn toWPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { return toWPath(wbuf, path_to_use); } pub fn toPathNormalized(buf: []u8, utf8: []const u8) [:0]const u8 { - var renormalized: bun.PathBuffer = undefined; + const renormalized = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(renormalized); - var path_to_use = normalizeSlashesOnly(&renormalized, utf8, '\\'); + var path_to_use = normalizeSlashesOnly(renormalized, utf8, '\\'); // is there a trailing slash? Let's remove it before converting to UTF-16 if (path_to_use.len > 3 and bun.path.isSepAny(path_to_use[path_to_use.len - 1])) { @@ -1990,17 +1993,20 @@ pub fn normalizeSlashesOnly(buf: []u8, utf8: []const u8, comptime desired_slash: } pub fn toWDirNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { - var renormalized: bun.PathBuffer = undefined; + var renormalized: ?*bun.PathBuffer = null; + defer if (renormalized) |r| bun.PathBufferPool.put(r); + var path_to_use = utf8; if (bun.strings.containsChar(utf8, '/')) { - @memcpy(renormalized[0..utf8.len], utf8); - for (renormalized[0..utf8.len]) |*c| { + renormalized = bun.PathBufferPool.get(); + @memcpy(renormalized.?[0..utf8.len], utf8); + for (renormalized.?[0..utf8.len]) |*c| { if (c.* == '/') { c.* = '\\'; } } - path_to_use = renormalized[0..utf8.len]; + path_to_use = renormalized.?[0..utf8.len]; } return toWDirPath(wbuf, path_to_use); diff --git a/src/sys.zig b/src/sys.zig index 0fca595558..2327c3bc7e 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -255,6 +255,7 @@ pub const Tag = enum(u8) { uv_pipe, uv_tty_set_mode, uv_open_osfhandle, + uv_os_homedir, // Below this line are Windows API calls only. @@ -465,8 +466,9 @@ pub fn getcwdZ(buf: *bun.PathBuffer) Maybe([:0]const u8) { buf[0] = 0; if (comptime Environment.isWindows) { - var wbuf: bun.WPathBuffer = undefined; - const len: windows.DWORD = kernel32.GetCurrentDirectoryW(wbuf.len, &wbuf); + var wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); + const len: windows.DWORD = kernel32.GetCurrentDirectoryW(wbuf.len, wbuf); if (Result.errnoSys(len, .getcwd)) |err| return err; return Result{ .result = bun.strings.fromWPath(buf, wbuf[0..len]) }; } @@ -504,8 +506,6 @@ pub fn chmod(path: [:0]const u8, mode: bun.Mode) Maybe(void) { } pub fn chdirOSPath(destination: bun.OSPathSliceZ) Maybe(void) { - assertIsValidWindowsPath(bun.OSPathChar, destination); - if (comptime Environment.isPosix) { const rc = syscall.chdir(destination); return Maybe(void).errnoSys(rc, .chdir) orelse Maybe(void).success; @@ -554,8 +554,9 @@ pub fn chdir(destination: anytype) Maybe(void) { return chdirOSPath(@as(bun.OSPathSliceZ, destination)); } - var wbuf: bun.WPathBuffer = undefined; - return chdirOSPath(bun.strings.toWDirPath(&wbuf, destination)); + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); + return chdirOSPath(bun.strings.toWDirPath(wbuf, destination)); } return Maybe(void).todo(); @@ -625,8 +626,9 @@ pub fn fstat(fd: bun.FileDescriptor) Maybe(bun.Stat) { } pub fn mkdiratA(dir_fd: bun.FileDescriptor, file_path: []const u8) Maybe(void) { - var buf: bun.WPathBuffer = undefined; - return mkdiratW(dir_fd, bun.strings.toWPathNormalized(&buf, file_path)); + const buf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(buf); + return mkdiratW(dir_fd, bun.strings.toWPathNormalized(buf, file_path)); } pub fn mkdiratZ(dir_fd: bun.FileDescriptor, file_path: [*:0]const u8, mode: mode_t) Maybe(void) { @@ -687,9 +689,10 @@ pub fn mkdir(file_path: [:0]const u8, flags: bun.Mode) Maybe(void) { .linux => Maybe(void).errnoSysP(syscall.mkdir(file_path, flags), .mkdir, file_path) orelse Maybe(void).success, .windows => { - var wbuf: bun.WPathBuffer = undefined; + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); return Maybe(void).errnoSysP( - kernel32.CreateDirectoryW(bun.strings.toWPath(&wbuf, file_path).ptr, null), + kernel32.CreateDirectoryW(bun.strings.toWPath(wbuf, file_path).ptr, null), .mkdir, file_path, ) orelse Maybe(void).success; @@ -719,8 +722,9 @@ pub fn mkdirA(file_path: []const u8, flags: bun.Mode) Maybe(void) { } if (comptime Environment.isWindows) { - var wbuf: bun.WPathBuffer = undefined; - const wpath = bun.strings.toWPath(&wbuf, file_path); + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); + const wpath = bun.strings.toWPath(wbuf, file_path); assertIsValidWindowsPath(u16, wpath); return Maybe(void).errnoSysP( kernel32.CreateDirectoryW(wpath.ptr, null), @@ -791,8 +795,9 @@ pub fn normalizePathWindows( if (comptime T != u8 and T != u16) { @compileError("normalizePathWindows only supports u8 and u16 character types"); } - var wbuf: if (T == u16) void else bun.WPathBuffer = undefined; - var path = if (T == u16) path_ else bun.strings.convertUTF8toUTF16InBuffer(&wbuf, path_); + const wbuf = if (T != u16) bun.WPathBufferPool.get() else {}; + defer if (T != u16) bun.WPathBufferPool.put(wbuf); + var path = if (T == u16) path_ else bun.strings.convertUTF8toUTF16InBuffer(wbuf, path_); if (std.fs.path.isAbsoluteWindowsWTF16(path)) { // handle the special "nul" device @@ -844,7 +849,8 @@ pub fn normalizePathWindows( path = path[2..]; } - var buf1: bun.WPathBuffer = undefined; + const buf1 = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(buf1); @memcpy(buf1[0..base_path.len], base_path); buf1[base_path.len] = '\\'; @memcpy(buf1[base_path.len + 1 .. base_path.len + 1 + path.len], path); @@ -963,9 +969,10 @@ fn openDirAtWindowsT( path: []const T, options: WindowsOpenDirOptions, ) Maybe(bun.FileDescriptor) { - var wbuf: bun.WPathBuffer = undefined; + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); - const norm = switch (normalizePathWindows(T, dirFd, path, &wbuf)) { + const norm = switch (normalizePathWindows(T, dirFd, path, wbuf)) { .err => |err| return .{ .err = err }, .result => |norm| norm, }; @@ -1156,9 +1163,10 @@ pub fn openFileAtWindowsT( disposition: w.ULONG, options: w.ULONG, ) Maybe(bun.FileDescriptor) { - var wbuf: bun.WPathBuffer = undefined; + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); - const norm = switch (normalizePathWindows(T, dirFd, path, &wbuf)) { + const norm = switch (normalizePathWindows(T, dirFd, path, wbuf)) { .err => |err| return .{ .err = err }, .result => |norm| norm, }; @@ -1973,14 +1981,18 @@ pub fn renameat2(from_dir: bun.FileDescriptor, from: [:0]const u8, to_dir: bun.F pub fn renameat(from_dir: bun.FileDescriptor, from: [:0]const u8, to_dir: bun.FileDescriptor, to: [:0]const u8) Maybe(void) { if (Environment.isWindows) { - var w_buf_from: bun.WPathBuffer = undefined; - var w_buf_to: bun.WPathBuffer = undefined; + const w_buf_from = bun.WPathBufferPool.get(); + const w_buf_to = bun.WPathBufferPool.get(); + defer { + bun.WPathBufferPool.put(w_buf_from); + bun.WPathBufferPool.put(w_buf_to); + } const rc = bun.C.renameAtW( from_dir, - bun.strings.toNTPath(&w_buf_from, from), + bun.strings.toNTPath(w_buf_from, from), to_dir, - bun.strings.toNTPath(&w_buf_to, to), + bun.strings.toNTPath(w_buf_to, to), true, ); @@ -2052,10 +2064,14 @@ pub fn symlinkOrJunction(dest: [:0]const u8, target: [:0]const u8) Maybe(void) { if (comptime !Environment.isWindows) @compileError("symlinkOrJunction is windows only"); if (!WindowsSymlinkOptions.has_failed_to_create_symlink) { - var sym16: bun.WPathBuffer = undefined; - var target16: bun.WPathBuffer = undefined; - const sym_path = bun.strings.toWPathNormalizeAutoExtend(&sym16, dest); - const target_path = bun.strings.toWPathNormalizeAutoExtend(&target16, target); + const sym16 = bun.WPathBufferPool.get(); + const target16 = bun.WPathBufferPool.get(); + defer { + bun.WPathBufferPool.put(sym16); + bun.WPathBufferPool.put(target16); + } + const sym_path = bun.strings.toWPathNormalizeAutoExtend(sym16, dest); + const target_path = bun.strings.toWPathNormalizeAutoExtend(target16, target); switch (symlinkW(sym_path, target_path, .{ .directory = true })) { .result => { return Maybe(void).success; @@ -2162,8 +2178,9 @@ pub fn unlinkW(from: [:0]const u16) Maybe(void) { pub fn unlink(from: [:0]const u8) Maybe(void) { if (comptime Environment.isWindows) { - var w_buf: bun.WPathBuffer = undefined; - return unlinkW(bun.strings.toNTPath(&w_buf, from)); + const w_buf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(w_buf); + return unlinkW(bun.strings.toNTPath(w_buf, from)); } while (true) { @@ -2184,8 +2201,9 @@ pub fn rmdirat(dirfd: bun.FileDescriptor, to: anytype) Maybe(void) { pub fn unlinkatWithFlags(dirfd: bun.FileDescriptor, to: anytype, flags: c_uint) Maybe(void) { if (Environment.isWindows) { if (comptime std.meta.Elem(@TypeOf(to)) == u8) { - var w_buf: bun.WPathBuffer = undefined; - return unlinkatWithFlags(dirfd, bun.strings.toNTPath(&w_buf, bun.span(to)), flags); + const w_buf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(w_buf); + return unlinkatWithFlags(dirfd, bun.strings.toNTPath(w_buf, bun.span(to)), flags); } return bun.windows.DeleteFileBun(to, .{ @@ -2598,8 +2616,9 @@ pub fn getFileAttributes(path: anytype) ?WindowsFileAttributes { const attributes: WindowsFileAttributes = @bitCast(dword); return attributes; } else { - var wbuf: bun.WPathBuffer = undefined; - const path_to_use = bun.strings.toWPath(&wbuf, path); + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); + const path_to_use = bun.strings.toWPath(wbuf, path); return getFileAttributes(path_to_use); } } @@ -2677,8 +2696,9 @@ pub fn faccessat(dir_: anytype, subpath: anytype) JSC.Maybe(bool) { pub fn directoryExistsAt(dir_: anytype, subpath: anytype) JSC.Maybe(bool) { const dir_fd = bun.toFD(dir_); if (comptime Environment.isWindows) { - var wbuf: bun.WPathBuffer = undefined; - const path = bun.strings.toNTPath(&wbuf, subpath); + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); + const path = bun.strings.toNTPath(wbuf, subpath); const path_len_bytes: u16 = @truncate(path.len * 2); var nt_name = w.UNICODE_STRING{ .Length = path_len_bytes, @@ -2744,8 +2764,9 @@ pub fn existsAt(fd: bun.FileDescriptor, subpath: [:0]const u8) bool { } if (comptime Environment.isWindows) { - var wbuf: bun.WPathBuffer = undefined; - const path = bun.strings.toNTPath(&wbuf, subpath); + const wbuf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(wbuf); + const path = bun.strings.toNTPath(wbuf, subpath); const path_len_bytes: u16 = @truncate(path.len * 2); var nt_name = w.UNICODE_STRING{ .Length = path_len_bytes, @@ -3411,15 +3432,19 @@ pub const File = struct { return .{ .result = buf[0..read_amount] }; } - pub fn readToEndWithArrayList(this: File, list: *std.ArrayList(u8)) Maybe(usize) { - const size = switch (this.getEndPos()) { - .err => |err| { - return .{ .err = err }; - }, - .result => |s| s, - }; - - list.ensureTotalCapacityPrecise(size + 16) catch bun.outOfMemory(); + pub fn readToEndWithArrayList(this: File, list: *std.ArrayList(u8), probably_small: bool) Maybe(usize) { + if (probably_small) { + list.ensureUnusedCapacity(64) catch bun.outOfMemory(); + } else { + list.ensureTotalCapacityPrecise( + switch (this.getEndPos()) { + .err => |err| { + return .{ .err = err }; + }, + .result => |s| s, + } + 16, + ) catch bun.outOfMemory(); + } var total: i64 = 0; while (true) { @@ -3447,9 +3472,22 @@ pub const File = struct { return .{ .result = @intCast(total) }; } + + /// Use this function on potentially large files. + /// Calls fstat() on the file to get the size of the file and avoids reallocations + extra read() calls. pub fn readToEnd(this: File, allocator: std.mem.Allocator) ReadToEndResult { var list = std.ArrayList(u8).init(allocator); - return switch (readToEndWithArrayList(this, &list)) { + return switch (readToEndWithArrayList(this, &list, false)) { + .err => |err| .{ .err = err, .bytes = list }, + .result => .{ .err = null, .bytes = list }, + }; + } + + /// Use this function on small files <= 1024 bytes. + /// This will skip the fstat() call, preallocating 64 bytes instead of the file's size. + pub fn readToEndSmall(this: File, allocator: std.mem.Allocator) ReadToEndResult { + var list = std.ArrayList(u8).init(allocator); + return switch (readToEndWithArrayList(this, &list, true)) { .err => |err| .{ .err = err, .bytes = list }, .result => .{ .err = null, .bytes = list }, }; diff --git a/src/tagged_pointer.zig b/src/tagged_pointer.zig index ef9122582f..f81aa656b3 100644 --- a/src/tagged_pointer.zig +++ b/src/tagged_pointer.zig @@ -153,6 +153,14 @@ pub fn TaggedPointerUnion(comptime Types: anytype) type { return this.repr.get(Type); } + pub inline fn setUintptr(this: *This, value: AddressableSize) void { + this.repr._ptr = value; + } + + pub inline fn asUintptr(this: This) AddressableSize { + return this.repr._ptr; + } + pub inline fn is(this: This, comptime Type: type) bool { comptime assert_type(Type); return this.repr.data == comptime @intFromEnum(@field(Tag, typeBaseName(@typeName(Type)))); diff --git a/src/bundler.zig b/src/transpiler.zig similarity index 79% rename from src/bundler.zig rename to src/transpiler.zig index 5a11d98b28..b0078da193 100644 --- a/src/bundler.zig +++ b/src/transpiler.zig @@ -13,7 +13,7 @@ const C = bun.C; const std = @import("std"); const lex = bun.js_lexer; const logger = bun.logger; -const options = @import("options.zig"); +pub const options = @import("options.zig"); const js_parser = bun.js_parser; const JSON = bun.JSON; const js_printer = bun.js_printer; @@ -340,13 +340,13 @@ pub const PluginRunner = struct { } }; -/// This structure was the JavaScript bundler before bundle_v2 was written. It now +/// This structure was the JavaScript transpiler before bundle_v2 was written. It now /// acts mostly as a configuration object, but it also contains stateful logic around /// logging errors (.log) and module resolution (.resolve_queue) /// /// This object is not exclusive to bundle_v2/Bun.build, one of these is stored /// on every VM so that the options can be used for transpilation. -pub const Bundler = struct { +pub const Transpiler = struct { options: options.BundleOptions, log: *logger.Log, allocator: std.mem.Allocator, @@ -369,7 +369,7 @@ pub const Bundler = struct { pub const isCacheEnabled = cache_files; - pub fn clone(this: *Bundler, allocator: std.mem.Allocator, to: *Bundler) !void { + pub fn clone(this: *Transpiler, allocator: std.mem.Allocator, to: *Transpiler) !void { to.* = this.*; to.setAllocator(allocator); to.log = try allocator.create(logger.Log); @@ -379,33 +379,33 @@ pub const Bundler = struct { to.linker.resolver = &to.resolver; } - pub inline fn getPackageManager(this: *Bundler) *PackageManager { + pub inline fn getPackageManager(this: *Transpiler) *PackageManager { return this.resolver.getPackageManager(); } - pub fn setLog(this: *Bundler, log: *logger.Log) void { + pub fn setLog(this: *Transpiler, log: *logger.Log) void { this.log = log; this.linker.log = log; this.resolver.log = log; } - pub fn setAllocator(this: *Bundler, allocator: std.mem.Allocator) void { + pub fn setAllocator(this: *Transpiler, allocator: std.mem.Allocator) void { this.allocator = allocator; this.linker.allocator = allocator; this.resolver.allocator = allocator; } - fn _resolveEntryPoint(bundler: *Bundler, entry_point: string) !_resolver.Result { - return bundler.resolver.resolveWithFramework(bundler.fs.top_level_dir, entry_point, .entry_point) catch |err| { + fn _resolveEntryPoint(transpiler: *Transpiler, entry_point: string) !_resolver.Result { + return transpiler.resolver.resolveWithFramework(transpiler.fs.top_level_dir, entry_point, .entry_point) catch |err| { // Relative entry points that were not resolved to a node_modules package are // interpreted as relative to the current working directory. if (!std.fs.path.isAbsolute(entry_point) and !(strings.hasPrefix(entry_point, "./") or strings.hasPrefix(entry_point, ".\\"))) { brk: { - return bundler.resolver.resolve( - bundler.fs.top_level_dir, - try strings.append(bundler.allocator, "./", entry_point), + return transpiler.resolver.resolve( + transpiler.fs.top_level_dir, + try strings.append(transpiler.allocator, "./", entry_point), .entry_point, ) catch { // return the original error @@ -417,8 +417,8 @@ pub const Bundler = struct { }; } - pub fn resolveEntryPoint(bundler: *Bundler, entry_point: string) !_resolver.Result { - return _resolveEntryPoint(bundler, entry_point) catch |err| { + pub fn resolveEntryPoint(transpiler: *Transpiler, entry_point: string) !_resolver.Result { + return _resolveEntryPoint(transpiler, entry_point) catch |err| { var cache_bust_buf: bun.PathBuffer = undefined; // Bust directory cache and try again @@ -436,7 +436,7 @@ pub const Bundler = struct { }; break :name bun.path.joinAbsStringBufZ( - bundler.fs.top_level_dir, + transpiler.fs.top_level_dir, &cache_bust_buf, &parts, .auto, @@ -444,15 +444,15 @@ pub const Bundler = struct { }; // Only re-query if we previously had something cached. - if (bundler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) { - if (_resolveEntryPoint(bundler, entry_point)) |result| + if (transpiler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) { + if (_resolveEntryPoint(transpiler, entry_point)) |result| return result else |_| { // ignore this error, we will print the original error } } - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{s} resolving \"{s}\" (entry point)", .{ @errorName(err), entry_point }) catch bun.outOfMemory(); + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{s} resolving \"{s}\" (entry point)", .{ @errorName(err), entry_point }) catch bun.outOfMemory(); return err; }; } @@ -462,7 +462,7 @@ pub const Bundler = struct { log: *logger.Log, opts: Api.TransformOptions, env_loader_: ?*DotEnv.Loader, - ) !Bundler { + ) !Transpiler { js_ast.Expr.Data.Store.create(); js_ast.Stmt.Data.Store.create(); @@ -496,7 +496,7 @@ pub const Bundler = struct { // }); const resolve_results = try allocator.create(ResolveResults); resolve_results.* = ResolveResults.init(allocator); - return Bundler{ + return Transpiler{ .options = bundle_options, .fs = fs, .allocator = allocator, @@ -513,36 +513,36 @@ pub const Bundler = struct { }; } - pub fn configureLinkerWithAutoJSX(bundler: *Bundler, auto_jsx: bool) void { - bundler.linker = Linker.init( - bundler.allocator, - bundler.log, - &bundler.resolve_queue, - &bundler.options, - &bundler.resolver, - bundler.resolve_results, - bundler.fs, + pub fn configureLinkerWithAutoJSX(transpiler: *Transpiler, auto_jsx: bool) void { + transpiler.linker = Linker.init( + transpiler.allocator, + transpiler.log, + &transpiler.resolve_queue, + &transpiler.options, + &transpiler.resolver, + transpiler.resolve_results, + transpiler.fs, ); if (auto_jsx) { // Most of the time, this will already be cached - if (bundler.resolver.readDirInfo(bundler.fs.top_level_dir) catch null) |root_dir| { + if (transpiler.resolver.readDirInfo(transpiler.fs.top_level_dir) catch null) |root_dir| { if (root_dir.tsconfig_json) |tsconfig| { // If we don't explicitly pass JSX, try to get it from the root tsconfig - if (bundler.options.transform_options.jsx == null) { - bundler.options.jsx = tsconfig.jsx; + if (transpiler.options.transform_options.jsx == null) { + transpiler.options.jsx = tsconfig.jsx; } - bundler.options.emit_decorator_metadata = tsconfig.emit_decorator_metadata; + transpiler.options.emit_decorator_metadata = tsconfig.emit_decorator_metadata; } } } } - pub fn configureLinker(bundler: *Bundler) void { - bundler.configureLinkerWithAutoJSX(true); + pub fn configureLinker(transpiler: *Transpiler) void { + transpiler.configureLinkerWithAutoJSX(true); } - pub fn runEnvLoader(this: *Bundler, skip_default_env: bool) !void { + pub fn runEnvLoader(this: *Transpiler, skip_default_env: bool) !void { switch (this.options.env.behavior) { .prefix, .load_all, .load_all_without_inlining => { // Step 1. Load the project root. @@ -585,7 +585,7 @@ pub const Bundler = struct { } // This must be run after a framework is configured, if a framework is enabled - pub fn configureDefines(this: *Bundler) !void { + pub fn configureDefines(this: *Transpiler) !void { if (this.options.defines_loaded) { return; } @@ -614,18 +614,18 @@ pub const Bundler = struct { } } - pub fn resetStore(_: *const Bundler) void { + pub fn resetStore(_: *const Transpiler) void { js_ast.Expr.Data.Store.reset(); js_ast.Stmt.Data.Store.reset(); } - pub noinline fn dumpEnvironmentVariables(bundler: *const Bundler) void { + pub noinline fn dumpEnvironmentVariables(transpiler: *const Transpiler) void { @setCold(true); const opts = std.json.StringifyOptions{ .whitespace = .indent_2, }; Output.flush(); - std.json.stringify(bundler.env.map.*, opts, Output.writer()) catch unreachable; + std.json.stringify(transpiler.env.map.*, opts, Output.writer()) catch unreachable; Output.flush(); } @@ -636,7 +636,7 @@ pub const Bundler = struct { }; pub fn buildWithResolveResult( - bundler: *Bundler, + transpiler: *Transpiler, resolve_result: _resolver.Result, allocator: std.mem.Allocator, loader: options.Loader, @@ -659,7 +659,7 @@ pub const Bundler = struct { }; } - errdefer bundler.resetStore(); + errdefer transpiler.resetStore(); var file_path = (resolve_result.pathConst() orelse { return BuildResolveResultPair{ @@ -668,68 +668,68 @@ pub const Bundler = struct { }; }).*; - if (strings.indexOf(file_path.text, bundler.fs.top_level_dir)) |i| { - file_path.pretty = file_path.text[i + bundler.fs.top_level_dir.len ..]; + if (strings.indexOf(file_path.text, transpiler.fs.top_level_dir)) |i| { + file_path.pretty = file_path.text[i + transpiler.fs.top_level_dir.len ..]; } else if (!file_path.is_symlink) { - file_path.pretty = allocator.dupe(u8, bundler.fs.relativeTo(file_path.text)) catch unreachable; + file_path.pretty = allocator.dupe(u8, transpiler.fs.relativeTo(file_path.text)) catch unreachable; } - const old_bundler_allocator = bundler.allocator; - bundler.allocator = allocator; - defer bundler.allocator = old_bundler_allocator; - const old_linker_allocator = bundler.linker.allocator; - defer bundler.linker.allocator = old_linker_allocator; - bundler.linker.allocator = allocator; + const old_bundler_allocator = transpiler.allocator; + transpiler.allocator = allocator; + defer transpiler.allocator = old_bundler_allocator; + const old_linker_allocator = transpiler.linker.allocator; + defer transpiler.linker.allocator = old_linker_allocator; + transpiler.linker.allocator = allocator; switch (loader) { .css => { const CSSBundlerHMR = Css.NewBundler( Writer, - @TypeOf(&bundler.linker), - @TypeOf(&bundler.resolver.caches.fs), + @TypeOf(&transpiler.linker), + @TypeOf(&transpiler.resolver.caches.fs), WatcherType, - @TypeOf(bundler.fs), + @TypeOf(transpiler.fs), true, import_path_format, ); const CSSBundler = Css.NewBundler( Writer, - @TypeOf(&bundler.linker), - @TypeOf(&bundler.resolver.caches.fs), + @TypeOf(&transpiler.linker), + @TypeOf(&transpiler.resolver.caches.fs), WatcherType, - @TypeOf(bundler.fs), + @TypeOf(transpiler.fs), false, import_path_format, ); const written = brk: { - if (bundler.options.hot_module_reloading) { + if (transpiler.options.hot_module_reloading) { break :brk (try CSSBundlerHMR.bundle( file_path.text, - bundler.fs, + transpiler.fs, writer, watcher, - &bundler.resolver.caches.fs, + &transpiler.resolver.caches.fs, filepath_hash, file_descriptor, allocator, - bundler.log, - &bundler.linker, + transpiler.log, + &transpiler.linker, origin, )).written; } else { break :brk (try CSSBundler.bundle( file_path.text, - bundler.fs, + transpiler.fs, writer, watcher, - &bundler.resolver.caches.fs, + &transpiler.resolver.caches.fs, filepath_hash, file_descriptor, allocator, - bundler.log, - &bundler.linker, + transpiler.log, + &transpiler.linker, origin, )).written; } @@ -741,7 +741,7 @@ pub const Bundler = struct { }; }, else => { - var result = bundler.parse( + var result = transpiler.parse( ParseOptions{ .allocator = allocator, .path = file_path, @@ -749,13 +749,13 @@ pub const Bundler = struct { .dirname_fd = resolve_result.dirname_fd, .file_descriptor = file_descriptor, .file_hash = filepath_hash, - .macro_remappings = bundler.options.macro_remap, + .macro_remappings = transpiler.options.macro_remap, .emit_decorator_metadata = resolve_result.emit_decorator_metadata, .jsx = resolve_result.jsx, }, client_entry_point, ) orelse { - bundler.resetStore(); + transpiler.resetStore(); return BuildResolveResultPair{ .written = 0, .input_fd = null, @@ -766,14 +766,14 @@ pub const Bundler = struct { return BuildResolveResultPair{ .written = 0, .input_fd = result.input_fd, .empty = true }; } - if (bundler.options.target.isBun()) { - if (!bundler.options.transform_only) { - try bundler.linker.link(file_path, &result, origin, import_path_format, false, true); + if (transpiler.options.target.isBun()) { + if (!transpiler.options.transform_only) { + try transpiler.linker.link(file_path, &result, origin, import_path_format, false, true); } return BuildResolveResultPair{ .written = switch (result.ast.exports_kind) { - .esm => try bundler.printWithSourceMapMaybe( + .esm => try transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -783,7 +783,7 @@ pub const Bundler = struct { source_map_handler, null, ), - .cjs => try bundler.printWithSourceMapMaybe( + .cjs => try transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -799,13 +799,13 @@ pub const Bundler = struct { }; } - if (!bundler.options.transform_only) { - try bundler.linker.link(file_path, &result, origin, import_path_format, false, false); + if (!transpiler.options.transform_only) { + try transpiler.linker.link(file_path, &result, origin, import_path_format, false, false); } return BuildResolveResultPair{ .written = switch (result.ast.exports_kind) { - .none, .esm => try bundler.printWithSourceMapMaybe( + .none, .esm => try transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -815,7 +815,7 @@ pub const Bundler = struct { source_map_handler, null, ), - .cjs => try bundler.printWithSourceMapMaybe( + .cjs => try transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -834,7 +834,7 @@ pub const Bundler = struct { } pub fn buildWithResolveResultEager( - bundler: *Bundler, + transpiler: *Transpiler, resolve_result: _resolver.Result, comptime import_path_format: options.BundleOptions.ImportPathFormat, comptime Outstream: type, @@ -848,13 +848,13 @@ pub const Bundler = struct { var file_path = (resolve_result.pathConst() orelse return null).*; // Step 1. Parse & scan - const loader = bundler.options.loader(file_path.name.ext); + const loader = transpiler.options.loader(file_path.name.ext); if (client_entry_point_) |client_entry_point| { file_path = client_entry_point.source.path; } - file_path.pretty = Linker.relative_paths_list.append(string, bundler.fs.relativeTo(file_path.text)) catch unreachable; + file_path.pretty = Linker.relative_paths_list.append(string, transpiler.fs.relativeTo(file_path.text)) catch unreachable; var output_file = options.OutputFile{ .src_path = file_path, @@ -867,15 +867,15 @@ pub const Bundler = struct { switch (loader) { .jsx, .tsx, .js, .ts, .json, .toml, .text => { - var result = bundler.parse( + var result = transpiler.parse( ParseOptions{ - .allocator = bundler.allocator, + .allocator = transpiler.allocator, .path = file_path, .loader = loader, .dirname_fd = resolve_result.dirname_fd, .file_descriptor = null, .file_hash = null, - .macro_remappings = bundler.options.macro_remap, + .macro_remappings = transpiler.options.macro_remap, .jsx = resolve_result.jsx, .emit_decorator_metadata = resolve_result.emit_decorator_metadata, }, @@ -883,38 +883,38 @@ pub const Bundler = struct { ) orelse { return null; }; - if (!bundler.options.transform_only) { - if (!bundler.options.target.isBun()) - try bundler.linker.link( + if (!transpiler.options.transform_only) { + if (!transpiler.options.target.isBun()) + try transpiler.linker.link( file_path, &result, - bundler.options.origin, + transpiler.options.origin, import_path_format, false, false, ) else - try bundler.linker.link( + try transpiler.linker.link( file_path, &result, - bundler.options.origin, + transpiler.options.origin, import_path_format, false, true, ); } - const buffer_writer = try js_printer.BufferWriter.init(bundler.allocator); + const buffer_writer = try js_printer.BufferWriter.init(transpiler.allocator); var writer = js_printer.BufferPrinter.init(buffer_writer); - output_file.size = switch (bundler.options.target) { - .browser, .node => try bundler.print( + output_file.size = switch (transpiler.options.target) { + .browser, .node => try transpiler.print( result, *js_printer.BufferPrinter, &writer, .esm, ), - .bun, .bun_macro, .bake_server_components_ssr => try bundler.print( + .bun, .bun_macro, .bake_server_components_ssr => try transpiler.print( result, *js_printer.BufferPrinter, &writer, @@ -923,7 +923,7 @@ pub const Bundler = struct { }; output_file.value = .{ .buffer = .{ - .allocator = bundler.allocator, + .allocator = transpiler.allocator, .bytes = writer.ctx.written, }, }; @@ -932,33 +932,34 @@ pub const Bundler = struct { Output.panic("TODO: dataurl, base64", .{}); // TODO }, .css => { - if (bundler.options.experimental_css) { - const alloc = bundler.allocator; + if (transpiler.options.experimental.css) { + const alloc = transpiler.allocator; - const entry = bundler.resolver.caches.fs.readFileWithAllocator( - bundler.allocator, - bundler.fs, + const entry = transpiler.resolver.caches.fs.readFileWithAllocator( + transpiler.allocator, + transpiler.fs, file_path.text, resolve_result.dirname_fd, false, null, ) catch |err| { - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{s} reading \"{s}\"", .{ @errorName(err), file_path.pretty }) catch {}; + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{s} reading \"{s}\"", .{ @errorName(err), file_path.pretty }) catch {}; return null; }; - var sheet = switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parse(alloc, entry.contents, bun.css.ParserOptions.default(alloc, bundler.log), null)) { + var sheet = switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parse(alloc, entry.contents, bun.css.ParserOptions.default(alloc, transpiler.log), null)) { .result => |v| v, .err => |e| { - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{} parsing", .{e}) catch unreachable; + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{} parsing", .{e}) catch unreachable; return null; }, }; if (sheet.minify(alloc, bun.css.MinifyOptions.default()).asErr()) |e| { - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{} while minifying", .{e.kind}) catch bun.outOfMemory(); + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{} while minifying", .{e.kind}) catch bun.outOfMemory(); return null; } const result = sheet.toCss(alloc, bun.css.PrinterOptions{ - .minify = bundler.options.minify_whitespace, + .targets = bun.css.Targets.forBundlerTarget(transpiler.options.target), + .minify = transpiler.options.minify_whitespace, }, null) catch |e| { bun.handleErrorReturnTrace(e, @errorReturnTrace()); return null; @@ -981,12 +982,12 @@ pub const Bundler = struct { const CSSBuildContext = struct { origin: URL, }; - const build_ctx = CSSBuildContext{ .origin = bundler.options.origin }; + const build_ctx = CSSBuildContext{ .origin = transpiler.options.origin }; const BufferedWriter = std.io.CountingWriter(std.io.BufferedWriter(8192, bun.sys.File.Writer)); const CSSWriter = Css.NewWriter( BufferedWriter.Writer, - @TypeOf(&bundler.linker), + @TypeOf(&transpiler.linker), import_path_format, CSSBuildContext, ); @@ -994,8 +995,8 @@ pub const Bundler = struct { .child_stream = .{ .unbuffered_writer = file.writer() }, .bytes_written = 0, }; - const entry = bundler.resolver.caches.fs.readFile( - bundler.fs, + const entry = transpiler.resolver.caches.fs.readFile( + transpiler.fs, file_path.text, resolve_result.dirname_fd, !cache_files, @@ -1003,19 +1004,19 @@ pub const Bundler = struct { ) catch return null; const _file = Fs.PathContentsPair{ .path = file_path, .contents = entry.contents }; - var source = try logger.Source.initFile(_file, bundler.allocator); + var source = try logger.Source.initFile(_file, transpiler.allocator); source.contents_is_recycled = !cache_files; var css_writer = CSSWriter.init( &source, buffered_writer.writer(), - &bundler.linker, - bundler.log, + &transpiler.linker, + transpiler.log, ); css_writer.buildCtx = build_ctx; - try css_writer.run(bundler.log, bundler.allocator); + try css_writer.run(transpiler.log, transpiler.allocator); try css_writer.ctx.context.child_stream.flush(); output_file.size = css_writer.ctx.context.bytes_written; var file_op = options.OutputFile.FileOperation.fromFile(file.handle, file_path.pretty); @@ -1027,7 +1028,7 @@ pub const Bundler = struct { if (Outstream == std.fs.Dir) { file_op.dir = bun.toFD(outstream.fd); - if (bundler.fs.fs.needToCloseFiles()) { + if (transpiler.fs.fs.needToCloseFiles()) { file.close(); file_op.fd = .zero; } @@ -1037,12 +1038,12 @@ pub const Bundler = struct { } }, - .bunsh, .sqlite_embedded, .sqlite, .wasm, .file, .napi => { - const hashed_name = try bundler.linker.getHashedFilename(file_path, null); - var pathname = try bundler.allocator.alloc(u8, hashed_name.len + file_path.name.ext.len); + .html, .bunsh, .sqlite_embedded, .sqlite, .wasm, .file, .napi => { + const hashed_name = try transpiler.linker.getHashedFilename(file_path, null); + var pathname = try transpiler.allocator.alloc(u8, hashed_name.len + file_path.name.ext.len); bun.copy(u8, pathname, hashed_name); bun.copy(u8, pathname[hashed_name.len..], file_path.name.ext); - const dir = if (bundler.options.output_dir_handle) |output_handle| bun.toFD(output_handle.fd) else .zero; + const dir = if (transpiler.options.output_dir_handle) |output_handle| bun.toFD(output_handle.fd) else .zero; output_file.value = .{ .copy = options.OutputFile.FileOperation{ @@ -1058,7 +1059,7 @@ pub const Bundler = struct { } pub fn printWithSourceMapMaybe( - bundler: *Bundler, + transpiler: *Transpiler, ast: js_ast.Ast, source: *const logger.Source, comptime Writer: type, @@ -1084,14 +1085,14 @@ pub const Bundler = struct { .{ .runtime_imports = ast.runtime_imports, .require_ref = ast.require_ref, - .css_import_behavior = bundler.options.cssImportBehavior(), + .css_import_behavior = transpiler.options.cssImportBehavior(), .source_map_handler = source_map_context, - .minify_whitespace = bundler.options.minify_whitespace, - .minify_syntax = bundler.options.minify_syntax, - .minify_identifiers = bundler.options.minify_identifiers, - .transform_only = bundler.options.transform_only, + .minify_whitespace = transpiler.options.minify_whitespace, + .minify_syntax = transpiler.options.minify_syntax, + .minify_identifiers = transpiler.options.minify_identifiers, + .transform_only = transpiler.options.transform_only, .runtime_transpiler_cache = runtime_transpiler_cache, - .print_dce_annotations = bundler.options.emit_dce_annotations, + .print_dce_annotations = transpiler.options.emit_dce_annotations, }, enable_source_map, ), @@ -1107,18 +1108,18 @@ pub const Bundler = struct { .runtime_imports = ast.runtime_imports, .require_ref = ast.require_ref, .source_map_handler = source_map_context, - .css_import_behavior = bundler.options.cssImportBehavior(), - .minify_whitespace = bundler.options.minify_whitespace, - .minify_syntax = bundler.options.minify_syntax, - .minify_identifiers = bundler.options.minify_identifiers, - .transform_only = bundler.options.transform_only, + .css_import_behavior = transpiler.options.cssImportBehavior(), + .minify_whitespace = transpiler.options.minify_whitespace, + .minify_syntax = transpiler.options.minify_syntax, + .minify_identifiers = transpiler.options.minify_identifiers, + .transform_only = transpiler.options.transform_only, .import_meta_ref = ast.import_meta_ref, .runtime_transpiler_cache = runtime_transpiler_cache, - .print_dce_annotations = bundler.options.emit_dce_annotations, + .print_dce_annotations = transpiler.options.emit_dce_annotations, }, enable_source_map, ), - .esm_ascii => switch (bundler.options.target.isBun()) { + .esm_ascii => switch (transpiler.options.target.isBun()) { inline else => |is_bun| try js_printer.printAst( Writer, writer, @@ -1129,16 +1130,16 @@ pub const Bundler = struct { .{ .runtime_imports = ast.runtime_imports, .require_ref = ast.require_ref, - .css_import_behavior = bundler.options.cssImportBehavior(), + .css_import_behavior = transpiler.options.cssImportBehavior(), .source_map_handler = source_map_context, - .minify_whitespace = bundler.options.minify_whitespace, - .minify_syntax = bundler.options.minify_syntax, - .minify_identifiers = bundler.options.minify_identifiers, - .transform_only = bundler.options.transform_only, - .module_type = if (is_bun and bundler.options.transform_only) + .minify_whitespace = transpiler.options.minify_whitespace, + .minify_syntax = transpiler.options.minify_syntax, + .minify_identifiers = transpiler.options.minify_identifiers, + .transform_only = transpiler.options.transform_only, + .module_type = if (is_bun and transpiler.options.transform_only) // this is for when using `bun build --no-bundle` // it should copy what was passed for the cli - bundler.options.output_format + transpiler.options.output_format else if (ast.exports_kind == .cjs) .cjs else @@ -1146,8 +1147,8 @@ pub const Bundler = struct { .inline_require_and_import_errors = false, .import_meta_ref = ast.import_meta_ref, .runtime_transpiler_cache = runtime_transpiler_cache, - .target = bundler.options.target, - .print_dce_annotations = bundler.options.emit_dce_annotations, + .target = transpiler.options.target, + .print_dce_annotations = transpiler.options.emit_dce_annotations, }, enable_source_map, ), @@ -1157,13 +1158,13 @@ pub const Bundler = struct { } pub fn print( - bundler: *Bundler, + transpiler: *Transpiler, result: ParseResult, comptime Writer: type, writer: Writer, comptime format: js_printer.Format, ) !usize { - return bundler.printWithSourceMapMaybe( + return transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -1176,7 +1177,7 @@ pub const Bundler = struct { } pub fn printWithSourceMap( - bundler: *Bundler, + transpiler: *Transpiler, result: ParseResult, comptime Writer: type, writer: Writer, @@ -1184,7 +1185,7 @@ pub const Bundler = struct { handler: js_printer.SourceMapHandler, ) !usize { if (bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS")) { - return bundler.printWithSourceMapMaybe( + return transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -1195,7 +1196,7 @@ pub const Bundler = struct { result.runtime_transpiler_cache, ); } - return bundler.printWithSourceMapMaybe( + return transpiler.printWithSourceMapMaybe( result.ast, &result.source, Writer, @@ -1238,21 +1239,21 @@ pub const Bundler = struct { }; pub fn parse( - bundler: *Bundler, + transpiler: *Transpiler, this_parse: ParseOptions, client_entry_point_: anytype, ) ?ParseResult { - return parseMaybeReturnFileOnly(bundler, this_parse, client_entry_point_, false); + return parseMaybeReturnFileOnly(transpiler, this_parse, client_entry_point_, false); } pub fn parseMaybeReturnFileOnly( - bundler: *Bundler, + transpiler: *Transpiler, this_parse: ParseOptions, client_entry_point_: anytype, comptime return_file_only: bool, ) ?ParseResult { return parseMaybeReturnFileOnlyAllowSharedBuffer( - bundler, + transpiler, this_parse, client_entry_point_, return_file_only, @@ -1261,7 +1262,7 @@ pub const Bundler = struct { } pub fn parseMaybeReturnFileOnlyAllowSharedBuffer( - bundler: *Bundler, + transpiler: *Transpiler, this_parse: ParseOptions, client_entry_point_: anytype, comptime return_file_only: bool, @@ -1297,32 +1298,32 @@ pub const Bundler = struct { if (strings.startsWith(path.text, "data:")) { const data_url = DataURL.parseWithoutCheck(path.text) catch |err| { - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{s} parsing data url \"{s}\"", .{ @errorName(err), path.text }) catch {}; + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{s} parsing data url \"{s}\"", .{ @errorName(err), path.text }) catch {}; return null; }; const body = data_url.decodeData(this_parse.allocator) catch |err| { - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{s} decoding data \"{s}\"", .{ @errorName(err), path.text }) catch {}; + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{s} decoding data \"{s}\"", .{ @errorName(err), path.text }) catch {}; return null; }; break :brk logger.Source.initPathString(path.text, body); } - const entry = bundler.resolver.caches.fs.readFileWithAllocator( + const entry = transpiler.resolver.caches.fs.readFileWithAllocator( if (use_shared_buffer) bun.fs_allocator else this_parse.allocator, - bundler.fs, + transpiler.fs, path.text, dirname_fd, use_shared_buffer, file_descriptor, ) catch |err| { - bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{s} reading \"{s}\"", .{ @errorName(err), path.text }) catch {}; + transpiler.log.addErrorFmt(null, logger.Loc.Empty, transpiler.allocator, "{s} reading \"{s}\"", .{ @errorName(err), path.text }) catch {}; return null; }; input_fd = entry.fd; if (this_parse.file_fd_ptr) |file_fd_ptr| { file_fd_ptr.* = entry.fd; } - break :brk logger.Source.initRecycledFile(.{ .path = path, .contents = entry.contents }, bundler.allocator) catch return null; + break :brk logger.Source.initRecycledFile(.{ .path = path, .contents = entry.contents }, transpiler.allocator) catch return null; }; if (comptime return_file_only) { @@ -1350,7 +1351,7 @@ pub const Bundler = struct { }; } - const target = bundler.options.target; + const target = transpiler.options.target; var jsx = this_parse.jsx; jsx.parse = loader.isJSX(); @@ -1358,43 +1359,43 @@ pub const Bundler = struct { var opts = js_parser.Parser.Options.init(jsx, loader); opts.features.emit_decorator_metadata = this_parse.emit_decorator_metadata; - opts.features.allow_runtime = bundler.options.allow_runtime; + opts.features.allow_runtime = transpiler.options.allow_runtime; opts.features.set_breakpoint_on_first_line = this_parse.set_breakpoint_on_first_line; - opts.features.trim_unused_imports = bundler.options.trim_unused_imports orelse loader.isTypeScript(); + opts.features.trim_unused_imports = transpiler.options.trim_unused_imports orelse loader.isTypeScript(); opts.features.use_import_meta_require = target.isBun(); - opts.features.no_macros = bundler.options.no_macros; + opts.features.no_macros = transpiler.options.no_macros; opts.features.runtime_transpiler_cache = this_parse.runtime_transpiler_cache; - opts.transform_only = bundler.options.transform_only; + opts.transform_only = transpiler.options.transform_only; - opts.ignore_dce_annotations = bundler.options.ignore_dce_annotations; + opts.ignore_dce_annotations = transpiler.options.ignore_dce_annotations; // @bun annotation opts.features.dont_bundle_twice = this_parse.dont_bundle_twice; opts.features.commonjs_at_runtime = this_parse.allow_commonjs; - opts.tree_shaking = bundler.options.tree_shaking; - opts.features.inlining = bundler.options.inlining; + opts.tree_shaking = transpiler.options.tree_shaking; + opts.features.inlining = transpiler.options.inlining; opts.filepath_hash_for_hmr = file_hash orelse 0; - opts.features.auto_import_jsx = bundler.options.auto_import_jsx; + opts.features.auto_import_jsx = transpiler.options.auto_import_jsx; opts.warn_about_unbundled_modules = !target.isBun(); opts.features.inject_jest_globals = this_parse.inject_jest_globals; - opts.features.minify_syntax = bundler.options.minify_syntax; - opts.features.minify_identifiers = bundler.options.minify_identifiers; - opts.features.dead_code_elimination = bundler.options.dead_code_elimination; + opts.features.minify_syntax = transpiler.options.minify_syntax; + opts.features.minify_identifiers = transpiler.options.minify_identifiers; + opts.features.dead_code_elimination = transpiler.options.dead_code_elimination; opts.features.remove_cjs_module_wrapper = this_parse.remove_cjs_module_wrapper; - if (bundler.macro_context == null) { - bundler.macro_context = js_ast.Macro.MacroContext.init(bundler); + if (transpiler.macro_context == null) { + transpiler.macro_context = js_ast.Macro.MacroContext.init(transpiler); } // we'll just always enable top-level await // this is incorrect for Node.js files which are CommonJS modules opts.features.top_level_await = true; - opts.macro_context = &bundler.macro_context.?; + opts.macro_context = &transpiler.macro_context.?; if (comptime !JSC.is_bindgen) { if (target != .bun_macro) { opts.macro_context.javascript_object = this_parse.macro_js_ctx; @@ -1404,11 +1405,11 @@ pub const Bundler = struct { opts.features.is_macro_runtime = target == .bun_macro; opts.features.replace_exports = this_parse.replace_exports; - return switch ((bundler.resolver.caches.js.parse( + return switch ((transpiler.resolver.caches.js.parse( allocator, opts, - bundler.options.define, - bundler.log, + transpiler.options.define, + transpiler.log, &source, ) catch null) orelse return null) { .ast => |value| ParseResult{ @@ -1457,11 +1458,11 @@ pub const Bundler = struct { // We allow importing tsconfig.*.json or jsconfig.*.json with comments // These files implicitly become JSONC files, which aligns with the behavior of text editors. if (source.path.isJSONCFile()) - JSON.parseTSConfig(&source, bundler.log, allocator, false) catch return null + JSON.parseTSConfig(&source, transpiler.log, allocator, false) catch return null else - JSON.parse(&source, bundler.log, allocator, false) catch return null + JSON.parse(&source, transpiler.log, allocator, false) catch return null else if (kind == .toml) - TOML.parse(&source, bundler.log, allocator, false) catch return null + TOML.parse(&source, transpiler.log, allocator, false) catch return null else @compileError("unreachable"); @@ -1611,12 +1612,12 @@ pub const Bundler = struct { }; }, .wasm => { - if (bundler.options.target.isBun()) { + if (transpiler.options.target.isBun()) { if (!source.isWebAssembly()) { - bundler.log.addErrorFmt( + transpiler.log.addErrorFmt( null, logger.Loc.Empty, - bundler.allocator, + transpiler.allocator, "Invalid wasm file \"{s}\" (missing magic header)", .{path.text}, ) catch {}; @@ -1643,18 +1644,16 @@ pub const Bundler = struct { threadlocal var tmp_buildfile_buf2: bun.PathBuffer = undefined; threadlocal var tmp_buildfile_buf3: bun.PathBuffer = undefined; - // We try to be mostly stateless when serving - // This means we need a slightly different resolver setup pub fn buildFile( - bundler: *Bundler, + transpiler: *Transpiler, log: *logger.Log, path_to_use_: string, comptime client_entry_point_enabled: bool, ) !ServeResult { - const old_log = bundler.log; + const old_log = transpiler.log; - bundler.setLog(log); - defer bundler.setLog(old_log); + transpiler.setLog(log); + defer transpiler.setLog(old_log); var path_to_use = path_to_use_; @@ -1663,11 +1662,6 @@ pub const Bundler = struct { js_ast.Stmt.Data.Store.reset(); } - // If the extension is .js, omit it. - // if (absolute_path.len > ".js".len and strings.eqlComptime(absolute_path[absolute_path.len - ".js".len ..], ".js")) { - // absolute_path = absolute_path[0 .. absolute_path.len - ".js".len]; - // } - // All non-absolute paths are ./paths if (path_to_use[0] != '/' and path_to_use[0] != '.') { tmp_buildfile_buf3[0..2].* = "./".*; @@ -1675,10 +1669,10 @@ pub const Bundler = struct { path_to_use = tmp_buildfile_buf3[0 .. 2 + path_to_use.len]; } - const resolved = if (comptime !client_entry_point_enabled) (try bundler.resolver.resolve(bundler.fs.top_level_dir, path_to_use, .stmt)) else brk: { + const resolved = if (comptime !client_entry_point_enabled) (try transpiler.resolver.resolve(transpiler.fs.top_level_dir, path_to_use, .stmt)) else brk: { const absolute_pathname = Fs.PathName.init(path_to_use); - const loader_for_ext = bundler.options.loader(absolute_pathname.ext); + const loader_for_ext = transpiler.options.loader(absolute_pathname.ext); // The expected pathname looks like: // /pages/index.entry.tsx @@ -1701,17 +1695,17 @@ pub const Bundler = struct { if (comptime Environment.allow_assert) bun.assert(len > 0); const decoded_entry_point_path = tmp_buildfile_buf2[0..len]; - break :brk try bundler.resolver.resolve(bundler.fs.top_level_dir, decoded_entry_point_path, .entry_point); + break :brk try transpiler.resolver.resolve(transpiler.fs.top_level_dir, decoded_entry_point_path, .entry_point); } } - break :brk (try bundler.resolver.resolve(bundler.fs.top_level_dir, path_to_use, .stmt)); + break :brk (try transpiler.resolver.resolve(transpiler.fs.top_level_dir, path_to_use, .stmt)); }; const path = (resolved.pathConst() orelse return error.ModuleNotFound); - const loader = bundler.options.loader(path.name.ext); - const mime_type_ext = bundler.options.out_extensions.get(path.name.ext) orelse path.name.ext; + const loader = transpiler.options.loader(path.name.ext); + const mime_type_ext = transpiler.options.out_extensions.get(path.name.ext) orelse path.name.ext; switch (loader) { .js, .jsx, .ts, .tsx, .css => { @@ -1744,14 +1738,14 @@ pub const Bundler = struct { } } - pub fn normalizeEntryPointPath(bundler: *Bundler, _entry: string) string { + pub fn normalizeEntryPointPath(transpiler: *Transpiler, _entry: string) string { var paths = [_]string{_entry}; - var entry = bundler.fs.abs(&paths); + var entry = transpiler.fs.abs(&paths); std.fs.accessAbsolute(entry, .{}) catch return _entry; - entry = bundler.fs.relativeTo(entry); + entry = transpiler.fs.relativeTo(entry); if (!strings.startsWith(entry, "./")) { // Entry point paths without a leading "./" are interpreted as package @@ -1766,7 +1760,7 @@ pub const Bundler = struct { // a leading "./" because the path may not be a file system path. For // example, it may be a URL. So only insert a leading "./" when the path // is an exact match for an existing file. - var __entry = bundler.allocator.alloc(u8, "./".len + entry.len) catch unreachable; + var __entry = transpiler.allocator.alloc(u8, "./".len + entry.len) catch unreachable; __entry[0] = '.'; __entry[1] = '/'; bun.copy(u8, __entry[2..__entry.len], entry); @@ -1776,18 +1770,18 @@ pub const Bundler = struct { return entry; } - fn enqueueEntryPoints(bundler: *Bundler, entry_points: []_resolver.Result, comptime normalize_entry_point: bool) usize { + fn enqueueEntryPoints(transpiler: *Transpiler, entry_points: []_resolver.Result, comptime normalize_entry_point: bool) usize { var entry_point_i: usize = 0; - for (bundler.options.entry_points) |_entry| { - const entry: string = if (comptime normalize_entry_point) bundler.normalizeEntryPointPath(_entry) else _entry; + for (transpiler.options.entry_points) |_entry| { + const entry: string = if (comptime normalize_entry_point) transpiler.normalizeEntryPointPath(_entry) else _entry; defer { js_ast.Expr.Data.Store.reset(); js_ast.Stmt.Data.Store.reset(); } - const result = bundler.resolver.resolve(bundler.fs.top_level_dir, entry, .entry_point) catch |err| { + const result = transpiler.resolver.resolve(transpiler.fs.top_level_dir, entry, .entry_point) catch |err| { Output.prettyError("Error resolving \"{s}\": {s}\n", .{ entry, @errorName(err) }); continue; }; @@ -1799,7 +1793,7 @@ pub const Bundler = struct { continue; } - if (bundler.linker.enqueueResolveResult(&result) catch unreachable) { + if (transpiler.linker.enqueueResolveResult(&result) catch unreachable) { entry_points[entry_point_i] = result; entry_point_i += 1; } @@ -1809,44 +1803,44 @@ pub const Bundler = struct { } pub fn transform( - bundler: *Bundler, + transpiler: *Transpiler, allocator: std.mem.Allocator, log: *logger.Log, opts: Api.TransformOptions, ) !options.TransformResult { _ = opts; - var entry_points = try allocator.alloc(_resolver.Result, bundler.options.entry_points.len); - entry_points = entry_points[0..bundler.enqueueEntryPoints(entry_points, true)]; + var entry_points = try allocator.alloc(_resolver.Result, transpiler.options.entry_points.len); + entry_points = entry_points[0..transpiler.enqueueEntryPoints(entry_points, true)]; if (log.level.atLeast(.debug)) { - bundler.resolver.debug_logs = try DebugLogs.init(allocator); + transpiler.resolver.debug_logs = try DebugLogs.init(allocator); } - bundler.options.transform_only = true; + transpiler.options.transform_only = true; const did_start = false; - if (bundler.options.output_dir_handle == null) { + if (transpiler.options.output_dir_handle == null) { const outstream = bun.sys.File.from(std.io.getStdOut()); if (!did_start) { - try switch (bundler.options.import_path_format) { - .relative => bundler.processResolveQueue(.relative, false, @TypeOf(outstream), outstream), - .absolute_url => bundler.processResolveQueue(.absolute_url, false, @TypeOf(outstream), outstream), - .absolute_path => bundler.processResolveQueue(.absolute_path, false, @TypeOf(outstream), outstream), - .package_path => bundler.processResolveQueue(.package_path, false, @TypeOf(outstream), outstream), + try switch (transpiler.options.import_path_format) { + .relative => transpiler.processResolveQueue(.relative, false, @TypeOf(outstream), outstream), + .absolute_url => transpiler.processResolveQueue(.absolute_url, false, @TypeOf(outstream), outstream), + .absolute_path => transpiler.processResolveQueue(.absolute_path, false, @TypeOf(outstream), outstream), + .package_path => transpiler.processResolveQueue(.package_path, false, @TypeOf(outstream), outstream), }; } } else { - const output_dir = bundler.options.output_dir_handle orelse { + const output_dir = transpiler.options.output_dir_handle orelse { Output.printError("Invalid or missing output directory.", .{}); Global.crash(); }; if (!did_start) { - try switch (bundler.options.import_path_format) { - .relative => bundler.processResolveQueue(.relative, false, std.fs.Dir, output_dir), - .absolute_url => bundler.processResolveQueue(.absolute_url, false, std.fs.Dir, output_dir), - .absolute_path => bundler.processResolveQueue(.absolute_path, false, std.fs.Dir, output_dir), - .package_path => bundler.processResolveQueue(.package_path, false, std.fs.Dir, output_dir), + try switch (transpiler.options.import_path_format) { + .relative => transpiler.processResolveQueue(.relative, false, std.fs.Dir, output_dir), + .absolute_url => transpiler.processResolveQueue(.absolute_url, false, std.fs.Dir, output_dir), + .absolute_path => transpiler.processResolveQueue(.absolute_path, false, std.fs.Dir, output_dir), + .package_path => transpiler.processResolveQueue(.package_path, false, std.fs.Dir, output_dir), }; } } @@ -1857,8 +1851,8 @@ pub const Bundler = struct { // } // } - if (bundler.linker.any_needs_runtime) { - // try bundler.output_files.append( + if (transpiler.linker.any_needs_runtime) { + // try transpiler.output_files.append( // options.OutputFile.initBuf( // runtime.Runtime.source_code, // bun.default_allocator, @@ -1870,32 +1864,32 @@ pub const Bundler = struct { // ); } - if (FeatureFlags.tracing and bundler.options.log.level.atLeast(.info)) { + if (FeatureFlags.tracing and transpiler.options.log.level.atLeast(.info)) { Output.prettyErrorln( "\n---Tracing---\nResolve time: {d}\nParsing time: {d}\n---Tracing--\n\n", .{ - bundler.resolver.elapsed, - bundler.elapsed, + transpiler.resolver.elapsed, + transpiler.elapsed, }, ); } - var final_result = try options.TransformResult.init(try allocator.dupe(u8, bundler.result.outbase), try bundler.output_files.toOwnedSlice(), log, allocator); - final_result.root_dir = bundler.options.output_dir_handle; + var final_result = try options.TransformResult.init(try allocator.dupe(u8, transpiler.result.outbase), try transpiler.output_files.toOwnedSlice(), log, allocator); + final_result.root_dir = transpiler.options.output_dir_handle; return final_result; } - // pub fn processResolveQueueWithThreadPool(bundler) + // pub fn processResolveQueueWithThreadPool(transpiler) pub fn processResolveQueue( - bundler: *Bundler, + transpiler: *Transpiler, comptime import_path_format: options.BundleOptions.ImportPathFormat, comptime wrap_entry_point: bool, comptime Outstream: type, outstream: Outstream, ) !void { // var count: u8 = 0; - while (bundler.resolve_queue.readItem()) |item| { + while (transpiler.resolve_queue.readItem()) |item| { js_ast.Expr.Data.Store.reset(); js_ast.Stmt.Data.Store.reset(); @@ -1903,21 +1897,21 @@ pub const Bundler = struct { if (comptime wrap_entry_point) { const path = item.pathConst() orelse unreachable; - const loader = bundler.options.loader(path.name.ext); + const loader = transpiler.options.loader(path.name.ext); if (item.import_kind == .entry_point and loader.supportsClientEntryPoint()) { - var client_entry_point = try bundler.allocator.create(EntryPoints.ClientEntryPoint); + var client_entry_point = try transpiler.allocator.create(EntryPoints.ClientEntryPoint); client_entry_point.* = EntryPoints.ClientEntryPoint{}; - try client_entry_point.generate(Bundler, bundler, path.name, bundler.options.framework.?.client.path); + try client_entry_point.generate(Transpiler, transpiler, path.name, transpiler.options.framework.?.client.path); - const entry_point_output_file = bundler.buildWithResolveResultEager( + const entry_point_output_file = transpiler.buildWithResolveResultEager( item, import_path_format, Outstream, outstream, client_entry_point, ) catch continue orelse continue; - bundler.output_files.append(entry_point_output_file) catch unreachable; + transpiler.output_files.append(entry_point_output_file) catch unreachable; js_ast.Expr.Data.Store.reset(); js_ast.Stmt.Data.Store.reset(); @@ -1926,29 +1920,29 @@ pub const Bundler = struct { // So we just immediately build it. var item_not_entrypointed = item; item_not_entrypointed.import_kind = .stmt; - const original_output_file = bundler.buildWithResolveResultEager( + const original_output_file = transpiler.buildWithResolveResultEager( item_not_entrypointed, import_path_format, Outstream, outstream, null, ) catch continue orelse continue; - bundler.output_files.append(original_output_file) catch unreachable; + transpiler.output_files.append(original_output_file) catch unreachable; continue; } } - const output_file = bundler.buildWithResolveResultEager( + const output_file = transpiler.buildWithResolveResultEager( item, import_path_format, Outstream, outstream, null, ) catch continue orelse continue; - bundler.output_files.append(output_file) catch unreachable; + transpiler.output_files.append(output_file) catch unreachable; - // if (count >= 3) return try bundler.processResolveQueueWithThreadPool(import_path_format, wrap_entry_point, Outstream, outstream); + // if (count >= 3) return try transpiler.processResolveQueueWithThreadPool(import_path_format, wrap_entry_point, Outstream, outstream); } } }; diff --git a/src/url.zig b/src/url.zig index 3e7260aa8a..f96325ade9 100644 --- a/src/url.zig +++ b/src/url.zig @@ -93,6 +93,10 @@ pub const URL = struct { return strings.eqlComptime(this.protocol, "https"); } + pub inline fn isS3(this: *const URL) bool { + return strings.eqlComptime(this.protocol, "s3"); + } + pub inline fn isHTTP(this: *const URL) bool { return strings.eqlComptime(this.protocol, "http"); } @@ -105,6 +109,12 @@ pub const URL = struct { return "localhost"; } + pub fn s3Path(this: *const URL) string { + // we need to remove protocol if exists and ignore searchParams, should be host + pathname + const href = if (this.protocol.len > 0 and this.href.len > this.protocol.len + 2) this.href[this.protocol.len + 2 ..] else this.href; + return href[0 .. href.len - (this.search.len + this.hash.len)]; + } + pub fn displayHost(this: *const URL) bun.fmt.HostFormatter { return bun.fmt.HostFormatter{ .host = if (this.host.len > 0) this.host else this.displayHostname(), @@ -488,7 +498,7 @@ pub const QueryStringMap = struct { pub const Iterator = struct { // Assume no query string param map will exceed 2048 keys // Browsers typically limit URL lengths to around 64k - const VisitedMap = std.bit_set.ArrayBitSet(usize, 2048); + const VisitedMap = bun.bit_set.ArrayBitSet(usize, 2048); i: usize = 0, map: *const QueryStringMap, diff --git a/src/which.zig b/src/which.zig index 093b141c4f..0470af9d61 100644 --- a/src/which.zig +++ b/src/which.zig @@ -20,8 +20,9 @@ pub fn which(buf: *bun.PathBuffer, path: []const u8, cwd: []const u8, bin: []con bun.Output.scoped(.which, true)("path={s} cwd={s} bin={s}", .{ path, cwd, bin }); if (bun.Environment.os == .windows) { - var convert_buf: bun.WPathBuffer = undefined; - const result = whichWin(&convert_buf, path, cwd, bin) orelse return null; + const convert_buf = bun.WPathBufferPool.get(); + defer bun.WPathBufferPool.put(convert_buf); + const result = whichWin(convert_buf, path, cwd, bin) orelse return null; const result_converted = bun.strings.convertUTF16toUTF8InBuffer(buf, result) catch unreachable; buf[result_converted.len] = 0; bun.assert(result_converted.ptr == buf.ptr); @@ -132,13 +133,14 @@ fn searchBinInPath(buf: *bun.WPathBuffer, path_buf: *bun.PathBuffer, path: []con /// It is similar to Get-Command in powershell. pub fn whichWin(buf: *bun.WPathBuffer, path: []const u8, cwd: []const u8, bin: []const u8) ?[:0]const u16 { if (bin.len == 0) return null; - var path_buf: bun.PathBuffer = undefined; + const path_buf = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(path_buf); const check_windows_extensions = !endsWithExtension(bin); // handle absolute paths if (std.fs.path.isAbsolute(bin)) { - const normalized_bin = PosixToWinNormalizer.resolveCWDWithExternalBuf(&path_buf, bin) catch return null; + const normalized_bin = PosixToWinNormalizer.resolveCWDWithExternalBuf(path_buf, bin) catch return null; const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, normalized_bin); buf[bin_utf16.len] = 0; return searchBin(buf, bin_utf16.len, check_windows_extensions); @@ -148,7 +150,7 @@ pub fn whichWin(buf: *bun.WPathBuffer, path: []const u8, cwd: []const u8, bin: [ if (bun.strings.containsChar(bin, '/') or bun.strings.containsChar(bin, '\\')) { if (searchBinInPath( buf, - &path_buf, + path_buf, cwd, bun.strings.withoutPrefixComptime(bin, "./"), check_windows_extensions, @@ -163,7 +165,7 @@ pub fn whichWin(buf: *bun.WPathBuffer, path: []const u8, cwd: []const u8, bin: [ // iterate over system path delimiter var path_iter = std.mem.tokenizeScalar(u8, path, ';'); while (path_iter.next()) |segment_part| { - if (searchBinInPath(buf, &path_buf, segment_part, bin, check_windows_extensions)) |bin_path| { + if (searchBinInPath(buf, path_buf, segment_part, bin, check_windows_extensions)) |bin_path| { return bin_path; } } diff --git a/src/windows-app-info.rc b/src/windows-app-info.rc index 2822798ed0..361145deb4 100644 --- a/src/windows-app-info.rc +++ b/src/windows-app-info.rc @@ -1,6 +1,6 @@ #include "windows.h" -IDI_MYICON ICON "@BUN_ICO_PATH@" +IDI_MYICON ICON "bun.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION @Bun_VERSION_MAJOR@,@Bun_VERSION_MINOR@,@Bun_VERSION_PATCH@,0 diff --git a/src/windows.zig b/src/windows.zig index 2f479c14db..b7d7d6860c 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -3627,3 +3627,33 @@ pub const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000; pub const JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x400; pub const JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x800; pub const JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000; + +const pe_header_offset_location = 0x3C; +const subsystem_offset = 0x5C; + +pub const Subsystem = enum(u16) { + windows_gui = 2, +}; + +pub fn editWin32BinarySubsystem(fd: bun.sys.File, subsystem: Subsystem) !void { + comptime bun.assert(bun.Environment.isWindows); + if (bun.windows.SetFilePointerEx(fd.handle.cast(), pe_header_offset_location, null, std.os.windows.FILE_BEGIN) == 0) + return error.Win32Error; + const offset = try fd.reader().readInt(u32, .little); + if (bun.windows.SetFilePointerEx(fd.handle.cast(), offset + subsystem_offset, null, std.os.windows.FILE_BEGIN) == 0) + return error.Win32Error; + try fd.writer().writeInt(u16, @intFromEnum(subsystem), .little); +} + +pub const rescle = struct { + extern fn rescle__setIcon([*:0]const u16, [*:0]const u16) c_int; + + pub fn setIcon(exe_path: [*:0]const u16, icon: [*:0]const u16) !void { + comptime bun.assert(bun.Environment.isWindows); + const status = rescle__setIcon(exe_path, icon); + return switch (status) { + 0 => {}, + else => error.IconEditError, + }; + } +}; diff --git a/src/windows_c.zig b/src/windows_c.zig index 86d3c32f95..df725226c6 100644 --- a/src/windows_c.zig +++ b/src/windows_c.zig @@ -8,6 +8,28 @@ const Stat = std.fs.File.Stat; const Kind = std.fs.File.Kind; const StatError = std.fs.File.StatError; +// Windows doesn't have memmem, so we need to implement it +pub export fn memmem(haystack: ?[*]const u8, haystacklen: usize, needle: ?[*]const u8, needlelen: usize) ?[*]const u8 { + // Handle null pointers + if (haystack == null or needle == null) return null; + + // Handle empty needle case + if (needlelen == 0) return haystack; + + // Handle case where needle is longer than haystack + if (needlelen > haystacklen) return null; + + const hay = haystack.?[0..haystacklen]; + const nee = needle.?[0..needlelen]; + + const i = std.mem.indexOf(u8, hay, nee) orelse return null; + return hay.ptr + i; +} + +comptime { + @export(memmem, .{ .name = "zig_memmem" }); +} + pub const lstat = blk: { const T = *const fn ([*c]const u8, [*c]std.c.Stat) callconv(.C) c_int; break :blk @extern(T, .{ .name = "lstat64" }); diff --git a/test/bun.lockb b/test/bun.lockb index 592e2bd028..5d98fa3753 100755 Binary files a/test/bun.lockb and b/test/bun.lockb differ diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index df1624622e..195f8fbe1d 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test"; import { readFileSync, writeFileSync } from "fs"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; import path, { join } from "path"; +import assert from "assert"; describe("Bun.build", () => { test("experimentalCss = true works", async () => { @@ -175,6 +176,26 @@ describe("Bun.build", () => { Bun.gc(true); }); + test("`throw: true` works", async () => { + Bun.gc(true); + try { + await Bun.build({ + entrypoints: [join(import.meta.dir, "does-not-exist.ts")], + throw: true, + }); + expect.unreachable(); + } catch (e) { + assert(e instanceof AggregateError); + expect(e.errors).toHaveLength(1); + expect(e.errors[0]).toBeInstanceOf(BuildMessage); + expect(e.errors[0].message).toMatch(/ModuleNotFound/); + expect(e.errors[0].name).toBe("BuildMessage"); + expect(e.errors[0].position).toEqual(null); + expect(e.errors[0].level).toEqual("error"); + Bun.gc(true); + } + }); + test("returns output files", async () => { Bun.gc(true); const build = await Bun.build({ @@ -546,4 +567,78 @@ describe("Bun.build", () => { expect(await bundle.outputs[0].text()).toBe("var o=/*@__PURE__*/console.log(1);export{o as OUT};\n"); }); + + test("you can write onLoad and onResolve plugins using the 'html' loader, and it includes script and link tags as bundled entrypoints", async () => { + const fixture = tempDirWithFiles("build-html-plugins", { + "index.html": ` + + + + + + + + `, + "style.css": ".foo { color: red; }", + + // Check we actually do bundle the script + "script.js": "console.log(1 + 2)", + }); + + let onLoadCalled = false; + let onResolveCalled = false; + + const build = await Bun.build({ + entrypoints: [join(fixture, "index.html")], + html: true, + experimentalCss: true, + minify: { + syntax: true, + }, + plugins: [ + { + name: "test-plugin", + setup(build) { + build.onLoad({ filter: /\.html$/ }, async args => { + onLoadCalled = true; + const contents = await Bun.file(args.path).text(); + return { + contents: contents.replace("", ""), + loader: "html", + }; + }); + + build.onResolve({ filter: /\.(js|css)$/ }, args => { + onResolveCalled = true; + return { + path: join(fixture, args.path), + namespace: "file", + }; + }); + }, + }, + ], + }); + + expect(build.success).toBe(true); + expect(onLoadCalled).toBe(true); + expect(onResolveCalled).toBe(true); + + // Should have 3 outputs - HTML, JS and CSS + expect(build.outputs).toHaveLength(3); + + // Verify we have one of each type + const types = build.outputs.map(o => o.type); + expect(types).toContain("text/html;charset=utf-8"); + expect(types).toContain("text/javascript;charset=utf-8"); + expect(types).toContain("text/css;charset=utf-8"); + + // Verify the JS output contains the __dirname + const js = build.outputs.find(o => o.type === "text/javascript;charset=utf-8"); + expect(await js?.text()).toContain("console.log(3)"); + + // Verify our plugin modified the HTML + const html = build.outputs.find(o => o.type === "text/html;charset=utf-8"); + expect(await html?.text()).toContain(""); + }); }); diff --git a/test/bundler/bundler_bun.test.ts b/test/bundler/bundler_bun.test.ts index 43b29c517e..9638fe5254 100644 --- a/test/bundler/bundler_bun.test.ts +++ b/test/bundler/bundler_bun.test.ts @@ -102,4 +102,51 @@ error: Hello World`, }, run: { stdout: "" }, }); + if (Bun.version.startsWith("1.2")) { + throw new Error("TODO: enable these tests please"); + for (const backend of ["api", "cli"] as const) { + itBundled("bun/ExportsConditionsDevelopment" + backend.toUpperCase(), { + files: { + "src/entry.js": `import 'pkg1'`, + "node_modules/pkg1/package.json": /* json */ ` + { + "exports": { + "development": "./custom1.js", + "default": "./default.js" + } + } + `, + "node_modules/pkg1/custom1.js": `console.log('SUCCESS')`, + "node_modules/pkg1/default.js": `console.log('FAIL')`, + }, + backend, + outfile: "out.js", + define: { "process.env.NODE_ENV": '"development"' }, + run: { + stdout: "SUCCESS", + }, + }); + itBundled("bun/ExportsConditionsDevelopmentInProduction" + backend.toUpperCase(), { + files: { + "src/entry.js": `import 'pkg1'`, + "node_modules/pkg1/package.json": /* json */ ` + { + "exports": { + "development": "./custom1.js", + "default": "./default.js" + } + } + `, + "node_modules/pkg1/custom1.js": `console.log('FAIL')`, + "node_modules/pkg1/default.js": `console.log('SUCCESS')`, + }, + backend, + outfile: "/Users/user/project/out.js", + define: { "process.env.NODE_ENV": '"production"' }, + run: { + stdout: "SUCCESS", + }, + }); + } + } }); diff --git a/test/bundler/bundler_defer.test.ts b/test/bundler/bundler_defer.test.ts index c3becd5ac4..7f9bca5f13 100644 --- a/test/bundler/bundler_defer.test.ts +++ b/test/bundler/bundler_defer.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from "bun:test"; -import { itBundled } from './expectBundled'; -import { bunExe, bunEnv, tempDirWithFiles } from 'harness'; -import * as path from 'node:path'; +import { itBundled } from "./expectBundled"; +import { bunExe, bunEnv, tempDirWithFiles } from "harness"; +import * as path from "node:path"; describe("defer", () => { { @@ -179,10 +179,12 @@ describe("defer", () => { }, }, ], + throw: true, }); console.log(result); - } catch (err) { + } catch (err: any) { expect(err).toBeDefined(); + expect(err.message).toBe("WOOPS"); return; } throw new Error("DIDNT GET ERROR!"); @@ -213,15 +215,15 @@ describe("defer", () => { console.log("Foo", foo, lmao); `, - "/lmao.ts": ` + "/lmao.ts": ` import { foo } from "./foo.ts"; export const lmao = "lolss"; console.log(foo); `, - "/foo.ts": ` + "/foo.ts": ` export const foo = 'lkdfjlsdf'; console.log('hi')`, - "/a.css": ` + "/a.css": ` h1 { color: blue; } diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts index ae9725fad4..a3bf53f778 100644 --- a/test/bundler/bundler_edgecase.test.ts +++ b/test/bundler/bundler_edgecase.test.ts @@ -1336,7 +1336,7 @@ describe("bundler", () => { target: "bun", run: true, todo: isBroken && isWindows, - debugTimeoutScale: 5, + timeoutScale: 5, }); itBundled("edgecase/PackageExternalDoNotBundleNodeModules", { files: { diff --git a/test/bundler/bundler_html.test.ts b/test/bundler/bundler_html.test.ts new file mode 100644 index 0000000000..29ac02c90f --- /dev/null +++ b/test/bundler/bundler_html.test.ts @@ -0,0 +1,724 @@ +import { describe, expect } from "bun:test"; +import { itBundled } from "./expectBundled"; + +describe("bundler", () => { + // Basic test for bundling HTML with JS and CSS + itBundled("html/basic", { + outdir: "out/", + files: { + "/index.html": ` + + + + + + + +

Hello World

+ +`, + "/styles.css": "body { background-color: red; }", + "/script.js": "console.log('Hello World')", + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/index.html"], + + onAfterBundle(api) { + // Check that output HTML references hashed filenames + api.expectFile("out/index.html").not.toContain("styles.css"); + api.expectFile("out/index.html").not.toContain("script.js"); + api.expectFile("out/index.html").toMatch(/href=".*\.css"/); + api.expectFile("out/index.html").toMatch(/src=".*\.js"/); + }, + }); + + // Test multiple script and style bundling + itBundled("html/multiple-assets", { + outdir: "out/", + files: { + "/index.html": ` + + + + + + + + + +

Multiple Assets

+ +`, + "/style1.css": "body { color: blue; }", + "/style2.css": "h1 { color: red; }", + "/script1.js": "console.log('First script')", + "/script2.js": "console.log('Second script')", + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/index.html"], + onAfterBundle(api) { + // Should combine CSS files into one + api.expectFile("out/index.html").toMatch(/href=".*\.css"/); + api.expectFile("out/index.html").not.toMatch(/href=".*style1\.css"/); + api.expectFile("out/index.html").not.toMatch(/href=".*style2\.css"/); + + // Should combine JS files into one + api.expectFile("out/index.html").toMatch(/src=".*\.js"/); + api.expectFile("out/index.html").not.toMatch(/src=".*script1\.js"/); + api.expectFile("out/index.html").not.toMatch(/src=".*script2\.js"/); + }, + }); + + // Test image hashing + itBundled("html/image-hashing", { + outdir: "out/", + files: { + "/index.html": ` + + + + Local image + External image + +`, + "/image.jpg": "fake image content", + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/index.html"], + onAfterBundle(api) { + // Local image should be hashed + api.expectFile("out/index.html").not.toContain("./image.jpg"); + api.expectFile("out/index.html").toMatch(/src=".*-[a-zA-Z0-9]+\.jpg"/); + + // External image URL should remain unchanged + api.expectFile("out/index.html").toContain("https://example.com/image.jpg"); + }, + }); + + // Test external assets preservation + itBundled("html/external-assets", { + outdir: "out/", + files: { + "/index.html": ` + + + + + + + +

External Assets

+ +`, + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/index.html"], + onAfterBundle(api) { + // External URLs should remain unchanged + api.expectFile("out/index.html").toContain("https://cdn.example.com/style.css"); + api.expectFile("out/index.html").toContain("https://cdn.example.com/script.js"); + }, + }); + + // Test mixed local and external assets + itBundled("html/mixed-assets", { + outdir: "out/", + files: { + "/index.html": ` + + + + + + + + + +

Mixed Assets

+ + + +`, + "/local.css": "body { margin: 0; }", + "/local.js": "console.log('Local script')", + "/local.jpg": "fake image content", + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/index.html"], + onAfterBundle(api) { + // Local assets should be hashed + api.expectFile("out/index.html").not.toContain("local.css"); + api.expectFile("out/index.html").not.toContain("local.js"); + api.expectFile("out/index.html").not.toContain("local.jpg"); + + // External assets should remain unchanged + api.expectFile("out/index.html").toContain("https://cdn.example.com/style.css"); + api.expectFile("out/index.html").toContain("https://cdn.example.com/script.js"); + api.expectFile("out/index.html").toContain("https://cdn.example.com/image.jpg"); + }, + }); + + // Test JS imports + itBundled("html/js-imports", { + outdir: "out/", + files: { + "/in/index.html": ` + + + + + + +

JS Imports

+ +`, + "/in/main.js": ` +import { greeting } from './utils/strings.js'; +import { formatDate } from './utils/date.js'; +console.log(greeting('World')); +console.log(formatDate(new Date()));`, + "/in/utils/strings.js": ` +export const greeting = (name) => \`Hello, \${name}!\`;`, + "/in/utils/date.js": ` +import { padZero } from './numbers.js'; +export const formatDate = (date) => \`\${date.getFullYear()}-\${padZero(date.getMonth() + 1)}-\${padZero(date.getDate())}\`;`, + "/in/utils/numbers.js": ` +export const padZero = (num) => String(num).padStart(2, '0');`, + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/in/index.html"], + onAfterBundle(api) { + // All JS should be bundled into one file + api.expectFile("out/index.html").toMatch(/src=".*\.js"/); + api.expectFile("out/index.html").not.toContain("main.js"); + + const htmlContent = api.readFile("out/index.html"); + // Check that the bundle contains all the imported code + const jsMatch = htmlContent.match(/src="(.*\.js)"/); + const jsBundle = api.readFile("out/" + jsMatch![1]); + expect(jsBundle).toContain("Hello"); + expect(jsBundle).toContain("padZero"); + expect(jsBundle).toContain("formatDate"); + }, + }); + + // Test CSS imports + itBundled("html/css-imports", { + outdir: "out/", + files: { + "/in/index.html": ` + + + + + + +

CSS Imports

+ +`, + "/in/styles/main.css": ` +@import './variables.css'; +@import './typography.css'; +body { + background-color: var(--background-color); +}`, + "/in/styles/variables.css": ` +:root { + --background-color: #f0f0f0; + --text-color: #333; + --heading-color: #000; +}`, + "/in/styles/typography.css": ` +@import './fonts.css'; +h1 { + color: var(--heading-color); + font-family: var(--heading-font); +}`, + "/in/styles/fonts.css": ` +:root { + --heading-font: 'Arial', sans-serif; + --body-font: 'Helvetica', sans-serif; +}`, + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/in/index.html"], + onAfterBundle(api) { + // All CSS should be bundled into one file + api.expectFile("out/index.html").toMatch(/href=".*\.css"/); + api.expectFile("out/index.html").not.toContain("main.css"); + + // Check that the bundle contains all the imported CSS + const htmlContent = api.readFile("out/index.html"); + const cssMatch = htmlContent.match(/href="(.*?\.css)"/); + if (!cssMatch) throw new Error("Could not find CSS file reference in HTML"); + const cssBundle = api.readFile("out/" + cssMatch[1]); + expect(cssBundle).toContain("--background-color"); + expect(cssBundle).toContain("--heading-font"); + expect(cssBundle).toContain("font-family"); + }, + }); + + // Test multiple HTML entry points + itBundled("html/multiple-entries", { + outdir: "out/", + files: { + "/in/pages/index.html": ` + + + + + + + +

Home Page

+ About + +`, + "/in/pages/about.html": ` + + + + + + + +

About Page

+ Home + +`, + "/in/styles/home.css": ` +@import './common.css'; +.home { color: blue; }`, + "/in/styles/about.css": ` +@import './common.css'; +.about { color: green; }`, + "/in/styles/common.css": ` +body { margin: 0; padding: 20px; }`, + "/in/scripts/home.js": ` +import { initNav } from './common.js'; +console.log('Home page'); +initNav();`, + "/in/scripts/about.js": ` +import { initNav } from './common.js'; +console.log('About page'); +initNav();`, + "/in/scripts/common.js": ` +export const initNav = () => console.log('Navigation initialized');`, + }, + entryPoints: ["/in/pages/index.html", "/in/pages/about.html"], + experimentalHtml: true, + experimentalCss: true, + onAfterBundle(api) { + // Check index.html + api.expectFile("out/index.html").toMatch(/href=".*\.css"/); + api.expectFile("out/index.html").toMatch(/src=".*\.js"/); + api.expectFile("out/index.html").not.toContain("home.css"); + api.expectFile("out/index.html").not.toContain("home.js"); + + // Check about.html + api.expectFile("out/about.html").toMatch(/href=".*\.css"/); + api.expectFile("out/about.html").toMatch(/src=".*\.js"/); + api.expectFile("out/about.html").not.toContain("about.css"); + api.expectFile("out/about.html").not.toContain("about.js"); + + // Verify we don't update the filenames for these + const indexHtml = api.readFile("out/index.html"); + const aboutHtml = api.readFile("out/about.html"); + expect(indexHtml).toContain('href="./about.html"'); + expect(aboutHtml).toContain('href="index.html"'); + + // Check that each page has its own bundle + const indexHtmlContent = api.readFile("out/index.html"); + const aboutHtmlContent = api.readFile("out/about.html"); + + const indexJsMatch = indexHtmlContent.match(/src="(.*\.js)"/); + const aboutJsMatch = aboutHtmlContent.match(/src="(.*\.js)"/); + + const indexJs = api.readFile("out/" + indexJsMatch![1]); + const aboutJs = api.readFile("out/" + aboutJsMatch![1]); + + expect(indexJs).toContain("Home page"); + expect(aboutJs).toContain("About page"); + expect(indexJs).toContain("Navigation initialized"); + expect(aboutJs).toContain("Navigation initialized"); + + // Check that each page has its own CSS bundle + const indexCssMatch = indexHtmlContent.match(/href="(.*\.css)"/); + const aboutCssMatch = aboutHtmlContent.match(/href="(.*\.css)"/); + + const indexCss = api.readFile("out/" + indexCssMatch![1]); + const aboutCss = api.readFile("out/" + aboutCssMatch![1]); + + expect(indexCss).toContain(".home"); + expect(aboutCss).toContain(".about"); + expect(indexCss).toContain("margin: 0"); + expect(aboutCss).toContain("margin: 0"); + }, + }); + + // Test multiple HTML entries with shared chunks + itBundled("html/shared-chunks", { + outdir: "out/", + // Makes this test easier to write + minifyWhitespace: true, + + files: { + "/in/pages/page1.html": ` + + + + + + + +

Page 1

+ +`, + "/in/pages/page2.html": ` + + + + + + + +

Page 2

+ +`, + "/in/styles/page1.css": ` +@import './shared.css'; +.page1 { font-size: 20px; }`, + "/in/styles/page2.css": ` +@import './shared.css'; +.page2 { font-size: 18px; }`, + "/in/styles/shared.css": ` +@import './reset.css'; +.shared { color: blue; }`, + "/in/styles/reset.css": ` +* { box-sizing: border-box; }`, + "/in/scripts/page1.js": ` +import { sharedUtil } from './shared.js'; +import { largeModule } from './large-module.js'; +console.log('Page 1'); +sharedUtil();`, + "/in/scripts/page2.js": ` +import { sharedUtil } from './shared.js'; +import { largeModule } from './large-module.js'; +console.log('Page 2'); +sharedUtil();`, + "/in/scripts/shared.js": ` +export const sharedUtil = () => console.log('Shared utility');`, + "/in/scripts/large-module.js": ` +export const largeModule = { + // Simulate a large shared module + bigData: new Array(1000).fill('data'), + methods: { /* ... */ } +};`, + }, + entryPoints: ["/in/pages/page1.html", "/in/pages/page2.html"], + experimentalHtml: true, + experimentalCss: true, + splitting: true, + onAfterBundle(api) { + // Check both pages + for (const page of ["page1", "page2"]) { + api.expectFile(`out/${page}.html`).toMatch(/href=".*\.css"/); + api.expectFile(`out/${page}.html`).toMatch(/src=".*\.js"/); + api.expectFile(`out/${page}.html`).not.toContain(`${page}.css`); + api.expectFile(`out/${page}.html`).not.toContain(`${page}.js`); + } + + // Verify that shared code exists in both bundles + const page1Html = api.readFile("out/page1.html"); + const page2Html = api.readFile("out/page2.html"); + + const page1JsPath = page1Html.match(/src="(.*\.js)"/)?.[1]; + const page2JsPath = page2Html.match(/src="(.*\.js)"/)?.[1]; + + expect(page1JsPath).toBeDefined(); + expect(page2JsPath).toBeDefined(); + + const page1Js = api.readFile("out/" + page1JsPath!); + const page2Js = api.readFile("out/" + page2JsPath!); + + // Check we imported the shared module + expect(page2Js).toContain("import{sharedUtil}"); + expect(page1Js).toContain("import{sharedUtil}"); + + // Check CSS bundles + const page1CssPath = page1Html.match(/href="(.*\.css)"/)?.[1]; + const page2CssPath = page2Html.match(/href="(.*\.css)"/)?.[1]; + + expect(page1CssPath).toBeDefined(); + expect(page2CssPath).toBeDefined(); + + const page1Css = api.readFile("out/" + page1CssPath!); + const page2Css = api.readFile("out/" + page2CssPath!); + expect(page1Css).toContain("box-sizing:border-box"); + expect(page2Css).toContain("box-sizing:border-box"); + expect(page1Css).toContain(".shared"); + expect(page2Css).toContain(".shared"); + }, + }); + + // Test JS importing HTML + itBundled("html/js-importing-html", { + outdir: "out/", + files: { + "/in/entry.js": ` +import htmlContent from './template.html'; +console.log('Loaded HTML:', htmlContent);`, + + "/in/template.html": ` + + + + HTML Template + + +

HTML Template

+ +`, + }, + experimentalHtml: true, + + // This becomes: + // + // - out/entry.js + // - out/template-hash.html + // + // Like a regular asset. + entryPoints: ["/in/entry.js"], + onAfterBundle(api) { + const entryBundle = api.readFile("out/entry.js"); + // Check taht we dind't bundle the HTML file + expect(entryBundle).toMatch(/\.\/template-.*\.html/); + }, + }); + + itBundled("html/js-importing-html-and-entry-point-side-effect-import", { + outdir: "out/", + target: "browser", + files: { + "/in/2nd.js": ` +console.log('2nd');`, + "/in/entry.js": ` +import './template.html'; +console.log('Loaded HTML!');`, + + "/in/template.html": ` + + + + HTML Template + + +

HTML Template

+ + + +`, + }, + experimentalHtml: true, + // This becomes: + // - ./template.html + // - ./template-*.js + // - ./entry.js + entryPointsRaw: ["in/template.html", "in/entry.js"], + onAfterBundle(api) { + const templateBundle = api.readFile("out/template.html"); + expect(templateBundle).toContain("HTML Template"); + + // Get the entry.js file from looking at + + +`, + }, + experimentalHtml: true, + entryPointsRaw: ["in/template.html", "in/entry.js"], + bundleErrors: { + "/in/entry.js": ['No matching export in "in/template.html" for import "default"'], + }, + onAfterBundle(api) { + const templateBundle = api.readFile("out/template.html"); + expect(templateBundle).toContain("HTML Template"); + + // Get the entry.js file from looking at + + +`, + }, + experimentalHtml: false, + entryPointsRaw: ["in/template.html", "in/entry.js"], + onAfterBundle(api) { + const entryBundle = api.readFile("out/entry.js"); + + // Verify we DID bundle the HTML file + expect(entryBundle).toMatch(/\.\/template-.*\.html/); + const filename = entryBundle.match(/\.\/(template-.*\.html)/)?.[1]; + expect(filename).toBeDefined(); + const templateBundle = api.readFile("out/" + filename!); + expect(templateBundle).toContain("HTML Template"); + }, + }); + + // Test circular dependencies between JS and HTML + itBundled("html/circular-js-html", { + outdir: "out/", + files: { + "/in/main.js": ` +import page from './page.html'; +console.log('Main JS loaded page:', page);`, + + "/in/page.html": ` + + + + + + +
Circular Import Test
+ +`, + }, + experimentalHtml: true, + entryPoints: ["/in/main.js"], + onAfterBundle(api) { + const bundle = api.readFile("out/main.js"); + + // Check that it is a hashed file + expect(bundle).toMatch(/\.\/page-.*\.html/); + }, + }); + + // Test HTML with only CSS (no JavaScript) + itBundled("html/css-only", { + outdir: "out/", + files: { + "/in/page.html": ` + + + + + + + +
+

Styled Page

+

This page only has CSS styling.

+
+ +`, + "/in/styles-imported.css": ` +* { + box-sizing: border-box; +} +`, + "/in/styles.css": ` +@import "./styles-imported.css"; +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; +} +.title { + color: navy; +}`, + "/in/theme.css": ` +@import "./styles-imported.css"; +.content { + line-height: 1.6; + color: #333; +} +body { + background-color: #f5f5f5; +}`, + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/in/page.html"], + onAfterBundle(api) { + const htmlBundle = api.readFile("out/page.html"); + + // Check that CSS is properly referenced and hashed + expect(htmlBundle).toMatch(/href=".*\.css"/); + expect(htmlBundle).not.toContain("styles.css"); + expect(htmlBundle).not.toContain("theme.css"); + + // Get the CSS bundle path + const cssPath = htmlBundle.match(/href="(.*\.css)"/)?.[1]; + expect(cssPath).toBeDefined(); + + // Check the CSS bundle contents + const cssBundle = api.readFile("out/" + cssPath!); + expect(cssBundle).toContain(".container"); + expect(cssBundle).toContain(".title"); + expect(cssBundle).toContain(".content"); + expect(cssBundle).toContain("background-color"); + expect(cssBundle).toContain("box-sizing: border-box"); + }, + }); +}); diff --git a/test/bundler/bundler_plugin.test.ts b/test/bundler/bundler_plugin.test.ts index bd264f17dc..f168f9b3c3 100644 --- a/test/bundler/bundler_plugin.test.ts +++ b/test/bundler/bundler_plugin.test.ts @@ -689,7 +689,7 @@ describe("bundler", () => { expect(resolveCount).toBe(5050); expect(loadCount).toBe(101); }, - debugTimeoutScale: 3, + timeoutScale: 3, }; }); // itBundled("plugin/ManyPlugins", ({ root }) => { @@ -832,4 +832,3 @@ describe("bundler", () => { }; }); }); - diff --git a/test/bundler/esbuild/css.test.ts b/test/bundler/esbuild/css.test.ts index 2666a81c42..db04947753 100644 --- a/test/bundler/esbuild/css.test.ts +++ b/test/bundler/esbuild/css.test.ts @@ -41,6 +41,7 @@ body { itBundled("css/CSSNesting", { experimentalCss: true, + target: "bun", files: { "/entry.css": /* css */ ` body { diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index e9cba3ca5f..a96a3aed1c 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -6,7 +6,7 @@ import { callerSourceOrigin } from "bun:jsc"; import type { Matchers } from "bun:test"; import * as esbuild from "esbuild"; import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs"; -import { bunEnv, bunExe, isDebug } from "harness"; +import { bunEnv, bunExe, isCI, isDebug } from "harness"; import { tmpdir } from "os"; import path from "path"; import { SourceMapConsumer } from "source-map"; @@ -194,6 +194,7 @@ export interface BundlerTestInput { minifyIdentifiers?: boolean; minifySyntax?: boolean; experimentalCss?: boolean; + experimentalHtml?: boolean; targetFromAPI?: "TargetWasConfigured"; minifyWhitespace?: boolean; splitting?: boolean; @@ -278,8 +279,6 @@ export interface BundlerTestInput { /** Multiplier for test timeout */ timeoutScale?: number; - /** Multiplier for test timeout when using bun-debug. Debug builds already have a higher timeout. */ - debugTimeoutScale?: number; /* determines whether or not anything should be passed to outfile, outdir, etc. */ generateOutput?: boolean; @@ -450,6 +449,7 @@ function expectBundled( minifySyntax, minifyWhitespace, experimentalCss, + experimentalHtml, onAfterBundle, outdir, dotenv, @@ -665,7 +665,7 @@ function expectBundled( } } - // Run bun build cli. In the future we can move to using `Bun.Bundler` + // Run bun build cli. In the future we can move to using `Bun.Transpiler.` let warningReference: Record = {}; const expectedErrors = bundleErrors ? Object.entries(bundleErrors).flatMap(([file, v]) => v.map(error => ({ file, error }))) @@ -697,6 +697,7 @@ function expectBundled( minifyWhitespace && `--minify-whitespace`, drop?.length && drop.map(x => ["--drop=" + x]), experimentalCss && "--experimental-css", + experimentalHtml && "--experimental-html", globalName && `--global-name=${globalName}`, jsx.runtime && ["--jsx-runtime", jsx.runtime], jsx.factory && ["--jsx-factory", jsx.factory], @@ -1035,7 +1036,9 @@ function expectBundled( emitDCEAnnotations, ignoreDCEAnnotations, experimentalCss, + html: experimentalHtml ? true : undefined, drop, + define: define ?? {}, } as BuildConfig; if (dotenv) { @@ -1052,12 +1055,9 @@ function expectBundled( const debugFile = `import path from 'path'; import assert from 'assert'; const {plugins} = (${x})({ root: ${JSON.stringify(root)} }); -const options = ${JSON.stringify({ ...buildConfig, plugins: undefined }, null, 2)}; +const options = ${JSON.stringify({ ...buildConfig, throw: true, plugins: undefined }, null, 2)}; options.plugins = typeof plugins === "function" ? [{ name: "plugin", setup: plugins }] : plugins; const build = await Bun.build(options); -if (build.logs) { - throw build.logs; -} for (const [key, blob] of build.outputs) { await Bun.write(path.join(options.outdir, blob.path), blob.result); } @@ -1663,8 +1663,7 @@ export function itBundled( id, () => expectBundled(id, opts as any), // sourcemap code is slow - (opts.snapshotSourceMap ? (isDebug ? Infinity : 30_000) : isDebug ? 15_000 : 5_000) * - ((isDebug ? opts.debugTimeoutScale : opts.timeoutScale) ?? 1), + isCI ? undefined : isDebug ? Infinity : (opts.snapshotSourceMap ? 30_000 : 5_000) * (opts.timeoutScale ?? 1), ); } return ref; @@ -1676,8 +1675,7 @@ itBundled.only = (id: string, opts: BundlerTestInput) => { id, () => expectBundled(id, opts as any), // sourcemap code is slow - (opts.snapshotSourceMap ? (isDebug ? Infinity : 30_000) : isDebug ? 15_000 : 5_000) * - ((isDebug ? opts.debugTimeoutScale : opts.timeoutScale) ?? 1), + isCI ? undefined : isDebug ? Infinity : (opts.snapshotSourceMap ? 30_000 : 5_000) * (opts.timeoutScale ?? 1), ); }; diff --git a/test/bundler/native-plugin.test.ts b/test/bundler/native-plugin.test.ts index 10c67d8ce5..8a09905eaf 100644 --- a/test/bundler/native-plugin.test.ts +++ b/test/bundler/native-plugin.test.ts @@ -264,7 +264,7 @@ const many_foo = ["foo","foo","foo","foo","foo","foo","foo"] await Bun.$`echo ${files.map(([fp]) => fp).join("\n")} >> index.ts`; await Bun.$`echo ${files.map(([, varname]) => `console.log(JSON.stringify(${varname}))`).join("\n")} >> index.ts`; - const resultPromise = Bun.build({ + const result = await Bun.build({ outdir, entrypoints: [path.join(tempdir, "index.ts")], plugins: [ @@ -290,12 +290,9 @@ const many_foo = ["foo","foo","foo","foo","foo","foo","foo"] }, }, ], + throw: true, }); - const result = await resultPromise; - - if (!result.success) console.log(result); - expect(result.success).toBeTrue(); const output = await Bun.$`${bunExe()} run dist/index.js`.cwd(tempdir).text(); const outputJsons = output .trim() diff --git a/test/bundler/transpiler/bun-pragma.test.ts b/test/bundler/transpiler/bun-pragma.test.ts new file mode 100644 index 0000000000..71d5e87f32 --- /dev/null +++ b/test/bundler/transpiler/bun-pragma.test.ts @@ -0,0 +1,45 @@ +import path from "path"; +import { promises as fs } from "fs"; +import { bunExe, bunEnv } from "harness"; + +const fixturePath = (...segs: string[]): string => path.join(import.meta.dirname, "fixtures", "bun-pragma", ...segs); + +const OK = 0; +const ERR = 1; + +const runFixture = async (path: string): Promise => { + const child = Bun.spawn({ + cmd: [bunExe(), "run", path], + env: bunEnv, + stdio: ["ignore", "ignore", "ignore"], + }); + await child.exited; + expect(child.exitCode).not.toBeNull(); + return child.exitCode!; +}; + +describe("@bun pragma", () => { + describe("valid files", async () => { + const passPath = fixturePath("pass"); + const passFiles: string[] = await fs.readdir(passPath, { encoding: "utf-8" }); + expect(passFiles).not.toHaveLength(0); + + it.each(passFiles)("bun run %s", async file => { + const fullpath = path.join(passPath, file); + const exitCode = await runFixture(fullpath); + expect(exitCode).toBe(OK); + }); + }); + + describe("invalid files", async () => { + const failPath = fixturePath("fail"); + const failFiles: string[] = await fs.readdir(failPath, { encoding: "utf-8" }); + expect(failFiles).not.toHaveLength(0); + + it.each(failFiles)("bun run %s", async file => { + const fullpath = path.join(failPath, file); + const exitCode = await runFixture(fullpath); + expect(exitCode).toBe(ERR); + }); + }); +}); diff --git a/test/bundler/transpiler/fixtures/bun-pragma/fail/bun-pragma-before-hashbang.ts b/test/bundler/transpiler/fixtures/bun-pragma/fail/bun-pragma-before-hashbang.ts new file mode 100644 index 0000000000..f2ed8f50d4 --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/fail/bun-pragma-before-hashbang.ts @@ -0,0 +1,5 @@ +#!/usr/bin/env bun +// @bun +export const foo: number = 123; + +// Not valid syntax. Bun pragma must be at the top of the file. diff --git a/test/bundler/transpiler/fixtures/bun-pragma/fail/ts-with-pragma.ts b/test/bundler/transpiler/fixtures/bun-pragma/fail/ts-with-pragma.ts new file mode 100644 index 0000000000..27e7c411d2 --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/fail/ts-with-pragma.ts @@ -0,0 +1,2 @@ +// @bun +export const foo: number = 123; diff --git a/test/bundler/transpiler/fixtures/bun-pragma/pass/bun-in-url.ts b/test/bundler/transpiler/fixtures/bun-pragma/pass/bun-in-url.ts new file mode 100644 index 0000000000..64af8d8e3b --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/pass/bun-in-url.ts @@ -0,0 +1,5 @@ +// https://bun.sh/docs/api/http#bun-serve +const a: string = "hello"; +console.log(a); + +// '#bun' spotted in first comment but it's not a valid bun pragma diff --git a/test/bundler/transpiler/fixtures/bun-pragma/pass/not-a-pragma.ts b/test/bundler/transpiler/fixtures/bun-pragma/pass/not-a-pragma.ts new file mode 100644 index 0000000000..47e995f78d --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/pass/not-a-pragma.ts @@ -0,0 +1,2 @@ +// @not-bun @bytecode +export const foo: number = 123; diff --git a/test/bundler/transpiler/fixtures/bun-pragma/pass/pragma-only-no-newline.ts b/test/bundler/transpiler/fixtures/bun-pragma/pass/pragma-only-no-newline.ts new file mode 100644 index 0000000000..aaa250af21 --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/pass/pragma-only-no-newline.ts @@ -0,0 +1 @@ +// @bun diff --git a/test/bundler/transpiler/fixtures/bun-pragma/pass/pragma-only-with-newline.ts b/test/bundler/transpiler/fixtures/bun-pragma/pass/pragma-only-with-newline.ts new file mode 100644 index 0000000000..440005bdd3 --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/pass/pragma-only-with-newline.ts @@ -0,0 +1,2 @@ +// @bun + diff --git a/test/bundler/transpiler/fixtures/bun-pragma/pass/valid-pragma-without-bun-prefix.ts b/test/bundler/transpiler/fixtures/bun-pragma/pass/valid-pragma-without-bun-prefix.ts new file mode 100644 index 0000000000..23d4025f62 --- /dev/null +++ b/test/bundler/transpiler/fixtures/bun-pragma/pass/valid-pragma-without-bun-prefix.ts @@ -0,0 +1,5 @@ +// @bytecode +export const foo: number = 122; + +// explanation: @bytecode is a valid pragma checked by the parser, but only if +// @bun is found before it. diff --git a/test/bundler/transpiler/fixtures/lots-of-for-loop.js b/test/bundler/transpiler/fixtures/lots-of-for-loop.js new file mode 100644 index 0000000000..096ff28911 --- /dev/null +++ b/test/bundler/transpiler/fixtures/lots-of-for-loop.js @@ -0,0 +1,713 @@ +let counter = 0; +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) + +for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) counter++; +console.log(counter); \ No newline at end of file diff --git a/test/bundler/transpiler/macro-test.test.ts b/test/bundler/transpiler/macro-test.test.ts index 5c95553400..be7aed0ac5 100644 --- a/test/bundler/transpiler/macro-test.test.ts +++ b/test/bundler/transpiler/macro-test.test.ts @@ -1,6 +1,13 @@ import { escapeHTML } from "bun" assert { type: "macro" }; import { expect, test } from "bun:test"; import { addStrings, addStringsUTF16, escape, identity } from "./macro.ts" assert { type: "macro" }; +import defaultMacro, { + default as defaultMacroAlias, + identity as identity1, + identity as identity2, +} from "./macro.ts" assert { type: "macro" }; + +import * as macros from "./macro.ts" assert { type: "macro" }; test("bun builtins can be used in macros", async () => { expect(escapeHTML("abc!")).toBe("abc!"); @@ -89,6 +96,24 @@ test("utf16 string", () => { expect(identity("😊 Smiling Face with Smiling Eyes Emoji")).toBe("😊 Smiling Face with Smiling Eyes Emoji"); }); +test("import aliases", () => { + expect(identity1({ a: 1 })).toEqual({ a: 1 }); + expect(identity1([1, 2, 3])).toEqual([1, 2, 3]); + expect(identity2({ a: 1 })).toEqual({ a: 1 }); + expect(identity2([1, 2, 3])).toEqual([1, 2, 3]); +}); + +test("default import", () => { + expect(defaultMacro()).toBe("defaultdefaultdefault"); + expect(defaultMacroAlias()).toBe("defaultdefaultdefault"); +}); + +test("namespace import", () => { + expect(macros.identity({ a: 1 })).toEqual({ a: 1 }); + expect(macros.identity([1, 2, 3])).toEqual([1, 2, 3]); + expect(macros.escape()).toBe("\\\f\n\r\t\v\0'\"`$\x00\x0B\x0C"); +}); + // test("template string ascii", () => { // expect(identity(`A${""}`)).toBe("A"); // }); diff --git a/test/bundler/transpiler/macro.ts b/test/bundler/transpiler/macro.ts index fc747333cb..430fab84ee 100644 --- a/test/bundler/transpiler/macro.ts +++ b/test/bundler/transpiler/macro.ts @@ -13,3 +13,7 @@ export function addStrings(arg: string) { export function addStringsUTF16(arg: string) { return arg + "\\\f\n\r\t\v\0'\"`$\x00\x0B\x0C" + "😊"; } + +export default function() { + return "defaultdefaultdefault"; +} diff --git a/test/bundler/transpiler/runtime-transpiler.test.ts b/test/bundler/transpiler/runtime-transpiler.test.ts index 7534e42042..19a234f556 100644 --- a/test/bundler/transpiler/runtime-transpiler.test.ts +++ b/test/bundler/transpiler/runtime-transpiler.test.ts @@ -1,6 +1,15 @@ import { beforeEach, describe, expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; +test("use strict causes CommonJS", () => { + const { stdout, exitCode } = Bun.spawnSync({ + cmd: [bunExe(), require.resolve("./use-strict-fixture.js")], + env: bunEnv, + }); + expect(stdout.toString()).toBe("function\n"); + expect(exitCode).toBe(0); +}); + test("non-ascii regexp literals", () => { var str = "🔴11 54 / 10,000"; expect(str.replace(/[🔵🔴,]+/g, "")).toBe("11 54 / 10000"); diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index 8757b968ae..0a49f8723f 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -315,6 +315,13 @@ describe("Bun.Transpiler", () => { exp("f<{}>()\nfunction f() {}", "let f = function() {\n};\nf()"); }); + it("malformed enums", () => { + const err = ts.expectParseError; + + err("enum Foo { [2]: 'hi' }", 'Expected identifier but found "["'); + err("enum [] { a }", 'Expected identifier but found "["'); + }); + // TODO: fix all the cases that report generic "Parse error" it("types", () => { const exp = ts.expectPrinted_; @@ -3441,3 +3448,19 @@ it("does not crash with 9 comments and typescript type skipping", () => { expect(stdout.toString()).toContain("success!"); expect(exitCode).toBe(0); }); + +it("runtime transpiler stack overflows", async () => { + expect(async () => await import("./fixtures/lots-of-for-loop.js")).toThrow(`Maximum call stack size exceeded`); +}); + +it("Bun.Transpiler.transformSync stack overflows", async () => { + const code = await Bun.file(join(import.meta.dir, "fixtures", "lots-of-for-loop.js")).text(); + const transpiler = new Bun.Transpiler(); + expect(() => transpiler.transformSync(code)).toThrow(`Maximum call stack size exceeded`); +}); + +it("Bun.Transpiler.transform stack overflows", async () => { + const code = await Bun.file(join(import.meta.dir, "fixtures", "lots-of-for-loop.js")).text(); + const transpiler = new Bun.Transpiler(); + expect(async () => await transpiler.transform(code)).toThrow(`Maximum call stack size exceeded`); +}); diff --git a/test/bundler/transpiler/use-strict-fixture.js b/test/bundler/transpiler/use-strict-fixture.js new file mode 100644 index 0000000000..49ee828032 --- /dev/null +++ b/test/bundler/transpiler/use-strict-fixture.js @@ -0,0 +1,5 @@ +"use strict"; + +// Test that 'use strict' makes it CommonJS when we otherwise don't know which one to pick. +// Without that, this direct eval becomes indirect, throwing a ReferenceError. +console.log(eval("typeof module.require")); diff --git a/test/cli/install/__snapshots__/bun-install.test.ts.snap b/test/cli/install/__snapshots__/bun-install.test.ts.snap index bd375763ac..96d0b00550 100644 --- a/test/cli/install/__snapshots__/bun-install.test.ts.snap +++ b/test/cli/install/__snapshots__/bun-install.test.ts.snap @@ -54,3 +54,22 @@ note: Package name is also declared here at [dir]/bar/package.json:1:9 " `; + +exports[`should handle modified git resolutions in bun.lock 1`] = `"{"lockfileVersion":0,"workspaces":{"":{"dependencies":{"jquery":"3.7.1"}}},"packages":{"jquery":["jquery@git+ssh://git@github.com/dylan-conway/install-test-8.git#3a1288830817d13da39e9231302261896f8721ea",{},"3a1288830817d13da39e9231302261896f8721ea"]}}"`; + +exports[`should read install.saveTextLockfile from bunfig.toml 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": {}, + "packages/pkg1": { + "name": "pkg-one", + "version": "1.0.0", + }, + }, + "packages": { + "pkg-one": ["pkg-one@workspace:packages/pkg1", {}], + } +} +" +`; diff --git a/test/cli/install/__snapshots__/bun-workspaces.test.ts.snap b/test/cli/install/__snapshots__/bun-workspaces.test.ts.snap index 2fca1723d8..423a2b49ae 100644 --- a/test/cli/install/__snapshots__/bun-workspaces.test.ts.snap +++ b/test/cli/install/__snapshots__/bun-workspaces.test.ts.snap @@ -25,7 +25,7 @@ exports[`dependency on workspace without version in package.json: version: * 1`] }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -152,7 +152,7 @@ exports[`dependency on workspace without version in package.json: version: *.*.* }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -279,7 +279,7 @@ exports[`dependency on workspace without version in package.json: version: =* 1` }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -406,7 +406,7 @@ exports[`dependency on workspace without version in package.json: version: kjwoe }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "dist_tag": { @@ -533,7 +533,7 @@ exports[`dependency on workspace without version in package.json: version: *.1.* }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -660,7 +660,7 @@ exports[`dependency on workspace without version in package.json: version: *-pre }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -787,7 +787,7 @@ exports[`dependency on workspace without version in package.json: version: 1 1`] }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -944,7 +944,7 @@ exports[`dependency on workspace without version in package.json: version: 1.* 1 }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -1101,7 +1101,7 @@ exports[`dependency on workspace without version in package.json: version: 1.1.* }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -1258,7 +1258,7 @@ exports[`dependency on workspace without version in package.json: version: 1.1.1 }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -1415,7 +1415,7 @@ exports[`dependency on workspace without version in package.json: version: *-pre }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -1572,7 +1572,7 @@ exports[`dependency on workspace without version in package.json: version: *+bui }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "id": 2, @@ -1729,7 +1729,7 @@ exports[`dependency on workspace without version in package.json: version: lates }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "dist_tag": { @@ -1886,7 +1886,7 @@ exports[`dependency on workspace without version in package.json: version: 1`] }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "dist_tag": { @@ -2043,7 +2043,7 @@ exports[`dependency on same name as workspace and dist-tag: with version 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, "workspace": true, }, "dist_tag": { diff --git a/test/cli/install/bun-add.test.ts b/test/cli/install/bun-add.test.ts index f670bc6827..89c4a9e312 100644 --- a/test/cli/install/bun-add.test.ts +++ b/test/cli/install/bun-add.test.ts @@ -1224,7 +1224,6 @@ it("should install version tagged with `latest` by default", async () => { }); const err2 = await new Response(stderr2).text(); expect(err2).not.toContain("error:"); - expect(err2).toContain("Saved lockfile"); const out2 = await new Response(stdout2).text(); expect(out2.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ expect.stringContaining("bun install v1."), @@ -1234,8 +1233,8 @@ it("should install version tagged with `latest` by default", async () => { "1 package installed", ]); expect(await exited2).toBe(0); - expect(urls.sort()).toEqual([`${root_url}/baz`, `${root_url}/baz-0.0.3.tgz`]); - expect(requested).toBe(4); + expect(urls.sort()).toEqual([`${root_url}/baz-0.0.3.tgz`]); + expect(requested).toBe(3); expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "baz"]); expect(await file(join(package_dir, "node_modules", "baz", "package.json")).json()).toEqual({ name: "baz", @@ -1607,8 +1606,7 @@ it("should add dependency without duplication", async () => { "installed bar@0.0.2", ]); expect(await exited2).toBe(0); - expect(urls.sort()).toBeEmpty(); - expect(requested).toBe(2); + expect(requested).toBe(3); expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "bar"]); expect(await readdirSorted(join(package_dir, "node_modules", "bar"))).toEqual(["package.json"]); expect(await file(join(package_dir, "node_modules", "bar", "package.json")).json()).toEqual({ diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index b6470eba7f..70addfbf37 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -1,4 +1,4 @@ -import { file, listen, Socket, spawn } from "bun"; +import { file, listen, Socket, spawn, write } from "bun"; import { afterAll, afterEach, @@ -11,7 +11,7 @@ import { setDefaultTimeout, test, } from "bun:test"; -import { access, mkdir, readlink, rm, writeFile, cp, stat } from "fs/promises"; +import { access, mkdir, readlink, rm, writeFile, cp, stat, exists } from "fs/promises"; import { bunEnv, bunExe, @@ -22,6 +22,7 @@ import { toHaveBins, runBunInstall, isWindows, + textLockfile, } from "harness"; import { join, sep, resolve } from "path"; import { @@ -35,6 +36,7 @@ import { requested, root_url, setHandler, + getPort, } from "./dummy.registry.js"; expect.extend({ @@ -1121,59 +1123,6 @@ it("should handle inter-dependency between workspaces (optionalDependencies)", a await access(join(package_dir, "bun.lockb")); }); -it("should ignore peerDependencies within workspaces", async () => { - await writeFile( - join(package_dir, "package.json"), - JSON.stringify({ - name: "Foo", - version: "0.0.1", - workspaces: ["packages/baz"], - peerDependencies: { - Bar: ">=0.0.2", - }, - }), - ); - await mkdir(join(package_dir, "packages", "baz"), { recursive: true }); - await writeFile( - join(package_dir, "packages", "baz", "package.json"), - JSON.stringify({ - name: "Baz", - version: "0.0.3", - peerDependencies: { - Moo: ">=0.0.4", - }, - }), - ); - await writeFile( - join(package_dir, "bunfig.toml"), - ` - [install] - peer = false - `, - ); - const { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: package_dir, - stdout: "pipe", - stdin: "pipe", - stderr: "pipe", - env, - }); - const err = await new Response(stderr).text(); - expect(err).toContain("Saved lockfile"); - const out = await new Response(stdout).text(); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - expect.stringContaining("bun install v1."), - "", - "1 package installed", - ]); - expect(await exited).toBe(0); - expect(requested).toBe(0); - expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual(["Baz"]); - expect(package_dir).toHaveWorkspaceLink(["Baz", "packages/baz"]); - await access(join(package_dir, "bun.lockb")); -}); - it("should handle installing the same peerDependency with different versions", async () => { const urls: string[] = []; setHandler(dummyRegistry(urls)); @@ -5708,14 +5657,8 @@ it("should handle tarball URL with existing lockfile", async () => { "3 packages installed", ]); expect(await exited2).toBe(0); - expect(urls.sort()).toEqual([ - `${root_url}/bar`, - `${root_url}/bar-0.0.2.tgz`, - `${root_url}/baz`, - `${root_url}/baz-0.0.3.tgz`, - `${root_url}/moo-0.1.0.tgz`, - ]); - expect(requested).toBe(10); + expect(urls.sort()).toEqual([`${root_url}/bar-0.0.2.tgz`, `${root_url}/baz-0.0.3.tgz`, `${root_url}/moo-0.1.0.tgz`]); + expect(requested).toBe(8); expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".bin", ".cache", "@barn", "bar", "baz"]); expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toHaveBins(["baz-run"]); expect(join(package_dir, "node_modules", ".bin", "baz-run")).toBeValidBin(join("..", "baz", "index.js")); @@ -5850,13 +5793,8 @@ it("should handle tarball path with existing lockfile", async () => { "3 packages installed", ]); expect(await exited2).toBe(0); - expect(urls.sort()).toEqual([ - `${root_url}/bar`, - `${root_url}/bar-0.0.2.tgz`, - `${root_url}/baz`, - `${root_url}/baz-0.0.3.tgz`, - ]); - expect(requested).toBe(8); + expect(urls.sort()).toEqual([`${root_url}/bar-0.0.2.tgz`, `${root_url}/baz-0.0.3.tgz`]); + expect(requested).toBe(6); expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".bin", ".cache", "@barn", "bar", "baz"]); expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toHaveBins(["baz-run"]); expect(join(package_dir, "node_modules", ".bin", "baz-run")).toBeValidBin(join("..", "baz", "index.js")); @@ -8361,3 +8299,240 @@ cache = false expect(await new Response(stdout).text()).toContain("installed @~39/empty@1.0.0"); expect(await exited).toBe(0); }); + +it("should handle modified git resolutions in bun.lock", async () => { + // install-test-8 has a dependency but because it's not in the lockfile + // it won't be included in the install. + await Promise.all([ + write( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + version: "0.0.1", + dependencies: { + "jquery": "3.7.1", + }, + }), + ), + write( + join(package_dir, "bun.lock"), + JSON.stringify({ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "jquery": "3.7.1", + }, + }, + }, + "packages": { + "jquery": [ + "jquery@git+ssh://git@github.com/dylan-conway/install-test-8.git#3a1288830817d13da39e9231302261896f8721ea", + {}, + "3a1288830817d13da39e9231302261896f8721ea", + ], + }, + }), + ), + ]); + + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + const out = await Bun.readableStreamToText(stdout); + expect(err).not.toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + + expect(out).toContain("1 package installed"); + expect(await exited).toBe(0); + + expect( + (await file(join(package_dir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234"), + ).toMatchSnapshot(); +}); + +it("should read install.saveTextLockfile from bunfig.toml", async () => { + await Promise.all([ + write( + join(package_dir, "bunfig.toml"), + ` +[install] +cache = false +registry = "http://localhost:${getPort()}/" +saveTextLockfile = true +`, + ), + write( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["packages/*"], + }), + ), + write( + join(package_dir, "packages", "pkg1", "package.json"), + JSON.stringify({ + name: "pkg-one", + version: "1.0.0", + }), + ), + ]); + + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).toContain("Saved lockfile"); + const out = await Bun.readableStreamToText(stdout); + expect(out).toContain("1 package installed"); + + expect(await exited).toBe(0); + expect(await Bun.file(join(package_dir, "node_modules", "pkg-one", "package.json")).json()).toEqual({ + name: "pkg-one", + version: "1.0.0", + }); + expect(await exists(join(package_dir, "bun.lockb"))).toBeFalse(); + expect(await file(join(package_dir, "bun.lock")).text()).toMatchSnapshot(); +}); + +test("providing invalid url in lockfile does not crash", async () => { + await Promise.all([ + write( + join(package_dir, "package.json"), + JSON.stringify({ + dependencies: { + "jquery": "3.7.1", + }, + }), + ), + write( + join(package_dir, "bun.lock"), + textLockfile(0, { + "workspaces": { + "": { + "dependencies": { + "jquery": "3.7.1", + }, + }, + }, + "packages": { + "jquery": [ + "jquery@3.7.1", + "invalid-url", + {}, + "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + ], + }, + }), + ), + ]); + + const { stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).toContain( + 'error: Expected tarball URL to start with https:// or http://, got "invalid-url" while fetching package "jquery"', + ); + expect(await exited).toBe(1); +}); + +test("optional dependencies do not need to be resolvable in text lockfile", async () => { + await Promise.all([ + write( + join(package_dir, "package.json"), + JSON.stringify({ + optionalDependencies: { + jquery: "3.7.1", + }, + }), + ), + write( + join(package_dir, "bun.lock"), + textLockfile(0, { + "workspaces": { + "": { + "optionalDependencies": { + "jquery": "3.7.1", + }, + }, + }, + "packages": {}, + }), + ), + ]); + + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("Saved lockfile"); + const out = await Bun.readableStreamToText(stdout); + expect(out).not.toContain("1 package installed"); + + expect(await exited).toBe(0); +}); + +test("non-optional dependencies need to be resolvable in text lockfile", async () => { + await Promise.all([ + write( + join(package_dir, "package.json"), + JSON.stringify({ + dependencies: { + jquery: "3.7.1", + }, + }), + ), + write( + join(package_dir, "bun.lock"), + textLockfile(0, { + workspaces: { + "": { + dependencies: { + "jquery": "3.7.1", + }, + }, + }, + packages: {}, + }), + ), + ]); + + const { stdout, stderr, exited } = spawn({ + // --production to fail early + cmd: [bunExe(), "install", "--production"], + cwd: package_dir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + const err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("Saved lockfile"); + expect(err).toContain("error: Failed to resolve root prod dependency 'jquery'"); + const out = await Bun.readableStreamToText(stdout); + expect(out).not.toContain("1 package installed"); + + expect(await exited).toBe(1); +}); diff --git a/test/cli/install/bun-link.test.ts b/test/cli/install/bun-link.test.ts index 8cdeaa4413..20d2be6439 100644 --- a/test/cli/install/bun-link.test.ts +++ b/test/cli/install/bun-link.test.ts @@ -1,7 +1,7 @@ import { file, spawn } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test"; import { access, mkdir, writeFile } from "fs/promises"; -import { bunExe, bunEnv as env, runBunInstall, tmpdirSync, toBeValidBin, toHaveBins } from "harness"; +import { bunExe, bunEnv as env, runBunInstall, tmpdirSync, toBeValidBin, toHaveBins, stderrForInstall } from "harness"; import { basename, join } from "path"; import { dummyAfterAll, @@ -56,7 +56,7 @@ it("should link and unlink workspace package", async () => { }), ); let { out, err } = await runBunInstall(env, link_dir); - expect(err.split(/\r?\n/)).toEqual(["Saved lockfile", ""]); + expect(err.split(/\r?\n/).slice(-2)).toEqual(["Saved lockfile", ""]); expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ expect.stringContaining("bun install v1."), "", @@ -72,7 +72,7 @@ it("should link and unlink workspace package", async () => { env, }); - err = await new Response(stderr).text(); + err = stderrForInstall(await new Response(stderr).text()); expect(err.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout).text()).toContain(`Success! Registered "moo"`); expect(await exited).toBe(0); @@ -86,7 +86,7 @@ it("should link and unlink workspace package", async () => { env, })); - err = await new Response(stderr).text(); + err = stderrForInstall(await new Response(stderr).text()); expect(err.split(/\r?\n/)).toEqual([""]); expect((await new Response(stdout).text()).replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ expect.stringContaining("bun link v1."), @@ -110,7 +110,7 @@ it("should link and unlink workspace package", async () => { env, })); - err = await new Response(stderr).text(); + err = stderrForInstall(await new Response(stderr).text()); expect(err.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout).text()).toContain(`success: unlinked package "moo"`); expect(await exited).toBe(0); @@ -125,7 +125,7 @@ it("should link and unlink workspace package", async () => { env, })); - err = await new Response(stderr).text(); + err = stderrForInstall(await new Response(stderr).text()); expect(err.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout).text()).toContain(`Success! Registered "foo"`); expect(await exited).toBe(0); @@ -139,7 +139,7 @@ it("should link and unlink workspace package", async () => { env, })); - err = await new Response(stderr).text(); + err = stderrForInstall(await new Response(stderr).text()); expect(err.split(/\r?\n/)).toEqual([""]); expect((await new Response(stdout).text()).replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ expect.stringContaining("bun link v1."), @@ -164,7 +164,7 @@ it("should link and unlink workspace package", async () => { env, })); - err = await new Response(stderr).text(); + err = stderrForInstall(await new Response(stderr).text()); expect(err.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout).text()).toContain(`success: unlinked package "foo"`); expect(await exited).toBe(0); @@ -199,7 +199,7 @@ it("should link package", async () => { stderr: "pipe", env, }); - const err1 = await new Response(stderr1).text(); + const err1 = stderrForInstall(await new Response(stderr1).text()); expect(err1.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout1).text()).toContain(`Success! Registered "${link_name}"`); expect(await exited1).toBe(0); @@ -216,7 +216,7 @@ it("should link package", async () => { stderr: "pipe", env, }); - const err2 = await new Response(stderr2).text(); + const err2 = stderrForInstall(await new Response(stderr2).text()); expect(err2.split(/\r?\n/)).toEqual([""]); const out2 = await new Response(stdout2).text(); expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ @@ -240,7 +240,7 @@ it("should link package", async () => { stderr: "pipe", env, }); - const err3 = await new Response(stderr3).text(); + const err3 = stderrForInstall(await new Response(stderr3).text()); expect(err3.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout3).text()).toContain(`success: unlinked package "${link_name}"`); expect(await exited3).toBe(0); @@ -257,7 +257,7 @@ it("should link package", async () => { stderr: "pipe", env, }); - const err4 = await new Response(stderr4).text(); + const err4 = stderrForInstall(await new Response(stderr4).text()); expect(err4).toContain(`error: Package "${link_name}" is not linked`); expect(await new Response(stdout4).text()).toEqual(expect.stringContaining("bun link v1.")); expect(await exited4).toBe(1); @@ -292,7 +292,7 @@ it("should link scoped package", async () => { stderr: "pipe", env, }); - const err1 = await new Response(stderr1).text(); + const err1 = stderrForInstall(await new Response(stderr1).text()); expect(err1.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout1).text()).toContain(`Success! Registered "${link_name}"`); expect(await exited1).toBe(0); @@ -309,7 +309,7 @@ it("should link scoped package", async () => { stderr: "pipe", env, }); - const err2 = await new Response(stderr2).text(); + const err2 = stderrForInstall(await new Response(stderr2).text()); expect(err2.split(/\r?\n/)).toEqual([""]); const out2 = await new Response(stdout2).text(); expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ @@ -333,7 +333,7 @@ it("should link scoped package", async () => { stderr: "pipe", env, }); - const err3 = await new Response(stderr3).text(); + const err3 = stderrForInstall(await new Response(stderr3).text()); expect(err3.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout3).text()).toContain(`success: unlinked package "${link_name}"`); expect(await exited3).toBe(0); @@ -350,7 +350,7 @@ it("should link scoped package", async () => { stderr: "pipe", env, }); - const err4 = await new Response(stderr4).text(); + const err4 = stderrForInstall(await new Response(stderr4).text()); expect(err4).toContain(`error: Package "${link_name}" is not linked`); expect((await new Response(stdout4).text()).split(/\r?\n/)).toEqual([expect.stringContaining("bun link v1."), ""]); expect(await exited4).toBe(1); @@ -392,14 +392,14 @@ it("should link dependency without crashing", async () => { stderr: "pipe", env, }); - const err1 = await new Response(stderr1).text(); + const err1 = stderrForInstall(await new Response(stderr1).text()); expect(err1.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout1).text()).toContain(`Success! Registered "${link_name}"`); expect(await exited1).toBe(0); const { out: stdout2, err: stderr2, exited: exited2 } = await runBunInstall(env, package_dir); - const err2 = await new Response(stderr2).text(); - expect(err2.split(/\r?\n/)).toEqual(["Saved lockfile", ""]); + const err2 = stderrForInstall(await new Response(stderr2).text()); + expect(err2.split(/\r?\n/).slice(-2)).toEqual(["Saved lockfile", ""]); const out2 = await new Response(stdout2).text(); expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ expect.stringContaining("bun install v1."), @@ -429,7 +429,7 @@ it("should link dependency without crashing", async () => { stderr: "pipe", env, }); - const err3 = await new Response(stderr3).text(); + const err3 = stderrForInstall(await new Response(stderr3).text()); expect(err3.split(/\r?\n/)).toEqual([""]); expect(await new Response(stdout3).text()).toContain(`success: unlinked package "${link_name}"`); expect(await exited3).toBe(0); @@ -446,7 +446,7 @@ it("should link dependency without crashing", async () => { stderr: "pipe", env, }); - const err4 = await new Response(stderr4).text(); + const err4 = stderrForInstall(await new Response(stderr4).text()); expect(err4).toContain(`error: FileNotFound installing ${link_name}`); const out4 = await new Response(stdout4).text(); expect(out4.replace(/\[[0-9\.]+m?s\]/, "[]").split(/\r?\n/)).toEqual([ diff --git a/test/cli/install/bun-lock.test.ts b/test/cli/install/bun-lock.test.ts new file mode 100644 index 0000000000..d5d6ce6c3c --- /dev/null +++ b/test/cli/install/bun-lock.test.ts @@ -0,0 +1,51 @@ +import { spawn } from "bun"; +import { expect, it } from "bun:test"; +import { access, copyFile, open, writeFile } from "fs/promises"; +import { bunExe, bunEnv as env, isWindows, tmpdirSync } from "harness"; +import { join } from "path"; + +it("should write plaintext lockfiles", async () => { + const package_dir = tmpdirSync(); + + // copy bar-0.0.2.tgz to package_dir + await copyFile(join(__dirname, "bar-0.0.2.tgz"), join(package_dir, "bar-0.0.2.tgz")); + + // Create a simple package.json + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "test-package", + version: "1.0.0", + dependencies: { + "dummy-package": "file:./bar-0.0.2.tgz", + }, + }), + ); + + // Run 'bun install' to generate the lockfile + const installResult = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: package_dir, + env, + }); + await installResult.exited; + + // Ensure the lockfile was created + await access(join(package_dir, "bun.lock")); + + // Assert that the lockfile has the correct permissions + const file = await open(join(package_dir, "bun.lock"), "r"); + const stat = await file.stat(); + + // in unix, 0o644 == 33188 + let mode = 33188; + // ..but windows is different + if (isWindows) { + mode = 33206; + } + expect(stat.mode).toBe(mode); + + expect(await file.readFile({ encoding: "utf8" })).toEqual( + `{\n \"lockfileVersion\": 0,\n \"workspaces\": {\n \"\": {\n \"dependencies\": {\n \"dummy-package\": \"file:./bar-0.0.2.tgz\",\n },\n },\n },\n \"packages\": {\n \"dummy-package\": [\"bar@./bar-0.0.2.tgz\", {}],\n }\n}\n`, + ); +}); diff --git a/test/cli/install/bun-lockb.test.ts b/test/cli/install/bun-lockb.test.ts index b97bc1759a..cf0ab704ef 100644 --- a/test/cli/install/bun-lockb.test.ts +++ b/test/cli/install/bun-lockb.test.ts @@ -1,7 +1,7 @@ import { spawn } from "bun"; import { expect, it } from "bun:test"; -import { access, copyFile, writeFile } from "fs/promises"; -import { bunExe, bunEnv as env, tmpdirSync } from "harness"; +import { access, copyFile, open, writeFile } from "fs/promises"; +import { bunExe, bunEnv as env, isWindows, tmpdirSync } from "harness"; import { join } from "path"; it("should not print anything to stderr when running bun.lockb", async () => { @@ -33,6 +33,18 @@ it("should not print anything to stderr when running bun.lockb", async () => { // Ensure the lockfile was created await access(join(package_dir, "bun.lockb")); + // Assert that the lockfile has the correct permissions + const file = await open(join(package_dir, "bun.lockb"), "r"); + const stat = await file.stat(); + + // in unix, 0o755 == 33261 + let mode = 33261; + // ..but windows is different + if(isWindows) { + mode = 33206; + } + expect(stat.mode).toBe(mode); + // create a .env await writeFile(join(package_dir, ".env"), "FOO=bar"); diff --git a/test/cli/install/bun-run.test.ts b/test/cli/install/bun-run.test.ts index 52954e16c5..99293d6013 100644 --- a/test/cli/install/bun-run.test.ts +++ b/test/cli/install/bun-run.test.ts @@ -275,7 +275,7 @@ console.log(minify("print(6 * 7)").code); BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), }, }); - const err1 = await new Response(stderr1).text(); + const err1 = stderrForInstall(await new Response(stderr1).text()); expect(err1).toBe(""); expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); expect(await readdirSorted(join(run_dir, ".cache"))).toContain("uglify-js"); @@ -339,7 +339,7 @@ for (const entry of await decompress(Buffer.from(buffer))) { BUN_INSTALL_CACHE_DIR: join(run_dir, ".cache"), }, }); - const err1 = await new Response(stderr1).text(); + const err1 = stderrForInstall(await new Response(stderr1).text()); expect(err1).toBe(""); expect(await readdirSorted(run_dir)).toEqual([".cache", "test.js"]); expect(await readdirSorted(join(run_dir, ".cache"))).toContain("decompress"); diff --git a/test/cli/install/dummy.registry.ts b/test/cli/install/dummy.registry.ts index 474511d489..060b50a0fe 100644 --- a/test/cli/install/dummy.registry.ts +++ b/test/cli/install/dummy.registry.ts @@ -116,6 +116,10 @@ export function dummyAfterAll() { server.stop(); } +export function getPort() { + return server.port; +} + let packageDirGetter: () => string = () => { return tmpdirSync(); }; diff --git a/test/cli/install/migration/migrate.test.ts b/test/cli/install/migration/migrate.test.ts index ccd379af61..ea083297a3 100644 --- a/test/cli/install/migration/migrate.test.ts +++ b/test/cli/install/migration/migrate.test.ts @@ -78,6 +78,24 @@ test("migrate package with dependency on root package", async () => { expect(fs.existsSync(join(testDir, "node_modules", "test-pkg", "package.json"))).toBeTrue(); }); +test("migrate package with npm dependency that resolves to a git package", async () => { + const testDir = tmpdirSync(); + + fs.cpSync(join(import.meta.dir, "npm-version-to-git-resolution"), testDir, { recursive: true }); + + const { exitCode } = Bun.spawnSync([bunExe(), "install"], { + env: bunEnv, + cwd: testDir, + stdout: "pipe", + }); + + expect(exitCode).toBe(0); + expect(await Bun.file(join(testDir, "node_modules", "jquery", "package.json")).json()).toHaveProperty( + "name", + "install-test", + ); +}); + test("migrate from npm lockfile that is missing `resolved` properties", async () => { const testDir = tmpdirSync(); diff --git a/test/cli/install/migration/npm-version-to-git-resolution/.gitignore b/test/cli/install/migration/npm-version-to-git-resolution/.gitignore new file mode 100644 index 0000000000..2fe28d55d5 --- /dev/null +++ b/test/cli/install/migration/npm-version-to-git-resolution/.gitignore @@ -0,0 +1 @@ +!package-lock.json \ No newline at end of file diff --git a/test/cli/install/migration/npm-version-to-git-resolution/package-lock.json b/test/cli/install/migration/npm-version-to-git-resolution/package-lock.json new file mode 100644 index 0000000000..dc1ba33747 --- /dev/null +++ b/test/cli/install/migration/npm-version-to-git-resolution/package-lock.json @@ -0,0 +1,20 @@ +{ + "name": "lock", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lock", + "version": "0.0.1", + "dependencies": { + "jquery": "3.7.1" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "git+ssh://git@github.com/dylan-conway/install-test.git#596234dab30564f37adae1e5c4d7123bcffce537", + "license": "MIT" + } + } +} diff --git a/test/cli/install/migration/npm-version-to-git-resolution/package.json b/test/cli/install/migration/npm-version-to-git-resolution/package.json new file mode 100644 index 0000000000..fdcf09b244 --- /dev/null +++ b/test/cli/install/migration/npm-version-to-git-resolution/package.json @@ -0,0 +1,7 @@ +{ + "name": "npm-version-to-git-resolution", + "version": "1.1.1", + "dependencies": { + "jquery": "3.7.1" + } +} diff --git a/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap b/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap index 3793083aef..9d2bebfe0c 100644 --- a/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap +++ b/test/cli/install/registry/__snapshots__/bun-install-registry.test.ts.snap @@ -159,7 +159,9 @@ exports[`text lockfile workspace sorting 1`] = ` }, "packages": { "b": ["b@workspace:packages/b", { "dependencies": { "no-deps": "1.0.0" } }], + "c": ["c@workspace:packages/c", { "dependencies": { "no-deps": "1.0.0" } }], + "no-deps": ["no-deps@1.0.0", "http://localhost:1234/no-deps/-/no-deps-1.0.0.tgz", {}, "sha512-v4w12JRjUGvfHDUP8vFDwu0gUWu04j0cv9hLb1Abf9VdaXu4XcrddYFTMVBVvmldKViGWH7jrb6xPJRF0wq6gw=="], } } @@ -196,10 +198,143 @@ exports[`text lockfile workspace sorting 2`] = ` }, "packages": { "a": ["a@workspace:packages/a", { "dependencies": { "no-deps": "1.0.0" } }], + "b": ["b@workspace:packages/b", { "dependencies": { "no-deps": "1.0.0" } }], + "c": ["c@workspace:packages/c", { "dependencies": { "no-deps": "1.0.0" } }], + "no-deps": ["no-deps@1.0.0", "http://localhost:1234/no-deps/-/no-deps-1.0.0.tgz", {}, "sha512-v4w12JRjUGvfHDUP8vFDwu0gUWu04j0cv9hLb1Abf9VdaXu4XcrddYFTMVBVvmldKViGWH7jrb6xPJRF0wq6gw=="], } } " `; + +exports[`text lockfile --frozen-lockfile 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "a-dep": "^1.0.2", + "no-deps": "^1.0.0", + }, + }, + "packages/pkg1": { + "name": "package1", + "dependencies": { + "peer-deps-too": "1.0.0", + }, + }, + }, + "packages": { + "a-dep": ["a-dep@1.0.10", "http://localhost:1234/a-dep/-/a-dep-1.0.10.tgz", {}, "sha512-NeQ6Ql9jRW8V+VOiVb+PSQAYOvVoSimW+tXaR0CoJk4kM9RIk/XlAUGCsNtn5XqjlDO4hcH8NcyaL507InevEg=="], + + "no-deps": ["no-deps@1.1.0", "http://localhost:1234/no-deps/-/no-deps-1.1.0.tgz", {}, "sha512-ebG2pipYAKINcNI3YxdsiAgFvNGp2gdRwxAKN2LYBm9+YxuH/lHH2sl+GKQTuGiNfCfNZRMHUyyLPEJD6HWm7w=="], + + "package1": ["package1@workspace:packages/pkg1", { "dependencies": { "peer-deps-too": "1.0.0" } }], + + "peer-deps-too": ["peer-deps-too@1.0.0", "http://localhost:1234/peer-deps-too/-/peer-deps-too-1.0.0.tgz", { "peerDependencies": { "no-deps": "*" } }, "sha512-sBx0TKrsB8FkRN2lzkDjMuctPGEKn1TmNUBv3dJOtnZM8nd255o5ZAPRpAI2XFLHZAavBlK/e73cZNwnUxlRog=="], + } +} +" +`; + +exports[`binaries each type of binary serializes correctly to text lockfile 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "dir-bin": "./dir-bin", + "file-bin": "./file-bin", + "map-bin": "./map-bin", + "named-file-bin": "./named-file-bin", + }, + }, + }, + "packages": { + "dir-bin": ["dir-bin@file:dir-bin", { "binDir": "./bins" }], + + "file-bin": ["file-bin@file:file-bin", { "bin": "./file-bin.js" }], + + "map-bin": ["map-bin@file:map-bin", { "bin": { "map-bin-1": "./map-bin-1.js", "map-bin-2": "./map-bin-2.js" } }], + + "named-file-bin": ["named-file-bin@file:named-file-bin", { "bin": { "named-file-bin": "./named-file-bin.js" } }], + } +} +" +`; + +exports[`binaries root resolution bins 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "fooooo": ".", + "no-deps": "1.0.0", + }, + }, + }, + "packages": { + "fooooo": ["fooooo@root:", { "bin": "fooooo.js" }], + + "no-deps": ["no-deps@1.0.0", "http://localhost:1234/no-deps/-/no-deps-1.0.0.tgz", {}, "sha512-v4w12JRjUGvfHDUP8vFDwu0gUWu04j0cv9hLb1Abf9VdaXu4XcrddYFTMVBVvmldKViGWH7jrb6xPJRF0wq6gw=="], + } +} +" +`; + +exports[`hoisting text lockfile is hoisted 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "hoist-lockfile-1": "1.0.0", + "hoist-lockfile-2": "1.0.0", + "hoist-lockfile-3": "1.0.0", + }, + }, + }, + "packages": { + "hoist-lockfile-1": ["hoist-lockfile-1@1.0.0", "http://localhost:1234/hoist-lockfile-1/-/hoist-lockfile-1-1.0.0.tgz", { "dependencies": { "hoist-lockfile-shared": "*" } }, "sha512-E2nwR7egMFDoYjeRno7CAa59kiwkLGfhTFy2Q335JWp2r2bDkwoAt1LdChd5PdGYkbo7SfViHkW44ga+WXA+eA=="], + + "hoist-lockfile-2": ["hoist-lockfile-2@1.0.0", "http://localhost:1234/hoist-lockfile-2/-/hoist-lockfile-2-1.0.0.tgz", { "dependencies": { "hoist-lockfile-shared": "^1.0.1" } }, "sha512-7iNRBJF/U078n9oZW7aDvVLkA7+076a2ONEFvITpjKdhT07KWaBei0SzHkFYW4f3foGZPNlHsv0aAgk949TPJg=="], + + "hoist-lockfile-3": ["hoist-lockfile-3@1.0.0", "http://localhost:1234/hoist-lockfile-3/-/hoist-lockfile-3-1.0.0.tgz", { "dependencies": { "hoist-lockfile-shared": ">=1.0.1" } }, "sha512-iGz7jH7jxz/zq4OZM8hhT7kUX2Ye1m+45SoyMVcWTM7ZB+cY306Ff1mQePKTjkn84/pJMITMdRgDv/qF8PuQUw=="], + + "hoist-lockfile-shared": ["hoist-lockfile-shared@2.0.2", "http://localhost:1234/hoist-lockfile-shared/-/hoist-lockfile-shared-2.0.2.tgz", {}, "sha512-xPWoyP8lv+/JrbClRzhJx1eUsHqDflSTmWOxx82xvMIEs6mbiIuvIp3/L+Ojc6mqex6y426h7L5j0hjLZE3V9w=="], + + "hoist-lockfile-2/hoist-lockfile-shared": ["hoist-lockfile-shared@1.0.2", "http://localhost:1234/hoist-lockfile-shared/-/hoist-lockfile-shared-1.0.2.tgz", {}, "sha512-p7IQ/BbkTRLG/GUx6j2cDQ+vTUc/v9OW9Ss9igh/GFysbr0Qjriz/DiETnISkxYaTFitqOkUSOUkEKyeLNJsfQ=="], + } +} +" +`; + +exports[`it should ignore peerDependencies within workspaces 1`] = ` +"{ + "lockfileVersion": 0, + "workspaces": { + "": { + "peerDependencies": { + "no-deps": ">=1.0.0", + }, + }, + "packages/baz": { + "name": "Baz", + "peerDependencies": { + "a-dep": ">=1.0.1", + }, + }, + }, + "packages": { + "Baz": ["Baz@workspace:packages/baz", { "peerDependencies": { "a-dep": ">=1.0.1" } }], + + "a-dep": ["a-dep@1.0.10", "http://localhost:1234/a-dep/-/a-dep-1.0.10.tgz", {}, "sha512-NeQ6Ql9jRW8V+VOiVb+PSQAYOvVoSimW+tXaR0CoJk4kM9RIk/XlAUGCsNtn5XqjlDO4hcH8NcyaL507InevEg=="], + + "no-deps": ["no-deps@2.0.0", "http://localhost:1234/no-deps/-/no-deps-2.0.0.tgz", {}, "sha512-W3duJKZPcMIG5rA1io5cSK/bhW9rWFz+jFxZsKS/3suK4qHDkQNxUTEXee9/hTaAoDCeHWQqogukWYKzfr6X4g=="], + } +} +" +`; diff --git a/test/cli/install/registry/bun-install-registry.test.ts b/test/cli/install/registry/bun-install-registry.test.ts index 03677d7d26..846635dfd4 100644 --- a/test/cli/install/registry/bun-install-registry.test.ts +++ b/test/cli/install/registry/bun-install-registry.test.ts @@ -1733,17 +1733,14 @@ describe("text lockfile", () => { ), ]); - let { stdout, stderr, exited } = spawn({ + let { exited } = spawn({ cmd: [bunExe(), "install", "--save-text-lockfile"], cwd: packageDir, - stdout: "pipe", - stderr: "pipe", + stdout: "ignore", + stderr: "ignore", env, }); - let out = await Bun.readableStreamToText(stdout); - let err = await Bun.readableStreamToText(stderr); - console.log({ out, err }); expect(await exited).toBe(0); expect( @@ -1761,22 +1758,452 @@ describe("text lockfile", () => { }), ); - ({ stdout, stderr, exited } = spawn({ + ({ exited } = spawn({ cmd: [bunExe(), "install"], cwd: packageDir, - stdout: "pipe", - stderr: "pipe", + stdout: "ignore", + stderr: "ignore", env, })); - out = await Bun.readableStreamToText(stdout); - err = await Bun.readableStreamToText(stderr); - console.log({ out, err }); expect(await exited).toBe(0); const lockfile = await Bun.file(join(packageDir, "bun.lock")).text(); expect(lockfile.replaceAll(/localhost:\d+/g, "localhost:1234")).toMatchSnapshot(); }); + + test("--frozen-lockfile", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["packages/*"], + dependencies: { + "no-deps": "^1.0.0", + "a-dep": "^1.0.2", + }, + }), + ), + write( + join(packageDir, "packages", "pkg1", "package.json"), + JSON.stringify({ + name: "package1", + dependencies: { + "peer-deps-too": "1.0.0", + }, + }), + ), + ]); + + let { stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env, + }); + + let err = await Bun.readableStreamToText(stderr); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + const firstLockfile = await Bun.file(join(packageDir, "bun.lock")).text(); + + expect(firstLockfile.replace(/localhost:\d+/g, "localhost:1234")).toMatchSnapshot(); + + await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); + + ({ stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--frozen-lockfile"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env, + })); + + err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + expect(await exited).toBe(0); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([ + "a-dep", + "no-deps", + "package1", + "peer-deps-too", + ]); + + expect(await Bun.file(join(packageDir, "bun.lock")).text()).toBe(firstLockfile); + }); + + for (const omit of ["dev", "peer", "optional"]) { + test(`resolvable lockfile with ${omit} dependencies disabled`, async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + peerDependencies: { "no-deps": "1.0.0" }, + devDependencies: { "a-dep": "1.0.1" }, + optionalDependencies: { "basic-1": "1.0.0" }, + }), + ), + ]); + + let { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile", `--omit=${omit}`], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + let err = await Bun.readableStreamToText(stderr); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + const depName = omit === "dev" ? "a-dep" : omit === "peer" ? "no-deps" : "basic-1"; + + expect(await exists(join(packageDir, "node_modules", depName))).toBeFalse(); + + const lockfile = (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll( + /localhost:\d+/g, + "localhost:1234", + ); + + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + })); + + err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + expect(await exited).toBe(0); + + expect(await exists(join(packageDir, "node_modules", depName))).toBeTrue(); + + expect((await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234")).toBe( + lockfile, + ); + }); + } +}); + +describe("bundledDependencies", () => { + for (const textLockfile of [true, false]) { + test(`(${textLockfile ? "bun.lock" : "bun.lockb"}) basic`, async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "bundled-basic", + version: "1.1.1", + dependencies: { + "bundled-1": "1.0.0", + }, + }), + ), + ]); + + const cmd = textLockfile ? [bunExe(), "install", "--save-text-lockfile"] : [bunExe(), "install"]; + let { exited } = spawn({ + cmd, + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + }); + + expect(await exited).toBe(0); + + expect( + await Promise.all([ + exists(join(packageDir, "node_modules", "no-deps", "package.json")), + exists(join(packageDir, "node_modules", "bundled-1", "node_modules", "no-deps", "package.json")), + ]), + ).toEqual([false, true]); + + ({ exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + })); + + expect(await exited).toBe(0); + + expect( + await Promise.all([ + exists(join(packageDir, "node_modules", "no-deps", "package.json")), + exists(join(packageDir, "node_modules", "bundled-1", "node_modules", "no-deps", "package.json")), + ]), + ).toEqual([false, true]); + }); + + test(`(${textLockfile ? "bun.lock" : "bun.lockb"}) bundledDependencies === true`, async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "bundled-true", + version: "1.1.1", + dependencies: { + "bundled-true": "1.0.0", + }, + }), + ), + ]); + + const cmd = textLockfile ? [bunExe(), "install", "--save-text-lockfile"] : [bunExe(), "install"]; + let { exited } = spawn({ + cmd, + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + }); + + expect(await exited).toBe(0); + + async function check() { + return Promise.all([ + exists(join(packageDir, "node_modules", "no-deps", "package.json")), + exists(join(packageDir, "node_modules", "one-dep", "package.json")), + exists(join(packageDir, "node_modules", "bundled-true", "node_modules", "no-deps", "package.json")), + exists(join(packageDir, "node_modules", "bundled-true", "node_modules", "one-dep", "package.json")), + exists( + join( + packageDir, + "node_modules", + "bundled-true", + "node_modules", + "one-dep", + "node_modules", + "no-deps", + "package.json", + ), + ), + ]); + } + + expect(await check()).toEqual([false, false, true, true, true]); + + ({ exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + })); + + expect(await exited).toBe(0); + + expect(await check()).toEqual([false, false, true, true, true]); + }); + + test(`(${textLockfile ? "bun.lock" : "bun.lockb"}) transitive bundled dependency collision`, async () => { + // Install a package with one bundled dependency and one regular dependency. + // The bundled dependency has a transitive dependency of the same regular dependency, + // but at a different version. Test that the regular dependency does not replace the + // other version (both should exist). + + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "bundled-collision", + dependencies: { + "bundled-transitive": "1.0.0", + // prevent both transitive dependencies from hoisting + "no-deps": "npm:a-dep@1.0.2", + "one-dep": "npm:a-dep@1.0.3", + }, + }), + ), + ]); + + const cmd = textLockfile ? [bunExe(), "install", "--save-text-lockfile"] : [bunExe(), "install"]; + let { exited } = spawn({ + cmd, + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + }); + + expect(await exited).toBe(0); + + async function check() { + expect( + await Promise.all([ + file(join(packageDir, "node_modules", "no-deps", "package.json")).json(), + file( + join(packageDir, "node_modules", "bundled-transitive", "node_modules", "no-deps", "package.json"), + ).json(), + file( + join( + packageDir, + "node_modules", + "bundled-transitive", + "node_modules", + "one-dep", + "node_modules", + "no-deps", + "package.json", + ), + ).json(), + exists(join(packageDir, "node_modules", "bundled-transitive", "node_modules", "one-dep", "package.json")), + ]), + ).toEqual([ + { name: "a-dep", version: "1.0.2" }, + { name: "no-deps", version: "1.0.0" }, + { name: "no-deps", version: "1.0.1" }, + true, + ]); + } + + await check(); + + ({ exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + })); + + expect(await exited).toBe(0); + + await check(); + }); + + test(`(${textLockfile ? "bun.lock" : "bun.lockb"}) git dependencies`, async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "bundled-git", + dependencies: { + // bundledDependencies: ["zod"], + "install-test1": "dylan-conway/bundled-install-test#7824752", + // bundledDependencies: true, + "install-test2": "git+ssh://git@github.com/dylan-conway/bundled-install-test#1301309", + }, + }), + ), + write( + join(packageDir, "bunfig.toml"), + ` +[install] +cache = "${join(packageDir, ".bun-cache")}" + `, + ), + ]); + + const cmd = textLockfile ? [bunExe(), "install", "--save-text-lockfile"] : [bunExe(), "install"]; + let { exited } = spawn({ + cmd, + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + }); + + expect(await exited).toBe(0); + + async function check() { + expect( + await Promise.all([ + exists(join(packageDir, "node_modules", "zod", "package.json")), + exists(join(packageDir, "node_modules", "install-test1", "node_modules", "zod", "package.json")), + exists(join(packageDir, "node_modules", "install-test2", "node_modules", "zod", "package.json")), + ]), + ).toEqual([false, true, true]); + } + + await check(); + + ({ exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + })); + + expect(await exited).toBe(0); + + await check(); + }); + + test(`(${textLockfile ? "bun.lock" : "bun.lockb"}) workspace dependencies bundle correctly`, async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "bundled-workspace", + workspaces: ["packages/*"], + }), + ), + write( + join(packageDir, "packages", "pkg-one-one-one", "package.json"), + JSON.stringify({ + name: "pkg-one-one-one", + dependencies: { + "no-deps": "1.0.0", + "bundled-1": "1.0.0", + }, + bundledDependencies: ["no-deps"], + }), + ), + ]); + + const cmd = textLockfile ? [bunExe(), "install", "--save-text-lockfile"] : [bunExe(), "install"]; + let { exited } = spawn({ + cmd, + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + }); + + expect(await exited).toBe(0); + + async function check() { + expect( + await Promise.all([ + exists(join(packageDir, "node_modules", "no-deps", "package.json")), + exists(join(packageDir, "packages", "pkg-one-one-one", "node_modules", "no-deps", "package.json")), + exists(join(packageDir, "node_modules", "bundled-1", "node_modules", "no-deps", "package.json")), + ]), + ).toEqual([true, false, true]); + } + + await check(); + + ({ exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "ignore", + env, + })); + + expect(await exited).toBe(0); + + await check(); + }); + } }); describe("optionalDependencies", () => { @@ -1913,6 +2340,71 @@ describe("optionalDependencies", () => { }); }); +test("it should ignore peerDependencies within workspaces", async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "foo", + workspaces: ["packages/baz"], + peerDependencies: { + "no-deps": ">=1.0.0", + }, + }), + ), + write( + join(packageDir, "packages", "baz", "package.json"), + JSON.stringify({ + name: "Baz", + peerDependencies: { + "a-dep": ">=1.0.1", + }, + }), + ), + write(join(packageDir, ".npmrc"), `omit=peer`), + ]); + + const { exited } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: packageDir, + env, + }); + + expect(await exited).toBe(0); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual(["Baz"]); + expect( + (await file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234"), + ).toMatchSnapshot(); + + // installing with them enabled works + await rm(join(packageDir, ".npmrc")); + await runBunInstall(env, packageDir, { savesLockfile: false }); + + expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual(["Baz", "a-dep", "no-deps"]); +}); + +test("disabled dev/peer/optional dependencies are still included in the lockfile", async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + devDependencies: { + "no-deps": "1.0.0", + }, + peerDependencies: { + "a-dep": "1.0.1", + }, + optionalDependencies: { + "basic-1": "1.0.0", + }, + }), + ), + ]); + + await runBunInstall; +}); + test("tarball override does not crash", async () => { await write( packageJson, @@ -3218,6 +3710,313 @@ describe("binaries", () => { }); } + test("each type of binary serializes correctly to text lockfile", async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "foo", + version: "1.1.1", + dependencies: { + "file-bin": "./file-bin", + "named-file-bin": "./named-file-bin", + "dir-bin": "./dir-bin", + "map-bin": "./map-bin", + }, + }), + ), + write( + join(packageDir, "file-bin", "package.json"), + JSON.stringify({ + name: "file-bin", + version: "1.1.1", + bin: "./file-bin.js", + }), + ), + write(join(packageDir, "file-bin", "file-bin.js"), `#!/usr/bin/env node\nconsole.log("file-bin")`), + write( + join(packageDir, "named-file-bin", "package.json"), + JSON.stringify({ + name: "named-file-bin", + version: "1.1.1", + bin: { "named-file-bin": "./named-file-bin.js" }, + }), + ), + write( + join(packageDir, "named-file-bin", "named-file-bin.js"), + `#!/usr/bin/env node\nconsole.log("named-file-bin")`, + ), + write( + join(packageDir, "dir-bin", "package.json"), + JSON.stringify({ + name: "dir-bin", + version: "1.1.1", + directories: { + bin: "./bins", + }, + }), + ), + write(join(packageDir, "dir-bin", "bins", "dir-bin-1.js"), `#!/usr/bin/env node\nconsole.log("dir-bin-1")`), + write( + join(packageDir, "map-bin", "package.json"), + JSON.stringify({ + name: "map-bin", + version: "1.1.1", + bin: { + "map-bin-1": "./map-bin-1.js", + "map-bin-2": "./map-bin-2.js", + }, + }), + ), + write(join(packageDir, "map-bin", "map-bin-1.js"), `#!/usr/bin/env node\nconsole.log("map-bin-1")`), + write(join(packageDir, "map-bin", "map-bin-2.js"), `#!/usr/bin/env node\nconsole.log("map-bin-2")`), + ]); + + let { stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env, + }); + + let err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + const firstLockfile = (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll( + /localhost:\d+/g, + "localhost:1234", + ); + + expect(join(packageDir, "node_modules", ".bin", "file-bin")).toBeValidBin(join("..", "file-bin", "file-bin.js")); + expect(join(packageDir, "node_modules", ".bin", "named-file-bin")).toBeValidBin( + join("..", "named-file-bin", "named-file-bin.js"), + ); + expect(join(packageDir, "node_modules", ".bin", "dir-bin-1.js")).toBeValidBin( + join("..", "dir-bin", "bins", "dir-bin-1.js"), + ); + expect(join(packageDir, "node_modules", ".bin", "map-bin-1")).toBeValidBin(join("..", "map-bin", "map-bin-1.js")); + expect(join(packageDir, "node_modules", ".bin", "map-bin-2")).toBeValidBin(join("..", "map-bin", "map-bin-2.js")); + + await rm(join(packageDir, "node_modules", ".bin"), { recursive: true, force: true }); + + // now install from the lockfile, only linking bins + ({ stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env, + })); + + err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).not.toContain("Saved lockfile"); + + expect(await exited).toBe(0); + + expect(firstLockfile).toBe( + (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234"), + ); + expect(firstLockfile).toMatchSnapshot(); + + expect(join(packageDir, "node_modules", ".bin", "file-bin")).toBeValidBin(join("..", "file-bin", "file-bin.js")); + expect(join(packageDir, "node_modules", ".bin", "named-file-bin")).toBeValidBin( + join("..", "named-file-bin", "named-file-bin.js"), + ); + expect(join(packageDir, "node_modules", ".bin", "dir-bin-1.js")).toBeValidBin( + join("..", "dir-bin", "bins", "dir-bin-1.js"), + ); + expect(join(packageDir, "node_modules", ".bin", "map-bin-1")).toBeValidBin(join("..", "map-bin", "map-bin-1.js")); + expect(join(packageDir, "node_modules", ".bin", "map-bin-2")).toBeValidBin(join("..", "map-bin", "map-bin-2.js")); + }); + + test.todo("text lockfile updates with new bin entry for folder dependencies", async () => { + await Promise.all([ + write( + packageJson, + JSON.stringify({ + name: "foo", + dependencies: { + "change-bin": "./change-bin", + }, + }), + ), + write( + join(packageDir, "change-bin", "package.json"), + JSON.stringify({ + name: "change-bin", + version: "1.0.0", + bin: { + "change-bin-1": "./change-bin-1.js", + }, + }), + ), + write(join(packageDir, "change-bin", "change-bin-1.js"), `#!/usr/bin/env node\nconsole.log("change-bin-1")`), + ]); + + let { stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env, + }); + + let err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).toContain("Saved lockfile"); + + expect(await exited).toBe(0); + + const firstLockfile = (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll( + /localhost:\d+/g, + "localhost:1234", + ); + + expect(join(packageDir, "node_modules", ".bin", "change-bin-1")).toBeValidBin( + join("..", "change-bin", "change-bin-1.js"), + ); + + await Promise.all([ + write( + join(packageDir, "change-bin", "package.json"), + JSON.stringify({ + name: "change-bin", + version: "1.0.0", + bin: { + "change-bin-1": "./change-bin-1.js", + "change-bin-2": "./change-bin-2.js", + }, + }), + ), + write(join(packageDir, "change-bin", "change-bin-2.js"), `#!/usr/bin/env node\nconsole.log("change-bin-2")`), + ]); + + ({ stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "ignore", + stderr: "pipe", + env, + })); + + err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + + // it should save + expect(err).toContain("Saved lockfile"); + + expect(await exited).toBe(0); + + const secondLockfile = (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll( + /localhost:\d+/g, + "localhost:1234", + ); + expect(firstLockfile).not.toBe(secondLockfile); + + expect(secondLockfile).toMatchSnapshot(); + + expect(join(packageDir, "node_modules", ".bin", "change-bin-1")).toBeValidBin( + join("..", "change-bin", "change-bin-1.js"), + ); + + expect(join(packageDir, "node_modules", ".bin", "change-bin-2")).toBeValidBin( + join("..", "change-bin", "change-bin-2.js"), + ); + }); + + test("root resolution bins", async () => { + // As of writing this test, the only way to get a root resolution + // is to migrate a package-lock.json with a root resolution. For now, + // we'll just mock the bun.lock. + + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "fooooo", + version: "2.2.2", + dependencies: { + "fooooo": ".", + "no-deps": "1.0.0", + }, + bin: "fooooo.js", + }), + ), + write(join(packageDir, "fooooo.js"), `#!/usr/bin/env node\nconsole.log("fooooo")`), + write( + join(packageDir, "bun.lock"), + JSON.stringify({ + "lockfileVersion": 0, + "workspaces": { + "": { + "dependencies": { + "fooooo": ".", + // out of date, no no-deps + }, + }, + }, + "packages": { + "fooooo": ["fooooo@root:", { bin: "fooooo.js" }], + }, + }), + ), + ]); + + let { stderr, stdout, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + }); + + let err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).toContain("Saved lockfile"); + + let out = await Bun.readableStreamToText(stdout); + expect(out).toContain("no-deps@1.0.0"); + + expect(await exited).toBe(0); + + const firstLockfile = (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll( + /localhost:\d+/g, + "localhost:1234", + ); + + expect(join(packageDir, "node_modules", ".bin", "fooooo")).toBeValidBin(join("..", "fooooo", "fooooo.js")); + + await rm(join(packageDir, "node_modules", ".bin"), { recursive: true, force: true }); + + ({ stderr, stdout, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "pipe", + stderr: "pipe", + env, + })); + + err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("error:"); + expect(err).not.toContain("Saved lockfile"); + + out = await Bun.readableStreamToText(stdout); + expect(out).not.toContain("no-deps@1.0.0"); + + expect(await exited).toBe(0); + + expect(firstLockfile).toBe( + (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234"), + ); + expect(firstLockfile).toMatchSnapshot(); + + expect(join(packageDir, "node_modules", ".bin", "fooooo")).toBeValidBin(join("..", "fooooo", "fooooo.js")); + }); + async function runBin(binName: string, expected: string, global: boolean) { const args = global ? [`./global-bin-dir/${binName}`] : [bunExe(), binName]; const result = Bun.spawn({ @@ -4412,6 +5211,64 @@ describe("hoisting", async () => { }); }); }); + + test("text lockfile is hoisted", async () => { + // Each dependency depends on 'hoist-lockfile-shared'. + // 1 - "*" + // 2 - "^1.0.1" + // 3 - ">=1.0.1" + await write( + packageJson, + JSON.stringify({ + name: "foo", + dependencies: { + "hoist-lockfile-1": "1.0.0", + "hoist-lockfile-2": "1.0.0", + "hoist-lockfile-3": "1.0.0", + }, + }), + ); + + let { exited, stderr } = spawn({ + cmd: [bunExe(), "install", "--save-text-lockfile"], + cwd: packageDir, + stderr: "pipe", + stdout: "ignore", + env, + }); + + let err = await Bun.readableStreamToText(stderr); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + const lockfile = (await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll( + /localhost:\d+/g, + "localhost:1234", + ); + expect(lockfile).toMatchSnapshot(); + + // second install should not save the lockfile + // with a different set of resolutions + ({ exited, stderr } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stderr: "pipe", + stdout: "ignore", + env, + })); + + err = await Bun.readableStreamToText(stderr); + expect(err).not.toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + + expect(await exited).toBe(0); + + expect((await Bun.file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234")).toBe( + lockfile, + ); + }); }); describe("workspaces", async () => { diff --git a/test/cli/install/registry/packages/bundled-1/bundled-1-1.0.0.tgz b/test/cli/install/registry/packages/bundled-1/bundled-1-1.0.0.tgz new file mode 100644 index 0000000000..a9f21c1b24 Binary files /dev/null and b/test/cli/install/registry/packages/bundled-1/bundled-1-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/bundled-1/package.json b/test/cli/install/registry/packages/bundled-1/package.json new file mode 100644 index 0000000000..f1c614636e --- /dev/null +++ b/test/cli/install/registry/packages/bundled-1/package.json @@ -0,0 +1,47 @@ +{ + "name": "bundled-1", + "versions": { + "1.0.0": { + "name": "bundled-1", + "version": "1.0.0", + "dependencies": { + "no-deps": "1.0.0" + }, + "bundleDependencies": [ + "no-deps" + ], + "_id": "bundled-1@1.0.0", + "_integrity": "sha512-YQ/maWZliKQyp1VIdYnPBH6qBHLCQ8Iy6G5vRZFXUHVXufiXT5aTjPVnLQ7xpVAgURFrzd/Fu1113ROLlaJBkQ==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-YQ/maWZliKQyp1VIdYnPBH6qBHLCQ8Iy6G5vRZFXUHVXufiXT5aTjPVnLQ7xpVAgURFrzd/Fu1113ROLlaJBkQ==", + "shasum": "b9052ebbae98de99bf628f65d9150bdcad3dba9d", + "dist": { + "integrity": "sha512-YQ/maWZliKQyp1VIdYnPBH6qBHLCQ8Iy6G5vRZFXUHVXufiXT5aTjPVnLQ7xpVAgURFrzd/Fu1113ROLlaJBkQ==", + "shasum": "b9052ebbae98de99bf628f65d9150bdcad3dba9d", + "tarball": "http://http://localhost:4873/bundled-1/-/bundled-1-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-30T18:14:22.143Z", + "created": "2024-12-30T18:14:22.143Z", + "1.0.0": "2024-12-30T18:14:22.143Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "bundled-1-1.0.0.tgz": { + "shasum": "b9052ebbae98de99bf628f65d9150bdcad3dba9d", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "bundled-1", + "readme": "" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/bundled-transitive/bundled-transitive-1.0.0.tgz b/test/cli/install/registry/packages/bundled-transitive/bundled-transitive-1.0.0.tgz new file mode 100644 index 0000000000..9f11f84695 Binary files /dev/null and b/test/cli/install/registry/packages/bundled-transitive/bundled-transitive-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/bundled-transitive/package.json b/test/cli/install/registry/packages/bundled-transitive/package.json new file mode 100644 index 0000000000..45bb091b16 --- /dev/null +++ b/test/cli/install/registry/packages/bundled-transitive/package.json @@ -0,0 +1,48 @@ +{ + "name": "bundled-transitive", + "versions": { + "1.0.0": { + "name": "bundled-transitive", + "version": "1.0.0", + "dependencies": { + "no-deps": "1.0.0", + "one-dep": "1.0.0" + }, + "bundleDependencies": [ + "no-deps" + ], + "_id": "bundled-transitive@1.0.0", + "_integrity": "sha512-1CC+XKeBwsdseQEm4yOpfBzom2zEyrXQcgb6N4t83pL2DalEunEFO80yUpkzylsQYIH1uxGLI8G+4jhwvyH1bQ==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-1CC+XKeBwsdseQEm4yOpfBzom2zEyrXQcgb6N4t83pL2DalEunEFO80yUpkzylsQYIH1uxGLI8G+4jhwvyH1bQ==", + "shasum": "8e0417711660590c7317add4a4a28388d4628cb1", + "dist": { + "integrity": "sha512-1CC+XKeBwsdseQEm4yOpfBzom2zEyrXQcgb6N4t83pL2DalEunEFO80yUpkzylsQYIH1uxGLI8G+4jhwvyH1bQ==", + "shasum": "8e0417711660590c7317add4a4a28388d4628cb1", + "tarball": "http://http://localhost:4873/bundled-transitive/-/bundled-transitive-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-30T22:05:28.027Z", + "created": "2024-12-30T22:05:28.027Z", + "1.0.0": "2024-12-30T22:05:28.027Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "bundled-transitive-1.0.0.tgz": { + "shasum": "8e0417711660590c7317add4a4a28388d4628cb1", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "bundled-transitive", + "readme": "" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/bundled-true/bundled-true-1.0.0.tgz b/test/cli/install/registry/packages/bundled-true/bundled-true-1.0.0.tgz new file mode 100644 index 0000000000..aad2709ff7 Binary files /dev/null and b/test/cli/install/registry/packages/bundled-true/bundled-true-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/bundled-true/package.json b/test/cli/install/registry/packages/bundled-true/package.json new file mode 100644 index 0000000000..1eb11cb00b --- /dev/null +++ b/test/cli/install/registry/packages/bundled-true/package.json @@ -0,0 +1,43 @@ +{ + "name": "bundled-true", + "versions": { + "1.0.0": { + "name": "bundled-true", + "version": "1.0.0", + "dependencies": { + "no-deps": "1.0.0", + "one-dep": "1.0.0" + }, + "bundleDependencies": true, + "_id": "bundled-true@1.0.0", + "_nodeVersion": "23.5.0", + "_npmVersion": "10.9.2", + "dist": { + "integrity": "sha512-VRh+fxqRwtIsAn8P8EH8sETvUD8Yh5zMwujrjISki+37DHQr7jfj8tZW0LaE5rGZW49BeT83zqddRoI237zA/Q==", + "shasum": "e45979379fbf62c4f92e9d7297374c37cb191537", + "tarball": "http://localhost:4873/bundled-true/-/bundled-true-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-30T21:52:11.900Z", + "created": "2024-12-30T21:52:11.900Z", + "1.0.0": "2024-12-30T21:52:11.900Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "bundled-true-1.0.0.tgz": { + "shasum": "e45979379fbf62c4f92e9d7297374c37cb191537", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "bundled-true", + "readme": "ERROR: No README data found!" +} diff --git a/test/cli/install/registry/packages/hoist-lockfile-1/hoist-lockfile-1-1.0.0.tgz b/test/cli/install/registry/packages/hoist-lockfile-1/hoist-lockfile-1-1.0.0.tgz new file mode 100644 index 0000000000..d44c7526c8 Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-1/hoist-lockfile-1-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-1/package.json b/test/cli/install/registry/packages/hoist-lockfile-1/package.json new file mode 100644 index 0000000000..54b1a2b812 --- /dev/null +++ b/test/cli/install/registry/packages/hoist-lockfile-1/package.json @@ -0,0 +1,44 @@ +{ + "name": "hoist-lockfile-1", + "versions": { + "1.0.0": { + "name": "hoist-lockfile-1", + "version": "1.0.0", + "dependencies": { + "hoist-lockfile-shared": "*" + }, + "_id": "hoist-lockfile-1@1.0.0", + "_integrity": "sha512-E2nwR7egMFDoYjeRno7CAa59kiwkLGfhTFy2Q335JWp2r2bDkwoAt1LdChd5PdGYkbo7SfViHkW44ga+WXA+eA==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-E2nwR7egMFDoYjeRno7CAa59kiwkLGfhTFy2Q335JWp2r2bDkwoAt1LdChd5PdGYkbo7SfViHkW44ga+WXA+eA==", + "shasum": "5765633927473de7697d72d0c0d6bfe04b6a7c71", + "dist": { + "integrity": "sha512-E2nwR7egMFDoYjeRno7CAa59kiwkLGfhTFy2Q335JWp2r2bDkwoAt1LdChd5PdGYkbo7SfViHkW44ga+WXA+eA==", + "shasum": "5765633927473de7697d72d0c0d6bfe04b6a7c71", + "tarball": "http://http://localhost:4873/hoist-lockfile-1/-/hoist-lockfile-1-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-15T10:01:42.154Z", + "created": "2024-12-15T10:01:42.154Z", + "1.0.0": "2024-12-15T10:01:42.154Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "hoist-lockfile-1-1.0.0.tgz": { + "shasum": "5765633927473de7697d72d0c0d6bfe04b6a7c71", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "hoist-lockfile-1", + "readme": "" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/hoist-lockfile-2/hoist-lockfile-2-1.0.0.tgz b/test/cli/install/registry/packages/hoist-lockfile-2/hoist-lockfile-2-1.0.0.tgz new file mode 100644 index 0000000000..e2fe749dec Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-2/hoist-lockfile-2-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-2/package.json b/test/cli/install/registry/packages/hoist-lockfile-2/package.json new file mode 100644 index 0000000000..ae6a8286eb --- /dev/null +++ b/test/cli/install/registry/packages/hoist-lockfile-2/package.json @@ -0,0 +1,44 @@ +{ + "name": "hoist-lockfile-2", + "versions": { + "1.0.0": { + "name": "hoist-lockfile-2", + "version": "1.0.0", + "dependencies": { + "hoist-lockfile-shared": "^1.0.1" + }, + "_id": "hoist-lockfile-2@1.0.0", + "_integrity": "sha512-7iNRBJF/U078n9oZW7aDvVLkA7+076a2ONEFvITpjKdhT07KWaBei0SzHkFYW4f3foGZPNlHsv0aAgk949TPJg==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-7iNRBJF/U078n9oZW7aDvVLkA7+076a2ONEFvITpjKdhT07KWaBei0SzHkFYW4f3foGZPNlHsv0aAgk949TPJg==", + "shasum": "9d4e79abefd802c410cb2710daeccfda9ecf8995", + "dist": { + "integrity": "sha512-7iNRBJF/U078n9oZW7aDvVLkA7+076a2ONEFvITpjKdhT07KWaBei0SzHkFYW4f3foGZPNlHsv0aAgk949TPJg==", + "shasum": "9d4e79abefd802c410cb2710daeccfda9ecf8995", + "tarball": "http://http://localhost:4873/hoist-lockfile-2/-/hoist-lockfile-2-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-15T10:02:22.248Z", + "created": "2024-12-15T10:02:22.248Z", + "1.0.0": "2024-12-15T10:02:22.248Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "hoist-lockfile-2-1.0.0.tgz": { + "shasum": "9d4e79abefd802c410cb2710daeccfda9ecf8995", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "hoist-lockfile-2", + "readme": "" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/hoist-lockfile-3/hoist-lockfile-3-1.0.0.tgz b/test/cli/install/registry/packages/hoist-lockfile-3/hoist-lockfile-3-1.0.0.tgz new file mode 100644 index 0000000000..3bf1f448c9 Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-3/hoist-lockfile-3-1.0.0.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-3/package.json b/test/cli/install/registry/packages/hoist-lockfile-3/package.json new file mode 100644 index 0000000000..a8edbef37c --- /dev/null +++ b/test/cli/install/registry/packages/hoist-lockfile-3/package.json @@ -0,0 +1,44 @@ +{ + "name": "hoist-lockfile-3", + "versions": { + "1.0.0": { + "name": "hoist-lockfile-3", + "version": "1.0.0", + "dependencies": { + "hoist-lockfile-shared": ">=1.0.1" + }, + "_id": "hoist-lockfile-3@1.0.0", + "_integrity": "sha512-iGz7jH7jxz/zq4OZM8hhT7kUX2Ye1m+45SoyMVcWTM7ZB+cY306Ff1mQePKTjkn84/pJMITMdRgDv/qF8PuQUw==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-iGz7jH7jxz/zq4OZM8hhT7kUX2Ye1m+45SoyMVcWTM7ZB+cY306Ff1mQePKTjkn84/pJMITMdRgDv/qF8PuQUw==", + "shasum": "04cab7133b1c33a55aa0b8158d0f615ebdc4b99c", + "dist": { + "integrity": "sha512-iGz7jH7jxz/zq4OZM8hhT7kUX2Ye1m+45SoyMVcWTM7ZB+cY306Ff1mQePKTjkn84/pJMITMdRgDv/qF8PuQUw==", + "shasum": "04cab7133b1c33a55aa0b8158d0f615ebdc4b99c", + "tarball": "http://http://localhost:4873/hoist-lockfile-3/-/hoist-lockfile-3-1.0.0.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-15T10:02:33.352Z", + "created": "2024-12-15T10:02:33.352Z", + "1.0.0": "2024-12-15T10:02:33.352Z" + }, + "users": {}, + "dist-tags": { + "latest": "1.0.0" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "hoist-lockfile-3-1.0.0.tgz": { + "shasum": "04cab7133b1c33a55aa0b8158d0f615ebdc4b99c", + "version": "1.0.0" + } + }, + "_rev": "", + "_id": "hoist-lockfile-3", + "readme": "" +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-1.0.1.tgz b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-1.0.1.tgz new file mode 100644 index 0000000000..cf3aca12ca Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-1.0.1.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-1.0.2.tgz b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-1.0.2.tgz new file mode 100644 index 0000000000..d5d8f9652d Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-1.0.2.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-2.0.1.tgz b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-2.0.1.tgz new file mode 100644 index 0000000000..4b60dcfb4f Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-2.0.1.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-2.0.2.tgz b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-2.0.2.tgz new file mode 100644 index 0000000000..3a0b3053da Binary files /dev/null and b/test/cli/install/registry/packages/hoist-lockfile-shared/hoist-lockfile-shared-2.0.2.tgz differ diff --git a/test/cli/install/registry/packages/hoist-lockfile-shared/package.json b/test/cli/install/registry/packages/hoist-lockfile-shared/package.json new file mode 100644 index 0000000000..969fa9dd4e --- /dev/null +++ b/test/cli/install/registry/packages/hoist-lockfile-shared/package.json @@ -0,0 +1,104 @@ +{ + "name": "hoist-lockfile-shared", + "versions": { + "1.0.1": { + "name": "hoist-lockfile-shared", + "version": "1.0.1", + "_id": "hoist-lockfile-shared@1.0.1", + "_integrity": "sha512-wPw8pTRj2OeZ/n7NeixjaSeI7FoM9DbMHWzdLv1kuBesSXJn+17UA0N7LV7t9dREnIMLw7ycRomhDL+56NRBmQ==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-wPw8pTRj2OeZ/n7NeixjaSeI7FoM9DbMHWzdLv1kuBesSXJn+17UA0N7LV7t9dREnIMLw7ycRomhDL+56NRBmQ==", + "shasum": "fde345ca6900e410c285e0468c8baf1e97400899", + "dist": { + "integrity": "sha512-wPw8pTRj2OeZ/n7NeixjaSeI7FoM9DbMHWzdLv1kuBesSXJn+17UA0N7LV7t9dREnIMLw7ycRomhDL+56NRBmQ==", + "shasum": "fde345ca6900e410c285e0468c8baf1e97400899", + "tarball": "http://http://localhost:4873/hoist-lockfile-shared/-/hoist-lockfile-shared-1.0.1.tgz" + }, + "contributors": [] + }, + "1.0.2": { + "name": "hoist-lockfile-shared", + "version": "1.0.2", + "_id": "hoist-lockfile-shared@1.0.2", + "_integrity": "sha512-p7IQ/BbkTRLG/GUx6j2cDQ+vTUc/v9OW9Ss9igh/GFysbr0Qjriz/DiETnISkxYaTFitqOkUSOUkEKyeLNJsfQ==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-p7IQ/BbkTRLG/GUx6j2cDQ+vTUc/v9OW9Ss9igh/GFysbr0Qjriz/DiETnISkxYaTFitqOkUSOUkEKyeLNJsfQ==", + "shasum": "027dac365f2e611c171bbca4f3cad0d0dd17d3c1", + "dist": { + "integrity": "sha512-p7IQ/BbkTRLG/GUx6j2cDQ+vTUc/v9OW9Ss9igh/GFysbr0Qjriz/DiETnISkxYaTFitqOkUSOUkEKyeLNJsfQ==", + "shasum": "027dac365f2e611c171bbca4f3cad0d0dd17d3c1", + "tarball": "http://http://localhost:4873/hoist-lockfile-shared/-/hoist-lockfile-shared-1.0.2.tgz" + }, + "contributors": [] + }, + "2.0.1": { + "name": "hoist-lockfile-shared", + "version": "2.0.1", + "_id": "hoist-lockfile-shared@2.0.1", + "_integrity": "sha512-r5/0DwsWyuolaBZtsYFTP0jBhwvDGNIekb/KrgBlPSROcYEgggIFBAuMPe2uU5CrsLkSH+MTc++K/reLLXgHNQ==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-r5/0DwsWyuolaBZtsYFTP0jBhwvDGNIekb/KrgBlPSROcYEgggIFBAuMPe2uU5CrsLkSH+MTc++K/reLLXgHNQ==", + "shasum": "e125f0cb27dd6e6fd8871060dbf6fba36ad1f917", + "dist": { + "integrity": "sha512-r5/0DwsWyuolaBZtsYFTP0jBhwvDGNIekb/KrgBlPSROcYEgggIFBAuMPe2uU5CrsLkSH+MTc++K/reLLXgHNQ==", + "shasum": "e125f0cb27dd6e6fd8871060dbf6fba36ad1f917", + "tarball": "http://http://localhost:4873/hoist-lockfile-shared/-/hoist-lockfile-shared-2.0.1.tgz" + }, + "contributors": [] + }, + "2.0.2": { + "name": "hoist-lockfile-shared", + "version": "2.0.2", + "_id": "hoist-lockfile-shared@2.0.2", + "_integrity": "sha512-xPWoyP8lv+/JrbClRzhJx1eUsHqDflSTmWOxx82xvMIEs6mbiIuvIp3/L+Ojc6mqex6y426h7L5j0hjLZE3V9w==", + "_nodeVersion": "22.6.0", + "_npmVersion": "10.8.3", + "integrity": "sha512-xPWoyP8lv+/JrbClRzhJx1eUsHqDflSTmWOxx82xvMIEs6mbiIuvIp3/L+Ojc6mqex6y426h7L5j0hjLZE3V9w==", + "shasum": "1ca0ded36e6aa42702055f61e9d65083831ae849", + "dist": { + "integrity": "sha512-xPWoyP8lv+/JrbClRzhJx1eUsHqDflSTmWOxx82xvMIEs6mbiIuvIp3/L+Ojc6mqex6y426h7L5j0hjLZE3V9w==", + "shasum": "1ca0ded36e6aa42702055f61e9d65083831ae849", + "tarball": "http://http://localhost:4873/hoist-lockfile-shared/-/hoist-lockfile-shared-2.0.2.tgz" + }, + "contributors": [] + } + }, + "time": { + "modified": "2024-12-15T09:57:52.172Z", + "created": "2024-12-15T09:57:36.725Z", + "1.0.1": "2024-12-15T09:57:36.725Z", + "1.0.2": "2024-12-15T09:57:44.209Z", + "2.0.1": "2024-12-15T09:57:49.265Z", + "2.0.2": "2024-12-15T09:57:52.172Z" + }, + "users": {}, + "dist-tags": { + "latest": "2.0.2" + }, + "_uplinks": {}, + "_distfiles": {}, + "_attachments": { + "hoist-lockfile-shared-1.0.1.tgz": { + "shasum": "fde345ca6900e410c285e0468c8baf1e97400899", + "version": "1.0.1" + }, + "hoist-lockfile-shared-1.0.2.tgz": { + "shasum": "027dac365f2e611c171bbca4f3cad0d0dd17d3c1", + "version": "1.0.2" + }, + "hoist-lockfile-shared-2.0.1.tgz": { + "shasum": "e125f0cb27dd6e6fd8871060dbf6fba36ad1f917", + "version": "2.0.1" + }, + "hoist-lockfile-shared-2.0.2.tgz": { + "shasum": "1ca0ded36e6aa42702055f61e9d65083831ae849", + "version": "2.0.2" + } + }, + "_rev": "", + "_id": "hoist-lockfile-shared", + "readme": "" +} \ No newline at end of file diff --git a/test/cli/run/cjs-fixture-bad.cjs b/test/cli/run/cjs-fixture-bad.cjs new file mode 100644 index 0000000000..a502af2f4b --- /dev/null +++ b/test/cli/run/cjs-fixture-bad.cjs @@ -0,0 +1,7 @@ +// @bun @bun-cjs + +function f() { + console.log("This module has been evaluated!"); +} + +f(); diff --git a/test/cli/run/commonjs-invalid.test.ts b/test/cli/run/commonjs-invalid.test.ts new file mode 100644 index 0000000000..b0461dcd62 --- /dev/null +++ b/test/cli/run/commonjs-invalid.test.ts @@ -0,0 +1,16 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; +import { join } from "path"; + +test("Loading an invalid commonjs module", () => { + const { stderr, exitCode } = Bun.spawnSync({ + cmd: [bunExe(), "run", join(import.meta.dir, "cjs-fixture-bad.cjs")], + env: bunEnv, + stdout: "inherit", + stderr: "pipe", + stdin: "inherit", + }); + + expect(stderr.toString().trim()).toContain("Expected CommonJS module to have a function wrapper"); + expect(exitCode).toBe(1); +}); diff --git a/test/cli/run/esm-fixture-leak-small.mjs b/test/cli/run/esm-fixture-leak-small.mjs index a045d59c5d..38abc2121a 100644 --- a/test/cli/run/esm-fixture-leak-small.mjs +++ b/test/cli/run/esm-fixture-leak-small.mjs @@ -20,12 +20,22 @@ setTimeout(() => { let diff = process.memoryUsage.rss() - baseline; diff = (diff / 1024 / 1024) | 0; console.log({ leaked: diff + " MB" }); + // This test seems to be more flaky on slow filesystems. // This used to be 40 MB, but the original version of Bun which this triggered on would reach 120 MB - // so we can increase it to 60 and still catch the leak. - if (diff > 60) { + // so we can increase it to 100 and still catch the leak. + // + // ❯ bunx bun@1.0.0 --smol test/cli/run/esm-fixture-leak-small.mjs + // { + // leaked: "100 MB" + // } + // ❯ bunx bun@1.1.0 --smol test/cli/run/esm-fixture-leak-small.mjs + // { + // leaked: "38 MB", + // } + if (diff >= 100) { console.log("\n--fail--\n"); process.exit(1); } else { console.log("\n--pass--\n"); } -}, 16); +}, 24); diff --git a/test/cli/run/filter-workspace.test.ts b/test/cli/run/filter-workspace.test.ts index a6c402c8a6..2d5b4f4fe9 100644 --- a/test/cli/run/filter-workspace.test.ts +++ b/test/cli/run/filter-workspace.test.ts @@ -86,6 +86,8 @@ function runInCwdSuccess({ antipattern, command = ["present"], auto = false, + env = {}, + elideCount, }: { cwd: string; pattern: string | string[]; @@ -93,8 +95,16 @@ function runInCwdSuccess({ antipattern?: RegExp | RegExp[]; command?: string[]; auto?: boolean; + env?: Record; + elideCount?: number; }) { const cmd = auto ? [bunExe()] : [bunExe(), "run"]; + + // Add elide-lines first if specified + if (elideCount !== undefined) { + cmd.push("--elide-lines", elideCount.toString()); + } + if (Array.isArray(pattern)) { for (const p of pattern) { cmd.push("--filter", p); @@ -102,13 +112,15 @@ function runInCwdSuccess({ } else { cmd.push("--filter", pattern); } + for (const c of command) { cmd.push(c); } + const { exitCode, stdout, stderr } = spawnSync({ - cwd: cwd, - cmd: cmd, - env: bunEnv, + cwd, + cmd, + env: { ...bunEnv, ...env }, stdout: "pipe", stderr: "pipe", }); @@ -416,4 +428,82 @@ describe("bun", () => { expect(stdoutval).toMatch(/code 23/); expect(exitCode).toBe(23); }); + + function runElideLinesTest({ + elideLines, + target_pattern, + antipattern, + win32ExpectedError, + }: { + elideLines: number; + target_pattern: RegExp[]; + antipattern?: RegExp[]; + win32ExpectedError: RegExp; + }) { + const dir = tempDirWithFiles("testworkspace", { + packages: { + dep0: { + "index.js": Array(20).fill("console.log('log_line');").join("\n"), + "package.json": JSON.stringify({ + name: "dep0", + scripts: { + script: `${bunExe()} run index.js`, + }, + }), + }, + }, + "package.json": JSON.stringify({ + name: "ws", + workspaces: ["packages/*"], + }), + }); + + if (process.platform === "win32") { + const { exitCode, stderr } = spawnSync({ + cwd: dir, + cmd: [bunExe(), "run", "--filter", "./packages/dep0", "--elide-lines", String(elideLines), "script"], + env: { ...bunEnv, FORCE_COLOR: "1", NO_COLOR: "0" }, + stdout: "pipe", + stderr: "pipe", + }); + expect(stderr.toString()).toMatch(win32ExpectedError); + expect(exitCode).not.toBe(0); + return; + } + + runInCwdSuccess({ + cwd: dir, + pattern: "./packages/dep0", + env: { FORCE_COLOR: "1", NO_COLOR: "0" }, + target_pattern, + antipattern, + command: ["script"], + elideCount: elideLines, + }); + } + + test("elides output by default when using --filter", () => { + runElideLinesTest({ + elideLines: 10, + target_pattern: [/\[10 lines elided\]/, /(?:log_line[\s\S]*?){20}/], + win32ExpectedError: /--elide-lines is only supported in terminal environments/, + }); + }); + + test("respects --elide-lines argument", () => { + runElideLinesTest({ + elideLines: 15, + target_pattern: [/\[5 lines elided\]/, /(?:log_line[\s\S]*?){20}/], + win32ExpectedError: /--elide-lines is only supported in terminal environments/, + }); + }); + + test("--elide-lines=0 shows all output", () => { + runElideLinesTest({ + elideLines: 0, + target_pattern: [/(?:log_line[\s\S]*?){20}/], + antipattern: [/lines elided/], + win32ExpectedError: /--elide-lines is only supported in terminal environments/, + }); + }); }); diff --git a/test/cli/run/require-cache.test.ts b/test/cli/run/require-cache.test.ts index dc45bd828a..a950c27a5a 100644 --- a/test/cli/run/require-cache.test.ts +++ b/test/cli/run/require-cache.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, isBroken, isIntelMacOS, isWindows, tempDirWithFiles } from "harness"; import { join } from "path"; test("require.cache is not an empty object literal when inspected", () => { @@ -32,7 +32,7 @@ test("require.cache does not include unevaluated modules", () => { expect(exitCode).toBe(0); }); -describe("files transpiled and loaded don't leak the output source code", () => { +describe.skipIf(isBroken && isIntelMacOS)("files transpiled and loaded don't leak the output source code", () => { test("via require() with a lot of long export names", () => { let text = ""; for (let i = 0; i < 10000; i++) { diff --git a/test/cli/run/syntax.test.ts b/test/cli/run/syntax.test.ts new file mode 100644 index 0000000000..8da864184f --- /dev/null +++ b/test/cli/run/syntax.test.ts @@ -0,0 +1,340 @@ +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { join } from "path"; + +const exitCode0 = [ + " ", + "\n", + "\t", + "\r\n", + " ", + "\n\n\n", + "export default '\\u{000C}'", + "export default '\\u{200B}'", + "export default '\\u{200C}'", + "export default '\\u{200D}'", + "export default '\\u{FEFF}'", + '"use strict";', + '"use strict";\n', + "'use strict';", + '"use strict";\n\n', + '"use asm";', + '"use asm";\n', + "'use strict'; 'use asm';", + '"use strict"; "use strict";', + "// empty comment", + "/* empty block comment */", + "/** JSDoc comment */", + "//\n//\n//", + "/* \n Multi\n line\n comment\n */", + "-->", + "// TODO: ", + "/** @type {number} */", + "const a = 123;", + "let a;", + "var a = undefined;", + "const a = null;", + "const [a] = [];", + "const {a} = {};", + "const [...a] = [];", + "const {...a} = {};", + "let [a = 1] = [];", + "let {a: b = 1} = {};", + "``", + "`template`", + "`template${123}`", + "`${null}`", + "`${'`'}`", + "`\n`", + "`\n`", + "({});", + "({ a: 1 });", + "({ ['computed']: 1 });", + "({ get x() { return 1; } });", + "({ __proto__: null });", + "({ get [1+2](){}, set [1+2](x){} });", + "({ a: 1, ...{b: 2} });", + "[];", + "[,];", + "[,,];", + "[1,];", + "[,...[1]];", + "[,,...[1]];", + "[[[[[]]]]]", + "[...[...[...[]]]]", + "[1,,2,,3];", + "Array(1_000_000).fill(1);", + "()=>{};", + "async()=>{};", + "(function(){}).bind(null);", + "()=>()=>()=>1;", + "function f(a=1,{b}={},...c){};", + "async function* f(){await 1; yield 2;};", + "(async()=>await 1)();", + "class A{}", + "class A extends null{}", + "class A{#private=1}", + "class A{static{}}", + "class A{get #a(){} set #a(x){}}", + "class A{*#gen(){yield this.#p}#p=1}", + "class A extends class B{}{};", + "export {};", + "export default 1;", + "export default function(){};", + "import.meta.url;", + "const pi = Math.PI;", + "const hello = 'world';", + "const star = '⭐';", + "'\\u0000\\uFFFF';", + "'\\x00\\xFF';", + "const \\u{61} = 1;", + "'\\0';", + "'\\v\\f\\b';", + "'\\\r\n';", + "/./;", + "/[]/;", + "/[^]/;", + "/\\u{0}/u;", + "/[\\u0000-\\u{10FFFF}]/u;", + "/./gimsuyd;", + "/\\p{Script=Latin}/u;", + "/(?<=a)b/;", + "/(?/;", + "1n;", + "0n;", + "9007199254740991n;", + "0b1n;", + "0o7n;", + "0xFn;", + "1_2_3n;", + "BigInt(1);", + "-0n;", + "~0n;", + "0b1_0_1;", + "0o7_7_7;", + "0xF_F_F;", + "1_2_3_4;", + "try{}catch{}", + "try{}catch(e){}", + "try{}catch{}finally{}", + "try{}finally{}", + "try{throw 1}catch(e){}", + "try{throw new Error}catch{}", + "try{throw{}}catch({message}){}", + "try{throw null}catch(e){}", + "try{throw undefined}catch(e){}", + "try{throw function(){}}catch(e){}", + "function*g(){yield;}", + "function*g(){yield*g();}", + "async function*g(){yield;}", + "async function*g(){await 1;}", + "(async function*(){for await(x of y){}});", + "function*g(){try{yield}finally{}}", + "[...new Set];", + "for(x of[]){}", + "for await(x of[]){}", + "async function f(){for await(x of y){}}", + "void 0;", + "new (class{})();", + "(class extends null{});", + "(class{[Symbol.hasInstance](){}});", + "function*g(){yield yield yield;}", + "1..toString();", + "`${`${`${1}`}`}`", + "String.raw`\n`", + "String.raw`\\n`", + "`${`${function*(){yield 1}}`}`", + "`${class{}}${()=>{}}${/./}`", + "`${`${async()=>await 1}`}`", + "`${`${class{static{``}}}`}`", + "await import('bun');", + "await import('bun:ffi');", + "await import(import.meta.path);", + "/(?:)/", + "/\\b\\B\\d\\D\\w\\W\\s\\S/", + "/\\cA\\cZ\\ca\\cz/", + "/\\p{General_Category=Letter}/u", + "/\\p{Script_Extensions=Latin}/u", + "/(?<=(?=a)b)c/", + "/(?\\k/", + "/\\u{10FFFF}\\u{0000}/u", + "/[\\p{ASCII}--\\p{Decimal_Number}]/v", + "const [{a: [b = 1] = []} = [{a: [2]}]] = [];", + "let {a: {b: {c = 1} = {}} = {}} = {};", + "const [a, , ...{0: b, length}] = [];", + "const {[class{}.prop]: x} = {};", + "const {[()=>{}]: x} = {};", + "let {[/./]: x} = {};", + "const {[class extends null{}]: x} = {};", + "const {[function*(){}]: x} = {};", + "function f([a] = [1], {b} = {b: 2}, ...c){}", + "(function({[key]: value} = {}){})", + "(({[this?.prop]: x} = {})=>{})", + "function f(a = () => b, b = 1){}", + "(a = class{}, b = new a)=>{}", + "function f(a = new.target){}", + "for await(const x of async function*(){yield 1}()){}", + "for(const x of function*(){yield 1}()){}", + "for(const {[key]: x} of []){}", + "for(let [{a = 1} = {}] of []){}", + "do{continue}while(false);", + "label:do{break label}while(false);", + "outer:for(;;)inner:break outer;", + "block:{break block;}", + "while(false)with(null);", + "switch(0){case 1:default:case 2:}", + "try{throw async()=>{}}catch(e){await e}", + "try{throw class{}}catch(e){new e}", + "try{throw{[Symbol.iterator]:()=>{}}}catch(e){}", + "try{throw Object.create(null)}catch(e){}", + "try{throw new Proxy({},{})}catch(e){}", + "try{throw new WeakMap}catch(e){}", + "try{throw new Int32Array}catch(e){}", + "try{throw new SharedArrayBuffer(0)}catch(e){}", + "try{throw new WebAssembly.Module(new Uint8Array)}catch(e){}", + "void void void 0;", + "typeof typeof typeof 0;", + "delete (delete (delete 0));", + "await (await (await 1));", + "--{a:1}.a;", + "(class{}).prototype.constructor;", + "(()=>{}).prototype?.call;", + "(async()=>{}).prototype?.call;", + "(function*(){}).prototype.next;", + "(async function*(){}).prototype.next;", + "this?.prop?.[key]?.();", + "0b0_1_0_1;", + "0B0_1_0_1;", + "0o0_1_2_3;", + "0O0_1_2_3;", + "0x0_1_2_3;", + "0X0_1_2_3;", + "1_2_3.4_5_6;", + "1_2_3e1_2_3;", + "1_2_3E1_2_3;", + ".0_1_2_3;", + "0b1_0_1n;", + "0B1_0_1n;", + "0o1_2_3n;", + "0O1_2_3n;", + "0x1_2_3n;", + "0X1_2_3n;", + "1_2_3_4_5n;", + "BigInt(0b101);", + "BigInt(0o777);", + "BigInt(0xff);", + "for(const [,,...a] of [[]]){}", + "for(let {a: [b]} of []){}", + "for await(const [a = await 1] of []){}", + "for(const {[await 1]: x} of []){}", + "for(var {[class{}]: x} of []){}", + "for(let {[/./]: x} of []){}", + "for(const {[`${a}`]: x} of []){}", + "for await(const x of async function*(){yield*[1,2,3]}()){}", + "new class extends (await Promise.resolve(class{})){}", + "function*g(){yield yield*function*(){yield yield yield;}();}", + "async function*f(){yield await Promise.all([1,2,3])}", + "async function*f(){yield*[await 1,await 2]}", + "(async function(){await (async()=>1)()})();", + "async function*f(){for await(const x of async function*(){yield 1}())yield x}", + "async function f(){return await new Promise(r=>r(1))}", + "async function*f(){try{yield await 1}finally{await 2}}", + "(async()=>{for await(const x of[])for await(const y of[]){}})();", + "async function*f(){yield await(yield await(yield await 1))}", + "async function*f(){yield*async function*(){yield await 1}()}", + "export default await(async()=>1)();", + "function*g(){yield*{[Symbol.iterator]:function*(){yield 1}}}", + "function*g(){yield*{async*[Symbol.asyncIterator](){yield 1}}}", + "function*g(){yield*function*(){yield function*(){yield 1}}()}", + "function*g(){yield*{get[Symbol.iterator](){return function*(){yield 1}}}}", + "function*g(){yield*(class{static*[Symbol.iterator](){yield 1}});}", + "function*g(){yield*class{static*[Symbol.iterator](){yield 1}}}", + "function*g(){yield*{*[Symbol.iterator](){yield class{}}}}", + "function*g(){yield*{*[Symbol.iterator](){yield function(){}}}}", + "function*g(){yield*{*[Symbol.iterator](){yield async()=>{}}}}", + "function*g(){yield*{*[Symbol.iterator](){yield function*(){}}}}", + "({[function*(){yield 1}]:1});", + "({[async function*(){yield 1}]:1});", + "({[(()=>class{})()]:1});", + "({[new class{valueOf(){return 1}}]:2});", + "({[new class{[Symbol.toPrimitive](){return 1}}]:2});", + "({[new class{toString(){return '1'}}]:2});", + "({[new class{get [Symbol.toPrimitive](){return()=>1}}]:2});", + "({[new class{static{this.prototype.valueOf=()=>1}}]:2});", + "label:while(1)try{continue label}finally{break label}", + "new Proxy(class{},{construct(){return{}}});", + "new Proxy(function(){},{apply(){return{}}});", + "new Proxy({},{get(){return new Proxy({},{})}})", + "new Proxy([],{get(){return new Proxy({},{})}});", + "new Proxy(function(){},{construct(){return new Proxy({},{})}})", + "new Proxy(class{},{construct(){return new Proxy({},{})}})", + "new Proxy({},{get(){return function(){return new Proxy({},{})}}})", + "new Proxy(function(){},{apply(){return new Proxy(class{},{})}})", + "new Proxy(class{},{construct(){return new Proxy(function(){},{})}})", + "Reflect.get(new Proxy({},{}),'')", + "Reflect.set(new Proxy({},{}),'')", + "Reflect.has(new Proxy({},{}),'')", + "Reflect.deleteProperty(new Proxy({},{}),'')", + "Reflect.getOwnPropertyDescriptor(new Proxy({},{}),'')", + "Reflect.getPrototypeOf(new Proxy({},{}))", + "Reflect.setPrototypeOf(new Proxy({},{}),{})", + "Reflect.isExtensible(new Proxy({},{}))", + "Reflect.preventExtensions(new Proxy({},{}))", + "Promise.all([Promise.race([]),Promise.allSettled([])])", + "Promise.race([Promise.all([]),Promise.any([])])", + "Promise.allSettled([Promise.race([]),Promise.all([])])", + "Promise.any([Promise.allSettled([]),Promise.race([])])", + "Promise.resolve(Promise.reject().catch(()=>Promise.all([])))", + "new Function(`return function*(){${`yield${`yield${`yield`}`}`}}`)();", + "new Function(`return async()=>${`await${`await${`1`}`}`}`)();", + "new Function(`return class{${`*[Symbol.iterator](){${`yield 1`}}`}}`)();", + "new Function(`return class{${`async*[Symbol.asyncIterator](){${`yield 1`}}`}}`)();", + "new Function(`return class{${`get #a(){${`return 1`}}`}}`)();", + "new Function(`return class{${`set #a(v){${`this.#b=v`}}#b`}}`)();", + "new Function(`return class{${`#a;${`#b;${`#c`}`}`}}`)();", + "for await(let {...x} of async function*(){yield*[]}()){}", + "for await(const x of (async function*(){yield await 1})()){}", + "function f(){let a=b,b=1}", + "(function(){const[a=b,b=1]=[]})", + "(()=>{let{a=b,b=1}={}})", +]; + +describe("exit code 0", () => { + const fixturePath = tempDirWithFiles("fixture", { + [`package.json`]: `{ + "name": "test", + + }`, + "fixture.js": "export default 1", + }); + + for (let i = 0; i < exitCode0.length; i++) { + const source = exitCode0[i]; + + test(`file #${i}: ${JSON.stringify(source)}`, async () => { + await Bun.write(join(fixturePath, "fixture.js"), source); + await using proc = Bun.spawn([bunExe(), "./fixture.js"], { + stdout: "inherit", + env: bunEnv, + cwd: fixturePath, + stderr: "inherit", + stdin: "inherit", + }); + const exitCode = await proc.exited; + expect(exitCode).toBe(0); + }); + + test(`eval #${i}: ${JSON.stringify(source)}`, async () => { + await using proc = Bun.spawn([bunExe(), "--eval", source], { + stdout: "inherit", + env: bunEnv, + stderr: "inherit", + stdin: "inherit", + }); + const exitCode = await proc.exited; + expect(exitCode).toBe(0); + }); + } +}); diff --git a/test/harness.ts b/test/harness.ts index 27a2bd0911..bbdc48006f 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -18,8 +18,10 @@ export const isWindows = process.platform === "win32"; export const isIntelMacOS = isMacOS && process.arch === "x64"; export const isDebug = Bun.version.includes("debug"); export const isCI = process.env.CI !== undefined; -export const isBuildKite = process.env.BUILDKITE === "true"; export const libcFamily = detectLibc.familySync() as "glibc" | "musl"; +export const isMusl = isLinux && libcFamily === "musl"; +export const isGlibc = isLinux && libcFamily === "glibc"; +export const isBuildKite = process.env.BUILDKITE === "true"; export const isVerbose = process.env.DEBUG === "1"; // Use these to mark a test as flaky or broken. @@ -548,6 +550,10 @@ Received ${JSON.stringify({ name: onDisk.name, version: onDisk.version })}`, case "npm": const name = dep.is_alias ? dep.npm.name : dep.name; if (!Bun.deepMatch({ name, version: pkg.resolution.value }, resolved)) { + if (dep.literal === "*") { + // allow any version, just needs to be resolvable + continue; + } if (dep.behavior.peer && dep.npm) { // allow peer dependencies to not match exactly, but still satisfy if (Bun.semver.satisfies(pkg.resolution.value, dep.npm.version)) continue; @@ -587,6 +593,10 @@ Received ${JSON.stringify({ name: onDisk.name, version: onDisk.version })}`, case "npm": const name = dep.is_alias ? dep.npm.name : dep.name; if (!Bun.deepMatch({ name, version: pkg.resolution.value }, resolved)) { + if (dep.literal === "*") { + // allow any version, just needs to be resolvable + continue; + } // workspaces don't need a version if (treePkg.resolution.tag === "workspace" && !resolved.version) continue; if (dep.behavior.peer && dep.npm) { @@ -1417,3 +1427,10 @@ export function rmScope(path: string) { }, }; } + +export function textLockfile(version: number, pkgs: any): string { + return JSON.stringify({ + lockfileVersion: version, + ...pkgs, + }); +} diff --git a/test/integration/jsdom/jsdom.test.ts b/test/integration/jsdom/jsdom.test.ts new file mode 100644 index 0000000000..b816e33d73 --- /dev/null +++ b/test/integration/jsdom/jsdom.test.ts @@ -0,0 +1,14 @@ +import { test, expect, describe } from "bun:test"; +import { JSDOM } from "jsdom"; + +describe("jsdom", () => { + for (const runScripts of ["dangerously", "outside-only", undefined]) { + test(`runScripts: ${runScripts}`, () => { + const dom = new JSDOM(`

Hello World!

`, { + url: "https://example.com", + runScripts, + }); + expect(dom.window.document.querySelector("h1").textContent).toBe("Hello World!"); + }); + } +}); diff --git a/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap b/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap index da10c802c8..b1bb52a16f 100644 --- a/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap +++ b/test/integration/next-pages/test/__snapshots__/dev-server-ssr-100.test.ts.snap @@ -5,7 +5,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "dependencies": [ { "behavior": { - "normal": true, + "prod": true, }, "id": 0, "literal": "20.7.0", @@ -18,7 +18,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 1, "literal": "18.2.22", @@ -31,7 +31,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 2, "literal": "18.2.7", @@ -44,7 +44,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 3, "literal": "10.4.16", @@ -57,7 +57,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 4, "literal": "^1.0.3", @@ -70,7 +70,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 5, "literal": "8.50.0", @@ -83,7 +83,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 6, "literal": "14.1.3", @@ -96,7 +96,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 7, "literal": "14.1.3", @@ -109,7 +109,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 8, "literal": "8.4.30", @@ -122,7 +122,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 9, "literal": "22.12.0", @@ -135,7 +135,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 10, "literal": "18.2.0", @@ -148,7 +148,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 11, "literal": "18.2.0", @@ -161,7 +161,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 12, "literal": "3.3.3", @@ -174,7 +174,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 13, "literal": "5.2.2", @@ -187,7 +187,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 14, "literal": "^5.0.2", @@ -200,7 +200,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 15, "literal": "^1.1.3", @@ -213,7 +213,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 16, "literal": "^1.18.2", @@ -226,7 +226,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 17, "literal": "^4.0.3", @@ -239,7 +239,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 18, "literal": "^8.4.23", @@ -252,7 +252,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 19, "literal": "^1.22.2", @@ -265,7 +265,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 20, "literal": "^3.32.0", @@ -278,7 +278,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 21, "literal": "^3.5.3", @@ -291,7 +291,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 22, "literal": "^3.2.12", @@ -304,7 +304,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 23, "literal": "^2.1.0", @@ -317,7 +317,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 24, "literal": "^1.2.2", @@ -330,7 +330,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 25, "literal": "^4.0.5", @@ -343,7 +343,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 26, "literal": "^1.0.0", @@ -356,7 +356,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 27, "literal": "^4.0.1", @@ -369,7 +369,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 28, "literal": "^6.0.2", @@ -382,7 +382,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 29, "literal": "^3.0.0", @@ -395,7 +395,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 30, "literal": "^3.0.0", @@ -408,7 +408,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 31, "literal": "^15.1.0", @@ -421,7 +421,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 32, "literal": "^6.0.1", @@ -434,7 +434,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 33, "literal": "^5.2.0", @@ -447,7 +447,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 34, "literal": "^4.0.1", @@ -460,7 +460,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 35, "literal": "^6.0.11", @@ -473,7 +473,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 36, "literal": "^3.0.0", @@ -486,7 +486,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 37, "literal": "^1.0.2", @@ -499,7 +499,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 38, "literal": "^2.3.4", @@ -512,7 +512,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 39, "literal": "^3.0.0", @@ -553,7 +553,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 42, "literal": "^3.3.6", @@ -566,7 +566,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 43, "literal": "^1.0.0", @@ -579,7 +579,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 44, "literal": "^1.0.2", @@ -592,7 +592,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 45, "literal": "^6.0.11", @@ -618,7 +618,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 47, "literal": "^4.0.0", @@ -631,7 +631,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 48, "literal": "^1.0.0", @@ -644,7 +644,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 49, "literal": "^1.1.7", @@ -670,7 +670,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 51, "literal": "^2.13.0", @@ -683,7 +683,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 52, "literal": "^1.0.7", @@ -696,7 +696,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 53, "literal": "^1.0.0", @@ -709,7 +709,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 54, "literal": "^2.0.0", @@ -722,7 +722,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 55, "literal": "^1.1.2", @@ -735,7 +735,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 56, "literal": "^2.3.0", @@ -748,7 +748,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 57, "literal": "^4.0.3", @@ -761,7 +761,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 58, "literal": "^2.1.1", @@ -774,7 +774,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 59, "literal": "^2.0.1", @@ -800,7 +800,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 61, "literal": "^3.0.3", @@ -813,7 +813,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 62, "literal": "^2.3.1", @@ -826,7 +826,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 63, "literal": "^7.1.1", @@ -839,7 +839,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 64, "literal": "^5.0.1", @@ -852,7 +852,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 65, "literal": "^7.0.0", @@ -865,7 +865,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 66, "literal": "^2.0.2", @@ -878,7 +878,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 67, "literal": "^1.2.3", @@ -891,7 +891,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 68, "literal": "^5.1.2", @@ -904,7 +904,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 69, "literal": "^1.3.0", @@ -917,7 +917,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 70, "literal": "^4.0.4", @@ -930,7 +930,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 71, "literal": "^4.0.1", @@ -943,7 +943,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 72, "literal": "2.1.5", @@ -956,7 +956,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 73, "literal": "^1.6.0", @@ -969,7 +969,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 74, "literal": "^1.0.4", @@ -982,7 +982,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 75, "literal": "2.0.5", @@ -995,7 +995,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 76, "literal": "^1.1.9", @@ -1008,7 +1008,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 77, "literal": "^1.2.2", @@ -1021,7 +1021,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 78, "literal": "~3.1.2", @@ -1034,7 +1034,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 79, "literal": "~3.0.2", @@ -1047,7 +1047,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 80, "literal": "~5.1.2", @@ -1060,7 +1060,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 81, "literal": "~2.1.0", @@ -1073,7 +1073,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 82, "literal": "~4.0.1", @@ -1086,7 +1086,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 83, "literal": "~3.0.0", @@ -1099,7 +1099,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 84, "literal": "~3.6.0", @@ -1125,7 +1125,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 86, "literal": "^2.2.1", @@ -1138,7 +1138,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 87, "literal": "^2.0.0", @@ -1151,7 +1151,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 88, "literal": "^3.0.0", @@ -1164,7 +1164,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 89, "literal": "^2.0.4", @@ -1177,7 +1177,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 90, "literal": "^0.3.2", @@ -1190,7 +1190,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 91, "literal": "^4.0.0", @@ -1203,7 +1203,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 92, "literal": "^10.3.10", @@ -1216,7 +1216,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 93, "literal": "^1.1.6", @@ -1229,7 +1229,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 94, "literal": "^2.7.0", @@ -1242,7 +1242,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 95, "literal": "^4.0.1", @@ -1255,7 +1255,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 96, "literal": "^0.1.9", @@ -1268,7 +1268,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 97, "literal": "^1.0.0", @@ -1281,7 +1281,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 98, "literal": "^4.0.1", @@ -1294,7 +1294,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 99, "literal": "^1.0.0", @@ -1307,7 +1307,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 100, "literal": ">= 3.1.0 < 4", @@ -1320,7 +1320,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 101, "literal": "^1.0.0", @@ -1333,7 +1333,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 102, "literal": "^3.1.0", @@ -1346,7 +1346,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 103, "literal": "^2.3.5", @@ -1359,7 +1359,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 104, "literal": "^9.0.1", @@ -1372,7 +1372,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 105, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -1385,7 +1385,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 106, "literal": "^1.10.1", @@ -1398,7 +1398,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 107, "literal": "^10.2.0", @@ -1411,7 +1411,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 108, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -1424,7 +1424,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 109, "literal": "^2.0.1", @@ -1437,7 +1437,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 110, "literal": "^1.0.0", @@ -1450,7 +1450,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 111, "literal": "^8.0.2", @@ -1476,7 +1476,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 113, "literal": "^5.1.2", @@ -1489,7 +1489,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 114, "is_alias": true, @@ -1503,7 +1503,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 115, "literal": "^7.0.1", @@ -1516,7 +1516,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 116, "is_alias": true, @@ -1530,7 +1530,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 117, "literal": "^8.1.0", @@ -1543,7 +1543,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 118, "is_alias": true, @@ -1557,7 +1557,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 119, "literal": "^4.0.0", @@ -1570,7 +1570,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 120, "literal": "^4.1.0", @@ -1583,7 +1583,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 121, "literal": "^6.0.0", @@ -1596,7 +1596,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 122, "literal": "^5.0.1", @@ -1609,7 +1609,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 123, "literal": "^8.0.0", @@ -1622,7 +1622,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 124, "literal": "^3.0.0", @@ -1635,7 +1635,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 125, "literal": "^6.0.1", @@ -1648,7 +1648,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 126, "literal": "^2.0.1", @@ -1661,7 +1661,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 127, "literal": "~1.1.4", @@ -1674,7 +1674,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 128, "literal": "^6.1.0", @@ -1687,7 +1687,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 129, "literal": "^5.0.1", @@ -1700,7 +1700,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 130, "literal": "^7.0.1", @@ -1713,7 +1713,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 131, "literal": "^6.0.1", @@ -1726,7 +1726,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 132, "literal": "^0.2.0", @@ -1739,7 +1739,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 133, "literal": "^9.2.2", @@ -1752,7 +1752,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 134, "literal": "^7.0.1", @@ -1765,7 +1765,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 135, "literal": "^7.0.0", @@ -1778,7 +1778,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 136, "literal": "^4.0.1", @@ -1791,7 +1791,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 137, "literal": "^3.1.0", @@ -1804,7 +1804,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 138, "literal": "^2.0.0", @@ -1817,7 +1817,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 139, "literal": "^2.0.1", @@ -1830,7 +1830,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 140, "literal": "^2.0.0", @@ -1843,7 +1843,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 141, "literal": "^3.0.0", @@ -1856,7 +1856,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 142, "literal": "^1.2.1", @@ -1869,7 +1869,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 143, "literal": "^1.4.10", @@ -1882,7 +1882,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 144, "literal": "^0.3.24", @@ -1895,7 +1895,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 145, "literal": "^3.1.0", @@ -1908,7 +1908,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 146, "literal": "^1.4.14", @@ -1921,7 +1921,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 147, "literal": "^0.23.0", @@ -1934,7 +1934,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 148, "literal": "^1.1.0", @@ -1960,7 +1960,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 150, "literal": "^1.1.0", @@ -1973,7 +1973,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 151, "literal": "^3.0.0 || ^4.0.0", @@ -1986,7 +1986,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 152, "literal": "^1.1.0", @@ -1999,7 +1999,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 153, "literal": "9.0.0", @@ -2012,7 +2012,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 154, "literal": "22.12.0", @@ -2025,7 +2025,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 155, "literal": "2.2.3", @@ -2038,7 +2038,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 156, "literal": "0.0.1299070", @@ -2051,7 +2051,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 157, "literal": "4.3.4", @@ -2064,7 +2064,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 158, "literal": "2.0.1", @@ -2077,7 +2077,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 159, "literal": "2.0.3", @@ -2090,7 +2090,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 160, "literal": "6.4.0", @@ -2103,7 +2103,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 161, "literal": "3.0.5", @@ -2116,7 +2116,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 162, "literal": "1.4.3", @@ -2129,7 +2129,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 163, "literal": "17.7.2", @@ -2142,7 +2142,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 164, "literal": "7.6.0", @@ -2155,7 +2155,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 165, "literal": "^6.0.0", @@ -2168,7 +2168,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 166, "literal": "^4.0.0", @@ -2181,7 +2181,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 167, "literal": "^8.0.1", @@ -2194,7 +2194,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 168, "literal": "^3.1.1", @@ -2207,7 +2207,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 169, "literal": "^2.0.5", @@ -2220,7 +2220,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 170, "literal": "^2.1.1", @@ -2233,7 +2233,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 171, "literal": "^4.2.3", @@ -2246,7 +2246,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 172, "literal": "^5.0.5", @@ -2259,7 +2259,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 173, "literal": "^21.1.1", @@ -2272,7 +2272,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 174, "literal": "^4.2.0", @@ -2285,7 +2285,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 175, "literal": "^6.0.1", @@ -2298,7 +2298,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 176, "literal": "^7.0.0", @@ -2311,7 +2311,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 177, "literal": "^5.2.1", @@ -2324,7 +2324,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 178, "literal": "^2.3.8", @@ -2337,7 +2337,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 179, "literal": "^1.3.1", @@ -2350,7 +2350,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 180, "literal": "^1.1.13", @@ -2363,7 +2363,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 181, "literal": "^3.0.0", @@ -2376,7 +2376,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 182, "literal": "^3.1.5", @@ -2415,7 +2415,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 185, "literal": "^2.1.0", @@ -2428,7 +2428,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 186, "literal": "^2.0.0", @@ -2441,7 +2441,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 187, "literal": "^2.0.0", @@ -2454,7 +2454,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 188, "literal": "^2.0.0", @@ -2467,7 +2467,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 189, "literal": "^2.18.0", @@ -2480,7 +2480,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 190, "literal": "^1.3.2", @@ -2493,7 +2493,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 191, "literal": "^1.0.1", @@ -2506,7 +2506,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 192, "literal": "^1.1.0", @@ -2532,7 +2532,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 194, "literal": "^1.6.4", @@ -2545,7 +2545,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 195, "literal": "^1.6.4", @@ -2558,7 +2558,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 196, "literal": "^1.2.0", @@ -2571,7 +2571,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 197, "literal": "^2.15.0", @@ -2584,7 +2584,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 198, "literal": "^1.1.0", @@ -2597,7 +2597,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 199, "literal": "^1.3.1", @@ -2610,7 +2610,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 200, "literal": "1", @@ -2623,7 +2623,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 201, "literal": "^1.4.0", @@ -2636,7 +2636,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 202, "literal": "^7.0.2", @@ -2649,7 +2649,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 203, "literal": "^4.3.4", @@ -2662,7 +2662,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 204, "literal": "^7.0.1", @@ -2675,7 +2675,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 205, "literal": "^7.0.3", @@ -2688,7 +2688,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 206, "literal": "^7.14.1", @@ -2701,7 +2701,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 207, "literal": "^7.0.1", @@ -2714,7 +2714,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 208, "literal": "^1.1.0", @@ -2727,7 +2727,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 209, "literal": "^8.0.2", @@ -2740,7 +2740,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 210, "literal": "^7.1.1", @@ -2753,7 +2753,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 211, "literal": "^4.3.4", @@ -2766,7 +2766,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 212, "literal": "^2.7.1", @@ -2779,7 +2779,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 213, "literal": "^9.0.5", @@ -2792,7 +2792,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 214, "literal": "^4.2.0", @@ -2805,7 +2805,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 215, "literal": "1.1.0", @@ -2818,7 +2818,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 216, "literal": "^1.1.3", @@ -2831,7 +2831,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 217, "literal": "2.1.2", @@ -2844,7 +2844,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 218, "literal": "^4.3.4", @@ -2857,7 +2857,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 219, "literal": "^0.23.0", @@ -2870,7 +2870,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 220, "literal": "^7.0.2", @@ -2883,7 +2883,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 221, "literal": "^4.3.4", @@ -2896,7 +2896,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 222, "literal": "^6.0.1", @@ -2909,7 +2909,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 223, "literal": "^7.0.0", @@ -2922,7 +2922,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 224, "literal": "^7.0.2", @@ -2935,7 +2935,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 225, "literal": "^7.0.0", @@ -2948,7 +2948,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 226, "literal": "^8.0.2", @@ -2961,7 +2961,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 227, "literal": "^5.0.0", @@ -2974,7 +2974,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 228, "literal": "^2.0.2", @@ -2987,7 +2987,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 229, "literal": "^0.13.4", @@ -3000,7 +3000,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 230, "literal": "^2.1.0", @@ -3013,7 +3013,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 231, "literal": "^4.0.1", @@ -3026,7 +3026,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 232, "literal": "^5.2.0", @@ -3039,7 +3039,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 233, "literal": "^2.0.2", @@ -3052,7 +3052,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 234, "literal": "^4.0.1", @@ -3078,7 +3078,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 236, "literal": "^2.0.1", @@ -3091,7 +3091,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 237, "literal": "^7.0.2", @@ -3104,7 +3104,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 238, "literal": "4", @@ -3117,7 +3117,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 239, "literal": "^7.1.0", @@ -3130,7 +3130,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 240, "literal": "^4.3.4", @@ -3143,7 +3143,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 241, "literal": "^5.0.2", @@ -3156,7 +3156,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 242, "literal": "^6.0.2", @@ -3169,7 +3169,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 243, "literal": "^4.3.4", @@ -3182,7 +3182,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 244, "literal": "^11.2.0", @@ -3195,7 +3195,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 245, "literal": "^4.2.0", @@ -3208,7 +3208,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 246, "literal": "^6.0.1", @@ -3221,7 +3221,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 247, "literal": "^2.0.0", @@ -3234,7 +3234,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 248, "literal": "^2.0.0", @@ -3260,7 +3260,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 250, "literal": "^4.1.1", @@ -3273,7 +3273,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 251, "literal": "^5.1.0", @@ -3286,7 +3286,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 252, "literal": "^2.10.0", @@ -3312,7 +3312,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 254, "literal": "*", @@ -3325,7 +3325,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 255, "literal": "~5.26.4", @@ -3338,7 +3338,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 256, "literal": "~1.1.0", @@ -3351,7 +3351,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 257, "literal": "~0.2.3", @@ -3364,7 +3364,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 258, "literal": "~1.2.0", @@ -3377,7 +3377,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 259, "literal": "^3.0.0", @@ -3390,7 +3390,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 260, "literal": "2.1.2", @@ -3403,7 +3403,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 261, "literal": "2.2.3", @@ -3416,7 +3416,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 262, "literal": "0.5.24", @@ -3429,7 +3429,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 263, "literal": "4.3.5", @@ -3442,7 +3442,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 264, "literal": "0.0.1299070", @@ -3455,7 +3455,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 265, "literal": "8.17.1", @@ -3496,7 +3496,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 268, "literal": "3.0.1", @@ -3509,7 +3509,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 269, "literal": "10.0.0", @@ -3522,7 +3522,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 270, "literal": "3.23.8", @@ -3548,7 +3548,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 272, "literal": "^2.2.1", @@ -3561,7 +3561,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 273, "literal": "^3.3.0", @@ -3574,7 +3574,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 274, "literal": "^4.1.0", @@ -3587,7 +3587,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 275, "literal": "^5.2.0", @@ -3614,7 +3614,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 277, "literal": "^7.0.0", @@ -3627,7 +3627,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 278, "literal": "^1.3.1", @@ -3640,7 +3640,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 279, "literal": "^2.3.0", @@ -3653,7 +3653,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 280, "literal": "^1.1.6", @@ -3666,7 +3666,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 281, "literal": "^0.2.1", @@ -3679,7 +3679,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 282, "literal": "^7.24.7", @@ -3692,7 +3692,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 283, "literal": "^1.0.0", @@ -3705,7 +3705,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 284, "literal": "^7.24.7", @@ -3718,7 +3718,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 285, "literal": "^2.4.2", @@ -3731,7 +3731,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 286, "literal": "^4.0.0", @@ -3744,7 +3744,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 287, "literal": "^1.0.0", @@ -3757,7 +3757,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 288, "literal": "^3.2.1", @@ -3770,7 +3770,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 289, "literal": "^1.0.5", @@ -3783,7 +3783,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 290, "literal": "^5.3.0", @@ -3796,7 +3796,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 291, "literal": "^3.0.0", @@ -3809,7 +3809,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 292, "literal": "^1.9.0", @@ -3822,7 +3822,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 293, "literal": "1.1.3", @@ -3835,7 +3835,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 294, "literal": "^2.0.1", @@ -3848,7 +3848,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 295, "literal": "^1.0.0", @@ -3861,7 +3861,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 296, "literal": "^4.0.0", @@ -3874,7 +3874,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 297, "literal": "^3.0.0", @@ -3887,7 +3887,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 298, "literal": "1.6.0", @@ -3900,7 +3900,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 299, "literal": "8.4.31", @@ -3913,7 +3913,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 300, "literal": "14.1.3", @@ -3926,7 +3926,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 301, "literal": "5.1.1", @@ -3939,7 +3939,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 302, "literal": "^4.2.11", @@ -3952,7 +3952,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 303, "literal": "0.5.2", @@ -3965,7 +3965,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 304, "literal": "^1.0.30001579", @@ -4149,7 +4149,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 318, "literal": "^2.4.0", @@ -4162,7 +4162,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 319, "literal": "0.0.1", @@ -4188,7 +4188,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 321, "literal": "^3.3.6", @@ -4201,7 +4201,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 322, "literal": "^1.0.0", @@ -4214,7 +4214,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 323, "literal": "^1.0.2", @@ -4227,7 +4227,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 324, "literal": "^1.1.0", @@ -4240,7 +4240,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 325, "literal": "^7.33.2", @@ -4253,7 +4253,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 326, "literal": "^2.28.1", @@ -4266,7 +4266,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 327, "literal": "^6.7.1", @@ -4279,7 +4279,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 328, "literal": "^1.3.3", @@ -4292,7 +4292,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 329, "literal": "14.1.3", @@ -4305,7 +4305,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 330, "literal": "^5.4.2 || ^6.0.0", @@ -4318,7 +4318,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", @@ -4331,7 +4331,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 332, "literal": "^0.3.6", @@ -4344,7 +4344,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 333, "literal": "^3.5.2", @@ -4384,7 +4384,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 336, "literal": "^6.12.4", @@ -4397,7 +4397,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 337, "literal": "^0.4.1", @@ -4410,7 +4410,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 338, "literal": "^4.0.0", @@ -4423,7 +4423,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 339, "literal": "^4.3.2", @@ -4436,7 +4436,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 340, "literal": "^9.6.1", @@ -4449,7 +4449,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 341, "literal": "^5.2.0", @@ -4462,7 +4462,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 342, "literal": "^1.4.2", @@ -4475,7 +4475,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 343, "literal": "^2.0.2", @@ -4488,7 +4488,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 344, "literal": "^5.0.0", @@ -4501,7 +4501,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 345, "literal": "^13.19.0", @@ -4514,7 +4514,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 346, "literal": "^4.0.0", @@ -4527,7 +4527,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 347, "literal": "^4.1.0", @@ -4540,7 +4540,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 348, "literal": "^3.0.0", @@ -4553,7 +4553,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 349, "literal": "^1.4.0", @@ -4566,7 +4566,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 350, "literal": "^3.1.2", @@ -4579,7 +4579,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 351, "literal": "8.50.0", @@ -4592,7 +4592,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 352, "literal": "^0.9.3", @@ -4605,7 +4605,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 353, "literal": "^6.0.1", @@ -4618,7 +4618,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 354, "literal": "^0.2.0", @@ -4631,7 +4631,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 355, "literal": "^7.0.2", @@ -4644,7 +4644,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 356, "literal": "^6.0.2", @@ -4657,7 +4657,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 357, "literal": "^0.1.4", @@ -4670,7 +4670,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 358, "literal": "^7.2.2", @@ -4683,7 +4683,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 359, "literal": "^4.6.2", @@ -4696,7 +4696,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 360, "literal": "^3.0.3", @@ -4709,7 +4709,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 361, "literal": "^3.1.3", @@ -4722,7 +4722,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 362, "literal": "^1.4.0", @@ -4735,7 +4735,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 363, "literal": "^2.1.2", @@ -4748,7 +4748,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 364, "literal": "^1.2.8", @@ -4761,7 +4761,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 365, "literal": "^6.0.1", @@ -4774,7 +4774,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 366, "literal": "^3.4.3", @@ -4787,7 +4787,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 367, "literal": "^4.0.0", @@ -4800,7 +4800,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 368, "literal": "^4.6.1", @@ -4813,7 +4813,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 369, "literal": "^0.11.11", @@ -4826,7 +4826,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 370, "literal": "^4.2.0", @@ -4839,7 +4839,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 371, "literal": "^1.0.1", @@ -4852,7 +4852,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 372, "literal": "^1.0.1", @@ -4865,7 +4865,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 373, "literal": "^3.3.0", @@ -4891,7 +4891,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 375, "literal": "^2.0.2", @@ -4904,7 +4904,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 376, "literal": "^4.3.1", @@ -4917,7 +4917,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 377, "literal": "^3.0.5", @@ -4930,7 +4930,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 378, "literal": "^1.1.7", @@ -4943,7 +4943,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 379, "literal": "^1.0.0", @@ -4956,7 +4956,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 380, "literal": "0.0.1", @@ -4969,7 +4969,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 381, "literal": "^3.0.4", @@ -4982,7 +4982,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 382, "literal": "^3.2.9", @@ -4995,7 +4995,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 383, "literal": "^4.5.3", @@ -5008,7 +5008,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 384, "literal": "^3.0.2", @@ -5021,7 +5021,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 385, "literal": "^7.1.3", @@ -5034,7 +5034,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 386, "literal": "^1.0.0", @@ -5047,7 +5047,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 387, "literal": "^1.0.4", @@ -5060,7 +5060,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 388, "literal": "2", @@ -5073,7 +5073,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 389, "literal": "^3.1.1", @@ -5086,7 +5086,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 390, "literal": "^1.3.0", @@ -5099,7 +5099,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 391, "literal": "^1.0.0", @@ -5112,7 +5112,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 392, "literal": "^1.3.0", @@ -5125,7 +5125,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 393, "literal": "1", @@ -5138,7 +5138,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 394, "literal": "3.0.1", @@ -5151,7 +5151,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 395, "literal": "^6.12.4", @@ -5164,7 +5164,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 396, "literal": "^4.3.2", @@ -5177,7 +5177,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 397, "literal": "^9.6.0", @@ -5190,7 +5190,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 398, "literal": "^13.19.0", @@ -5203,7 +5203,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 399, "literal": "^5.2.0", @@ -5216,7 +5216,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 400, "literal": "^3.2.1", @@ -5229,7 +5229,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 401, "literal": "^4.1.0", @@ -5242,7 +5242,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 402, "literal": "^3.1.2", @@ -5255,7 +5255,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 403, "literal": "^3.1.1", @@ -5268,7 +5268,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 404, "literal": "^0.20.2", @@ -5281,7 +5281,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 405, "literal": "^8.9.0", @@ -5294,7 +5294,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 406, "literal": "^5.3.2", @@ -5307,7 +5307,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 407, "literal": "^3.4.1", @@ -5333,7 +5333,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 409, "literal": "^3.1.1", @@ -5346,7 +5346,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 410, "literal": "^2.0.0", @@ -5359,7 +5359,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 411, "literal": "^0.4.1", @@ -5372,7 +5372,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 412, "literal": "^4.2.2", @@ -5385,7 +5385,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 413, "literal": "^2.1.0", @@ -5398,7 +5398,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 414, "literal": "^4.3.0", @@ -5411,7 +5411,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 415, "literal": "^5.2.0", @@ -5424,7 +5424,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 416, "literal": "^5.2.0", @@ -5437,7 +5437,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 417, "literal": "^1.2.1", @@ -5450,7 +5450,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 418, "literal": "^0.1.3", @@ -5463,7 +5463,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 419, "literal": "^1.2.5", @@ -5476,7 +5476,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 420, "literal": "^0.4.0", @@ -5489,7 +5489,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 421, "literal": "^0.4.1", @@ -5502,7 +5502,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 422, "literal": "^2.0.6", @@ -5515,7 +5515,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 423, "literal": "^1.2.1", @@ -5528,7 +5528,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 424, "literal": "~0.4.0", @@ -5541,7 +5541,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 425, "literal": "^1.2.1", @@ -5554,7 +5554,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 426, "literal": "^2.0.2", @@ -5567,7 +5567,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 427, "literal": "^6.0.0", @@ -5580,7 +5580,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 428, "literal": "^4.0.0", @@ -5593,7 +5593,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 429, "literal": "^5.0.0", @@ -5606,7 +5606,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 430, "literal": "^3.0.2", @@ -5619,7 +5619,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 431, "literal": "^0.1.0", @@ -5632,7 +5632,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 432, "literal": "^5.1.0", @@ -5645,7 +5645,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 433, "literal": "^4.1.0", @@ -5658,7 +5658,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 434, "literal": "^7.1.0", @@ -5671,7 +5671,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 435, "literal": "^4.0.0", @@ -5684,7 +5684,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 436, "literal": "^4.3.4", @@ -5697,7 +5697,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 437, "literal": "^5.12.0", @@ -5710,7 +5710,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 438, "literal": "^2.7.4", @@ -5723,7 +5723,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 439, "literal": "^3.3.1", @@ -5736,7 +5736,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 440, "literal": "^4.5.0", @@ -5749,7 +5749,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 441, "literal": "^2.11.0", @@ -5762,7 +5762,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 442, "literal": "^4.0.3", @@ -5801,7 +5801,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 445, "literal": "^3.1.7", @@ -5814,7 +5814,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 446, "literal": "^1.2.3", @@ -5827,7 +5827,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 447, "literal": "^1.3.2", @@ -5840,7 +5840,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 448, "literal": "^1.3.2", @@ -5853,7 +5853,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 449, "literal": "^3.2.7", @@ -5866,7 +5866,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 450, "literal": "^2.1.0", @@ -5879,7 +5879,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 451, "literal": "^0.3.9", @@ -5892,7 +5892,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 452, "literal": "^2.8.0", @@ -5905,7 +5905,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 453, "literal": "^2.0.0", @@ -5918,7 +5918,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 454, "literal": "^2.13.1", @@ -5931,7 +5931,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 455, "literal": "^4.0.3", @@ -5944,7 +5944,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 456, "literal": "^3.1.2", @@ -5957,7 +5957,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 457, "literal": "^2.0.7", @@ -5970,7 +5970,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 458, "literal": "^1.0.1", @@ -5983,7 +5983,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 459, "literal": "^1.1.7", @@ -5996,7 +5996,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 460, "literal": "^6.3.1", @@ -6009,7 +6009,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 461, "literal": "^3.15.0", @@ -6035,7 +6035,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 463, "literal": "^0.0.29", @@ -6048,7 +6048,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 464, "literal": "^1.0.2", @@ -6061,7 +6061,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 465, "literal": "^1.2.6", @@ -6074,7 +6074,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 466, "literal": "^3.0.0", @@ -6087,7 +6087,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 467, "literal": "^1.2.0", @@ -6100,7 +6100,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 468, "literal": "^1.0.7", @@ -6113,7 +6113,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 469, "literal": "^1.2.1", @@ -6126,7 +6126,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 470, "literal": "^1.0.0", @@ -6139,7 +6139,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 471, "literal": "^1.3.0", @@ -6152,7 +6152,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 472, "literal": "^1.0.1", @@ -6165,7 +6165,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 473, "literal": "^1.0.0", @@ -6178,7 +6178,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 474, "literal": "^1.1.1", @@ -6191,7 +6191,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 475, "literal": "^1.0.0", @@ -6204,7 +6204,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 476, "literal": "^1.2.4", @@ -6217,7 +6217,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 477, "literal": "^1.3.0", @@ -6230,7 +6230,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 478, "literal": "^1.1.2", @@ -6243,7 +6243,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 479, "literal": "^1.0.1", @@ -6256,7 +6256,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 480, "literal": "^1.0.3", @@ -6269,7 +6269,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 481, "literal": "^2.0.0", @@ -6282,7 +6282,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 482, "literal": "^1.0.0", @@ -6295,7 +6295,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 483, "literal": "^1.3.0", @@ -6308,7 +6308,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 484, "literal": "^1.0.1", @@ -6321,7 +6321,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 485, "literal": "^1.1.3", @@ -6334,7 +6334,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 486, "literal": "^1.0.0", @@ -6347,7 +6347,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 487, "literal": "^1.3.0", @@ -6360,7 +6360,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 488, "literal": "^1.1.2", @@ -6373,7 +6373,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 489, "literal": "^1.2.4", @@ -6386,7 +6386,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 490, "literal": "^1.2.1", @@ -6399,7 +6399,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 491, "literal": "^1.1.4", @@ -6412,7 +6412,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 492, "literal": "^1.3.0", @@ -6425,7 +6425,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 493, "literal": "^1.1.2", @@ -6438,7 +6438,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 494, "literal": "^1.2.4", @@ -6451,7 +6451,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 495, "literal": "^1.0.1", @@ -6464,7 +6464,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 496, "literal": "^1.0.2", @@ -6477,7 +6477,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 497, "literal": "^1.0.7", @@ -6490,7 +6490,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 498, "literal": "^1.2.1", @@ -6503,7 +6503,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 499, "literal": "^1.23.2", @@ -6516,7 +6516,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 500, "literal": "^1.0.1", @@ -6529,7 +6529,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 501, "literal": "^1.0.3", @@ -6542,7 +6542,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 502, "literal": "^1.0.7", @@ -6555,7 +6555,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 503, "literal": "^1.0.7", @@ -6568,7 +6568,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 504, "literal": "^1.0.1", @@ -6581,7 +6581,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 505, "literal": "^1.0.1", @@ -6594,7 +6594,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 506, "literal": "^1.0.0", @@ -6607,7 +6607,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 507, "literal": "^1.0.0", @@ -6620,7 +6620,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 508, "literal": "^1.3.0", @@ -6633,7 +6633,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 509, "literal": "^1.0.0", @@ -6646,7 +6646,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 510, "literal": "^2.0.3", @@ -6659,7 +6659,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 511, "literal": "^1.2.1", @@ -6672,7 +6672,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 512, "literal": "^1.1.6", @@ -6685,7 +6685,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 513, "literal": "^1.2.4", @@ -6698,7 +6698,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 514, "literal": "^1.0.2", @@ -6711,7 +6711,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 515, "literal": "^1.0.3", @@ -6724,7 +6724,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 516, "literal": "^1.0.1", @@ -6737,7 +6737,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 517, "literal": "^1.0.2", @@ -6750,7 +6750,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 518, "literal": "^1.0.3", @@ -6763,7 +6763,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 519, "literal": "^1.0.3", @@ -6776,7 +6776,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 520, "literal": "^2.0.2", @@ -6789,7 +6789,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 521, "literal": "^1.0.7", @@ -6802,7 +6802,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 522, "literal": "^3.0.4", @@ -6815,7 +6815,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 523, "literal": "^1.2.7", @@ -6828,7 +6828,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 524, "literal": "^1.0.1", @@ -6841,7 +6841,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 525, "literal": "^2.0.3", @@ -6854,7 +6854,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 526, "literal": "^1.1.4", @@ -6867,7 +6867,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 527, "literal": "^1.0.3", @@ -6880,7 +6880,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 528, "literal": "^1.0.7", @@ -6893,7 +6893,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 529, "literal": "^1.1.13", @@ -6906,7 +6906,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 530, "literal": "^1.0.2", @@ -6919,7 +6919,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 531, "literal": "^1.13.1", @@ -6932,7 +6932,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 532, "literal": "^1.1.1", @@ -6945,7 +6945,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 533, "literal": "^4.1.5", @@ -6958,7 +6958,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 534, "literal": "^1.5.2", @@ -6971,7 +6971,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 535, "literal": "^1.1.2", @@ -6984,7 +6984,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 536, "literal": "^1.0.3", @@ -6997,7 +6997,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 537, "literal": "^1.2.9", @@ -7010,7 +7010,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 538, "literal": "^1.0.8", @@ -7023,7 +7023,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 539, "literal": "^1.0.8", @@ -7036,7 +7036,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 540, "literal": "^1.0.2", @@ -7049,7 +7049,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 541, "literal": "^1.0.1", @@ -7062,7 +7062,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 542, "literal": "^1.0.2", @@ -7075,7 +7075,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 543, "literal": "^1.0.6", @@ -7088,7 +7088,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 544, "literal": "^1.0.2", @@ -7101,7 +7101,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 545, "literal": "^1.1.15", @@ -7114,7 +7114,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 546, "literal": "^1.0.7", @@ -7127,7 +7127,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 547, "literal": "^1.0.7", @@ -7140,7 +7140,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 548, "literal": "^0.3.3", @@ -7153,7 +7153,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 549, "literal": "^1.0.1", @@ -7166,7 +7166,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 550, "literal": "^1.0.2", @@ -7179,7 +7179,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 551, "literal": "^1.0.3", @@ -7192,7 +7192,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 552, "literal": "^1.1.3", @@ -7205,7 +7205,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 553, "literal": "^1.0.0", @@ -7218,7 +7218,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 554, "literal": "^1.0.2", @@ -7231,7 +7231,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 555, "literal": "^1.0.2", @@ -7244,7 +7244,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 556, "literal": "^1.0.3", @@ -7257,7 +7257,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 557, "literal": "^1.0.2", @@ -7270,7 +7270,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 558, "literal": "^1.0.1", @@ -7283,7 +7283,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 559, "literal": "^1.1.0", @@ -7296,7 +7296,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 560, "literal": "^1.0.4", @@ -7309,7 +7309,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 561, "literal": "^1.0.5", @@ -7322,7 +7322,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 562, "literal": "^1.0.3", @@ -7335,7 +7335,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 563, "literal": "^1.0.2", @@ -7348,7 +7348,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 564, "literal": "^1.0.0", @@ -7361,7 +7361,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 565, "literal": "^1.0.0", @@ -7374,7 +7374,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 566, "literal": "^1.0.2", @@ -7387,7 +7387,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 567, "literal": "^1.0.0", @@ -7400,7 +7400,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 568, "literal": "^1.0.1", @@ -7413,7 +7413,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 569, "literal": "^1.0.7", @@ -7426,7 +7426,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 570, "literal": "^0.3.3", @@ -7439,7 +7439,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 571, "literal": "^1.0.1", @@ -7452,7 +7452,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 572, "literal": "^1.0.3", @@ -7465,7 +7465,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 573, "literal": "^1.1.13", @@ -7478,7 +7478,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 574, "literal": "^1.0.0", @@ -7491,7 +7491,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 575, "literal": "^1.1.14", @@ -7504,7 +7504,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 576, "literal": "^1.0.7", @@ -7517,7 +7517,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 577, "literal": "^1.0.7", @@ -7530,7 +7530,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 578, "literal": "^0.3.3", @@ -7543,7 +7543,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 579, "literal": "^1.0.1", @@ -7556,7 +7556,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 580, "literal": "^1.0.3", @@ -7569,7 +7569,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 581, "literal": "^1.1.13", @@ -7582,7 +7582,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 582, "literal": "^1.0.7", @@ -7595,7 +7595,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 583, "literal": "^0.3.3", @@ -7608,7 +7608,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 584, "literal": "^1.0.1", @@ -7621,7 +7621,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 585, "literal": "^1.0.3", @@ -7634,7 +7634,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 586, "literal": "^1.1.13", @@ -7647,7 +7647,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 587, "literal": "^1.0.7", @@ -7660,7 +7660,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 588, "literal": "^1.3.0", @@ -7673,7 +7673,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 589, "literal": "^1.1.13", @@ -7686,7 +7686,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 590, "literal": "^1.0.7", @@ -7699,7 +7699,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 591, "literal": "^1.2.1", @@ -7712,7 +7712,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 592, "literal": "^1.0.0", @@ -7725,7 +7725,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 593, "literal": "^1.0.7", @@ -7738,7 +7738,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 594, "literal": "^1.2.1", @@ -7751,7 +7751,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 595, "literal": "^1.0.0", @@ -7764,7 +7764,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 596, "literal": "^1.0.7", @@ -7777,7 +7777,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 597, "literal": "^1.2.1", @@ -7790,7 +7790,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 598, "literal": "^1.23.0", @@ -7803,7 +7803,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 599, "literal": "^1.0.0", @@ -7816,7 +7816,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 600, "literal": "^1.0.6", @@ -7829,7 +7829,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 601, "literal": "^1.3.0", @@ -7842,7 +7842,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 602, "literal": "^1.1.4", @@ -7855,7 +7855,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 603, "literal": "^1.0.2", @@ -7868,7 +7868,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 604, "literal": "^1.0.0", @@ -7881,7 +7881,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 605, "literal": "^1.0.7", @@ -7894,7 +7894,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 606, "literal": "^1.2.4", @@ -7907,7 +7907,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 607, "literal": "^1.0.3", @@ -7920,7 +7920,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 608, "literal": "^2.0.5", @@ -7933,7 +7933,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 609, "literal": "^1.0.6", @@ -7946,7 +7946,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 610, "literal": "^1.2.1", @@ -7959,7 +7959,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 611, "literal": "^1.3.0", @@ -7972,7 +7972,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 612, "literal": "^2.0.1", @@ -7985,7 +7985,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 613, "literal": "^1.1.4", @@ -7998,7 +7998,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 614, "literal": "^1.3.0", @@ -8011,7 +8011,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 615, "literal": "^1.2.3", @@ -8024,7 +8024,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 616, "literal": "^1.0.2", @@ -8037,7 +8037,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 617, "literal": "^1.0.5", @@ -8050,7 +8050,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 618, "literal": "^1.2.1", @@ -8063,7 +8063,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 619, "literal": "^1.0.3", @@ -8076,7 +8076,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 620, "literal": "^1.1.1", @@ -8089,7 +8089,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 621, "literal": "^1.0.2", @@ -8102,7 +8102,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 622, "literal": "^1.0.7", @@ -8115,7 +8115,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 623, "literal": "^1.1.13", @@ -8128,7 +8128,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 624, "literal": "^1.0.2", @@ -8141,7 +8141,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 625, "literal": "^1.2.1", @@ -8154,7 +8154,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 626, "literal": "^1.3.0", @@ -8167,7 +8167,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 627, "literal": "^2.0.0", @@ -8180,7 +8180,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 628, "literal": "^1.0.4", @@ -8193,7 +8193,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 629, "literal": "^1.0.7", @@ -8206,7 +8206,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 630, "literal": "^1.3.0", @@ -8219,7 +8219,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 631, "literal": "^1.2.4", @@ -8232,7 +8232,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 632, "literal": "^1.13.1", @@ -8245,7 +8245,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 633, "literal": "^1.2.1", @@ -8258,7 +8258,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 634, "literal": "^1.0.1", @@ -8271,7 +8271,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 635, "literal": "^1.0.5", @@ -8284,7 +8284,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 636, "literal": "^1.3.0", @@ -8297,7 +8297,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 637, "literal": "^1.2.4", @@ -8310,7 +8310,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 638, "literal": "^1.0.2", @@ -8323,7 +8323,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 639, "literal": "^1.2.0", @@ -8336,7 +8336,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 640, "literal": "^1.22.1", @@ -8349,7 +8349,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 641, "literal": "^1.2.3", @@ -8362,7 +8362,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 642, "literal": "^1.1.4", @@ -8375,7 +8375,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 643, "literal": "^1.0.1", @@ -8388,7 +8388,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 644, "literal": "^1.0.2", @@ -8401,7 +8401,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 645, "literal": "^1.0.0", @@ -8414,7 +8414,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 646, "literal": "^1.2.4", @@ -8427,7 +8427,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 647, "literal": "^1.0.2", @@ -8440,7 +8440,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 648, "literal": "^2.0.1", @@ -8453,7 +8453,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 649, "literal": "^1.0.6", @@ -8466,7 +8466,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 650, "literal": "^1.3.0", @@ -8479,7 +8479,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 651, "literal": "^1.0.1", @@ -8492,7 +8492,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 652, "literal": "^1.0.7", @@ -8505,7 +8505,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 653, "literal": "^1.3.0", @@ -8518,7 +8518,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 654, "literal": "^1.0.1", @@ -8531,7 +8531,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 655, "literal": "^1.0.6", @@ -8544,7 +8544,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 656, "literal": "^1.3.0", @@ -8557,7 +8557,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 657, "literal": "^1.0.1", @@ -8570,7 +8570,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 658, "literal": "^1.0.1", @@ -8583,7 +8583,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 659, "literal": "^1.0.5", @@ -8596,7 +8596,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 660, "literal": "^1.2.1", @@ -8609,7 +8609,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 661, "literal": "^1.22.3", @@ -8622,7 +8622,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 662, "literal": "^1.2.1", @@ -8635,7 +8635,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 663, "literal": "^1.2.3", @@ -8648,7 +8648,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 664, "literal": "^3.0.4", @@ -8661,7 +8661,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 665, "literal": "^1.0.2", @@ -8674,7 +8674,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 666, "literal": "^1.0.5", @@ -8687,7 +8687,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 667, "literal": "^3.0.4", @@ -8700,7 +8700,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 668, "literal": "^1.0.7", @@ -8713,7 +8713,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 669, "literal": "^1.2.1", @@ -8726,7 +8726,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 670, "literal": "^1.23.2", @@ -8739,7 +8739,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 671, "literal": "^1.0.0", @@ -8752,7 +8752,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 672, "literal": "^3.2.7", @@ -8765,7 +8765,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 673, "literal": "^2.1.1", @@ -8778,7 +8778,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 674, "literal": "^3.2.7", @@ -8791,7 +8791,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 675, "literal": "^2.13.0", @@ -8804,7 +8804,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 676, "literal": "^1.22.4", @@ -8817,7 +8817,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 677, "literal": "^2.0.2", @@ -8830,7 +8830,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 678, "literal": "^1.0.2", @@ -8843,7 +8843,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 679, "literal": "^1.2.0", @@ -8856,7 +8856,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 680, "literal": "^1.22.1", @@ -8869,7 +8869,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 681, "literal": "^1.0.0", @@ -8882,7 +8882,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 682, "literal": "^2.0.0", @@ -8895,7 +8895,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 683, "literal": "^1.0.2", @@ -8908,7 +8908,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 684, "literal": "^1.2.0", @@ -8921,7 +8921,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 685, "literal": "^1.22.1", @@ -8934,7 +8934,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 686, "literal": "^1.0.0", @@ -8947,7 +8947,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 687, "literal": "^1.0.7", @@ -8960,7 +8960,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 688, "literal": "^1.2.1", @@ -8973,7 +8973,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 689, "literal": "^1.23.2", @@ -8986,7 +8986,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 690, "literal": "^1.3.0", @@ -8999,7 +8999,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 691, "literal": "^1.0.0", @@ -9012,7 +9012,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 692, "literal": "^1.0.2", @@ -9025,7 +9025,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 693, "literal": "^1.0.7", @@ -9038,7 +9038,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 694, "literal": "^1.2.1", @@ -9051,7 +9051,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 695, "literal": "^1.23.2", @@ -9064,7 +9064,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 696, "literal": "^1.0.0", @@ -9077,7 +9077,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 697, "literal": "^1.2.4", @@ -9090,7 +9090,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 698, "literal": "^1.0.7", @@ -9103,7 +9103,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 699, "literal": "^1.0.0", @@ -9116,7 +9116,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 700, "literal": "^4.2.4", @@ -9129,7 +9129,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 701, "literal": "^2.2.0", @@ -9155,7 +9155,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 703, "literal": "^4.3.4", @@ -9168,7 +9168,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 704, "literal": "6.21.0", @@ -9181,7 +9181,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 705, "literal": "6.21.0", @@ -9194,7 +9194,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 706, "literal": "6.21.0", @@ -9207,7 +9207,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 707, "literal": "6.21.0", @@ -9233,7 +9233,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 709, "literal": "^4.3.4", @@ -9246,7 +9246,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 710, "literal": "^11.1.0", @@ -9259,7 +9259,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 711, "literal": "^7.5.4", @@ -9272,7 +9272,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 712, "literal": "^4.0.3", @@ -9285,7 +9285,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 713, "literal": "9.0.3", @@ -9298,7 +9298,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 714, "literal": "^1.0.1", @@ -9311,7 +9311,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 715, "literal": "6.21.0", @@ -9324,7 +9324,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 716, "literal": "6.21.0", @@ -9337,7 +9337,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 717, "literal": "^3.4.1", @@ -9350,7 +9350,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 718, "literal": "6.21.0", @@ -9376,7 +9376,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 720, "literal": "^2.0.1", @@ -9389,7 +9389,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 721, "literal": "^2.1.0", @@ -9402,7 +9402,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 722, "literal": "^3.0.1", @@ -9415,7 +9415,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 723, "literal": "^3.2.9", @@ -9428,7 +9428,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 724, "literal": "^5.2.0", @@ -9441,7 +9441,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 725, "literal": "^1.4.1", @@ -9454,7 +9454,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 726, "literal": "^3.0.0", @@ -9467,7 +9467,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 727, "literal": "^4.0.0", @@ -9480,7 +9480,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 728, "literal": "6.21.0", @@ -9493,7 +9493,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 729, "literal": "6.21.0", @@ -9506,7 +9506,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 730, "literal": "10.3.10", @@ -9519,7 +9519,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 731, "literal": "^7.23.2", @@ -9532,7 +9532,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 732, "literal": "^5.3.0", @@ -9545,7 +9545,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 733, "literal": "^3.1.7", @@ -9558,7 +9558,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 734, "literal": "^1.3.2", @@ -9571,7 +9571,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 735, "literal": "^0.0.8", @@ -9584,7 +9584,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 736, "literal": "=4.7.0", @@ -9597,7 +9597,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 737, "literal": "^3.2.1", @@ -9610,7 +9610,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 738, "literal": "^1.0.8", @@ -9623,7 +9623,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 739, "literal": "^9.2.2", @@ -9636,7 +9636,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 740, "literal": "^1.0.15", @@ -9649,7 +9649,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 741, "literal": "^2.0.0", @@ -9662,7 +9662,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 742, "literal": "^3.3.5", @@ -9675,7 +9675,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 743, "literal": "^1.0.9", @@ -9688,7 +9688,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 744, "literal": "^3.1.2", @@ -9701,7 +9701,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 745, "literal": "^1.1.7", @@ -9714,7 +9714,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 746, "literal": "^2.0.7", @@ -9740,7 +9740,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 748, "literal": "^1.0.7", @@ -9753,7 +9753,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 749, "literal": "^1.2.1", @@ -9766,7 +9766,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 750, "literal": "^1.0.0", @@ -9779,7 +9779,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 751, "literal": "^0.3.20", @@ -9792,7 +9792,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 752, "literal": "^3.1.6", @@ -9805,7 +9805,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 753, "literal": "^1.3.1", @@ -9818,7 +9818,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 754, "literal": "^4.1.4", @@ -9831,7 +9831,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 755, "literal": "^1.1.6", @@ -9844,7 +9844,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 756, "literal": "^1.0.7", @@ -9857,7 +9857,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 757, "literal": "^1.2.1", @@ -9870,7 +9870,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 758, "literal": "^1.23.3", @@ -9883,7 +9883,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 759, "literal": "^1.3.0", @@ -9896,7 +9896,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 760, "literal": "^2.0.3", @@ -9909,7 +9909,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 761, "literal": "^1.1.2", @@ -9922,7 +9922,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 762, "literal": "^1.2.4", @@ -9935,7 +9935,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 763, "literal": "^1.0.3", @@ -9948,7 +9948,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 764, "literal": "^1.0.2", @@ -9961,7 +9961,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 765, "literal": "^1.0.3", @@ -9974,7 +9974,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 766, "literal": "^1.0.3", @@ -9987,7 +9987,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 767, "literal": "^1.0.7", @@ -10000,7 +10000,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 768, "literal": "^1.1.2", @@ -10013,7 +10013,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 769, "literal": "^1.1.2", @@ -10026,7 +10026,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 770, "literal": "^1.2.1", @@ -10039,7 +10039,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 771, "literal": "^1.2.1", @@ -10052,7 +10052,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 772, "literal": "^1.0.3", @@ -10065,7 +10065,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 773, "literal": "^1.0.4", @@ -10078,7 +10078,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 774, "literal": "^2.0.1", @@ -10091,7 +10091,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 775, "literal": "^1.0.7", @@ -10104,7 +10104,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 776, "literal": "^1.2.1", @@ -10117,7 +10117,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 777, "literal": "^1.23.1", @@ -10130,7 +10130,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 778, "literal": "^1.3.0", @@ -10143,7 +10143,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 779, "literal": "^1.2.4", @@ -10156,7 +10156,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 780, "literal": "^1.0.3", @@ -10169,7 +10169,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 781, "literal": "^1.1.3", @@ -10182,7 +10182,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 782, "literal": "^1.1.5", @@ -10195,7 +10195,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 783, "literal": "^1.0.0", @@ -10208,7 +10208,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 784, "literal": "^2.0.0", @@ -10221,7 +10221,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 785, "literal": "^1.0.5", @@ -10234,7 +10234,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 786, "literal": "^1.0.2", @@ -10247,7 +10247,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 787, "literal": "^1.0.10", @@ -10260,7 +10260,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 788, "literal": "^1.1.4", @@ -10273,7 +10273,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 789, "literal": "^1.0.2", @@ -10286,7 +10286,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 790, "literal": "^2.0.5", @@ -10299,7 +10299,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 791, "literal": "^1.0.2", @@ -10312,7 +10312,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 792, "literal": "^1.0.1", @@ -10325,7 +10325,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 793, "literal": "^1.1.9", @@ -10338,7 +10338,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 794, "literal": "^2.0.3", @@ -10351,7 +10351,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 795, "literal": "^2.0.3", @@ -10364,7 +10364,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 796, "literal": "^2.0.2", @@ -10377,7 +10377,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 797, "literal": "^2.0.3", @@ -10390,7 +10390,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 798, "literal": "^1.0.7", @@ -10403,7 +10403,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 799, "literal": "^1.2.4", @@ -10416,7 +10416,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 800, "literal": "^1.0.0", @@ -10429,7 +10429,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 801, "literal": "^1.0.2", @@ -10442,7 +10442,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 802, "literal": "^1.0.0", @@ -10455,7 +10455,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 803, "literal": "^2.0.3", @@ -10468,7 +10468,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 804, "literal": "^2.0.3", @@ -10481,7 +10481,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 805, "literal": "^0.14.0", @@ -10494,7 +10494,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 806, "literal": "^3.1.8", @@ -10507,7 +10507,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 807, "literal": "^1.2.5", @@ -10520,7 +10520,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 808, "literal": "^1.3.2", @@ -10533,7 +10533,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 809, "literal": "^1.1.2", @@ -10546,7 +10546,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 810, "literal": "^1.1.3", @@ -10559,7 +10559,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 811, "literal": "^2.1.0", @@ -10572,7 +10572,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 812, "literal": "^1.0.19", @@ -10585,7 +10585,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 813, "literal": "^5.3.0", @@ -10598,7 +10598,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 814, "literal": "^2.4.1 || ^3.0.0", @@ -10611,7 +10611,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 815, "literal": "^3.1.2", @@ -10624,7 +10624,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 816, "literal": "^1.1.8", @@ -10637,7 +10637,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 817, "literal": "^2.0.8", @@ -10650,7 +10650,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 818, "literal": "^1.1.4", @@ -10663,7 +10663,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 819, "literal": "^1.2.0", @@ -10676,7 +10676,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 820, "literal": "^15.8.1", @@ -10689,7 +10689,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 821, "literal": "^2.0.0-next.5", @@ -10702,7 +10702,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 822, "literal": "^6.3.1", @@ -10715,7 +10715,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 823, "literal": "^4.0.11", @@ -10741,7 +10741,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 825, "literal": "^1.0.7", @@ -10754,7 +10754,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 826, "literal": "^1.2.1", @@ -10767,7 +10767,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 827, "literal": "^1.23.2", @@ -10780,7 +10780,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 828, "literal": "^1.3.0", @@ -10793,7 +10793,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 829, "literal": "^1.0.0", @@ -10806,7 +10806,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 830, "literal": "^1.2.4", @@ -10819,7 +10819,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 831, "literal": "^1.0.1", @@ -10832,7 +10832,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 832, "literal": "^1.0.3", @@ -10845,7 +10845,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 833, "literal": "^1.0.7", @@ -10858,7 +10858,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 834, "literal": "^1.5.2", @@ -10871,7 +10871,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 835, "literal": "^2.0.2", @@ -10884,7 +10884,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 836, "literal": "^1.0.6", @@ -10897,7 +10897,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 837, "literal": "^2.13.0", @@ -10910,7 +10910,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 838, "literal": "^1.0.7", @@ -10923,7 +10923,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 839, "literal": "^1.0.0", @@ -10936,7 +10936,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 840, "literal": "^1.4.0", @@ -10949,7 +10949,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 841, "literal": "^4.1.1", @@ -10962,7 +10962,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 842, "literal": "^16.13.1", @@ -10975,7 +10975,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 843, "literal": "^1.2.1", @@ -10988,7 +10988,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 844, "literal": "^1.23.2", @@ -11001,7 +11001,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 845, "literal": "^1.0.0", @@ -11014,7 +11014,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 846, "literal": "^1.0.7", @@ -11027,7 +11027,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 847, "literal": "^1.2.1", @@ -11040,7 +11040,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 848, "literal": "^1.23.3", @@ -11053,7 +11053,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 849, "literal": "^1.3.0", @@ -11066,7 +11066,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 850, "literal": "^1.0.2", @@ -11079,7 +11079,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 851, "literal": "^1.0.2", @@ -11092,7 +11092,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 852, "literal": "^1.2.0", @@ -11105,7 +11105,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 853, "literal": "^1.22.1", @@ -11118,7 +11118,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 854, "literal": "^1.0.0", @@ -11131,7 +11131,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 855, "literal": "^1.0.7", @@ -11144,7 +11144,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 856, "literal": "^1.2.1", @@ -11157,7 +11157,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 857, "literal": "^1.23.2", @@ -11170,7 +11170,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 858, "literal": "^1.3.0", @@ -11183,7 +11183,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 859, "literal": "^1.0.0", @@ -11196,7 +11196,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 860, "literal": "^1.0.2", @@ -11209,7 +11209,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 861, "literal": "~8.5.10", @@ -11222,7 +11222,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 862, "literal": "~20.12.8", @@ -11235,7 +11235,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 863, "literal": "*", @@ -11248,7 +11248,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 864, "literal": "^4.21.10", @@ -11261,7 +11261,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 865, "literal": "^1.0.30001538", @@ -11274,7 +11274,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 866, "literal": "^4.3.6", @@ -11287,7 +11287,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 867, "literal": "^0.1.2", @@ -11300,7 +11300,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 868, "literal": "^1.0.0", @@ -11313,7 +11313,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 869, "literal": "^4.2.0", @@ -11339,7 +11339,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 871, "literal": "^1.0.30001587", @@ -11352,7 +11352,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 872, "literal": "^1.4.668", @@ -11365,7 +11365,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 873, "literal": "^2.0.14", @@ -11378,7 +11378,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 874, "literal": "^1.0.13", @@ -11391,7 +11391,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 875, "literal": "^3.1.2", @@ -11404,7 +11404,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 876, "literal": "^1.0.1", @@ -11430,7 +11430,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 878, "literal": "*", @@ -11443,7 +11443,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 879, "literal": "*", @@ -11456,7 +11456,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 880, "literal": "*", @@ -11469,7 +11469,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 881, "literal": "^3.0.2", @@ -20772,7 +20772,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 374, }, "array-includes": { - "id": 806, + "id": 445, "package_id": 384, }, "array-union": { @@ -20792,7 +20792,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 382, }, "array.prototype.flatmap": { - "id": 808, + "id": 448, "package_id": 380, }, "array.prototype.toreversed": { @@ -21068,11 +21068,11 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 316, }, "es-errors": { - "id": 858, + "id": 690, "package_id": 312, }, "es-iterator-helpers": { - "id": 812, + "id": 740, "package_id": 409, }, "es-object-atoms": { @@ -21084,7 +21084,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 369, }, "es-shim-unscopables": { - "id": 860, + "id": 692, "package_id": 381, }, "es-to-primitive": { @@ -21120,7 +21120,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 302, }, "eslint-module-utils": { - "id": 452, + "id": 438, "package_id": 376, }, "eslint-plugin-import": { @@ -21164,7 +21164,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 279, }, "estraverse": { - "id": 432, + "id": 415, "package_id": 166, }, "esutils": { @@ -21248,7 +21248,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 51, }, "function-bind": { - "id": 761, + "id": 55, "package_id": 21, }, "function.prototype.name": { @@ -21412,7 +21412,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 329, }, "is-core-module": { - "id": 454, + "id": 675, "package_id": 19, }, "is-data-view": { @@ -21556,7 +21556,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 174, }, "jsx-ast-utils": { - "id": 814, + "id": 742, "package_id": 408, }, "keyv": { @@ -21680,11 +21680,11 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 355, }, "object.entries": { - "id": 816, + "id": 745, "package_id": 405, }, "object.fromentries": { - "id": 817, + "id": 457, "package_id": 375, }, "object.groupby": { @@ -21696,7 +21696,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 434, }, "object.values": { - "id": 819, + "id": 459, "package_id": 310, }, "once": { @@ -21924,7 +21924,7 @@ exports[`ssr works for 100-ish requests 1`] = ` "package_id": 112, }, "semver": { - "id": 822, + "id": 460, "package_id": 309, }, "set-function-length": { @@ -22271,18 +22271,14 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "dependencies": { - "doctrine": { - "id": 811, - "package_id": 379, - }, - "resolve": { - "id": 821, - "package_id": 431, + "debug": { + "id": 674, + "package_id": 377, }, }, "depth": 1, "id": 4, - "path": "node_modules/eslint-plugin-react/node_modules", + "path": "node_modules/eslint-import-resolver-node/node_modules", }, { "dependencies": { @@ -22301,14 +22297,18 @@ exports[`ssr works for 100-ish requests 1`] = ` }, { "dependencies": { - "debug": { - "id": 674, - "package_id": 377, + "doctrine": { + "id": 811, + "package_id": 379, + }, + "resolve": { + "id": 821, + "package_id": 431, }, }, "depth": 1, "id": 6, - "path": "node_modules/eslint-import-resolver-node/node_modules", + "path": "node_modules/eslint-plugin-react/node_modules", }, { "dependencies": { @@ -22358,17 +22358,6 @@ exports[`ssr works for 100-ish requests 1`] = ` "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, - { - "dependencies": { - "debug": { - "id": 672, - "package_id": 377, - }, - }, - "depth": 1, - "id": 11, - "path": "node_modules/eslint-module-utils/node_modules", - }, { "dependencies": { "minimatch": { @@ -22377,7 +22366,7 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/glob/node_modules", }, { @@ -22392,9 +22381,20 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, + { + "dependencies": { + "debug": { + "id": 672, + "package_id": 377, + }, + }, + "depth": 1, + "id": 13, + "path": "node_modules/eslint-module-utils/node_modules", + }, { "dependencies": { "lru-cache": { @@ -22439,17 +22439,6 @@ exports[`ssr works for 100-ish requests 1`] = ` "id": 17, "path": "node_modules/path-scurry/node_modules", }, - { - "dependencies": { - "lru-cache": { - "id": 165, - "package_id": 117, - }, - }, - "depth": 2, - "id": 18, - "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", - }, { "dependencies": { "brace-expansion": { @@ -22458,9 +22447,20 @@ exports[`ssr works for 100-ish requests 1`] = ` }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, + { + "dependencies": { + "lru-cache": { + "id": 165, + "package_id": 117, + }, + }, + "depth": 2, + "id": 19, + "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", + }, { "dependencies": { "@types/node": { diff --git a/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap b/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap index 7b40a27d78..6bacb22ab5 100644 --- a/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap +++ b/test/integration/next-pages/test/__snapshots__/dev-server.test.ts.snap @@ -5,7 +5,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "dependencies": [ { "behavior": { - "normal": true, + "prod": true, }, "id": 0, "literal": "20.7.0", @@ -18,7 +18,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 1, "literal": "18.2.22", @@ -31,7 +31,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 2, "literal": "18.2.7", @@ -44,7 +44,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 3, "literal": "10.4.16", @@ -57,7 +57,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 4, "literal": "^1.0.3", @@ -70,7 +70,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 5, "literal": "8.50.0", @@ -83,7 +83,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 6, "literal": "14.1.3", @@ -96,7 +96,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 7, "literal": "14.1.3", @@ -109,7 +109,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 8, "literal": "8.4.30", @@ -122,7 +122,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 9, "literal": "22.12.0", @@ -135,7 +135,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 10, "literal": "18.2.0", @@ -148,7 +148,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 11, "literal": "18.2.0", @@ -161,7 +161,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 12, "literal": "3.3.3", @@ -174,7 +174,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 13, "literal": "5.2.2", @@ -187,7 +187,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 14, "literal": "^5.0.2", @@ -200,7 +200,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 15, "literal": "^1.1.3", @@ -213,7 +213,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 16, "literal": "^1.18.2", @@ -226,7 +226,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 17, "literal": "^4.0.3", @@ -239,7 +239,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 18, "literal": "^8.4.23", @@ -252,7 +252,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 19, "literal": "^1.22.2", @@ -265,7 +265,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 20, "literal": "^3.32.0", @@ -278,7 +278,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 21, "literal": "^3.5.3", @@ -291,7 +291,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 22, "literal": "^3.2.12", @@ -304,7 +304,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 23, "literal": "^2.1.0", @@ -317,7 +317,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 24, "literal": "^1.2.2", @@ -330,7 +330,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 25, "literal": "^4.0.5", @@ -343,7 +343,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 26, "literal": "^1.0.0", @@ -356,7 +356,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 27, "literal": "^4.0.1", @@ -369,7 +369,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 28, "literal": "^6.0.2", @@ -382,7 +382,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 29, "literal": "^3.0.0", @@ -395,7 +395,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 30, "literal": "^3.0.0", @@ -408,7 +408,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 31, "literal": "^15.1.0", @@ -421,7 +421,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 32, "literal": "^6.0.1", @@ -434,7 +434,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 33, "literal": "^5.2.0", @@ -447,7 +447,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 34, "literal": "^4.0.1", @@ -460,7 +460,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 35, "literal": "^6.0.11", @@ -473,7 +473,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 36, "literal": "^3.0.0", @@ -486,7 +486,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 37, "literal": "^1.0.2", @@ -499,7 +499,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 38, "literal": "^2.3.4", @@ -512,7 +512,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 39, "literal": "^3.0.0", @@ -553,7 +553,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 42, "literal": "^3.3.6", @@ -566,7 +566,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 43, "literal": "^1.0.0", @@ -579,7 +579,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 44, "literal": "^1.0.2", @@ -592,7 +592,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 45, "literal": "^6.0.11", @@ -618,7 +618,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 47, "literal": "^4.0.0", @@ -631,7 +631,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 48, "literal": "^1.0.0", @@ -644,7 +644,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 49, "literal": "^1.1.7", @@ -670,7 +670,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 51, "literal": "^2.13.0", @@ -683,7 +683,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 52, "literal": "^1.0.7", @@ -696,7 +696,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 53, "literal": "^1.0.0", @@ -709,7 +709,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 54, "literal": "^2.0.0", @@ -722,7 +722,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 55, "literal": "^1.1.2", @@ -735,7 +735,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 56, "literal": "^2.3.0", @@ -748,7 +748,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 57, "literal": "^4.0.3", @@ -761,7 +761,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 58, "literal": "^2.1.1", @@ -774,7 +774,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 59, "literal": "^2.0.1", @@ -800,7 +800,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 61, "literal": "^3.0.3", @@ -813,7 +813,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 62, "literal": "^2.3.1", @@ -826,7 +826,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 63, "literal": "^7.1.1", @@ -839,7 +839,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 64, "literal": "^5.0.1", @@ -852,7 +852,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 65, "literal": "^7.0.0", @@ -865,7 +865,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 66, "literal": "^2.0.2", @@ -878,7 +878,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 67, "literal": "^1.2.3", @@ -891,7 +891,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 68, "literal": "^5.1.2", @@ -904,7 +904,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 69, "literal": "^1.3.0", @@ -917,7 +917,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 70, "literal": "^4.0.4", @@ -930,7 +930,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 71, "literal": "^4.0.1", @@ -943,7 +943,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 72, "literal": "2.1.5", @@ -956,7 +956,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 73, "literal": "^1.6.0", @@ -969,7 +969,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 74, "literal": "^1.0.4", @@ -982,7 +982,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 75, "literal": "2.0.5", @@ -995,7 +995,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 76, "literal": "^1.1.9", @@ -1008,7 +1008,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 77, "literal": "^1.2.2", @@ -1021,7 +1021,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 78, "literal": "~3.1.2", @@ -1034,7 +1034,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 79, "literal": "~3.0.2", @@ -1047,7 +1047,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 80, "literal": "~5.1.2", @@ -1060,7 +1060,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 81, "literal": "~2.1.0", @@ -1073,7 +1073,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 82, "literal": "~4.0.1", @@ -1086,7 +1086,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 83, "literal": "~3.0.0", @@ -1099,7 +1099,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 84, "literal": "~3.6.0", @@ -1125,7 +1125,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 86, "literal": "^2.2.1", @@ -1138,7 +1138,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 87, "literal": "^2.0.0", @@ -1151,7 +1151,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 88, "literal": "^3.0.0", @@ -1164,7 +1164,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 89, "literal": "^2.0.4", @@ -1177,7 +1177,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 90, "literal": "^0.3.2", @@ -1190,7 +1190,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 91, "literal": "^4.0.0", @@ -1203,7 +1203,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 92, "literal": "^10.3.10", @@ -1216,7 +1216,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 93, "literal": "^1.1.6", @@ -1229,7 +1229,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 94, "literal": "^2.7.0", @@ -1242,7 +1242,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 95, "literal": "^4.0.1", @@ -1255,7 +1255,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 96, "literal": "^0.1.9", @@ -1268,7 +1268,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 97, "literal": "^1.0.0", @@ -1281,7 +1281,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 98, "literal": "^4.0.1", @@ -1294,7 +1294,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 99, "literal": "^1.0.0", @@ -1307,7 +1307,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 100, "literal": ">= 3.1.0 < 4", @@ -1320,7 +1320,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 101, "literal": "^1.0.0", @@ -1333,7 +1333,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 102, "literal": "^3.1.0", @@ -1346,7 +1346,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 103, "literal": "^2.3.5", @@ -1359,7 +1359,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 104, "literal": "^9.0.1", @@ -1372,7 +1372,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 105, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -1385,7 +1385,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 106, "literal": "^1.10.1", @@ -1398,7 +1398,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 107, "literal": "^10.2.0", @@ -1411,7 +1411,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 108, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -1424,7 +1424,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 109, "literal": "^2.0.1", @@ -1437,7 +1437,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 110, "literal": "^1.0.0", @@ -1450,7 +1450,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 111, "literal": "^8.0.2", @@ -1476,7 +1476,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 113, "literal": "^5.1.2", @@ -1489,7 +1489,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 114, "is_alias": true, @@ -1503,7 +1503,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 115, "literal": "^7.0.1", @@ -1516,7 +1516,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 116, "is_alias": true, @@ -1530,7 +1530,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 117, "literal": "^8.1.0", @@ -1543,7 +1543,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 118, "is_alias": true, @@ -1557,7 +1557,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 119, "literal": "^4.0.0", @@ -1570,7 +1570,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 120, "literal": "^4.1.0", @@ -1583,7 +1583,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 121, "literal": "^6.0.0", @@ -1596,7 +1596,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 122, "literal": "^5.0.1", @@ -1609,7 +1609,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 123, "literal": "^8.0.0", @@ -1622,7 +1622,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 124, "literal": "^3.0.0", @@ -1635,7 +1635,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 125, "literal": "^6.0.1", @@ -1648,7 +1648,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 126, "literal": "^2.0.1", @@ -1661,7 +1661,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 127, "literal": "~1.1.4", @@ -1674,7 +1674,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 128, "literal": "^6.1.0", @@ -1687,7 +1687,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 129, "literal": "^5.0.1", @@ -1700,7 +1700,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 130, "literal": "^7.0.1", @@ -1713,7 +1713,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 131, "literal": "^6.0.1", @@ -1726,7 +1726,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 132, "literal": "^0.2.0", @@ -1739,7 +1739,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 133, "literal": "^9.2.2", @@ -1752,7 +1752,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 134, "literal": "^7.0.1", @@ -1765,7 +1765,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 135, "literal": "^7.0.0", @@ -1778,7 +1778,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 136, "literal": "^4.0.1", @@ -1791,7 +1791,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 137, "literal": "^3.1.0", @@ -1804,7 +1804,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 138, "literal": "^2.0.0", @@ -1817,7 +1817,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 139, "literal": "^2.0.1", @@ -1830,7 +1830,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 140, "literal": "^2.0.0", @@ -1843,7 +1843,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 141, "literal": "^3.0.0", @@ -1856,7 +1856,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 142, "literal": "^1.2.1", @@ -1869,7 +1869,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 143, "literal": "^1.4.10", @@ -1882,7 +1882,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 144, "literal": "^0.3.24", @@ -1895,7 +1895,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 145, "literal": "^3.1.0", @@ -1908,7 +1908,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 146, "literal": "^1.4.14", @@ -1921,7 +1921,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 147, "literal": "^0.23.0", @@ -1934,7 +1934,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 148, "literal": "^1.1.0", @@ -1960,7 +1960,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 150, "literal": "^1.1.0", @@ -1973,7 +1973,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 151, "literal": "^3.0.0 || ^4.0.0", @@ -1986,7 +1986,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 152, "literal": "^1.1.0", @@ -1999,7 +1999,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 153, "literal": "9.0.0", @@ -2012,7 +2012,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 154, "literal": "22.12.0", @@ -2025,7 +2025,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 155, "literal": "2.2.3", @@ -2038,7 +2038,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 156, "literal": "0.0.1299070", @@ -2051,7 +2051,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 157, "literal": "4.3.4", @@ -2064,7 +2064,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 158, "literal": "2.0.1", @@ -2077,7 +2077,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 159, "literal": "2.0.3", @@ -2090,7 +2090,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 160, "literal": "6.4.0", @@ -2103,7 +2103,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 161, "literal": "3.0.5", @@ -2116,7 +2116,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 162, "literal": "1.4.3", @@ -2129,7 +2129,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 163, "literal": "17.7.2", @@ -2142,7 +2142,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 164, "literal": "7.6.0", @@ -2155,7 +2155,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 165, "literal": "^6.0.0", @@ -2168,7 +2168,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 166, "literal": "^4.0.0", @@ -2181,7 +2181,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 167, "literal": "^8.0.1", @@ -2194,7 +2194,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 168, "literal": "^3.1.1", @@ -2207,7 +2207,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 169, "literal": "^2.0.5", @@ -2220,7 +2220,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 170, "literal": "^2.1.1", @@ -2233,7 +2233,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 171, "literal": "^4.2.3", @@ -2246,7 +2246,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 172, "literal": "^5.0.5", @@ -2259,7 +2259,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 173, "literal": "^21.1.1", @@ -2272,7 +2272,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 174, "literal": "^4.2.0", @@ -2285,7 +2285,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 175, "literal": "^6.0.1", @@ -2298,7 +2298,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 176, "literal": "^7.0.0", @@ -2311,7 +2311,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 177, "literal": "^5.2.1", @@ -2324,7 +2324,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 178, "literal": "^2.3.8", @@ -2337,7 +2337,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 179, "literal": "^1.3.1", @@ -2350,7 +2350,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 180, "literal": "^1.1.13", @@ -2363,7 +2363,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 181, "literal": "^3.0.0", @@ -2376,7 +2376,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 182, "literal": "^3.1.5", @@ -2415,7 +2415,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 185, "literal": "^2.1.0", @@ -2428,7 +2428,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 186, "literal": "^2.0.0", @@ -2441,7 +2441,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 187, "literal": "^2.0.0", @@ -2454,7 +2454,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 188, "literal": "^2.0.0", @@ -2467,7 +2467,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 189, "literal": "^2.18.0", @@ -2480,7 +2480,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 190, "literal": "^1.3.2", @@ -2493,7 +2493,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 191, "literal": "^1.0.1", @@ -2506,7 +2506,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 192, "literal": "^1.1.0", @@ -2532,7 +2532,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 194, "literal": "^1.6.4", @@ -2545,7 +2545,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 195, "literal": "^1.6.4", @@ -2558,7 +2558,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 196, "literal": "^1.2.0", @@ -2571,7 +2571,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 197, "literal": "^2.15.0", @@ -2584,7 +2584,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 198, "literal": "^1.1.0", @@ -2597,7 +2597,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 199, "literal": "^1.3.1", @@ -2610,7 +2610,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 200, "literal": "1", @@ -2623,7 +2623,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 201, "literal": "^1.4.0", @@ -2636,7 +2636,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 202, "literal": "^7.0.2", @@ -2649,7 +2649,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 203, "literal": "^4.3.4", @@ -2662,7 +2662,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 204, "literal": "^7.0.1", @@ -2675,7 +2675,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 205, "literal": "^7.0.3", @@ -2688,7 +2688,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 206, "literal": "^7.14.1", @@ -2701,7 +2701,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 207, "literal": "^7.0.1", @@ -2714,7 +2714,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 208, "literal": "^1.1.0", @@ -2727,7 +2727,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 209, "literal": "^8.0.2", @@ -2740,7 +2740,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 210, "literal": "^7.1.1", @@ -2753,7 +2753,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 211, "literal": "^4.3.4", @@ -2766,7 +2766,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 212, "literal": "^2.7.1", @@ -2779,7 +2779,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 213, "literal": "^9.0.5", @@ -2792,7 +2792,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 214, "literal": "^4.2.0", @@ -2805,7 +2805,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 215, "literal": "1.1.0", @@ -2818,7 +2818,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 216, "literal": "^1.1.3", @@ -2831,7 +2831,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 217, "literal": "2.1.2", @@ -2844,7 +2844,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 218, "literal": "^4.3.4", @@ -2857,7 +2857,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 219, "literal": "^0.23.0", @@ -2870,7 +2870,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 220, "literal": "^7.0.2", @@ -2883,7 +2883,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 221, "literal": "^4.3.4", @@ -2896,7 +2896,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 222, "literal": "^6.0.1", @@ -2909,7 +2909,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 223, "literal": "^7.0.0", @@ -2922,7 +2922,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 224, "literal": "^7.0.2", @@ -2935,7 +2935,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 225, "literal": "^7.0.0", @@ -2948,7 +2948,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 226, "literal": "^8.0.2", @@ -2961,7 +2961,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 227, "literal": "^5.0.0", @@ -2974,7 +2974,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 228, "literal": "^2.0.2", @@ -2987,7 +2987,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 229, "literal": "^0.13.4", @@ -3000,7 +3000,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 230, "literal": "^2.1.0", @@ -3013,7 +3013,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 231, "literal": "^4.0.1", @@ -3026,7 +3026,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 232, "literal": "^5.2.0", @@ -3039,7 +3039,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 233, "literal": "^2.0.2", @@ -3052,7 +3052,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 234, "literal": "^4.0.1", @@ -3078,7 +3078,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 236, "literal": "^2.0.1", @@ -3091,7 +3091,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 237, "literal": "^7.0.2", @@ -3104,7 +3104,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 238, "literal": "4", @@ -3117,7 +3117,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 239, "literal": "^7.1.0", @@ -3130,7 +3130,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 240, "literal": "^4.3.4", @@ -3143,7 +3143,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 241, "literal": "^5.0.2", @@ -3156,7 +3156,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 242, "literal": "^6.0.2", @@ -3169,7 +3169,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 243, "literal": "^4.3.4", @@ -3182,7 +3182,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 244, "literal": "^11.2.0", @@ -3195,7 +3195,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 245, "literal": "^4.2.0", @@ -3208,7 +3208,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 246, "literal": "^6.0.1", @@ -3221,7 +3221,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 247, "literal": "^2.0.0", @@ -3234,7 +3234,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 248, "literal": "^2.0.0", @@ -3260,7 +3260,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 250, "literal": "^4.1.1", @@ -3273,7 +3273,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 251, "literal": "^5.1.0", @@ -3286,7 +3286,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 252, "literal": "^2.10.0", @@ -3312,7 +3312,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 254, "literal": "*", @@ -3325,7 +3325,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 255, "literal": "~5.26.4", @@ -3338,7 +3338,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 256, "literal": "~1.1.0", @@ -3351,7 +3351,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 257, "literal": "~0.2.3", @@ -3364,7 +3364,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 258, "literal": "~1.2.0", @@ -3377,7 +3377,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 259, "literal": "^3.0.0", @@ -3390,7 +3390,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 260, "literal": "2.1.2", @@ -3403,7 +3403,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 261, "literal": "2.2.3", @@ -3416,7 +3416,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 262, "literal": "0.5.24", @@ -3429,7 +3429,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 263, "literal": "4.3.5", @@ -3442,7 +3442,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 264, "literal": "0.0.1299070", @@ -3455,7 +3455,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 265, "literal": "8.17.1", @@ -3496,7 +3496,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 268, "literal": "3.0.1", @@ -3509,7 +3509,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 269, "literal": "10.0.0", @@ -3522,7 +3522,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 270, "literal": "3.23.8", @@ -3548,7 +3548,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 272, "literal": "^2.2.1", @@ -3561,7 +3561,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 273, "literal": "^3.3.0", @@ -3574,7 +3574,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 274, "literal": "^4.1.0", @@ -3587,7 +3587,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 275, "literal": "^5.2.0", @@ -3614,7 +3614,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 277, "literal": "^7.0.0", @@ -3627,7 +3627,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 278, "literal": "^1.3.1", @@ -3640,7 +3640,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 279, "literal": "^2.3.0", @@ -3653,7 +3653,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 280, "literal": "^1.1.6", @@ -3666,7 +3666,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 281, "literal": "^0.2.1", @@ -3679,7 +3679,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 282, "literal": "^7.24.7", @@ -3692,7 +3692,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 283, "literal": "^1.0.0", @@ -3705,7 +3705,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 284, "literal": "^7.24.7", @@ -3718,7 +3718,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 285, "literal": "^2.4.2", @@ -3731,7 +3731,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 286, "literal": "^4.0.0", @@ -3744,7 +3744,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 287, "literal": "^1.0.0", @@ -3757,7 +3757,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 288, "literal": "^3.2.1", @@ -3770,7 +3770,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 289, "literal": "^1.0.5", @@ -3783,7 +3783,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 290, "literal": "^5.3.0", @@ -3796,7 +3796,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 291, "literal": "^3.0.0", @@ -3809,7 +3809,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 292, "literal": "^1.9.0", @@ -3822,7 +3822,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 293, "literal": "1.1.3", @@ -3835,7 +3835,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 294, "literal": "^2.0.1", @@ -3848,7 +3848,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 295, "literal": "^1.0.0", @@ -3861,7 +3861,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 296, "literal": "^4.0.0", @@ -3874,7 +3874,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 297, "literal": "^3.0.0", @@ -3887,7 +3887,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 298, "literal": "1.6.0", @@ -3900,7 +3900,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 299, "literal": "8.4.31", @@ -3913,7 +3913,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 300, "literal": "14.1.3", @@ -3926,7 +3926,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 301, "literal": "5.1.1", @@ -3939,7 +3939,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 302, "literal": "^4.2.11", @@ -3952,7 +3952,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 303, "literal": "0.5.2", @@ -3965,7 +3965,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 304, "literal": "^1.0.30001579", @@ -4149,7 +4149,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 318, "literal": "^2.4.0", @@ -4162,7 +4162,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 319, "literal": "0.0.1", @@ -4188,7 +4188,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 321, "literal": "^3.3.6", @@ -4201,7 +4201,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 322, "literal": "^1.0.0", @@ -4214,7 +4214,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 323, "literal": "^1.0.2", @@ -4227,7 +4227,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 324, "literal": "^1.1.0", @@ -4240,7 +4240,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 325, "literal": "^7.33.2", @@ -4253,7 +4253,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 326, "literal": "^2.28.1", @@ -4266,7 +4266,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 327, "literal": "^6.7.1", @@ -4279,7 +4279,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 328, "literal": "^1.3.3", @@ -4292,7 +4292,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 329, "literal": "14.1.3", @@ -4305,7 +4305,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 330, "literal": "^5.4.2 || ^6.0.0", @@ -4318,7 +4318,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", @@ -4331,7 +4331,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 332, "literal": "^0.3.6", @@ -4344,7 +4344,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 333, "literal": "^3.5.2", @@ -4384,7 +4384,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 336, "literal": "^6.12.4", @@ -4397,7 +4397,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 337, "literal": "^0.4.1", @@ -4410,7 +4410,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 338, "literal": "^4.0.0", @@ -4423,7 +4423,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 339, "literal": "^4.3.2", @@ -4436,7 +4436,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 340, "literal": "^9.6.1", @@ -4449,7 +4449,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 341, "literal": "^5.2.0", @@ -4462,7 +4462,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 342, "literal": "^1.4.2", @@ -4475,7 +4475,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 343, "literal": "^2.0.2", @@ -4488,7 +4488,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 344, "literal": "^5.0.0", @@ -4501,7 +4501,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 345, "literal": "^13.19.0", @@ -4514,7 +4514,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 346, "literal": "^4.0.0", @@ -4527,7 +4527,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 347, "literal": "^4.1.0", @@ -4540,7 +4540,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 348, "literal": "^3.0.0", @@ -4553,7 +4553,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 349, "literal": "^1.4.0", @@ -4566,7 +4566,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 350, "literal": "^3.1.2", @@ -4579,7 +4579,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 351, "literal": "8.50.0", @@ -4592,7 +4592,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 352, "literal": "^0.9.3", @@ -4605,7 +4605,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 353, "literal": "^6.0.1", @@ -4618,7 +4618,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 354, "literal": "^0.2.0", @@ -4631,7 +4631,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 355, "literal": "^7.0.2", @@ -4644,7 +4644,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 356, "literal": "^6.0.2", @@ -4657,7 +4657,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 357, "literal": "^0.1.4", @@ -4670,7 +4670,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 358, "literal": "^7.2.2", @@ -4683,7 +4683,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 359, "literal": "^4.6.2", @@ -4696,7 +4696,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 360, "literal": "^3.0.3", @@ -4709,7 +4709,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 361, "literal": "^3.1.3", @@ -4722,7 +4722,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 362, "literal": "^1.4.0", @@ -4735,7 +4735,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 363, "literal": "^2.1.2", @@ -4748,7 +4748,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 364, "literal": "^1.2.8", @@ -4761,7 +4761,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 365, "literal": "^6.0.1", @@ -4774,7 +4774,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 366, "literal": "^3.4.3", @@ -4787,7 +4787,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 367, "literal": "^4.0.0", @@ -4800,7 +4800,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 368, "literal": "^4.6.1", @@ -4813,7 +4813,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 369, "literal": "^0.11.11", @@ -4826,7 +4826,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 370, "literal": "^4.2.0", @@ -4839,7 +4839,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 371, "literal": "^1.0.1", @@ -4852,7 +4852,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 372, "literal": "^1.0.1", @@ -4865,7 +4865,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 373, "literal": "^3.3.0", @@ -4891,7 +4891,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 375, "literal": "^2.0.2", @@ -4904,7 +4904,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 376, "literal": "^4.3.1", @@ -4917,7 +4917,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 377, "literal": "^3.0.5", @@ -4930,7 +4930,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 378, "literal": "^1.1.7", @@ -4943,7 +4943,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 379, "literal": "^1.0.0", @@ -4956,7 +4956,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 380, "literal": "0.0.1", @@ -4969,7 +4969,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 381, "literal": "^3.0.4", @@ -4982,7 +4982,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 382, "literal": "^3.2.9", @@ -4995,7 +4995,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 383, "literal": "^4.5.3", @@ -5008,7 +5008,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 384, "literal": "^3.0.2", @@ -5021,7 +5021,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 385, "literal": "^7.1.3", @@ -5034,7 +5034,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 386, "literal": "^1.0.0", @@ -5047,7 +5047,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 387, "literal": "^1.0.4", @@ -5060,7 +5060,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 388, "literal": "2", @@ -5073,7 +5073,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 389, "literal": "^3.1.1", @@ -5086,7 +5086,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 390, "literal": "^1.3.0", @@ -5099,7 +5099,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 391, "literal": "^1.0.0", @@ -5112,7 +5112,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 392, "literal": "^1.3.0", @@ -5125,7 +5125,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 393, "literal": "1", @@ -5138,7 +5138,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 394, "literal": "3.0.1", @@ -5151,7 +5151,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 395, "literal": "^6.12.4", @@ -5164,7 +5164,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 396, "literal": "^4.3.2", @@ -5177,7 +5177,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 397, "literal": "^9.6.0", @@ -5190,7 +5190,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 398, "literal": "^13.19.0", @@ -5203,7 +5203,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 399, "literal": "^5.2.0", @@ -5216,7 +5216,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 400, "literal": "^3.2.1", @@ -5229,7 +5229,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 401, "literal": "^4.1.0", @@ -5242,7 +5242,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 402, "literal": "^3.1.2", @@ -5255,7 +5255,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 403, "literal": "^3.1.1", @@ -5268,7 +5268,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 404, "literal": "^0.20.2", @@ -5281,7 +5281,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 405, "literal": "^8.9.0", @@ -5294,7 +5294,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 406, "literal": "^5.3.2", @@ -5307,7 +5307,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 407, "literal": "^3.4.1", @@ -5333,7 +5333,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 409, "literal": "^3.1.1", @@ -5346,7 +5346,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 410, "literal": "^2.0.0", @@ -5359,7 +5359,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 411, "literal": "^0.4.1", @@ -5372,7 +5372,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 412, "literal": "^4.2.2", @@ -5385,7 +5385,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 413, "literal": "^2.1.0", @@ -5398,7 +5398,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 414, "literal": "^4.3.0", @@ -5411,7 +5411,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 415, "literal": "^5.2.0", @@ -5424,7 +5424,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 416, "literal": "^5.2.0", @@ -5437,7 +5437,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 417, "literal": "^1.2.1", @@ -5450,7 +5450,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 418, "literal": "^0.1.3", @@ -5463,7 +5463,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 419, "literal": "^1.2.5", @@ -5476,7 +5476,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 420, "literal": "^0.4.0", @@ -5489,7 +5489,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 421, "literal": "^0.4.1", @@ -5502,7 +5502,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 422, "literal": "^2.0.6", @@ -5515,7 +5515,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 423, "literal": "^1.2.1", @@ -5528,7 +5528,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 424, "literal": "~0.4.0", @@ -5541,7 +5541,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 425, "literal": "^1.2.1", @@ -5554,7 +5554,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 426, "literal": "^2.0.2", @@ -5567,7 +5567,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 427, "literal": "^6.0.0", @@ -5580,7 +5580,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 428, "literal": "^4.0.0", @@ -5593,7 +5593,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 429, "literal": "^5.0.0", @@ -5606,7 +5606,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 430, "literal": "^3.0.2", @@ -5619,7 +5619,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 431, "literal": "^0.1.0", @@ -5632,7 +5632,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 432, "literal": "^5.1.0", @@ -5645,7 +5645,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 433, "literal": "^4.1.0", @@ -5658,7 +5658,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 434, "literal": "^7.1.0", @@ -5671,7 +5671,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 435, "literal": "^4.0.0", @@ -5684,7 +5684,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 436, "literal": "^4.3.4", @@ -5697,7 +5697,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 437, "literal": "^5.12.0", @@ -5710,7 +5710,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 438, "literal": "^2.7.4", @@ -5723,7 +5723,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 439, "literal": "^3.3.1", @@ -5736,7 +5736,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 440, "literal": "^4.5.0", @@ -5749,7 +5749,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 441, "literal": "^2.11.0", @@ -5762,7 +5762,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 442, "literal": "^4.0.3", @@ -5801,7 +5801,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 445, "literal": "^3.1.7", @@ -5814,7 +5814,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 446, "literal": "^1.2.3", @@ -5827,7 +5827,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 447, "literal": "^1.3.2", @@ -5840,7 +5840,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 448, "literal": "^1.3.2", @@ -5853,7 +5853,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 449, "literal": "^3.2.7", @@ -5866,7 +5866,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 450, "literal": "^2.1.0", @@ -5879,7 +5879,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 451, "literal": "^0.3.9", @@ -5892,7 +5892,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 452, "literal": "^2.8.0", @@ -5905,7 +5905,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 453, "literal": "^2.0.0", @@ -5918,7 +5918,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 454, "literal": "^2.13.1", @@ -5931,7 +5931,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 455, "literal": "^4.0.3", @@ -5944,7 +5944,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 456, "literal": "^3.1.2", @@ -5957,7 +5957,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 457, "literal": "^2.0.7", @@ -5970,7 +5970,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 458, "literal": "^1.0.1", @@ -5983,7 +5983,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 459, "literal": "^1.1.7", @@ -5996,7 +5996,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 460, "literal": "^6.3.1", @@ -6009,7 +6009,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 461, "literal": "^3.15.0", @@ -6035,7 +6035,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 463, "literal": "^0.0.29", @@ -6048,7 +6048,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 464, "literal": "^1.0.2", @@ -6061,7 +6061,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 465, "literal": "^1.2.6", @@ -6074,7 +6074,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 466, "literal": "^3.0.0", @@ -6087,7 +6087,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 467, "literal": "^1.2.0", @@ -6100,7 +6100,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 468, "literal": "^1.0.7", @@ -6113,7 +6113,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 469, "literal": "^1.2.1", @@ -6126,7 +6126,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 470, "literal": "^1.0.0", @@ -6139,7 +6139,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 471, "literal": "^1.3.0", @@ -6152,7 +6152,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 472, "literal": "^1.0.1", @@ -6165,7 +6165,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 473, "literal": "^1.0.0", @@ -6178,7 +6178,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 474, "literal": "^1.1.1", @@ -6191,7 +6191,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 475, "literal": "^1.0.0", @@ -6204,7 +6204,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 476, "literal": "^1.2.4", @@ -6217,7 +6217,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 477, "literal": "^1.3.0", @@ -6230,7 +6230,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 478, "literal": "^1.1.2", @@ -6243,7 +6243,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 479, "literal": "^1.0.1", @@ -6256,7 +6256,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 480, "literal": "^1.0.3", @@ -6269,7 +6269,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 481, "literal": "^2.0.0", @@ -6282,7 +6282,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 482, "literal": "^1.0.0", @@ -6295,7 +6295,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 483, "literal": "^1.3.0", @@ -6308,7 +6308,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 484, "literal": "^1.0.1", @@ -6321,7 +6321,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 485, "literal": "^1.1.3", @@ -6334,7 +6334,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 486, "literal": "^1.0.0", @@ -6347,7 +6347,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 487, "literal": "^1.3.0", @@ -6360,7 +6360,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 488, "literal": "^1.1.2", @@ -6373,7 +6373,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 489, "literal": "^1.2.4", @@ -6386,7 +6386,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 490, "literal": "^1.2.1", @@ -6399,7 +6399,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 491, "literal": "^1.1.4", @@ -6412,7 +6412,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 492, "literal": "^1.3.0", @@ -6425,7 +6425,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 493, "literal": "^1.1.2", @@ -6438,7 +6438,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 494, "literal": "^1.2.4", @@ -6451,7 +6451,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 495, "literal": "^1.0.1", @@ -6464,7 +6464,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 496, "literal": "^1.0.2", @@ -6477,7 +6477,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 497, "literal": "^1.0.7", @@ -6490,7 +6490,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 498, "literal": "^1.2.1", @@ -6503,7 +6503,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 499, "literal": "^1.23.2", @@ -6516,7 +6516,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 500, "literal": "^1.0.1", @@ -6529,7 +6529,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 501, "literal": "^1.0.3", @@ -6542,7 +6542,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 502, "literal": "^1.0.7", @@ -6555,7 +6555,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 503, "literal": "^1.0.7", @@ -6568,7 +6568,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 504, "literal": "^1.0.1", @@ -6581,7 +6581,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 505, "literal": "^1.0.1", @@ -6594,7 +6594,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 506, "literal": "^1.0.0", @@ -6607,7 +6607,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 507, "literal": "^1.0.0", @@ -6620,7 +6620,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 508, "literal": "^1.3.0", @@ -6633,7 +6633,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 509, "literal": "^1.0.0", @@ -6646,7 +6646,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 510, "literal": "^2.0.3", @@ -6659,7 +6659,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 511, "literal": "^1.2.1", @@ -6672,7 +6672,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 512, "literal": "^1.1.6", @@ -6685,7 +6685,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 513, "literal": "^1.2.4", @@ -6698,7 +6698,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 514, "literal": "^1.0.2", @@ -6711,7 +6711,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 515, "literal": "^1.0.3", @@ -6724,7 +6724,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 516, "literal": "^1.0.1", @@ -6737,7 +6737,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 517, "literal": "^1.0.2", @@ -6750,7 +6750,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 518, "literal": "^1.0.3", @@ -6763,7 +6763,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 519, "literal": "^1.0.3", @@ -6776,7 +6776,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 520, "literal": "^2.0.2", @@ -6789,7 +6789,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 521, "literal": "^1.0.7", @@ -6802,7 +6802,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 522, "literal": "^3.0.4", @@ -6815,7 +6815,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 523, "literal": "^1.2.7", @@ -6828,7 +6828,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 524, "literal": "^1.0.1", @@ -6841,7 +6841,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 525, "literal": "^2.0.3", @@ -6854,7 +6854,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 526, "literal": "^1.1.4", @@ -6867,7 +6867,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 527, "literal": "^1.0.3", @@ -6880,7 +6880,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 528, "literal": "^1.0.7", @@ -6893,7 +6893,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 529, "literal": "^1.1.13", @@ -6906,7 +6906,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 530, "literal": "^1.0.2", @@ -6919,7 +6919,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 531, "literal": "^1.13.1", @@ -6932,7 +6932,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 532, "literal": "^1.1.1", @@ -6945,7 +6945,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 533, "literal": "^4.1.5", @@ -6958,7 +6958,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 534, "literal": "^1.5.2", @@ -6971,7 +6971,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 535, "literal": "^1.1.2", @@ -6984,7 +6984,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 536, "literal": "^1.0.3", @@ -6997,7 +6997,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 537, "literal": "^1.2.9", @@ -7010,7 +7010,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 538, "literal": "^1.0.8", @@ -7023,7 +7023,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 539, "literal": "^1.0.8", @@ -7036,7 +7036,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 540, "literal": "^1.0.2", @@ -7049,7 +7049,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 541, "literal": "^1.0.1", @@ -7062,7 +7062,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 542, "literal": "^1.0.2", @@ -7075,7 +7075,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 543, "literal": "^1.0.6", @@ -7088,7 +7088,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 544, "literal": "^1.0.2", @@ -7101,7 +7101,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 545, "literal": "^1.1.15", @@ -7114,7 +7114,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 546, "literal": "^1.0.7", @@ -7127,7 +7127,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 547, "literal": "^1.0.7", @@ -7140,7 +7140,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 548, "literal": "^0.3.3", @@ -7153,7 +7153,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 549, "literal": "^1.0.1", @@ -7166,7 +7166,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 550, "literal": "^1.0.2", @@ -7179,7 +7179,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 551, "literal": "^1.0.3", @@ -7192,7 +7192,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 552, "literal": "^1.1.3", @@ -7205,7 +7205,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 553, "literal": "^1.0.0", @@ -7218,7 +7218,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 554, "literal": "^1.0.2", @@ -7231,7 +7231,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 555, "literal": "^1.0.2", @@ -7244,7 +7244,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 556, "literal": "^1.0.3", @@ -7257,7 +7257,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 557, "literal": "^1.0.2", @@ -7270,7 +7270,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 558, "literal": "^1.0.1", @@ -7283,7 +7283,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 559, "literal": "^1.1.0", @@ -7296,7 +7296,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 560, "literal": "^1.0.4", @@ -7309,7 +7309,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 561, "literal": "^1.0.5", @@ -7322,7 +7322,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 562, "literal": "^1.0.3", @@ -7335,7 +7335,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 563, "literal": "^1.0.2", @@ -7348,7 +7348,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 564, "literal": "^1.0.0", @@ -7361,7 +7361,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 565, "literal": "^1.0.0", @@ -7374,7 +7374,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 566, "literal": "^1.0.2", @@ -7387,7 +7387,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 567, "literal": "^1.0.0", @@ -7400,7 +7400,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 568, "literal": "^1.0.1", @@ -7413,7 +7413,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 569, "literal": "^1.0.7", @@ -7426,7 +7426,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 570, "literal": "^0.3.3", @@ -7439,7 +7439,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 571, "literal": "^1.0.1", @@ -7452,7 +7452,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 572, "literal": "^1.0.3", @@ -7465,7 +7465,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 573, "literal": "^1.1.13", @@ -7478,7 +7478,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 574, "literal": "^1.0.0", @@ -7491,7 +7491,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 575, "literal": "^1.1.14", @@ -7504,7 +7504,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 576, "literal": "^1.0.7", @@ -7517,7 +7517,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 577, "literal": "^1.0.7", @@ -7530,7 +7530,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 578, "literal": "^0.3.3", @@ -7543,7 +7543,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 579, "literal": "^1.0.1", @@ -7556,7 +7556,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 580, "literal": "^1.0.3", @@ -7569,7 +7569,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 581, "literal": "^1.1.13", @@ -7582,7 +7582,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 582, "literal": "^1.0.7", @@ -7595,7 +7595,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 583, "literal": "^0.3.3", @@ -7608,7 +7608,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 584, "literal": "^1.0.1", @@ -7621,7 +7621,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 585, "literal": "^1.0.3", @@ -7634,7 +7634,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 586, "literal": "^1.1.13", @@ -7647,7 +7647,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 587, "literal": "^1.0.7", @@ -7660,7 +7660,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 588, "literal": "^1.3.0", @@ -7673,7 +7673,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 589, "literal": "^1.1.13", @@ -7686,7 +7686,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 590, "literal": "^1.0.7", @@ -7699,7 +7699,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 591, "literal": "^1.2.1", @@ -7712,7 +7712,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 592, "literal": "^1.0.0", @@ -7725,7 +7725,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 593, "literal": "^1.0.7", @@ -7738,7 +7738,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 594, "literal": "^1.2.1", @@ -7751,7 +7751,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 595, "literal": "^1.0.0", @@ -7764,7 +7764,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 596, "literal": "^1.0.7", @@ -7777,7 +7777,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 597, "literal": "^1.2.1", @@ -7790,7 +7790,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 598, "literal": "^1.23.0", @@ -7803,7 +7803,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 599, "literal": "^1.0.0", @@ -7816,7 +7816,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 600, "literal": "^1.0.6", @@ -7829,7 +7829,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 601, "literal": "^1.3.0", @@ -7842,7 +7842,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 602, "literal": "^1.1.4", @@ -7855,7 +7855,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 603, "literal": "^1.0.2", @@ -7868,7 +7868,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 604, "literal": "^1.0.0", @@ -7881,7 +7881,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 605, "literal": "^1.0.7", @@ -7894,7 +7894,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 606, "literal": "^1.2.4", @@ -7907,7 +7907,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 607, "literal": "^1.0.3", @@ -7920,7 +7920,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 608, "literal": "^2.0.5", @@ -7933,7 +7933,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 609, "literal": "^1.0.6", @@ -7946,7 +7946,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 610, "literal": "^1.2.1", @@ -7959,7 +7959,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 611, "literal": "^1.3.0", @@ -7972,7 +7972,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 612, "literal": "^2.0.1", @@ -7985,7 +7985,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 613, "literal": "^1.1.4", @@ -7998,7 +7998,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 614, "literal": "^1.3.0", @@ -8011,7 +8011,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 615, "literal": "^1.2.3", @@ -8024,7 +8024,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 616, "literal": "^1.0.2", @@ -8037,7 +8037,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 617, "literal": "^1.0.5", @@ -8050,7 +8050,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 618, "literal": "^1.2.1", @@ -8063,7 +8063,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 619, "literal": "^1.0.3", @@ -8076,7 +8076,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 620, "literal": "^1.1.1", @@ -8089,7 +8089,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 621, "literal": "^1.0.2", @@ -8102,7 +8102,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 622, "literal": "^1.0.7", @@ -8115,7 +8115,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 623, "literal": "^1.1.13", @@ -8128,7 +8128,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 624, "literal": "^1.0.2", @@ -8141,7 +8141,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 625, "literal": "^1.2.1", @@ -8154,7 +8154,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 626, "literal": "^1.3.0", @@ -8167,7 +8167,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 627, "literal": "^2.0.0", @@ -8180,7 +8180,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 628, "literal": "^1.0.4", @@ -8193,7 +8193,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 629, "literal": "^1.0.7", @@ -8206,7 +8206,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 630, "literal": "^1.3.0", @@ -8219,7 +8219,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 631, "literal": "^1.2.4", @@ -8232,7 +8232,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 632, "literal": "^1.13.1", @@ -8245,7 +8245,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 633, "literal": "^1.2.1", @@ -8258,7 +8258,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 634, "literal": "^1.0.1", @@ -8271,7 +8271,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 635, "literal": "^1.0.5", @@ -8284,7 +8284,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 636, "literal": "^1.3.0", @@ -8297,7 +8297,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 637, "literal": "^1.2.4", @@ -8310,7 +8310,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 638, "literal": "^1.0.2", @@ -8323,7 +8323,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 639, "literal": "^1.2.0", @@ -8336,7 +8336,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 640, "literal": "^1.22.1", @@ -8349,7 +8349,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 641, "literal": "^1.2.3", @@ -8362,7 +8362,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 642, "literal": "^1.1.4", @@ -8375,7 +8375,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 643, "literal": "^1.0.1", @@ -8388,7 +8388,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 644, "literal": "^1.0.2", @@ -8401,7 +8401,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 645, "literal": "^1.0.0", @@ -8414,7 +8414,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 646, "literal": "^1.2.4", @@ -8427,7 +8427,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 647, "literal": "^1.0.2", @@ -8440,7 +8440,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 648, "literal": "^2.0.1", @@ -8453,7 +8453,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 649, "literal": "^1.0.6", @@ -8466,7 +8466,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 650, "literal": "^1.3.0", @@ -8479,7 +8479,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 651, "literal": "^1.0.1", @@ -8492,7 +8492,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 652, "literal": "^1.0.7", @@ -8505,7 +8505,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 653, "literal": "^1.3.0", @@ -8518,7 +8518,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 654, "literal": "^1.0.1", @@ -8531,7 +8531,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 655, "literal": "^1.0.6", @@ -8544,7 +8544,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 656, "literal": "^1.3.0", @@ -8557,7 +8557,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 657, "literal": "^1.0.1", @@ -8570,7 +8570,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 658, "literal": "^1.0.1", @@ -8583,7 +8583,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 659, "literal": "^1.0.5", @@ -8596,7 +8596,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 660, "literal": "^1.2.1", @@ -8609,7 +8609,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 661, "literal": "^1.22.3", @@ -8622,7 +8622,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 662, "literal": "^1.2.1", @@ -8635,7 +8635,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 663, "literal": "^1.2.3", @@ -8648,7 +8648,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 664, "literal": "^3.0.4", @@ -8661,7 +8661,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 665, "literal": "^1.0.2", @@ -8674,7 +8674,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 666, "literal": "^1.0.5", @@ -8687,7 +8687,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 667, "literal": "^3.0.4", @@ -8700,7 +8700,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 668, "literal": "^1.0.7", @@ -8713,7 +8713,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 669, "literal": "^1.2.1", @@ -8726,7 +8726,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 670, "literal": "^1.23.2", @@ -8739,7 +8739,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 671, "literal": "^1.0.0", @@ -8752,7 +8752,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 672, "literal": "^3.2.7", @@ -8765,7 +8765,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 673, "literal": "^2.1.1", @@ -8778,7 +8778,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 674, "literal": "^3.2.7", @@ -8791,7 +8791,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 675, "literal": "^2.13.0", @@ -8804,7 +8804,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 676, "literal": "^1.22.4", @@ -8817,7 +8817,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 677, "literal": "^2.0.2", @@ -8830,7 +8830,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 678, "literal": "^1.0.2", @@ -8843,7 +8843,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 679, "literal": "^1.2.0", @@ -8856,7 +8856,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 680, "literal": "^1.22.1", @@ -8869,7 +8869,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 681, "literal": "^1.0.0", @@ -8882,7 +8882,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 682, "literal": "^2.0.0", @@ -8895,7 +8895,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 683, "literal": "^1.0.2", @@ -8908,7 +8908,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 684, "literal": "^1.2.0", @@ -8921,7 +8921,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 685, "literal": "^1.22.1", @@ -8934,7 +8934,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 686, "literal": "^1.0.0", @@ -8947,7 +8947,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 687, "literal": "^1.0.7", @@ -8960,7 +8960,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 688, "literal": "^1.2.1", @@ -8973,7 +8973,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 689, "literal": "^1.23.2", @@ -8986,7 +8986,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 690, "literal": "^1.3.0", @@ -8999,7 +8999,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 691, "literal": "^1.0.0", @@ -9012,7 +9012,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 692, "literal": "^1.0.2", @@ -9025,7 +9025,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 693, "literal": "^1.0.7", @@ -9038,7 +9038,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 694, "literal": "^1.2.1", @@ -9051,7 +9051,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 695, "literal": "^1.23.2", @@ -9064,7 +9064,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 696, "literal": "^1.0.0", @@ -9077,7 +9077,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 697, "literal": "^1.2.4", @@ -9090,7 +9090,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 698, "literal": "^1.0.7", @@ -9103,7 +9103,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 699, "literal": "^1.0.0", @@ -9116,7 +9116,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 700, "literal": "^4.2.4", @@ -9129,7 +9129,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 701, "literal": "^2.2.0", @@ -9155,7 +9155,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 703, "literal": "^4.3.4", @@ -9168,7 +9168,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 704, "literal": "6.21.0", @@ -9181,7 +9181,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 705, "literal": "6.21.0", @@ -9194,7 +9194,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 706, "literal": "6.21.0", @@ -9207,7 +9207,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 707, "literal": "6.21.0", @@ -9233,7 +9233,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 709, "literal": "^4.3.4", @@ -9246,7 +9246,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 710, "literal": "^11.1.0", @@ -9259,7 +9259,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 711, "literal": "^7.5.4", @@ -9272,7 +9272,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 712, "literal": "^4.0.3", @@ -9285,7 +9285,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 713, "literal": "9.0.3", @@ -9298,7 +9298,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 714, "literal": "^1.0.1", @@ -9311,7 +9311,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 715, "literal": "6.21.0", @@ -9324,7 +9324,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 716, "literal": "6.21.0", @@ -9337,7 +9337,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 717, "literal": "^3.4.1", @@ -9350,7 +9350,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 718, "literal": "6.21.0", @@ -9376,7 +9376,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 720, "literal": "^2.0.1", @@ -9389,7 +9389,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 721, "literal": "^2.1.0", @@ -9402,7 +9402,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 722, "literal": "^3.0.1", @@ -9415,7 +9415,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 723, "literal": "^3.2.9", @@ -9428,7 +9428,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 724, "literal": "^5.2.0", @@ -9441,7 +9441,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 725, "literal": "^1.4.1", @@ -9454,7 +9454,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 726, "literal": "^3.0.0", @@ -9467,7 +9467,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 727, "literal": "^4.0.0", @@ -9480,7 +9480,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 728, "literal": "6.21.0", @@ -9493,7 +9493,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 729, "literal": "6.21.0", @@ -9506,7 +9506,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 730, "literal": "10.3.10", @@ -9519,7 +9519,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 731, "literal": "^7.23.2", @@ -9532,7 +9532,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 732, "literal": "^5.3.0", @@ -9545,7 +9545,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 733, "literal": "^3.1.7", @@ -9558,7 +9558,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 734, "literal": "^1.3.2", @@ -9571,7 +9571,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 735, "literal": "^0.0.8", @@ -9584,7 +9584,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 736, "literal": "=4.7.0", @@ -9597,7 +9597,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 737, "literal": "^3.2.1", @@ -9610,7 +9610,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 738, "literal": "^1.0.8", @@ -9623,7 +9623,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 739, "literal": "^9.2.2", @@ -9636,7 +9636,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 740, "literal": "^1.0.15", @@ -9649,7 +9649,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 741, "literal": "^2.0.0", @@ -9662,7 +9662,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 742, "literal": "^3.3.5", @@ -9675,7 +9675,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 743, "literal": "^1.0.9", @@ -9688,7 +9688,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 744, "literal": "^3.1.2", @@ -9701,7 +9701,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 745, "literal": "^1.1.7", @@ -9714,7 +9714,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 746, "literal": "^2.0.7", @@ -9740,7 +9740,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 748, "literal": "^1.0.7", @@ -9753,7 +9753,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 749, "literal": "^1.2.1", @@ -9766,7 +9766,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 750, "literal": "^1.0.0", @@ -9779,7 +9779,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 751, "literal": "^0.3.20", @@ -9792,7 +9792,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 752, "literal": "^3.1.6", @@ -9805,7 +9805,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 753, "literal": "^1.3.1", @@ -9818,7 +9818,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 754, "literal": "^4.1.4", @@ -9831,7 +9831,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 755, "literal": "^1.1.6", @@ -9844,7 +9844,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 756, "literal": "^1.0.7", @@ -9857,7 +9857,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 757, "literal": "^1.2.1", @@ -9870,7 +9870,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 758, "literal": "^1.23.3", @@ -9883,7 +9883,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 759, "literal": "^1.3.0", @@ -9896,7 +9896,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 760, "literal": "^2.0.3", @@ -9909,7 +9909,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 761, "literal": "^1.1.2", @@ -9922,7 +9922,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 762, "literal": "^1.2.4", @@ -9935,7 +9935,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 763, "literal": "^1.0.3", @@ -9948,7 +9948,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 764, "literal": "^1.0.2", @@ -9961,7 +9961,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 765, "literal": "^1.0.3", @@ -9974,7 +9974,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 766, "literal": "^1.0.3", @@ -9987,7 +9987,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 767, "literal": "^1.0.7", @@ -10000,7 +10000,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 768, "literal": "^1.1.2", @@ -10013,7 +10013,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 769, "literal": "^1.1.2", @@ -10026,7 +10026,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 770, "literal": "^1.2.1", @@ -10039,7 +10039,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 771, "literal": "^1.2.1", @@ -10052,7 +10052,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 772, "literal": "^1.0.3", @@ -10065,7 +10065,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 773, "literal": "^1.0.4", @@ -10078,7 +10078,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 774, "literal": "^2.0.1", @@ -10091,7 +10091,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 775, "literal": "^1.0.7", @@ -10104,7 +10104,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 776, "literal": "^1.2.1", @@ -10117,7 +10117,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 777, "literal": "^1.23.1", @@ -10130,7 +10130,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 778, "literal": "^1.3.0", @@ -10143,7 +10143,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 779, "literal": "^1.2.4", @@ -10156,7 +10156,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 780, "literal": "^1.0.3", @@ -10169,7 +10169,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 781, "literal": "^1.1.3", @@ -10182,7 +10182,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 782, "literal": "^1.1.5", @@ -10195,7 +10195,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 783, "literal": "^1.0.0", @@ -10208,7 +10208,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 784, "literal": "^2.0.0", @@ -10221,7 +10221,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 785, "literal": "^1.0.5", @@ -10234,7 +10234,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 786, "literal": "^1.0.2", @@ -10247,7 +10247,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 787, "literal": "^1.0.10", @@ -10260,7 +10260,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 788, "literal": "^1.1.4", @@ -10273,7 +10273,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 789, "literal": "^1.0.2", @@ -10286,7 +10286,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 790, "literal": "^2.0.5", @@ -10299,7 +10299,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 791, "literal": "^1.0.2", @@ -10312,7 +10312,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 792, "literal": "^1.0.1", @@ -10325,7 +10325,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 793, "literal": "^1.1.9", @@ -10338,7 +10338,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 794, "literal": "^2.0.3", @@ -10351,7 +10351,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 795, "literal": "^2.0.3", @@ -10364,7 +10364,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 796, "literal": "^2.0.2", @@ -10377,7 +10377,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 797, "literal": "^2.0.3", @@ -10390,7 +10390,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 798, "literal": "^1.0.7", @@ -10403,7 +10403,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 799, "literal": "^1.2.4", @@ -10416,7 +10416,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 800, "literal": "^1.0.0", @@ -10429,7 +10429,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 801, "literal": "^1.0.2", @@ -10442,7 +10442,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 802, "literal": "^1.0.0", @@ -10455,7 +10455,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 803, "literal": "^2.0.3", @@ -10468,7 +10468,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 804, "literal": "^2.0.3", @@ -10481,7 +10481,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 805, "literal": "^0.14.0", @@ -10494,7 +10494,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 806, "literal": "^3.1.8", @@ -10507,7 +10507,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 807, "literal": "^1.2.5", @@ -10520,7 +10520,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 808, "literal": "^1.3.2", @@ -10533,7 +10533,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 809, "literal": "^1.1.2", @@ -10546,7 +10546,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 810, "literal": "^1.1.3", @@ -10559,7 +10559,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 811, "literal": "^2.1.0", @@ -10572,7 +10572,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 812, "literal": "^1.0.19", @@ -10585,7 +10585,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 813, "literal": "^5.3.0", @@ -10598,7 +10598,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 814, "literal": "^2.4.1 || ^3.0.0", @@ -10611,7 +10611,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 815, "literal": "^3.1.2", @@ -10624,7 +10624,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 816, "literal": "^1.1.8", @@ -10637,7 +10637,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 817, "literal": "^2.0.8", @@ -10650,7 +10650,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 818, "literal": "^1.1.4", @@ -10663,7 +10663,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 819, "literal": "^1.2.0", @@ -10676,7 +10676,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 820, "literal": "^15.8.1", @@ -10689,7 +10689,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 821, "literal": "^2.0.0-next.5", @@ -10702,7 +10702,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 822, "literal": "^6.3.1", @@ -10715,7 +10715,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 823, "literal": "^4.0.11", @@ -10741,7 +10741,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 825, "literal": "^1.0.7", @@ -10754,7 +10754,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 826, "literal": "^1.2.1", @@ -10767,7 +10767,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 827, "literal": "^1.23.2", @@ -10780,7 +10780,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 828, "literal": "^1.3.0", @@ -10793,7 +10793,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 829, "literal": "^1.0.0", @@ -10806,7 +10806,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 830, "literal": "^1.2.4", @@ -10819,7 +10819,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 831, "literal": "^1.0.1", @@ -10832,7 +10832,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 832, "literal": "^1.0.3", @@ -10845,7 +10845,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 833, "literal": "^1.0.7", @@ -10858,7 +10858,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 834, "literal": "^1.5.2", @@ -10871,7 +10871,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 835, "literal": "^2.0.2", @@ -10884,7 +10884,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 836, "literal": "^1.0.6", @@ -10897,7 +10897,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 837, "literal": "^2.13.0", @@ -10910,7 +10910,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 838, "literal": "^1.0.7", @@ -10923,7 +10923,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 839, "literal": "^1.0.0", @@ -10936,7 +10936,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 840, "literal": "^1.4.0", @@ -10949,7 +10949,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 841, "literal": "^4.1.1", @@ -10962,7 +10962,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 842, "literal": "^16.13.1", @@ -10975,7 +10975,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 843, "literal": "^1.2.1", @@ -10988,7 +10988,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 844, "literal": "^1.23.2", @@ -11001,7 +11001,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 845, "literal": "^1.0.0", @@ -11014,7 +11014,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 846, "literal": "^1.0.7", @@ -11027,7 +11027,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 847, "literal": "^1.2.1", @@ -11040,7 +11040,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 848, "literal": "^1.23.3", @@ -11053,7 +11053,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 849, "literal": "^1.3.0", @@ -11066,7 +11066,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 850, "literal": "^1.0.2", @@ -11079,7 +11079,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 851, "literal": "^1.0.2", @@ -11092,7 +11092,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 852, "literal": "^1.2.0", @@ -11105,7 +11105,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 853, "literal": "^1.22.1", @@ -11118,7 +11118,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 854, "literal": "^1.0.0", @@ -11131,7 +11131,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 855, "literal": "^1.0.7", @@ -11144,7 +11144,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 856, "literal": "^1.2.1", @@ -11157,7 +11157,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 857, "literal": "^1.23.2", @@ -11170,7 +11170,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 858, "literal": "^1.3.0", @@ -11183,7 +11183,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 859, "literal": "^1.0.0", @@ -11196,7 +11196,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 860, "literal": "^1.0.2", @@ -11209,7 +11209,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 861, "literal": "~8.5.10", @@ -11222,7 +11222,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 862, "literal": "~20.12.8", @@ -11235,7 +11235,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 863, "literal": "*", @@ -11248,7 +11248,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 864, "literal": "^4.21.10", @@ -11261,7 +11261,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 865, "literal": "^1.0.30001538", @@ -11274,7 +11274,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 866, "literal": "^4.3.6", @@ -11287,7 +11287,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 867, "literal": "^0.1.2", @@ -11300,7 +11300,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 868, "literal": "^1.0.0", @@ -11313,7 +11313,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 869, "literal": "^4.2.0", @@ -11339,7 +11339,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 871, "literal": "^1.0.30001587", @@ -11352,7 +11352,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 872, "literal": "^1.4.668", @@ -11365,7 +11365,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 873, "literal": "^2.0.14", @@ -11378,7 +11378,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 874, "literal": "^1.0.13", @@ -11391,7 +11391,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 875, "literal": "^3.1.2", @@ -11404,7 +11404,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 876, "literal": "^1.0.1", @@ -11430,7 +11430,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 878, "literal": "*", @@ -11443,7 +11443,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 879, "literal": "*", @@ -11456,7 +11456,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 880, "literal": "*", @@ -11469,7 +11469,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 881, "literal": "^3.0.2", @@ -20772,7 +20772,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 374, }, "array-includes": { - "id": 806, + "id": 445, "package_id": 384, }, "array-union": { @@ -20792,7 +20792,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 382, }, "array.prototype.flatmap": { - "id": 808, + "id": 448, "package_id": 380, }, "array.prototype.toreversed": { @@ -21068,11 +21068,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 316, }, "es-errors": { - "id": 858, + "id": 690, "package_id": 312, }, "es-iterator-helpers": { - "id": 812, + "id": 740, "package_id": 409, }, "es-object-atoms": { @@ -21084,7 +21084,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 369, }, "es-shim-unscopables": { - "id": 860, + "id": 692, "package_id": 381, }, "es-to-primitive": { @@ -21120,7 +21120,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 302, }, "eslint-module-utils": { - "id": 452, + "id": 438, "package_id": 376, }, "eslint-plugin-import": { @@ -21164,7 +21164,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 279, }, "estraverse": { - "id": 432, + "id": 415, "package_id": 166, }, "esutils": { @@ -21248,7 +21248,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 51, }, "function-bind": { - "id": 761, + "id": 55, "package_id": 21, }, "function.prototype.name": { @@ -21412,7 +21412,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 329, }, "is-core-module": { - "id": 454, + "id": 675, "package_id": 19, }, "is-data-view": { @@ -21556,7 +21556,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 174, }, "jsx-ast-utils": { - "id": 814, + "id": 742, "package_id": 408, }, "keyv": { @@ -21680,11 +21680,11 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 355, }, "object.entries": { - "id": 816, + "id": 745, "package_id": 405, }, "object.fromentries": { - "id": 817, + "id": 457, "package_id": 375, }, "object.groupby": { @@ -21696,7 +21696,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 434, }, "object.values": { - "id": 819, + "id": 459, "package_id": 310, }, "once": { @@ -21924,7 +21924,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "package_id": 112, }, "semver": { - "id": 822, + "id": 460, "package_id": 309, }, "set-function-length": { @@ -22271,18 +22271,14 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "dependencies": { - "doctrine": { - "id": 811, - "package_id": 379, - }, - "resolve": { - "id": 821, - "package_id": 431, + "debug": { + "id": 674, + "package_id": 377, }, }, "depth": 1, "id": 4, - "path": "node_modules/eslint-plugin-react/node_modules", + "path": "node_modules/eslint-import-resolver-node/node_modules", }, { "dependencies": { @@ -22301,14 +22297,18 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, { "dependencies": { - "debug": { - "id": 674, - "package_id": 377, + "doctrine": { + "id": 811, + "package_id": 379, + }, + "resolve": { + "id": 821, + "package_id": 431, }, }, "depth": 1, "id": 6, - "path": "node_modules/eslint-import-resolver-node/node_modules", + "path": "node_modules/eslint-plugin-react/node_modules", }, { "dependencies": { @@ -22358,17 +22358,6 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, - { - "dependencies": { - "debug": { - "id": 672, - "package_id": 377, - }, - }, - "depth": 1, - "id": 11, - "path": "node_modules/eslint-module-utils/node_modules", - }, { "dependencies": { "minimatch": { @@ -22377,7 +22366,7 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/glob/node_modules", }, { @@ -22392,9 +22381,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, + { + "dependencies": { + "debug": { + "id": 672, + "package_id": 377, + }, + }, + "depth": 1, + "id": 13, + "path": "node_modules/eslint-module-utils/node_modules", + }, { "dependencies": { "lru-cache": { @@ -22439,17 +22439,6 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` "id": 17, "path": "node_modules/path-scurry/node_modules", }, - { - "dependencies": { - "lru-cache": { - "id": 165, - "package_id": 117, - }, - }, - "depth": 2, - "id": 18, - "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", - }, { "dependencies": { "brace-expansion": { @@ -22458,9 +22447,20 @@ exports[`hot reloading works on the client (+ tailwind hmr) 1`] = ` }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, + { + "dependencies": { + "lru-cache": { + "id": 165, + "package_id": 117, + }, + }, + "depth": 2, + "id": 19, + "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", + }, { "dependencies": { "@types/node": { diff --git a/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap b/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap index c23871f444..fe8eb9c7d8 100644 --- a/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap +++ b/test/integration/next-pages/test/__snapshots__/next-build.test.ts.snap @@ -5,7 +5,7 @@ exports[`next build works: bun 1`] = ` "dependencies": [ { "behavior": { - "normal": true, + "prod": true, }, "id": 0, "literal": "20.7.0", @@ -18,7 +18,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 1, "literal": "18.2.22", @@ -31,7 +31,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 2, "literal": "18.2.7", @@ -44,7 +44,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 3, "literal": "10.4.16", @@ -57,7 +57,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 4, "literal": "^1.0.3", @@ -70,7 +70,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 5, "literal": "8.50.0", @@ -83,7 +83,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 6, "literal": "14.1.3", @@ -96,7 +96,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 7, "literal": "14.1.3", @@ -109,7 +109,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 8, "literal": "8.4.30", @@ -122,7 +122,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 9, "literal": "22.12.0", @@ -135,7 +135,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 10, "literal": "18.2.0", @@ -148,7 +148,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 11, "literal": "18.2.0", @@ -161,7 +161,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 12, "literal": "3.3.3", @@ -174,7 +174,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 13, "literal": "5.2.2", @@ -187,7 +187,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 14, "literal": "^5.0.2", @@ -200,7 +200,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 15, "literal": "^1.1.3", @@ -213,7 +213,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 16, "literal": "^1.18.2", @@ -226,7 +226,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 17, "literal": "^4.0.3", @@ -239,7 +239,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 18, "literal": "^8.4.23", @@ -252,7 +252,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 19, "literal": "^1.22.2", @@ -265,7 +265,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 20, "literal": "^3.32.0", @@ -278,7 +278,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 21, "literal": "^3.5.3", @@ -291,7 +291,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 22, "literal": "^3.2.12", @@ -304,7 +304,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 23, "literal": "^2.1.0", @@ -317,7 +317,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 24, "literal": "^1.2.2", @@ -330,7 +330,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 25, "literal": "^4.0.5", @@ -343,7 +343,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 26, "literal": "^1.0.0", @@ -356,7 +356,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 27, "literal": "^4.0.1", @@ -369,7 +369,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 28, "literal": "^6.0.2", @@ -382,7 +382,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 29, "literal": "^3.0.0", @@ -395,7 +395,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 30, "literal": "^3.0.0", @@ -408,7 +408,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 31, "literal": "^15.1.0", @@ -421,7 +421,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 32, "literal": "^6.0.1", @@ -434,7 +434,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 33, "literal": "^5.2.0", @@ -447,7 +447,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 34, "literal": "^4.0.1", @@ -460,7 +460,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 35, "literal": "^6.0.11", @@ -473,7 +473,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 36, "literal": "^3.0.0", @@ -486,7 +486,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 37, "literal": "^1.0.2", @@ -499,7 +499,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 38, "literal": "^2.3.4", @@ -512,7 +512,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 39, "literal": "^3.0.0", @@ -553,7 +553,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 42, "literal": "^3.3.6", @@ -566,7 +566,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 43, "literal": "^1.0.0", @@ -579,7 +579,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 44, "literal": "^1.0.2", @@ -592,7 +592,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 45, "literal": "^6.0.11", @@ -618,7 +618,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 47, "literal": "^4.0.0", @@ -631,7 +631,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 48, "literal": "^1.0.0", @@ -644,7 +644,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 49, "literal": "^1.1.7", @@ -670,7 +670,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 51, "literal": "^2.13.0", @@ -683,7 +683,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 52, "literal": "^1.0.7", @@ -696,7 +696,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 53, "literal": "^1.0.0", @@ -709,7 +709,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 54, "literal": "^2.0.0", @@ -722,7 +722,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 55, "literal": "^1.1.2", @@ -735,7 +735,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 56, "literal": "^2.3.0", @@ -748,7 +748,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 57, "literal": "^4.0.3", @@ -761,7 +761,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 58, "literal": "^2.1.1", @@ -774,7 +774,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 59, "literal": "^2.0.1", @@ -800,7 +800,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 61, "literal": "^3.0.3", @@ -813,7 +813,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 62, "literal": "^2.3.1", @@ -826,7 +826,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 63, "literal": "^7.1.1", @@ -839,7 +839,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 64, "literal": "^5.0.1", @@ -852,7 +852,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 65, "literal": "^7.0.0", @@ -865,7 +865,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 66, "literal": "^2.0.2", @@ -878,7 +878,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 67, "literal": "^1.2.3", @@ -891,7 +891,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 68, "literal": "^5.1.2", @@ -904,7 +904,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 69, "literal": "^1.3.0", @@ -917,7 +917,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 70, "literal": "^4.0.4", @@ -930,7 +930,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 71, "literal": "^4.0.1", @@ -943,7 +943,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 72, "literal": "2.1.5", @@ -956,7 +956,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 73, "literal": "^1.6.0", @@ -969,7 +969,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 74, "literal": "^1.0.4", @@ -982,7 +982,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 75, "literal": "2.0.5", @@ -995,7 +995,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 76, "literal": "^1.1.9", @@ -1008,7 +1008,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 77, "literal": "^1.2.2", @@ -1021,7 +1021,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 78, "literal": "~3.1.2", @@ -1034,7 +1034,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 79, "literal": "~3.0.2", @@ -1047,7 +1047,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 80, "literal": "~5.1.2", @@ -1060,7 +1060,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 81, "literal": "~2.1.0", @@ -1073,7 +1073,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 82, "literal": "~4.0.1", @@ -1086,7 +1086,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 83, "literal": "~3.0.0", @@ -1099,7 +1099,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 84, "literal": "~3.6.0", @@ -1125,7 +1125,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 86, "literal": "^2.2.1", @@ -1138,7 +1138,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 87, "literal": "^2.0.0", @@ -1151,7 +1151,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 88, "literal": "^3.0.0", @@ -1164,7 +1164,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 89, "literal": "^2.0.4", @@ -1177,7 +1177,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 90, "literal": "^0.3.2", @@ -1190,7 +1190,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 91, "literal": "^4.0.0", @@ -1203,7 +1203,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 92, "literal": "^10.3.10", @@ -1216,7 +1216,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 93, "literal": "^1.1.6", @@ -1229,7 +1229,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 94, "literal": "^2.7.0", @@ -1242,7 +1242,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 95, "literal": "^4.0.1", @@ -1255,7 +1255,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 96, "literal": "^0.1.9", @@ -1268,7 +1268,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 97, "literal": "^1.0.0", @@ -1281,7 +1281,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 98, "literal": "^4.0.1", @@ -1294,7 +1294,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 99, "literal": "^1.0.0", @@ -1307,7 +1307,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 100, "literal": ">= 3.1.0 < 4", @@ -1320,7 +1320,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 101, "literal": "^1.0.0", @@ -1333,7 +1333,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 102, "literal": "^3.1.0", @@ -1346,7 +1346,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 103, "literal": "^2.3.5", @@ -1359,7 +1359,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 104, "literal": "^9.0.1", @@ -1372,7 +1372,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 105, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -1385,7 +1385,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 106, "literal": "^1.10.1", @@ -1398,7 +1398,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 107, "literal": "^10.2.0", @@ -1411,7 +1411,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 108, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -1424,7 +1424,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 109, "literal": "^2.0.1", @@ -1437,7 +1437,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 110, "literal": "^1.0.0", @@ -1450,7 +1450,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 111, "literal": "^8.0.2", @@ -1476,7 +1476,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 113, "literal": "^5.1.2", @@ -1489,7 +1489,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 114, "is_alias": true, @@ -1503,7 +1503,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 115, "literal": "^7.0.1", @@ -1516,7 +1516,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 116, "is_alias": true, @@ -1530,7 +1530,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 117, "literal": "^8.1.0", @@ -1543,7 +1543,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 118, "is_alias": true, @@ -1557,7 +1557,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 119, "literal": "^4.0.0", @@ -1570,7 +1570,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 120, "literal": "^4.1.0", @@ -1583,7 +1583,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 121, "literal": "^6.0.0", @@ -1596,7 +1596,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 122, "literal": "^5.0.1", @@ -1609,7 +1609,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 123, "literal": "^8.0.0", @@ -1622,7 +1622,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 124, "literal": "^3.0.0", @@ -1635,7 +1635,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 125, "literal": "^6.0.1", @@ -1648,7 +1648,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 126, "literal": "^2.0.1", @@ -1661,7 +1661,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 127, "literal": "~1.1.4", @@ -1674,7 +1674,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 128, "literal": "^6.1.0", @@ -1687,7 +1687,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 129, "literal": "^5.0.1", @@ -1700,7 +1700,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 130, "literal": "^7.0.1", @@ -1713,7 +1713,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 131, "literal": "^6.0.1", @@ -1726,7 +1726,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 132, "literal": "^0.2.0", @@ -1739,7 +1739,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 133, "literal": "^9.2.2", @@ -1752,7 +1752,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 134, "literal": "^7.0.1", @@ -1765,7 +1765,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 135, "literal": "^7.0.0", @@ -1778,7 +1778,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 136, "literal": "^4.0.1", @@ -1791,7 +1791,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 137, "literal": "^3.1.0", @@ -1804,7 +1804,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 138, "literal": "^2.0.0", @@ -1817,7 +1817,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 139, "literal": "^2.0.1", @@ -1830,7 +1830,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 140, "literal": "^2.0.0", @@ -1843,7 +1843,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 141, "literal": "^3.0.0", @@ -1856,7 +1856,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 142, "literal": "^1.2.1", @@ -1869,7 +1869,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 143, "literal": "^1.4.10", @@ -1882,7 +1882,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 144, "literal": "^0.3.24", @@ -1895,7 +1895,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 145, "literal": "^3.1.0", @@ -1908,7 +1908,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 146, "literal": "^1.4.14", @@ -1921,7 +1921,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 147, "literal": "^0.23.0", @@ -1934,7 +1934,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 148, "literal": "^1.1.0", @@ -1960,7 +1960,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 150, "literal": "^1.1.0", @@ -1973,7 +1973,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 151, "literal": "^3.0.0 || ^4.0.0", @@ -1986,7 +1986,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 152, "literal": "^1.1.0", @@ -1999,7 +1999,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 153, "literal": "9.0.0", @@ -2012,7 +2012,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 154, "literal": "22.12.0", @@ -2025,7 +2025,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 155, "literal": "2.2.3", @@ -2038,7 +2038,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 156, "literal": "0.0.1299070", @@ -2051,7 +2051,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 157, "literal": "4.3.4", @@ -2064,7 +2064,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 158, "literal": "2.0.1", @@ -2077,7 +2077,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 159, "literal": "2.0.3", @@ -2090,7 +2090,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 160, "literal": "6.4.0", @@ -2103,7 +2103,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 161, "literal": "3.0.5", @@ -2116,7 +2116,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 162, "literal": "1.4.3", @@ -2129,7 +2129,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 163, "literal": "17.7.2", @@ -2142,7 +2142,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 164, "literal": "7.6.0", @@ -2155,7 +2155,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 165, "literal": "^6.0.0", @@ -2168,7 +2168,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 166, "literal": "^4.0.0", @@ -2181,7 +2181,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 167, "literal": "^8.0.1", @@ -2194,7 +2194,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 168, "literal": "^3.1.1", @@ -2207,7 +2207,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 169, "literal": "^2.0.5", @@ -2220,7 +2220,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 170, "literal": "^2.1.1", @@ -2233,7 +2233,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 171, "literal": "^4.2.3", @@ -2246,7 +2246,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 172, "literal": "^5.0.5", @@ -2259,7 +2259,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 173, "literal": "^21.1.1", @@ -2272,7 +2272,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 174, "literal": "^4.2.0", @@ -2285,7 +2285,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 175, "literal": "^6.0.1", @@ -2298,7 +2298,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 176, "literal": "^7.0.0", @@ -2311,7 +2311,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 177, "literal": "^5.2.1", @@ -2324,7 +2324,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 178, "literal": "^2.3.8", @@ -2337,7 +2337,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 179, "literal": "^1.3.1", @@ -2350,7 +2350,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 180, "literal": "^1.1.13", @@ -2363,7 +2363,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 181, "literal": "^3.0.0", @@ -2376,7 +2376,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 182, "literal": "^3.1.5", @@ -2415,7 +2415,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 185, "literal": "^2.1.0", @@ -2428,7 +2428,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 186, "literal": "^2.0.0", @@ -2441,7 +2441,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 187, "literal": "^2.0.0", @@ -2454,7 +2454,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 188, "literal": "^2.0.0", @@ -2467,7 +2467,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 189, "literal": "^2.18.0", @@ -2480,7 +2480,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 190, "literal": "^1.3.2", @@ -2493,7 +2493,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 191, "literal": "^1.0.1", @@ -2506,7 +2506,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 192, "literal": "^1.1.0", @@ -2532,7 +2532,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 194, "literal": "^1.6.4", @@ -2545,7 +2545,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 195, "literal": "^1.6.4", @@ -2558,7 +2558,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 196, "literal": "^1.2.0", @@ -2571,7 +2571,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 197, "literal": "^2.15.0", @@ -2584,7 +2584,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 198, "literal": "^1.1.0", @@ -2597,7 +2597,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 199, "literal": "^1.3.1", @@ -2610,7 +2610,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 200, "literal": "1", @@ -2623,7 +2623,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 201, "literal": "^1.4.0", @@ -2636,7 +2636,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 202, "literal": "^7.0.2", @@ -2649,7 +2649,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 203, "literal": "^4.3.4", @@ -2662,7 +2662,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 204, "literal": "^7.0.1", @@ -2675,7 +2675,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 205, "literal": "^7.0.3", @@ -2688,7 +2688,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 206, "literal": "^7.14.1", @@ -2701,7 +2701,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 207, "literal": "^7.0.1", @@ -2714,7 +2714,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 208, "literal": "^1.1.0", @@ -2727,7 +2727,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 209, "literal": "^8.0.2", @@ -2740,7 +2740,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 210, "literal": "^7.1.1", @@ -2753,7 +2753,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 211, "literal": "^4.3.4", @@ -2766,7 +2766,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 212, "literal": "^2.7.1", @@ -2779,7 +2779,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 213, "literal": "^9.0.5", @@ -2792,7 +2792,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 214, "literal": "^4.2.0", @@ -2805,7 +2805,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 215, "literal": "1.1.0", @@ -2818,7 +2818,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 216, "literal": "^1.1.3", @@ -2831,7 +2831,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 217, "literal": "2.1.2", @@ -2844,7 +2844,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 218, "literal": "^4.3.4", @@ -2857,7 +2857,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 219, "literal": "^0.23.0", @@ -2870,7 +2870,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 220, "literal": "^7.0.2", @@ -2883,7 +2883,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 221, "literal": "^4.3.4", @@ -2896,7 +2896,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 222, "literal": "^6.0.1", @@ -2909,7 +2909,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 223, "literal": "^7.0.0", @@ -2922,7 +2922,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 224, "literal": "^7.0.2", @@ -2935,7 +2935,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 225, "literal": "^7.0.0", @@ -2948,7 +2948,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 226, "literal": "^8.0.2", @@ -2961,7 +2961,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 227, "literal": "^5.0.0", @@ -2974,7 +2974,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 228, "literal": "^2.0.2", @@ -2987,7 +2987,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 229, "literal": "^0.13.4", @@ -3000,7 +3000,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 230, "literal": "^2.1.0", @@ -3013,7 +3013,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 231, "literal": "^4.0.1", @@ -3026,7 +3026,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 232, "literal": "^5.2.0", @@ -3039,7 +3039,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 233, "literal": "^2.0.2", @@ -3052,7 +3052,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 234, "literal": "^4.0.1", @@ -3078,7 +3078,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 236, "literal": "^2.0.1", @@ -3091,7 +3091,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 237, "literal": "^7.0.2", @@ -3104,7 +3104,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 238, "literal": "4", @@ -3117,7 +3117,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 239, "literal": "^7.1.0", @@ -3130,7 +3130,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 240, "literal": "^4.3.4", @@ -3143,7 +3143,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 241, "literal": "^5.0.2", @@ -3156,7 +3156,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 242, "literal": "^6.0.2", @@ -3169,7 +3169,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 243, "literal": "^4.3.4", @@ -3182,7 +3182,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 244, "literal": "^11.2.0", @@ -3195,7 +3195,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 245, "literal": "^4.2.0", @@ -3208,7 +3208,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 246, "literal": "^6.0.1", @@ -3221,7 +3221,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 247, "literal": "^2.0.0", @@ -3234,7 +3234,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 248, "literal": "^2.0.0", @@ -3260,7 +3260,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 250, "literal": "^4.1.1", @@ -3273,7 +3273,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 251, "literal": "^5.1.0", @@ -3286,7 +3286,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 252, "literal": "^2.10.0", @@ -3312,7 +3312,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 254, "literal": "*", @@ -3325,7 +3325,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 255, "literal": "~5.26.4", @@ -3338,7 +3338,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 256, "literal": "~1.1.0", @@ -3351,7 +3351,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 257, "literal": "~0.2.3", @@ -3364,7 +3364,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 258, "literal": "~1.2.0", @@ -3377,7 +3377,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 259, "literal": "^3.0.0", @@ -3390,7 +3390,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 260, "literal": "2.1.2", @@ -3403,7 +3403,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 261, "literal": "2.2.3", @@ -3416,7 +3416,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 262, "literal": "0.5.24", @@ -3429,7 +3429,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 263, "literal": "4.3.5", @@ -3442,7 +3442,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 264, "literal": "0.0.1299070", @@ -3455,7 +3455,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 265, "literal": "8.17.1", @@ -3496,7 +3496,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 268, "literal": "3.0.1", @@ -3509,7 +3509,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 269, "literal": "10.0.0", @@ -3522,7 +3522,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 270, "literal": "3.23.8", @@ -3548,7 +3548,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 272, "literal": "^2.2.1", @@ -3561,7 +3561,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 273, "literal": "^3.3.0", @@ -3574,7 +3574,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 274, "literal": "^4.1.0", @@ -3587,7 +3587,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 275, "literal": "^5.2.0", @@ -3614,7 +3614,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 277, "literal": "^7.0.0", @@ -3627,7 +3627,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 278, "literal": "^1.3.1", @@ -3640,7 +3640,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 279, "literal": "^2.3.0", @@ -3653,7 +3653,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 280, "literal": "^1.1.6", @@ -3666,7 +3666,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 281, "literal": "^0.2.1", @@ -3679,7 +3679,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 282, "literal": "^7.24.7", @@ -3692,7 +3692,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 283, "literal": "^1.0.0", @@ -3705,7 +3705,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 284, "literal": "^7.24.7", @@ -3718,7 +3718,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 285, "literal": "^2.4.2", @@ -3731,7 +3731,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 286, "literal": "^4.0.0", @@ -3744,7 +3744,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 287, "literal": "^1.0.0", @@ -3757,7 +3757,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 288, "literal": "^3.2.1", @@ -3770,7 +3770,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 289, "literal": "^1.0.5", @@ -3783,7 +3783,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 290, "literal": "^5.3.0", @@ -3796,7 +3796,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 291, "literal": "^3.0.0", @@ -3809,7 +3809,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 292, "literal": "^1.9.0", @@ -3822,7 +3822,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 293, "literal": "1.1.3", @@ -3835,7 +3835,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 294, "literal": "^2.0.1", @@ -3848,7 +3848,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 295, "literal": "^1.0.0", @@ -3861,7 +3861,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 296, "literal": "^4.0.0", @@ -3874,7 +3874,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 297, "literal": "^3.0.0", @@ -3887,7 +3887,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 298, "literal": "1.6.0", @@ -3900,7 +3900,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 299, "literal": "8.4.31", @@ -3913,7 +3913,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 300, "literal": "14.1.3", @@ -3926,7 +3926,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 301, "literal": "5.1.1", @@ -3939,7 +3939,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 302, "literal": "^4.2.11", @@ -3952,7 +3952,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 303, "literal": "0.5.2", @@ -3965,7 +3965,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 304, "literal": "^1.0.30001579", @@ -4149,7 +4149,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 318, "literal": "^2.4.0", @@ -4162,7 +4162,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 319, "literal": "0.0.1", @@ -4188,7 +4188,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 321, "literal": "^3.3.6", @@ -4201,7 +4201,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 322, "literal": "^1.0.0", @@ -4214,7 +4214,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 323, "literal": "^1.0.2", @@ -4227,7 +4227,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 324, "literal": "^1.1.0", @@ -4240,7 +4240,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 325, "literal": "^7.33.2", @@ -4253,7 +4253,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 326, "literal": "^2.28.1", @@ -4266,7 +4266,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 327, "literal": "^6.7.1", @@ -4279,7 +4279,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 328, "literal": "^1.3.3", @@ -4292,7 +4292,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 329, "literal": "14.1.3", @@ -4305,7 +4305,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 330, "literal": "^5.4.2 || ^6.0.0", @@ -4318,7 +4318,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", @@ -4331,7 +4331,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 332, "literal": "^0.3.6", @@ -4344,7 +4344,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 333, "literal": "^3.5.2", @@ -4384,7 +4384,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 336, "literal": "^6.12.4", @@ -4397,7 +4397,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 337, "literal": "^0.4.1", @@ -4410,7 +4410,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 338, "literal": "^4.0.0", @@ -4423,7 +4423,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 339, "literal": "^4.3.2", @@ -4436,7 +4436,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 340, "literal": "^9.6.1", @@ -4449,7 +4449,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 341, "literal": "^5.2.0", @@ -4462,7 +4462,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 342, "literal": "^1.4.2", @@ -4475,7 +4475,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 343, "literal": "^2.0.2", @@ -4488,7 +4488,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 344, "literal": "^5.0.0", @@ -4501,7 +4501,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 345, "literal": "^13.19.0", @@ -4514,7 +4514,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 346, "literal": "^4.0.0", @@ -4527,7 +4527,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 347, "literal": "^4.1.0", @@ -4540,7 +4540,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 348, "literal": "^3.0.0", @@ -4553,7 +4553,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 349, "literal": "^1.4.0", @@ -4566,7 +4566,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 350, "literal": "^3.1.2", @@ -4579,7 +4579,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 351, "literal": "8.50.0", @@ -4592,7 +4592,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 352, "literal": "^0.9.3", @@ -4605,7 +4605,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 353, "literal": "^6.0.1", @@ -4618,7 +4618,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 354, "literal": "^0.2.0", @@ -4631,7 +4631,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 355, "literal": "^7.0.2", @@ -4644,7 +4644,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 356, "literal": "^6.0.2", @@ -4657,7 +4657,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 357, "literal": "^0.1.4", @@ -4670,7 +4670,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 358, "literal": "^7.2.2", @@ -4683,7 +4683,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 359, "literal": "^4.6.2", @@ -4696,7 +4696,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 360, "literal": "^3.0.3", @@ -4709,7 +4709,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 361, "literal": "^3.1.3", @@ -4722,7 +4722,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 362, "literal": "^1.4.0", @@ -4735,7 +4735,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 363, "literal": "^2.1.2", @@ -4748,7 +4748,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 364, "literal": "^1.2.8", @@ -4761,7 +4761,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 365, "literal": "^6.0.1", @@ -4774,7 +4774,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 366, "literal": "^3.4.3", @@ -4787,7 +4787,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 367, "literal": "^4.0.0", @@ -4800,7 +4800,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 368, "literal": "^4.6.1", @@ -4813,7 +4813,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 369, "literal": "^0.11.11", @@ -4826,7 +4826,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 370, "literal": "^4.2.0", @@ -4839,7 +4839,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 371, "literal": "^1.0.1", @@ -4852,7 +4852,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 372, "literal": "^1.0.1", @@ -4865,7 +4865,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 373, "literal": "^3.3.0", @@ -4891,7 +4891,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 375, "literal": "^2.0.2", @@ -4904,7 +4904,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 376, "literal": "^4.3.1", @@ -4917,7 +4917,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 377, "literal": "^3.0.5", @@ -4930,7 +4930,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 378, "literal": "^1.1.7", @@ -4943,7 +4943,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 379, "literal": "^1.0.0", @@ -4956,7 +4956,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 380, "literal": "0.0.1", @@ -4969,7 +4969,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 381, "literal": "^3.0.4", @@ -4982,7 +4982,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 382, "literal": "^3.2.9", @@ -4995,7 +4995,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 383, "literal": "^4.5.3", @@ -5008,7 +5008,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 384, "literal": "^3.0.2", @@ -5021,7 +5021,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 385, "literal": "^7.1.3", @@ -5034,7 +5034,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 386, "literal": "^1.0.0", @@ -5047,7 +5047,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 387, "literal": "^1.0.4", @@ -5060,7 +5060,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 388, "literal": "2", @@ -5073,7 +5073,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 389, "literal": "^3.1.1", @@ -5086,7 +5086,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 390, "literal": "^1.3.0", @@ -5099,7 +5099,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 391, "literal": "^1.0.0", @@ -5112,7 +5112,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 392, "literal": "^1.3.0", @@ -5125,7 +5125,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 393, "literal": "1", @@ -5138,7 +5138,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 394, "literal": "3.0.1", @@ -5151,7 +5151,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 395, "literal": "^6.12.4", @@ -5164,7 +5164,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 396, "literal": "^4.3.2", @@ -5177,7 +5177,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 397, "literal": "^9.6.0", @@ -5190,7 +5190,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 398, "literal": "^13.19.0", @@ -5203,7 +5203,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 399, "literal": "^5.2.0", @@ -5216,7 +5216,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 400, "literal": "^3.2.1", @@ -5229,7 +5229,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 401, "literal": "^4.1.0", @@ -5242,7 +5242,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 402, "literal": "^3.1.2", @@ -5255,7 +5255,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 403, "literal": "^3.1.1", @@ -5268,7 +5268,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 404, "literal": "^0.20.2", @@ -5281,7 +5281,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 405, "literal": "^8.9.0", @@ -5294,7 +5294,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 406, "literal": "^5.3.2", @@ -5307,7 +5307,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 407, "literal": "^3.4.1", @@ -5333,7 +5333,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 409, "literal": "^3.1.1", @@ -5346,7 +5346,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 410, "literal": "^2.0.0", @@ -5359,7 +5359,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 411, "literal": "^0.4.1", @@ -5372,7 +5372,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 412, "literal": "^4.2.2", @@ -5385,7 +5385,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 413, "literal": "^2.1.0", @@ -5398,7 +5398,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 414, "literal": "^4.3.0", @@ -5411,7 +5411,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 415, "literal": "^5.2.0", @@ -5424,7 +5424,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 416, "literal": "^5.2.0", @@ -5437,7 +5437,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 417, "literal": "^1.2.1", @@ -5450,7 +5450,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 418, "literal": "^0.1.3", @@ -5463,7 +5463,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 419, "literal": "^1.2.5", @@ -5476,7 +5476,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 420, "literal": "^0.4.0", @@ -5489,7 +5489,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 421, "literal": "^0.4.1", @@ -5502,7 +5502,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 422, "literal": "^2.0.6", @@ -5515,7 +5515,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 423, "literal": "^1.2.1", @@ -5528,7 +5528,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 424, "literal": "~0.4.0", @@ -5541,7 +5541,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 425, "literal": "^1.2.1", @@ -5554,7 +5554,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 426, "literal": "^2.0.2", @@ -5567,7 +5567,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 427, "literal": "^6.0.0", @@ -5580,7 +5580,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 428, "literal": "^4.0.0", @@ -5593,7 +5593,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 429, "literal": "^5.0.0", @@ -5606,7 +5606,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 430, "literal": "^3.0.2", @@ -5619,7 +5619,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 431, "literal": "^0.1.0", @@ -5632,7 +5632,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 432, "literal": "^5.1.0", @@ -5645,7 +5645,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 433, "literal": "^4.1.0", @@ -5658,7 +5658,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 434, "literal": "^7.1.0", @@ -5671,7 +5671,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 435, "literal": "^4.0.0", @@ -5684,7 +5684,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 436, "literal": "^4.3.4", @@ -5697,7 +5697,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 437, "literal": "^5.12.0", @@ -5710,7 +5710,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 438, "literal": "^2.7.4", @@ -5723,7 +5723,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 439, "literal": "^3.3.1", @@ -5736,7 +5736,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 440, "literal": "^4.5.0", @@ -5749,7 +5749,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 441, "literal": "^2.11.0", @@ -5762,7 +5762,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 442, "literal": "^4.0.3", @@ -5801,7 +5801,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 445, "literal": "^3.1.7", @@ -5814,7 +5814,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 446, "literal": "^1.2.3", @@ -5827,7 +5827,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 447, "literal": "^1.3.2", @@ -5840,7 +5840,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 448, "literal": "^1.3.2", @@ -5853,7 +5853,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 449, "literal": "^3.2.7", @@ -5866,7 +5866,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 450, "literal": "^2.1.0", @@ -5879,7 +5879,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 451, "literal": "^0.3.9", @@ -5892,7 +5892,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 452, "literal": "^2.8.0", @@ -5905,7 +5905,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 453, "literal": "^2.0.0", @@ -5918,7 +5918,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 454, "literal": "^2.13.1", @@ -5931,7 +5931,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 455, "literal": "^4.0.3", @@ -5944,7 +5944,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 456, "literal": "^3.1.2", @@ -5957,7 +5957,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 457, "literal": "^2.0.7", @@ -5970,7 +5970,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 458, "literal": "^1.0.1", @@ -5983,7 +5983,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 459, "literal": "^1.1.7", @@ -5996,7 +5996,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 460, "literal": "^6.3.1", @@ -6009,7 +6009,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 461, "literal": "^3.15.0", @@ -6035,7 +6035,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 463, "literal": "^0.0.29", @@ -6048,7 +6048,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 464, "literal": "^1.0.2", @@ -6061,7 +6061,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 465, "literal": "^1.2.6", @@ -6074,7 +6074,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 466, "literal": "^3.0.0", @@ -6087,7 +6087,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 467, "literal": "^1.2.0", @@ -6100,7 +6100,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 468, "literal": "^1.0.7", @@ -6113,7 +6113,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 469, "literal": "^1.2.1", @@ -6126,7 +6126,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 470, "literal": "^1.0.0", @@ -6139,7 +6139,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 471, "literal": "^1.3.0", @@ -6152,7 +6152,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 472, "literal": "^1.0.1", @@ -6165,7 +6165,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 473, "literal": "^1.0.0", @@ -6178,7 +6178,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 474, "literal": "^1.1.1", @@ -6191,7 +6191,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 475, "literal": "^1.0.0", @@ -6204,7 +6204,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 476, "literal": "^1.2.4", @@ -6217,7 +6217,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 477, "literal": "^1.3.0", @@ -6230,7 +6230,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 478, "literal": "^1.1.2", @@ -6243,7 +6243,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 479, "literal": "^1.0.1", @@ -6256,7 +6256,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 480, "literal": "^1.0.3", @@ -6269,7 +6269,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 481, "literal": "^2.0.0", @@ -6282,7 +6282,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 482, "literal": "^1.0.0", @@ -6295,7 +6295,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 483, "literal": "^1.3.0", @@ -6308,7 +6308,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 484, "literal": "^1.0.1", @@ -6321,7 +6321,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 485, "literal": "^1.1.3", @@ -6334,7 +6334,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 486, "literal": "^1.0.0", @@ -6347,7 +6347,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 487, "literal": "^1.3.0", @@ -6360,7 +6360,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 488, "literal": "^1.1.2", @@ -6373,7 +6373,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 489, "literal": "^1.2.4", @@ -6386,7 +6386,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 490, "literal": "^1.2.1", @@ -6399,7 +6399,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 491, "literal": "^1.1.4", @@ -6412,7 +6412,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 492, "literal": "^1.3.0", @@ -6425,7 +6425,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 493, "literal": "^1.1.2", @@ -6438,7 +6438,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 494, "literal": "^1.2.4", @@ -6451,7 +6451,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 495, "literal": "^1.0.1", @@ -6464,7 +6464,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 496, "literal": "^1.0.2", @@ -6477,7 +6477,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 497, "literal": "^1.0.7", @@ -6490,7 +6490,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 498, "literal": "^1.2.1", @@ -6503,7 +6503,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 499, "literal": "^1.23.2", @@ -6516,7 +6516,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 500, "literal": "^1.0.1", @@ -6529,7 +6529,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 501, "literal": "^1.0.3", @@ -6542,7 +6542,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 502, "literal": "^1.0.7", @@ -6555,7 +6555,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 503, "literal": "^1.0.7", @@ -6568,7 +6568,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 504, "literal": "^1.0.1", @@ -6581,7 +6581,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 505, "literal": "^1.0.1", @@ -6594,7 +6594,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 506, "literal": "^1.0.0", @@ -6607,7 +6607,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 507, "literal": "^1.0.0", @@ -6620,7 +6620,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 508, "literal": "^1.3.0", @@ -6633,7 +6633,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 509, "literal": "^1.0.0", @@ -6646,7 +6646,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 510, "literal": "^2.0.3", @@ -6659,7 +6659,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 511, "literal": "^1.2.1", @@ -6672,7 +6672,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 512, "literal": "^1.1.6", @@ -6685,7 +6685,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 513, "literal": "^1.2.4", @@ -6698,7 +6698,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 514, "literal": "^1.0.2", @@ -6711,7 +6711,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 515, "literal": "^1.0.3", @@ -6724,7 +6724,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 516, "literal": "^1.0.1", @@ -6737,7 +6737,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 517, "literal": "^1.0.2", @@ -6750,7 +6750,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 518, "literal": "^1.0.3", @@ -6763,7 +6763,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 519, "literal": "^1.0.3", @@ -6776,7 +6776,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 520, "literal": "^2.0.2", @@ -6789,7 +6789,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 521, "literal": "^1.0.7", @@ -6802,7 +6802,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 522, "literal": "^3.0.4", @@ -6815,7 +6815,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 523, "literal": "^1.2.7", @@ -6828,7 +6828,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 524, "literal": "^1.0.1", @@ -6841,7 +6841,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 525, "literal": "^2.0.3", @@ -6854,7 +6854,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 526, "literal": "^1.1.4", @@ -6867,7 +6867,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 527, "literal": "^1.0.3", @@ -6880,7 +6880,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 528, "literal": "^1.0.7", @@ -6893,7 +6893,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 529, "literal": "^1.1.13", @@ -6906,7 +6906,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 530, "literal": "^1.0.2", @@ -6919,7 +6919,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 531, "literal": "^1.13.1", @@ -6932,7 +6932,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 532, "literal": "^1.1.1", @@ -6945,7 +6945,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 533, "literal": "^4.1.5", @@ -6958,7 +6958,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 534, "literal": "^1.5.2", @@ -6971,7 +6971,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 535, "literal": "^1.1.2", @@ -6984,7 +6984,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 536, "literal": "^1.0.3", @@ -6997,7 +6997,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 537, "literal": "^1.2.9", @@ -7010,7 +7010,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 538, "literal": "^1.0.8", @@ -7023,7 +7023,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 539, "literal": "^1.0.8", @@ -7036,7 +7036,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 540, "literal": "^1.0.2", @@ -7049,7 +7049,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 541, "literal": "^1.0.1", @@ -7062,7 +7062,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 542, "literal": "^1.0.2", @@ -7075,7 +7075,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 543, "literal": "^1.0.6", @@ -7088,7 +7088,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 544, "literal": "^1.0.2", @@ -7101,7 +7101,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 545, "literal": "^1.1.15", @@ -7114,7 +7114,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 546, "literal": "^1.0.7", @@ -7127,7 +7127,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 547, "literal": "^1.0.7", @@ -7140,7 +7140,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 548, "literal": "^0.3.3", @@ -7153,7 +7153,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 549, "literal": "^1.0.1", @@ -7166,7 +7166,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 550, "literal": "^1.0.2", @@ -7179,7 +7179,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 551, "literal": "^1.0.3", @@ -7192,7 +7192,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 552, "literal": "^1.1.3", @@ -7205,7 +7205,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 553, "literal": "^1.0.0", @@ -7218,7 +7218,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 554, "literal": "^1.0.2", @@ -7231,7 +7231,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 555, "literal": "^1.0.2", @@ -7244,7 +7244,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 556, "literal": "^1.0.3", @@ -7257,7 +7257,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 557, "literal": "^1.0.2", @@ -7270,7 +7270,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 558, "literal": "^1.0.1", @@ -7283,7 +7283,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 559, "literal": "^1.1.0", @@ -7296,7 +7296,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 560, "literal": "^1.0.4", @@ -7309,7 +7309,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 561, "literal": "^1.0.5", @@ -7322,7 +7322,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 562, "literal": "^1.0.3", @@ -7335,7 +7335,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 563, "literal": "^1.0.2", @@ -7348,7 +7348,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 564, "literal": "^1.0.0", @@ -7361,7 +7361,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 565, "literal": "^1.0.0", @@ -7374,7 +7374,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 566, "literal": "^1.0.2", @@ -7387,7 +7387,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 567, "literal": "^1.0.0", @@ -7400,7 +7400,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 568, "literal": "^1.0.1", @@ -7413,7 +7413,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 569, "literal": "^1.0.7", @@ -7426,7 +7426,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 570, "literal": "^0.3.3", @@ -7439,7 +7439,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 571, "literal": "^1.0.1", @@ -7452,7 +7452,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 572, "literal": "^1.0.3", @@ -7465,7 +7465,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 573, "literal": "^1.1.13", @@ -7478,7 +7478,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 574, "literal": "^1.0.0", @@ -7491,7 +7491,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 575, "literal": "^1.1.14", @@ -7504,7 +7504,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 576, "literal": "^1.0.7", @@ -7517,7 +7517,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 577, "literal": "^1.0.7", @@ -7530,7 +7530,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 578, "literal": "^0.3.3", @@ -7543,7 +7543,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 579, "literal": "^1.0.1", @@ -7556,7 +7556,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 580, "literal": "^1.0.3", @@ -7569,7 +7569,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 581, "literal": "^1.1.13", @@ -7582,7 +7582,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 582, "literal": "^1.0.7", @@ -7595,7 +7595,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 583, "literal": "^0.3.3", @@ -7608,7 +7608,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 584, "literal": "^1.0.1", @@ -7621,7 +7621,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 585, "literal": "^1.0.3", @@ -7634,7 +7634,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 586, "literal": "^1.1.13", @@ -7647,7 +7647,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 587, "literal": "^1.0.7", @@ -7660,7 +7660,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 588, "literal": "^1.3.0", @@ -7673,7 +7673,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 589, "literal": "^1.1.13", @@ -7686,7 +7686,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 590, "literal": "^1.0.7", @@ -7699,7 +7699,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 591, "literal": "^1.2.1", @@ -7712,7 +7712,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 592, "literal": "^1.0.0", @@ -7725,7 +7725,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 593, "literal": "^1.0.7", @@ -7738,7 +7738,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 594, "literal": "^1.2.1", @@ -7751,7 +7751,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 595, "literal": "^1.0.0", @@ -7764,7 +7764,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 596, "literal": "^1.0.7", @@ -7777,7 +7777,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 597, "literal": "^1.2.1", @@ -7790,7 +7790,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 598, "literal": "^1.23.0", @@ -7803,7 +7803,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 599, "literal": "^1.0.0", @@ -7816,7 +7816,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 600, "literal": "^1.0.6", @@ -7829,7 +7829,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 601, "literal": "^1.3.0", @@ -7842,7 +7842,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 602, "literal": "^1.1.4", @@ -7855,7 +7855,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 603, "literal": "^1.0.2", @@ -7868,7 +7868,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 604, "literal": "^1.0.0", @@ -7881,7 +7881,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 605, "literal": "^1.0.7", @@ -7894,7 +7894,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 606, "literal": "^1.2.4", @@ -7907,7 +7907,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 607, "literal": "^1.0.3", @@ -7920,7 +7920,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 608, "literal": "^2.0.5", @@ -7933,7 +7933,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 609, "literal": "^1.0.6", @@ -7946,7 +7946,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 610, "literal": "^1.2.1", @@ -7959,7 +7959,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 611, "literal": "^1.3.0", @@ -7972,7 +7972,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 612, "literal": "^2.0.1", @@ -7985,7 +7985,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 613, "literal": "^1.1.4", @@ -7998,7 +7998,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 614, "literal": "^1.3.0", @@ -8011,7 +8011,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 615, "literal": "^1.2.3", @@ -8024,7 +8024,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 616, "literal": "^1.0.2", @@ -8037,7 +8037,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 617, "literal": "^1.0.5", @@ -8050,7 +8050,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 618, "literal": "^1.2.1", @@ -8063,7 +8063,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 619, "literal": "^1.0.3", @@ -8076,7 +8076,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 620, "literal": "^1.1.1", @@ -8089,7 +8089,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 621, "literal": "^1.0.2", @@ -8102,7 +8102,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 622, "literal": "^1.0.7", @@ -8115,7 +8115,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 623, "literal": "^1.1.13", @@ -8128,7 +8128,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 624, "literal": "^1.0.2", @@ -8141,7 +8141,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 625, "literal": "^1.2.1", @@ -8154,7 +8154,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 626, "literal": "^1.3.0", @@ -8167,7 +8167,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 627, "literal": "^2.0.0", @@ -8180,7 +8180,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 628, "literal": "^1.0.4", @@ -8193,7 +8193,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 629, "literal": "^1.0.7", @@ -8206,7 +8206,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 630, "literal": "^1.3.0", @@ -8219,7 +8219,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 631, "literal": "^1.2.4", @@ -8232,7 +8232,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 632, "literal": "^1.13.1", @@ -8245,7 +8245,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 633, "literal": "^1.2.1", @@ -8258,7 +8258,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 634, "literal": "^1.0.1", @@ -8271,7 +8271,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 635, "literal": "^1.0.5", @@ -8284,7 +8284,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 636, "literal": "^1.3.0", @@ -8297,7 +8297,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 637, "literal": "^1.2.4", @@ -8310,7 +8310,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 638, "literal": "^1.0.2", @@ -8323,7 +8323,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 639, "literal": "^1.2.0", @@ -8336,7 +8336,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 640, "literal": "^1.22.1", @@ -8349,7 +8349,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 641, "literal": "^1.2.3", @@ -8362,7 +8362,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 642, "literal": "^1.1.4", @@ -8375,7 +8375,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 643, "literal": "^1.0.1", @@ -8388,7 +8388,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 644, "literal": "^1.0.2", @@ -8401,7 +8401,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 645, "literal": "^1.0.0", @@ -8414,7 +8414,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 646, "literal": "^1.2.4", @@ -8427,7 +8427,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 647, "literal": "^1.0.2", @@ -8440,7 +8440,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 648, "literal": "^2.0.1", @@ -8453,7 +8453,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 649, "literal": "^1.0.6", @@ -8466,7 +8466,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 650, "literal": "^1.3.0", @@ -8479,7 +8479,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 651, "literal": "^1.0.1", @@ -8492,7 +8492,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 652, "literal": "^1.0.7", @@ -8505,7 +8505,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 653, "literal": "^1.3.0", @@ -8518,7 +8518,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 654, "literal": "^1.0.1", @@ -8531,7 +8531,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 655, "literal": "^1.0.6", @@ -8544,7 +8544,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 656, "literal": "^1.3.0", @@ -8557,7 +8557,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 657, "literal": "^1.0.1", @@ -8570,7 +8570,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 658, "literal": "^1.0.1", @@ -8583,7 +8583,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 659, "literal": "^1.0.5", @@ -8596,7 +8596,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 660, "literal": "^1.2.1", @@ -8609,7 +8609,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 661, "literal": "^1.22.3", @@ -8622,7 +8622,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 662, "literal": "^1.2.1", @@ -8635,7 +8635,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 663, "literal": "^1.2.3", @@ -8648,7 +8648,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 664, "literal": "^3.0.4", @@ -8661,7 +8661,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 665, "literal": "^1.0.2", @@ -8674,7 +8674,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 666, "literal": "^1.0.5", @@ -8687,7 +8687,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 667, "literal": "^3.0.4", @@ -8700,7 +8700,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 668, "literal": "^1.0.7", @@ -8713,7 +8713,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 669, "literal": "^1.2.1", @@ -8726,7 +8726,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 670, "literal": "^1.23.2", @@ -8739,7 +8739,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 671, "literal": "^1.0.0", @@ -8752,7 +8752,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 672, "literal": "^3.2.7", @@ -8765,7 +8765,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 673, "literal": "^2.1.1", @@ -8778,7 +8778,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 674, "literal": "^3.2.7", @@ -8791,7 +8791,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 675, "literal": "^2.13.0", @@ -8804,7 +8804,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 676, "literal": "^1.22.4", @@ -8817,7 +8817,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 677, "literal": "^2.0.2", @@ -8830,7 +8830,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 678, "literal": "^1.0.2", @@ -8843,7 +8843,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 679, "literal": "^1.2.0", @@ -8856,7 +8856,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 680, "literal": "^1.22.1", @@ -8869,7 +8869,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 681, "literal": "^1.0.0", @@ -8882,7 +8882,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 682, "literal": "^2.0.0", @@ -8895,7 +8895,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 683, "literal": "^1.0.2", @@ -8908,7 +8908,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 684, "literal": "^1.2.0", @@ -8921,7 +8921,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 685, "literal": "^1.22.1", @@ -8934,7 +8934,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 686, "literal": "^1.0.0", @@ -8947,7 +8947,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 687, "literal": "^1.0.7", @@ -8960,7 +8960,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 688, "literal": "^1.2.1", @@ -8973,7 +8973,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 689, "literal": "^1.23.2", @@ -8986,7 +8986,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 690, "literal": "^1.3.0", @@ -8999,7 +8999,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 691, "literal": "^1.0.0", @@ -9012,7 +9012,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 692, "literal": "^1.0.2", @@ -9025,7 +9025,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 693, "literal": "^1.0.7", @@ -9038,7 +9038,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 694, "literal": "^1.2.1", @@ -9051,7 +9051,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 695, "literal": "^1.23.2", @@ -9064,7 +9064,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 696, "literal": "^1.0.0", @@ -9077,7 +9077,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 697, "literal": "^1.2.4", @@ -9090,7 +9090,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 698, "literal": "^1.0.7", @@ -9103,7 +9103,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 699, "literal": "^1.0.0", @@ -9116,7 +9116,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 700, "literal": "^4.2.4", @@ -9129,7 +9129,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 701, "literal": "^2.2.0", @@ -9155,7 +9155,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 703, "literal": "^4.3.4", @@ -9168,7 +9168,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 704, "literal": "6.21.0", @@ -9181,7 +9181,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 705, "literal": "6.21.0", @@ -9194,7 +9194,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 706, "literal": "6.21.0", @@ -9207,7 +9207,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 707, "literal": "6.21.0", @@ -9233,7 +9233,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 709, "literal": "^4.3.4", @@ -9246,7 +9246,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 710, "literal": "^11.1.0", @@ -9259,7 +9259,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 711, "literal": "^7.5.4", @@ -9272,7 +9272,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 712, "literal": "^4.0.3", @@ -9285,7 +9285,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 713, "literal": "9.0.3", @@ -9298,7 +9298,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 714, "literal": "^1.0.1", @@ -9311,7 +9311,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 715, "literal": "6.21.0", @@ -9324,7 +9324,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 716, "literal": "6.21.0", @@ -9337,7 +9337,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 717, "literal": "^3.4.1", @@ -9350,7 +9350,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 718, "literal": "6.21.0", @@ -9376,7 +9376,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 720, "literal": "^2.0.1", @@ -9389,7 +9389,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 721, "literal": "^2.1.0", @@ -9402,7 +9402,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 722, "literal": "^3.0.1", @@ -9415,7 +9415,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 723, "literal": "^3.2.9", @@ -9428,7 +9428,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 724, "literal": "^5.2.0", @@ -9441,7 +9441,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 725, "literal": "^1.4.1", @@ -9454,7 +9454,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 726, "literal": "^3.0.0", @@ -9467,7 +9467,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 727, "literal": "^4.0.0", @@ -9480,7 +9480,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 728, "literal": "6.21.0", @@ -9493,7 +9493,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 729, "literal": "6.21.0", @@ -9506,7 +9506,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 730, "literal": "10.3.10", @@ -9519,7 +9519,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 731, "literal": "^7.23.2", @@ -9532,7 +9532,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 732, "literal": "^5.3.0", @@ -9545,7 +9545,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 733, "literal": "^3.1.7", @@ -9558,7 +9558,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 734, "literal": "^1.3.2", @@ -9571,7 +9571,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 735, "literal": "^0.0.8", @@ -9584,7 +9584,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 736, "literal": "=4.7.0", @@ -9597,7 +9597,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 737, "literal": "^3.2.1", @@ -9610,7 +9610,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 738, "literal": "^1.0.8", @@ -9623,7 +9623,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 739, "literal": "^9.2.2", @@ -9636,7 +9636,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 740, "literal": "^1.0.15", @@ -9649,7 +9649,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 741, "literal": "^2.0.0", @@ -9662,7 +9662,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 742, "literal": "^3.3.5", @@ -9675,7 +9675,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 743, "literal": "^1.0.9", @@ -9688,7 +9688,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 744, "literal": "^3.1.2", @@ -9701,7 +9701,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 745, "literal": "^1.1.7", @@ -9714,7 +9714,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 746, "literal": "^2.0.7", @@ -9740,7 +9740,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 748, "literal": "^1.0.7", @@ -9753,7 +9753,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 749, "literal": "^1.2.1", @@ -9766,7 +9766,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 750, "literal": "^1.0.0", @@ -9779,7 +9779,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 751, "literal": "^0.3.20", @@ -9792,7 +9792,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 752, "literal": "^3.1.6", @@ -9805,7 +9805,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 753, "literal": "^1.3.1", @@ -9818,7 +9818,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 754, "literal": "^4.1.4", @@ -9831,7 +9831,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 755, "literal": "^1.1.6", @@ -9844,7 +9844,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 756, "literal": "^1.0.7", @@ -9857,7 +9857,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 757, "literal": "^1.2.1", @@ -9870,7 +9870,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 758, "literal": "^1.23.3", @@ -9883,7 +9883,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 759, "literal": "^1.3.0", @@ -9896,7 +9896,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 760, "literal": "^2.0.3", @@ -9909,7 +9909,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 761, "literal": "^1.1.2", @@ -9922,7 +9922,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 762, "literal": "^1.2.4", @@ -9935,7 +9935,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 763, "literal": "^1.0.3", @@ -9948,7 +9948,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 764, "literal": "^1.0.2", @@ -9961,7 +9961,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 765, "literal": "^1.0.3", @@ -9974,7 +9974,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 766, "literal": "^1.0.3", @@ -9987,7 +9987,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 767, "literal": "^1.0.7", @@ -10000,7 +10000,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 768, "literal": "^1.1.2", @@ -10013,7 +10013,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 769, "literal": "^1.1.2", @@ -10026,7 +10026,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 770, "literal": "^1.2.1", @@ -10039,7 +10039,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 771, "literal": "^1.2.1", @@ -10052,7 +10052,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 772, "literal": "^1.0.3", @@ -10065,7 +10065,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 773, "literal": "^1.0.4", @@ -10078,7 +10078,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 774, "literal": "^2.0.1", @@ -10091,7 +10091,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 775, "literal": "^1.0.7", @@ -10104,7 +10104,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 776, "literal": "^1.2.1", @@ -10117,7 +10117,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 777, "literal": "^1.23.1", @@ -10130,7 +10130,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 778, "literal": "^1.3.0", @@ -10143,7 +10143,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 779, "literal": "^1.2.4", @@ -10156,7 +10156,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 780, "literal": "^1.0.3", @@ -10169,7 +10169,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 781, "literal": "^1.1.3", @@ -10182,7 +10182,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 782, "literal": "^1.1.5", @@ -10195,7 +10195,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 783, "literal": "^1.0.0", @@ -10208,7 +10208,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 784, "literal": "^2.0.0", @@ -10221,7 +10221,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 785, "literal": "^1.0.5", @@ -10234,7 +10234,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 786, "literal": "^1.0.2", @@ -10247,7 +10247,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 787, "literal": "^1.0.10", @@ -10260,7 +10260,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 788, "literal": "^1.1.4", @@ -10273,7 +10273,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 789, "literal": "^1.0.2", @@ -10286,7 +10286,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 790, "literal": "^2.0.5", @@ -10299,7 +10299,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 791, "literal": "^1.0.2", @@ -10312,7 +10312,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 792, "literal": "^1.0.1", @@ -10325,7 +10325,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 793, "literal": "^1.1.9", @@ -10338,7 +10338,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 794, "literal": "^2.0.3", @@ -10351,7 +10351,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 795, "literal": "^2.0.3", @@ -10364,7 +10364,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 796, "literal": "^2.0.2", @@ -10377,7 +10377,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 797, "literal": "^2.0.3", @@ -10390,7 +10390,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 798, "literal": "^1.0.7", @@ -10403,7 +10403,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 799, "literal": "^1.2.4", @@ -10416,7 +10416,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 800, "literal": "^1.0.0", @@ -10429,7 +10429,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 801, "literal": "^1.0.2", @@ -10442,7 +10442,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 802, "literal": "^1.0.0", @@ -10455,7 +10455,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 803, "literal": "^2.0.3", @@ -10468,7 +10468,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 804, "literal": "^2.0.3", @@ -10481,7 +10481,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 805, "literal": "^0.14.0", @@ -10494,7 +10494,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 806, "literal": "^3.1.8", @@ -10507,7 +10507,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 807, "literal": "^1.2.5", @@ -10520,7 +10520,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 808, "literal": "^1.3.2", @@ -10533,7 +10533,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 809, "literal": "^1.1.2", @@ -10546,7 +10546,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 810, "literal": "^1.1.3", @@ -10559,7 +10559,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 811, "literal": "^2.1.0", @@ -10572,7 +10572,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 812, "literal": "^1.0.19", @@ -10585,7 +10585,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 813, "literal": "^5.3.0", @@ -10598,7 +10598,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 814, "literal": "^2.4.1 || ^3.0.0", @@ -10611,7 +10611,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 815, "literal": "^3.1.2", @@ -10624,7 +10624,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 816, "literal": "^1.1.8", @@ -10637,7 +10637,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 817, "literal": "^2.0.8", @@ -10650,7 +10650,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 818, "literal": "^1.1.4", @@ -10663,7 +10663,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 819, "literal": "^1.2.0", @@ -10676,7 +10676,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 820, "literal": "^15.8.1", @@ -10689,7 +10689,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 821, "literal": "^2.0.0-next.5", @@ -10702,7 +10702,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 822, "literal": "^6.3.1", @@ -10715,7 +10715,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 823, "literal": "^4.0.11", @@ -10741,7 +10741,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 825, "literal": "^1.0.7", @@ -10754,7 +10754,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 826, "literal": "^1.2.1", @@ -10767,7 +10767,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 827, "literal": "^1.23.2", @@ -10780,7 +10780,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 828, "literal": "^1.3.0", @@ -10793,7 +10793,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 829, "literal": "^1.0.0", @@ -10806,7 +10806,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 830, "literal": "^1.2.4", @@ -10819,7 +10819,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 831, "literal": "^1.0.1", @@ -10832,7 +10832,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 832, "literal": "^1.0.3", @@ -10845,7 +10845,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 833, "literal": "^1.0.7", @@ -10858,7 +10858,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 834, "literal": "^1.5.2", @@ -10871,7 +10871,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 835, "literal": "^2.0.2", @@ -10884,7 +10884,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 836, "literal": "^1.0.6", @@ -10897,7 +10897,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 837, "literal": "^2.13.0", @@ -10910,7 +10910,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 838, "literal": "^1.0.7", @@ -10923,7 +10923,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 839, "literal": "^1.0.0", @@ -10936,7 +10936,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 840, "literal": "^1.4.0", @@ -10949,7 +10949,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 841, "literal": "^4.1.1", @@ -10962,7 +10962,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 842, "literal": "^16.13.1", @@ -10975,7 +10975,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 843, "literal": "^1.2.1", @@ -10988,7 +10988,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 844, "literal": "^1.23.2", @@ -11001,7 +11001,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 845, "literal": "^1.0.0", @@ -11014,7 +11014,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 846, "literal": "^1.0.7", @@ -11027,7 +11027,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 847, "literal": "^1.2.1", @@ -11040,7 +11040,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 848, "literal": "^1.23.3", @@ -11053,7 +11053,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 849, "literal": "^1.3.0", @@ -11066,7 +11066,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 850, "literal": "^1.0.2", @@ -11079,7 +11079,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 851, "literal": "^1.0.2", @@ -11092,7 +11092,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 852, "literal": "^1.2.0", @@ -11105,7 +11105,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 853, "literal": "^1.22.1", @@ -11118,7 +11118,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 854, "literal": "^1.0.0", @@ -11131,7 +11131,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 855, "literal": "^1.0.7", @@ -11144,7 +11144,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 856, "literal": "^1.2.1", @@ -11157,7 +11157,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 857, "literal": "^1.23.2", @@ -11170,7 +11170,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 858, "literal": "^1.3.0", @@ -11183,7 +11183,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 859, "literal": "^1.0.0", @@ -11196,7 +11196,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 860, "literal": "^1.0.2", @@ -11209,7 +11209,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 861, "literal": "~8.5.10", @@ -11222,7 +11222,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 862, "literal": "~20.12.8", @@ -11235,7 +11235,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 863, "literal": "*", @@ -11248,7 +11248,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 864, "literal": "^4.21.10", @@ -11261,7 +11261,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 865, "literal": "^1.0.30001538", @@ -11274,7 +11274,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 866, "literal": "^4.3.6", @@ -11287,7 +11287,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 867, "literal": "^0.1.2", @@ -11300,7 +11300,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 868, "literal": "^1.0.0", @@ -11313,7 +11313,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 869, "literal": "^4.2.0", @@ -11339,7 +11339,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 871, "literal": "^1.0.30001587", @@ -11352,7 +11352,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 872, "literal": "^1.4.668", @@ -11365,7 +11365,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 873, "literal": "^2.0.14", @@ -11378,7 +11378,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 874, "literal": "^1.0.13", @@ -11391,7 +11391,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 875, "literal": "^3.1.2", @@ -11404,7 +11404,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 876, "literal": "^1.0.1", @@ -11430,7 +11430,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 878, "literal": "*", @@ -11443,7 +11443,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 879, "literal": "*", @@ -11456,7 +11456,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 880, "literal": "*", @@ -11469,7 +11469,7 @@ exports[`next build works: bun 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 881, "literal": "^3.0.2", @@ -20772,7 +20772,7 @@ exports[`next build works: bun 1`] = ` "package_id": 374, }, "array-includes": { - "id": 806, + "id": 445, "package_id": 384, }, "array-union": { @@ -20792,7 +20792,7 @@ exports[`next build works: bun 1`] = ` "package_id": 382, }, "array.prototype.flatmap": { - "id": 808, + "id": 448, "package_id": 380, }, "array.prototype.toreversed": { @@ -21068,11 +21068,11 @@ exports[`next build works: bun 1`] = ` "package_id": 316, }, "es-errors": { - "id": 858, + "id": 690, "package_id": 312, }, "es-iterator-helpers": { - "id": 812, + "id": 740, "package_id": 409, }, "es-object-atoms": { @@ -21084,7 +21084,7 @@ exports[`next build works: bun 1`] = ` "package_id": 369, }, "es-shim-unscopables": { - "id": 860, + "id": 692, "package_id": 381, }, "es-to-primitive": { @@ -21120,7 +21120,7 @@ exports[`next build works: bun 1`] = ` "package_id": 302, }, "eslint-module-utils": { - "id": 452, + "id": 438, "package_id": 376, }, "eslint-plugin-import": { @@ -21164,7 +21164,7 @@ exports[`next build works: bun 1`] = ` "package_id": 279, }, "estraverse": { - "id": 432, + "id": 415, "package_id": 166, }, "esutils": { @@ -21248,7 +21248,7 @@ exports[`next build works: bun 1`] = ` "package_id": 51, }, "function-bind": { - "id": 761, + "id": 55, "package_id": 21, }, "function.prototype.name": { @@ -21412,7 +21412,7 @@ exports[`next build works: bun 1`] = ` "package_id": 329, }, "is-core-module": { - "id": 454, + "id": 675, "package_id": 19, }, "is-data-view": { @@ -21556,7 +21556,7 @@ exports[`next build works: bun 1`] = ` "package_id": 174, }, "jsx-ast-utils": { - "id": 814, + "id": 742, "package_id": 408, }, "keyv": { @@ -21680,11 +21680,11 @@ exports[`next build works: bun 1`] = ` "package_id": 355, }, "object.entries": { - "id": 816, + "id": 745, "package_id": 405, }, "object.fromentries": { - "id": 817, + "id": 457, "package_id": 375, }, "object.groupby": { @@ -21696,7 +21696,7 @@ exports[`next build works: bun 1`] = ` "package_id": 434, }, "object.values": { - "id": 819, + "id": 459, "package_id": 310, }, "once": { @@ -21924,7 +21924,7 @@ exports[`next build works: bun 1`] = ` "package_id": 112, }, "semver": { - "id": 822, + "id": 460, "package_id": 309, }, "set-function-length": { @@ -22271,18 +22271,14 @@ exports[`next build works: bun 1`] = ` }, { "dependencies": { - "doctrine": { - "id": 811, - "package_id": 379, - }, - "resolve": { - "id": 821, - "package_id": 431, + "debug": { + "id": 674, + "package_id": 377, }, }, "depth": 1, "id": 4, - "path": "node_modules/eslint-plugin-react/node_modules", + "path": "node_modules/eslint-import-resolver-node/node_modules", }, { "dependencies": { @@ -22301,14 +22297,18 @@ exports[`next build works: bun 1`] = ` }, { "dependencies": { - "debug": { - "id": 674, - "package_id": 377, + "doctrine": { + "id": 811, + "package_id": 379, + }, + "resolve": { + "id": 821, + "package_id": 431, }, }, "depth": 1, "id": 6, - "path": "node_modules/eslint-import-resolver-node/node_modules", + "path": "node_modules/eslint-plugin-react/node_modules", }, { "dependencies": { @@ -22358,17 +22358,6 @@ exports[`next build works: bun 1`] = ` "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, - { - "dependencies": { - "debug": { - "id": 672, - "package_id": 377, - }, - }, - "depth": 1, - "id": 11, - "path": "node_modules/eslint-module-utils/node_modules", - }, { "dependencies": { "minimatch": { @@ -22377,7 +22366,7 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/glob/node_modules", }, { @@ -22392,9 +22381,20 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, + { + "dependencies": { + "debug": { + "id": 672, + "package_id": 377, + }, + }, + "depth": 1, + "id": 13, + "path": "node_modules/eslint-module-utils/node_modules", + }, { "dependencies": { "lru-cache": { @@ -22439,17 +22439,6 @@ exports[`next build works: bun 1`] = ` "id": 17, "path": "node_modules/path-scurry/node_modules", }, - { - "dependencies": { - "lru-cache": { - "id": 165, - "package_id": 117, - }, - }, - "depth": 2, - "id": 18, - "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", - }, { "dependencies": { "brace-expansion": { @@ -22458,9 +22447,20 @@ exports[`next build works: bun 1`] = ` }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, + { + "dependencies": { + "lru-cache": { + "id": 165, + "package_id": 117, + }, + }, + "depth": 2, + "id": 19, + "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", + }, { "dependencies": { "@types/node": { @@ -22609,7 +22609,7 @@ exports[`next build works: node 1`] = ` "dependencies": [ { "behavior": { - "normal": true, + "prod": true, }, "id": 0, "literal": "20.7.0", @@ -22622,7 +22622,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 1, "literal": "18.2.22", @@ -22635,7 +22635,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 2, "literal": "18.2.7", @@ -22648,7 +22648,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 3, "literal": "10.4.16", @@ -22661,7 +22661,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 4, "literal": "^1.0.3", @@ -22674,7 +22674,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 5, "literal": "8.50.0", @@ -22687,7 +22687,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 6, "literal": "14.1.3", @@ -22700,7 +22700,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 7, "literal": "14.1.3", @@ -22713,7 +22713,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 8, "literal": "8.4.30", @@ -22726,7 +22726,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 9, "literal": "22.12.0", @@ -22739,7 +22739,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 10, "literal": "18.2.0", @@ -22752,7 +22752,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 11, "literal": "18.2.0", @@ -22765,7 +22765,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 12, "literal": "3.3.3", @@ -22778,7 +22778,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 13, "literal": "5.2.2", @@ -22791,7 +22791,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 14, "literal": "^5.0.2", @@ -22804,7 +22804,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 15, "literal": "^1.1.3", @@ -22817,7 +22817,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 16, "literal": "^1.18.2", @@ -22830,7 +22830,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 17, "literal": "^4.0.3", @@ -22843,7 +22843,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 18, "literal": "^8.4.23", @@ -22856,7 +22856,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 19, "literal": "^1.22.2", @@ -22869,7 +22869,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 20, "literal": "^3.32.0", @@ -22882,7 +22882,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 21, "literal": "^3.5.3", @@ -22895,7 +22895,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 22, "literal": "^3.2.12", @@ -22908,7 +22908,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 23, "literal": "^2.1.0", @@ -22921,7 +22921,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 24, "literal": "^1.2.2", @@ -22934,7 +22934,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 25, "literal": "^4.0.5", @@ -22947,7 +22947,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 26, "literal": "^1.0.0", @@ -22960,7 +22960,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 27, "literal": "^4.0.1", @@ -22973,7 +22973,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 28, "literal": "^6.0.2", @@ -22986,7 +22986,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 29, "literal": "^3.0.0", @@ -22999,7 +22999,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 30, "literal": "^3.0.0", @@ -23012,7 +23012,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 31, "literal": "^15.1.0", @@ -23025,7 +23025,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 32, "literal": "^6.0.1", @@ -23038,7 +23038,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 33, "literal": "^5.2.0", @@ -23051,7 +23051,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 34, "literal": "^4.0.1", @@ -23064,7 +23064,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 35, "literal": "^6.0.11", @@ -23077,7 +23077,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 36, "literal": "^3.0.0", @@ -23090,7 +23090,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 37, "literal": "^1.0.2", @@ -23103,7 +23103,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 38, "literal": "^2.3.4", @@ -23116,7 +23116,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 39, "literal": "^3.0.0", @@ -23157,7 +23157,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 42, "literal": "^3.3.6", @@ -23170,7 +23170,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 43, "literal": "^1.0.0", @@ -23183,7 +23183,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 44, "literal": "^1.0.2", @@ -23196,7 +23196,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 45, "literal": "^6.0.11", @@ -23222,7 +23222,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 47, "literal": "^4.0.0", @@ -23235,7 +23235,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 48, "literal": "^1.0.0", @@ -23248,7 +23248,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 49, "literal": "^1.1.7", @@ -23274,7 +23274,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 51, "literal": "^2.13.0", @@ -23287,7 +23287,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 52, "literal": "^1.0.7", @@ -23300,7 +23300,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 53, "literal": "^1.0.0", @@ -23313,7 +23313,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 54, "literal": "^2.0.0", @@ -23326,7 +23326,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 55, "literal": "^1.1.2", @@ -23339,7 +23339,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 56, "literal": "^2.3.0", @@ -23352,7 +23352,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 57, "literal": "^4.0.3", @@ -23365,7 +23365,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 58, "literal": "^2.1.1", @@ -23378,7 +23378,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 59, "literal": "^2.0.1", @@ -23404,7 +23404,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 61, "literal": "^3.0.3", @@ -23417,7 +23417,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 62, "literal": "^2.3.1", @@ -23430,7 +23430,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 63, "literal": "^7.1.1", @@ -23443,7 +23443,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 64, "literal": "^5.0.1", @@ -23456,7 +23456,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 65, "literal": "^7.0.0", @@ -23469,7 +23469,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 66, "literal": "^2.0.2", @@ -23482,7 +23482,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 67, "literal": "^1.2.3", @@ -23495,7 +23495,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 68, "literal": "^5.1.2", @@ -23508,7 +23508,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 69, "literal": "^1.3.0", @@ -23521,7 +23521,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 70, "literal": "^4.0.4", @@ -23534,7 +23534,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 71, "literal": "^4.0.1", @@ -23547,7 +23547,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 72, "literal": "2.1.5", @@ -23560,7 +23560,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 73, "literal": "^1.6.0", @@ -23573,7 +23573,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 74, "literal": "^1.0.4", @@ -23586,7 +23586,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 75, "literal": "2.0.5", @@ -23599,7 +23599,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 76, "literal": "^1.1.9", @@ -23612,7 +23612,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 77, "literal": "^1.2.2", @@ -23625,7 +23625,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 78, "literal": "~3.1.2", @@ -23638,7 +23638,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 79, "literal": "~3.0.2", @@ -23651,7 +23651,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 80, "literal": "~5.1.2", @@ -23664,7 +23664,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 81, "literal": "~2.1.0", @@ -23677,7 +23677,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 82, "literal": "~4.0.1", @@ -23690,7 +23690,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 83, "literal": "~3.0.0", @@ -23703,7 +23703,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 84, "literal": "~3.6.0", @@ -23729,7 +23729,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 86, "literal": "^2.2.1", @@ -23742,7 +23742,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 87, "literal": "^2.0.0", @@ -23755,7 +23755,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 88, "literal": "^3.0.0", @@ -23768,7 +23768,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 89, "literal": "^2.0.4", @@ -23781,7 +23781,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 90, "literal": "^0.3.2", @@ -23794,7 +23794,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 91, "literal": "^4.0.0", @@ -23807,7 +23807,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 92, "literal": "^10.3.10", @@ -23820,7 +23820,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 93, "literal": "^1.1.6", @@ -23833,7 +23833,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 94, "literal": "^2.7.0", @@ -23846,7 +23846,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 95, "literal": "^4.0.1", @@ -23859,7 +23859,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 96, "literal": "^0.1.9", @@ -23872,7 +23872,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 97, "literal": "^1.0.0", @@ -23885,7 +23885,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 98, "literal": "^4.0.1", @@ -23898,7 +23898,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 99, "literal": "^1.0.0", @@ -23911,7 +23911,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 100, "literal": ">= 3.1.0 < 4", @@ -23924,7 +23924,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 101, "literal": "^1.0.0", @@ -23937,7 +23937,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 102, "literal": "^3.1.0", @@ -23950,7 +23950,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 103, "literal": "^2.3.5", @@ -23963,7 +23963,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 104, "literal": "^9.0.1", @@ -23976,7 +23976,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 105, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -23989,7 +23989,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 106, "literal": "^1.10.1", @@ -24002,7 +24002,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 107, "literal": "^10.2.0", @@ -24015,7 +24015,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 108, "literal": "^5.0.0 || ^6.0.2 || ^7.0.0", @@ -24028,7 +24028,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 109, "literal": "^2.0.1", @@ -24041,7 +24041,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 110, "literal": "^1.0.0", @@ -24054,7 +24054,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 111, "literal": "^8.0.2", @@ -24080,7 +24080,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 113, "literal": "^5.1.2", @@ -24093,7 +24093,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 114, "is_alias": true, @@ -24107,7 +24107,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 115, "literal": "^7.0.1", @@ -24120,7 +24120,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 116, "is_alias": true, @@ -24134,7 +24134,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 117, "literal": "^8.1.0", @@ -24147,7 +24147,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 118, "is_alias": true, @@ -24161,7 +24161,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 119, "literal": "^4.0.0", @@ -24174,7 +24174,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 120, "literal": "^4.1.0", @@ -24187,7 +24187,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 121, "literal": "^6.0.0", @@ -24200,7 +24200,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 122, "literal": "^5.0.1", @@ -24213,7 +24213,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 123, "literal": "^8.0.0", @@ -24226,7 +24226,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 124, "literal": "^3.0.0", @@ -24239,7 +24239,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 125, "literal": "^6.0.1", @@ -24252,7 +24252,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 126, "literal": "^2.0.1", @@ -24265,7 +24265,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 127, "literal": "~1.1.4", @@ -24278,7 +24278,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 128, "literal": "^6.1.0", @@ -24291,7 +24291,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 129, "literal": "^5.0.1", @@ -24304,7 +24304,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 130, "literal": "^7.0.1", @@ -24317,7 +24317,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 131, "literal": "^6.0.1", @@ -24330,7 +24330,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 132, "literal": "^0.2.0", @@ -24343,7 +24343,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 133, "literal": "^9.2.2", @@ -24356,7 +24356,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 134, "literal": "^7.0.1", @@ -24369,7 +24369,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 135, "literal": "^7.0.0", @@ -24382,7 +24382,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 136, "literal": "^4.0.1", @@ -24395,7 +24395,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 137, "literal": "^3.1.0", @@ -24408,7 +24408,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 138, "literal": "^2.0.0", @@ -24421,7 +24421,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 139, "literal": "^2.0.1", @@ -24434,7 +24434,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 140, "literal": "^2.0.0", @@ -24447,7 +24447,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 141, "literal": "^3.0.0", @@ -24460,7 +24460,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 142, "literal": "^1.2.1", @@ -24473,7 +24473,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 143, "literal": "^1.4.10", @@ -24486,7 +24486,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 144, "literal": "^0.3.24", @@ -24499,7 +24499,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 145, "literal": "^3.1.0", @@ -24512,7 +24512,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 146, "literal": "^1.4.14", @@ -24525,7 +24525,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 147, "literal": "^0.23.0", @@ -24538,7 +24538,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 148, "literal": "^1.1.0", @@ -24564,7 +24564,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 150, "literal": "^1.1.0", @@ -24577,7 +24577,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 151, "literal": "^3.0.0 || ^4.0.0", @@ -24590,7 +24590,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 152, "literal": "^1.1.0", @@ -24603,7 +24603,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 153, "literal": "9.0.0", @@ -24616,7 +24616,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 154, "literal": "22.12.0", @@ -24629,7 +24629,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 155, "literal": "2.2.3", @@ -24642,7 +24642,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 156, "literal": "0.0.1299070", @@ -24655,7 +24655,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 157, "literal": "4.3.4", @@ -24668,7 +24668,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 158, "literal": "2.0.1", @@ -24681,7 +24681,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 159, "literal": "2.0.3", @@ -24694,7 +24694,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 160, "literal": "6.4.0", @@ -24707,7 +24707,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 161, "literal": "3.0.5", @@ -24720,7 +24720,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 162, "literal": "1.4.3", @@ -24733,7 +24733,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 163, "literal": "17.7.2", @@ -24746,7 +24746,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 164, "literal": "7.6.0", @@ -24759,7 +24759,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 165, "literal": "^6.0.0", @@ -24772,7 +24772,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 166, "literal": "^4.0.0", @@ -24785,7 +24785,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 167, "literal": "^8.0.1", @@ -24798,7 +24798,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 168, "literal": "^3.1.1", @@ -24811,7 +24811,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 169, "literal": "^2.0.5", @@ -24824,7 +24824,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 170, "literal": "^2.1.1", @@ -24837,7 +24837,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 171, "literal": "^4.2.3", @@ -24850,7 +24850,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 172, "literal": "^5.0.5", @@ -24863,7 +24863,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 173, "literal": "^21.1.1", @@ -24876,7 +24876,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 174, "literal": "^4.2.0", @@ -24889,7 +24889,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 175, "literal": "^6.0.1", @@ -24902,7 +24902,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 176, "literal": "^7.0.0", @@ -24915,7 +24915,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 177, "literal": "^5.2.1", @@ -24928,7 +24928,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 178, "literal": "^2.3.8", @@ -24941,7 +24941,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 179, "literal": "^1.3.1", @@ -24954,7 +24954,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 180, "literal": "^1.1.13", @@ -24967,7 +24967,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 181, "literal": "^3.0.0", @@ -24980,7 +24980,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 182, "literal": "^3.1.5", @@ -25019,7 +25019,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 185, "literal": "^2.1.0", @@ -25032,7 +25032,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 186, "literal": "^2.0.0", @@ -25045,7 +25045,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 187, "literal": "^2.0.0", @@ -25058,7 +25058,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 188, "literal": "^2.0.0", @@ -25071,7 +25071,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 189, "literal": "^2.18.0", @@ -25084,7 +25084,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 190, "literal": "^1.3.2", @@ -25097,7 +25097,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 191, "literal": "^1.0.1", @@ -25110,7 +25110,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 192, "literal": "^1.1.0", @@ -25136,7 +25136,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 194, "literal": "^1.6.4", @@ -25149,7 +25149,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 195, "literal": "^1.6.4", @@ -25162,7 +25162,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 196, "literal": "^1.2.0", @@ -25175,7 +25175,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 197, "literal": "^2.15.0", @@ -25188,7 +25188,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 198, "literal": "^1.1.0", @@ -25201,7 +25201,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 199, "literal": "^1.3.1", @@ -25214,7 +25214,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 200, "literal": "1", @@ -25227,7 +25227,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 201, "literal": "^1.4.0", @@ -25240,7 +25240,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 202, "literal": "^7.0.2", @@ -25253,7 +25253,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 203, "literal": "^4.3.4", @@ -25266,7 +25266,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 204, "literal": "^7.0.1", @@ -25279,7 +25279,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 205, "literal": "^7.0.3", @@ -25292,7 +25292,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 206, "literal": "^7.14.1", @@ -25305,7 +25305,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 207, "literal": "^7.0.1", @@ -25318,7 +25318,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 208, "literal": "^1.1.0", @@ -25331,7 +25331,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 209, "literal": "^8.0.2", @@ -25344,7 +25344,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 210, "literal": "^7.1.1", @@ -25357,7 +25357,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 211, "literal": "^4.3.4", @@ -25370,7 +25370,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 212, "literal": "^2.7.1", @@ -25383,7 +25383,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 213, "literal": "^9.0.5", @@ -25396,7 +25396,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 214, "literal": "^4.2.0", @@ -25409,7 +25409,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 215, "literal": "1.1.0", @@ -25422,7 +25422,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 216, "literal": "^1.1.3", @@ -25435,7 +25435,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 217, "literal": "2.1.2", @@ -25448,7 +25448,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 218, "literal": "^4.3.4", @@ -25461,7 +25461,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 219, "literal": "^0.23.0", @@ -25474,7 +25474,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 220, "literal": "^7.0.2", @@ -25487,7 +25487,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 221, "literal": "^4.3.4", @@ -25500,7 +25500,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 222, "literal": "^6.0.1", @@ -25513,7 +25513,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 223, "literal": "^7.0.0", @@ -25526,7 +25526,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 224, "literal": "^7.0.2", @@ -25539,7 +25539,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 225, "literal": "^7.0.0", @@ -25552,7 +25552,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 226, "literal": "^8.0.2", @@ -25565,7 +25565,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 227, "literal": "^5.0.0", @@ -25578,7 +25578,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 228, "literal": "^2.0.2", @@ -25591,7 +25591,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 229, "literal": "^0.13.4", @@ -25604,7 +25604,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 230, "literal": "^2.1.0", @@ -25617,7 +25617,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 231, "literal": "^4.0.1", @@ -25630,7 +25630,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 232, "literal": "^5.2.0", @@ -25643,7 +25643,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 233, "literal": "^2.0.2", @@ -25656,7 +25656,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 234, "literal": "^4.0.1", @@ -25682,7 +25682,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 236, "literal": "^2.0.1", @@ -25695,7 +25695,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 237, "literal": "^7.0.2", @@ -25708,7 +25708,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 238, "literal": "4", @@ -25721,7 +25721,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 239, "literal": "^7.1.0", @@ -25734,7 +25734,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 240, "literal": "^4.3.4", @@ -25747,7 +25747,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 241, "literal": "^5.0.2", @@ -25760,7 +25760,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 242, "literal": "^6.0.2", @@ -25773,7 +25773,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 243, "literal": "^4.3.4", @@ -25786,7 +25786,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 244, "literal": "^11.2.0", @@ -25799,7 +25799,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 245, "literal": "^4.2.0", @@ -25812,7 +25812,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 246, "literal": "^6.0.1", @@ -25825,7 +25825,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 247, "literal": "^2.0.0", @@ -25838,7 +25838,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 248, "literal": "^2.0.0", @@ -25864,7 +25864,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 250, "literal": "^4.1.1", @@ -25877,7 +25877,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 251, "literal": "^5.1.0", @@ -25890,7 +25890,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 252, "literal": "^2.10.0", @@ -25916,7 +25916,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 254, "literal": "*", @@ -25929,7 +25929,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 255, "literal": "~5.26.4", @@ -25942,7 +25942,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 256, "literal": "~1.1.0", @@ -25955,7 +25955,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 257, "literal": "~0.2.3", @@ -25968,7 +25968,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 258, "literal": "~1.2.0", @@ -25981,7 +25981,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 259, "literal": "^3.0.0", @@ -25994,7 +25994,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 260, "literal": "2.1.2", @@ -26007,7 +26007,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 261, "literal": "2.2.3", @@ -26020,7 +26020,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 262, "literal": "0.5.24", @@ -26033,7 +26033,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 263, "literal": "4.3.5", @@ -26046,7 +26046,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 264, "literal": "0.0.1299070", @@ -26059,7 +26059,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 265, "literal": "8.17.1", @@ -26100,7 +26100,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 268, "literal": "3.0.1", @@ -26113,7 +26113,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 269, "literal": "10.0.0", @@ -26126,7 +26126,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 270, "literal": "3.23.8", @@ -26152,7 +26152,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 272, "literal": "^2.2.1", @@ -26165,7 +26165,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 273, "literal": "^3.3.0", @@ -26178,7 +26178,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 274, "literal": "^4.1.0", @@ -26191,7 +26191,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 275, "literal": "^5.2.0", @@ -26218,7 +26218,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 277, "literal": "^7.0.0", @@ -26231,7 +26231,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 278, "literal": "^1.3.1", @@ -26244,7 +26244,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 279, "literal": "^2.3.0", @@ -26257,7 +26257,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 280, "literal": "^1.1.6", @@ -26270,7 +26270,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 281, "literal": "^0.2.1", @@ -26283,7 +26283,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 282, "literal": "^7.24.7", @@ -26296,7 +26296,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 283, "literal": "^1.0.0", @@ -26309,7 +26309,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 284, "literal": "^7.24.7", @@ -26322,7 +26322,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 285, "literal": "^2.4.2", @@ -26335,7 +26335,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 286, "literal": "^4.0.0", @@ -26348,7 +26348,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 287, "literal": "^1.0.0", @@ -26361,7 +26361,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 288, "literal": "^3.2.1", @@ -26374,7 +26374,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 289, "literal": "^1.0.5", @@ -26387,7 +26387,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 290, "literal": "^5.3.0", @@ -26400,7 +26400,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 291, "literal": "^3.0.0", @@ -26413,7 +26413,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 292, "literal": "^1.9.0", @@ -26426,7 +26426,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 293, "literal": "1.1.3", @@ -26439,7 +26439,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 294, "literal": "^2.0.1", @@ -26452,7 +26452,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 295, "literal": "^1.0.0", @@ -26465,7 +26465,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 296, "literal": "^4.0.0", @@ -26478,7 +26478,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 297, "literal": "^3.0.0", @@ -26491,7 +26491,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 298, "literal": "1.6.0", @@ -26504,7 +26504,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 299, "literal": "8.4.31", @@ -26517,7 +26517,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 300, "literal": "14.1.3", @@ -26530,7 +26530,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 301, "literal": "5.1.1", @@ -26543,7 +26543,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 302, "literal": "^4.2.11", @@ -26556,7 +26556,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 303, "literal": "0.5.2", @@ -26569,7 +26569,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 304, "literal": "^1.0.30001579", @@ -26753,7 +26753,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 318, "literal": "^2.4.0", @@ -26766,7 +26766,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 319, "literal": "0.0.1", @@ -26792,7 +26792,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 321, "literal": "^3.3.6", @@ -26805,7 +26805,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 322, "literal": "^1.0.0", @@ -26818,7 +26818,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 323, "literal": "^1.0.2", @@ -26831,7 +26831,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 324, "literal": "^1.1.0", @@ -26844,7 +26844,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 325, "literal": "^7.33.2", @@ -26857,7 +26857,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 326, "literal": "^2.28.1", @@ -26870,7 +26870,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 327, "literal": "^6.7.1", @@ -26883,7 +26883,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 328, "literal": "^1.3.3", @@ -26896,7 +26896,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 329, "literal": "14.1.3", @@ -26909,7 +26909,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 330, "literal": "^5.4.2 || ^6.0.0", @@ -26922,7 +26922,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 331, "literal": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", @@ -26935,7 +26935,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 332, "literal": "^0.3.6", @@ -26948,7 +26948,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 333, "literal": "^3.5.2", @@ -26988,7 +26988,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 336, "literal": "^6.12.4", @@ -27001,7 +27001,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 337, "literal": "^0.4.1", @@ -27014,7 +27014,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 338, "literal": "^4.0.0", @@ -27027,7 +27027,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 339, "literal": "^4.3.2", @@ -27040,7 +27040,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 340, "literal": "^9.6.1", @@ -27053,7 +27053,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 341, "literal": "^5.2.0", @@ -27066,7 +27066,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 342, "literal": "^1.4.2", @@ -27079,7 +27079,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 343, "literal": "^2.0.2", @@ -27092,7 +27092,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 344, "literal": "^5.0.0", @@ -27105,7 +27105,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 345, "literal": "^13.19.0", @@ -27118,7 +27118,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 346, "literal": "^4.0.0", @@ -27131,7 +27131,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 347, "literal": "^4.1.0", @@ -27144,7 +27144,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 348, "literal": "^3.0.0", @@ -27157,7 +27157,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 349, "literal": "^1.4.0", @@ -27170,7 +27170,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 350, "literal": "^3.1.2", @@ -27183,7 +27183,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 351, "literal": "8.50.0", @@ -27196,7 +27196,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 352, "literal": "^0.9.3", @@ -27209,7 +27209,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 353, "literal": "^6.0.1", @@ -27222,7 +27222,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 354, "literal": "^0.2.0", @@ -27235,7 +27235,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 355, "literal": "^7.0.2", @@ -27248,7 +27248,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 356, "literal": "^6.0.2", @@ -27261,7 +27261,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 357, "literal": "^0.1.4", @@ -27274,7 +27274,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 358, "literal": "^7.2.2", @@ -27287,7 +27287,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 359, "literal": "^4.6.2", @@ -27300,7 +27300,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 360, "literal": "^3.0.3", @@ -27313,7 +27313,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 361, "literal": "^3.1.3", @@ -27326,7 +27326,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 362, "literal": "^1.4.0", @@ -27339,7 +27339,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 363, "literal": "^2.1.2", @@ -27352,7 +27352,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 364, "literal": "^1.2.8", @@ -27365,7 +27365,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 365, "literal": "^6.0.1", @@ -27378,7 +27378,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 366, "literal": "^3.4.3", @@ -27391,7 +27391,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 367, "literal": "^4.0.0", @@ -27404,7 +27404,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 368, "literal": "^4.6.1", @@ -27417,7 +27417,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 369, "literal": "^0.11.11", @@ -27430,7 +27430,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 370, "literal": "^4.2.0", @@ -27443,7 +27443,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 371, "literal": "^1.0.1", @@ -27456,7 +27456,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 372, "literal": "^1.0.1", @@ -27469,7 +27469,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 373, "literal": "^3.3.0", @@ -27495,7 +27495,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 375, "literal": "^2.0.2", @@ -27508,7 +27508,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 376, "literal": "^4.3.1", @@ -27521,7 +27521,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 377, "literal": "^3.0.5", @@ -27534,7 +27534,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 378, "literal": "^1.1.7", @@ -27547,7 +27547,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 379, "literal": "^1.0.0", @@ -27560,7 +27560,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 380, "literal": "0.0.1", @@ -27573,7 +27573,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 381, "literal": "^3.0.4", @@ -27586,7 +27586,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 382, "literal": "^3.2.9", @@ -27599,7 +27599,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 383, "literal": "^4.5.3", @@ -27612,7 +27612,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 384, "literal": "^3.0.2", @@ -27625,7 +27625,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 385, "literal": "^7.1.3", @@ -27638,7 +27638,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 386, "literal": "^1.0.0", @@ -27651,7 +27651,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 387, "literal": "^1.0.4", @@ -27664,7 +27664,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 388, "literal": "2", @@ -27677,7 +27677,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 389, "literal": "^3.1.1", @@ -27690,7 +27690,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 390, "literal": "^1.3.0", @@ -27703,7 +27703,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 391, "literal": "^1.0.0", @@ -27716,7 +27716,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 392, "literal": "^1.3.0", @@ -27729,7 +27729,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 393, "literal": "1", @@ -27742,7 +27742,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 394, "literal": "3.0.1", @@ -27755,7 +27755,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 395, "literal": "^6.12.4", @@ -27768,7 +27768,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 396, "literal": "^4.3.2", @@ -27781,7 +27781,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 397, "literal": "^9.6.0", @@ -27794,7 +27794,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 398, "literal": "^13.19.0", @@ -27807,7 +27807,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 399, "literal": "^5.2.0", @@ -27820,7 +27820,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 400, "literal": "^3.2.1", @@ -27833,7 +27833,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 401, "literal": "^4.1.0", @@ -27846,7 +27846,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 402, "literal": "^3.1.2", @@ -27859,7 +27859,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 403, "literal": "^3.1.1", @@ -27872,7 +27872,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 404, "literal": "^0.20.2", @@ -27885,7 +27885,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 405, "literal": "^8.9.0", @@ -27898,7 +27898,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 406, "literal": "^5.3.2", @@ -27911,7 +27911,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 407, "literal": "^3.4.1", @@ -27937,7 +27937,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 409, "literal": "^3.1.1", @@ -27950,7 +27950,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 410, "literal": "^2.0.0", @@ -27963,7 +27963,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 411, "literal": "^0.4.1", @@ -27976,7 +27976,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 412, "literal": "^4.2.2", @@ -27989,7 +27989,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 413, "literal": "^2.1.0", @@ -28002,7 +28002,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 414, "literal": "^4.3.0", @@ -28015,7 +28015,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 415, "literal": "^5.2.0", @@ -28028,7 +28028,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 416, "literal": "^5.2.0", @@ -28041,7 +28041,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 417, "literal": "^1.2.1", @@ -28054,7 +28054,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 418, "literal": "^0.1.3", @@ -28067,7 +28067,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 419, "literal": "^1.2.5", @@ -28080,7 +28080,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 420, "literal": "^0.4.0", @@ -28093,7 +28093,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 421, "literal": "^0.4.1", @@ -28106,7 +28106,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 422, "literal": "^2.0.6", @@ -28119,7 +28119,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 423, "literal": "^1.2.1", @@ -28132,7 +28132,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 424, "literal": "~0.4.0", @@ -28145,7 +28145,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 425, "literal": "^1.2.1", @@ -28158,7 +28158,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 426, "literal": "^2.0.2", @@ -28171,7 +28171,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 427, "literal": "^6.0.0", @@ -28184,7 +28184,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 428, "literal": "^4.0.0", @@ -28197,7 +28197,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 429, "literal": "^5.0.0", @@ -28210,7 +28210,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 430, "literal": "^3.0.2", @@ -28223,7 +28223,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 431, "literal": "^0.1.0", @@ -28236,7 +28236,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 432, "literal": "^5.1.0", @@ -28249,7 +28249,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 433, "literal": "^4.1.0", @@ -28262,7 +28262,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 434, "literal": "^7.1.0", @@ -28275,7 +28275,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 435, "literal": "^4.0.0", @@ -28288,7 +28288,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 436, "literal": "^4.3.4", @@ -28301,7 +28301,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 437, "literal": "^5.12.0", @@ -28314,7 +28314,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 438, "literal": "^2.7.4", @@ -28327,7 +28327,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 439, "literal": "^3.3.1", @@ -28340,7 +28340,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 440, "literal": "^4.5.0", @@ -28353,7 +28353,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 441, "literal": "^2.11.0", @@ -28366,7 +28366,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 442, "literal": "^4.0.3", @@ -28405,7 +28405,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 445, "literal": "^3.1.7", @@ -28418,7 +28418,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 446, "literal": "^1.2.3", @@ -28431,7 +28431,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 447, "literal": "^1.3.2", @@ -28444,7 +28444,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 448, "literal": "^1.3.2", @@ -28457,7 +28457,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 449, "literal": "^3.2.7", @@ -28470,7 +28470,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 450, "literal": "^2.1.0", @@ -28483,7 +28483,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 451, "literal": "^0.3.9", @@ -28496,7 +28496,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 452, "literal": "^2.8.0", @@ -28509,7 +28509,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 453, "literal": "^2.0.0", @@ -28522,7 +28522,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 454, "literal": "^2.13.1", @@ -28535,7 +28535,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 455, "literal": "^4.0.3", @@ -28548,7 +28548,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 456, "literal": "^3.1.2", @@ -28561,7 +28561,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 457, "literal": "^2.0.7", @@ -28574,7 +28574,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 458, "literal": "^1.0.1", @@ -28587,7 +28587,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 459, "literal": "^1.1.7", @@ -28600,7 +28600,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 460, "literal": "^6.3.1", @@ -28613,7 +28613,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 461, "literal": "^3.15.0", @@ -28639,7 +28639,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 463, "literal": "^0.0.29", @@ -28652,7 +28652,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 464, "literal": "^1.0.2", @@ -28665,7 +28665,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 465, "literal": "^1.2.6", @@ -28678,7 +28678,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 466, "literal": "^3.0.0", @@ -28691,7 +28691,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 467, "literal": "^1.2.0", @@ -28704,7 +28704,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 468, "literal": "^1.0.7", @@ -28717,7 +28717,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 469, "literal": "^1.2.1", @@ -28730,7 +28730,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 470, "literal": "^1.0.0", @@ -28743,7 +28743,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 471, "literal": "^1.3.0", @@ -28756,7 +28756,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 472, "literal": "^1.0.1", @@ -28769,7 +28769,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 473, "literal": "^1.0.0", @@ -28782,7 +28782,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 474, "literal": "^1.1.1", @@ -28795,7 +28795,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 475, "literal": "^1.0.0", @@ -28808,7 +28808,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 476, "literal": "^1.2.4", @@ -28821,7 +28821,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 477, "literal": "^1.3.0", @@ -28834,7 +28834,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 478, "literal": "^1.1.2", @@ -28847,7 +28847,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 479, "literal": "^1.0.1", @@ -28860,7 +28860,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 480, "literal": "^1.0.3", @@ -28873,7 +28873,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 481, "literal": "^2.0.0", @@ -28886,7 +28886,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 482, "literal": "^1.0.0", @@ -28899,7 +28899,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 483, "literal": "^1.3.0", @@ -28912,7 +28912,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 484, "literal": "^1.0.1", @@ -28925,7 +28925,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 485, "literal": "^1.1.3", @@ -28938,7 +28938,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 486, "literal": "^1.0.0", @@ -28951,7 +28951,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 487, "literal": "^1.3.0", @@ -28964,7 +28964,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 488, "literal": "^1.1.2", @@ -28977,7 +28977,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 489, "literal": "^1.2.4", @@ -28990,7 +28990,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 490, "literal": "^1.2.1", @@ -29003,7 +29003,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 491, "literal": "^1.1.4", @@ -29016,7 +29016,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 492, "literal": "^1.3.0", @@ -29029,7 +29029,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 493, "literal": "^1.1.2", @@ -29042,7 +29042,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 494, "literal": "^1.2.4", @@ -29055,7 +29055,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 495, "literal": "^1.0.1", @@ -29068,7 +29068,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 496, "literal": "^1.0.2", @@ -29081,7 +29081,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 497, "literal": "^1.0.7", @@ -29094,7 +29094,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 498, "literal": "^1.2.1", @@ -29107,7 +29107,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 499, "literal": "^1.23.2", @@ -29120,7 +29120,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 500, "literal": "^1.0.1", @@ -29133,7 +29133,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 501, "literal": "^1.0.3", @@ -29146,7 +29146,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 502, "literal": "^1.0.7", @@ -29159,7 +29159,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 503, "literal": "^1.0.7", @@ -29172,7 +29172,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 504, "literal": "^1.0.1", @@ -29185,7 +29185,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 505, "literal": "^1.0.1", @@ -29198,7 +29198,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 506, "literal": "^1.0.0", @@ -29211,7 +29211,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 507, "literal": "^1.0.0", @@ -29224,7 +29224,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 508, "literal": "^1.3.0", @@ -29237,7 +29237,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 509, "literal": "^1.0.0", @@ -29250,7 +29250,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 510, "literal": "^2.0.3", @@ -29263,7 +29263,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 511, "literal": "^1.2.1", @@ -29276,7 +29276,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 512, "literal": "^1.1.6", @@ -29289,7 +29289,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 513, "literal": "^1.2.4", @@ -29302,7 +29302,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 514, "literal": "^1.0.2", @@ -29315,7 +29315,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 515, "literal": "^1.0.3", @@ -29328,7 +29328,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 516, "literal": "^1.0.1", @@ -29341,7 +29341,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 517, "literal": "^1.0.2", @@ -29354,7 +29354,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 518, "literal": "^1.0.3", @@ -29367,7 +29367,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 519, "literal": "^1.0.3", @@ -29380,7 +29380,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 520, "literal": "^2.0.2", @@ -29393,7 +29393,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 521, "literal": "^1.0.7", @@ -29406,7 +29406,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 522, "literal": "^3.0.4", @@ -29419,7 +29419,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 523, "literal": "^1.2.7", @@ -29432,7 +29432,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 524, "literal": "^1.0.1", @@ -29445,7 +29445,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 525, "literal": "^2.0.3", @@ -29458,7 +29458,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 526, "literal": "^1.1.4", @@ -29471,7 +29471,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 527, "literal": "^1.0.3", @@ -29484,7 +29484,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 528, "literal": "^1.0.7", @@ -29497,7 +29497,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 529, "literal": "^1.1.13", @@ -29510,7 +29510,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 530, "literal": "^1.0.2", @@ -29523,7 +29523,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 531, "literal": "^1.13.1", @@ -29536,7 +29536,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 532, "literal": "^1.1.1", @@ -29549,7 +29549,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 533, "literal": "^4.1.5", @@ -29562,7 +29562,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 534, "literal": "^1.5.2", @@ -29575,7 +29575,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 535, "literal": "^1.1.2", @@ -29588,7 +29588,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 536, "literal": "^1.0.3", @@ -29601,7 +29601,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 537, "literal": "^1.2.9", @@ -29614,7 +29614,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 538, "literal": "^1.0.8", @@ -29627,7 +29627,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 539, "literal": "^1.0.8", @@ -29640,7 +29640,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 540, "literal": "^1.0.2", @@ -29653,7 +29653,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 541, "literal": "^1.0.1", @@ -29666,7 +29666,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 542, "literal": "^1.0.2", @@ -29679,7 +29679,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 543, "literal": "^1.0.6", @@ -29692,7 +29692,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 544, "literal": "^1.0.2", @@ -29705,7 +29705,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 545, "literal": "^1.1.15", @@ -29718,7 +29718,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 546, "literal": "^1.0.7", @@ -29731,7 +29731,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 547, "literal": "^1.0.7", @@ -29744,7 +29744,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 548, "literal": "^0.3.3", @@ -29757,7 +29757,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 549, "literal": "^1.0.1", @@ -29770,7 +29770,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 550, "literal": "^1.0.2", @@ -29783,7 +29783,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 551, "literal": "^1.0.3", @@ -29796,7 +29796,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 552, "literal": "^1.1.3", @@ -29809,7 +29809,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 553, "literal": "^1.0.0", @@ -29822,7 +29822,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 554, "literal": "^1.0.2", @@ -29835,7 +29835,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 555, "literal": "^1.0.2", @@ -29848,7 +29848,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 556, "literal": "^1.0.3", @@ -29861,7 +29861,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 557, "literal": "^1.0.2", @@ -29874,7 +29874,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 558, "literal": "^1.0.1", @@ -29887,7 +29887,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 559, "literal": "^1.1.0", @@ -29900,7 +29900,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 560, "literal": "^1.0.4", @@ -29913,7 +29913,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 561, "literal": "^1.0.5", @@ -29926,7 +29926,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 562, "literal": "^1.0.3", @@ -29939,7 +29939,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 563, "literal": "^1.0.2", @@ -29952,7 +29952,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 564, "literal": "^1.0.0", @@ -29965,7 +29965,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 565, "literal": "^1.0.0", @@ -29978,7 +29978,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 566, "literal": "^1.0.2", @@ -29991,7 +29991,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 567, "literal": "^1.0.0", @@ -30004,7 +30004,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 568, "literal": "^1.0.1", @@ -30017,7 +30017,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 569, "literal": "^1.0.7", @@ -30030,7 +30030,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 570, "literal": "^0.3.3", @@ -30043,7 +30043,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 571, "literal": "^1.0.1", @@ -30056,7 +30056,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 572, "literal": "^1.0.3", @@ -30069,7 +30069,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 573, "literal": "^1.1.13", @@ -30082,7 +30082,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 574, "literal": "^1.0.0", @@ -30095,7 +30095,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 575, "literal": "^1.1.14", @@ -30108,7 +30108,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 576, "literal": "^1.0.7", @@ -30121,7 +30121,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 577, "literal": "^1.0.7", @@ -30134,7 +30134,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 578, "literal": "^0.3.3", @@ -30147,7 +30147,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 579, "literal": "^1.0.1", @@ -30160,7 +30160,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 580, "literal": "^1.0.3", @@ -30173,7 +30173,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 581, "literal": "^1.1.13", @@ -30186,7 +30186,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 582, "literal": "^1.0.7", @@ -30199,7 +30199,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 583, "literal": "^0.3.3", @@ -30212,7 +30212,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 584, "literal": "^1.0.1", @@ -30225,7 +30225,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 585, "literal": "^1.0.3", @@ -30238,7 +30238,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 586, "literal": "^1.1.13", @@ -30251,7 +30251,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 587, "literal": "^1.0.7", @@ -30264,7 +30264,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 588, "literal": "^1.3.0", @@ -30277,7 +30277,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 589, "literal": "^1.1.13", @@ -30290,7 +30290,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 590, "literal": "^1.0.7", @@ -30303,7 +30303,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 591, "literal": "^1.2.1", @@ -30316,7 +30316,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 592, "literal": "^1.0.0", @@ -30329,7 +30329,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 593, "literal": "^1.0.7", @@ -30342,7 +30342,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 594, "literal": "^1.2.1", @@ -30355,7 +30355,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 595, "literal": "^1.0.0", @@ -30368,7 +30368,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 596, "literal": "^1.0.7", @@ -30381,7 +30381,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 597, "literal": "^1.2.1", @@ -30394,7 +30394,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 598, "literal": "^1.23.0", @@ -30407,7 +30407,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 599, "literal": "^1.0.0", @@ -30420,7 +30420,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 600, "literal": "^1.0.6", @@ -30433,7 +30433,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 601, "literal": "^1.3.0", @@ -30446,7 +30446,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 602, "literal": "^1.1.4", @@ -30459,7 +30459,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 603, "literal": "^1.0.2", @@ -30472,7 +30472,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 604, "literal": "^1.0.0", @@ -30485,7 +30485,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 605, "literal": "^1.0.7", @@ -30498,7 +30498,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 606, "literal": "^1.2.4", @@ -30511,7 +30511,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 607, "literal": "^1.0.3", @@ -30524,7 +30524,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 608, "literal": "^2.0.5", @@ -30537,7 +30537,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 609, "literal": "^1.0.6", @@ -30550,7 +30550,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 610, "literal": "^1.2.1", @@ -30563,7 +30563,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 611, "literal": "^1.3.0", @@ -30576,7 +30576,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 612, "literal": "^2.0.1", @@ -30589,7 +30589,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 613, "literal": "^1.1.4", @@ -30602,7 +30602,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 614, "literal": "^1.3.0", @@ -30615,7 +30615,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 615, "literal": "^1.2.3", @@ -30628,7 +30628,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 616, "literal": "^1.0.2", @@ -30641,7 +30641,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 617, "literal": "^1.0.5", @@ -30654,7 +30654,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 618, "literal": "^1.2.1", @@ -30667,7 +30667,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 619, "literal": "^1.0.3", @@ -30680,7 +30680,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 620, "literal": "^1.1.1", @@ -30693,7 +30693,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 621, "literal": "^1.0.2", @@ -30706,7 +30706,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 622, "literal": "^1.0.7", @@ -30719,7 +30719,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 623, "literal": "^1.1.13", @@ -30732,7 +30732,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 624, "literal": "^1.0.2", @@ -30745,7 +30745,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 625, "literal": "^1.2.1", @@ -30758,7 +30758,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 626, "literal": "^1.3.0", @@ -30771,7 +30771,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 627, "literal": "^2.0.0", @@ -30784,7 +30784,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 628, "literal": "^1.0.4", @@ -30797,7 +30797,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 629, "literal": "^1.0.7", @@ -30810,7 +30810,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 630, "literal": "^1.3.0", @@ -30823,7 +30823,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 631, "literal": "^1.2.4", @@ -30836,7 +30836,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 632, "literal": "^1.13.1", @@ -30849,7 +30849,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 633, "literal": "^1.2.1", @@ -30862,7 +30862,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 634, "literal": "^1.0.1", @@ -30875,7 +30875,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 635, "literal": "^1.0.5", @@ -30888,7 +30888,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 636, "literal": "^1.3.0", @@ -30901,7 +30901,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 637, "literal": "^1.2.4", @@ -30914,7 +30914,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 638, "literal": "^1.0.2", @@ -30927,7 +30927,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 639, "literal": "^1.2.0", @@ -30940,7 +30940,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 640, "literal": "^1.22.1", @@ -30953,7 +30953,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 641, "literal": "^1.2.3", @@ -30966,7 +30966,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 642, "literal": "^1.1.4", @@ -30979,7 +30979,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 643, "literal": "^1.0.1", @@ -30992,7 +30992,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 644, "literal": "^1.0.2", @@ -31005,7 +31005,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 645, "literal": "^1.0.0", @@ -31018,7 +31018,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 646, "literal": "^1.2.4", @@ -31031,7 +31031,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 647, "literal": "^1.0.2", @@ -31044,7 +31044,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 648, "literal": "^2.0.1", @@ -31057,7 +31057,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 649, "literal": "^1.0.6", @@ -31070,7 +31070,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 650, "literal": "^1.3.0", @@ -31083,7 +31083,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 651, "literal": "^1.0.1", @@ -31096,7 +31096,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 652, "literal": "^1.0.7", @@ -31109,7 +31109,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 653, "literal": "^1.3.0", @@ -31122,7 +31122,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 654, "literal": "^1.0.1", @@ -31135,7 +31135,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 655, "literal": "^1.0.6", @@ -31148,7 +31148,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 656, "literal": "^1.3.0", @@ -31161,7 +31161,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 657, "literal": "^1.0.1", @@ -31174,7 +31174,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 658, "literal": "^1.0.1", @@ -31187,7 +31187,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 659, "literal": "^1.0.5", @@ -31200,7 +31200,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 660, "literal": "^1.2.1", @@ -31213,7 +31213,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 661, "literal": "^1.22.3", @@ -31226,7 +31226,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 662, "literal": "^1.2.1", @@ -31239,7 +31239,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 663, "literal": "^1.2.3", @@ -31252,7 +31252,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 664, "literal": "^3.0.4", @@ -31265,7 +31265,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 665, "literal": "^1.0.2", @@ -31278,7 +31278,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 666, "literal": "^1.0.5", @@ -31291,7 +31291,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 667, "literal": "^3.0.4", @@ -31304,7 +31304,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 668, "literal": "^1.0.7", @@ -31317,7 +31317,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 669, "literal": "^1.2.1", @@ -31330,7 +31330,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 670, "literal": "^1.23.2", @@ -31343,7 +31343,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 671, "literal": "^1.0.0", @@ -31356,7 +31356,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 672, "literal": "^3.2.7", @@ -31369,7 +31369,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 673, "literal": "^2.1.1", @@ -31382,7 +31382,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 674, "literal": "^3.2.7", @@ -31395,7 +31395,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 675, "literal": "^2.13.0", @@ -31408,7 +31408,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 676, "literal": "^1.22.4", @@ -31421,7 +31421,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 677, "literal": "^2.0.2", @@ -31434,7 +31434,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 678, "literal": "^1.0.2", @@ -31447,7 +31447,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 679, "literal": "^1.2.0", @@ -31460,7 +31460,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 680, "literal": "^1.22.1", @@ -31473,7 +31473,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 681, "literal": "^1.0.0", @@ -31486,7 +31486,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 682, "literal": "^2.0.0", @@ -31499,7 +31499,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 683, "literal": "^1.0.2", @@ -31512,7 +31512,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 684, "literal": "^1.2.0", @@ -31525,7 +31525,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 685, "literal": "^1.22.1", @@ -31538,7 +31538,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 686, "literal": "^1.0.0", @@ -31551,7 +31551,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 687, "literal": "^1.0.7", @@ -31564,7 +31564,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 688, "literal": "^1.2.1", @@ -31577,7 +31577,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 689, "literal": "^1.23.2", @@ -31590,7 +31590,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 690, "literal": "^1.3.0", @@ -31603,7 +31603,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 691, "literal": "^1.0.0", @@ -31616,7 +31616,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 692, "literal": "^1.0.2", @@ -31629,7 +31629,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 693, "literal": "^1.0.7", @@ -31642,7 +31642,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 694, "literal": "^1.2.1", @@ -31655,7 +31655,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 695, "literal": "^1.23.2", @@ -31668,7 +31668,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 696, "literal": "^1.0.0", @@ -31681,7 +31681,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 697, "literal": "^1.2.4", @@ -31694,7 +31694,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 698, "literal": "^1.0.7", @@ -31707,7 +31707,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 699, "literal": "^1.0.0", @@ -31720,7 +31720,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 700, "literal": "^4.2.4", @@ -31733,7 +31733,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 701, "literal": "^2.2.0", @@ -31759,7 +31759,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 703, "literal": "^4.3.4", @@ -31772,7 +31772,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 704, "literal": "6.21.0", @@ -31785,7 +31785,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 705, "literal": "6.21.0", @@ -31798,7 +31798,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 706, "literal": "6.21.0", @@ -31811,7 +31811,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 707, "literal": "6.21.0", @@ -31837,7 +31837,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 709, "literal": "^4.3.4", @@ -31850,7 +31850,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 710, "literal": "^11.1.0", @@ -31863,7 +31863,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 711, "literal": "^7.5.4", @@ -31876,7 +31876,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 712, "literal": "^4.0.3", @@ -31889,7 +31889,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 713, "literal": "9.0.3", @@ -31902,7 +31902,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 714, "literal": "^1.0.1", @@ -31915,7 +31915,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 715, "literal": "6.21.0", @@ -31928,7 +31928,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 716, "literal": "6.21.0", @@ -31941,7 +31941,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 717, "literal": "^3.4.1", @@ -31954,7 +31954,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 718, "literal": "6.21.0", @@ -31980,7 +31980,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 720, "literal": "^2.0.1", @@ -31993,7 +31993,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 721, "literal": "^2.1.0", @@ -32006,7 +32006,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 722, "literal": "^3.0.1", @@ -32019,7 +32019,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 723, "literal": "^3.2.9", @@ -32032,7 +32032,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 724, "literal": "^5.2.0", @@ -32045,7 +32045,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 725, "literal": "^1.4.1", @@ -32058,7 +32058,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 726, "literal": "^3.0.0", @@ -32071,7 +32071,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 727, "literal": "^4.0.0", @@ -32084,7 +32084,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 728, "literal": "6.21.0", @@ -32097,7 +32097,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 729, "literal": "6.21.0", @@ -32110,7 +32110,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 730, "literal": "10.3.10", @@ -32123,7 +32123,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 731, "literal": "^7.23.2", @@ -32136,7 +32136,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 732, "literal": "^5.3.0", @@ -32149,7 +32149,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 733, "literal": "^3.1.7", @@ -32162,7 +32162,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 734, "literal": "^1.3.2", @@ -32175,7 +32175,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 735, "literal": "^0.0.8", @@ -32188,7 +32188,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 736, "literal": "=4.7.0", @@ -32201,7 +32201,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 737, "literal": "^3.2.1", @@ -32214,7 +32214,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 738, "literal": "^1.0.8", @@ -32227,7 +32227,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 739, "literal": "^9.2.2", @@ -32240,7 +32240,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 740, "literal": "^1.0.15", @@ -32253,7 +32253,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 741, "literal": "^2.0.0", @@ -32266,7 +32266,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 742, "literal": "^3.3.5", @@ -32279,7 +32279,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 743, "literal": "^1.0.9", @@ -32292,7 +32292,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 744, "literal": "^3.1.2", @@ -32305,7 +32305,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 745, "literal": "^1.1.7", @@ -32318,7 +32318,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 746, "literal": "^2.0.7", @@ -32344,7 +32344,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 748, "literal": "^1.0.7", @@ -32357,7 +32357,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 749, "literal": "^1.2.1", @@ -32370,7 +32370,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 750, "literal": "^1.0.0", @@ -32383,7 +32383,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 751, "literal": "^0.3.20", @@ -32396,7 +32396,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 752, "literal": "^3.1.6", @@ -32409,7 +32409,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 753, "literal": "^1.3.1", @@ -32422,7 +32422,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 754, "literal": "^4.1.4", @@ -32435,7 +32435,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 755, "literal": "^1.1.6", @@ -32448,7 +32448,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 756, "literal": "^1.0.7", @@ -32461,7 +32461,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 757, "literal": "^1.2.1", @@ -32474,7 +32474,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 758, "literal": "^1.23.3", @@ -32487,7 +32487,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 759, "literal": "^1.3.0", @@ -32500,7 +32500,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 760, "literal": "^2.0.3", @@ -32513,7 +32513,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 761, "literal": "^1.1.2", @@ -32526,7 +32526,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 762, "literal": "^1.2.4", @@ -32539,7 +32539,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 763, "literal": "^1.0.3", @@ -32552,7 +32552,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 764, "literal": "^1.0.2", @@ -32565,7 +32565,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 765, "literal": "^1.0.3", @@ -32578,7 +32578,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 766, "literal": "^1.0.3", @@ -32591,7 +32591,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 767, "literal": "^1.0.7", @@ -32604,7 +32604,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 768, "literal": "^1.1.2", @@ -32617,7 +32617,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 769, "literal": "^1.1.2", @@ -32630,7 +32630,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 770, "literal": "^1.2.1", @@ -32643,7 +32643,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 771, "literal": "^1.2.1", @@ -32656,7 +32656,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 772, "literal": "^1.0.3", @@ -32669,7 +32669,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 773, "literal": "^1.0.4", @@ -32682,7 +32682,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 774, "literal": "^2.0.1", @@ -32695,7 +32695,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 775, "literal": "^1.0.7", @@ -32708,7 +32708,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 776, "literal": "^1.2.1", @@ -32721,7 +32721,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 777, "literal": "^1.23.1", @@ -32734,7 +32734,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 778, "literal": "^1.3.0", @@ -32747,7 +32747,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 779, "literal": "^1.2.4", @@ -32760,7 +32760,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 780, "literal": "^1.0.3", @@ -32773,7 +32773,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 781, "literal": "^1.1.3", @@ -32786,7 +32786,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 782, "literal": "^1.1.5", @@ -32799,7 +32799,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 783, "literal": "^1.0.0", @@ -32812,7 +32812,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 784, "literal": "^2.0.0", @@ -32825,7 +32825,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 785, "literal": "^1.0.5", @@ -32838,7 +32838,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 786, "literal": "^1.0.2", @@ -32851,7 +32851,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 787, "literal": "^1.0.10", @@ -32864,7 +32864,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 788, "literal": "^1.1.4", @@ -32877,7 +32877,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 789, "literal": "^1.0.2", @@ -32890,7 +32890,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 790, "literal": "^2.0.5", @@ -32903,7 +32903,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 791, "literal": "^1.0.2", @@ -32916,7 +32916,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 792, "literal": "^1.0.1", @@ -32929,7 +32929,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 793, "literal": "^1.1.9", @@ -32942,7 +32942,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 794, "literal": "^2.0.3", @@ -32955,7 +32955,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 795, "literal": "^2.0.3", @@ -32968,7 +32968,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 796, "literal": "^2.0.2", @@ -32981,7 +32981,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 797, "literal": "^2.0.3", @@ -32994,7 +32994,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 798, "literal": "^1.0.7", @@ -33007,7 +33007,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 799, "literal": "^1.2.4", @@ -33020,7 +33020,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 800, "literal": "^1.0.0", @@ -33033,7 +33033,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 801, "literal": "^1.0.2", @@ -33046,7 +33046,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 802, "literal": "^1.0.0", @@ -33059,7 +33059,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 803, "literal": "^2.0.3", @@ -33072,7 +33072,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 804, "literal": "^2.0.3", @@ -33085,7 +33085,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 805, "literal": "^0.14.0", @@ -33098,7 +33098,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 806, "literal": "^3.1.8", @@ -33111,7 +33111,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 807, "literal": "^1.2.5", @@ -33124,7 +33124,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 808, "literal": "^1.3.2", @@ -33137,7 +33137,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 809, "literal": "^1.1.2", @@ -33150,7 +33150,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 810, "literal": "^1.1.3", @@ -33163,7 +33163,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 811, "literal": "^2.1.0", @@ -33176,7 +33176,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 812, "literal": "^1.0.19", @@ -33189,7 +33189,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 813, "literal": "^5.3.0", @@ -33202,7 +33202,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 814, "literal": "^2.4.1 || ^3.0.0", @@ -33215,7 +33215,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 815, "literal": "^3.1.2", @@ -33228,7 +33228,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 816, "literal": "^1.1.8", @@ -33241,7 +33241,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 817, "literal": "^2.0.8", @@ -33254,7 +33254,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 818, "literal": "^1.1.4", @@ -33267,7 +33267,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 819, "literal": "^1.2.0", @@ -33280,7 +33280,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 820, "literal": "^15.8.1", @@ -33293,7 +33293,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 821, "literal": "^2.0.0-next.5", @@ -33306,7 +33306,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 822, "literal": "^6.3.1", @@ -33319,7 +33319,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 823, "literal": "^4.0.11", @@ -33345,7 +33345,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 825, "literal": "^1.0.7", @@ -33358,7 +33358,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 826, "literal": "^1.2.1", @@ -33371,7 +33371,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 827, "literal": "^1.23.2", @@ -33384,7 +33384,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 828, "literal": "^1.3.0", @@ -33397,7 +33397,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 829, "literal": "^1.0.0", @@ -33410,7 +33410,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 830, "literal": "^1.2.4", @@ -33423,7 +33423,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 831, "literal": "^1.0.1", @@ -33436,7 +33436,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 832, "literal": "^1.0.3", @@ -33449,7 +33449,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 833, "literal": "^1.0.7", @@ -33462,7 +33462,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 834, "literal": "^1.5.2", @@ -33475,7 +33475,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 835, "literal": "^2.0.2", @@ -33488,7 +33488,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 836, "literal": "^1.0.6", @@ -33501,7 +33501,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 837, "literal": "^2.13.0", @@ -33514,7 +33514,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 838, "literal": "^1.0.7", @@ -33527,7 +33527,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 839, "literal": "^1.0.0", @@ -33540,7 +33540,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 840, "literal": "^1.4.0", @@ -33553,7 +33553,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 841, "literal": "^4.1.1", @@ -33566,7 +33566,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 842, "literal": "^16.13.1", @@ -33579,7 +33579,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 843, "literal": "^1.2.1", @@ -33592,7 +33592,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 844, "literal": "^1.23.2", @@ -33605,7 +33605,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 845, "literal": "^1.0.0", @@ -33618,7 +33618,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 846, "literal": "^1.0.7", @@ -33631,7 +33631,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 847, "literal": "^1.2.1", @@ -33644,7 +33644,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 848, "literal": "^1.23.3", @@ -33657,7 +33657,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 849, "literal": "^1.3.0", @@ -33670,7 +33670,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 850, "literal": "^1.0.2", @@ -33683,7 +33683,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 851, "literal": "^1.0.2", @@ -33696,7 +33696,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 852, "literal": "^1.2.0", @@ -33709,7 +33709,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 853, "literal": "^1.22.1", @@ -33722,7 +33722,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 854, "literal": "^1.0.0", @@ -33735,7 +33735,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 855, "literal": "^1.0.7", @@ -33748,7 +33748,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 856, "literal": "^1.2.1", @@ -33761,7 +33761,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 857, "literal": "^1.23.2", @@ -33774,7 +33774,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 858, "literal": "^1.3.0", @@ -33787,7 +33787,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 859, "literal": "^1.0.0", @@ -33800,7 +33800,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 860, "literal": "^1.0.2", @@ -33813,7 +33813,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 861, "literal": "~8.5.10", @@ -33826,7 +33826,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 862, "literal": "~20.12.8", @@ -33839,7 +33839,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 863, "literal": "*", @@ -33852,7 +33852,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 864, "literal": "^4.21.10", @@ -33865,7 +33865,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 865, "literal": "^1.0.30001538", @@ -33878,7 +33878,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 866, "literal": "^4.3.6", @@ -33891,7 +33891,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 867, "literal": "^0.1.2", @@ -33904,7 +33904,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 868, "literal": "^1.0.0", @@ -33917,7 +33917,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 869, "literal": "^4.2.0", @@ -33943,7 +33943,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 871, "literal": "^1.0.30001587", @@ -33956,7 +33956,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 872, "literal": "^1.4.668", @@ -33969,7 +33969,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 873, "literal": "^2.0.14", @@ -33982,7 +33982,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 874, "literal": "^1.0.13", @@ -33995,7 +33995,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 875, "literal": "^3.1.2", @@ -34008,7 +34008,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 876, "literal": "^1.0.1", @@ -34034,7 +34034,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 878, "literal": "*", @@ -34047,7 +34047,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 879, "literal": "*", @@ -34060,7 +34060,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 880, "literal": "*", @@ -34073,7 +34073,7 @@ exports[`next build works: node 1`] = ` }, { "behavior": { - "normal": true, + "prod": true, }, "id": 881, "literal": "^3.0.2", @@ -43376,7 +43376,7 @@ exports[`next build works: node 1`] = ` "package_id": 374, }, "array-includes": { - "id": 806, + "id": 445, "package_id": 384, }, "array-union": { @@ -43396,7 +43396,7 @@ exports[`next build works: node 1`] = ` "package_id": 382, }, "array.prototype.flatmap": { - "id": 808, + "id": 448, "package_id": 380, }, "array.prototype.toreversed": { @@ -43672,11 +43672,11 @@ exports[`next build works: node 1`] = ` "package_id": 316, }, "es-errors": { - "id": 858, + "id": 690, "package_id": 312, }, "es-iterator-helpers": { - "id": 812, + "id": 740, "package_id": 409, }, "es-object-atoms": { @@ -43688,7 +43688,7 @@ exports[`next build works: node 1`] = ` "package_id": 369, }, "es-shim-unscopables": { - "id": 860, + "id": 692, "package_id": 381, }, "es-to-primitive": { @@ -43724,7 +43724,7 @@ exports[`next build works: node 1`] = ` "package_id": 302, }, "eslint-module-utils": { - "id": 452, + "id": 438, "package_id": 376, }, "eslint-plugin-import": { @@ -43768,7 +43768,7 @@ exports[`next build works: node 1`] = ` "package_id": 279, }, "estraverse": { - "id": 432, + "id": 415, "package_id": 166, }, "esutils": { @@ -43852,7 +43852,7 @@ exports[`next build works: node 1`] = ` "package_id": 51, }, "function-bind": { - "id": 761, + "id": 55, "package_id": 21, }, "function.prototype.name": { @@ -44016,7 +44016,7 @@ exports[`next build works: node 1`] = ` "package_id": 329, }, "is-core-module": { - "id": 454, + "id": 675, "package_id": 19, }, "is-data-view": { @@ -44160,7 +44160,7 @@ exports[`next build works: node 1`] = ` "package_id": 174, }, "jsx-ast-utils": { - "id": 814, + "id": 742, "package_id": 408, }, "keyv": { @@ -44284,11 +44284,11 @@ exports[`next build works: node 1`] = ` "package_id": 355, }, "object.entries": { - "id": 816, + "id": 745, "package_id": 405, }, "object.fromentries": { - "id": 817, + "id": 457, "package_id": 375, }, "object.groupby": { @@ -44300,7 +44300,7 @@ exports[`next build works: node 1`] = ` "package_id": 434, }, "object.values": { - "id": 819, + "id": 459, "package_id": 310, }, "once": { @@ -44528,7 +44528,7 @@ exports[`next build works: node 1`] = ` "package_id": 112, }, "semver": { - "id": 822, + "id": 460, "package_id": 309, }, "set-function-length": { @@ -44875,18 +44875,14 @@ exports[`next build works: node 1`] = ` }, { "dependencies": { - "doctrine": { - "id": 811, - "package_id": 379, - }, - "resolve": { - "id": 821, - "package_id": 431, + "debug": { + "id": 674, + "package_id": 377, }, }, "depth": 1, "id": 4, - "path": "node_modules/eslint-plugin-react/node_modules", + "path": "node_modules/eslint-import-resolver-node/node_modules", }, { "dependencies": { @@ -44905,14 +44901,18 @@ exports[`next build works: node 1`] = ` }, { "dependencies": { - "debug": { - "id": 674, - "package_id": 377, + "doctrine": { + "id": 811, + "package_id": 379, + }, + "resolve": { + "id": 821, + "package_id": 431, }, }, "depth": 1, "id": 6, - "path": "node_modules/eslint-import-resolver-node/node_modules", + "path": "node_modules/eslint-plugin-react/node_modules", }, { "dependencies": { @@ -44962,17 +44962,6 @@ exports[`next build works: node 1`] = ` "id": 10, "path": "node_modules/postcss-load-config/node_modules", }, - { - "dependencies": { - "debug": { - "id": 672, - "package_id": 377, - }, - }, - "depth": 1, - "id": 11, - "path": "node_modules/eslint-module-utils/node_modules", - }, { "dependencies": { "minimatch": { @@ -44981,7 +44970,7 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 12, + "id": 11, "path": "node_modules/glob/node_modules", }, { @@ -44996,9 +44985,20 @@ exports[`next build works: node 1`] = ` }, }, "depth": 1, - "id": 13, + "id": 12, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules", }, + { + "dependencies": { + "debug": { + "id": 672, + "package_id": 377, + }, + }, + "depth": 1, + "id": 13, + "path": "node_modules/eslint-module-utils/node_modules", + }, { "dependencies": { "lru-cache": { @@ -45043,17 +45043,6 @@ exports[`next build works: node 1`] = ` "id": 17, "path": "node_modules/path-scurry/node_modules", }, - { - "dependencies": { - "lru-cache": { - "id": 165, - "package_id": 117, - }, - }, - "depth": 2, - "id": 18, - "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", - }, { "dependencies": { "brace-expansion": { @@ -45062,9 +45051,20 @@ exports[`next build works: node 1`] = ` }, }, "depth": 2, - "id": 19, + "id": 18, "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules", }, + { + "dependencies": { + "lru-cache": { + "id": 165, + "package_id": 117, + }, + }, + "depth": 2, + "id": 19, + "path": "node_modules/@typescript-eslint/typescript-estree/node_modules/semver/node_modules", + }, { "dependencies": { "@types/node": { diff --git a/test/js/bun/bun-object/deep-match.spec.ts b/test/js/bun/bun-object/deep-match.spec.ts new file mode 100644 index 0000000000..1a70301937 --- /dev/null +++ b/test/js/bun/bun-object/deep-match.spec.ts @@ -0,0 +1,222 @@ +type TestCase = [a: unknown, b: unknown]; + +// @ts-ignore +if (typeof Bun === "undefined") + [ + // @ts-ignore + (globalThis.Bun = { + deepMatch(a, b) { + try { + expect(b).toMatchObject(a); + return true; + } catch (e) { + if (e instanceof TypeError) throw e; + return false; + } + }, + }), + ]; +describe("Bun.deepMatch", () => { + it.each([ + // force line break + {}, + { a: 1 }, + [[1, 2, 3]], + ] as TestCase[])("returns `true` for referentially equal objects (%p)", obj => { + expect(Bun.deepMatch(obj, obj)).toBe(true); + // expect(Bun.deepMatch(obj, obj)).toBe(true); + }); + + // prettier-ignore + it.each([ + // POJOs + [{}, {}], + [{ a: 1 }, { a: 1 }], + [{ a: Symbol.for("foo") }, { a: Symbol.for("foo") }], + [ + { a: { b: "foo" }, c: true }, + { a: { b: "foo" }, c: true }, + ], + [ + { a: [{ b: [] }, "foo", 0, null] }, + { a: [{ b: [] }, "foo", 0, null] } + ], + [{ }, { a: undefined }], // NOTE: `b` may be a superset of `a`, but not vice-versa + [{ a: { b: "foo" } }, { a: { b: "foo", c: undefined } }], + [{ a: { b: "foo" } }, { a: { b: "foo", c: 1 } }], + + // Arrays + [[], []], + [ + [1, 2, 3], + [1, 2, 3], + ], + [ + [{}, "foo", 1], + [{}, "foo", 1], + ], + + // Maps + [new Map(), new Map()], + [ + new Map([ [1, 2], [2, 3], [3, 4] ]), + new Map([ [1, 2], [2, 3], [3, 4] ]), + ], + [ + new Map([ ["foo", 1] ]), + new Map([ ["foo", 1] ]), + ], + + // Sets + [new Set(), new Set()], + [ + new Set([1, 2, 3]), + new Set([1, 2, 3]), + ], + [ + new Set(["a", "b", "c"]), + new Set(["a", "b", "c"]), + ], + ])("Bun.deepMatch(%p, %p) === true", (a, b) => { + expect(Bun.deepMatch(a, b)).toBe(true); + }); + + // prettier-ignore + it.each([ + // POJOs + [{ a: undefined }, { }], // NOTE: `a` may not be a superset of `b` + [{ a: 1 }, { a: 2 }], + [{ a: 1 }, { b: 1 }], + [{ a: null }, { a: undefined }], + [{ a: { b: "foo" } }, { a: { b: "bar"} }], + [{ a: { b: "foo", c: 1 } }, { a: { b: "foo" } }], + [{ a: Symbol.for("a") }, { a: Symbol.for("b") }], + [{ a: Symbol("a") }, { a: Symbol("a") }], // new symbols are never equal + + // Arrays + [[1, 2, 3], [1, 2]], + [[1, 2, 3], [1, 2, 4]], + [[null], [undefined]], + [[], [undefined]], + [["a", "b", "c"], ["a", "b", "d"]], + + // Maps + // FIXME: I assume this is incorrect but I need confirmation on expected behavior. + // [ + // new Map([ [1, 2], [2, 3], [3, 4] ]), + // new Map([ [1, 2], [2, 3] ]), + // ], + // [ + // new Map([ [1, 2], [2, 3], [3, 4] ]), + // new Map([ [1, 2], [2, 3], [3, 4], [4, 5] ]), + // ], + // [ + // new Map([ [1, 2], [2, 3], [3, 4], [4, 5] ]), + // new Map([ [1, 2], [2, 3], [3, 4] ]), + // ], + + // Sets + // FIXME: I assume this is incorrect but I need confirmation on expected behavior. + // [ + // new Set([1, 2, 3]), + // new Set([4, 5, 6]), + // ], + // [ + // new Set([1, 2, 3]), + // new Set([1, 2]), + // ], + // [ + // new Set([1, 2]), + // new Set([1, 2, 3]), + // ], + // [ + // new Set(["a", "b", "c"]), + // new Set(["a", "b", "d"]), + // ], + ])("Bun.deepMatch(%p, %p) === false", (a, b) => { + expect(Bun.deepMatch(a, b)).toBe(false); + }); + + it("When comparing same-shape objects with different constructors, returns true", () => { + class Foo {} + class Bar {} + + expect(Bun.deepMatch(new Foo(), new Bar())).toBe(true); + }); + + describe("When provided objects with circular references", () => { + let foo: Record; + + const makeCircular = () => { + let foo = { bar: undefined as any }; + let bar = { foo: undefined as any }; + foo.bar = bar; + bar.foo = foo; + return foo; + }; + + beforeEach(() => { + foo = makeCircular(); + }); + + // a, b are ref equal + it("when a and b are _exactly_ the same object, returns true", () => { + expect(Bun.deepMatch(foo, foo)).toBe(true); + }); + + // a, b are not ref equal but their properties are + it("When a and b are different objects whose properties point to the same object, returns true", () => { + const foo2 = { ...foo }; // pointer to bar is copied. + expect(Bun.deepMatch(foo, foo2)).toBe(true); + }); + + // a, b are structurally equal but share no pointers + it.skip("when a and b are structurally equal but share no pointers, returns true", () => { + const bar = makeCircular(); + expect(Bun.deepMatch(foo, bar)).toBe(true); + }); + + // a, b are neither ref or structurally equal + it("when a and b are different, returns false", () => { + const bar = { bar: undefined } as any; + bar.bar = bar; + expect(Bun.deepMatch(foo, bar)).toBe(false); + }); + }); + + describe("array inputs", () => { + it.each([ + // line break + [[1, 2, 3], [1, 2, 3], true], + ] as [any[], any[], boolean][])("Bun.deepMatch(%p, %p) === %p", (a, b, expected) => { + expect(Bun.deepMatch(a, b)).toBe(expected); + }); + }); + + it("does not work on functions", () => { + function foo() {} + function bar() {} + function baz(a) { + return a; + } + expect(Bun.deepMatch(foo, foo)).toBe(true); + expect(Bun.deepMatch(foo, bar)).toBe(true); + // FIXME + // expect(Bun.deepMatch(foo, baz)).toBe(false); + }); + + describe("Invalid arguments", () => { + it.each([ + [null, null], + [undefined, undefined], + [1, 1], + [true, true], + [true, false], + ["a", "a"], + [Symbol.for("a"), Symbol.for("a")], + [Symbol("a"), Symbol("a")], + ])("throws a TypeError for primitives", (a, b) => { + expect(() => Bun.deepMatch(a, b)).toThrow(TypeError); + }); + }); +}); diff --git a/test/js/bun/css/css-fuzz.test.ts b/test/js/bun/css/css-fuzz.test.ts index 668847af94..6a144bc607 100644 --- a/test/js/bun/css/css-fuzz.test.ts +++ b/test/js/bun/css/css-fuzz.test.ts @@ -183,12 +183,9 @@ if (!isCI) { const result = await Bun.build({ entrypoints: ["invalid.css"], experimentalCss: true, + throw: true, }); - if (result.logs.length > 0) { - throw new AggregateError("CSS parser returned logs", result.logs); - } - // We expect the parser to either throw an error or return a valid result // If it returns undefined/null, that's a potential issue if (result === undefined || result === null) { diff --git a/test/js/bun/css/css.test.ts b/test/js/bun/css/css.test.ts index da634ba716..2e4405893a 100644 --- a/test/js/bun/css/css.test.ts +++ b/test/js/bun/css/css.test.ts @@ -8,7 +8,37 @@ import path from "path"; import { attrTest, cssTest, indoc, minify_test, minifyTest, prefix_test } from "./util"; describe("css tests", () => { - test("edge case", () => { + describe("pseudo-class edge case", () => { + cssTest( + indoc`[type="file"]::file-selector-button:-moz-any() { + --pico-background-color: var(--pico-primary-hover-background); + --pico-border-color: var(--pico-primary-hover-border); + --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 #0000); + --pico-color: var(--pico-primary-inverse); + }`, + indoc`[type="file"]::-webkit-file-upload-button:-webkit-any() { + --pico-background-color: var(--pico-primary-hover-background); + --pico-border-color: var(--pico-primary-hover-border); + --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 #0000); + --pico-color: var(--pico-primary-inverse); + } + [type="file"]::file-selector-button:is() { + --pico-background-color: var(--pico-primary-hover-background); + --pico-border-color: var(--pico-primary-hover-border); + --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 #0000); + --pico-color: var(--pico-primary-inverse); + }`, + { + chrome: 80 << 16, + edge: 80 << 16, + firefox: 78 << 16, + safari: 14 << 16, + opera: 67 << 16, + }, + ); + }); + + test("calc edge case", () => { minifyTest( // Problem: the value is being printed as Infinity in our restrict_prec thing but the internal thing actually wants it as 3.40282e38px `.rounded-full { diff --git a/test/js/bun/css/doesnt_crash.test.ts b/test/js/bun/css/doesnt_crash.test.ts index c418f74e22..64eaa7172d 100644 --- a/test/js/bun/css/doesnt_crash.test.ts +++ b/test/js/bun/css/doesnt_crash.test.ts @@ -15,47 +15,60 @@ describe("doesnt_crash", async () => { files = readdirSync(files_dir).map(file => path.join(files_dir, file)); console.log("Tempdir", temp_dir); - files.map(absolute => { + files.forEach(absolute => { absolute = absolute.replaceAll("\\", "/"); const file = path.basename(absolute); - const outfile1 = path.join(temp_dir, "file-1" + file).replaceAll("\\", "/"); - const outfile2 = path.join(temp_dir, "file-2" + file).replaceAll("\\", "/"); - const outfile3 = path.join(temp_dir, "file-3" + file).replaceAll("\\", "/"); - const outfile4 = path.join(temp_dir, "file-4" + file).replaceAll("\\", "/"); - test(file, async () => { - { - const { stdout, stderr, exitCode } = - await Bun.$`${bunExe()} build --experimental-css ${absolute} --outfile=${outfile1}`.quiet().env(bunEnv); - expect(exitCode).toBe(0); - expect(stdout.toString()).not.toContain("error"); - expect(stderr.toString()).toBeEmpty(); - } + const configs: { target: string; minify: boolean }[] = [ + { target: "bun", minify: false }, + { target: "bun", minify: true }, + { target: "browser", minify: false }, + { target: "browser", minify: true }, + ]; - const { stdout, stderr, exitCode } = - await Bun.$`${bunExe()} build --experimental-css ${outfile1} --outfile=${outfile2}`.quiet().env(bunEnv); - expect(exitCode).toBe(0); - expect(stdout.toString()).not.toContain("error"); - expect(stderr.toString()).toBeEmpty(); - }); + for (const { target, minify } of configs) { + test(`${file} - ${minify ? "minify" : "not minify"}`, async () => { + const timeLog = `Transpiled ${file} - ${minify ? "minify" : "not minify"}`; + console.time(timeLog); + const { logs, outputs } = await Bun.build({ + entrypoints: [absolute], + experimentalCss: true, + minify: minify, + target, + throw: true, + }); + console.timeEnd(timeLog); - test(`(minify) ${file}`, async () => { - { - const { stdout, stderr, exitCode } = - await Bun.$`${bunExe()} build --experimental-css ${absolute} --minify --outfile=${outfile3}` - .quiet() - .env(bunEnv); - expect(exitCode).toBe(0); - expect(stdout.toString()).not.toContain("error"); - expect(stderr.toString()).toBeEmpty(); - } - const { stdout, stderr, exitCode } = - await Bun.$`${bunExe()} build --experimental-css ${outfile3} --minify --outfile=${outfile4}` - .quiet() - .env(bunEnv); - expect(exitCode).toBe(0); - expect(stdout.toString()).not.toContain("error"); - expect(stderr.toString()).toBeEmpty(); - }); + if (logs?.length) { + throw new Error(logs.join("\n")); + } + + expect(outputs.length).toBe(1); + const outfile1 = path.join(temp_dir, "file-1" + file).replaceAll("\\", "/"); + + await Bun.write(outfile1, outputs[0]); + + { + const timeLog = `Re-transpiled ${file} - ${minify ? "minify" : "not minify"}`; + console.time(timeLog); + console.log(" Transpiled file path:", outfile1); + const { logs, outputs } = await Bun.build({ + entrypoints: [outfile1], + experimentalCss: true, + target, + minify: minify, + throw: true, + }); + + if (logs?.length) { + throw new Error(logs.join("\n")); + } + + expect(outputs.length).toBe(1); + expect(await outputs[0].text()).not.toBeEmpty(); + console.timeEnd(timeLog); + } + }); + } }); }); diff --git a/test/js/bun/css/util.ts b/test/js/bun/css/util.ts index 09b4701739..0ec5da219c 100644 --- a/test/js/bun/css/util.ts +++ b/test/js/bun/css/util.ts @@ -30,12 +30,14 @@ export function prefix_test(source: string, expected: string, targets: Browsers) }); } -export function css_test(source: string, expected: string) { - return cssTest(source, expected); +export function css_test(source: string, expected: string, browsers?: Browsers) { + return cssTest(source, expected, browsers); } -export function cssTest(source: string, expected: string) { +export function cssTest(source: string, expected: string, browsers?: Browsers) { test(source, () => { - expect(testWithOptions(source, expected)).toEqualIgnoringWhitespace(expected); + const output = testWithOptions(source, expected, browsers); + console.log("Output", output); + expect(output).toEqualIgnoringWhitespace(expected); }); } diff --git a/test/js/bun/http/async-iterator-throws.fixture.js b/test/js/bun/http/async-iterator-throws.fixture.js index c35b68ecef..2227f9dccd 100644 --- a/test/js/bun/http/async-iterator-throws.fixture.js +++ b/test/js/bun/http/async-iterator-throws.fixture.js @@ -1,5 +1,6 @@ const server = Bun.serve({ port: 0, + idleTimeout: 0, async fetch(req) { return new Response( diff --git a/test/js/bun/http/big-form-data.fixture.js b/test/js/bun/http/big-form-data.fixture.js index b3ff882004..e91de65867 100644 --- a/test/js/bun/http/big-form-data.fixture.js +++ b/test/js/bun/http/big-form-data.fixture.js @@ -3,6 +3,7 @@ const content = Buffer.alloc(3 * 15360000, "Bun").toString(); const server = Bun.serve({ port: 0, + idleTimeout: 0, fetch: async req => { const data = await req.formData(); return new Response(data.get("name") === content ? "OK" : "NO"); diff --git a/test/js/bun/http/body-leak-test-fixture.ts b/test/js/bun/http/body-leak-test-fixture.ts index 1cc5a28298..5dbc7a1ea8 100644 --- a/test/js/bun/http/body-leak-test-fixture.ts +++ b/test/js/bun/http/body-leak-test-fixture.ts @@ -1,5 +1,6 @@ const server = Bun.serve({ port: 0, + idleTimeout: 0, async fetch(req: Request) { const url = req.url; if (url.endsWith("/report")) { diff --git a/test/js/bun/http/bun-server.test.ts b/test/js/bun/http/bun-server.test.ts index de430f36e3..771debc7f1 100644 --- a/test/js/bun/http/bun-server.test.ts +++ b/test/js/bun/http/bun-server.test.ts @@ -1,6 +1,6 @@ import type { Server, ServerWebSocket, Socket } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, rejectUnauthorizedScope } from "harness"; +import { bunEnv, bunExe, rejectUnauthorizedScope, tempDirWithFiles } from "harness"; import path from "path"; describe("Server", () => { @@ -317,8 +317,7 @@ describe("Server", () => { } }); - - test('server should return a body for a OPTIONS Request', async () => { + test("server should return a body for a OPTIONS Request", async () => { using server = Bun.serve({ port: 0, fetch(req) { @@ -327,16 +326,17 @@ describe("Server", () => { }); { const url = `http://${server.hostname}:${server.port}/`; - const response = await fetch(new Request(url, { - method: 'OPTIONS', - })); + const response = await fetch( + new Request(url, { + method: "OPTIONS", + }), + ); expect(await response.text()).toBe("Hello World!"); expect(response.status).toBe(200); expect(response.url).toBe(url); } }); - test("abort signal on server with stream", async () => { { let signalOnServer = false; @@ -456,7 +456,7 @@ describe("Server", () => { env: bunEnv, stderr: "pipe", }); - expect(stderr.toString('utf-8')).toBeEmpty(); + expect(stderr.toString("utf-8")).toBeEmpty(); expect(exitCode).toBe(0); }); }); @@ -768,3 +768,214 @@ test.skip("should be able to stream huge amounts of data", async () => { expect(written).toBe(CONTENT_LENGTH); expect(received).toBe(CONTENT_LENGTH); }, 30_000); + +describe("HEAD requests #15355", () => { + test("should be able to make HEAD requests with content-length or transfer-encoding (async)", async () => { + using server = Bun.serve({ + port: 0, + async fetch(req) { + await Bun.sleep(1); + if (req.method === "HEAD") { + if (req.url.endsWith("/content-length")) { + return new Response(null, { + headers: { + "Content-Length": "11", + }, + }); + } + return new Response(null, { + headers: { + "Transfer-Encoding": "chunked", + }, + }); + } + if (req.url.endsWith("/content-length")) { + return new Response("Hello World"); + } + return new Response(async function* () { + yield "Hello"; + await Bun.sleep(1); + yield " "; + await Bun.sleep(1); + yield "World"; + }); + }, + }); + + { + const response = await fetch(server.url + "/content-length"); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe("Hello World"); + } + { + const response = await fetch(server.url + "/chunked"); + expect(response.status).toBe(200); + expect(response.headers.get("transfer-encoding")).toBe("chunked"); + expect(await response.text()).toBe("Hello World"); + } + + { + const response = await fetch(server.url + "/content-length", { + method: "HEAD", + }); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe(""); + } + { + const response = await fetch(server.url + "/chunked", { + method: "HEAD", + }); + expect(response.status).toBe(200); + expect(response.headers.get("transfer-encoding")).toBe("chunked"); + expect(await response.text()).toBe(""); + } + }); + + test("should be able to make HEAD requests with content-length or transfer-encoding (sync)", async () => { + using server = Bun.serve({ + port: 0, + fetch(req) { + if (req.method === "HEAD") { + if (req.url.endsWith("/content-length")) { + return new Response(null, { + headers: { + "Content-Length": "11", + }, + }); + } + return new Response(null, { + headers: { + "Transfer-Encoding": "chunked", + }, + }); + } + if (req.url.endsWith("/content-length")) { + return new Response("Hello World"); + } + return new Response(async function* () { + yield "Hello"; + await Bun.sleep(1); + yield " "; + await Bun.sleep(1); + yield "World"; + }); + }, + }); + + { + const response = await fetch(server.url + "/content-length"); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe("Hello World"); + } + { + const response = await fetch(server.url + "/chunked"); + expect(response.status).toBe(200); + expect(response.headers.get("transfer-encoding")).toBe("chunked"); + expect(await response.text()).toBe("Hello World"); + } + + { + const response = await fetch(server.url + "/content-length", { + method: "HEAD", + }); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe(""); + } + { + const response = await fetch(server.url + "/chunked", { + method: "HEAD", + }); + expect(response.status).toBe(200); + expect(response.headers.get("transfer-encoding")).toBe("chunked"); + expect(await response.text()).toBe(""); + } + }); + + test("HEAD requests should not have body", async () => { + const dir = tempDirWithFiles("fsr", { + "hello": "Hello World", + }); + + const filename = path.join(dir, "hello"); + using server = Bun.serve({ + port: 0, + fetch(req) { + if (req.url.endsWith("/file")) { + return new Response(Bun.file(filename)); + } + return new Response("Hello World"); + }, + }); + + { + const response = await fetch(server.url); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe("Hello World"); + } + { + const response = await fetch(server.url + "/file"); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe("Hello World"); + } + + function doHead(server: Server, path: string): Promise<{ headers: string; body: string }> { + const { promise, resolve } = Promise.withResolvers(); + // use node net to make a HEAD request + const net = require("net"); + const url = new URL(server.url); + const socket = net.createConnection(url.port, url.hostname); + socket.write(`HEAD ${path} HTTP/1.1\r\nHost: ${url.hostname}:${url.port}\r\n\r\n`); + let body = ""; + let headers = ""; + socket.on("data", data => { + body += data.toString(); + if (!headers) { + const headerIndex = body.indexOf("\r\n\r\n"); + if (headerIndex !== -1) { + headers = body.slice(0, headerIndex); + body = body.slice(headerIndex + 4); + + setTimeout(() => { + // wait to see if we get extra data + resolve({ headers, body }); + socket.destroy(); + }, 100); + } + } + }); + return promise as Promise<{ headers: string; body: string }>; + } + { + const response = await fetch(server.url, { + method: "HEAD", + }); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe(""); + } + { + const response = await fetch(server.url + "/file", { + method: "HEAD", + }); + expect(response.status).toBe(200); + expect(response.headers.get("content-length")).toBe("11"); + expect(await response.text()).toBe(""); + } + { + const { headers, body } = await doHead(server, "/"); + expect(headers.toLowerCase()).toContain("content-length: 11"); + expect(body).toBe(""); + } + { + const { headers, body } = await doHead(server, "/file"); + expect(headers.toLowerCase()).toContain("content-length: 11"); + expect(body).toBe(""); + } + }); +}); diff --git a/test/js/bun/http/readable-stream-throws.fixture.js b/test/js/bun/http/readable-stream-throws.fixture.js index a1d8d4ec06..ff32f94507 100644 --- a/test/js/bun/http/readable-stream-throws.fixture.js +++ b/test/js/bun/http/readable-stream-throws.fixture.js @@ -1,6 +1,6 @@ const server = Bun.serve({ port: 0, - + idleTimeout: 0, error(err) { return new Response("Failed", { status: 555 }); }, diff --git a/test/js/bun/http/rejected-promise-fixture.js b/test/js/bun/http/rejected-promise-fixture.js index f63f774a2a..3b50761f1a 100644 --- a/test/js/bun/http/rejected-promise-fixture.js +++ b/test/js/bun/http/rejected-promise-fixture.js @@ -1,5 +1,6 @@ const server = Bun.serve({ hostname: "localhost", + idleTimeout: 0, async fetch() { throw new Error("Error"); }, diff --git a/test/js/bun/http/serve-body-leak.test.ts b/test/js/bun/http/serve-body-leak.test.ts index 40f260bea5..ed40ed810d 100644 --- a/test/js/bun/http/serve-body-leak.test.ts +++ b/test/js/bun/http/serve-body-leak.test.ts @@ -9,15 +9,9 @@ const totalCount = 10_000; const zeroCopyPayload = new Blob([payload]); const zeroCopyJSONPayload = new Blob([JSON.stringify({ bun: payload })]); -let url: URL; -let process: Subprocess<"ignore", "pipe", "inherit"> | null = null; -beforeEach(async () => { - if (process) { - process?.kill(); - } - - let defer = Promise.withResolvers(); - process = Bun.spawn([bunExe(), "--smol", join(import.meta.dirname, "body-leak-test-fixture.ts")], { +async function getURL() { + let defer = Promise.withResolvers(); + const process = Bun.spawn([bunExe(), "--smol", join(import.meta.dirname, "body-leak-test-fixture.ts")], { env: bunEnv, stdout: "inherit", stderr: "inherit", @@ -26,19 +20,17 @@ beforeEach(async () => { defer.resolve(message); }, }); - url = new URL(await defer.promise); + const url: URL = new URL(await defer.promise); process.unref(); - await warmup(); -}); -afterEach(() => { - process?.kill(); -}); + await warmup(url); + return { url, process }; +} -async function getMemoryUsage(): Promise { +async function getMemoryUsage(url: URL): Promise { return (await fetch(`${url.origin}/report`).then(res => res.json())) as number; } -async function warmup() { +async function warmup(url: URL) { var remaining = totalCount; while (remaining > 0) { @@ -54,17 +46,17 @@ async function warmup() { remaining -= batchSize; } // clean up memory before first test - await getMemoryUsage(); + await getMemoryUsage(url); } -async function callBuffering() { +async function callBuffering(url: URL) { const result = await fetch(`${url.origin}/buffering`, { method: "POST", body: zeroCopyPayload, }).then(res => res.text()); expect(result).toBe("Ok"); } -async function callJSONBuffering() { +async function callJSONBuffering(url: URL) { const result = await fetch(`${url.origin}/json-buffering`, { method: "POST", body: zeroCopyJSONPayload, @@ -72,35 +64,35 @@ async function callJSONBuffering() { expect(result).toBe("Ok"); } -async function callBufferingBodyGetter() { +async function callBufferingBodyGetter(url: URL) { const result = await fetch(`${url.origin}/buffering+body-getter`, { method: "POST", body: zeroCopyPayload, }).then(res => res.text()); expect(result).toBe("Ok"); } -async function callStreaming() { +async function callStreaming(url: URL) { const result = await fetch(`${url.origin}/streaming`, { method: "POST", body: zeroCopyPayload, }).then(res => res.text()); expect(result).toBe("Ok"); } -async function callIncompleteStreaming() { +async function callIncompleteStreaming(url: URL) { const result = await fetch(`${url.origin}/incomplete-streaming`, { method: "POST", body: zeroCopyPayload, }).then(res => res.text()); expect(result).toBe("Ok"); } -async function callStreamingEcho() { +async function callStreamingEcho(url: URL) { const result = await fetch(`${url.origin}/streaming-echo`, { method: "POST", body: zeroCopyPayload, }).then(res => res.text()); expect(result).toBe(payload); } -async function callIgnore() { +async function callIgnore(url: URL) { const result = await fetch(url, { method: "POST", body: zeroCopyPayload, @@ -108,8 +100,8 @@ async function callIgnore() { expect(result).toBe("Ok"); } -async function calculateMemoryLeak(fn: () => Promise) { - const start_memory = await getMemoryUsage(); +async function calculateMemoryLeak(fn: (url: URL) => Promise, url: URL) { + const start_memory = await getMemoryUsage(url); const memory_examples: Array = []; let peak_memory = start_memory; @@ -117,14 +109,14 @@ async function calculateMemoryLeak(fn: () => Promise) { while (remaining > 0) { const batch = new Array(batchSize); for (let j = 0; j < batchSize; j++) { - batch[j] = fn(); + batch[j] = fn(url); } await Promise.all(batch); remaining -= batchSize; // garbage collect and check memory usage every 1000 requests if (remaining > 0 && remaining % 1000 === 0) { - const report = await getMemoryUsage(); + const report = await getMemoryUsage(url); if (report > peak_memory) { peak_memory = report; } @@ -133,7 +125,7 @@ async function calculateMemoryLeak(fn: () => Promise) { } // wait for the last memory usage to be stable - const end_memory = await getMemoryUsage(); + const end_memory = await getMemoryUsage(url); if (end_memory > peak_memory) { peak_memory = end_memory; } @@ -160,7 +152,9 @@ for (const test_info of [ it.todoIf(skip)( testName, async () => { - const report = await calculateMemoryLeak(fn); + const { url, process } = await getURL(); + await using processHandle = process; + const report = await calculateMemoryLeak(fn, url); // peak memory is too high expect(report.peak_memory).not.toBeGreaterThan(report.start_memory * 2.5); // acceptable memory leak diff --git a/test/js/bun/http/serve.test.ts b/test/js/bun/http/serve.test.ts index daadea5474..7b83ca75ff 100644 --- a/test/js/bun/http/serve.test.ts +++ b/test/js/bun/http/serve.test.ts @@ -1,7 +1,7 @@ import { file, gc, Serve, serve, Server } from "bun"; import { afterAll, afterEach, describe, expect, it, mock } from "bun:test"; import { readFileSync, writeFileSync } from "fs"; -import { bunEnv, bunExe, dumpStats, isIPv4, isIPv6, isPosix, tls, tmpdirSync } from "harness"; +import { bunEnv, bunExe, dumpStats, isBroken, isIntelMacOS, isIPv4, isIPv6, isPosix, tls, tmpdirSync } from "harness"; import { join, resolve } from "path"; // import { renderToReadableStream } from "react-dom/server"; // import app_jsx from "./app.jsx"; @@ -213,92 +213,95 @@ for (let withDelay of [true, false]) { }); } } -describe("1000 uploads & downloads in batches of 64 do not leak ReadableStream", () => { - for (let isDirect of [true, false] as const) { - it( - isDirect ? "direct" : "default", - async () => { - const blob = new Blob([new Uint8Array(1024 * 768).fill(123)]); - Bun.gc(true); +describe.todoIf(isBroken && isIntelMacOS)( + "1000 uploads & downloads in batches of 64 do not leak ReadableStream", + () => { + for (let isDirect of [true, false] as const) { + it( + isDirect ? "direct" : "default", + async () => { + const blob = new Blob([new Uint8Array(1024 * 768).fill(123)]); + Bun.gc(true); - const expected = Bun.CryptoHasher.hash("sha256", blob, "base64"); - const initialCount = heapStats().objectTypeCounts.ReadableStream || 0; + const expected = Bun.CryptoHasher.hash("sha256", blob, "base64"); + const initialCount = heapStats().objectTypeCounts.ReadableStream || 0; - await runTest( - { - async fetch(req) { - var hasher = new Bun.SHA256(); - for await (const chunk of req.body) { - await Bun.sleep(0); - hasher.update(chunk); + await runTest( + { + async fetch(req) { + var hasher = new Bun.SHA256(); + for await (const chunk of req.body) { + await Bun.sleep(0); + hasher.update(chunk); + } + return new Response( + isDirect + ? new ReadableStream({ + type: "direct", + async pull(controller) { + await Bun.sleep(0); + controller.write(Buffer.from(hasher.digest("base64"))); + await controller.flush(); + controller.close(); + }, + }) + : new ReadableStream({ + async pull(controller) { + await Bun.sleep(0); + controller.enqueue(Buffer.from(hasher.digest("base64"))); + controller.close(); + }, + }), + ); + }, + }, + async server => { + const count = 1000; + async function callback() { + const response = await fetch(server.url, { + body: blob, + method: "POST", + }); + + // We are testing for ReadableStream leaks, so we use the ReadableStream here. + const chunks = []; + for await (const chunk of response.body) { + chunks.push(chunk); + } + + const digest = Buffer.from(Bun.concatArrayBuffers(chunks)).toString(); + + expect(digest).toBe(expected); + Bun.gc(false); } - return new Response( - isDirect - ? new ReadableStream({ - type: "direct", - async pull(controller) { - await Bun.sleep(0); - controller.write(Buffer.from(hasher.digest("base64"))); - await controller.flush(); - controller.close(); - }, - }) - : new ReadableStream({ - async pull(controller) { - await Bun.sleep(0); - controller.enqueue(Buffer.from(hasher.digest("base64"))); - controller.close(); - }, - }), + { + let remaining = count; + + const batchSize = 64; + while (remaining > 0) { + const promises = new Array(count); + for (let i = 0; i < batchSize && remaining > 0; i++) { + promises[i] = callback(); + } + await Promise.all(promises); + remaining -= batchSize; + } + } + + Bun.gc(true); + dumpStats(); + expect(heapStats().objectTypeCounts.ReadableStream).toBeWithin( + Math.max(initialCount - count / 2, 0), + initialCount + count / 2, ); }, - }, - async server => { - const count = 1000; - async function callback() { - const response = await fetch(server.url, { - body: blob, - method: "POST", - }); - - // We are testing for ReadableStream leaks, so we use the ReadableStream here. - const chunks = []; - for await (const chunk of response.body) { - chunks.push(chunk); - } - - const digest = Buffer.from(Bun.concatArrayBuffers(chunks)).toString(); - - expect(digest).toBe(expected); - Bun.gc(false); - } - { - let remaining = count; - - const batchSize = 64; - while (remaining > 0) { - const promises = new Array(count); - for (let i = 0; i < batchSize && remaining > 0; i++) { - promises[i] = callback(); - } - await Promise.all(promises); - remaining -= batchSize; - } - } - - Bun.gc(true); - dumpStats(); - expect(heapStats().objectTypeCounts.ReadableStream).toBeWithin( - Math.max(initialCount - count / 2, 0), - initialCount + count / 2, - ); - }, - ); - }, - 100000, - ); - } -}); + ); + }, + 100000, + ); + } + }, +); [200, 200n, 303, 418, 599, 599n].forEach(statusCode => { it(`should response with HTTP status code (${statusCode})`, async () => { diff --git a/test/js/bun/net/socket.test.ts b/test/js/bun/net/socket.test.ts index c60c267cee..e3735148f3 100644 --- a/test/js/bun/net/socket.test.ts +++ b/test/js/bun/net/socket.test.ts @@ -372,7 +372,7 @@ it("should allow large amounts of data to be sent and received", async () => { it("it should not crash when getting a ReferenceError on client socket open", async () => { using server = Bun.serve({ - port: 8080, + port: 0, hostname: "localhost", fetch() { return new Response("Hello World"); @@ -413,7 +413,7 @@ it("it should not crash when getting a ReferenceError on client socket open", as it("it should not crash when returning a Error on client socket open", async () => { using server = Bun.serve({ - port: 8080, + port: 0, hostname: "localhost", fetch() { return new Response("Hello World"); diff --git a/test/js/bun/s3/bun-write-leak-fixture.js b/test/js/bun/s3/bun-write-leak-fixture.js new file mode 100644 index 0000000000..0a7dabee81 --- /dev/null +++ b/test/js/bun/s3/bun-write-leak-fixture.js @@ -0,0 +1,31 @@ +// Avoid using String.prototype.repeat in this file because it's very slow in +// debug builds of JavaScriptCore +let MAX_ALLOWED_MEMORY_USAGE = 0; +let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; +const { randomUUID } = require("crypto"); + +const payload = new Buffer(1024 * 1024 * 1, "A".charCodeAt(0)).toString("utf-8"); +async function writeLargeFile() { + const dest = `s3://${randomUUID()}`; + await Bun.write(dest, payload); + await Bun.file(dest).unlink(); +} +async function run() { + { + // base line + await Promise.all(new Array(10).fill(writeLargeFile())); + await Bun.sleep(10); + Bun.gc(true); + } + MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + + { + await Promise.all(new Array(100).fill(writeLargeFile())); + Bun.gc(true); + } + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + if (rss > MAX_ALLOWED_MEMORY_USAGE) { + throw new Error("Memory usage is too high"); + } +} +await run(); diff --git a/test/js/bun/s3/s3-stream-leak-fixture.js b/test/js/bun/s3/s3-stream-leak-fixture.js new file mode 100644 index 0000000000..f2ad73edd7 --- /dev/null +++ b/test/js/bun/s3/s3-stream-leak-fixture.js @@ -0,0 +1,40 @@ +// Avoid using String.prototype.repeat in this file because it's very slow in +// debug builds of JavaScriptCore +let MAX_ALLOWED_MEMORY_USAGE = 0; +let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; +const { randomUUID } = require("crypto"); + +const s3Dest = randomUUID() + "-s3-stream-leak-fixture"; + +const s3file = Bun.s3(s3Dest); +async function readLargeFile() { + const stream = Bun.s3(s3Dest).stream(); + const reader = stream.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + } +} +async function run(inputType) { + await s3file.write(inputType); + Bun.gc(true); + + { + // base line + await Promise.all(new Array(10).fill(readLargeFile())); + await Bun.sleep(10); + Bun.gc(true); + } + MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + { + await Promise.all(new Array(100).fill(readLargeFile())); + Bun.gc(true); + } + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + if (rss > MAX_ALLOWED_MEMORY_USAGE) { + await s3file.unlink(); + throw new Error("Memory usage is too high"); + } +} +await run(new Buffer(1024 * 1024 * 1, "A".charCodeAt(0)).toString("utf-8")); +await s3file.unlink(); diff --git a/test/js/bun/s3/s3-text-leak-fixture.js b/test/js/bun/s3/s3-text-leak-fixture.js new file mode 100644 index 0000000000..e564a9edb5 --- /dev/null +++ b/test/js/bun/s3/s3-text-leak-fixture.js @@ -0,0 +1,35 @@ +// Avoid using String.prototype.repeat in this file because it's very slow in +// debug builds of JavaScriptCore +let MAX_ALLOWED_MEMORY_USAGE = 0; +let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; +const { randomUUID } = require("crypto"); + +const s3Dest = randomUUID() + "-s3-stream-leak-fixture"; + +const s3file = Bun.s3(s3Dest); +async function readLargeFile() { + await Bun.s3(s3Dest).text(); +} +async function run(inputType) { + await s3file.write(inputType); + Bun.gc(true); + + { + // base line + await Promise.all(new Array(10).fill(readLargeFile())); + await Bun.sleep(10); + Bun.gc(true); + } + MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + { + await Promise.all(new Array(100).fill(readLargeFile())); + Bun.gc(true); + } + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + if (rss > MAX_ALLOWED_MEMORY_USAGE) { + await s3file.unlink(); + throw new Error("Memory usage is too high"); + } +} +await run(new Buffer(1024 * 1024 * 1, "A".charCodeAt(0)).toString("utf-8")); +await s3file.unlink(); diff --git a/test/js/bun/s3/s3-write-leak-fixture.js b/test/js/bun/s3/s3-write-leak-fixture.js new file mode 100644 index 0000000000..019b8121a0 --- /dev/null +++ b/test/js/bun/s3/s3-write-leak-fixture.js @@ -0,0 +1,31 @@ +// Avoid using String.prototype.repeat in this file because it's very slow in +// debug builds of JavaScriptCore +let MAX_ALLOWED_MEMORY_USAGE = 0; +let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; +const dest = process.argv.at(-1); +const { randomUUID } = require("crypto"); +const payload = new Buffer(1024 * 1024 + 1, "A".charCodeAt(0)).toString("utf-8"); +async function writeLargeFile() { + const s3file = Bun.s3(randomUUID()); + await s3file.write(payload); + await s3file.unlink(); +} +async function run() { + { + // base line + await Promise.all(new Array(10).fill(writeLargeFile())); + await Bun.sleep(10); + Bun.gc(true); + } + MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + + { + await Promise.all(new Array(100).fill(writeLargeFile())); + Bun.gc(true); + } + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + if (rss > MAX_ALLOWED_MEMORY_USAGE) { + throw new Error("Memory usage is too high"); + } +} +await run(); diff --git a/test/js/bun/s3/s3-writer-leak-fixture.js b/test/js/bun/s3/s3-writer-leak-fixture.js new file mode 100644 index 0000000000..80a42b3795 --- /dev/null +++ b/test/js/bun/s3/s3-writer-leak-fixture.js @@ -0,0 +1,39 @@ +// Avoid using String.prototype.repeat in this file because it's very slow in +// debug builds of JavaScriptCore +let MAX_ALLOWED_MEMORY_USAGE = 0; +let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; +const dest = process.argv.at(-1); +const { randomUUID } = require("crypto"); +const payload = new Buffer(1024 * 256, "A".charCodeAt(0)).toString("utf-8"); +async function writeLargeFile() { + const s3file = Bun.s3(randomUUID()); + const writer = s3file.writer(); + writer.write(payload); + await Bun.sleep(10); + writer.write(payload); + await Bun.sleep(10); + writer.write(payload); + await Bun.sleep(10); + writer.write(payload); + await writer.end(); + await s3file.unlink(); +} +async function run() { + { + // base line + await Promise.all(new Array(10).fill(writeLargeFile())); + await Bun.sleep(10); + Bun.gc(true); + } + MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + + { + await Promise.all(new Array(100).fill(writeLargeFile())); + Bun.gc(true); + } + const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; + if (rss > MAX_ALLOWED_MEMORY_USAGE) { + throw new Error("Memory usage is too high"); + } +} +await run(); diff --git a/test/js/bun/s3/s3.leak.test.ts b/test/js/bun/s3/s3.leak.test.ts new file mode 100644 index 0000000000..9b25c622cb --- /dev/null +++ b/test/js/bun/s3/s3.leak.test.ts @@ -0,0 +1,172 @@ +import { describe, expect, it } from "bun:test"; +import { bunExe, bunEnv, getSecret, tempDirWithFiles } from "harness"; +import type { S3FileOptions } from "bun"; +import path from "path"; +const s3Options: S3FileOptions = { + accessKeyId: getSecret("S3_R2_ACCESS_KEY"), + secretAccessKey: getSecret("S3_R2_SECRET_KEY"), + endpoint: getSecret("S3_R2_ENDPOINT"), +}; + +const S3Bucket = getSecret("S3_R2_BUCKET"); + +describe.skipIf(!s3Options.accessKeyId)("s3", () => { + describe("leak tests", () => { + it( + "s3().stream() should not leak", + async () => { + const dir = tempDirWithFiles("s3-stream-leak-fixture", { + "s3-stream-leak-fixture.js": await Bun.file(path.join(import.meta.dir, "s3-stream-leak-fixture.js")).text(), + "out.bin": "here", + }); + + const dest = path.join(dir, "out.bin"); + + const { exitCode, stderr } = Bun.spawnSync( + [bunExe(), "--smol", path.join(dir, "s3-stream-leak-fixture.js"), dest], + { + env: { + ...bunEnv, + BUN_JSC_gcMaxHeapSize: "503316", + AWS_ACCESS_KEY_ID: s3Options.accessKeyId, + AWS_SECRET_ACCESS_KEY: s3Options.secretAccessKey, + AWS_ENDPOINT: s3Options.endpoint, + AWS_BUCKET: S3Bucket, + }, + stderr: "pipe", + stdout: "inherit", + stdin: "ignore", + }, + ); + expect(exitCode).toBe(0); + expect(stderr.toString()).toBe(""); + }, + 30 * 1000, + ); + it( + "s3().text() should not leak", + async () => { + const dir = tempDirWithFiles("s3-text-leak-fixture", { + "s3-text-leak-fixture.js": await Bun.file(path.join(import.meta.dir, "s3-text-leak-fixture.js")).text(), + "out.bin": "here", + }); + + const dest = path.join(dir, "out.bin"); + + const { exitCode, stderr } = Bun.spawnSync( + [bunExe(), "--smol", path.join(dir, "s3-text-leak-fixture.js"), dest], + { + env: { + ...bunEnv, + BUN_JSC_gcMaxHeapSize: "503316", + AWS_ACCESS_KEY_ID: s3Options.accessKeyId, + AWS_SECRET_ACCESS_KEY: s3Options.secretAccessKey, + AWS_ENDPOINT: s3Options.endpoint, + AWS_BUCKET: S3Bucket, + }, + stderr: "pipe", + stdout: "inherit", + stdin: "ignore", + }, + ); + expect(exitCode).toBe(0); + expect(stderr.toString()).toBe(""); + }, + 30 * 1000, + ); + it( + "s3().writer().write() should not leak", + async () => { + const dir = tempDirWithFiles("s3-writer-leak-fixture", { + "s3-writer-leak-fixture.js": await Bun.file(path.join(import.meta.dir, "s3-writer-leak-fixture.js")).text(), + "out.bin": "here", + }); + + const dest = path.join(dir, "out.bin"); + + const { exitCode, stderr } = Bun.spawnSync( + [bunExe(), "--smol", path.join(dir, "s3-writer-leak-fixture.js"), dest], + { + env: { + ...bunEnv, + BUN_JSC_gcMaxHeapSize: "503316", + AWS_ACCESS_KEY_ID: s3Options.accessKeyId, + AWS_SECRET_ACCESS_KEY: s3Options.secretAccessKey, + AWS_ENDPOINT: s3Options.endpoint, + AWS_BUCKET: S3Bucket, + }, + stderr: "pipe", + stdout: "inherit", + stdin: "ignore", + }, + ); + expect(exitCode).toBe(0); + expect(stderr.toString()).toBe(""); + }, + 30 * 1000, + ); + it( + "s3().write() should not leak", + async () => { + const dir = tempDirWithFiles("s3-write-leak-fixture", { + "s3-write-leak-fixture.js": await Bun.file(path.join(import.meta.dir, "s3-write-leak-fixture.js")).text(), + "out.bin": "here", + }); + + const dest = path.join(dir, "out.bin"); + + const { exitCode, stderr } = Bun.spawnSync( + [bunExe(), "--smol", path.join(dir, "s3-write-leak-fixture.js"), dest], + { + env: { + ...bunEnv, + BUN_JSC_gcMaxHeapSize: "503316", + AWS_ACCESS_KEY_ID: s3Options.accessKeyId, + AWS_SECRET_ACCESS_KEY: s3Options.secretAccessKey, + AWS_ENDPOINT: s3Options.endpoint, + AWS_BUCKET: S3Bucket, + }, + stderr: "pipe", + stdout: "inherit", + stdin: "ignore", + }, + ); + expect(exitCode).toBe(0); + expect(stderr.toString()).toBe(""); + }, + 30 * 1000, + ); + + it( + "Bun.write should not leak", + async () => { + const dir = tempDirWithFiles("bun-write-leak-fixture", { + "bun-write-leak-fixture.js": await Bun.file(path.join(import.meta.dir, "bun-write-leak-fixture.js")).text(), + "out.bin": "here", + }); + + const dest = path.join(dir, "out.bin"); + + const { exitCode, stderr } = Bun.spawnSync( + [bunExe(), "--smol", path.join(dir, "bun-write-leak-fixture.js"), dest], + { + env: { + ...bunEnv, + BUN_JSC_gcMaxHeapSize: "503316", + AWS_ACCESS_KEY_ID: s3Options.accessKeyId, + AWS_SECRET_ACCESS_KEY: s3Options.secretAccessKey, + AWS_ENDPOINT: s3Options.endpoint, + AWS_BUCKET: S3Bucket, + }, + stderr: "pipe", + stdout: "inherit", + stdin: "ignore", + }, + ); + expect(exitCode).toBe(0); + expect(stderr.toString()).toBe(""); + }, + 30 * 1000, + ); + }); +}); diff --git a/test/js/bun/s3/s3.test.ts b/test/js/bun/s3/s3.test.ts new file mode 100644 index 0000000000..7ac336fc6c --- /dev/null +++ b/test/js/bun/s3/s3.test.ts @@ -0,0 +1,617 @@ +import { describe, expect, it, beforeAll, afterAll } from "bun:test"; +import { bunExe, bunEnv, getSecret, tempDirWithFiles } from "harness"; +import { randomUUID } from "crypto"; +import { S3, s3, file } from "bun"; +import type { S3File, S3FileOptions } from "bun"; +import path from "path"; +const s3Options: S3FileOptions = { + accessKeyId: getSecret("S3_R2_ACCESS_KEY"), + secretAccessKey: getSecret("S3_R2_SECRET_KEY"), + endpoint: getSecret("S3_R2_ENDPOINT"), +}; + +const S3Bucket = getSecret("S3_R2_BUCKET"); + +function makePayLoadFrom(text: string, size: number): string { + while (Buffer.byteLength(text) < size) { + text += text; + } + return text.slice(0, size); +} + +// 10 MiB big enough to Multipart upload in more than one part +const bigPayload = makePayLoadFrom("Bun is the best runtime ever", 10 * 1024 * 1024); +const bigishPayload = makePayLoadFrom("Bun is the best runtime ever", 1 * 1024 * 1024); + +describe.skipIf(!s3Options.accessKeyId)("s3", () => { + for (let bucketInName of [true, false]) { + describe("fetch", () => { + describe(bucketInName ? "bucket in path" : "bucket in options", () => { + var tmp_filename: string; + const options = bucketInName ? s3Options : { ...s3Options, bucket: S3Bucket }; + beforeAll(async () => { + tmp_filename = bucketInName ? `s3://${S3Bucket}/${randomUUID()}` : `s3://${randomUUID()}`; + const result = await fetch(tmp_filename, { + method: "PUT", + body: "Hello Bun!", + s3: options, + }); + expect(result.status).toBe(200); + }); + + afterAll(async () => { + const result = await fetch(tmp_filename, { + method: "DELETE", + s3: options, + }); + expect(result.status).toBe(204); + }); + + it("should download file via fetch GET", async () => { + const result = await fetch(tmp_filename, { s3: options }); + expect(result.status).toBe(200); + expect(await result.text()).toBe("Hello Bun!"); + }); + + it("should download range", async () => { + const result = await fetch(tmp_filename, { + headers: { "range": "bytes=6-10" }, + s3: options, + }); + expect(result.status).toBe(206); + expect(await result.text()).toBe("Bun!"); + }); + + it("should check if a key exists or content-length", async () => { + const result = await fetch(tmp_filename, { + method: "HEAD", + s3: options, + }); + expect(result.status).toBe(200); // 404 if do not exists + expect(result.headers.get("content-length")).toBe("10"); // content-length + }); + + it("should check if a key does not exist", async () => { + const result = await fetch(tmp_filename + "-does-not-exist", { s3: options }); + expect(result.status).toBe(404); + }); + + it("should be able to set content-type", async () => { + { + const result = await fetch(tmp_filename, { + method: "PUT", + body: "Hello Bun!", + headers: { + "Content-Type": "application/json", + }, + s3: options, + }); + expect(result.status).toBe(200); + const response = await fetch(tmp_filename, { s3: options }); + expect(response.headers.get("content-type")).toStartWith("application/json"); + } + { + const result = await fetch(tmp_filename, { + method: "PUT", + body: "Hello Bun!", + headers: { + "Content-Type": "text/plain", + }, + s3: options, + }); + expect(result.status).toBe(200); + const response = await fetch(tmp_filename, { s3: options }); + expect(response.headers.get("content-type")).toStartWith("text/plain"); + } + }); + + it("should be able to upload large files", async () => { + // 10 MiB big enough to Multipart upload in more than one part + const buffer = Buffer.alloc(1 * 1024 * 1024, "a"); + { + await fetch(tmp_filename, { + method: "PUT", + body: async function* () { + for (let i = 0; i < 10; i++) { + await Bun.sleep(10); + yield buffer; + } + }, + s3: options, + }).then(res => res.text()); + + const result = await fetch(tmp_filename, { method: "HEAD", s3: options }); + expect(result.status).toBe(200); + expect(result.headers.get("content-length")).toBe((buffer.byteLength * 10).toString()); + } + }, 10_000); + }); + }); + + describe("Bun.S3", () => { + describe(bucketInName ? "bucket in path" : "bucket in options", () => { + const tmp_filename = bucketInName ? `${S3Bucket}/${randomUUID()}` : `${randomUUID()}`; + const options = bucketInName ? s3Options : { ...s3Options, bucket: S3Bucket }; + beforeAll(async () => { + const file = new S3(tmp_filename, options); + await file.write("Hello Bun!"); + }); + + afterAll(async () => { + const file = new S3(tmp_filename, options); + await file.unlink(); + }); + + it("should download file via Bun.s3().text()", async () => { + const file = new S3(tmp_filename, options); + const text = await file.text(); + expect(text).toBe("Hello Bun!"); + }); + + it("should download range", async () => { + const file = new S3(tmp_filename, options); + const text = await file.slice(6, 10).text(); + expect(text).toBe("Bun!"); + }); + + it("should check if a key exists or content-length", async () => { + const file = new S3(tmp_filename, options); + const exists = await file.exists(); + expect(exists).toBe(true); + const contentLength = await file.size; + expect(contentLength).toBe(10); + }); + + it("should check if a key does not exist", async () => { + const file = new S3(tmp_filename + "-does-not-exist", options); + const exists = await file.exists(); + expect(exists).toBe(false); + }); + + it("should be able to set content-type", async () => { + { + const s3file = new S3(tmp_filename, { ...options, type: "text/css" }); + await s3file.write("Hello Bun!"); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("text/css"); + } + { + const s3file = new S3(tmp_filename, options); + await s3file.write("Hello Bun!", { type: "text/plain" }); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("text/plain"); + } + + { + const s3file = new S3(tmp_filename, options); + const writer = s3file.writer({ type: "application/json" }); + writer.write("Hello Bun!"); + await writer.end(); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("application/json"); + } + + { + await S3.upload(tmp_filename, "Hello Bun!", { ...options, type: "application/xml" }); + const response = await fetch(s3(tmp_filename, options).presign()); + expect(response.headers.get("content-type")).toStartWith("application/xml"); + } + }); + + it("should be able to upload large files using S3.upload + readable Request", async () => { + { + await S3.upload( + tmp_filename, + new Request("https://example.com", { + method: "PUT", + body: async function* () { + for (let i = 0; i < 10; i++) { + if (i % 5 === 0) { + await Bun.sleep(10); + } + yield bigishPayload; + } + }, + }), + options, + ); + expect(await S3.size(tmp_filename, options)).toBe(Buffer.byteLength(bigishPayload) * 10); + } + }, 10_000); + + it("should be able to upload large files in one go using S3.upload", async () => { + { + await S3.upload(tmp_filename, bigPayload, options); + expect(await S3.size(tmp_filename, options)).toBe(Buffer.byteLength(bigPayload)); + expect(await new S3(tmp_filename, options).text()).toBe(bigPayload); + } + }, 10_000); + + it("should be able to upload large files in one go using S3File.write", async () => { + { + const s3File = new S3(tmp_filename, options); + await s3File.write(bigPayload); + expect(await s3File.size).toBe(Buffer.byteLength(bigPayload)); + expect(await s3File.text()).toBe(bigPayload); + } + }, 10_000); + }); + }); + + describe("Bun.file", () => { + describe(bucketInName ? "bucket in path" : "bucket in options", () => { + const tmp_filename = bucketInName ? `s3://${S3Bucket}/${randomUUID()}` : `s3://${randomUUID()}`; + const options = bucketInName ? s3Options : { ...s3Options, bucket: S3Bucket }; + beforeAll(async () => { + const s3file = file(tmp_filename, options); + await s3file.write("Hello Bun!"); + }); + + afterAll(async () => { + const s3file = file(tmp_filename, options); + await s3file.unlink(); + }); + + it("should download file via Bun.file().text()", async () => { + const s3file = file(tmp_filename, options); + const text = await s3file.text(); + expect(text).toBe("Hello Bun!"); + }); + + it("should download range", async () => { + const s3file = file(tmp_filename, options); + const text = await s3file.slice(6, 10).text(); + expect(text).toBe("Bun!"); + }); + + it("should check if a key exists or content-length", async () => { + const s3file = file(tmp_filename, options); + const exists = await s3file.exists(); + expect(exists).toBe(true); + const contentLength = await s3file.size; + expect(contentLength).toBe(10); + }); + + it("should check if a key does not exist", async () => { + const s3file = file(tmp_filename + "-does-not-exist", options); + const exists = await s3file.exists(); + expect(exists).toBe(false); + }); + + it("should be able to set content-type", async () => { + { + const s3file = file(tmp_filename, { ...options, type: "text/css" }); + await s3file.write("Hello Bun!"); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("text/css"); + } + { + const s3file = file(tmp_filename, options); + await s3file.write("Hello Bun!", { type: "text/plain" }); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("text/plain"); + } + + { + const s3file = file(tmp_filename, options); + const writer = s3file.writer({ type: "application/json" }); + writer.write("Hello Bun!"); + await writer.end(); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("application/json"); + } + }); + + it("should be able to upload large files in one go using Bun.write", async () => { + { + await Bun.write(file(tmp_filename, options), bigPayload); + expect(await S3.size(tmp_filename, options)).toBe(Buffer.byteLength(bigPayload)); + expect(await file(tmp_filename, options).text()).toEqual(bigPayload); + } + }, 15_000); + + it("should be able to upload large files in one go using S3File.write", async () => { + { + const s3File = file(tmp_filename, options); + await s3File.write(bigPayload); + expect(await s3File.size).toBe(Buffer.byteLength(bigPayload)); + expect(await s3File.text()).toBe(bigPayload); + } + }, 10_000); + }); + }); + + describe("Bun.s3", () => { + describe(bucketInName ? "bucket in path" : "bucket in options", () => { + const tmp_filename = bucketInName ? `${S3Bucket}/${randomUUID()}` : `${randomUUID()}`; + const options = bucketInName ? s3Options : { ...s3Options, bucket: S3Bucket }; + beforeAll(async () => { + const s3file = s3(tmp_filename, options); + await s3file.write("Hello Bun!"); + }); + + afterAll(async () => { + const s3file = s3(tmp_filename, options); + await s3file.unlink(); + }); + + it("should download file via Bun.s3().text()", async () => { + const s3file = s3(tmp_filename, options); + const text = await s3file.text(); + expect(text).toBe("Hello Bun!"); + }); + + it("should download range", async () => { + const s3file = s3(tmp_filename, options); + const text = await s3file.slice(6, 10).text(); + expect(text).toBe("Bun!"); + }); + + it("should check if a key exists or content-length", async () => { + const s3file = s3(tmp_filename, options); + const exists = await s3file.exists(); + expect(exists).toBe(true); + const contentLength = await s3file.size; + expect(contentLength).toBe(10); + }); + + it("should check if a key does not exist", async () => { + const s3file = s3(tmp_filename + "-does-not-exist", options); + const exists = await s3file.exists(); + expect(exists).toBe(false); + }); + + it("presign url", async () => { + const s3file = s3(tmp_filename, options); + const response = await fetch(s3file.presign()); + expect(response.status).toBe(200); + expect(await response.text()).toBe("Hello Bun!"); + }); + + it("should be able to set content-type", async () => { + { + const s3file = s3(tmp_filename, { ...options, type: "text/css" }); + await s3file.write("Hello Bun!"); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("text/css"); + } + { + const s3file = s3(tmp_filename, options); + await s3file.write("Hello Bun!", { type: "text/plain" }); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("text/plain"); + } + + { + const s3file = s3(tmp_filename, options); + const writer = s3file.writer({ type: "application/json" }); + writer.write("Hello Bun!"); + await writer.end(); + const response = await fetch(s3file.presign()); + expect(response.headers.get("content-type")).toStartWith("application/json"); + } + }); + + it("should be able to upload large files in one go using S3.upload", async () => { + { + await S3.upload(s3(tmp_filename, options), bigPayload); + expect(await S3.size(tmp_filename, options)).toBe(Buffer.byteLength(bigPayload)); + } + }, 10_000); + + it("should be able to upload large files in one go using Bun.write", async () => { + { + await Bun.write(s3(tmp_filename, options), bigPayload); + expect(await S3.size(tmp_filename, options)).toBe(Buffer.byteLength(bigPayload)); + expect(await s3(tmp_filename, options).text()).toBe(bigPayload); + } + }, 10_000); + + it("should be able to upload large files in one go using S3File.write", async () => { + { + const s3File = s3(tmp_filename, options); + await s3File.write(bigPayload); + expect(await s3File.size).toBe(Buffer.byteLength(bigPayload)); + expect(await s3File.text()).toBe(bigPayload); + } + }, 10_000); + + describe("readable stream", () => { + afterAll(async () => { + await Promise.all([ + s3(tmp_filename + "-readable-stream", options).unlink(), + s3(tmp_filename + "-readable-stream-big", options).unlink(), + ]); + }); + it("should work with small files", async () => { + const s3file = s3(tmp_filename + "-readable-stream", options); + await s3file.write("Hello Bun!"); + const stream = s3file.stream(); + const reader = stream.getReader(); + let bytes = 0; + let chunks: Array = []; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + bytes += value?.length ?? 0; + + if (value) chunks.push(value as Buffer); + } + expect(bytes).toBe(10); + expect(Buffer.concat(chunks)).toEqual(Buffer.from("Hello Bun!")); + }); + it("should work with large files ", async () => { + const s3file = s3(tmp_filename + "-readable-stream-big", options); + await s3file.write(bigishPayload); + const stream = s3file.stream(); + const reader = stream.getReader(); + let bytes = 0; + let chunks: Array = []; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + bytes += value?.length ?? 0; + if (value) chunks.push(value as Buffer); + } + expect(bytes).toBe(Buffer.byteLength(bigishPayload)); + expect(Buffer.concat(chunks).toString()).toBe(bigishPayload); + }, 30_000); + }); + }); + }); + } + + describe("credentials", () => { + it("should error with invalid access key id", async () => { + [s3, (...args) => new S3(...args), file].forEach(fn => { + const s3file = fn("s3://bucket/credentials-test", { + ...s3Options, + accessKeyId: "invalid", + }); + expect(s3file.write("Hello Bun!")).rejects.toThrow(); + }); + }); + it("should error with invalid secret key id", async () => { + [s3, (...args) => new S3(...args), file].forEach(fn => { + const s3file = fn("s3://bucket/credentials-test", { + ...s3Options, + secretAccessKey: "invalid", + }); + expect(s3file.write("Hello Bun!")).rejects.toThrow(); + }); + }); + + it("should error with invalid endpoint", async () => { + [s3, (...args) => new S3(...args), file].forEach(fn => { + const s3file = fn("s3://bucket/credentials-test", { + ...s3Options, + endpoint: "🙂.🥯", + }); + expect(s3file.write("Hello Bun!")).rejects.toThrow(); + }); + }); + + it("should error with invalid endpoint", async () => { + [s3, (...args) => new S3(...args), file].forEach(fn => { + const s3file = fn("s3://bucket/credentials-test", { + ...s3Options, + endpoint: "..asd.@%&&&%%", + }); + expect(s3file.write("Hello Bun!")).rejects.toThrow(); + }); + }); + + it("should error with invalid bucket", async () => { + [s3, (...args) => new S3(...args), file].forEach(fn => { + const s3file = fn("s3://credentials-test", { + ...s3Options, + bucket: "invalid", + }); + expect(s3file.write("Hello Bun!")).rejects.toThrow(); + }); + }); + }); + + describe("S3 static methods", () => { + describe("presign", () => { + it("should work", async () => { + const s3file = s3("s3://bucket/credentials-test", s3Options); + const url = s3file.presign(); + expect(url).toBeDefined(); + expect(url.includes("X-Amz-Expires=86400")).toBe(true); + expect(url.includes("X-Amz-Date")).toBe(true); + expect(url.includes("X-Amz-Signature")).toBe(true); + expect(url.includes("X-Amz-Credential")).toBe(true); + expect(url.includes("X-Amz-Algorithm")).toBe(true); + expect(url.includes("X-Amz-SignedHeaders")).toBe(true); + }); + it("should work with expires", async () => { + const s3file = s3("s3://bucket/credentials-test", s3Options); + const url = s3file.presign({ + expiresIn: 10, + }); + expect(url).toBeDefined(); + expect(url.includes("X-Amz-Expires=10")).toBe(true); + expect(url.includes("X-Amz-Date")).toBe(true); + expect(url.includes("X-Amz-Signature")).toBe(true); + expect(url.includes("X-Amz-Credential")).toBe(true); + expect(url.includes("X-Amz-Algorithm")).toBe(true); + expect(url.includes("X-Amz-SignedHeaders")).toBe(true); + }); + + it("S3.presign should work", async () => { + const url = S3.presign("s3://bucket/credentials-test", { + ...s3Options, + expiresIn: 10, + }); + expect(url).toBeDefined(); + expect(url.includes("X-Amz-Expires=10")).toBe(true); + expect(url.includes("X-Amz-Date")).toBe(true); + expect(url.includes("X-Amz-Signature")).toBe(true); + expect(url.includes("X-Amz-Credential")).toBe(true); + expect(url.includes("X-Amz-Algorithm")).toBe(true); + expect(url.includes("X-Amz-SignedHeaders")).toBe(true); + }); + + it("S3.presign endpoint should work", async () => { + const url = S3.presign("s3://bucket/credentials-test", { + ...s3Options, + expiresIn: 10, + endpoint: "https://s3.bun.sh", + }); + expect(url).toBeDefined(); + expect(url.includes("https://s3.bun.sh")).toBe(true); + expect(url.includes("X-Amz-Expires=10")).toBe(true); + expect(url.includes("X-Amz-Date")).toBe(true); + expect(url.includes("X-Amz-Signature")).toBe(true); + expect(url.includes("X-Amz-Credential")).toBe(true); + expect(url.includes("X-Amz-Algorithm")).toBe(true); + expect(url.includes("X-Amz-SignedHeaders")).toBe(true); + }); + + it("S3.presign endpoint should work", async () => { + const url = S3.presign("s3://folder/credentials-test", { + ...s3Options, + expiresIn: 10, + bucket: "my-bucket", + }); + expect(url).toBeDefined(); + expect(url.includes("my-bucket")).toBe(true); + expect(url.includes("X-Amz-Expires=10")).toBe(true); + expect(url.includes("X-Amz-Date")).toBe(true); + expect(url.includes("X-Amz-Signature")).toBe(true); + expect(url.includes("X-Amz-Credential")).toBe(true); + expect(url.includes("X-Amz-Algorithm")).toBe(true); + expect(url.includes("X-Amz-SignedHeaders")).toBe(true); + }); + }); + + it("exists, upload, size, unlink should work", async () => { + const filename = randomUUID(); + const fullPath = `s3://${S3Bucket}/${filename}`; + expect(await S3.exists(fullPath, s3Options)).toBe(false); + + await S3.upload(fullPath, "bun", s3Options); + expect(await S3.exists(fullPath, s3Options)).toBe(true); + expect(await S3.size(fullPath, s3Options)).toBe(3); + await S3.unlink(fullPath, s3Options); + expect(await S3.exists(fullPath, s3Options)).toBe(false); + }); + + it("should be able to upload a slice", async () => { + const filename = randomUUID(); + const fullPath = `s3://${S3Bucket}/${filename}`; + const s3file = s3(fullPath, s3Options); + await s3file.write("Hello Bun!"); + const slice = s3file.slice(6, 10); + expect(await slice.text()).toBe("Bun!"); + expect(await s3file.text()).toBe("Hello Bun!"); + + await S3.upload(fullPath, slice, s3Options); + const text = await s3file.text(); + expect(text).toBe("Bun!"); + await s3file.unlink(); + }); + }); +}); diff --git a/test/js/bun/sqlite/sqlite.test.js b/test/js/bun/sqlite/sqlite.test.js index ae57c6cad8..28795ec13f 100644 --- a/test/js/bun/sqlite/sqlite.test.js +++ b/test/js/bun/sqlite/sqlite.test.js @@ -1242,27 +1242,33 @@ it("should dispose", () => { it("can continue to use existing statements after database has been GC'd", async () => { let called = false; - const registry = new FinalizationRegistry(() => { - called = true; - }); - function leakTheStatement() { - const db = new Database(":memory:"); - console.log("---"); - db.exec("CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)"); - db.exec("INSERT INTO foo (name) VALUES ('foo')"); - const prepared = db.prepare("SELECT * FROM foo"); - registry.register(db); - return prepared; + async function run() { + const registry = new FinalizationRegistry(() => { + called = true; + }); + function leakTheStatement() { + const db = new Database(":memory:"); + console.log("---"); + db.exec("CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)"); + db.exec("INSERT INTO foo (name) VALUES ('foo')"); + const prepared = db.prepare("SELECT * FROM foo"); + registry.register(db); + return prepared; + } + + const stmt = leakTheStatement(); + + Bun.gc(true); + await Bun.sleep(1); + Bun.gc(true); + expect(stmt.all()).toEqual([{ id: 1, name: "foo" }]); + stmt.finalize(); + expect(() => stmt.all()).toThrow(); } - - const stmt = leakTheStatement(); - + await run(); Bun.gc(true); await Bun.sleep(1); Bun.gc(true); - expect(stmt.all()).toEqual([{ id: 1, name: "foo" }]); - stmt.finalize(); - expect(() => stmt.all()).toThrow(); if (!isWindows) { // on Windows, FinalizationRegistry is more flaky than on POSIX. expect(called).toBe(true); diff --git a/test/js/bun/test/mock-fn.test.js b/test/js/bun/test/mock-fn.test.js index c9c29fd63d..4f813f7ffa 100644 --- a/test/js/bun/test/mock-fn.test.js +++ b/test/js/bun/test/mock-fn.test.js @@ -100,13 +100,16 @@ describe("mock()", () => { } test("are callable", () => { const fn = jest.fn(() => 42); + expect(fn).not.toHaveBeenCalledOnce(); expect(fn()).toBe(42); + expect(fn).toHaveBeenCalledOnce(); expect(fn).toHaveBeenCalled(); expect(fn).toHaveBeenCalledTimes(1); expect(fn.mock.calls).toHaveLength(1); expect(fn.mock.calls[0]).toBeEmpty(); expect(fn).toHaveBeenLastCalledWith(); expect(fn()).toBe(42); + expect(fn).not.toHaveBeenCalledOnce(); expect(fn).toHaveBeenCalledTimes(2); expect(fn.mock.calls).toHaveLength(2); expect(fn.mock.calls[1]).toBeEmpty(); diff --git a/test/js/bun/test/stack.test.ts b/test/js/bun/test/stack.test.ts index ac3fde49c0..dd32267d49 100644 --- a/test/js/bun/test/stack.test.ts +++ b/test/js/bun/test/stack.test.ts @@ -93,11 +93,16 @@ test("throwing inside an error suppresses the error and prints the stack", async const { stderr, exitCode } = result; - expect(stderr.toString().trim()).toStartWith( - `error: My custom error message - at http://example.com/test.js:42 - `.trim(), - ); + expect(stderr.toString().trim().split("\n").slice(0, -1).join("\n").trim()).toMatchInlineSnapshot(` +"error: My custom error message +{ + message: "My custom error message", + name: [Getter], + line: 42, + sourceURL: "http://example.com/test.js", +} + at http://example.com/test.js:42" +`); expect(exitCode).toBe(1); }); @@ -108,8 +113,10 @@ test("throwing inside an error suppresses the error and continues printing prope const { stderr, exitCode } = result; - expect(stderr.toString().trim()).toStartWith( - 'ENOENT: No such file or directory\n errno: -2\n syscall: "open"\n path: "this-file-path-is-bad"'.trim(), - ); + expect(stderr.toString().trim()).toStartWith(`ENOENT: No such file or directory + path: "this-file-path-is-bad", + syscall: "open", + errno: -2, +`); expect(exitCode).toBe(1); }); diff --git a/test/js/bun/util/error-gc-test.test.js b/test/js/bun/util/error-gc-test.test.js index da1cfecb02..19bb1210b9 100644 --- a/test/js/bun/util/error-gc-test.test.js +++ b/test/js/bun/util/error-gc-test.test.js @@ -50,9 +50,12 @@ test("error gc test #3", () => { // - The test failure message gets a non-sensical error test("error gc test #4", () => { const tmp = tmpdirSync(); - for (let i = 0; i < 1000; i++) { + const base = Buffer.from(join(tmp, "does", "not", "exist").repeat(10)); + + function iterate() { // Use a long-enough string for it to be obvious if we leak memory - let path = join(tmp, join("does", "not", "exist").repeat(10)); + // Use .toString() on the Buffer to ensure we clone the string every time. + let path = base.toString(); try { readFileSync(path); throw new Error("unreachable"); @@ -61,8 +64,14 @@ test("error gc test #4", () => { throw e; } - const inspected = Bun.inspect(e); + path = path.replaceAll("\\", "/"); + if (e.path) { + e.path = e.path.replaceAll("\\", "/"); + } + + let inspected = Bun.inspect(e); Bun.gc(true); + inspected = inspected.replaceAll("\\", "/"); // Deliberately avoid using .toContain() directly to avoid // BunString shenanigins. @@ -80,4 +89,8 @@ test("error gc test #4", () => { Bun.gc(true); } } + + for (let i = 0; i < 1000; i++) { + iterate(); + } }); diff --git a/test/js/bun/util/heap-snapshot.test.ts b/test/js/bun/util/heap-snapshot.test.ts new file mode 100644 index 0000000000..2bd5aaa995 --- /dev/null +++ b/test/js/bun/util/heap-snapshot.test.ts @@ -0,0 +1,187 @@ +import { describe, it, expect } from "bun:test"; +import { parseHeapSnapshot, summarizeByType } from "./heap"; +import { estimateShallowMemoryUsageOf } from "bun:jsc"; + +describe("Native types report their size correctly", () => { + it("FormData", () => { + var formData = new FormData(); + globalThis.formData = formData; + let original = estimateShallowMemoryUsageOf(formData); + formData.append("a", Buffer.alloc(1024 * 1024 * 8, "abc").toString()); + const afterBuffer = estimateShallowMemoryUsageOf(formData); + expect(afterBuffer).toBeGreaterThan(original + 1024 * 1024 * 8); + formData.append("a", new Blob([Buffer.alloc(1024 * 1024 * 2, "yooa")])); + const afterBlob = estimateShallowMemoryUsageOf(formData); + expect(afterBlob).toBeGreaterThan(afterBuffer + 1024 * 1024 * 2); + formData.append("a", new Blob([Buffer.alloc(1024 * 1024 * 2, "yooa")])); + const afterBlob2 = estimateShallowMemoryUsageOf(formData); + expect(afterBlob2).toBeGreaterThan(afterBlob + 1024 * 1024 * 2); + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + expect(summariesMap.get("FormData")?.size).toBeGreaterThan( + // Test that FormData includes the size of the strings and the blobs + 1024 * 1024 * 8 + 1024 * 1024 * 2 + 1024 * 1024 * 2, + ); + + delete globalThis.formData; + }); + + it("Request", () => { + var request = new Request("https://example.com", { + body: Buffer.alloc(1024 * 1024 * 2, "yoo"), + }); + globalThis.request = request; + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + expect(summariesMap.get("Request")?.size).toBeGreaterThan(1024 * 1024 * 2); + expect(summariesMap.get("Request")?.size).toBeLessThan(1024 * 1024 * 4); + + delete globalThis.request; + }); + + it("Response", () => { + var response = new Response(Buffer.alloc(1024 * 1024 * 4, "yoo"), { + headers: { + "Content-Type": "text/plain", + }, + }); + globalThis.response = response; + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + expect(summariesMap.get("Response")?.size).toBeGreaterThan(1024 * 1024 * 4); + + delete globalThis.response; + }); + + it("URL", () => { + const searchParams = new URLSearchParams(); + for (let i = 0; i < 1000; i++) { + searchParams.set(`a${i}`, `b${i}`); + } + + var url = new URL("https://example.com"); + globalThis.url = url; + url.search = searchParams.toString(); + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + expect(summariesMap.get("URL")?.size).toBeGreaterThan(searchParams.toString().length); + + delete globalThis.url; + }); + + it("URLSearchParams", () => { + const searchParams = new URLSearchParams(); + globalThis.searchParams = searchParams; + const original = estimateShallowMemoryUsageOf(searchParams); + for (let i = 0; i < 1000; i++) { + searchParams.set(`a${i}`, `b${i}`); + } + const after = estimateShallowMemoryUsageOf(searchParams); + expect(after).toBeGreaterThan(original + 1000 * 2); + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + expect(summariesMap.get("URLSearchParams")?.size).toBeGreaterThan( + // toString() is greater because of the "?" and "&" + [...searchParams.keys(), ...searchParams.values()].join("").length, + ); + + delete globalThis.searchParams; + }); + + it("Headers", () => { + const headers = new Headers(); + const original = estimateShallowMemoryUsageOf(headers); + for (let i = 0; i < 1000; i++) { + headers.set(`a${i}`, `b${i}`); + } + const after = estimateShallowMemoryUsageOf(headers); + expect(after).toBeGreaterThan(original + 1000 * 2); + + globalThis.headers = headers; + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + // Test that Headers includes the size of the strings + expect(summariesMap.get("Headers")?.size).toBeGreaterThan([...headers.keys(), ...headers.values()].join("").length); + + delete globalThis.headers; + }); + + it("WebSocket + ServerWebSocket + Request", async () => { + using server = Bun.serve({ + port: 0, + websocket: { + open(ws) {}, + drain(ws) {}, + message(ws, message) { + const before = estimateShallowMemoryUsageOf(ws); + ws.send(message); + const after = estimateShallowMemoryUsageOf(ws); + const bufferedAmount = ws.getBufferedAmount(); + if (bufferedAmount > 0) { + expect(after).toBeGreaterThan(before + bufferedAmount); + } + }, + }, + + fetch(req, server) { + const before = estimateShallowMemoryUsageOf(req); + server.upgrade(req); + const after = estimateShallowMemoryUsageOf(req); + + // We detach the request context from the request object on upgrade. + expect(after).toBeLessThan(before); + + return new Response("hello"); + }, + }); + const ws = new WebSocket(server.url); + const original = estimateShallowMemoryUsageOf(ws); + globalThis.ws = ws; + + const { promise, resolve } = Promise.withResolvers(); + ws.onopen = () => { + // Send more than we can possibly send in a single message + ws.send(Buffer.alloc(1024 * 128, "hello")); + }; + ws.onmessage = event => { + resolve(event.data); + }; + await promise; + + const after = estimateShallowMemoryUsageOf(ws); + expect(after).toBeGreaterThan(original + 1024 * 128); + + const snapshot = Bun.generateHeapSnapshot(); + const parsed = parseHeapSnapshot(snapshot); + const summariesList = Array.from(summarizeByType(parsed)); + const summariesMap = new Map(summariesList.map(summary => [summary.name, summary])); + + expect(summariesMap.get("WebSocket")?.size).toBeGreaterThan(1024 * 128); + + delete globalThis.ws; + }); +}); diff --git a/test/js/bun/util/heap.ts b/test/js/bun/util/heap.ts new file mode 100644 index 0000000000..dd696dc6bb --- /dev/null +++ b/test/js/bun/util/heap.ts @@ -0,0 +1,244 @@ +//! This is a decently effecient heap profiler reader. + +export interface HeapSnapshotData { + nodes: Float64Array; + edges: Float64Array; + nodeClassNames: string[]; + edgeNames: string[]; + edgeTypes: string[]; + type: "Inspector" | "GCDebugging"; +} + +const enum NodeLayout { + ID = 0, + SIZE = 1, + CLASS_NAME_IDX = 2, + FLAGS = 3, + LABEL_IDX = 4, + CELL_ADDR = 5, + WRAPPED_ADDR = 6, + STRIDE_GCDEBUGGING = 7, + STRIDE_INSPECTOR = 4, +} + +const enum EdgeLayout { + FROM_NODE = 0, + TO_NODE = 1, + TYPE = 2, + NAME_OR_INDEX = 3, + STRIDE = 4, +} + +const enum TypeStatsLayout { + NAME = 0, + SIZE = 1, + COUNT = 2, + RETAINED_SIZE = 3, + STRIDE = 4, +} + +export class TypeStats { + constructor(private stats: Array) {} + + [Symbol.iterator]() { + const stats = this.stats; + let i = 0; + var iterator: IterableIterator<{ + name: string; + size: number; + count: number; + retainedSize: number; + }> = { + [Symbol.iterator]() { + return iterator; + }, + next() { + if (i >= stats.length) { + return { done: true, value: undefined }; + } + const name = stats[i++] as string; + const size = stats[i++] as number; + const count = stats[i++] as number; + const retainedSize = stats[i++] as number; + return { + done: false, + value: { name, size, count, retainedSize }, + }; + }, + }; + return iterator; + } +} + +export function parseHeapSnapshot(data: { + nodes: number[]; + edges: number[]; + nodeClassNames: string[]; + edgeNames: string[]; + edgeTypes: string[]; + type: "Inspector" | "GCDebugging"; +}): HeapSnapshotData { + return { + nodes: new Float64Array(data.nodes), + edges: new Float64Array(data.edges), + nodeClassNames: data.nodeClassNames, + edgeNames: data.edgeNames, + edgeTypes: data.edgeTypes, + type: data.type, + }; +} + +function getNodeStride(data: HeapSnapshotData): number { + return data.type === "GCDebugging" ? NodeLayout.STRIDE_GCDEBUGGING : NodeLayout.STRIDE_INSPECTOR; +} + +export function summarizeByType(data: HeapSnapshotData): TypeStats { + const nodeStride = getNodeStride(data); + const statsArray = new Array(data.nodeClassNames.length * TypeStatsLayout.STRIDE); + + // Initialize the stats array + for (let i = 0, nameIdx = 0; nameIdx < data.nodeClassNames.length; nameIdx++) { + statsArray[i++] = data.nodeClassNames[nameIdx]; + statsArray[i++] = 0; // size + statsArray[i++] = 0; // count + statsArray[i++] = 0; // retained size + } + + // Calculate retained sizes + const retainedSizes = computeRetainedSizes(data); + + // Accumulate stats + for (let i = 0, nodeIndex = 0, nodes = data.nodes; i < nodes.length; i += nodeStride, nodeIndex++) { + const classNameIdx = nodes[i + NodeLayout.CLASS_NAME_IDX]; + const size = nodes[i + NodeLayout.SIZE]; + + const statsOffset = classNameIdx * TypeStatsLayout.STRIDE; + statsArray[statsOffset + 1] += size; // Add to size + statsArray[statsOffset + 2] += 1; // Increment count + statsArray[statsOffset + 3] += retainedSizes[nodeIndex]; // Add retained size + } + + return new TypeStats(statsArray); +} + +// TODO: this is wrong. +function computeRetainedSizes(data: HeapSnapshotData): Float64Array { + const nodeStride = getNodeStride(data); + const nodeCount = Math.floor(data.nodes.length / nodeStride); + + // Initialize arrays + const retainedSizes = new Float64Array(nodeCount); + const processedNodes = new Uint8Array(nodeCount); + const incomingEdgeCount = new Uint32Array(nodeCount); + const isRoot = new Uint8Array(nodeCount); + + // Initialize with shallow sizes + for (let i = 0; i < nodeCount; i++) { + const offset = i * nodeStride; + retainedSizes[i] = data.nodes[offset + NodeLayout.SIZE] || 0; + } + + // Mark node 0 as root + isRoot[0] = 1; + + // Build outgoing edges list and count incoming edges + const outgoingEdges = new Array(nodeCount); + for (let i = 0; i < nodeCount; i++) { + outgoingEdges[i] = []; + } + + // First pass - count incoming edges + for (let i = 0; i < data.edges.length; i += EdgeLayout.STRIDE) { + const fromNode = data.edges[i + EdgeLayout.FROM_NODE]; + const toNode = data.edges[i + EdgeLayout.TO_NODE]; + + if (fromNode >= 0 && fromNode < nodeCount && toNode >= 0 && toNode < nodeCount && fromNode !== toNode) { + incomingEdgeCount[toNode]++; + outgoingEdges[fromNode].push(toNode); + } + } + + // Find roots - nodes with no incoming edges + for (let i = 1; i < nodeCount; i++) { + if (incomingEdgeCount[i] === 0) { + isRoot[i] = 1; + } + } + + function computeRetainedSize(nodeIndex: number): number { + if (processedNodes[nodeIndex]) return retainedSizes[nodeIndex]; + processedNodes[nodeIndex] = 1; + + let size = retainedSizes[nodeIndex]; + + // If we're a root, include everything we retain + if (isRoot[nodeIndex]) { + const outgoing = outgoingEdges[nodeIndex]; + for (let i = 0; i < outgoing.length; i++) { + const childIndex = outgoing[i]; + if (childIndex !== nodeIndex) { + size += computeRetainedSize(childIndex); + } + } + } else { + // For non-roots, only include uniquely retained children + const outgoing = outgoingEdges[nodeIndex]; + for (let i = 0; i < outgoing.length; i++) { + const childIndex = outgoing[i]; + if (childIndex !== nodeIndex && incomingEdgeCount[childIndex] === 1) { + size += computeRetainedSize(childIndex); + } + } + } + + retainedSizes[nodeIndex] = size; + return size; + } + + // Process roots first + for (let i = 0; i < nodeCount; i++) { + if (isRoot[i]) { + computeRetainedSize(i); + } + } + + // Process remaining nodes + for (let i = 0; i < nodeCount; i++) { + if (!processedNodes[i]) { + computeRetainedSize(i); + } + } + + return retainedSizes; +} + +if (import.meta.main) { + let json = JSON.parse(require("fs").readFileSync(process.argv[2], "utf-8")); + if (json?.snapshot) { + json = json.snapshot; + } + + const snapshot = parseHeapSnapshot(json); + + const classNames = summarizeByType(snapshot); + const numberFormatter = new Intl.NumberFormat(); + const formatBytes = (bytes: number) => { + if (bytes < 1024) { + return `${bytes} bytes`; + } + if (bytes < 1024 * 1024) { + return `${(bytes / 1024).toFixed(2)} KB`; + } + + return `${(bytes / 1024 / 1024).toFixed(2)} MB`; + }; + + let results = Array.from(classNames).sort((a, b) => b.retainedSize - a.retainedSize); + for (const { name, size, count, retainedSize } of results) { + console.log( + `${name}: ${numberFormatter.format(count)} instances, ${formatBytes( + size, + )} size, ${formatBytes(retainedSize)} retained`, + ); + } +} diff --git a/test/js/bun/util/inspect-error.test.js b/test/js/bun/util/inspect-error.test.js index a65edd62b3..8e438f9cc1 100644 --- a/test/js/bun/util/inspect-error.test.js +++ b/test/js/bun/util/inspect-error.test.js @@ -1,22 +1,51 @@ -import { expect, test } from "bun:test"; +import { expect, test, describe, jest } from "bun:test"; test("error.cause", () => { const err = new Error("error 1"); const err2 = new Error("error 2", { cause: err }); expect( Bun.inspect(err2) - .replaceAll(import.meta.dir, "[dir]") - .replaceAll("\\", "/"), - ).toMatchSnapshot(); + .replaceAll("\\", "/") + .replaceAll(import.meta.dir.replaceAll("\\", "/"), "[dir]"), + ).toMatchInlineSnapshot(` +"1 | import { expect, test, describe, jest } from "bun:test"; +2 | +3 | test("error.cause", () => { +4 | const err = new Error("error 1"); +5 | const err2 = new Error("error 2", { cause: err }); + ^ +error: error 2 + at [dir]/inspect-error.test.js:5:16 + +1 | import { expect, test, describe, jest } from "bun:test"; +2 | +3 | test("error.cause", () => { +4 | const err = new Error("error 1"); + ^ +error: error 1 + at [dir]/inspect-error.test.js:4:15 +" +`); }); test("Error", () => { const err = new Error("my message"); expect( Bun.inspect(err) - .replaceAll(import.meta.dir, "[dir]") - .replaceAll("\\", "/"), - ).toMatchSnapshot(); + .replaceAll("\\", "/") + .replaceAll(import.meta.dir.replaceAll("\\", "/"), "[dir]"), + ).toMatchInlineSnapshot(` +"27 | " +28 | \`); +29 | }); +30 | +31 | test("Error", () => { +32 | const err = new Error("my message"); + ^ +error: my message + at [dir]/inspect-error.test.js:32:15 +" +`); }); test("BuildMessage", async () => { @@ -26,9 +55,19 @@ test("BuildMessage", async () => { } catch (e) { expect( Bun.inspect(e) - .replaceAll(import.meta.dir, "[dir]") - .replaceAll("\\", "/"), - ).toMatchSnapshot(); + .replaceAll("\\", "/") + .replaceAll(import.meta.dir.replaceAll("\\", "/"), "[dir]"), + ).toMatchInlineSnapshot(` +"2 | const duplicateConstDecl = 456; + ^ +error: "duplicateConstDecl" has already been declared + at [dir]/inspect-error-fixture-bad.js:2:7 + +1 | const duplicateConstDecl = 123; + ^ +note: "duplicateConstDecl" was originally declared here + at [dir]/inspect-error-fixture-bad.js:1:7" +`); } }); @@ -66,11 +105,23 @@ test("Error inside minified file (no color) ", () => { expect( normalizeError( Bun.inspect(e) - .replaceAll(import.meta.dir, "[dir]") .replaceAll("\\", "/") + .replaceAll(import.meta.dir.replaceAll("\\", "/"), "[dir]") .trim(), ), - ).toMatchSnapshot(); + ).toMatchInlineSnapshot(` +"21 | exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Z; +22 | exports.cache=function(a){return function(){var b=U.current;if(!b)return a.apply(null,arguments);var c=b.getCacheForType(V);b=c.get(a);void 0===b&&(b=W(),c.set(a,b));c=0;for(var f=arguments.length;c { normalizeError( stripANSIColors( Bun.inspect(e, { colors: true }) - .replaceAll(import.meta.dir, "[dir]") .replaceAll("\\", "/") + .replaceAll(import.meta.dir.replaceAll("\\", "/"), "[dir]") .trim(), ).trim(), ), - ).toMatchSnapshot(); + ).toMatchInlineSnapshot(` +"21 | exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Z; +22 | exports.cache=function(a){return function(){var b=U.current;if(!b)return a.apply(null,arguments);var c=b.getCacheForType(V);b=c.get(a);void 0===b&&(b=W(),c.set(a,b));c=0;for(var f=arguments.length;c { + const err = new Error("my message"); + expect( + require("util") + .inspect(err) + .replaceAll("\\", "/") + .replaceAll(import.meta.path.replaceAll("\\", "/"), "[file]"), + ).toMatchInlineSnapshot(` +"Error: my message + at ([file]:160:19)" +`); +}); + +describe("observable properties", () => { + for (let property of ["sourceURL", "line", "column"]) { + test(`${property} is observable`, () => { + const mock = jest.fn(); + const err = new Error("my message"); + Object.defineProperty(err, property, { + get: mock, + enumerable: true, + configurable: true, + }); + expect(mock).not.toHaveBeenCalled(); + Bun.inspect(err); + expect(mock).not.toHaveBeenCalled(); + }); + } +}); + +test("error.stack throwing an error doesn't lead to a crash", () => { + const err = new Error("my message"); + Object.defineProperty(err, "stack", { + get: () => { + throw new Error("my message"); + }, + enumerable: true, + configurable: true, + }); + expect(() => { + throw err; + }).toThrow(); +}); diff --git a/test/js/bun/util/reportError.test.ts b/test/js/bun/util/reportError.test.ts index 14f0466af3..f1e6008991 100644 --- a/test/js/bun/util/reportError.test.ts +++ b/test/js/bun/util/reportError.test.ts @@ -18,5 +18,49 @@ test("reportError", () => { // remove bun version from output output = output.split("\n").slice(0, -2).join("\n"); - expect(output).toMatchSnapshot(); + expect(output.replaceAll("\\", "/").replaceAll("/reportError.ts", "[file]")).toMatchInlineSnapshot( + ` +"1 | reportError(new Error("reportError Test!")); + ^ +error: reportError Test! + at [file]:1:13 +error: true +true +error: false +false +error: null +null +error: 123 +123 +error: Infinity +Infinity +error: NaN +NaN +error: NaN +NaN +error + +error +Uint8Array(1) [ 0 ] +error +Uint8Array(0) [ ] +error +ArrayBuffer(0) [ ] +error +ArrayBuffer(1) [ 0 ] +error: string +string +error +[] +error +[ 123, null ] +error +{} +error +[ + {} +] +" +`, + ); }); diff --git a/test/js/bun/websocket/websocket-server-fixture.js b/test/js/bun/websocket/websocket-server-fixture.js index 8e140b4b1f..23d5bb55ed 100644 --- a/test/js/bun/websocket/websocket-server-fixture.js +++ b/test/js/bun/websocket/websocket-server-fixture.js @@ -9,6 +9,7 @@ let pending = []; using server = Bun.serve({ port: 0, + idleTimeout: 0, websocket: { open(ws) { globalThis.sockets ??= []; diff --git a/test/js/node/child_process/child_process-node.test.js b/test/js/node/child_process/child_process-node.test.js index 42931a8dcf..1d4a7a4305 100644 --- a/test/js/node/child_process/child_process-node.test.js +++ b/test/js/node/child_process/child_process-node.test.js @@ -659,7 +659,7 @@ describe("fork", () => { code: "ERR_INVALID_ARG_TYPE", name: "TypeError", message: expect.stringContaining( - `The "modulePath" argument must be of type string, Buffer or URL. Received: `, + `The "modulePath" argument must be of type string, Buffer, or URL. Received `, ), }), ); @@ -718,7 +718,7 @@ describe("fork", () => { expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE", name: "TypeError", - message: expect.stringContaining(`The "options" argument must be of type object. Received: `), + message: expect.stringContaining(`The "options" argument must be of type object. Received `), }), ); }); diff --git a/test/js/node/child_process/child_process.test.ts b/test/js/node/child_process/child_process.test.ts index f70772d421..a259c6897d 100644 --- a/test/js/node/child_process/child_process.test.ts +++ b/test/js/node/child_process/child_process.test.ts @@ -96,7 +96,7 @@ describe("spawn()", () => { it("should disallow invalid filename", () => { // @ts-ignore expect(() => spawn(123)).toThrow({ - message: 'The "file" argument must be of type string. Received 123', + message: 'The "file" argument must be of type string. Received type number (123)', code: "ERR_INVALID_ARG_TYPE", }); }); diff --git a/test/js/node/fs/fs.test.ts b/test/js/node/fs/fs.test.ts index 3307edae09..d4f52071e5 100644 --- a/test/js/node/fs/fs.test.ts +++ b/test/js/node/fs/fs.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, spyOn } from "bun:test"; -import { bunEnv, bunExe, gc, getMaxFD, isIntelMacOS, isWindows, tempDirWithFiles, tmpdirSync } from "harness"; +import { bunEnv, bunExe, gc, getMaxFD, isBroken, isIntelMacOS, isWindows, tempDirWithFiles, tmpdirSync } from "harness"; import { isAscii } from "node:buffer"; import fs, { closeSync, @@ -2225,110 +2225,73 @@ describe("fs.ReadStream", () => { }); describe("createWriteStream", () => { - it("simple write stream finishes", async () => { - const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStream.txt`; - const stream = createWriteStream(path); + it.todoIf(isBroken && isWindows)("simple write stream finishes", async () => { + const streamPath = join(tmpdirSync(), "create-write-stream.txt"); + const { promise: done, resolve, reject } = Promise.withResolvers(); + + const stream = createWriteStream(streamPath); + stream.on("error", reject); + stream.on("finish", resolve); stream.write("Test file written successfully"); stream.end(); - return await new Promise((resolve, reject) => { - stream.on("error", e => { - reject(e); - }); - - stream.on("finish", () => { - expect(readFileSync(path, "utf8")).toBe("Test file written successfully"); - resolve(true); - }); - }); + await done; + expect(readFileSync(streamPath, "utf8")).toBe("Test file written successfully"); }); it("writing null throws ERR_STREAM_NULL_VALUES", async () => { - const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStreamNulls.txt`; - const stream = createWriteStream(path); - try { - stream.write(null); - expect(() => {}).toThrow(Error); - } catch (exception: any) { - expect(exception.code).toBe("ERR_STREAM_NULL_VALUES"); - } + const streamPath = join(tmpdirSync(), "create-write-stream-nulls.txt"); + const stream = createWriteStream(streamPath); + expect.toThrowWithCode(() => stream.write(null), "ERR_STREAM_NULL_VALUES"); }); it("writing null throws ERR_STREAM_NULL_VALUES (objectMode: true)", async () => { - const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStreamNulls.txt`; - const stream = createWriteStream(path, { + const streamPath = join(tmpdirSync(), "create-write-stream-nulls-object-mode.txt"); + const stream = createWriteStream(streamPath, { // @ts-ignore-next-line objectMode: true, }); - try { - stream.write(null); - expect(() => {}).toThrow(Error); - } catch (exception: any) { - expect(exception.code).toBe("ERR_STREAM_NULL_VALUES"); - } + expect.toThrowWithCode(() => stream.write(null), "ERR_STREAM_NULL_VALUES"); }); it("writing false throws ERR_INVALID_ARG_TYPE", async () => { - const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStreamFalse.txt`; - const stream = createWriteStream(path); - try { - stream.write(false); - expect(() => {}).toThrow(Error); - } catch (exception: any) { - expect(exception.code).toBe("ERR_INVALID_ARG_TYPE"); - } + const streamPath = join(tmpdirSync(), "create-write-stream-false.txt"); + const stream = createWriteStream(streamPath); + expect.toThrowWithCode(() => stream.write(false), "ERR_INVALID_ARG_TYPE"); }); it("writing false throws ERR_INVALID_ARG_TYPE (objectMode: true)", async () => { - const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStreamFalse.txt`; - const stream = createWriteStream(path, { + const streamPath = join(tmpdirSync(), "create-write-stream-false-object-mode.txt"); + const stream = createWriteStream(streamPath, { // @ts-ignore-next-line objectMode: true, }); - try { - stream.write(false); - expect(() => {}).toThrow(Error); - } catch (exception: any) { - expect(exception.code).toBe("ERR_INVALID_ARG_TYPE"); - } + expect.toThrowWithCode(() => stream.write(false), "ERR_INVALID_ARG_TYPE"); }); it("writing in append mode should not truncate the file", async () => { - const path = `${tmpdir()}/fs.test.ts/${Date.now()}.createWriteStreamAppend.txt`; - const stream = createWriteStream(path, { + const streamPath = join(tmpdirSync(), "create-write-stream-append.txt"); + const stream = createWriteStream(streamPath, { // @ts-ignore-next-line flags: "a", }); + + const { promise: done1, resolve: resolve1, reject: reject1 } = Promise.withResolvers(); + stream.on("error", reject1); + stream.on("finish", resolve1); stream.write("first line\n"); stream.end(); + await done1; - await new Promise((resolve, reject) => { - stream.on("error", e => { - reject(e); - }); - - stream.on("finish", () => { - resolve(true); - }); - }); - - const stream2 = createWriteStream(path, { - // @ts-ignore-next-line - flags: "a", - }); + const { promise: done2, resolve: resolve2, reject: reject2 } = Promise.withResolvers(); + const stream2 = createWriteStream(streamPath, { flags: "a" }); + stream2.on("error", reject2); + stream2.on("finish", resolve2); stream2.write("second line\n"); stream2.end(); + await done2; - return await new Promise((resolve, reject) => { - stream2.on("error", e => { - reject(e); - }); - - stream2.on("finish", () => { - expect(readFileSync(path, "utf8")).toBe("first line\nsecond line\n"); - resolve(true); - }); - }); + expect(readFileSync(streamPath, "utf8")).toBe("first line\nsecond line\n"); }); it("should emit open and call close callback", done => { diff --git a/test/js/node/os/os.test.js b/test/js/node/os/os.test.js index 469089c2a6..a887b113cd 100644 --- a/test/js/node/os/os.test.js +++ b/test/js/node/os/os.test.js @@ -222,3 +222,22 @@ describe("toString works like node", () => { }); } }); + +it("getPriority system error object", () => { + try { + os.getPriority(-1); + expect.unreachable(); + } catch (err) { + expect(err.name).toBe("SystemError"); + expect(err.message).toBe("A system error occurred: uv_os_getpriority returned ESRCH (no such process)"); + expect(err.code).toBe("ERR_SYSTEM_ERROR"); + expect(err.info).toEqual({ + errno: isWindows ? -4040 : -3, + code: "ESRCH", + message: "no such process", + syscall: "uv_os_getpriority", + }); + expect(err.errno).toBe(isWindows ? -4040 : -3); + expect(err.syscall).toBe("uv_os_getpriority"); + } +}); diff --git a/test/js/node/path/15704.test.js b/test/js/node/path/15704.test.js new file mode 100644 index 0000000000..81c12fb9a7 --- /dev/null +++ b/test/js/node/path/15704.test.js @@ -0,0 +1,10 @@ +import path from "path"; +import assert from "assert"; + +test("too-long path names do not crash when joined", () => { + const length = 4096; + const tooLengthyFolderName = Array.from({ length }).fill("b").join(""); + assert.equal(path.join(tooLengthyFolderName), "b".repeat(length)); + assert.equal(path.win32.join(tooLengthyFolderName), "b".repeat(length)); + assert.equal(path.posix.join(tooLengthyFolderName), "b".repeat(length)); +}); diff --git a/test/js/node/process/process-on-fixture.ts b/test/js/node/process/process-on-fixture.ts new file mode 100644 index 0000000000..61d57ecad4 --- /dev/null +++ b/test/js/node/process/process-on-fixture.ts @@ -0,0 +1,26 @@ +export function initialize() { + const handler = () => { + console.log("SIGINT"); + }; + + const handler2 = () => { + console.log("SIGTERM"); + }; + + process.on("SIGINT", handler); + process.on("SIGTERM", handler2); + process.off("SIGTERM", handler2); + process.off("SIGINT", handler); + + process.on("SIGINT", handler); + process.on("SIGTERM", handler2); + process.off("SIGTERM", handler2); + process.off("SIGINT", handler); + + process.on("SIGINT", handler); + process.on("SIGTERM", handler2); + process.off("SIGTERM", handler2); + process.off("SIGINT", handler); +} + +initialize(); diff --git a/test/js/node/process/process-on.test.ts b/test/js/node/process/process-on.test.ts new file mode 100644 index 0000000000..092626b233 --- /dev/null +++ b/test/js/node/process/process-on.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, it } from "bun:test"; +import { bunEnv, tempDirWithFiles } from "harness"; +import { bunExe } from "harness"; +import path from "path"; + +describe("process.on", () => { + it("when called from the main thread", () => { + const result = Bun.spawnSync({ + cmd: [bunExe(), path.join(__dirname, "process-on-fixture.ts")], + env: bunEnv, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }); + + expect(result.exitCode).toBe(0); + }); + + it("should work inside --compile", () => { + const dir = tempDirWithFiles("process-on-test", { + "process-on-fixture.ts": require("fs").readFileSync(require.resolve("./process-on-fixture.ts"), "utf-8"), + "package.json": `{ + "name": "process-on-test", + "type": "module", + "scripts": { + "start": "bun run process-on-fixture.ts" + } + }`, + }); + const result1 = Bun.spawnSync({ + cmd: [bunExe(), "build", "--compile", path.join(dir, "./process-on-fixture.ts"), "--outfile=./out"], + env: bunEnv, + cwd: dir, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }); + + expect(result1.exitCode).toBe(0); + + const result2 = Bun.spawnSync({ + cmd: ["./out"], + env: bunEnv, + cwd: dir, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }); + expect(result2.exitCode).toBe(0); + }); + + it("should work inside a macro", () => { + const dir = tempDirWithFiles("process-on-test", { + "process-on-fixture.ts": require("fs").readFileSync(require.resolve("./process-on-fixture.ts"), "utf-8"), + "entry.ts": `import { initialize } from "./process-on-fixture.ts" with {type: "macro"}; + initialize();`, + "package.json": `{ + "name": "process-on-test", + "type": "module", + "scripts": { + "start": "bun run entry.ts" + } + }`, + }); + + expect( + Bun.spawnSync({ + cmd: [bunExe(), "build", "--target=bun", path.join(dir, "entry.ts"), "--outfile=./out.ts"], + env: bunEnv, + cwd: dir, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }).exitCode, + ).toBe(0); + + const result2 = Bun.spawnSync({ + cmd: [bunExe(), "run", "./out.ts"], + env: bunEnv, + cwd: dir, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }); + expect(result2.exitCode).toBe(0); + }); +}); diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 09f9bb28f4..7055550847 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -123,8 +123,8 @@ it("process.hrtime()", async () => { const end = process.hrtime(start); expect(end[0]).toBe(0); - // Flaky on Ubuntu. - await Bun.sleep(0); + // Flaky on Ubuntu & Windows. + await Bun.sleep(16); const end2 = process.hrtime(); expect(end2[1] > start[1]).toBe(true); @@ -1056,3 +1056,10 @@ describe("process.exitCode", () => { it("process._exiting", () => { expect(process._exiting).toBe(false); }); + +it("process.memoryUsage.arrayBuffers", () => { + const initial = process.memoryUsage().arrayBuffers; + const array = new ArrayBuffer(1024 * 1024 * 16); + array.buffer; + expect(process.memoryUsage().arrayBuffers).toBeGreaterThanOrEqual(initial + 16 * 1024 * 1024); +}); diff --git a/test/js/node/test/common/gc.js b/test/js/node/test/common/gc.js index 8e2c5ee5da..3774f81cc8 100644 --- a/test/js/node/test/common/gc.js +++ b/test/js/node/test/common/gc.js @@ -120,8 +120,22 @@ async function checkIfCollectableByCounting(fn, ctor, count, waitTime = 20) { throw new Error(`${name} cannot be collected`); } +var finalizationRegistry = new FinalizationRegistry(heldValue => { + heldValue.ongc(); +}) + +function onGC(value, holder) { + if (holder?.ongc) { + + finalizationRegistry.register(value, { ongc: holder.ongc }); + } +} + module.exports = { checkIfCollectable, runAndBreathe, checkIfCollectableByCounting, + onGC, }; + + diff --git a/test/js/node/test/common/index.js b/test/js/node/test/common/index.js index 9a044595e8..6b5d1079ff 100644 --- a/test/js/node/test/common/index.js +++ b/test/js/node/test/common/index.js @@ -119,6 +119,10 @@ if (process.argv.length === 2 && // If the binary is build without `intl` the inspect option is // invalid. The test itself should handle this case. (process.features.inspector || !flag.startsWith('--inspect'))) { + if (flag === "--expose-gc" && process.versions.bun) { + globalThis.gc ??= () => Bun.gc(true); + break; + } console.log( 'NOTE: The test started as a child_process using these flags:', inspect(flags), @@ -130,7 +134,9 @@ if (process.argv.length === 2 && if (result.signal) { process.kill(0, result.signal); } else { - process.exit(result.status); + // Ensure we don't call the "exit" callbacks, as that will cause the + // test to fail when it may have passed in the child process. + process.kill(process.pid, result.status); } } } @@ -1212,3 +1218,5 @@ module.exports = new Proxy(common, { return obj[prop]; }, }); + + diff --git a/test/js/node/test/parallel/test-async-hooks-asyncresource-constructor.js b/test/js/node/test/parallel/test-async-hooks-asyncresource-constructor.js new file mode 100644 index 0000000000..8b504aa7a7 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-asyncresource-constructor.js @@ -0,0 +1,41 @@ +'use strict'; + +// This tests that AsyncResource throws an error if bad parameters are passed + +require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); +const { AsyncResource } = async_hooks; + +// Setup init hook such parameters are validated +async_hooks.createHook({ + init() {} +}).enable(); + +assert.throws(() => { + return new AsyncResource(); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', +}); + +assert.throws(() => { + new AsyncResource(''); +}, { + code: 'ERR_ASYNC_TYPE', + name: 'TypeError', +}); + +assert.throws(() => { + new AsyncResource('type', -4); +}, { + code: 'ERR_INVALID_ASYNC_ID', + name: 'RangeError', +}); + +assert.throws(() => { + new AsyncResource('type', Math.PI); +}, { + code: 'ERR_INVALID_ASYNC_ID', + name: 'RangeError', +}); diff --git a/test/js/node/test/parallel/test-async-hooks-constructor.js b/test/js/node/test/parallel/test-async-hooks-constructor.js new file mode 100644 index 0000000000..62ec854108 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-constructor.js @@ -0,0 +1,21 @@ +'use strict'; + +// This tests that AsyncHooks throws an error if bad parameters are passed. + +require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); +const nonFunctionArray = [null, -1, 1, {}, []]; + +['init', 'before', 'after', 'destroy', 'promiseResolve'].forEach( + (functionName) => { + nonFunctionArray.forEach((nonFunction) => { + assert.throws(() => { + async_hooks.createHook({ [functionName]: nonFunction }); + }, { + code: 'ERR_ASYNC_CALLBACK', + name: 'TypeError', + message: `hook.${functionName} must be a function`, + }); + }); + }); diff --git a/test/js/node/test/parallel/test-async-hooks-vm-gc.js b/test/js/node/test/parallel/test-async-hooks-vm-gc.js new file mode 100644 index 0000000000..da95e3579d --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-vm-gc.js @@ -0,0 +1,15 @@ +// Flags: --expose-gc +'use strict'; + +require('../common'); +const asyncHooks = require('async_hooks'); +const vm = require('vm'); + +// This is a regression test for https://github.com/nodejs/node/issues/39019 +// +// It should not segfault. + +const hook = asyncHooks.createHook({ init() {} }).enable(); +vm.createContext(); +globalThis.gc(); +hook.disable(); diff --git a/test/js/node/test/parallel/test-async-wrap-constructor.js b/test/js/node/test/parallel/test-async-wrap-constructor.js new file mode 100644 index 0000000000..853898aa0a --- /dev/null +++ b/test/js/node/test/parallel/test-async-wrap-constructor.js @@ -0,0 +1,21 @@ +'use strict'; + +// This tests that using falsy values in createHook throws an error. + +require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +const falsyValues = [0, 1, false, true, null, 'hello']; +for (const badArg of falsyValues) { + const hookNames = ['init', 'before', 'after', 'destroy', 'promiseResolve']; + for (const hookName of hookNames) { + assert.throws(() => { + async_hooks.createHook({ [hookName]: badArg }); + }, { + code: 'ERR_ASYNC_CALLBACK', + name: 'TypeError', + message: `hook.${hookName} must be a function` + }); + } +} diff --git a/test/js/node/test/parallel/test-dgram-send-address-types.js b/test/js/node/test/parallel/test-dgram-send-address-types.js new file mode 100644 index 0000000000..a31e53f903 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-address-types.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const buf = Buffer.from('test'); + +const defaultCases = ['', null, undefined]; + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); +}, defaultCases.length + 1); + +const client = dgram.createSocket('udp4').bind(0, () => { + const port = client.address().port; + + // Check valid addresses + defaultCases.forEach((address) => { + client.send(buf, port, address, onMessage); + }); + + // Valid address: not provided + client.send(buf, port, onMessage); + + // Check invalid addresses + [ + [], + 0, + 1, + true, + false, + 0n, + 1n, + {}, + Symbol(), + ].forEach((invalidInput) => { + const expectedError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "address" argument must be of type string.' + + `${common.invalidArgTypeHelper(invalidInput)}` + }; + assert.throws(() => client.send(buf, port, invalidInput), expectedError); + }); +}); + +client.unref(); diff --git a/test/js/node/test/parallel/test-event-emitter-errors.js b/test/js/node/test/parallel/test-event-emitter-errors.js new file mode 100644 index 0000000000..f22fc3bd58 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-errors.js @@ -0,0 +1,37 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); +const util = require('util'); + +const EE = new EventEmitter(); + +assert.throws( + () => EE.emit('error', 'Accepts a string'), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: "Unhandled error. ('Accepts a string')", + } +); + +assert.throws( + () => EE.emit('error', { message: 'Error!' }), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: "Unhandled error. ({ message: 'Error!' })", + } +); + +assert.throws( + () => EE.emit('error', { + message: 'Error!', + [util.inspect.custom]() { throw new Error(); }, + }), + { + code: 'ERR_UNHANDLED_ERROR', + name: 'Error', + message: 'Unhandled error. ([object Object])', + } +); diff --git a/test/js/node/test/parallel/test-event-emitter-invalid-listener.js b/test/js/node/test/parallel/test-event-emitter-invalid-listener.js new file mode 100644 index 0000000000..f05766c72e --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-invalid-listener.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const eventsMethods = ['on', 'once', 'removeListener', 'prependOnceListener']; + +// Verify that the listener must be a function for events methods +for (const method of eventsMethods) { + assert.throws(() => { + const ee = new EventEmitter(); + ee[method]('foo', null); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "listener" argument must be of type function. ' + + 'Received null', + }); +} diff --git a/test/js/node/test/parallel/test-event-emitter-listeners-side-effects.js b/test/js/node/test/parallel/test-event-emitter-listeners-side-effects.js new file mode 100644 index 0000000000..f1a9e659e0 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-listeners-side-effects.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); + +const EventEmitter = require('events').EventEmitter; + +const e = new EventEmitter(); +let fl; // foo listeners + +fl = e.listeners('foo'); +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 0); +assert(!(e._events instanceof Object)); +assert.deepStrictEqual(Object.keys(e._events), []); + +e.on('foo', assert.fail); +fl = e.listeners('foo'); +assert.deepEqual(e._events.foo, [assert.fail]); +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 1); +assert.strictEqual(fl[0], assert.fail); + +e.listeners('bar'); + +e.on('foo', assert.ok); +fl = e.listeners('foo'); + +assert(Array.isArray(e._events.foo)); +assert.strictEqual(e._events.foo.length, 2); +assert.strictEqual(e._events.foo[0], assert.fail); +assert.strictEqual(e._events.foo[1], assert.ok); + +assert(Array.isArray(fl)); +assert.strictEqual(fl.length, 2); +assert.strictEqual(fl[0], assert.fail); +assert.strictEqual(fl[1], assert.ok); + +console.log('ok'); diff --git a/test/js/node/test/parallel/test-event-emitter-listeners.js b/test/js/node/test/parallel/test-event-emitter-listeners.js new file mode 100644 index 0000000000..eb1da829c9 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-listeners.js @@ -0,0 +1,124 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); +const events = require('events'); + +function listener() {} + +function listener2() {} + +function listener3() { + return 0; +} + +function listener4() { + return 1; +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const fooListeners = ee.listeners('foo'); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); + ee.removeAllListeners('foo'); + assert.deepStrictEqual(ee.listeners('foo'), []); + assert.deepStrictEqual(fooListeners, [listener]); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const eeListenersCopy = ee.listeners('foo'); + assert.deepStrictEqual(eeListenersCopy, [listener]); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); + eeListenersCopy.push(listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); + assert.deepStrictEqual(eeListenersCopy, [listener, listener2]); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const eeListenersCopy = ee.listeners('foo'); + ee.on('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener, listener2]); + assert.deepStrictEqual(eeListenersCopy, [listener]); +} + +{ + const ee = new events.EventEmitter(); + ee.once('foo', listener); + assert.deepStrictEqual(ee.listeners('foo'), [listener]); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + ee.once('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener, listener2]); +} + +{ + const ee = new events.EventEmitter(); + ee._events = undefined; + assert.deepStrictEqual(ee.listeners('foo'), []); +} + +{ + class TestStream extends events.EventEmitter {} + const s = new TestStream(); + assert.deepStrictEqual(s.listeners('foo'), []); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', listener); + const wrappedListener = ee.rawListeners('foo'); + assert.strictEqual(wrappedListener.length, 1); + assert.strictEqual(wrappedListener[0], listener); + assert.notStrictEqual(wrappedListener, ee.rawListeners('foo')); + ee.once('foo', listener); + const wrappedListeners = ee.rawListeners('foo'); + assert.strictEqual(wrappedListeners.length, 2); + assert.strictEqual(wrappedListeners[0], listener); + assert.notStrictEqual(wrappedListeners[1], listener); + assert.strictEqual(wrappedListeners[1].listener, listener); + assert.notStrictEqual(wrappedListeners, ee.rawListeners('foo')); + ee.emit('foo'); + assert.strictEqual(wrappedListeners.length, 2); + assert.strictEqual(wrappedListeners[1].listener, listener); +} + +{ + const ee = new events.EventEmitter(); + ee.once('foo', listener3); + ee.on('foo', listener4); + const rawListeners = ee.rawListeners('foo'); + assert.strictEqual(rawListeners.length, 2); + assert.strictEqual(rawListeners[0](), 0); + const rawListener = ee.rawListeners('foo'); + assert.strictEqual(rawListener.length, 1); + assert.strictEqual(rawListener[0](), 1); +} diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-null.js b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-null.js new file mode 100644 index 0000000000..673b42336e --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-null.js @@ -0,0 +1,22 @@ +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +const e = new events.EventEmitter(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, null); + assert.ok(warning.message.includes('2 null listeners added to [EventEmitter]. MaxListeners is 1.')); +})); + +e.on(null, () => {}); +e.on(null, () => {}); diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js new file mode 100644 index 0000000000..e654b7697c --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning-for-symbol.js @@ -0,0 +1,24 @@ +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +const symbol = Symbol('symbol'); + +const e = new events.EventEmitter(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, symbol); + assert.ok(warning.message.includes('2 Symbol(symbol) listeners added to [EventEmitter]. MaxListeners is 1.')); +})); + +e.on(symbol, () => {}); +e.on(symbol, () => {}); diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners-warning.js b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning.js new file mode 100644 index 0000000000..31bd8d0712 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners-warning.js @@ -0,0 +1,30 @@ +// Flags: --no-warnings +// The flag suppresses stderr output but the warning event will still emit +'use strict'; + +const common = require('../common'); +const events = require('events'); +const assert = require('assert'); + +class FakeInput extends events.EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +const e = new FakeInput(); +e.setMaxListeners(1); + +process.on('warning', common.mustCall((warning) => { + assert.ok(warning instanceof Error); + assert.strictEqual(warning.name, 'MaxListenersExceededWarning'); + assert.strictEqual(warning.emitter, e); + assert.strictEqual(warning.count, 2); + assert.strictEqual(warning.type, 'event-type'); + assert.ok(warning.message.includes('2 event-type listeners added to [FakeInput]. MaxListeners is 1.')); +})); + +e.on('event-type', () => {}); +e.on('event-type', () => {}); // Trigger warning. +e.on('event-type', () => {}); // Verify that warning is emitted only once. diff --git a/test/js/node/test/parallel/test-event-emitter-max-listeners.js b/test/js/node/test/parallel/test-event-emitter-max-listeners.js new file mode 100644 index 0000000000..9b9c2ad0d5 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-max-listeners.js @@ -0,0 +1,88 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const e = new events.EventEmitter(); + +e.on('maxListeners', common.mustCall()); + +// Should not corrupt the 'maxListeners' queue. +e.setMaxListeners(42); + +const rangeErrorObjs = [NaN, -1]; +const typeErrorObj = 'and even this'; + +for (const obj of rangeErrorObjs) { + assert.throws( + () => e.setMaxListeners(obj), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); + + assert.throws( + () => events.defaultMaxListeners = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); +} + +assert.throws( + () => e.setMaxListeners(typeErrorObj), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); + +assert.throws( + () => events.defaultMaxListeners = typeErrorObj, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); + +e.emit('maxListeners'); + +{ + const { EventEmitter, defaultMaxListeners } = events; + for (const obj of rangeErrorObjs) { + assert.throws(() => EventEmitter.setMaxListeners(obj), { + code: 'ERR_OUT_OF_RANGE', + }); + } + + assert.throws(() => EventEmitter.setMaxListeners(typeErrorObj), { + code: 'ERR_INVALID_ARG_TYPE', + }); + + assert.throws( + () => EventEmitter.setMaxListeners(defaultMaxListeners, 'INVALID_EMITTER'), + { code: 'ERR_INVALID_ARG_TYPE' } + ); +} diff --git a/test/js/node/test/parallel/test-event-emitter-no-error-provided-to-error-event.js b/test/js/node/test/parallel/test-event-emitter-no-error-provided-to-error-event.js new file mode 100644 index 0000000000..5c30b54533 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-no-error-provided-to-error-event.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const domain = require('domain'); + +{ + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert(er instanceof Error, 'error created'); + })); + e.emit('error'); +} + +for (const arg of [false, null, undefined]) { + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert(er instanceof Error, 'error created'); + })); + e.emit('error', arg); +} + +for (const arg of [42, 'fortytwo', true]) { + const e = new events.EventEmitter(); + const d = domain.create(); + d.add(e); + d.on('error', common.mustCall((er) => { + assert.strictEqual(er, arg); + })); + e.emit('error', arg); +} diff --git a/test/js/node/test/parallel/test-event-emitter-once.js b/test/js/node/test/parallel/test-event-emitter-once.js new file mode 100644 index 0000000000..983f6141b9 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-once.js @@ -0,0 +1,70 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const e = new EventEmitter(); + +e.once('hello', common.mustCall()); + +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); + +function remove() { + assert.fail('once->foo should not be emitted'); +} + +e.once('foo', remove); +e.removeListener('foo', remove); +e.emit('foo'); + +e.once('e', common.mustCall(function() { + e.emit('e'); +})); + +e.once('e', common.mustCall()); + +e.emit('e'); + +{ + // once() has different code paths based on the number of arguments being + // emitted. Verify that all of the cases are covered. + const maxArgs = 4; + + for (let i = 0; i <= maxArgs; ++i) { + const ee = new EventEmitter(); + const args = ['foo']; + + for (let j = 0; j < i; ++j) + args.push(j); + + ee.once('foo', common.mustCall((...params) => { + assert.deepStrictEqual(params, args.slice(1)); + })); + + EventEmitter.prototype.emit.apply(ee, args); + } +} diff --git a/test/js/node/test/parallel/test-event-emitter-remove-all-listeners.js b/test/js/node/test/parallel/test-event-emitter-remove-all-listeners.js new file mode 100644 index 0000000000..c62183fd08 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-remove-all-listeners.js @@ -0,0 +1,123 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); + + +function expect(expected) { + const actual = []; + process.on('exit', function() { + assert.deepStrictEqual(actual.sort(), expected.sort()); + }); + function listener(name) { + actual.push(name); + } + return common.mustCall(listener, expected.length); +} + +{ + const ee = new events.EventEmitter(); + const noop = common.mustNotCall(); + ee.on('foo', noop); + ee.on('bar', noop); + ee.on('baz', noop); + ee.on('baz', noop); + const fooListeners = ee.listeners('foo'); + const barListeners = ee.listeners('bar'); + const bazListeners = ee.listeners('baz'); + ee.on('removeListener', expect(['bar', 'baz', 'baz'])); + ee.removeAllListeners('bar'); + ee.removeAllListeners('baz'); + assert.deepStrictEqual(ee.listeners('foo'), [noop]); + assert.deepStrictEqual(ee.listeners('bar'), []); + assert.deepStrictEqual(ee.listeners('baz'), []); + // After calling removeAllListeners(), + // the old listeners array should stay unchanged. + assert.deepStrictEqual(fooListeners, [noop]); + assert.deepStrictEqual(barListeners, [noop]); + assert.deepStrictEqual(bazListeners, [noop, noop]); + // After calling removeAllListeners(), + // new listeners arrays is different from the old. + assert.notStrictEqual(ee.listeners('bar'), barListeners); + assert.notStrictEqual(ee.listeners('baz'), bazListeners); +} + +{ + const ee = new events.EventEmitter(); + ee.on('foo', common.mustNotCall()); + ee.on('bar', common.mustNotCall()); + // Expect LIFO order + ee.on('removeListener', expect(['foo', 'bar', 'removeListener'])); + ee.on('removeListener', expect(['foo', 'bar'])); + ee.removeAllListeners(); + assert.deepStrictEqual([], ee.listeners('foo')); + assert.deepStrictEqual([], ee.listeners('bar')); +} + +{ + const ee = new events.EventEmitter(); + ee.on('removeListener', common.mustNotCall()); + // Check for regression where removeAllListeners() throws when + // there exists a 'removeListener' listener, but there exists + // no listeners for the provided event type. + ee.removeAllListeners.bind(ee, 'foo'); +} + +{ + const ee = new events.EventEmitter(); + let expectLength = 2; + ee.on('removeListener', function(name, noop) { + assert.strictEqual(expectLength--, this.listeners('baz').length); + }); + ee.on('baz', common.mustNotCall()); + ee.on('baz', common.mustNotCall()); + ee.on('baz', common.mustNotCall()); + assert.strictEqual(ee.listeners('baz').length, expectLength + 1); + ee.removeAllListeners('baz'); + assert.strictEqual(ee.listeners('baz').length, 0); +} + +{ + const ee = new events.EventEmitter(); + assert.deepStrictEqual(ee, ee.removeAllListeners()); +} + +{ + const ee = new events.EventEmitter(); + ee._events = undefined; + assert.strictEqual(ee, ee.removeAllListeners()); +} + +{ + const ee = new events.EventEmitter(); + const symbol = Symbol('symbol'); + const noop = common.mustNotCall(); + ee.on(symbol, noop); + + ee.on('removeListener', common.mustCall((...args) => { + assert.deepStrictEqual(args, [symbol, noop]); + })); + + ee.removeAllListeners(); +} diff --git a/test/js/node/test/parallel/test-event-emitter-remove-listeners.js b/test/js/node/test/parallel/test-event-emitter-remove-listeners.js new file mode 100644 index 0000000000..5ab52b8320 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-remove-listeners.js @@ -0,0 +1,170 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +function listener1() {} + +function listener2() {} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('removeListener', common.mustNotCall()); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([listener1], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('hello', listener2); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + + function remove1() { + assert.fail('remove1 should not have been called'); + } + + function remove2() { + assert.fail('remove2 should not have been called'); + } + + ee.on('removeListener', common.mustCall(function(name, cb) { + if (cb !== remove1) return; + this.removeListener('quux', remove2); + this.emit('quux'); + }, 2)); + ee.on('quux', remove1); + ee.on('quux', remove2); + ee.removeListener('quux', remove1); +} + +{ + const ee = new EventEmitter(); + ee.on('hello', listener1); + ee.on('hello', listener2); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener1); + assert.deepStrictEqual([listener2], ee.listeners('hello')); + ee.once('removeListener', common.mustCall((name, cb) => { + assert.strictEqual(name, 'hello'); + assert.strictEqual(cb, listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener2); + assert.deepStrictEqual([], ee.listeners('hello')); + })); + ee.removeListener('hello', listener1); + assert.deepStrictEqual([], ee.listeners('hello')); +} + +{ + const ee = new EventEmitter(); + const listener3 = common.mustCall(() => { + ee.removeListener('hello', listener4); + }, 2); + const listener4 = common.mustCall(); + + ee.on('hello', listener3); + ee.on('hello', listener4); + + // listener4 will still be called although it is removed by listener 3. + ee.emit('hello'); + // This is so because the internal listener array at time of emit + // was [listener3,listener4] + + // Internal listener array [listener3] + ee.emit('hello'); +} + +{ + const ee = new EventEmitter(); + + ee.once('hello', listener1); + ee.on('removeListener', common.mustCall((eventName, listener) => { + assert.strictEqual(eventName, 'hello'); + assert.strictEqual(listener, listener1); + })); + ee.emit('hello'); +} + +{ + const ee = new EventEmitter(); + + assert.deepStrictEqual(ee, ee.removeListener('foo', () => {})); +} + +{ + const ee = new EventEmitter(); + const listener = () => {}; + ee._events = undefined; + const e = ee.removeListener('foo', listener); + assert.strictEqual(e, ee); +} + +{ + const ee = new EventEmitter(); + + ee.on('foo', listener1); + ee.on('foo', listener2); + assert.deepStrictEqual(ee.listeners('foo'), [listener1, listener2]); + + ee.removeListener('foo', listener1); + assert.deepEqual(ee._events.foo, [listener2]); + + ee.on('foo', listener1); + assert.deepStrictEqual(ee.listeners('foo'), [listener2, listener1]); + + ee.removeListener('foo', listener1); + assert.deepEqual(ee._events.foo, [listener2]); +} diff --git a/test/js/node/test/parallel/test-internal-module-require.js b/test/js/node/test/parallel/test-internal-module-require.js index c6e2057d3d..3a02e25cd0 100644 --- a/test/js/node/test/parallel/test-internal-module-require.js +++ b/test/js/node/test/parallel/test-internal-module-require.js @@ -1,112 +1,114 @@ 'use strict'; -// Flags: --expose-internals -// This verifies that -// 1. We do not leak internal modules unless the --require-internals option -// is on. -// 2. We do not accidentally leak any modules to the public global scope. -// 3. Deprecated modules are properly deprecated. +// // Flags: --expose-internals +// // This verifies that +// // 1. We do not leak internal modules unless the --require-internals option +// // is on. +// // 2. We do not accidentally leak any modules to the public global scope. +// // 3. Deprecated modules are properly deprecated. const common = require('../common'); -if (!common.isMainThread) { - common.skip('Cannot test the existence of --expose-internals from worker'); -} +common.skip("This test is not going to be implemented in Bun. We do not support --expose-internals.") -const assert = require('assert'); -const fork = require('child_process').fork; +// if (!common.isMainThread) { +// common.skip('Cannot test the existence of --expose-internals from worker'); +// } -const expectedPublicModules = new Set([ - '_http_agent', - '_http_client', - '_http_common', - '_http_incoming', - '_http_outgoing', - '_http_server', - '_stream_duplex', - '_stream_passthrough', - '_stream_readable', - '_stream_transform', - '_stream_wrap', - '_stream_writable', - '_tls_common', - '_tls_wrap', - 'assert', - 'async_hooks', - 'buffer', - 'child_process', - 'cluster', - 'console', - 'constants', - 'crypto', - 'dgram', - 'dns', - 'domain', - 'events', - 'fs', - 'http', - 'http2', - 'https', - 'inspector', - 'module', - 'net', - 'os', - 'path', - 'perf_hooks', - 'process', - 'punycode', - 'querystring', - 'readline', - 'repl', - 'stream', - 'string_decoder', - 'sys', - 'timers', - 'tls', - 'trace_events', - 'tty', - 'url', - 'util', - 'v8', - 'vm', - 'worker_threads', - 'zlib', -]); +// const assert = require('assert'); +// const fork = require('child_process').fork; -if (process.argv[2] === 'child') { - assert(!process.execArgv.includes('--expose-internals')); - process.once('message', ({ allBuiltins }) => { - const publicModules = new Set(); - for (const id of allBuiltins) { - if (id.startsWith('internal/')) { - assert.throws(() => { - require(id); - }, { - code: 'MODULE_NOT_FOUND', - message: `Cannot find module '${id}'` - }); - } else { - require(id); - publicModules.add(id); - } - } - assert(allBuiltins.length > publicModules.size); - // Make sure all the public modules are available through - // require('module').builtinModules - assert.deepStrictEqual( - publicModules, - new Set(require('module').builtinModules) - ); - assert.deepStrictEqual(publicModules, expectedPublicModules); - }); -} else { - assert(process.execArgv.includes('--expose-internals')); - const child = fork(__filename, ['child'], { - execArgv: [] - }); - const { builtinModules } = require('module'); - // When --expose-internals is on, require('module').builtinModules - // contains internal modules. - const message = { allBuiltins: builtinModules }; - child.send(message); -} +// const expectedPublicModules = new Set([ +// '_http_agent', +// '_http_client', +// '_http_common', +// '_http_incoming', +// '_http_outgoing', +// '_http_server', +// '_stream_duplex', +// '_stream_passthrough', +// '_stream_readable', +// '_stream_transform', +// '_stream_wrap', +// '_stream_writable', +// '_tls_common', +// '_tls_wrap', +// 'assert', +// 'async_hooks', +// 'buffer', +// 'child_process', +// 'cluster', +// 'console', +// 'constants', +// 'crypto', +// 'dgram', +// 'dns', +// 'domain', +// 'events', +// 'fs', +// 'http', +// 'http2', +// 'https', +// 'inspector', +// 'module', +// 'net', +// 'os', +// 'path', +// 'perf_hooks', +// 'process', +// 'punycode', +// 'querystring', +// 'readline', +// 'repl', +// 'stream', +// 'string_decoder', +// 'sys', +// 'timers', +// 'tls', +// 'trace_events', +// 'tty', +// 'url', +// 'util', +// 'v8', +// 'vm', +// 'worker_threads', +// 'zlib', +// ]); + +// if (process.argv[2] === 'child') { +// assert(!process.execArgv.includes('--expose-internals')); +// process.once('message', ({ allBuiltins }) => { +// const publicModules = new Set(); +// for (const id of allBuiltins) { +// if (id.startsWith('internal/')) { +// assert.throws(() => { +// require(id); +// }, { +// code: 'MODULE_NOT_FOUND', +// message: `Cannot find module '${id}'` +// }); +// } else { +// require(id); +// publicModules.add(id); +// } +// } +// assert(allBuiltins.length > publicModules.size); +// // Make sure all the public modules are available through +// // require('module').builtinModules +// assert.deepStrictEqual( +// publicModules, +// new Set(require('module').builtinModules) +// ); +// assert.deepStrictEqual(publicModules, expectedPublicModules); +// }); +// } else { +// assert(process.execArgv.includes('--expose-internals')); +// const child = fork(__filename, ['child'], { +// execArgv: [] +// }); +// const { builtinModules } = require('module'); +// // When --expose-internals is on, require('module').builtinModules +// // contains internal modules. +// const message = { allBuiltins: builtinModules }; +// child.send(message); +// } diff --git a/test/js/node/test/parallel/test-os-eol.js b/test/js/node/test/parallel/test-os-eol.js new file mode 100644 index 0000000000..412751a151 --- /dev/null +++ b/test/js/node/test/parallel/test-os-eol.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const os = require('os'); + +const eol = common.isWindows ? '\r\n' : '\n'; + +assert.strictEqual(os.EOL, eol); + +// Test that the `Error` is a `TypeError` but do not check the message as it +// varies between different JavaScript engines. +assert.throws(function() { os.EOL = 123; }, TypeError); + +const foo = 'foo'; +Object.defineProperties(os, { + EOL: { + configurable: true, + enumerable: true, + writable: false, + value: foo + } +}); +assert.strictEqual(os.EOL, foo); diff --git a/test/js/node/test/parallel/test-os-homedir-no-envvar.js b/test/js/node/test/parallel/test-os-homedir-no-envvar.js new file mode 100644 index 0000000000..75d439b2ed --- /dev/null +++ b/test/js/node/test/parallel/test-os-homedir-no-envvar.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const os = require('os'); +const path = require('path'); + + +if (process.argv[2] === 'child') { + if (common.isWindows) + assert.strictEqual(process.env.USERPROFILE, undefined); + else + assert.strictEqual(process.env.HOME, undefined); + + const home = os.homedir(); + + assert.strictEqual(typeof home, 'string'); + assert(home.includes(path.sep)); +} else { + if (common.isWindows) + delete process.env.USERPROFILE; + else + delete process.env.HOME; + + const child = cp.spawnSync(process.execPath, [__filename, 'child'], { + env: process.env, + stdio: 'inherit', + }); + + assert.strictEqual(child.status, 0); +} diff --git a/test/js/node/test/parallel/test-os-process-priority.js b/test/js/node/test/parallel/test-os-process-priority.js new file mode 100644 index 0000000000..2edabf53df --- /dev/null +++ b/test/js/node/test/parallel/test-os-process-priority.js @@ -0,0 +1,145 @@ +'use strict'; +const common = require('../common'); +// IBMi process priority is different. +if (common.isIBMi) + common.skip('IBMi has a different process priority'); + +const assert = require('assert'); +const os = require('os'); +const { + PRIORITY_LOW, + PRIORITY_BELOW_NORMAL, + PRIORITY_NORMAL, + PRIORITY_ABOVE_NORMAL, + PRIORITY_HIGH, + PRIORITY_HIGHEST +} = os.constants.priority; + +// Validate priority constants. +assert.strictEqual(typeof PRIORITY_LOW, 'number'); +assert.strictEqual(typeof PRIORITY_BELOW_NORMAL, 'number'); +assert.strictEqual(typeof PRIORITY_NORMAL, 'number'); +assert.strictEqual(typeof PRIORITY_ABOVE_NORMAL, 'number'); +assert.strictEqual(typeof PRIORITY_HIGH, 'number'); +assert.strictEqual(typeof PRIORITY_HIGHEST, 'number'); + +// Test pid type validation. +[null, true, false, 'foo', {}, [], /x/].forEach((pid) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + message: /The "pid" argument must be of type number\./ + }; + + assert.throws(() => { + os.setPriority(pid, PRIORITY_NORMAL); + }, errObj); + + assert.throws(() => { + os.getPriority(pid); + }, errObj); +}); + +// Test pid range validation. +[NaN, Infinity, -Infinity, 3.14, 2 ** 32].forEach((pid) => { + const errObj = { + code: 'ERR_OUT_OF_RANGE', + message: /The value of "pid" is out of range\./ + }; + + assert.throws(() => { + os.setPriority(pid, PRIORITY_NORMAL); + }, errObj); + + assert.throws(() => { + os.getPriority(pid); + }, errObj); +}); + +// Test priority type validation. +[null, true, false, 'foo', {}, [], /x/].forEach((priority) => { + assert.throws(() => { + os.setPriority(0, priority); + }, { + code: 'ERR_INVALID_ARG_TYPE', + message: /The "priority" argument must be of type number\./ + }); +}); + +// Test priority range validation. +[ + NaN, + Infinity, + -Infinity, + 3.14, + 2 ** 32, + PRIORITY_HIGHEST - 1, + PRIORITY_LOW + 1, +].forEach((priority) => { + assert.throws(() => { + os.setPriority(0, priority); + }, { + code: 'ERR_OUT_OF_RANGE', + message: /The value of "priority" is out of range\./ + }); +}); + +// Verify that valid values work. +for (let i = PRIORITY_HIGHEST; i <= PRIORITY_LOW; i++) { + // A pid of 0 corresponds to the current process. + try { + os.setPriority(0, i); + } catch (err) { + // The current user might not have sufficient permissions to set this + // specific priority level. Skip this priority, but keep trying lower + // priorities. + if (err.info.code === 'EACCES') + continue; + + assert(err); + } + + checkPriority(0, i); + + // An undefined pid corresponds to the current process. + os.setPriority(i); + checkPriority(undefined, i); + + // Specifying the actual pid works. + os.setPriority(process.pid, i); + checkPriority(process.pid, i); +} + +{ + assert.throws(() => { os.getPriority(-1); }, { + code: 'ERR_SYSTEM_ERROR', + message: /A system error occurred: uv_os_getpriority returned /, + name: 'SystemError' + }); +} + + +function checkPriority(pid, expected) { + const priority = os.getPriority(pid); + + // Verify that the priority values match on Unix, and are range mapped on + // Windows. + if (!common.isWindows) { + assert.strictEqual(priority, expected); + return; + } + + // On Windows setting PRIORITY_HIGHEST will only work for elevated user, + // for others it will be silently reduced to PRIORITY_HIGH + if (expected < PRIORITY_HIGH) + assert.ok(priority === PRIORITY_HIGHEST || priority === PRIORITY_HIGH); + else if (expected < PRIORITY_ABOVE_NORMAL) + assert.strictEqual(priority, PRIORITY_HIGH); + else if (expected < PRIORITY_NORMAL) + assert.strictEqual(priority, PRIORITY_ABOVE_NORMAL); + else if (expected < PRIORITY_BELOW_NORMAL) + assert.strictEqual(priority, PRIORITY_NORMAL); + else if (expected < PRIORITY_LOW) + assert.strictEqual(priority, PRIORITY_BELOW_NORMAL); + else + assert.strictEqual(priority, PRIORITY_LOW); +} diff --git a/test/js/node/test/parallel/test-os-userinfo-handles-getter-errors.js b/test/js/node/test/parallel/test-os-userinfo-handles-getter-errors.js new file mode 100644 index 0000000000..ca7b560012 --- /dev/null +++ b/test/js/node/test/parallel/test-os-userinfo-handles-getter-errors.js @@ -0,0 +1,19 @@ +'use strict'; +// Tests that os.userInfo correctly handles errors thrown by option property +// getters. See https://github.com/nodejs/node/issues/12370. + +const common = require('../common'); +const assert = require('assert'); +const execFile = require('child_process').execFile; + +const script = `os.userInfo({ + get encoding() { + throw new Error('xyz'); + } +})`; + +const node = process.execPath; +execFile(node, [ '-e', script ], common.mustCall((err, stdout, stderr) => { + // Edited for Bun to lowercase `error` + assert(stderr.includes('xyz'), 'userInfo crashes'); +})); diff --git a/test/js/node/test/parallel/test-os.js b/test/js/node/test/parallel/test-os.js new file mode 100644 index 0000000000..3d9fe5c1a6 --- /dev/null +++ b/test/js/node/test/parallel/test-os.js @@ -0,0 +1,281 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const os = require('os'); +const path = require('path'); +const { inspect } = require('util'); + +const is = { + number: (value, key) => { + assert(!Number.isNaN(value), `${key} should not be NaN`); + assert.strictEqual(typeof value, 'number'); + }, + string: (value) => { assert.strictEqual(typeof value, 'string'); }, + array: (value) => { assert.ok(Array.isArray(value)); }, + object: (value) => { + assert.strictEqual(typeof value, 'object'); + assert.notStrictEqual(value, null); + } +}; + +process.env.TMPDIR = '/tmpdir'; +process.env.TMP = '/tmp'; +process.env.TEMP = '/temp'; +if (common.isWindows) { + assert.strictEqual(os.tmpdir(), '/temp'); + process.env.TEMP = ''; + assert.strictEqual(os.tmpdir(), '/tmp'); + process.env.TMP = ''; + const expected = `${process.env.SystemRoot || process.env.windir}\\temp`; + assert.strictEqual(os.tmpdir(), expected); + process.env.TEMP = '\\temp\\'; + assert.strictEqual(os.tmpdir(), '\\temp'); + process.env.TEMP = '\\tmpdir/'; + assert.strictEqual(os.tmpdir(), '\\tmpdir/'); + process.env.TEMP = '\\'; + assert.strictEqual(os.tmpdir(), '\\'); + process.env.TEMP = 'C:\\'; + assert.strictEqual(os.tmpdir(), 'C:\\'); +} else { + assert.strictEqual(os.tmpdir(), '/tmpdir'); + process.env.TMPDIR = ''; + assert.strictEqual(os.tmpdir(), '/tmp'); + process.env.TMP = ''; + assert.strictEqual(os.tmpdir(), '/temp'); + process.env.TEMP = ''; + assert.strictEqual(os.tmpdir(), '/tmp'); + process.env.TMPDIR = '/tmpdir/'; + assert.strictEqual(os.tmpdir(), '/tmpdir'); + process.env.TMPDIR = '/tmpdir\\'; + assert.strictEqual(os.tmpdir(), '/tmpdir\\'); + process.env.TMPDIR = '/'; + assert.strictEqual(os.tmpdir(), '/'); +} + +const endianness = os.endianness(); +is.string(endianness); +assert.match(endianness, /[BL]E/); + +const hostname = os.hostname(); +is.string(hostname); +assert.ok(hostname.length > 0); + +// IBMi process priority is different. +if (!common.isIBMi) { + const { PRIORITY_BELOW_NORMAL, PRIORITY_LOW } = os.constants.priority; + // Priority means niceness: higher numeric value <=> lower priority + const LOWER_PRIORITY = os.getPriority() < PRIORITY_BELOW_NORMAL ? PRIORITY_BELOW_NORMAL : PRIORITY_LOW; + os.setPriority(LOWER_PRIORITY); + const priority = os.getPriority(); + is.number(priority); + assert.strictEqual(priority, LOWER_PRIORITY); +} + +// On IBMi, os.uptime() returns 'undefined' +if (!common.isIBMi) { + const uptime = os.uptime(); + is.number(uptime); + assert.ok(uptime > 0); +} + +const cpus = os.cpus(); +is.array(cpus); +assert.ok(cpus.length > 0); +for (const cpu of cpus) { + assert.strictEqual(typeof cpu.model, 'string'); + assert.strictEqual(typeof cpu.speed, 'number'); + assert.strictEqual(typeof cpu.times.user, 'number'); + assert.strictEqual(typeof cpu.times.nice, 'number'); + assert.strictEqual(typeof cpu.times.sys, 'number'); + assert.strictEqual(typeof cpu.times.idle, 'number'); + assert.strictEqual(typeof cpu.times.irq, 'number'); +} + +const type = os.type(); +is.string(type); +assert.ok(type.length > 0); + +const release = os.release(); +is.string(release); +assert.ok(release.length > 0); +// TODO: Check format on more than just AIX +if (common.isAIX) + assert.match(release, /^\d+\.\d+$/); + +const platform = os.platform(); +is.string(platform); +assert.ok(platform.length > 0); + +const arch = os.arch(); +is.string(arch); +assert.ok(arch.length > 0); + +if (!common.isSunOS) { + // not implemented yet + assert.ok(os.loadavg().length > 0); + assert.ok(os.freemem() > 0); + assert.ok(os.totalmem() > 0); +} + +const interfaces = os.networkInterfaces(); +switch (platform) { + case 'linux': { + const filter = (e) => + e.address === '127.0.0.1' && + e.netmask === '255.0.0.0'; + + const actual = interfaces.lo.filter(filter); + const expected = [{ + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true, + cidr: '127.0.0.1/8' + }]; + assert.deepStrictEqual(actual, expected); + break; + } + case 'win32': { + const filter = (e) => + e.address === '127.0.0.1'; + + const actual = interfaces['Loopback Pseudo-Interface 1'].filter(filter); + const expected = [{ + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true, + cidr: '127.0.0.1/8' + }]; + assert.deepStrictEqual(actual, expected); + break; + } +} +const netmaskToCIDRSuffixMap = new Map(Object.entries({ + '255.0.0.0': 8, + '255.255.255.0': 24, + 'ffff:ffff:ffff:ffff::': 64, + 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128 +})); + +Object.values(interfaces) + .flat(Infinity) + .map((v) => ({ v, mask: netmaskToCIDRSuffixMap.get(v.netmask) })) + .forEach(({ v, mask }) => { + assert.ok('cidr' in v, `"cidr" prop not found in ${inspect(v)}`); + if (mask) { + assert.strictEqual(v.cidr, `${v.address}/${mask}`); + } + }); + +const EOL = os.EOL; +if (common.isWindows) { + assert.strictEqual(EOL, '\r\n'); +} else { + assert.strictEqual(EOL, '\n'); +} + +const home = os.homedir(); +is.string(home); +assert.ok(home.includes(path.sep)); + +const version = os.version(); +assert.strictEqual(typeof version, 'string'); +assert(version); + +if (common.isWindows && process.env.USERPROFILE) { + assert.strictEqual(home, process.env.USERPROFILE); + delete process.env.USERPROFILE; + assert.ok(os.homedir().includes(path.sep)); + process.env.USERPROFILE = home; +} else if (!common.isWindows && process.env.HOME) { + assert.strictEqual(home, process.env.HOME); + delete process.env.HOME; + assert.ok(os.homedir().includes(path.sep)); + process.env.HOME = home; +} + +const pwd = os.userInfo(); +is.object(pwd); +const pwdBuf = os.userInfo({ encoding: 'buffer' }); + +if (common.isWindows) { + assert.strictEqual(pwd.uid, -1); + assert.strictEqual(pwd.gid, -1); + assert.strictEqual(pwd.shell, null); + assert.strictEqual(pwdBuf.uid, -1); + assert.strictEqual(pwdBuf.gid, -1); + assert.strictEqual(pwdBuf.shell, null); +} else { + is.number(pwd.uid); + is.number(pwd.gid); + assert.strictEqual(typeof pwd.shell, 'string'); + // It's possible for /etc/passwd to leave the user's shell blank. + if (pwd.shell.length > 0) { + assert(pwd.shell.includes(path.sep)); + } + assert.strictEqual(pwd.uid, pwdBuf.uid); + assert.strictEqual(pwd.gid, pwdBuf.gid); + assert.strictEqual(pwd.shell, pwdBuf.shell.toString('utf8')); +} + +is.string(pwd.username); +assert.ok(pwd.homedir.includes(path.sep)); +assert.strictEqual(pwd.username, pwdBuf.username.toString('utf8')); +assert.strictEqual(pwd.homedir, pwdBuf.homedir.toString('utf8')); + +assert.strictEqual(`${os.hostname}`, os.hostname()); +assert.strictEqual(`${os.homedir}`, os.homedir()); +assert.strictEqual(`${os.release}`, os.release()); +assert.strictEqual(`${os.type}`, os.type()); +assert.strictEqual(`${os.endianness}`, os.endianness()); +assert.strictEqual(`${os.tmpdir}`, os.tmpdir()); +assert.strictEqual(`${os.arch}`, os.arch()); +assert.strictEqual(`${os.platform}`, os.platform()); +assert.strictEqual(`${os.version}`, os.version()); +assert.strictEqual(`${os.machine}`, os.machine()); +assert.strictEqual(+os.totalmem, os.totalmem()); + +// Assert that the following values are coercible to numbers. +// On IBMi, os.uptime() returns 'undefined' +if (!common.isIBMi) { + is.number(+os.uptime, 'uptime'); + is.number(os.uptime(), 'uptime'); +} + +is.number(+os.availableParallelism, 'availableParallelism'); +is.number(os.availableParallelism(), 'availableParallelism'); +is.number(+os.freemem, 'freemem'); +is.number(os.freemem(), 'freemem'); + +const devNull = os.devNull; +if (common.isWindows) { + assert.strictEqual(devNull, '\\\\.\\nul'); +} else { + assert.strictEqual(devNull, '/dev/null'); +} + +assert.ok(os.availableParallelism() > 0); diff --git a/test/js/node/test/parallel/test-require-delete-array-iterator.js b/test/js/node/test/parallel/test-require-delete-array-iterator.js new file mode 100644 index 0000000000..5424ef8b75 --- /dev/null +++ b/test/js/node/test/parallel/test-require-delete-array-iterator.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../common'); + + +const ArrayIteratorPrototype = + Object.getPrototypeOf(Array.prototype[Symbol.iterator]()); + +delete Array.prototype[Symbol.iterator]; +delete ArrayIteratorPrototype.next; + +require('../common/fixtures'); +import('../fixtures/es-modules/test-esm-ok.mjs').then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-stream-readable-setEncoding-null.js b/test/js/node/test/parallel/test-stream-readable-setEncoding-null.js new file mode 100644 index 0000000000..b95b26bb79 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-setEncoding-null.js @@ -0,0 +1,15 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + + +{ + const readable = new Readable({ encoding: 'hex' }); + assert.strictEqual(readable._readableState.encoding, 'hex'); + + readable.setEncoding(null); + + assert.strictEqual(readable._readableState.encoding, 'utf8'); +} diff --git a/test/js/node/test/parallel/test-stream-readable-unshift.js b/test/js/node/test/parallel/test-stream-readable-unshift.js new file mode 100644 index 0000000000..cccc834fc1 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-unshift.js @@ -0,0 +1,170 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +{ + // Check that strings are saved as Buffer + const readable = new Readable({ read() {} }); + + const string = 'abc'; + + readable.on('data', common.mustCall((chunk) => { + assert(Buffer.isBuffer(chunk)); + assert.strictEqual(chunk.toString('utf8'), string); + }, 1)); + + readable.unshift(string); + +} + +{ + // Check that data goes at the beginning + const readable = new Readable({ read() {} }); + const unshift = 'front'; + const push = 'back'; + + const expected = [unshift, push]; + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString('utf8'), expected.shift()); + }, 2)); + + + readable.push(push); + readable.unshift(unshift); +} + +{ + // Check that buffer is saved with correct encoding + const readable = new Readable({ read() {} }); + + const encoding = 'base64'; + const string = Buffer.from('abc').toString(encoding); + + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(encoding), string); + }, 1)); + + readable.unshift(string, encoding); + +} + +{ + + const streamEncoding = 'base64'; + + function checkEncoding(readable) { + + // chunk encodings + const encodings = ['utf8', 'binary', 'hex', 'base64']; + const expected = []; + + readable.on('data', common.mustCall((chunk) => { + const { encoding, string } = expected.pop(); + assert.strictEqual(chunk.toString(encoding), string); + }, encodings.length)); + + for (const encoding of encodings) { + const string = 'abc'; + + // If encoding is the same as the state.encoding the string is + // saved as is + const expect = encoding !== streamEncoding ? + Buffer.from(string, encoding).toString(streamEncoding) : string; + + expected.push({ encoding, string: expect }); + + readable.unshift(string, encoding); + } + } + + const r1 = new Readable({ read() {} }); + r1.setEncoding(streamEncoding); + checkEncoding(r1); + + const r2 = new Readable({ read() {}, encoding: streamEncoding }); + checkEncoding(r2); + +} + +{ + // Both .push & .unshift should have the same behaviour + // When setting an encoding, each chunk should be emitted with that encoding + const encoding = 'base64'; + + function checkEncoding(readable) { + const string = 'abc'; + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, Buffer.from(string).toString(encoding)); + }, 2)); + + readable.push(string); + readable.unshift(string); + } + + const r1 = new Readable({ read() {} }); + r1.setEncoding(encoding); + checkEncoding(r1); + + const r2 = new Readable({ read() {}, encoding }); + checkEncoding(r2); + +} + +{ + // Check that ObjectMode works + const readable = new Readable({ objectMode: true, read() {} }); + + const chunks = ['a', 1, {}, []]; + + readable.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, chunks.pop()); + }, chunks.length)); + + for (const chunk of chunks) { + readable.unshift(chunk); + } +} + +{ + + // Should not throw: https://github.com/nodejs/node/issues/27192 + const highWaterMark = 50; + class ArrayReader extends Readable { + constructor(opt) { + super({ highWaterMark }); + // The error happened only when pushing above hwm + this.buffer = new Array(highWaterMark * 2).fill(0).map(String); + } + _read(size) { + while (this.buffer.length) { + const chunk = this.buffer.shift(); + if (!this.buffer.length) { + this.push(chunk); + this.push(null); + return true; + } + if (!this.push(chunk)) + return; + } + } + } + + function onRead() { + while (null !== (stream.read())) { + // Remove the 'readable' listener before unshifting + stream.removeListener('readable', onRead); + stream.unshift('a'); + stream.on('data', (chunk) => { + console.log(chunk.length); + }); + break; + } + } + + const stream = new ArrayReader(); + stream.once('readable', common.mustCall(onRead)); + stream.on('end', common.mustCall()); + +} diff --git a/test/js/node/test/parallel/test-util-styletext.js b/test/js/node/test/parallel/test-util-styletext.js new file mode 100644 index 0000000000..6baa6a60ea --- /dev/null +++ b/test/js/node/test/parallel/test-util-styletext.js @@ -0,0 +1,43 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const util = require('util'); + +[ + undefined, + null, + false, + 5n, + 5, + Symbol(), + () => {}, + {}, +].forEach((invalidOption) => { + assert.throws(() => { + util.styleText(invalidOption, 'test'); + }, { + code: 'ERR_INVALID_ARG_VALUE', + }); + assert.throws(() => { + util.styleText('red', invalidOption); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +assert.throws(() => { + util.styleText('invalid', 'text'); +}, { + code: 'ERR_INVALID_ARG_VALUE', +}); + +assert.strictEqual(util.styleText('red', 'test'), '\u001b[31mtest\u001b[39m'); + +assert.strictEqual(util.styleText(['bold', 'red'], 'test'), '\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m'); +assert.strictEqual(util.styleText(['bold', 'red'], 'test'), util.styleText('bold', util.styleText('red', 'test'))); + +assert.throws(() => { + util.styleText(['invalid'], 'text'); +}, { + code: 'ERR_INVALID_ARG_VALUE', +}); diff --git a/test/js/node/test/parallel/test-vm-create-and-run-in-context.js b/test/js/node/test/parallel/test-vm-create-and-run-in-context.js new file mode 100644 index 0000000000..bd746cf2df --- /dev/null +++ b/test/js/node/test/parallel/test-vm-create-and-run-in-context.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Flags: --expose-gc +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); + +// Run in a new empty context +let context = vm.createContext(); +let result = vm.runInContext('"passed";', context); +assert.strictEqual(result, 'passed'); + +// Create a new pre-populated context +context = vm.createContext({ 'foo': 'bar', 'thing': 'lala' }); +assert.strictEqual(context.foo, 'bar'); +assert.strictEqual(context.thing, 'lala'); + +// Test updating context +result = vm.runInContext('var foo = 3;', context); +assert.strictEqual(context.foo, 3); +assert.strictEqual(context.thing, 'lala'); + +// https://github.com/nodejs/node/issues/5768 +// Run in contextified sandbox without referencing the context +const sandbox = { x: 1 }; +vm.createContext(sandbox); +global.gc(); +vm.runInContext('x = 2', sandbox); +// Should not crash. diff --git a/test/js/node/test/parallel/test-http-no-content-length.js b/test/js/node/test/parallel/test-vm-create-context-arg.js similarity index 66% rename from test/js/node/test/parallel/test-http-no-content-length.js rename to test/js/node/test/parallel/test-vm-create-context-arg.js index a3a51c015e..c07079cb6c 100644 --- a/test/js/node/test/parallel/test-http-no-content-length.js +++ b/test/js/node/test/parallel/test-vm-create-context-arg.js @@ -20,25 +20,21 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const net = require('net'); -const http = require('http'); +const vm = require('vm'); -const server = net.createServer(function(socket) { - // Neither Content-Length nor Connection - socket.end('HTTP/1.1 200 ok\r\n\r\nHello'); -}).listen(0, common.mustCall(function() { - http.get({ port: this.address().port }, common.mustCall(function(res) { - let body = ''; +assert.throws(() => { + vm.createContext('string is not supported'); +}, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}); - res.setEncoding('utf8'); - res.on('data', function(chunk) { - body += chunk; - }); - res.on('end', common.mustCall(function() { - assert.strictEqual(body, 'Hello'); - server.close(); - })); - })); -})); +// Should not throw. +vm.createContext({ a: 1 }); +vm.createContext([0, 1, 2, 3]); + +const sandbox = {}; +vm.createContext(sandbox); +vm.createContext(sandbox); diff --git a/test/js/node/test/parallel/test-vm-getters.js b/test/js/node/test/parallel/test-vm-getters.js new file mode 100644 index 0000000000..af27eeee64 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-getters.js @@ -0,0 +1,24 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/2734 +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const sandbox = {}; + +Object.defineProperty(sandbox, 'prop', { + get() { + return 'foo'; + } +}); + +const descriptor = Object.getOwnPropertyDescriptor(sandbox, 'prop'); +const context = vm.createContext(sandbox); +const code = 'Object.getOwnPropertyDescriptor(this, "prop");'; +const result = vm.runInContext(code, context); + +// Ref: https://github.com/nodejs/node/issues/11803 + +assert.deepStrictEqual(Object.keys(result), Object.keys(descriptor)); +for (const prop of Object.keys(result)) { + assert.strictEqual(result[prop], descriptor[prop]); +} diff --git a/test/js/node/test/parallel/test-vm-global-define-property.js b/test/js/node/test/parallel/test-vm-global-define-property.js new file mode 100644 index 0000000000..0b9a4dfb88 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-define-property.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); + +const code = + 'Object.defineProperty(this, "f", {\n' + + ' get: function() { return x; },\n' + + ' set: function(k) { x = k; },\n' + + ' configurable: true,\n' + + ' enumerable: true\n' + + '});\n' + + 'g = f;\n' + + 'f;\n'; + +const x = {}; +const o = vm.createContext({ console, x }); + +const res = vm.runInContext(code, o, 'test'); + +assert(res); +assert.strictEqual(typeof res, 'object'); +assert.strictEqual(res, x); +assert.strictEqual(o.f, res); +assert.deepStrictEqual(Object.keys(o), ['console', 'x', 'f', 'g']); diff --git a/test/js/node/test/parallel/test-vm-global-non-writable-properties.js b/test/js/node/test/parallel/test-vm-global-non-writable-properties.js new file mode 100644 index 0000000000..771c9794bd --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-non-writable-properties.js @@ -0,0 +1,15 @@ +'use strict'; +// https://github.com/nodejs/node/issues/10223 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const ctx = vm.createContext(); +vm.runInContext('Object.defineProperty(this, "x", { value: 42 })', ctx); +assert.strictEqual(vm.runInContext('x', ctx), 42); +vm.runInContext('x = 0', ctx); // Does not throw but x... +assert.strictEqual(vm.runInContext('x', ctx), 42); // ...should be unaltered. +assert.throws(() => vm.runInContext('"use strict"; x = 0', ctx), + /Attempted to assign to readonly property./); +assert.strictEqual(vm.runInContext('x', ctx), 42); diff --git a/test/js/node/test/parallel/test-vm-global-property-enumerator.js b/test/js/node/test/parallel/test-vm-global-property-enumerator.js new file mode 100644 index 0000000000..7b37c2af41 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-property-enumerator.js @@ -0,0 +1,49 @@ +'use strict'; +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +// Regression of https://github.com/nodejs/node/issues/53346 + +const cases = [ + { + get key() { + return 'value'; + }, + }, + { + // Intentionally single setter. + // eslint-disable-next-line accessor-pairs + set key(value) {}, + }, + {}, + { + key: 'value', + }, + (new class GetterObject { + get key() { + return 'value'; + } + }()), + (new class SetterObject { + // Intentionally single setter. + // eslint-disable-next-line accessor-pairs + set key(value) { + // noop + } + }()), + [], + [['key', 'value']], + { + __proto__: { + key: 'value', + }, + }, +]; + +for (const [idx, obj] of cases.entries()) { + const ctx = vm.createContext(obj); + const globalObj = vm.runInContext('this', ctx); + const keys = Object.keys(globalObj); + assert.deepStrictEqual(keys, Object.keys(obj), `Case ${idx} failed`); +} diff --git a/test/js/node/test/parallel/test-vm-global-property-interceptors.js b/test/js/node/test/parallel/test-vm-global-property-interceptors.js new file mode 100644 index 0000000000..c80ed04e3e --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-property-interceptors.js @@ -0,0 +1,129 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const dSymbol = Symbol('d'); +const sandbox = { + a: 'a', + dSymbol +}; + +Object.defineProperties(sandbox, { + b: { + value: 'b' + }, + c: { + value: 'c', + writable: true, + enumerable: true + }, + [dSymbol]: { + value: 'd' + }, + e: { + value: 'e', + configurable: true + }, + f: {} +}); + +const ctx = vm.createContext(sandbox); + +const result = vm.runInContext(` +const getDesc = (prop) => Object.getOwnPropertyDescriptor(this, prop); +const result = { + a: getDesc('a'), + b: getDesc('b'), + c: getDesc('c'), + d: getDesc(dSymbol), + e: getDesc('e'), + f: getDesc('f'), + g: getDesc('g') +}; +result; +`, ctx); + +// eslint-disable-next-line no-restricted-properties +assert.deepEqual(result, { + a: { value: 'a', writable: true, enumerable: true, configurable: true }, + b: { value: 'b', writable: false, enumerable: false, configurable: false }, + c: { value: 'c', writable: true, enumerable: true, configurable: false }, + d: { value: 'd', writable: false, enumerable: false, configurable: false }, + e: { value: 'e', writable: false, enumerable: false, configurable: true }, + f: { + value: undefined, + writable: false, + enumerable: false, + configurable: false + }, + g: undefined +}); + +// Define new properties +vm.runInContext(` +Object.defineProperty(this, 'h', {value: 'h'}); +Object.defineProperty(this, 'i', {}); +Object.defineProperty(this, 'j', { + get() { return 'j'; } +}); +let kValue = 0; +Object.defineProperty(this, 'k', { + get() { return kValue; }, + set(value) { kValue = value } +}); +`, ctx); + +assert.deepStrictEqual(Object.getOwnPropertyDescriptor(ctx, 'h'), { + value: 'h', + writable: false, + enumerable: false, + configurable: false +}); + +assert.deepStrictEqual(Object.getOwnPropertyDescriptor(ctx, 'i'), { + value: undefined, + writable: false, + enumerable: false, + configurable: false +}); + +const jDesc = Object.getOwnPropertyDescriptor(ctx, 'j'); +assert.strictEqual(typeof jDesc.get, 'function'); +assert.strictEqual(typeof jDesc.set, 'undefined'); +assert.strictEqual(jDesc.enumerable, false); +assert.strictEqual(jDesc.configurable, false); + +const kDesc = Object.getOwnPropertyDescriptor(ctx, 'k'); +assert.strictEqual(typeof kDesc.get, 'function'); +assert.strictEqual(typeof kDesc.set, 'function'); +assert.strictEqual(kDesc.enumerable, false); +assert.strictEqual(kDesc.configurable, false); + +assert.strictEqual(ctx.k, 0); +ctx.k = 1; +assert.strictEqual(ctx.k, 1); +assert.strictEqual(vm.runInContext('k;', ctx), 1); +vm.runInContext('k = 2;', ctx); +assert.strictEqual(ctx.k, 2); +assert.strictEqual(vm.runInContext('k;', ctx), 2); + +// Redefine properties on the global object +assert.strictEqual(typeof vm.runInContext('encodeURI;', ctx), 'function'); +assert.strictEqual(ctx.encodeURI, undefined); +vm.runInContext(` +Object.defineProperty(this, 'encodeURI', { value: 42 }); +`, ctx); +assert.strictEqual(vm.runInContext('encodeURI;', ctx), 42); +assert.strictEqual(ctx.encodeURI, 42); + +// Redefine properties on the sandbox +vm.runInContext(` +Object.defineProperty(this, 'e', { value: 'newE' }); +`, ctx); +assert.strictEqual(ctx.e, 'newE'); + +assert.throws(() => vm.runInContext(` +'use strict'; +Object.defineProperty(this, 'f', { value: 'newF' }); +`, ctx), /TypeError: .*readonly property.,*/); diff --git a/test/js/node/test/parallel/test-vm-global-property-prototype.js b/test/js/node/test/parallel/test-vm-global-property-prototype.js new file mode 100644 index 0000000000..fe8abc8be4 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-property-prototype.js @@ -0,0 +1,83 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { + onSelf: 'onSelf', +}; + +function onSelfGetter() { + return 'onSelfGetter'; +} + +Object.defineProperty(sandbox, 'onSelfGetter', { + get: onSelfGetter, +}); + +Object.defineProperty(sandbox, 1, { + value: 'onSelfIndexed', + writable: false, + enumerable: false, + configurable: true, +}); + +const ctx = vm.createContext(sandbox); + +const result = vm.runInContext(` +Object.prototype.onProto = 'onProto'; +Object.defineProperty(Object.prototype, 'onProtoGetter', { + get() { + return 'onProtoGetter'; + }, +}); +Object.defineProperty(Object.prototype, 2, { + value: 'onProtoIndexed', + writable: false, + enumerable: false, + configurable: true, +}); + +const resultHasOwn = { + onSelf: Object.hasOwn(this, 'onSelf'), + onSelfGetter: Object.hasOwn(this, 'onSelfGetter'), + onSelfIndexed: Object.hasOwn(this, 1), + onProto: Object.hasOwn(this, 'onProto'), + onProtoGetter: Object.hasOwn(this, 'onProtoGetter'), + onProtoIndexed: Object.hasOwn(this, 2), +}; + +const getDesc = (prop) => Object.getOwnPropertyDescriptor(this, prop); +const resultDesc = { + onSelf: getDesc('onSelf'), + onSelfGetter: getDesc('onSelfGetter'), + onSelfIndexed: getDesc(1), + onProto: getDesc('onProto'), + onProtoGetter: getDesc('onProtoGetter'), + onProtoIndexed: getDesc(2), +}; +({ + resultHasOwn, + resultDesc, +}); +`, ctx); + +// eslint-disable-next-line no-restricted-properties +assert.deepEqual(result, { + resultHasOwn: { + onSelf: true, + onSelfGetter: true, + onSelfIndexed: true, + onProto: false, + onProtoGetter: false, + onProtoIndexed: false, + }, + resultDesc: { + onSelf: { value: 'onSelf', writable: true, enumerable: true, configurable: true }, + onSelfGetter: { get: onSelfGetter, set: undefined, enumerable: false, configurable: false }, + onSelfIndexed: { value: 'onSelfIndexed', writable: false, enumerable: false, configurable: true }, + onProto: undefined, + onProtoGetter: undefined, + onProtoIndexed: undefined, + }, +}); diff --git a/test/js/node/test/parallel/test-vm-global-setter.js b/test/js/node/test/parallel/test-vm-global-setter.js new file mode 100644 index 0000000000..a8e390bc2a --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-setter.js @@ -0,0 +1,161 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const getSetSymbolReceivingFunction = Symbol('sym-1'); +const getSetSymbolReceivingNumber = Symbol('sym-2'); +const symbolReceivingNumber = Symbol('sym-3'); +const unknownSymbolReceivingNumber = Symbol('sym-4'); + +const window = createWindow(); + +const descriptor1 = Object.getOwnPropertyDescriptor( + window.globalProxy, + 'getSetPropReceivingFunction' +); +assert.strictEqual(typeof descriptor1.get, 'function'); +assert.strictEqual(typeof descriptor1.set, 'function'); +assert.strictEqual(descriptor1.configurable, true); + +const descriptor2 = Object.getOwnPropertyDescriptor( + window.globalProxy, + 'getSetPropReceivingNumber' +); +assert.strictEqual(typeof descriptor2.get, 'function'); +assert.strictEqual(typeof descriptor2.set, 'function'); +assert.strictEqual(descriptor2.configurable, true); + +const descriptor3 = Object.getOwnPropertyDescriptor( + window.globalProxy, + 'propReceivingNumber' +); +assert.strictEqual(descriptor3.value, 44); + +const descriptor4 = Object.getOwnPropertyDescriptor( + window.globalProxy, + 'unknownPropReceivingNumber' +); +assert.strictEqual(descriptor4, undefined); + +const descriptor5 = Object.getOwnPropertyDescriptor( + window.globalProxy, + getSetSymbolReceivingFunction +); +assert.strictEqual(typeof descriptor5.get, 'function'); +assert.strictEqual(typeof descriptor5.set, 'function'); +assert.strictEqual(descriptor5.configurable, true); + +const descriptor6 = Object.getOwnPropertyDescriptor( + window.globalProxy, + getSetSymbolReceivingNumber +); +assert.strictEqual(typeof descriptor6.get, 'function'); +assert.strictEqual(typeof descriptor6.set, 'function'); +assert.strictEqual(descriptor6.configurable, true); + +const descriptor7 = Object.getOwnPropertyDescriptor( + window.globalProxy, + symbolReceivingNumber +); +assert.strictEqual(descriptor7.value, 48); + +const descriptor8 = Object.getOwnPropertyDescriptor( + window.globalProxy, + unknownSymbolReceivingNumber +); +assert.strictEqual(descriptor8, undefined); + +const descriptor9 = Object.getOwnPropertyDescriptor( + window.globalProxy, + 'getSetPropThrowing' +); +assert.strictEqual(typeof descriptor9.get, 'function'); +assert.strictEqual(typeof descriptor9.set, 'function'); +assert.strictEqual(descriptor9.configurable, true); + +const descriptor10 = Object.getOwnPropertyDescriptor( + window.globalProxy, + 'nonWritableProp' +); +assert.strictEqual(descriptor10.value, 51); +assert.strictEqual(descriptor10.writable, false); + +// Regression test for GH-42962. This assignment should not throw. +window.globalProxy.getSetPropReceivingFunction = () => {}; +assert.strictEqual(window.globalProxy.getSetPropReceivingFunction, 42); + +window.globalProxy.getSetPropReceivingNumber = 143; +assert.strictEqual(window.globalProxy.getSetPropReceivingNumber, 43); + +window.globalProxy.propReceivingNumber = 144; +assert.strictEqual(window.globalProxy.propReceivingNumber, 144); + +window.globalProxy.unknownPropReceivingNumber = 145; +assert.strictEqual(window.globalProxy.unknownPropReceivingNumber, 145); + +window.globalProxy[getSetSymbolReceivingFunction] = () => {}; +assert.strictEqual(window.globalProxy[getSetSymbolReceivingFunction], 46); + +window.globalProxy[getSetSymbolReceivingNumber] = 147; +assert.strictEqual(window.globalProxy[getSetSymbolReceivingNumber], 47); + +window.globalProxy[symbolReceivingNumber] = 148; +assert.strictEqual(window.globalProxy[symbolReceivingNumber], 148); + +window.globalProxy[unknownSymbolReceivingNumber] = 149; +assert.strictEqual(window.globalProxy[unknownSymbolReceivingNumber], 149); + +assert.throws( + () => (window.globalProxy.getSetPropThrowing = 150), + new Error('setter called') +); +assert.strictEqual(window.globalProxy.getSetPropThrowing, 50); + +assert.throws( + () => (window.globalProxy.nonWritableProp = 151), + new TypeError('Attempted to assign to readonly property.') +); +assert.strictEqual(window.globalProxy.nonWritableProp, 51); + +function createWindow() { + const obj = {}; + vm.createContext(obj); + Object.defineProperty(obj, 'getSetPropReceivingFunction', { + get: common.mustCall(() => 42), + set: common.mustCall(), + configurable: true, + }); + Object.defineProperty(obj, 'getSetPropReceivingNumber', { + get: common.mustCall(() => 43), + set: common.mustCall(), + configurable: true, + }); + obj.propReceivingNumber = 44; + Object.defineProperty(obj, getSetSymbolReceivingFunction, { + get: common.mustCall(() => 46), + set: common.mustCall(), + configurable: true, + }); + Object.defineProperty(obj, getSetSymbolReceivingNumber, { + get: common.mustCall(() => 47), + set: common.mustCall(), + configurable: true, + }); + obj[symbolReceivingNumber] = 48; + Object.defineProperty(obj, 'getSetPropThrowing', { + get: common.mustCall(() => 50), + set: common.mustCall(() => { + throw new Error('setter called'); + }), + configurable: true, + }); + Object.defineProperty(obj, 'nonWritableProp', { + value: 51, + writable: false, + }); + + obj.globalProxy = vm.runInContext('this', obj); + + return obj; +} diff --git a/test/js/node/test/parallel/test-vm-inherited_properties.js b/test/js/node/test/parallel/test-vm-inherited_properties.js new file mode 100644 index 0000000000..92cd64a6df --- /dev/null +++ b/test/js/node/test/parallel/test-vm-inherited_properties.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); + +const vm = require('vm'); +const assert = require('assert'); + +let base = { + propBase: 1 +}; + +let sandbox = Object.create(base, { + propSandbox: { value: 3 } +}); + +const context = vm.createContext(sandbox); + +let result = vm.runInContext('Object.hasOwnProperty(this, "propBase");', + context); + +assert.strictEqual(result, false); + + +// Ref: https://github.com/nodejs/node/issues/5350 +base = { __proto__: null }; +base.x = 1; +base.y = 2; + +sandbox = { __proto__: base }; +sandbox.z = 3; + +assert.deepStrictEqual(Object.keys(sandbox), ['z']); + +const code = 'x = 0; z = 4;'; +result = vm.runInNewContext(code, sandbox); +assert.strictEqual(result, 4); + +// Check that y is not an own property. +assert.deepStrictEqual(Object.keys(sandbox), ['z', 'x']); diff --git a/test/js/node/test/parallel/test-vm-not-strict.js b/test/js/node/test/parallel/test-vm-not-strict.js new file mode 100644 index 0000000000..8a72158a8a --- /dev/null +++ b/test/js/node/test/parallel/test-vm-not-strict.js @@ -0,0 +1,37 @@ +/* eslint-disable strict, no-var, no-delete-var, no-undef, node-core/required-modules, node-core/require-common-first */ +// Importing common would break the execution. Indeed running `vm.runInThisContext` alters the global context +// when declaring new variables with `var`. The other rules (strict, no-var, no-delete-var) have been disabled +// in order to be able to test this specific not-strict case playing with `var` and `delete`. +// Related to bug report: https://github.com/nodejs/node/issues/43129 +var assert = require('assert'); +var vm = require('vm'); + +var data = []; +var a = 'direct'; +delete a; +data.push(a); + +var item2 = vm.runInThisContext(` +var unusedB = 1; +var data = []; +var b = "this"; +delete b; +data.push(b); +data[0] +`); +data.push(item2); + +vm.runInContext( + ` +var unusedC = 1; +var c = "new"; +delete c; +data.push(c); +`, + vm.createContext({ data: data }) +); + +assert.deepStrictEqual(data, ['direct', 'this', 'new']); + +assert.strictEqual(typeof unusedB, 'number'); // Declared within runInThisContext +assert.strictEqual(typeof unusedC, 'undefined'); // Declared within runInContext diff --git a/test/js/node/test/parallel/test-vm-ownkeys.js b/test/js/node/test/parallel/test-vm-ownkeys.js new file mode 100644 index 0000000000..47938a176b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-ownkeys.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +const sym1 = Symbol('1'); +const sym2 = Symbol('2'); +const sandbox = { + a: true, + [sym1]: true, +}; +Object.defineProperty(sandbox, 'b', { value: true }); +Object.defineProperty(sandbox, sym2, { value: true }); + +const ctx = vm.createContext(sandbox); + +assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); +assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); +assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); + +const nativeKeys = vm.runInNewContext('Reflect.ownKeys(this);'); +const ownKeys = vm.runInContext('Reflect.ownKeys(this);', ctx); +const restKeys = ownKeys.filter((key) => !nativeKeys.includes(key)); +// This should not fail +assert.deepStrictEqual(Array.from(restKeys), ['a', 'b', sym1, sym2]); diff --git a/test/js/node/test/parallel/test-vm-ownpropertynames.js b/test/js/node/test/parallel/test-vm-ownpropertynames.js new file mode 100644 index 0000000000..f076195257 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-ownpropertynames.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +const sym1 = Symbol('1'); +const sym2 = Symbol('2'); +const sandbox = { + a: true, + [sym1]: true, +}; +Object.defineProperty(sandbox, 'b', { value: true }); +Object.defineProperty(sandbox, sym2, { value: true }); + +const ctx = vm.createContext(sandbox); + +assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); +assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); +assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); + +const nativeNames = vm.runInNewContext('Object.getOwnPropertyNames(this);'); +const ownNames = vm.runInContext('Object.getOwnPropertyNames(this);', ctx); +const restNames = ownNames.filter((name) => !nativeNames.includes(name)); +// This should not fail +assert.deepStrictEqual(Array.from(restNames), ['a', 'b']); diff --git a/test/js/node/test/parallel/test-vm-ownpropertysymbols.js b/test/js/node/test/parallel/test-vm-ownpropertysymbols.js new file mode 100644 index 0000000000..f943b86049 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-ownpropertysymbols.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +const sym1 = Symbol('1'); +const sym2 = Symbol('2'); +const sandbox = { + a: true, + [sym1]: true, +}; +Object.defineProperty(sandbox, 'b', { value: true }); +Object.defineProperty(sandbox, sym2, { value: true }); + +const ctx = vm.createContext(sandbox); + +assert.deepStrictEqual(Reflect.ownKeys(sandbox), ['a', 'b', sym1, sym2]); +assert.deepStrictEqual(Object.getOwnPropertyNames(sandbox), ['a', 'b']); +assert.deepStrictEqual(Object.getOwnPropertySymbols(sandbox), [sym1, sym2]); + +const nativeSym = vm.runInNewContext('Object.getOwnPropertySymbols(this);'); +const ownSym = vm.runInContext('Object.getOwnPropertySymbols(this);', ctx); +const restSym = ownSym.filter((sym) => !nativeSym.includes(sym)); +// This should not fail +assert.deepStrictEqual(Array.from(restSym), [sym1, sym2]); diff --git a/test/js/node/test/parallel/test-vm-preserves-property.js b/test/js/node/test/parallel/test-vm-preserves-property.js new file mode 100644 index 0000000000..0846fd4f79 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-preserves-property.js @@ -0,0 +1,25 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); + +const x = {}; +Object.defineProperty(x, 'prop', { + configurable: false, + enumerable: false, + writable: false, + value: 'val' +}); +const o = vm.createContext(x); + +const code = 'Object.getOwnPropertyDescriptor(this, "prop")'; +const res = vm.runInContext(code, o, 'test'); + +assert(res); +assert.strictEqual(typeof res, 'object'); +assert.strictEqual(res.value, 'val'); +assert.strictEqual(res.configurable, false); +assert.strictEqual(res.enumerable, false); +assert.strictEqual(res.writable, false); diff --git a/test/js/node/test/parallel/test-vm-set-property-proxy.js b/test/js/node/test/parallel/test-vm-set-property-proxy.js new file mode 100644 index 0000000000..2e3293bf62 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-set-property-proxy.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// Regression test for https://github.com/nodejs/node/issues/34606 + +const handler = { + getOwnPropertyDescriptor: common.mustCallAtLeast(() => { + return {}; + }) +}; + +const proxy = new Proxy({}, handler); +assert.throws(() => vm.runInNewContext('p = 6', proxy), + /getOwnPropertyDescriptor/); diff --git a/test/js/node/test/parallel/test-vm-set-proto-null-on-globalthis.js b/test/js/node/test/parallel/test-vm-set-proto-null-on-globalthis.js new file mode 100644 index 0000000000..869124fa86 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-set-proto-null-on-globalthis.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); + +// Setting __proto__ on vm context's globalThis should not cause a crash +// Regression test for https://github.com/nodejs/node/issues/47798 + +const vm = require('vm'); +const context = vm.createContext(); + +const contextGlobalThis = vm.runInContext('this', context); + +// Should not crash. +contextGlobalThis.__proto__ = null; // eslint-disable-line no-proto diff --git a/test/js/node/test/parallel/test-vm-symbols.js b/test/js/node/test/parallel/test-vm-symbols.js new file mode 100644 index 0000000000..e5a4e9e756 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-symbols.js @@ -0,0 +1,23 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); + +const symbol = Symbol(); + +function Document() { + this[symbol] = 'foo'; +} + +Document.prototype.getSymbolValue = function() { + return this[symbol]; +}; + +const context = new Document(); +vm.createContext(context); + +assert.strictEqual(context.getSymbolValue(), 'foo'); + +assert.strictEqual(vm.runInContext('this.getSymbolValue()', context), 'foo'); diff --git a/test/js/node/test/parallel/test-weakref.js b/test/js/node/test/parallel/test-weakref.js new file mode 100644 index 0000000000..ca7485aaa6 --- /dev/null +++ b/test/js/node/test/parallel/test-weakref.js @@ -0,0 +1,13 @@ +'use strict'; + +// Flags: --expose-gc + +require('../common'); +const assert = require('assert'); + +const w = new globalThis.WeakRef({}); + +setTimeout(() => { + globalThis.gc(); + assert.strictEqual(w.deref(), undefined); +}, 200); diff --git a/test/js/node/test/parallel/test-worker-fs-stat-watcher.js b/test/js/node/test/parallel/test-worker-fs-stat-watcher.js deleted file mode 100644 index c648792af7..0000000000 --- a/test/js/node/test/parallel/test-worker-fs-stat-watcher.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; -const common = require('../common'); -const { Worker, parentPort } = require('worker_threads'); -const fs = require('fs'); - -// Checks that terminating Workers does not crash the process if fs.watchFile() -// has active handles. - -// Do not use isMainThread so that this test itself can be run inside a Worker. -if (!process.env.HAS_STARTED_WORKER) { - process.env.HAS_STARTED_WORKER = 1; - const worker = new Worker(__filename); - worker.on('message', common.mustCall(() => worker.terminate())); -} else { - fs.watchFile(__filename, () => {}); - parentPort.postMessage('running'); -} diff --git a/test/js/node/test/parallel/test-worker-message-channel-sharedarraybuffer.js b/test/js/node/test/parallel/test-worker-message-channel-sharedarraybuffer.js new file mode 100644 index 0000000000..220aa978b1 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-channel-sharedarraybuffer.js @@ -0,0 +1,28 @@ +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +{ + const sharedArrayBuffer = new SharedArrayBuffer(12); + const local = Buffer.from(sharedArrayBuffer); + + const w = new Worker(` + const { parentPort } = require('worker_threads'); + parentPort.on('message', ({ sharedArrayBuffer }) => { + const local = Buffer.from(sharedArrayBuffer); + local.write('world!', 6); + parentPort.postMessage('written!'); + }); + `, { eval: true }); + w.on('message', common.mustCall(() => { + assert.strictEqual(local.toString(), 'Hello world!'); + global.gc(); + w.terminate(); + })); + w.postMessage({ sharedArrayBuffer }); + // This would be a race condition if the memory regions were overlapping + local.write('Hello '); +} diff --git a/test/js/node/test/parallel/test-worker-process-exit-async-module.js b/test/js/node/test/parallel/test-worker-process-exit-async-module.js deleted file mode 100644 index 38d4ad74c7..0000000000 --- a/test/js/node/test/parallel/test-worker-process-exit-async-module.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const { Worker } = require('worker_threads'); - -// Regression for https://github.com/nodejs/node/issues/43182. -const w = new Worker(new URL('data:text/javascript,process.exit(1);await new Promise(()=>{ process.exit(2); })')); -w.on('exit', common.mustCall((code) => { - assert.strictEqual(code, 1); -})); diff --git a/test/js/node/test/parallel/test-worker-workerdata-sharedarraybuffer.js b/test/js/node/test/parallel/test-worker-workerdata-sharedarraybuffer.js new file mode 100644 index 0000000000..4e3d508ac9 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-workerdata-sharedarraybuffer.js @@ -0,0 +1,32 @@ +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +{ + const sharedArrayBuffer = new SharedArrayBuffer(12); + const local = Buffer.from(sharedArrayBuffer); + + const w = new Worker(` + const { parentPort, workerData } = require('worker_threads'); + const local = Buffer.from(workerData.sharedArrayBuffer); + + parentPort.on('message', () => { + local.write('world!', 6); + parentPort.postMessage('written!'); + }); + `, { + eval: true, + workerData: { sharedArrayBuffer } + }); + w.on('message', common.mustCall(() => { + assert.strictEqual(local.toString(), 'Hello world!'); + global.gc(); + w.terminate(); + })); + w.postMessage({}); + // This would be a race condition if the memory regions were overlapping + local.write('Hello '); +} diff --git a/test/js/node/test/parallel/test-zlib-deflate-constructors.js b/test/js/node/test/parallel/test-zlib-deflate-constructors.js new file mode 100644 index 0000000000..bf4b6d3374 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-deflate-constructors.js @@ -0,0 +1,309 @@ +'use strict'; + +require('../common'); + +const zlib = require('zlib'); +const assert = require('assert'); + +// Work with and without `new` keyword +assert.ok(zlib.Deflate() instanceof zlib.Deflate); +assert.ok(new zlib.Deflate() instanceof zlib.Deflate); + +assert.ok(zlib.DeflateRaw() instanceof zlib.DeflateRaw); +assert.ok(new zlib.DeflateRaw() instanceof zlib.DeflateRaw); + +// Throws if `options.chunkSize` is invalid +assert.throws( + () => new zlib.Deflate({ chunkSize: 'test' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.chunkSize" property must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate({ chunkSize: -Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.chunkSize" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ chunkSize: 0 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.chunkSize" is out of range. It must ' + + 'be >= 64. Received 0' + } +); + +// Confirm that maximum chunk size cannot be exceeded because it is `Infinity`. +assert.strictEqual(zlib.constants.Z_MAX_CHUNK, Infinity); + +// Throws if `options.windowBits` is invalid +assert.throws( + () => new zlib.Deflate({ windowBits: 'test' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.windowBits" property must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate({ windowBits: -Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ windowBits: Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. It must ' + + 'be a finite number. Received Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ windowBits: 0 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. It must ' + + 'be >= 8 and <= 15. Received 0' + } +); + +// Throws if `options.level` is invalid +assert.throws( + () => new zlib.Deflate({ level: 'test' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.level" property must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate({ level: -Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.level" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ level: Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.level" is out of range. It must ' + + 'be a finite number. Received Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ level: -2 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.level" is out of range. It must ' + + 'be >= -1 and <= 9. Received -2' + } +); + +// Throws if `level` invalid in `Deflate.prototype.params()` +assert.throws( + () => new zlib.Deflate().params('test'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "level" argument must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate().params(-Infinity), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "level" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate().params(Infinity), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "level" is out of range. It must ' + + 'be a finite number. Received Infinity' + } +); + +assert.throws( + () => new zlib.Deflate().params(-2), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "level" is out of range. It must ' + + 'be >= -1 and <= 9. Received -2' + } +); + +// Throws if options.memLevel is invalid +assert.throws( + () => new zlib.Deflate({ memLevel: 'test' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.memLevel" property must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate({ memLevel: -Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.memLevel" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ memLevel: Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.memLevel" is out of range. It must ' + + 'be a finite number. Received Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ memLevel: -2 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.memLevel" is out of range. It must ' + + 'be >= 1 and <= 9. Received -2' + } +); + +// Does not throw if opts.strategy is valid +new zlib.Deflate({ strategy: zlib.constants.Z_FILTERED }); +new zlib.Deflate({ strategy: zlib.constants.Z_HUFFMAN_ONLY }); +new zlib.Deflate({ strategy: zlib.constants.Z_RLE }); +new zlib.Deflate({ strategy: zlib.constants.Z_FIXED }); +new zlib.Deflate({ strategy: zlib.constants.Z_DEFAULT_STRATEGY }); + +// Throws if options.strategy is invalid +assert.throws( + () => new zlib.Deflate({ strategy: 'test' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.strategy" property must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate({ strategy: -Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.strategy" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ strategy: Infinity }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.strategy" is out of range. It must ' + + 'be a finite number. Received Infinity' + } +); + +assert.throws( + () => new zlib.Deflate({ strategy: -2 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.strategy" is out of range. It must ' + + 'be >= 0 and <= 4. Received -2' + } +); + +// Throws TypeError if `strategy` is invalid in `Deflate.prototype.params()` +assert.throws( + () => new zlib.Deflate().params(0, 'test'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "strategy" argument must be of type number. ' + + 'Received type string ("test")' + } +); + +assert.throws( + () => new zlib.Deflate().params(0, -Infinity), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "strategy" is out of range. It must ' + + 'be a finite number. Received -Infinity' + } +); + +assert.throws( + () => new zlib.Deflate().params(0, Infinity), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "strategy" is out of range. It must ' + + 'be a finite number. Received Infinity' + } +); + +assert.throws( + () => new zlib.Deflate().params(0, -2), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "strategy" is out of range. It must ' + + 'be >= 0 and <= 4. Received -2' + } +); + +// Throws if opts.dictionary is not a Buffer +assert.throws( + () => new zlib.Deflate({ dictionary: 'not a buffer' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); diff --git a/test/js/node/test/parallel/test-zlib-dictionary.js b/test/js/node/test/parallel/test-zlib-dictionary.js new file mode 100644 index 0000000000..47eaaa62d0 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-dictionary.js @@ -0,0 +1,175 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test compression/decompression with dictionary + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const spdyDict = Buffer.from([ + 'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-', + 'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi', + 'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser', + '-agent10010120020120220320420520630030130230330430530630740040140240340440', + '5406407408409410411412413414415416417500501502503504505accept-rangesageeta', + 'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic', + 'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran', + 'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati', + 'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo', + 'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe', + 'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic', + 'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1', + '.1statusversionurl\0', +].join('')); + +const input = [ + 'HTTP/1.1 200 Ok', + 'Server: node.js', + 'Content-Length: 0', + '', +].join('\r\n'); + +function basicDictionaryTest(spdyDict) { + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.end(); +} + +function deflateResetDictionaryTest(spdyDict) { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.flush(function() { + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); + }); +} + +function rawDictionaryTest(spdyDict) { + let output = ''; + const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); + const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.end(); +} + +function deflateRawResetDictionaryTest(spdyDict) { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); + const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.flush(function() { + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); + }); +} + +for (const dict of [spdyDict, ...common.getBufferSources(spdyDict)]) { + basicDictionaryTest(dict); + deflateResetDictionaryTest(dict); + rawDictionaryTest(dict); + deflateRawResetDictionaryTest(dict); +} \ No newline at end of file diff --git a/test/js/node/test/parallel/test-zlib-failed-init.js b/test/js/node/test/parallel/test-zlib-failed-init.js new file mode 100644 index 0000000000..95f401a371 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-failed-init.js @@ -0,0 +1,46 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const zlib = require('zlib'); + +assert.throws( + () => zlib.createGzip({ chunkSize: 0 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.chunkSize" is out of range. It must ' + + 'be >= 64. Received 0' + } +); + +assert.throws( + () => zlib.createGzip({ windowBits: 0 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. It must ' + + 'be >= 9 and <= 15. Received 0' + } +); + +assert.throws( + () => zlib.createGzip({ memLevel: 0 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.memLevel" is out of range. It must ' + + 'be >= 1 and <= 9. Received 0' + } +); + +{ + const stream = zlib.createGzip({ level: NaN }); + assert.strictEqual(stream._level, zlib.constants.Z_DEFAULT_COMPRESSION); +} + +{ + const stream = zlib.createGzip({ strategy: NaN }); + assert.strictEqual(stream._strategy, zlib.constants.Z_DEFAULT_STRATEGY); +} diff --git a/test/js/node/test/parallel/test-zlib-flush-flags.js b/test/js/node/test/parallel/test-zlib-flush-flags.js new file mode 100644 index 0000000000..f3392b7a41 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-flags.js @@ -0,0 +1,48 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH }); + +assert.throws( + () => zlib.createGzip({ flush: 'foobar' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.flush" property must be of type number. ' + + 'Received type string ("foobar")' + } +); + +assert.throws( + () => zlib.createGzip({ flush: 10000 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.flush" is out of range. It must ' + + 'be >= 0 and <= 5. Received 10000' + } +); + +zlib.createGzip({ finishFlush: zlib.constants.Z_SYNC_FLUSH }); + +assert.throws( + () => zlib.createGzip({ finishFlush: 'foobar' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options.finishFlush" property must be of type number. ' + + 'Received type string ("foobar")' + } +); + +assert.throws( + () => zlib.createGzip({ finishFlush: 10000 }), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.finishFlush" is out of range. It must ' + + 'be >= 0 and <= 5. Received 10000' + } +); diff --git a/test/js/node/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js b/test/js/node/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js new file mode 100644 index 0000000000..477a6c544f --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js @@ -0,0 +1,66 @@ +'use strict'; +// Test unzipping a gzip file that has trailing garbage + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Should ignore trailing null-bytes +let data = Buffer.concat([ + zlib.gzipSync('abc'), + zlib.gzipSync('def'), + Buffer.alloc(10), +]); + +assert.strictEqual(zlib.gunzipSync(data).toString(), 'abcdef'); + +zlib.gunzip(data, common.mustSucceed((result) => { + assert.strictEqual( + result.toString(), + 'abcdef', + `result '${result.toString()}' should match original string` + ); +})); + +// If the trailing garbage happens to look like a gzip header, it should +// throw an error. +data = Buffer.concat([ + zlib.gzipSync('abc'), + zlib.gzipSync('def'), + Buffer.from([0x1f, 0x8b, 0xff, 0xff]), + Buffer.alloc(10), +]); + +assert.throws( + () => zlib.gunzipSync(data), + /^Error: unknown compression method$/ +); + +zlib.gunzip(data, common.mustCall((err, result) => { + common.expectsError({ + code: 'Z_DATA_ERROR', + name: 'Error', + message: 'unknown compression method' + })(err); + assert.strictEqual(result, undefined); +})); + +// In this case the trailing junk is too short to be a gzip segment +// So we ignore it and decompression succeeds. +data = Buffer.concat([ + zlib.gzipSync('abc'), + zlib.gzipSync('def'), + Buffer.from([0x1f, 0x8b, 0xff, 0xff]), +]); + +assert.throws( + () => zlib.gunzipSync(data), + /^Error: unknown compression method$/ +); + +zlib.gunzip(data, common.mustCall((err, result) => { + assert(err instanceof Error); + assert.strictEqual(err.code, 'Z_DATA_ERROR'); + assert.strictEqual(err.message, 'unknown compression method'); + assert.strictEqual(result, undefined); +})); diff --git a/test/js/node/test/parallel/test-zlib-invalid-input-memory.js b/test/js/node/test/parallel/test-zlib-invalid-input-memory.js new file mode 100644 index 0000000000..c4dbe4c081 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-invalid-input-memory.js @@ -0,0 +1,28 @@ +// Flags: --expose-gc +'use strict'; +const common = require('../common'); +const { onGC } = require('../common/gc'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Checks that, if a zlib context fails with an error, it can still be GC'ed: +// Refs: https://github.com/nodejs/node/issues/22705 + +const ongc = common.mustCall(); + +{ + const input = Buffer.from('foobar'); + const strm = zlib.createInflate(); + strm.end(input); + strm.once('error', common.mustCall((err) => { + assert(err); + setImmediate(() => { + global.gc(); + // Keep the event loop alive for seeing the async_hooks destroy hook + // we use for GC tracking... + // TODO(addaleax): This should maybe not be necessary? + setImmediate(() => {}); + }); + })); + onGC(strm, { ongc }); +} \ No newline at end of file diff --git a/test/js/node/test/parallel/test-zlib-not-string-or-buffer.js b/test/js/node/test/parallel/test-zlib-not-string-or-buffer.js new file mode 100644 index 0000000000..35e969737f --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-not-string-or-buffer.js @@ -0,0 +1,30 @@ +'use strict'; + +// Check the error condition testing for passing something other than a string +// or buffer. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +[ + undefined, + null, + true, + false, + 0, + 1, + [1, 2, 3], + { foo: 'bar' }, +].forEach((input) => { + assert.throws( + () => zlib.deflateSync(input), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "buffer" argument must be of type string, ' + + 'Buffer, TypedArray, DataView, or ArrayBuffer.' + + common.invalidArgTypeHelper(input) + } + ); +}); diff --git a/test/js/node/util/bun-inspect.test.ts b/test/js/node/util/bun-inspect.test.ts index 57151a7b5b..65de3b0ed4 100644 --- a/test/js/node/util/bun-inspect.test.ts +++ b/test/js/node/util/bun-inspect.test.ts @@ -3,13 +3,13 @@ import stripAnsi from "strip-ansi"; describe("Bun.inspect", () => { it("reports error instead of [native code]", () => { - expect( + expect(() => Bun.inspect({ [Symbol.for("nodejs.util.inspect.custom")]() { throw new Error("custom inspect"); }, }), - ).toBe("[custom formatter threw an exception]"); + ).toThrow("custom inspect"); }); it("supports colors: false", () => { @@ -47,18 +47,46 @@ describe("Bun.inspect", () => { expect(() => Bun.inspect({}, { depth: -1 })).toThrow(); expect(() => Bun.inspect({}, { depth: -13210 })).toThrow(); }); - it("depth = Infinity works", () => { - function createRecursiveObject(n: number): any { - if (n === 0) return { hi: true }; - return { a: createRecursiveObject(n - 1) }; + for (let base of [new Error("hi"), { a: "hi" }]) { + it(`depth = Infinity works for ${base.constructor.name}`, () => { + function createRecursiveObject(n: number): any { + if (n === 0) { + return { a: base }; + } + return { a: createRecursiveObject(n - 1) }; + } + + const obj = createRecursiveObject(512); + expect(Bun.inspect(obj, { depth: Infinity })).toContain("hi"); + // this gets converted to u16, which if just truncating, will turn into 0 + expect(Bun.inspect(obj, { depth: 0x0fff0000 })).toContain("hi"); + }); + } + + it("stack overflow is thrown when it should be for objects", () => { + var object = { a: { b: { c: { d: 1 } } } }; + for (let i = 0; i < 16 * 1024; i++) { + object = { a: object }; } - const obj = createRecursiveObject(1000); - - expect(Bun.inspect(obj, { depth: Infinity })).toContain("hi"); - // this gets converted to u16, which if just truncating, will turn into 0 - expect(Bun.inspect(obj, { depth: 0x0fff0000 })).toContain("hi"); + expect(() => Bun.inspect(object, { depth: Infinity })).toThrowErrorMatchingInlineSnapshot( + `"Maximum call stack size exceeded."`, + ); }); + + it("stack overflow is thrown when it should be for Error", () => { + var object = { a: { b: { c: { d: 1 } } } }; + for (let i = 0; i < 16 * 1024; i++) { + const err = new Error("hello"); + err.object = object; + object = err; + } + + expect(() => Bun.inspect(object, { depth: Infinity })).toThrowErrorMatchingInlineSnapshot( + `"Maximum call stack size exceeded."`, + ); + }); + it("depth = 0", () => { expect(Bun.inspect({ a: { b: { c: { d: 1 } } } }, { depth: 0 })).toEqual("{\n a: [Object ...],\n}"); }); diff --git a/test/js/node/util/custom-inspect.test.js b/test/js/node/util/custom-inspect.test.js index 040f0abb8d..bc8763b214 100644 --- a/test/js/node/util/custom-inspect.test.js +++ b/test/js/node/util/custom-inspect.test.js @@ -155,11 +155,6 @@ for (const [name, inspect] of process.versions.bun }, }; - if (Bun.inspect === inspect) { - // make sure this doesnt crash - expect(inspect(obj)).toBeString(); - } else { - expect(() => inspect(obj)).toThrow(); - } + expect(() => inspect(obj)).toThrow(); }); } diff --git a/test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js b/test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js index 0c4bc55a79..5dffd034fa 100644 --- a/test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js +++ b/test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js @@ -656,14 +656,9 @@ test("no assertion failures 2", () => { // Prevent non-enumerable error properties from being printed. { - // TODO(bun): Make originalLine and originalColumn non-enumerable let err = new Error(); err.message = "foobar"; - let out = util - .inspect(err) - .replace(/\{\s*originalLine: .+\s*originalColumn: .+\s*\}/, "") - .trim() - .split("\n"); + let out = util.inspect(err).trim().split("\n"); assert.strictEqual(out[0], "Error: foobar"); assert(out.at(-1).startsWith(" at "), 'Expected "' + out.at(-1) + '" to start with " at "'); // Reset the error, the stack is otherwise not recreated. @@ -671,21 +666,13 @@ test("no assertion failures 2", () => { err.message = "foobar"; err.name = "Unique"; Object.defineProperty(err, "stack", { value: err.stack, enumerable: true }); - out = util - .inspect(err) - .replace(/\{\s*originalLine: .+\s*originalColumn: .+\s*\}/, "") - .trim() - .split("\n"); + out = util.inspect(err).trim().split("\n"); assert.strictEqual(out[0], "Unique: foobar"); assert(out.at(-1).startsWith(" at "), 'Expected "' + out.at(-1) + '" to start with " at "'); err.name = "Baz"; - out = util - .inspect(err) - .replace(/\n\s*originalLine: .+\s*originalColumn: .+/, "") - .trim() - .split("\n"); + out = util.inspect(err).trim().split("\n"); assert.strictEqual(out[0], "Unique: foobar"); - assert.strictEqual(out.at(-2), " name: 'Baz',"); + assert.strictEqual(out.at(-2), " name: 'Baz'"); assert.strictEqual(out.at(-1), "}"); } diff --git a/test/js/node/util/test-util-types.test.js b/test/js/node/util/test-util-types.test.js index 031d18ca20..084da3bfac 100644 --- a/test/js/node/util/test-util-types.test.js +++ b/test/js/node/util/test-util-types.test.js @@ -47,6 +47,7 @@ for (const [value, _method] of [ [new DataView(new ArrayBuffer())], [new SharedArrayBuffer()], [new Proxy({}, {}), "isProxy"], + [new EventTarget()], ]) { const method = _method || `is${value.constructor.name}`; test(method, () => { diff --git a/test/js/node/util/util.test.js b/test/js/node/util/util.test.js index dd55d12b1a..3f02c2b183 100644 --- a/test/js/node/util/util.test.js +++ b/test/js/node/util/util.test.js @@ -341,7 +341,13 @@ describe("util", () => { }); it("styleText", () => { - [undefined, null, false, 5n, 5, Symbol(), () => {}, {}, []].forEach(invalidOption => { + it("multiplecolors", () => { + expect(util.styleText(["bold", "red"], "test")).toBe("\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m"); + expect(util.styleText("bold"), "test").toBe("\u001b[1mtest\u001b[22m"); + expect(util.styleText("red", "test")).toBe("\u001b[31mtest\u001b[39m"); + }); + + [undefined, null, false, 5n, 5, Symbol(), () => {}, {}].forEach(invalidOption => { assert.throws( () => { util.styleText(invalidOption, "test"); diff --git a/test/js/node/worker_threads/worker_thread_check.ts b/test/js/node/worker_threads/worker_thread_check.ts index 48ae9de7a1..004786cad6 100644 --- a/test/js/node/worker_threads/worker_thread_check.ts +++ b/test/js/node/worker_threads/worker_thread_check.ts @@ -28,6 +28,7 @@ if (isMainThread) { action, port: server.port, }, + env: process.env, }); worker.ref(); const { promise, resolve } = Promise.withResolvers(); diff --git a/test/js/sql/sql.test.ts b/test/js/sql/sql.test.ts index 8c0089c760..92fd82931b 100644 --- a/test/js/sql/sql.test.ts +++ b/test/js/sql/sql.test.ts @@ -1,5 +1,5 @@ import { postgres, sql } from "bun:sql"; -import { expect, test } from "bun:test"; +import { expect, test, mock } from "bun:test"; import { $ } from "bun"; import { bunExe, isCI, withoutAggressiveGC } from "harness"; import path from "path"; @@ -13,18 +13,20 @@ if (!isCI) { // local all postgres trust // local all bun_sql_test_scram scram-sha-256 // local all bun_sql_test trust - // + // local all bun_sql_test_md5 md5 + // # IPv4 local connections: // host all ${USERNAME} 127.0.0.1/32 trust // host all postgres 127.0.0.1/32 trust // host all bun_sql_test_scram 127.0.0.1/32 scram-sha-256 // host all bun_sql_test 127.0.0.1/32 trust + // host all bun_sql_test_md5 127.0.0.1/32 md5 // # IPv6 local connections: // host all ${USERNAME} ::1/128 trust // host all postgres ::1/128 trust // host all bun_sql_test ::1/128 trust // host all bun_sql_test_scram ::1/128 scram-sha-256 - // + // host all bun_sql_test_md5 ::1/128 md5 // # Allow replication connections from localhost, by a user with the // # replication privilege. // local replication all trust @@ -33,9 +35,6 @@ if (!isCI) { // --- Expected pg_hba.conf --- process.env.DATABASE_URL = "postgres://bun_sql_test@localhost:5432/bun_sql_test"; - const delay = ms => Bun.sleep(ms); - const rel = x => new URL(x, import.meta.url); - const login = { username: "bun_sql_test", }; @@ -54,8 +53,8 @@ if (!isCI) { db: "bun_sql_test", username: login.username, password: login.password, - idle_timeout: 1, - connect_timeout: 1, + idle_timeout: 0, + connect_timeout: 0, max: 1, }; @@ -67,6 +66,97 @@ if (!isCI) { expect(result).toBe(1); }); + test("Connection timeout works", async () => { + const onclose = mock(); + const onconnect = mock(); + await using sql = postgres({ + ...options, + hostname: "unreachable_host", + connection_timeout: 1, + onconnect, + onclose, + }); + let error: any; + try { + await sql`select pg_sleep(2)`; + } catch (e) { + error = e; + } + expect(error.code).toBe(`ERR_POSTGRES_CONNECTION_TIMEOUT`); + expect(error.message).toContain("Connection timeout after 1ms"); + expect(onconnect).not.toHaveBeenCalled(); + expect(onclose).toHaveBeenCalledTimes(1); + }); + + test("Idle timeout works at start", async () => { + const onclose = mock(); + const onconnect = mock(); + await using sql = postgres({ + ...options, + idle_timeout: 1, + onconnect, + onclose, + }); + let error: any; + try { + await sql`select pg_sleep(2)`; + } catch (e) { + error = e; + } + expect(error.code).toBe(`ERR_POSTGRES_IDLE_TIMEOUT`); + expect(onconnect).toHaveBeenCalled(); + expect(onclose).toHaveBeenCalledTimes(1); + }); + + test("Idle timeout is reset when a query is run", async () => { + const onClosePromise = Promise.withResolvers(); + const onclose = mock(err => { + onClosePromise.resolve(err); + }); + const onconnect = mock(); + await using sql = postgres({ + ...options, + idle_timeout: 100, + onconnect, + onclose, + }); + expect(await sql`select 123 as x`).toEqual([{ x: 123 }]); + expect(onconnect).toHaveBeenCalledTimes(1); + expect(onclose).not.toHaveBeenCalled(); + const err = await onClosePromise.promise; + expect(err.code).toBe(`ERR_POSTGRES_IDLE_TIMEOUT`); + }); + + test("Max lifetime works", async () => { + const onClosePromise = Promise.withResolvers(); + const onclose = mock(err => { + onClosePromise.resolve(err); + }); + const onconnect = mock(); + const sql = postgres({ + ...options, + max_lifetime: 64, + onconnect, + onclose, + }); + let error: any; + expect(await sql`select 1 as x`).toEqual([{ x: 1 }]); + expect(onconnect).toHaveBeenCalledTimes(1); + try { + while (true) { + for (let i = 0; i < 100; i++) { + await sql`select pg_sleep(1)`; + } + } + } catch (e) { + error = e; + } + + expect(onclose).toHaveBeenCalledTimes(1); + + expect(error.code).toBe(`ERR_POSTGRES_LIFETIME_TIMEOUT`); + }); + test("Uses default database without slash", async () => { const sql = postgres("postgres://localhost"); expect(sql.options.username).toBe(sql.options.database); @@ -145,10 +235,9 @@ if (!isCI) { expect(x).toEqual({ a: "hello", b: 42 }); }); - // It's treating as a string. - test.todo("implicit jsonb", async () => { + test("implicit jsonb", async () => { const x = (await sql`select ${{ a: "hello", b: 42 }}::jsonb as x`)[0].x; - expect([x.a, x.b].join(",")).toBe("hello,42"); + expect(x).toEqual({ a: "hello", b: 42 }); }); test("bulk insert nested sql()", async () => { @@ -428,9 +517,11 @@ if (!isCI) { test("Null sets to null", async () => expect((await sql`select ${null} as x`)[0].x).toBeNull()); // Add code property. - test.todo("Throw syntax error", async () => { - const code = await sql`wat 1`.catch(x => x); - console.log({ code }); + test("Throw syntax error", async () => { + const err = await sql`wat 1`.catch(x => x); + expect(err.code).toBe("ERR_POSTGRES_SYNTAX_ERROR"); + expect(err.errno).toBe(42601); + expect(err).toBeInstanceOf(SyntaxError); }); // t('Connect using uri', async() => @@ -502,13 +593,26 @@ if (!isCI) { // return [1, (await sql`select 1 as x`)[0].x] // }) - // t('Login without password', async() => { - // return [true, (await postgres({ ...options, ...login })`select true as x`)[0].x] - // }) + test("Login without password", async () => { + await using sql = postgres({ ...options, ...login }); + expect((await sql`select true as x`)[0].x).toBe(true); + }); - // t('Login using MD5', async() => { - // return [true, (await postgres({ ...options, ...login_md5 })`select true as x`)[0].x] - // }) + test("Login using MD5", async () => { + await using sql = postgres({ ...options, ...login_md5 }); + expect(await sql`select true as x`).toEqual([{ x: true }]); + }); + + test("Login with bad credentials propagates error from server", async () => { + const sql = postgres({ ...options, ...login_md5, username: "bad_user", password: "bad_password" }); + let err; + try { + await sql`select true as x`; + } catch (e) { + err = e; + } + expect(err.code).toBe("ERR_POSTGRES_SERVER_ERROR"); + }); test("Login using scram-sha-256", async () => { await using sql = postgres({ ...options, ...login_scram }); @@ -1159,9 +1263,10 @@ if (!isCI) { // ] // }) - // t('dynamic column name', async() => { - // return ['!not_valid', Object.keys((await sql`select 1 as ${ sql('!not_valid') }`)[0])[0]] - // }) + test.todo("dynamic column name", async () => { + const result = await sql`select 1 as ${"\\!not_valid"}`; + expect(Object.keys(result[0])[0]).toBe("!not_valid"); + }); // t('dynamic select as', async() => { // return ['2', (await sql`select ${ sql({ a: 1, b: 2 }) }`)[0].b] @@ -1178,12 +1283,12 @@ if (!isCI) { // return ['the answer', (await sql`insert into test ${ sql(x) } returning *`)[0].b, await sql`drop table test`] // }) - // t('dynamic insert pluck', async() => { - // await sql`create table test (a int, b text)` - // const x = { a: 42, b: 'the answer' } - - // return [null, (await sql`insert into test ${ sql(x, 'a') } returning *`)[0].b, await sql`drop table test`] - // }) + // test.todo("dynamic insert pluck", async () => { + // await sql`create table test (a int, b text)`; + // const x = { a: 42, b: "the answer" }; + // const [{ b }] = await sql`insert into test ${sql(x, "a")} returning *`; + // expect(b).toBe("the answer"); + // }); // t('dynamic in with empty array', async() => { // await sql`create table test (a int)` diff --git a/test/js/third_party/@duckdb/node-api/duckdb.test.ts b/test/js/third_party/@duckdb/node-api/duckdb.test.ts new file mode 100644 index 0000000000..4fb3c1e012 --- /dev/null +++ b/test/js/third_party/@duckdb/node-api/duckdb.test.ts @@ -0,0 +1,1038 @@ +// Copyright 2018-2023 Stichting DuckDB Foundation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Copied from https://github.com/duckdb/duckdb-node-neo/blob/3f85023c6b42d6b288a2e0f92dd7c7b40cf2a63c/api/test/api.test.ts, +// with minor modifications to work as a Bun test + +import { libcFamily } from "harness"; +if (libcFamily == "musl") { + // @duckdb/node-bindings does not distribute musl binaries, so we skip this test on musl to avoid CI noise + process.exit(0); +} + +import { describe, test } from "bun:test"; +import assert from "node:assert"; +// Must be CJS require so that the above code can exit before we attempt to import DuckDB +const { + DateParts, + DuckDBAnyType, + DuckDBArrayType, + DuckDBArrayVector, + DuckDBBigIntType, + DuckDBBigIntVector, + DuckDBBitType, + DuckDBBitVector, + DuckDBBlobType, + DuckDBBlobValue, + DuckDBBlobVector, + DuckDBBooleanType, + DuckDBBooleanVector, + DuckDBConnection, + DuckDBDataChunk, + DuckDBDateType, + DuckDBDateValue, + DuckDBDateVector, + DuckDBDecimal16Vector, + DuckDBDecimal2Vector, + DuckDBDecimal4Vector, + DuckDBDecimal8Vector, + DuckDBDecimalType, + DuckDBDecimalValue, + DuckDBDoubleType, + DuckDBDoubleVector, + DuckDBEnum1Vector, + DuckDBEnum2Vector, + DuckDBEnum4Vector, + DuckDBEnumType, + DuckDBFloatType, + DuckDBFloatVector, + DuckDBHugeIntType, + DuckDBHugeIntVector, + DuckDBInstance, + DuckDBIntegerType, + DuckDBIntegerVector, + DuckDBIntervalType, + DuckDBIntervalVector, + DuckDBListType, + DuckDBListVector, + DuckDBMapType, + DuckDBMapVector, + DuckDBPendingResultState, + DuckDBResult, + DuckDBSQLNullType, + DuckDBSmallIntType, + DuckDBSmallIntVector, + DuckDBStructType, + DuckDBStructVector, + DuckDBTimeTZType, + DuckDBTimeTZValue, + DuckDBTimeTZVector, + DuckDBTimeType, + DuckDBTimeValue, + DuckDBTimeVector, + DuckDBTimestampMillisecondsType, + DuckDBTimestampMillisecondsValue, + DuckDBTimestampMillisecondsVector, + DuckDBTimestampNanosecondsType, + DuckDBTimestampNanosecondsValue, + DuckDBTimestampNanosecondsVector, + DuckDBTimestampSecondsType, + DuckDBTimestampSecondsValue, + DuckDBTimestampSecondsVector, + DuckDBTimestampTZType, + DuckDBTimestampTZValue, + DuckDBTimestampTZVector, + DuckDBTimestampType, + DuckDBTimestampValue, + DuckDBTimestampVector, + DuckDBTinyIntType, + DuckDBTinyIntVector, + DuckDBType, + DuckDBTypeId, + DuckDBUBigIntType, + DuckDBUBigIntVector, + DuckDBUHugeIntType, + DuckDBUHugeIntVector, + DuckDBUIntegerType, + DuckDBUIntegerVector, + DuckDBUSmallIntType, + DuckDBUSmallIntVector, + DuckDBUTinyIntType, + DuckDBUTinyIntVector, + DuckDBUUIDType, + DuckDBUUIDValue, + DuckDBUUIDVector, + DuckDBUnionType, + DuckDBUnionVector, + DuckDBValue, + DuckDBVarCharType, + DuckDBVarCharVector, + DuckDBVarIntType, + DuckDBVarIntVector, + DuckDBVector, + ResultReturnType, + StatementType, + TimeParts, + TimeTZParts, + TimestampParts, + arrayValue, + bitValue, + configurationOptionDescriptions, + dateValue, + decimalValue, + intervalValue, + listValue, + mapValue, + structValue, + timeTZValue, + timeValue, + timestampTZValue, + timestampValue, + unionValue, + version, +} = require("@duckdb/node-api"); + +const BI_10_8 = 100000000n; +const BI_10_10 = 10000000000n; +const BI_18_9s = BI_10_8 * BI_10_10 - 1n; +const BI_38_9s = BI_10_8 * BI_10_10 * BI_10_10 * BI_10_10 - 1n; + +async function sleep(ms: number): Promise { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + +async function withConnection(fn: (connection: DuckDBConnection) => Promise) { + const instance = await DuckDBInstance.create(); + const connection = await instance.connect(); + await fn(connection); +} + +interface ExpectedColumn { + readonly name: string; + readonly type: DuckDBType; +} + +function assertColumns(result: DuckDBResult, expectedColumns: readonly ExpectedColumn[]) { + assert.strictEqual(result.columnCount, expectedColumns.length, "column count"); + for (let i = 0; i < expectedColumns.length; i++) { + const { name, type } = expectedColumns[i]; + assert.strictEqual(result.columnName(i), name, "column name"); + assert.strictEqual(result.columnTypeId(i), type.typeId, `column type id (column: ${name})`); + assert.deepStrictEqual(result.columnType(i), type, `column type (column: ${name})`); + } +} + +function isVectorType>( + vector: DuckDBVector | null, + vectorType: new (...args: any[]) => TVector, +): vector is TVector { + return vector instanceof vectorType; +} + +function getColumnVector>( + chunk: DuckDBDataChunk, + columnIndex: number, + vectorType: new (...args: any[]) => TVector, +): TVector { + const columnVector = chunk.getColumnVector(columnIndex); + if (!isVectorType(columnVector, vectorType)) { + assert.fail(`expected column ${columnIndex} to be a ${vectorType}`); + } + return columnVector; +} + +function assertVectorValues( + vector: DuckDBVector | null | undefined, + values: readonly TValue[], + vectorName: string, +) { + if (!vector) { + assert.fail(`${vectorName} unexpectedly null or undefined`); + } + assert.strictEqual( + vector.itemCount, + values.length, + `expected vector ${vectorName} item count to be ${values.length} but found ${vector.itemCount}`, + ); + for (let i = 0; i < values.length; i++) { + const actual: TValue | null = vector.getItem(i); + const expected = values[i]; + assert.deepStrictEqual( + actual, + expected, + `expected vector ${vectorName}[${i}] to be ${expected} but found ${actual}`, + ); + } +} + +function assertValues>( + chunk: DuckDBDataChunk, + columnIndex: number, + vectorType: new (...args: any[]) => TVector, + values: readonly (TValue | null)[], +) { + const vector = getColumnVector(chunk, columnIndex, vectorType); + assertVectorValues(vector, values, `${columnIndex}`); +} + +function bigints(start: bigint, end: bigint) { + return Array.from({ length: Number(end - start) + 1 }).map((_, i) => start + BigInt(i)); +} + +describe("api", () => { + test("should expose version", () => { + const ver = version(); + assert.ok(ver.startsWith("v"), `version starts with 'v'`); + }); + test("should expose configuration option descriptions", () => { + const descriptions = configurationOptionDescriptions(); + assert.ok(descriptions["memory_limit"], `descriptions has 'memory_limit'`); + }); + test("ReturnResultType enum", () => { + assert.equal(ResultReturnType.INVALID, 0); + assert.equal(ResultReturnType.CHANGED_ROWS, 1); + assert.equal(ResultReturnType.NOTHING, 2); + assert.equal(ResultReturnType.QUERY_RESULT, 3); + + assert.equal(ResultReturnType[ResultReturnType.INVALID], "INVALID"); + assert.equal(ResultReturnType[ResultReturnType.CHANGED_ROWS], "CHANGED_ROWS"); + assert.equal(ResultReturnType[ResultReturnType.NOTHING], "NOTHING"); + assert.equal(ResultReturnType[ResultReturnType.QUERY_RESULT], "QUERY_RESULT"); + }); + test("StatementType enum", () => { + assert.equal(StatementType.INVALID, 0); + assert.equal(StatementType.SELECT, 1); + assert.equal(StatementType.INSERT, 2); + assert.equal(StatementType.UPDATE, 3); + assert.equal(StatementType.EXPLAIN, 4); + assert.equal(StatementType.DELETE, 5); + assert.equal(StatementType.PREPARE, 6); + assert.equal(StatementType.CREATE, 7); + assert.equal(StatementType.EXECUTE, 8); + assert.equal(StatementType.ALTER, 9); + assert.equal(StatementType.TRANSACTION, 10); + assert.equal(StatementType.COPY, 11); + assert.equal(StatementType.ANALYZE, 12); + assert.equal(StatementType.VARIABLE_SET, 13); + assert.equal(StatementType.CREATE_FUNC, 14); + assert.equal(StatementType.DROP, 15); + assert.equal(StatementType.EXPORT, 16); + assert.equal(StatementType.PRAGMA, 17); + assert.equal(StatementType.VACUUM, 18); + assert.equal(StatementType.CALL, 19); + assert.equal(StatementType.SET, 20); + assert.equal(StatementType.LOAD, 21); + assert.equal(StatementType.RELATION, 22); + assert.equal(StatementType.EXTENSION, 23); + assert.equal(StatementType.LOGICAL_PLAN, 24); + assert.equal(StatementType.ATTACH, 25); + assert.equal(StatementType.DETACH, 26); + assert.equal(StatementType.MULTI, 27); + + assert.equal(StatementType[StatementType.INVALID], "INVALID"); + assert.equal(StatementType[StatementType.SELECT], "SELECT"); + assert.equal(StatementType[StatementType.INSERT], "INSERT"); + assert.equal(StatementType[StatementType.UPDATE], "UPDATE"); + assert.equal(StatementType[StatementType.EXPLAIN], "EXPLAIN"); + assert.equal(StatementType[StatementType.DELETE], "DELETE"); + assert.equal(StatementType[StatementType.PREPARE], "PREPARE"); + assert.equal(StatementType[StatementType.CREATE], "CREATE"); + assert.equal(StatementType[StatementType.EXECUTE], "EXECUTE"); + assert.equal(StatementType[StatementType.ALTER], "ALTER"); + assert.equal(StatementType[StatementType.TRANSACTION], "TRANSACTION"); + assert.equal(StatementType[StatementType.COPY], "COPY"); + assert.equal(StatementType[StatementType.ANALYZE], "ANALYZE"); + assert.equal(StatementType[StatementType.VARIABLE_SET], "VARIABLE_SET"); + assert.equal(StatementType[StatementType.CREATE_FUNC], "CREATE_FUNC"); + assert.equal(StatementType[StatementType.DROP], "DROP"); + assert.equal(StatementType[StatementType.EXPORT], "EXPORT"); + assert.equal(StatementType[StatementType.PRAGMA], "PRAGMA"); + assert.equal(StatementType[StatementType.VACUUM], "VACUUM"); + assert.equal(StatementType[StatementType.CALL], "CALL"); + assert.equal(StatementType[StatementType.SET], "SET"); + assert.equal(StatementType[StatementType.LOAD], "LOAD"); + assert.equal(StatementType[StatementType.RELATION], "RELATION"); + assert.equal(StatementType[StatementType.EXTENSION], "EXTENSION"); + assert.equal(StatementType[StatementType.LOGICAL_PLAN], "LOGICAL_PLAN"); + assert.equal(StatementType[StatementType.ATTACH], "ATTACH"); + assert.equal(StatementType[StatementType.DETACH], "DETACH"); + assert.equal(StatementType[StatementType.MULTI], "MULTI"); + }); + test("DuckDBType toString", () => { + assert.equal(DuckDBBooleanType.instance.toString(), "BOOLEAN"); + assert.equal(DuckDBTinyIntType.instance.toString(), "TINYINT"); + assert.equal(DuckDBSmallIntType.instance.toString(), "SMALLINT"); + assert.equal(DuckDBIntegerType.instance.toString(), "INTEGER"); + assert.equal(DuckDBBigIntType.instance.toString(), "BIGINT"); + assert.equal(DuckDBUTinyIntType.instance.toString(), "UTINYINT"); + assert.equal(DuckDBUSmallIntType.instance.toString(), "USMALLINT"); + assert.equal(DuckDBUIntegerType.instance.toString(), "UINTEGER"); + assert.equal(DuckDBUBigIntType.instance.toString(), "UBIGINT"); + assert.equal(DuckDBFloatType.instance.toString(), "FLOAT"); + assert.equal(DuckDBDoubleType.instance.toString(), "DOUBLE"); + assert.equal(DuckDBTimestampType.instance.toString(), "TIMESTAMP"); + assert.equal(DuckDBDateType.instance.toString(), "DATE"); + assert.equal(DuckDBTimeType.instance.toString(), "TIME"); + assert.equal(DuckDBIntervalType.instance.toString(), "INTERVAL"); + assert.equal(DuckDBHugeIntType.instance.toString(), "HUGEINT"); + assert.equal(DuckDBUHugeIntType.instance.toString(), "UHUGEINT"); + assert.equal(DuckDBVarCharType.instance.toString(), "VARCHAR"); + assert.equal(DuckDBBlobType.instance.toString(), "BLOB"); + assert.equal(new DuckDBDecimalType(17, 5).toString(), "DECIMAL(17,5)"); + assert.equal(DuckDBTimestampSecondsType.instance.toString(), "TIMESTAMP_S"); + assert.equal(DuckDBTimestampMillisecondsType.instance.toString(), "TIMESTAMP_MS"); + assert.equal(DuckDBTimestampNanosecondsType.instance.toString(), "TIMESTAMP_NS"); + assert.equal( + new DuckDBEnumType(["fly", "swim", "walk"], DuckDBTypeId.UTINYINT).toString(), + `ENUM('fly', 'swim', 'walk')`, + ); + assert.equal(new DuckDBListType(DuckDBIntegerType.instance).toString(), "INTEGER[]"); + assert.equal( + new DuckDBStructType(["id", "ts"], [DuckDBVarCharType.instance, DuckDBTimestampType.instance]).toString(), + 'STRUCT("id" VARCHAR, "ts" TIMESTAMP)', + ); + assert.equal( + new DuckDBMapType(DuckDBIntegerType.instance, DuckDBVarCharType.instance).toString(), + "MAP(INTEGER, VARCHAR)", + ); + assert.equal(new DuckDBArrayType(DuckDBIntegerType.instance, 3).toString(), "INTEGER[3]"); + assert.equal(DuckDBUUIDType.instance.toString(), "UUID"); + assert.equal( + new DuckDBUnionType(["str", "num"], [DuckDBVarCharType.instance, DuckDBIntegerType.instance]).toString(), + 'UNION("str" VARCHAR, "num" INTEGER)', + ); + assert.equal(DuckDBBitType.instance.toString(), "BIT"); + assert.equal(DuckDBTimeTZType.instance.toString(), "TIME WITH TIME ZONE"); + assert.equal(DuckDBTimestampTZType.instance.toString(), "TIMESTAMP WITH TIME ZONE"); + assert.equal(DuckDBAnyType.instance.toString(), "ANY"); + assert.equal(DuckDBVarIntType.instance.toString(), "VARINT"); + assert.equal(DuckDBSQLNullType.instance.toString(), "SQLNULL"); + }); + test("should support creating, connecting, running a basic query, and reading results", async () => { + const instance = await DuckDBInstance.create(); + const connection = await instance.connect(); + const result = await connection.run("select 42 as num"); + assertColumns(result, [{ name: "num", type: DuckDBIntegerType.instance }]); + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 1); + assert.strictEqual(chunk.rowCount, 1); + assertValues(chunk, 0, DuckDBIntegerVector, [42]); + }); + test("should support running prepared statements", async () => { + await withConnection(async connection => { + const prepared = await connection.prepare("select $num as a, $str as b, $bool as c, $null as d"); + assert.strictEqual(prepared.parameterCount, 4); + assert.strictEqual(prepared.parameterName(1), "num"); + assert.strictEqual(prepared.parameterName(2), "str"); + assert.strictEqual(prepared.parameterName(3), "bool"); + assert.strictEqual(prepared.parameterName(4), "null"); + prepared.bindInteger(1, 10); + prepared.bindVarchar(2, "abc"); + prepared.bindBoolean(3, true); + prepared.bindNull(4); + const result = await prepared.run(); + assertColumns(result, [ + { name: "a", type: DuckDBIntegerType.instance }, + { name: "b", type: DuckDBVarCharType.instance }, + { name: "c", type: DuckDBBooleanType.instance }, + { name: "d", type: DuckDBIntegerType.instance }, + ]); + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 4); + assert.strictEqual(chunk.rowCount, 1); + assertValues(chunk, 0, DuckDBIntegerVector, [10]); + assertValues(chunk, 1, DuckDBVarCharVector, ["abc"]); + assertValues(chunk, 2, DuckDBBooleanVector, [true]); + assertValues(chunk, 3, DuckDBIntegerVector, [null]); + }); + }); + test("should support starting prepared statements and running them incrementally", async () => { + await withConnection(async connection => { + const prepared = await connection.prepare("select int from test_all_types()"); + const pending = prepared.start(); + let taskCount = 0; + while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) { + taskCount++; + if (taskCount > 100) { + // arbitrary upper bound on the number of tasks expected for this simple query + assert.fail("Unexpectedly large number of tasks"); + } + await sleep(1); + } + // console.debug('task count: ', taskCount); + const result = await pending.getResult(); + assertColumns(result, [{ name: "int", type: DuckDBIntegerType.instance }]); + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 1); + assert.strictEqual(chunk.rowCount, 3); + assertValues(chunk, 0, DuckDBIntegerVector, [DuckDBIntegerType.Min, DuckDBIntegerType.Max, null]); + }); + }); + test("should support streaming results from prepared statements", async () => { + await withConnection(async connection => { + const prepared = await connection.prepare("from range(10000)"); + const pending = prepared.start(); + const result = await pending.getResult(); + assertColumns(result, [{ name: "range", type: DuckDBBigIntType.instance }]); + const chunks: DuckDBDataChunk[] = []; + let currentChunk: DuckDBDataChunk | null = null; + currentChunk = await result.fetchChunk(); + while (currentChunk.rowCount > 0) { + chunks.push(currentChunk); + currentChunk = await result.fetchChunk(); + } + currentChunk = null; + assert.strictEqual(chunks.length, 5); // ceil(10000 / 2048) = 5 + assertValues(chunks[0], 0, DuckDBBigIntVector, bigints(0n, 2048n - 1n)); + assertValues(chunks[1], 0, DuckDBBigIntVector, bigints(2048n, 2048n * 2n - 1n)); + assertValues(chunks[2], 0, DuckDBBigIntVector, bigints(2048n * 2n, 2048n * 3n - 1n)); + assertValues(chunks[3], 0, DuckDBBigIntVector, bigints(2048n * 3n, 2048n * 4n - 1n)); + assertValues(chunks[4], 0, DuckDBBigIntVector, bigints(2048n * 4n, 9999n)); + }); + }); + test("should support all data types", async () => { + await withConnection(async connection => { + const result = await connection.run("from test_all_types(use_large_enum=true)"); + const smallEnumValues = ["DUCK_DUCK_ENUM", "GOOSE"]; + const mediumEnumValues = Array.from({ length: 300 }).map((_, i) => `enum_${i}`); + const largeEnumValues = Array.from({ length: 70000 }).map((_, i) => `enum_${i}`); + assertColumns(result, [ + { name: "bool", type: DuckDBBooleanType.instance }, + { name: "tinyint", type: DuckDBTinyIntType.instance }, + { name: "smallint", type: DuckDBSmallIntType.instance }, + { name: "int", type: DuckDBIntegerType.instance }, + { name: "bigint", type: DuckDBBigIntType.instance }, + { name: "hugeint", type: DuckDBHugeIntType.instance }, + { name: "uhugeint", type: DuckDBUHugeIntType.instance }, + { name: "utinyint", type: DuckDBUTinyIntType.instance }, + { name: "usmallint", type: DuckDBUSmallIntType.instance }, + { name: "uint", type: DuckDBUIntegerType.instance }, + { name: "ubigint", type: DuckDBUBigIntType.instance }, + { name: "varint", type: DuckDBVarIntType.instance }, + { name: "date", type: DuckDBDateType.instance }, + { name: "time", type: DuckDBTimeType.instance }, + { name: "timestamp", type: DuckDBTimestampType.instance }, + { name: "timestamp_s", type: DuckDBTimestampSecondsType.instance }, + { name: "timestamp_ms", type: DuckDBTimestampMillisecondsType.instance }, + { name: "timestamp_ns", type: DuckDBTimestampNanosecondsType.instance }, + { name: "time_tz", type: DuckDBTimeTZType.instance }, + { name: "timestamp_tz", type: DuckDBTimestampTZType.instance }, + { name: "float", type: DuckDBFloatType.instance }, + { name: "double", type: DuckDBDoubleType.instance }, + { name: "dec_4_1", type: new DuckDBDecimalType(4, 1) }, + { name: "dec_9_4", type: new DuckDBDecimalType(9, 4) }, + { name: "dec_18_6", type: new DuckDBDecimalType(18, 6) }, + { name: "dec38_10", type: new DuckDBDecimalType(38, 10) }, + { name: "uuid", type: DuckDBUUIDType.instance }, + { name: "interval", type: DuckDBIntervalType.instance }, + { name: "varchar", type: DuckDBVarCharType.instance }, + { name: "blob", type: DuckDBBlobType.instance }, + { name: "bit", type: DuckDBBitType.instance }, + { name: "small_enum", type: new DuckDBEnumType(smallEnumValues, DuckDBTypeId.UTINYINT) }, + { name: "medium_enum", type: new DuckDBEnumType(mediumEnumValues, DuckDBTypeId.USMALLINT) }, + { name: "large_enum", type: new DuckDBEnumType(largeEnumValues, DuckDBTypeId.UINTEGER) }, + { name: "int_array", type: new DuckDBListType(DuckDBIntegerType.instance) }, + { name: "double_array", type: new DuckDBListType(DuckDBDoubleType.instance) }, + { name: "date_array", type: new DuckDBListType(DuckDBDateType.instance) }, + { name: "timestamp_array", type: new DuckDBListType(DuckDBTimestampType.instance) }, + { name: "timestamptz_array", type: new DuckDBListType(DuckDBTimestampTZType.instance) }, + { name: "varchar_array", type: new DuckDBListType(DuckDBVarCharType.instance) }, + { name: "nested_int_array", type: new DuckDBListType(new DuckDBListType(DuckDBIntegerType.instance)) }, + { + name: "struct", + type: new DuckDBStructType(["a", "b"], [DuckDBIntegerType.instance, DuckDBVarCharType.instance]), + }, + { + name: "struct_of_arrays", + type: new DuckDBStructType( + ["a", "b"], + [new DuckDBListType(DuckDBIntegerType.instance), new DuckDBListType(DuckDBVarCharType.instance)], + ), + }, + { + name: "array_of_structs", + type: new DuckDBListType( + new DuckDBStructType(["a", "b"], [DuckDBIntegerType.instance, DuckDBVarCharType.instance]), + ), + }, + { name: "map", type: new DuckDBMapType(DuckDBVarCharType.instance, DuckDBVarCharType.instance) }, + { + name: "union", + type: new DuckDBUnionType(["name", "age"], [DuckDBVarCharType.instance, DuckDBSmallIntType.instance]), + }, + { name: "fixed_int_array", type: new DuckDBArrayType(DuckDBIntegerType.instance, 3) }, + { name: "fixed_varchar_array", type: new DuckDBArrayType(DuckDBVarCharType.instance, 3) }, + { + name: "fixed_nested_int_array", + type: new DuckDBArrayType(new DuckDBArrayType(DuckDBIntegerType.instance, 3), 3), + }, + { + name: "fixed_nested_varchar_array", + type: new DuckDBArrayType(new DuckDBArrayType(DuckDBVarCharType.instance, 3), 3), + }, + { + name: "fixed_struct_array", + type: new DuckDBArrayType( + new DuckDBStructType(["a", "b"], [DuckDBIntegerType.instance, DuckDBVarCharType.instance]), + 3, + ), + }, + { + name: "struct_of_fixed_array", + type: new DuckDBStructType( + ["a", "b"], + [new DuckDBArrayType(DuckDBIntegerType.instance, 3), new DuckDBArrayType(DuckDBVarCharType.instance, 3)], + ), + }, + { + name: "fixed_array_of_int_list", + type: new DuckDBArrayType(new DuckDBListType(DuckDBIntegerType.instance), 3), + }, + { + name: "list_of_fixed_int_array", + type: new DuckDBListType(new DuckDBArrayType(DuckDBIntegerType.instance, 3)), + }, + ]); + + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 54); + assert.strictEqual(chunk.rowCount, 3); + + assertValues(chunk, 0, DuckDBBooleanVector, [false, true, null]); + assertValues(chunk, 1, DuckDBTinyIntVector, [DuckDBTinyIntType.Min, DuckDBTinyIntType.Max, null]); + assertValues(chunk, 2, DuckDBSmallIntVector, [DuckDBSmallIntType.Min, DuckDBSmallIntType.Max, null]); + assertValues(chunk, 3, DuckDBIntegerVector, [DuckDBIntegerType.Min, DuckDBIntegerType.Max, null]); + assertValues(chunk, 4, DuckDBBigIntVector, [DuckDBBigIntType.Min, DuckDBBigIntType.Max, null]); + assertValues(chunk, 5, DuckDBHugeIntVector, [DuckDBHugeIntType.Min, DuckDBHugeIntType.Max, null]); + assertValues(chunk, 6, DuckDBUHugeIntVector, [DuckDBUHugeIntType.Min, DuckDBUHugeIntType.Max, null]); + assertValues(chunk, 7, DuckDBUTinyIntVector, [DuckDBUTinyIntType.Min, DuckDBUTinyIntType.Max, null]); + assertValues(chunk, 8, DuckDBUSmallIntVector, [DuckDBUSmallIntType.Min, DuckDBUSmallIntType.Max, null]); + assertValues(chunk, 9, DuckDBUIntegerVector, [DuckDBUIntegerType.Min, DuckDBUIntegerType.Max, null]); + assertValues(chunk, 10, DuckDBUBigIntVector, [DuckDBUBigIntType.Min, DuckDBUBigIntType.Max, null]); + assertValues(chunk, 11, DuckDBVarIntVector, [DuckDBVarIntType.Min, DuckDBVarIntType.Max, null]); + assertValues(chunk, 12, DuckDBDateVector, [DuckDBDateValue.Min, DuckDBDateValue.Max, null]); + assertValues(chunk, 13, DuckDBTimeVector, [DuckDBTimeValue.Min, DuckDBTimeValue.Max, null]); + assertValues(chunk, 14, DuckDBTimestampVector, [DuckDBTimestampValue.Min, DuckDBTimestampValue.Max, null]); + assertValues(chunk, 15, DuckDBTimestampSecondsVector, [ + DuckDBTimestampSecondsValue.Min, + DuckDBTimestampSecondsValue.Max, + null, + ]); + assertValues(chunk, 16, DuckDBTimestampMillisecondsVector, [ + DuckDBTimestampMillisecondsValue.Min, + DuckDBTimestampMillisecondsValue.Max, + null, + ]); + assertValues(chunk, 17, DuckDBTimestampNanosecondsVector, [ + DuckDBTimestampNanosecondsValue.Min, + DuckDBTimestampNanosecondsValue.Max, + null, + ]); + assertValues(chunk, 18, DuckDBTimeTZVector, [DuckDBTimeTZValue.Min, DuckDBTimeTZValue.Max, null]); + assertValues(chunk, 19, DuckDBTimestampTZVector, [DuckDBTimestampTZValue.Min, DuckDBTimestampTZValue.Max, null]); + assertValues(chunk, 20, DuckDBFloatVector, [DuckDBFloatType.Min, DuckDBFloatType.Max, null]); + assertValues(chunk, 21, DuckDBDoubleVector, [DuckDBDoubleType.Min, DuckDBDoubleType.Max, null]); + assertValues(chunk, 22, DuckDBDecimal2Vector, [decimalValue(-9999n, 4, 1), decimalValue(9999n, 4, 1), null]); + assertValues(chunk, 23, DuckDBDecimal4Vector, [ + decimalValue(-999999999n, 9, 4), + decimalValue(999999999n, 9, 4), + null, + ]); + assertValues(chunk, 24, DuckDBDecimal8Vector, [ + decimalValue(-BI_18_9s, 18, 6), + decimalValue(BI_18_9s, 18, 6), + null, + ]); + assertValues(chunk, 25, DuckDBDecimal16Vector, [ + decimalValue(-BI_38_9s, 38, 10), + decimalValue(BI_38_9s, 38, 10), + null, + ]); + assertValues(chunk, 26, DuckDBUUIDVector, [DuckDBUUIDValue.Min, DuckDBUUIDValue.Max, null]); + assertValues(chunk, 27, DuckDBIntervalVector, [ + intervalValue(0, 0, 0n), + intervalValue(999, 999, 999999999n), + null, + ]); + assertValues(chunk, 28, DuckDBVarCharVector, ["🦆🦆🦆🦆🦆🦆", "goo\0se", null]); + assertValues(chunk, 29, DuckDBBlobVector, [ + DuckDBBlobValue.fromString("thisisalongblob\x00withnullbytes"), + DuckDBBlobValue.fromString("\x00\x00\x00a"), + null, + ]); + assertValues(chunk, 30, DuckDBBitVector, [bitValue("0010001001011100010101011010111"), bitValue("10101"), null]); + assertValues(chunk, 31, DuckDBEnum1Vector, [ + smallEnumValues[0], + smallEnumValues[smallEnumValues.length - 1], + null, + ]); + assertValues(chunk, 32, DuckDBEnum2Vector, [ + mediumEnumValues[0], + mediumEnumValues[mediumEnumValues.length - 1], + null, + ]); + assertValues(chunk, 33, DuckDBEnum4Vector, [ + largeEnumValues[0], + largeEnumValues[largeEnumValues.length - 1], + null, + ]); + // int_array + assertValues(chunk, 34, DuckDBListVector, [listValue([]), listValue([42, 999, null, null, -42]), null]); + // double_array + assertValues(chunk, 35, DuckDBListVector, [ + listValue([]), + listValue([42.0, NaN, Infinity, -Infinity, null, -42.0]), + null, + ]); + // date_array + assertValues(chunk, 36, DuckDBListVector, [ + listValue([]), + listValue([dateValue(0), DuckDBDateValue.PosInf, DuckDBDateValue.NegInf, null, dateValue(19124)]), + null, + ]); + // timestamp_array + assertValues(chunk, 37, DuckDBListVector, [ + listValue([]), + listValue([ + DuckDBTimestampValue.Epoch, + DuckDBTimestampValue.PosInf, + DuckDBTimestampValue.NegInf, + null, + // 1652372625 is 2022-05-12 16:23:45 + timestampValue(1652372625n * 1000n * 1000n), + ]), + null, + ]); + // timestamptz_array + assertValues(chunk, 38, DuckDBListVector, [ + listValue([]), + listValue([ + DuckDBTimestampTZValue.Epoch, + DuckDBTimestampTZValue.PosInf, + DuckDBTimestampTZValue.NegInf, + null, + // 1652397825 = 1652372625 + 25200, 25200 = 7 * 60 * 60 = 7 hours in seconds + // This 7 hour difference is hard coded into test_all_types (value is 2022-05-12 16:23:45-07) + timestampTZValue(1652397825n * 1000n * 1000n), + ]), + null, + ]); + // varchar_array + assertValues(chunk, 39, DuckDBListVector, [ + listValue([]), + // Note that the string 'goose' in varchar_array does NOT have an embedded null character. + listValue(["🦆🦆🦆🦆🦆🦆", "goose", null, ""]), + null, + ]); + // nested_int_array + assertValues(chunk, 40, DuckDBListVector, [ + listValue([]), + listValue([ + listValue([]), + listValue([42, 999, null, null, -42]), + null, + listValue([]), + listValue([42, 999, null, null, -42]), + ]), + null, + ]); + assertValues(chunk, 41, DuckDBStructVector, [ + structValue({ "a": null, "b": null }), + structValue({ "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }), + null, + ]); + // struct_of_arrays + assertValues(chunk, 42, DuckDBStructVector, [ + structValue({ "a": null, "b": null }), + structValue({ + "a": listValue([42, 999, null, null, -42]), + "b": listValue(["🦆🦆🦆🦆🦆🦆", "goose", null, ""]), + }), + null, + ]); + // array_of_structs + assertValues(chunk, 43, DuckDBListVector, [ + listValue([]), + listValue([structValue({ "a": null, "b": null }), structValue({ "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }), null]), + null, + ]); + assertValues(chunk, 44, DuckDBMapVector, [ + mapValue([]), + mapValue([ + { key: "key1", value: "🦆🦆🦆🦆🦆🦆" }, + { key: "key2", value: "goose" }, + ]), + null, + ]); + assertValues(chunk, 45, DuckDBUnionVector, [ + unionValue("name", "Frank"), + unionValue("age", 5), + null, + ]); + // fixed_int_array + assertValues(chunk, 46, DuckDBArrayVector, [arrayValue([null, 2, 3]), arrayValue([4, 5, 6]), null]); + // fixed_varchar_array + assertValues(chunk, 47, DuckDBArrayVector, [arrayValue(["a", null, "c"]), arrayValue(["d", "e", "f"]), null]); + // fixed_nested_int_array + assertValues(chunk, 48, DuckDBArrayVector, [ + arrayValue([arrayValue([null, 2, 3]), null, arrayValue([null, 2, 3])]), + arrayValue([arrayValue([4, 5, 6]), arrayValue([null, 2, 3]), arrayValue([4, 5, 6])]), + null, + ]); + // fixed_nested_varchar_array + assertValues(chunk, 49, DuckDBArrayVector, [ + arrayValue([arrayValue(["a", null, "c"]), null, arrayValue(["a", null, "c"])]), + arrayValue([arrayValue(["d", "e", "f"]), arrayValue(["a", null, "c"]), arrayValue(["d", "e", "f"])]), + null, + ]); + // fixed_struct_array + assertValues(chunk, 50, DuckDBArrayVector, [ + arrayValue([ + structValue({ "a": null, "b": null }), + structValue({ "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }), + structValue({ "a": null, "b": null }), + ]), + arrayValue([ + structValue({ "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }), + structValue({ "a": null, "b": null }), + structValue({ "a": 42, "b": "🦆🦆🦆🦆🦆🦆" }), + ]), + null, + ]); + // struct_of_fixed_array + assertValues(chunk, 51, DuckDBStructVector, [ + structValue({ + "a": arrayValue([null, 2, 3]), + "b": arrayValue(["a", null, "c"]), + }), + structValue({ + "a": arrayValue([4, 5, 6]), + "b": arrayValue(["d", "e", "f"]), + }), + null, + ]); + // fixed_array_of_int_list + assertValues(chunk, 52, DuckDBArrayVector, [ + arrayValue([listValue([]), listValue([42, 999, null, null, -42]), listValue([])]), + arrayValue([listValue([42, 999, null, null, -42]), listValue([]), listValue([42, 999, null, null, -42])]), + null, + ]); + // list_of_fixed_int_array + assertValues(chunk, 53, DuckDBListVector, [ + listValue([arrayValue([null, 2, 3]), arrayValue([4, 5, 6]), arrayValue([null, 2, 3])]), + listValue([arrayValue([4, 5, 6]), arrayValue([null, 2, 3]), arrayValue([4, 5, 6])]), + null, + ]); + }); + }); + test("values toString", () => { + // array + assert.equal(arrayValue([]).toString(), "[]"); + assert.equal(arrayValue([1, 2, 3]).toString(), "[1, 2, 3]"); + assert.equal(arrayValue(["a", "b", "c"]).toString(), `['a', 'b', 'c']`); + + // bit + assert.equal(bitValue("").toString(), ""); + assert.equal(bitValue("10101").toString(), "10101"); + assert.equal(bitValue("0010001001011100010101011010111").toString(), "0010001001011100010101011010111"); + + // blob + assert.equal(DuckDBBlobValue.fromString("").toString(), ""); + assert.equal( + DuckDBBlobValue.fromString("thisisalongblob\x00withnullbytes").toString(), + "thisisalongblob\\x00withnullbytes", + ); + assert.equal(DuckDBBlobValue.fromString("\x00\x00\x00a").toString(), "\\x00\\x00\\x00a"); + + // date + assert.equal(DuckDBDateValue.Epoch.toString(), "1970-01-01"); + assert.equal(DuckDBDateValue.Max.toString(), "5881580-07-10"); + assert.equal(DuckDBDateValue.Min.toString(), "5877642-06-25 (BC)"); + + // decimal + assert.equal(decimalValue(0n, 4, 1).toString(), "0.0"); + assert.equal(decimalValue(9876n, 4, 1).toString(), "987.6"); + assert.equal(decimalValue(-9876n, 4, 1).toString(), "-987.6"); + + assert.equal(decimalValue(0n, 9, 4).toString(), "0.0000"); + assert.equal(decimalValue(987654321n, 9, 4).toString(), "98765.4321"); + assert.equal(decimalValue(-987654321n, 9, 4).toString(), "-98765.4321"); + + assert.equal(decimalValue(0n, 18, 6).toString(), "0.000000"); + assert.equal(decimalValue(987654321098765432n, 18, 6).toString(), "987654321098.765432"); + assert.equal(decimalValue(-987654321098765432n, 18, 6).toString(), "-987654321098.765432"); + + assert.equal(decimalValue(0n, 38, 10).toString(), "0.0000000000"); + assert.equal( + decimalValue(98765432109876543210987654321098765432n, 38, 10).toString(), + "9876543210987654321098765432.1098765432", + ); + assert.equal( + decimalValue(-98765432109876543210987654321098765432n, 38, 10).toString(), + "-9876543210987654321098765432.1098765432", + ); + + // interval + assert.equal(intervalValue(0, 0, 0n).toString(), "00:00:00"); + + assert.equal(intervalValue(1, 0, 0n).toString(), "1 month"); + assert.equal(intervalValue(-1, 0, 0n).toString(), "-1 month"); + assert.equal(intervalValue(2, 0, 0n).toString(), "2 months"); + assert.equal(intervalValue(-2, 0, 0n).toString(), "-2 months"); + assert.equal(intervalValue(12, 0, 0n).toString(), "1 year"); + assert.equal(intervalValue(-12, 0, 0n).toString(), "-1 year"); + assert.equal(intervalValue(24, 0, 0n).toString(), "2 years"); + assert.equal(intervalValue(-24, 0, 0n).toString(), "-2 years"); + assert.equal(intervalValue(25, 0, 0n).toString(), "2 years 1 month"); + assert.equal(intervalValue(-25, 0, 0n).toString(), "-2 years -1 month"); + + assert.equal(intervalValue(0, 1, 0n).toString(), "1 day"); + assert.equal(intervalValue(0, -1, 0n).toString(), "-1 day"); + assert.equal(intervalValue(0, 2, 0n).toString(), "2 days"); + assert.equal(intervalValue(0, -2, 0n).toString(), "-2 days"); + assert.equal(intervalValue(0, 30, 0n).toString(), "30 days"); + assert.equal(intervalValue(0, 365, 0n).toString(), "365 days"); + + assert.equal(intervalValue(0, 0, 1n).toString(), "00:00:00.000001"); + assert.equal(intervalValue(0, 0, -1n).toString(), "-00:00:00.000001"); + assert.equal(intervalValue(0, 0, 987654n).toString(), "00:00:00.987654"); + assert.equal(intervalValue(0, 0, -987654n).toString(), "-00:00:00.987654"); + assert.equal(intervalValue(0, 0, 1000000n).toString(), "00:00:01"); + assert.equal(intervalValue(0, 0, -1000000n).toString(), "-00:00:01"); + assert.equal(intervalValue(0, 0, 59n * 1000000n).toString(), "00:00:59"); + assert.equal(intervalValue(0, 0, -59n * 1000000n).toString(), "-00:00:59"); + assert.equal(intervalValue(0, 0, 60n * 1000000n).toString(), "00:01:00"); + assert.equal(intervalValue(0, 0, -60n * 1000000n).toString(), "-00:01:00"); + assert.equal(intervalValue(0, 0, 59n * 60n * 1000000n).toString(), "00:59:00"); + assert.equal(intervalValue(0, 0, -59n * 60n * 1000000n).toString(), "-00:59:00"); + assert.equal(intervalValue(0, 0, 60n * 60n * 1000000n).toString(), "01:00:00"); + assert.equal(intervalValue(0, 0, -60n * 60n * 1000000n).toString(), "-01:00:00"); + assert.equal(intervalValue(0, 0, 24n * 60n * 60n * 1000000n).toString(), "24:00:00"); + assert.equal(intervalValue(0, 0, -24n * 60n * 60n * 1000000n).toString(), "-24:00:00"); + assert.equal(intervalValue(0, 0, 2147483647n * 60n * 60n * 1000000n).toString(), "2147483647:00:00"); + assert.equal(intervalValue(0, 0, -2147483647n * 60n * 60n * 1000000n).toString(), "-2147483647:00:00"); + assert.equal(intervalValue(0, 0, 2147483647n * 60n * 60n * 1000000n + 1n).toString(), "2147483647:00:00.000001"); + assert.equal( + intervalValue(0, 0, -(2147483647n * 60n * 60n * 1000000n + 1n)).toString(), + "-2147483647:00:00.000001", + ); + + assert.equal( + intervalValue(2 * 12 + 3, 5, (7n * 60n * 60n + 11n * 60n + 13n) * 1000000n + 17n).toString(), + "2 years 3 months 5 days 07:11:13.000017", + ); + assert.equal( + intervalValue(-(2 * 12 + 3), -5, -((7n * 60n * 60n + 11n * 60n + 13n) * 1000000n + 17n)).toString(), + "-2 years -3 months -5 days -07:11:13.000017", + ); + + // list + assert.equal(listValue([]).toString(), "[]"); + assert.equal(listValue([1, 2, 3]).toString(), "[1, 2, 3]"); + assert.equal(listValue(["a", "b", "c"]).toString(), `['a', 'b', 'c']`); + + // map + assert.equal(mapValue([]).toString(), "{}"); + assert.equal( + mapValue([ + { key: 1, value: "a" }, + { key: 2, value: "b" }, + ]).toString(), + `{1: 'a', 2: 'b'}`, + ); + + // struct + assert.equal(structValue({}).toString(), "{}"); + assert.equal(structValue({ a: 1, b: 2 }).toString(), `{'a': 1, 'b': 2}`); + + // timestamp milliseconds + assert.equal(DuckDBTimestampMillisecondsValue.Epoch.toString(), "1970-01-01 00:00:00"); + assert.equal(DuckDBTimestampMillisecondsValue.Max.toString(), "294247-01-10 04:00:54.775"); + assert.equal(DuckDBTimestampMillisecondsValue.Min.toString(), "290309-12-22 (BC) 00:00:00"); + + // timestamp nanoseconds + assert.equal(DuckDBTimestampNanosecondsValue.Epoch.toString(), "1970-01-01 00:00:00"); + assert.equal(DuckDBTimestampNanosecondsValue.Max.toString(), "2262-04-11 23:47:16.854775806"); + assert.equal(DuckDBTimestampNanosecondsValue.Min.toString(), "1677-09-22 00:00:00"); + + // timestamp seconds + assert.equal(DuckDBTimestampSecondsValue.Epoch.toString(), "1970-01-01 00:00:00"); + assert.equal(DuckDBTimestampSecondsValue.Max.toString(), "294247-01-10 04:00:54"); + assert.equal(DuckDBTimestampSecondsValue.Min.toString(), "290309-12-22 (BC) 00:00:00"); + + // timestamp tz + assert.equal(DuckDBTimestampTZValue.Epoch.toString(), "1970-01-01 00:00:00"); + // assert.equal(DuckDBTimestampTZValue.Max.toString(), '294247-01-09 20:00:54.775806-08'); // in PST + assert.equal(DuckDBTimestampTZValue.Max.toString(), "294247-01-10 04:00:54.775806"); // TODO TZ + // assert.equal(DuckDBTimestampTZValue.Min.toString(), '290309-12-21 (BC) 16:00:00-08'); // in PST + assert.equal(DuckDBTimestampTZValue.Min.toString(), "290309-12-22 (BC) 00:00:00"); // TODO TZ + assert.equal(DuckDBTimestampTZValue.PosInf.toString(), "infinity"); + assert.equal(DuckDBTimestampTZValue.NegInf.toString(), "-infinity"); + + // timestamp + assert.equal(DuckDBTimestampValue.Epoch.toString(), "1970-01-01 00:00:00"); + assert.equal(DuckDBTimestampValue.Max.toString(), "294247-01-10 04:00:54.775806"); + assert.equal(DuckDBTimestampValue.Min.toString(), "290309-12-22 (BC) 00:00:00"); + assert.equal(DuckDBTimestampValue.PosInf.toString(), "infinity"); + assert.equal(DuckDBTimestampValue.NegInf.toString(), "-infinity"); + + // time tz + assert.equal(timeTZValue(0n, 0).toString(), "00:00:00"); + // assert.equal(DuckDBTimeTZValue.Max.toString(), '24:00:00-15:59:59'); + assert.equal(DuckDBTimeTZValue.Max.toString(), "24:00:00"); // TODO TZ + // assert.equal(DuckDBTimeTZValue.Max.toString(), '00:00:00+15:59:59'); + assert.equal(DuckDBTimeTZValue.Min.toString(), "00:00:00"); // TODO TZ + + // time + assert.equal(DuckDBTimeValue.Max.toString(), "24:00:00"); + assert.equal(DuckDBTimeValue.Min.toString(), "00:00:00"); + assert.equal(timeValue((12n * 60n * 60n + 34n * 60n + 56n) * 1000000n + 987654n).toString(), "12:34:56.987654"); + + // union + assert.equal(unionValue("a", 42).toString(), "42"); + assert.equal(unionValue("b", "duck").toString(), "duck"); + + // uuid + assert.equal(DuckDBUUIDValue.Min.toString(), "00000000-0000-0000-0000-000000000000"); + assert.equal(DuckDBUUIDValue.Max.toString(), "ffffffff-ffff-ffff-ffff-ffffffffffff"); + }); + test("date isFinite", () => { + assert(DuckDBDateValue.Epoch.isFinite); + assert(DuckDBDateValue.Max.isFinite); + assert(DuckDBDateValue.Min.isFinite); + assert(!DuckDBDateValue.PosInf.isFinite); + assert(!DuckDBDateValue.NegInf.isFinite); + }); + test("value conversion", () => { + const dateParts: DateParts = { year: 2024, month: 6, day: 3 }; + const timeParts: TimeParts = { hour: 12, min: 34, sec: 56, micros: 789123 }; + const timeTZParts: TimeTZParts = { time: timeParts, offset: DuckDBTimeTZValue.MinOffset }; + const timestampParts: TimestampParts = { date: dateParts, time: timeParts }; + + assert.deepEqual(DuckDBDateValue.fromParts(dateParts).toParts(), dateParts); + assert.deepEqual(DuckDBTimeValue.fromParts(timeParts).toParts(), timeParts); + assert.deepEqual(DuckDBTimeTZValue.fromParts(timeTZParts).toParts(), timeTZParts); + assert.deepEqual(DuckDBTimestampValue.fromParts(timestampParts).toParts(), timestampParts); + assert.deepEqual(DuckDBTimestampTZValue.fromParts(timestampParts).toParts(), timestampParts); + + assert.deepEqual(DuckDBDecimalValue.fromDouble(3.14159, 6, 5), decimalValue(314159n, 6, 5)); + assert.deepEqual(decimalValue(314159n, 6, 5).toDouble(), 3.14159); + }); + test("result inspection conveniences", async () => { + await withConnection(async connection => { + const result = await connection.run("select i::int as a, i::int + 10 as b from range(3) t(i)"); + assert.deepEqual(result.columnNames(), ["a", "b"]); + assert.deepEqual(result.columnTypes(), [DuckDBIntegerType.instance, DuckDBIntegerType.instance]); + const chunks = await result.fetchAllChunks(); + const chunkColumns = chunks.map(chunk => chunk.getColumns()); + assert.deepEqual(chunkColumns, [ + [ + [0, 1, 2], + [10, 11, 12], + ], + ]); + const chunkRows = chunks.map(chunk => chunk.getRows()); + assert.deepEqual(chunkRows, [ + [ + [0, 10], + [1, 11], + [2, 12], + ], + ]); + }); + }); + test("result reader", async () => { + await withConnection(async connection => { + const reader = await connection.runAndReadAll("select i::int as a, i::int + 10000 as b from range(5000) t(i)"); + assert.deepEqual(reader.columnNames(), ["a", "b"]); + assert.deepEqual(reader.columnTypes(), [DuckDBIntegerType.instance, DuckDBIntegerType.instance]); + const columns = reader.getColumns(); + assert.equal(columns.length, 2); + assert.equal(columns[0][0], 0); + assert.equal(columns[0][4999], 4999); + assert.equal(columns[1][0], 10000); + assert.equal(columns[1][4999], 14999); + const rows = reader.getRows(); + assert.equal(rows.length, 5000); + assert.deepEqual(rows[0], [0, 10000]); + assert.deepEqual(rows[4999], [4999, 14999]); + }); + }); + test("default duckdb_api without explicit options", async () => { + const instance = await DuckDBInstance.create(); + const connection = await instance.connect(); + const result = await connection.run(`select current_setting('duckdb_api') as duckdb_api`); + assertColumns(result, [{ name: "duckdb_api", type: DuckDBVarCharType.instance }]); + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 1); + assert.strictEqual(chunk.rowCount, 1); + assertValues(chunk, 0, DuckDBVarCharVector, ["node-neo-api"]); + }); + test("default duckdb_api with explicit options", async () => { + const instance = await DuckDBInstance.create(undefined, {}); + const connection = await instance.connect(); + const result = await connection.run(`select current_setting('duckdb_api') as duckdb_api`); + assertColumns(result, [{ name: "duckdb_api", type: DuckDBVarCharType.instance }]); + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 1); + assert.strictEqual(chunk.rowCount, 1); + assertValues(chunk, 0, DuckDBVarCharVector, ["node-neo-api"]); + }); + test("overriding duckdb_api", async () => { + const instance = await DuckDBInstance.create(undefined, { "duckdb_api": "custom-duckdb-api" }); + const connection = await instance.connect(); + const result = await connection.run(`select current_setting('duckdb_api') as duckdb_api`); + assertColumns(result, [{ name: "duckdb_api", type: DuckDBVarCharType.instance }]); + const chunk = await result.fetchChunk(); + assert.strictEqual(chunk.columnCount, 1); + assert.strictEqual(chunk.rowCount, 1); + assertValues(chunk, 0, DuckDBVarCharVector, ["custom-duckdb-api"]); + }); +}); diff --git a/test/js/third_party/@napi-rs/canvas/.gitignore b/test/js/third_party/@napi-rs/canvas/.gitignore new file mode 100644 index 0000000000..9b1ee42e84 --- /dev/null +++ b/test/js/third_party/@napi-rs/canvas/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/test/js/third_party/@napi-rs/canvas/expected.png b/test/js/third_party/@napi-rs/canvas/expected.png new file mode 100644 index 0000000000..3a98dd5ee0 Binary files /dev/null and b/test/js/third_party/@napi-rs/canvas/expected.png differ diff --git a/test/js/third_party/@napi-rs/canvas/icon-small.png b/test/js/third_party/@napi-rs/canvas/icon-small.png new file mode 100644 index 0000000000..69bc385e8c Binary files /dev/null and b/test/js/third_party/@napi-rs/canvas/icon-small.png differ diff --git a/test/js/third_party/@napi-rs/canvas/napi-rs-canvas.test.ts b/test/js/third_party/@napi-rs/canvas/napi-rs-canvas.test.ts new file mode 100644 index 0000000000..b03b58df0e --- /dev/null +++ b/test/js/third_party/@napi-rs/canvas/napi-rs-canvas.test.ts @@ -0,0 +1,25 @@ +// Create an image, then print it as binary to stdout +import { createCanvas, loadImage } from "@napi-rs/canvas"; +import { Jimp } from "jimp"; +import { join } from "path"; + +describe("@napi-rs/canvas", () => { + it("produces correct output", async () => { + const canvas = createCanvas(200, 200); + const ctx = canvas.getContext("2d"); + + ctx.lineWidth = 10; + ctx.strokeStyle = "red"; + ctx.fillStyle = "blue"; + + ctx.fillRect(0, 0, 200, 200); + ctx.strokeRect(50, 50, 100, 100); + + const image = await loadImage(join(__dirname, "icon-small.png")); + ctx.drawImage(image, 0, 0); + + const expected = await Jimp.read(join(__dirname, "expected.png")); + const actual = await Jimp.read(await canvas.encode("png")); + expect(Array.from(actual.bitmap.data)).toEqual(Array.from(expected.bitmap.data)); + }); +}); diff --git a/test/js/third_party/pnpm/pnpm.test.ts b/test/js/third_party/pnpm/pnpm.test.ts index 9094de5eaf..c6e8e6ff03 100644 --- a/test/js/third_party/pnpm/pnpm.test.ts +++ b/test/js/third_party/pnpm/pnpm.test.ts @@ -9,7 +9,7 @@ it("successfully traverses pnpm-generated install directory", async () => { // let { exited } = Bun.spawn({ - cmd: [bunExe(), "create", "vite", "my-vite-app", "--template", "solid-ts"], + cmd: [bunExe(), "create", "vite@5", "my-vite-app", "--template", "solid-ts"], cwd: package_dir, stdio: ["ignore", "inherit", "inherit"], env: bunEnv, diff --git a/test/js/web/fetch/fetch-leak-test-fixture-3.js b/test/js/web/fetch/fetch-leak-test-fixture-3.js index 3e75027ebf..33796c1667 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-3.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-3.js @@ -1,8 +1,15 @@ -const payload = Buffer.alloc(64 * 64 * 1024, "X"); +// Reduce memory pressure by not cloning the buffer each Response. +const payload = new Blob([Buffer.alloc(64 * 64 * 1024, "X")]); + const server = Bun.serve({ port: 0, + idleTimeout: 0, async fetch(req) { return new Response(payload); }, }); -console.log(server.url.href); +if (process.send) { + process.send(server.url.href); +} else { + console.log(server.url.href); +} diff --git a/test/js/web/fetch/fetch-leak-test-fixture-4.js b/test/js/web/fetch/fetch-leak-test-fixture-4.js index 73c8ccd5d6..c25971c462 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-4.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-4.js @@ -23,8 +23,16 @@ try { Bun.gc(true); await Bun.sleep(10); const stats = getHeapStats(); - expect(stats.Response || 0).toBeLessThanOrEqual(threshold); - expect(stats.Promise || 0).toBeLessThanOrEqual(threshold); + let { Response, Promise } = stats; + Response ||= 0; + Promise ||= 0; + console.log({ + rss: ((process.memoryUsage.rss() / 1024 / 1024) | 0) + " MB", + Response, + Promise, + }); + expect(Response).toBeLessThanOrEqual(threshold); + expect(Promise).toBeLessThanOrEqual(threshold); } } process.exit(0); diff --git a/test/js/web/fetch/fetch-leak.test.ts b/test/js/web/fetch/fetch-leak.test.ts index bf90582a79..b3b288b3ab 100644 --- a/test/js/web/fetch/fetch-leak.test.ts +++ b/test/js/web/fetch/fetch-leak.test.ts @@ -10,7 +10,7 @@ describe("fetch doesn't leak", () => { var count = 0; using server = Bun.serve({ port: 0, - + idleTimeout: 0, fetch(req) { count++; return new Response(body); @@ -20,7 +20,7 @@ describe("fetch doesn't leak", () => { const proc = Bun.spawn({ env: { ...bunEnv, - SERVER: `http://${server.hostname}:${server.port}`, + SERVER: server.url.href, COUNT: "200", }, stderr: "inherit", @@ -49,6 +49,7 @@ describe("fetch doesn't leak", () => { const serveOptions = { port: 0, + idleTimeout: 0, fetch(req) { return new Response(body, { headers }); }, @@ -62,8 +63,8 @@ describe("fetch doesn't leak", () => { const env = { ...bunEnv, - SERVER: `${tls ? "https" : "http"}://${server.hostname}:${server.port}`, - BUN_JSC_forceRAMSize: (1024 * 1024 * 64).toString("10"), + SERVER: server.url.href, + BUN_JSC_forceRAMSize: (1024 * 1024 * 64).toString(10), NAME: name, }; @@ -105,6 +106,7 @@ describe.each(["FormData", "Blob", "Buffer", "String", "URLSearchParams", "strea async () => { using server = Bun.serve({ port: 0, + idleTimeout: 0, fetch(req) { return new Response(); }, @@ -151,7 +153,7 @@ test("do not leak", async () => { let url; let isDone = false; - server.listen(0, function attack() { + server.listen(0, "127.0.0.1", function attack() { if (isDone) { return; } @@ -165,7 +167,7 @@ test("do not leak", async () => { let prev = Infinity; let count = 0; - const interval = setInterval(() => { + var interval = setInterval(() => { isDone = true; gc(); const next = process.memoryUsage().heapUsed; diff --git a/test/js/web/fetch/fetch.test.ts b/test/js/web/fetch/fetch.test.ts index 2b9f434aa2..6c67d63859 100644 --- a/test/js/web/fetch/fetch.test.ts +++ b/test/js/web/fetch/fetch.test.ts @@ -1,7 +1,7 @@ import { AnyFunction, serve, ServeOptions, Server, sleep, TCPSocketListener } from "bun"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test"; import { chmodSync, readFileSync, rmSync, writeFileSync } from "fs"; -import { bunEnv, bunExe, gc, isWindows, tls, tmpdirSync, withoutAggressiveGC } from "harness"; +import { bunEnv, bunExe, gc, isBroken, isWindows, tls, tmpdirSync, withoutAggressiveGC } from "harness"; import { mkfifo } from "mkfifo"; import net from "net"; import { join } from "path"; @@ -17,6 +17,7 @@ const fetchFixture4 = join(import.meta.dir, "fetch-leak-test-fixture-4.js"); let server: Server; function startServer({ fetch, ...options }: ServeOptions) { server = serve({ + idleTimeout: 0, ...options, fetch, port: 0, @@ -1314,9 +1315,16 @@ describe("Response", () => { method: "POST", body: await Bun.file(import.meta.dir + "/fixtures/file.txt").arrayBuffer(), }); - var input = await response.arrayBuffer(); + const input = await response.bytes(); var output = await Bun.file(import.meta.dir + "/fixtures/file.txt").stream(); - expect(new Uint8Array(input)).toEqual((await output.getReader().read()).value); + let chunks: Uint8Array[] = []; + const reader = output.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + chunks.push(value); + } + expect(input).toEqual(Buffer.concat(chunks)); }); }); @@ -2010,7 +2018,7 @@ describe("http/1.1 response body length", () => { expect(response.arrayBuffer()).resolves.toHaveLength(0); }); - it("should ignore body on HEAD", async () => { + it.todoIf(isBroken)("should ignore body on HEAD", async () => { const response = await fetch(`http://${getHost()}/text`, { method: "HEAD" }); expect(response.status).toBe(200); expect(response.arrayBuffer()).resolves.toHaveLength(0); @@ -2018,35 +2026,31 @@ describe("http/1.1 response body length", () => { }); describe("fetch Response life cycle", () => { it("should not keep Response alive if not consumed", async () => { - const serverProcess = Bun.spawn({ + let deferred = Promise.withResolvers(); + + await using serverProcess = Bun.spawn({ cmd: [bunExe(), "--smol", fetchFixture3], stderr: "inherit", - stdout: "pipe", - stdin: "ignore", + stdout: "inherit", + stdin: "inherit", env: bunEnv, + ipc(message) { + deferred.resolve(message); + }, }); - async function getServerUrl() { - const reader = serverProcess.stdout.getReader(); - const { done, value } = await reader.read(); - return new TextDecoder().decode(value); - } - const serverUrl = await getServerUrl(); - const clientProcess = Bun.spawn({ + const serverUrl = await deferred.promise; + await using clientProcess = Bun.spawn({ cmd: [bunExe(), "--smol", fetchFixture4, serverUrl], stderr: "inherit", - stdout: "pipe", - stdin: "ignore", + stdout: "inherit", + stdin: "inherit", env: bunEnv, }); - try { - expect(await clientProcess.exited).toBe(0); - } finally { - serverProcess.kill(); - } + expect(await clientProcess.exited).toBe(0); }); it("should allow to get promise result after response is GC'd", async () => { - const server = Bun.serve({ + using server = Bun.serve({ port: 0, async fetch(request: Request) { return new Response( @@ -2235,6 +2239,7 @@ describe("fetch should allow duplex", () => { it("should work in redirects .manual when using duplex", async () => { using server = Bun.serve({ port: 0, + idleTimeout: 0, async fetch(req) { if (req.url.indexOf("/redirect") === -1) { return Response.redirect("/"); @@ -2298,7 +2303,11 @@ it("should allow to follow redirect if connection is closed, abort should work e await once(server.listen(0), "listening"); try { - const response = await fetch(`http://localhost:${(server.address() as AddressInfo).port}/redirect`, { + let { address, port } = server.address() as AddressInfo; + if (address === "::") { + address = "[::]"; + } + const response = await fetch(`http://${address}:${port}/redirect`, { signal: AbortSignal.timeout(150), }); if (type === "delay") { diff --git a/test/js/web/html/html-rewriter-doctype.test.ts b/test/js/web/html/html-rewriter-doctype.test.ts new file mode 100644 index 0000000000..632a6fccb3 --- /dev/null +++ b/test/js/web/html/html-rewriter-doctype.test.ts @@ -0,0 +1,24 @@ +import { expect, test, describe } from "bun:test"; + +describe("HTMLRewriter DOCTYPE handler", () => { + test("remove and removed property work on DOCTYPE", () => { + const html = "Hello"; + let sawDoctype = false; + let wasRemoved = false; + + const rewriter = new HTMLRewriter().onDocument({ + doctype(doctype) { + sawDoctype = true; + doctype.remove(); + wasRemoved = doctype.removed; + }, + }); + + const result = rewriter.transform(html); + + expect(sawDoctype).toBe(true); + expect(wasRemoved).toBe(true); + expect(result).not.toContain(""); + }); +}); diff --git a/test/js/web/timers/performance-entries.test.ts b/test/js/web/timers/performance-entries.test.ts new file mode 100644 index 0000000000..bfc297cc1a --- /dev/null +++ b/test/js/web/timers/performance-entries.test.ts @@ -0,0 +1,17 @@ +import { expect, test } from "bun:test"; +import { estimateShallowMemoryUsageOf } from "bun:jsc"; + +test("memory usage of Performance", () => { + const initial = estimateShallowMemoryUsageOf(performance); + for (let i = 0; i < 1024; i++) { + performance.mark(`mark-${i}`); + } + const final = estimateShallowMemoryUsageOf(performance); + + for (let i = 1; i < 1024; i++) { + performance.measure(`measure-${i}`, `mark-${i}`, `mark-${i - 1}`); + } + const final2 = estimateShallowMemoryUsageOf(performance); + expect(final2).toBeGreaterThan(final); + expect(final).toBeGreaterThan(initial); +}); diff --git a/test/js/web/websocket/websocket-client.test.ts b/test/js/web/websocket/websocket-client.test.ts index 3e260f6eba..d771350fad 100644 --- a/test/js/web/websocket/websocket-client.test.ts +++ b/test/js/web/websocket/websocket-client.test.ts @@ -3,6 +3,15 @@ import { spawn } from "bun"; import { afterEach, beforeEach, describe, expect, it } from "bun:test"; import { bunEnv, bunExe, nodeExe } from "harness"; import * as path from "node:path"; +function test( + label: string, + fn: (ws: WebSocket, done: (err?: unknown) => void) => void, + timeout?: number, + isOnly = false, +) { + return makeTest(label, fn, timeout, isOnly); +} +test.only = (label, fn, timeout) => makeTest(label, fn, timeout, true); const strings = [ { @@ -236,8 +245,13 @@ describe("WebSocket", () => { }); }); -function test(label: string, fn: (ws: WebSocket, done: (err?: unknown) => void) => void, timeout?: number) { - it( +function makeTest( + label: string, + fn: (ws: WebSocket, done: (err?: unknown) => void) => void, + timeout?: number, + isOnly = false, +) { + return (isOnly ? it.only : it)( label, testDone => { let isDone = false; diff --git a/test/js/web/websocket/websocket.test.js b/test/js/web/websocket/websocket.test.js index e1736b1199..7a84a615e3 100644 --- a/test/js/web/websocket/websocket.test.js +++ b/test/js/web/websocket/websocket.test.js @@ -500,34 +500,8 @@ describe("WebSocket", () => { }); it("instances should be finalized when GC'd", async () => { - const { expect } = require("bun:test"); - - using server = Bun.serve({ - port: 0, - fetch(req, server) { - return server.upgrade(req); - }, - websocket: { - open() {}, - data() {}, - message() {}, - drain() {}, - }, - }); - - function openAndCloseWS() { - const { promise, resolve } = Promise.withResolvers(); - const sock = new WebSocket(server.url.href.replace("http", "ws")); - sock.addEventListener("open", _ => { - sock.addEventListener("close", () => { - resolve(); - }); - sock.close(); - }); - - return promise; - } - + let current_websocket_count = 0; + let initial_websocket_count = 0; function getWebSocketCount() { Bun.gc(true); const objectTypeCounts = require("bun:jsc").heapStats().objectTypeCounts || { @@ -535,25 +509,53 @@ describe("WebSocket", () => { }; return objectTypeCounts.WebSocket || 0; } - let current_websocket_count = 0; - let initial_websocket_count = 0; - for (let i = 0; i < 1000; i++) { - await openAndCloseWS(); - if (i % 100 === 0) { - current_websocket_count = getWebSocketCount(); - // if we have more than 1 batch of websockets open, we have a problem - expect(current_websocket_count).toBeLessThanOrEqual(100); - if (initial_websocket_count === 0) { - initial_websocket_count = current_websocket_count; + async function run() { + using server = Bun.serve({ + port: 0, + fetch(req, server) { + return server.upgrade(req); + }, + websocket: { + open() {}, + data() {}, + message() {}, + drain() {}, + }, + }); + + function onOpen(sock, resolve) { + sock.addEventListener("close", resolve, { once: true }); + sock.close(); + } + + function openAndCloseWS() { + const { promise, resolve } = Promise.withResolvers(); + const sock = new WebSocket(server.url.href.replace("http", "ws")); + sock.addEventListener("open", onOpen.bind(undefined, sock, resolve), { + once: true, + }); + + return promise; + } + + for (let i = 0; i < 1000; i++) { + await openAndCloseWS(); + if (i % 100 === 0) { + if (initial_websocket_count === 0) { + initial_websocket_count = getWebSocketCount(); + } } } } + await run(); + // wait next tick to run the last time - await Bun.sleep(1); + await Bun.sleep(100); current_websocket_count = getWebSocketCount(); + console.log({ current_websocket_count, initial_websocket_count }); // expect that current and initial websocket be close to the same (normaly 1 or 2 difference) - expect(Math.abs(current_websocket_count - initial_websocket_count)).toBeLessThanOrEqual(5); + expect(Math.abs(current_websocket_count - initial_websocket_count)).toBeLessThanOrEqual(50); }); it("should be able to send big messages", async () => { diff --git a/test/napi/napi-app/binding.gyp b/test/napi/napi-app/binding.gyp index aebdebb7ef..39b61162a2 100644 --- a/test/napi/napi-app/binding.gyp +++ b/test/napi/napi-app/binding.gyp @@ -10,7 +10,7 @@ "AdditionalOptions": ["/std:c++20"], }, }, - "sources": ["main.cpp"], + "sources": ["main.cpp", "wrap_tests.cpp"], "include_dirs": [" - -#include +#include "napi_with_version.h" +#include "utils.h" +#include "wrap_tests.h" #include #include +#include #include #include #include @@ -30,23 +31,6 @@ napi_value fail_fmt(napi_env env, const char *fmt, ...) { return fail(env, buf); } -napi_value ok(napi_env env) { - napi_value result; - napi_get_undefined(env, &result); - return result; -} - -static void run_gc(const Napi::CallbackInfo &info) { - info[0].As().Call(0, nullptr); -} - -// calls napi_typeof and asserts it returns napi_ok -static napi_valuetype get_typeof(napi_env env, napi_value value) { - napi_valuetype result; - assert(napi_typeof(env, value, &result) == napi_ok); - return result; -} - napi_value test_issue_7685(const Napi::CallbackInfo &info) { Napi::Env env(info.Env()); Napi::HandleScope scope(env); @@ -595,33 +579,6 @@ napi_value was_finalize_called(const Napi::CallbackInfo &info) { return ret; } -static const char *napi_valuetype_to_string(napi_valuetype type) { - switch (type) { - case napi_undefined: - return "undefined"; - case napi_null: - return "null"; - case napi_boolean: - return "boolean"; - case napi_number: - return "number"; - case napi_string: - return "string"; - case napi_symbol: - return "symbol"; - case napi_object: - return "object"; - case napi_function: - return "function"; - case napi_external: - return "external"; - case napi_bigint: - return "bigint"; - default: - return "unknown"; - } -} - // calls a function (the sole argument) which must throw. catches and returns // the thrown error napi_value call_and_get_exception(const Napi::CallbackInfo &info) { @@ -1013,6 +970,112 @@ static napi_value try_add_tag(const Napi::CallbackInfo &info) { return Napi::Boolean::New(env, status == napi_ok); } +static napi_value bigint_to_i64(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + // start at 1 is intentional, since argument 0 is the callback to run GC + // passed to every function + // perform test on all arguments + for (size_t i = 1; i < info.Length(); i++) { + napi_value bigint = info[i]; + + napi_valuetype type; + NODE_API_CALL(env, napi_typeof(env, bigint, &type)); + + int64_t result = 0; + bool lossless = false; + + if (type != napi_bigint) { + printf("napi_get_value_bigint_int64 return for non-bigint: %d\n", + napi_get_value_bigint_int64(env, bigint, &result, &lossless)); + } else { + NODE_API_CALL( + env, napi_get_value_bigint_int64(env, bigint, &result, &lossless)); + printf("napi_get_value_bigint_int64 result: %" PRId64 "\n", result); + printf("lossless: %s\n", lossless ? "true" : "false"); + } + } + + return ok(env); +} + +static napi_value bigint_to_u64(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + // start at 1 is intentional, since argument 0 is the callback to run GC + // passed to every function + // perform test on all arguments + for (size_t i = 1; i < info.Length(); i++) { + napi_value bigint = info[i]; + + napi_valuetype type; + NODE_API_CALL(env, napi_typeof(env, bigint, &type)); + + uint64_t result; + bool lossless; + + if (type != napi_bigint) { + printf("napi_get_value_bigint_uint64 return for non-bigint: %d\n", + napi_get_value_bigint_uint64(env, bigint, &result, &lossless)); + } else { + NODE_API_CALL( + env, napi_get_value_bigint_uint64(env, bigint, &result, &lossless)); + printf("napi_get_value_bigint_uint64 result: %" PRIu64 "\n", result); + printf("lossless: %s\n", lossless ? "true" : "false"); + } + } + + return ok(env); +} + +static napi_value bigint_to_64_null(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + + napi_value bigint; + NODE_API_CALL(env, napi_create_bigint_int64(env, 5, &bigint)); + + int64_t result_signed; + uint64_t result_unsigned; + bool lossless; + + printf("status (int64, null result) = %d\n", + napi_get_value_bigint_int64(env, bigint, nullptr, &lossless)); + printf("status (int64, null lossless) = %d\n", + napi_get_value_bigint_int64(env, bigint, &result_signed, nullptr)); + printf("status (uint64, null result) = %d\n", + napi_get_value_bigint_uint64(env, bigint, nullptr, &lossless)); + printf("status (uint64, null lossless) = %d\n", + napi_get_value_bigint_uint64(env, bigint, &result_unsigned, nullptr)); + + return ok(env); +} + +static napi_value create_weird_bigints(const Napi::CallbackInfo &info) { + // create bigints by passing weird parameters to napi_create_bigint_words + napi_env env = info.Env(); + + std::array bigints; + std::array words{{123, 0, 0, 0}}; + + NODE_API_CALL(env, napi_create_bigint_int64(env, 0, &bigints[0])); + NODE_API_CALL(env, napi_create_bigint_uint64(env, 0, &bigints[1])); + // sign is not 0 or 1 (should be interpreted as negative) + NODE_API_CALL(env, + napi_create_bigint_words(env, 2, 1, words.data(), &bigints[2])); + // leading zeroes in word representation + NODE_API_CALL(env, + napi_create_bigint_words(env, 0, 4, words.data(), &bigints[3])); + // zero + NODE_API_CALL(env, + napi_create_bigint_words(env, 1, 0, words.data(), &bigints[4])); + + napi_value array; + NODE_API_CALL(env, + napi_create_array_with_length(env, bigints.size(), &array)); + for (size_t i = 0; i < bigints.size(); i++) { + NODE_API_CALL(env, napi_set_element(env, array, (uint32_t)i, bigints[i])); + } + return array; +} + Napi::Value RunCallback(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); // this function is invoked without the GC callback @@ -1079,6 +1142,13 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports1) { exports.Set("add_tag", Napi::Function::New(env, add_tag)); exports.Set("try_add_tag", Napi::Function::New(env, try_add_tag)); exports.Set("check_tag", Napi::Function::New(env, check_tag)); + exports.Set("bigint_to_i64", Napi::Function::New(env, bigint_to_i64)); + exports.Set("bigint_to_u64", Napi::Function::New(env, bigint_to_u64)); + exports.Set("bigint_to_64_null", Napi::Function::New(env, bigint_to_64_null)); + exports.Set("create_weird_bigints", + Napi::Function::New(env, create_weird_bigints)); + + napitests::register_wrap_tests(env, exports); return exports; } diff --git a/test/napi/napi-app/module.js b/test/napi/napi-app/module.js index 6cb1280cf2..8516abb5b6 100644 --- a/test/napi/napi-app/module.js +++ b/test/napi/napi-app/module.js @@ -1,4 +1,30 @@ const nativeTests = require("./build/Release/napitests.node"); +const secondAddon = require("./build/Release/second_addon.node"); + +function assert(ok) { + if (!ok) { + throw new Error("assertion failed"); + } +} + +async function gcUntil(fn) { + const MAX = 100; + for (let i = 0; i < MAX; i++) { + await new Promise(resolve => { + setTimeout(resolve, 1); + }); + if (typeof Bun == "object") { + Bun.gc(true); + } else { + // if this fails, you need to pass --expose-gc to node + global.gc(); + } + if (fn()) { + return; + } + } + throw new Error(`Condition was not met after ${MAX} GC attempts`); +} nativeTests.test_napi_class_constructor_handle_scope = () => { const NapiClass = nativeTests.get_class_with_constructor(); @@ -270,4 +296,202 @@ nativeTests.test_type_tag = () => { console.log("o2 matches o2:", nativeTests.check_tag(o2, 3, 4)); }; +nativeTests.test_napi_wrap = () => { + const values = [ + {}, + {}, // should be able to be wrapped differently than the distinct empty object above + 5, + new Number(5), + "abc", + new String("abc"), + null, + Symbol("abc"), + Symbol.for("abc"), + new (nativeTests.get_class_with_constructor())(), + new Proxy( + {}, + Object.fromEntries( + [ + "apply", + "construct", + "defineProperty", + "deleteProperty", + "get", + "getOwnPropertyDescriptor", + "getPrototypeOf", + "has", + "isExtensible", + "ownKeys", + "preventExtensions", + "set", + "setPrototypeOf", + ].map(name => [ + name, + () => { + throw new Error("oops"); + }, + ]), + ), + ), + ]; + const wrapSuccess = Array(values.length).fill(false); + for (const [i, v] of values.entries()) { + wrapSuccess[i] = nativeTests.try_wrap(v, i + 1); + console.log(`${typeof v} did wrap: `, wrapSuccess[i]); + } + + for (const [i, v] of values.entries()) { + if (wrapSuccess[i]) { + if (nativeTests.try_unwrap(v) !== i + 1) { + throw new Error("could not unwrap same value"); + } + } else { + if (nativeTests.try_unwrap(v) !== undefined) { + throw new Error("value unwraps without being successfully wrapped"); + } + } + } +}; + +nativeTests.test_napi_wrap_proxy = () => { + const target = {}; + const proxy = new Proxy(target, {}); + assert(nativeTests.try_wrap(target, 5)); + assert(nativeTests.try_wrap(proxy, 6)); + console.log(nativeTests.try_unwrap(target), nativeTests.try_unwrap(proxy)); +}; + +nativeTests.test_napi_wrap_cross_addon = () => { + const wrapped = {}; + console.log("wrap succeeds:", nativeTests.try_wrap(wrapped, 42)); + console.log("unwrapped from other addon", secondAddon.try_unwrap(wrapped)); +}; + +nativeTests.test_napi_wrap_prototype = () => { + class Foo {} + console.log("wrap prototype succeeds:", nativeTests.try_wrap(Foo.prototype, 42)); + // wrapping should not look at prototype chain + console.log("unwrap instance:", nativeTests.try_unwrap(new Foo())); +}; + +nativeTests.test_napi_remove_wrap = () => { + const targets = [{}, new (nativeTests.get_class_with_constructor())()]; + for (const t of targets) { + const target = {}; + // fails + assert(nativeTests.try_remove_wrap(target) === undefined); + // wrap it + assert(nativeTests.try_wrap(target, 5)); + // remove yields the wrapped value + assert(nativeTests.try_remove_wrap(target) === 5); + // neither remove nor unwrap work anymore + assert(nativeTests.try_unwrap(target) === undefined); + assert(nativeTests.try_remove_wrap(target) === undefined); + // can re-wrap + assert(nativeTests.try_wrap(target, 6)); + assert(nativeTests.try_unwrap(target) === 6); + } +}; + +// parameters to create_wrap are: object, ask_for_ref, strong +const createWrapWithoutRef = o => nativeTests.create_wrap(o, false, false); +const createWrapWithWeakRef = o => nativeTests.create_wrap(o, true, false); +const createWrapWithStrongRef = o => nativeTests.create_wrap(o, true, true); + +nativeTests.test_wrap_lifetime_without_ref = async () => { + let object = { foo: "bar" }; + assert(createWrapWithoutRef(object) === object); + assert(nativeTests.get_wrap_data(object) === 42); + object = undefined; + await gcUntil(() => nativeTests.was_wrap_finalize_called()); +}; + +nativeTests.test_wrap_lifetime_with_weak_ref = async () => { + // this looks the same as test_wrap_lifetime_without_ref because it is -- these cases should behave the same + let object = { foo: "bar" }; + assert(createWrapWithWeakRef(object) === object); + assert(nativeTests.get_wrap_data(object) === 42); + object = undefined; + await gcUntil(() => nativeTests.was_wrap_finalize_called()); +}; + +nativeTests.test_wrap_lifetime_with_strong_ref = async () => { + let object = { foo: "bar" }; + assert(createWrapWithStrongRef(object) === object); + assert(nativeTests.get_wrap_data(object) === 42); + + object = undefined; + // still referenced by native module so this should fail + try { + await gcUntil(() => nativeTests.was_wrap_finalize_called()); + throw new Error("object was garbage collected while still referenced by native code"); + } catch (e) { + if (!e.toString().includes("Condition was not met")) { + throw e; + } + } + + // can still get the value using the ref + assert(nativeTests.get_wrap_data_from_ref() === 42); + + // now we free it + nativeTests.unref_wrapped_value(); + await gcUntil(() => nativeTests.was_wrap_finalize_called()); +}; + +nativeTests.test_remove_wrap_lifetime_with_weak_ref = async () => { + let object = { foo: "bar" }; + assert(createWrapWithWeakRef(object) === object); + + assert(nativeTests.get_wrap_data(object) === 42); + + nativeTests.remove_wrap(object); + assert(nativeTests.get_wrap_data(object) === undefined); + assert(nativeTests.get_wrap_data_from_ref() === undefined); + assert(nativeTests.get_object_from_ref() === object); + + object = undefined; + + // ref will stop working once the object is collected + await gcUntil(() => nativeTests.get_object_from_ref() === undefined); + + // finalizer shouldn't have been called + assert(nativeTests.was_wrap_finalize_called() === false); +}; + +nativeTests.test_remove_wrap_lifetime_with_strong_ref = async () => { + let object = { foo: "bar" }; + assert(createWrapWithStrongRef(object) === object); + + assert(nativeTests.get_wrap_data(object) === 42); + + nativeTests.remove_wrap(object); + assert(nativeTests.get_wrap_data(object) === undefined); + assert(nativeTests.get_wrap_data_from_ref() === undefined); + assert(nativeTests.get_object_from_ref() === object); + + object = undefined; + + // finalizer should not be called and object should not be freed + try { + await gcUntil(() => nativeTests.was_wrap_finalize_called() || nativeTests.get_object_from_ref() === undefined); + throw new Error("finalizer ran"); + } catch (e) { + if (!e.toString().includes("Condition was not met")) { + throw e; + } + } + + // native code can still get the object + assert(JSON.stringify(nativeTests.get_object_from_ref()) === `{"foo":"bar"}`); + + // now it gets deleted + nativeTests.unref_wrapped_value(); + await gcUntil(() => nativeTests.get_object_from_ref() === undefined); +}; + +nativeTests.test_create_bigint_words = () => { + console.log(nativeTests.create_weird_bigints()); +}; + module.exports = nativeTests; diff --git a/test/napi/napi-app/napi_with_version.h b/test/napi/napi-app/napi_with_version.h new file mode 100644 index 0000000000..f852184087 --- /dev/null +++ b/test/napi/napi-app/napi_with_version.h @@ -0,0 +1,8 @@ +#pragma once +#define NAPI_EXPERIMENTAL +#include +#include + +// TODO(@190n): remove this when CI has Node 22.6 +typedef struct napi_env__ *napi_env; +typedef napi_env node_api_basic_env; diff --git a/test/napi/napi-app/second_addon.c b/test/napi/napi-app/second_addon.c new file mode 100644 index 0000000000..85232861dd --- /dev/null +++ b/test/napi/napi-app/second_addon.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#define NODE_API_CALL(env, call) \ + do { \ + napi_status status = (call); \ + if (status != napi_ok) { \ + const napi_extended_error_info *error_info = NULL; \ + napi_get_last_error_info((env), &error_info); \ + const char *err_message = error_info->error_message; \ + bool is_pending; \ + napi_is_exception_pending((env), &is_pending); \ + /* If an exception is already pending, don't rethrow it */ \ + if (!is_pending) { \ + const char *message = \ + (err_message == NULL) ? "empty error message" : err_message; \ + napi_throw_error((env), NULL, message); \ + } \ + return NULL; \ + } \ + } while (0) + +static napi_value try_unwrap(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value argv[1]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + if (argc != 1) { + napi_throw_error(env, NULL, "Wrong number of arguments to try_unwrap"); + return NULL; + } + + double *pointer; + if (napi_unwrap(env, argv[0], (void **)(&pointer)) != napi_ok) { + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + return undefined; + } else { + napi_value number; + NODE_API_CALL(env, napi_create_double(env, *pointer, &number)); + return number; + } +} + +/* napi_value */ NAPI_MODULE_INIT(/* napi_env env, napi_value exports */) { + napi_value try_unwrap_function; + NODE_API_CALL(env, + napi_create_function(env, "try_unwrap", NAPI_AUTO_LENGTH, + try_unwrap, NULL, &try_unwrap_function)); + NODE_API_CALL(env, napi_set_named_property(env, exports, "try_unwrap", + try_unwrap_function)); + return exports; +} diff --git a/test/napi/napi-app/utils.h b/test/napi/napi-app/utils.h new file mode 100644 index 0000000000..92e158e6b7 --- /dev/null +++ b/test/napi/napi-app/utils.h @@ -0,0 +1,89 @@ +#pragma once +#include "napi_with_version.h" +#include + +// e.g NODE_API_CALL(env, napi_create_int32(env, 5, &my_napi_integer)) +#define NODE_API_CALL(env, call) NODE_API_CALL_CUSTOM_RETURN(env, NULL, call) + +// Version of NODE_API_CALL for functions not returning napi_value +#define NODE_API_CALL_CUSTOM_RETURN(env, value_to_return_if_threw, call) \ + NODE_API_ASSERT_CUSTOM_RETURN(env, value_to_return_if_threw, \ + (call) == napi_ok) + +// Throw an error in the given napi_env and return if expr is false +#define NODE_API_ASSERT(env, expr) \ + NODE_API_ASSERT_CUSTOM_RETURN(env, NULL, expr) + +#ifdef _MSC_VER +#define CURRENT_FUNCTION_NAME __FUNCSIG__ +#else +#define CURRENT_FUNCTION_NAME __PRETTY_FUNCTION__ +#endif + +// Version of NODE_API_ASSERT for functions not returning napi_value +#define NODE_API_ASSERT_CUSTOM_RETURN(ENV, VALUE_TO_RETURN_IF_THREW, EXPR) \ + do { \ + if (!(EXPR)) { \ + bool is_pending; \ + napi_is_exception_pending((ENV), &is_pending); \ + /* If an exception is already pending, don't rethrow it */ \ + if (!is_pending) { \ + char buf[4096] = {0}; \ + snprintf(buf, sizeof(buf) - 1, "%s (%s:%d): Assertion failed: %s", \ + CURRENT_FUNCTION_NAME, __FILE__, __LINE__, #EXPR); \ + napi_throw_error((ENV), NULL, buf); \ + } \ + return (VALUE_TO_RETURN_IF_THREW); \ + } \ + } while (0) + +#define REGISTER_FUNCTION(ENV, EXPORTS, FUNCTION) \ + EXPORTS.Set(#FUNCTION, Napi::Function::New(ENV, FUNCTION)) + +static inline napi_value ok(napi_env env) { + napi_value result; + napi_get_undefined(env, &result); + return result; +} + +// For functions that take a garbage collection callback as the first argument +// (functions not called directly by module.js), use this to trigger GC +static inline void run_gc(const Napi::CallbackInfo &info) { + info[0].As().Call(0, nullptr); +} + +// calls napi_typeof and asserts it returns napi_ok +static inline napi_valuetype get_typeof(napi_env env, napi_value value) { + napi_valuetype result; + // return an invalid napi_valuetype if the call to napi_typeof fails + NODE_API_CALL_CUSTOM_RETURN(env, static_cast(INT_MAX), + napi_typeof(env, value, &result)); + return result; +} + +static inline const char *napi_valuetype_to_string(napi_valuetype type) { + switch (type) { + case napi_undefined: + return "undefined"; + case napi_null: + return "null"; + case napi_boolean: + return "boolean"; + case napi_number: + return "number"; + case napi_string: + return "string"; + case napi_symbol: + return "symbol"; + case napi_object: + return "object"; + case napi_function: + return "function"; + case napi_external: + return "external"; + case napi_bigint: + return "bigint"; + default: + return "unknown"; + } +} diff --git a/test/napi/napi-app/wrap_tests.cpp b/test/napi/napi-app/wrap_tests.cpp new file mode 100644 index 0000000000..5365a29e89 --- /dev/null +++ b/test/napi/napi-app/wrap_tests.cpp @@ -0,0 +1,232 @@ +#include "wrap_tests.h" + +#include "utils.h" +#include + +namespace napitests { + +static napi_ref ref_to_wrapped_object = nullptr; +static bool wrap_finalize_called = false; + +// static void delete_the_ref(napi_env env, void *_data, void *_hint) { +// printf("delete_the_ref\n"); +// // not using NODE_API_ASSERT as this runs in a finalizer where allocating +// an +// // error might cause a harder-to-debug crash +// assert(ref_to_wrapped_object); +// napi_delete_reference(env, ref_to_wrapped_object); +// ref_to_wrapped_object = nullptr; +// } + +static void finalize_for_create_wrap(napi_env env, void *opaque_data, + void *opaque_hint) { + int *data = reinterpret_cast(opaque_data); + int *hint = reinterpret_cast(opaque_hint); + printf("finalize_for_create_wrap, data = %d, hint = %d\n", *data, *hint); + delete data; + delete hint; + // TODO: this needs https://github.com/oven-sh/bun/pulls/14501 to work + // if (ref_to_wrapped_object) { + // node_api_post_finalizer(env, delete_the_ref, nullptr, nullptr); + // } + wrap_finalize_called = true; +} + +// create_wrap(js_object: object, ask_for_ref: boolean, strong: boolean): object +static napi_value create_wrap(const Napi::CallbackInfo &info) { + wrap_finalize_called = false; + napi_env env = info.Env(); + napi_value js_object = info[0]; + + napi_value js_ask_for_ref = info[1]; + bool ask_for_ref; + NODE_API_CALL(env, napi_get_value_bool(env, js_ask_for_ref, &ask_for_ref)); + napi_value js_strong = info[2]; + bool strong; + NODE_API_CALL(env, napi_get_value_bool(env, js_strong, &strong)); + + // wrap it + int *wrap_data = new int(42); + int *wrap_hint = new int(123); + + NODE_API_CALL(env, napi_wrap(env, js_object, wrap_data, + finalize_for_create_wrap, wrap_hint, + ask_for_ref ? &ref_to_wrapped_object : nullptr)); + if (ask_for_ref && strong) { + uint32_t new_refcount; + NODE_API_CALL( + env, napi_reference_ref(env, ref_to_wrapped_object, &new_refcount)); + NODE_API_ASSERT(env, new_refcount == 1); + } + + if (!ask_for_ref) { + ref_to_wrapped_object = nullptr; + } + + return js_object; +} + +// get_wrap_data(js_object: object): number +static napi_value get_wrap_data(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + napi_value js_object = info[0]; + + void *wrapped_data; + napi_status status = napi_unwrap(env, js_object, &wrapped_data); + if (status != napi_ok) { + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + return undefined; + } + + napi_value js_number; + NODE_API_CALL(env, + napi_create_int32(env, *reinterpret_cast(wrapped_data), + &js_number)); + return js_number; +} + +// get_object_from_ref(): object +static napi_value get_object_from_ref(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + + napi_value wrapped_object; + NODE_API_CALL(env, napi_get_reference_value(env, ref_to_wrapped_object, + &wrapped_object)); + + if (!wrapped_object) { + NODE_API_CALL(env, napi_get_undefined(env, &wrapped_object)); + } + return wrapped_object; +} + +// get_wrap_data_from_ref(): number|undefined +static napi_value get_wrap_data_from_ref(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + + napi_value wrapped_object; + NODE_API_CALL(env, napi_get_reference_value(env, ref_to_wrapped_object, + &wrapped_object)); + + void *wrapped_data; + napi_status status = napi_unwrap(env, wrapped_object, &wrapped_data); + if (status == napi_ok) { + napi_value js_number; + NODE_API_CALL(env, + napi_create_int32(env, *reinterpret_cast(wrapped_data), + &js_number)); + return js_number; + } else if (status == napi_invalid_arg) { + // no longer wrapped + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + return undefined; + } else { + NODE_API_ASSERT(env, false && "this should not be reached"); + return nullptr; + } +} + +// remove_wrap_data(js_object: object): undefined +static napi_value remove_wrap(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + napi_value js_object = info[0]; + + void *wrap_data; + NODE_API_CALL(env, napi_remove_wrap(env, js_object, &wrap_data)); + + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + return undefined; +} + +// unref_wrapped_value(): undefined +static napi_value unref_wrapped_value(const Napi::CallbackInfo &info) { + napi_env env = info.Env(); + uint32_t new_refcount; + NODE_API_CALL( + env, napi_reference_unref(env, ref_to_wrapped_object, &new_refcount)); + // should never have been set higher than 1 + NODE_API_ASSERT(env, new_refcount == 0); + + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + return undefined; +} + +static napi_value was_wrap_finalize_called(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + return Napi::Boolean::New(env, wrap_finalize_called); +} + +// try_wrap(value: any, num: number): bool +// wraps value in a C++ object corresponding to the number num +// true if success +static napi_value try_wrap(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + napi_value value = info[0]; + napi_value js_num = info[1]; + double c_num; + NODE_API_CALL(env, napi_get_value_double(env, js_num, &c_num)); + + napi_status status = napi_wrap( + env, value, reinterpret_cast(new double{c_num}), + [](napi_env env, void *data, void *hint) { + (void)env; + (void)hint; + delete reinterpret_cast(data); + }, + nullptr, nullptr); + + napi_value js_result; + assert(napi_get_boolean(env, status == napi_ok, &js_result) == napi_ok); + return js_result; +} + +// try_unwrap(any): number|undefined +static napi_value try_unwrap(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + napi_value value = info[0]; + + double *wrapped; + napi_status status = + napi_unwrap(env, value, reinterpret_cast(&wrapped)); + napi_value result; + if (status == napi_ok) { + NODE_API_CALL(env, napi_create_double(env, *wrapped, &result)); + } else { + NODE_API_CALL(env, napi_get_undefined(env, &result)); + } + return result; +} + +static napi_value try_remove_wrap(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + napi_value value = info[0]; + + double *wrapped; + napi_status status = + napi_remove_wrap(env, value, reinterpret_cast(&wrapped)); + napi_value result; + if (status == napi_ok) { + NODE_API_CALL(env, napi_create_double(env, *wrapped, &result)); + } else { + NODE_API_CALL(env, napi_get_undefined(env, &result)); + } + return result; +} + +void register_wrap_tests(Napi::Env env, Napi::Object exports) { + REGISTER_FUNCTION(env, exports, create_wrap); + REGISTER_FUNCTION(env, exports, get_wrap_data); + REGISTER_FUNCTION(env, exports, get_object_from_ref); + REGISTER_FUNCTION(env, exports, get_wrap_data_from_ref); + REGISTER_FUNCTION(env, exports, remove_wrap); + REGISTER_FUNCTION(env, exports, unref_wrapped_value); + REGISTER_FUNCTION(env, exports, was_wrap_finalize_called); + REGISTER_FUNCTION(env, exports, try_wrap); + REGISTER_FUNCTION(env, exports, try_unwrap); + REGISTER_FUNCTION(env, exports, try_remove_wrap); +} + +} // namespace napitests diff --git a/test/napi/napi-app/wrap_tests.h b/test/napi/napi-app/wrap_tests.h new file mode 100644 index 0000000000..a70a44240e --- /dev/null +++ b/test/napi/napi-app/wrap_tests.h @@ -0,0 +1,11 @@ +#pragma once + +// Helper functions used by JS to test napi_wrap + +#include "napi_with_version.h" + +namespace napitests { + +void register_wrap_tests(Napi::Env env, Napi::Object exports); + +} // namespace napitests diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 25144c80f7..b00513cb86 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -115,6 +115,7 @@ describe("napi", () => { outdir: dir, target, format, + throw: true, }); expect(build.logs).toBeEmpty(); @@ -319,6 +320,64 @@ describe("napi", () => { checkSameOutput("test_type_tag", []); }); }); + + describe("napi_wrap", () => { + it("accepts the right kinds of values", () => { + checkSameOutput("test_napi_wrap", []); + }); + + it("is shared between addons", () => { + checkSameOutput("test_napi_wrap_cross_addon", []); + }); + + it("does not follow prototypes", () => { + checkSameOutput("test_napi_wrap_prototype", []); + }); + + it("does not consider proxies", () => { + checkSameOutput("test_napi_wrap_proxy", []); + }); + + it("can remove a wrap", () => { + checkSameOutput("test_napi_remove_wrap", []); + }); + + it("has the right lifetime", () => { + checkSameOutput("test_wrap_lifetime_without_ref", []); + checkSameOutput("test_wrap_lifetime_with_weak_ref", []); + checkSameOutput("test_wrap_lifetime_with_strong_ref", []); + checkSameOutput("test_remove_wrap_lifetime_with_weak_ref", []); + checkSameOutput("test_remove_wrap_lifetime_with_strong_ref", []); + }); + }); + + describe("bigint conversion to int64/uint64", () => { + it("works", () => { + const tests = [-1n, 0n, 1n]; + for (const power of [63, 64, 65]) { + for (const sign of [-1, 1]) { + const boundary = BigInt(sign) * 2n ** BigInt(power); + tests.push(boundary, boundary - 1n, boundary + 1n); + } + } + + const testsString = "[" + tests.map(bigint => bigint.toString() + "n").join(",") + "]"; + checkSameOutput("bigint_to_i64", testsString); + checkSameOutput("bigint_to_u64", testsString); + }); + it("returns the right error code", () => { + const badTypes = '[null, undefined, 5, "123", "abc"]'; + checkSameOutput("bigint_to_i64", badTypes); + checkSameOutput("bigint_to_u64", badTypes); + checkSameOutput("bigint_to_64_null", []); + }); + }); + + describe("create_bigint_words", () => { + it("works", () => { + checkSameOutput("test_create_bigint_words", []); + }); + }); }); function checkSameOutput(test: string, args: any[] | string) { diff --git a/test/package-json-lint.test.ts b/test/package-json-lint.test.ts index fb3d9a55c3..aa253f17e3 100644 --- a/test/package-json-lint.test.ts +++ b/test/package-json-lint.test.ts @@ -22,20 +22,25 @@ describe("package.json dependencies must be exact versions", async () => { optionalDependencies = {}, } = await Bun.file(join(dir, "./package.json")).json(); + // Hyphen is necessary to accept prerelease versions like "1.1.3-alpha.7" + // This regex still forbids semver ranges like "1.0.0 - 1.2.0", as those must have spaces + // around the hyphen. + const okRegex = /^([a-zA-Z0-9\.\-])+$/; + for (const [name, dep] of Object.entries(dependencies)) { - expect(dep).toMatch(/^([a-zA-Z0-9\.])+$/); + expect(dep, `dependency ${name} specifies non-exact version "${dep}"`).toMatch(okRegex); } for (const [name, dep] of Object.entries(devDependencies)) { - expect(dep).toMatch(/^([a-zA-Z0-9\.])+$/); + expect(dep, `dev dependency ${name} specifies non-exact version "${dep}"`).toMatch(okRegex); } for (const [name, dep] of Object.entries(peerDependencies)) { - expect(dep).toMatch(/^([a-zA-Z0-9\.])+$/); + expect(dep, `peer dependency ${name} specifies non-exact version "${dep}"`).toMatch(okRegex); } for (const [name, dep] of Object.entries(optionalDependencies)) { - expect(dep).toMatch(/^([a-zA-Z0-9\.])+$/); + expect(dep, `optional dependency ${name} specifies non-exact version "${dep}"`).toMatch(okRegex); } }); } diff --git a/test/package.json b/test/package.json index 5fc461dc32..efe5c43799 100644 --- a/test/package.json +++ b/test/package.json @@ -10,9 +10,10 @@ }, "dependencies": { "@azure/service-bus": "7.9.4", + "@duckdb/node-api": "1.1.3-alpha.7", "@grpc/grpc-js": "1.12.0", "@grpc/proto-loader": "0.7.10", - "@napi-rs/canvas": "0.1.47", + "@napi-rs/canvas": "0.1.65", "@prisma/client": "5.8.0", "@remix-run/react": "2.10.3", "@remix-run/serve": "2.10.3", @@ -33,6 +34,8 @@ "iconv-lite": "0.6.3", "isbot": "5.1.13", "jest-extended": "4.0.0", + "jimp": "1.6.0", + "jsdom": "25.0.1", "jsonwebtoken": "9.0.2", "jws": "4.0.0", "lodash": "4.17.21", diff --git a/test/preload.ts b/test/preload.ts index 811af099b9..35322932e1 100644 --- a/test/preload.ts +++ b/test/preload.ts @@ -4,6 +4,7 @@ import * as harness from "./harness"; // so process.env = {} causes them to be out of sync and we assume Bun.env is for (let key in process.env) { if (key === "TZ") continue; + if (key in harness.bunEnv) continue; delete process.env[key]; } @@ -12,7 +13,6 @@ for (let key in harness.bunEnv) { if (harness.bunEnv[key] === undefined) { continue; } - process.env[key] = harness.bunEnv[key] + ""; } diff --git a/test/regression/issue/14976/14976.test.ts b/test/regression/issue/14976/14976.test.ts index 37e7c72df0..804eb7cc20 100644 --- a/test/regression/issue/14976/14976.test.ts +++ b/test/regression/issue/14976/14976.test.ts @@ -39,6 +39,7 @@ test("bun build --target=bun outputs only ascii", async () => { const build_result = await Bun.build({ entrypoints: [import.meta.dirname + "/import_target.ts"], target: "bun", + throw: true, }); expect(build_result.success).toBe(true); expect(build_result.outputs.length).toBe(1); diff --git a/test/regression/issue/16007.test.ts b/test/regression/issue/16007.test.ts new file mode 100644 index 0000000000..fa09d4fedc --- /dev/null +++ b/test/regression/issue/16007.test.ts @@ -0,0 +1,12 @@ +import { it, expect } from "bun:test"; + +it("Set is propperly formatted in Bun.inspect()", () => { + const set = new Set(["foo", "bar"]); + const formatted = Bun.inspect({ set }); + expect(formatted).toBe(`{ + set: Set(2) { + "foo", + "bar", + }, +}`); +}); diff --git a/test/regression/issue/ctrl-c.test.ts b/test/regression/issue/ctrl-c.test.ts index 1a511a6df7..232e9f17d2 100644 --- a/test/regression/issue/ctrl-c.test.ts +++ b/test/regression/issue/ctrl-c.test.ts @@ -1,5 +1,50 @@ import { expect, it, test } from "bun:test"; import { bunEnv, bunExe, isWindows, tempDirWithFiles } from "harness"; +import { join } from "path"; + +test.skipIf(isWindows)("verify that we can call sigint 4096 times", () => { + const dir = tempDirWithFiles("ctrlc", { + "index.js": /*js*/ ` + let count = 0; + process.exitCode = 1; + + const handler = () => { + count++; + console.count("SIGINT"); + if (count === 1024 * 4) { + process.off("SIGINT", handler); + process.exitCode = 0; + clearTimeout(timer); + } + }; + process.on("SIGINT", handler); + var timer = setTimeout(() => {}, 999999); + var remaining = 1024 * 4; + + function go() { + for (var i = 0, end = Math.min(1024, remaining); i < end; i++) { + process.kill(process.pid, "SIGINT"); + } + remaining -= i; + + if (remaining > 0) { + setTimeout(go, 10); + } + } + go(); + `, + }); + + const result = Bun.spawnSync({ + cmd: [bunExe(), join(dir, "index.js")], + cwd: dir, + env: bunEnv, + stdout: "inherit", + stderr: "inherit", + }); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); +}); test.skipIf(isWindows)("verify that we forward SIGINT from parent to child in bun run", () => { const dir = tempDirWithFiles("ctrlc", { @@ -16,12 +61,12 @@ test.skipIf(isWindows)("verify that we forward SIGINT from parent to child in bu { "name": "ctrlc", "scripts": { - "start": "${bunExe()} index.js" + "start": " ${bunExe()} index.js" } } `, }); - + console.log(dir); const result = Bun.spawnSync({ cmd: [bunExe(), "start"], cwd: dir, @@ -90,14 +135,18 @@ for (const mode of [ killed: proc.killed, exitCode: proc.exitCode, signalCode: proc.signalCode, - }).toEqual(isWindows ? { - killed: true, - exitCode: 1, - signalCode: null, - } : { - killed: true, - exitCode: null, - signalCode: "SIGINT", - }); + }).toEqual( + isWindows + ? { + killed: true, + exitCode: 1, + signalCode: null, + } + : { + killed: true, + exitCode: null, + signalCode: "SIGINT", + }, + ); }); } diff --git a/test/v8/v8.test.ts b/test/v8/v8.test.ts index ad7b2b1a3d..1f4ff131ea 100644 --- a/test/v8/v8.test.ts +++ b/test/v8/v8.test.ts @@ -1,6 +1,6 @@ import { spawn, spawnSync } from "bun"; import { beforeAll, describe, expect, it } from "bun:test"; -import { bunEnv, bunExe, tmpdirSync, isWindows } from "harness"; +import { bunEnv, bunExe, tmpdirSync, isWindows, isMusl, isBroken } from "harness"; import assert from "node:assert"; import fs from "node:fs/promises"; import { join, basename } from "path"; @@ -84,163 +84,165 @@ async function build( }; } -beforeAll(async () => { - // set up clean directories for our 4 builds - directories.bunRelease = tmpdirSync(); - directories.bunDebug = tmpdirSync(); - directories.node = tmpdirSync(); - directories.badModules = tmpdirSync(); +describe.todoIf(isBroken && isMusl)("node:v8", () => { + beforeAll(async () => { + // set up clean directories for our 4 builds + directories.bunRelease = tmpdirSync(); + directories.bunDebug = tmpdirSync(); + directories.node = tmpdirSync(); + directories.badModules = tmpdirSync(); - await install(srcDir, directories.bunRelease, Runtime.bun); - await install(srcDir, directories.bunDebug, Runtime.bun); - await install(srcDir, directories.node, Runtime.node); - await install(join(__dirname, "bad-modules"), directories.badModules, Runtime.node); + await install(srcDir, directories.bunRelease, Runtime.bun); + await install(srcDir, directories.bunDebug, Runtime.bun); + await install(srcDir, directories.node, Runtime.node); + await install(join(__dirname, "bad-modules"), directories.badModules, Runtime.node); - const results = await Promise.all([ - build(srcDir, directories.bunRelease, Runtime.bun, BuildMode.release), - build(srcDir, directories.bunDebug, Runtime.bun, BuildMode.debug), - build(srcDir, directories.node, Runtime.node, BuildMode.release), - build(join(__dirname, "bad-modules"), directories.badModules, Runtime.node, BuildMode.release), - ]); - for (const r of results) { - console.log(r.description, "stdout:"); - console.log(r.out); - console.log(r.description, "stderr:"); - console.log(r.err); - } -}); + const results = await Promise.all([ + build(srcDir, directories.bunRelease, Runtime.bun, BuildMode.release), + build(srcDir, directories.bunDebug, Runtime.bun, BuildMode.debug), + build(srcDir, directories.node, Runtime.node, BuildMode.release), + build(join(__dirname, "bad-modules"), directories.badModules, Runtime.node, BuildMode.release), + ]); + for (const r of results) { + console.log(r.description, "stdout:"); + console.log(r.out); + console.log(r.description, "stderr:"); + console.log(r.err); + } + }); -describe("module lifecycle", () => { - it("can call a basic native function", () => { - checkSameOutput("test_v8_native_call", []); - }); -}); - -describe("primitives", () => { - it("can create and distinguish between null, undefined, true, and false", () => { - checkSameOutput("test_v8_primitives", []); - }); -}); - -describe("Number", () => { - it("can create small integer", () => { - checkSameOutput("test_v8_number_int", []); - }); - // non-i32 v8::Number is not implemented yet - it("can create large integer", () => { - checkSameOutput("test_v8_number_large_int", []); - }); - it("can create fraction", () => { - checkSameOutput("test_v8_number_fraction", []); - }); -}); - -describe("String", () => { - it("can create and read back strings with only ASCII characters", () => { - checkSameOutput("test_v8_string_ascii", []); - }); - // non-ASCII strings are not implemented yet - it("can create and read back strings with UTF-8 characters", () => { - checkSameOutput("test_v8_string_utf8", []); - }); - it("handles replacement correctly in strings with invalid UTF-8 sequences", () => { - checkSameOutput("test_v8_string_invalid_utf8", []); - }); - it("can create strings from null-terminated Latin-1 data", () => { - checkSameOutput("test_v8_string_latin1", []); - }); - describe("WriteUtf8", () => { - it("truncates the string correctly", () => { - checkSameOutput("test_v8_string_write_utf8", []); + describe("module lifecycle", () => { + it("can call a basic native function", () => { + checkSameOutput("test_v8_native_call", []); }); }); -}); -describe("External", () => { - it("can create an external and read back the correct value", () => { - checkSameOutput("test_v8_external", []); - }); -}); - -describe("Object", () => { - it("can create an object and set properties", () => { - checkSameOutput("test_v8_object", []); - }); -}); -describe("Array", () => { - // v8::Array::New is broken as it still tries to reinterpret locals as JSValues - it.skip("can create an array from a C array of Locals", () => { - checkSameOutput("test_v8_array_new", []); - }); -}); - -describe("ObjectTemplate", () => { - it("creates objects with internal fields", () => { - checkSameOutput("test_v8_object_template", []); - }); -}); - -describe("FunctionTemplate", () => { - it("keeps the data parameter alive", () => { - checkSameOutput("test_v8_function_template", []); - }); -}); - -describe("Function", () => { - it("correctly receives all its arguments from JS", () => { - checkSameOutput("print_values_from_js", [5.0, true, null, false, "meow", {}]); - checkSameOutput("print_native_function", []); + describe("primitives", () => { + it("can create and distinguish between null, undefined, true, and false", () => { + checkSameOutput("test_v8_primitives", []); + }); }); - it("correctly receives the this value from JS", () => { - checkSameOutput("call_function_with_weird_this_values", []); - }); -}); - -describe("error handling", () => { - it("throws an error for modules built using the wrong ABI version", () => { - expect(() => require(join(directories.badModules, "build/Release/mismatched_abi_version.node"))).toThrow( - "The module 'mismatched_abi_version' was compiled against a different Node.js ABI version using NODE_MODULE_VERSION 42.", - ); + describe("Number", () => { + it("can create small integer", () => { + checkSameOutput("test_v8_number_int", []); + }); + // non-i32 v8::Number is not implemented yet + it("can create large integer", () => { + checkSameOutput("test_v8_number_large_int", []); + }); + it("can create fraction", () => { + checkSameOutput("test_v8_number_fraction", []); + }); }); - it("throws an error for modules with no entrypoint", () => { - expect(() => require(join(directories.badModules, "build/Release/no_entrypoint.node"))).toThrow( - "The module 'no_entrypoint' has no declared entry point.", - ); + describe("String", () => { + it("can create and read back strings with only ASCII characters", () => { + checkSameOutput("test_v8_string_ascii", []); + }); + // non-ASCII strings are not implemented yet + it("can create and read back strings with UTF-8 characters", () => { + checkSameOutput("test_v8_string_utf8", []); + }); + it("handles replacement correctly in strings with invalid UTF-8 sequences", () => { + checkSameOutput("test_v8_string_invalid_utf8", []); + }); + it("can create strings from null-terminated Latin-1 data", () => { + checkSameOutput("test_v8_string_latin1", []); + }); + describe("WriteUtf8", () => { + it("truncates the string correctly", () => { + checkSameOutput("test_v8_string_write_utf8", []); + }); + }); }); -}); -describe("Global", () => { - it("can create, modify, and read the value from global handles", () => { - checkSameOutput("test_v8_global", []); + describe("External", () => { + it("can create an external and read back the correct value", () => { + checkSameOutput("test_v8_external", []); + }); }); -}); -describe("HandleScope", () => { - it("can hold a lot of locals", () => { - checkSameOutput("test_many_v8_locals", []); + describe("Object", () => { + it("can create an object and set properties", () => { + checkSameOutput("test_v8_object", []); + }); }); - it("keeps GC objects alive", () => { - checkSameOutput("test_handle_scope_gc", []); - }, 10000); -}); - -describe("EscapableHandleScope", () => { - it("keeps handles alive in the outer scope", () => { - checkSameOutput("test_v8_escapable_handle_scope", []); + describe("Array", () => { + // v8::Array::New is broken as it still tries to reinterpret locals as JSValues + it.skip("can create an array from a C array of Locals", () => { + checkSameOutput("test_v8_array_new", []); + }); }); -}); -describe("uv_os_getpid", () => { - it.skipIf(isWindows)("returns the same result as getpid on POSIX", () => { - checkSameOutput("test_uv_os_getpid", []); + describe("ObjectTemplate", () => { + it("creates objects with internal fields", () => { + checkSameOutput("test_v8_object_template", []); + }); }); -}); -describe("uv_os_getppid", () => { - it.skipIf(isWindows)("returns the same result as getppid on POSIX", () => { - checkSameOutput("test_uv_os_getppid", []); + describe("FunctionTemplate", () => { + it("keeps the data parameter alive", () => { + checkSameOutput("test_v8_function_template", []); + }); + }); + + describe("Function", () => { + it("correctly receives all its arguments from JS", () => { + checkSameOutput("print_values_from_js", [5.0, true, null, false, "meow", {}]); + checkSameOutput("print_native_function", []); + }); + + it("correctly receives the this value from JS", () => { + checkSameOutput("call_function_with_weird_this_values", []); + }); + }); + + describe("error handling", () => { + it("throws an error for modules built using the wrong ABI version", () => { + expect(() => require(join(directories.badModules, "build/Release/mismatched_abi_version.node"))).toThrow( + "The module 'mismatched_abi_version' was compiled against a different Node.js ABI version using NODE_MODULE_VERSION 42.", + ); + }); + + it("throws an error for modules with no entrypoint", () => { + expect(() => require(join(directories.badModules, "build/Release/no_entrypoint.node"))).toThrow( + "The module 'no_entrypoint' has no declared entry point.", + ); + }); + }); + + describe("Global", () => { + it("can create, modify, and read the value from global handles", () => { + checkSameOutput("test_v8_global", []); + }); + }); + + describe("HandleScope", () => { + it("can hold a lot of locals", () => { + checkSameOutput("test_many_v8_locals", []); + }); + it("keeps GC objects alive", () => { + checkSameOutput("test_handle_scope_gc", []); + }, 10000); + }); + + describe("EscapableHandleScope", () => { + it("keeps handles alive in the outer scope", () => { + checkSameOutput("test_v8_escapable_handle_scope", []); + }); + }); + + describe("uv_os_getpid", () => { + it.skipIf(isWindows)("returns the same result as getpid on POSIX", () => { + checkSameOutput("test_uv_os_getpid", []); + }); + }); + + describe("uv_os_getppid", () => { + it.skipIf(isWindows)("returns the same result as getppid on POSIX", () => { + checkSameOutput("test_uv_os_getppid", []); + }); }); });