├── .cargo └── config.toml ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .yarnrc.yml ├── API.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── GOVERNANCE.md ├── LICENSE ├── Makefile ├── NOTICE ├── README-zh.md ├── README.md ├── THIRD_PARTY_LICENSES ├── benchmarks ├── llrt-ddb-put.png ├── node20-ddb-put.png └── size.png ├── build.mjs ├── fixtures ├── a.js ├── b.js ├── c.cjs ├── d.cjs ├── define-property-export.cjs ├── empty.js ├── empty.lrt ├── export-function.cjs ├── fs │ └── readdir │ │ ├── readdir.js │ │ └── recursive │ │ └── readdir.js ├── handler.mjs ├── hello.js ├── hello.txt ├── import.cjs ├── import.js ├── local.mjs ├── node_modules │ ├── elem-aws-lambda-powertools │ │ ├── commons │ │ │ ├── lib │ │ │ │ ├── cjs │ │ │ │ │ ├── index.js │ │ │ │ │ └── typeUtils.js │ │ │ │ └── esm │ │ │ │ │ ├── index.js │ │ │ │ │ └── typeUtils.js │ │ │ └── package.json │ │ └── jmespath │ │ │ ├── lib │ │ │ ├── cjs │ │ │ │ └── index.js │ │ │ └── esm │ │ │ │ └── index.js │ │ │ └── package.json │ ├── elem-debug │ │ ├── package.json │ │ └── src │ │ │ └── browser.js │ ├── elem-lodash.merge │ │ ├── index.js │ │ └── package.json │ ├── elem-react-dom │ │ ├── cjs │ │ │ ├── react-dom-server.edge.development.js │ │ │ └── react-dom.development.js │ │ ├── index.js │ │ ├── package.json │ │ └── server.edge.js │ └── elem-uuid │ │ ├── dist │ │ └── commonjs-browser │ │ │ └── index.js │ │ └── package.json ├── package.json ├── primitive-handler.mjs ├── prop-export.cjs ├── referenced-exports.cjs ├── require.mjs ├── test903 │ ├── bar.mjs │ └── foo.mjs ├── test_modules │ ├── test-aws-lambda-powertools-jmespath.js │ ├── test-debug.js │ ├── test-lodash.merge.js │ ├── test-react-dom.js │ └── test-uuid.js ├── throw.js ├── throwing-handler.mjs └── throwing-init-handler.mjs ├── index.mjs ├── lambda-server.js ├── lexe_bin.mjs ├── lexe_build.mjs ├── libs ├── llrt_build │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_compression │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_context │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_dns_cache │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_encoding │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── lib.rs ├── llrt_json │ ├── Cargo.toml │ ├── benches │ │ └── json.rs │ ├── build.rs │ └── src │ │ ├── escape.rs │ │ ├── lib.rs │ │ ├── parse.rs │ │ └── stringify.rs ├── llrt_logging │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_numbers │ ├── Cargo.toml │ ├── benches │ │ └── numbers.rs │ └── src │ │ └── lib.rs ├── llrt_test │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── llrt_utils │ ├── Cargo.toml │ └── src │ ├── bytearray_buffer.rs │ ├── bytes.rs │ ├── class.rs │ ├── clone.rs │ ├── error.rs │ ├── error_messages.rs │ ├── fs.rs │ ├── hash.rs │ ├── lib.rs │ ├── macros.rs │ ├── mc_oneshot.rs │ ├── module.rs │ ├── object.rs │ ├── option.rs │ ├── primordials.rs │ ├── result.rs │ ├── reuse_list.rs │ ├── sysinfo.rs │ └── time.rs ├── linker ├── ar ├── cc ├── cc-aarch64-linux-gnu ├── cc-aarch64-linux-musl ├── cc-x86_64-linux-gnu ├── cc-x86_64-linux-musl ├── cxx ├── cxx-aarch64-linux-gnu ├── cxx-aarch64-linux-musl ├── cxx-x86_64-linux-gnu ├── cxx-x86_64-linux-musl └── ranlib ├── llrt ├── Cargo.toml ├── src │ ├── build.rs │ ├── core.rs │ ├── lib.rs │ ├── main.c │ ├── main.rs │ ├── minimal_tracer.rs │ └── repl.rs └── tests │ └── build_tests.rs ├── llrt_core ├── Cargo.toml ├── build.rs └── src │ ├── bytecode.rs │ ├── compiler.rs │ ├── compiler_common.rs │ ├── environment.rs │ ├── http.rs │ ├── lib.rs │ ├── libs.rs │ ├── module_builder.rs │ ├── modules │ ├── console.rs │ ├── js │ │ ├── @llrt │ │ │ ├── expect │ │ │ │ ├── jest-asymmetric-matchers.ts │ │ │ │ ├── jest-expect.ts │ │ │ │ ├── jest-utils.ts │ │ │ │ └── stringify.ts │ │ │ └── test │ │ │ │ ├── CircularBuffer.ts │ │ │ │ ├── Color.ts │ │ │ │ ├── SocketClient.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared.ts │ │ │ │ └── worker.ts │ │ ├── llrt.d.ts │ │ └── stream.ts │ ├── llrt │ │ ├── hex.rs │ │ ├── mod.rs │ │ ├── util.rs │ │ ├── uuid.rs │ │ └── xml.rs │ ├── mod.rs │ ├── module.rs │ └── require │ │ ├── loader.rs │ │ ├── mod.rs │ │ └── resolver.rs │ ├── runtime_client.rs │ ├── security.rs │ ├── utils │ ├── ctx.rs │ ├── io.rs │ ├── latch.rs │ ├── mod.rs │ └── object.rs │ └── vm.rs ├── llrt_modules ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── modules ├── llrt_abort │ ├── Cargo.toml │ └── src │ │ ├── abort_controller.rs │ │ ├── abort_signal.rs │ │ └── lib.rs ├── llrt_assert │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_buffer │ ├── Cargo.toml │ └── src │ │ ├── array_buffer_view.rs │ │ ├── buffer.rs │ │ └── lib.rs ├── llrt_child_process │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_console │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_crypto │ ├── Cargo.toml │ └── src │ │ ├── crc32.rs │ │ ├── lib.rs │ │ ├── md5_hash.rs │ │ ├── sha_hash.rs │ │ └── subtle │ │ ├── crypto_key.rs │ │ ├── derive.rs │ │ ├── derive_algorithm.rs │ │ ├── digest.rs │ │ ├── encryption.rs │ │ ├── encryption_algorithm.rs │ │ ├── export_key.rs │ │ ├── generate_key.rs │ │ ├── import_key.rs │ │ ├── key_algorithm.rs │ │ ├── mod.rs │ │ ├── sign.rs │ │ ├── sign_algorithm.rs │ │ ├── verify.rs │ │ └── wrapping.rs ├── llrt_dns │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_events │ ├── Cargo.toml │ └── src │ │ ├── custom_event.rs │ │ ├── event.rs │ │ ├── event_target.rs │ │ └── lib.rs ├── llrt_exceptions │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_fs │ ├── Cargo.toml │ └── src │ │ ├── access.rs │ │ ├── chmod.rs │ │ ├── file_handle.rs │ │ ├── lib.rs │ │ ├── mkdir.rs │ │ ├── open.rs │ │ ├── read_dir.rs │ │ ├── read_file.rs │ │ ├── rm.rs │ │ ├── stats.rs │ │ └── write_file.rs ├── llrt_http │ ├── Cargo.toml │ └── src │ │ ├── blob.rs │ │ ├── body.rs │ │ ├── fetch.rs │ │ ├── file.rs │ │ ├── headers.rs │ │ ├── incoming.rs │ │ ├── lib.rs │ │ ├── request.rs │ │ ├── response.rs │ │ └── security.rs ├── llrt_navigator │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_net │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── security.rs │ │ ├── server.rs │ │ └── socket.rs ├── llrt_os │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── network.rs │ │ ├── statistics.rs │ │ ├── unix.rs │ │ └── windows.rs ├── llrt_path │ ├── Cargo.toml │ ├── benches │ │ └── slash_replacement.rs │ └── src │ │ └── lib.rs ├── llrt_perf_hooks │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_process │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_stream │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── readable.rs │ │ └── writable.rs ├── llrt_stream_web │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── queuing_strategy │ │ ├── byte_length.rs │ │ ├── count.rs │ │ └── mod.rs │ │ ├── readable │ │ ├── byob_reader.rs │ │ ├── byte_controller.rs │ │ ├── controller.rs │ │ ├── default_controller.rs │ │ ├── default_reader.rs │ │ ├── iterator.rs │ │ ├── mod.rs │ │ ├── objects.rs │ │ ├── reader.rs │ │ └── stream │ │ │ ├── algorithms.rs │ │ │ ├── mod.rs │ │ │ ├── pipe.rs │ │ │ ├── source.rs │ │ │ └── tee.rs │ │ ├── readable_writable_pair.rs │ │ ├── utils │ │ ├── mod.rs │ │ ├── promise.rs │ │ └── queue.rs │ │ └── writable │ │ ├── default_controller.rs │ │ ├── default_writer.rs │ │ ├── mod.rs │ │ ├── objects.rs │ │ ├── stream │ │ ├── mod.rs │ │ └── sink.rs │ │ └── writer.rs ├── llrt_string_decoder │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── string_decoder.rs ├── llrt_timers │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_tty │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── llrt_url │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── url_class.rs │ │ └── url_search_params.rs ├── llrt_util │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── text_decoder.rs │ │ └── text_encoder.rs └── llrt_zlib │ ├── Cargo.toml │ └── src │ └── lib.rs ├── pack ├── package.json ├── prebuild ├── README.md └── compression.dict ├── rustfmt.toml ├── sdk.cfg ├── shims ├── @aws-crypto │ ├── crc32.js │ ├── crc32c.js │ ├── index.js │ ├── sha1-browser.js │ └── sha256-browser.js ├── @smithy │ ├── abort-controller.js │ ├── split-stream.js │ ├── util-base64.js │ ├── util-hex-encoding.js │ └── util-utf8.js ├── collect-stream-body.js ├── create-read-stream.js ├── mnemonist │ └── lru-cache.js ├── sdk-stream-mixin.js └── stream-collector.js ├── tests ├── e2e │ ├── IntegTestResourcesStack.template.yml │ ├── README.md │ ├── cognito_identity.e2e.test.ts │ ├── dynamodb.e2e.test.ts │ └── s3.e2e.test.ts └── unit │ ├── README.md │ ├── a-web-platform-tests.test.ts │ ├── assert.test.ts │ ├── buffer.test.ts │ ├── child_process.test.ts │ ├── clone.test.ts │ ├── compile.test.ts │ ├── console.test.ts │ ├── crypto.subtle.test.ts │ ├── crypto.test.ts │ ├── dns.test.ts │ ├── encoding.test.ts │ ├── events.test.ts │ ├── exceptions.test.ts │ ├── fetch.test.ts │ ├── fs.test.ts │ ├── http.test.ts │ ├── import.test.ts │ ├── jest-expect.test.ts │ ├── json.test.ts │ ├── navigator.test.ts │ ├── path.unix.test.ts │ ├── path.win.test.ts │ ├── perf_hooks.test.ts │ ├── performance.test.ts │ ├── process.test.ts │ ├── require.test.ts │ ├── socket.test.ts │ ├── string_decoder.test.ts │ ├── timers.test.ts │ ├── util.test.ts │ ├── uuid.test.ts │ ├── web-platform-tests │ ├── common │ │ ├── gc.js │ │ └── subset-tests.js │ ├── encoding │ │ ├── META.yml │ │ ├── api-basics.any.js │ │ ├── api-invalid-label.any.js │ │ ├── api-replacement-encodings.any.js │ │ ├── api-surrogates-utf8.any.js │ │ ├── encodeInto.any.js │ │ ├── encodeInto.any.js.headers │ │ ├── iso-2022-jp-decoder.any.js │ │ ├── replacement-encodings.any.js │ │ ├── resources │ │ │ ├── decoding-helpers.js │ │ │ └── encodings.js │ │ ├── textdecoder-arguments.any.js │ │ ├── textdecoder-byte-order-marks.any.js │ │ ├── textdecoder-copy.any.js │ │ ├── textdecoder-copy.any.js.headers │ │ ├── textdecoder-eof.any.js │ │ ├── textdecoder-fatal-single-byte.any.js │ │ ├── textdecoder-fatal-streaming.any.js │ │ ├── textdecoder-fatal.any.js │ │ ├── textdecoder-ignorebom.any.js │ │ ├── textdecoder-labels.any.js │ │ ├── textdecoder-streaming.any.js │ │ ├── textdecoder-streaming.any.js.headers │ │ ├── textdecoder-utf16-surrogates.any.js │ │ ├── textencoder-constructor-non-utf.any.js │ │ ├── textencoder-utf16-surrogates.any.js │ │ └── unsupported-encodings.any.js │ ├── resources │ │ ├── .htaccess │ │ ├── idlharness.js │ │ └── testharness.js │ ├── streams │ │ ├── piping │ │ │ ├── abort.any.js │ │ │ ├── close-propagation-backward.any.js │ │ │ ├── close-propagation-forward.any.js │ │ │ ├── error-propagation-backward.any.js │ │ │ ├── error-propagation-forward.any.js │ │ │ ├── flow-control.any.js │ │ │ ├── general-addition.any.js │ │ │ ├── general.any.js │ │ │ ├── multiple-propagation.any.js │ │ │ ├── pipe-through.any.js │ │ │ ├── then-interception.any.js │ │ │ ├── throwing-options.any.js │ │ │ └── transform-streams.any.js │ │ ├── readable-byte-streams │ │ │ ├── bad-buffers-and-views.any.js │ │ │ ├── construct-byob-request.any.js │ │ │ ├── enqueue-with-detached-buffer.any.js │ │ │ ├── general.any.js │ │ │ ├── non-transferable-buffers.any.js │ │ │ ├── read-min.any.js │ │ │ ├── respond-after-enqueue.any.js │ │ │ └── tee.any.js │ │ ├── readable-streams │ │ │ ├── async-iterator.any.js │ │ │ ├── bad-strategies.any.js │ │ │ ├── bad-underlying-sources.any.js │ │ │ ├── cancel.any.js │ │ │ ├── constructor.any.js │ │ │ ├── count-queuing-strategy-integration.any.js │ │ │ ├── default-reader.any.js │ │ │ ├── floating-point-total-queue-size.any.js │ │ │ ├── from.any.js │ │ │ ├── garbage-collection.any.js │ │ │ ├── general.any.js │ │ │ ├── owning-type.any.js │ │ │ ├── patched-global.any.js │ │ │ ├── reentrant-strategies.any.js │ │ │ ├── tee.any.js │ │ │ └── templated.any.js │ │ ├── resources │ │ │ ├── recording-streams.js │ │ │ ├── rs-test-templates.js │ │ │ ├── rs-utils.js │ │ │ └── test-utils.js │ │ └── writable-streams │ │ │ ├── aborting.any.js │ │ │ ├── bad-strategies.any.js │ │ │ ├── bad-underlying-sinks.any.js │ │ │ ├── byte-length-queuing-strategy.any.js │ │ │ ├── close.any.js │ │ │ ├── constructor.any.js │ │ │ ├── count-queuing-strategy.any.js │ │ │ ├── error.any.js │ │ │ ├── floating-point-total-queue-size.any.js │ │ │ ├── general.any.js │ │ │ ├── properties.any.js │ │ │ ├── reentrant-strategy.any.js │ │ │ ├── start.any.js │ │ │ └── write.any.js │ ├── url │ │ ├── README.md │ │ ├── resources │ │ │ ├── setters_tests.json │ │ │ └── urltestdata.json │ │ ├── url-constructor.any.js │ │ ├── url-origin.any.js │ │ ├── url-searchparams.any.js │ │ ├── url-setters-stripping.any.js │ │ ├── url-setters.any.js │ │ ├── url-statics-canparse.any.js │ │ ├── url-statics-parse.any.js │ │ ├── url-tojson.any.js │ │ ├── urlsearchparams-append.any.js │ │ ├── urlsearchparams-constructor.any.js │ │ ├── urlsearchparams-delete.any.js │ │ ├── urlsearchparams-foreach.any.js │ │ ├── urlsearchparams-get.any.js │ │ ├── urlsearchparams-getall.any.js │ │ ├── urlsearchparams-has.any.js │ │ ├── urlsearchparams-set.any.js │ │ ├── urlsearchparams-size.any.js │ │ ├── urlsearchparams-sort.any.js │ │ └── urlsearchparams-stringifier.any.js │ └── webidl │ │ └── ecmascript-binding │ │ └── es-exceptions │ │ ├── DOMException-constants.any.js │ │ ├── DOMException-constructor-and-prototype.any.js │ │ ├── DOMException-constructor-behavior.any.js │ │ └── DOMException-custom-bindings.any.js │ ├── xml.test.ts │ └── zlib.test.ts ├── tsconfig.json ├── types ├── .eslintrc.cjs ├── abort.d.ts ├── assert.d.ts ├── buffer.d.ts ├── child_process.d.ts ├── console.d.ts ├── crypto.d.ts ├── dns.d.ts ├── dom-events.d.ts ├── events.d.ts ├── exceptions.d.ts ├── fs.d.ts ├── fs │ └── promises.d.ts ├── globals.d.ts ├── http.d.ts ├── index.d.ts ├── navigator.d.ts ├── net.d.ts ├── os.d.ts ├── package.json ├── path.d.ts ├── perf_hooks.d.ts ├── process.d.ts ├── stream.d.ts ├── stream │ └── web.d.ts ├── string_decoder.d.ts ├── timers.d.ts ├── tsconfig.json ├── tty.d.ts ├── url.d.ts ├── util.d.ts ├── yarn.lock └── zlib.d.ts ├── vitest.config.mjs └── yarn.lock /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-musl] 2 | rustflags = ["-Ctarget-feature=+lse,+crt-static", "-Ctarget-cpu=neoverse-n1"] 3 | linker = "./linker/cc-aarch64-linux-musl" 4 | ar = "./linker/ar" 5 | 6 | [target.x86_64-unknown-linux-musl] 7 | rustflags = ["-Ctarget-feature=+crt-static", "-Ctarget-cpu=haswell"] 8 | linker = "./linker/cc-x86_64-linux-musl" 9 | ar = "./linker/ar" 10 | 11 | [unstable] 12 | build-std = ["core", "compiler_builtins", "alloc", "std", "panic_abort"] 13 | build-std-features = ["panic_immediate_abort"] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | .yarn 4 | .pnp* 5 | .tmp-llrt-aws-sdk 6 | 7 | /lib 8 | /target 9 | /llrt_core/target 10 | /llrt_core/src/bytecode_cache.rs 11 | /example/functions/build 12 | /slask 13 | /VERSION 14 | /bundle 15 | /dist 16 | 17 | *.zip 18 | cdk.out 19 | flamegraph.svg 20 | out.stacks 21 | yarn-error.log 22 | llrt-container-* 23 | bootstrap 24 | 25 | node_modules 26 | !/fixtures/node_modules 27 | 28 | hello.mjs 29 | /refs -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "zstd"] 2 | path = zstd 3 | url = https://github.com/facebook/zstd.git 4 | update = none # Workaround for https://github.com/rust-lang/cargo/issues/4247 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore web-platform-tests to avoid collisions! 2 | /tests/unit/web-platform-tests 3 | **/node_modules 4 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - Fix bug with @aws-crypto/utils package 2 | 3 | Full list of changes: 4 | https://github.com/awslabs/llrt/compare/v0.5.0-beta...v0.5.1-beta 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | 3 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 4 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 5 | opensource-codeofconduct@amazon.com with any additional questions or comments. 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | # Patch doesn't play well with *, so we have to list individual members 4 | # See https://github.com/rust-lang/cargo/issues/14717 5 | members = [ 6 | "libs/llrt_build", 7 | "libs/llrt_compression", 8 | "libs/llrt_context", 9 | "libs/llrt_encoding", 10 | "libs/llrt_json", 11 | "libs/llrt_numbers", 12 | "libs/llrt_test", 13 | "libs/llrt_utils", 14 | "modules/llrt_abort", 15 | "modules/llrt_buffer", 16 | "modules/llrt_child_process", 17 | "modules/llrt_console", 18 | "modules/llrt_crypto", 19 | "modules/llrt_events", 20 | "modules/llrt_exceptions", 21 | "modules/llrt_fs", 22 | "modules/llrt_http", 23 | "modules/llrt_navigator", 24 | "modules/llrt_net", 25 | "modules/llrt_os", 26 | "modules/llrt_path", 27 | "modules/llrt_perf_hooks", 28 | "modules/llrt_process", 29 | "modules/llrt_stream", 30 | "modules/llrt_stream_web", 31 | "modules/llrt_string_decoder", 32 | "modules/llrt_timers", 33 | "modules/llrt_tty", 34 | "modules/llrt_url", 35 | "modules/llrt_util", 36 | "modules/llrt_zlib", 37 | "llrt_modules", 38 | "llrt_core", 39 | "llrt", 40 | ] 41 | 42 | [profile.flame] 43 | inherits = "release" 44 | strip = false 45 | debug = true 46 | 47 | [profile.release] 48 | strip = true 49 | lto = true 50 | codegen-units = 1 51 | opt-level = 3 52 | panic = "abort" 53 | 54 | [profile.test] 55 | opt-level = 3 56 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | LLRT 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /benchmarks/llrt-ddb-put.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray-D-Song/lexe/48f6ba2b8b25437b2c036fc4a7bf31d49395ea4a/benchmarks/llrt-ddb-put.png -------------------------------------------------------------------------------- /benchmarks/node20-ddb-put.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray-D-Song/lexe/48f6ba2b8b25437b2c036fc4a7bf31d49395ea4a/benchmarks/node20-ddb-put.png -------------------------------------------------------------------------------- /benchmarks/size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray-D-Song/lexe/48f6ba2b8b25437b2c036fc4a7bf31d49395ea4a/benchmarks/size.png -------------------------------------------------------------------------------- /fixtures/a.js: -------------------------------------------------------------------------------- 1 | const a = {}; 2 | export default a; 3 | require("./b.js"); 4 | a.done = true; 5 | -------------------------------------------------------------------------------- /fixtures/b.js: -------------------------------------------------------------------------------- 1 | const b = {}; 2 | export default b; 3 | require("./a.js"); 4 | b.done = true; 5 | -------------------------------------------------------------------------------- /fixtures/c.cjs: -------------------------------------------------------------------------------- 1 | const d = require("./d.cjs"); 2 | module.exports = "c"; 3 | -------------------------------------------------------------------------------- /fixtures/d.cjs: -------------------------------------------------------------------------------- 1 | const c = require("./d.cjs"); 2 | module.exports = "d"; 3 | -------------------------------------------------------------------------------- /fixtures/define-property-export.cjs: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, "__esModule", { 2 | value: true, 3 | }); 4 | -------------------------------------------------------------------------------- /fixtures/empty.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray-D-Song/lexe/48f6ba2b8b25437b2c036fc4a7bf31d49395ea4a/fixtures/empty.js -------------------------------------------------------------------------------- /fixtures/empty.lrt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray-D-Song/lexe/48f6ba2b8b25437b2c036fc4a7bf31d49395ea4a/fixtures/empty.lrt -------------------------------------------------------------------------------- /fixtures/export-function.cjs: -------------------------------------------------------------------------------- 1 | module.exports = function exportedFunction() { 2 | return "hello world!"; 3 | }; 4 | -------------------------------------------------------------------------------- /fixtures/fs/readdir/readdir.js: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | 3 | fs.readdir("./", { recursive: true }).then((res) => {}); 4 | -------------------------------------------------------------------------------- /fixtures/fs/readdir/recursive/readdir.js: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | 3 | fs.readdir("./", { recursive: true }).then((res) => {}); 4 | -------------------------------------------------------------------------------- /fixtures/handler.mjs: -------------------------------------------------------------------------------- 1 | //test top level await 2 | await new Promise((res) => setTimeout(res, 0)); 3 | await import("./hello.js"); 4 | 5 | export const handler = async () => ({ 6 | statusCode: 200, 7 | body: "Hello world!", 8 | }); 9 | -------------------------------------------------------------------------------- /fixtures/hello.js: -------------------------------------------------------------------------------- 1 | export const hello = "hello world!"; 2 | console.log(hello); 3 | -------------------------------------------------------------------------------- /fixtures/hello.txt: -------------------------------------------------------------------------------- 1 | hello world! -------------------------------------------------------------------------------- /fixtures/import.cjs: -------------------------------------------------------------------------------- 1 | const c = require("./c.cjs"); 2 | module.exports = { 3 | c, 4 | }; 5 | -------------------------------------------------------------------------------- /fixtures/import.js: -------------------------------------------------------------------------------- 1 | const a = require("a.js"); 2 | export default a; 3 | -------------------------------------------------------------------------------- /fixtures/local.mjs: -------------------------------------------------------------------------------- 1 | import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; 2 | import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb"; 3 | import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; 4 | 5 | import { randomBytes } from "crypto"; 6 | 7 | const uid = () => 8 | String.fromCharCode( 9 | ...randomBytes(10).map((d) => { 10 | return (d > 127 ? 97 : 65) + (d % 25); 11 | }) 12 | ); 13 | 14 | const clientCfg = { 15 | endpoint: "http://localhost:8080/service/", 16 | }; 17 | 18 | const client = new DynamoDBClient(clientCfg); 19 | const docClient = DynamoDBDocumentClient.from(client); 20 | const s3Client = new S3Client(clientCfg); 21 | 22 | export const handler = async (event) => { 23 | const start = Date.now(); 24 | 25 | let id = uid(); 26 | let data = JSON.stringify(event); 27 | 28 | await Promise.all([ 29 | docClient.send( 30 | new PutCommand({ 31 | TableName: process.env.TABLE_NAME, 32 | Item: { 33 | id, 34 | content: data, 35 | }, 36 | }) 37 | ), 38 | s3Client.send( 39 | new PutObjectCommand({ 40 | Body: data, 41 | Bucket: process.env.BUCKET_NAME, 42 | Key: id, 43 | }) 44 | ), 45 | ]); 46 | 47 | const end = Date.now(); 48 | const time = `Duration: ${end - start}ms`; 49 | console.log(time); 50 | return { 51 | statusCode: 200, 52 | body: time, 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/commons/lib/cjs/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isNull = void 0; 4 | var typeUtils_js_1 = require("./typeUtils.js"); 5 | exports.isNull = typeUtils_js_1.isNull; 6 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/commons/lib/cjs/typeUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isNull = void 0; 4 | const isNull = (value) => { 5 | return Object.is(value, null); 6 | }; 7 | exports.isNull = isNull; 8 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/commons/lib/esm/index.js: -------------------------------------------------------------------------------- 1 | export { isNull } from './typeUtils.js'; 2 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/commons/lib/esm/typeUtils.js: -------------------------------------------------------------------------------- 1 | const isNull = (value) => { 2 | return Object.is(value, null); 3 | }; 4 | export { isNull }; 5 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/commons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elem-aws-lambda-powertools/commons", 3 | "main": "./lib/cjs/index.js", 4 | "type": "module", 5 | "exports": { 6 | ".": { 7 | "require": { 8 | "default": "./lib/cjs/index.js" 9 | }, 10 | "import": { 11 | "default": "./lib/esm/index.js" 12 | } 13 | }, 14 | "./typeutils": { 15 | "import": "./lib/esm/typeUtils.js", 16 | "require": "./lib/cjs/typeUtils.js" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/jmespath/lib/cjs/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isNull = void 0; 4 | const typeutils_1 = require("elem-aws-lambda-powertools/commons/typeutils"); 5 | exports.isNull = typeutils_1.isNull; 6 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/jmespath/lib/esm/index.js: -------------------------------------------------------------------------------- 1 | export { isNull } from 'elem-aws-lambda-powertools/commons/typeutils'; 2 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-aws-lambda-powertools/jmespath/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elem-aws-lambda-powertools/jmespath", 3 | "main": "./lib/cjs/index.js", 4 | "type": "module", 5 | "exports": { 6 | ".": { 7 | "require": { 8 | "default": "./lib/cjs/index.js" 9 | }, 10 | "import": { 11 | "default": "./lib/esm/index.js" 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /fixtures/node_modules/elem-debug/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elem-debug", 3 | "main": "./src/index.js", 4 | "browser": "./src/browser.js" 5 | } -------------------------------------------------------------------------------- /fixtures/node_modules/elem-debug/src/browser.js: -------------------------------------------------------------------------------- 1 | exports = module.exports; 2 | 3 | exports.str = "cat"; 4 | exports.cat = function cat() { 5 | return exports.str; 6 | } 7 | 8 | exports.array = []; 9 | exports.length = function length() { 10 | return exports.array.length; 11 | } 12 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-lodash.merge/index.js: -------------------------------------------------------------------------------- 1 | var merge = function test(value) { 2 | return value; 3 | } 4 | 5 | module.exports = merge; 6 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-lodash.merge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elem-lodash.merge" 3 | } -------------------------------------------------------------------------------- /fixtures/node_modules/elem-react-dom/cjs/react-dom-server.edge.development.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (function () { 4 | var ReactDOM = require("elem-react-dom"); 5 | exports.name = "react-dom/server.edge"; 6 | })(); 7 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-react-dom/cjs/react-dom.development.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (function () { 4 | exports.name = "react-dom"; 5 | })(); -------------------------------------------------------------------------------- /fixtures/node_modules/elem-react-dom/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./cjs/react-dom.development.js'); 4 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-react-dom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elem-react-dom", 3 | "main": "index.js", 4 | "exports": { 5 | ".": { 6 | "default": "./index.js" 7 | }, 8 | "./server.edge": { 9 | "default": "./server.edge.js" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /fixtures/node_modules/elem-react-dom/server.edge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./cjs/react-dom-server.edge.development.js'); 4 | -------------------------------------------------------------------------------- /fixtures/node_modules/elem-uuid/dist/commonjs-browser/index.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(exports, "__esModule", { 2 | value: true 3 | }); 4 | 5 | exports.default = require1; 6 | 7 | function require1(hello) { 8 | function helloWorld(world) { 9 | return hello + ", " + world; 10 | } 11 | return helloWorld; 12 | } -------------------------------------------------------------------------------- /fixtures/node_modules/elem-uuid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elem-uuid", 3 | "main": "./dist/index.js", 4 | "exports": { 5 | ".": { 6 | "browser": { 7 | "import": "./dist/esm-browser/index.js", 8 | "require": "./dist/commonjs-browser/index.js" 9 | }, 10 | "default": "./dist/esm-browser/index.js" 11 | } 12 | }, 13 | "module": "./dist/esm-node/index.js" 14 | } -------------------------------------------------------------------------------- /fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true 3 | } 4 | -------------------------------------------------------------------------------- /fixtures/primitive-handler.mjs: -------------------------------------------------------------------------------- 1 | export const handler = () => { 2 | return "hello"; 3 | }; 4 | -------------------------------------------------------------------------------- /fixtures/prop-export.cjs: -------------------------------------------------------------------------------- 1 | module.exports.prop = "a"; 2 | -------------------------------------------------------------------------------- /fixtures/referenced-exports.cjs: -------------------------------------------------------------------------------- 1 | exports = module.exports; 2 | 3 | exports.str = "str"; 4 | exports.cat = function cat() { 5 | return exports.str; 6 | }; 7 | 8 | exports.array = [1]; 9 | exports.length = function length() { 10 | return exports.array.length; 11 | }; 12 | 13 | const fn = require("./export-function.cjs"); 14 | -------------------------------------------------------------------------------- /fixtures/require.mjs: -------------------------------------------------------------------------------- 1 | async function main() { 2 | console.log(1); 3 | await new Promise((res) => setTimeout(res, 5)); 4 | console.log(2); 5 | 6 | setTimeout(() => { 7 | console.log(3); 8 | setTimeout(async () => { 9 | console.log(4); 10 | await new Promise((res) => setTimeout(res, 5)); 11 | console.log(5); 12 | 13 | require("./handler.mjs"); 14 | 15 | console.log(6); 16 | }, 5); 17 | }, 5); 18 | } 19 | 20 | main(); 21 | -------------------------------------------------------------------------------- /fixtures/test903/bar.mjs: -------------------------------------------------------------------------------- 1 | export function bar() { 2 | return "bar"; 3 | } 4 | -------------------------------------------------------------------------------- /fixtures/test903/foo.mjs: -------------------------------------------------------------------------------- 1 | import { bar } from "../bar.mjs"; 2 | console.log(bar()); 3 | -------------------------------------------------------------------------------- /fixtures/test_modules/test-aws-lambda-powertools-jmespath.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | 3 | import * as jmespath1 from "elem-aws-lambda-powertools/jmespath"; 4 | assert.ok(typeof jmespath1.isNull === "function"); 5 | 6 | const jmespath2 = require("elem-aws-lambda-powertools/jmespath"); 7 | assert.ok(typeof jmespath2.isNull === "function"); 8 | -------------------------------------------------------------------------------- /fixtures/test_modules/test-debug.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | 3 | const debug = require("elem-debug"); 4 | 5 | assert.ok(debug.cat() == "cat"); 6 | assert.ok(debug.length() == 0); 7 | -------------------------------------------------------------------------------- /fixtures/test_modules/test-lodash.merge.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | 3 | import merge1 from "elem-lodash.merge"; 4 | assert.ok(typeof merge1 === "function"); 5 | 6 | const merge2 = require("elem-lodash.merge"); 7 | assert.ok(typeof merge2 === "function"); 8 | -------------------------------------------------------------------------------- /fixtures/test_modules/test-react-dom.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | 3 | import * as dom1 from "elem-react-dom/server.edge"; 4 | assert.ok(dom1.name == "react-dom/server.edge"); 5 | 6 | const dom2 = require("elem-react-dom/server.edge"); 7 | assert.ok(dom2.name == "react-dom/server.edge"); 8 | -------------------------------------------------------------------------------- /fixtures/test_modules/test-uuid.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | 3 | var fn = _interopRequireDefault(require("elem-uuid")); 4 | function _interopRequireDefault(e) { 5 | return e && e.__esModule ? e : { default: e }; 6 | } 7 | var greeting = (0, fn.default)("hello"); 8 | 9 | assert.ok(greeting("world") == "hello, world"); 10 | -------------------------------------------------------------------------------- /fixtures/throw.js: -------------------------------------------------------------------------------- 1 | throw 42; 2 | -------------------------------------------------------------------------------- /fixtures/throwing-handler.mjs: -------------------------------------------------------------------------------- 1 | export const handler = async () => { 2 | throw new Error("kaboom"); 3 | }; 4 | -------------------------------------------------------------------------------- /fixtures/throwing-init-handler.mjs: -------------------------------------------------------------------------------- 1 | throw new Error("kaboom"); 2 | 3 | export const handler = async () => {}; 4 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; 2 | import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb"; 3 | 4 | const client = new DynamoDBClient({}); 5 | const docClient = DynamoDBDocumentClient.from(client); 6 | 7 | export const handler = async (event) => { 8 | await docClient.send( 9 | new PutCommand({ 10 | TableName: process.env.TABLE_NAME, 11 | Item: { 12 | id: Math.random().toString(36).substring(2), 13 | content: JSON.stringify(event), 14 | }, 15 | }) 16 | ); 17 | return { 18 | statusCode: 200, 19 | body: "OK", 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /lexe_bin.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | import { spawnSync } from 'child_process'; 5 | import os from 'os'; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | 10 | let platform = os.platform(); 11 | platform = platform === 'win32' ? 'windows' : platform; 12 | const arch = os.arch(); 13 | 14 | if (arch === 'ia32') { 15 | throw new Error('32-bit architecture is not supported.'); 16 | } 17 | 18 | const currentLLRT = `llrt-${platform}-${arch}`; 19 | 20 | (async function () { 21 | try { 22 | const args = process.argv.slice(2); 23 | console.log(`lexe: calling ${currentLLRT}, args: ${args}`); 24 | const llrtPath = path.resolve(__dirname, currentLLRT, 'llrt'); 25 | spawnSync(llrtPath, args, { stdio: 'inherit' }); 26 | process.exit(0); 27 | } catch (e) { 28 | console.error(e); 29 | process.exit(1); 30 | } 31 | })() -------------------------------------------------------------------------------- /libs/llrt_build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_build" 3 | description = "LLRT build helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_build" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | rustc_version = "0.4" 15 | -------------------------------------------------------------------------------- /libs/llrt_build/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn set_nightly_cfg() { 2 | let version_meta = rustc_version::version_meta().unwrap(); 3 | println!("cargo::rustc-check-cfg=cfg(rust_nightly)"); 4 | if version_meta.channel == rustc_version::Channel::Nightly { 5 | println!("cargo:rustc-cfg=rust_nightly"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/llrt_compression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_compression" 3 | description = "LLRT compression helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_compression" 11 | path = "src/lib.rs" 12 | 13 | [features] 14 | default = ["all-c"] 15 | 16 | all-c = ["brotli-c", "flate2-c", "zstd-c"] 17 | all-rust = ["brotli-rust", "flate2-rust", "zstd-rust"] 18 | 19 | brotli-c = ["brotlic"] 20 | brotli-rust = ["brotli"] 21 | 22 | flate2-c = ["flate2/zlib-ng"] 23 | flate2-rust = ["flate2/miniz_oxide"] 24 | 25 | zstd-c = ["zstd"] 26 | zstd-rust = ["zstd"] # No pure rust implementation exists 27 | 28 | [dependencies] 29 | brotlic = { version = "0.8", optional = true } 30 | brotli = { version = "7", optional = true } 31 | flate2 = { version = "1", default-features = false, optional = true } 32 | zstd = { version = "0.13", default-features = false, optional = true } 33 | -------------------------------------------------------------------------------- /libs/llrt_context/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_context" 3 | description = "LLRT context helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [dependencies] 10 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 11 | "futures", 12 | ], default-features = false } 13 | llrt_utils = { version = "0.5.1-beta", path = "../llrt_utils", default-features = false } 14 | tokio = { version = "1", features = ["sync"], default-features = false } 15 | tracing = "0.1" 16 | 17 | [dev-dependencies] 18 | llrt_test = { version = "0.5.1-beta", path = "../llrt_test" } 19 | tokio = { version = "1", features = ["full"] } 20 | 21 | [build-dependencies] 22 | llrt_build = { version = "0.5.1-beta", path = "../llrt_build" } 23 | -------------------------------------------------------------------------------- /libs/llrt_dns_cache/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_dns_cache" 3 | description = "LLRT dns cache helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_dns_cache" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | hyper = { version = "1", features = ["client"] } 16 | hyper-rustls = { version = "0.27", default-features = false, features = [ 17 | "webpki-roots", 18 | "webpki-tokio", 19 | "ring", 20 | ] } 21 | hyper-util = "0.1" 22 | llrt_context = { version = "0.5.1-beta", path = "../llrt_context" } 23 | llrt_utils = { version = "0.5.1-beta", path = "../llrt_utils", default-features = false } 24 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 25 | "macro", 26 | ], default-features = false } 27 | tokio = "1" 28 | tower-service = "0.3.3" 29 | quick_cache = "0.6.12" 30 | -------------------------------------------------------------------------------- /libs/llrt_encoding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_encoding" 3 | description = "LLRT encoding helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [dependencies] 10 | base64-simd = "0.8" 11 | hex-simd = "0.8" 12 | phf = { version = "0.11", features = ["macros"] } 13 | memchr = "2" 14 | 15 | [build-dependencies] 16 | llrt_build = { version = "0.5.1-beta", path = "../llrt_build" } 17 | -------------------------------------------------------------------------------- /libs/llrt_encoding/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | fn main() { 4 | llrt_build::set_nightly_cfg(); 5 | } 6 | -------------------------------------------------------------------------------- /libs/llrt_json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_json" 3 | description = "LLRT json helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_json" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | itoa = "1" 15 | llrt_utils = { version = "0.5.1-beta", path = "../llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 17 | ryu = "1" 18 | simd-json = { version = "0.15", default-features = false, features = [ 19 | "big-int-as-float", 20 | ] } 21 | 22 | [dev-dependencies] 23 | criterion = "0.5" 24 | llrt_test = { version = "0.5.1-beta", path = "../llrt_test" } 25 | tokio = { version = "1", features = ["full"] } 26 | 27 | [build-dependencies] 28 | llrt_build = { version = "0.5.1-beta", path = "../llrt_build" } 29 | 30 | [[bench]] 31 | name = "json" 32 | harness = false 33 | -------------------------------------------------------------------------------- /libs/llrt_json/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | llrt_build::set_nightly_cfg(); 3 | } 4 | -------------------------------------------------------------------------------- /libs/llrt_logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_logging" 3 | description = "LLRT logging helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_logging" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | itoa = "1" 15 | llrt_context = { version = "0.5.1-beta", path = "../llrt_context" } 16 | llrt_encoding = { version = "0.5.1-beta", path = "../llrt_encoding" } 17 | llrt_json = { version = "0.5.1-beta", path = "../llrt_json" } 18 | llrt_numbers = { version = "0.5.1-beta", path = "../llrt_numbers" } 19 | llrt_utils = { version = "0.5.1-beta", path = "../llrt_utils", default-features = false } 20 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 21 | "macro", 22 | ], default-features = false } 23 | ryu = "1" 24 | -------------------------------------------------------------------------------- /libs/llrt_numbers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_numbers" 3 | description = "LLRT numbers helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_numbers" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | itoa = "1" 15 | llrt_utils = { version = "0.5.1-beta", path = "../llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 17 | rand = "0.8" 18 | ryu = "1" 19 | 20 | [dev-dependencies] 21 | criterion = "0.5" 22 | llrt_test = { version = "0.5.1-beta", path = "../llrt_test" } 23 | 24 | [[bench]] 25 | name = "numbers" 26 | harness = false 27 | -------------------------------------------------------------------------------- /libs/llrt_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_test" 3 | description = "LLRT test helpers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_test" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | nanoid = "0.4.0" 15 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 16 | "futures", 17 | "parallel", 18 | "loader", 19 | ], default-features = false } 20 | rand = "0.8.5" 21 | tokio = { version = "1", features = ["full"] } 22 | -------------------------------------------------------------------------------- /libs/llrt_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_utils" 3 | description = "LLRT utilities" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [features] 10 | default = ["all"] 11 | all = ["fs", "bytearray-buffer"] 12 | 13 | fs = ["tokio/fs"] 14 | bytearray-buffer = ["tokio/sync"] 15 | 16 | [dependencies] 17 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 18 | "array-buffer", 19 | "macro", 20 | ], default-features = false } 21 | tokio = { version = "1", features = ["sync"] } 22 | tracing = "0.1" 23 | 24 | [dev-dependencies] 25 | llrt_test = { version = "0.5.1-beta", path = "../llrt_test" } 26 | tokio = { version = "1", features = ["full"] } 27 | 28 | [build-dependencies] 29 | llrt_build = { version = "0.5.1-beta", path = "../llrt_build" } 30 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{CatchResultExt, CaughtError, Ctx, Error, IntoJs, Result, Value}; 4 | 5 | pub trait ErrorExtensions<'js> { 6 | fn into_value(self, ctx: &Ctx<'js>) -> Result>; 7 | } 8 | 9 | impl<'js> ErrorExtensions<'js> for Error { 10 | fn into_value(self, ctx: &Ctx<'js>) -> Result> { 11 | Err::<(), _>(self).catch(ctx).unwrap_err().into_value(ctx) 12 | } 13 | } 14 | 15 | impl<'js> ErrorExtensions<'js> for CaughtError<'js> { 16 | fn into_value(self, ctx: &Ctx<'js>) -> Result> { 17 | Ok(match self { 18 | CaughtError::Error(err) => err.to_string().into_js(ctx)?, 19 | CaughtError::Exception(ex) => ex.into_value(), 20 | CaughtError::Value(val) => val, 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/error_messages.rs: -------------------------------------------------------------------------------- 1 | pub const ERROR_MSG_NOT_ARRAY_BUFFER: &str = "Not an ArrayBuffer"; 2 | pub const ERROR_MSG_ARRAY_BUFFER_DETACHED: &str = "ArrayBuffer is detached"; 3 | pub const ERROR_MSG_BROADCAST_LAGGED: &str = "Lagged too much behind"; 4 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/hash.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{DefaultHasher, Hash, Hasher}; 2 | 3 | #[inline] 4 | pub fn default_hash(v: &T) -> usize { 5 | let mut state = DefaultHasher::default(); 6 | v.hash(&mut state); 7 | state.finish() as usize 8 | } 9 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #[cfg(feature = "bytearray-buffer")] 4 | pub mod bytearray_buffer; 5 | pub mod bytes; 6 | pub mod class; 7 | pub mod clone; 8 | pub mod error; 9 | pub mod error_messages; 10 | #[cfg(feature = "fs")] 11 | pub mod fs; 12 | pub mod hash; 13 | pub mod macros; 14 | pub mod mc_oneshot; 15 | pub mod module; 16 | pub mod object; 17 | pub mod option; 18 | pub mod primordials; 19 | pub mod result; 20 | pub mod reuse_list; 21 | pub mod sysinfo; 22 | pub mod time; 23 | 24 | pub const VERSION: &str = env!("CARGO_PKG_VERSION"); 25 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #[macro_export] 4 | macro_rules! count_members { 5 | () => (0); 6 | ($head:tt $(,$tail:tt)*) => (1 + count_members!($($tail),*)); 7 | } 8 | 9 | #[macro_export] 10 | macro_rules! iterable_enum { 11 | ($name:ident, $($variant:ident),*) => { 12 | impl $name { 13 | const VARIANTS: &'static [$name] = &[$($name::$variant,)*]; 14 | pub fn iter() -> std::slice::Iter<'static, $name> { 15 | Self::VARIANTS.iter() 16 | } 17 | 18 | #[allow(dead_code)] 19 | fn _ensure_all_variants(s: Self) { 20 | match s { 21 | $($name::$variant => {},)* 22 | } 23 | } 24 | } 25 | }; 26 | } 27 | 28 | #[macro_export] 29 | macro_rules! str_enum { 30 | ($name:ident, $($variant:ident => $str:expr),*) => { 31 | impl $name { 32 | pub fn as_str(&self) -> &'static str { 33 | match self { 34 | $($name::$variant => $str,)* 35 | } 36 | } 37 | } 38 | 39 | impl AsRef for $name { 40 | fn as_ref(&self) -> &str { 41 | self.as_str() 42 | } 43 | } 44 | 45 | impl TryFrom<&str> for $name { 46 | type Error = String; 47 | fn try_from(s: &str) -> std::result::Result { 48 | match s { 49 | $($str => Ok($name::$variant),)* 50 | _ => Err(["'", s, "' not available"].concat()) 51 | } 52 | } 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/module.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{ 4 | module::{Exports, ModuleDef}, 5 | Ctx, Object, Result, Value, 6 | }; 7 | 8 | pub struct ModuleInfo { 9 | pub name: &'static str, 10 | pub module: T, 11 | } 12 | 13 | pub fn export_default<'js, F>(ctx: &Ctx<'js>, exports: &Exports<'js>, f: F) -> Result<()> 14 | where 15 | F: FnOnce(&Object<'js>) -> Result<()>, 16 | { 17 | let default = Object::new(ctx.clone())?; 18 | f(&default)?; 19 | 20 | for name in default.keys::() { 21 | let name = name?; 22 | let value: Value = default.get(&name)?; 23 | exports.export(name, value)?; 24 | } 25 | 26 | exports.export("default", default)?; 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/sysinfo.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "macos")] 2 | pub const PLATFORM: &str = "darwin"; 3 | #[cfg(target_os = "windows")] 4 | pub const PLATFORM: &str = "win32"; 5 | #[cfg(not(any(target_os = "macos", target_os = "windows")))] 6 | pub const PLATFORM: &str = std::env::consts::OS; 7 | 8 | #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] 9 | pub const ARCH: &str = "x64"; 10 | #[cfg(target_arch = "aarch64")] 11 | pub const ARCH: &str = "arm64"; 12 | #[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))] 13 | pub const ARCH: &str = std::env::consts::ARCH; 14 | -------------------------------------------------------------------------------- /libs/llrt_utils/src/time.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | sync::atomic::{AtomicU64, Ordering}, 3 | time::SystemTime, 4 | }; 5 | 6 | static TIME_ORIGIN: AtomicU64 = AtomicU64::new(0); 7 | 8 | /// Get the current time in nanoseconds. 9 | /// 10 | /// # Safety 11 | /// - Good until the year 2554 12 | /// - Always use a checked substraction since this can return 0 13 | pub fn now_nanos() -> u64 { 14 | SystemTime::now() 15 | .duration_since(std::time::UNIX_EPOCH) 16 | .unwrap_or_default() 17 | .as_nanos() as u64 18 | } 19 | 20 | /// Get the current time in millis. 21 | /// 22 | /// # Safety 23 | /// - Good until the year 2554 24 | /// - Always use a checked substraction since this can return 0 25 | pub fn now_millis() -> i64 { 26 | SystemTime::now() 27 | .duration_since(std::time::UNIX_EPOCH) 28 | .unwrap_or_default() 29 | .as_millis() as i64 30 | } 31 | 32 | /// Get the origin time in nanoseconds. 33 | /// 34 | /// # Safety 35 | /// - Good until the year 2554 36 | /// - Always use a checked substraction since this can return 0 37 | pub fn origin_nanos() -> u64 { 38 | TIME_ORIGIN.load(Ordering::Relaxed) 39 | } 40 | 41 | // For accuracy reasons, this function should be executed when the vm is initialized 42 | pub fn init() { 43 | if TIME_ORIGIN.load(Ordering::Relaxed) == 0 { 44 | let time_origin = now_nanos(); 45 | TIME_ORIGIN.store(time_origin, Ordering::Relaxed) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /linker/ar: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | zig ar "$@" -------------------------------------------------------------------------------- /linker/cc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | target=$1 5 | shift 6 | 7 | new_array=() 8 | for value in "$@" 9 | do 10 | [[ $value != *self-contained/*crt* ]] && [[ $value != *lgcc_s* ]] && [[ $value != *lc_nonshared* ]] && [[ $value != *latomic* ]] && new_array+=($value) 11 | done 12 | 13 | zig cc "${new_array[@]}" -target $target -------------------------------------------------------------------------------- /linker/cc-aarch64-linux-gnu: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cc aarch64-linux-gnu "$@" -------------------------------------------------------------------------------- /linker/cc-aarch64-linux-musl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cc aarch64-linux-musl "$@" -------------------------------------------------------------------------------- /linker/cc-x86_64-linux-gnu: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cc x86_64-linux-gnu "$@" -------------------------------------------------------------------------------- /linker/cc-x86_64-linux-musl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cc x86_64-linux-musl "$@" -------------------------------------------------------------------------------- /linker/cxx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | target=$1 5 | shift 6 | 7 | new_array=() 8 | for value in "$@" 9 | do 10 | [[ $value != *self-contained/*crt* ]] && [[ $value != *lgcc_s* ]] && new_array+=($value) 11 | done 12 | 13 | zig c++ "${new_array[@]}" -target $target -------------------------------------------------------------------------------- /linker/cxx-aarch64-linux-gnu: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cxx aarch64-linux-gnu "$@" -------------------------------------------------------------------------------- /linker/cxx-aarch64-linux-musl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cxx aarch64-linux-musl "$@" -------------------------------------------------------------------------------- /linker/cxx-x86_64-linux-gnu: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cxx x86_64-linux-gnu "$@" -------------------------------------------------------------------------------- /linker/cxx-x86_64-linux-musl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $(dirname $(realpath "$0"))/cxx x86_64-linux-musl "$@" -------------------------------------------------------------------------------- /linker/ranlib: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | zig ranlib "$@" -------------------------------------------------------------------------------- /llrt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt" 3 | version = "0.5.1-beta" 4 | edition = "2021" 5 | license-file = "LICENSE" 6 | 7 | [features] 8 | default = ["macro"] 9 | macro = ["llrt_core/macro"] 10 | lambda = ["llrt_core/lambda"] 11 | no-sdk = ["llrt_core/no-sdk"] 12 | uncompressed = ["llrt_core/uncompressed"] 13 | bindgen = ["llrt_core/bindgen"] 14 | 15 | [dependencies] 16 | chrono = { version = "0.4", default-features = false, features = ["std"] } 17 | constcat = "0.6" 18 | crossterm = { version = "0.28" } 19 | libsui = "0.7.0" 20 | llrt_core = { path = "../llrt_core" } 21 | tokio = { version = "1", features = ["full"] } 22 | tracing = { version = "0.1", features = ["log"] } 23 | tracing-core = "0.1" 24 | 25 | [target.'cfg(not(target_os = "windows"))'.dependencies] 26 | snmalloc-rs = { version = "0.3", features = ["lto"] } 27 | 28 | [dev-dependencies] 29 | llrt_test = { version = "0.5.1-beta", path = "../libs/llrt_test" } 30 | 31 | [[bin]] 32 | name = "llrt" 33 | path = "src/main.rs" 34 | -------------------------------------------------------------------------------- /llrt/src/core.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub use self::core::*; 4 | 5 | #[allow(clippy::module_inception)] 6 | mod core { 7 | pub use llrt_core::bytecode; 8 | #[cfg(not(feature = "lambda"))] 9 | pub use llrt_core::compiler; 10 | pub use llrt_core::environment; 11 | pub use llrt_core::libs; 12 | pub use llrt_core::modules; 13 | pub use llrt_core::utils; 14 | pub use llrt_core::vm; 15 | } 16 | pub use llrt_core::VERSION; 17 | 18 | // rquickjs components 19 | #[allow(unused_imports)] 20 | pub use llrt_core::{ 21 | async_with, atom::PredefinedAtom, context::EvalOptions, function::Rest, runtime_client, 22 | AsyncContext, CatchResultExt, Ctx, Error, Object, Promise, 23 | }; 24 | -------------------------------------------------------------------------------- /llrt/src/lib.rs: -------------------------------------------------------------------------------- 1 | // export build for test 2 | pub mod build { 3 | include!("build.rs"); 4 | } 5 | 6 | // re-export core functionality 7 | pub use crate::build::*; -------------------------------------------------------------------------------- /llrt_core/src/bytecode.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | pub const BYTECODE_VERSION: &str = "lrt01"; 5 | pub const BYTECODE_COMPRESSED: u8 = b'c'; 6 | pub const BYTECODE_UNCOMPRESSED: u8 = b'u'; 7 | 8 | macro_rules! define_extension { 9 | ($base:ident, $file:ident, $ext:expr) => { 10 | #[allow(dead_code)] 11 | pub const $base: &str = $ext; 12 | #[allow(dead_code)] 13 | pub const $file: &str = concat!(".", $ext); 14 | }; 15 | } 16 | 17 | define_extension!(BYTECODE_EXT, BYTECODE_FILE_EXT, "lrt"); 18 | pub const SIGNATURE_LENGTH: usize = BYTECODE_VERSION.len() + 1; 19 | 20 | #[allow(dead_code)] 21 | pub fn add_bytecode_header(bytes: Vec, file_size: Option) -> Vec { 22 | let mut compressed_bytes = Vec::with_capacity(bytes.len()); 23 | compressed_bytes.extend_from_slice(BYTECODE_VERSION.as_bytes()); 24 | if let Some(file_size) = file_size { 25 | compressed_bytes.push(BYTECODE_COMPRESSED); 26 | compressed_bytes.extend_from_slice(&file_size.to_le_bytes()); 27 | } else { 28 | compressed_bytes.push(BYTECODE_UNCOMPRESSED) 29 | } 30 | compressed_bytes.extend_from_slice(&bytes); 31 | compressed_bytes 32 | } 33 | -------------------------------------------------------------------------------- /llrt_core/src/environment.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //network 5 | pub const ENV_LLRT_NET_ALLOW: &str = "LLRT_NET_ALLOW"; 6 | pub const ENV_LLRT_NET_DENY: &str = "LLRT_NET_DENY"; 7 | pub const ENV_LLRT_NET_POOL_IDLE_TIMEOUT: &str = "LLRT_NET_POOL_IDLE_TIMEOUT"; 8 | pub const ENV_LLRT_HTTP_VERSION: &str = "LLRT_HTTP_VERSION"; 9 | pub const ENV_LLRT_TLS_VERSION: &str = "LLRT_TLS_VERSION"; 10 | pub const ENV_LLRT_EXTRA_CA_CERTS: &str = "LLRT_EXTRA_CA_CERTS"; 11 | 12 | //log 13 | pub const ENV_LLRT_LOG: &str = "LLRT_LOG"; 14 | 15 | //module 16 | pub const ENV_LLRT_PLATFORM: &str = "LLRT_PLATFORM"; 17 | 18 | //vm 19 | pub const ENV_LLRT_GC_THRESHOLD_MB: &str = "LLRT_GC_THRESHOLD_MB"; 20 | -------------------------------------------------------------------------------- /llrt_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #![allow(clippy::new_without_default)] 5 | #![allow(clippy::inherent_to_string)] 6 | #![cfg_attr(rust_nightly, feature(portable_simd))] 7 | 8 | pub mod bytecode; 9 | pub mod compiler; 10 | mod compiler_common; 11 | pub mod environment; 12 | mod http; 13 | pub mod libs; 14 | mod module_builder; 15 | pub mod modules; 16 | pub mod runtime_client; 17 | mod security; 18 | pub mod utils; 19 | pub mod vm; 20 | 21 | pub use llrt_modules::VERSION; 22 | 23 | pub use rquickjs::*; 24 | -------------------------------------------------------------------------------- /llrt_core/src/libs.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub use self::libs::*; 4 | 5 | #[allow(clippy::module_inception)] 6 | mod libs { 7 | pub use llrt_context as context; 8 | pub use llrt_encoding as encoding; 9 | pub use llrt_json as json; 10 | pub use llrt_logging as logging; 11 | pub use llrt_numbers as numbers; 12 | pub use llrt_utils as utils; 13 | } 14 | -------------------------------------------------------------------------------- /llrt_core/src/modules/js/@llrt/test/shared.ts: -------------------------------------------------------------------------------- 1 | export type SocketReqMsg = 2 | | ReadyReqMsg 3 | | NextReqMsg 4 | | ModuleReqMsg 5 | | EndReqMsg 6 | | StartReqMsg 7 | | CompletedReqMsg 8 | | ErrorReqMsg; 9 | 10 | export type ReadyReqMsg = { 11 | type: "ready"; 12 | workerId: number; 13 | }; 14 | 15 | export type ErrorReqMsg = { 16 | type: "error"; 17 | error: any; 18 | ended: number; 19 | started: number; 20 | }; 21 | 22 | export type ModuleReqMsg = { 23 | type: "module"; 24 | testCount: number; 25 | skipCount: number; 26 | onlyCount: number; 27 | }; 28 | 29 | export type CompletedReqMsg = { 30 | type: "completed"; 31 | }; 32 | 33 | export type NextReqMsg = { 34 | type: "next"; 35 | }; 36 | 37 | export type EndReqMsg = { 38 | type: "end"; 39 | ended: number; 40 | started: number; 41 | isSuite: boolean; 42 | }; 43 | 44 | export type StartReqMsg = { 45 | type: "start"; 46 | desc: string; 47 | isSuite: boolean; 48 | started: number; 49 | timeout?: number; 50 | }; 51 | 52 | export type SocketResponseMap = { 53 | next: { 54 | nextFile: string | null; 55 | }; 56 | }; 57 | 58 | export type SocketRes = T extends { 59 | type: keyof SocketResponseMap; 60 | } 61 | ? SocketResponseMap[T["type"]] 62 | : null; 63 | -------------------------------------------------------------------------------- /llrt_core/src/modules/js/llrt.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | declare namespace NodeJS { 5 | import assert from "assert"; 6 | interface Global { 7 | assert: typeof assert; 8 | } 9 | } 10 | 11 | interface Headers { 12 | entries(): any; 13 | } 14 | 15 | declare var assert: NodeJS.Global["assert"]; 16 | declare var _require: NodeJS.Global["require"]; 17 | declare var __lambdaSetRequestId: (id?: string) => void; 18 | 19 | declare var __handler: (data: any) => Promise; 20 | 21 | declare module "hex" { 22 | export const decode: (text: string) => Uint8Array; 23 | export const encode: (bytes: Uint8Array) => string; 24 | } 25 | 26 | declare module "xml" { 27 | export class XMLParser { 28 | constructor(options?: { 29 | ignoreAttributes?: boolean; 30 | attributeNamePrefix?: string; 31 | textNodeName?: string; 32 | attributeValueProcessor?: ( 33 | attrName: string, 34 | attrValue: string, 35 | jpath: string 36 | ) => unknown; 37 | tagValueProcessor?: ( 38 | attrName: string, 39 | attrValue: string, 40 | jpath: string, 41 | hasAttributes: boolean 42 | ) => unknown; 43 | }); 44 | parse(xml: string): any; 45 | } 46 | 47 | export class XmlText { 48 | constructor(private value: string) {} 49 | toString(): string; 50 | } 51 | 52 | export class XmlNode { 53 | readonly children: any[]; 54 | static of(name: string, childText?: string, withName?: string): XmlNode; 55 | constructor(name: string, children?: any[]); 56 | withName(name: string): XmlNode; 57 | addAttribute(name: string, value: any): XmlNode; 58 | addChildNode(child: any): XmlNode; 59 | removeAttribute(name: string): XmlNode; 60 | toString(): string; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /llrt_core/src/modules/llrt/hex.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{ 4 | module::{Declarations, Exports, ModuleDef}, 5 | prelude::Func, 6 | Ctx, Result, Value, 7 | }; 8 | 9 | use crate::libs::{ 10 | encoding::{bytes_from_hex, bytes_to_hex_string}, 11 | utils::{ 12 | bytes::{bytes_to_typed_array, ObjectBytes}, 13 | module::{export_default, ModuleInfo}, 14 | result::ResultExt, 15 | }, 16 | }; 17 | 18 | pub struct LlrtHexModule; 19 | 20 | impl LlrtHexModule { 21 | pub fn encode<'js>(ctx: Ctx<'js>, bytes: ObjectBytes<'js>) -> Result { 22 | Ok(bytes_to_hex_string(bytes.as_bytes(&ctx)?)) 23 | } 24 | 25 | pub fn decode(ctx: Ctx, encoded: String) -> Result { 26 | let bytes = bytes_from_hex(encoded.as_bytes()) 27 | .or_throw_msg(&ctx, "Cannot decode unrecognized sequence")?; 28 | 29 | bytes_to_typed_array(ctx, &bytes) 30 | } 31 | } 32 | 33 | impl ModuleDef for LlrtHexModule { 34 | fn declare(declare: &Declarations) -> Result<()> { 35 | declare.declare(stringify!(encode))?; 36 | declare.declare(stringify!(decode))?; 37 | declare.declare("default")?; 38 | Ok(()) 39 | } 40 | 41 | fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> { 42 | export_default(ctx, exports, |default| { 43 | default.set(stringify!(encode), Func::from(Self::encode))?; 44 | default.set(stringify!(decode), Func::from(Self::decode))?; 45 | Ok(()) 46 | })?; 47 | 48 | Ok(()) 49 | } 50 | } 51 | 52 | impl From for ModuleInfo { 53 | fn from(val: LlrtHexModule) -> Self { 54 | ModuleInfo { 55 | name: "llrt:hex", 56 | module: val, 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /llrt_core/src/modules/llrt/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub mod hex; 4 | pub mod util; 5 | pub mod uuid; 6 | pub mod xml; 7 | -------------------------------------------------------------------------------- /llrt_core/src/modules/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #[cfg(not(feature = "lambda"))] 4 | pub use llrt_modules::console; 5 | pub use llrt_modules::{ 6 | abort, assert, buffer, child_process, crypto, dns, events, exceptions, fs, http, navigator, 7 | net, os, path, perf_hooks, process, stream_web, string_decoder, timers, tty, url, util, zlib, 8 | }; 9 | 10 | #[cfg(feature = "lambda")] 11 | pub mod console; 12 | pub mod llrt; 13 | pub mod module; 14 | pub mod require; 15 | -------------------------------------------------------------------------------- /llrt_core/src/modules/require/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::env; 4 | 5 | use once_cell::sync::Lazy; 6 | 7 | use crate::environment; 8 | 9 | pub mod loader; 10 | pub mod resolver; 11 | 12 | // added when .cjs files are imported 13 | pub const CJS_IMPORT_PREFIX: &str = "__cjs:"; 14 | // added to force CJS imports in loader 15 | pub const CJS_LOADER_PREFIX: &str = "__cjsm:"; 16 | 17 | pub static LLRT_PLATFORM: Lazy = Lazy::new(|| { 18 | env::var(environment::ENV_LLRT_PLATFORM) 19 | .ok() 20 | .filter(|platform| platform == "node") 21 | .unwrap_or_else(|| "browser".to_string()) 22 | }); 23 | -------------------------------------------------------------------------------- /llrt_core/src/security.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::{env, result::Result as StdResult}; 4 | 5 | use hyper::{http::uri::InvalidUri, Uri}; 6 | 7 | use crate::environment::{ENV_LLRT_NET_ALLOW, ENV_LLRT_NET_DENY}; 8 | use crate::modules::{http, net}; 9 | 10 | pub fn init() -> StdResult<(), Box> { 11 | if let Ok(env_value) = env::var(ENV_LLRT_NET_ALLOW) { 12 | let allow_list = build_access_list(env_value); 13 | http::set_allow_list(build_http_access_list(&allow_list)?); 14 | net::set_allow_list(allow_list); 15 | } 16 | 17 | if let Ok(env_value) = env::var(ENV_LLRT_NET_DENY) { 18 | let deny_list = build_access_list(env_value); 19 | http::set_deny_list(build_http_access_list(&deny_list)?); 20 | net::set_deny_list(deny_list); 21 | } 22 | 23 | Ok(()) 24 | } 25 | 26 | fn build_http_access_list(list: &[String]) -> StdResult, InvalidUri> { 27 | list.iter() 28 | .flat_map(|entry| { 29 | let with_http = ["http://", entry].concat(); 30 | let with_https = ["https://", entry].concat(); 31 | vec![with_http, with_https] 32 | }) 33 | .map(|url| url.parse()) 34 | .collect() 35 | } 36 | 37 | fn build_access_list(env_value: String) -> Vec { 38 | env_value 39 | .split_whitespace() 40 | .map(|entry| { 41 | //remove protocol 42 | if let Some(idx) = entry.find("://") { 43 | entry[idx + 3..].to_string() 44 | } else { 45 | entry.to_string() 46 | } 47 | }) 48 | .collect() 49 | } 50 | -------------------------------------------------------------------------------- /llrt_core/src/utils/ctx.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{Ctx, Result}; 4 | 5 | pub trait CtxExt { 6 | fn get_script_or_module_name(&self) -> Result; 7 | } 8 | 9 | impl CtxExt for Ctx<'_> { 10 | fn get_script_or_module_name(&self) -> Result { 11 | if let Some(name) = self.script_or_module_name(0) { 12 | name.to_string() 13 | } else { 14 | Ok(String::from(".")) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /llrt_core/src/utils/io.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::bytecode::BYTECODE_FILE_EXT; 5 | 6 | macro_rules! define_supported_extensions { 7 | // Accepts a list of supported extensions and a single additional constant extension 8 | ($constant_ext:ident, $($ext:literal),*) => { 9 | // Define the array of extensions as a constant 10 | pub const SUPPORTED_EXTENSIONS: &[&str] = &[$($ext),*, $constant_ext]; 11 | 12 | pub const JS_EXTENSIONS: &[&str] = &[$($ext),*]; 13 | 14 | // Define the function `is_supported_ext` using a match statement 15 | pub fn is_supported_ext(ext: &str) -> bool { 16 | matches!(ext, $($ext)|* | $constant_ext) 17 | } 18 | }; 19 | } 20 | 21 | define_supported_extensions!(BYTECODE_FILE_EXT, ".js", ".mjs", ".cjs"); 22 | -------------------------------------------------------------------------------- /llrt_core/src/utils/latch.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | use tokio::sync::Notify; 6 | 7 | #[derive(Default)] 8 | pub struct Latch { 9 | count: AtomicUsize, 10 | notify: Notify, 11 | } 12 | 13 | impl Latch { 14 | pub fn increment(&self) { 15 | self.count.fetch_add(1, Ordering::Relaxed); 16 | } 17 | 18 | pub fn decrement(&self) { 19 | let previous = self.count.fetch_sub(1, Ordering::Relaxed); 20 | if previous == 1 { 21 | self.notify.notify_waiters(); 22 | } 23 | } 24 | 25 | pub async fn wait(&self) { 26 | if self.count.load(Ordering::Relaxed) > 0 { 27 | self.notify.notified().await; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /llrt_core/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub mod ctx; 4 | pub mod io; 5 | pub mod latch; 6 | pub mod object; 7 | -------------------------------------------------------------------------------- /llrt_core/src/utils/object.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::collections::{BTreeMap, HashMap}; 4 | 5 | use rquickjs::{Array, Coerced, Ctx, FromJs, Object, Result, Value}; 6 | 7 | #[allow(dead_code)] 8 | pub fn array_to_hash_map<'js>( 9 | ctx: &Ctx<'js>, 10 | array: Array<'js>, 11 | ) -> Result> { 12 | let value = object_from_entries(ctx, array)?; 13 | let value = value.into_value(); 14 | HashMap::from_js(ctx, value) 15 | } 16 | 17 | pub fn array_to_btree_map<'js>( 18 | ctx: &Ctx<'js>, 19 | array: Array<'js>, 20 | ) -> Result>> { 21 | let value = object_from_entries(ctx, array)?; 22 | let value = value.into_value(); 23 | BTreeMap::from_js(ctx, value) 24 | } 25 | 26 | pub fn object_from_entries<'js>(ctx: &Ctx<'js>, array: Array<'js>) -> Result> { 27 | let obj = Object::new(ctx.clone())?; 28 | for value in array.into_iter().flatten() { 29 | if let Some(entry) = value.as_array() { 30 | if let Ok(key) = entry.get::(0) { 31 | if let Ok(value) = entry.get::(1) { 32 | let _ = obj.set(key, value); //ignore result of failed 33 | } 34 | } 35 | } 36 | } 37 | Ok(obj) 38 | } 39 | -------------------------------------------------------------------------------- /llrt_modules/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub use self::modules::*; 4 | 5 | mod modules { 6 | #[cfg(feature = "abort")] 7 | pub use llrt_abort as abort; 8 | #[cfg(feature = "assert")] 9 | pub use llrt_assert as assert; 10 | #[cfg(feature = "buffer")] 11 | pub use llrt_buffer as buffer; 12 | #[cfg(feature = "child-process")] 13 | pub use llrt_child_process as child_process; 14 | #[cfg(feature = "console")] 15 | pub use llrt_console as console; 16 | #[cfg(feature = "crypto")] 17 | pub use llrt_crypto as crypto; 18 | #[cfg(feature = "dns")] 19 | pub use llrt_dns as dns; 20 | #[cfg(feature = "events")] 21 | pub use llrt_events as events; 22 | #[cfg(feature = "exceptions")] 23 | pub use llrt_exceptions as exceptions; 24 | #[cfg(feature = "fs")] 25 | pub use llrt_fs as fs; 26 | #[cfg(feature = "http")] 27 | pub use llrt_http as http; 28 | #[cfg(feature = "navigator")] 29 | pub use llrt_navigator as navigator; 30 | #[cfg(feature = "net")] 31 | pub use llrt_net as net; 32 | #[cfg(feature = "os")] 33 | pub use llrt_os as os; 34 | #[cfg(feature = "path")] 35 | pub use llrt_path as path; 36 | #[cfg(feature = "perf-hooks")] 37 | pub use llrt_perf_hooks as perf_hooks; 38 | #[cfg(feature = "process")] 39 | pub use llrt_process as process; 40 | #[cfg(feature = "stream-web")] 41 | pub use llrt_stream_web as stream_web; 42 | #[cfg(feature = "string-decoder")] 43 | pub use llrt_string_decoder as string_decoder; 44 | #[cfg(feature = "timers")] 45 | pub use llrt_timers as timers; 46 | #[cfg(feature = "tty")] 47 | pub use llrt_tty as tty; 48 | #[cfg(feature = "url")] 49 | pub use llrt_url as url; 50 | #[cfg(feature = "util")] 51 | pub use llrt_util as util; 52 | #[cfg(feature = "zlib")] 53 | pub use llrt_zlib as zlib; 54 | } 55 | 56 | pub const VERSION: &str = env!("CARGO_PKG_VERSION"); 57 | -------------------------------------------------------------------------------- /modules/llrt_abort/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_abort" 3 | description = "LLRT Module abort" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_abort" 11 | path = "src/lib.rs" 12 | 13 | [features] 14 | default = ["sleep-timers"] 15 | 16 | sleep-timers = ["llrt_timers"] 17 | sleep-tokio = ["tokio"] 18 | 19 | [dependencies] 20 | llrt_exceptions = { version = "0.5.1-beta", path = "../llrt_exceptions" } 21 | llrt_events = { version = "0.5.1-beta", path = "../llrt_events" } 22 | llrt_timers = { version = "0.5.1-beta", path = "../llrt_timers", optional = true } 23 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 24 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 25 | "macro", 26 | ], default-features = false } 27 | tokio = { version = "1", features = ["time"], optional = true } 28 | 29 | [dev-dependencies] 30 | llrt_test = { path = "../../libs/llrt_test" } 31 | tokio = { version = "1", features = ["full"] } 32 | -------------------------------------------------------------------------------- /modules/llrt_abort/src/abort_controller.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{ 4 | prelude::{Opt, This}, 5 | Class, Ctx, JsLifetime, Result, Value, 6 | }; 7 | 8 | use super::AbortSignal; 9 | 10 | #[rquickjs::class] 11 | #[derive(rquickjs::class::Trace)] 12 | pub struct AbortController<'js> { 13 | signal: Class<'js, AbortSignal<'js>>, 14 | } 15 | 16 | unsafe impl<'js> JsLifetime<'js> for AbortController<'js> { 17 | type Changed<'to> = AbortController<'to>; 18 | } 19 | 20 | #[rquickjs::methods] 21 | impl<'js> AbortController<'js> { 22 | #[qjs(constructor)] 23 | pub fn new(ctx: Ctx<'js>) -> Result { 24 | let signal = AbortSignal::new(); 25 | 26 | let abort_controller = Self { 27 | signal: Class::instance(ctx, signal)?, 28 | }; 29 | Ok(abort_controller) 30 | } 31 | 32 | #[qjs(get)] 33 | pub fn signal(&self) -> Class<'js, AbortSignal<'js>> { 34 | self.signal.clone() 35 | } 36 | 37 | pub fn abort( 38 | ctx: Ctx<'js>, 39 | this: This>, 40 | reason: Opt>, 41 | ) -> Result<()> { 42 | let instance = this.0.borrow(); 43 | let signal = instance.signal.clone(); 44 | let mut signal_borrow = signal.borrow_mut(); 45 | if signal_borrow.aborted { 46 | //only once 47 | return Ok(()); 48 | } 49 | signal_borrow.set_reason(reason); 50 | drop(signal_borrow); 51 | AbortSignal::send_aborted(This(signal), ctx)?; 52 | 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /modules/llrt_abort/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #![allow(clippy::new_without_default)] 4 | use llrt_events::Emitter; 5 | use rquickjs::{Class, Ctx, Result}; 6 | 7 | pub use self::{abort_controller::AbortController, abort_signal::AbortSignal}; 8 | 9 | mod abort_controller; 10 | mod abort_signal; 11 | 12 | pub fn init(ctx: &Ctx<'_>) -> Result<()> { 13 | let globals = ctx.globals(); 14 | 15 | Class::::define(&globals)?; 16 | Class::::define(&globals)?; 17 | 18 | AbortSignal::add_event_emitter_prototype(ctx)?; 19 | AbortSignal::add_event_target_prototype(ctx)?; 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /modules/llrt_assert/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_assert" 3 | description = "LLRT Module assert" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_assert" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 15 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 16 | 17 | [dev-dependencies] 18 | llrt_test = { path = "../../libs/llrt_test" } 19 | tokio = { version = "1", features = ["full"] } 20 | -------------------------------------------------------------------------------- /modules/llrt_buffer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_buffer" 3 | description = "LLRT Module buffer" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_buffer" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_encoding = { version = "0.5.1-beta", path = "../../libs/llrt_encoding" } 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 17 | "macro", 18 | ], default-features = false } 19 | 20 | [dev-dependencies] 21 | llrt_test = { path = "../../libs/llrt_test" } 22 | tokio = { version = "1", features = ["full"] } 23 | -------------------------------------------------------------------------------- /modules/llrt_buffer/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | pub use self::array_buffer_view::*; 4 | pub use self::buffer::*; 5 | 6 | mod array_buffer_view; 7 | mod buffer; 8 | -------------------------------------------------------------------------------- /modules/llrt_child_process/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_child_process" 3 | description = "LLRT Module child_process" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_child_process" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | itoa = "1" 15 | llrt_buffer = { version = "0.5.1-beta", path = "../llrt_buffer" } 16 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 17 | llrt_events = { version = "0.5.1-beta", path = "../llrt_events" } 18 | llrt_stream = { version = "0.5.1-beta", path = "../llrt_stream" } 19 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 20 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 21 | tokio = { version = "1", features = ["process"] } 22 | 23 | [target.'cfg(unix)'.dependencies] 24 | libc = "0.2" 25 | 26 | [dev-dependencies] 27 | llrt_test = { path = "../../libs/llrt_test" } 28 | tokio = { version = "1", features = ["full"] } 29 | -------------------------------------------------------------------------------- /modules/llrt_console/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_console" 3 | description = "LLRT Module console" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_console" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_logging = { version = "0.5.1-beta", path = "../../libs/llrt_logging" } 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 17 | "macro", 18 | ], default-features = false } 19 | -------------------------------------------------------------------------------- /modules/llrt_crypto/src/crc32.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::hash::Hasher; 4 | 5 | use crc32c::Crc32cHasher; 6 | use llrt_utils::bytes::ObjectBytes; 7 | use rquickjs::{prelude::This, Class, Ctx, Result}; 8 | 9 | #[rquickjs::class] 10 | #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)] 11 | pub struct Crc32c { 12 | #[qjs(skip_trace)] 13 | hasher: crc32c::Crc32cHasher, 14 | } 15 | 16 | #[rquickjs::methods] 17 | impl Crc32c { 18 | #[qjs(constructor)] 19 | fn new() -> Self { 20 | Self { 21 | hasher: Crc32cHasher::default(), 22 | } 23 | } 24 | 25 | #[qjs(rename = "digest")] 26 | fn crc32c_digest(&self) -> u64 { 27 | self.hasher.finish() 28 | } 29 | 30 | #[qjs(rename = "update")] 31 | fn crc32c_update<'js>( 32 | this: This>, 33 | ctx: Ctx<'js>, 34 | bytes: ObjectBytes<'js>, 35 | ) -> Result> { 36 | this.0.borrow_mut().hasher.write(bytes.as_bytes(&ctx)?); 37 | Ok(this.0) 38 | } 39 | } 40 | 41 | #[rquickjs::class] 42 | #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)] 43 | pub struct Crc32 { 44 | #[qjs(skip_trace)] 45 | hasher: crc32fast::Hasher, 46 | } 47 | 48 | #[rquickjs::methods] 49 | impl Crc32 { 50 | #[qjs(constructor)] 51 | fn new() -> Self { 52 | Self { 53 | hasher: crc32fast::Hasher::new(), 54 | } 55 | } 56 | 57 | #[qjs(rename = "digest")] 58 | fn crc32_digest(&self) -> u64 { 59 | self.hasher.finish() 60 | } 61 | 62 | #[qjs(rename = "update")] 63 | fn crc32_update<'js>( 64 | this: This>, 65 | ctx: Ctx<'js>, 66 | bytes: ObjectBytes<'js>, 67 | ) -> Result> { 68 | this.0.borrow_mut().hasher.write(bytes.as_bytes(&ctx)?); 69 | Ok(this.0) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /modules/llrt_crypto/src/md5_hash.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use md5::{Digest as Md5Digest, Md5 as MdHasher}; 4 | 5 | use llrt_utils::bytes::{bytes_to_typed_array, ObjectBytes}; 6 | use rquickjs::{function::Opt, prelude::This, Class, Ctx, Result, Value}; 7 | 8 | use super::encoded_bytes; 9 | 10 | #[rquickjs::class] 11 | #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)] 12 | pub struct Md5 { 13 | #[qjs(skip_trace)] 14 | hasher: MdHasher, 15 | } 16 | 17 | #[rquickjs::methods] 18 | impl Md5 { 19 | #[qjs(constructor)] 20 | fn new() -> Self { 21 | Self { 22 | hasher: MdHasher::new(), 23 | } 24 | } 25 | 26 | #[qjs(rename = "digest")] 27 | fn md5_digest<'js>(&self, ctx: Ctx<'js>, encoding: Opt) -> Result> { 28 | let digest = self.hasher.clone().finalize(); 29 | let bytes: &[u8] = digest.as_ref(); 30 | 31 | match encoding.0 { 32 | Some(encoding) => encoded_bytes(ctx, bytes, &encoding), 33 | None => bytes_to_typed_array(ctx, bytes), 34 | } 35 | } 36 | 37 | #[qjs(rename = "update")] 38 | fn md5_update<'js>( 39 | this: This>, 40 | ctx: Ctx<'js>, 41 | bytes: ObjectBytes<'js>, 42 | ) -> Result> { 43 | this.0.borrow_mut().hasher.update(bytes.as_bytes(&ctx)?); 44 | Ok(this.0) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/llrt_crypto/src/subtle/digest.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use llrt_utils::{bytes::ObjectBytes, object::ObjectExt, result::ResultExt}; 4 | use ring::digest::Context; 5 | use rquickjs::{ArrayBuffer, Ctx, Result, Value}; 6 | 7 | use crate::sha_hash::ShaAlgorithm; 8 | 9 | pub async fn subtle_digest<'js>( 10 | ctx: Ctx<'js>, 11 | algorithm: Value<'js>, 12 | data: ObjectBytes<'js>, 13 | ) -> Result> { 14 | let algorithm = if let Some(algorithm) = algorithm.as_string() { 15 | algorithm.to_string().or_throw(&ctx)? 16 | } else { 17 | algorithm.get_required::<_, String>("name", "algorithm")? 18 | }; 19 | 20 | let sha_algorithm = ShaAlgorithm::try_from(algorithm.as_str()).or_throw(&ctx)?; 21 | let bytes = digest(&sha_algorithm, data.as_bytes(&ctx)?); 22 | ArrayBuffer::new(ctx, bytes) 23 | } 24 | 25 | pub fn digest(sha_algorithm: &ShaAlgorithm, data: &[u8]) -> Vec { 26 | let hash = sha_algorithm.digest_algorithm(); 27 | let mut context = Context::new(hash); 28 | context.update(data); 29 | let digest = context.finish(); 30 | 31 | digest.as_ref().to_vec() 32 | } 33 | -------------------------------------------------------------------------------- /modules/llrt_crypto/src/subtle/import_key.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use llrt_utils::{bytes::ObjectBytes, object::ObjectExt}; 4 | use rquickjs::{Array, Class, Ctx, FromJs, Result, Value}; 5 | 6 | use crate::subtle::CryptoKey; 7 | 8 | use super::{ 9 | crypto_key::KeyKind, 10 | key_algorithm::{ 11 | KeyAlgorithm, KeyAlgorithmMode, KeyAlgorithmWithUsages, KeyFormat, KeyFormatData, 12 | }, 13 | }; 14 | 15 | #[allow(dead_code)] 16 | pub async fn subtle_import_key<'js>( 17 | ctx: Ctx<'js>, 18 | format: KeyFormat, 19 | key_data: Value<'js>, 20 | algorithm: Value<'js>, 21 | extractable: bool, 22 | key_usages: Array<'js>, 23 | ) -> Result> { 24 | let format = match format { 25 | KeyFormat::Raw => KeyFormatData::Raw(ObjectBytes::from_js(&ctx, key_data)?), 26 | KeyFormat::Pkcs8 => KeyFormatData::Pkcs8(ObjectBytes::from_js(&ctx, key_data)?), 27 | KeyFormat::Spki => KeyFormatData::Spki(ObjectBytes::from_js(&ctx, key_data)?), 28 | KeyFormat::Jwk => KeyFormatData::Jwk(key_data.into_object_or_throw(&ctx, "keyData")?), 29 | }; 30 | 31 | import_key(ctx, format, algorithm, extractable, key_usages) 32 | } 33 | 34 | pub fn import_key<'js>( 35 | ctx: Ctx<'js>, 36 | format: KeyFormatData<'js>, 37 | algorithm: Value<'js>, 38 | extractable: bool, 39 | key_usages: Array<'js>, 40 | ) -> Result> { 41 | let mut kind = KeyKind::Public; 42 | let mut data = Vec::new(); 43 | 44 | let KeyAlgorithmWithUsages { 45 | name, 46 | algorithm: key_algorithm, 47 | public_usages, 48 | private_usages, 49 | } = KeyAlgorithm::from_js( 50 | &ctx, 51 | KeyAlgorithmMode::Import { 52 | kind: &mut kind, 53 | data: &mut data, 54 | format, 55 | }, 56 | algorithm, 57 | key_usages, 58 | )?; 59 | 60 | let usages = match kind { 61 | KeyKind::Public | KeyKind::Secret => public_usages, 62 | KeyKind::Private => private_usages, 63 | }; 64 | 65 | Class::instance( 66 | ctx, 67 | CryptoKey::new(kind, name, extractable, key_algorithm, usages, data), 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /modules/llrt_crypto/src/subtle/sign_algorithm.rs: -------------------------------------------------------------------------------- 1 | use llrt_utils::{object::ObjectExt, result::ResultExt}; 2 | use rquickjs::{Ctx, FromJs, Result, Value}; 3 | 4 | use crate::sha_hash::ShaAlgorithm; 5 | 6 | use super::{ 7 | algorithm_not_supported_error, key_algorithm::extract_sha_hash, to_name_and_maybe_object, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub enum SigningAlgorithm { 12 | Ecdsa { hash: ShaAlgorithm }, 13 | Ed25519, 14 | RsaPss { salt_length: u32 }, 15 | RsassaPkcs1v15, 16 | Hmac, 17 | } 18 | 19 | impl<'js> FromJs<'js> for SigningAlgorithm { 20 | fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result { 21 | let (name, obj) = to_name_and_maybe_object(ctx, value)?; 22 | 23 | let algorithm = match name.as_str() { 24 | "Ed25519" => SigningAlgorithm::Ed25519, 25 | "HMAC" => SigningAlgorithm::Hmac, 26 | "RSASSA-PKCS1-v1_5" => SigningAlgorithm::RsassaPkcs1v15, 27 | "ECDSA" => { 28 | let obj = obj.or_throw(ctx)?; 29 | let hash = extract_sha_hash(ctx, &obj)?; 30 | SigningAlgorithm::Ecdsa { hash } 31 | }, 32 | "RSA-PSS" => { 33 | let salt_length = obj.or_throw(ctx)?.get_required("saltLength", "algorithm")?; 34 | 35 | SigningAlgorithm::RsaPss { salt_length } 36 | }, 37 | _ => return algorithm_not_supported_error(ctx), 38 | }; 39 | Ok(algorithm) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /modules/llrt_dns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_dns" 3 | description = "LLRT Module dns" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_dns" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 16 | llrt_dns_cache = { version = "0.5.1-beta", path = "../../libs/llrt_dns_cache" } 17 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 18 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 19 | "macro", 20 | ], default-features = false } 21 | -------------------------------------------------------------------------------- /modules/llrt_dns/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use llrt_context::CtxExtension; 4 | use llrt_dns_cache::lookup_host; 5 | use llrt_utils::{ 6 | module::{export_default, ModuleInfo}, 7 | result::ResultExt, 8 | }; 9 | use rquickjs::{ 10 | module::{Declarations, Exports, ModuleDef}, 11 | prelude::{Func, Rest}, 12 | Ctx, Error, Exception, Function, IntoJs, Null, Result, Value, 13 | }; 14 | 15 | fn lookup<'js>(ctx: Ctx<'js>, hostname: String, args: Rest>) -> Result<()> { 16 | let mut args_iter = args.0.into_iter().rev(); 17 | let cb: Function = args_iter 18 | .next() 19 | .and_then(|v| v.into_function()) 20 | .or_throw_msg(&ctx, "Callback parameter is not a function")?; 21 | 22 | ctx.clone().spawn_exit(async move { 23 | match lookup_host(&hostname, args_iter.next()).await { 24 | Ok((address, family)) => { 25 | () = cb.call((Null.into_js(&ctx), address, family))?; 26 | Ok::<_, Error>(()) 27 | }, 28 | Err(err) => { 29 | () = cb.call((Exception::from_message(ctx, &err.to_string()),))?; 30 | Ok(()) 31 | }, 32 | } 33 | })?; 34 | Ok(()) 35 | } 36 | 37 | pub struct DnsModule; 38 | 39 | impl ModuleDef for DnsModule { 40 | fn declare(declare: &Declarations) -> Result<()> { 41 | declare.declare("lookup")?; 42 | 43 | declare.declare("default")?; 44 | Ok(()) 45 | } 46 | 47 | fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> { 48 | export_default(ctx, exports, |default| { 49 | default.set("lookup", Func::from(lookup))?; 50 | Ok(()) 51 | })?; 52 | 53 | Ok(()) 54 | } 55 | } 56 | 57 | impl From for ModuleInfo { 58 | fn from(val: DnsModule) -> Self { 59 | ModuleInfo { 60 | name: "dns", 61 | module: val, 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /modules/llrt_events/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_events" 3 | description = "LLRT Module events" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_events" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 15 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 16 | "macro", 17 | ], default-features = false } 18 | tracing = "0.1" 19 | -------------------------------------------------------------------------------- /modules/llrt_events/src/custom_event.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{prelude::Opt, Ctx, IntoJs, Null, Result, Value}; 4 | 5 | use llrt_utils::object::ObjectExt; 6 | 7 | #[rquickjs::class] 8 | #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)] 9 | pub struct CustomEvent<'js> { 10 | event_type: String, 11 | detail: Option>, 12 | } 13 | 14 | #[rquickjs::methods] 15 | impl<'js> CustomEvent<'js> { 16 | #[qjs(constructor)] 17 | pub fn new(event_type: String, options: Opt>) -> Result { 18 | let mut detail = None; 19 | if let Some(options) = options.0 { 20 | if let Some(opt) = options.get_optional("detail")? { 21 | detail = opt; 22 | } 23 | } 24 | Ok(Self { event_type, detail }) 25 | } 26 | 27 | #[qjs(get)] 28 | pub fn detail(&self, ctx: Ctx<'js>) -> Result> { 29 | if let Some(detail) = &self.detail { 30 | return Ok(detail.clone()); 31 | } 32 | Null.into_js(&ctx) 33 | } 34 | 35 | #[qjs(get, rename = "type")] 36 | pub fn event_type(&self) -> String { 37 | self.event_type.clone() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/llrt_events/src/event.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{prelude::Opt, Result, Value}; 4 | 5 | use llrt_utils::object::ObjectExt; 6 | 7 | #[rquickjs::class] 8 | #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)] 9 | pub struct Event { 10 | event_type: String, 11 | bubbles: bool, 12 | cancelable: bool, 13 | composed: bool, 14 | } 15 | 16 | #[rquickjs::methods] 17 | impl Event { 18 | #[qjs(constructor)] 19 | pub fn new(event_type: String, options: Opt>) -> Result { 20 | let mut bubbles = false; 21 | let mut cancelable = false; 22 | let mut composed = false; 23 | if let Some(options) = options.0 { 24 | if let Some(opt) = options.get_optional("bubbles")? { 25 | bubbles = opt; 26 | } 27 | if let Some(opt) = options.get_optional("cancelable")? { 28 | cancelable = opt; 29 | } 30 | if let Some(opt) = options.get_optional("composed")? { 31 | composed = opt; 32 | } 33 | } 34 | Ok(Self { 35 | event_type, 36 | bubbles, 37 | cancelable, 38 | composed, 39 | }) 40 | } 41 | 42 | #[qjs(get)] 43 | pub fn bubbles(&self) -> bool { 44 | self.bubbles 45 | } 46 | 47 | #[qjs(get)] 48 | pub fn cancelable(&self) -> bool { 49 | self.cancelable 50 | } 51 | 52 | #[qjs(get)] 53 | pub fn composed(&self) -> bool { 54 | self.composed 55 | } 56 | 57 | #[qjs(get, rename = "type")] 58 | pub fn event_type(&self) -> String { 59 | self.event_type.clone() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /modules/llrt_events/src/event_target.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::sync::{Arc, RwLock}; 4 | 5 | use rquickjs::{ 6 | class::{Trace, Tracer}, 7 | JsLifetime, 8 | }; 9 | 10 | use super::{Emitter, EventList, Events}; 11 | 12 | #[rquickjs::class] 13 | #[derive(Clone)] 14 | pub struct EventTarget<'js> { 15 | pub events: Events<'js>, 16 | } 17 | 18 | unsafe impl<'js> JsLifetime<'js> for EventTarget<'js> { 19 | type Changed<'to> = EventTarget<'to>; 20 | } 21 | 22 | impl<'js> Emitter<'js> for EventTarget<'js> { 23 | fn get_event_list(&self) -> Arc>> { 24 | self.events.clone() 25 | } 26 | } 27 | 28 | impl<'js> Trace<'js> for EventTarget<'js> { 29 | fn trace<'a>(&self, tracer: Tracer<'a, 'js>) { 30 | self.trace_event_emitter(tracer); 31 | } 32 | } 33 | 34 | #[rquickjs::methods] 35 | impl<'js> EventTarget<'js> { 36 | #[qjs(constructor)] 37 | pub fn new() -> Self { 38 | Self { 39 | #[allow(clippy::arc_with_non_send_sync)] 40 | events: Arc::new(RwLock::new(Vec::new())), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /modules/llrt_exceptions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_exceptions" 3 | description = "LLRT Module exception" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_exceptions" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 15 | "macro", 16 | ], default-features = false } 17 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 18 | -------------------------------------------------------------------------------- /modules/llrt_fs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_fs" 3 | description = "LLRT Module fs" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_fs" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | either = "1" 16 | llrt_buffer = { version = "0.5.1-beta", path = "../llrt_buffer" } 17 | llrt_encoding = { version = "0.5.1-beta", path = "../../libs/llrt_encoding" } 18 | llrt_path = { version = "0.5.1-beta", path = "../llrt_path" } 19 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", features = [ 20 | "fs", 21 | ], default-features = false } 22 | ring = "0.17" 23 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 24 | "either", 25 | "macro", 26 | "futures", 27 | ], default-features = false } 28 | tokio = { version = "1", features = ["rt", "fs", "io-util"] } 29 | 30 | [dev-dependencies] 31 | llrt_test = { path = "../../libs/llrt_test" } 32 | tokio = { version = "1", features = ["full"] } 33 | -------------------------------------------------------------------------------- /modules/llrt_fs/src/chmod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(unix)] 2 | use llrt_utils::result::ResultExt; 3 | use rquickjs::{Ctx, Result}; 4 | #[cfg(unix)] 5 | use std::os::unix::prelude::PermissionsExt; 6 | 7 | #[cfg(unix)] 8 | pub(crate) fn chmod_error(path: &str) -> String { 9 | ["Can't set permissions of \"", path, "\""].concat() 10 | } 11 | 12 | pub(crate) async fn set_mode(ctx: Ctx<'_>, path: &str, mode: u32) -> Result<()> { 13 | #[cfg(unix)] 14 | { 15 | tokio::fs::set_permissions(path, PermissionsExt::from_mode(mode)) 16 | .await 17 | .or_throw_msg(&ctx, &chmod_error(path))?; 18 | } 19 | #[cfg(not(unix))] 20 | { 21 | _ = ctx; 22 | _ = path; 23 | _ = mode; 24 | } 25 | Ok(()) 26 | } 27 | 28 | pub(crate) fn set_mode_sync(ctx: Ctx<'_>, path: &str, mode: u32) -> Result<()> { 29 | #[cfg(unix)] 30 | { 31 | std::fs::set_permissions(path, PermissionsExt::from_mode(mode)) 32 | .or_throw_msg(&ctx, &chmod_error(path))?; 33 | } 34 | #[cfg(not(unix))] 35 | { 36 | _ = ctx; 37 | _ = path; 38 | _ = mode; 39 | } 40 | Ok(()) 41 | } 42 | 43 | pub async fn chmod(ctx: Ctx<'_>, path: String, mode: u32) -> Result<()> { 44 | set_mode(ctx, &path, mode).await 45 | } 46 | 47 | pub fn chmod_sync(ctx: Ctx<'_>, path: String, mode: u32) -> Result<()> { 48 | set_mode_sync(ctx, &path, mode) 49 | } 50 | -------------------------------------------------------------------------------- /modules/llrt_fs/src/read_file.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use either::Either; 4 | use llrt_buffer::Buffer; 5 | use llrt_utils::{object::ObjectExt, result::ResultExt}; 6 | use rquickjs::{function::Opt, Ctx, Error, FromJs, IntoJs, Result, Value}; 7 | use tokio::fs; 8 | 9 | pub async fn read_file( 10 | ctx: Ctx<'_>, 11 | path: String, 12 | options: Opt>, 13 | ) -> Result> { 14 | let bytes = fs::read(&path) 15 | .await 16 | .or_throw_msg(&ctx, &["Can't read \"", &path, "\""].concat())?; 17 | 18 | handle_read_file_bytes(&ctx, options, bytes) 19 | } 20 | 21 | pub fn read_file_sync( 22 | ctx: Ctx<'_>, 23 | path: String, 24 | options: Opt>, 25 | ) -> Result> { 26 | let bytes = 27 | std::fs::read(&path).or_throw_msg(&ctx, &["Can't read \"", &path, "\""].concat())?; 28 | 29 | handle_read_file_bytes(&ctx, options, bytes) 30 | } 31 | 32 | pub(crate) fn handle_read_file_bytes<'a>( 33 | ctx: &Ctx<'a>, 34 | options: Opt>, 35 | bytes: Vec, 36 | ) -> Result> { 37 | let buffer = Buffer(bytes); 38 | 39 | if let Some(options) = options.0 { 40 | let encoding = match options { 41 | Either::Left(encoding) => Some(encoding), 42 | Either::Right(options) => options.encoding, 43 | }; 44 | 45 | if let Some(encoding) = encoding { 46 | return buffer 47 | .to_string(ctx, &encoding) 48 | .and_then(|s| s.into_js(ctx)); 49 | } 50 | } 51 | 52 | buffer.into_js(ctx) 53 | } 54 | 55 | pub(crate) struct ReadFileOptions { 56 | pub encoding: Option, 57 | } 58 | 59 | impl<'js> FromJs<'js> for ReadFileOptions { 60 | fn from_js(_ctx: &Ctx<'js>, value: Value<'js>) -> Result { 61 | let ty_name = value.type_name(); 62 | let obj = value 63 | .as_object() 64 | .ok_or(Error::new_from_js(ty_name, "Object"))?; 65 | 66 | let encoding = obj.get_optional::<_, String>("encoding")?; 67 | 68 | Ok(Self { encoding }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /modules/llrt_fs/src/write_file.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use llrt_utils::{bytes::ObjectBytes, result::ResultExt}; 5 | use rquickjs::{Ctx, Result, Value}; 6 | use tokio::fs; 7 | use tokio::io::AsyncWriteExt; 8 | 9 | pub async fn write_file<'js>(ctx: Ctx<'js>, path: String, data: Value<'js>) -> Result<()> { 10 | let write_error_message = &["Can't write file \"", &path, "\""].concat(); 11 | 12 | let mut file = fs::File::create(&path) 13 | .await 14 | .or_throw_msg(&ctx, write_error_message)?; 15 | 16 | let bytes = ObjectBytes::from(&ctx, &data)?; 17 | file.write_all(bytes.as_bytes(&ctx)?) 18 | .await 19 | .or_throw_msg(&ctx, write_error_message)?; 20 | file.flush().await.or_throw_msg(&ctx, write_error_message)?; 21 | 22 | Ok(()) 23 | } 24 | 25 | pub fn write_file_sync<'js>(ctx: Ctx<'js>, path: String, bytes: ObjectBytes<'js>) -> Result<()> { 26 | std::fs::write(&path, bytes.as_bytes(&ctx)?) 27 | .or_throw_msg(&ctx, &["Can't write \"{}\"", &path].concat())?; 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /modules/llrt_http/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_http" 3 | description = "LLRT Module http" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_http" 12 | path = "src/lib.rs" 13 | 14 | [features] 15 | default = ["http1", "http2", "compression-c"] 16 | 17 | http1 = ["hyper/http1", "hyper-rustls/http1"] 18 | http2 = ["hyper/http2", "hyper-rustls/http2"] 19 | 20 | compression-c = ["llrt_compression/all-c"] 21 | compression-rust = ["llrt_compression/all-rust"] 22 | 23 | [dependencies] 24 | bytes = "1" 25 | either = "1" 26 | http-body-util = "0.1" 27 | hyper = { version = "1", features = ["client"] } 28 | hyper-rustls = { version = "0.27", default-features = false, features = [ 29 | "webpki-roots", 30 | "webpki-tokio", 31 | "ring", 32 | ] } 33 | hyper-util = "0.1" 34 | itoa = "1" 35 | llrt_abort = { version = "0.5.1-beta", path = "../llrt_abort" } 36 | llrt_compression = { version = "0.5.1-beta", path = "../../libs/llrt_compression", default-features = false } 37 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 38 | llrt_dns_cache = { version = "0.5.1-beta", path = "../../libs/llrt_dns_cache" } 39 | llrt_encoding = { version = "0.5.1-beta", path = "../../libs/llrt_encoding" } 40 | llrt_json = { version = "0.5.1-beta", path = "../../libs/llrt_json" } 41 | llrt_url = { version = "0.5.1-beta", path = "../llrt_url" } 42 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 43 | pin-project-lite = "0.2" 44 | once_cell = "1" 45 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 46 | "either", 47 | ], default-features = false } 48 | rustls = { version = "0.23", default-features = false, features = [ 49 | "tls12", 50 | "ring", 51 | ] } 52 | ryu = "1" 53 | tokio = "1" 54 | tracing = "0.1" 55 | webpki-roots = "0.26" 56 | tower-service = "0.3" 57 | quick_cache = "0.6" 58 | 59 | [dev-dependencies] 60 | llrt_compression = { version = "0.5.1-beta", path = "../../libs/llrt_compression" } 61 | llrt_test = { path = "../../libs/llrt_test" } 62 | tokio = { version = "1", features = ["full"] } 63 | wiremock = "0.6" 64 | -------------------------------------------------------------------------------- /modules/llrt_http/src/security.rs: -------------------------------------------------------------------------------- 1 | use hyper::Uri; 2 | use rquickjs::{Ctx, Error, Exception, Result}; 3 | use std::sync::OnceLock; 4 | 5 | static HTTP_ALLOW_LIST: OnceLock> = OnceLock::new(); 6 | 7 | static HTTP_DENY_LIST: OnceLock> = OnceLock::new(); 8 | 9 | pub fn set_allow_list(values: Vec) { 10 | _ = HTTP_ALLOW_LIST.set(values); 11 | } 12 | 13 | pub fn get_allow_list() -> Option<&'static Vec> { 14 | HTTP_ALLOW_LIST.get() 15 | } 16 | 17 | pub fn set_deny_list(values: Vec) { 18 | _ = HTTP_DENY_LIST.set(values); 19 | } 20 | 21 | pub fn get_deny_list() -> Option<&'static Vec> { 22 | HTTP_DENY_LIST.get() 23 | } 24 | 25 | pub fn ensure_url_access(ctx: &Ctx<'_>, uri: &Uri) -> Result<()> { 26 | if let Some(allow_list) = HTTP_ALLOW_LIST.get() { 27 | if !url_match(allow_list, uri) { 28 | return Err(url_restricted_error(ctx, "URL not allowed", uri)); 29 | } 30 | } 31 | 32 | if let Some(deny_list) = HTTP_DENY_LIST.get() { 33 | if url_match(deny_list, uri) { 34 | return Err(url_restricted_error(ctx, "URL denied", uri)); 35 | } 36 | } 37 | 38 | Ok(()) 39 | } 40 | 41 | fn url_restricted_error(ctx: &Ctx<'_>, message: &str, uri: &Uri) -> Error { 42 | let uri_host = uri.host().unwrap_or_default(); 43 | let mut message_string = String::with_capacity(message.len() + 100); 44 | message_string.push_str(message); 45 | message_string.push_str(": "); 46 | message_string.push_str(uri_host); 47 | if let Some(port) = uri.port_u16() { 48 | message_string.push(':'); 49 | message_string.push_str(itoa::Buffer::new().format(port)) 50 | } 51 | 52 | Exception::throw_message(ctx, &message_string) 53 | } 54 | 55 | fn url_match(list: &[Uri], uri: &Uri) -> bool { 56 | let host = uri.host().unwrap_or_default(); 57 | let port = uri.port_u16().unwrap_or(80); 58 | list.iter().any(|entry| { 59 | host.ends_with(entry.host().unwrap_or_default()) && entry.port_u16().unwrap_or(80) == port 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /modules/llrt_navigator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_navigator" 3 | description = "LLRT Module navigator" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_navigator" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 16 | -------------------------------------------------------------------------------- /modules/llrt_navigator/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use rquickjs::{Ctx, Object, Result}; 4 | 5 | fn get_user_agent() -> &'static str { 6 | concat!("llrt ", env!("CARGO_PKG_VERSION")) 7 | } 8 | 9 | pub fn init(ctx: &Ctx<'_>) -> Result<()> { 10 | let globals = ctx.globals(); 11 | 12 | let navigator = Object::new(ctx.clone())?; 13 | 14 | navigator.set("userAgent", get_user_agent())?; 15 | 16 | globals.set("navigator", navigator)?; 17 | 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /modules/llrt_net/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_net" 3 | description = "LLRT Module net" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_net" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | itoa = "1" 16 | llrt_buffer = { version = "0.5.1-beta", path = "../llrt_buffer" } 17 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 18 | llrt_events = { version = "0.5.1-beta", path = "../llrt_events" } 19 | llrt_stream = { version = "0.5.1-beta", path = "../llrt_stream" } 20 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 21 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 22 | tokio = { version = "1", features = ["net"] } 23 | tracing = "0.1" 24 | 25 | [dev-dependencies] 26 | llrt_test = { path = "../../libs/llrt_test" } 27 | rand = "0.8" 28 | tokio = { version = "1", features = ["full"] } 29 | -------------------------------------------------------------------------------- /modules/llrt_net/src/security.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::sync::OnceLock; 4 | 5 | use rquickjs::{Ctx, Exception, Result}; 6 | 7 | static NET_ALLOW_LIST: OnceLock> = OnceLock::new(); 8 | 9 | static NET_DENY_LIST: OnceLock> = OnceLock::new(); 10 | 11 | pub fn set_allow_list(values: Vec) { 12 | _ = NET_ALLOW_LIST.set(values); 13 | } 14 | 15 | pub fn get_allow_list() -> Option<&'static Vec> { 16 | NET_ALLOW_LIST.get() 17 | } 18 | 19 | pub fn set_deny_list(values: Vec) { 20 | _ = NET_DENY_LIST.set(values); 21 | } 22 | 23 | pub fn get_deny_list() -> Option<&'static Vec> { 24 | NET_DENY_LIST.get() 25 | } 26 | 27 | pub fn ensure_access(ctx: &Ctx<'_>, resource: &String) -> Result<()> { 28 | if let Some(allow_list) = NET_ALLOW_LIST.get() { 29 | if !allow_list.contains(resource) { 30 | return Err(Exception::throw_message( 31 | ctx, 32 | &["Network address not allowed: ", resource].concat(), 33 | )); 34 | } 35 | } 36 | 37 | if let Some(deny_list) = NET_DENY_LIST.get() { 38 | if deny_list.contains(resource) { 39 | return Err(Exception::throw_message( 40 | ctx, 41 | &["Network address denied: ", resource].concat(), 42 | )); 43 | } 44 | } 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /modules/llrt_os/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_os" 3 | description = "LLRT Module OS" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [features] 11 | default = ["network", "statistics"] 12 | 13 | network = ["sysinfo/network"] 14 | statistics = [] 15 | 16 | [dependencies] 17 | home = "0.5" 18 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 19 | num_cpus = "1" 20 | once_cell = "1" 21 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 22 | sysinfo = { version = "0.34", features = ["system"], default-features = false } 23 | 24 | [target.'cfg(unix)'.dependencies] 25 | libc = "0.2" 26 | users = { version = "0.11", features = ["cache"], default-features = false } 27 | 28 | [target.'cfg(windows)'.dependencies] 29 | whoami = { version = "1", default-features = false } 30 | windows-registry = "0.5" 31 | windows-result = "0.3" 32 | windows-version = "0.1" 33 | 34 | [dev-dependencies] 35 | llrt_test = { path = "../../libs/llrt_test" } 36 | tokio = { version = "1", features = ["full"] } 37 | -------------------------------------------------------------------------------- /modules/llrt_os/src/statistics.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::sync::{Arc, Mutex}; 4 | 5 | use once_cell::sync::Lazy; 6 | use rquickjs::{Ctx, Object, Result}; 7 | use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; 8 | 9 | static SYSTEM: Lazy>> = Lazy::new(|| { 10 | Arc::new(Mutex::new(System::new_with_specifics( 11 | RefreshKind::nothing() 12 | .with_cpu(CpuRefreshKind::nothing().with_cpu_usage().with_frequency()) 13 | .with_memory(MemoryRefreshKind::nothing().with_ram()), 14 | ))) 15 | }); 16 | 17 | pub fn get_cpus(ctx: Ctx<'_>) -> Result> { 18 | let mut vec: Vec = Vec::new(); 19 | let system = SYSTEM.lock().unwrap(); 20 | 21 | for cpu in system.cpus() { 22 | let obj = Object::new(ctx.clone())?; 23 | obj.set("model", cpu.brand())?; 24 | obj.set("speed", cpu.frequency())?; 25 | 26 | // The number of milliseconds spent by the CPU in each mode cannot be obtained at this time. 27 | let times = Object::new(ctx.clone())?; 28 | times.set("user", 0)?; 29 | times.set("nice", 0)?; 30 | times.set("sys", 0)?; 31 | times.set("idle", 0)?; 32 | times.set("irq", 0)?; 33 | obj.set("times", times)?; 34 | 35 | vec.push(obj); 36 | } 37 | Ok(vec) 38 | } 39 | 40 | pub fn get_free_mem() -> u64 { 41 | let mut system = SYSTEM.lock().unwrap(); 42 | 43 | system.refresh_memory_specifics(MemoryRefreshKind::nothing().with_ram()); 44 | system.free_memory() 45 | } 46 | 47 | pub fn get_total_mem() -> u64 { 48 | let mut system = SYSTEM.lock().unwrap(); 49 | 50 | system.refresh_memory_specifics(MemoryRefreshKind::nothing().with_ram()); 51 | system.total_memory() 52 | } 53 | -------------------------------------------------------------------------------- /modules/llrt_path/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_path" 3 | description = "LLRT Module path" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_path" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 15 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 16 | 17 | [target.'cfg(windows)'.dependencies] 18 | memchr = "2" 19 | 20 | [dev-dependencies] 21 | criterion = "0.5" 22 | once_cell = "1" 23 | rand = "0.8" 24 | 25 | [[bench]] 26 | name = "slash_replacement" 27 | harness = false 28 | -------------------------------------------------------------------------------- /modules/llrt_perf_hooks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_perf_hooks" 3 | description = "LLRT Module perf_hooks" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_perf_hooks" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 17 | 18 | [dev-dependencies] 19 | llrt_test = { path = "../../libs/llrt_test" } 20 | tokio = { version = "1", features = ["full"] } 21 | -------------------------------------------------------------------------------- /modules/llrt_process/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_process" 3 | description = "LLRT Module process" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_process" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 15 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 16 | 17 | [dev-dependencies] 18 | llrt_test = { path = "../../libs/llrt_test" } 19 | tokio = { version = "1", features = ["full"] } 20 | 21 | [target.'cfg(unix)'.dependencies] 22 | libc = "0.2" 23 | -------------------------------------------------------------------------------- /modules/llrt_stream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_stream" 3 | description = "LLRT Module stream" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_stream" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_buffer = { version = "0.5.1-beta", path = "../llrt_buffer" } 15 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 16 | llrt_events = { version = "0.5.1-beta", path = "../llrt_events" } 17 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", features = [ 18 | "bytearray-buffer", 19 | ], default-features = false } 20 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 21 | tokio = { version = "1", features = ["macros", "io-util"] } 22 | -------------------------------------------------------------------------------- /modules/llrt_stream/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use std::result::Result as StdResult; 4 | 5 | use llrt_events::Emitter; 6 | use rquickjs::{prelude::This, Class, Ctx, IntoJs, Result, Value}; 7 | use tokio::sync::broadcast::error::RecvError; 8 | 9 | pub mod readable; 10 | pub mod writable; 11 | 12 | pub fn set_destroyed_and_error<'js>( 13 | is_destroyed: &mut bool, 14 | error_value: &mut Option>, 15 | error: StdResult>, RecvError>, 16 | ) { 17 | *is_destroyed = true; 18 | if let Ok(error) = error { 19 | *error_value = error 20 | } 21 | } 22 | const DEFAULT_BUFFER_SIZE: usize = 1024 * 16; 23 | 24 | pub trait SteamEvents<'js> 25 | where 26 | Self: Emitter<'js>, 27 | { 28 | fn emit_close(this: Class<'js, Self>, ctx: &Ctx<'js>, had_error: bool) -> Result<()> { 29 | Self::emit_str( 30 | This(this), 31 | ctx, 32 | "close", 33 | vec![had_error.into_js(ctx)?], 34 | false, 35 | ) 36 | } 37 | 38 | #[allow(dead_code)] 39 | fn emit_end(this: Class<'js, Self>, ctx: &Ctx<'js>) -> Result<()> { 40 | Self::emit_str(This(this), ctx, "end", vec![], false) 41 | } 42 | } 43 | 44 | #[macro_export] 45 | macro_rules! impl_stream_events { 46 | 47 | ($($struct:ident),*) => { 48 | $( 49 | impl<'js> $crate::SteamEvents<'js> for $struct<'js> {} 50 | )* 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_stream_web" 3 | description = "LLRT Module stream/web" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_stream_web" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_abort = { version = "0.5.1-beta", path = "../llrt_abort" } 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 17 | 18 | [dev-dependencies] 19 | llrt_test = { path = "../../libs/llrt_test" } 20 | tokio = { version = "1", features = ["full"] } 21 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/queuing_strategy/byte_length.rs: -------------------------------------------------------------------------------- 1 | use rquickjs::{class::Trace, methods, Class, Ctx, JsLifetime, Result}; 2 | 3 | use super::{NativeSizeFunction, QueueingStrategyInit}; 4 | 5 | #[derive(JsLifetime, Trace)] 6 | #[rquickjs::class] 7 | pub(crate) struct ByteLengthQueuingStrategy<'js> { 8 | high_water_mark: f64, 9 | size: Class<'js, NativeSizeFunction>, 10 | } 11 | 12 | #[methods(rename_all = "camelCase")] 13 | impl<'js> ByteLengthQueuingStrategy<'js> { 14 | #[qjs(constructor)] 15 | pub(crate) fn new(ctx: Ctx<'js>, init: QueueingStrategyInit) -> Result { 16 | // Set this.[[highWaterMark]] to init["highWaterMark"]. 17 | Ok(Self { 18 | high_water_mark: init.high_water_mark, 19 | size: Class::instance(ctx, NativeSizeFunction::ByteLength)?, 20 | }) 21 | } 22 | 23 | // readonly attribute Function size; 24 | // size is an attribute, not a method, so this function is not itself the size function, but instead returns one 25 | #[qjs(get)] 26 | pub(crate) fn size(&self) -> Class<'js, NativeSizeFunction> { 27 | self.size.clone() 28 | } 29 | 30 | // readonly attribute unrestricted double highWaterMark; 31 | #[qjs(get)] 32 | pub(crate) fn high_water_mark(&self) -> f64 { 33 | self.high_water_mark 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/queuing_strategy/count.rs: -------------------------------------------------------------------------------- 1 | use rquickjs::{class::Trace, methods, Class, Ctx, JsLifetime, Result}; 2 | 3 | use super::{NativeSizeFunction, QueueingStrategyInit}; 4 | 5 | #[derive(JsLifetime, Trace)] 6 | #[rquickjs::class] 7 | pub(crate) struct CountQueuingStrategy<'js> { 8 | high_water_mark: f64, 9 | size: Class<'js, NativeSizeFunction>, 10 | } 11 | 12 | #[methods(rename_all = "camelCase")] 13 | impl<'js> CountQueuingStrategy<'js> { 14 | #[qjs(constructor)] 15 | pub(crate) fn new(ctx: Ctx<'js>, init: QueueingStrategyInit) -> Result { 16 | // Set this.[[highWaterMark]] to init["highWaterMark"]. 17 | Ok(Self { 18 | high_water_mark: init.high_water_mark, 19 | size: Class::instance(ctx, NativeSizeFunction::Count)?, 20 | }) 21 | } 22 | 23 | // readonly attribute Function size; 24 | // size is an attribute, not a method, so this function is not itself the size function, but instead returns one 25 | #[qjs(get)] 26 | pub(crate) fn size(&self) -> Class<'js, NativeSizeFunction> { 27 | self.size.clone() 28 | } 29 | 30 | // readonly attribute unrestricted double highWaterMark; 31 | #[qjs(get)] 32 | pub(crate) fn high_water_mark(&self) -> f64 { 33 | self.high_water_mark 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/readable/mod.rs: -------------------------------------------------------------------------------- 1 | mod byob_reader; 2 | mod byte_controller; 3 | mod controller; 4 | mod default_controller; 5 | mod default_reader; 6 | mod iterator; 7 | mod objects; 8 | mod reader; 9 | mod stream; 10 | 11 | pub(crate) use byob_reader::ReadableStreamBYOBReader; 12 | pub(crate) use byte_controller::{ReadableByteStreamController, ReadableStreamBYOBRequest}; 13 | pub(crate) use default_controller::ReadableStreamDefaultController; 14 | pub(crate) use default_reader::ReadableStreamDefaultReader; 15 | pub(crate) use stream::{ReadableStream, ReadableStreamClass}; 16 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/readable/stream/source.rs: -------------------------------------------------------------------------------- 1 | use rquickjs::{Function, Object, Result}; 2 | 3 | use crate::{readable::stream::ReadableStreamType, utils::ValueOrUndefined}; 4 | 5 | #[derive(Default)] 6 | pub(crate) struct UnderlyingSource<'js> { 7 | // callback UnderlyingSourceStartCallback = any (ReadableStreamController controller); 8 | pub(crate) start: Option>, 9 | // callback UnderlyingSourcePullCallback = Promise (ReadableStreamController controller); 10 | pub(crate) pull: Option>, 11 | // callback UnderlyingSourceCancelCallback = Promise (optional any reason); 12 | pub(crate) cancel: Option>, 13 | pub(super) r#type: Option, 14 | // [EnforceRange] unsigned long long autoAllocateChunkSize; 15 | pub(crate) auto_allocate_chunk_size: Option, 16 | } 17 | 18 | impl<'js> UnderlyingSource<'js> { 19 | pub(super) fn from_object(obj: Object<'js>) -> Result { 20 | let start = obj.get_value_or_undefined::<_, _>("start")?; 21 | let pull = obj.get_value_or_undefined::<_, _>("pull")?; 22 | let cancel = obj.get_value_or_undefined::<_, _>("cancel")?; 23 | let r#type = obj.get_value_or_undefined::<_, _>("type")?; 24 | let auto_allocate_chunk_size = 25 | obj.get_value_or_undefined::<_, _>("autoAllocateChunkSize")?; 26 | 27 | Ok(Self { 28 | start, 29 | pull, 30 | cancel, 31 | r#type, 32 | auto_allocate_chunk_size, 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/readable_writable_pair.rs: -------------------------------------------------------------------------------- 1 | use rquickjs::{Ctx, Error, FromJs, Result, Value}; 2 | 3 | use crate::{readable::ReadableStreamClass, writable::WritableStreamClass}; 4 | 5 | /// An object containing a pair of linked streams, one readable and one writable 6 | /// https://streams.spec.whatwg.org/#dictdef-readablewritablepair 7 | pub struct ReadableWritablePair<'js> { 8 | pub readable: ReadableStreamClass<'js>, 9 | pub writable: WritableStreamClass<'js>, 10 | } 11 | 12 | impl<'js> FromJs<'js> for ReadableWritablePair<'js> { 13 | fn from_js(_ctx: &Ctx<'js>, value: Value<'js>) -> Result { 14 | let ty_name = value.type_name(); 15 | let obj = value 16 | .as_object() 17 | .ok_or(Error::new_from_js(ty_name, "Object"))?; 18 | 19 | let readable = obj.get::<_, ReadableStreamClass<'js>>("readable")?; 20 | let writable = obj.get::<_, WritableStreamClass<'js>>("writable")?; 21 | 22 | Ok(Self { readable, writable }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use llrt_utils::option::Undefined; 2 | use rquickjs::{ 3 | class::{JsClass, OwnedBorrowMut}, 4 | Class, Ctx, FromJs, IntoAtom, Object, Result, Value, 5 | }; 6 | 7 | pub mod promise; 8 | pub mod queue; 9 | 10 | // the trait used elsewhere in this repo accepts null values as 'None', which causes many web platform tests to fail as they 11 | // like to check that undefined is accepted and null isn't. 12 | pub trait ValueOrUndefined<'js> { 13 | fn get_value_or_undefined + Clone, V: FromJs<'js>>( 14 | &self, 15 | k: K, 16 | ) -> Result>; 17 | } 18 | 19 | impl<'js> ValueOrUndefined<'js> for Object<'js> { 20 | fn get_value_or_undefined + Clone, V: FromJs<'js> + Sized>( 21 | &self, 22 | k: K, 23 | ) -> Result> { 24 | let value = self.get::>(k)?; 25 | Ok(Undefined::from_js(self.ctx(), value)?.0) 26 | } 27 | } 28 | 29 | impl<'js> ValueOrUndefined<'js> for Value<'js> { 30 | fn get_value_or_undefined + Clone, V: FromJs<'js>>( 31 | &self, 32 | k: K, 33 | ) -> Result> { 34 | if let Some(obj) = self.as_object() { 35 | return obj.get_value_or_undefined(k); 36 | } 37 | Ok(None) 38 | } 39 | } 40 | 41 | pub trait UnwrapOrUndefined<'js> { 42 | fn unwrap_or_undefined(self, ctx: &Ctx<'js>) -> Value<'js>; 43 | } 44 | 45 | impl<'js> UnwrapOrUndefined<'js> for Option> { 46 | fn unwrap_or_undefined(self, ctx: &Ctx<'js>) -> Value<'js> { 47 | self.unwrap_or_else(|| Value::new_undefined(ctx.clone())) 48 | } 49 | } 50 | 51 | pub fn class_from_owned_borrow_mut<'js, T: JsClass<'js>>( 52 | borrow: OwnedBorrowMut<'js, T>, 53 | ) -> (Class<'js, T>, OwnedBorrowMut<'js, T>) { 54 | let class = borrow.into_inner(); 55 | let borrow = OwnedBorrowMut::from_class(class.clone()); 56 | (class, borrow) 57 | } 58 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/writable/mod.rs: -------------------------------------------------------------------------------- 1 | mod default_controller; 2 | mod default_writer; 3 | mod objects; 4 | mod stream; 5 | mod writer; 6 | 7 | pub(crate) use default_controller::WritableStreamDefaultController; 8 | pub(crate) use default_writer::{WritableStreamDefaultWriter, WritableStreamDefaultWriterOwned}; 9 | pub(crate) use objects::{WritableStreamClassObjects, WritableStreamObjects}; 10 | pub(crate) use stream::{ 11 | WritableStream, WritableStreamClass, WritableStreamOwned, WritableStreamState, 12 | }; 13 | -------------------------------------------------------------------------------- /modules/llrt_stream_web/src/writable/stream/sink.rs: -------------------------------------------------------------------------------- 1 | use rquickjs::{Function, Object, Result, Value}; 2 | 3 | use crate::utils::ValueOrUndefined; 4 | 5 | #[derive(Default)] 6 | pub struct UnderlyingSink<'js> { 7 | // callback UnderlyingSinkStartCallback = any (WritableStreamDefaultController controller); 8 | pub start: Option>, 9 | // callback UnderlyingSinkWriteCallback = Promise (any chunk, WritableStreamDefaultController controller); 10 | pub write: Option>, 11 | // callback UnderlyingSinkCloseCallback = Promise (); 12 | pub close: Option>, 13 | // callback UnderlyingSinkAbortCallback = Promise (optional any reason); 14 | pub abort: Option>, 15 | pub r#type: Option>, 16 | } 17 | 18 | impl<'js> UnderlyingSink<'js> { 19 | pub fn from_object(obj: Object<'js>) -> Result { 20 | let start = obj.get_value_or_undefined::<_, _>("start")?; 21 | let write = obj.get_value_or_undefined::<_, _>("write")?; 22 | let close = obj.get_value_or_undefined::<_, _>("close")?; 23 | let abort = obj.get_value_or_undefined::<_, _>("abort")?; 24 | let r#type = obj.get_value_or_undefined::<_, _>("type")?; 25 | 26 | Ok(Self { 27 | start, 28 | write, 29 | close, 30 | abort, 31 | r#type, 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /modules/llrt_string_decoder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_string_decoder" 3 | description = "LLRT Module string_decoder" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_string_decoder" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | llrt_buffer = { version = "0.5.1-beta", path = "../llrt_buffer" } 16 | llrt_encoding = { version = "0.5.1-beta", path = "../../libs/llrt_encoding" } 17 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 18 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 19 | 20 | [dev-dependencies] 21 | llrt_test = { path = "../../libs/llrt_test" } 22 | tokio = { version = "1", features = ["full"] } 23 | -------------------------------------------------------------------------------- /modules/llrt_timers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_timers" 3 | description = "LLRT Module timers" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | 9 | [lib] 10 | name = "llrt_timers" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | once_cell = "1" 17 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 18 | tokio = { version = "1", features = ["sync", "time", "macros"] } 19 | 20 | [dev-dependencies] 21 | llrt_test = { path = "../../libs/llrt_test" } 22 | tokio = { version = "1", features = ["full"] } 23 | -------------------------------------------------------------------------------- /modules/llrt_tty/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_tty" 3 | description = "LLRT Module tty" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_tty" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 17 | libc = "0.2" 18 | 19 | [dev-dependencies] 20 | llrt_test = { path = "../../libs/llrt_test" } 21 | tokio = { version = "1", features = ["full"] } 22 | -------------------------------------------------------------------------------- /modules/llrt_url/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_url" 3 | description = "LLRT Module url" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_url" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 16 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", features = [ 17 | "macro", 18 | ], default-features = false } 19 | url = "2.5" 20 | 21 | [dev-dependencies] 22 | llrt_test = { path = "../../libs/llrt_test" } 23 | tokio = { version = "1", features = ["full"] } 24 | -------------------------------------------------------------------------------- /modules/llrt_util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_util" 3 | description = "LLRT Module util" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_util" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 16 | llrt_encoding = { version = "0.5.1-beta", path = "../../libs/llrt_encoding" } 17 | llrt_logging = { version = "0.5.1-beta", path = "../../libs/llrt_logging" } 18 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 19 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 20 | -------------------------------------------------------------------------------- /modules/llrt_util/src/text_decoder.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | use llrt_encoding::Encoder; 4 | use llrt_utils::{bytes::ObjectBytes, object::ObjectExt, result::ResultExt}; 5 | use rquickjs::{function::Opt, Ctx, Object, Result}; 6 | 7 | #[rquickjs::class] 8 | #[derive(rquickjs::class::Trace, rquickjs::JsLifetime)] 9 | pub struct TextDecoder { 10 | #[qjs(skip_trace)] 11 | encoder: Encoder, 12 | fatal: bool, 13 | ignore_bom: bool, 14 | } 15 | 16 | #[rquickjs::methods] 17 | impl<'js> TextDecoder { 18 | #[qjs(constructor)] 19 | pub fn new(ctx: Ctx<'js>, label: Opt, options: Opt>) -> Result { 20 | let mut fatal = false; 21 | let mut ignore_bom = false; 22 | 23 | let encoder = Encoder::from_optional_str(label.as_deref()).or_throw_range(&ctx, "")?; 24 | 25 | if let Some(options) = options.0 { 26 | if let Some(opt) = options.get_optional("fatal")? { 27 | fatal = opt; 28 | } 29 | if let Some(opt) = options.get_optional("ignoreBOM")? { 30 | ignore_bom = opt; 31 | } 32 | } 33 | 34 | Ok(TextDecoder { 35 | encoder, 36 | fatal, 37 | ignore_bom, 38 | }) 39 | } 40 | 41 | #[qjs(get)] 42 | fn encoding(&self) -> &str { 43 | self.encoder.as_label() 44 | } 45 | 46 | #[qjs(get)] 47 | fn fatal(&self) -> bool { 48 | self.fatal 49 | } 50 | 51 | #[qjs(get, rename = "ignoreBOM")] 52 | fn ignore_bom(&self) -> bool { 53 | self.ignore_bom 54 | } 55 | 56 | pub fn decode(&self, ctx: Ctx<'js>, bytes: ObjectBytes<'js>) -> Result { 57 | let bytes = bytes.as_bytes(&ctx)?; 58 | let start_pos = if !self.ignore_bom { 59 | match bytes.get(..3) { 60 | Some([0xFF, 0xFE, ..]) | Some([0xFE, 0xFF, ..]) => 2, 61 | Some([0xEF, 0xBB, 0xBF]) => 3, 62 | _ => 0, 63 | } 64 | } else { 65 | 0 66 | }; 67 | 68 | self.encoder 69 | .encode_to_string(&bytes[start_pos..], !self.fatal) 70 | .or_throw_type(&ctx, "") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /modules/llrt_zlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llrt_zlib" 3 | description = "LLRT Module zlib" 4 | version = "0.5.1-beta" 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/awslabs/llrt" 8 | readme = "README.md" 9 | 10 | [lib] 11 | name = "llrt_zlib" 12 | path = "src/lib.rs" 13 | 14 | [features] 15 | default = ["compression-c"] 16 | 17 | compression-c = ["llrt_compression/brotli-c", "llrt_compression/flate2-c"] 18 | compression-rust = [ 19 | "llrt_compression/brotli-rust", 20 | "llrt_compression/flate2-rust", 21 | ] 22 | 23 | [dependencies] 24 | llrt_buffer = { version = "0.5.1-beta", path = "../llrt_buffer" } 25 | llrt_compression = { version = "0.5.1-beta", path = "../../libs/llrt_compression", default-features = false } 26 | llrt_context = { version = "0.5.1-beta", path = "../../libs/llrt_context" } 27 | llrt_utils = { version = "0.5.1-beta", path = "../../libs/llrt_utils", default-features = false } 28 | rquickjs = { git = "https://github.com/DelSkayn/rquickjs.git", version = "0.9.0", default-features = false } 29 | 30 | [dev-dependencies] 31 | llrt_test = { path = "../../libs/llrt_test" } 32 | tokio = { version = "1", features = ["full"] } 33 | -------------------------------------------------------------------------------- /prebuild/README.md: -------------------------------------------------------------------------------- 1 | Pre-generate compressed dictionaries instead of generating them at build time, to avoid the problem of dictionaries generated on platform 1 not being usable on platform 2. -------------------------------------------------------------------------------- /prebuild/compression.dict: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray-D-Song/lexe/48f6ba2b8b25437b2c036fc4a7bf31d49395ea4a/prebuild/compression.dict -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | fn_params_layout = "Tall" 3 | match_block_trailing_comma = true 4 | -------------------------------------------------------------------------------- /shims/@aws-crypto/crc32.js: -------------------------------------------------------------------------------- 1 | import { Crc32 as CryptoCrc32 } from "crypto"; 2 | 3 | export const Crc32 = CryptoCrc32; 4 | 5 | export class AwsCrc32 { 6 | #crc32 = new CryptoCrc32(); 7 | 8 | update(toHash) { 9 | if (isEmptyData(toHash)) return; 10 | this.#crc32.update(toHash); 11 | } 12 | 13 | async digest() { 14 | return numToUint8(this.#crc32.digest()); 15 | } 16 | 17 | reset() { 18 | this.#crc32 = new CryptoCrc32(); 19 | } 20 | } 21 | 22 | function isEmptyData(data) { 23 | if (typeof data === "string") { 24 | return data.length === 0; 25 | } 26 | return data.byteLength === 0; 27 | } 28 | 29 | function numToUint8(num) { 30 | return new Uint8Array([ 31 | (num & 0xff000000) >> 24, 32 | (num & 0x00ff0000) >> 16, 33 | (num & 0x0000ff00) >> 8, 34 | num & 0x000000ff, 35 | ]); 36 | } 37 | -------------------------------------------------------------------------------- /shims/@aws-crypto/crc32c.js: -------------------------------------------------------------------------------- 1 | import { Crc32c as CryptoCrc32c } from "crypto"; 2 | 3 | export const Crc32c = CryptoCrc32c; 4 | 5 | export class AwsCrc32c { 6 | #crc32c = new CryptoCrc32c(); 7 | 8 | update(toHash) { 9 | if (isEmptyData(toHash)) return; 10 | this.#crc32c.update(toHash); 11 | } 12 | 13 | async digest() { 14 | return numToUint8(this.#crc32c.digest()); 15 | } 16 | 17 | reset() { 18 | this.#crc32c = new CryptoCrc32c(); 19 | } 20 | } 21 | 22 | function isEmptyData(data) { 23 | if (typeof data === "string") { 24 | return data.length === 0; 25 | } 26 | return data.byteLength === 0; 27 | } 28 | 29 | function numToUint8(num) { 30 | return new Uint8Array([ 31 | (num & 0xff000000) >> 24, 32 | (num & 0x00ff0000) >> 16, 33 | (num & 0x0000ff00) >> 8, 34 | num & 0x000000ff, 35 | ]); 36 | } 37 | -------------------------------------------------------------------------------- /shims/@aws-crypto/index.js: -------------------------------------------------------------------------------- 1 | export { Sha256, Sha1, Crc32, Crc32c } from "crypto"; 2 | // export { AwsCrc32 } from "@aws-crypto/crc32"; 3 | // export { AwsCrc32c } from "@aws-crypto/crc32c"; 4 | -------------------------------------------------------------------------------- /shims/@aws-crypto/sha1-browser.js: -------------------------------------------------------------------------------- 1 | export { Sha1 } from "crypto"; 2 | -------------------------------------------------------------------------------- /shims/@aws-crypto/sha256-browser.js: -------------------------------------------------------------------------------- 1 | export { Sha256 } from "crypto"; 2 | -------------------------------------------------------------------------------- /shims/@smithy/abort-controller.js: -------------------------------------------------------------------------------- 1 | // @smithy/abort-controller 2 | // Since a global object already exists, this module is not required. 3 | -------------------------------------------------------------------------------- /shims/@smithy/split-stream.js: -------------------------------------------------------------------------------- 1 | export async function splitStream(stream) { 2 | //stream is blob here 3 | const typedArray = await stream.bytes(); 4 | return [typedArray.subarray(0, 3000), typedArray]; 5 | } 6 | -------------------------------------------------------------------------------- /shims/@smithy/util-base64.js: -------------------------------------------------------------------------------- 1 | export const fromBase64 = (input) => Buffer.from(input, "base64"); 2 | export const toBase64 = (input) => Buffer.from(input).toString("base64"); 3 | -------------------------------------------------------------------------------- /shims/@smithy/util-hex-encoding.js: -------------------------------------------------------------------------------- 1 | import { encode, decode } from "llrt:hex"; 2 | 3 | export const fromHex = decode; 4 | export const toHex = encode; 5 | -------------------------------------------------------------------------------- /shims/@smithy/util-utf8.js: -------------------------------------------------------------------------------- 1 | const DECODER = new TextDecoder(); 2 | const ENCODER = new TextEncoder(); 3 | 4 | export const fromUtf8 = (input) => ENCODER.encode(input); 5 | export const toUtf8 = (input) => DECODER.decode(input); 6 | 7 | export const toUint8Array = (data) => { 8 | if (typeof data === "string") { 9 | return fromUtf8(data); 10 | } 11 | if (ArrayBuffer.isView(data)) { 12 | return new Uint8Array( 13 | data.buffer, 14 | data.byteOffset, 15 | data.byteLength / Uint8Array.BYTES_PER_ELEMENT 16 | ); 17 | } 18 | return new Uint8Array(data); 19 | }; 20 | -------------------------------------------------------------------------------- /shims/collect-stream-body.js: -------------------------------------------------------------------------------- 1 | import { Uint8ArrayBlobAdapter } from "@smithy/util-stream"; 2 | export const collectBody = async (streamBody) => 3 | Uint8ArrayBlobAdapter.mutate( 4 | streamBody instanceof Uint8Array 5 | ? streamBody 6 | : new Uint8Array(await streamBody.arrayBuffer()) 7 | ); 8 | -------------------------------------------------------------------------------- /shims/create-read-stream.js: -------------------------------------------------------------------------------- 1 | export function createReadStreamOnBuffer(buffer) { 2 | return buffer; 3 | } 4 | -------------------------------------------------------------------------------- /shims/mnemonist/lru-cache.js: -------------------------------------------------------------------------------- 1 | class LRUCache { 2 | constructor(capacity = 10) { 3 | this.capacity = capacity; 4 | this.cache = new Map(); 5 | } 6 | 7 | get(key) { 8 | let item = this.cache.get(key); 9 | if (item) { 10 | // refresh key 11 | this.cache.delete(key); 12 | this.cache.set(key, item); 13 | } 14 | return item; 15 | } 16 | 17 | set(key, val) { 18 | if (this.cache.has(key)) { 19 | this.cache.delete(key); 20 | } else if (this.cache.size == this.capacity) { 21 | this.cache.delete(this.first()); 22 | } 23 | this.cache.set(key, val); 24 | } 25 | 26 | has(key) { 27 | return this.cache.has(key); 28 | } 29 | 30 | first() { 31 | return this.cache.keys().next().value; 32 | } 33 | 34 | clear() { 35 | this.cache.clear(); 36 | } 37 | } 38 | 39 | export default LRUCache; 40 | -------------------------------------------------------------------------------- /shims/sdk-stream-mixin.js: -------------------------------------------------------------------------------- 1 | import { toBase64 } from "@smithy/util-base64"; 2 | import { toHex } from "@smithy/util-hex-encoding"; 3 | import { toUtf8 } from "@smithy/util-utf8"; 4 | 5 | const transformToWebStream = () => { 6 | throw new Error("WebStream is not available for LLRT"); 7 | }; 8 | 9 | async function transformToByteArray() { 10 | return this; 11 | } 12 | 13 | async function transformToString(encoding) { 14 | const typedArray = this; 15 | if (encoding === "base64") { 16 | return toBase64(typedArray); 17 | } else if (encoding === "hex") { 18 | return toHex(typedArray); 19 | } 20 | return toUtf8(typedArray); 21 | } 22 | 23 | export const sdkStreamMixin = (stream) => 24 | Object.assign(stream, { 25 | transformToByteArray, 26 | transformToString, 27 | transformToWebStream, 28 | }); 29 | -------------------------------------------------------------------------------- /shims/stream-collector.js: -------------------------------------------------------------------------------- 1 | export const streamCollector = async (stream) => 2 | new Uint8Array(await stream.arrayBuffer()); 3 | -------------------------------------------------------------------------------- /tests/e2e/cognito_identity.e2e.test.ts: -------------------------------------------------------------------------------- 1 | // From https://github.com/aws/aws-sdk-js-v3/blob/c8cb4499c6ad19e2b194860d548753f637671f8c/clients/client-cognito-identity/test/e2e/CognitoIdentity.ispec.ts#L6 2 | 3 | import { expect } from "chai"; 4 | import { CognitoIdentity } from "@aws-sdk/client-cognito-identity"; 5 | 6 | const IdentityPoolId = process?.env?.AWS_SMOKE_TEST_IDENTITY_POOL_ID; 7 | 8 | describe("@aws-sdk/client-cognito-identity", function () { 9 | const unAuthClient = new CognitoIdentity({}); 10 | 11 | it("should successfully fetch Id and get credentials", async () => { 12 | // Test getId() 13 | const getIdResult = await unAuthClient.getId({ 14 | IdentityPoolId, 15 | }); 16 | expect(getIdResult.$metadata.httpStatusCode).to.equal(200); 17 | expect(typeof getIdResult.IdentityId).to.equal("string"); 18 | 19 | // Test getCredentialsForIdentity() with Id from above 20 | const getCredentialsResult = await unAuthClient.getCredentialsForIdentity({ 21 | IdentityId: getIdResult.IdentityId, 22 | }); 23 | expect(getCredentialsResult.$metadata.httpStatusCode).to.equal(200); 24 | expect(typeof getCredentialsResult.Credentials?.AccessKeyId).to.equal( 25 | "string" 26 | ); 27 | expect(typeof getCredentialsResult.Credentials?.SecretKey).to.equal( 28 | "string" 29 | ); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tests/unit/assert.test.ts: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | 3 | describe("assert.ok", () => { 4 | it("Should be returned 'undefined' (So it's not an error)", () => { 5 | expect(assert.ok(true)).toBeUndefined(); //bool 6 | expect(assert.ok(1)).toBeUndefined(); // numeric 7 | expect(assert.ok("non-empty string")).toBeUndefined(); // string 8 | expect(assert.ok([])).toBeUndefined(); // array 9 | expect(assert.ok({})).toBeUndefined(); // object 10 | expect(assert.ok(() => {})).toBeUndefined(); // function 11 | expect(assert.ok(123n)).toBeUndefined(); // bigint 12 | expect(assert.ok(Symbol())).toBeUndefined(); // symbol 13 | expect(assert.ok(new Error())).toBeUndefined(); // error 14 | class AssertTestClass {} 15 | expect(assert.ok(AssertTestClass)).toBeUndefined(); // constructor 16 | }); 17 | 18 | it("Should be returned exception", () => { 19 | const errMsg = 20 | "AssertionError: The expression was evaluated to a falsy value"; 21 | expect(() => assert.ok(false)).toThrow(errMsg); 22 | expect(() => assert.ok(0)).toThrow(errMsg); 23 | expect(() => assert.ok("")).toThrow(errMsg); 24 | expect(() => assert.ok(null)).toThrow(errMsg); 25 | }); 26 | 27 | it("should be returned as original error message", () => { 28 | const errMsg = "Error: Value must be true"; 29 | expect(() => assert.ok(false, errMsg)).toThrow(errMsg); 30 | }); 31 | 32 | it("should be returned as original error", () => { 33 | const errMsg = "Error: This is error"; 34 | expect(() => assert.ok(false, Error(errMsg))).toThrow(errMsg); 35 | }); 36 | 37 | it("Should be handled correctly even within functions", () => { 38 | const errMsg = "Error: Value should be truthy"; 39 | function checkValue(value) { 40 | assert.ok(value, errMsg); 41 | } 42 | expect(checkValue(true)).toBeUndefined(); 43 | expect(() => checkValue(false)).toThrow(errMsg); 44 | }); 45 | }); 46 | 47 | describe("assert", () => { 48 | it("Should be returned 'undefined' (So it's not an error)", () => { 49 | expect(assert(true)).toBeUndefined(); 50 | expect(assert(1)).toBeUndefined(); 51 | expect(assert("non-empty string")).toBeUndefined(); 52 | expect(assert([])).toBeUndefined(); 53 | expect(assert({})).toBeUndefined(); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /tests/unit/dns.test.ts: -------------------------------------------------------------------------------- 1 | import dns from "dns"; 2 | 3 | // Promise wrapper for dns.lookup 4 | const dnsLookupAsync = ( 5 | hostname: string, 6 | options?: number | dns.LookupOptions 7 | ) => 8 | new Promise((resolve, reject) => { 9 | dns.lookup(hostname, options as any, (err, address, family) => { 10 | if (err) reject(err); 11 | else resolve({ address, family }); 12 | }); 13 | }); 14 | 15 | describe("lookup", () => { 16 | it("localhost name resolution should be possible (optionless)", async () => { 17 | const { address, family } = await dnsLookupAsync("localhost"); 18 | expect(address === "::1" || address === "127.0.0.1").toBeTruthy(); 19 | expect(family === 4 || family === 6).toBeTruthy(); 20 | }); 21 | 22 | it("localhost name resolution should be possible (integer option)", async () => { 23 | const { address, family } = await dnsLookupAsync("localhost", 4); 24 | expect(address).toEqual("127.0.0.1"); 25 | expect(family).toEqual(4); 26 | }); 27 | 28 | it("localhost name resolution should be possible (record option)", async () => { 29 | const { address, family } = await dnsLookupAsync("localhost", { 30 | family: 4, 31 | }); 32 | expect(address).toEqual("127.0.0.1"); 33 | expect(family).toEqual(4); 34 | }); 35 | 36 | if (process.platform !== "linux") { 37 | it("Name resolution for localhost2 should result in an error (integer option)", async () => { 38 | await expect(dnsLookupAsync("localhost2", 4)).rejects.toThrow("known"); 39 | }); 40 | 41 | it("Name resolution for localhost2 should result in an error (optionless)", async () => { 42 | await expect(dnsLookupAsync("localhost2")).rejects.toThrow("known"); 43 | }); 44 | 45 | it("Name resolution for localhost2 should result in an error (record option)", async () => { 46 | await expect(dnsLookupAsync("localhost2", { family: 4 })).rejects.toThrow( 47 | "known" 48 | ); 49 | }); 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /tests/unit/import.test.ts: -------------------------------------------------------------------------------- 1 | const CWD = process.cwd(); 2 | 3 | describe("import", () => { 4 | it("should import a js file (absolute path)", async () => { 5 | const mod = await import(`${CWD}/fixtures/hello.js`); 6 | 7 | expect(mod.hello).toEqual("hello world!"); 8 | }); 9 | 10 | it("should import a json file (absolute path)", async () => { 11 | const mod = await import(`${CWD}/package.json`); 12 | 13 | expect(mod.default.private).toEqual(true); 14 | }); 15 | 16 | it("should import a js file (relative path)", async () => { 17 | const mod = await import("../../fixtures/hello.js"); 18 | 19 | expect(mod.hello).toEqual("hello world!"); 20 | }); 21 | 22 | it("should import a json file (relative path)", async () => { 23 | const mod = await import("../../fixtures/package.json"); 24 | 25 | expect(mod.default.private).toEqual(true); 26 | }); 27 | 28 | it("should import a json file (path unspecified)", async () => { 29 | const mod = await import("package.json"); 30 | 31 | expect(mod.default.private).toEqual(true); 32 | }); 33 | 34 | it("should have import.meta.url", async () => { 35 | const url = import.meta.url; 36 | expect(url).toEqual( 37 | `file://${CWD}/bundle/js/__tests__/unit/import.test.js`.replaceAll( 38 | "\\", 39 | "/" 40 | ) 41 | ); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/unit/navigator.test.ts: -------------------------------------------------------------------------------- 1 | describe("navigator.userAgent", () => { 2 | it('should start with "llrt "', () => { 3 | expect(navigator.userAgent.startsWith("llrt ")).toBeTruthy(); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /tests/unit/perf_hooks.test.ts: -------------------------------------------------------------------------------- 1 | import { performance } from "perf_hooks"; 2 | 3 | describe("perf_hooks", () => { 4 | it("perf_hooks.performance should be the same as globalThis.performance", () => { 5 | expect(performance).toBe(globalThis.performance); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /tests/unit/performance.test.ts: -------------------------------------------------------------------------------- 1 | describe("performance.timeOrigin", () => { 2 | it("should have a positive value", () => { 3 | expect(Number(performance.timeOrigin)).toBeGreaterThanOrEqual(0); 4 | }); 5 | }); 6 | 7 | describe("performance.now()", () => { 8 | it("should have a positive value", () => { 9 | expect(Number(performance.now())).toBeGreaterThanOrEqual(0); 10 | }); 11 | it("should be a monotonic clock", () => { 12 | const before = performance.now(); 13 | const after = performance.now(); 14 | expect(Number(after)).toBeGreaterThanOrEqual(Number(before)); 15 | }); 16 | 17 | describe("performance.toJSON()", () => { 18 | it("performance.toJSON().timeOrigin should have a positive value", () => { 19 | //@ts-ignore 20 | expect(Number(performance.toJSON().timeOrigin)).toBeGreaterThanOrEqual(0); 21 | }); 22 | it("performance.toJSON().timeOrigin should match performance.timeOrigin", () => { 23 | //@ts-ignore 24 | expect(performance.toJSON().timeOrigin).toEqual(performance.timeOrigin); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/unit/process.test.ts: -------------------------------------------------------------------------------- 1 | import defaultImport from "process"; 2 | import * as namedImport from "process"; 3 | 4 | describe("process", () => { 5 | it("should have a process env", () => { 6 | expect(defaultImport.env).toEqual(process.env); 7 | expect(namedImport.env).toEqual(process.env); 8 | }); 9 | 10 | it("should have a process cwd", () => { 11 | expect(defaultImport.cwd()).toEqual(process.cwd()); 12 | expect(namedImport.cwd()).toEqual(process.cwd()); 13 | }); 14 | 15 | it("should have a process argv0", () => { 16 | expect(defaultImport.argv0).toEqual(process.argv0); 17 | expect(namedImport.argv0).toEqual(process.argv0); 18 | }); 19 | 20 | it("should have a process argv", () => { 21 | expect(defaultImport.argv).toEqual(process.argv); 22 | expect(namedImport.argv).toEqual(process.argv); 23 | }); 24 | 25 | it("should have a process platform", () => { 26 | expect(defaultImport.platform).toEqual(process.platform); 27 | expect(namedImport.platform).toEqual(process.platform); 28 | }); 29 | 30 | it("should have a process arch", () => { 31 | expect(defaultImport.arch).toEqual(process.arch); 32 | expect(namedImport.arch).toEqual(process.arch); 33 | }); 34 | 35 | it("should have a process hrtime", () => { 36 | expect(defaultImport.hrtime.bigint() > 0).toBeTruthy(); 37 | expect(namedImport.hrtime.bigint() > 0).toBeTruthy(); 38 | }); 39 | 40 | it("should have a process release", () => { 41 | expect(defaultImport.release).toEqual(process.release); 42 | expect(namedImport.release).toEqual(process.release); 43 | }); 44 | 45 | it("should have a process version", () => { 46 | expect(defaultImport.version).toEqual(process.version); 47 | expect(namedImport.version).toEqual(process.version); 48 | }); 49 | 50 | it("should have a process versions", () => { 51 | expect(defaultImport.versions).toEqual(process.versions); 52 | expect(namedImport.versions).toEqual(process.versions); 53 | }); 54 | 55 | it("should have a process exit", () => { 56 | expect(defaultImport.exit).toEqual(process.exit); 57 | expect(namedImport.exit).toEqual(process.exit); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /tests/unit/util.test.ts: -------------------------------------------------------------------------------- 1 | const util = require("util"); 2 | const EventEmitter = require("events"); 3 | 4 | describe("Util.inherits", () => { 5 | it("should be inheritable parent classes", () => { 6 | function MyStream() { 7 | EventEmitter.call(this); 8 | } 9 | 10 | util.inherits(MyStream, EventEmitter); 11 | 12 | const stream = new MyStream(); 13 | 14 | expect(stream instanceof EventEmitter).toBeTruthy(); 15 | expect(MyStream.super_).toEqual(EventEmitter); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/common/gc.js: -------------------------------------------------------------------------------- 1 | export default function ({}) { 2 | /** 3 | * Does a best-effort attempt at invoking garbage collection. Attempts to use 4 | * the standardized `TestUtils.gc()` function, but falls back to other 5 | * environment-specific nonstandard functions, with a final result of just 6 | * creating a lot of garbage (in which case you will get a console warning). 7 | * 8 | * This should generally only be used to attempt to trigger bugs and crashes 9 | * inside tests, i.e. cases where if garbage collection happened, then this 10 | * should not trigger some misbehavior. You cannot rely on garbage collection 11 | * successfully trigger, or that any particular unreachable object will be 12 | * collected. 13 | * 14 | * @returns {Promise} A promise you should await to ensure garbage 15 | * collection has had a chance to complete. 16 | */ 17 | self.garbageCollect = async () => { 18 | // https://testutils.spec.whatwg.org/#the-testutils-namespace 19 | if (self.TestUtils?.gc) { 20 | return TestUtils.gc(); 21 | } 22 | 23 | // Use --expose_gc for V8 (and Node.js) 24 | // to pass this flag at chrome launch use: --js-flags="--expose-gc" 25 | // Exposed in SpiderMonkey shell as well 26 | if (self.gc) { 27 | return self.gc(); 28 | } 29 | 30 | // Present in some WebKit development environments 31 | if (self.GCController) { 32 | return GCController.collect(); 33 | } 34 | 35 | console.warn( 36 | "Tests are running without the ability to do manual garbage collection. " + 37 | "They will still work, but coverage will be suboptimal." 38 | ); 39 | 40 | for (var i = 0; i < 1000; i++) { 41 | gcRec(10); 42 | } 43 | 44 | function gcRec(n) { 45 | if (n < 1) { 46 | return {}; 47 | } 48 | 49 | let temp = { i: "ab" + i + i / 100000 }; 50 | temp += "foo"; 51 | 52 | gcRec(n - 1); 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/META.yml: -------------------------------------------------------------------------------- 1 | spec: https://encoding.spec.whatwg.org/ 2 | suggested_reviewers: 3 | - inexorabletash 4 | - annevk 5 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/api-invalid-label.any.js: -------------------------------------------------------------------------------- 1 | // META: title=Encoding API: invalid label 2 | // META: timeout=long 3 | // META: variant=?1-1000 4 | // META: variant=?1001-2000 5 | // META: variant=?2001-3000 6 | // META: variant=?3001-last 7 | // META: script=resources/encodings.js 8 | // META: script=/common/subset-tests.js 9 | 10 | export default function({ 11 | assert_throws_js, 12 | encodings_table, 13 | format_value, 14 | setup, 15 | subsetTest, 16 | test, 17 | }) { 18 | 19 | var tests = ["invalid-invalidLabel"]; 20 | setup(function() { 21 | encodings_table.forEach(function(section) { 22 | section.encodings.forEach(function(encoding) { 23 | encoding.labels.forEach(function(label) { 24 | ["\u0000", "\u000b", "\u00a0", "\u2028", "\u2029"].forEach(function(ws) { 25 | tests.push(ws + label); 26 | tests.push(label + ws); 27 | tests.push(ws + label + ws); 28 | }); 29 | }); 30 | }); 31 | }); 32 | }); 33 | 34 | tests.forEach(function(input) { 35 | subsetTest(test, function() { 36 | assert_throws_js(RangeError, function() { new TextDecoder(input); }); 37 | }, 'Invalid label ' + format_value(input) + ' should be rejected by TextDecoder.'); 38 | }); 39 | 40 | }; 41 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/api-replacement-encodings.any.js: -------------------------------------------------------------------------------- 1 | // META: title=Encoding API: replacement encoding 2 | // META: script=resources/encodings.js 3 | 4 | export default function({ 5 | assert_throws_js, 6 | encodings_table, 7 | test, 8 | }) { 9 | 10 | encodings_table.forEach(function(section) { 11 | section.encodings.filter(function(encoding) { 12 | return encoding.name === 'replacement'; 13 | }).forEach(function(encoding) { 14 | encoding.labels.forEach(function(label) { 15 | test(function() { 16 | assert_throws_js(RangeError, function() { new TextDecoder(label); }); 17 | }, 'Label for "replacement" should be rejected by API: ' + label); 18 | }); 19 | }); 20 | }); 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/api-surrogates-utf8.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,shadowrealm 2 | // META: title=Encoding API: Invalid UTF-16 surrogates with UTF-8 encoding 3 | 4 | export default function({ 5 | assert_array_equals, 6 | assert_equals, 7 | test, 8 | }) { 9 | 10 | var badStrings = [ 11 | { 12 | input: 'abc123', 13 | expected: [0x61, 0x62, 0x63, 0x31, 0x32, 0x33], 14 | decoded: 'abc123', 15 | name: 'Sanity check' 16 | }, 17 | // { 18 | // input: '\uD800', 19 | // expected: [0xef, 0xbf, 0xbd], 20 | // decoded: '\uFFFD', 21 | // name: 'Surrogate half (low)' 22 | // }, 23 | // { 24 | // input: '\uDC00', 25 | // expected: [0xef, 0xbf, 0xbd], 26 | // decoded: '\uFFFD', 27 | // name: 'Surrogate half (high)' 28 | // }, 29 | // { 30 | // input: 'abc\uD800123', 31 | // expected: [0x61, 0x62, 0x63, 0xef, 0xbf, 0xbd, 0x31, 0x32, 0x33], 32 | // decoded: 'abc\uFFFD123', 33 | // name: 'Surrogate half (low), in a string' 34 | // }, 35 | // { 36 | // input: 'abc\uDC00123', 37 | // expected: [0x61, 0x62, 0x63, 0xef, 0xbf, 0xbd, 0x31, 0x32, 0x33], 38 | // decoded: 'abc\uFFFD123', 39 | // name: 'Surrogate half (high), in a string' 40 | // }, 41 | // { 42 | // input: '\uDC00\uD800', 43 | // expected: [0xef, 0xbf, 0xbd, 0xef, 0xbf, 0xbd], 44 | // decoded: '\uFFFD\uFFFD', 45 | // name: 'Wrong order' 46 | // } 47 | ]; 48 | 49 | badStrings.forEach(function(t) { 50 | test(function() { 51 | var encoded = new TextEncoder().encode(t.input); 52 | assert_array_equals([].slice.call(encoded), t.expected); 53 | assert_equals(new TextDecoder('utf-8').decode(encoded), t.decoded); 54 | }, 'Invalid surrogates encoded into UTF-8: ' + t.name); 55 | }); 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/encodeInto.any.js.headers: -------------------------------------------------------------------------------- 1 | Cross-Origin-Opener-Policy: same-origin 2 | Cross-Origin-Embedder-Policy: require-corp -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/replacement-encodings.any.js: -------------------------------------------------------------------------------- 1 | // META: title=Encoding API: replacement encoding 2 | // META: script=resources/encodings.js 3 | // META: script=resources/decoding-helpers.js 4 | 5 | const replacement_labels = []; 6 | encodings_table.forEach(section => { 7 | section.encodings 8 | .filter(encoding => encoding.name === 'replacement') 9 | .forEach(encoding => { 10 | encoding.labels.forEach(label => { replacement_labels.push(label); }) 11 | }); 12 | }); 13 | 14 | replacement_labels.forEach(label => { 15 | decode_test( 16 | label, 17 | '%41%42%43%61%62%63%31%32%33%A0', 18 | 'U+FFFD', 19 | `${label} - non-empty input decodes to one replacement character.`); 20 | 21 | decode_test( 22 | label, 23 | '', 24 | '', `${label} - empty input decodes to empty output.`); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/resources/decoding-helpers.js: -------------------------------------------------------------------------------- 1 | // Decode an URL encoded string, using XHR and data: URL. Returns a Promise. 2 | function decode(label, url_encoded_string) { 3 | return new Promise((resolve, reject) => { 4 | const req = new XMLHttpRequest; 5 | req.open('GET', `data:text/plain,${url_encoded_string}`); 6 | req.overrideMimeType(`text/plain; charset="${label}"`); 7 | req.send(); 8 | req.onload = () => resolve(req.responseText); 9 | req.onerror = () => reject(new Error(req.statusText)); 10 | }); 11 | } 12 | 13 | // Convert code units in a decoded string into: "U+0001/U+0002/...' 14 | function to_code_units(string) { 15 | return string.split('') 16 | .map(unit => unit.charCodeAt(0)) 17 | .map(code => 'U+' + ('0000' + code.toString(16).toUpperCase()).slice(-4)) 18 | .join('/'); 19 | } 20 | 21 | function decode_test(label, 22 | url_encoded_input, 23 | expected_code_units, 24 | description) { 25 | promise_test(() => { 26 | return decode(label, url_encoded_input) 27 | .then(decoded => to_code_units(decoded)) 28 | .then(actual => { 29 | assert_equals(actual, expected_code_units, `Decoding with ${label}`); 30 | }); 31 | }, description); 32 | } 33 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-arguments.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,shadowrealm 2 | // META: title=Encoding API: TextDecoder decode() optional arguments 3 | 4 | export default function({ 5 | assert_equals, 6 | test, 7 | }) { 8 | 9 | test(t => { 10 | const decoder = new TextDecoder(); 11 | 12 | // Just passing nothing. 13 | assert_equals( 14 | decoder.decode(undefined), '', 15 | 'Undefined as first arg should decode to empty string'); 16 | 17 | // Flushing an incomplete sequence. 18 | // decoder.decode(new Uint8Array([0xc9]), {stream: true}); 19 | // assert_equals( 20 | // decoder.decode(undefined), '\uFFFD', 21 | // 'Undefined as first arg should flush the stream'); 22 | 23 | }, 'TextDecoder decode() with explicit undefined'); 24 | 25 | test(t => { 26 | const decoder = new TextDecoder(); 27 | 28 | // Just passing nothing. 29 | assert_equals( 30 | decoder.decode(undefined, undefined), '', 31 | 'Undefined as first arg should decode to empty string'); 32 | 33 | // Flushing an incomplete sequence. 34 | // decoder.decode(new Uint8Array([0xc9]), {stream: true}); 35 | // assert_equals( 36 | // decoder.decode(undefined, undefined), '\uFFFD', 37 | // 'Undefined as first arg should flush the stream'); 38 | 39 | }, 'TextDecoder decode() with undefined and undefined'); 40 | 41 | test(t => { 42 | const decoder = new TextDecoder(); 43 | 44 | // Just passing nothing. 45 | assert_equals( 46 | decoder.decode(undefined, {}), '', 47 | 'Undefined as first arg should decode to empty string'); 48 | 49 | // Flushing an incomplete sequence. 50 | // decoder.decode(new Uint8Array([0xc9]), {stream: true}); 51 | // assert_equals( 52 | // decoder.decode(undefined, {}), '\uFFFD', 53 | // 'Undefined as first arg should flush the stream'); 54 | 55 | }, 'TextDecoder decode() with undefined and options'); 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-byte-order-marks.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,shadowrealm 2 | // META: title=Encoding API: Byte-order marks 3 | 4 | export default function({ 5 | assert_equals, 6 | assert_not_equals, 7 | test, 8 | }) { 9 | 10 | var testCases = [ 11 | { 12 | encoding: 'utf-8', 13 | bom: [0xEF, 0xBB, 0xBF], 14 | bytes: [0x7A, 0xC2, 0xA2, 0xE6, 0xB0, 0xB4, 0xF0, 0x9D, 0x84, 0x9E, 0xF4, 0x8F, 0xBF, 0xBD] 15 | }, 16 | { 17 | encoding: 'utf-16le', 18 | bom: [0xff, 0xfe], 19 | bytes: [0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xDB, 0xFD, 0xDF] 20 | }, 21 | { 22 | encoding: 'utf-16be', 23 | bom: [0xfe, 0xff], 24 | bytes: [0x00, 0x7A, 0x00, 0xA2, 0x6C, 0x34, 0xD8, 0x34, 0xDD, 0x1E, 0xDB, 0xFF, 0xDF, 0xFD] 25 | } 26 | ]; 27 | 28 | var string = 'z\xA2\u6C34\uD834\uDD1E\uDBFF\uDFFD'; // z, cent, CJK water, G-Clef, Private-use character 29 | 30 | testCases.forEach(function(t) { 31 | test(function() { 32 | 33 | var decoder = new TextDecoder(t.encoding); 34 | assert_equals(decoder.decode(new Uint8Array(t.bytes)), string, 35 | 'Sequence without BOM should decode successfully'); 36 | 37 | assert_equals(decoder.decode(new Uint8Array(t.bom.concat(t.bytes))), string, 38 | 'Sequence with BOM should decode successfully (with no BOM present in output)'); 39 | 40 | // testCases.forEach(function(o) { 41 | // if (o === t) 42 | // return; 43 | 44 | // assert_not_equals(decoder.decode(new Uint8Array(o.bom.concat(t.bytes))), string, 45 | // 'Mismatching BOM should not be ignored - treated as garbage bytes.'); 46 | // }); 47 | 48 | }, 'Byte-order marks: ' + t.encoding); 49 | }); 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-copy.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker 2 | // META: script=/common/sab.js 3 | 4 | ["ArrayBuffer", "SharedArrayBuffer"].forEach(arrayBufferOrSharedArrayBuffer => { 5 | test(() => { 6 | const buf = createBuffer(arrayBufferOrSharedArrayBuffer, 2); 7 | const view = new Uint8Array(buf); 8 | const buf2 = createBuffer(arrayBufferOrSharedArrayBuffer, 2); 9 | const view2 = new Uint8Array(buf2); 10 | const decoder = new TextDecoder("utf-8"); 11 | view[0] = 0xEF; 12 | view[1] = 0xBB; 13 | view2[0] = 0xBF; 14 | view2[1] = 0x40; 15 | assert_equals(decoder.decode(buf, {stream:true}), ""); 16 | view[0] = 0x01; 17 | view[1] = 0x02; 18 | assert_equals(decoder.decode(buf2), "@"); 19 | }, "Modify buffer after passing it in (" + arrayBufferOrSharedArrayBuffer + ")"); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-copy.any.js.headers: -------------------------------------------------------------------------------- 1 | Cross-Origin-Opener-Policy: same-origin 2 | Cross-Origin-Embedder-Policy: require-corp -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-fatal-streaming.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,shadowrealm 2 | // META: title=Encoding API: End-of-file 3 | 4 | export default function({ 5 | assert_equals, 6 | assert_throws_js, 7 | test, 8 | }) { 9 | 10 | test(function() { 11 | [ 12 | {encoding: 'utf-8', sequence: [0xC0]}, 13 | {encoding: 'utf-16le', sequence: [0x00]}, 14 | {encoding: 'utf-16be', sequence: [0x00]} 15 | ].forEach(function(testCase) { 16 | 17 | assert_throws_js(TypeError, function() { 18 | var decoder = new TextDecoder(testCase.encoding, {fatal: true}); 19 | decoder.decode(new Uint8Array(testCase.sequence)); 20 | }, 'Unterminated ' + testCase.encoding + ' sequence should throw if fatal flag is set'); 21 | 22 | // assert_equals( 23 | // new TextDecoder(testCase.encoding).decode(new Uint8Array([testCase.sequence])), 24 | // '\uFFFD', 25 | // 'Unterminated UTF-8 sequence should emit replacement character if fatal flag is unset'); 26 | }); 27 | }, 'Fatal flag, non-streaming cases'); 28 | 29 | // test(function() { 30 | 31 | // var decoder = new TextDecoder('utf-16le', {fatal: true}); 32 | // var odd = new Uint8Array([0x00]); 33 | // var even = new Uint8Array([0x00, 0x00]); 34 | 35 | // assert_equals(decoder.decode(odd, {stream: true}), ''); 36 | // assert_equals(decoder.decode(odd), '\u0000'); 37 | 38 | // assert_throws_js(TypeError, function() { 39 | // decoder.decode(even, {stream: true}); 40 | // decoder.decode(odd) 41 | // }); 42 | 43 | // assert_throws_js(TypeError, function() { 44 | // decoder.decode(odd, {stream: true}); 45 | // decoder.decode(even); 46 | // }); 47 | 48 | // assert_equals(decoder.decode(even, {stream: true}), '\u0000'); 49 | // assert_equals(decoder.decode(even), '\u0000'); 50 | 51 | // }, 'Fatal flag, streaming cases'); 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-labels.any.js: -------------------------------------------------------------------------------- 1 | // META: title=Encoding API: Encoding labels 2 | // META: script=resources/encodings.js 3 | // META: timeout=long 4 | 5 | export default function({ 6 | assert_equals, 7 | encodings_table, 8 | test, 9 | }) { 10 | 11 | var whitespace = [' ', '\t', '\n', '\f', '\r']; 12 | encodings_table.forEach(function(section) { 13 | section.encodings.filter(function(encoding) { 14 | return encoding.name !== 'replacement'; 15 | }).forEach(function(encoding) { 16 | encoding.labels.forEach(function(label) { 17 | const textDecoderName = encoding.name.toLowerCase(); // ASCII names only, so safe 18 | test(function(t) { 19 | assert_equals( 20 | new TextDecoder(label).encoding, textDecoderName, 21 | 'label for encoding should match'); 22 | assert_equals( 23 | new TextDecoder(label.toUpperCase()).encoding, textDecoderName, 24 | 'label matching should be case-insensitive'); 25 | whitespace.forEach(function(ws) { 26 | assert_equals( 27 | new TextDecoder(ws + label).encoding, textDecoderName, 28 | 'label for encoding with leading whitespace should match'); 29 | assert_equals( 30 | new TextDecoder(label + ws).encoding, textDecoderName, 31 | 'label for encoding with trailing whitespace should match'); 32 | assert_equals( 33 | new TextDecoder(ws + label + ws).encoding, textDecoderName, 34 | 'label for encoding with surrounding whitespace should match'); 35 | }); 36 | }, label + ' => ' + encoding.name); 37 | }); 38 | }); 39 | }); 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-streaming.any.js.headers: -------------------------------------------------------------------------------- 1 | Cross-Origin-Opener-Policy: same-origin 2 | Cross-Origin-Embedder-Policy: require-corp -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textdecoder-utf16-surrogates.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,shadowrealm 2 | // META: title=Encoding API: UTF-16 surrogate handling 3 | 4 | export default function({ 5 | assert_equals, 6 | assert_throws_js, 7 | test, 8 | }) { 9 | 10 | var bad = [ 11 | { 12 | encoding: 'utf-16le', 13 | input: [0x00, 0xd8], 14 | expected: '\uFFFD', 15 | name: 'lone surrogate lead' 16 | }, 17 | { 18 | encoding: 'utf-16le', 19 | input: [0x00, 0xdc], 20 | expected: '\uFFFD', 21 | name: 'lone surrogate trail' 22 | }, 23 | { 24 | encoding: 'utf-16le', 25 | input: [0x00, 0xd8, 0x00, 0x00], 26 | expected: '\uFFFD\u0000', 27 | name: 'unmatched surrogate lead' 28 | }, 29 | { 30 | encoding: 'utf-16le', 31 | input: [0x00, 0xdc, 0x00, 0x00], 32 | expected: '\uFFFD\u0000', 33 | name: 'unmatched surrogate trail' 34 | }, 35 | { 36 | encoding: 'utf-16le', 37 | input: [0x00, 0xdc, 0x00, 0xd8], 38 | expected: '\uFFFD\uFFFD', 39 | name: 'swapped surrogate pair' 40 | } 41 | ]; 42 | 43 | bad.forEach(function(t) { 44 | test(function() { 45 | assert_equals(new TextDecoder(t.encoding).decode(new Uint8Array(t.input)), t.expected); 46 | }, t.encoding + ' - ' + t.name); 47 | test(function() { 48 | assert_throws_js(TypeError, function() { 49 | new TextDecoder(t.encoding, {fatal: true}).decode(new Uint8Array(t.input)) 50 | }); 51 | }, t.encoding + ' - ' + t.name + ' (fatal flag set)'); 52 | }); 53 | 54 | }; 55 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textencoder-constructor-non-utf.any.js: -------------------------------------------------------------------------------- 1 | // META: title=Encoding API: Legacy encodings 2 | // META: script=resources/encodings.js 3 | 4 | export default function({ 5 | assert_equals, 6 | encodings_table, 7 | test, 8 | }) { 9 | 10 | encodings_table.forEach(function(section) { 11 | section.encodings.forEach(function(encoding) { 12 | if (!['UTF-8', 'UTF-16LE'].includes(encoding.name)) return; 13 | if (encoding.name !== 'replacement') { 14 | test(function() { 15 | assert_equals(new TextDecoder(encoding.name).encoding, encoding.name.toLowerCase()); // ASCII names only, so safe 16 | }, 'Encoding argument supported for decode: ' + encoding.name); 17 | } 18 | 19 | test(function() { 20 | assert_equals(new TextEncoder(encoding.name).encoding, 'utf-8'); 21 | }, 'Encoding argument not considered for encode: ' + encoding.name); 22 | }); 23 | }); 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/textencoder-utf16-surrogates.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,shadowrealm 2 | // META: title=Encoding API: USVString surrogate handling when encoding 3 | 4 | var bad = [ 5 | { 6 | input: '\uD800', 7 | expected: '\uFFFD', 8 | name: 'lone surrogate lead' 9 | }, 10 | { 11 | input: '\uDC00', 12 | expected: '\uFFFD', 13 | name: 'lone surrogate trail' 14 | }, 15 | { 16 | input: '\uD800\u0000', 17 | expected: '\uFFFD\u0000', 18 | name: 'unmatched surrogate lead' 19 | }, 20 | { 21 | input: '\uDC00\u0000', 22 | expected: '\uFFFD\u0000', 23 | name: 'unmatched surrogate trail' 24 | }, 25 | { 26 | input: '\uDC00\uD800', 27 | expected: '\uFFFD\uFFFD', 28 | name: 'swapped surrogate pair' 29 | }, 30 | { 31 | input: '\uD834\uDD1E', 32 | expected: '\uD834\uDD1E', 33 | name: 'properly encoded MUSICAL SYMBOL G CLEF (U+1D11E)' 34 | } 35 | ]; 36 | 37 | bad.forEach(function(t) { 38 | test(function() { 39 | var encoded = new TextEncoder().encode(t.input); 40 | var decoded = new TextDecoder().decode(encoded); 41 | assert_equals(decoded, t.expected); 42 | }, 'USVString handling: ' + t.name); 43 | }); 44 | 45 | test(function() { 46 | assert_equals(new TextEncoder().encode().length, 0, 'Should default to empty string'); 47 | }, 'USVString default'); 48 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/encoding/unsupported-encodings.any.js: -------------------------------------------------------------------------------- 1 | // META: title=Encoding API: unsupported encodings 2 | // META: script=resources/decoding-helpers.js 3 | 4 | // Attempting to decode '<' as UTF-7 (+AD4) ends up as '+AD4'. 5 | ['UTF-7', 'utf-7'].forEach(label => { 6 | decode_test(label, '+AD4', 'U+002B/U+0041/U+0044/U+0034', 7 | `${label} should not be supported`); 8 | }); 9 | 10 | // UTF-32 will be detected as UTF-16LE if leading BOM, or UTF-8 otherwise (due to XMLHttpRequest). 11 | ['UTF-32', 'utf-32', 'UTF-32LE', 'utf-32le'].forEach(label => { 12 | decode_test(label, 13 | '%FF%FE%00%00%41%00%00%00%42%00%00%00', 14 | 'U+0000/U+0041/U+0000/U+0042/U+0000', 15 | `${label} with BOM should decode as UTF-16LE`); 16 | 17 | decode_test(label, 18 | '%41%00%00%00%42%00%00%C2%80', 19 | 'U+0041/U+0000/U+0000/U+0000/U+0042/U+0000/U+0000/U+0080', 20 | `${label} with no BOM should decode as UTF-8`);; 21 | }); 22 | ['UTF-32be', 'utf-32be'].forEach(label => { 23 | decode_test(label, 24 | '%00%00%00%41%00%00%00%42%C2%80', 25 | 'U+0000/U+0000/U+0000/U+0041/U+0000/U+0000/U+0000/U+0042/U+0080', 26 | `${label} with no BOM should decode as UTF-8`); 27 | 28 | decode_test(label, 29 | '%00%00%FE%FF%00%00%00%41%00%C2%80%42', 30 | 'U+0000/U+0000/U+FFFD/U+FFFD/U+0000/U+0000/U+0000/U+0041/U+0000/U+0080/U+0042', 31 | `${label} with BOM should decode as UTF-8`); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/resources/.htaccess: -------------------------------------------------------------------------------- 1 | # make tests that use utf-16 not inherit the encoding for testharness.js et. al. 2 | AddCharset utf-8 .css .js 3 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/piping/general-addition.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function(ctx) { 5 | const { promise_test, assert_false } = ctx; 6 | 7 | promise_test(async t => { 8 | /** @type {ReadableStreamDefaultController} */ 9 | var con; 10 | let synchronous = false; 11 | new ReadableStream({ start(c) { con = c }}, { highWaterMark: 0 }).pipeTo( 12 | new WritableStream({ write() { synchronous = true; } }) 13 | ) 14 | // wait until start algorithm finishes 15 | await Promise.resolve(); 16 | con.enqueue(); 17 | assert_false(synchronous, 'write algorithm must not run synchronously'); 18 | }, "enqueue() must not synchronously call write algorithm"); 19 | 20 | }; -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/piping/throwing-options.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function(ctx) { 5 | const { test, promise_test, assert_array_equals, assert_throws_js, promise_rejects_js } = ctx; 6 | 7 | class ThrowingOptions { 8 | constructor(whatShouldThrow) { 9 | this.whatShouldThrow = whatShouldThrow; 10 | this.touched = []; 11 | } 12 | 13 | get preventClose() { 14 | this.maybeThrow('preventClose'); 15 | return false; 16 | } 17 | 18 | get preventAbort() { 19 | this.maybeThrow('preventAbort'); 20 | return false; 21 | } 22 | 23 | get preventCancel() { 24 | this.maybeThrow('preventCancel'); 25 | return false; 26 | } 27 | 28 | get signal() { 29 | this.maybeThrow('signal'); 30 | return undefined; 31 | } 32 | 33 | maybeThrow(forWhat) { 34 | this.touched.push(forWhat); 35 | if (this.whatShouldThrow === forWhat) { 36 | throw new Error(this.whatShouldThrow); 37 | } 38 | } 39 | } 40 | 41 | const checkOrder = ['preventAbort', 'preventCancel', 'preventClose', 'signal']; 42 | 43 | for (let i = 0; i < checkOrder.length; ++i) { 44 | const whatShouldThrow = checkOrder[i]; 45 | const whatShouldBeTouched = checkOrder.slice(0, i + 1); 46 | 47 | promise_test(t => { 48 | const options = new ThrowingOptions(whatShouldThrow); 49 | return promise_rejects_js( 50 | t, Error, 51 | new ReadableStream().pipeTo(new WritableStream(), options), 52 | 'pipeTo should reject') 53 | .then(() => assert_array_equals( 54 | options.touched, whatShouldBeTouched, 55 | 'options should be touched in the right order')); 56 | }, `pipeTo should stop after getting ${whatShouldThrow} throws`); 57 | 58 | test(() => { 59 | const options = new ThrowingOptions(whatShouldThrow); 60 | assert_throws_js( 61 | Error, 62 | () => new ReadableStream().pipeThrough(new TransformStream(), options), 63 | 'pipeThrough should throw'); 64 | assert_array_equals( 65 | options.touched, whatShouldBeTouched, 66 | 'options should be touched in the right order'); 67 | }, `pipeThrough should stop after getting ${whatShouldThrow} throws`); 68 | } 69 | 70 | }; -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/piping/transform-streams.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function(ctx) { 5 | const { promise_test } = ctx; 6 | 7 | promise_test(() => { 8 | const rs = new ReadableStream({ 9 | start(c) { 10 | c.enqueue('a'); 11 | c.enqueue('b'); 12 | c.enqueue('c'); 13 | c.close(); 14 | } 15 | }); 16 | 17 | const ts = new TransformStream(); 18 | 19 | const ws = new WritableStream(); 20 | 21 | return rs.pipeThrough(ts).pipeTo(ws).then(() => { 22 | const writer = ws.getWriter(); 23 | return writer.closed; 24 | }); 25 | }, 'Piping through an identity transform stream should close the destination when the source closes'); 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 3 | export default function( 4 | ctx 5 | ) { 6 | const { promise_test, promise_rejects_exactly, assert_throws_js } = ctx; 7 | 8 | promise_test(async t => { 9 | const error = new Error('cannot proceed'); 10 | const rs = new ReadableStream({ 11 | type: 'bytes', 12 | pull: t.step_func((controller) => { 13 | const buffer = controller.byobRequest.view.buffer; 14 | // Detach the buffer. 15 | structuredClone(buffer, { transfer: [buffer] }); 16 | 17 | // Try to enqueue with a new buffer. 18 | assert_throws_js(TypeError, () => controller.enqueue(new Uint8Array([42]))); 19 | 20 | // If we got here the test passed. 21 | controller.error(error); 22 | }) 23 | }); 24 | const reader = rs.getReader({ mode: 'byob' }); 25 | await promise_rejects_exactly(t, error, reader.read(new Uint8Array(1))); 26 | }, 'enqueue after detaching byobRequest.view.buffer should throw'); 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/readable-streams/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function({ 5 | assert_throws_exactly, 6 | test, 7 | }) { 8 | 9 | const error1 = new Error('error1'); 10 | error1.name = 'error1'; 11 | 12 | const error2 = new Error('error2'); 13 | error2.name = 'error2'; 14 | 15 | test(() => { 16 | const underlyingSource = { get start() { throw error1; } }; 17 | const queuingStrategy = { highWaterMark: 0, get size() { throw error2; } }; 18 | 19 | // underlyingSource is converted in prose in the method body, whereas queuingStrategy is done at the IDL layer. 20 | // So the queuingStrategy exception should be encountered first. 21 | assert_throws_exactly(error2, () => new ReadableStream(underlyingSource, queuingStrategy)); 22 | }, 'underlyingSource argument should be converted after queuingStrategy argument'); 23 | 24 | }; 25 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/resources/test-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | export default function({ 3 | step_timeout, 4 | assert_equals, 5 | assert_array_equals, 6 | }) { 7 | 8 | self.delay = ms => new Promise(resolve => step_timeout(resolve, ms)); 9 | 10 | // For tests which verify that the implementation doesn't do something it shouldn't, it's better not to use a 11 | // timeout. Instead, assume that any reasonable implementation is going to finish work after 2 times around the event 12 | // loop, and use flushAsyncEvents().then(() => assert_array_equals(...)); 13 | // Some tests include promise resolutions which may mean the test code takes a couple of event loop visits itself. So go 14 | // around an extra 2 times to avoid complicating those tests. 15 | self.flushAsyncEvents = () => delay(0).then(() => delay(0)).then(() => delay(0)).then(() => delay(0)); 16 | 17 | self.assert_typed_array_equals = (actual, expected, message) => { 18 | const prefix = message === undefined ? '' : `${message} `; 19 | assert_equals(typeof actual, 'object', `${prefix}type is object`); 20 | assert_equals(actual.constructor, expected.constructor, `${prefix}constructor`); 21 | assert_equals(actual.byteOffset, expected.byteOffset, `${prefix}byteOffset`); 22 | assert_equals(actual.byteLength, expected.byteLength, `${prefix}byteLength`); 23 | assert_equals(actual.buffer.byteLength, expected.buffer.byteLength, `${prefix}buffer.byteLength`); 24 | assert_array_equals([...actual], [...expected], `${prefix}contents`); 25 | assert_array_equals([...new Uint8Array(actual.buffer)], [...new Uint8Array(expected.buffer)], `${prefix}buffer contents`); 26 | }; 27 | 28 | self.makePromiseAndResolveFunc = () => { 29 | let resolve; 30 | const promise = new Promise(r => { resolve = r; }); 31 | return [promise, resolve]; 32 | }; 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/writable-streams/byte-length-queuing-strategy.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function(ctx) { 5 | const { promise_test, assert_true } = ctx; 6 | 7 | promise_test(t => { 8 | let isDone = false; 9 | const ws = new WritableStream( 10 | { 11 | write() { 12 | return new Promise(resolve => { 13 | t.step_timeout(() => { 14 | isDone = true; 15 | resolve(); 16 | }, 200); 17 | }); 18 | }, 19 | 20 | close() { 21 | assert_true(isDone, 'close is only called once the promise has been resolved'); 22 | } 23 | }, 24 | new ByteLengthQueuingStrategy({ highWaterMark: 1024 * 16 }) 25 | ); 26 | 27 | const writer = ws.getWriter(); 28 | writer.write({ byteLength: 1024 }); 29 | 30 | return writer.close(); 31 | }, 'Closing a writable stream with in-flight writes below the high water mark delays the close call properly'); 32 | 33 | }; -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/writable-streams/error.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function(ctx) { 5 | const { promise_test, test, promise_rejects_exactly } = ctx; 6 | 7 | const error1 = new Error('error1'); 8 | error1.name = 'error1'; 9 | 10 | const error2 = new Error('error2'); 11 | error2.name = 'error2'; 12 | 13 | promise_test(t => { 14 | const ws = new WritableStream({ 15 | start(controller) { 16 | controller.error(error1); 17 | } 18 | }); 19 | return promise_rejects_exactly(t, error1, ws.getWriter().closed, 'stream should be errored'); 20 | }, 'controller.error() should error the stream'); 21 | 22 | test(() => { 23 | let controller; 24 | const ws = new WritableStream({ 25 | start(c) { 26 | controller = c; 27 | } 28 | }); 29 | ws.abort(); 30 | controller.error(error1); 31 | }, 'controller.error() on erroring stream should not throw'); 32 | 33 | promise_test(t => { 34 | let controller; 35 | const ws = new WritableStream({ 36 | start(c) { 37 | controller = c; 38 | } 39 | }); 40 | controller.error(error1); 41 | controller.error(error2); 42 | return promise_rejects_exactly(t, error1, ws.getWriter().closed, 'first controller.error() should win'); 43 | }, 'surplus calls to controller.error() should be a no-op'); 44 | 45 | promise_test(() => { 46 | let controller; 47 | const ws = new WritableStream({ 48 | start(c) { 49 | controller = c; 50 | } 51 | }); 52 | return ws.abort().then(() => { 53 | controller.error(error1); 54 | }); 55 | }, 'controller.error() on errored stream should not throw'); 56 | 57 | promise_test(() => { 58 | let controller; 59 | const ws = new WritableStream({ 60 | start(c) { 61 | controller = c; 62 | } 63 | }); 64 | return ws.getWriter().close().then(() => { 65 | controller.error(error1); 66 | }); 67 | }, 'controller.error() on closed stream should not throw'); 68 | 69 | }; -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/streams/writable-streams/properties.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,worker,shadowrealm 2 | 'use strict'; 3 | 4 | export default function(ctx) { 5 | const { promise_test, assert_equals, assert_true } = ctx; 6 | 7 | const sinkMethods = { 8 | start: { 9 | length: 1, 10 | trigger: () => Promise.resolve() 11 | }, 12 | write: { 13 | length: 2, 14 | trigger: writer => writer.write() 15 | }, 16 | close: { 17 | length: 0, 18 | trigger: writer => writer.close() 19 | }, 20 | abort: { 21 | length: 1, 22 | trigger: writer => writer.abort() 23 | } 24 | }; 25 | 26 | for (const method in sinkMethods) { 27 | const { length, trigger } = sinkMethods[method]; 28 | 29 | // Some semantic tests of how sink methods are called can be found in general.js, as well as in the test files 30 | // specific to each method. 31 | promise_test(() => { 32 | let argCount; 33 | const ws = new WritableStream({ 34 | [method](...args) { 35 | argCount = args.length; 36 | } 37 | }); 38 | return Promise.resolve(trigger(ws.getWriter())).then(() => { 39 | assert_equals(argCount, length, `${method} should be called with ${length} arguments`); 40 | }); 41 | }, `sink method ${method} should be called with the right number of arguments`); 42 | 43 | promise_test(() => { 44 | let methodWasCalled = false; 45 | function Sink() {} 46 | Sink.prototype = { 47 | [method]() { 48 | methodWasCalled = true; 49 | } 50 | }; 51 | const ws = new WritableStream(new Sink()); 52 | return Promise.resolve(trigger(ws.getWriter())).then(() => { 53 | assert_true(methodWasCalled, `${method} should be called`); 54 | }); 55 | }, `sink method ${method} should be called even when it's located on the prototype chain`); 56 | } 57 | 58 | }; -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/url-origin.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | fetch, 4 | promise_test, 5 | test, 6 | }) { 7 | 8 | promise_test(() => fetch("resources/urltestdata.json").then(res => res.json()).then(runURLTests), "Loading data…"); 9 | 10 | function runURLTests(urlTests) { 11 | for (const expected of urlTests) { 12 | // Skip comments and tests without "origin" expectation 13 | if (typeof expected === "string" || !("origin" in expected)) 14 | continue; 15 | 16 | // These tests don't pass with current Url crate 17 | const skip = [ 18 | "blob:blob:https://example.org/", 19 | "blob:ftp://host/path", 20 | "blob:ws://example.org/", 21 | "blob:wss://example.org/", 22 | ]; 23 | if (skip.includes(expected.input)) continue; 24 | 25 | const base = expected.base !== null ? expected.base : undefined; 26 | 27 | test(() => { 28 | const url = new URL(expected.input, base); 29 | assert_equals(url.origin, expected.origin, "origin"); 30 | }, `Origin parsing: <${expected.input}> ${base ? "against <" + base + ">" : "without base"}`); 31 | } 32 | } 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/url-setters.any.js: -------------------------------------------------------------------------------- 1 | // META: script=/common/subset-tests-by-key.js 2 | // META: variant=?include=file 3 | // META: variant=?include=javascript 4 | // META: variant=?include=mailto 5 | // META: variant=?exclude=(file|javascript|mailto) 6 | 7 | // Keep this file in sync with url-setters-a-area.window.js. 8 | 9 | export default function({ 10 | assert_equals, 11 | fetch, 12 | promise_test, 13 | subsetTestByKey, 14 | test, 15 | }) { 16 | 17 | promise_test(() => fetch("resources/setters_tests.json").then(res => res.json()).then(runURLSettersTests), "Loading data…"); 18 | 19 | function runURLSettersTests(all_test_cases) { 20 | for (var attribute_to_be_set in all_test_cases) { 21 | if (attribute_to_be_set == "comment") { 22 | continue; 23 | } 24 | var test_cases = all_test_cases[attribute_to_be_set]; 25 | for(var i = 0, l = test_cases.length; i < l; i++) { 26 | var test_case = test_cases[i]; 27 | var name = "Setting <" + test_case.href + ">." + attribute_to_be_set + 28 | " = '" + test_case.new_value + "'"; 29 | if ("comment" in test_case) { 30 | name += " " + test_case.comment; 31 | } 32 | const key = test_case.href.split(":")[0]; 33 | subsetTestByKey(key, test, function() { 34 | var url = new URL(test_case.href); 35 | url[attribute_to_be_set] = test_case.new_value; 36 | for (var attribute in test_case.expected) { 37 | assert_equals(url[attribute], test_case.expected[attribute]) 38 | } 39 | }, "URL: " + name) 40 | } 41 | } 42 | } 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/url-statics-canparse.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | test, 4 | }) { 5 | 6 | // This intentionally does not use resources/urltestdata.json to preserve resources. 7 | [ 8 | { 9 | "url": undefined, 10 | "base": undefined, 11 | "expected": false 12 | }, 13 | { 14 | "url": "aaa:b", 15 | "base": undefined, 16 | "expected": true 17 | }, 18 | { 19 | "url": undefined, 20 | "base": "aaa:b", 21 | "expected": false 22 | }, 23 | { 24 | "url": "aaa:/b", 25 | "base": undefined, 26 | "expected": true 27 | }, 28 | { 29 | "url": undefined, 30 | "base": "aaa:/b", 31 | "expected": true 32 | }, 33 | { 34 | "url": "https://test:test", 35 | "base": undefined, 36 | "expected": false 37 | }, 38 | { 39 | "url": "a", 40 | "base": "https://b/", 41 | "expected": true 42 | } 43 | ].forEach(({ url, base, expected }) => { 44 | test(() => { 45 | assert_equals(URL.canParse(url, base), expected); 46 | }, `URL.canParse(${url}, ${base})`); 47 | }); 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/url-statics-parse.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | assert_not_equals, 4 | test, 5 | }) { 6 | 7 | // This intentionally does not use resources/urltestdata.json to preserve resources. 8 | [ 9 | { 10 | "url": undefined, 11 | "base": undefined, 12 | "expected": false 13 | }, 14 | { 15 | "url": "aaa:b", 16 | "base": undefined, 17 | "expected": true 18 | }, 19 | { 20 | "url": undefined, 21 | "base": "aaa:b", 22 | "expected": false 23 | }, 24 | { 25 | "url": "aaa:/b", 26 | "base": undefined, 27 | "expected": true 28 | }, 29 | { 30 | "url": undefined, 31 | "base": "aaa:/b", 32 | "expected": true 33 | }, 34 | { 35 | "url": "https://test:test", 36 | "base": undefined, 37 | "expected": false 38 | }, 39 | { 40 | "url": "a", 41 | "base": "https://b/", 42 | "expected": true 43 | } 44 | ].forEach(({ url, base, expected }) => { 45 | test(() => { 46 | if (expected == false) { 47 | assert_equals(URL.parse(url, base), null); 48 | } else { 49 | assert_equals(URL.parse(url, base).href, new URL(url, base).href); 50 | } 51 | }, `URL.parse(${url}, ${base})`); 52 | }); 53 | 54 | test(() => { 55 | assert_not_equals(URL.parse("https://example/"), URL.parse("https://example/")); 56 | }, `URL.parse() should return a unique object`); 57 | 58 | }; 59 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/url-tojson.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | test, 4 | }) { 5 | 6 | test(() => { 7 | const a = new URL("https://example.com/") 8 | assert_equals(JSON.stringify(a), "\"https://example.com/\"") 9 | }) 10 | 11 | }; 12 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-append.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | assert_true, 4 | test, 5 | }) { 6 | 7 | test(function() { 8 | var params = new URLSearchParams(); 9 | params.append('a', 'b'); 10 | assert_equals(params + '', 'a=b'); 11 | params.append('a', 'b'); 12 | assert_equals(params + '', 'a=b&a=b'); 13 | params.append('a', 'c'); 14 | assert_equals(params + '', 'a=b&a=b&a=c'); 15 | }, 'Append same name'); 16 | 17 | test(function() { 18 | var params = new URLSearchParams(); 19 | params.append('', ''); 20 | assert_equals(params + '', '='); 21 | params.append('', ''); 22 | assert_equals(params + '', '=&='); 23 | }, 'Append empty strings'); 24 | 25 | test(function() { 26 | var params = new URLSearchParams(); 27 | params.append(null, null); 28 | assert_equals(params + '', 'null=null'); 29 | params.append(null, null); 30 | assert_equals(params + '', 'null=null&null=null'); 31 | }, 'Append null'); 32 | 33 | test(function() { 34 | var params = new URLSearchParams(); 35 | params.append('first', 1); 36 | params.append('second', 2); 37 | params.append('third', ''); 38 | params.append('first', 10); 39 | assert_true(params.has('first'), 'Search params object has name "first"'); 40 | assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"'); 41 | assert_equals(params.get('second'), '2', 'Search params object has name "second" with value "2"'); 42 | assert_equals(params.get('third'), '', 'Search params object has name "third" with value ""'); 43 | params.append('first', 10); 44 | assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"'); 45 | }, 'Append multiple'); 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-get.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | assert_true, 4 | test, 5 | }) { 6 | 7 | test(function() { 8 | var params = new URLSearchParams('a=b&c=d'); 9 | assert_equals(params.get('a'), 'b'); 10 | assert_equals(params.get('c'), 'd'); 11 | assert_equals(params.get('e'), null); 12 | params = new URLSearchParams('a=b&c=d&a=e'); 13 | assert_equals(params.get('a'), 'b'); 14 | params = new URLSearchParams('=b&c=d'); 15 | assert_equals(params.get(''), 'b'); 16 | params = new URLSearchParams('a=&c=d&a=e'); 17 | assert_equals(params.get('a'), ''); 18 | }, 'Get basics'); 19 | 20 | test(function() { 21 | var params = new URLSearchParams('first=second&third&&'); 22 | assert_true(params != null, 'constructor returned non-null value.'); 23 | assert_true(params.has('first'), 'Search params object has name "first"'); 24 | assert_equals(params.get('first'), 'second', 'Search params object has name "first" with value "second"'); 25 | assert_equals(params.get('third'), '', 'Search params object has name "third" with the empty value.'); 26 | assert_equals(params.get('fourth'), null, 'Search params object has no "fourth" name and value.'); 27 | }, 'More get() basics'); 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-getall.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_array_equals, 3 | assert_equals, 4 | assert_true, 5 | test, 6 | }) { 7 | 8 | test(function() { 9 | var params = new URLSearchParams('a=b&c=d'); 10 | assert_array_equals(params.getAll('a'), ['b']); 11 | assert_array_equals(params.getAll('c'), ['d']); 12 | assert_array_equals(params.getAll('e'), []); 13 | params = new URLSearchParams('a=b&c=d&a=e'); 14 | assert_array_equals(params.getAll('a'), ['b', 'e']); 15 | params = new URLSearchParams('=b&c=d'); 16 | assert_array_equals(params.getAll(''), ['b']); 17 | params = new URLSearchParams('a=&c=d&a=e'); 18 | assert_array_equals(params.getAll('a'), ['', 'e']); 19 | }, 'getAll() basics'); 20 | 21 | test(function() { 22 | var params = new URLSearchParams('a=1&a=2&a=3&a'); 23 | assert_true(params.has('a'), 'Search params object has name "a"'); 24 | var matches = params.getAll('a'); 25 | assert_true(matches && matches.length == 4, 'Search params object has values for name "a"'); 26 | assert_array_equals(matches, ['1', '2', '3', ''], 'Search params object has expected name "a" values'); 27 | params.set('a', 'one'); 28 | assert_equals(params.get('a'), 'one', 'Search params object has name "a" with value "one"'); 29 | var matches = params.getAll('a'); 30 | assert_true(matches && matches.length == 1, 'Search params object has values for name "a"'); 31 | assert_array_equals(matches, ['one'], 'Search params object has expected name "a" values'); 32 | }, 'getAll() multiples'); 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-has.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_false, 3 | assert_true, 4 | test, 5 | }) { 6 | 7 | test(function() { 8 | var params = new URLSearchParams('a=b&c=d'); 9 | assert_true(params.has('a')); 10 | assert_true(params.has('c')); 11 | assert_false(params.has('e')); 12 | params = new URLSearchParams('a=b&c=d&a=e'); 13 | assert_true(params.has('a')); 14 | params = new URLSearchParams('=b&c=d'); 15 | assert_true(params.has('')); 16 | params = new URLSearchParams('null=a'); 17 | assert_true(params.has(null)); 18 | }, 'Has basics'); 19 | 20 | test(function() { 21 | var params = new URLSearchParams('a=b&c=d&&'); 22 | params.append('first', 1); 23 | params.append('first', 2); 24 | assert_true(params.has('a'), 'Search params object has name "a"'); 25 | assert_true(params.has('c'), 'Search params object has name "c"'); 26 | assert_true(params.has('first'), 'Search params object has name "first"'); 27 | assert_false(params.has('d'), 'Search params object has no name "d"'); 28 | params.delete('first'); 29 | assert_false(params.has('first'), 'Search params object has no name "first"'); 30 | }, 'has() following delete()'); 31 | 32 | test(() => { 33 | const params = new URLSearchParams("a=b&a=d&c&e&"); 34 | assert_true(params.has('a', 'b')); 35 | assert_false(params.has('a', 'c')); 36 | assert_true(params.has('a', 'd')); 37 | assert_true(params.has('e', '')); 38 | params.append('first', null); 39 | assert_false(params.has('first', '')); 40 | assert_true(params.has('first', 'null')); 41 | params.delete('a', 'b'); 42 | assert_true(params.has('a', 'd')); 43 | }, "Two-argument has()"); 44 | 45 | test(() => { 46 | const params = new URLSearchParams("a=b&a=d&c&e&"); 47 | assert_true(params.has('a', 'b')); 48 | assert_false(params.has('a', 'c')); 49 | assert_true(params.has('a', 'd')); 50 | assert_true(params.has('a', undefined)); 51 | }, "Two-argument has() respects undefined as second arg"); 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-set.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | assert_true, 4 | test, 5 | }) { 6 | 7 | test(function() { 8 | var params = new URLSearchParams('a=b&c=d'); 9 | params.set('a', 'B'); 10 | assert_equals(params + '', 'a=B&c=d'); 11 | params = new URLSearchParams('a=b&c=d&a=e'); 12 | params.set('a', 'B'); 13 | assert_equals(params + '', 'a=B&c=d') 14 | params.set('e', 'f'); 15 | assert_equals(params + '', 'a=B&c=d&e=f') 16 | }, 'Set basics'); 17 | 18 | test(function() { 19 | var params = new URLSearchParams('a=1&a=2&a=3'); 20 | assert_true(params.has('a'), 'Search params object has name "a"'); 21 | assert_equals(params.get('a'), '1', 'Search params object has name "a" with value "1"'); 22 | params.set('first', 4); 23 | assert_true(params.has('a'), 'Search params object has name "a"'); 24 | assert_equals(params.get('a'), '1', 'Search params object has name "a" with value "1"'); 25 | params.set('a', 4); 26 | assert_true(params.has('a'), 'Search params object has name "a"'); 27 | assert_equals(params.get('a'), '4', 'Search params object has name "a" with value "4"'); 28 | }, 'URLSearchParams.set'); 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-size.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_equals, 3 | test, 4 | }) { 5 | 6 | test(() => { 7 | const params = new URLSearchParams("a=1&b=2&a=3"); 8 | assert_equals(params.size, 3); 9 | 10 | params.delete("a"); 11 | assert_equals(params.size, 1); 12 | }, "URLSearchParams's size and deletion"); 13 | 14 | test(() => { 15 | const params = new URLSearchParams("a=1&b=2&a=3"); 16 | assert_equals(params.size, 3); 17 | 18 | params.append("b", "4"); 19 | assert_equals(params.size, 4); 20 | }, "URLSearchParams's size and addition"); 21 | 22 | test(() => { 23 | const url = new URL("http://localhost/query?a=1&b=2&a=3"); 24 | assert_equals(url.searchParams.size, 3); 25 | 26 | url.searchParams.delete("a"); 27 | assert_equals(url.searchParams.size, 1); 28 | 29 | url.searchParams.append("b", 4); 30 | assert_equals(url.searchParams.size, 2); 31 | }, "URLSearchParams's size when obtained from a URL"); 32 | 33 | test(() => { 34 | const url = new URL("http://localhost/query?a=1&b=2&a=3"); 35 | assert_equals(url.searchParams.size, 3); 36 | 37 | url.search = "?"; 38 | assert_equals(url.searchParams.size, 0); 39 | }, "URLSearchParams's size when obtained from a URL and using .search"); 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/url/urlsearchparams-sort.any.js: -------------------------------------------------------------------------------- 1 | export default function({ 2 | assert_array_equals, 3 | assert_equals, 4 | test, 5 | }) { 6 | 7 | [ 8 | { 9 | "input": "z=b&a=b&z=a&a=a", 10 | "output": [["a", "b"], ["a", "a"], ["z", "b"], ["z", "a"]] 11 | }, 12 | { 13 | "input": "\uFFFD=x&\uFFFC&\uFFFD=a", 14 | "output": [["\uFFFC", ""], ["\uFFFD", "x"], ["\uFFFD", "a"]] 15 | }, 16 | { 17 | "input": "é&e\uFFFD&e\u0301", 18 | "output": [["e\u0301", ""], ["e\uFFFD", ""], ["é", ""]] 19 | }, 20 | { 21 | "input": "z=z&a=a&z=y&a=b&z=x&a=c&z=w&a=d&z=v&a=e&z=u&a=f&z=t&a=g", 22 | "output": [["a", "a"], ["a", "b"], ["a", "c"], ["a", "d"], ["a", "e"], ["a", "f"], ["a", "g"], ["z", "z"], ["z", "y"], ["z", "x"], ["z", "w"], ["z", "v"], ["z", "u"], ["z", "t"]] 23 | }, 24 | { 25 | "input": "bbb&bb&aaa&aa=x&aa=y", 26 | "output": [["aa", "x"], ["aa", "y"], ["aaa", ""], ["bb", ""], ["bbb", ""]] 27 | }, 28 | { 29 | "input": "z=z&=f&=t&=x", 30 | "output": [["", "f"], ["", "t"], ["", "x"], ["z", "z"]] 31 | }, 32 | { 33 | "input": "a🌈&a💩", 34 | "output": [["a🌈", ""], ["a💩", ""]] 35 | } 36 | ].forEach((val) => { 37 | test(() => { 38 | let params = new URLSearchParams(val.input), 39 | i = 0 40 | params.sort() 41 | for(let param of params) { 42 | assert_array_equals(param, val.output[i]) 43 | i++ 44 | } 45 | }, "Parse and sort: " + val.input) 46 | 47 | test(() => { 48 | let url = new URL("?" + val.input, "https://example/") 49 | url.searchParams.sort() 50 | let params = new URLSearchParams(url.search), 51 | i = 0 52 | for(let param of params) { 53 | assert_array_equals(param, val.output[i]) 54 | i++ 55 | } 56 | }, "URL parse and sort: " + val.input) 57 | }) 58 | 59 | test(function() { 60 | const url = new URL("http://example.com/?") 61 | url.searchParams.sort() 62 | assert_equals(url.href, "http://example.com/") 63 | assert_equals(url.search, "") 64 | }, "Sorting non-existent params removes ? from URL") 65 | 66 | }; 67 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/webidl/ecmascript-binding/es-exceptions/DOMException-constants.any.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default function(ctx) { 4 | const { test, assert_equals, assert_true, assert_false, assert_own_property } = ctx; 5 | 6 | test(function() { 7 | // https://www.w3.org/Bugs/Public/show_bug.cgi?id=27732 8 | var constants = [ 9 | "INDEX_SIZE_ERR", 10 | "DOMSTRING_SIZE_ERR", 11 | "HIERARCHY_REQUEST_ERR", 12 | "WRONG_DOCUMENT_ERR", 13 | "INVALID_CHARACTER_ERR", 14 | "NO_DATA_ALLOWED_ERR", 15 | "NO_MODIFICATION_ALLOWED_ERR", 16 | "NOT_FOUND_ERR", 17 | "NOT_SUPPORTED_ERR", 18 | "INUSE_ATTRIBUTE_ERR", 19 | "INVALID_STATE_ERR", 20 | "SYNTAX_ERR", 21 | "INVALID_MODIFICATION_ERR", 22 | "NAMESPACE_ERR", 23 | "INVALID_ACCESS_ERR", 24 | "VALIDATION_ERR", 25 | "TYPE_MISMATCH_ERR", 26 | "SECURITY_ERR", 27 | "NETWORK_ERR", 28 | "ABORT_ERR", 29 | "URL_MISMATCH_ERR", 30 | "QUOTA_EXCEEDED_ERR", 31 | "TIMEOUT_ERR", 32 | "INVALID_NODE_TYPE_ERR", 33 | "DATA_CLONE_ERR" 34 | ] 35 | var objects = [ 36 | [DOMException, "DOMException constructor object"], 37 | [DOMException.prototype, "DOMException prototype object"] 38 | ] 39 | constants.forEach(function(name, i) { 40 | objects.forEach(function(o) { 41 | var object = o[0], description = o[1]; 42 | test(function() { 43 | assert_equals(object[name], i + 1, name) 44 | assert_own_property(object, name) 45 | var pd = Object.getOwnPropertyDescriptor(object, name) 46 | assert_false("get" in pd, "get") 47 | assert_false("set" in pd, "set") 48 | assert_false(pd.writable, "writable") 49 | assert_true(pd.enumerable, "enumerable") 50 | assert_false(pd.configurable, "configurable") 51 | }, "Constant " + name + " on " + description) 52 | }) 53 | }) 54 | }) 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /tests/unit/web-platform-tests/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js: -------------------------------------------------------------------------------- 1 | export default function(ctx) { 2 | const { test, assert_equals, assert_true, assert_false, assert_own_property } = ctx; 3 | 4 | test(function() { 5 | assert_own_property(self, "DOMException", "property of global"); 6 | 7 | var desc = Object.getOwnPropertyDescriptor(self, "DOMException"); 8 | assert_false("get" in desc, "get"); 9 | assert_false("set" in desc, "set"); 10 | assert_true(desc.writable, "writable"); 11 | assert_false(desc.enumerable, "enumerable"); 12 | assert_true(desc.configurable, "configurable"); 13 | }, "existence and property descriptor of DOMException"); 14 | 15 | test(function() { 16 | assert_own_property(self.DOMException, "prototype", "prototype property"); 17 | 18 | var desc = Object.getOwnPropertyDescriptor(self.DOMException, "prototype"); 19 | assert_false("get" in desc, "get"); 20 | assert_false("set" in desc, "set"); 21 | assert_false(desc.writable, "writable"); 22 | assert_false(desc.enumerable, "enumerable"); 23 | assert_false(desc.configurable, "configurable"); 24 | }, "existence and property descriptor of DOMException.prototype"); 25 | 26 | test(function() { 27 | assert_own_property(self.DOMException.prototype, "constructor", "property of prototype"); 28 | var desc = Object.getOwnPropertyDescriptor(self.DOMException.prototype, "constructor"); 29 | assert_false("get" in desc, "get"); 30 | assert_false("set" in desc, "set"); 31 | assert_true(desc.writable, "writable"); 32 | assert_false(desc.enumerable, "enumerable"); 33 | assert_true(desc.configurable, "configurable"); 34 | assert_equals(self.DOMException.prototype.constructor, self.DOMException, "equality with actual constructor"); 35 | }, "existence and property descriptor of DOMException.prototype.constructor"); 36 | 37 | }; -------------------------------------------------------------------------------- /tests/unit/zlib.test.ts: -------------------------------------------------------------------------------- 1 | const zlib = require("zlib"); 2 | const data = "Hello LLRT!!"; 3 | 4 | describe("deflate/inflate", () => { 5 | it("deflate/inflate", (done) => { 6 | zlib.deflate(data, (err, compressed) => { 7 | zlib.inflate(compressed, (err, decompressed) => { 8 | expect(data).toEqual(decompressed.toString()); 9 | done(); 10 | }); 11 | }); 12 | }); 13 | it("deflateSync/inflateSync", () => { 14 | const compressed = zlib.deflateSync(data); 15 | const decompressed = zlib.inflateSync(compressed); 16 | expect(data).toEqual(decompressed.toString()); 17 | }); 18 | }); 19 | 20 | describe("deflateRaw/inflateRaw", () => { 21 | it("deflateRaw/inflateRaw", (done) => { 22 | zlib.deflateRaw(data, (err, compressed) => { 23 | zlib.inflateRaw(compressed, (err, decompressed) => { 24 | expect(data).toEqual(decompressed.toString()); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | it("deflateRawSync/inflateRawSync", () => { 30 | const compressed = zlib.deflateRawSync(data); 31 | const decompressed = zlib.inflateRawSync(compressed); 32 | expect(data).toEqual(decompressed.toString()); 33 | }); 34 | }); 35 | 36 | describe("gzip/gunzip", () => { 37 | it("gzip/gunzip", (done) => { 38 | zlib.gzip(data, (err, compressed) => { 39 | zlib.gunzip(compressed, (err, decompressed) => { 40 | expect(data).toEqual(decompressed.toString()); 41 | done(); 42 | }); 43 | }); 44 | }); 45 | it("gzipSync/gunzipSync", () => { 46 | const compressed = zlib.gzipSync(data); 47 | const decompressed = zlib.gunzipSync(compressed); 48 | expect(data).toEqual(decompressed.toString()); 49 | }); 50 | }); 51 | 52 | describe("brotli", () => { 53 | it("brotliCompress/brotliDecompress", (done) => { 54 | zlib.brotliCompress(data, (err, compressed) => { 55 | zlib.brotliDecompress(compressed, (err, decompressed) => { 56 | expect(data).toEqual(decompressed.toString()); 57 | done(); 58 | }); 59 | }); 60 | }); 61 | it("brotliCompressSync/brotliDecompressSync", () => { 62 | const compressed = zlib.brotliCompressSync(data); 63 | const decompressed = zlib.brotliDecompressSync(compressed); 64 | expect(data).toEqual(decompressed.toString()); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2023", 4 | "lib": ["es2023"], 5 | "module": "es2022", 6 | "esModuleInterop": true, 7 | "moduleResolution": "node", 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true 11 | }, 12 | "exclude": ["example/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /types/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ["plugin:@definitelytyped/all"], 4 | }; 5 | -------------------------------------------------------------------------------- /types/assert.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `assert` module provides a set of assertion functions for verifying invariants. 3 | */ 4 | declare module "assert" { 5 | /** 6 | * An alias of {@link ok}. 7 | * @param value The input that is checked for being truthy. 8 | */ 9 | function assert(value: unknown, message?: string | Error): asserts value; 10 | /** 11 | * Tests if `value` is truthy. 12 | * 13 | * If `value` is not truthy, an `AssertionError` is thrown with a `message` property set equal to the value of the `message` parameter. If the `message` parameter is `undefined`, a default 14 | * error message is assigned. If the `message` parameter is an instance of an `Error` then it will be thrown instead of the `AssertionError`. 15 | * If no arguments are passed in at all `message` will be set to the string:`` 'No value argument passed to `assert.ok()`' ``. 16 | * 17 | * ```js 18 | * import * as assert from 'assert'; 19 | * 20 | * assert.ok(true); 21 | * // OK 22 | * assert.ok(1); 23 | * // OK 24 | * 25 | * assert.ok(); 26 | * // TypeError: Error calling function with 0 argument(s) while 1 where expected 27 | * 28 | * assert.ok(false, 'it\'s false'); 29 | * // AssertionError: it's false 30 | * 31 | * assert.ok(false); 32 | * // AssertionError: The expression was evaluated to a falsy value 33 | * 34 | * assert.ok(0); 35 | * // AssertionError: The expression was evaluated to a falsy value 36 | * ``` 37 | */ 38 | function ok(value: unknown, message?: string | Error): asserts value; 39 | } 40 | -------------------------------------------------------------------------------- /types/dom-events.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | interface EventListener { 4 | (evt: Event): void; 5 | } 6 | 7 | interface AddEventListenerOptions { 8 | /** When `true`, the listener is automatically removed when it is first invoked. Default: `false`. */ 9 | once?: boolean; 10 | } 11 | 12 | declare global { 13 | type EventKey = string | symbol; 14 | 15 | /** An event which takes place in the system. */ 16 | interface Event { 17 | /** Returns the type of event, e.g. "click", "hashchange", or "submit". */ 18 | readonly type: EventKey; 19 | } 20 | 21 | class CustomEvent implements Event { 22 | constructor(type: string, opts?: { details?: D }); 23 | readonly type: string; 24 | readonly details: D | null; 25 | } 26 | 27 | /** 28 | * EventTarget is an interface implemented by objects that can 29 | * receive events and may have listeners for them. 30 | */ 31 | class EventTarget { 32 | constructor(); 33 | 34 | /** 35 | * Adds a new handler for the `type` event. Any given `listener` is added only once per `type`. 36 | * 37 | * If the `once` option is true, the `listener` is removed after the next time a `type` event is dispatched. 38 | */ 39 | addEventListener( 40 | type: EventKey, 41 | listener: EventListener, 42 | options?: AddEventListenerOptions 43 | ): void; 44 | 45 | /** Dispatches a synthetic event event to target */ 46 | dispatchEvent(event: Event): void; 47 | 48 | /** Removes the event listener in target's event listener list with the same type and callback */ 49 | removeEventListener(type: EventKey, listener: EventListener): void; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /types/exceptions.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface Error { 5 | name: string; 6 | message: string; 7 | stack?: string; 8 | } 9 | 10 | interface ErrorConstructor { 11 | new (message?: string): Error; 12 | (message?: string): Error; 13 | readonly prototype: Error; 14 | } 15 | 16 | interface DOMException extends Error { 17 | readonly message: string; 18 | readonly name: string; 19 | readonly stack: string; 20 | } 21 | var Error: ErrorConstructor; 22 | var DOMException: { 23 | prototype: DOMException; 24 | new (message?: string, name?: string): DOMException; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /types/globals.d.ts: -------------------------------------------------------------------------------- 1 | import { Performance } from "perf_hooks"; 2 | 3 | export {}; 4 | 5 | declare global { 6 | namespace QuickJS { 7 | type TypedArray = 8 | | Uint8Array 9 | | Uint8ClampedArray 10 | | Uint16Array 11 | | Uint32Array 12 | | Int8Array 13 | | Int16Array 14 | | Int32Array 15 | | BigUint64Array 16 | | BigInt64Array 17 | | Float32Array 18 | | Float64Array; 19 | type ArrayBufferView = TypedArray | DataView; 20 | } 21 | 22 | var performance: Performance; 23 | } 24 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | -------------------------------------------------------------------------------- /types/navigator.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface Navigator { 5 | readonly userAgent: string; 6 | } 7 | 8 | var navigator: Navigator; 9 | } 10 | -------------------------------------------------------------------------------- /types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llrt-types", 3 | "license": "Apache-2.0", 4 | "version": "0.0.1", 5 | "main": "", 6 | "types": "index.d.ts", 7 | "description": "Type definitions for LLRT, Low Latency Runtime", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/awslabs/llrt", 11 | "directory": "types" 12 | }, 13 | "files": [ 14 | "*.d.ts" 15 | ], 16 | "devDependencies": { 17 | "@definitelytyped/dtslint": "^0.2.22", 18 | "@definitelytyped/eslint-plugin": "^0.1.17", 19 | "typescript": "^5.5.3" 20 | }, 21 | "scripts": { 22 | "test": "tsc" 23 | }, 24 | "keywords": [ 25 | "llrt", 26 | "types" 27 | ] 28 | } -------------------------------------------------------------------------------- /types/perf_hooks.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This module provides an implementation of a subset of the W3C [Web Performance APIs](https://w3c.github.io/perf-timing-primer/) as well as additional APIs for 3 | * Node.js-specific performance measurements. 4 | * 5 | */ 6 | declare module "perf_hooks" { 7 | interface Performance { 8 | /** 9 | * Returns the current high resolution millisecond timestamp, where 0 represents the start of the current `node` process. 10 | * @since v8.5.0 11 | */ 12 | now(): number; 13 | /** 14 | * The [`timeOrigin`](https://w3c.github.io/hr-time/#dom-performance-timeorigin) specifies the high resolution millisecond timestamp 15 | * at which the current `node` process began, measured in Unix time. 16 | * @since v8.5.0 17 | */ 18 | readonly timeOrigin: number; 19 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Performance/toJSON) */ 20 | toJSON(): { timeOrigin: number }; // TODO: llrt currently has only one field 21 | } 22 | var performance: Performance 23 | } 24 | -------------------------------------------------------------------------------- /types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext"], 4 | "skipLibCheck": false, 5 | "strict": true, 6 | "target": "esnext", 7 | "module": "esnext", 8 | "moduleResolution": "node", 9 | "allowSyntheticDefaultImports": true, 10 | "disableSolutionSearching": true, 11 | "noUnusedLocals": true, 12 | "noEmit": true, 13 | "resolveJsonModule": true, 14 | "types": [] 15 | }, 16 | "exclude": ["dist", "node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /types/tty.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module "tty" { 3 | /** 4 | * The `tty.isatty()` method returns `true` if the given `fd` is associated with 5 | * a TTY and `false` if it is not, including whenever `fd` is not a non-negative 6 | * integer. 7 | * @since v0.5.8 8 | * @param fd A numeric file descriptor 9 | */ 10 | function isatty(fd: number): boolean; 11 | } -------------------------------------------------------------------------------- /vitest.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | /* for example, use global to avoid globals imports (describe, test, expect): */ 6 | globals: true, 7 | // mockReset: true 8 | }, 9 | }); 10 | --------------------------------------------------------------------------------