Script tasks
A script task runs a small piece of code as a step in your process. TinyConductor supports two kinds:
- Inline FEEL. The engine evaluates a FEEL expression itself.
- Job-worker script. A separate program,
bpm-script-worker, runs JavaScript in a sandbox. The engine hands the script to it as a job.
The two forms
Section titled “The two forms”A bpmn:scriptTask has two alternative implementations, written as extension
elements in the tc: namespace:
| Form | BPMN | Who runs it |
|---|---|---|
| Inline FEEL | <tc:script expression="=..." resultVariable="..."/> | The engine evaluates it during activation. |
| Job worker | <tc:taskDefinition type="..." retries="..."/> plus optional <tc:taskHeaders> | A job worker. In this form a script task behaves exactly like a service task. |
A script task must declare exactly one of the two. If it declares both or
neither, deployment is rejected with rule script-task-implementation:
Script task must declare exactly one of the script or taskDefinition extensions:both are declared; keep the inline FEEL script extension or the job-worker taskDefinition extension, not bothIn the job-worker form the engine uses the service-task path unchanged:
- the
typeandretriesexpressions; - task headers, passed exactly as written (a leading
=is not evaluated); - input mappings before the job, and output mappings after completion;
- incidents when retries run out;
- boundary events, including BPMN errors thrown by the worker;
start/endexecution listeners.
The script worker
Section titled “The script worker”bpm-script-worker is a separate process that keeps no state. It talks to the
engine only over the public job API. You deploy, scale and secure it like any
other connector runtime. For each job it does three things:
- It long-polls
POST /v2/jobs/activation. If the engine answers that route with 404, 405 or 501, it falls back toPOST /v1/jobs/activation. Every deployment mode serves both routes, so the fallback matters only for an endpoint that exposes/v1alone. - It runs each job’s script in a fresh JavaScript isolate, within the sandbox limits.
- It sends one of three commands:
completionwith the result variables,failurewith retries and a backoff, orerrorfor a BPMN error. It tries/v2first for jobs activated over v2, and uses/v1otherwise. Each command carries anIdempotency-Key, and transient transport errors and 5xx responses are retried up to three times.
Minimal example
Section titled “Minimal example”This uses the community script-worker convention:
<bpmn:scriptTask id="price" name="Price the quote"> <bpmn:extensionElements> <tc:taskDefinition type="script" retries="3"/> <tc:taskHeaders> <tc:header key="language" value="javascript"/> <tc:header key="script" value="netPrice * (1 + vatRate)"/> <tc:header key="resultVariable" value="grossPrice"/> </tc:taskHeaders> </bpmn:extensionElements></bpmn:scriptTask>In the console’s Modeler, select the task and apply the Script
(JavaScript) template (Apply a template… over the canvas, or
Templates…), just as you would pick a connector. The template writes job type tc:script:1, the language
and script headers, the result and error expressions, the retries and retry
backoff, and optional limits for the task.
Writing scripts
Section titled “Writing scripts”- Variables. Every variable visible at the task can be used in two ways:
- As a top-level binding (
a + b). This is a mutable deep copy. A variable whose name is not a plain identifier, or would shadow a global such asJSON, is not bound this way. - Through
variables, a deep-frozen object that holds every variable (variables['order-id']).
- As a top-level binding (
- Result. The result is the value of the script’s last expression, as in the
community worker. A script may also use top-level
return, andawait: it then runs as the body of a (possibly async) function. The worker picks the form once, when it prepares the script, without running it. - Promises must settle synchronously. There are no timers or I/O, so a script that waits on something that never resolves fails with “the script’s promise never settled”.
- BPMN errors. Call
throw new BpmnError(code, message?, variables?). The job gets a BPMN error, and a matching error boundary or event sub-process catches it. scriptMetaholds{ numberMode, convertedNumbers }(see Value mapping).
Language support
Section titled “Language support”language | Behavior |
|---|---|
javascript (also js, ecmascript) | Runs on the configured backend (QuickJS by default). |
feel | Evaluated by the worker with the engine’s own FEEL implementation. |
groovy, kotlin, mustache, anything else | Rejected. The job fails with 0 retries and a message that names the supported languages. The community worker’s JVM engines don’t exist in this Rust worker. |
Sandbox
Section titled “Sandbox”- There is no filesystem, network, process, timer (
setTimeout/setInterval),console,require, or module loader. A staticimportdoes not parse, and a dynamicimport()rejects. - Each job runs in a fresh isolate, so nothing one job’s script does survives into the next. This matters because a shared worker runs scripts from several tenants.
- Preparation parses the script and never runs it. Even a source crafted to escape
a wrapper (
} while (true) {} {) does not run during preparation.
Limits
Section titled “Limits”Worker-wide defaults come from environment variables. A task header can
tighten a limit for one task, but never loosen it: the effective value is
min(worker, header), so a model cannot widen the sandbox that an operator set
for a shared worker.
| Limit | Default | Worker env | Task header | QuickJS (default) | Boa |
|---|---|---|---|---|---|
| Wall-clock timeout | 5 s | SCRIPT_TIMEOUT_MS | scriptTimeoutMs | Enforced by the interrupt handler, which is uncatchable. | Not enforceable in-engine. A late result is discarded as a timeout, and the worker fails the job at the deadline, but the thread runs on until a loop or recursion limit stops it. The concurrency slot stays held until then. |
| CPU / instruction budget | 100,000,000 ops | SCRIPT_INSTRUCTION_BUDGET | scriptInstructionBudget | Enforced across the whole execution. Granularity is one interrupt poll per ~10,000 branch/call events, and it is uncatchable. | Partial: it is a loop-iteration limit per call frame, so a loop split across function calls escapes it. |
| Memory cap | 64 MiB | SCRIPT_MEMORY_LIMIT_BYTES | scriptMemoryLimitBytes | Enforced (JS_SetMemoryLimit). | Not enforceable. Boa has no allocation limit. |
| Stack | 512 KiB | SCRIPT_MAX_STACK_BYTES | scriptMaxStackBytes | Enforced. This byte cap also bounds recursion. | The VM value-stack limit applies instead. |
| Recursion depth | 400 frames | SCRIPT_RECURSION_LIMIT | scriptRecursionLimit | Not enforced as a frame count. The stack byte cap bounds recursion. | Enforced, as an exact frame count. |
The instruction budget is the limit to rely on: it gives the same result on
every run, so tests use it. The wall-clock timeout is only an outer safety net
for untrusted code. As a last guard, the worker fails a job as timeout when
its script has not returned 2 s after the timeout.
Failures, retries and incidents
Section titled “Failures, retries and incidents”| Class (metric label) | Cause | Retries sent |
|---|---|---|
syntax | The script does not parse. | 0: the failure is deterministic, so the incident is raised at once. |
configuration | Unknown language, missing script, or a malformed header or expression. | 0 |
value | A result that isn’t JSON (NaN, Infinity, a cycle), or an unsafe integer under the reject policy. | 0 |
runtime | The script threw, or a promise rejected. | job.retries - 1 |
timeout, cpu_budget, memory, stack | A sandbox limit was hit. | job.retries - 1 |
job_error | errorExpression returned jobError(...). | As the expression says. |
The backoff comes from the retryBackoff header (ISO-8601, for example PT10S, or
milliseconds). If that is absent, it is CONDUCTOR_SCRIPT_WORKER_RETRY_BACKOFF
(default 5 s). When retries reach 0, the engine raises an incident. Resolving the
incident retries the job with the retries the operator sets.
Value mapping
Section titled “Value mapping”| Direction | Value | Mapping |
|---|---|---|
| In | JSON object/array/string/boolean/null | The same JavaScript values. |
| In | Integer with magnitude ≤ 2^53−1 | JavaScript number, exact. |
| In | Integer beyond ±(2^53−1) | Per NUMBER_POLICY: string (default) passes its exact digits as a string; bigint passes a BigInt; reject fails the job (value); lossy passes the nearest number. Converted locations are listed in scriptMeta.convertedNumbers as key paths such as [["order","id"]]. |
| In | Fraction | JavaScript number. The engine’s job API already carries fractions as IEEE-754 binary64 (its variable documents are JSON values), so the worker loses no further precision. FEEL decimals that are not binary64 values are rounded by the engine when it writes them to JSON, before the worker sees them. |
| In | Date, time or duration | These are ISO-8601 strings on the job API. Parse them with new Date(...). |
| Out | number | A JSON number. NaN and ±Infinity fail the job (value). JSON would silently turn them into null. |
| Out | BigInt | A JSON integer when it fits i64/u64; otherwise its exact decimal string. |
| Out | Date | An ISO-8601 UTC string (toJSON), for example 2024-02-29T12:30:00.000Z. |
| Out | undefined | null as the whole result. Inside objects the key is dropped, and inside arrays it becomes null, exactly as JSON.stringify does. |
| Out | Map, Set, functions, symbols | Serialized as JSON.stringify does: {} or dropped. Convert them explicitly. |
resultExpression and errorExpression read the result through FEEL. JSON
numbers become FEEL decimals there, and FEEL results go back to JSON numbers.
Configuration
Section titled “Configuration”Every setting is an environment variable. There is no config file.
| Variable | Default | Meaning |
|---|---|---|
CONDUCTOR_SCRIPT_WORKER_ENGINE_URL | (required) | Engine base URL, for example http://tinyconductor:8080. |
CONDUCTOR_SCRIPT_WORKER_API_VERSION | auto | auto (v2, falling back to v1), v2 or v1. |
CONDUCTOR_SCRIPT_WORKER_TOKEN or CONDUCTOR_SCRIPT_WORKER_TOKEN_FILE | unset | Static bearer token, or a file that holds one. |
CONDUCTOR_SCRIPT_WORKER_OAUTH_TOKEN_URL | unset | Enables the OAuth client-credentials grant — against the engine’s own <engine>/oauth/token or your identity provider. This wins over CONDUCTOR_SCRIPT_WORKER_TOKEN. |
CONDUCTOR_SCRIPT_WORKER_OAUTH_CLIENT_ID | unset | Client id. Required with CONDUCTOR_SCRIPT_WORKER_OAUTH_TOKEN_URL. |
CONDUCTOR_SCRIPT_WORKER_OAUTH_CLIENT_SECRET or CONDUCTOR_SCRIPT_WORKER_OAUTH_CLIENT_SECRET_FILE | unset | Client secret. Required with CONDUCTOR_SCRIPT_WORKER_OAUTH_TOKEN_URL. |
CONDUCTOR_SCRIPT_WORKER_OAUTH_AUDIENCE, CONDUCTOR_SCRIPT_WORKER_OAUTH_SCOPE | unset | Optional token-request parameters. |
CONDUCTOR_SCRIPT_WORKER_TENANT_ID | unset | Sent as X-Tenant-Id, and on v2 as tenantIds. It must be a tenant the credential may act in; a credential of several tenants (Cluster mode) needs it. |
CONDUCTOR_SCRIPT_WORKER_JOB_TYPES | tc:script:1,script | Comma-separated job types, polled round-robin. |
CONDUCTOR_SCRIPT_WORKER_WORKER_NAME | bpm-script-worker-$HOSTNAME | Worker name on activation. |
CONDUCTOR_SCRIPT_WORKER_CONCURRENCY | CPU count | Maximum jobs in flight. Each job uses one blocking thread. |
CONDUCTOR_SCRIPT_WORKER_JOB_TIMEOUT_MS | 60000 | Job lease requested on activation. |
CONDUCTOR_SCRIPT_WORKER_REQUEST_TIMEOUT_MS | 20000 | Long-poll duration requested on activation. |
CONDUCTOR_SCRIPT_WORKER_POLL_INTERVAL_MS | 250 | Pause after an empty or failed poll. |
CONDUCTOR_SCRIPT_WORKER_BACKEND | quickjs | quickjs or boa. The backend must be compiled in. |
CONDUCTOR_SCRIPT_WORKER_CACHE_SIZE | 256 | Prepared-script handles cached, by SHA-256 of the source. |
CONDUCTOR_SCRIPT_WORKER_SCRIPT_TIMEOUT_MS | 5000 | Wall-clock limit. |
CONDUCTOR_SCRIPT_WORKER_SCRIPT_INSTRUCTION_BUDGET | 100000000 | CPU budget in backend operations. |
CONDUCTOR_SCRIPT_WORKER_SCRIPT_MEMORY_LIMIT_BYTES | 67108864 | Heap cap. |
CONDUCTOR_SCRIPT_WORKER_SCRIPT_MAX_STACK_BYTES | 524288 | Stack cap (QuickJS). |
CONDUCTOR_SCRIPT_WORKER_SCRIPT_RECURSION_LIMIT | 400 | Call-depth cap (Boa). |
CONDUCTOR_SCRIPT_WORKER_NUMBER_POLICY | string | How unsafe integers are passed: string, bigint, reject or lossy. |
CONDUCTOR_SCRIPT_WORKER_RETRY_BACKOFF | PT5S | Default failure backoff (ISO-8601 or milliseconds). |
CONDUCTOR_SCRIPT_WORKER_METRICS_BIND | 127.0.0.1:9464 | Listener for /metrics, /healthz and /readyz. A listener other hosts can reach needs METRICS_TOKEN. |
CONDUCTOR_SCRIPT_WORKER_METRICS_TOKEN (or CONDUCTOR_SCRIPT_WORKER_METRICS_TOKEN_FILE) | unset | Bearer token /metrics requires. /healthz and /readyz stay open. |
CONDUCTOR_SCRIPT_WORKER_SHUTDOWN_GRACE_MS | 30000 | How long a SIGTERM waits for in-flight jobs. |
RUST_LOG | info | Log filter. Logs are JSON lines on stdout, with trace_id and span_id while a job runs. |
CONDUCTOR_LOGGING_FORMAT | json | json or text. |
OTEL_* | unset | OpenTelemetry export of traces, metrics and logs, exactly as for the engine. See Observability. |
An invalid value names its variable, and the process exits with code 2.
Operations
Section titled “Operations”- Metrics (
GET /metrics, Prometheus text):bpm_script_worker_executions_total{job_type,backend,outcome};bpm_script_worker_failures_total{job_type,backend,class};bpm_script_worker_execution_seconds{job_type,backend}(histogram);bpm_script_worker_api_errors_total{operation};bpm_script_worker_jobs_in_flight;bpm_script_worker_script_cache_{hits,misses}_totaland_entries. The listener is loopback-only by default. To scrape it from another host, bind0.0.0.0:9464and setCONDUCTOR_SCRIPT_WORKER_METRICS_TOKEN; Prometheus then sendsAuthorization: Bearer <token>. Job types appear as labels and can name business functions, so the worker refuses to start with a reachable listener and no token.
- Tracing. Each job runs in a
script.executespan that continues the trace in the job’straceparentheader, so the worker’s span sits between the engine’s job activation and the completion request in one trace. The completion, failure or error request carries the span’straceparentand the job’sbpm.correlationIdasbpm-correlation-id. Configure export with theOTEL_*variables (see Observability). - Health.
GET /healthzreturns 200 while the process runs.GET /readyzreturns 200 after the first successful activation call. The image’sHEALTHCHECKrunsbpm-script-worker healthcheck, because the runtime image has no shell.bpm-script-worker --version(or-V) prints the version and the source commit and exits without reading any settings. - Graceful shutdown. On SIGTERM or Ctrl-C the worker stops activating and waits
up to
SHUTDOWN_GRACE_MSfor in-flight jobs. A job still running after that keeps its lease, times out on the engine, and another replica picks it up. - Scaling. Replicas share nothing: no state, no leader and no local storage. Run as many as the job load needs. The engine hands each job to one worker lease at a time.
Container
Section titled “Container”The worker is published as the container image
registry.tinyfactory.ai/tinyblox/tinyconductor-script-worker, for linux/amd64 and
linux/arm64, with the same version tags as the engine images:
docker run -d --name script-worker \ -e CONDUCTOR_SCRIPT_WORKER_ENGINE_URL=http://tinyconductor:8080 \ -e CONDUCTOR_SCRIPT_WORKER_TOKEN=<engine token> \ registry.tinyfactory.ai/tinyblox/tinyconductor-script-worker:0.1.0The image is distroless cc-debian12:nonroot (uid 65532), with no shell or
package manager. It runs scripts with QuickJS; the published image does not
include the Boa backend.
docker compose, next to the engine
Section titled “docker compose, next to the engine”services: tinyconductor: image: registry.tinyfactory.ai/tinyblox/tinyconductor-bundled:0.1.0 ports: ["127.0.0.1:8080:8080"] volumes: ["tinyconductor-data:/var/lib/postgresql/data"]
script-worker: image: registry.tinyfactory.ai/tinyblox/tinyconductor-script-worker:0.1.0 depends_on: [tinyconductor] environment: CONDUCTOR_SCRIPT_WORKER_ENGINE_URL: http://tinyconductor:8080 CONDUCTOR_SCRIPT_WORKER_TOKEN_FILE: /run/secrets/engine-token CONDUCTOR_SCRIPT_WORKER_CONCURRENCY: "8" CONDUCTOR_SCRIPT_WORKER_SCRIPT_TIMEOUT_MS: "2000" secrets: [engine-token] deploy: replicas: 2
secrets: engine-token: file: ./engine-token # an API client's token (or, to try it, the first-boot token)
volumes: tinyconductor-data: {}Kubernetes
Section titled “Kubernetes”The tinyconductor-script-worker Helm chart runs the worker this way, with the
token or client secret mounted from a Secret (see Kubernetes).
The plain manifest below shows the same settings:
apiVersion: apps/v1kind: Deploymentmetadata: name: bpm-script-workerspec: replicas: 3 selector: matchLabels: { app: bpm-script-worker } template: metadata: labels: { app: bpm-script-worker } annotations: prometheus.io/scrape: "true" prometheus.io/port: "9464" spec: terminationGracePeriodSeconds: 45 # > CONDUCTOR_SCRIPT_WORKER_SHUTDOWN_GRACE_MS securityContext: runAsNonRoot: true runAsUser: 65532 seccompProfile: { type: RuntimeDefault } containers: - name: worker image: registry.tinyfactory.ai/tinyblox/tinyconductor-script-worker:0.1.0 env: - { name: CONDUCTOR_SCRIPT_WORKER_ENGINE_URL, value: "http://tinyconductor:8080" } - { name: CONDUCTOR_SCRIPT_WORKER_OAUTH_TOKEN_URL, value: "https://idp.example/oauth/token" } - { name: CONDUCTOR_SCRIPT_WORKER_OAUTH_CLIENT_ID, value: "script-worker" } - name: CONDUCTOR_SCRIPT_WORKER_OAUTH_CLIENT_SECRET valueFrom: { secretKeyRef: { name: script-worker, key: client-secret } } - { name: CONDUCTOR_SCRIPT_WORKER_CONCURRENCY, value: "4" } - { name: CONDUCTOR_SCRIPT_WORKER_SHUTDOWN_GRACE_MS, value: "30000" } # Reachable by the kubelet probes and Prometheus, so a token guards /metrics. - { name: CONDUCTOR_SCRIPT_WORKER_METRICS_BIND, value: "0.0.0.0:9464" } - name: CONDUCTOR_SCRIPT_WORKER_METRICS_TOKEN valueFrom: { secretKeyRef: { name: script-worker, key: metrics-token } } - { name: OTEL_EXPORTER_OTLP_ENDPOINT, value: "http://otel-collector.observability:4318" } - { name: OTEL_EXPORTER_OTLP_INSECURE, value: "true" } ports: [{ name: metrics, containerPort: 9464 }] livenessProbe: { httpGet: { path: /healthz, port: metrics } } readinessProbe: { httpGet: { path: /readyz, port: metrics } } resources: # QuickJS caps each script at SCRIPT_MEMORY_LIMIT_BYTES; size the # limit as CONCURRENCY x that cap plus ~64 MiB for the process. requests: { cpu: "500m", memory: "128Mi" } limits: { cpu: "4", memory: "384Mi" } securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: { drop: ["ALL"] }Compatibility with existing script workers and connectors
Section titled “Compatibility with existing script workers and connectors”Community script worker
Section titled “Community script worker”These conventions come from the public README of the widely used community script worker, including the section on its older header conventions. No source code was consulted.
| Convention | Status here |
|---|---|
Default job type script | ✅ Subscribed by default, next to tc:script:1. |
language task header | ✅ javascript, and also feel. |
script task header | ✅ |
resultVariable task header | ✅ Optional here. With no result header, the job completes without variables. |
| Variables available to the script | ✅ As top-level bindings and as the frozen variables object. |
Script result = last expression value (a + b) | ✅ Also supports return and synchronous await. |
| JavaScript engine GraalVM JS | ≠ QuickJS by default, or Boa. ES2023-level syntax: see Backends. |
groovy, kotlin, mustache | ❌ Rejected with 0 retries and a clear message. These are JVM engines. |
job / client objects in the context | ❌ The community worker also dropped these (“no longer provides access”). |
| Script location/resource header | ❌ The README documents none. Scripts are inline. |
Custom engines through ScriptEngineFactory / SPI | ❌ Not applicable: add a Rust ScriptBackend instead. |
| Throwing a BPMN error from a script | ➕ The README documents no convention. This worker adds throw new BpmnError(code, message, variables), plus errorExpression. |
Connector conventions
Section titled “Connector conventions”These are the published conventions of compatible connectors (see Compatibility).
| Convention | Status here |
|---|---|
Connector-style job type (io.camunda:http-json:1 style) | ✅ tc:script:1, configurable. |
| Element template that generates the task definition, inputs and output/error handling | ✅ The Script (JavaScript) template the engine ships (GET /api/v1/console/modeling/element-templates), applied in the console’s Modeler. It follows the standard element-template JSON shape. |
resultVariable header | ✅ |
resultExpression header, FEEL over the response | ✅ Evaluated in the worker with bpm-feel, over result (alias response) and the job’s variables. It must return a context. |
errorExpression header with bpmnError(code, message[, variables]) / jobError(message[, variables[, retries[, retryBackoff]]]) | ✅ Evaluated in the worker, over result/response after success, or over error = {code, type, message} after a failure. null or {} means “no error”. |
retryBackoff header (ISO-8601) | ✅ |
Task-definition retries | ✅ The engine enforces it, as for service tasks. |
Inputs as tc:ioMapping input variables (script, language) | 🟡 Accepted by the worker, but the template uses task headers for them. This engine compiles every input-mapping source as FEEL, so raw JavaScript in an input mapping is rejected at deployment. A FEEL string literal (="a + b") works. Headers are always delivered exactly as written. |
Secrets ({{secrets.X}}) | ❌ Not supported: this worker has no secret store. Pass values as process variables or headers. |
Outbound connector SDK (Java OutboundConnectorFunction), connector runtime discovery, inbound connectors | ❌ Not applicable. This is a Rust job worker. It is “a connector” in the sense of the job-worker protocol, the template and the header conventions, not the Java SDK. |
| Stock connector runtime running this template | ❌ The template’s job type has no Java implementation. Conversely, stock connectors run against TinyConductor’s /v2 API as described in Connectors. |
Backends
Section titled “Backends”QuickJS (js-quickjs) is the default feature and the default backend. Boa
(js-boa) is an optional feature. The choice rests on a benchmark of both
(Apple M5 Max, release build, fresh isolate per execution):
| Scenario | QuickJS warm p50 / p99 (ms) | Boa warm p50 / p99 (ms) |
|---|---|---|
| Arithmetic + JSON transform | 0.25 / 0.41 | 0.40 / 0.98 |
| String templating (50 lines) | 0.31 / 0.47 | 0.88 / 2.03 |
| Array map/filter over 10k items | 26.3 / 29.5 | 161.0 / 203.6 |
| Date calculation | 0.21 / 0.28 | 0.38 / 2.44 |
| Regex over 1k strings | 1.26 / 1.37 | 4.66 / 7.78 |
QuickJS enforces every limit: CPU budget, wall-clock timeout, memory cap and stack. Boa enforces only the per-frame loop limit and recursion depth. Because scripts from several tenants share a worker, limit enforcement decided the default.
Dependencies and licences
Section titled “Dependencies and licences”The script worker image carries these third-party libraries beyond those of the engine, with their licences. The image’s third-party notices hold the full texts.
| Crates | Licence | Feature |
|---|---|---|
rquickjs, rquickjs-core, rquickjs-sys 0.10 (bundles QuickJS-ng C sources, MIT) | MIT | js-quickjs (default) |
foldhash 0.2 (via hashbrown 0.16 in rquickjs-core) | Zlib (permissive) | js-quickjs |
hashbrown 0.16, rustc-hash 2, portable-atomic, proc-macro-crate, toml_edit/toml_parser/toml_datetime, rustversion, num-integer | MIT OR Apache-2.0 | js-quickjs |
tap, winnow 1 | MIT | js-quickjs |
boa_engine and boa_* 0.20 | MIT OR Unlicense | js-boa |
icu_collections, icu_locid, icu_normalizer, icu_properties, icu_provider (+ data/macros) 1.5, litemap, tinystr, writeable, yoke, zerovec | Unicode-3.0 (permissive) | js-boa only |
ryu-js | Apache-2.0 OR BSL-1.0 (take Apache-2.0) | js-boa |
bytemuck | Zlib OR Apache-2.0 OR MIT (take MIT) | js-boa |
num_enum | BSD-3-Clause OR MIT OR Apache-2.0 (take MIT) | js-boa |
regress, time, num-bigint, itertools, dashmap, phf, thin-vec, intrusive-collections, pollster, paste, static_assertions, and the rest | MIT and/or Apache-2.0 | js-boa |
The worker also uses libraries it shares with the engine: reqwest (rustls),
axum, tokio, serde_json, sha2, uuid, rust_decimal and chrono.